mirror of
https://github.com/CTR-tools/CTR-ModSDK.git
synced 2024-11-30 08:50:33 +00:00
merge
This commit is contained in:
commit
6d3772efef
4
.gitignore
vendored
4
.gitignore
vendored
@ -21,7 +21,9 @@ __pycache__/
|
||||
build/
|
||||
debug/
|
||||
x64/
|
||||
Launcher/
|
||||
data/
|
||||
packages/
|
||||
externals/
|
||||
*.log
|
||||
offset.txt
|
||||
comport.txt
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -19,9 +19,6 @@
|
||||
[submodule "externals/json"]
|
||||
path = externals/json
|
||||
url = https://github.com/nlohmann/json
|
||||
[submodule "externals/HTTPRequest"]
|
||||
path = externals/HTTPRequest
|
||||
url = https://github.com/elnormous/HTTPRequest
|
||||
[submodule "externals/cpp-httplib"]
|
||||
path = externals/cpp-httplib
|
||||
url = https://github.com/yhirose/cpp-httplib
|
||||
|
@ -142,10 +142,6 @@ struct OnlineCTR
|
||||
|
||||
char desiredFPS;
|
||||
|
||||
// control when PSX and PC send/recv
|
||||
char sleepControl;
|
||||
char gpuSubmitTooLate;
|
||||
char enableDeferredGPU;
|
||||
#ifdef PINE_DEBUG
|
||||
int stateChangeCounter;
|
||||
#endif
|
||||
|
@ -5,23 +5,6 @@ typedef void (*VehicleFuncPtr)(struct Thread* thread, struct Driver* driver);
|
||||
#ifdef USE_ONLINE
|
||||
#include "../AltMods/OnlineCTR/global.h"
|
||||
void RunVehicleThread(VehicleFuncPtr func, struct Thread* thread, struct Driver* driver);
|
||||
|
||||
#pragma optimize("", off)
|
||||
void FrameStall()
|
||||
{
|
||||
// dont stall for this
|
||||
if(octr->CurrState < LOBBY_HOST_TRACK_PICK)
|
||||
return;
|
||||
|
||||
// wait for PC client to reset
|
||||
while (octr->sleepControl == 1)
|
||||
{
|
||||
// required, or the register never updates
|
||||
printf("");
|
||||
}
|
||||
}
|
||||
#pragma optimize("", on)
|
||||
|
||||
#endif
|
||||
|
||||
void DECOMP_MainFrame_GameLogic(struct GameTracker* gGT, struct GamepadSystem* gGamepads)
|
||||
@ -221,7 +204,7 @@ LAB_80035098:
|
||||
(gGT->threadBuckets[iVar4].thread != 0)
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
// online multiplayer
|
||||
#ifdef USE_ONLINE
|
||||
|
||||
@ -234,37 +217,31 @@ LAB_80035098:
|
||||
if(gGT->trafficLightsTimer > 3600)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (iVar4 == 0)
|
||||
{
|
||||
struct Driver* dOnline = gGT->drivers[0];
|
||||
if(dOnline != 0)
|
||||
{
|
||||
struct Thread* dThread = dOnline->instSelf->thread;
|
||||
|
||||
|
||||
DECOMP_VehPickupItem_ShootOnCirclePress(dOnline);
|
||||
|
||||
|
||||
RunVehicleSet13(dThread, dOnline);
|
||||
|
||||
octr->sleepControl = 1;
|
||||
octr->desiredFPS = FPS_DOUBLE(30);
|
||||
|
||||
// stall
|
||||
if(octr->enableDeferredGPU == 1)
|
||||
FrameStall();
|
||||
}
|
||||
|
||||
|
||||
for(int other = 1; other < 8; other++)
|
||||
{
|
||||
dOnline = gGT->drivers[other];
|
||||
if(dOnline == 0) continue;
|
||||
|
||||
|
||||
struct Thread* dThread = dOnline->instSelf->thread;
|
||||
|
||||
|
||||
RunVehicleSet13(dThread, dOnline);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// offline
|
||||
#else
|
||||
if (iVar4 == 0)
|
||||
|
@ -340,7 +340,7 @@ void DECOMP_MainFrame_RenderFrame(struct GameTracker* gGT, struct GamepadSystem*
|
||||
if((gGT->renderFlags & 0x8000) != 0)
|
||||
{
|
||||
WindowBoxLines(gGT);
|
||||
|
||||
|
||||
WindowDivsionLines(gGT);
|
||||
}
|
||||
#endif
|
||||
@ -1483,7 +1483,7 @@ void MultiplayerWumpaHUD(struct GameTracker* gGT)
|
||||
for(int i = 0; i < gGT->numPlyrCurrGame; i++)
|
||||
{
|
||||
struct Driver* d = gGT->drivers[i];
|
||||
|
||||
|
||||
// if race is over for driver
|
||||
if((d->actionsFlagSet & 0x2000000) != 0)
|
||||
{
|
||||
@ -1711,16 +1711,10 @@ void RenderVSYNC(struct GameTracker* gGT)
|
||||
|
||||
if(ReadyToFlip(gGT))
|
||||
{
|
||||
|
||||
#ifdef USE_ONLINE
|
||||
if(boolFirstFrame)
|
||||
octr->gpuSubmitTooLate = 1;
|
||||
#endif
|
||||
|
||||
// quit, end of stall
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_ONLINE
|
||||
// gpu submission is not too late,
|
||||
// we got to this while() loop before
|
||||
|
@ -22,7 +22,7 @@
|
||||
//#define USE_VR // Virtual Reality
|
||||
|
||||
#ifdef USE_ONLINE
|
||||
#define USE_60FPS
|
||||
//#define USE_60FPS
|
||||
#define USE_BOOSTBAR
|
||||
#define USE_16BY9
|
||||
#define USE_RAMEX
|
||||
|
@ -1320,18 +1320,7 @@ int main(int argc, char *argv[])
|
||||
//perhaps instead of reading, keep a local counter, increment that, and then
|
||||
//write it (without needing a blocking read first).
|
||||
(*octr.get()).windowsClientSync++;
|
||||
|
||||
if (octr.get()->windowsClientSync == 0)
|
||||
{
|
||||
// On Niko's computer with MAPPED MEMORY
|
||||
// 30fps 1x resolution = 4500
|
||||
// 30fps 9x resolution = 2500
|
||||
// 60fps = 0
|
||||
|
||||
// With the new PINE system, always zero,
|
||||
// We can not defer the GPU until the PC port is done :(
|
||||
//printf("Debug: SleepCount=%d\n", sleepCount);
|
||||
}
|
||||
octr.startWrite();
|
||||
|
||||
// should rename to room selection
|
||||
if (octr.get()->CurrState >= LAUNCH_PICK_ROOM)
|
||||
@ -1339,55 +1328,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
StartAnimation();
|
||||
|
||||
// Wait for PSX to have P1 data,
|
||||
// which is set at octr->sleepControl
|
||||
void FrameStall(); FrameStall();
|
||||
|
||||
if (octr.get()->CurrState >= 0)
|
||||
ClientState[octr.get()->CurrState]();
|
||||
|
||||
//UPDATE: the former version of this code sort of unconditionally usleep'd for a static amount
|
||||
//of time (depending on 30 or 60fps). It's been updated to be dynamic, in case of lag/poor pc
|
||||
//perf, or if PINE overhead is particularly large. If at any point in the future duckstation
|
||||
//isn't at a locked 30/60fps, this may be the culprit.
|
||||
|
||||
// check for frame lag
|
||||
if (octr.get()->gpuSubmitTooLate == 1)
|
||||
{
|
||||
octr.get()->gpuSubmitTooLate = 0;
|
||||
|
||||
// if 1-9 frame stalls
|
||||
if (sleepCount >= 500)
|
||||
{
|
||||
// remove from sleep
|
||||
sleepCount -= 500;
|
||||
}
|
||||
|
||||
// if 10+ frame stalls
|
||||
else
|
||||
{
|
||||
sleepCount = 0;
|
||||
enableDeferredGPU = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// PC writes to PSX,
|
||||
// PSX is read-only
|
||||
octr.get()->enableDeferredGPU = enableDeferredGPU;
|
||||
|
||||
// delay GPU between SEND and RECV
|
||||
if (enableDeferredGPU == 1)
|
||||
usleep(sleepCount);
|
||||
|
||||
// now check for new RECV message
|
||||
ProcessNewMessages();
|
||||
|
||||
// allow PSX to resume
|
||||
octr.get()->sleepControl = 0;
|
||||
|
||||
octr.startWrite(); //only write the things that have changed.
|
||||
|
||||
GCDeadPineData(); //this is probably a decent place to do this.
|
||||
|
||||
// Wait for PSX to have P1 data,
|
||||
// which is set at octr->sleepControl
|
||||
void FrameStall(); FrameStall();
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
@ -1409,17 +1361,14 @@ void usleep(__int64 usec)
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma optimize("", off)
|
||||
int gGT_timer = 0;
|
||||
void FrameStall()
|
||||
{
|
||||
// wait for next frame
|
||||
//TODO: make this a submember of octr
|
||||
ps1ptr<int> OCTRsleepControl = pBuf.at<int>(octr.get_address() + offsetof(OnlineCTR, sleepControl));
|
||||
while ((*OCTRsleepControl.get()) == 0)
|
||||
ps1ptr<int> OCTRsleepControl = pBuf.at<int>(0x80096b20 + 0x1cf8);
|
||||
while (gGT_timer == (*OCTRsleepControl.get()))
|
||||
{
|
||||
usleep(1);
|
||||
OCTRsleepControl.blockingRead();
|
||||
}
|
||||
(*octr.get()).sleepControl = (*OCTRsleepControl.get());
|
||||
}
|
||||
#pragma optimize("", on)
|
||||
}
|
@ -157,6 +157,7 @@
|
||||
<ClCompile Include="requests.cpp" />
|
||||
<ClCompile Include="third_party\xdelta3\xdelta3.c" />
|
||||
<ClCompile Include="ui.cpp" />
|
||||
<ClCompile Include="updater.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="app.h" />
|
||||
@ -166,6 +167,7 @@
|
||||
<ClInclude Include="patch.h" />
|
||||
<ClInclude Include="requests.h" />
|
||||
<ClInclude Include="ui.h" />
|
||||
<ClInclude Include="updater.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
@ -174,7 +176,7 @@
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="packages\sdl2.nuget.redist.2.30.3\build\native\sdl2.nuget.redist.targets" Condition="Exists('packages\sdl2.nuget.redist.2.30.3\build\native\sdl2.nuget.redist.targets')" />
|
||||
<Import Project="packages\sdl2.nuget.2.30.3\build\native\sdl2.nuget.targets" Condition="Exists('packages\sdl2.nuget.2.30.3\build\native\sdl2.nuget.targets')" />
|
||||
<Import Project="packages\EbolaChan.LibZip.1.10.1\build\native\EbolaChan.LibZip.targets" Condition="Exists('packages\EbolaChan.LibZip.1.10.1\build\native\EbolaChan.LibZip.targets')" />
|
||||
<Import Project="packages\libzip-c.1.9.2.6\build\native\libzip-c.targets" Condition="Exists('packages\libzip-c.1.9.2.6\build\native\libzip-c.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@ -182,6 +184,6 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\sdl2.nuget.redist.2.30.3\build\native\sdl2.nuget.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\sdl2.nuget.redist.2.30.3\build\native\sdl2.nuget.redist.targets'))" />
|
||||
<Error Condition="!Exists('packages\sdl2.nuget.2.30.3\build\native\sdl2.nuget.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\sdl2.nuget.2.30.3\build\native\sdl2.nuget.targets'))" />
|
||||
<Error Condition="!Exists('packages\EbolaChan.LibZip.1.10.1\build\native\EbolaChan.LibZip.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\EbolaChan.LibZip.1.10.1\build\native\EbolaChan.LibZip.targets'))" />
|
||||
<Error Condition="!Exists('packages\libzip-c.1.9.2.6\build\native\libzip-c.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\libzip-c.1.9.2.6\build\native\libzip-c.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
@ -75,6 +75,9 @@
|
||||
<ClCompile Include="third_party\xdelta3\xdelta3.c">
|
||||
<Filter>xdelta3</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="updater.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="app.h">
|
||||
@ -98,6 +101,9 @@
|
||||
<ClInclude Include="io.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="updater.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
@ -122,11 +122,7 @@ void App::Run()
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
int width, height;
|
||||
SDL_GetWindowSize(m_window, &width, &height);
|
||||
ui.Render(width, height);
|
||||
|
||||
Main();
|
||||
ImGui::Render();
|
||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
glClearColor(clearColor.x * clearColor.w, clearColor.y * clearColor.w, clearColor.z * clearColor.w, clearColor.w);
|
||||
@ -136,6 +132,13 @@ void App::Run()
|
||||
}
|
||||
}
|
||||
|
||||
void App::Main()
|
||||
{
|
||||
int width, height;
|
||||
SDL_GetWindowSize(m_window, &width, &height);
|
||||
ui.Render(width, height);
|
||||
}
|
||||
|
||||
void App::Close()
|
||||
{
|
||||
g_dataManager.SaveData();
|
||||
|
@ -10,6 +10,7 @@ class App
|
||||
public:
|
||||
void Init();
|
||||
void Run();
|
||||
void Main();
|
||||
void Close();
|
||||
#ifdef _DEBUG
|
||||
void RunImGuiExample();
|
||||
@ -24,7 +25,7 @@ private:
|
||||
|
||||
private:
|
||||
UI ui;
|
||||
const std::string m_version = "v0.1";
|
||||
const std::string m_version = "v0.2";
|
||||
std::string m_glslVer;
|
||||
SDL_GLContext m_glContext;
|
||||
SDL_Window* m_window;
|
||||
|
@ -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";
|
||||
|
||||
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 + "settings.ini";
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "io.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <zip.h>
|
||||
|
||||
void IO::ReadBinaryFile(std::vector<char>& v, const std::string& path)
|
||||
{
|
||||
@ -19,3 +21,35 @@ void IO::WriteBinaryFile(const std::vector<char>& v, const std::string& path)
|
||||
output.write(v.data(), v.size());
|
||||
output.close();
|
||||
}
|
||||
|
||||
bool IO::DecompressFiles(const std::string& path, const std::string& filename)
|
||||
{
|
||||
int err;
|
||||
zip_t* zipArchive = zip_open((path + filename).c_str(), 0, &err);
|
||||
if (!zipArchive) { return false; }
|
||||
|
||||
zip_stat_t zipStat;
|
||||
for (int i = 0; i < zip_get_num_entries(zipArchive, 0); i++)
|
||||
{
|
||||
if (zip_stat_index(zipArchive, i, 0, &zipStat) == 0)
|
||||
{
|
||||
std::string archiveName(zipStat.name);
|
||||
if (archiveName.back() == '/' || archiveName.back() == '\\')
|
||||
{
|
||||
std::filesystem::create_directory(path + archiveName);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<char> decompressedFile;
|
||||
decompressedFile.resize(zipStat.size);
|
||||
zip_file* file = zip_fopen_index(zipArchive, i, 0);
|
||||
if (!file) { return false; }
|
||||
zip_fread(file, decompressedFile.data(), zipStat.size);
|
||||
zip_fclose(file);
|
||||
IO::WriteBinaryFile(decompressedFile, path + archiveName);
|
||||
}
|
||||
}
|
||||
}
|
||||
zip_close(zipArchive);
|
||||
return true;
|
||||
}
|
||||
|
@ -7,4 +7,5 @@ namespace IO
|
||||
{
|
||||
void ReadBinaryFile(std::vector<char>& v, const std::string& path);
|
||||
void WriteBinaryFile(const std::vector<char>& v, const std::string& path);
|
||||
bool DecompressFiles(const std::string& path, const std::string& filename);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="EbolaChan.LibZip" version="1.10.1" targetFramework="native" />
|
||||
<package id="libzip-c" version="1.9.2.6" targetFramework="native" />
|
||||
<package id="sdl2.nuget" version="2.30.3" targetFramework="native" />
|
||||
<package id="sdl2.nuget.redist" version="2.30.3" targetFramework="native" />
|
||||
</packages>
|
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
此静态库目前仅支持平台工具集v143,有Debug和Release两个版本。要使用Debug版本,必须将项目配置设置为以字母D开头;要使用Release版本,必须将项目配置设置为以字母R开头。
|
||||
This static library currently only supports platform toolset v143, available in Debug and Release versions. To use the Debug version, you must set the project configuration to start with the letter D; to use the Release version, you must set the project configuration to start with the letter R.
|
Binary file not shown.
Before Width: | Height: | Size: 53 KiB |
@ -3,28 +3,6 @@
|
||||
#include "io.h"
|
||||
|
||||
#include <xdelta3.h>
|
||||
#include <zip.h>
|
||||
|
||||
static bool DecompressFile(const std::string& path, const std::string& filename, const std::string& ext)
|
||||
{
|
||||
int err;
|
||||
zip_t* zipArchive = zip_open(path.c_str(), 0, &err);
|
||||
if (!zipArchive) { return false; }
|
||||
|
||||
zip_stat_t zipStat;
|
||||
zip_stat_init(&zipStat);
|
||||
zip_stat(zipArchive, filename.c_str(), 0, &zipStat);
|
||||
std::vector<char> decompressedFile;
|
||||
decompressedFile.resize(zipStat.size);
|
||||
zip_file* file = zip_fopen(zipArchive, filename.c_str(), 0);
|
||||
zip_fread(file, decompressedFile.data(), zipStat.size);
|
||||
zip_fclose(file);
|
||||
zip_close(zipArchive);
|
||||
|
||||
std::string newFilePath = path.substr(0, path.find(".")) + ext;
|
||||
IO::WriteBinaryFile(decompressedFile, newFilePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DecompressXDelta(const std::string& xdeltaPath, const std::string& inputPath, const std::string& ext)
|
||||
{
|
||||
@ -44,7 +22,19 @@ static bool DecompressXDelta(const std::string& xdeltaPath, const std::string& i
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Patch::NewVersion(const std::string& path, const std::string& gamePath)
|
||||
bool Patch::NewVersion(const std::string& path, const std::string& gamePath, std::string& status)
|
||||
{
|
||||
return DecompressFile(path + g_clientString, g_clientExecutable, ".exe") && DecompressXDelta(path + g_patchString, gamePath, ".bin");
|
||||
status = "Decompressing " + g_clientExecutable + "...";
|
||||
if (!IO::DecompressFiles(path, g_clientString))
|
||||
{
|
||||
status = "Error decompressing " + g_clientExecutable;
|
||||
return false;
|
||||
}
|
||||
status = "Applying xdelta patch...";
|
||||
if (!DecompressXDelta(path + g_patchString, gamePath, ".bin"))
|
||||
{
|
||||
status = "Error applying xdelta patch";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -4,5 +4,5 @@
|
||||
|
||||
namespace Patch
|
||||
{
|
||||
bool NewVersion(const std::string& path, const std::string& gamePath);
|
||||
bool NewVersion(const std::string& path, const std::string& gamePath, std::string& status);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
@ -29,13 +44,20 @@ bool Requests::CheckUpdates(std::string& version)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Requests::DownloadUpdates(const std::string& path)
|
||||
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)
|
||||
{
|
||||
if (!DownloadFile(file, path + file)) { return false; }
|
||||
status = "Downloading " + file + "...";
|
||||
if (!DownloadFile(octrDomain, octrPath + file, path + file))
|
||||
{
|
||||
status = "Error downloading " + file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -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);
|
||||
bool DownloadUpdates(const std::string& version, std::string& status);
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
#include "ui.h"
|
||||
#include "dataManager.h"
|
||||
#include "IconsFontAwesome6.h"
|
||||
#include "requests.h"
|
||||
#include "patch.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <misc/cpp/imgui_stdlib.h>
|
||||
#include <portable-file-dialogs.h>
|
||||
#include <filesystem>
|
||||
@ -12,54 +9,66 @@
|
||||
|
||||
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");
|
||||
g_dataManager.BindData(&m_updated, DataType::BOOL, "Updated");
|
||||
m_updater.CheckForUpdates(m_status, m_version);
|
||||
}
|
||||
|
||||
static int FilterUsernameChar(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
if (data->EventChar >= 'a' && data->EventChar <= 'z') { return 0; }
|
||||
if (data->EventChar >= 'A' && data->EventChar <= 'Z') { return 0; }
|
||||
if (data->EventChar >= '0' && data->EventChar <= '9') { return 0; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
void UI::Render(int width, int height)
|
||||
{
|
||||
static bool update = false;
|
||||
if (update) { Update(); update = false; }
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(.0f, .0f), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(static_cast<float>(width), static_cast<float>(height)), ImGuiCond_Always);
|
||||
ImGui::Begin("Main", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
std::string icon = m_username.empty() ? ICON_FA_CIRCLE_XMARK : ICON_FA_CIRCLE_CHECK;
|
||||
ImGui::InputText(("Username " + icon).c_str(), &m_username);
|
||||
ImGui::SetItemTooltip("Special characters:\n* = Cross Button\n< = Left Arrow\n@ = Circle\n[ = Square\n^ = Triangle\n& = Space");
|
||||
ImGui::InputText(("Username " + icon).c_str(), &m_username, ImGuiInputTextFlags_CallbackCharFilter, FilterUsernameChar);
|
||||
if (m_username.size() > 9) { m_username = m_username.substr(0, 9); }
|
||||
|
||||
static bool readBios = true;
|
||||
bool updateReady = true;
|
||||
updateReady &= SelectFile(m_gamePath, "Game Path", ".bin", {"Game Files", "*.bin"}, "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");
|
||||
updateReady &= SelectFile(m_biosPath, "Bios Path ", {".bin"}, {"PSX Bios File", "*.bin"}, "Path to a PS1 NTSC-U bios.");
|
||||
if (updateReady)
|
||||
{
|
||||
if (readBios)
|
||||
{
|
||||
if (m_updater.IsValidBios(m_biosPath)) { readBios = false; }
|
||||
else { updateReady = false; }
|
||||
}
|
||||
}
|
||||
else { readBios = true; }
|
||||
updateReady &= SelectFile(m_gamePath, "Game Path", {".bin", ".img", ".iso"}, {"Game Files", "*.bin *.img *.iso"}, "Path to the clean NTSC-U version of CTR");
|
||||
ImGui::Text(("Version: " + m_version).c_str());
|
||||
|
||||
ImGui::BeginDisabled(!m_updated);
|
||||
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(!updateReady);
|
||||
if (ImGui::Button("Update")) { update = true; m_status = "Updating..."; }
|
||||
ImGui::BeginDisabled(m_updater.IsBusy() || !updateReady);
|
||||
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()); }
|
||||
@ -67,41 +76,27 @@ void UI::Render(int width, int height)
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void UI::Update()
|
||||
bool UI::SelectFile(std::string& str, const std::string& label, const std::vector<std::string>& ext, const std::vector<std::string>& filters, const std::string& tip)
|
||||
{
|
||||
std::string version;
|
||||
if (Requests::CheckUpdates(version))
|
||||
{
|
||||
if (version != m_version)
|
||||
{
|
||||
m_updated = false;
|
||||
std::string path = g_dataFolder + version + "/";
|
||||
if (Requests::DownloadUpdates(path))
|
||||
{
|
||||
if (Patch::NewVersion(path, m_gamePath))
|
||||
{
|
||||
std::string s_ini;
|
||||
if (m_iniPath.back() == '/' || m_iniPath.back() == '\\') { s_ini = m_iniPath + g_configString; }
|
||||
else { s_ini = m_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_version = version;
|
||||
m_status = "Update completed.";
|
||||
}
|
||||
else { m_status = "Error: could not decompress files"; }
|
||||
}
|
||||
else { m_status = "Error: could not establish connection"; }
|
||||
}
|
||||
else { m_status = "Already on the latest patch"; }
|
||||
}
|
||||
else { m_status = "Error: could not establish connection"; }
|
||||
}
|
||||
|
||||
bool UI::SelectFile(std::string& str, const std::string& label, const std::string& ext, const std::vector<std::string>& filters, const std::string& tip)
|
||||
{
|
||||
bool validPath = std::filesystem::exists(str) && str.ends_with(ext);
|
||||
std::string icon = validPath ? ICON_FA_CIRCLE_CHECK : ICON_FA_CIRCLE_XMARK;
|
||||
std::string lowercaseStr;
|
||||
for (char c : str)
|
||||
{
|
||||
if (c <= 'Z' && c >= 'A') { c = c - ('Z' - 'z'); };
|
||||
lowercaseStr += c;
|
||||
}
|
||||
|
||||
auto checkValidPath = [&]
|
||||
{
|
||||
if (std::filesystem::exists(str))
|
||||
{
|
||||
for (const std::string& s : ext)
|
||||
{
|
||||
if (lowercaseStr.ends_with(s)) { return true; }
|
||||
}
|
||||
}
|
||||
};
|
||||
std::string icon = checkValidPath() ? ICON_FA_CIRCLE_CHECK : ICON_FA_CIRCLE_XMARK;
|
||||
ImGui::InputText((label + " " + icon).c_str(), &str);
|
||||
if (!tip.empty()) { ImGui::SetItemTooltip(tip.c_str()); }
|
||||
ImGui::SameLine();
|
||||
@ -111,7 +106,7 @@ bool UI::SelectFile(std::string& str, const std::string& label, const std::strin
|
||||
if (selection.empty()) { return false; }
|
||||
str = selection.front();
|
||||
}
|
||||
return validPath;
|
||||
return checkValidPath();
|
||||
}
|
||||
|
||||
bool UI::SelectFolder(std::string& str, const std::string& label, const std::string& tip)
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "updater.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -10,16 +13,14 @@ public:
|
||||
void Render(int width, int height);
|
||||
|
||||
private:
|
||||
void Update();
|
||||
bool SelectFile(std::string& str, const std::string& label, const std::string& ext, const std::vector<std::string>& filters, const std::string& tip);
|
||||
bool SelectFile(std::string& str, const std::string& label, const std::vector<std::string>& ext, const std::vector<std::string>& filters, const std::string& tip);
|
||||
bool SelectFolder(std::string& str, const std::string& label, const std::string& tip);
|
||||
|
||||
private:
|
||||
bool m_updated = false;
|
||||
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;
|
||||
};
|
123
tools/Launcher/updater.cpp
Normal file
123
tools/Launcher/updater.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "updater.h"
|
||||
#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()
|
||||
{
|
||||
return m_updated;
|
||||
}
|
||||
|
||||
bool Updater::IsBusy()
|
||||
{
|
||||
return m_routineRunning;
|
||||
}
|
||||
|
||||
bool Updater::IsValidBios(const std::string& path)
|
||||
{
|
||||
std::vector<char> v;
|
||||
IO::ReadBinaryFile(v, path);
|
||||
return v.size() == static_cast<size_t>(0x100000);
|
||||
}
|
||||
|
||||
bool Updater::CheckForUpdates(std::string& status, const std::string& currVersion)
|
||||
{
|
||||
return StartRoutine([&]
|
||||
{
|
||||
std::string version;
|
||||
Requests::CheckUpdates(version);
|
||||
if (currVersion != version)
|
||||
{
|
||||
m_updateAvailable = true;
|
||||
m_versionAvailable = version;
|
||||
status = "Update available! v" + m_versionAvailable;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool Updater::Update(std::string& status, std::string& currVersion, const std::string& gamePath, const std::string& biosPath)
|
||||
{
|
||||
return StartRoutine([&]
|
||||
{
|
||||
std::string version;
|
||||
bool copyIni = false;
|
||||
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;
|
||||
copyIni = true;
|
||||
}
|
||||
status = "Checking for new updates...";
|
||||
if (m_updateAvailable || Requests::CheckUpdates(version))
|
||||
{
|
||||
if (m_updateAvailable || version != currVersion)
|
||||
{
|
||||
m_versionAvailable = m_updateAvailable ? m_versionAvailable : version;
|
||||
std::string path = g_dataFolder + m_versionAvailable + "/";
|
||||
if (Requests::DownloadUpdates(path, status) && Patch::NewVersion(path, gamePath, status))
|
||||
{
|
||||
if (copyIni) { std::filesystem::copy_file(GetIniPath_Version(m_versionAvailable), 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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool Updater::StartRoutine(const std::function<void(void)>& func)
|
||||
{
|
||||
if (m_routineRunning) { return false; }
|
||||
|
||||
m_routine = func;
|
||||
m_routineRunning = true;
|
||||
m_updateRoutine = std::async(std::launch::async, [&] { m_routine(); m_routineRunning = false; });
|
||||
return true;
|
||||
}
|
27
tools/Launcher/updater.h
Normal file
27
tools/Launcher/updater.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <future>
|
||||
|
||||
class Updater
|
||||
{
|
||||
public:
|
||||
Updater();
|
||||
bool IsUpdated();
|
||||
bool IsBusy();
|
||||
bool IsValidBios(const std::string& path);
|
||||
bool CheckForUpdates(std::string& status, const std::string& currVersion);
|
||||
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);
|
||||
|
||||
private:
|
||||
std::future<void> m_updateRoutine;
|
||||
std::function<void(void)> m_routine;
|
||||
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