#//
#// Format: (triple slashes) func=function_name entry=entry_value purge=val retval=val

#// func
#//      specifies the function name. No need to specify the module name since
#//      it is deduced from the name of the script file. The file name has
#//      always the following format: api_modulename.idc
#//
#// entry
#//      module_name.function_name means to forward to another module
#//      idc_function_name         means to call the specified IDC function
#//                                The return value of the script controls the execution:
#//                                  - zero: means to continue execution
#//                                  - non-zero: suspend the application
#//      this attribute is optional.
#//      If it is missing, a dummy stub will be generated by IDA. The next
#//      two attributes are used by the stub generator:
#// purge
#//      How many bytes to purge from the stack. Do not specify this value
#//      if you are forwarding to another module.
#// retval
#//      The return value (this attribute is used only to generate stubs)
#//
#//

#//--------------------------------------------------------------------------
#// These are system definitions.
#// !! Do not change them unless you know what you are doing !!
#///func=GetModuleFileNameA entry=bochsys32._BxGetModuleFileNameA@12
#///func=GetModuleFileNameW entry=bochsys32._BxGetModuleFileNameW@12
#///func=GetModuleHandleA entry=bochsys32._BxGetModuleHandleA@4
#///func=GetModuleHandleW entry=bochsys32._BxGetModuleHandleW@4
#///func=GetCommandLineA entry=bochsys32._BxWin32GetCommandLineA@0
#///func=GetCommandLineW entry=bochsys32._BxWin32GetCommandLineW@0
#///func=LoadLibraryA entry=bochsys32._BxLoadLibraryA@4
#///func=LoadLibraryW entry=bochsys32._BxLoadLibraryW@4
#///func=GetEnvironmentStrings entry=bochsys32._BxWin32GetEnvironmentStringsA@0
#///func=GetEnvironmentStringsA entry=bochsys32._BxWin32GetEnvironmentStringsA@0
#///func=GetEnvironmentStringsW entry=bochsys32._BxWin32GetEnvironmentStringsW@0
#///func=GetProcAddress entry=bochsys32._BxGetProcAddress@8
#///func=ExitProcess entry=bochsys32._BxExitProcess@4
#///func=VirtualAlloc entry=bochsys32._BxVirtualAlloc@16
#///func=VirtualFree entry=bochsys32._BxVirtualFree@12
#///func=VirtualProtect entry=bochsys32._BxVirtualProtect@16
#///func=GetTickCount entry=bochsys32._BxGetTickCount@0
#///func=GetLastError entry=ntdll.RtlGetLastWin32Error
#///func=SetLastError entry=ntdll.RtlSetLastWin32Error
#///func=TlsAlloc entry=bochsys32._BxWin32TlsAlloc@0
#///func=TlsFree entry=bochsys32._BxWin32TlsFree@4
#///func=TlsGetValue entry=bochsys32._BxWin32TlsGetValue@4
#///func=TlsSetValue entry=bochsys32._BxWin32TlsSetValue@8
#///func=FlsAlloc entry=bochsys32._BxWin32FlsAlloc@4
#///func=FlsFree entry=bochsys32._BxWin32TlsFree@4
#///func=FlsGetValue entry=bochsys32._BxWin32TlsGetValue@4
#///func=FlsSetValue entry=bochsys32._BxWin32TlsSetValue@8
#///func=HeapCreate retval=1
#///func=HeapFree retval=1 purge=12
#///func=InitializeCriticalSectionAndSpinCount retval=1
#///func=lstrcpyA entry=bochsys32._BxStrCpyA@8
#///func=lstrcpyW entry=bochsys32._BxStrCpyW@8
#///func=lstrcatA entry=bochsys32._BxStrCatA@8
#///func=lstrcatW entry=bochsys32._BxStrCatW@8

#// example: user dll implementation: func=GetCommandLineA entry=mydll.MyGetCommandLineA purge=0

# HMODULE WINAPI LoadLibraryExA(LPCSTR lpFileName, HANDLE hFile, DWORD dwFlags);
#///func=LoadLibraryExA entry=k32_LoadLibraryExA
def k32_LoadLibraryExA():
  hFile = BochsGetParam(2)
  if hFile != 0:
    cpu.eax = 0
    return 0
  lpFileName = get_strlit_contents(BochsGetParam(1), -1, STRTYPE_C)
  dwFlags    = BochsGetParam(3)
  # Since Bochs plugin does not support dynamic DLL loading, we simply return the module handle.
  # (the DLL must be declared in startup.idc so it is pre-loaded)
  cpu.eax = BochsGetModuleHandle(lpFileName)
  return 0 # continue execution

