#include"GXP.h"
/**
 *  jichi 5/11/2014: Hook to the beginning of a function
 *
 *  Executable description shows "AVGEngineV2"
 *
 *  Cached wrong text can also be found in GetGlyphOutlineW.
 *
 *  4/27/2015 old logic:
 *  1. find the following location
 *     00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0
 *     i.e. 0x66833C7000
 *     There are several matches, the first one is used.
 *  2. find the first push operation after it
 *  3. find the function call after push, and hook to it
 *     The text is in the arg1, which is character by character
 *
 *  But in the new game since ウルスラグ� there the function call is not immediately after 0x66833C7000 any more.
 *  My own way to find the function to hook is as follows:
 *  1. find the following location
 *     00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0
 *     i.e. 0x66833C7000
 *     There are several matches, the first one is used.
 *  2. Use Ollydbg to debug step by step until the first function call is encountered
 *     Then, the text character is directly on the stack
 *
 *  Here's an example of Demonion II (reladdr = 0x18c540):
 *  The text is displayed character by character.
 *  sub_58C540 proc near
 *  arg_0 = dword ptr  8 // LPCSTR with 1 character
 *
 *  0138C540  /$ 55             PUSH EBP
 *  0138C541  |. 8BEC           MOV EBP,ESP
 *  0138C543  |. 83E4 F8        AND ESP,0xFFFFFFF8
 *  0138C546  |. 8B43 0C        MOV EAX,DWORD PTR DS:[EBX+0xC]
 *  0138C549  |. 83EC 08        SUB ESP,0x8
 *  0138C54C  |. 56             PUSH ESI
 *  0138C54D  |. 57             PUSH EDI
 *  0138C54E  |. 85C0           TEST EAX,EAX
 *  0138C550  |. 75 04          JNZ SHORT demonion.0138C556
 *  0138C552  |. 33F6           XOR ESI,ESI
 *  0138C554  |. EB 18          JMP SHORT demonion.0138C56E
 *  0138C556  |> 8B4B 14        MOV ECX,DWORD PTR DS:[EBX+0x14]
 *  0138C559  |. 2BC8           SUB ECX,EAX
 *  0138C55B  |. B8 93244992    MOV EAX,0x92492493
 *  0138C560  |. F7E9           IMUL ECX
 *  0138C562  |. 03D1           ADD EDX,ECX
 *  0138C564  |. C1FA 04        SAR EDX,0x4
 *  0138C567  |. 8BF2           MOV ESI,EDX
 *  0138C569  |. C1EE 1F        SHR ESI,0x1F
 *  0138C56C  |. 03F2           ADD ESI,EDX
 *  0138C56E  |> 8B7B 10        MOV EDI,DWORD PTR DS:[EBX+0x10]
 *  0138C571  |. 8BCF           MOV ECX,EDI
 *  0138C573  |. 2B4B 0C        SUB ECX,DWORD PTR DS:[EBX+0xC]
 *  0138C576  |. B8 93244992    MOV EAX,0x92492493
 *  0138C57B  |. F7E9           IMUL ECX
 *  0138C57D  |. 03D1           ADD EDX,ECX
 *  0138C57F  |. C1FA 04        SAR EDX,0x4
 *  0138C582  |. 8BC2           MOV EAX,EDX
 *  0138C584  |. C1E8 1F        SHR EAX,0x1F
 *  0138C587  |. 03C2           ADD EAX,EDX
 *  0138C589  |. 3BC6           CMP EAX,ESI
 *  0138C58B  |. 73 2F          JNB SHORT demonion.0138C5BC
 *  0138C58D  |. C64424 08 00   MOV BYTE PTR SS:[ESP+0x8],0x0
 *  0138C592  |. 8B4C24 08      MOV ECX,DWORD PTR SS:[ESP+0x8]
 *  0138C596  |. 8B5424 08      MOV EDX,DWORD PTR SS:[ESP+0x8]
 *  0138C59A  |. 51             PUSH ECX
 *  0138C59B  |. 8B4D 08        MOV ECX,DWORD PTR SS:[EBP+0x8]
 *  0138C59E  |. 52             PUSH EDX
 *  0138C59F  |. B8 01000000    MOV EAX,0x1
 *  0138C5A4  |. 8BD7           MOV EDX,EDI
 *  0138C5A6  |. E8 F50E0000    CALL demonion.0138D4A0
 *  0138C5AB  |. 83C4 08        ADD ESP,0x8
 *  0138C5AE  |. 83C7 1C        ADD EDI,0x1C
 *  0138C5B1  |. 897B 10        MOV DWORD PTR DS:[EBX+0x10],EDI
 *  0138C5B4  |. 5F             POP EDI
 *  0138C5B5  |. 5E             POP ESI
 *  0138C5B6  |. 8BE5           MOV ESP,EBP
 *  0138C5B8  |. 5D             POP EBP
 *  0138C5B9  |. C2 0400        RETN 0x4
 *  0138C5BC  |> 397B 0C        CMP DWORD PTR DS:[EBX+0xC],EDI
 *  0138C5BF  |. 76 05          JBE SHORT demonion.0138C5C6
 *  0138C5C1  |. E8 1B060D00    CALL demonion.0145CBE1
 *  0138C5C6  |> 8B03           MOV EAX,DWORD PTR DS:[EBX]
 *  0138C5C8  |. 57             PUSH EDI                                 ; /Arg4
 *  0138C5C9  |. 50             PUSH EAX                                 ; |Arg3
 *  0138C5CA  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+0x8]           ; |
 *  0138C5CD  |. 50             PUSH EAX                                 ; |Arg2
 *  0138C5CE  |. 8D4C24 14      LEA ECX,DWORD PTR SS:[ESP+0x14]          ; |
 *  0138C5D2  |. 51             PUSH ECX                                 ; |Arg1
 *  0138C5D3  |. 8BC3           MOV EAX,EBX                              ; |
 *  0138C5D5  |. E8 D6010000    CALL demonion.0138C7B0                   ; \demonion.0138C7B0
 *  0138C5DA  |. 5F             POP EDI
 *  0138C5DB  |. 5E             POP ESI
 *  0138C5DC  |. 8BE5           MOV ESP,EBP
 *  0138C5DE  |. 5D             POP EBP
 *  0138C5DF  \. C2 0400        RETN 0x4
 *
 *  4/26/2015  ウルスラグ� *  base = 0xa30000, old hook addr = 0xbe6360
 *
 *  00A7813A   EB 02            JMP SHORT .00A7813E
 *  00A7813C   8BC7             MOV EAX,EDI
 *  00A7813E   8BB3 E4020000    MOV ESI,DWORD PTR DS:[EBX+0x2E4]
 *  00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0  ; jich: here's the first found segment
 *  00A78149   74 36            JE SHORT .00A78181
 *  00A7814B   837F 14 08       CMP DWORD PTR DS:[EDI+0x14],0x8
 *  00A7814F   72 08            JB SHORT .00A78159
 *  00A78151   8B07             MOV EAX,DWORD PTR DS:[EDI]
 *
 *  00A7883A   24 3C            AND AL,0x3C
 *  00A7883C   50               PUSH EAX
 *  00A7883D   C74424 4C 000000>MOV DWORD PTR SS:[ESP+0x4C],0x0
 *  00A78845   0F5B             ???                                      ; Unknown command
 *  00A78847   C9               LEAVE
 *  00A78848   F3:0F114424 44   MOVSS DWORD PTR SS:[ESP+0x44],XMM0
 *  00A7884E   F3:0F114C24 48   MOVSS DWORD PTR SS:[ESP+0x48],XMM1
 *  00A78854   E8 37040000      CALL .00A78C90  ; jichi: here's the target function to hook to, text char on the stack[0]
 *  00A78859   A1 888EDD00      MOV EAX,DWORD PTR DS:[0xDD8E88]
 *  00A7885E   A8 01            TEST AL,0x1
 *  00A78860   75 30            JNZ SHORT .00A78892
 *  00A78862   83C8 01          OR EAX,0x1
 *  00A78865   A3 888EDD00      MOV DWORD PTR DS:[0xDD8E88],EAX
 *
 *  Here's the new function call:
 *  00A78C8A   CC               INT3
 *  00A78C8B   CC               INT3
 *  00A78C8C   CC               INT3
 *  00A78C8D   CC               INT3
 *  00A78C8E   CC               INT3
 *  00A78C8F   CC               INT3
 *  00A78C90   55               PUSH EBP
 *  00A78C91   8BEC             MOV EBP,ESP
 *  00A78C93   56               PUSH ESI
 *  00A78C94   8BF1             MOV ESI,ECX
 *  00A78C96   57               PUSH EDI
 *  00A78C97   8B7D 08          MOV EDI,DWORD PTR SS:[EBP+0x8]
 *  00A78C9A   8B4E 04          MOV ECX,DWORD PTR DS:[ESI+0x4]
 *  00A78C9D   3BF9             CMP EDI,ECX
 *  00A78C9F   73 76            JNB SHORT .00A78D17
 *  00A78CA1   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  00A78CA3   3BC7             CMP EAX,EDI
 *  00A78CA5   77 70            JA SHORT .00A78D17
 *  00A78CA7   2BF8             SUB EDI,EAX
 *  00A78CA9   B8 93244992      MOV EAX,0x92492493
 *  00A78CAE   F7EF             IMUL EDI
 *  00A78CB0   03D7             ADD EDX,EDI
 *  00A78CB2   C1FA 04          SAR EDX,0x4
 *  00A78CB5   8BFA             MOV EDI,EDX
 *  00A78CB7   C1EF 1F          SHR EDI,0x1F
 *  00A78CBA   03FA             ADD EDI,EDX
 *  00A78CBC   3B4E 08          CMP ECX,DWORD PTR DS:[ESI+0x8]
 *  00A78CBF   75 09            JNZ SHORT .00A78CCA
 *  00A78CC1   6A 01            PUSH 0x1
 *  00A78CC3   8BCE             MOV ECX,ESI
 *  00A78CC5   E8 36030000      CALL .00A79000
 *  00A78CCA   8B56 04          MOV EDX,DWORD PTR DS:[ESI+0x4]
 *  00A78CCD   8D0CFD 00000000  LEA ECX,DWORD PTR DS:[EDI*8]
 *  00A78CD4   2BCF             SUB ECX,EDI
 *  00A78CD6   8B3E             MOV EDI,DWORD PTR DS:[ESI]
 *  00A78CD8   85D2             TEST EDX,EDX
 *  00A78CDA   74 7B            JE SHORT .00A78D57
 *  00A78CDC   66:8B048F        MOV AX,WORD PTR DS:[EDI+ECX*4]
 *  00A78CE0   66:8902          MOV WORD PTR DS:[EDX],AX
 *  00A78CE3   8B448F 04        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x4]
 *  00A78CE7   8942 04          MOV DWORD PTR DS:[EDX+0x4],EAX
 *  00A78CEA   8B448F 08        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x8]
 *  00A78CEE   8942 08          MOV DWORD PTR DS:[EDX+0x8],EAX
 *  00A78CF1   8B448F 0C        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0xC]
 *  00A78CF5   8942 0C          MOV DWORD PTR DS:[EDX+0xC],EAX
 *  00A78CF8   C742 10 00000000 MOV DWORD PTR DS:[EDX+0x10],0x0
 *  00A78CFF   8B448F 14        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x14]
 *  00A78D03   8942 14          MOV DWORD PTR DS:[EDX+0x14],EAX
 *  00A78D06   8A448F 18        MOV AL,BYTE PTR DS:[EDI+ECX*4+0x18]
 *  00A78D0A   8842 18          MOV BYTE PTR DS:[EDX+0x18],AL
 *  00A78D0D   8346 04 1C       ADD DWORD PTR DS:[ESI+0x4],0x1C
 *  00A78D11   5F               POP EDI
 *  00A78D12   5E               POP ESI
 *  00A78D13   5D               POP EBP
 *  00A78D14   C2 0400          RETN 0x4
 *  00A78D17   3B4E 08          CMP ECX,DWORD PTR DS:[ESI+0x8]
 *  00A78D1A   75 09            JNZ SHORT .00A78D25
 *  00A78D1C   6A 01            PUSH 0x1
 *  00A78D1E   8BCE             MOV ECX,ESI
 *  00A78D20   E8 DB020000      CALL .00A79000
 *  00A78D25   8B4E 04          MOV ECX,DWORD PTR DS:[ESI+0x4]
 *  00A78D28   85C9             TEST ECX,ECX
 *  00A78D2A   74 2B            JE SHORT .00A78D57
 *  00A78D2C   66:8B07          MOV AX,WORD PTR DS:[EDI]
 *  00A78D2F   66:8901          MOV WORD PTR DS:[ECX],AX
 *  00A78D32   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A78D35   8941 04          MOV DWORD PTR DS:[ECX+0x4],EAX
 *  00A78D38   8B47 08          MOV EAX,DWORD PTR DS:[EDI+0x8]
 *  00A78D3B   8941 08          MOV DWORD PTR DS:[ECX+0x8],EAX
 *  00A78D3E   8B47 0C          MOV EAX,DWORD PTR DS:[EDI+0xC]
 *  00A78D41   8941 0C          MOV DWORD PTR DS:[ECX+0xC],EAX
 *  00A78D44   C741 10 00000000 MOV DWORD PTR DS:[ECX+0x10],0x0
 *  00A78D4B   8B47 14          MOV EAX,DWORD PTR DS:[EDI+0x14]
 *  00A78D4E   8941 14          MOV DWORD PTR DS:[ECX+0x14],EAX
 *  00A78D51   8A47 18          MOV AL,BYTE PTR DS:[EDI+0x18]
 *  00A78D54   8841 18          MOV BYTE PTR DS:[ECX+0x18],AL
 *  00A78D57   8346 04 1C       ADD DWORD PTR DS:[ESI+0x4],0x1C
 *  00A78D5B   5F               POP EDI
 *  00A78D5C   5E               POP ESI
 *  00A78D5D   5D               POP EBP
 *  00A78D5E   C2 0400          RETN 0x4
 *  00A78D61   CC               INT3
 *  00A78D62   CC               INT3
 *  00A78D63   CC               INT3
 *  00A78D64   CC               INT3
 *  00A78D65   CC               INT3
 */
