Update CatSystem.cpp

This commit is contained in:
恍兮惚兮 2024-08-30 04:51:31 +08:00
parent 5fa33f740c
commit 067b0c288b

View File

@ -1,4 +1,4 @@
#include"CatSystem.h"
#include "CatSystem.h"
// jichi 5/10/2014
// See also: http://bbs.sumisora.org/read.php?tid=11044704&fpage=2
//
@ -133,9 +133,9 @@
// 051ae554 042c4c20
// 051ae558 0000002c
static void SpecialHookCatSystem3(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
static void SpecialHookCatSystem3(hook_stack *stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
{
//DWORD ch = *data = *(DWORD *)(esp_base + hp->offset); // arg2
// DWORD ch = *data = *(DWORD *)(esp_base + hp->offset); // arg2
DWORD ch = *data = stack->stack[2];
*len = LeadByteTable[(ch >> 8) & 0xff]; // CODEC_ANSI_BE
*split = stack->edx >> 16;
@ -143,43 +143,53 @@ static void SpecialHookCatSystem3(hook_stack* stack, HookParam *, uintptr_t *da
bool InsertCatSystemHook()
{
//DWORD search=0x95EB60F;
//DWORD j,i=SearchPattern(processStartAddress,processStopAddress-processStartAddress,&search,4);
//if (i==0) return;
//i+=processStartAddress;
//for (j=i-0x100;i>j;i--)
// DWORD search=0x95EB60F;
// DWORD j,i=SearchPattern(processStartAddress,processStopAddress-processStartAddress,&search,4);
// if (i==0) return;
// i+=processStartAddress;
// for (j=i-0x100;i>j;i--)
// if (*(DWORD*)i==0xcccccccc) break;
//if (i==j) return;
//hp.address=i+4;
//hp.offset=get_reg(regs::eax);
//hp.index=4;
//hp.type =CODEC_ANSI_BE|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
//hp.length_offset=1;
// if (i==j) return;
// hp.address=i+4;
// hp.offset=get_reg(regs::eax);
// hp.index=4;
// hp.type =CODEC_ANSI_BE|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
// hp.length_offset=1;
enum { beg = 0xff6acccc }; // jichi 7/12/2014: beginning of the function
enum { addr_offset = 2 }; // skip two leading 0xcc
enum
{
beg = 0xff6acccc
}; // jichi 7/12/2014: beginning of the function
enum
{
addr_offset = 2
}; // skip two leading 0xcc
ULONG addr = MemDbg::findCallerAddress((ULONG)::GetTextMetricsA, beg, processStartAddress, processStopAddress);
if (!addr) {
if (!addr)
{
ConsoleOutput("CatSystem2: pattern not exist");
return false;
}
HookParam hp;
hp.address = addr + addr_offset; // skip 1 push?
hp.offset=get_stack(2); // text character is in arg2
hp.offset = get_stack(2); // text character is in arg2
// jichi 12/23/2014: Modify split for new catsystem
bool newEngine = Util::CheckFile(L"cs2conf.dll");
if (newEngine) {
//hp.text_fun = SpecialHookCatSystem3; // type not needed
//NewHook(hp, "CatSystem3");
//ConsoleOutput("INSERT CatSystem3");
hp.type = CODEC_ANSI_BE|USING_SPLIT;
if (newEngine)
{
// hp.text_fun = SpecialHookCatSystem3; // type not needed
// NewHook(hp, "CatSystem3");
// ConsoleOutput("INSERT CatSystem3");
hp.type = CODEC_ANSI_BE | USING_SPLIT;
hp.split = get_reg(regs::esi);
ConsoleOutput("INSERT CatSystem3new");
return NewHook(hp, "CatSystem3new");
} else {
hp.type = CODEC_ANSI_BE|USING_SPLIT;
}
else
{
hp.type = CODEC_ANSI_BE | USING_SPLIT;
hp.split = get_reg(regs::edx);
ConsoleOutput("INSERT CatSystem2");
return NewHook(hp, "CatSystem2");
@ -205,23 +215,30 @@ bool InsertCatSystem2Hook()
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("CatSystem2new: pattern not found");
if (!addr)
{
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.type = USING_STRING|CODEC_UTF8;
ConsoleOutput("INSERT CatSystem2new");
hp.offset = get_reg(regs::eax);
hp.type = USING_STRING | CODEC_UTF8;
hp.filter_fun = [](void *data, size_t *len, HookParam *hp)
{
static std::regex rx(R"(\[(.+?)/.+\])");
auto _ = std::regex_replace(std::string((char *)data, *len), rx, "$1");
return write_string_overwrite(data, len, _);
};
return NewHook(hp, "CatSystem2new");
}
namespace { // unnamed
namespace Patch {
namespace
{ // unnamed
namespace Patch
{
namespace Private {
namespace Private
{
// String in ecx
// bool __fastcall isLeadByteChar(const char *s, DWORD edx)
// bool isLeadByteChar(hook_stack*s,void* data, size_t* len,uintptr_t*role)
@ -234,17 +251,17 @@ namespace Private {
// //return dynsjis::isleadstr(s); // no idea why this will cause Grisaia3 to hang
// //return ::IsDBCSLeadByte(HIBYTE(testChar));
// }
bool isLeadByteChar(char* s)
bool isLeadByteChar(char *s)
{
return s && dynsjis::isleadchar(*s);
//return dynsjis::isleadstr(s); // no idea why this will cause Grisaia3 to hang
//return ::IsDBCSLeadByte(HIBYTE(testChar));
// return dynsjis::isleadstr(s); // no idea why this will cause Grisaia3 to hang
// return ::IsDBCSLeadByte(HIBYTE(testChar));
}
} // namespace Private
} // namespace Private
/**
/**
* Sample game:
*
* This function is found by searching the following instruction:
@ -362,8 +379,8 @@ namespace Private {
* 004148C8 CC INT3
*/
ULONG patchEncoding(ULONG startAddress, ULONG stopAddress)
{
ULONG patchEncoding(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x74, 0x29, // 00511c8c 74 29 je short .00511cb7
0x3c, 0x81 // 00511c8e 3c 81 cmp al,0x81
@ -375,14 +392,14 @@ ULONG patchEncoding(ULONG startAddress, ULONG stopAddress)
if (!addr)
return false;
for (auto p = addr; p - addr < 20; p += ::disasm((LPCVOID)p))
if (*(WORD *)p == 0xc985)// 00414890 85C9 TEST ECX,ECX ; jichi: text in ecx
return addr;//winhook::replace_fun(p, (ULONG)Private::isLeadByteChar);
if (*(WORD *)p == 0xc985) // 00414890 85C9 TEST ECX,ECX ; jichi: text in ecx
return addr; // winhook::replace_fun(p, (ULONG)Private::isLeadByteChar);
return 0;
}
}
} // namespace Patch
} // namespace Patch
/**
/**
* Sample game:
*
* Example prefix to skip:
@ -407,17 +424,19 @@ ULONG patchEncoding(ULONG startAddress, ULONG stopAddress)
*
* 1 select_go_tar
*/
template <typename strT>
strT ltrim(strT text)
{
template <typename strT>
strT ltrim(strT text)
{
strT lastText = nullptr;
while (*text && text != lastText) {
while (*text && text != lastText)
{
lastText = text;
if (text[0] == 0x20)
text++;
if ((UINT8)text[0] == 0x81 && (UINT8)text[1] == 0x40) // skip space \u3000 (0x8140 in sjis)
text += 2;
if (text[0] == '\\') {
if (text[0] == '\\')
{
text++;
while (::islower(text[0]) || text[0] == '@')
text++;
@ -426,19 +445,21 @@ strT ltrim(strT text)
while ((signed char)text[0] > 0 && text[0] != '[') // skip all leading ascii characters except "[" needed for ruby
text++;
return text;
}
}
// Remove trailing '\@'
size_t rtrim(LPCSTR text)
{
// Remove trailing '\@'
size_t rtrim(LPCSTR text)
{
size_t size = ::strlen(text);
while (size >= 2 && text[size - 2] == '\\' && (UINT8)text[size - 1] <= 127)
size -= 2;
return size;
}
}
namespace ScenarioHook {
namespace Private {
namespace ScenarioHook
{
namespace Private
{
bool isOtherText(LPCSTR text)
{
@ -460,48 +481,63 @@ namespace Private {
* 03283AC8 00 00 00 00 48 80 4F 03 00 00 00 00 CC CC CC CC ....H€O....
* 03283AD8 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
*/
//struct ClassArgument // for ecx
// struct ClassArgument // for ecx
//{
// DWORD unknown[7],
// split1, // 0x20 - 9
// split2; // 0x20
// // split1 - split2 is always 0x94
// DWORD split() const { return split1 - split2; } //
//};
// };
static bool containsNamePunct_(const char *text)
{
static bool containsNamePunct_(const char *text)
{
static const char *puncts[] = {
"\x81\x41" /* 、 */
, "\x81\x43" /* */
, "\x81\x42" /* 。 */
,
"\x81\x43" /* */
,
"\x81\x42" /* 。 */
//, "\x81\x48" /* */
, "\x81\x49" /* */
, "\x81\x63" /* … */
, "\x81\x64" /* ‥ */
,
"\x81\x49" /* */
,
"\x81\x63" /* … */
,
"\x81\x64" /* ‥ */
//, "\x81\x79" /* 【 */
//, "\x81\x7a" /* 】 */
, "\x81\x75" /* 「 */
, "\x81\x76" /* 」 */
, "\x81\x77" /* 『 */
, "\x81\x78" /* 』 */
,
"\x81\x75" /* 「 */
,
"\x81\x76" /* 」 */
,
"\x81\x77" /* 『 */
,
"\x81\x78" /* 』 */
//, "\x81\x69" /* */
//, "\x81\x6a" /* */
//, "\x81\x6f" /* */
//, "\x81\x70" /* */
//, "\x81\x71" /* 〈 */
//, "\x81\x72" /* 〉 */
, "\x81\x6d" /* */
, "\x81\x6e" /* */
,
"\x81\x6d" /* */
,
"\x81\x6e" /* */
//, "\x81\x83", /* */
//, "\x81\x84", /* */
, "\x81\x65" /* */
, "\x81\x66" /* */
, "\x81\x67" /* “ */
, "\x81\x68" /* ” */
,
"\x81\x65" /* */
,
"\x81\x66" /* */
,
"\x81\x67" /* “ */
,
"\x81\x68" /* ” */
};
for (size_t i = 0; i < sizeof(puncts)/sizeof(*puncts); i++)
for (size_t i = 0; i < sizeof(puncts) / sizeof(*puncts); i++)
if (::strstr(text, puncts[i]))
return true;
@ -509,43 +545,47 @@ static bool containsNamePunct_(const char *text)
&& !::strstr(text, "\x81\x48\x81\x48\x81\x48")) /* */
return true;
return false;
}
}
bool guessIsNameText(const char *text, size_t size)
{
enum { MaximumNameSize = 0x10 };
{
enum
{
MaximumNameSize = 0x10
};
if (!size)
size = ::strlen(text);
return size < MaximumNameSize && !containsNamePunct_(text);
}
LPSTR trimmedText;size_t trimmedSize;
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
}
LPSTR trimmedText;
size_t trimmedSize;
bool hookBefore(hook_stack *s, void *data, size_t *len, uintptr_t *role)
{
//static std::unordered_set<uint64_t> hashes_;
// static std::unordered_set<uint64_t> hashes_;
auto text = (LPSTR)s->eax; // arg1
if (!text || !*text || all_ascii(text))
return false;
// Alternatively, if do not skip ascii chars, edx is always 0x4ef74 for Japanese texts
//if (s->edx != 0x4ef74)
// if (s->edx != 0x4ef74)
// return true;
trimmedText = ltrim(text);
if (!trimmedText || !*trimmedText)
return false;
trimmedSize = rtrim(trimmedText);
* role = Engine::OtherRole;
//DOUT(QString::fromLocal8Bit((LPCSTR)s->esi));
//auto splitText = (LPCSTR)s->esi;
//if (::strcmp(splitText, "MES_SETNAME")) // This is for scenario text with voice
//if (::strcmp(splitText, "MES_SETFACE"))
//if (::strcmp(splitText, "pcm")) // first scenario or history without text
*role = Engine::OtherRole;
// DOUT(QString::fromLocal8Bit((LPCSTR)s->esi));
// auto splitText = (LPCSTR)s->esi;
// if (::strcmp(splitText, "MES_SETNAME")) // This is for scenario text with voice
// if (::strcmp(splitText, "MES_SETFACE"))
// if (::strcmp(splitText, "pcm")) // first scenario or history without text
// return true;
//auto retaddr = s->stack[1]; // caller
//auto retaddr = s->stack[13]; // parent caller
//auto split = *(DWORD *)s->esi;
//auto split = s->esi - s->eax;
//DOUT(split);
//auto self = (ClassArgument *)s->ecx;
//auto split = self->split();
//enum { sig = 0 };
// auto retaddr = s->stack[1]; // caller
// auto retaddr = s->stack[13]; // parent caller
// auto split = *(DWORD *)s->esi;
// auto split = s->esi - s->eax;
// DOUT(split);
// auto self = (ClassArgument *)s->ecx;
// auto split = self->split();
// enum { sig = 0 };
auto self = s->ecx;
if (!Engine::isAddressWritable(self)) // old cs2 game such as Grisaia
self = s->stack[2]; // arg1
@ -555,8 +595,9 @@ static bool containsNamePunct_(const char *text)
{
static ULONG minimumGroupId_ = -1; // I assume scenario thread to have minimum groupId
//if (session_.addText(groupId, Engine::hashCharArray(text))) {
if (groupId <= minimumGroupId_) {
// if (session_.addText(groupId, Engine::hashCharArray(text))) {
if (groupId <= minimumGroupId_)
{
minimumGroupId_ = groupId;
*role = Engine::ScenarioRole;
@ -567,24 +608,24 @@ static bool containsNamePunct_(const char *text)
else if (trimmedText == text && !trimmedText[trimmedSize] // no prefix and suffix
&& guessIsNameText(trimmedText, trimmedSize))
*role = Engine::NameRole;
}
}
std::string oldData(trimmedText, trimmedSize);
return write_string_overwrite(data,len,oldData);
return write_string_overwrite(data, len, oldData);
}
void hookafter(hook_stack*s,void* data, size_t len){
void hookafter(hook_stack *s, void *data, size_t len)
{
auto newData =std::string((char*)data,len);
auto newData = std::string((char *)data, len);
if (trimmedText[trimmedSize])
newData.append(trimmedText + trimmedSize);
::strcpy(trimmedText, newData.c_str());
}
} // namespace Private
} // namespace Private
/**
/**
* Sample game:
*
* Debugging message:
@ -749,46 +790,62 @@ static bool containsNamePunct_(const char *text)
* 004985CC CC INT3
* 004985CD CC INT3
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
bool attach(ULONG startAddress, ULONG stopAddress, HookParamType code)
{
const uint8_t bytes[] = {
0xe8, XX4, // 004b00dc e8 7f191300 call .005e1a60 ; jichi: hook after here
0xbe, XX4, // 004b00e1 be d0e87b00 mov esi,.007be8d0
0x8b,0xc8, // 004b00e6 8bc8 mov ecx,eax
0x2b,0xf0 // 004b00e8 2bf0 sub esi,eax
//XX2, XX, 0x00,0x00,0x00 // 004b00ea 8d9b 00000000 lea ebx,dword ptr ds:[ebx]
0x8b, 0xc8, // 004b00e6 8bc8 mov ecx,eax
0x2b, 0xf0 // 004b00e8 2bf0 sub esi,eax
// XX2, XX, 0x00,0x00,0x00 // 004b00ea 8d9b 00000000 lea ebx,dword ptr ds:[ebx]
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if(addr==0)return false;
if (addr == 0)
return false;
HookParam hp;
hp.address=addr+5;
hp.type=USING_STRING|EMBED_ABLE|EMBED_DYNA_SJIS;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hookafter;
hp.hook_font=F_GetGlyphOutlineA;
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
hp.address = addr + 5;
hp.type = USING_STRING | EMBED_ABLE;
if (code)
hp.type |= code;
else
hp.type |= EMBED_DYNA_SJIS;
hp.hook_before = Private::hookBefore;
hp.hook_after = Private::hookafter;
hp.hook_font = F_GetGlyphOutlineA;
hp.filter_fun = [](void *data, size_t *len, HookParam *hp)
{
static std::regex rx(R"(\[(.+?)/.+\])");
auto _=std::regex_replace(std::string((char*)data,*len), rx, "$1");
return write_string_overwrite(data,len,_);
auto _ = std::regex_replace(std::string((char *)data, *len), rx, "$1");
return write_string_overwrite(data, len, _);
};
static ULONG p;
p=Patch::patchEncoding(startAddress, stopAddress) ;
if(p){
hp.type|= EMBED_DYNA_SJIS;
hp.hook_font=F_GetGlyphOutlineA;
patch_fun=[](){
ReplaceFunction((PVOID*)&p, (PVOID)(ULONG)Patch::Private::isLeadByteChar);
p = Patch::patchEncoding(startAddress, stopAddress);
if (p)
{
hp.type |= EMBED_DYNA_SJIS;
hp.hook_font = F_GetGlyphOutlineA;
patch_fun = []()
{
ReplaceFunction((PVOID *)&p, (PVOID)(ULONG)Patch::Private::isLeadByteChar);
};
}
return NewHook(hp,"EmbedCS2");
}
}
return NewHook(hp, "EmbedCS2");
}
}
} // namespace ScenarioHook
bool CatSystem::attach_function() {
auto embed=ScenarioHook::attach(processStartAddress,processStopAddress);
return InsertCatSystemHook()||InsertCatSystem2Hook()||embed;
bool CatSystem::attach_function()
{
HookParamType code = CODEC_ANSI_LE;
auto b1 = InsertCatSystemHook();
if (!b1)
{
b1 |= InsertCatSystem2Hook();
code = CODEC_UTF8;
}
auto embed = ScenarioHook::attach(processStartAddress, processStopAddress, code);
b1 |= embed;
return b1;
}