mirror of
https://github.com/CTR-tools/CTR-ModSDK.git
synced 2024-11-23 05:29:49 +00:00
Launcher: automatic duckstation
This commit is contained in:
parent
a42be5271a
commit
6a76670186
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,6 +23,7 @@ debug/
|
|||||||
x64/
|
x64/
|
||||||
data/
|
data/
|
||||||
packages/
|
packages/
|
||||||
|
externals/
|
||||||
*.log
|
*.log
|
||||||
offset.txt
|
offset.txt
|
||||||
comport.txt
|
comport.txt
|
||||||
|
@ -4,27 +4,34 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
const std::string g_dataFolder = "data/";
|
const std::string g_dataFolder = "data/";
|
||||||
|
const std::string g_duckFolder = g_dataFolder + "duckstation/";
|
||||||
|
const std::string g_duckExecutable = g_duckFolder + "duckstation-qt-x64-ReleaseLTCG.exe";
|
||||||
const std::string g_clientString = "client.zip";
|
const std::string g_clientString = "client.zip";
|
||||||
const std::string g_clientExecutable = "Client.exe";
|
const std::string g_clientExecutable = "Client.exe";
|
||||||
const std::string g_patchString = "ctr-u_Online30.xdelta";
|
const std::string g_patchString = "ctr-u_Online30.xdelta";
|
||||||
const std::string g_configString = "SCUS-94426.ini";
|
const std::string g_configString = "settings.ini";
|
||||||
|
|
||||||
std::string GetClientPath(const std::string& version)
|
const std::string GetClientPath(const std::string& version)
|
||||||
{
|
{
|
||||||
return g_dataFolder + version + "/" + g_clientExecutable;
|
return g_dataFolder + version + "/" + g_clientExecutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetPatchedGamePath(const std::string& version)
|
const std::string GetPatchedGamePath(const std::string& version)
|
||||||
{
|
{
|
||||||
std::string s_patch = g_dataFolder + version + "/" + g_patchString;
|
std::string s_patch = g_dataFolder + version + "/" + g_patchString;
|
||||||
return s_patch.substr(0, s_patch.find(".")) + ".bin";
|
return s_patch.substr(0, s_patch.find(".")) + ".bin";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetConfigPath(const std::string& version)
|
const std::string GetIniPath_Version(const std::string& version)
|
||||||
{
|
{
|
||||||
return g_dataFolder + version + "/" + g_configString;
|
return g_dataFolder + version + "/" + g_configString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string GetIniPath_Duck()
|
||||||
|
{
|
||||||
|
return g_duckFolder + g_configString;
|
||||||
|
}
|
||||||
|
|
||||||
DataManager g_dataManager;
|
DataManager g_dataManager;
|
||||||
|
|
||||||
DataManager::DataManager()
|
DataManager::DataManager()
|
||||||
|
@ -6,14 +6,17 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
extern const std::string g_dataFolder;
|
extern const std::string g_dataFolder;
|
||||||
|
extern const std::string g_duckFolder;
|
||||||
|
extern const std::string g_duckExecutable;
|
||||||
extern const std::string g_clientString;
|
extern const std::string g_clientString;
|
||||||
extern const std::string g_clientExecutable;
|
extern const std::string g_clientExecutable;
|
||||||
extern const std::string g_patchString;
|
extern const std::string g_patchString;
|
||||||
extern const std::string g_configString;
|
extern const std::string g_configString;
|
||||||
|
|
||||||
std::string GetClientPath(const std::string& version);
|
const std::string GetClientPath(const std::string& version);
|
||||||
std::string GetPatchedGamePath(const std::string& version);
|
const std::string GetPatchedGamePath(const std::string& version);
|
||||||
std::string GetConfigPath(const std::string& version);
|
const std::string GetIniPath_Version(const std::string& version);
|
||||||
|
const std::string GetIniPath_Duck();
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
@ -4,8 +4,12 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
App app;
|
App app;
|
||||||
app.Init();
|
app.Init();
|
||||||
|
#ifdef _DEBUG
|
||||||
|
app.Run();
|
||||||
|
#else
|
||||||
try { app.Run(); }
|
try { app.Run(); }
|
||||||
catch (...) {};
|
catch (...) {};
|
||||||
|
#endif
|
||||||
app.Close();
|
app.Close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,31 @@
|
|||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
static bool DownloadFile(const std::string& sitePath, const std::string& filePath)
|
bool Requests::DownloadFile(const std::string& domain, const std::string& sitePath, const std::string& filePath)
|
||||||
{
|
{
|
||||||
httplib::SSLClient request("www.online-ctr.com");
|
httplib::SSLClient request(domain);
|
||||||
httplib::Result response = request.Get("/wp-content/uploads/onlinectr_patches/" + sitePath);
|
httplib::Result response = request.Get(sitePath);
|
||||||
if (response && response->status == 200) {
|
if (!response) { return false; }
|
||||||
|
if (response->status == 200)
|
||||||
|
{
|
||||||
std::ofstream file(filePath.c_str(), std::ios::binary);
|
std::ofstream file(filePath.c_str(), std::ios::binary);
|
||||||
file.write(response->body.c_str(), response->body.size());
|
file.write(response->body.c_str(), response->body.size());
|
||||||
file.close();
|
file.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (response->status == 302)
|
||||||
|
{
|
||||||
|
const std::string hostStart = "://";
|
||||||
|
const std::string pathStart = "/";
|
||||||
|
std::string location = response->get_header_value("Location");
|
||||||
|
if (location == domain + sitePath) { return false; }
|
||||||
|
|
||||||
|
size_t hostStartIndex = location.find(hostStart) + hostStart.length();
|
||||||
|
size_t pathStartIndex = location.find(pathStart, hostStartIndex);
|
||||||
|
std::string host = location.substr(hostStartIndex, pathStartIndex - hostStartIndex);
|
||||||
|
std::string path = location.substr(pathStartIndex);
|
||||||
|
return DownloadFile(host, path, filePath);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,12 +46,14 @@ bool Requests::CheckUpdates(std::string& version)
|
|||||||
|
|
||||||
bool Requests::DownloadUpdates(const std::string& path, std::string& status)
|
bool Requests::DownloadUpdates(const std::string& path, std::string& status)
|
||||||
{
|
{
|
||||||
|
const std::string octrDomain = "www.online-ctr.com";
|
||||||
|
const std::string octrPath = "/wp-content/uploads/onlinectr_patches/";
|
||||||
const std::vector<std::string> files = { g_clientString, g_patchString, g_configString };
|
const std::vector<std::string> files = { g_clientString, g_patchString, g_configString };
|
||||||
if (!std::filesystem::is_directory(path)) { std::filesystem::create_directory(path); }
|
if (!std::filesystem::is_directory(path)) { std::filesystem::create_directory(path); }
|
||||||
for (const std::string& file : files)
|
for (const std::string& file : files)
|
||||||
{
|
{
|
||||||
status = "Downloading " + file + "...";
|
status = "Downloading " + file + "...";
|
||||||
if (!DownloadFile(file, path + file))
|
if (!DownloadFile(octrDomain, octrPath + file, path + file))
|
||||||
{
|
{
|
||||||
status = "Error downloading " + file;
|
status = "Error downloading " + file;
|
||||||
return false;
|
return false;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
namespace Requests
|
namespace Requests
|
||||||
{
|
{
|
||||||
|
bool DownloadFile(const std::string& domain, const std::string& sitePath, const std::string& filePath);
|
||||||
bool CheckUpdates(std::string& version);
|
bool CheckUpdates(std::string& version);
|
||||||
bool DownloadUpdates(const std::string& version, std::string& status);
|
bool DownloadUpdates(const std::string& version, std::string& status);
|
||||||
}
|
}
|
@ -10,10 +10,9 @@
|
|||||||
|
|
||||||
UI::UI()
|
UI::UI()
|
||||||
{
|
{
|
||||||
|
g_dataManager.BindData(&m_biosPath, DataType::STRING, "BiosPath");
|
||||||
g_dataManager.BindData(&m_gamePath, DataType::STRING, "GamePath");
|
g_dataManager.BindData(&m_gamePath, DataType::STRING, "GamePath");
|
||||||
g_dataManager.BindData(&m_duckPath, DataType::STRING, "DuckPath");
|
|
||||||
g_dataManager.BindData(&m_version, DataType::STRING, "GameVersion");
|
g_dataManager.BindData(&m_version, DataType::STRING, "GameVersion");
|
||||||
g_dataManager.BindData(&m_iniPath, DataType::STRING, "IniPath");
|
|
||||||
g_dataManager.BindData(&m_username, DataType::STRING, "Username");
|
g_dataManager.BindData(&m_username, DataType::STRING, "Username");
|
||||||
m_updater.CheckForUpdates(m_status, m_version);
|
m_updater.CheckForUpdates(m_status, m_version);
|
||||||
}
|
}
|
||||||
@ -30,31 +29,30 @@ void UI::Render(int width, int height)
|
|||||||
if (m_username.size() > 9) { m_username = m_username.substr(0, 9); }
|
if (m_username.size() > 9) { m_username = m_username.substr(0, 9); }
|
||||||
|
|
||||||
bool updateReady = true;
|
bool updateReady = true;
|
||||||
|
updateReady &= SelectFile(m_biosPath, "Bios Path ", {".bin"}, {"PSX Bios File", "*.bin"}, "Path to a PS1 NTSC-U bios.");
|
||||||
updateReady &= SelectFile(m_gamePath, "Game Path", {".bin", ".img", ".iso"}, {"Game Files", "*.bin *.img *.iso"}, "Path to the clean NTSC-U version of CTR");
|
updateReady &= SelectFile(m_gamePath, "Game Path", {".bin", ".img", ".iso"}, {"Game Files", "*.bin *.img *.iso"}, "Path to the clean NTSC-U version of CTR");
|
||||||
updateReady &= SelectFile(m_duckPath, "Duck Path ", {".exe"}, {"Executable Files", "*.exe"}, "Path to the duckstation executable");
|
|
||||||
updateReady &= SelectFolder(m_iniPath, "Ini Path ", "Duckstation gamesettings folder.\nUsually in Documents/DuckStation/gamesettings");
|
|
||||||
ImGui::Text(("Version: " + m_version).c_str());
|
ImGui::Text(("Version: " + m_version).c_str());
|
||||||
|
|
||||||
ImGui::BeginDisabled(m_updater.IsBusy() || !m_updater.IsUpdated());
|
ImGui::BeginDisabled(m_updater.IsBusy() || !m_updater.IsUpdated());
|
||||||
if (ImGui::Button("Launch Game"))
|
if (ImGui::Button("Launch Game"))
|
||||||
{
|
{
|
||||||
std::string s_clientPath = GetClientPath(m_version);
|
const std::string s_clientPath = GetClientPath(m_version);
|
||||||
std::string s_patchedPath = GetPatchedGamePath(m_version);
|
const std::string s_patchedPath = GetPatchedGamePath(m_version);
|
||||||
if (!std::filesystem::exists(s_clientPath)) { m_status = "Error: could not find " + s_clientPath; }
|
if (!std::filesystem::exists(s_clientPath)) { m_status = "Error: could not find " + s_clientPath; }
|
||||||
else if (!std::filesystem::exists(s_patchedPath)) { m_status = "Error: could not find " + s_patchedPath; }
|
else if (!std::filesystem::exists(s_patchedPath)) { m_status = "Error: could not find " + s_patchedPath; }
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g_dataManager.SaveData();
|
g_dataManager.SaveData();
|
||||||
std::string clientCommand = "start /b \"\" \"" + std::filesystem::current_path().string() + "/" + GetClientPath(m_version) + "\" " + m_username + " &";
|
const std::string clientCommand = "start /b \"\" \"" + std::filesystem::current_path().string() + "/" + GetClientPath(m_version) + "\" " + m_username + " &";
|
||||||
std::system(clientCommand.c_str());
|
std::system(clientCommand.c_str());
|
||||||
const std::string duckCommand = "start /b \"\" \"" + m_duckPath + "\" \"" + s_patchedPath + "\" &";
|
const std::string duckCommand = "start /b \"\" \"" + g_duckExecutable + "\" \"" + s_patchedPath + "\" &";
|
||||||
std::system(duckCommand.c_str());
|
std::system(duckCommand.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginDisabled(m_updater.IsBusy() || !updateReady);
|
ImGui::BeginDisabled(m_updater.IsBusy() || !updateReady);
|
||||||
if (ImGui::Button("Update")) { m_updater.Update(m_status, m_version, m_gamePath, m_iniPath); }
|
if (ImGui::Button("Update")) { m_updater.Update(m_status, m_version, m_gamePath, m_biosPath); }
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
if (!m_status.empty()) { ImGui::Text(m_status.c_str()); }
|
if (!m_status.empty()) { ImGui::Text(m_status.c_str()); }
|
||||||
|
@ -17,9 +17,8 @@ private:
|
|||||||
private:
|
private:
|
||||||
Updater m_updater;
|
Updater m_updater;
|
||||||
std::string m_version = "None";
|
std::string m_version = "None";
|
||||||
|
std::string m_biosPath;
|
||||||
std::string m_gamePath;
|
std::string m_gamePath;
|
||||||
std::string m_duckPath;
|
|
||||||
std::string m_iniPath;
|
|
||||||
std::string m_username;
|
std::string m_username;
|
||||||
std::string m_status;
|
std::string m_status;
|
||||||
};
|
};
|
@ -2,12 +2,15 @@
|
|||||||
#include "requests.h"
|
#include "requests.h"
|
||||||
#include "dataManager.h"
|
#include "dataManager.h"
|
||||||
#include "patch.h"
|
#include "patch.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
Updater::Updater()
|
Updater::Updater()
|
||||||
{
|
{
|
||||||
g_dataManager.BindData(&m_updated, DataType::BOOL, "Updated");
|
g_dataManager.BindData(&m_updated, DataType::BOOL, "Updated");
|
||||||
|
g_dataManager.BindData(&m_hasDuckstation, DataType::BOOL, "Duck");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Updater::IsUpdated()
|
bool Updater::IsUpdated()
|
||||||
@ -36,11 +39,45 @@ bool Updater::CheckForUpdates(std::string& status, const std::string& currVersio
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Updater::Update(std::string& status, std::string& currVersion, const std::string& gamePath, const std::string& iniPath)
|
bool Updater::Update(std::string& status, std::string& currVersion, const std::string& gamePath, const std::string& biosPath)
|
||||||
{
|
{
|
||||||
return StartRoutine([&]
|
return StartRoutine([&]
|
||||||
{
|
{
|
||||||
std::string version;
|
std::string version;
|
||||||
|
if (!m_hasDuckstation)
|
||||||
|
{
|
||||||
|
status = "Downloading Duckstation...";
|
||||||
|
std::filesystem::create_directory(g_duckFolder);
|
||||||
|
const std::string duckArchive = "duckstation.zip";
|
||||||
|
if (!Requests::DownloadFile("github.com", "/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.zip", g_duckFolder + duckArchive))
|
||||||
|
{
|
||||||
|
status = "Error: could not download Duckstation.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
status = "Decompressing Duckstation...";
|
||||||
|
if (!IO::DecompressFiles(g_duckFolder, duckArchive))
|
||||||
|
{
|
||||||
|
status = "Error: could not decompress Duckstation.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
status = "Installing custom settings...";
|
||||||
|
const std::string g_biosFolder = g_duckFolder + "bios/";
|
||||||
|
std::filesystem::create_directory(g_biosFolder);
|
||||||
|
std::string biosName;
|
||||||
|
for (int i = static_cast<int>(biosPath.size()) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (biosPath[i] == '/' || biosPath[i] == '\\')
|
||||||
|
{
|
||||||
|
biosName = biosPath.substr(i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::filesystem::copy_file(biosPath, g_biosFolder + biosName);
|
||||||
|
const std::string duckPortable = g_duckFolder + "portable.txt";
|
||||||
|
std::ofstream portableFile(duckPortable.c_str());
|
||||||
|
portableFile.close();
|
||||||
|
m_hasDuckstation = true;
|
||||||
|
}
|
||||||
status = "Checking for new updates...";
|
status = "Checking for new updates...";
|
||||||
if (m_updateAvailable || Requests::CheckUpdates(version))
|
if (m_updateAvailable || Requests::CheckUpdates(version))
|
||||||
{
|
{
|
||||||
@ -50,20 +87,18 @@ bool Updater::Update(std::string& status, std::string& currVersion, const std::s
|
|||||||
std::string path = g_dataFolder + m_versionAvailable + "/";
|
std::string path = g_dataFolder + m_versionAvailable + "/";
|
||||||
if (Requests::DownloadUpdates(path, status) && Patch::NewVersion(path, gamePath, status))
|
if (Requests::DownloadUpdates(path, status) && Patch::NewVersion(path, gamePath, status))
|
||||||
{
|
{
|
||||||
std::string s_ini;
|
std::filesystem::copy_file(GetIniPath_Version(currVersion), GetIniPath_Duck());
|
||||||
if (iniPath.back() == '/' || iniPath.back() == '\\') { s_ini = iniPath + g_configString; }
|
|
||||||
else { s_ini = iniPath + "/" + g_configString; }
|
|
||||||
if (std::filesystem::exists(s_ini)) { std::filesystem::remove(s_ini); }
|
|
||||||
std::filesystem::copy(path + g_configString, s_ini);
|
|
||||||
m_updated = true;
|
m_updated = true;
|
||||||
m_updateAvailable = false;
|
m_updateAvailable = false;
|
||||||
currVersion = m_versionAvailable;
|
currVersion = m_versionAvailable;
|
||||||
status = "Update completed.";
|
status = "Update completed.";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { status = "Already on the latest patch"; }
|
else { status = "Already on the latest patch"; }
|
||||||
}
|
}
|
||||||
else { status = "Error: could not establish connection"; }
|
else { status = "Error: could not establish connection"; }
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ public:
|
|||||||
bool IsUpdated();
|
bool IsUpdated();
|
||||||
bool IsBusy();
|
bool IsBusy();
|
||||||
bool CheckForUpdates(std::string& status, const std::string& currVersion);
|
bool CheckForUpdates(std::string& status, const std::string& currVersion);
|
||||||
bool Update(std::string& status, std::string& currVersion, const std::string& gamePath, const std::string& iniPath);
|
bool Update(std::string& status, std::string& currVersion, const std::string& gamePath, const std::string& biosPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool StartRoutine(const std::function<void(void)>& func);
|
bool StartRoutine(const std::function<void(void)>& func);
|
||||||
@ -21,5 +21,6 @@ private:
|
|||||||
std::string m_versionAvailable;
|
std::string m_versionAvailable;
|
||||||
bool m_routineRunning = false;
|
bool m_routineRunning = false;
|
||||||
bool m_updateAvailable = false;
|
bool m_updateAvailable = false;
|
||||||
|
bool m_hasDuckstation = false;
|
||||||
bool m_updated;
|
bool m_updated;
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user