#///func=WideCharToMultiByte entry=k32_WideCharToMultiByte
def k32_WideCharToMultiByte():
  len_wide  = BochsGetParam(4)
  len_multi = BochsGetParam(6) #  int cbMultiByte,
  if len_wide == 0:
    cpu.eax = 0
    return 0

  if len_multi == 0:
    len = 0
  elif len_wide == -1 or len_wide > len_multi:
    len = len_multi
  else:
    len = len_wide

  p_wide    = BochsGetParam(3) # LPCWSTR lpWideCharStr
  p_multi   = BochsGetParam(5) # LPSTR lpMultiByteStr

  i=0
  if len == 0:
    while True:
      if get_wide_byte(p_wide) == 0:
        break;
      p_wide += 2
      i+=1
  else:
    while i<len:
      BochsPatchDbgDword(p_multi, get_wide_byte(p_wide))
      p_wide  += 2
      p_multi += 1
      i += 1
  cpu.eax = i
  return 0

#///func=Beep entry=beep purge=8
def beep():
  param1 = BochsGetParam(1)
  param2 = BochsGetParam(2)

  msg("I am Beep(%d, %d)\n" % (param1, param2) )

  # The emulated function returns 1:
  set_reg_value(1, "EAX")

  # Our return value controls execution of the debugged application:
  #   1 = suspend execution (inside IDACALL)
  #   0 = continue transparently
  return 0

#///func=GlobalAlloc entry=k32_GlobalAlloc purge=8
def k32_GlobalAlloc():
  # Redirect GlobalAlloc -> VirtualAlloc
  set_reg_value(BochsVirtAlloc(0, BochsGetParam(2), 1), "EAX")
  return 0

#///func=GlobalFree entry=k32_GlobalFree purge=4
def k32_GlobalFree():
  # Redirect GlobalFree -> VirtualFree
  set_reg_value(BochsVirtFree(BochsGetParam(1), 0), "EAX")
  return 0

#///func=GetCurrentThread entry=k32_GetCurrentThread purge=0
def k32_GetCurrentThread():
  set_reg_value(-2, "EAX")
  return 0

STD_INPUT_HANDLE  = 0xFFFFFFF6
STD_OUTPUT_HANDLE = 0xFFFFFFF5
STD_ERROR_HANDLE  = 0xFFFFFFF4

# BOOL WINAPI WriteFile(HANDLE hFile,LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,LPDWORD lpNumberOfBytesWritten,LPOVERLAPPED lpOverlapped);
#///func=WriteFile entry=k32_writefile
def k32_writefile():
  hfile = BochsGetParam(1);
  ea   = BochsGetParam(2);
  len   = BochsGetParam(3);

  if hfile == STD_OUTPUT_HANDLE or hfile == STD_ERROR_HANDLE:
    print("WriteFile(STDOUT): %s\n" % get_strlit_contents(ea, len, STRTYPE_C))
  else:
    of = idaapi.fopenM("writefile.bin")
    if of:
      pos = idaapi.qfsize(of)
      retval = idaapi.base2file(of, pos, ea, ea+len)
      print("fp=%X, written %d bytes"%(hfile, len))
      idaapi.eclose(of)

  buf = BochsGetParam(4)
  if buf:
    BochsPatchDbgDword(get_wide_dword(buf), len) # save the number of bytes written
  cpu.eax = 1 # success
  return 0

#///func=get_current_thread entry=k32_GetCurrentThreadId
def k32_GetCurrentThreadId():
  cpu.eax = get_current_thread()
  return 0

#///func=GetStdHandle entry=k32_GetStdHandle
def k32_GetStdHandle():
  # return same value
  h = BochsGetParam(1)
  print("GetStdHandle(%08X)" % h)
  cpu.eax = h
  return 0

#///func=GlobalMemoryStatus entry=k32_GlobalMemoryStatus
def k32_GlobalMemoryStatus():
  """
  00000000 MEMORYSTATUS    struc
  00000000 dwLength        dd
  00000004 dwMemoryLoad    dd
  00000008 dwTotalPhys     dd
  0000000C dwAvailPhys     dd
  00000010 dwTotalPageFile dd
  00000014 dwAvailPageFile dd
  00000018 dwTotalVirtual  dd
  0000001C dwAvailVirtual  dd
  00000020 MEMORYSTATUS    ends
  """

  ms = BochsGetParam(1)
  avail = BochsGetFreeMem()
  total = BochsGetMaxMem()

  BochsPatchDbgDword(ms + 0x08, total) # total phys
  BochsPatchDbgDword(ms + 0x0C, avail) # avail phys
  BochsPatchDbgDword(ms + 0x10, 0    ) # total page
  BochsPatchDbgDword(ms + 0x14, 0    ) # avail page
  BochsPatchDbgDword(ms + 0x18, total) # total virt
  BochsPatchDbgDword(ms + 0x1C, avail) # avail virt

  return 0
