#include "debug.h"
#include <windows.h>

#include "cenk.h"
#include "cever_deps.h"
#include "kernelmisc.h"


#include "stringutils.h"
#include "vectorutils.h"
#include "ptrutils.h"

#define LOADLIBEX 148
/*
 *  this is an experiment to insert a hook into a system call handler
 *
 *  currently it does not work yet.
 *
 *  
 *  other things I may try, is to add a OEMLoadModule handler
 *  like in ./PLATFORM/EMULATOR/KERNEL/HAL/key1024.c:
 *
 *  or implement the 'SH_PATCHER'-api, as used by 
 *     .\PRIVATE\WINCEOS\COREOS\NK\KERNEL\loader.c 
 *
 */
void DumpApiSets()
{
    CINFO **sets= (CINFO **)KData.aInfo[KINX_APISETS];
/*
    debug("\n\napisets= %08lx\n", sets);
    for (int apinr=0 ; apinr<32 ; apinr++) {
        if (sets[apinr]==NULL) {
            printf("CINFO=%08lx : NULL\n", sets[apinr]);
            continue;
        }
        debug("CINFO=%08lx '%hs' dsp=%02x tp=%02x n=%d fn=%08lx sig=%08lx svr=%08lx\n",
                sets[apinr], std::string(sets[apinr]->acName, sets[apinr]->acName+4).c_str(),
                sets[apinr]->disp, sets[apinr]->type, sets[apinr]->cMethods, sets[apinr]->ppfnMethods,
                sets[apinr]->pdwSig, sets[apinr]->pServer);
        if (sets[apinr]->ppfnMethods) {
            for (int callnr=0 ; callnr<10 && callnr<sets[apinr]->cMethods ; callnr++) {
                debug(" %d=%08lx", callnr, sets[apinr]->ppfnMethods[callnr]);
            }
        }
        debug("\n");

    }
*/
    debug("%08lx:%08lx:fn148(%08lx)=%hs\n", sets[0], sets[0]->ppfnMethods, sets[0]->ppfnMethods[LOADLIBEX], hexdump((BYTE*)sets[0]->ppfnMethods[LOADLIBEX], 7, 4).c_str());
    debug("\n\n");
}

DWORD AddModuleBase(void*p)
{
    HDATA *hd= cvHandle2HDataPtr((HANDLE)GetCurrentProcessId());
    PROCESS *proc= (PROCESS*)hd->pvObj;
    return DWORD(p)|proc->dwVMBase;
}
bool setNameEquals(const CINFO* set, const char *name)
{
    return memcmp(set->acName, name, 4)==0;
}
bool setNameEquals(const CINFO* set1, const CINFO *set2)
{
    return memcmp(set1->acName, set2->acName, 4)==0;
}
bool FindApiSet(const char *name, PCINFO& pset, DWORD& physaddr)
{
    CINFO **sets= (CINFO **)KData.aInfo[KINX_APISETS];
    for (int i=0 ; i<NUM_SYSTEM_SETS ; i++)
        if (setNameEquals(sets[i], name)) {
            pset= sets[i];
            physaddr= (DWORD)sets[i];
            debug("found %s at %08lx -> %08lx\n", name, &sets[i], sets[i]);
            return true;
        }

    debug("FindApiSet: could not find apiset %s\n", name);
    return false;
}
bool SetApiSet(const PCINFO set, DWORD physaddr)
{
    CINFO **sets= (CINFO **)KData.aInfo[KINX_APISETS];
    for (int i=0 ; i<NUM_SYSTEM_SETS ; i++)
        if (setNameEquals(sets[i], set)) {
            sets[i]= (CINFO*)physaddr;
            return true;
        }

    debug("SetApiSet: could not find apiset\n");
    return false;
}
DWORD GetNrOfPages(DWORD dwBytes)
{
    return (dwBytes+KData.aInfo[KINX_PAGESIZE]-1)/KData.aInfo[KINX_PAGESIZE];
}

