/*
 *  Copyright (C) 2005-2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#include "utils/log.h"
#include "dll_util.h"

#ifdef TARGET_WINDOWS
#include "platform/win32/CharsetConverter.h"
#include <windows.h>
#endif
#include <stdlib.h>
#include <string.h>

#ifdef _cplusplus
extern "C"
{
#endif

static int iDllDummyOutputCall = 0;
void dll_dummy_output(char* dllname, char* funcname)
{
  CLog::Log(LOGERROR, "%s: Unresolved function called (%s), Count number %d", dllname, funcname, ++iDllDummyOutputCall);
}

// this piece of asm code only calls dll_dummy_output(s, s) and will return NULL
unsigned char dummy_func[] = {
    0x55,                            // push        ebp
    0x8b, 0xec,                      // mov         ebp,esp
    0xa1, 0, 0, 0, 0,                // mov         eax,dword ptr [0 0 0 0]
    0x50,                            // push        eax
    0xa1, 0, 0, 0, 0,                // mov         eax,dword ptr [0 0 0 0]
    0x50,                            // push        eax
    0xff, 0x15, 0, 0, 0, 0,          // call        dword ptr[dll_dummy_output]
    0x83, 0xc4, 0x08,                // add         esp,8
    0x33, 0xc0,                      // xor         eax,eax       // return NULL
    0x5d,                            // pop         ebp
    0xc3                            // ret
  };

/* Create a new callable function
 * This allocates a few bytes with the next content
 *
 * 1 function in assembly code (dummy_func)
 * 2 datapointer               (pointer to dll string)
 * 3 datapointer               (pointer to function string)
 * 4 datapointer               (pointer to function string)
 * 5 string                    (string of chars representing dll name)
 * 6 string                    (string of chars representing function name)
 */
uintptr_t create_dummy_function(const char* strDllName, const char* strFunctionName)
{
  size_t iFunctionSize = sizeof(dummy_func);
  size_t iDllNameSize = strlen(strDllName) + 1;
  size_t iFunctionNameSize = strlen(strFunctionName) + 1;

  // allocate memory for function + strings + 3 x 4 bytes for three datapointers
  char* pData = (char*)malloc(iFunctionSize + 12 + iDllNameSize + iFunctionNameSize);
  if (!pData)
    return 0;

  char* offDataPointer1 = pData + iFunctionSize;
  char* offDataPointer2 = pData + iFunctionSize + 4;
  char* offDataPointer3 = pData + iFunctionSize + 8;
  char* offStringDll = pData + iFunctionSize + 12;
  char* offStringFunc = pData + iFunctionSize + 12 + iDllNameSize;

  // 1 copy assembly code
  memcpy(pData, dummy_func, iFunctionSize);

  // insert pointers to datapointers into assembly code (fills 0x00000000 in dummy_func)
  *(int*)(pData + 4) = (intptr_t)offDataPointer1;
  *(int*)(pData + 10) = (intptr_t)offDataPointer2;
  *(int*)(pData + 17) = (intptr_t)offDataPointer3;

  // 2 fill datapointer with pointer to 5 (string)
  *(int*)offDataPointer1 = (intptr_t)offStringFunc;
  // 3 fill datapointer with pointer to 6 (string)
  *(int*)offDataPointer2 = (intptr_t)offStringDll;
  // 4 fill datapointer with pointer to dll_dummy_output
  *(int*)offDataPointer3 = (intptr_t)dll_dummy_output;

  // copy arguments to 5 (string) and 6 (string)
  memcpy(offStringDll, strDllName, iDllNameSize);
  memcpy(offStringFunc, strFunctionName, iFunctionNameSize);

  return (uintptr_t)pData;
}

uintptr_t get_win_function_address(const char* strDllName, const char* strFunctionName)
{
#ifdef TARGET_WINDOWS_DESKTOP
  using KODI::PLATFORM::WINDOWS::ToW;
  auto strDllNameW = ToW(strDllName);
  HMODULE handle = GetModuleHandle(strDllNameW.c_str());
  if(handle == nullptr)
  {
    handle = LoadLibrary(strDllNameW.c_str());
  }
  if(handle != nullptr)
  {
    auto pGNSI = reinterpret_cast<uintptr_t>(GetProcAddress(handle, strFunctionName));
    if(pGNSI != NULL)
      return pGNSI;

    FreeLibrary(handle);
  }
#endif
  return 0;
}

#ifdef _cplusplus
}
#endif
