mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-27 15:54:03 +08:00
ppsspp
1 1
This commit is contained in:
parent
dbc359e396
commit
476bdbf9b3
@ -3638,13 +3638,6 @@ bool InsertPPSSPPHooks()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ppsspp{
|
|
||||||
std::unordered_map<uintptr_t,emfuncinfo> loademfunctionhooks()
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool PPSSPP::attach_function() {
|
bool PPSSPP::attach_function() {
|
||||||
auto succ=InsertPPSSPPcommonhooks();
|
auto succ=InsertPPSSPPcommonhooks();
|
||||||
|
@ -1,327 +1,6 @@
|
|||||||
#include"PPSSPP.h"
|
#include"PPSSPP.h"
|
||||||
#include"ppsspp/psputils.hpp"
|
#include"ppsspp/psputils.hpp"
|
||||||
#include<queue>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
template<int index,int offset=0>
|
|
||||||
void simple932getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
hp->type=USING_STRING|NO_CONTEXT;
|
|
||||||
hp->codepage=932;
|
|
||||||
auto address= emu_arg(stack)[index]+offset;
|
|
||||||
*data=address;*len=strlen((char*)address);
|
|
||||||
}
|
|
||||||
template<int index>
|
|
||||||
void simpleutf8getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
auto address=emu_arg(stack)[index];
|
|
||||||
hp->type=USING_STRING|CODEC_UTF8|NO_CONTEXT;
|
|
||||||
*data=address;*len=strlen((char*)address);
|
|
||||||
}
|
|
||||||
template<int index,DWORD _type=0>
|
|
||||||
void simpleutf16getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
auto address=emu_arg(stack)[index];
|
|
||||||
hp->type=USING_STRING|CODEC_UTF16|NO_CONTEXT|_type;
|
|
||||||
*data=address;*len=wcslen((wchar_t*)address)*2;
|
|
||||||
}
|
|
||||||
class emu_arg{
|
|
||||||
hook_stack* stack;
|
|
||||||
public:
|
|
||||||
emu_arg(hook_stack* stack_):stack(stack_){};
|
|
||||||
uintptr_t operator [](int idx){
|
|
||||||
auto base=stack->rbx;
|
|
||||||
auto args=stack->r14;
|
|
||||||
auto offR = -0x80;
|
|
||||||
auto offset = offR + 0x10 + idx * 4;
|
|
||||||
return base+*(uint32_t*)(args+offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ULJS00403_filter(void* data, size_t* len, HookParam* hp){
|
|
||||||
std::string result = std::string((char*)data,*len);
|
|
||||||
std::regex newlinePattern(R"((\\n)+)");
|
|
||||||
result = std::regex_replace(result, newlinePattern, " ");
|
|
||||||
std::regex pattern(R"((\\d$|^\@[a-z]+|#.*?#|\$))");
|
|
||||||
result = std::regex_replace(result, pattern, "");
|
|
||||||
return write_string_overwrite(data,len,result);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ULJS00339(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
hp->type=USING_STRING|NO_CONTEXT;
|
|
||||||
hp->codepage=932;
|
|
||||||
auto base=stack->rbx;
|
|
||||||
auto a2= emu_arg(stack)[0];
|
|
||||||
|
|
||||||
auto vm = *(DWORD*)(a2+(0x28));
|
|
||||||
vm=*(DWORD*)(vm+base);
|
|
||||||
vm=*(DWORD*)(vm+base+8);
|
|
||||||
auto address=vm+base;
|
|
||||||
auto len1=*(DWORD*)(address+4);
|
|
||||||
auto p=address+0x20;
|
|
||||||
if(len1>4 && *(WORD*)(p+2)==0){
|
|
||||||
auto p1=*(DWORD*)(address+8);
|
|
||||||
vm=*(DWORD*)(vm+base);
|
|
||||||
vm=*(DWORD*)(vm+base+0xC);
|
|
||||||
p=vm+base;
|
|
||||||
}
|
|
||||||
static int fm=0;
|
|
||||||
static std::string pre;
|
|
||||||
auto b=fm;
|
|
||||||
auto s=[](uintptr_t address){
|
|
||||||
auto frist = *(WORD*)address;
|
|
||||||
auto lo = frist & 0xFF; // uppercase: 41->5A
|
|
||||||
auto hi = frist >> 8;
|
|
||||||
if (hi == 0 && (lo > 0x5a || lo < 0x41) /* T,W,? */) {
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
std::string s ;int i = 0;WORD c;
|
|
||||||
char buf[3]={0};
|
|
||||||
while ((c = *(WORD*)(address+i)) != 0) {
|
|
||||||
// reverse endian: ShiftJIS BE => LE
|
|
||||||
buf[0] = c >> 8;
|
|
||||||
buf[1] = c & 0xFF;
|
|
||||||
|
|
||||||
if (c == 0x815e /* / */) {
|
|
||||||
s += ' '; // single line
|
|
||||||
}
|
|
||||||
else if (buf[0] == 0) {
|
|
||||||
//// UTF16 LE turned BE: 5700=>0057, 3100, 3500
|
|
||||||
//// 4e00 6d00=>PLAYER
|
|
||||||
// do nothing
|
|
||||||
if (buf[1] == 0x4e) {
|
|
||||||
s += "PLAYER";
|
|
||||||
fm++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
s+=buf;
|
|
||||||
}
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}(p);
|
|
||||||
if(b>0){
|
|
||||||
fm--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(s==pre)return ;
|
|
||||||
pre=s;
|
|
||||||
write_string_new(data,len,s);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool NPJH50909_filter(void* data, size_t* len, HookParam* hp){
|
|
||||||
std::string result = std::string((char*)data,*len);
|
|
||||||
|
|
||||||
// Remove single line markers
|
|
||||||
result = std::regex_replace(result, std::regex("(\\%N)+"), " ");
|
|
||||||
|
|
||||||
// Remove scale marker
|
|
||||||
result = std::regex_replace(result, std::regex("\\%\\@\\%\\d+"), "");
|
|
||||||
|
|
||||||
// Reformat name
|
|
||||||
std::smatch match;
|
|
||||||
if (std::regex_search(result, match, std::regex("(^[^「]+)「"))) {
|
|
||||||
std::string name = match[1].str();
|
|
||||||
result = std::regex_replace(result, std::regex("^[^「]+"), "");
|
|
||||||
result = name + "\n" + result;
|
|
||||||
}
|
|
||||||
return write_string_overwrite(data,len,result);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ULJM06119_filter(void* data, size_t* len, HookParam* hp){
|
|
||||||
std::string s = std::string((char*)data,*len);
|
|
||||||
|
|
||||||
std::regex pattern(R"(/\[[^\]]+./g)");
|
|
||||||
s = std::regex_replace(s, pattern, "");
|
|
||||||
|
|
||||||
std::regex tagPattern(R"(/\\k|\\x|%C|%B)");
|
|
||||||
s = std::regex_replace(s, tagPattern, "");
|
|
||||||
|
|
||||||
std::regex colorPattern(R"(/\%\d+\#[0-9a-fA-F]*\;)");
|
|
||||||
s = std::regex_replace(s, colorPattern, "");
|
|
||||||
|
|
||||||
std::regex newlinePattern(R"(/\n+)");
|
|
||||||
s = std::regex_replace(s, newlinePattern, " ");
|
|
||||||
return write_string_overwrite(data,len,s);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ULJM06036_filter(void* data, size_t* len, HookParam* hp){
|
|
||||||
std::wstring result = std::wstring((wchar_t*)data,*len/2);
|
|
||||||
std::wregex pattern(LR"(<R([^\/]+).([^>]+).>)");
|
|
||||||
result = std::regex_replace(result, pattern, L"$2");
|
|
||||||
std::wregex tagPattern(LR"(<[A-Z]+>)");
|
|
||||||
result = std::regex_replace(result, tagPattern, L"");
|
|
||||||
return write_string_overwrite(data,len,result);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Corda{
|
|
||||||
std::string readBinaryString(uintptr_t address,bool* haveName){
|
|
||||||
* haveName=false;
|
|
||||||
if ((*(WORD*)address & 0xF0FF) == 0x801b) {
|
|
||||||
*haveName = true;
|
|
||||||
address = address+2; // (1)
|
|
||||||
}
|
|
||||||
std::string s;int i=0;uint8_t c;
|
|
||||||
while ((c = *(uint8_t*)(address+i)) != 0) {
|
|
||||||
if (c == 0x1b) {
|
|
||||||
if (*haveName)
|
|
||||||
return s; // (1) skip junk after name
|
|
||||||
|
|
||||||
c = *(uint8_t*)(address+(i + 1));
|
|
||||||
if (c == 0x7f)
|
|
||||||
i += 5;
|
|
||||||
else
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
else if (c == 0x0a) {
|
|
||||||
s += '\n';
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
else if (c == 0x20) {
|
|
||||||
s += ' ';
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto len=1+(IsDBCSLeadByteEx(932,*(BYTE*)(address+i)));
|
|
||||||
s += std::string((char*)(address+i),len);
|
|
||||||
i += len;//encoder.encode(c).byteLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ULJM05428(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
hp->type=USING_STRING|NO_CONTEXT;
|
|
||||||
hp->codepage=932;
|
|
||||||
auto address= emu_arg(stack)[1];
|
|
||||||
bool haveNamve;
|
|
||||||
auto s=Corda::readBinaryString(address,&haveNamve);
|
|
||||||
*split=haveNamve;
|
|
||||||
write_string_new(data,len,s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ULJM05054(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
hp->type=USING_STRING|NO_CONTEXT;
|
|
||||||
hp->codepage=932;
|
|
||||||
if (hp->user_value != 0x886162c) {
|
|
||||||
auto addr=emu_arg(stack)[0]+0x3c;
|
|
||||||
*data=addr;*len=strlen((char*)addr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto address= emu_arg(stack)[1];
|
|
||||||
bool haveNamve;
|
|
||||||
auto s=Corda::readBinaryString(address,&haveNamve);
|
|
||||||
*split=haveNamve;
|
|
||||||
write_string_new(data,len,s);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ULJM05943F(void* data, size_t* len, HookParam* hp){
|
|
||||||
auto s = std::string((char*)data,*len);
|
|
||||||
std::regex pattern1("#n+");
|
|
||||||
std::string replacement1 = " ";
|
|
||||||
std::string result1 = std::regex_replace(s, pattern1, replacement1);
|
|
||||||
std::regex pattern2("#[A-Za-z]+\\[(\\d*\\.)?\\d+\\]+");
|
|
||||||
std::string replacement2 = "";
|
|
||||||
std::string result2 = std::regex_replace(result1, pattern2, replacement2);
|
|
||||||
return write_string_overwrite(data,len,result2);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NPJH50619F(void* data, size_t* len, HookParam* hp){
|
|
||||||
auto s = std::string((char*)data,*len);
|
|
||||||
std::regex pattern1("[\\r\\n]+");
|
|
||||||
std::string replacement1 = "";
|
|
||||||
std::string result1 = std::regex_replace(s, pattern1, replacement1);
|
|
||||||
std::regex pattern2("^(.*?)\\)+");
|
|
||||||
std::string replacement2 = "";
|
|
||||||
std::string result2 = std::regex_replace(result1, pattern2, replacement2);
|
|
||||||
std::regex pattern3("#ECL+");
|
|
||||||
std::string replacement3 = "";
|
|
||||||
std::string result3 = std::regex_replace(result2, pattern3, replacement3);
|
|
||||||
std::regex pattern4("(#.+?\\))+");
|
|
||||||
std::string replacement4 = "";
|
|
||||||
std::string result4 = std::regex_replace(result3, pattern4, replacement4);
|
|
||||||
return write_string_overwrite(data,len,result4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool NPJH50505F(void* data, size_t* len, HookParam* hp){
|
|
||||||
auto s = std::string((char*)data,*len);
|
|
||||||
|
|
||||||
std::regex pattern2("#RUBS(#[A-Z0-9]+)*[^#]+");
|
|
||||||
std::string replacement2 = "";
|
|
||||||
std::string result2 = std::regex_replace(s, pattern2, replacement2);
|
|
||||||
|
|
||||||
std::regex pattern3("#FAMILY");
|
|
||||||
std::string replacement3 = "$FAMILY";
|
|
||||||
std::string result3 = std::regex_replace(result2, pattern3, replacement3);
|
|
||||||
|
|
||||||
std::regex pattern4("#GIVE");
|
|
||||||
std::string replacement4 = "$GIVE";
|
|
||||||
std::string result4 = std::regex_replace(result3, pattern4, replacement4);
|
|
||||||
|
|
||||||
std::regex pattern5("(#[A-Z0-9\\-]+)+");
|
|
||||||
std::string replacement5 = "";
|
|
||||||
std::string result5 = std::regex_replace(result4, pattern5, replacement5);
|
|
||||||
|
|
||||||
std::regex pattern6("\\n+");
|
|
||||||
std::string replacement6 = " ";
|
|
||||||
std::string result6 = std::regex_replace(result5, pattern6, replacement6);
|
|
||||||
|
|
||||||
return write_string_overwrite(data,len,result6);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QNPJH50909(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
|
||||||
hp->type=USING_STRING|NO_CONTEXT;
|
|
||||||
hp->codepage=932;
|
|
||||||
auto base=stack->rbx;
|
|
||||||
auto addr = (base+0x08975110);
|
|
||||||
auto adr=addr+0x20;
|
|
||||||
auto len1=*(DWORD*)(addr+0x14)*2;
|
|
||||||
|
|
||||||
*data=adr+len1-2;*len=2;
|
|
||||||
if(0x6e87==*(WORD*)*data)*len=0;
|
|
||||||
if(0x000a==*(WORD*)*data)*len=0;
|
|
||||||
}
|
|
||||||
namespace ppsspp{
|
|
||||||
std::unordered_map<uintptr_t,emfuncinfo> loademfunctionhooks()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
/*
|
|
||||||
0x883b0bc: mainHandler.bind_(null, 2), // a2 - choices (un-formated)
|
|
||||||
0x883cf04: mainHandler.bind_(null, 3), // a3 - choices + nameX2
|
|
||||||
0x883bf34: mainHandler.bind_(null, 1), // a1 - choices + dialogue + nameX2 <----
|
|
||||||
0x8836984: mainHandler.bind_(null, 1), // a1 - dialogue
|
|
||||||
0x883cecc: mainHandler.bind_(null, 3), // a3 - dialogue
|
|
||||||
*/
|
|
||||||
{0x883bf34,{"Shinigami to Shoujo",simple932getter<1>,ULJS00403_filter,L"ULJS00403"}},
|
|
||||||
{0x0886775c,{"Amagami",ULJS00339,0,L"ULJS00339"}},// String.length()
|
|
||||||
{0x8814adc,{"Sekai de Ichiban Dame na Koi",simple932getter<0>,NPJH50909_filter,L"ULJM05878"}},// name + dialouge
|
|
||||||
{0x8850b2c,{"Sekai de Ichiban Dame na Koi",simple932getter<0>,NPJH50909_filter,L"ULJM05878"}},// onscreen toast
|
|
||||||
{0x0891D72C,{"Dunamis15",simpleutf8getter<0>,ULJM06119_filter,L"ULJM06119"}},
|
|
||||||
{0x88506d0,{"Princess Evangile Portable",simpleutf16getter<2>,ULJM06036_filter,L"ULJM06036"}},// [0x88506d0(2)...0x088507C0(?)] // name text text (line doubled)
|
|
||||||
{0x89b59dc,{"Kin'iro no Corda 2f",ULJM05428,0,L"ULJM05428"}},
|
|
||||||
{0x886162c,{"Kin'iro no Corda",ULJM05054,0,L"ULJM05054"}},// dialogue: 0x886162c (x1), 0x889d5fc-0x889d520(a2) fullLine
|
|
||||||
{0x8899e90,{"Kin'iro no Corda",ULJM05054,0,L"ULJM05054"}},// name 0x88da57c, 0x8899ca4 (x0, oneTime), 0x8899e90
|
|
||||||
{0x8952cfc,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//dialog
|
|
||||||
{0x884aad4,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//description
|
|
||||||
{0x882e1b0,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//system
|
|
||||||
{0x88bb108,{"Sol Trigger",simpleutf8getter<2>,NPJH50619F,L"NPJH50619"}},//battle tutorial
|
|
||||||
{0x89526a0,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//battle info
|
|
||||||
{0x88bcef8,{"Sol Trigger",simpleutf8getter<1>,NPJH50619F,L"NPJH50619"}},//battle talk
|
|
||||||
{0x8958490,{"Fate/EXTRA CCC",simple932getter<0>,NPJH50505F,L"NPJH50505"}},
|
|
||||||
{0x088630f8,{"Kamigami no Asobi InFinite",QNPJH50909,0,L"NPJH50909"}}, // text, choice (debounce trailing 400ms), TODO: better hook
|
|
||||||
{0x0887813c,{"Kamigami no Asobi InFinite",simple932getter<3,4>,0,L"NPJH50909"}}, // Question YN
|
|
||||||
|
|
||||||
{0x88eeba4,{"Gekka Ryouran Romance",simple932getter<0,0>,ULJM05943F,L"ULJM05943"}},// a0 - monologue text
|
|
||||||
{0x8875e0c,{"Gekka Ryouran Romance",simple932getter<1,6>,ULJM05943F,L"ULJM05943"}},// a1 - dialogue text
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool PPSSPP::attach_function()
|
bool PPSSPP::attach_function()
|
||||||
{
|
{
|
||||||
return InsertPPSSPPcommonhooks();
|
return InsertPPSSPPcommonhooks();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include"engine.h"
|
#include"engine.h"
|
||||||
#include"util/util.h"
|
#include"util/util.h"
|
||||||
#include"psputils.hpp"
|
#include"psputils.hpp"
|
||||||
|
#include"specialgames.hpp"
|
||||||
// See: https://github.com/hrydgard/ppsspp
|
// See: https://github.com/hrydgard/ppsspp
|
||||||
|
|
||||||
// Core/HLE (High Level Emulator)
|
// Core/HLE (High Level Emulator)
|
||||||
@ -275,7 +276,6 @@ bool checkiscurrentgame(const emfuncinfo& em){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::unordered_set<uintptr_t>breakpoints;
|
std::unordered_set<uintptr_t>breakpoints;
|
||||||
std::unordered_map<uintptr_t,emfuncinfo>emfunctionhooks=loademfunctionhooks();
|
|
||||||
|
|
||||||
bool hookPPSSPPDoJit(){
|
bool hookPPSSPPDoJit(){
|
||||||
auto DoJitPtr=getDoJitAddress();
|
auto DoJitPtr=getDoJitAddress();
|
||||||
@ -285,7 +285,7 @@ bool hookPPSSPPDoJit(){
|
|||||||
ConsoleOutput("DoJitPtr %p",DoJitPtr);
|
ConsoleOutput("DoJitPtr %p",DoJitPtr);
|
||||||
|
|
||||||
hp.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
hp.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
auto em_address=stack->ARG2;
|
auto em_address=stack->THISCALLARG1;
|
||||||
|
|
||||||
if(emfunctionhooks.find(em_address)==emfunctionhooks.end())return;
|
if(emfunctionhooks.find(em_address)==emfunctionhooks.end())return;
|
||||||
|
|
||||||
@ -303,7 +303,23 @@ bool hookPPSSPPDoJit(){
|
|||||||
|
|
||||||
auto em_address=hp->user_value;
|
auto em_address=hp->user_value;
|
||||||
auto op=emfunctionhooks.at(em_address);
|
auto op=emfunctionhooks.at(em_address);
|
||||||
|
ConsoleOutput("jit function addr %p",ret);
|
||||||
|
#ifndef _WIN64
|
||||||
|
BYTE sig[]={
|
||||||
|
0x8b,XX2,//mov reg,[ebp-off]
|
||||||
|
0x8b,0xc6,//mov eax,esi
|
||||||
|
0x25,0xff,0xff,0xff,0x3f,//and eax,0x3fffffff
|
||||||
|
0x89,XX,XX4,//mov [eax+base+off],reg
|
||||||
|
|
||||||
|
};
|
||||||
|
auto findbase=MemDbg::findBytes(sig,sizeof(sig),ret,ret+0x20);
|
||||||
|
if(!findbase)
|
||||||
|
findbase=MemDbg::findBytes(sig,sizeof(sig),ret-0x1000,ret+0x1000);
|
||||||
|
if(!findbase)
|
||||||
|
ConsoleOutput("can't find emu_baseaddr");
|
||||||
|
ppsspp::x86_baseaddr=(*(DWORD*)(findbase+12))&0xffff0000;
|
||||||
|
ConsoleOutput("x86 base addr %p",x86_baseaddr);
|
||||||
|
#endif
|
||||||
HookParam hpinternal;
|
HookParam hpinternal;
|
||||||
hpinternal.address=ret;
|
hpinternal.address=ret;
|
||||||
hpinternal.user_value=em_address;
|
hpinternal.user_value=em_address;
|
||||||
|
@ -9,7 +9,6 @@ namespace ppsspp
|
|||||||
void* filterfun;
|
void* filterfun;
|
||||||
const wchar_t* _id;
|
const wchar_t* _id;
|
||||||
};
|
};
|
||||||
std::unordered_map<uintptr_t,emfuncinfo> loademfunctionhooks();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
341
LunaHook/engines/ppsspp/specialgames.hpp
Normal file
341
LunaHook/engines/ppsspp/specialgames.hpp
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
#include<queue>
|
||||||
|
namespace ppsspp{
|
||||||
|
inline DWORD x86_baseaddr;
|
||||||
|
}
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
template<int index,int offset=0>
|
||||||
|
void simple932getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
hp->type=USING_STRING|NO_CONTEXT;
|
||||||
|
hp->codepage=932;
|
||||||
|
auto address= emu_arg(stack)[index]+offset;
|
||||||
|
*data=address;*len=strlen((char*)address);
|
||||||
|
}
|
||||||
|
template<int index>
|
||||||
|
void simpleutf8getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
auto address=emu_arg(stack)[index];
|
||||||
|
hp->type=USING_STRING|CODEC_UTF8|NO_CONTEXT;
|
||||||
|
*data=address;*len=strlen((char*)address);
|
||||||
|
}
|
||||||
|
template<int index,DWORD _type=0>
|
||||||
|
void simpleutf16getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
auto address=emu_arg(stack)[index];
|
||||||
|
hp->type=USING_STRING|CODEC_UTF16|NO_CONTEXT|_type;
|
||||||
|
*data=address;*len=wcslen((wchar_t*)address)*2;
|
||||||
|
}
|
||||||
|
class emu_addr{
|
||||||
|
hook_stack* stack;
|
||||||
|
DWORD addr;
|
||||||
|
public:
|
||||||
|
emu_addr(hook_stack* stack_,DWORD addr_):stack(stack_),addr(addr_){};
|
||||||
|
operator uintptr_t(){
|
||||||
|
#ifndef _WIN64
|
||||||
|
auto base=ppsspp::x86_baseaddr;
|
||||||
|
#else
|
||||||
|
auto base=stack->rbx;
|
||||||
|
#endif
|
||||||
|
return base+addr;
|
||||||
|
}
|
||||||
|
operator DWORD*(){
|
||||||
|
return (DWORD*)(uintptr_t)*this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class emu_arg{
|
||||||
|
hook_stack* stack;
|
||||||
|
public:
|
||||||
|
|
||||||
|
emu_arg(hook_stack* stack_):stack(stack_){};
|
||||||
|
uintptr_t operator [](int idx){
|
||||||
|
#ifndef _WIN64
|
||||||
|
auto args=stack->ebp;
|
||||||
|
#else
|
||||||
|
auto args=stack->r14;
|
||||||
|
#endif
|
||||||
|
auto offR = -0x80;
|
||||||
|
auto offset = offR + 0x10 + idx * 4;
|
||||||
|
return (uintptr_t)emu_addr(stack,*(uint32_t*)(args+offset));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ULJS00403_filter(void* data, size_t* len, HookParam* hp){
|
||||||
|
std::string result = std::string((char*)data,*len);
|
||||||
|
std::regex newlinePattern(R"((\\n)+)");
|
||||||
|
result = std::regex_replace(result, newlinePattern, " ");
|
||||||
|
std::regex pattern(R"((\\d$|^\@[a-z]+|#.*?#|\$))");
|
||||||
|
result = std::regex_replace(result, pattern, "");
|
||||||
|
return write_string_overwrite(data,len,result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ULJS00339(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
hp->type=USING_STRING|NO_CONTEXT;
|
||||||
|
hp->codepage=932;
|
||||||
|
auto a2= emu_arg(stack)[0];
|
||||||
|
|
||||||
|
auto vm = *(DWORD*)(a2+(0x28));
|
||||||
|
vm=*(DWORD*)emu_addr(stack,vm);
|
||||||
|
vm=*(DWORD*)emu_addr(stack,vm+8);
|
||||||
|
uintptr_t address=emu_addr(stack,vm);
|
||||||
|
auto len1=*(DWORD*)(address+4);
|
||||||
|
auto p=address+0x20;
|
||||||
|
if(len1>4 && *(WORD*)(p+2)==0){
|
||||||
|
auto p1=*(DWORD*)(address+8);
|
||||||
|
vm=*(DWORD*)emu_addr(stack,vm);
|
||||||
|
vm=*(DWORD*)emu_addr(stack,vm+0xC);
|
||||||
|
p=emu_addr(stack,vm);
|
||||||
|
}
|
||||||
|
static int fm=0;
|
||||||
|
static std::string pre;
|
||||||
|
auto b=fm;
|
||||||
|
auto s=[](uintptr_t address){
|
||||||
|
auto frist = *(WORD*)address;
|
||||||
|
auto lo = frist & 0xFF; // uppercase: 41->5A
|
||||||
|
auto hi = frist >> 8;
|
||||||
|
if (hi == 0 && (lo > 0x5a || lo < 0x41) /* T,W,? */) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
std::string s ;int i = 0;WORD c;
|
||||||
|
char buf[3]={0};
|
||||||
|
while ((c = *(WORD*)(address+i)) != 0) {
|
||||||
|
// reverse endian: ShiftJIS BE => LE
|
||||||
|
buf[0] = c >> 8;
|
||||||
|
buf[1] = c & 0xFF;
|
||||||
|
|
||||||
|
if (c == 0x815e /* / */) {
|
||||||
|
s += ' '; // single line
|
||||||
|
}
|
||||||
|
else if (buf[0] == 0) {
|
||||||
|
//// UTF16 LE turned BE: 5700=>0057, 3100, 3500
|
||||||
|
//// 4e00 6d00=>PLAYER
|
||||||
|
// do nothing
|
||||||
|
if (buf[1] == 0x4e) {
|
||||||
|
s += "PLAYER";
|
||||||
|
fm++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s+=buf;
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}(p);
|
||||||
|
if(b>0){
|
||||||
|
fm--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(s==pre)return ;
|
||||||
|
pre=s;
|
||||||
|
write_string_new(data,len,s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool NPJH50909_filter(void* data, size_t* len, HookParam* hp){
|
||||||
|
std::string result = std::string((char*)data,*len);
|
||||||
|
|
||||||
|
// Remove single line markers
|
||||||
|
result = std::regex_replace(result, std::regex("(\\%N)+"), " ");
|
||||||
|
|
||||||
|
// Remove scale marker
|
||||||
|
result = std::regex_replace(result, std::regex("\\%\\@\\%\\d+"), "");
|
||||||
|
|
||||||
|
// Reformat name
|
||||||
|
std::smatch match;
|
||||||
|
if (std::regex_search(result, match, std::regex("(^[^「]+)「"))) {
|
||||||
|
std::string name = match[1].str();
|
||||||
|
result = std::regex_replace(result, std::regex("^[^「]+"), "");
|
||||||
|
result = name + "\n" + result;
|
||||||
|
}
|
||||||
|
return write_string_overwrite(data,len,result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ULJM06119_filter(void* data, size_t* len, HookParam* hp){
|
||||||
|
std::string s = std::string((char*)data,*len);
|
||||||
|
|
||||||
|
std::regex pattern(R"(/\[[^\]]+./g)");
|
||||||
|
s = std::regex_replace(s, pattern, "");
|
||||||
|
|
||||||
|
std::regex tagPattern(R"(/\\k|\\x|%C|%B)");
|
||||||
|
s = std::regex_replace(s, tagPattern, "");
|
||||||
|
|
||||||
|
std::regex colorPattern(R"(/\%\d+\#[0-9a-fA-F]*\;)");
|
||||||
|
s = std::regex_replace(s, colorPattern, "");
|
||||||
|
|
||||||
|
std::regex newlinePattern(R"(/\n+)");
|
||||||
|
s = std::regex_replace(s, newlinePattern, " ");
|
||||||
|
return write_string_overwrite(data,len,s);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ULJM06036_filter(void* data, size_t* len, HookParam* hp){
|
||||||
|
std::wstring result = std::wstring((wchar_t*)data,*len/2);
|
||||||
|
std::wregex pattern(LR"(<R([^\/]+).([^>]+).>)");
|
||||||
|
result = std::regex_replace(result, pattern, L"$2");
|
||||||
|
std::wregex tagPattern(LR"(<[A-Z]+>)");
|
||||||
|
result = std::regex_replace(result, tagPattern, L"");
|
||||||
|
return write_string_overwrite(data,len,result);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Corda{
|
||||||
|
std::string readBinaryString(uintptr_t address,bool* haveName){
|
||||||
|
* haveName=false;
|
||||||
|
if ((*(WORD*)address & 0xF0FF) == 0x801b) {
|
||||||
|
*haveName = true;
|
||||||
|
address = address+2; // (1)
|
||||||
|
}
|
||||||
|
std::string s;int i=0;uint8_t c;
|
||||||
|
while ((c = *(uint8_t*)(address+i)) != 0) {
|
||||||
|
if (c == 0x1b) {
|
||||||
|
if (*haveName)
|
||||||
|
return s; // (1) skip junk after name
|
||||||
|
|
||||||
|
c = *(uint8_t*)(address+(i + 1));
|
||||||
|
if (c == 0x7f)
|
||||||
|
i += 5;
|
||||||
|
else
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if (c == 0x0a) {
|
||||||
|
s += '\n';
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else if (c == 0x20) {
|
||||||
|
s += ' ';
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto len=1+(IsDBCSLeadByteEx(932,*(BYTE*)(address+i)));
|
||||||
|
s += std::string((char*)(address+i),len);
|
||||||
|
i += len;//encoder.encode(c).byteLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ULJM05428(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
hp->type=USING_STRING|NO_CONTEXT;
|
||||||
|
hp->codepage=932;
|
||||||
|
auto address= emu_arg(stack)[1];
|
||||||
|
bool haveNamve;
|
||||||
|
auto s=Corda::readBinaryString(address,&haveNamve);
|
||||||
|
*split=haveNamve;
|
||||||
|
write_string_new(data,len,s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ULJM05054(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
hp->type=USING_STRING|NO_CONTEXT;
|
||||||
|
hp->codepage=932;
|
||||||
|
if (hp->user_value != 0x886162c) {
|
||||||
|
auto addr=emu_arg(stack)[0]+0x3c;
|
||||||
|
*data=addr;*len=strlen((char*)addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto address= emu_arg(stack)[1];
|
||||||
|
bool haveNamve;
|
||||||
|
auto s=Corda::readBinaryString(address,&haveNamve);
|
||||||
|
*split=haveNamve;
|
||||||
|
write_string_new(data,len,s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ULJM05943F(void* data, size_t* len, HookParam* hp){
|
||||||
|
auto s = std::string((char*)data,*len);
|
||||||
|
std::regex pattern1("#n+");
|
||||||
|
std::string replacement1 = " ";
|
||||||
|
std::string result1 = std::regex_replace(s, pattern1, replacement1);
|
||||||
|
std::regex pattern2("#[A-Za-z]+\\[(\\d*\\.)?\\d+\\]+");
|
||||||
|
std::string replacement2 = "";
|
||||||
|
std::string result2 = std::regex_replace(result1, pattern2, replacement2);
|
||||||
|
return write_string_overwrite(data,len,result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NPJH50619F(void* data, size_t* len, HookParam* hp){
|
||||||
|
auto s = std::string((char*)data,*len);
|
||||||
|
std::regex pattern1("[\\r\\n]+");
|
||||||
|
std::string replacement1 = "";
|
||||||
|
std::string result1 = std::regex_replace(s, pattern1, replacement1);
|
||||||
|
std::regex pattern2("^(.*?)\\)+");
|
||||||
|
std::string replacement2 = "";
|
||||||
|
std::string result2 = std::regex_replace(result1, pattern2, replacement2);
|
||||||
|
std::regex pattern3("#ECL+");
|
||||||
|
std::string replacement3 = "";
|
||||||
|
std::string result3 = std::regex_replace(result2, pattern3, replacement3);
|
||||||
|
std::regex pattern4("(#.+?\\))+");
|
||||||
|
std::string replacement4 = "";
|
||||||
|
std::string result4 = std::regex_replace(result3, pattern4, replacement4);
|
||||||
|
return write_string_overwrite(data,len,result4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool NPJH50505F(void* data, size_t* len, HookParam* hp){
|
||||||
|
auto s = std::string((char*)data,*len);
|
||||||
|
|
||||||
|
std::regex pattern2("#RUBS(#[A-Z0-9]+)*[^#]+");
|
||||||
|
std::string replacement2 = "";
|
||||||
|
std::string result2 = std::regex_replace(s, pattern2, replacement2);
|
||||||
|
|
||||||
|
std::regex pattern3("#FAMILY");
|
||||||
|
std::string replacement3 = "$FAMILY";
|
||||||
|
std::string result3 = std::regex_replace(result2, pattern3, replacement3);
|
||||||
|
|
||||||
|
std::regex pattern4("#GIVE");
|
||||||
|
std::string replacement4 = "$GIVE";
|
||||||
|
std::string result4 = std::regex_replace(result3, pattern4, replacement4);
|
||||||
|
|
||||||
|
std::regex pattern5("(#[A-Z0-9\\-]+)+");
|
||||||
|
std::string replacement5 = "";
|
||||||
|
std::string result5 = std::regex_replace(result4, pattern5, replacement5);
|
||||||
|
|
||||||
|
std::regex pattern6("\\n+");
|
||||||
|
std::string replacement6 = " ";
|
||||||
|
std::string result6 = std::regex_replace(result5, pattern6, replacement6);
|
||||||
|
|
||||||
|
return write_string_overwrite(data,len,result6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QNPJH50909(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||||||
|
hp->type=USING_STRING|NO_CONTEXT;
|
||||||
|
hp->codepage=932;
|
||||||
|
uintptr_t addr = emu_addr(stack,0x08975110);
|
||||||
|
auto adr=addr+0x20;
|
||||||
|
auto len1=*(DWORD*)(addr+0x14)*2;
|
||||||
|
|
||||||
|
*data=adr+len1-2;*len=2;
|
||||||
|
if(0x6e87==*(WORD*)*data)*len=0;
|
||||||
|
if(0x000a==*(WORD*)*data)*len=0;
|
||||||
|
}
|
||||||
|
namespace ppsspp{
|
||||||
|
std::unordered_map<uintptr_t,emfuncinfo>emfunctionhooks= {
|
||||||
|
/*
|
||||||
|
0x883b0bc: mainHandler.bind_(null, 2), // a2 - choices (un-formated)
|
||||||
|
0x883cf04: mainHandler.bind_(null, 3), // a3 - choices + nameX2
|
||||||
|
0x883bf34: mainHandler.bind_(null, 1), // a1 - choices + dialogue + nameX2 <----
|
||||||
|
0x8836984: mainHandler.bind_(null, 1), // a1 - dialogue
|
||||||
|
0x883cecc: mainHandler.bind_(null, 3), // a3 - dialogue
|
||||||
|
*/
|
||||||
|
{0x883bf34,{"Shinigami to Shoujo",simple932getter<1>,ULJS00403_filter,L"ULJS00403"}},
|
||||||
|
{0x0886775c,{"Amagami",ULJS00339,0,L"ULJS00339"}},// String.length()
|
||||||
|
{0x8814adc,{"Sekai de Ichiban Dame na Koi",simple932getter<0>,NPJH50909_filter,L"ULJM05878"}},// name + dialouge
|
||||||
|
{0x8850b2c,{"Sekai de Ichiban Dame na Koi",simple932getter<0>,NPJH50909_filter,L"ULJM05878"}},// onscreen toast
|
||||||
|
{0x0891D72C,{"Dunamis15",simpleutf8getter<0>,ULJM06119_filter,L"ULJM06119"}},
|
||||||
|
{0x88506d0,{"Princess Evangile Portable",simpleutf16getter<2>,ULJM06036_filter,L"ULJM06036"}},// [0x88506d0(2)...0x088507C0(?)] // name text text (line doubled)
|
||||||
|
{0x89b59dc,{"Kin'iro no Corda 2f",ULJM05428,0,L"ULJM05428"}},
|
||||||
|
{0x886162c,{"Kin'iro no Corda",ULJM05054,0,L"ULJM05054"}},// dialogue: 0x886162c (x1), 0x889d5fc-0x889d520(a2) fullLine
|
||||||
|
{0x8899e90,{"Kin'iro no Corda",ULJM05054,0,L"ULJM05054"}},// name 0x88da57c, 0x8899ca4 (x0, oneTime), 0x8899e90
|
||||||
|
{0x8952cfc,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//dialog
|
||||||
|
{0x884aad4,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//description
|
||||||
|
{0x882e1b0,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//system
|
||||||
|
{0x88bb108,{"Sol Trigger",simpleutf8getter<2>,NPJH50619F,L"NPJH50619"}},//battle tutorial
|
||||||
|
{0x89526a0,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//battle info
|
||||||
|
{0x88bcef8,{"Sol Trigger",simpleutf8getter<1>,NPJH50619F,L"NPJH50619"}},//battle talk
|
||||||
|
{0x8958490,{"Fate/EXTRA CCC",simple932getter<0>,NPJH50505F,L"NPJH50505"}},
|
||||||
|
{0x088630f8,{"Kamigami no Asobi InFinite",QNPJH50909,0,L"NPJH50909"}}, // text, choice (debounce trailing 400ms), TODO: better hook
|
||||||
|
{0x0887813c,{"Kamigami no Asobi InFinite",simple932getter<3,4>,0,L"NPJH50909"}}, // Question YN
|
||||||
|
|
||||||
|
{0x88eeba4,{"Gekka Ryouran Romance",simple932getter<0,0>,ULJM05943F,L"ULJM05943"}},// a0 - monologue text
|
||||||
|
{0x8875e0c,{"Gekka Ryouran Romance",simple932getter<1,6>,ULJM05943F,L"ULJM05943"}},// a1 - dialogue text
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -90,6 +90,7 @@ inline uintptr_t regof(regs reg,hook_stack* stack){
|
|||||||
#define RETADDR eax
|
#define RETADDR eax
|
||||||
#define THISCALL __thiscall
|
#define THISCALL __thiscall
|
||||||
#define THISCALLTHIS ecx
|
#define THISCALLTHIS ecx
|
||||||
|
#define THISCALLARG1 stack[1]
|
||||||
#define GETARG1 get_stack(1)
|
#define GETARG1 get_stack(1)
|
||||||
#define GETARG2 get_stack(2)
|
#define GETARG2 get_stack(2)
|
||||||
#define GETARG3 get_stack(3)
|
#define GETARG3 get_stack(3)
|
||||||
@ -100,6 +101,7 @@ inline uintptr_t regof(regs reg,hook_stack* stack){
|
|||||||
#define ARG3 r8
|
#define ARG3 r8
|
||||||
#define RETADDR rax
|
#define RETADDR rax
|
||||||
#define THISCALLTHIS rcx
|
#define THISCALLTHIS rcx
|
||||||
|
#define THISCALLARG1 rdx
|
||||||
#define THISCALL
|
#define THISCALL
|
||||||
#define GETARG1 get_reg(regs::rcx)
|
#define GETARG1 get_reg(regs::rcx)
|
||||||
#define GETARG2 get_reg(regs::rdx)
|
#define GETARG2 get_reg(regs::rdx)
|
||||||
|
Loading…
Reference in New Issue
Block a user