bool DuplicateApi(const char *name, PCINFO& oldset, DWORD& physold, PCINFO& newset, DWORD& physnew)
{
    if (!FindApiSet(name, oldset, physold)) {
        debug("DuplicateApi: FindApiSet\n");
        return false;
    }

    DWORD dwSize= sizeof(CINFO)+oldset->cMethods*sizeof(DWORD);
    // todo: this does not allocate a contigeous memory block!!!
    newset= (CINFO*)LocalAlloc(LPTR, dwSize);
    if (newset==NULL) {
        error("DuplicateApi: LocalAlloc");
        return false;
    }
    // NOTE: if newset+dwSize is on a different page than newset
    //       this will not work.
    physnew= AddModuleBase(newset);

    debug("vmem=%08lx pmem=%08lx\n", newset, physnew);

    DWORD *pfnMethods= (DWORD*)&newset[1];

    memcpy(newset, oldset, sizeof(CINFO));
    memcpy(pfnMethods, oldset->ppfnMethods, oldset->cMethods*sizeof(DWORD));

    newset->ppfnMethods= (PFNVOID*)(physnew+sizeof(CINFO));

    return true;
}
PFNVOID ChangeHook(CINFO *newset, int apinr, PFNVOID newfn, int codesize)
{
    PFNVOID oldfn= newset->ppfnMethods[apinr];

    // NOTE: if newfn+dwSize is on a different page than newfn
    //       this will not work.
    DWORD physfn= AddModuleBase(newfn);

    debug("vfunc: %08lx %hs\n", newfn, hexdump((BYTE*)newfn, codesize, 4).c_str());
    debug("pfunc: %08lx %hs\n", physfn, hexdump((BYTE*)physfn, codesize, 4).c_str());

    debug("changing %08lx at %08lx to %08lx\n", oldfn, &newset->ppfnMethods[apinr], physfn);
    const_cast<PFNVOID>(newset->ppfnMethods[apinr])= (PFNVOID)physfn;

    return oldfn;
}
// 
// bigest problem seems to be how to pass data to the kernel
// address space.
//
// but o.t.h this 'return invalid' also does not work.
//
typedef HANDLE (*PFNOrigLoadLibraryEx)(LPCWSTR lpszFileName, DWORD dwFlags, LPDWORD pdwCnt);
PFNOrigLoadLibraryEx OrigLoadLibraryEx;
HANDLE HookLoadLibraryEx(LPCWSTR lpszFileName, DWORD dwFlags, LPDWORD pdwCnt)
{
    return INVALID_HANDLE_VALUE;
/*
    HANDLE hSem= CreateSemaphore(NULL, 0, 0, L"HookLoadLibCalls");
    MessageBox(0, L"asada", L"qwqwqwe", 0);
    ReleaseSemaphore(hSem, 1, NULL);
    CloseHandle(hSem);
    
    return OrigLoadLibraryEx(lpszFileName, dwFlags, pdwCnt);
*/
}
int WINAPI WinMain( HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
    DebugSetLogfile("hookapi.log");
    KernelMode _km;

    DumpApiSets();

    HANDLE hSem= CreateSemaphore(NULL, 0, 0, L"HookLoadLibCalls");

    PCINFO oldset; DWORD physold;
    PCINFO newset; DWORD physnew;
    if (!DuplicateApi("Wn32", oldset, physold, newset, physnew)) {
        debug("error duplication api\n");
        return 1;
    }

    debug("vold=%08lx pold=%08lx  vnew=%08lx pnew=%08lx\n", 
            oldset, physold, newset, physnew);

    debug("created new apiset: %d methods\n", newset->cMethods);

    OrigLoadLibraryEx= (PFNOrigLoadLibraryEx )ChangeHook(newset, LOADLIBEX, (PFNVOID)HookLoadLibraryEx, PTR_DIFF(HookLoadLibraryEx, WinMain));

    if (!SetApiSet(newset, physnew)) {
        debug("error switching apiset\n");
        return 1;
    }

    DumpApiSets();

    HMODULE hLib= LoadLibrary(L"ril.dll");
    if (hLib==NULL || hLib==INVALID_HANDLE_VALUE)
        error("loadlib");
    else {
        debug("loaded lib: %08lx\n", hLib);
        FreeLibrary(hLib);
    }

    if (!SetApiSet(oldset, physold)) {
        debug("error restoring apiset\n");
        return 1;
    }
    DumpApiSets();

    int libs=0;
    while (WaitForSingleObject(hSem, 0)==WAIT_OBJECT_0)
        libs++;

    CloseHandle(hSem);

    debug("test  %d\n", libs);

    return 0;
}
