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/
|
build/
|
||||||
debug/
|
debug/
|
||||||
x64/
|
x64/
|
||||||
Launcher/
|
data/
|
||||||
|
packages/
|
||||||
|
externals/
|
||||||
*.log
|
*.log
|
||||||
offset.txt
|
offset.txt
|
||||||
comport.txt
|
comport.txt
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -19,9 +19,6 @@
|
|||||||
[submodule "externals/json"]
|
[submodule "externals/json"]
|
||||||
path = externals/json
|
path = externals/json
|
||||||
url = https://github.com/nlohmann/json
|
url = https://github.com/nlohmann/json
|
||||||
[submodule "externals/HTTPRequest"]
|
|
||||||
path = externals/HTTPRequest
|
|
||||||
url = https://github.com/elnormous/HTTPRequest
|
|
||||||
[submodule "externals/cpp-httplib"]
|
[submodule "externals/cpp-httplib"]
|
||||||
path = externals/cpp-httplib
|
path = externals/cpp-httplib
|
||||||
url = https://github.com/yhirose/cpp-httplib
|
url = https://github.com/yhirose/cpp-httplib
|
||||||
|
@ -142,10 +142,6 @@ struct OnlineCTR
|
|||||||
|
|
||||||
char desiredFPS;
|
char desiredFPS;
|
||||||
|
|
||||||
// control when PSX and PC send/recv
|
|
||||||
char sleepControl;
|
|
||||||
char gpuSubmitTooLate;
|
|
||||||
char enableDeferredGPU;
|
|
||||||
#ifdef PINE_DEBUG
|
#ifdef PINE_DEBUG
|
||||||
int stateChangeCounter;
|
int stateChangeCounter;
|
||||||
#endif
|
#endif
|
||||||
|
@ -5,23 +5,6 @@ typedef void (*VehicleFuncPtr)(struct Thread* thread, struct Driver* driver);
|
|||||||
#ifdef USE_ONLINE
|
#ifdef USE_ONLINE
|
||||||
#include "../AltMods/OnlineCTR/global.h"
|
#include "../AltMods/OnlineCTR/global.h"
|
||||||
void RunVehicleThread(VehicleFuncPtr func, struct Thread* thread, struct Driver* driver);
|
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
|
#endif
|
||||||
|
|
||||||
void DECOMP_MainFrame_GameLogic(struct GameTracker* gGT, struct GamepadSystem* gGamepads)
|
void DECOMP_MainFrame_GameLogic(struct GameTracker* gGT, struct GamepadSystem* gGamepads)
|
||||||
@ -221,7 +204,7 @@ LAB_80035098:
|
|||||||
(gGT->threadBuckets[iVar4].thread != 0)
|
(gGT->threadBuckets[iVar4].thread != 0)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
// online multiplayer
|
// online multiplayer
|
||||||
#ifdef USE_ONLINE
|
#ifdef USE_ONLINE
|
||||||
|
|
||||||
@ -234,37 +217,31 @@ LAB_80035098:
|
|||||||
if(gGT->trafficLightsTimer > 3600)
|
if(gGT->trafficLightsTimer > 3600)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iVar4 == 0)
|
if (iVar4 == 0)
|
||||||
{
|
{
|
||||||
struct Driver* dOnline = gGT->drivers[0];
|
struct Driver* dOnline = gGT->drivers[0];
|
||||||
if(dOnline != 0)
|
if(dOnline != 0)
|
||||||
{
|
{
|
||||||
struct Thread* dThread = dOnline->instSelf->thread;
|
struct Thread* dThread = dOnline->instSelf->thread;
|
||||||
|
|
||||||
DECOMP_VehPickupItem_ShootOnCirclePress(dOnline);
|
DECOMP_VehPickupItem_ShootOnCirclePress(dOnline);
|
||||||
|
|
||||||
RunVehicleSet13(dThread, dOnline);
|
RunVehicleSet13(dThread, dOnline);
|
||||||
|
|
||||||
octr->sleepControl = 1;
|
|
||||||
octr->desiredFPS = FPS_DOUBLE(30);
|
octr->desiredFPS = FPS_DOUBLE(30);
|
||||||
|
|
||||||
// stall
|
|
||||||
if(octr->enableDeferredGPU == 1)
|
|
||||||
FrameStall();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int other = 1; other < 8; other++)
|
for(int other = 1; other < 8; other++)
|
||||||
{
|
{
|
||||||
dOnline = gGT->drivers[other];
|
dOnline = gGT->drivers[other];
|
||||||
if(dOnline == 0) continue;
|
if(dOnline == 0) continue;
|
||||||
|
|
||||||
struct Thread* dThread = dOnline->instSelf->thread;
|
struct Thread* dThread = dOnline->instSelf->thread;
|
||||||
|
|
||||||
RunVehicleSet13(dThread, dOnline);
|
RunVehicleSet13(dThread, dOnline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// offline
|
// offline
|
||||||
#else
|
#else
|
||||||
if (iVar4 == 0)
|
if (iVar4 == 0)
|
||||||
|
@ -340,7 +340,7 @@ void DECOMP_MainFrame_RenderFrame(struct GameTracker* gGT, struct GamepadSystem*
|
|||||||
if((gGT->renderFlags & 0x8000) != 0)
|
if((gGT->renderFlags & 0x8000) != 0)
|
||||||
{
|
{
|
||||||
WindowBoxLines(gGT);
|
WindowBoxLines(gGT);
|
||||||
|
|
||||||
WindowDivsionLines(gGT);
|
WindowDivsionLines(gGT);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1483,7 +1483,7 @@ void MultiplayerWumpaHUD(struct GameTracker* gGT)
|
|||||||
for(int i = 0; i < gGT->numPlyrCurrGame; i++)
|
for(int i = 0; i < gGT->numPlyrCurrGame; i++)
|
||||||
{
|
{
|
||||||
struct Driver* d = gGT->drivers[i];
|
struct Driver* d = gGT->drivers[i];
|
||||||
|
|
||||||
// if race is over for driver
|
// if race is over for driver
|
||||||
if((d->actionsFlagSet & 0x2000000) != 0)
|
if((d->actionsFlagSet & 0x2000000) != 0)
|
||||||
{
|
{
|
||||||
@ -1711,16 +1711,10 @@ void RenderVSYNC(struct GameTracker* gGT)
|
|||||||
|
|
||||||
if(ReadyToFlip(gGT))
|
if(ReadyToFlip(gGT))
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef USE_ONLINE
|
|
||||||
if(boolFirstFrame)
|
|
||||||
octr->gpuSubmitTooLate = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// quit, end of stall
|
// quit, end of stall
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ONLINE
|
#ifdef USE_ONLINE
|
||||||
// gpu submission is not too late,
|
// gpu submission is not too late,
|
||||||
// we got to this while() loop before
|
// we got to this while() loop before
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
//#define USE_VR // Virtual Reality
|
//#define USE_VR // Virtual Reality
|
||||||
|
|
||||||
#ifdef USE_ONLINE
|
#ifdef USE_ONLINE
|
||||||
#define USE_60FPS
|
//#define USE_60FPS
|
||||||
#define USE_BOOSTBAR
|
#define USE_BOOSTBAR
|
||||||
#define USE_16BY9
|
#define USE_16BY9
|
||||||
#define USE_RAMEX
|
#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
|
//perhaps instead of reading, keep a local counter, increment that, and then
|
||||||
//write it (without needing a blocking read first).
|
//write it (without needing a blocking read first).
|
||||||
(*octr.get()).windowsClientSync++;
|
(*octr.get()).windowsClientSync++;
|
||||||
|
octr.startWrite();
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// should rename to room selection
|
// should rename to room selection
|
||||||
if (octr.get()->CurrState >= LAUNCH_PICK_ROOM)
|
if (octr.get()->CurrState >= LAUNCH_PICK_ROOM)
|
||||||
@ -1339,55 +1328,18 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
StartAnimation();
|
StartAnimation();
|
||||||
|
|
||||||
// Wait for PSX to have P1 data,
|
|
||||||
// which is set at octr->sleepControl
|
|
||||||
void FrameStall(); FrameStall();
|
|
||||||
|
|
||||||
if (octr.get()->CurrState >= 0)
|
if (octr.get()->CurrState >= 0)
|
||||||
ClientState[octr.get()->CurrState]();
|
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
|
// now check for new RECV message
|
||||||
ProcessNewMessages();
|
ProcessNewMessages();
|
||||||
|
|
||||||
// allow PSX to resume
|
|
||||||
octr.get()->sleepControl = 0;
|
|
||||||
|
|
||||||
octr.startWrite(); //only write the things that have changed.
|
octr.startWrite(); //only write the things that have changed.
|
||||||
|
|
||||||
GCDeadPineData(); //this is probably a decent place to do this.
|
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");
|
printf("\n");
|
||||||
@ -1409,17 +1361,14 @@ void usleep(__int64 usec)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#pragma optimize("", off)
|
int gGT_timer = 0;
|
||||||
void FrameStall()
|
void FrameStall()
|
||||||
{
|
{
|
||||||
// wait for next frame
|
// wait for next frame
|
||||||
//TODO: make this a submember of octr
|
//TODO: make this a submember of octr
|
||||||
ps1ptr<int> OCTRsleepControl = pBuf.at<int>(octr.get_address() + offsetof(OnlineCTR, sleepControl));
|
ps1ptr<int> OCTRsleepControl = pBuf.at<int>(0x80096b20 + 0x1cf8);
|
||||||
while ((*OCTRsleepControl.get()) == 0)
|
while (gGT_timer == (*OCTRsleepControl.get()))
|
||||||
{
|
{
|
||||||
usleep(1);
|
|
||||||
OCTRsleepControl.blockingRead();
|
OCTRsleepControl.blockingRead();
|
||||||
}
|
}
|
||||||
(*octr.get()).sleepControl = (*OCTRsleepControl.get());
|
}
|
||||||
}
|
|
||||||
#pragma optimize("", on)
|
|
@ -157,6 +157,7 @@
|
|||||||
<ClCompile Include="requests.cpp" />
|
<ClCompile Include="requests.cpp" />
|
||||||
<ClCompile Include="third_party\xdelta3\xdelta3.c" />
|
<ClCompile Include="third_party\xdelta3\xdelta3.c" />
|
||||||
<ClCompile Include="ui.cpp" />
|
<ClCompile Include="ui.cpp" />
|
||||||
|
<ClCompile Include="updater.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="app.h" />
|
<ClInclude Include="app.h" />
|
||||||
@ -166,6 +167,7 @@
|
|||||||
<ClInclude Include="patch.h" />
|
<ClInclude Include="patch.h" />
|
||||||
<ClInclude Include="requests.h" />
|
<ClInclude Include="requests.h" />
|
||||||
<ClInclude Include="ui.h" />
|
<ClInclude Include="ui.h" />
|
||||||
|
<ClInclude Include="updater.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
@ -174,7 +176,7 @@
|
|||||||
<ImportGroup Label="ExtensionTargets">
|
<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.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\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>
|
</ImportGroup>
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@ -182,6 +184,6 @@
|
|||||||
</PropertyGroup>
|
</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.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\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>
|
</Target>
|
||||||
</Project>
|
</Project>
|
@ -75,6 +75,9 @@
|
|||||||
<ClCompile Include="third_party\xdelta3\xdelta3.c">
|
<ClCompile Include="third_party\xdelta3\xdelta3.c">
|
||||||
<Filter>xdelta3</Filter>
|
<Filter>xdelta3</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="updater.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="app.h">
|
<ClInclude Include="app.h">
|
||||||
@ -98,6 +101,9 @@
|
|||||||
<ClInclude Include="io.h">
|
<ClInclude Include="io.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="updater.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
@ -122,11 +122,7 @@ void App::Run()
|
|||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplSDL2_NewFrame();
|
ImGui_ImplSDL2_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
Main();
|
||||||
int width, height;
|
|
||||||
SDL_GetWindowSize(m_window, &width, &height);
|
|
||||||
ui.Render(width, height);
|
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
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);
|
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()
|
void App::Close()
|
||||||
{
|
{
|
||||||
g_dataManager.SaveData();
|
g_dataManager.SaveData();
|
||||||
|
@ -10,6 +10,7 @@ class App
|
|||||||
public:
|
public:
|
||||||
void Init();
|
void Init();
|
||||||
void Run();
|
void Run();
|
||||||
|
void Main();
|
||||||
void Close();
|
void Close();
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
void RunImGuiExample();
|
void RunImGuiExample();
|
||||||
@ -24,7 +25,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
UI ui;
|
UI ui;
|
||||||
const std::string m_version = "v0.1";
|
const std::string m_version = "v0.2";
|
||||||
std::string m_glslVer;
|
std::string m_glslVer;
|
||||||
SDL_GLContext m_glContext;
|
SDL_GLContext m_glContext;
|
||||||
SDL_Window* m_window;
|
SDL_Window* m_window;
|
||||||
|
@ -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 = "SCUS-94426.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 + "settings.ini";
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <zip.h>
|
||||||
|
|
||||||
void IO::ReadBinaryFile(std::vector<char>& v, const std::string& path)
|
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.write(v.data(), v.size());
|
||||||
output.close();
|
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 ReadBinaryFile(std::vector<char>& v, const std::string& path);
|
||||||
void WriteBinaryFile(const 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 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;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<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" version="2.30.3" targetFramework="native" />
|
||||||
<package id="sdl2.nuget.redist" version="2.30.3" targetFramework="native" />
|
<package id="sdl2.nuget.redist" version="2.30.3" targetFramework="native" />
|
||||||
</packages>
|
</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 "io.h"
|
||||||
|
|
||||||
#include <xdelta3.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)
|
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;
|
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
|
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 <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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,13 +44,20 @@ bool Requests::CheckUpdates(std::string& version)
|
|||||||
return false;
|
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 };
|
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)
|
||||||
{
|
{
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
bool DownloadUpdates(const std::string& version, std::string& status);
|
||||||
}
|
}
|
@ -1,10 +1,7 @@
|
|||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "dataManager.h"
|
#include "dataManager.h"
|
||||||
#include "IconsFontAwesome6.h"
|
#include "IconsFontAwesome6.h"
|
||||||
#include "requests.h"
|
|
||||||
#include "patch.h"
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <misc/cpp/imgui_stdlib.h>
|
#include <misc/cpp/imgui_stdlib.h>
|
||||||
#include <portable-file-dialogs.h>
|
#include <portable-file-dialogs.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@ -12,54 +9,66 @@
|
|||||||
|
|
||||||
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");
|
||||||
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)
|
void UI::Render(int width, int height)
|
||||||
{
|
{
|
||||||
static bool update = false;
|
|
||||||
if (update) { Update(); update = false; }
|
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(.0f, .0f), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(.0f, .0f), ImGuiCond_Always);
|
||||||
ImGui::SetNextWindowSize(ImVec2(static_cast<float>(width), static_cast<float>(height)), 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);
|
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;
|
std::string icon = m_username.empty() ? ICON_FA_CIRCLE_XMARK : ICON_FA_CIRCLE_CHECK;
|
||||||
ImGui::InputText(("Username " + icon).c_str(), &m_username);
|
ImGui::InputText(("Username " + icon).c_str(), &m_username, ImGuiInputTextFlags_CallbackCharFilter, FilterUsernameChar);
|
||||||
ImGui::SetItemTooltip("Special characters:\n* = Cross Button\n< = Left Arrow\n@ = Circle\n[ = Square\n^ = Triangle\n& = Space");
|
|
||||||
if (m_username.size() > 9) { m_username = m_username.substr(0, 9); }
|
if (m_username.size() > 9) { m_username = m_username.substr(0, 9); }
|
||||||
|
|
||||||
|
static bool readBios = true;
|
||||||
bool updateReady = 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_biosPath, "Bios Path ", {".bin"}, {"PSX Bios File", "*.bin"}, "Path to a PS1 NTSC-U bios.");
|
||||||
updateReady &= SelectFile(m_duckPath, "Duck Path ", ".exe", {"Executable Files", "*.exe"}, "Path to the duckstation executable");
|
if (updateReady)
|
||||||
updateReady &= SelectFolder(m_iniPath, "Ini Path ", "Duckstation gamesettings folder.\nUsually in Documents/DuckStation/gamesettings");
|
{
|
||||||
|
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::Text(("Version: " + m_version).c_str());
|
||||||
|
|
||||||
ImGui::BeginDisabled(!m_updated);
|
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(!updateReady);
|
ImGui::BeginDisabled(m_updater.IsBusy() || !updateReady);
|
||||||
if (ImGui::Button("Update")) { update = true; m_status = "Updating..."; }
|
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()); }
|
||||||
@ -67,41 +76,27 @@ void UI::Render(int width, int height)
|
|||||||
ImGui::End();
|
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)
|
std::string lowercaseStr;
|
||||||
{
|
for (char c : str)
|
||||||
bool validPath = std::filesystem::exists(str) && str.ends_with(ext);
|
{
|
||||||
std::string icon = validPath ? ICON_FA_CIRCLE_CHECK : ICON_FA_CIRCLE_XMARK;
|
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);
|
ImGui::InputText((label + " " + icon).c_str(), &str);
|
||||||
if (!tip.empty()) { ImGui::SetItemTooltip(tip.c_str()); }
|
if (!tip.empty()) { ImGui::SetItemTooltip(tip.c_str()); }
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@ -111,7 +106,7 @@ bool UI::SelectFile(std::string& str, const std::string& label, const std::strin
|
|||||||
if (selection.empty()) { return false; }
|
if (selection.empty()) { return false; }
|
||||||
str = selection.front();
|
str = selection.front();
|
||||||
}
|
}
|
||||||
return validPath;
|
return checkValidPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UI::SelectFolder(std::string& str, const std::string& label, const std::string& tip)
|
bool UI::SelectFolder(std::string& str, const std::string& label, const std::string& tip)
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "updater.h"
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -10,16 +13,14 @@ public:
|
|||||||
void Render(int width, int height);
|
void Render(int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Update();
|
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 SelectFile(std::string& str, const std::string& label, const 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);
|
bool SelectFolder(std::string& str, const std::string& label, const std::string& tip);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_updated = false;
|
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;
|
||||||
};
|
};
|
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