static bool InsertGXP1Hook()
{
  union {
    DWORD i;
    DWORD *id;
    BYTE *ib;
  };
  for (i = processStartAddress + 0x1000; i < processStopAddress - 4; i++) {
    // jichi example:
    // 00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0

    //find cmp word ptr [esi*2+eax],0
    if (*id != 0x703c8366)
      continue;
    i += 4;
    if (*ib != 0)
      continue;
    i++;
    DWORD j = i + 0x200;
    j = j < (processStopAddress - 8) ? j : (processStopAddress - 8);

    DWORD flag = false;
    while (i < j) {
      DWORD k = disasm(ib);
      if (k == 0)
        break;
      if (k == 1 && (*ib & 0xf8) == 0x50) { // push reg
        flag = true;
        break;
      }
      i += k;
    }
    if (flag)
      while (i < j) {
        if (*ib == 0xe8) { // jichi: find first long call after the push operation
          i++;
          DWORD addr = *id + i + 4;
          if (addr > processStartAddress && addr < processStopAddress) {
            HookParam hp;
            hp.address = addr;
            //hp.type = CODEC_UTF16|DATA_INDIRECT;
            hp.type = USING_STRING|CODEC_UTF16|DATA_INDIRECT|NO_CONTEXT|FIXING_SPLIT; // jichi 4/25/2015: Fixing split
            hp.offset=get_stack(1);

            //GROWL_DWORD3(hp.address, processStartAddress, hp.address - processStartAddress);

            //DWORD call = Util::FindCallAndEntryAbs(hp.address, processStopAddress - processStartAddress, processStartAddress, 0xec81); // zero
            //DWORD call = Util::FindCallAndEntryAbs(hp.address, processStopAddress - processStartAddress, processStartAddress, 0xec83); // zero
            //DWORD call = Util::FindCallAndEntryAbs(hp.address, processStopAddress - processStartAddress, processStartAddress, 0xec8b55); // zero
            //GROWL_DWORD3(call, processStartAddress, call - processStartAddress);

            ConsoleOutput("INSERT GXP");
            

            // jichi 5/13/2015: Disable hooking to GetGlyphOutlineW
            // FIXME: GetGlyphOutlineW can extract name, but GXP cannot
            ConsoleOutput("GXP: disable GDI hooks");
            
            return NewHook(hp, "GXP");
          }
        }
        i++;
      }
  }
  //ConsoleOutput("Unknown GXP engine.");
  ConsoleOutput("GXP: failed");
  return false;
}

