XSteam/src/xsteam_ex.cpp

626 lines
21 KiB
C++

#include "xsteam_ex.h"
#include <iostream>
#include <filesystem>
void xsteam_init_opts()
{
// current dir
ex_base_exe_path = QDir::currentPath();
// current dir+/
ex_base_exe_ex = ex_base_exe_path + QStringLiteral("/");
// current dir+/save
ex_save_path = ex_base_exe_path + QStringLiteral("/save");
// current dir+/save/
ex_save_path_ex = ex_save_path + QStringLiteral("/");
xsteam_data_unserialize();
if (_data.s_data.size() > 0)
{
for (auto x : _data.s_data)
{
xsteam_branch_tag_data_read(x.src_name);
}
}
}
void xsteam_vdf_free_dir_file(UidData u_data)
{
}
void xsteam_git2_error_check(int error)
{
if (error != 0)
{
const git_error *err = git_error_last();
if (err)
{
printf("ERROR %d: %s\n", err->klass, err->message);
}
else
{
printf("ERROR %d: no detailed info\n", error);
}
}
}
void xsteam_msg_tip(QWidget *parent, QString tipInfo)
{
UNUSED(parent);
QMessageBox::information(nullptr, "TIP", tipInfo);
}
void xsteam_msg_error(QWidget *parent, QString tipInfo)
{
UNUSED(parent);
QMessageBox::information(nullptr, "ERROR", tipInfo);
}
bool xsteam_msg_select(QWidget *parent, QString tipText)
{
QMessageBox *msgBox = nullptr;
if (msgBox != nullptr)
{
delete msgBox;
msgBox = nullptr;
}
msgBox = new QMessageBox(parent);
msgBox->setWindowModality(Qt::WindowModal);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint);
msgBox->setInformativeText(tipText);
msgBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox->setStyleSheet("QLabel{"
"qproperty-alignment:AlignHCenter;"
"min-width: 400px;"
"min-height: 20px; "
"}");
int msgRet = msgBox->exec();
if (msgRet == QMessageBox::Ok)
{
return true;
}
return false;
}
void xsteam_branch_tag_data_read(std::string _src_name)
{
QString save_file_str = ex_save_path_ex + "tags_" + QString::fromStdString(_src_name) + ".data";
if (!QFile(save_file_str).exists())
{
return;
}
_data.s_ex_data[_src_name].clear();
std::ifstream file(save_file_str.toStdString(), std::ios::binary);
if (!file.is_open())
{
qDebug() << "Failed to open file: " << save_file_str;
}
std::string line;
while (std::getline(file, line))
{
// qDebug() << "Line_Branch_Tag:" << QString::fromStdString(line);
_data.s_ex_data[_src_name].push_back(line);
}
file.close();
}
vdf::object xsteam_vdf_read(QString vdf_path)
{
std::basic_ifstream<char> _T_File(vdf_path.toStdString(), std::ios::in);
if (!_T_File.is_open())
{
throw std::runtime_error("Failed to open file: " + vdf_path.toStdString());
}
std::stringstream buffer;
buffer << _T_File.rdbuf();
std::string vdf_str = buffer.str();
vdf::object vdf_data;
bool ok = false;
vdf_data = vdf::read(vdf_str.begin(), vdf_str.end(), &ok);
return vdf_data;
}
void xsteam_vdf_write(QString vdf_file, vdf::object &vdf_data)
{
std::ofstream _T_W_File(vdf_file.toStdString().c_str());
std::unordered_map<std::basic_string<char>, std::shared_ptr<vdf::basic_object<char>>> _childs;
std::unordered_map<std::basic_string<char>, std::basic_string<char>> _attribs;
for (auto x : vdf_data.childs)
{
if (x.first == "Software")
{
_childs.emplace(x.first, x.second);
}
else if (x.first == "streaming")
{
_childs.emplace(x.first, x.second);
}
else if (x.first == "Music")
{
_childs.emplace(x.first, x.second);
}
else if (x.first == "AuthorizedDevice")
{
_childs.emplace(x.first, x.second);
}
vdf_data.childs.erase(x.first);
}
for (auto u : vdf_data.attribs)
{
if (u.first == "SDL_GamepadBind")
{
vdf_data.attribs.erase(u.first);
}
}
vdf_data.childs.swap(_childs);
vdf_data.attribs.swap(_attribs);
vdf::write(_T_W_File, vdf_data);
}
// 从拉取的库中读取key的信息以std::map类型返回
void xsteam_vdf_read_key(vdf::object vdf_data, UidData &u_data)
{
std::cout << vdf_data.name << std::endl;
std::map<std::string, std::string> keys;
u_data.uid_key.clear();
for (auto &k : vdf_data.childs)
{
std::string u_id = k.first;
std::string u_key = k.second->attribs.find("DecryptionKey")->second;
// 游戏UID depots的key
qDebug() << QString::fromStdString(u_id) << ":" << QString::fromStdString(u_key);
u_data.uid_key.insert(std::pair<std::string, std::string>(u_id, u_key));
}
}
// 读取指定uid目录下的文件名
void xsteam_vdf_manifest_name(QString uid_dir, UidData &u_data)
{
QDir _uid_dir(uid_dir);
QStringList manifest_list = _uid_dir.entryList(QStringList() << "*.manifest", QDir::Files);
u_data.manifest.clear();
for (auto &x : manifest_list)
{
u_data.manifest.push_back(x.toStdString());
}
}
void xsteam_vdf_manifest_move(UidData &u_data)
{
// vdf manifest dir str
QString src_manifest_dir_str = ex_base_exe_ex + QString::fromStdString(u_data.uid);
// steam steamtools dir
QString steam_tools_dir_depotcache_str = QString::fromStdString(_data.x_config.steam_dir) + "/depotcache";
for (auto f : u_data.manifest)
{
qDebug() << f.c_str();
// src manifest file path str
QString src_manifest_file_str = src_manifest_dir_str + "/" + QString::fromStdString(f);
// src QFile object
if (QDir(src_manifest_dir_str).exists() && QFile(src_manifest_file_str).exists() && QDir(steam_tools_dir_depotcache_str).exists())
{
QString dest_manifest_file_str = steam_tools_dir_depotcache_str + "/" + QString::fromStdString(f);
QFile::copy(src_manifest_file_str, dest_manifest_file_str);
}
else
{
return;
}
}
}
void xsteam_vdf_edit(vdf::object, std::map<std::string, std::string> data, VDF_EDIT_MODE edit_mode)
{
if (edit_mode == VDF_EDIT_MODE::ADD)
{
}
}
// curl get steam id info to _data
bool xsteam_curl_steam_id_app_info(UidData &u_data)
{
xsteam_curl *_x_curl = new xsteam_curl();
auto info_str = _x_curl->get_steam_id_info(_data.x_config.steam_api_url, u_data.uid);
delete _x_curl;
_x_curl = nullptr;
if (info_str == "error" || info_str == "")
{
return false;
}
json info_json;
try
{
info_json = json::parse(info_str);
}
catch (json::parse_error &e)
{
std::cerr << "解析错误:" << e.what() << std::endl;
std::cerr << "位置:字符索引 " << e.byte << std::endl;
return false;
}
qDebug() << QString::fromStdString(info_json.dump(4));
for (auto &root_data : info_json.items())
{
auto node_data = root_data.value().at("data").items();
for (auto &data : node_data)
{
if (data.value().is_string())
{
if (data.key() == "name")
{
u_data.name = data.value();
}
}
if (data.value().is_array())
{
if (data.key() == "dlc")
{
if (data.value().size() > 0)
{
u_data.r_dlc.clear();
for (auto x : data.value())
{
u_data.r_dlc.push_back(x.dump());
}
}
}
}
}
}
if (u_data.name != "")
{
//_data.u_data.push_back(u_data);
return true;
}
return false;
}
// 读取xsteam软件保存在save目录下的data.json并且反序列化读取到全局变量内
void xsteam_data_unserialize()
{
QString save_file = ex_save_path_ex + "data.json";
if (!QDir(ex_save_path).exists())
{
QDir().mkdir(ex_save_path);
}
if (QFile(save_file).exists())
{
QFile _save_file(save_file);
_save_file.open(QIODevice::ReadOnly | QIODevice::Text);
std::string temp_str = _save_file.readAll().toStdString();
_save_file.close();
json j3 = json::parse(temp_str);
if (j3 == nullptr)
{
return;
}
for (auto &k : j3.items())
{
// 托管数据服务器列表反序列化
if (k.value().is_object() && k.key() == "src_data")
{
for (auto &u : k.value().items())
{
SrcData _src_data = {};
_src_data.src_name = u.value().at("src_name");
if (u.value().at("src_type") == 0)
{
_src_data.src_type = GIT_MODE::BRANCH;
}
else
{
_src_data.src_type = GIT_MODE::TAG;
}
_src_data.src_url = u.value().at("src_url");
_data.s_data.push_back(_src_data);
}
}
// 入库游戏信息列表反序列化
if (k.value().is_object() && k.key() == "uid_data")
{
// x的key是uid x的value是内容
for (auto &x : k.value().items())
{
UidData _temp_data = {};
_temp_data.name = x.value().at("Name");
_temp_data.uid = x.value().at("Uid");
_temp_data.i_time = x.value().at("In_Time");
_temp_data.m_time = x.value().at("Up_Time");
_temp_data.ico = x.value().at("Ico");
_temp_data.s_name = x.value().at("Src_Name");
if (x.value().contains("Manifest"))
{
_temp_data.manifest = x.value().at("Manifest").get<std::vector<std::string>>();
}
if (x.value().contains("UidKey"))
{
_temp_data.uid_key = x.value().at("UidKey").get<std::map<std::string, std::string>>();
}
if (x.value().contains("ALL_DLC"))
{
_temp_data.r_dlc = x.value().at("ALL_DLC").get<std::vector<std::string>>();
}
if (x.value().contains("IN_DLC"))
{
_temp_data.n_dlc = x.value().at("IN_DLC").get<std::vector<std::string>>();
}
std::cout << "manifest size:" << _temp_data.manifest.size() << std::endl;
std::cout << "uid_key size:" << _temp_data.uid_key.size() << std::endl;
std::cout << "all_dlc size:" << _temp_data.r_dlc.size() << std::endl;
std::cout << "in_dlc size:" << _temp_data.n_dlc.size() << std::endl;
_data.u_data.push_back(_temp_data);
}
}
}
_data.x_config.style = j3.at("xsteam_sets").at("style");
_data.x_config.font = j3.at("xsteam_sets").at("font");
_data.x_config.font_bold = j3.at("xsteam_sets").at("font_bold");
_data.x_config.steam_dir = j3.at("xsteam_sets").at("steam_dir");
_data.x_config.steamtools_dir = j3.at("xsteam_sets").at("steamtools_dir");
_data.x_config.steamtools_st_dir = j3.at("xsteam_sets").at("steamtools_st_dir");
_data.x_config.steam_api_url = j3.at("xsteam_sets").at("steam_api_url");
_data.x_config.steam_logo_url = j3.at("xsteam_sets").at("steam_logo_url");
}
else
{
// 当本地保存数据文件不存在或者读取data.json文件出错或者导致用户修改配置使得读取data.json出错.
// 默认测试数据源
for (int i = 0; i <= ex_default_src_name.count() - 1; i++)
{
SrcData _src_data;
_src_data.src_name = ex_default_src_name[i].toStdString();
_src_data.src_url = ex_default_src_url[i].toStdString();
_src_data.src_type = GIT_MODE::TAG;
_data.s_data.push_back(_src_data);
}
// 默认配置
_data.x_config.font = 0;
_data.x_config.font_bold = 0;
_data.x_config.style = 1;
QSettings settings("HKEY_CURRENT_USER\\Software\\Valve\\Steam", QSettings::NativeFormat);
_data.x_config.steam_dir = (settings.value("SteamPath", "").toString()).toStdString();
_data.x_config.steam_api_url = "https://store.steampowered.com/api/appdetails/?appids=";
_data.x_config.steam_logo_url = "https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/";
_data.x_config.steamtools_dir = _data.x_config.steam_dir + "/config/stUI/";
_data.x_config.steamtools_st_dir = _data.x_config.steam_dir + "/config/stplug-in/";
}
}
// 序列化全局变量中需要保存在data.json目录下的数据
json xsteam_data_serialize(json jsonData)
{
// 序列化数据托管服务器
if (_data.s_data.size() > 0)
{
for (auto &k : _data.s_data)
{
jsonData[toStr(src_data)][k.src_name][toStr(src_name)] = k.src_name;
jsonData[toStr(src_data)][k.src_name][toStr(src_type)] = k.src_type;
jsonData[toStr(src_data)][k.src_name][toStr(src_url)] = k.src_url;
}
}
// 序列化入库游戏数据
if (_data.u_data.size() > 0)
{
for (auto &u : _data.u_data)
{
// 游戏名
jsonData[toStr(uid_data)][u.uid][toStr(Name)] = u.name;
// 游戏UID
jsonData[toStr(uid_data)][u.uid][toStr(Uid)] = u.uid;
// 游戏入库时间
jsonData[toStr(uid_data)][u.uid][toStr(In_Time)] = u.i_time;
// 游戏清单最后更新时间
jsonData[toStr(uid_data)][u.uid][toStr(Up_Time)] = u.m_time;
// 游戏ico图标参数
jsonData[toStr(uid_data)][u.uid][toStr(Ico)] = u.ico;
jsonData[toStr(uid_data)][u.uid][toStr(Src_Name)] = u.s_name;
// 游戏清单列表
for (auto &w : u.manifest)
{
jsonData[toStr(uid_data)][u.uid][toStr(Manifest)].push_back(w);
}
for (auto &[x, y] : u.uid_key)
{
jsonData[toStr(uid_data)][u.uid][toStr(UidKey)][x] = y;
}
// 游戏所有DLC清单
for (auto w : u.r_dlc)
{
jsonData[toStr(uid_data)][u.uid][toStr(ALL_DLC)].push_back(w);
}
// 游戏已经入库DLC清单
for (auto w : u.n_dlc)
{
jsonData[toStr(uid_data)][u.uid][toStr(IN_DLC)].push_back(w);
}
}
}
jsonData[toStr(xsteam_sets)][toStr(style)] = _data.x_config.style;
jsonData[toStr(xsteam_sets)][toStr(font)] = _data.x_config.font;
jsonData[toStr(xsteam_sets)][toStr(font_bold)] = _data.x_config.font_bold;
jsonData[toStr(xsteam_sets)][toStr(steam_dir)] = _data.x_config.steam_dir;
jsonData[toStr(xsteam_sets)][toStr(steamtools_dir)] = _data.x_config.steamtools_dir;
jsonData[toStr(xsteam_sets)][toStr(steamtools_st_dir)] = _data.x_config.steamtools_st_dir;
jsonData[toStr(xsteam_sets)][toStr(steam_api_url)] = _data.x_config.steam_api_url;
jsonData[toStr(xsteam_sets)][toStr(steam_logo_url)] = _data.x_config.steam_logo_url;
return jsonData;
}
void xsteam_save_data_write()
{
QString save_file = ex_save_path_ex + "data.json";
json jsonData = nullptr;
jsonData = xsteam_data_serialize(jsonData);
std::ofstream out(save_file.toStdString(), std::ios::binary);
std::cout << jsonData << std::endl;
out << std::setw(4) << jsonData;
out.close();
}
bool xsteam_lua_to_com(UidData u_data)
{
// 文件路径的设定和检测
QString lua_file = ex_base_exe_ex + QString::fromStdString(u_data.uid) + ".lua";
QString lua_packa_file = ex_base_exe_ex + "luapacka.exe";
QString lua_to_steam_st = QString::fromStdString(_data.x_config.steamtools_st_dir) + QString::fromStdString(u_data.uid) + ".st";
QFile _lua_file(lua_file);
QFile _lua_packa_file(lua_packa_file);
if (_lua_file.exists())
{
QFile::remove(lua_file);
}
std::ofstream dp(lua_file.toStdString());
if (!dp.is_open())
{
xsteam_msg_error(nullptr, "写入lua文件错误");
return false;
}
// 游戏的uid和DLC的uid拟合输出lua脚本文件
QString lua_str;
// 增加游戏uid
/*
1-一般游戏的UID和DLC末尾为0是游戏的解锁代码
2-一般UID内末尾为1则是windows系统的文件包
*/
// 游戏UID-0
lua_str = "addappid(" + QString::fromStdString(u_data.uid) + ",1,\"None\")";
dp << lua_str.toStdString() << "\n";
qDebug() << "UID_KEY_0" << lua_str.toStdString();
// 增加已经入库的DLC的UID-0
for (auto &x : u_data.n_dlc)
{
// 增加DLC的uid同时必须和uid_key列表的u比对只增加远程服务器有的DLC
lua_str = "addappid(" + QString::fromStdString(x) + ",1,\"None\")";
dp << lua_str.toStdString() << "\n";
qDebug() << "DLC_KEY_0" << lua_str.toStdString();
}
for (auto &[uid, key] : u_data.uid_key)
{
if (std::to_string((std::stoi(u_data.uid) + 1)) == uid)
{
// 增加游戏本体的uid和key
lua_str = "addappid(" + QString::fromStdString(uid) + ",1," + "\"" + QString::fromStdString(key) + "\"" + ")";
dp << lua_str.toStdString() << "\n";
qDebug() << "UID_KEY:" << lua_str.toStdString();
}
}
for (auto &d_id : u_data.n_dlc)
{
for (auto &[uid, key] : u_data.uid_key)
{
if (std::to_string((std::stoi(d_id) + 1)) == uid)
{
// 增加DLC的uid和key
lua_str = "addappid(" + QString::fromStdString(uid) + ",1," + "\"" + QString::fromStdString(key) + "\"" + ")";
dp << lua_str.toStdString() << "\n";
qDebug() << "DLC_KEY:" << lua_str.toStdString();
}
}
}
dp.close();
qDebug() << "n_dlc_size:" << u_data.n_dlc;
// luapacka文件不存在则释放luapacka.exe文件
if (!_lua_packa_file.exists())
{
QFile f_dp;
f_dp.copy(":/res/bin/luapacka.exe", lua_packa_file);
f_dp.close();
}
// 输入lua脚本文件输出lua二进制文件
QProcess *m_cmd = new QProcess();
std::cout << "lua_packa_file:" << lua_packa_file.toStdString() << std::endl;
std::cout << "lua_file:" << lua_file.toStdString() << std::endl;
std::cout << "lua_to_steam_st:" << lua_to_steam_st.toStdString() << std::endl;
m_cmd->setWorkingDirectory(ex_base_exe_path);
m_cmd->start(lua_packa_file, QStringList() << lua_file << lua_to_steam_st);
m_cmd->waitForFinished();
m_cmd->kill();
delete m_cmd;
m_cmd = nullptr;
std::cout << "lua_file exists:" << _lua_file.exists() << std::endl;
std::cout << "lua_packa_file exists:" << _lua_packa_file.exists() << std::endl;
if (_lua_file.exists())
{
_lua_file.remove();
};
if (_lua_packa_file.exists())
{
QFile::setPermissions(lua_packa_file,QFile::ReadOther | QFile::WriteOther);
_lua_packa_file.remove();
};
return true;
}
void xsteam_run_steam_steamtools(RUN_MODE _mode)
{
QString run_steam_steamtools_path;
QString _tips;
if (_mode == RUN_MODE::STEAM)
{
run_steam_steamtools_path = QString::fromStdString(_data.x_config.steam_dir) + "/steam.exe";
_tips = "未检测到Steam安装,请先配置Steam路径";
}
else if (_mode == RUN_MODE::STEAMTOOLS)
{
run_steam_steamtools_path = QString::fromStdString(_data.x_config.steamtools_dir) + "Steamtools.exe";
_tips = "未检测到SteamTools安装,请先配置SteamTools路径";
}
if (QFile(run_steam_steamtools_path).exists())
{
QFuture<void> future = QtConcurrent::run([=]()
{
QProcess *m_cmd = new QProcess();
m_cmd->startDetached(run_steam_steamtools_path);
m_cmd->waitForFinished();
m_cmd->kill();
delete m_cmd;
m_cmd = nullptr; });
}
else
{
xsteam_msg_error(nullptr, _tips);
}
}
bool xsteam_uid_exists(std::string uid)
{
for (auto u : _data.u_data)
{
if (u.uid == uid)
{
return true;
}
}
return false;
}
bool xsteam_steam_steamtools_exists(bool &steam_ok, bool &steamtools_ok)
{
if (!_data.x_config.steam_dir.empty())
{
if (QDir(QString::fromStdString(_data.x_config.steam_dir)).exists())
{
steam_ok = true;
}
}
if (!_data.x_config.steamtools_dir.empty())
{
if (QDir(QString::fromStdString(_data.x_config.steamtools_dir)).exists())
{
steamtools_ok = true;
}
}
return steam_ok && steamtools_ok;
}
void xsteam_delete_uid_st(std::string uid)
{
for (auto it = _data.u_data.begin(); it != _data.u_data.end(); ++it)
{
if (it->uid == uid)
{
QFile st(QString::fromStdString(_data.x_config.steamtools_st_dir + uid + ".st"));
if (st.exists())
{
st.remove();
}
_data.u_data.erase(it);
xsteam_save_data_write();
break;
}
}
}
bool xsteam_src_data_exists(std::string src_name)
{
QString save_file_str = ex_save_path_ex + "tags_" + QString::fromStdString(src_name) + ".data";
if (QFile(save_file_str).exists())
{
return true;
}
return false;
}