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/
|
||||
data/
|
||||
packages/
|
||||
externals/
|
||||
*.log
|
||||
offset.txt
|
||||
comport.txt
|
||||
|
@ -4,27 +4,34 @@
|
||||
#include <filesystem>
|
||||
|
||||
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_clientExecutable = "Client.exe";
|
||||
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;
|
||||
}
|
||||
|
||||
std::string GetPatchedGamePath(const std::string& version)
|
||||
const std::string GetPatchedGamePath(const std::string& version)
|
||||
{
|
||||
std::string s_patch = g_dataFolder + version + "/" + g_patchString;
|
||||
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;
|
||||
}
|
||||
|
||||
const std::string GetIniPath_Duck()
|
||||
{
|
||||
return g_duckFolder + g_configString;
|
||||
}
|
||||
|
||||
DataManager g_dataManager;
|
||||
|
||||
DataManager::DataManager()
|
||||
|
@ -6,14 +6,17 @@
|
||||
#include <string>
|
||||
|
||||
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_clientExecutable;
|
||||
extern const std::string g_patchString;
|
||||
extern const std::string g_configString;
|
||||
|
||||
std::string GetClientPath(const std::string& version);
|
||||
std::string GetPatchedGamePath(const std::string& version);
|
||||
std::string GetConfigPath(const std::string& version);
|
||||
const std::string GetClientPath(const std::string& version);
|
||||
const std::string GetPatchedGamePath(const std::string& version);
|
||||
const std::string GetIniPath_Version(const std::string& version);
|
||||
const std::string GetIniPath_Duck();
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
@ -4,8 +4,12 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
App app;
|
||||
app.Init();
|
||||
#ifdef _DEBUG
|
||||
app.Run();
|
||||
#else
|
||||
try { app.Run(); }
|
||||
catch (...) {};
|
||||
#endif
|
||||
app.Close();
|
||||
return 0;
|
||||
}
|
||||
|
@ -5,16 +5,31 @@
|
||||
#include <httplib.h>
|
||||
#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::Result response = request.Get("/wp-content/uploads/onlinectr_patches/" + sitePath);
|
||||
if (response && response->status == 200) {
|
||||
httplib::SSLClient request(domain);
|
||||
httplib::Result response = request.Get(sitePath);
|
||||
if (!response) { return false; }
|
||||
if (response->status == 200)
|
||||
{
|
||||
std::ofstream file(filePath.c_str(), std::ios::binary);
|
||||
file.write(response->body.c_str(), response->body.size());
|
||||
file.close();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -31,12 +46,14 @@ bool Requests::CheckUpdates(std::string& version)
|
||||
|
||||
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 };
|
||||
if (!std::filesystem::is_directory(path)) { std::filesystem::create_directory(path); }
|
||||
for (const std::string& file : files)
|
||||
{
|
||||
status = "Downloading " + file + "...";
|
||||
if (!DownloadFile(file, path + file))
|
||||
if (!DownloadFile(octrDomain, octrPath + file, path + file))
|
||||
{
|
||||
status = "Error downloading " + file;
|
||||
return false;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
namespace Requests
|
||||
{
|
||||
bool DownloadFile(const std::string& domain, const std::string& sitePath, const std::string& filePath);
|
||||
bool CheckUpdates(std::string& version);
|
||||
bool DownloadUpdates(const std::string& version, std::string& status);
|
||||
}
|
@ -10,10 +10,9 @@
|
||||
|
||||
UI::UI()
|
||||
{
|
||||
g_dataManager.BindData(&m_biosPath, DataType::STRING, "BiosPath");
|
||||
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_iniPath, DataType::STRING, "IniPath");
|
||||
g_dataManager.BindData(&m_username, DataType::STRING, "Username");
|
||||
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); }
|
||||
|
||||
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_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::BeginDisabled(m_updater.IsBusy() || !m_updater.IsUpdated());
|
||||
if (ImGui::Button("Launch Game"))
|
||||
{
|
||||
std::string s_clientPath = GetClientPath(m_version);
|
||||
std::string s_patchedPath = GetPatchedGamePath(m_version);
|
||||
const std::string s_clientPath = GetClientPath(m_version);
|
||||
const std::string s_patchedPath = GetPatchedGamePath(m_version);
|
||||
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
|
||||
{
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
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();
|
||||
|
||||
if (!m_status.empty()) { ImGui::Text(m_status.c_str()); }
|
||||
|
@ -17,9 +17,8 @@ private:
|
||||
private:
|
||||
Updater m_updater;
|
||||
std::string m_version = "None";
|
||||
std::string m_biosPath;
|
||||
std::string m_gamePath;
|
||||
std::string m_duckPath;
|
||||
std::string m_iniPath;
|
||||
std::string m_username;
|
||||
std::string m_status;
|
||||
};
|
@ -2,12 +2,15 @@
|
||||
#include "requests.h"
|
||||
#include "dataManager.h"
|
||||
#include "patch.h"
|
||||
#include "io.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
Updater::Updater()
|
||||
{
|
||||
g_dataManager.BindData(&m_updated, DataType::BOOL, "Updated");
|
||||
g_dataManager.BindData(&m_hasDuckstation, DataType::BOOL, "Duck");
|
||||
}
|
||||
|
||||
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([&]
|
||||
{
|
||||
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...";
|
||||
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 + "/";
|
||||
if (Requests::DownloadUpdates(path, status) && Patch::NewVersion(path, gamePath, status))
|
||||
{
|
||||
std::string s_ini;
|
||||
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);
|
||||
std::filesystem::copy_file(GetIniPath_Version(currVersion), GetIniPath_Duck());
|
||||
m_updated = true;
|
||||
m_updateAvailable = false;
|
||||
currVersion = m_versionAvailable;
|
||||
status = "Update completed.";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else { status = "Already on the latest patch"; }
|
||||
}
|
||||
else { status = "Error: could not establish connection"; }
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public:
|
||||
bool IsUpdated();
|
||||
bool IsBusy();
|
||||
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:
|
||||
bool StartRoutine(const std::function<void(void)>& func);
|
||||
@ -21,5 +21,6 @@ private:
|
||||
std::string m_versionAvailable;
|
||||
bool m_routineRunning = false;
|
||||
bool m_updateAvailable = false;
|
||||
bool m_hasDuckstation = false;
|
||||
bool m_updated;
|
||||
};
|
Loading…
Reference in New Issue
Block a user