mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-24 14:25:37 +08:00
334 lines
11 KiB
C++
334 lines
11 KiB
C++
|
#include"engine.h"
|
|||
|
namespace mages{
|
|||
|
|
|||
|
regs reg=regs::invalid;
|
|||
|
int gametype=0;
|
|||
|
std::map<WORD, std::wstring> createTable() {
|
|||
|
|
|||
|
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"
|
|||
|
}[gametype],L"COMPOUND_CHARS");
|
|||
|
auto charsetA=LoadResData(std::vector<const wchar_t*>{
|
|||
|
L"charset_default",
|
|||
|
L"charset_Robotics_Notes_Elite",
|
|||
|
L"charset_Robotics_Notes_Dash"
|
|||
|
}[gametype],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) {
|
|||
|
static auto table = createTable();
|
|||
|
if (table.find(charCode) == table.end()) {
|
|||
|
std::wstringstream _;
|
|||
|
_ << std::hex << charCode;
|
|||
|
return L"[" + _.str() + L"]";
|
|||
|
}
|
|||
|
else {
|
|||
|
return table[charCode];
|
|||
|
}
|
|||
|
}
|
|||
|
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);
|
|||
|
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);
|
|||
|
}
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
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);
|
|||
|
}
|
|||
|
} // 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);
|
|||
|
bottom += cc;
|
|||
|
s += cc;
|
|||
|
}
|
|||
|
}
|
|||
|
if (rubi != L"") {
|
|||
|
//console.log('rubi: ', rubi);
|
|||
|
//console.log('char: ', bottom);
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
// do nothing (one byte control)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if(filter){
|
|||
|
static std::wstring last=L"";
|
|||
|
if(last==s)return;
|
|||
|
last=s;
|
|||
|
}
|
|||
|
|
|||
|
wchar_t* _data=new wchar_t[s.size()+1];
|
|||
|
wcscpy(_data,s.c_str());
|
|||
|
*data=(uintptr_t)_data;
|
|||
|
*len=s.size()*2;
|
|||
|
}
|
|||
|
#ifndef _WIN64
|
|||
|
bool MAGES() {
|
|||
|
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
|
|||
|
|
|||
|
bool MAGES() {
|
|||
|
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
|
|||
|
|
|||
|
|
|||
|
}
|