static bool InsertGXP2Hook()
{
  // pattern = 0x0f5bc9f30f11442444f30f114c2448e8
  const BYTE bytes[] = {
     0x0f,0x5b,                      // 00A78845   0F5B             ???                                      ; Unknown command
     0xc9,                           // 00A78847   C9               LEAVE
     0xf3,0x0f,0x11,0x44,0x24, 0x44, // 00A78848   F3:0F114424 44   MOVSS DWORD PTR SS:[ESP+0x44],XMM0
     0xf3,0x0f,0x11,0x4c,0x24, 0x48, // 00A7884E   F3:0F114C24 48   MOVSS DWORD PTR SS:[ESP+0x48],XMM1
     0xe8 //37040000                 // 00A78854   E8 37040000      CALL .00A78C90  ; jichi: here's the target function to hook to, text char on the stack[0]
  };
  enum { addr_offset = sizeof(bytes) - 1 }; // 0x00a78854 - 0x00a78845
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
  if (!addr) {
    ConsoleOutput("GXP2: pattern not found");
    return false;
  }

  HookParam hp;
  hp.address = addr + addr_offset;
  hp.type = CODEC_UTF16|NO_CONTEXT|DATA_INDIRECT|FIXING_SPLIT|USING_STRING;
  ConsoleOutput("INSERT GXP2");
  
  ConsoleOutput("GXP: disable GDI hooks");
  
  return NewHook(hp, "GXP2");
}

