mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-12-27 13:44:11 +08:00
8267dda1cb
Update yuzusuyu.cpp 1 Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp 1 Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp Update yuzusuyu.cpp
349 lines
11 KiB
C++
349 lines
11 KiB
C++
#include"mages/mages.h"
|
||
|
||
namespace mages{
|
||
|
||
std::map<WORD, std::wstring> createTable(int _idx) {
|
||
|
||
auto compound_charsA=LoadResData(std::vector<const wchar_t*>{
|
||
L"compound_chars_default",
|
||
L"compound_chars_Robotics_Notes_Elite",
|
||
L"compound_chars_Robotics_Notes_Dash",
|
||
L"",
|
||
L""
|
||
}[_idx],L"COMPOUND_CHARS");
|
||
auto charsetA=LoadResData(std::vector<const wchar_t*>{
|
||
L"charset_default",
|
||
L"charset_Robotics_Notes_Elite",
|
||
L"charset_Robotics_Notes_Dash",
|
||
L"charset_Famicom_Tantei_Club",
|
||
L"charset_SINce_Memories"
|
||
}[_idx],L"CHARSET");
|
||
|
||
|
||
auto compound_chars=StringToWideString(compound_charsA);
|
||
auto charset=StringToWideString(charsetA);
|
||
|
||
std::map<WORD, std::wstring> table = {};
|
||
|
||
for (auto line : strSplit(compound_chars, L"\n")) {
|
||
auto pair = strSplit(line, L"=");
|
||
if (pair.size() != 2) continue;
|
||
auto key = pair[0].substr(1, pair[0].size() - 2);
|
||
auto val = pair[1];
|
||
auto keys = strSplit(key, L"-");
|
||
if (keys.size() == 1)keys.push_back(key);
|
||
size_t _;
|
||
auto start = std::stoi(keys[0], &_, 16);
|
||
auto end = std::stoi(keys[1], &_, 16);
|
||
for (auto i = start; i <= end; i++) {
|
||
auto charCode = ((i & 0xFF) << 8) | i >> 8; // swap endian
|
||
table[charCode] = val;
|
||
}
|
||
}
|
||
|
||
|
||
WORD charCode;
|
||
for (auto i = 0; i < charset.size(); i++) {
|
||
charCode = 0x8000 + i;
|
||
charCode = ((charCode & 0xFF) << 8) | charCode >> 8; // swap endian (0x8001 -> 0x0180)
|
||
table[charCode] = charset[i];
|
||
}
|
||
return table;
|
||
}
|
||
|
||
std::wstring mages_decode(WORD charCode,int _idx) {
|
||
static auto table = createTable(_idx);
|
||
if (table.find(charCode) == table.end()) {
|
||
std::wstringstream _;
|
||
_ << std::hex << charCode;
|
||
return L"[" + _.str() + L"]";
|
||
}
|
||
else {
|
||
return table[charCode];
|
||
}
|
||
}
|
||
std::wstring readString(uintptr_t address,int _idx) {
|
||
auto edx=address;
|
||
std::wstring s = L"", bottom = L"";
|
||
while (1) {
|
||
auto c = *(BYTE*)edx;
|
||
if (c == 0xff)break; // terminated
|
||
if (c >= 0xb0) {// b4: next page?
|
||
edx += 1;
|
||
continue;
|
||
}
|
||
if (c >= 0x80) {// readChar
|
||
auto charCode = *(WORD*)edx;
|
||
edx += 2;
|
||
s += mages_decode(charCode,_idx);
|
||
}
|
||
else {// readControl
|
||
edx += 1;
|
||
if (c == 0) {
|
||
s += L' ';
|
||
}
|
||
else if (c == 1) {// speaker
|
||
bottom = L"";
|
||
while (1)
|
||
{
|
||
auto c2 = *(BYTE*)edx;
|
||
if (c2 == 2) {
|
||
edx += 1; break;
|
||
}
|
||
else if (c2 < 0x20)edx += 1;
|
||
else {
|
||
auto charCode = *(WORD*)edx;
|
||
edx += 2;
|
||
bottom += mages_decode(charCode,_idx);
|
||
}
|
||
}
|
||
if(bottom.size()) s = s + bottom + L": ";
|
||
}
|
||
else if (c == 2) { // line
|
||
// do nothing -> back to readChar
|
||
}
|
||
else if (c == 4 || c == 0x15) { // SetColor, EvaluateExpression => SKIP
|
||
////if (c !== 4) console.warn('Warning: ', c, hexdump(address));
|
||
// https://github.com/CommitteeOfZero/SciAdv.Net/blob/32489cd21921079975291dbdce9151ad66f1b06a/src/SciAdvNet.SC3/Text/SC3StringDecoder.cs#L98
|
||
// https://github.com/CommitteeOfZero/SciAdv.Net/blob/32489cd21921079975291dbdce9151ad66f1b06a/src/SciAdvNet.SC3/Text/StringSegmentCodes.cs#L3
|
||
// https://github.com/shiiion/steinsgate_textractor/blob/master/steinsgatetextractor/sg_text_extractor.cpp#L46
|
||
auto token = *(BYTE*)edx; // BYTE token = read_single<BYTE>(cur_index);
|
||
if (!token) {
|
||
edx +=1; // return cur_index + 1;
|
||
}
|
||
else {
|
||
do {
|
||
if (token & 0x80) {
|
||
switch (token & 0x60) {
|
||
case 0:
|
||
edx +=2 ; //cur_index += 2;
|
||
break;
|
||
case 0x20:
|
||
edx +=3; //cur_index += 3;
|
||
break;
|
||
case 0x40:
|
||
edx +=4; //cur_index += 4;
|
||
break;
|
||
case 0x60:
|
||
edx +=5; //cur_index += 5;
|
||
break;
|
||
default:
|
||
// impossible
|
||
break;
|
||
}
|
||
} else {
|
||
edx +=2; //cur_index += 2;
|
||
}
|
||
token = *(BYTE*)edx; //token = read_single<BYTE>(cur_index);
|
||
} while (token);
|
||
}
|
||
}
|
||
else if (c == 0x0C // SetFontSize
|
||
|| c == 0x11 // SetTopMargin
|
||
|| c == 0x12 // SetLeftMargin
|
||
|| c == 0x13 // STT_GetHardcodedValue: https://github.com/CommitteeOfZero/impacto/blob/master/src/text.cpp#L43
|
||
) {
|
||
edx+=2;
|
||
}
|
||
else if (c == 9) { // ruby (09_text_0A_rubi_0B)
|
||
std::wstring rubi = L"";
|
||
bottom = L"";
|
||
while (true) {
|
||
auto c2 = *(BYTE*)edx;
|
||
if (c2 == 0x0A) { // rubi
|
||
edx+=1;
|
||
while (true) {
|
||
c2 = *(BYTE*)edx;
|
||
if (c2 == 0x0B) { // end rubi
|
||
// address = address.add(1);
|
||
break; // break lv2 loop
|
||
}
|
||
else if (c2 < 0x20) { // another control
|
||
edx+=1;
|
||
}
|
||
else { // rubi
|
||
auto charCode = *(WORD*)edx;
|
||
edx+=2;
|
||
|
||
rubi += mages_decode(charCode,_idx);
|
||
}
|
||
} // end while
|
||
}
|
||
else if (c2 == 0x0B) { // end rubi
|
||
edx+=1;
|
||
break; // break lv1 loop
|
||
}
|
||
else if (c2 < 0x20) { // another control (color?)
|
||
edx+=1;
|
||
}
|
||
else { // char (text)
|
||
auto charCode = *(WORD*)edx;
|
||
edx+=2;
|
||
|
||
auto cc = mages_decode(charCode,_idx);
|
||
bottom += cc;
|
||
s += cc;
|
||
}
|
||
}
|
||
if (rubi != L"") {
|
||
//console.log('rubi: ', rubi);
|
||
//console.log('char: ', bottom);
|
||
}
|
||
}
|
||
else {
|
||
// do nothing (one byte control)
|
||
}
|
||
}
|
||
}
|
||
return s;
|
||
}
|
||
|
||
}
|
||
|
||
namespace hookmages{
|
||
|
||
regs reg=regs::invalid;
|
||
int gametype=0;
|
||
|
||
template<int filter>
|
||
void SpecialHookMAGES(hook_stack* stack, HookParam*, uintptr_t* data, uintptr_t* split, size_t* len)
|
||
{
|
||
auto edx = regof(reg,stack);//regof(edx, esp_base);
|
||
|
||
auto s=mages::readString(edx,gametype);
|
||
|
||
if(filter){
|
||
static std::wstring last=L"";
|
||
if(last==s)return;
|
||
last=s;
|
||
}
|
||
|
||
write_string_new(data,len,s);
|
||
}
|
||
|
||
bool MAGES() {
|
||
#ifndef _WIN64
|
||
auto dialogSigOffset = 2;
|
||
BYTE dialogSig1 []={
|
||
0x85,XX,0x74,XX,0x83,XX,0x01,0x74,XX,0x83,XX,0x04,0x74,XX,0xc7,0x05,XX,XX,XX,XX,0x01,0x00,0x00,0x00
|
||
};
|
||
auto addr=MemDbg::findBytes(dialogSig1,sizeof(dialogSig1),processStartAddress,processStopAddress);
|
||
if(addr==0){
|
||
dialogSigOffset = 3;
|
||
BYTE dialogSig2 []={
|
||
0x57,0x85,XX,0x74,XX,0x83,XX,0x01,0x74,XX,0x83,XX,0x04
|
||
};
|
||
addr=MemDbg::findBytes(dialogSig2,sizeof(dialogSig2),processStartAddress,processStopAddress);
|
||
|
||
}
|
||
if(addr==0)return false;
|
||
auto pos = addr+dialogSigOffset;
|
||
//.text:00431D3F 74 16 jz short loc_431D57
|
||
auto jzoff=*(BYTE*)(pos+1);
|
||
pos+=jzoff+2;
|
||
auto hookaddr=pos;
|
||
for(int i=0;i<0x200;i++){
|
||
if(((*(BYTE*)(pos))==0x8a)){
|
||
|
||
switch(((*(BYTE*)(pos+1)))){
|
||
// case 0:reg=pusha_eax_off;break;
|
||
//YU-NO
|
||
//.text:00431D63 89 0D 20 A9 BF 00 mov dword_BFA920, ecx
|
||
//在加载到内存后,有时变成89 0d 20 a9 8a 00,导致崩溃,且这个没有遇到过,故注释掉。
|
||
// case 3:reg=pusha_ebx_off;break;
|
||
// case 1:reg=pusha_ecx_off;break;
|
||
// case 2:reg=pusha_edx_off;break;
|
||
// case 6:reg=pusha_ebp_off;break;
|
||
// case 7:reg=pusha_edi_off;break;
|
||
case 3:reg=regs::ebx;break;
|
||
case 1:reg=regs::ecx;break;
|
||
case 2:reg=regs::edx;break;
|
||
case 6:reg=regs::ebp;break;
|
||
case 7:reg=regs::edi;break;
|
||
default:reg=regs::invalid;
|
||
}
|
||
if(reg!=regs::invalid)break;
|
||
}
|
||
pos+=1;
|
||
}
|
||
if(reg==regs::invalid)return false;
|
||
switch(pos-processStartAddress){
|
||
case 0x9f723:
|
||
//Robotics;Notes-Elite
|
||
gametype=1;
|
||
break;
|
||
case 0xf70a6:
|
||
//Robotics;Notes-Dash
|
||
gametype=2;
|
||
break;
|
||
|
||
default:
|
||
//YU-NO
|
||
//测试无效:
|
||
//Steins;Gate-0
|
||
//Steins;Gate
|
||
//未测试:
|
||
//Steins;Gate-Elite
|
||
//Chaos;Child
|
||
//CHAOS;HEAD_NOAH
|
||
//Memories_Off_-Innocent_Fille
|
||
//Memories_Off_-Innocent_Fille-_for_Dearest
|
||
gametype=0;
|
||
}
|
||
//ConsoleOutput("%x",pos-processStartAddress);
|
||
HookParam hp;
|
||
//hp.address = hookaddr;
|
||
hp.address=hookaddr;
|
||
//想い出にかわる君 ~メモリーズオフ~ 想君:秋之回忆3在hookaddr上无法正确读取。
|
||
//hookaddr上是没有重复的,pos上是都能读到但有重复。
|
||
hp.text_fun = SpecialHookMAGES<0>;
|
||
hp.type = CODEC_UTF16 | USING_STRING|NO_CONTEXT;
|
||
auto _=NewHook(hp, "5pb_MAGES");
|
||
hp.address=pos;
|
||
hp.text_fun = SpecialHookMAGES<1>;
|
||
_|=NewHook(hp, "5pb_MAGES");
|
||
return _;
|
||
|
||
#else
|
||
|
||
auto dialogSigOffset = 2;
|
||
BYTE dialogSig1 []={
|
||
0x85,XX,0x74,XX,0x41,0x83,XX,0x01,0x74,XX,0x41,0x83,XX,0x04,0x74,XX,0x41
|
||
};
|
||
auto addr=MemDbg::findBytes(dialogSig1,sizeof(dialogSig1),processStartAddress,processStopAddress);
|
||
ConsoleOutput("%p",addr);
|
||
if(addr==0)return false;
|
||
auto pos = addr+dialogSigOffset;
|
||
auto jzoff=*(BYTE*)(pos+1);
|
||
pos+=jzoff+2;
|
||
auto hookaddr=pos;
|
||
//
|
||
for(int i=0;i<0x200;i++){
|
||
//.text:000000014004116B 0F B6 13 movzx edx, byte ptr [rbx]
|
||
//->rbx
|
||
if((((*(DWORD*)(pos))&0xffffff)==0x13b60f)){
|
||
reg=regs::rbx;//rbx
|
||
//ConsoleOutput("%p",pos-processStartAddress);
|
||
break;
|
||
}
|
||
pos+=1;
|
||
}
|
||
if(reg==regs::invalid)return false;
|
||
switch(pos-processStartAddress){
|
||
|
||
default:
|
||
//CHAOS;HEAD_NOAH
|
||
gametype=0;
|
||
}
|
||
HookParam hp;
|
||
hp.address=hookaddr;
|
||
hp.text_fun = SpecialHookMAGES<0>;
|
||
hp.type = CODEC_UTF16 | USING_STRING|NO_CONTEXT;
|
||
return NewHook(hp, "5pb_MAGES");
|
||
|
||
|
||
#endif
|
||
}
|
||
|
||
|
||
} |