diff --git a/source-code/PD-Loader.sln b/source-code/PD-Loader.sln
index 7ebb452..1d85914 100644
--- a/source-code/PD-Loader.sln
+++ b/source-code/PD-Loader.sln
@@ -23,6 +23,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderPatch", "source\plugi
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DivaWig", "source\plugins\DivaWig\DivaWig.vcxproj", "{2394C586-F73B-4EF2-BA3E-2A0FC3034B1A}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DSCRemote", "source\plugins\DSCRemote\DSCRemote.vcxproj", "{8544C599-F73B-4EF2-BA3E-2A0FC3034B1A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -69,6 +71,10 @@ Global
{2394C586-F73B-4EF2-BA3E-2A0FC3034B1A}.Debug|x64.Build.0 = Debug|x64
{2394C586-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x64.ActiveCfg = Release|x64
{2394C586-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x64.Build.0 = Release|x64
+ {8544C599-F73B-4EF2-BA3E-2A0FC3034B1A}.Debug|x64.ActiveCfg = Debug|x64
+ {8544C599-F73B-4EF2-BA3E-2A0FC3034B1A}.Debug|x64.Build.0 = Debug|x64
+ {8544C599-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x64.ActiveCfg = Release|x64
+ {8544C599-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/source-code/data/plugins/config.ini b/source-code/data/plugins/config.ini
index ef1e02f..9a2d80a 100644
--- a/source-code/data/plugins/config.ini
+++ b/source-code/data/plugins/config.ini
@@ -102,3 +102,4 @@ r.Height=1080
[plugins]
DivaWig.dva=0
+DSCRemote.dva=0
diff --git a/source-code/source/plugins/DSCRemote/DSCRemote.vcxproj b/source-code/source/plugins/DSCRemote/DSCRemote.vcxproj
new file mode 100644
index 0000000..1f6bd1b
--- /dev/null
+++ b/source-code/source/plugins/DSCRemote/DSCRemote.vcxproj
@@ -0,0 +1,98 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ {8544C599-F73B-4EF2-BA3E-2A0FC3034B1A}
+ DSCRemote
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v142
+ MultiByte
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .dva
+
+
+ .dva
+
+
+
+ Level3
+ Disabled
+ true
+ true
+ ..\..\..\dependencies\detours\include;%(AdditionalIncludeDirectories)
+ -d2FH4- %(AdditionalOptions)
+
+
+ Console
+ ..\..\..\dependencies\detours\lib;%(AdditionalLibraryDirectories)
+ detours.lib;evr.lib;mfplat.lib;mfuuid.lib;strmiids.lib;syelog.lib;%(AdditionalDependencies)
+ -d2:-FH4- %(AdditionalOptions)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+ ..\..\..\dependencies\detours\include;%(AdditionalIncludeDirectories)
+ MultiThreaded
+ -d2FH4- %(AdditionalOptions)
+
+
+ Console
+ true
+ true
+ detours.lib;evr.lib;mfplat.lib;mfuuid.lib;syelog.lib;%(AdditionalDependencies)
+ ..\..\..\dependencies\detours\lib;%(AdditionalLibraryDirectories)
+ -d2:-FH4- %(AdditionalOptions)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source-code/source/plugins/DSCRemote/DSCRemote.vcxproj.filters b/source-code/source/plugins/DSCRemote/DSCRemote.vcxproj.filters
new file mode 100644
index 0000000..ce68d0f
--- /dev/null
+++ b/source-code/source/plugins/DSCRemote/DSCRemote.vcxproj.filters
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source-code/source/plugins/DSCRemote/PluginConfigApi.h b/source-code/source/plugins/DSCRemote/PluginConfigApi.h
new file mode 100644
index 0000000..d0ffd94
--- /dev/null
+++ b/source-code/source/plugins/DSCRemote/PluginConfigApi.h
@@ -0,0 +1,168 @@
+#pragma once
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#include
+#include
+
+// resolution class to store and sort the width and height easily
+class resolution
+{
+public:
+ unsigned int width;
+ unsigned int height;
+
+ resolution()
+ {
+ width = 0;
+ height = 0;
+ }
+
+ resolution(unsigned int width, unsigned int height)
+ {
+ resolution::width = width;
+ resolution::height = height;
+ }
+
+ bool operator ==(const resolution &res2)
+ {
+ return width == res2.width && height == res2.height;
+ }
+
+ // in comparisons width takes priority because it's usually displayed first
+ bool operator <(const resolution &res2)
+ {
+ if (width == res2.width)
+ return height < res2.height;
+ else
+ return width < res2.width;
+ }
+ bool operator >(const resolution &res2)
+ {
+ if (width == res2.width)
+ return height > res2.height;
+ else
+ return width > res2.width;
+ }
+};
+
+namespace PluginConfig
+{
+#pragma pack(push, 1)
+ enum ConfigType {
+ CONFIG_BOOLEAN,
+ CONFIG_NUMERIC,
+ CONFIG_STRING,
+ CONFIG_DROPDOWN_INDEX,
+ CONFIG_DROPDOWN_TEXT,
+ CONFIG_DROPDOWN_NUMBER,
+ CONFIG_RESOLUTION,
+ CONFIG_GROUP_START,
+ CONFIG_GROUP_END,
+ CONFIG_BUTTON,
+ CONFIG_SPACER
+ };
+
+ struct PluginConfigBooleanData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ bool defaultVal;
+ bool saveAsString;
+ };
+
+ struct PluginConfigNumericData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ int defaultVal;
+ int minVal;
+ int maxVal;
+ };
+
+ struct PluginConfigStringData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ LPCWSTR defaultVal;
+ bool useUtf8;
+ };
+
+ struct PluginConfigDropdownIndexData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ int defaultVal;
+ std::vector valueStrings;
+ };
+
+ struct PluginConfigDropdownTextData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ LPCWSTR defaultVal;
+ std::vector valueStrings;
+ bool editable;
+ bool useUtf8;
+ };
+
+ struct PluginConfigDropdownNumberData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ int defaultVal;
+ std::vector valueInts;
+ bool editable;
+ };
+
+ struct PluginConfigResolutionData {
+ LPCWSTR iniVarName;
+ LPCWSTR iniVarName2;
+ LPCWSTR iniSectionName;
+ LPCWSTR iniFilePath;
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ resolution defaultVal;
+ std::vector valueResolutions;
+ bool editable;
+ };
+
+ struct PluginConfigGroupData
+ {
+ LPCWSTR name;
+ int height;
+ };
+
+ struct PluginConfigButtonData {
+ LPCWSTR friendlyName;
+ LPCWSTR description;
+ void(*func)();
+ };
+
+ struct PluginConfigSpacerData {
+ int height;
+ };
+
+ struct PluginConfigOption
+ {
+ ConfigType cfgType;
+ void* data;
+ };
+
+ struct PluginConfigArray
+ {
+ int len;
+ PluginConfigOption* options;
+ };
+#pragma pack(pop)
+}
\ No newline at end of file
diff --git a/source-code/source/plugins/DSCRemote/dllmain.cpp b/source-code/source/plugins/DSCRemote/dllmain.cpp
new file mode 100644
index 0000000..9f5a635
--- /dev/null
+++ b/source-code/source/plugins/DSCRemote/dllmain.cpp
@@ -0,0 +1,201 @@
+#include
+#pragma comment(lib, "Ws2_32.lib")
+#include
+#include
+#include
+#include "framework.h"
+#include "PluginConfigApi.h"
+#include
+
+bool(__cdecl* DSC_EXECUTE_COMMAND)(__int64 dsc_address, float delta_time, __int64 start_time, void* wait_timer, int* a5, int a6, int a7)
+= (bool(__cdecl*)(__int64 dsc_address, float delta_time, __int64 start_time, void* wait_timer, int* a5, int a6, int a7))
+0x14011CBA0;
+bool hookedDSC_EXECUTE_COMMAND(__int64 dsc_address, float delta_time, __int64 start_time, void* wait_timer, int* a5, int a6, int a7);
+void serve();
+
+bool debug = false;
+unsigned short port = 8139;
+
+void loadConfig()
+{
+ port = GetPrivateProfileIntW(L"general", L"port", 8139, CONFIG_FILE);
+ debug = GetPrivateProfileIntW(L"general", L"debug", 0, CONFIG_FILE) > 0;
+
+ return;
+}
+
+float last_delta_time = 0.0;
+__int64 last_start_time = 0i64;
+void* last_wait_timer = nullptr;
+int* last_a5 = nullptr;
+int last_a6 = 0;
+int last_a7 = 0;
+
+bool hookedDSC_EXECUTE_COMMAND(__int64 dsc_address, float delta_time, __int64 start_time, void* wait_timer, int* a5, int a6, int a7)
+{
+ last_delta_time = delta_time;
+ last_start_time = start_time;
+ last_wait_timer = wait_timer;
+ last_a5 = a5;
+ last_a6 = a6;
+ last_a7 = a7;
+
+ return DSC_EXECUTE_COMMAND(dsc_address, delta_time, start_time, wait_timer, a5, a6, a7);
+}
+
+void serve()
+{
+ using namespace std;
+
+ cout << "[DSCRemote] Staring server..." << endl;
+
+ WSADATA wsadata;
+ int result = WSAStartup(MAKEWORD(2, 2), &wsadata);
+ if (result) // error
+ {
+ cout << "[DSCRemote] E: WSAStartup error " << result << '.' << endl;
+ return;
+ }
+
+ SOCKET sock;
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (result == INVALID_SOCKET)
+ {
+ cout << "[DSCRemote] E: Invalid socket." << endl;
+ WSACleanup();
+ return;
+ }
+
+ sockaddr_in saddrin;
+ saddrin.sin_family = AF_INET;
+ saddrin.sin_addr.s_addr = 0;
+ saddrin.sin_port = htons(port);
+
+ result = bind(sock, (sockaddr*)&saddrin, sizeof(sockaddr_in));
+ if (result) // error
+ {
+ cout << "[DSCRemote] E: Couldn't bind port " << port << '.' << endl;
+ closesocket(sock);
+ WSACleanup();
+ return;
+ }
+
+ result = listen(sock, 1);
+ if (result) // error
+ {
+ cout << "[DSCRemote] E: Couldn't listen." << endl;
+ closesocket(sock);
+ WSACleanup();
+ return;
+ }
+ while (true)
+ {
+ cout << "[DSCRemote] Waiting for client..." << endl;
+ SOCKET client_sock;
+ client_sock = accept(sock, NULL, NULL);
+ cout << "[DSCRemote] Client accepted." << endl;
+
+ char buff_recv[64];
+ vector cmdStack;
+ do
+ {
+ result = recv(client_sock, buff_recv, sizeof(buff_recv), 0);
+ if (result > 0)
+ {
+ if (debug) cout << "[DSCRemote] D: Received: " << buff_recv << endl;
+ if (buff_recv[0] != 'i')
+ {
+ long stack = stol(buff_recv);
+ for (int i = 0; i < sizeof(buff_recv); i++) buff_recv[i] = '\0';
+ cmdStack.push_back(stack);
+ const char buff_out[] = "Stacking command/parameter. Send 'i' to inject.";
+ send(client_sock, buff_out, sizeof(buff_out), 0);
+ continue;
+ }
+ for (int i = 0; i < sizeof(buff_recv); i++) buff_recv[i] = '\0';
+
+ const char buff_out[] = "Injecting stack.";
+ send(client_sock, buff_out, sizeof(buff_out), 0);
+
+ const int64_t dscbaseaddr = 0x140CDD978i64;
+ int* currentpos = (int*)(dscbaseaddr + 0x2bf2c);
+ int* command = (int*)(dscbaseaddr + 0xc);
+
+ int posbk = *currentpos;
+ vector cmdbk;
+ for (int i=0; i 0);
+ closesocket(client_sock);
+ cout << "[DSCRemote] Restarting..." << endl;
+ }
+
+ cout << "[DSCRemote] Bye!" << endl;
+ closesocket(sock);
+ WSACleanup();
+}
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+
+ if (ul_reason_for_call == DLL_PROCESS_ATTACH)
+ {
+ using namespace std;
+
+ loadConfig();
+ DisableThreadLibraryCalls(hModule);
+ DetourTransactionBegin();
+ DetourUpdateThread(GetCurrentThread());
+ cout << "[DSCRemote] Hooking functions..." << endl;
+ DetourAttach(&(PVOID&)DSC_EXECUTE_COMMAND, (PVOID)hookedDSC_EXECUTE_COMMAND);
+ cout << "[DSCRemote] Functions hooked." << endl;
+ DetourTransactionCommit();
+
+ thread* server_thread = new thread(serve);
+ }
+
+ return TRUE;
+}
+
+PluginConfig::PluginConfigOption config[] = {
+ { PluginConfig::CONFIG_NUMERIC, new PluginConfig::PluginConfigNumericData{ L"port", L"general", CONFIG_FILE, L"Port", L"TCP/IP port.", 8139, 1, USHRT_MAX } },
+ { PluginConfig::CONFIG_BOOLEAN, new PluginConfig::PluginConfigBooleanData{ L"debug", L"general", CONFIG_FILE, L"Debug", L"Print extra information.", false } },
+};
+
+extern "C" __declspec(dllexport) LPCWSTR GetPluginName(void)
+{
+ return L"DSCRemote";
+}
+
+extern "C" __declspec(dllexport) LPCWSTR GetPluginDescription(void)
+{
+ return L"Allows external applications to send DSC commands via TCP/IP.";
+}
+
+extern "C" __declspec(dllexport) PluginConfig::PluginConfigArray GetPluginOptions(void)
+{
+ return PluginConfig::PluginConfigArray{ _countof(config), config };
+}
diff --git a/source-code/source/plugins/DSCRemote/framework.h b/source-code/source/plugins/DSCRemote/framework.h
new file mode 100644
index 0000000..e9b2d2b
--- /dev/null
+++ b/source-code/source/plugins/DSCRemote/framework.h
@@ -0,0 +1,17 @@
+#include
+#include
+
+std::wstring ExePath() {
+ WCHAR buffer[MAX_PATH];
+ GetModuleFileNameW(NULL, buffer, MAX_PATH);
+ return std::wstring(buffer);
+}
+
+std::wstring DirPath() {
+ std::wstring exepath = ExePath();
+ std::wstring::size_type pos = exepath.find_last_of(L"\\/");
+ return exepath.substr(0, pos);
+}
+
+std::wstring CONFIG_FILE_STRING = DirPath() + L"\\plugins\\DSCRemote.ini";
+LPCWSTR CONFIG_FILE = CONFIG_FILE_STRING.c_str();