#include"HorkEye.h"


  
/** 10/20/2014 jichi: HorkEye, http://horkeye.com
 *  Sample game: [150226] 結城友奈�勀��ある 体験版
 *
 *  No GDI functions are used by this game.
 *
 *  Debug method:
 *  There are two matched texts.
 *  The one having fixed address is used to insert hw breakpoints.
 *
 *  I found are two functions addressing the address, both of which seems to be good.
 *  The first one is used:
 *
 *  013cda60   8d4c24 1c        lea ecx,dword ptr ss:[esp+0x1c]
 *  013cda64   51               push ecx
 *  013cda65   68 48a8c201      push .01c2a848                                     ; ascii "if"
 *  013cda6a   e8 d1291600      call .01530440
 *  013cda6f   83c4 0c          add esp,0xc
 *  013cda72   6a 01            push 0x1
 *  013cda74   83ec 1c          sub esp,0x1c
 *  013cda77   8bcc             mov ecx,esp
 *  013cda79   896424 30        mov dword ptr ss:[esp+0x30],esp
 *  013cda7d   6a 10            push 0x10
 *  013cda7f   c741 14 0f000000 mov dword ptr ds:[ecx+0x14],0xf
 *  013cda86   c741 10 00000000 mov dword ptr ds:[ecx+0x10],0x0
 *  013cda8d   68 80125601      push .01561280
 *  013cda92   c601 00          mov byte ptr ds:[ecx],0x0
 *  013cda95   e8 5681ffff      call .013c5bf0
 *  013cda9a   e8 717a0900      call .01465510
 *  013cda9f   83c4 20          add esp,0x20
 *  013cdaa2   b8 01000000      mov eax,0x1
 *  013cdaa7   8b8c24 b8000000  mov ecx,dword ptr ss:[esp+0xb8]
 *  013cdaae   5f               pop edi
 *  013cdaaf   5e               pop esi
 *  013cdab0   5d               pop ebp
 *  013cdab1   5b               pop ebx
 *  013cdab2   33cc             xor ecx,esp
 *  013cdab4   e8 c7361600      call .01531180
 *  013cdab9   81c4 ac000000    add esp,0xac
 *  013cdabf   c3               retn
 *  013cdac0   83ec 40          sub esp,0x40
 *  013cdac3   a1 24805d01      mov eax,dword ptr ds:[0x15d8024]
 *  013cdac8   8b15 c4709901    mov edx,dword ptr ds:[0x19970c4]
 *  013cdace   8d0c00           lea ecx,dword ptr ds:[eax+eax]
 *  013cdad1   a1 9c506b01      mov eax,dword ptr ds:[0x16b509c]
 *  013cdad6   0305 18805d01    add eax,dword ptr ds:[0x15d8018]
 *  013cdadc   53               push ebx
 *  013cdadd   8b5c24 48        mov ebx,dword ptr ss:[esp+0x48]
 *  013cdae1   55               push ebp
 *  013cdae2   8b6c24 50        mov ebp,dword ptr ss:[esp+0x50]
 *  013cdae6   894c24 34        mov dword ptr ss:[esp+0x34],ecx
 *  013cdaea   8b0d 20805d01    mov ecx,dword ptr ds:[0x15d8020]
 *  013cdaf0   894424 18        mov dword ptr ss:[esp+0x18],eax
 *  013cdaf4   a1 1c805d01      mov eax,dword ptr ds:[0x15d801c]
 *  013cdaf9   03c8             add ecx,eax
 *  013cdafb   56               push esi
 *  013cdafc   33f6             xor esi,esi
 *  013cdafe   d1f8             sar eax,1
 *  013cdb00   45               inc ebp
 *  013cdb01   896c24 24        mov dword ptr ss:[esp+0x24],ebp
 *  013cdb05   897424 0c        mov dword ptr ss:[esp+0xc],esi
 *  013cdb09   894c24 18        mov dword ptr ss:[esp+0x18],ecx
 *  013cdb0d   8a0c1a           mov cl,byte ptr ds:[edx+ebx]        jichi: here
 *  013cdb10   894424 30        mov dword ptr ss:[esp+0x30],eax
 *  013cdb14   8a441a 01        mov al,byte ptr ds:[edx+ebx+0x1]
 *  013cdb18   57               push edi
 *  013cdb19   897424 14        mov dword ptr ss:[esp+0x14],esi
 *  013cdb1d   3935 c8709901    cmp dword ptr ds:[0x19970c8],esi
 *
 *  The hooked place is only accessed once.
 *  013cdb0d   8a0c1a           mov cl,byte ptr ds:[edx+ebx]        jichi: here
 *  ebx is the text to be base address.
 *  edx is the offset to skip character name.
 *
 *  023B66A0  81 79 89 C4 EA A3 2C 53 30 30 35 5F 42 5F 30 30  【夏偾,S005_B_00
 *  023B66B0  30 32 81 7A 81 75 83 6F 81 5B 83 65 83 62 83 4E  02】「バーッ�ク
 *  023B66C0  83 58 82 CD 82 B1 82 C1 82 BF 82 CC 93 73 8D 87  スはこっちの都� *  023B66D0  82 C8 82 C7 82 A8 8D 5C 82 A2 82 C8 82 B5 81 63  などお構いなし…
 *
 *  There are garbage in character name.
 *
 *  1/15/2015
 *  Alternative hook that might not need a text filter:
 *  http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page753
 *  /HA-4@552B5:姉小路直子と銀色の死�exe
 *  If this hook no longer works, try that one instead.

 *  Artikash 12/26/2018: Old HorkEye hook can't be found in shukusei no girlfriend https://vndb.org/v22880
 *  This function can be used instead. Hook code: /HS4@funcaddr
0022DD80 - 83 EC 44              - sub esp,44 { 68 }
0022DD83 - A1 3C704400           - mov eax,[0044703C] { [0000001C] }
0022DD88 - 8B 0D 34704400        - mov ecx,[00447034] { [00000014] }
0022DD8E - 03 C0                 - add eax,eax
0022DD90 - 8B 54 24 48           - mov edx,[esp+48]
0022DD94 - 89 44 24 2C           - mov [esp+2C],eax
0022DD98 - A1 C87E5500           - mov eax,[00557EC8] { [00000002] }
0022DD9D - 03 05 30704400        - add eax,[00447030] { [00000014] }
0022DDA3 - 89 44 24 18           - mov [esp+18],eax
0022DDA7 - A1 38704400           - mov eax,[00447038] { [00000008] }
0022DDAC - 03 C1                 - add eax,ecx
0022DDAE - D1 F9                 - sar ecx,1
0022DDB0 - 53                    - push ebx
0022DDB1 - 55                    - push ebp
0022DDB2 - 56                    - push esi
0022DDB3 - 8B 74 24 58           - mov esi,[esp+58]
0022DDB7 - 33 DB                 - xor ebx,ebx
0022DDB9 - 89 4C 24 48           - mov [esp+48],ecx
0022DDBD - 46                    - inc esi
0022DDBE - 8B 0D 5CA28300        - mov ecx,[0083A25C] { [00000000] }
0022DDC4 - 57                    - push edi
0022DDC5 - 8B 3D 887E5500        - mov edi,[00557E88] { [00000040] }
0022DDCB - 89 74 24 2C           - mov [esp+2C],esi
0022DDCF - 89 44 24 34           - mov [esp+34],eax
0022DDD3 - 89 5C 24 18           - mov [esp+18],ebx
0022DDD7 - 8A 24 11              - mov ah,[ecx+edx]
0022DDDA - 8A 44 11 01           - mov al,[ecx+edx+01]
0022DDDE - 89 7C 24 20           - mov [esp+20],edi
0022DDE2 - 39 1D 60A28300        - cmp [0083A260],ebx { [00000000] }
0022DDE8 - 0F85 DD000000         - jne 0022DECB
0022DDEE - 80 FC 5B              - cmp ah,5B { 91 }
0022DDF1 - 0F85 9C000000         - jne 0022DE93
0022DDF7 - 8B C1                 - mov eax,ecx
0022DDF9 - 3B C6                 - cmp eax,esi
0022DDFB - 7D 10                 - jnl 0022DE0D
0022DDFD - 0F1F 00               - nop [eax]
0022DE00 - 80 3C 10  5D          - cmp byte ptr [eax+edx],5D { 93 }
0022DE04 - 74 79                 - je 0022DE7F
0022DE06 - 40                    - inc eax
0022DE07 - 3B 44 24 2C           - cmp eax,[esp+2C]
0022DE0B - 7C F3                 - jl 0022DE00
0022DE0D - A1 BC7E5500           - mov eax,[00557EBC] { [00000001] }
0022DE12 - 85 C0                 - test eax,eax
0022DE14 - 0F84 A7000000         - je 0022DEC1
0022DE1A - BE 02000000           - mov esi,00000002 { 2 }
0022DE1F - 89 74 24 1C           - mov [esp+1C],esi
0022DE23 - 89 35 68A28300        - mov [0083A268],esi { [00000000] }
0022DE29 - 83 F8 01              - cmp eax,01 { 1 }
0022DE2C - 0F85 A3000000         - jne 0022DED5
0022DE32 - 83 3D C07E5500 00     - cmp dword ptr [00557EC0],00 { 0 }
0022DE39 - 8B 2D 506D5500        - mov ebp,[00556D50] { [00000028] }
0022DE3F - 75 2D                 - jne 0022DE6E
0022DE41 - 8B C7                 - mov eax,edi
0022DE43 - 8D 8D 50855100        - lea ecx,[ebp+00518550]
0022DE49 - C1 E0 0A              - shl eax,0A { 10 }
0022DE4C - 03 C8                 - add ecx,eax
0022DE4E - 66 A1 58704400        - mov ax,[00447058] { [00004081] }
0022DE54 - 83 C5 02              - add ebp,02 { 2 }
0022DE57 - 89 2D 506D5500        - mov [00556D50],ebp { [00000028] }
0022DE5D - 66 89 01              - mov [ecx],ax
0022DE60 - A0 5A704400           - mov al,[0044705A] { [0] }
0022DE65 - 88 41 02              - mov [ecx+02],al
0022DE68 - 8B 0D 5CA28300        - mov ecx,[0083A25C] { [00000000] }
...
*/
// Skip text between "," and "�, and remove [n]
// ex:【夏偾,S005_B_0002】「バーッ�ク
static bool HorkEyeFilter(LPVOID data, size_t *size, HookParam *)
{
  size_t len = *size;
  char *str = reinterpret_cast<char *>(data),
       *start,
       *stop;

  // Remove text between , and ]
  // FIXME: This does not work well because of the ascii encoding
  if ((start = (char *)::memchr(str, ',', len)) &&
      (stop = cpp_strnstr(start, "\x81\x7a", len - (start - str))) &&
      (len -= stop - start)) // = u'�.encode('sjis')
    ::memmove(start, stop, len - (start - str));

  // Remove [n]
  enum { skip_len = 3 }; // = length of "[n]"
  while (len >= skip_len &&
         (start = cpp_strnstr(str, "[n]", len)) &&
         (len -= skip_len))
    ::memmove(start, start + skip_len, len - (start - str));

  *size = len;
  return true;
}
namespace{
  template <typename strT>
  strT ltrim(strT text)
  {
    strT lastText = nullptr;
    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] == '\\') {
        text++;
        while (::islower(text[0]) || text[0] == '@')
          text++;
      }
    }
    while ((signed char)text[0] > 0 && text[0] != '[') // skip all leading ascii characters except "[" needed for ruby
      text++;
    return text;
  }
  template<int offset=1>
  bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role){
    auto str=(LPSTR)(s->stack[offset]);//stack-2:eax
    int len=strlen(str);//s->ecx; 
    char *stop;
  if ((stop = cpp_strnstr(str, "\x81\x7a", len )) &&
        (len -= (stop - str+2))){
          str=stop+2;
    } // = u'�.encode('sjis')
      auto old=std::string(str,len);
     return write_string_overwrite(data,len1,old);
  }
  template<int offset=1>
  void hookafter(hook_stack*s,void* data, size_t len1){
      
      auto newData =std::string((char*)data,len1);
       auto str=(LPSTR)(s->stack[offset]);//stack-2:eax
    int len=strlen(str);//s->ecx; 
    int lensave=len;
       char *stop;
      if ( (stop = cpp_strnstr(str, "\x81\x7a", len )) &&
        (len -= (stop - str+2))){
         auto old=std::string(str,stop+2-str);
         newData=old+newData;
    }
    for(int i=0;i<lensave-newData.size();i++)newData.push_back(' ');
    memcpy((void*)str,newData.c_str(),lensave);
  //   s->ecx=newData.size(); 修改ecx没用
  }
}
bool InsertHorkEyeHook()
{
  const BYTE bytes[] = {
    0x89,0x6c,0x24, 0x24,   // 013cdb01   896c24 24        mov dword ptr ss:[esp+0x24],ebp
    0x89,0x74,0x24, 0x0c,   // 013cdb05   897424 0c        mov dword ptr ss:[esp+0xc],esi
    0x89,0x4c,0x24, 0x18,   // 013cdb09   894c24 18        mov dword ptr ss:[esp+0x18],ecx
    0x8a,0x0c,0x1a          // 013cdb0d   8a0c1a           mov cl,byte ptr ds:[edx+ebx]        jichi: here
  };
  enum { addr_offset = sizeof(bytes) - 3 }; // 8a0c1a
  ;
  if (ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress)) {
	  HookParam hp;
	  hp.address = addr + addr_offset;
	  hp.offset=get_reg(regs::ebx);
	  hp.type = USING_STRING| NO_CONTEXT|FIXING_SPLIT|EMBED_ABLE|EMBED_DYNA_SJIS;
    hp.hook_before=hookBefore<-4-1>;
    hp.hook_after=hookafter<-4-1>;
	  hp.filter_fun = HorkEyeFilter;
    hp.newlineseperator=L"[n]";
	  ConsoleOutput("INSERT HorkEye");
	  
	  return NewHook(hp, "HorkEye");
  }

  memcpy(spDefault.pattern, Array<BYTE>{ 0xcc, 0xcc, 0xcc, XX, 0xec }, spDefault.length = 5);
  spDefault.offset = 3;

  const BYTE bytes2[] =
  {
	  0x83, 0xec, XX, // sub esp,??
	  0xa1, XX4, // mov eax,??
	  0x8b, 0x0d, XX4, // mov ecx,??
	  0x03, 0xc0 // add eax,eax
  };

  for (auto addr : Util::SearchMemory(bytes2, sizeof(bytes2),PAGE_EXECUTE_READWRITE,processStartAddress, processStopAddress))
  {
	  HookParam hp;
	  hp.address = addr;
	  hp.offset=get_stack(1);
    hp.type = USING_STRING| EMBED_ABLE|EMBED_DYNA_SJIS;
    hp.hook_before=hookBefore<1>;
    hp.hook_after=hookafter<1>;
	  
	  return NewHook(hp, "HorkEye2");
  }

  ConsoleOutput("HorkEye: pattern not found");
  return false;
  
}

