Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates, nix flake cleanup, .envrc, etc. #13

Merged
merged 9 commits into from
Feb 18, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup, porting (mostly untested)
jchv committed Feb 17, 2024
commit 08d1a07d247067032c732493bc97208b8edb154f
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ OBJS := \
obj/dll/rugburn/main.o \
obj/hooks/kernel32/inject.o \
obj/hooks/msvcr100/msvcr100.o \
obj/hooks/projectg/us852/ranking.o \
obj/hooks/user32/window.o \
obj/hooks/ws2_32/redir.o \
obj/hooks/wininet/netredir.o \
@@ -38,6 +39,7 @@ OBJS := \
obj/bootstrap.o \
obj/common.o \
obj/config.o \
obj/hex.o \
obj/ijlfwd.o \
obj/json.o \
obj/patch.o \
7 changes: 3 additions & 4 deletions rugburn.vcxproj
Original file line number Diff line number Diff line change
@@ -141,7 +141,6 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -150,7 +149,7 @@
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<ModuleDefinitionFile>exportvs.def</ModuleDefinitionFile>
<EntryPointSymbol>DllMain</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;user32.lib;shlwapi.lib;libvcruntime.lib;libcmt.lib;libucrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -193,7 +192,7 @@
<ClCompile Include="src\regex.c" />
<ClCompile Include="src\third_party\lend\ld32.c" />
<ClCompile Include="src\hooks\msvcr100\msvcr100.c" />
<ClCompile Include="src\patch_usa_852.c" />
<ClCompile Include="src\hooks\projectg\us852\ranking.c" />
</ItemGroup>
<ItemGroup>
<None Include="exportvs.def" />
@@ -204,4 +203,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
2 changes: 1 addition & 1 deletion scripts/setup-clangd.sh
Original file line number Diff line number Diff line change
@@ -22,5 +22,5 @@ cp ".tmp/h/sdkddkve.h" ".tmp/h/sdkddkver.h"
echo "-I$PWD/.tmp/h" > compile_flags.txt
echo "-D__va_list=__builtin_va_list" >> compile_flags.txt
echo "-D_exception_code=__exception_code" >> compile_flags.txt
echo "-D__stdcall=" >> compile_flags.txt
echo "-fms-extensions" >> compile_flags.txt

2 changes: 1 addition & 1 deletion src/bootstrap.c
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ typedef struct _TEB {
#else // x86
PTEB tebPtr = (PTEB)__readfsdword(OFFSETOF(NT_TIB, Self));
#endif
return tebPtr;
return tebPtr;
}
#endif

28 changes: 15 additions & 13 deletions src/config.c
Original file line number Diff line number Diff line change
@@ -58,8 +58,8 @@ void ReadJsonPatchAddressMap(LPSTR *json, LPCSTR key) {
FatalError("Reached maximum number of Patch address!");
}

Config.PatchAddress[Config.NumPatchAddress].addr = ReadDword(key);
TranslateHexInText(value, Config.PatchAddress[Config.NumPatchAddress].patch, sizeof(Config.PatchAddress[Config.NumPatchAddress].patch), &Config.PatchAddress[Config.NumPatchAddress].patch_len);
Config.PatchAddress[Config.NumPatchAddress].addr = ParseAddress(key);
ParsePatch(value, &Config.PatchAddress[Config.NumPatchAddress].patch, &Config.PatchAddress[Config.NumPatchAddress].patchLen);
Config.NumPatchAddress++;
}

@@ -141,15 +141,17 @@ BOOL RewriteAddr(LPSOCKADDR_IN addr) {
}

void PatchAddress() {
int i;

for (i = 0; i < Config.NumPatchAddress; i++) {

if (Config.PatchAddress[i].addr != 0 && Config.PatchAddress[i].patch_len > 0 && Config.PatchAddress[i].patch[0] != '\0') {

Patch((LPVOID)Config.PatchAddress[i].addr, Config.PatchAddress[i].patch, Config.PatchAddress[i].patch_len);

Log("PatchAddress: 0x%08lX, Len: %d, Value: %s\r\n", Config.PatchAddress[i].addr, Config.PatchAddress[i].patch_len, Config.PatchAddress[i].patch);
}
}
int i;
for (i = 0; i < Config.NumPatchAddress; i++) {
if (Config.PatchAddress[i].addr == 0) {
Warning("Patch %d at address 0 will be ignored.", i);
continue;
}
if (Config.PatchAddress[i].patchLen == 0) {
Warning("Patch %d is empty.", i);
continue;
}
Patch((LPVOID)Config.PatchAddress[i].addr, Config.PatchAddress[i].patch, Config.PatchAddress[i].patchLen);
Log("PatchAddress: 0x%08lX, Len: %d, Value: %s\r\n", Config.PatchAddress[i].addr, Config.PatchAddress[i].patchLen, Config.PatchAddress[i].patch);
}
}
4 changes: 2 additions & 2 deletions src/config.h
Original file line number Diff line number Diff line change
@@ -21,8 +21,8 @@ typedef struct _PORTREWRITERULE {

typedef struct _PATCHADDRESS {
DWORD addr;
CHAR patch[1024];
int patch_len;
LPSTR patch;
DWORD patchLen;
} PATCHADDRESS, *LPPATCHADDRESS;

typedef struct _RUGBURNCONFIG {
97 changes: 50 additions & 47 deletions src/dll/rugburn/main.c
Original file line number Diff line number Diff line change
@@ -14,10 +14,10 @@
#include "../../bootstrap.h"
#include "../../common.h"
#include "../../config.h"
#include "../../hooks/hooks.h"
#include "../../ijlfwd.h"
#include "../../patch.h"
#include "../../hooks/hooks.h"
#include "../../patch_usa_852.h"
#include "../../hooks/projectg/us852/ranking.h"

/**
* InitEnvironment configures the PANGYA_ARG environment to avoid needing to
@@ -44,9 +44,9 @@ static VOID InitEnvironment() {
* Implements the GameGuard patches for Pangya US 852.00.
*/
static DWORD STDCALL PatchGG_US852(PVOID unused) {
while(1) {
while (1) {
// TODO(john): Remove hardcoded addresses.
if (*(DWORD*)0x00A495E0 == 0x8F143D83) {
if (*(DWORD *)0x00A495E0 == 0x8F143D83) {
Patch((LPVOID)0x00A495E0, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A49670, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A49690, "\xC3\x90\x90\x90\x90\x90\x90", 7);
@@ -56,35 +56,36 @@ static DWORD STDCALL PatchGG_US852(PVOID unused) {
Log("Patched GG check routines (US 852)\r\n");

Patch((LPVOID)0x00A6ECC9, "\x30\xC0", 2);
Log("Patched Cookie Point Item (US 852)\r\n");

Patch((LPVOID)0x005FB990, "\x80\xB9\x40\x02\x00\x00\x00\x0F\x85\x0D\x00\x00\x00\x8B\x89\x8C\x01\x00\x00\x8B\x01\x8B\x50\x4C\xFF\xD2\xC2\x04\x00", 29);
Log("Patched Cookie Btn in onCallback that's disabled (US 852)\r\n");

Patch((LPVOID)0x005FB9AD, "\xB3\x01\x31\xFF\x90\x53\xBA\xB4\x6A\xCE\x00\xE9\xC9\x31\x00\x00", 16);
Patch((LPVOID)0x005FEB7E, "\xE9\x2A\xCE\xFF\xFF", 5);
Patch((LPVOID)0x005FEB8D, "\x53", 1);
Patch((LPVOID)0x005FEB9A, "\x53", 1);
Patch((LPVOID)0x005FB9BD, "\x6A\x01\xBA\xB4\x6A\xCE\x00\x8B\xCE\xE8\xF5\x2C\x00\x00\x6A\x01\xBA\xB0\x69\xCE\x00\xE9\x29\x32\x00\x00", 26);
Patch((LPVOID)0x005FEBF9, "\xE9\xBF\xCD\xFF\xFF", 5);
Log("Patched Btn Cookie, Gacha and Scratch disabled (US 852)\r\n");

Patch((LPVOID)0x008BC729, "\x01", 1);
Patch((LPVOID)0x008C1495, "\xEB\x0C", 2);
Patch((LPVOID)0x008C14A3, "\xE8\xF8\xB2\xFF\xFF\x88\x86\xE4\x00\x00\x00\x5E\xC3", 13);
Log("Patched Btn Change Nickname disabled (US 852)\r\n");

unsigned char jmp_to_patch_ranking[5] = { 0xE9u, 0u, 0u, 0u, 0u };

DWORD relAddr = (DWORD)OnUnderBar_RankingUp - 0x00655630u - 5u;

memcpy(&jmp_to_patch_ranking[1], &relAddr, 4u);

Patch((LPVOID)0x00655630, jmp_to_patch_ranking, sizeof(jmp_to_patch_ranking));
Log("Patched Ranking System disabled (US 852)\r\n");
Log("Patched Cookie Point Item (US 852)\r\n");

Patch((LPVOID)0x005FB990,
"\x80\xB9\x40\x02\x00\x00\x00\x0F\x85\x0D\x00\x00\x00\x8B\x89\x8C\x01\x00\x00\x8B"
"\x01\x8B\x50\x4C\xFF\xD2\xC2\x04\x00",
29);
Log("Patched Cookie Btn in onCallback that's disabled (US 852)\r\n");

Patch((LPVOID)0x005FB9AD,
"\xB3\x01\x31\xFF\x90\x53\xBA\xB4\x6A\xCE\x00\xE9\xC9\x31\x00\x00", 16);
Patch((LPVOID)0x005FEB7E, "\xE9\x2A\xCE\xFF\xFF", 5);
Patch((LPVOID)0x005FEB8D, "\x53", 1);
Patch((LPVOID)0x005FEB9A, "\x53", 1);
Patch((LPVOID)0x005FB9BD,
"\x6A\x01\xBA\xB4\x6A\xCE\x00\x8B\xCE\xE8\xF5\x2C\x00\x00\x6A\x01\xBA\xB0\x69\xCE"
"\x00\xE9\x29\x32\x00\x00",
26);
Patch((LPVOID)0x005FEBF9, "\xE9\xBF\xCD\xFF\xFF", 5);
Log("Patched Btn Cookie, Gacha and Scratch disabled (US 852)\r\n");

Patch((LPVOID)0x008BC729, "\x01", 1);
Patch((LPVOID)0x008C1495, "\xEB\x0C", 2);
Patch((LPVOID)0x008C14A3, "\xE8\xF8\xB2\xFF\xFF\x88\x86\xE4\x00\x00\x00\x5E\xC3", 13);
Log("Patched Btn Change Nickname disabled (US 852)\r\n");

InitUS852RankingHook();
Log("Patched Ranking System disabled (US 852)\r\n");
return TRUE;
}
if (*(DWORD*)0x00A49580 == 0x8F143D83) {
if (*(DWORD *)0x00A49580 == 0x8F143D83) {
Patch((LPVOID)0x00A49580, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A49670, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A49690, "\xC3\x90\x90\x90\x90\x90\x90", 7);
@@ -94,18 +95,18 @@ static DWORD STDCALL PatchGG_US852(PVOID unused) {
Log("Patched GG check routines (US 824)\r\n");
return TRUE;
}
Delay(5);
Delay(5);
}
return FALSE;
return FALSE;
}

/**
* Implements the GameGuard patches for Pangya JP 972.00.
*/
static DWORD STDCALL PatchGG_JP972(PVOID unused) {
while(1) {
while (1) {
// TODO(john): Remove hardcoded addresses.
if (*(DWORD*)0x00A5CD10 == 0x1BA43D83) {
if (*(DWORD *)0x00A5CD10 == 0x1BA43D83) {
Patch((LPVOID)0x00A5CD10, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A5CDA0, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A5CDC0, "\xC3\x90\x90\x90\x90\x90\x90", 7);
@@ -115,7 +116,7 @@ static DWORD STDCALL PatchGG_JP972(PVOID unused) {
Log("Patched GG check routines (JP 972)\r\n");
return TRUE;
}
if (*(DWORD*)0x00A5CF80 == 0x1BA43D83) {
if (*(DWORD *)0x00A5CF80 == 0x1BA43D83) {
Patch((LPVOID)0x00A5CF80, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A5C010, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A5C030, "\xC3\x90\x90\x90\x90\x90\x90", 7);
@@ -125,7 +126,7 @@ static DWORD STDCALL PatchGG_JP972(PVOID unused) {
Log("Patched GG check routines (JP 974)\r\n");
return TRUE;
}
if (*(DWORD*)0x00A5CF80 == 0x1C143D83) {
if (*(DWORD *)0x00A5CF80 == 0x1C143D83) {
Patch((LPVOID)0x00A5CF80, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A5C010, "\xC3\x90\x90\x90\x90\x90\x90", 7);
Patch((LPVOID)0x00A5C030, "\xC3\x90\x90\x90\x90\x90\x90", 7);
@@ -135,20 +136,22 @@ static DWORD STDCALL PatchGG_JP972(PVOID unused) {
Log("Patched GG check routines (JP 983)\r\n");
return TRUE;
}
Delay(5);
Delay(5);
}
return FALSE;
return FALSE;
}

static VOID STDCALL PatchDynamicAndGG(LPTHREAD_START_ROUTINE patchThread) {

if (!patchThread) {
Warning("It looks like no patch exists for this version of PangYa™.\nThe game will likely exit a couple minutes after detecting GameGuard is not present.");
PatchAddress();
}else {
if (patchThread(NULL) == TRUE)
PatchAddress();
}
if (!patchThread) {
Warning("It looks like no patch exists for this version of PangYa™.\nThe game will likely "
"exit a couple minutes after detecting GameGuard is not present.");
PatchAddress();
} else {
if (patchThread(NULL) == TRUE) {
PatchAddress();
}
}
}

/**
@@ -202,7 +205,7 @@ extern BOOL STDCALL SlipstrmDllMain(HANDLE hInstance, DWORD dwReason, LPVOID res
PFNDLLMAINPROC pSlipstreamOep;
BOOL bOepResult;

pSlipstreamOep = (PFNDLLMAINPROC)((*(DWORD*)((DWORD)hInstance + 0x40))+(DWORD)hInstance);
pSlipstreamOep = (PFNDLLMAINPROC)((*(DWORD *)((DWORD)hInstance + 0x40)) + (DWORD)hInstance);

// Call OEP; return failure if it fails.
bOepResult = pSlipstreamOep(hInstance, dwReason, reserved);
252 changes: 122 additions & 130 deletions src/hex.c
Original file line number Diff line number Diff line change
@@ -1,130 +1,122 @@
#include "hex.h"

DWORD ReadDword(LPCSTR _text) {

if (_text == NULL) {
FatalError("Hex - Read Dword hex in text, parameters invalid!");
}

if (_text[0] == '\0')
return 0;

LPSTR pos = (LPSTR)_text;
DWORD dw = 0;
int len = 0;

if ((pos = strstr(pos, "0x")) == NULL) {
FatalError("Hex - Read Dword hex in text, invalid dword hex!");
}

while (isxdigit(pos[len + 2]))
len++;

if (len > 0) {

CHAR ctmp = pos[len + 2];
pos[len + 2] = '\0';

dw = strtol(pos + 2, NULL, 16);
}

return dw;
}

void TranslateHexInText(LPCSTR _text, LPSTR _result, int _size_result, int* _size_out) {

if (_text == NULL || _result == NULL || _size_result == 0 || _size_out == NULL) {
FatalError("Hex - Translate hex in text, parameters invalid!");
}

*_size_out = 0;

if (_text[0] == '\0') {

_result[*_size_out++] = '\0';

return;
}

LPSTR pos = (LPSTR)_text;
LPSTR rest = NULL;
int len = 0, num = 0;

do {

rest = strstr(pos, "\\x");

if (rest != NULL) {

len = rest - pos;

if (len > 0) {

if ((*_size_out + len) > _size_result) {
FatalError("Hex - Translate hex in text, overflow result buffer!");
}

memcpy(_result + *_size_out, pos, len);
*_size_out += len;

pos = rest;
}

len = 0;

while (len < 2 && isxdigit(pos[len + 2]))
len++;

if (len > 0) {

CHAR ctmp = pos[len + 2];
pos[len + 2] = '\0';

num = strtol(pos + 2, NULL, 16);

if ((*_size_out + 1) > _size_result) {
FatalError("Hex - Translate hex in text, overflow result buffer!");
}

pos += len + 2;
*pos = ctmp;

_result[*_size_out] = (CHAR)num;

*_size_out += 1;

}else {

if ((*_size_out + 1) > _size_result) {
FatalError("Hex - Translate hex in text, overflow result buffer!");
}

_result[*_size_out] = *pos++;

*_size_out += 1;
}

}else {

len = strlen(pos);

if ((*_size_out + len) > _size_result) {
FatalError("Hex - Translate hex in text, overflow result buffer!");
}

memcpy(_result + *_size_out, pos, len);
*_size_out += len;

pos += len;
}

} while(*pos != '\0');

if ((*_size_out + 1) > _size_result) {
FatalError("Hex - Translate hex in text, overflow result buffer!");
}

_result[*_size_out] = '\0';

*_size_out += 1;
}
#include "hex.h"

static BOOL ParseHex(CHAR ch, LPDWORD pOutValue) {
if (ch >= '0' && ch <= '9') {
*pOutValue = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
*pOutValue = ch - 'a' + 10;
} else if (ch >= 'A' && ch <= 'F') {
*pOutValue = ch - 'A' + 10;
} else {
return FALSE;
}
return TRUE;
}

DWORD ParseAddress(LPCSTR lpszText) {
LPCSTR pos = lpszText;
DWORD hexDigitVal;
DWORD result = 0;

if (lpszText == NULL) {
FatalError("ParseAddress: Received unexpected NULL string");
}

if (lpszText[0] == '\0') {
return 0;
}

if (pos[0] != '0' || pos[1] != 'x' || pos[2] == 0) {
FatalError("ParseAddress: Invalid input, expected 0x-prefixed hex number");
}

pos += 2;

while (ParseHex(*pos, &hexDigitVal) == TRUE) {
result <<= 4;
result |= hexDigitVal;
pos++;
}

if (*pos) {
FatalError("ParseAddress: Unexpected text remaining after address: %s", pos);
}

return result;
}

void ParsePatch(LPCSTR lpszText, LPSTR *pDataOut, DWORD *pSizeOut) {
DWORD hexDigitVal;
DWORD hexOctetVal;
LPCSTR inPos;
LPSTR outPos;
*pSizeOut = 0;

// Calculate length
inPos = lpszText;
while (*inPos) {
switch (*inPos++) {
case '\\':
switch (*inPos++) {
case 'x':
if (ParseHex(*inPos++, &hexDigitVal) == FALSE) {
FatalError("ParsePatch: Bad hex escape near %s", inPos);
}
if (ParseHex(*inPos++, &hexDigitVal) == FALSE) {
FatalError("ParsePatch: Bad hex escape near %s", inPos);
}
(*pSizeOut)++;
break;
case '\\':
(*pSizeOut)++;
break;
case '\0':
FatalError("ParsePatch: Unexpected end of string in escape code");
break;
default:
FatalError("ParsePatch: Unknown escape code: %s", inPos);
}
default:
(*pSizeOut)++;
}
inPos++;
}

// Allocate memory
*pDataOut = AllocMem(*pSizeOut);

// Parse
inPos = lpszText;
outPos = *pDataOut;
while (*inPos) {
switch (*inPos) {
case '\\':
inPos++;
switch (*inPos++) {
case 'x':
if (!ParseHex(*inPos++, &hexDigitVal)) {
FatalError("ParsePatch: Bad hex escape near %s", inPos);
}
hexOctetVal = hexDigitVal << 4;
if (!ParseHex(*inPos++, &hexDigitVal)) {
FatalError("ParsePatch: Bad hex escape near %s", inPos);
}
hexOctetVal |= hexDigitVal;
*outPos++ = (CHAR)(BYTE)hexOctetVal;
break;
case '\\':
(*pSizeOut)++;
*outPos++ = '\\';
break;
case '\0':
FatalError("ParsePatch: Unexpected end of string in escape code");
break;
default:
FatalError("ParsePatch: Unknown escape code: %s", inPos);
}
default:
*(outPos++) = *(inPos++);
}
inPos++;
}
}
18 changes: 9 additions & 9 deletions src/hex.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#ifndef HEX_H
#define HEX_H

#include "common.h"

DWORD ReadDword(LPCSTR _text);
void TranslateHexInText(LPCSTR _text, LPSTR _result, int _size_result, int* _size_out);

#endif
#ifndef HEX_H
#define HEX_H

#include "common.h"

DWORD ParseAddress(LPCSTR lpszText);
void ParsePatch(LPCSTR lpszText, LPSTR *pDataOut, DWORD *pSizeOut);

#endif
206 changes: 206 additions & 0 deletions src/hooks/projectg/us852/ranking.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#include "ranking.h"
#include "../../../patch.h"

typedef struct _stMsgObject {
LPVOID m_pIActor;
DWORD m_id;
DWORD m_com[5];
DWORD m_time;
} stMsgObject;

inline stMsgObject MakeMsgObject(LPVOID _pIActor, DWORD _id, DWORD _c0, DWORD _c1, DWORD _c2, DWORD _c3, DWORD _c4) {
stMsgObject obj;
obj.m_pIActor = _pIActor;
obj.m_id = _id;
obj.m_com[0] = _c0;
obj.m_com[1] = _c1;
obj.m_com[2] = _c2;
obj.m_com[3] = _c3;
obj.m_com[4] = _c4;
obj.m_time = 0u;
return obj;
}

#define GETTHISPOINTERMEMBER(_type_, _addr_, _this_) (_type_ *)(((BYTE *)(_this_)) + (_addr_))
#define GETVTABLEFN(_this_, _addr_) ((LPVOID)(*((LPCSTR *)(_this_)) + (_addr_)))

typedef LPVOID (__cdecl *PFNMAKEINSTANCEFRLOGINRSDLG)(void);
typedef LPCSTR (STDCALL *PFNBRASILTRANSLATESTRINGPROC)(LPCSTR _str);
typedef VOID (STDCALL *PFNFRLOGINRSDLGSETSTATEPROC)(DWORD _state);

typedef LPVOID (STDCALL *PFNFRFORM_INITPROC)(LPVOID, LPVOID, LPVOID, LPCSTR, LPVOID);
typedef LPVOID (STDCALL *PFNIOBJECTDYNAMICCASTPROC)(LPVOID, LPCVOID);
typedef LPVOID (STDCALL *PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC)(LPVOID, DWORD);

typedef DWORD64 (STDCALL *PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC)(LPVOID, DWORD64);
typedef VOID (STDCALL *PFNVIRTUALIACTORHANDLEMSGPROC)(LPVOID, stMsgObject *);
typedef VOID (STDCALL *PFNVIRTUALIACTORCLOSEALLDIALOGSPROC)(LPVOID);
typedef BOOL (STDCALL *PFNWNETWORKSYSTEMISCONNECTEDPROC)(LPVOID, DWORD);
typedef BOOL (STDCALL *PFNWNETWORKSYSTEMINITPROC)(LPVOID, DWORD);
typedef BOOL (STDCALL *PFNWNETWORKSYSTEMSENDMESSAGEAPROC)(LPVOID, DWORD, DWORD);
typedef VOID (STDCALL *PFNCONSTRUCTORWSENDPACKETPROC)(LPVOID, DWORD);
typedef VOID (STDCALL *PFNDESTRUCTORWSENDPACKETPROC)(LPVOID);
typedef VOID (STDCALL *PFNWSENDPACKETSENDPROC)(LPVOID, DWORD);
typedef VOID (STDCALL *PFNCLOBBYMAINCLEARTOOLTIPPROC)(LPVOID, LPVOID);
typedef BOOL (STDCALL *PFNFRLOGINRSDLGONLOGINRSDLGRESULTPROC)(LPVOID, DWORD, LPVOID);
typedef BOOL (STDCALL *PFNFRFORMOPENPROC)(LPVOID, LPVOID, DWORD, DWORD, DWORD, DWORD);

static PFNMAKEINSTANCEFRLOGINRSDLG pMakeInstanceFrLoginRsDlg = NULL;
static PFNFRFORM_INITPROC p_Init = NULL;
static PFNIOBJECTDYNAMICCASTPROC pDynamicCast = NULL;

static PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC pIsControlServerService = NULL;
static PFNBRASILTRANSLATESTRINGPROC pBrasilTranslateString = NULL;
static PFNWNETWORKSYSTEMISCONNECTEDPROC pIsConnected = NULL;
static PFNWNETWORKSYSTEMINITPROC pInit = NULL;
static PFNWNETWORKSYSTEMSENDMESSAGEAPROC pSendMessageA = NULL;
static PFNCONSTRUCTORWSENDPACKETPROC pConstructorWSendPacket = NULL;
static PFNDESTRUCTORWSENDPACKETPROC pDestructorWSendPacket = NULL;
static PFNWSENDPACKETSENDPROC pSend = NULL;
static PFNCLOBBYMAINCLEARTOOLTIPPROC pClearToolTip = NULL;
static PFNFRLOGINRSDLGSETSTATEPROC pSetState = NULL;
static LPVOID pOnUnderBar_RankingUpThunk = NULL;
static LPVOID pOnLoginRsDlgResultThunk = NULL;

static void STDCALL OnUnderBar_RankingUp(LPVOID _this);
static BOOL STDCALL OnLoginRsDlgResult(LPVOID _this, DWORD exit_code, LPVOID _pFrForm);

VOID InitUS852RankingHook() {
pMakeInstanceFrLoginRsDlg = (PFNMAKEINSTANCEFRLOGINRSDLG)0x00AC4680u;
p_Init = (PFNFRFORM_INITPROC)BuildStdcallToThiscallThunk((LPVOID)0x00BCDD40u);
pDynamicCast = (PFNIOBJECTDYNAMICCASTPROC)BuildStdcallToThiscallThunk((LPVOID)0x0047EFF0u);

pIsControlServerService =
(PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC)BuildStdcallToThiscallThunk((LPVOID)0x0062E4A0);
pBrasilTranslateString =
(PFNBRASILTRANSLATESTRINGPROC)BuildStdcallToThiscallThunk((LPVOID)0x00B3A7E0u);
pIsConnected =
(PFNWNETWORKSYSTEMISCONNECTEDPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3AEB0u);
pInit = (PFNWNETWORKSYSTEMINITPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3AD40u);
pSendMessageA =
(PFNWNETWORKSYSTEMSENDMESSAGEAPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3ADB0u);
pConstructorWSendPacket =
(PFNCONSTRUCTORWSENDPACKETPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3BEE0u);
pDestructorWSendPacket =
(PFNDESTRUCTORWSENDPACKETPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3BCD0u);
pSend = (PFNWSENDPACKETSENDPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3B340u);
pClearToolTip = (PFNCLOBBYMAINCLEARTOOLTIPPROC)BuildStdcallToThiscallThunk((LPVOID)0x00656770u);
pSetState = (PFNFRLOGINRSDLGSETSTATEPROC)BuildStdcallToThiscallThunk((LPVOID)0x00AC4380u);
pOnUnderBar_RankingUpThunk = BuildThiscallToStdcallThunk((LPVOID)OnUnderBar_RankingUp);
pOnLoginRsDlgResultThunk = BuildThiscallToStdcallThunk((LPVOID)OnLoginRsDlgResult);
InstallHook((LPVOID)0x00655630, (LPVOID)pOnUnderBar_RankingUpThunk);
}

static LPVOID CreateFormFrLoginRsDlg(LPVOID _pFrWndManager, LPVOID _pFrCmdTarget, LPCSTR _name,
LPVOID _pFrWnd) {
static LPVOID g_pFrLoginRsDlgObject = NULL;

LPCVOID pFrLoginRsDlg_m_RTTI = (LPCVOID)0x00ECA148u;

LPVOID pFrLoginRsDlgObject = pMakeInstanceFrLoginRsDlg();

LPVOID pFrLoginRsDlgObjectAfter;

if (pFrLoginRsDlgObject == NULL) {
return NULL;
}

pFrLoginRsDlgObjectAfter =
p_Init(pFrLoginRsDlgObject, _pFrWndManager, _pFrCmdTarget, _name, _pFrWnd);

if (pFrLoginRsDlgObjectAfter != NULL) {
// Test to see if it saves the value in tls as it is in pangya
g_pFrLoginRsDlgObject = pFrLoginRsDlgObjectAfter;

return pDynamicCast(pFrLoginRsDlgObjectAfter, pFrLoginRsDlg_m_RTTI);
}

if (pFrLoginRsDlgObject != NULL) {
// TODO: need virtual thunk
PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC pPerformLoginRsDlgScalarDeletingDestructor =
(PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC)BuildStdcallToThiscallThunk(
GETVTABLEFN(pFrLoginRsDlgObject, 0x04u));
pPerformLoginRsDlgScalarDeletingDestructor(pFrLoginRsDlgObject, 1u);
FreeMem(pPerformLoginRsDlgScalarDeletingDestructor);
}

return NULL;
}

static BOOL STDCALL OnLoginRsDlgResult(LPVOID _this, DWORD exit_code /*I think*/, LPVOID _pFrForm) {
if (_this != NULL) {
*GETTHISPOINTERMEMBER(LPVOID, 0x160u, _this) = NULL;
}

return TRUE;
}

static void STDCALL OnUnderBar_RankingUp(LPVOID _this) {
LPCSTR gRankingDisabledServiceMsg = (LPCSTR)0x00D244E8u;
LPVOID gCSharedDocInstance = (*(LPVOID *)0x00E10FACu);
LPVOID gWNetworkSystemInstance = (*(LPVOID *)0x00E3CFD0);
LPVOID gFresh = (*(LPVOID *)0x00EC9640);
LPVOID *FormWndObject = NULL;

if (pIsControlServerService(gCSharedDocInstance, 0x10000u) != 0u) {
LPCSTR translatedMsgStr;
stMsgObject msg;

if (_this == NULL) {
return;
}

translatedMsgStr = pBrasilTranslateString(gRankingDisabledServiceMsg);
msg = MakeMsgObject(_this, 0x23u, (DWORD)translatedMsgStr, 0u, 0u, 0u, 0u);

// TODO: need virtual thunk
{
PFNVIRTUALIACTORHANDLEMSGPROC pIActorHandleMsg =
(PFNVIRTUALIACTORHANDLEMSGPROC)BuildStdcallToThiscallThunk(GETVTABLEFN(_this, 0x2Cu));
pIActorHandleMsg(_this, &msg);
FreeMem(pIActorHandleMsg);
return;
}
}

// TODO: need virtual thunk
{
PFNVIRTUALIACTORCLOSEALLDIALOGSPROC pIActorCloseAllDialogs =
(PFNVIRTUALIACTORCLOSEALLDIALOGSPROC)BuildStdcallToThiscallThunk(
GETVTABLEFN(_this, 0x3Cu));
pIActorCloseAllDialogs(_this);
FreeMem(pIActorCloseAllDialogs);
}

if (pIsConnected(gWNetworkSystemInstance, 0) == FALSE) {
pInit(gWNetworkSystemInstance, 3);
pSendMessageA(gWNetworkSystemInstance, 3, 0);
} else {
BYTE WSendPacketObject[40u];
pConstructorWSendPacket((LPVOID)WSendPacketObject, 0x47u);
pSend((LPVOID)WSendPacketObject, 0);
pDestructorWSendPacket((LPVOID)WSendPacketObject);
}

FormWndObject = GETTHISPOINTERMEMBER(void *, 0x160u, _this);

if (*FormWndObject == NULL) {
*FormWndObject = CreateFormFrLoginRsDlg(*GETTHISPOINTERMEMBER(void *, 0x40u, gFresh),
(void *)GETTHISPOINTERMEMBER(void *, 0x18, _this),
"login_gs", NULL);

pSetState(1u);

// TODO: need virtual thunk
{
PFNFRFORMOPENPROC pFrFormOpen =
(PFNFRFORMOPENPROC)BuildStdcallToThiscallThunk(GETVTABLEFN(*FormWndObject, 0x64u));
pFrFormOpen(*FormWndObject, pOnLoginRsDlgResultThunk, 0xFFFFFFE8u, 0, 0, 0x41);
FreeMem(pFrFormOpen);
}
}

pClearToolTip(_this, NULL);

return;
}
8 changes: 8 additions & 0 deletions src/hooks/projectg/us852/ranking.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef HOOKS_PROJECTG_US852_RANKING_H
#define HOOKS_PROJECTG_US852_RANKING_H

#include "../../../common.h"

VOID InitUS852RankingHook();

#endif
74 changes: 74 additions & 0 deletions src/patch.c
Original file line number Diff line number Diff line change
@@ -143,3 +143,77 @@ PVOID HookProc(HMODULE hModule, LPCSTR szName, PVOID pfnTargetProc) {

return pfnTrampolineProc;
}

/**
* Creates a thunk that translates from MSVC thiscall to stdcall calling
* convention. The returned function pointer can be used in MSVC ABI vtables.
*/
PVOID BuildThiscallToStdcallThunk(PVOID pfnProc) {
DWORD thunkLen = 9;
DWORD oldProtect;
DWORD relAddr;
PCHAR codeblock;

// Calculate the jump address.
relAddr = (DWORD)pfnProc - (DWORD)codeblock;

// Allocate data for thunk.
codeblock = AllocMem(thunkLen);

// Create calling convention thunk.
// We want to put the this pointer, from ecx, onto the stack at the
// left-most argument. Since we were called via stdcall, return address is
// the current value on the stack, followed by left-most argument. So, pop
// the return address, push the this pointer, and then push the return
// address back onto the stack.
codeblock[0] = 0x58; // pop eax
codeblock[1] = 0x51; // push ecx
codeblock[2] = 0x50; // push eax
codeblock[3] = 0xE9; // jmp
memcpy(&codeblock[4], &relAddr, 4);

// ...and a return at the end, for good measure.
codeblock[8] = 0xC3;

// Mark the codeblock as executable.
pVirtualProtect(codeblock, thunkLen, PAGE_EXECUTE_READWRITE, &oldProtect);

return codeblock;
}

/**
* Creates a thunk that translates from stdcall to MSVC thiscall calling
* convention. The returned function pointer can be called using stdcall.
*/
PVOID BuildStdcallToThiscallThunk(PVOID pfnProc) {
DWORD thunkLen = 9;
DWORD oldProtect;
DWORD relAddr;
PCHAR codeblock;

// Calculate the jump address.
relAddr = (DWORD)pfnProc - (DWORD)codeblock;

// Allocate data for thunk.
codeblock = AllocMem(thunkLen);

// Create calling convention thunk.
// We want to put the this pointer, from the stack at the left-most
// argument, into ecx. Since we were called via thiscall, return address is
// the current value on the stack, followed by the this pointer argument.
// So, pop the return address, pop the this pointer to ecx, and then push
// the return address back onto the stack.
codeblock[0] = 0x58; // pop eax
codeblock[1] = 0x59; // pop ecx
codeblock[2] = 0x50; // push eax
codeblock[3] = 0xE9; // jmp
memcpy(&codeblock[4], &relAddr, 4);

// ...and a return at the end, for good measure.
codeblock[8] = 0xC3;

// Mark the codeblock as executable.
pVirtualProtect(codeblock, thunkLen, PAGE_EXECUTE_READWRITE, &oldProtect);

return codeblock;
}
2 changes: 2 additions & 0 deletions src/patch.h
Original file line number Diff line number Diff line change
@@ -10,5 +10,7 @@ DWORD CountOpcodeBytes(FARPROC fn, DWORD minBytes);
PCHAR BuildTrampoline(DWORD fn, DWORD prefixLen);
PVOID HookFunc(PVOID pfnProc, PVOID pvTargetProc);
PVOID HookProc(HMODULE hModule, LPCSTR szName, PVOID pfnTargetProc);
PVOID BuildThiscallToStdcallThunk(PVOID pfnProc);
PVOID BuildStdcallToThiscallThunk(PVOID pfnProc);

#endif
143 changes: 0 additions & 143 deletions src/patch_usa_852.c

This file was deleted.

12 changes: 0 additions & 12 deletions src/patch_usa_852.h

This file was deleted.