namespace { std::optional ParseRCode(std::wstring RCode) { RCode.erase(0,1); std::wsmatch match; HookParam hp; hp.type |= DIRECT_READ; // {S|Q|V|M} switch (RCode[0]) { case L'S': break; case L'Q': hp.type |= CODEC_UTF16; break; case L'U': hp.type |= CODEC_UTF32; break; case L'V': hp.type |= CODEC_UTF8; break; default: return {}; } RCode.erase(0, 1); // [codepage#] if (std::regex_search(RCode, match, std::wregex(L"^([0-9]+)#"))) { hp.codepage = std::stoi(match[1]); RCode.erase(0, match[0].length()); } // @addr if (!std::regex_match(RCode, match, std::wregex(L"@([[:xdigit:]]+)"))) return {}; hp.address = std::stoull(match[1], nullptr, 16); return hp; } std::optional ParseHCode(std::wstring HCode,std::optional hpo={}) { auto hp=hpo?hpo.value():HookParam{}; if(HCode[0]=='L'){ hp.type|=HOOK_RETURN; HCode.erase(0,1); } switch (HCode[0]) { case L'B': hp.type|=BREAK_POINT; case L'H': break; default: return {}; } HCode.erase(0,1); if(endWith(HCode,L":JIT:YUZU")) hp.jittype=JITTYPE::YUZU; else if(endWith(HCode,L":JIT:PPSSPP")) hp.jittype=JITTYPE::PPSSPP; else if(endWith(HCode,L":JIT:VITA3K")) hp.jittype=JITTYPE::VITA3K; else if(endWith(HCode,L":JIT:RPCS3")) hp.jittype=JITTYPE::RPCS3; else if(endWith(HCode,L":JIT:UNITY")) hp.jittype=JITTYPE::UNITY; // {A|B|W|H|S|Q|V|M} switch (HCode[0]) { case L'A': hp.type |= CODEC_ANSI_BE; break; case L'B': //ANSI LE break; case L'W': hp.type |= CODEC_UTF16; break; case L'I': hp.type |= CODEC_UTF32; break; case L'S': hp.type |= USING_STRING; break; case L'Q': hp.type |= USING_STRING | CODEC_UTF16; break; case L'M': hp.type |=SPECIAL_JIT_STRING|USING_STRING|CODEC_UTF16; break; case L'U': hp.type |= USING_STRING | CODEC_UTF32; break; case L'V': hp.type |= USING_STRING | CODEC_UTF8; break; default: return {}; } HCode.erase(0, 1); if (hp.type & USING_STRING) { if (HCode[0] == L'F') { hp.type |= FULL_STRING; HCode.erase(0, 1); } } // [N] if (HCode[0] == L'N') { hp.type |= NO_CONTEXT; HCode.erase(0, 1); } std::wsmatch match; // [codepage#] if (std::regex_search(HCode, match, std::wregex(L"^([0-9]+)#"))) { hp.codepage = std::stoi(match[1]); HCode.erase(0, match[0].length()); } // [padding+] if (std::regex_search(HCode, match, std::wregex(L"^([[:xdigit:]]+)\\+"))) { hp.padding = std::stoull(match[1], nullptr, 16); HCode.erase(0, match[0].length()); } auto ConsumeHexInt = [&HCode] { size_t size = 0; int value = 0; try { value = std::stoi(HCode, &size, 16); } catch (std::invalid_argument) {} HCode.erase(0, size); return value; }; // data_offset hp.offset = ConsumeHexInt(); // [*deref_offset1] if (HCode[0] == L'*') { hp.type |= DATA_INDIRECT; HCode.erase(0, 1); hp.index = ConsumeHexInt(); } // [:split_offset[*deref_offset2]] if (HCode[0] == L':') { hp.type |= USING_SPLIT; HCode.erase(0, 1); hp.split = ConsumeHexInt(); if (HCode[0] == L'*') { hp.type |= SPLIT_INDIRECT; HCode.erase(0, 1); hp.split_index = ConsumeHexInt(); } } if(hp.jittype==JITTYPE::UNITY){ if(HCode[0]!=L'@')return {}; HCode.erase(0,1); HCode=HCode.substr(0,HCode.size()-wcslen(L":JIT:UNITY")); hp.argidx=hp.offset; hp.offset=0; hp.address=0; hp.type &= ~MODULE_OFFSET; hp.type &= ~FUNCTION_OFFSET; strcpy(hp.function,""); wcscpy(hp.module,L""); strcpy(hp.unityfunctioninfo,wcasta(HCode).c_str()); } else{ // @addr[:module[:func]] if (!std::regex_match(HCode, match, std::wregex(L"^@([[:xdigit:]]+)(:.+?)?(:.+)?"))) return {}; hp.address = std::stoull(match[1], nullptr, 16); if (match[2].matched) { hp.type |= MODULE_OFFSET; wcsncpy_s(hp.module, match[2].str().erase(0, 1).c_str(), MAX_MODULE_SIZE - 1); } if (match[3].matched) { hp.type |= FUNCTION_OFFSET; std::wstring func = match[3]; strncpy_s(hp.function, std::string(func.begin(), func.end()).erase(0, 1).c_str(), MAX_MODULE_SIZE - 1); } // ITH has registers offset by 4 vs AGTH: need this to correct if (hp.offset < 0) hp.offset -= 4; if (hp.split < 0) hp.split -= 4; if(hp.jittype!=JITTYPE::PC){ hp.emu_addr=hp.address; hp.argidx=hp.offset; hp.offset=0; hp.address=0; hp.type &= ~MODULE_OFFSET; hp.type &= ~FUNCTION_OFFSET; strcpy(hp.function,""); wcscpy(hp.module,L""); } } return hp; } std::optional ParseECode(std::wstring code) { code.erase(0,1); HookParam hp; hp.type|=EMBED_ABLE; if(code[0]==L'D') { hp.type|=EMBED_DYNA_SJIS; code.erase(0,1); } if(code[0]==L'S') { code.erase(0,1); hp.type|=EMBED_BEFORE_SIMPLE; if(code[0]==L'N') hp.type|=EMBED_AFTER_NEW; else if(code[0]==L'O') hp.type|=EMBED_AFTER_OVERWRITE; else return ParseHCode(code,hp); code.erase(0,1); } return ParseHCode(code,hp); } std::wstring HexString(int64_t num) { if (num < 0) return FormatString(L"-%I64X", -num); return FormatString(L"%I64X", num); } std::wstring GenerateRCode(HookParam hp) { std::wstring RCode = L"R"; if(hp.type&CODEC_UTF16) RCode += L'Q'; else if (hp.type&CODEC_UTF32) RCode += L'U'; else if (hp.type&CODEC_UTF8) RCode += L'V'; else { RCode += L'S'; if (hp.codepage != 0) RCode += std::to_wstring(hp.codepage) + L'#'; } RCode += L'@' + HexString(hp.address); return RCode; } std::wstring GenerateHCode(HookParam hp, DWORD processId) { std::wstring HCode; if(hp.type&EMBED_ABLE) { HCode +=L"E"; if (hp.hook_before || hp.hook_after) HCode += L'X'; else { if(hp.type&EMBED_DYNA_SJIS) HCode+=L"D"; if(hp.type&EMBED_BEFORE_SIMPLE) HCode+=L"S"; if(hp.type&EMBED_AFTER_NEW) HCode+=L"N"; else if(hp.type&EMBED_AFTER_OVERWRITE) HCode+=L"O"; } } if(hp.type&BREAK_POINT) HCode+=L"B"; else HCode += L"H"; if(hp.type & USING_STRING) { if(hp.type&SPECIAL_JIT_STRING) HCode+=L'M'; else if(hp.type&CODEC_UTF16) HCode += L'Q'; else if(hp.type&CODEC_UTF8) HCode += L'V'; else if(hp.type&CODEC_UTF32) HCode += L'U'; else HCode += L'S'; } else { if(hp.type&CODEC_UTF16) HCode += L'W'; else if(hp.type&CODEC_UTF32) HCode += L'I'; else if (hp.type & CODEC_ANSI_BE) HCode += L'A'; else HCode += L'B'; } if (hp.text_fun || hp.filter_fun) HCode += L'X'; if (hp.type & FULL_STRING) HCode += L'F'; if (hp.type & NO_CONTEXT) HCode += L'N'; if (hp.codepage != 0 && !(hp.type & CODEC_UTF8)&&!(hp.type & CODEC_UTF16)&&!(hp.type & CODEC_UTF32) ) HCode += std::to_wstring(hp.codepage) + L'#'; if (hp.padding) HCode += HexString(hp.padding) + L'+'; if (hp.offset < 0) hp.offset += 4; if (hp.split < 0) hp.split += 4; if(hp.jittype==JITTYPE::PC){ HCode += HexString(hp.offset); } else{ HCode += HexString(hp.argidx); } if (hp.type & DATA_INDIRECT) HCode += L'*' + HexString(hp.index); if (hp.type & USING_SPLIT) HCode += L':' + HexString(hp.split); if (hp.type & SPLIT_INDIRECT) HCode += L'*' + HexString(hp.split_index); // Attempt to make the address relative if(hp.jittype==JITTYPE::PC) { if (processId && !(hp.type & MODULE_OFFSET)) if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) if (MEMORY_BASIC_INFORMATION info = {}; VirtualQueryEx(process, (LPCVOID)hp.address, &info, sizeof(info))) if (auto moduleName = GetModuleFilename(processId, (HMODULE)info.AllocationBase)) { hp.type |= MODULE_OFFSET; hp.address -= (uint64_t)info.AllocationBase; wcsncpy_s(hp.module, moduleName->c_str() + moduleName->rfind(L'\\') + 1, MAX_MODULE_SIZE - 1); } HCode += L'@' + HexString(hp.address); if (hp.type & MODULE_OFFSET) HCode += L':' + std::wstring(hp.module); if (hp.type & FUNCTION_OFFSET) HCode += L':' + StringToWideString(hp.function); } else { if(hp.jittype== JITTYPE::UNITY){ HCode+=L'@'; HCode+=acastw(hp.unityfunctioninfo); HCode+=L":JIT:UNITY"; } else{ HCode += L'@' + HexString(hp.emu_addr); switch (hp.jittype) { case JITTYPE::YUZU: HCode+=L":JIT:YUZU"; break; case JITTYPE::PPSSPP: HCode+=L":JIT:PPSSPP"; break; case JITTYPE::VITA3K: HCode+=L":JIT:VITA3K"; break; case JITTYPE::RPCS3: HCode+=L":JIT:RPCS3"; break; } } } return HCode; } } namespace HookCode { std::optional Parse(std::wstring code) { if (code[0] == L'/') code.erase(0, 1); code.erase(std::find(code.begin(), code.end(), L'/'), code.end()); // legacy/AGTH compatibility Trim(code); if (code[0] == L'R') return ParseRCode(code); else if (code[0] == L'E') return ParseECode(code); else return ParseHCode(code); } std::wstring Generate(HookParam hp, DWORD processId) { std::wstring HCode =L""; return HCode+=(hp.type & DIRECT_READ ? GenerateRCode(hp) : GenerateHCode(hp, processId)); } }