bool InsertHorkEye3Hook()
{ 
  const BYTE bytes2[] =
  { 
    0x55,
    0x8d,0xac,0x24,XX4, 
    0x81,0xec,XX4,
    0x6a,0xff,
    0x68,XX4,
    0x64,0xa1,0x00,0x00,0x00,0x00,
    0x50,
    0x83,0xec,0x38,   //必须是0x38,不能是XX,否则有重的。

//.text:0042E7F0 55                            push    ebp
//.text : 0042E7F1 8D AC 24 24 FF FF FF          lea     ebp,[esp - 0DCh]
//.text : 0042E7F8 81 EC DC 00 00 00             sub     esp, 0DCh
//.text : 0042E7FE 6A FF                         push    0FFFFFFFFh
//.text : 0042E800 68 51 1E 5C 00                push    offset SEH_42E7F0
//.text : 0042E805 64 A1 00 00 00 00             mov     eax, large fs : 0
//.text : 0042E80B 50                            push    eax
//.text : 0042E80C 83 EC 38                      sub     esp, 38h
//.text : 0042E80F A1 24 D0 64 00                mov     eax, ___security_cookie
//.text : 0042E814 33 C5 xor eax, ebp
//.text : 0042E816 89 85 D8 00 00 00             mov[ebp + 0DCh + var_4], eax
  };

  auto addr=MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
  if (addr == 0)return false;
  HookParam hp;
  hp.address = addr;
  hp.offset=get_stack(1);
  hp.type = USING_STRING| EMBED_ABLE|EMBED_DYNA_SJIS;
  hp.hook_before=hookBefore<1>;
  hp.hook_after=hookafter<1>;  
  
  return NewHook(hp, "HorkEye3");

}

bool InsertHorkEye4Hook()
{
  //辻堂さんのバージンロード
  //辻堂さんの純愛ロード
  const BYTE bytes2[] =
  {
    0xf7,0xd8,
    0x1b,0xc0,
    0x83,0xc0,0x02
  };
  auto addr = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
  if (addr == 0)return false;
  const BYTE bytebetter[] = {
    0x8b,XX,XX,XX,
    0xa1,XX4,
    0x83,0xc4,XX,
    0x8b,XX
  };
  auto addr1 = MemDbg::findBytes(bytebetter, sizeof(bytebetter), addr - 0x100, addr);
  if (addr1)
    addr = addr1;
  else
    addr = MemDbg::findEnclosingAlignedFunction(addr);
  if (addr == 0)return false;
  HookParam hp;
  hp.address = addr;
  hp.offset=get_reg(regs::eax);
  hp.type = USING_STRING| NO_CONTEXT|EMBED_ABLE|EMBED_DYNA_SJIS;
  hp.hook_before=hookBefore<-1-1>;
  hp.hook_after=hookafter<-1-1>;
  
  return NewHook(hp, "HorkEye4");

}
  
bool InsertHorkEye6Hook()
{
  //みなとカーニバルFD

  const BYTE bytes2[] =
  {
    0x83,0xc2,0x6c,
    0x52,
    0xe8
  };
  auto addr = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
  if (addr == 0)return false;
  ConsoleOutput("hk6 %p", addr);
  const BYTE start[] = { 0x6A ,0xFF };
  addr = reverseFindBytes(start, sizeof(start), addr - 0x1000, addr);
  if (addr == 0)return false;
  ConsoleOutput("hk6 %p", addr);
  HookParam hp;
  hp.address = addr;
  hp.offset=get_stack(3);
  hp.type = CODEC_ANSI_BE ;
  ConsoleOutput("INSERT HorkEye6 %p", addr);
  
  return NewHook(hp, "HorkEye6");

}

bool HorkEye::attach_function() {  
    bool b1=InsertHorkEyeHook();
    bool b2=InsertHorkEye3Hook();
    bool b3=InsertHorkEye4Hook();
    bool b4=InsertHorkEye6Hook();
    return b1||b2||b3||b4;
}