bool InsertGXPHook()
{
  // GXP1 and GXP2 are harmless to each other
  bool ok = InsertGXP1Hook();
  ok = InsertGXP2Hook() || ok;
  return ok;
}
#include"util/textunion.h"
namespace { // unnamed

ULONG moduleBaseAddress_; // saved only for debugging purposes

bool isBadText(LPCWSTR text)
{
  return text[0] <= 127 || text[::wcslen(text) - 1] <= 127 // skip ascii text
      || ::wcschr(text, 0xff3f); // Skip system text containing: _
}

namespace ScenarioHook1 { // for old GXP1
namespace Private {
  TextUnionW *arg_,
             argValue_;
  bool hookBefore(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
  { 
    
    static std::wstring text_; // persistent storage, which makes this function not thread-safe
      
    auto arg = (TextUnionW *)(s->stack[0] + 4); // arg1 + 0x4
    if (!arg->isValid())
      return 0;

    auto text = arg->getText();
    if (isBadText(text))
      return 0;
    return write_string_overwrite(data1,len,text);
  }
  void hook2a(hook_stack*s,void* data1, size_t len)
  { 
     auto text_=new wchar_t[len/2+1];
     auto n=std::wstring((LPWSTR)data1,len/2);
     wcscpy(text_,n.c_str());
    auto arg = (TextUnionW *)(s->stack[0] + 4); // arg1 + 0x4
    arg_ = arg;
    argValue_ = *arg;

    arg->setText(text_); 
    //if (arg->size)
    //  hashes_.insert(Engine::hashWCharArray(arg->text, arg->size));
    // return true;
  }
  bool hookAfter(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
  {
    if (arg_) {
      *arg_ = argValue_;
      arg_ = nullptr;
    }
    return 0;
  }
} // namespace Private

/**
 *  Sample game: 塔の下のエクセルキトゥス体験版
 *  Executable description shows "AVGEngineV2"
 *
 *  Debugging method: Find the fixed text address, and check when it is being modified
 *
 *  Scenario caller, text in the struct of arg1 + 0x4.
 */
bool attach(ULONG startAddress, ULONG stopAddress)
{
  const uint8_t bytes[] = {
    0xeb, 0x02,           // 01313bb6   eb 02            jmp short trial.01313bba
    0x8b,0xc5,            // 01313bb8   8bc5             mov eax,ebp
    0x8b,0x54,0x24, 0x18, // 01313bba   8b5424 18        mov edx,dword ptr ss:[esp+0x18]
    0x8d,0x0c,0x51,       // 01313bbe   8d0c51           lea ecx,dword ptr ds:[ecx+edx*2]
    0x8d,0x1c,0x3f        // 01313bc1   8d1c3f           lea ebx,dword ptr ds:[edi+edi]
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr)
    return addr;
  addr = MemDbg::findEnclosingAlignedFunction(addr);
  if (!addr)
    return addr;
  //return winhook::hook_before(addr, Private::hookBefore);

  int count = 0;
  auto fun = [&count](ULONG addr) -> bool {
     auto retaddr=addr+5;
      
     if (*(DWORD *)retaddr!= 0x0c244c8a)
      return true;
    if (*(BYTE *)retaddr == 0x4f ||
        (*(DWORD *)retaddr & 0x00ff00ff) == 0x0024008b) // skip truncated texts
      return true;
    HookParam hp;
    hp.address=addr;
    hp.hook_before=Private::hookBefore;
    hp.hook_after=Private::hook2a;
    hp.type=EMBED_ABLE|CODEC_UTF16|USING_STRING;
    hp.newlineseperator=L"%r";
    hp.hook_font=F_GetGlyphOutlineW;
    bool succ=NewHook(hp,"EmbedGXP");
    hp.address=addr+5;
    hp.hook_before=Private::hookAfter;
    succ|=NewHook(hp,"EmbedGXP");
    count+=1;
    return succ; // replace all functions
  };
  MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress); 
  return count;
}
} // namespace ScenarioHook1

namespace ScenarioHook2 { // for new GXP2
namespace Private {
   TextUnionW *arg_,
             argValue_;
  bool hookBefore(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
  {
    static std::wstring text_; // persistent storage, which makes this function not thread-safe
    auto arg = (TextUnionW *)s->stack[0]; // arg1
    if (!arg->isValid())
      return 0;
 
    auto text = arg->getText();
    if (isBadText(text))
      return 0; 
    return write_string_overwrite(data1,len,text);
    
  }
    void hook2a(hook_stack*s,void* data1, size_t len)
  { 
    auto text_=new wchar_t[len/2+1];
     auto n=std::wstring((LPWSTR)data1,len/2);
     wcscpy(text_,n.c_str());
auto arg = (TextUnionW *)s->stack[0]; // arg1 + 0x4
    arg_ = arg;
    argValue_ = *arg;
 
    arg->setText(text_); 
  }
         
  bool hookAfter(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
  {
    if (arg_) {
      *arg_ = argValue_;
      arg_ = nullptr;
    }
    return 0;
  }
} // namespace Private
 
bool attach(ULONG startAddress, ULONG stopAddress)
{
  const uint8_t bytes[] = {
    0x8d,0x04,0x3f, // 08159fd  |. 8d043f         lea eax,dword ptr ds:[edi+edi]	; jichi: edi *= 2 for wchar_t
    0x50,           // 0815a00  |. 50             push eax	; jichi: size
    0x8d,0x04,0x4b, // 0815a01  |. 8d044b         lea eax,dword ptr ds:[ebx+ecx*2]
    0x50,           // 0815a04  |. 50             push eax	; jichi: source text
    0x52            // 0815a05  |. 52             push edx	; jichi: target text
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr)
    return addr;
  addr = MemDbg::findEnclosingAlignedFunction(addr);
  if (!addr)
    return addr;
  //return winhook::hook_before(addr, Private::hookBefore);

  int count = 0;
  auto fun = [&count](ULONG addr) -> bool {
     auto retaddr=addr+5;
     if (*(WORD *)retaddr != 0x458a)
      return true;
    if (*(BYTE *)retaddr == 0xa1)
      return true;
    HookParam hp;
    hp.address=addr;
    hp.hook_before=Private::hookBefore;
    hp.hook_after=Private::hook2a;
    hp.type=EMBED_ABLE|CODEC_UTF16|USING_STRING;
    hp.newlineseperator=L"%r";
    hp.hook_font=F_GetGlyphOutlineW;
    bool succ=NewHook(hp,"EmbedGXP2");
    hp.address=addr+5;
    hp.hook_before=Private::hookAfter;
    succ|=NewHook(hp,"EmbedGXP2");
    count+=1;
    return succ; // replace all functions
  };
  MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress); 
  return count;
}
} // namespace ScenarioHook2
/*
namespace PopupHook1 { // only for old GXP1 engine
namespace Private {
  bool hookBefore(winhook::hook_stack *s)
  {
    static std::wstring text_; // persistent storage, which makes this function not thread-safe
    auto arg = (TextUnionW *)(s->ecx + 0x1ec); // [ecx + 0x1ec]
    if (!arg->isValid())
      return true;
    auto text = arg->getText();
    if (isBadText(text))
      return true;
    auto retaddr = s->stack[0];
    auto reladdr = retaddr - moduleBaseAddress_;
    enum { role = Engine::OtherRole };
    std::wstring oldText = std::wstring(text),
            newText = EngineController::instance()->dispatchTextWSTD(oldText, role, reladdr);
    if (newText == oldText)
      return true;
    text_ = newText;
    arg->setText(text_);
    return true;
  }
} // Private
 bool attach(ULONG startAddress, ULONG stopAddress)
{
  const uint8_t bytes[] = {
    0x8b,0x86, 0xec,0x01,0x00,0x00, // 001092a9   8b86 ec010000    mov eax,dword ptr ds:[esi+0x1ec] ; jichi: text in eax
    0xeb, 0x06,                     // 001092af   eb 06            jmp short trial.001092b7
    0x8d,0x86, 0xec,0x01,0x00,0x00, // 001092b1   8d86 ec010000    lea eax,dword ptr ds:[esi+0x1ec]
    0x0f,0xb7,0x14,0x78,            // 001092b7   0fb71478         movzx edx,word ptr ds:[eax+edi*2]
    0x52                            // 001092bb   52               push edx
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr)
    return false;
  addr = MemDbg::findEnclosingAlignedFunction(addr);
  if (!addr)
    return false;
  return winhook::hook_before(addr, Private::hookBefore);
  // Function called at runtime
  //int count = 0;
  //auto fun = [&count](ULONG addr) -> bool {
  //  auto before = std::bind(Private::hookBefore, addr + 5, std::placeholders::_1);
  //  count += winhook::hook_both(addr, before, Private::hookAfter);
  //  return true; // replace all functions
  //};
  //MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress);
  //DOUT("call number =" << count);
  //return count;
}
} // namespace PopupHook1

namespace OtherHook { // for all GXP engines
namespace Private {
  bool hookBefore(winhook::hook_stack *s)
  {
    static std::wstring text_;
    auto text = (LPCWSTR)s->stack[3]; // arg3
    if (!text || !*text)
      return true;
    auto retaddr = s->stack[0];
    auto reladdr = retaddr - moduleBaseAddress_;
    enum { role = Engine::OtherRole };
    std::wstring oldText = std::wstring(text),
            newText = EngineController::instance()->dispatchTextWSTD(oldText, role, reladdr);
    if (newText.empty() || oldText == newText)
      return true;
    strReplace(newText, L"%r", L"\n");
    //newText.replace("%r", "\n");
    text_ = newText;
    s->stack[3] = (ULONG)text_.c_str();
    return true;
  }
} // Private
 bool attach(ULONG startAddress, ULONG stopAddress)
{
  const uint8_t bytes[] = {
    0x99,           // 014d45ae   99               cdq
    0x2b,0xc2,      // 014d45af   2bc2             sub eax,edx
    0xd1,0xf8,      // 014d45b1   d1f8             sar eax,1
    0x03 //,0xf0,   // 014d45b3   03f0             add esi,eax
  };
  int count = 0;
  auto fun = [&count](ULONG addr) -> bool {
    count +=
        (addr = MemDbg::findEnclosingAlignedFunction(addr))
        && winhook::hook_before(addr, Private::hookBefore);
    return true;
  };
  MemDbg::iterFindBytes(fun, bytes, sizeof(bytes), startAddress, stopAddress);
  DOUT("call number =" << count);
  return count;
}
} // namespace OtherHook
*/

bool  attach()
{
  ULONG startAddress=processStartAddress, stopAddress=processStopAddress;
   
  moduleBaseAddress_ = startAddress; // used to calculate reladdr for debug purposes
  if (ScenarioHook2::attach(startAddress, stopAddress)) {
    
  } else if (ScenarioHook1::attach(startAddress, stopAddress)) {
     
    //  (PopupHook1::attach(startAddress, stopAddress));
      
  } else
    return false;
   // (OtherHook::attach(startAddress, stopAddress))
    
  return true;
}

} // unnamed namespace
bool GXP::attach_function() {  
    auto _=InsertGXPHook();
    return attach()||_;
}