chore: tidy + split off experimental code
This commit is contained in:
parent
17b2ad960a
commit
8e55b9af11
|
|
@ -1,9 +1,9 @@
|
||||||
# GitHub official CMake gitignore template
|
|
||||||
# https://github.com/github/gitignore/blob/main/CMake.gitignore
|
|
||||||
|
|
||||||
build
|
build
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
# GitHub official CMake gitignore template
|
||||||
|
# https://github.com/github/gitignore/blob/main/CMake.gitignore
|
||||||
|
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
|
|
||||||
|
|
@ -26,29 +26,10 @@ if(COMPILE_PLAYER OR COMPILE_SERVER)
|
||||||
list(APPEND SOURCE Source/Hooks/CRoblox.cpp)
|
list(APPEND SOURCE Source/Hooks/CRoblox.cpp)
|
||||||
list(APPEND HEADER Header/Hooks/CRoblox.hpp)
|
list(APPEND HEADER Header/Hooks/CRoblox.hpp)
|
||||||
|
|
||||||
if(COMPILE_PLAYER)
|
|
||||||
# Discord Rich Presence integration
|
|
||||||
list(APPEND SOURCE Source/Discord.cpp)
|
|
||||||
list(APPEND HEADER Header/Discord.hpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(COMPILE_SERVER)
|
if(COMPILE_SERVER)
|
||||||
# Hook DataModel, ServerReplicator, and StandardOut as well as include our custom server interface
|
# Hook ServerReplicator
|
||||||
list(APPEND SOURCE
|
list(APPEND SOURCE Source/Hooks/ServerReplicator.cpp)
|
||||||
Source/Server.cpp
|
list(APPEND HEADER Header/Hooks/ServerReplicator.hpp)
|
||||||
|
|
||||||
Source/Hooks/DataModel.cpp
|
|
||||||
Source/Hooks/ServerReplicator.cpp
|
|
||||||
Source/Hooks/StandardOut.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
list(APPEND HEADER
|
|
||||||
Header/Server.hpp
|
|
||||||
|
|
||||||
Header/Hooks/DataModel.hpp
|
|
||||||
Header/Hooks/ServerReplicator.hpp
|
|
||||||
Header/Hooks/StandardOut.hpp
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -56,23 +37,15 @@ add_library(Kiseki.Patcher SHARED ${SOURCE} ${HEADER})
|
||||||
|
|
||||||
# Packages
|
# Packages
|
||||||
find_package(CURL CONFIG REQUIRED)
|
find_package(CURL CONFIG REQUIRED)
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
find_package(pugixml CONFIG REQUIRED)
|
|
||||||
find_package(RapidJSON CONFIG REQUIRED)
|
|
||||||
|
|
||||||
find_path(DETOURS_INCLUDE_DIRS "detours/detours.h")
|
find_path(DETOURS_INCLUDE_DIRS "detours/detours.h")
|
||||||
find_library(DETOURS_LIBRARY detours REQUIRED)
|
find_library(DETOURS_LIBRARY detours REQUIRED)
|
||||||
|
|
||||||
target_include_directories(Kiseki.Patcher PRIVATE Header ${DETOURS_INCLUDE_DIRS})
|
target_include_directories(Kiseki.Patcher PRIVATE Header ${DETOURS_INCLUDE_DIRS})
|
||||||
target_link_libraries(Kiseki.Patcher PRIVATE CURL::libcurl ${DETOURS_LIBRARY} OpenSSL::SSL OpenSSL::Crypto pugixml::static pugixml::pugixml rapidjson)
|
target_link_libraries(Kiseki.Patcher PRIVATE CURL::libcurl ${DETOURS_LIBRARY})
|
||||||
|
|
||||||
# Target-specific linking and compile options
|
# Target-specific linking and compile options
|
||||||
if(COMPILE_PLAYER)
|
if(COMPILE_PLAYER)
|
||||||
find_path(DISCORD_RPC_INCLUDE_DIRS "discord_rpc.h")
|
|
||||||
find_library(DISCORD_RPC_LIBRARY discord-rpc REQUIRED)
|
|
||||||
|
|
||||||
target_include_directories(Kiseki.Patcher PRIVATE ${DISCORD_RPC_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(Kiseki.Patcher PRIVATE ${DISCORD_RPC_LIBRARY})
|
|
||||||
target_compile_definitions(Kiseki.Patcher PRIVATE PLAYER)
|
target_compile_definitions(Kiseki.Patcher PRIVATE PLAYER)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#ifdef PLAYER
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <discord_rpc.h>
|
|
||||||
#include <discord_register.h>
|
|
||||||
#include <rapidjson/document.h>
|
|
||||||
|
|
||||||
#include "Hooks/CRoblox.hpp"
|
|
||||||
|
|
||||||
#include "Globals.hpp"
|
|
||||||
|
|
||||||
class Discord {
|
|
||||||
public:
|
|
||||||
static void Initialize(const std::string joinScriptUrl);
|
|
||||||
static void Cleanup();
|
|
||||||
private:
|
|
||||||
static void Update();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -11,24 +11,11 @@
|
||||||
|
|
||||||
#define PUBLIC_KEY 0x06, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xA5, 0x11, 0xD0, 0x9F, 0xAB, 0x9B, 0x3A, 0x96, 0xC5, 0xBE, 0x50, 0xBB, 0xCA, 0x0C, 0xBB, 0xC8, 0x1A, 0x9C, 0xC1, 0x2F, 0x22, 0x7A, 0x80, 0x2C, 0x31, 0x01, 0xE1, 0x21, 0xC2, 0x7F, 0xE0, 0x10, 0xA1, 0x2D, 0x09, 0xED, 0x10, 0x3E, 0x28, 0xB9, 0xBD, 0x0F, 0x38, 0xDB, 0x52, 0x78, 0xC0, 0xEC, 0x04, 0xD4, 0x00, 0xAD, 0x45, 0xD3, 0xC7, 0x78, 0xF2, 0x83, 0xB5, 0x5B, 0x16, 0x0C, 0x32, 0x5D, 0xB3, 0x3B, 0xDA, 0xA2, 0x9C, 0x73, 0xB2, 0x0C, 0x09, 0x93, 0xD8, 0xF8, 0xD9, 0xC5, 0x75, 0xAB, 0x33, 0x19, 0xD3, 0x6A, 0xAF, 0x20, 0x21, 0x6C, 0x78, 0x31, 0x41, 0xD1, 0xCD, 0x6D, 0x4D, 0xA1, 0x6D, 0x49, 0x3A, 0x6A, 0x33, 0x10, 0x6D, 0x52, 0x33, 0x4A, 0x10, 0x32, 0x3D, 0x42, 0xE6, 0xEC, 0x38, 0x97, 0x2F, 0x05, 0x41, 0xDD, 0xD6, 0xB6, 0xAC, 0x17, 0x4B, 0x7E, 0xFA, 0xFE, 0xA4, 0xD2
|
#define PUBLIC_KEY 0x06, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xA5, 0x11, 0xD0, 0x9F, 0xAB, 0x9B, 0x3A, 0x96, 0xC5, 0xBE, 0x50, 0xBB, 0xCA, 0x0C, 0xBB, 0xC8, 0x1A, 0x9C, 0xC1, 0x2F, 0x22, 0x7A, 0x80, 0x2C, 0x31, 0x01, 0xE1, 0x21, 0xC2, 0x7F, 0xE0, 0x10, 0xA1, 0x2D, 0x09, 0xED, 0x10, 0x3E, 0x28, 0xB9, 0xBD, 0x0F, 0x38, 0xDB, 0x52, 0x78, 0xC0, 0xEC, 0x04, 0xD4, 0x00, 0xAD, 0x45, 0xD3, 0xC7, 0x78, 0xF2, 0x83, 0xB5, 0x5B, 0x16, 0x0C, 0x32, 0x5D, 0xB3, 0x3B, 0xDA, 0xA2, 0x9C, 0x73, 0xB2, 0x0C, 0x09, 0x93, 0xD8, 0xF8, 0xD9, 0xC5, 0x75, 0xAB, 0x33, 0x19, 0xD3, 0x6A, 0xAF, 0x20, 0x21, 0x6C, 0x78, 0x31, 0x41, 0xD1, 0xCD, 0x6D, 0x4D, 0xA1, 0x6D, 0x49, 0x3A, 0x6A, 0x33, 0x10, 0x6D, 0x52, 0x33, 0x4A, 0x10, 0x32, 0x3D, 0x42, 0xE6, 0xEC, 0x38, 0x97, 0x2F, 0x05, 0x41, 0xDD, 0xD6, 0xB6, 0xAC, 0x17, 0x4B, 0x7E, 0xFA, 0xFE, 0xA4, 0xD2
|
||||||
|
|
||||||
#define RBX__MESSAGE_INFO 0
|
|
||||||
#define RBX__MESSAGE_OUTPUT 1
|
|
||||||
#define RBX__MESSAGE_WARNING 2
|
|
||||||
#define RBX__MESSAGE_ERROR 3
|
|
||||||
|
|
||||||
#define CLASSPADDING_DATAMODEL__JOBID 739
|
|
||||||
|
|
||||||
#define ADDRESS_HTTP__HTTPGETPOSTWININET 0x006F03B0
|
|
||||||
#define ADDRESS_HTTP__HTTPGETPOSTWINHTTP 0x006EEE70
|
|
||||||
#define ADDRESS_HTTP__TRUSTCHECK 0x005B7050
|
#define ADDRESS_HTTP__TRUSTCHECK 0x005B7050
|
||||||
#define ADDRESS_CRYPT__VERIFYSIGNATUREBASE64 0x00809EC0
|
#define ADDRESS_CRYPT__VERIFYSIGNATUREBASE64 0x00809EC0
|
||||||
#define ADDRESS_DATAMODEL__GETJOBID 0x005E70C0
|
|
||||||
#define ADDRESS_STANDARDOUT__PRINT 0x005B25E0
|
#define ADDRESS_STANDARDOUT__PRINT 0x005B25E0
|
||||||
#define ADDRESS_SERVERREPLICATOR__SENDTOP 0x00513E80
|
#define ADDRESS_SERVERREPLICATOR__SENDTOP 0x00513E80
|
||||||
#define ADDRESS_SERVERREPLICATOR__PROCESSTICKET 0x00514B60
|
#define ADDRESS_SERVERREPLICATOR__PROCESSTICKET 0x00514B60
|
||||||
#define ADDRESS_SCRIPTCONTEXT__OPENSTATE 0x006245E0
|
|
||||||
#define ADDRESS_LUA_PUSHCFUNCTION 0x0076B8E0
|
|
||||||
#define ADDRESS_LUA_SETGLOBAL 0x0076B810
|
|
||||||
|
|
||||||
#define CLASSLOCATION_CROBLOXAPP 0x00CBA8A0
|
#define CLASSLOCATION_CROBLOXAPP 0x00CBA8A0
|
||||||
#define CLASSLOCATION_CAPP 0x00406D80
|
#define CLASSLOCATION_CAPP 0x00406D80
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,6 @@
|
||||||
|
|
||||||
#include "Globals.hpp"
|
#include "Globals.hpp"
|
||||||
#include "Helpers.hpp"
|
#include "Helpers.hpp"
|
||||||
#include "Discord.hpp"
|
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
#include "Server.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CWorkspace;
|
class CWorkspace;
|
||||||
|
|
||||||
|
|
@ -49,9 +44,4 @@ void __fastcall CRobloxCommandLineInfo__ParseParam_hook(CRobloxCommandLineInfo*
|
||||||
extern CRobloxApp__InitInstance_t CRobloxApp__InitInstance;
|
extern CRobloxApp__InitInstance_t CRobloxApp__InitInstance;
|
||||||
extern CRobloxCommandLineInfo__ParseParam_t CRobloxCommandLineInfo__ParseParam;
|
extern CRobloxCommandLineInfo__ParseParam_t CRobloxCommandLineInfo__ParseParam;
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
extern std::wstring jobId;
|
|
||||||
extern bool hasJobId;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#ifdef SERVER
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Hooks/CRoblox.hpp"
|
|
||||||
|
|
||||||
#include "Globals.hpp"
|
|
||||||
#include "Helpers.hpp"
|
|
||||||
|
|
||||||
struct DataModel
|
|
||||||
{
|
|
||||||
void* padding1[CLASSPADDING_DATAMODEL__JOBID];
|
|
||||||
std::string jobId;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef INT(__thiscall* DataModel__getJobId_t)(DataModel* _this, int a2);
|
|
||||||
int __fastcall DataModel__getJobId_hook(DataModel* _this, void*, int a2);
|
|
||||||
extern DataModel__getJobId_t DataModel__getJobId;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -3,30 +3,12 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <rapidjson/document.h>
|
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
#include "Hooks/StandardOut.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Globals.hpp"
|
#include "Globals.hpp"
|
||||||
#include "Helpers.hpp"
|
#include "Helpers.hpp"
|
||||||
|
|
||||||
struct Http
|
|
||||||
{
|
|
||||||
#if PADDING_STRUCT != 0
|
|
||||||
void* padding1[1];
|
|
||||||
#endif
|
|
||||||
std::string alternateUrl;
|
|
||||||
void* padding2[3 + PADDING_STRUCT];
|
|
||||||
std::string url;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (__thiscall* Http__httpGetPostWinInet_t)(Http* _this, bool isPost, int a3, bool compressData, LPCSTR additionalHeaders, int a6);
|
|
||||||
typedef bool(__thiscall* Http__trustCheck_t)(const char* url);
|
typedef bool(__thiscall* Http__trustCheck_t)(const char* url);
|
||||||
|
|
||||||
void __fastcall Http__httpGetPostWinInet_hook(Http* _this, void*, bool isPost, int a3, bool compressData, LPCSTR additionalHeaders, int a6);
|
|
||||||
bool __fastcall Http__trustCheck_hook(const char* url);
|
bool __fastcall Http__trustCheck_hook(const char* url);
|
||||||
|
|
||||||
extern Http__httpGetPostWinInet_t Http__httpGetPostWinInet;
|
|
||||||
extern Http__trustCheck_t Http__trustCheck;
|
extern Http__trustCheck_t Http__trustCheck;
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "Globals.hpp"
|
#include "Globals.hpp"
|
||||||
|
|
||||||
struct Packet
|
struct Packet
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#ifdef SERVER
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Globals.hpp"
|
|
||||||
#include "Helpers.hpp"
|
|
||||||
#include "Patcher.hpp"
|
|
||||||
#include "Server.hpp"
|
|
||||||
|
|
||||||
typedef void(__thiscall* StandardOut__print_t)(int _this, int type, std::string* message);
|
|
||||||
void __fastcall StandardOut__print_hook(int _this, void*, int type, std::string* message);
|
|
||||||
extern StandardOut__print_t StandardOut__print;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
#ifdef SERVER
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Hooks/StandardOut.hpp"
|
|
||||||
|
|
||||||
#include "Globals.hpp"
|
|
||||||
#include "Helpers.hpp"
|
|
||||||
|
|
||||||
enum class RequestType {
|
|
||||||
POST,
|
|
||||||
GET
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class LogSeverity {
|
|
||||||
Information = RBX__MESSAGE_INFO,
|
|
||||||
Output = RBX__MESSAGE_OUTPUT,
|
|
||||||
Warning = RBX__MESSAGE_WARNING,
|
|
||||||
Error = RBX__MESSAGE_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
class Server {
|
|
||||||
public:
|
|
||||||
static HANDLE Handle;
|
|
||||||
|
|
||||||
static void Initialize(const std::wstring jobId);
|
|
||||||
static void Cleanup();
|
|
||||||
|
|
||||||
struct Log
|
|
||||||
{
|
|
||||||
static void Http(const RequestType type, const std::string message);
|
|
||||||
static void Output(const LogSeverity severity, const std::string message);
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
static std::string HttpLogPath;
|
|
||||||
static std::string OutputLogPath;
|
|
||||||
|
|
||||||
static std::ofstream HttpLog;
|
|
||||||
static std::ofstream OutputLog;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
#ifdef PLAYER
|
|
||||||
|
|
||||||
#include "Discord.hpp"
|
|
||||||
|
|
||||||
bool isRunning = false;
|
|
||||||
std::string username;
|
|
||||||
int placeId;
|
|
||||||
|
|
||||||
void Discord::Initialize(const std::string joinScriptUrl)
|
|
||||||
{
|
|
||||||
std::pair<bool, std::map<std::string, std::string>> parsed = Helpers::parseURL(joinScriptUrl);
|
|
||||||
|
|
||||||
if (!parsed.first)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsed.second["query"].empty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, std::string> query = Helpers::parseQueryString(parsed.second["query"]);
|
|
||||||
|
|
||||||
if (query.find("ticket") == query.end())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query.find("discord") == query.end())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query["ticket"].empty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query["discord"] == "0")
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the username and placeId
|
|
||||||
std::pair<bool, std::string> response = Helpers::httpGet(Helpers::getBaseUrl() + std::string("/api/places/information?ticket=" + query["ticket"]));
|
|
||||||
if (!response.first)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rapidjson::Document document;
|
|
||||||
document.Parse(response.second.c_str());
|
|
||||||
|
|
||||||
if (document.HasParseError() || !document.HasMember("username") || !document.HasMember("placeId"))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
username = document["username"].GetString();
|
|
||||||
placeId = document["placeId"].GetInt();
|
|
||||||
|
|
||||||
Discord::Update();
|
|
||||||
|
|
||||||
// Run the updater
|
|
||||||
std::thread updater(Discord::Update);
|
|
||||||
updater.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Discord::Update()
|
|
||||||
{
|
|
||||||
isRunning = true;
|
|
||||||
|
|
||||||
while (isRunning)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(60 * 1000));
|
|
||||||
|
|
||||||
std::string title = "";
|
|
||||||
int size = 0;
|
|
||||||
int max = 0;
|
|
||||||
|
|
||||||
// Get title, size, and max
|
|
||||||
std::pair<bool, std::string> response = Helpers::httpGet(Helpers::getBaseUrl() + std::string("/api/places/information?id=" + placeId));
|
|
||||||
if (!response.first)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rapidjson::Document document;
|
|
||||||
document.Parse(response.second.c_str());
|
|
||||||
|
|
||||||
if (document.HasParseError() || !document.HasMember("title") || !document.HasMember("size") || !document.HasMember("max"))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
title = document["title"].GetString();
|
|
||||||
size = document["size"].GetInt();
|
|
||||||
max = document["max"].GetInt();
|
|
||||||
|
|
||||||
DiscordRichPresence presence;
|
|
||||||
memset(&presence, 0, sizeof(presence));
|
|
||||||
|
|
||||||
presence.largeImageText = username.c_str();
|
|
||||||
presence.largeImageKey = "logo";
|
|
||||||
presence.smallImageText = "2011";
|
|
||||||
presence.smallImageKey = "2011";
|
|
||||||
|
|
||||||
presence.details = title.c_str();
|
|
||||||
presence.state = "In a game";
|
|
||||||
presence.partySize = size;
|
|
||||||
presence.partyMax = max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Discord::Cleanup()
|
|
||||||
{
|
|
||||||
isRunning = false;
|
|
||||||
Discord_Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -147,7 +147,6 @@ std::pair<bool, std::map<std::string, std::string>> Helpers::parseURL(const std:
|
||||||
curl_free(encodedQuery);
|
curl_free(encodedQuery);
|
||||||
|
|
||||||
CURLU* curl = curl_url();
|
CURLU* curl = curl_url();
|
||||||
CURLUcode result = curl_url_set(curl, CURLUPART_URL, url.c_str(), 0);
|
|
||||||
CURLUcode result = curl_url_set(curl, CURLUPART_URL, encodedUrl.c_str(), 0);
|
CURLUcode result = curl_url_set(curl, CURLUPART_URL, encodedUrl.c_str(), 0);
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,6 @@ std::wstring authenticationUrl;
|
||||||
std::wstring authenticationTicket;
|
std::wstring authenticationTicket;
|
||||||
std::wstring joinScriptUrl;
|
std::wstring joinScriptUrl;
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
bool hasJobId = false;
|
|
||||||
std::wstring jobId;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CRobloxApp__InitInstance_t CRobloxApp__InitInstance = (CRobloxApp__InitInstance_t)ADDRESS_CROBLOXAPP__INITINSTANCE;
|
CRobloxApp__InitInstance_t CRobloxApp__InitInstance = (CRobloxApp__InitInstance_t)ADDRESS_CROBLOXAPP__INITINSTANCE;
|
||||||
CRobloxCommandLineInfo__ParseParam_t CRobloxCommandLineInfo__ParseParam = (CRobloxCommandLineInfo__ParseParam_t)ADDRESS_CROBLOXCOMMANDLINEINFO__PARSEPARAM;
|
CRobloxCommandLineInfo__ParseParam_t CRobloxCommandLineInfo__ParseParam = (CRobloxCommandLineInfo__ParseParam_t)ADDRESS_CROBLOXCOMMANDLINEINFO__PARSEPARAM;
|
||||||
|
|
||||||
|
|
@ -45,17 +40,7 @@ BOOL __fastcall CRobloxApp__InitInstance_hook(CRobloxApp* _this)
|
||||||
|
|
||||||
if (!hasAuthenticationUrl || !hasAuthenticationTicket || !hasJoinScriptUrl)
|
if (!hasAuthenticationUrl || !hasAuthenticationTicket || !hasJoinScriptUrl)
|
||||||
{
|
{
|
||||||
ShellExecuteW(0, 0, L"https://kiseki.lol/games", 0, 0, SW_SHOW);
|
ShellExecute(0, 0, "https://kiseki.lol/games", 0, 0, SW_SHOW);
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
if (!hasJobId)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
MessageBoxW(nullptr, L"Missing job ID", L"Kiseki", MB_OK | MB_ICONERROR);
|
|
||||||
#endif
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -94,10 +79,6 @@ void __fastcall CRobloxCommandLineInfo__ParseParam_hook(CRobloxCommandLineInfo*
|
||||||
_this->m_bRunAutomated = TRUE;
|
_this->m_bRunAutomated = TRUE;
|
||||||
|
|
||||||
CCommandLineInfo__ParseLast(_this, bLast);
|
CCommandLineInfo__ParseLast(_this, bLast);
|
||||||
|
|
||||||
#ifdef PLAYER
|
|
||||||
Discord::Initialize(Helpers::ws2s(joinScriptUrl));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAuthenticationUrl && authenticationUrl.empty())
|
if (hasAuthenticationUrl && authenticationUrl.empty())
|
||||||
|
|
@ -119,19 +100,7 @@ void __fastcall CRobloxCommandLineInfo__ParseParam_hook(CRobloxCommandLineInfo*
|
||||||
CCommandLineInfo__ParseLast(_this, bLast);
|
CCommandLineInfo__ParseLast(_this, bLast);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
if (hasJobId && jobId.empty())
|
|
||||||
{
|
|
||||||
int size = MultiByteToWideChar(CP_ACP, 0, pszParam, strlen(pszParam), nullptr, 0);
|
|
||||||
jobId.resize(size);
|
|
||||||
MultiByteToWideChar(CP_ACP, 0, pszParam, strlen(pszParam), &jobId[0], size);
|
|
||||||
|
|
||||||
CCommandLineInfo__ParseLast(_this, bLast);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (bFlag && _stricmp(pszParam, "a") == 0)
|
if (bFlag && _stricmp(pszParam, "a") == 0)
|
||||||
{
|
{
|
||||||
hasAuthenticationUrl = true;
|
hasAuthenticationUrl = true;
|
||||||
|
|
@ -153,18 +122,6 @@ void __fastcall CRobloxCommandLineInfo__ParseParam_hook(CRobloxCommandLineInfo*
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
if (bFlag && _stricmp(pszParam, "jobId") == 0)
|
|
||||||
{
|
|
||||||
hasJobId = true;
|
|
||||||
CCommandLineInfo__ParseLast(_this, bLast);
|
|
||||||
|
|
||||||
Server::Initialize(jobId);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CRobloxCommandLineInfo__ParseParam(_this, pszParam, bFlag, bLast);
|
CRobloxCommandLineInfo__ParseParam(_this, pszParam, bFlag, bLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#ifdef SERVER
|
|
||||||
|
|
||||||
#include "Hooks/DataModel.hpp"
|
|
||||||
|
|
||||||
bool setJobId = false;
|
|
||||||
|
|
||||||
DataModel__getJobId_t DataModel__getJobId = (DataModel__getJobId_t)ADDRESS_DATAMODEL__GETJOBID;
|
|
||||||
|
|
||||||
int __fastcall DataModel__getJobId_hook(DataModel* _this, void*, int a2)
|
|
||||||
{
|
|
||||||
if (!setJobId && hasJobId && !jobId.empty())
|
|
||||||
{
|
|
||||||
_this->jobId = Helpers::ws2s(jobId);
|
|
||||||
setJobId = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DataModel__getJobId(_this, a2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,104 +1,9 @@
|
||||||
#include "Hooks/Http.hpp"
|
#include "Hooks/Http.hpp"
|
||||||
|
|
||||||
#define CHECK(condition, code) \
|
|
||||||
if(!error) { \
|
|
||||||
if ((condition)) { \
|
|
||||||
error = code; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
Http__httpGetPostWinInet_t Http__httpGetPostWinInet = (Http__httpGetPostWinInet_t)ADDRESS_HTTP__HTTPGETPOSTWININET;
|
|
||||||
Http__trustCheck_t Http__trustCheck = (Http__trustCheck_t)ADDRESS_HTTP__TRUSTCHECK;
|
Http__trustCheck_t Http__trustCheck = (Http__trustCheck_t)ADDRESS_HTTP__TRUSTCHECK;
|
||||||
|
|
||||||
void __fastcall Http__httpGetPostWinInet_hook(Http* _this, void*, bool isPost, int a3, bool compressData, LPCSTR additionalHeaders, int a6)
|
|
||||||
{
|
|
||||||
std::pair<bool, std::map<std::string, std::string>> parsed = Helpers::parseURL(_this->url);
|
|
||||||
Http _changed = *_this;
|
|
||||||
|
|
||||||
if (parsed.first)
|
|
||||||
{
|
|
||||||
std::map<std::string, std::string> url = parsed.second;
|
|
||||||
|
|
||||||
if (!url["path"].empty() && !url["host"].empty() && !url["query"].empty())
|
|
||||||
{
|
|
||||||
url["path"] = Helpers::toLower(url["path"]);
|
|
||||||
|
|
||||||
if (url["host"] == "roblox.com" || url["host"] == "www.roblox.com")
|
|
||||||
{
|
|
||||||
if (url["path"] == "/asset" || url["path"] == "/asset/" || url["path"] == "/asset/default.ashx")
|
|
||||||
{
|
|
||||||
_changed.url = "https://assetdelivery.roblox.com/v1/asset/?" + url["query"];
|
|
||||||
_this = &_changed;
|
|
||||||
}
|
|
||||||
else if (url["path"] == "/thumbs/asset.ashx" || url["path"] == "/thumbs/avatar.ashx")
|
|
||||||
{
|
|
||||||
std::string api = "https://www.roblox.com/" + std::string(url["path"] == "/thumbs/asset.ashx" ? "asset" : "avatar") + "/request-thumbnail-fix";
|
|
||||||
|
|
||||||
std::map<std::string, std::string> source = Helpers::parseQueryString(url["query"]);
|
|
||||||
std::map<std::string, std::string> fixed = {
|
|
||||||
{ url["path"] == "/thumbs/asset.ashx" ? "overrideModeration" : "dummy", "false" },
|
|
||||||
{ "thumbnailFormatId", "0" }
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto& pair : source)
|
|
||||||
{
|
|
||||||
fixed[Helpers::toLower(pair.first)] = pair.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fixed.find("id") != fixed.end())
|
|
||||||
{
|
|
||||||
auto handler = fixed.extract("id");
|
|
||||||
handler.key() = url["path"] == "/thumbs/asset.ashx" ? "assetId" : "userId";
|
|
||||||
|
|
||||||
fixed.insert(std::move(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
api += Helpers::joinQueryString(fixed);
|
|
||||||
|
|
||||||
// Get the API response
|
|
||||||
std::pair<bool, std::string> response = Helpers::httpGet(api);
|
|
||||||
if (!response.first)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Unexpected error occurred when fetching Roblox API: 0x0");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string data = response.second;
|
|
||||||
|
|
||||||
rapidjson::Document document;
|
|
||||||
document.Parse(data.c_str());
|
|
||||||
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
CHECK(document.HasParseError(), 0x01);
|
|
||||||
CHECK(!document.HasMember("d"), 0x02);
|
|
||||||
CHECK(!document["d"].IsObject(), 0x03);
|
|
||||||
CHECK(!document["d"].HasMember("url"), 0x04);
|
|
||||||
CHECK(!document["d"]["url"].IsString(), 0x05);
|
|
||||||
|
|
||||||
if (error != 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Unexpected error occurred when fetching Roblox API: 0x0" + std::to_string(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
_changed.url = document["d"]["url"].GetString();
|
|
||||||
_this = &_changed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
if (Server::Handle)
|
|
||||||
{
|
|
||||||
Server::Log::Http((RequestType)isPost, _this->url);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Http__httpGetPostWinInet(_this, isPost, a3, compressData, additionalHeaders, a6);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool __fastcall Http__trustCheck_hook(const char* url)
|
bool __fastcall Http__trustCheck_hook(const char* url)
|
||||||
{
|
{
|
||||||
if (strlen(url) == 7 && !Helpers::isASCII(url))
|
if (strlen(url) == 7 && !Helpers::isASCII(url))
|
||||||
{
|
{
|
||||||
url = ((char**)url)[0];
|
url = ((char**)url)[0];
|
||||||
|
|
@ -133,7 +38,7 @@ bool __fastcall Http__trustCheck_hook(const char* url)
|
||||||
{
|
{
|
||||||
for (std::string wildcard : Helpers::allowedWildcardDomains)
|
for (std::string wildcard : Helpers::allowedWildcardDomains)
|
||||||
{
|
{
|
||||||
if (parsed["host"].size() >= wildcard.size() && 0 == wildcard.compare(parsed["host"].size() - wildcard.size(), wildcard.size(), wildcard))
|
if (parsed["host"].length() >= wildcard.length() && parsed["host"].compare(parsed["host"].length() - wildcard.length(), wildcard.length(), wildcard))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -143,5 +48,5 @@ bool __fastcall Http__trustCheck_hook(const char* url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
#ifdef SERVER
|
#ifdef SERVER
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "Hooks/ServerReplicator.hpp"
|
#include "Hooks/ServerReplicator.hpp"
|
||||||
|
|
||||||
static std::map<ServerReplicator*, RakPeerInterface*> rakPeers;
|
static std::map<ServerReplicator*, RakPeerInterface*> rakPeers;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#ifdef SERVER
|
|
||||||
|
|
||||||
#include "Hooks/StandardOut.hpp"
|
|
||||||
|
|
||||||
StandardOut__print_t StandardOut__print = (StandardOut__print_t)ADDRESS_STANDARDOUT__PRINT;
|
|
||||||
|
|
||||||
void __fastcall StandardOut__print_hook(int _this, void*, int type, std::string* message)
|
|
||||||
{
|
|
||||||
StandardOut__print(_this, type, message);
|
|
||||||
|
|
||||||
if (Server::Handle)
|
|
||||||
{
|
|
||||||
#ifndef _DEBUG
|
|
||||||
// Message pointer is offset 4 bytes when the DLL is compiled as release
|
|
||||||
message = reinterpret_cast<std::string*>((int)message + 4);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Server::Log::Output((LogSeverity)type, message->c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
#ifdef SERVER
|
|
||||||
|
|
||||||
#include "Server.hpp"
|
|
||||||
|
|
||||||
HANDLE Server::Handle;
|
|
||||||
|
|
||||||
std::ofstream Server::OutputLog;
|
|
||||||
std::ofstream Server::HttpLog;
|
|
||||||
|
|
||||||
std::string Server::OutputLogPath;
|
|
||||||
std::string Server::HttpLogPath;
|
|
||||||
|
|
||||||
void Server::Initialize(const std::wstring jobId)
|
|
||||||
{
|
|
||||||
std::string _jobId = Helpers::ws2s(jobId);
|
|
||||||
std::string signature = "Kiseki.Patcher v1.0.0";
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
signature += " (compiled as Debug)";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AllocConsole();
|
|
||||||
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
|
|
||||||
Server::Handle = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
SetStdHandle(STD_OUTPUT_HANDLE, Server::Handle);
|
|
||||||
|
|
||||||
printf((signature + "\n\n").c_str());
|
|
||||||
|
|
||||||
// Initialize file logging
|
|
||||||
fs::create_directory(fs::path(Helpers::getModulePath()).parent_path() / "logs");
|
|
||||||
|
|
||||||
OutputLogPath = (fs::path(Helpers::getModulePath()).parent_path() / "logs" / (_jobId + "-Output.log")).string();
|
|
||||||
HttpLogPath = (fs::path(Helpers::getModulePath()).parent_path() / "logs" / (_jobId + "-Http.log")).string();
|
|
||||||
|
|
||||||
OutputLog.open(OutputLogPath, std::ios::out);
|
|
||||||
HttpLog.open(HttpLogPath, std::ios::out);
|
|
||||||
|
|
||||||
OutputLog << signature << " - StandardOut" << std::endl << std::endl;
|
|
||||||
HttpLog << signature << " - Http" << std::endl << std::endl;
|
|
||||||
|
|
||||||
OutputLog.close();
|
|
||||||
HttpLog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::Log::Output(const LogSeverity severity, const std::string message)
|
|
||||||
{
|
|
||||||
std::string type;
|
|
||||||
std::string time = Helpers::getISOTimestamp();
|
|
||||||
|
|
||||||
switch (severity)
|
|
||||||
{
|
|
||||||
case LogSeverity::Output:
|
|
||||||
SetConsoleTextAttribute(Server::Handle, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
|
||||||
type = "out";
|
|
||||||
break;
|
|
||||||
case LogSeverity::Information:
|
|
||||||
SetConsoleTextAttribute(Server::Handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
|
||||||
type = "info";
|
|
||||||
break;
|
|
||||||
case LogSeverity::Warning:
|
|
||||||
SetConsoleTextAttribute(Server::Handle, FOREGROUND_RED | FOREGROUND_GREEN);
|
|
||||||
type = "warn";
|
|
||||||
break;
|
|
||||||
case LogSeverity::Error:
|
|
||||||
type = "err";
|
|
||||||
SetConsoleTextAttribute(Server::Handle, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%s\n", message.c_str());
|
|
||||||
SetConsoleTextAttribute(Server::Handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
|
||||||
|
|
||||||
OutputLog.open(OutputLogPath, std::ios::out);
|
|
||||||
OutputLog << "[" << time << "] [" << type << "] " << message << std::endl;
|
|
||||||
OutputLog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::Log::Http(const RequestType request, const std::string url)
|
|
||||||
{
|
|
||||||
std::string type;
|
|
||||||
std::string time = Helpers::getISOTimestamp();
|
|
||||||
|
|
||||||
type = (request == RequestType::GET ? "GET" : "POST");
|
|
||||||
|
|
||||||
HttpLog.open(HttpLogPath, std::ios::out);
|
|
||||||
HttpLog << "[" << time << "] [" << type << "] " << "'" << url << "'" << std::endl;
|
|
||||||
HttpLog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::Cleanup()
|
|
||||||
{
|
|
||||||
CloseHandle(Server::Handle);
|
|
||||||
OutputLog.close();
|
|
||||||
HttpLog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,30 +1,18 @@
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "Globals.hpp"
|
#include "Globals.hpp"
|
||||||
#include "Patcher.hpp"
|
#include "Patcher.hpp"
|
||||||
|
|
||||||
#ifdef PLAYER
|
|
||||||
#include "Discord.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
#include "Server.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Hooks/Http.hpp"
|
#include "Hooks/Http.hpp"
|
||||||
#include "Hooks/Crypt.hpp"
|
#include "Hooks/Crypt.hpp"
|
||||||
#include "Hooks/CRoblox.hpp"
|
#include "Hooks/CRoblox.hpp"
|
||||||
|
|
||||||
#ifdef SERVER
|
#ifdef SERVER
|
||||||
#include "Hooks/DataModel.hpp"
|
|
||||||
#include "Hooks/StandardOut.hpp"
|
|
||||||
#include "Hooks/ServerReplicator.hpp"
|
#include "Hooks/ServerReplicator.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
START_PATCH_LIST()
|
START_PATCH_LIST()
|
||||||
|
|
||||||
ADD_PATCH(Http__httpGetPostWinInet, Http__httpGetPostWinInet_hook)
|
|
||||||
ADD_PATCH(Http__trustCheck, Http__trustCheck_hook)
|
ADD_PATCH(Http__trustCheck, Http__trustCheck_hook)
|
||||||
|
|
||||||
ADD_PATCH(Crypt__verifySignatureBase64, Crypt__verifySignatureBase64_hook)
|
ADD_PATCH(Crypt__verifySignatureBase64, Crypt__verifySignatureBase64_hook)
|
||||||
|
|
@ -35,10 +23,6 @@ ADD_PATCH(CRobloxCommandLineInfo__ParseParam, CRobloxCommandLineInfo__ParseParam
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SERVER
|
#ifdef SERVER
|
||||||
ADD_PATCH(DataModel__getJobId, DataModel__getJobId_hook)
|
|
||||||
|
|
||||||
ADD_PATCH(StandardOut__print, StandardOut__print_hook)
|
|
||||||
|
|
||||||
ADD_PATCH(ServerReplicator__sendTop, ServerReplicator__sendTop_hook)
|
ADD_PATCH(ServerReplicator__sendTop, ServerReplicator__sendTop_hook)
|
||||||
ADD_PATCH(ServerReplicator__processTicket, ServerReplicator__processTicket_hook)
|
ADD_PATCH(ServerReplicator__processTicket, ServerReplicator__processTicket_hook)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -75,22 +59,9 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||||
if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
||||||
{
|
{
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
|
|
||||||
#ifdef PLAYER
|
|
||||||
Discord::Cleanup();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SERVER
|
|
||||||
if (Server::Handle)
|
|
||||||
{
|
|
||||||
Server::Cleanup();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SERVER
|
void __declspec(dllexport) entrypoint() {}
|
||||||
void __declspec(dllexport) entrypoint() {}
|
|
||||||
#endif
|
|
||||||
|
|
@ -4,10 +4,6 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"curl",
|
"curl",
|
||||||
"detours",
|
"detours"
|
||||||
"discord-rpc",
|
|
||||||
"openssl",
|
|
||||||
"pugixml",
|
|
||||||
"rapidjson"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue