// a kernel library which writes a stackdump for each exception
//
// printf "#\"loadklib\"stackdumper.dll" | pput -f -c "%CSIDL_STARTUP%\stackdumper.lnk"

//    area free in kaiser rom
// 802fa000-802fb000
// 802fc000-80300000
// 80308000-8031f000

#include <wtypes.h>
#include "cenk.h"
#include "printfformat.h"
#include "debuglogger.h"
//#include "kernelmisc.h"

// HDATA: {00: fwd,back, hValue, lock, 10: ref, *CINFO, pvObj, dwInfo }
// CINFO: {00: acName, 04:disp, 05:type, 06:cMethods, 08:ppfnMethods, 0c:pdwSig, 10:*PROCESS}


DWORD PhysToVirt(DWORD dwPhysOffset)
{
    // KSEG0_BASE .. KSEG1_BASE  = cached kernel physical
    for (DWORD ixPage= 0x800 ; ixPage < 0xa00 ; ixPage++)
    {
        DWORD dwEntry= FirstPT[ixPage]; // FirstPT is defined in nkarm.h
        if (((dwEntry&3)==2) 
            && ((dwEntry&0xfff00000)==(dwPhysOffset&0xfff00000)))
            return (dwPhysOffset&0xfffff)|(ixPage<<20);
    }
//    debug("Physical address %08lx is not mapped\n", dwPhysOffset);
    return 0;
}
DWORD PhysToVirtUC(DWORD dwPhysOffset)
{
    return PhysToVirt(dwPhysOffset)|0x20000000;
}

DWORD VirtToPhys(DWORD dwAddr)
{
    if (dwAddr==0)
        return INVALID_PHYSICAL_ADDRESS;

    if ((dwAddr<0x80000000)
#if _WIN32_WCE>=400
            || (IsSecureVa(dwAddr)) // IsSecureVa from pkfuncs.h
#endif
            )
    {
        //  fedcba9876543210fedcba9876543210
        //  0ssssssbbbbbbbbbpppp............
        // 
        //  sectiontable[sssssss]->memblock[bbbbbbbbb]->page[pppp]
        //     
        SECTION *pscn = 
#if _WIN32_WCE>=400
            (dwAddr & 0x80000000) ? (SECTION*)KInfoTable[KINX_NKSECTION] :
#endif
                SectionTable[dwAddr>>VA_SECTION];
        DWORD ixBlock = (dwAddr >> VA_BLOCK) & BLOCK_MASK;
        DWORD ixPage = (dwAddr >> VA_PAGE) & PAGE_MASK;
        MEMBLOCK *pmb = (*pscn)[ixBlock];

        if (pmb==NULL_BLOCK || pmb==RESERVED_BLOCK)
            return INVALID_PHYSICAL_ADDRESS;
        if (pmb->aPages[ixPage]==0 || pmb->aPages[ixPage]==BAD_PAGE)
            return INVALID_PHYSICAL_ADDRESS;

        return (pmb->aPages[ixPage]&0xfffff000) | (dwAddr&0xfff);
    }
    else {
        DWORD ixPage= (dwAddr>>20)&0xfff;
        DWORD dwEntry= FirstPT[ixPage]; // FirstPT is defined in nkarm.h
        if ((dwEntry&3)==2) {
            //  fedcba9876543210fedcba9876543210
            //  ssssssssssss.................... index in 1st ptbl
            //
            // section descriptor
            return (dwEntry&0xfff00000)|(dwAddr&0xfffff);
        }
        else if ((dwEntry&0xfffff)==0) {
            return INVALID_PHYSICAL_ADDRESS;
        }
        else if ((dwEntry&3)==1) {
            //  fedcba9876543210fedcba9876543210
            //  ............22222222............  index in 2nd ptbl
            // coarse page table entry
            DWORD phys_2nd_tlb= dwEntry&~0x3f;
            DWORD virt_2nd_tlb= PhysToVirtUC(phys_2nd_tlb);

            DWORD ix2ndPage= (dwAddr>>12)&0xff;
            DWORD dw2ndEntry= ((DWORD*)virt_2nd_tlb)[ix2ndPage];
            if ((dw2ndEntry&3)==1) {
                // large page
                //
                //  fedcba9876543210fedcba9876543210
                //  ............22222222............  index in 2nd ptbl
                //  ................llllllllllllllll  large page adress mask
                //
                // note that there is an overlap between used addressbits here
                // and used bits to index ix2ndpage.
                // -> virt_2nd_tlb[ix2ndPage&~0xf+i] for i= 0 ..15 must be equal
                return (dw2ndEntry&0xffff0000)|(dwAddr&0xffff);
            }
            else if ((dw2ndEntry&3)==2) {
                // small page
                //
                //  fedcba9876543210fedcba9876543210
                //  ............22222222............  index in 2nd ptbl
                //  ....................ssssssssssss  small page adress mask
                //
                return (dw2ndEntry&0xfffff000)|(dwAddr&0xfff);;
            }
            // tiny pages should only occur in fine 2nd level pages
            return INVALID_PHYSICAL_ADDRESS;
        }
        else if ((dwEntry&3)==3) {
            //  fedcba9876543210fedcba9876543210
            //  ............2222222222..........  index in 2nd ptbl
            // fine page table entry
            DWORD phys_2nd_tlb= dwEntry&~0xfff;
            DWORD virt_2nd_tlb= PhysToVirtUC(phys_2nd_tlb);

            DWORD ix2ndPage= (dwAddr>>10)&0x3ff;
            DWORD dw2ndEntry= ((DWORD*)virt_2nd_tlb)[ix2ndPage];
            if ((dw2ndEntry&3)==1) {
                // large page
                //
                //  fedcba9876543210fedcba9876543210
                //  ............2222222222..........  index in 2nd ptbl
                //  ................llllllllllllllll  large page adress mask
                //
                return (dw2ndEntry&0xffff0000)|(dwAddr&0xffff);
            }
            else if ((dw2ndEntry&3)==2) {
                // small page
                //
                //  fedcba9876543210fedcba9876543210
                //  ............2222222222..........  index in 2nd ptbl
                //  ....................ssssssssssss  small page adress mask
                //
                return (dw2ndEntry&0xfffff000)|(dwAddr&0xfff);;
            }
            else if ((dw2ndEntry&3)==3) {
                // tiny page
                //
                //  fedcba9876543210fedcba9876543210
                //  ............2222222222..........  index in 2nd ptbl
                //  ......................tttttttttt  tiny page address mask
                //
                return (dw2ndEntry&0xfffffc00)|(dwAddr&0x3ff);;

            }
            return INVALID_PHYSICAL_ADDRESS;
        }
        return INVALID_PHYSICAL_ADDRESS;
    }
}



bool isValidPtr(DWORD dwAddr)
{
    return VirtToPhys(dwAddr)!=INVALID_PHYSICAL_ADDRESS;
}




void dummy() { }

#define DECLARE_API(name, result, params) \
    typedef result (*name##_fn) params; \
    name##_fn name##_orig=(name##_fn)dummy;

#define CANHOOK(apinr,callnr) \
    ((SystemApiSets) && (SystemApiSets[apinr]) && (SystemApiSets[apinr]->ppfnMethods))

#define GETAPI(apinr,callnr,name) \
    name##_orig = (name##_fn)SystemApiSets[apinr]->ppfnMethods[callnr];

#define HOOKFUNCTION(name,result,params) \
    result name##_hook params
#define SETAPI(apinr,callnr,name) \
    set_api_method(apinr, callnr, (PFNVOID)name##_hook); \
    NKDbgPrintf(L"hooked api(%d,%d) : %s\n", apinr, callnr, L#name);

// note: all functions need to use the same nr of macro levels
// otherwise one will use the name -as-is-, while the other will use
// the name redefined to nameW ( as often done in windows.h for UNICODE apis )
#define DECLARE_HOOK(name, result, params) \
    DECLARE_API(name,result,params) \
    HOOKFUNCTION(name,result,params)
#define HOOKAPI(apinr,callnr,name) \
    if (CANHOOK(apinr,callnr)) { \
        GETAPI(apinr,callnr,name) \
        SETAPI(apinr,callnr,name) \
    } \
    else { \
        NKDbgPrintf(L"could not hook api(%d, %d) : %s\n", apinr, callnr, L#name); \
    }

#define ORIGAPIX(name) name##_orig
#define ORIGAPI(name) ORIGAPIX(name)

#define GETAPIX(apinr,callnr,name) GETAPI(apinr,callnr,name)

// use kernel versions of :
// SetLastError    0   88    f000fea0
DECLARE_API(SetLastError,void,(DWORD))
// GetLastError    0   89    f000fe9c
DECLARE_API(GetLastError,DWORD,())
// LeaveCriticalSection       f000ff1c    0   57  LeaveCritSec
DECLARE_API(LeaveCriticalSection,void, (LPCRITICAL_SECTION))
// EnterCriticalSection       f000ff20    0   56  TakeCritSec
DECLARE_API(EnterCriticalSection,void, (LPCRITICAL_SECTION))
// InitializeCriticalSection  f000fec8    0   78  CreateCrit
DECLARE_API(InitializeCriticalSection,HANDLE, (LPCRITICAL_SECTION))
// AllocPhysMem    0  168     f000fd60
DECLARE_API(AllocPhysMem,LPVOID,(DWORD cbSize, DWORD fdwProtect, DWORD dwAlignmentMask, DWORD dwFlags, PULONG pPhysicalAddress))
// GetTickCount    0   13     f000ffcc
DECLARE_API(GetTickCount,DWORD,())

// memcpy  - use compiler intrinsic ... how?
void mymemcpy(BYTE *dst, BYTE *src, DWORD n)
{
    while (n--)
        *dst++ = *src++;
}


#define SystemApiSets ((CINFO**)(KData.aInfo[KINX_APISETS]))
// f000fef0    0   68 
DECLARE_API(GetCallerProcess, HANDLE, ())
// f000ff08    0   62 
DECLARE_API(IsBadPtr, BOOL, (DWORD flag, LPBYTE ptr, DWORD len))


#if 0
typedef HANDLE (*CreateFile_t)(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); 
CreateFile_t MyCreateFile;

typedef BOOL (*CloseHandle_t)(HANDLE h);
CloseHandle_t MyCloseHandle;

typedef BOOL (*WriteFile_t)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
WriteFile_t MyWriteFile;

typedef BOOL (*SetFilePointer_t)(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod);
SetFilePointer_t MySetFilePointer;
#endif

//#define NKvDbgPrintf (*(void (*)(const WCHAR*msg, const void *lpParms))0xF000FFA4)

void init();

#define dwptr(p)  ((DWORD*)(p))
#define ProcessHandle(p)  ((p)?(p)->hProc:0)
#define ThreadHandle(p)  ((p)?(p)->hTh:0)

void NKDbgPrintf(const WCHAR *msg, ...);


BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
#ifdef BUFFERLOGGING
    logmsg(L"libklog: DllMain\n");
#endif
    if (ul_reason_for_call==0) {
        init();
    }
    NKDbgPrintf(L"libklog: hmod=%08lx reason=%d  res=%08lx\n", hModule, ul_reason_for_call, lpReserved);
    return TRUE;
}
extern "C" __declspec(dllexport) void dumpstack(int r0, ...)
{
    DWORD esp= (DWORD)(&esp)&~0x1F;
    DWORD stacktop= (esp|0xFFFF)+1;
    NKDbgPrintf(L"stackdump: r0=%08lx, &r0=%08lx, stack=%08lx-%08lx curproc=%08lx:%08lx, curthread=%08lx:%08lx\n",
            r0, &r0, esp, stacktop, 
            KData.pCurPrc, ProcessHandle(KData.pCurPrc),
            KData.pCurThd, ThreadHandle(KData.pCurThd));
    if (KData.pCurThd) {
        NKDbgPrintf(L"callstack: \n");
        for (CALLSTACK *cs= KData.pCurThd->pcstkTop ; cs ; cs= cs->pcstkNext)
        {
            NKDbgPrintf(L"  %08lx %08lx:%08lx %08lx %08lx %08lx %08lx:",
                cs->retAddr, cs->pprcLast,ProcessHandle(cs->pprcLast), cs->akyLast, cs->extra, cs->dwPrevSP, cs->dwPrcInfo);
            if (cs->dwPrevSP)
                NKDbgPrintf(L" %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
                    dwptr(cs->dwPrevSP)[0], dwptr(cs->dwPrevSP)[1], dwptr(cs->dwPrevSP)[2], dwptr(cs->dwPrevSP)[3],
                    dwptr(cs->dwPrevSP)[4], dwptr(cs->dwPrevSP)[5], dwptr(cs->dwPrevSP)[6], dwptr(cs->dwPrevSP)[7]);
            else
                NKDbgPrintf(L" -\n");
        }

    }
    while (esp<stacktop) {
        NKDbgPrintf(L"%08lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", 
                esp, dwptr(esp)[0], dwptr(esp)[1], dwptr(esp)[2], dwptr(esp)[3],
                dwptr(esp)[4], dwptr(esp)[5], dwptr(esp)[6], dwptr(esp)[7]);
        esp+=32;
    }
}

#define APIHOOKFMT  L"apihook:%08lx->%08lx"
#define APIHOOKARGS() ORIGAPI(GetCallerProcess)(), hCurProc

// f000ff2c    0   53 
DECLARE_HOOK(CreateProcess, BOOL, (LPCWSTR lpszImageName, LPCWSTR lpszCommandLine, LPSECURITY_ATTRIBUTES lpsaProcess, LPSECURITY_ATTRIBUTES lpsaThread, BOOL fInheritHandles, DWORD fdwCreate, LPVOID lpvEnvironment, LPWSTR lpszCurDir, LPSTARTUPINFO lpsiStartInfo, LPPROCESS_INFORMATION lppiProcInfo))
{
    BOOL result= ORIGAPI(CreateProcess)(lpszImageName, lpszCommandLine, lpsaProcess, lpsaThread, fInheritHandles, fdwCreate, lpvEnvironment, lpszCurDir, lpsiStartInfo, lppiProcInfo);
    NKDbgPrintf(APIHOOKFMT L" CreateProcess('%ls', '%ls' -> %08lx)= %08lx\n", APIHOOKARGS(),
            lpszImageName, lpszCommandLine, lppiProcInfo?lppiProcInfo->hProcess:0,
            result);
    return result;
}

// f000ffe0    0    8 LoadLibraryW
// f000fdb0    0  148 LoadLibraryEx
DECLARE_HOOK(LoadLibraryEx, HMODULE, (LPCSTR lpLibFileName, DWORD dwFlags, LPDWORD pdwCnt))
{
    HMODULE result= ORIGAPI(LoadLibraryEx)(lpLibFileName, dwFlags, pdwCnt);
    NKDbgPrintf(APIHOOKFMT L" LoadLibraryEx('%ls', %08lx, %08lx=%08lx)= %08lx\n", APIHOOKARGS(),
            lpLibFileName, dwFlags, pdwCnt, pdwCnt?*pdwCnt:0,
            result);
    return result;
}
// f000ffd8    0   10 GetProcAddressW
// f000ff04    0   63 GetProcAddrBits
// f000fdc0    0  144 GetProcAddressA
DECLARE_HOOK(GetProcAddress, LPVOID, (HMODULE hMod, LPCWSTR szProcname))
{
    LPVOID result= ORIGAPI(GetProcAddress)(hMod, szProcname);
    if (DWORD(szProcname)<0x10000)
        NKDbgPrintf(APIHOOKFMT L" GetProcAddress(%08lx, ord_%04d)= %08lx\n", APIHOOKARGS(), hMod, szProcname, result);
    else
        NKDbgPrintf(APIHOOKFMT L" GetProcAddress(%08lx, '%ls')= %08lx\n", APIHOOKARGS(), hMod, szProcname, result);
    return result;
}

// f000fe84    0   95 
DECLARE_HOOK(CreateFileMapping, HANDLE, (HANDLE hFile, LPSECURITY_ATTRIBUTES lpsa, DWORD flProtect, DWORD dwMaxSizeHigh, DWORD dwMaxSizeLow, LPCWSTR lpName))
{
    HANDLE result= ORIGAPI(CreateFileMapping)(hFile, lpsa, flProtect, dwMaxSizeHigh, dwMaxSizeLow, lpName);
    NKDbgPrintf(APIHOOKFMT L" CreateFileMapping(%08lx, %08lx, %08lx, %08lx, %08lx, %08lx:'%ls')= %08lx\n", APIHOOKARGS(),
            hFile, lpsa, flProtect, dwMaxSizeHigh, dwMaxSizeLow, lpName, lpName?lpName:L"",
            result);
    return result;
}

// f000ff48    0   46
DECLARE_HOOK(CloseProcOE, void, (DWORD bClose))
{
    ORIGAPI(CloseProcOE)(bClose);
    NKDbgPrintf(APIHOOKFMT L" CloseProcOE(%08lx)\n", APIHOOKARGS(),
            bClose);
}
// f000fef8    0   66
DECLARE_HOOK(KillAllOtherThreads, VOID, (void))
{
    ORIGAPI(KillAllOtherThreads)();
    NKDbgPrintf(APIHOOKFMT L" KillAllOtherThreads\n", APIHOOKARGS());
}
// f000fea4    0   87
DECLARE_HOOK(NKTerminateThread, void, (DWORD dwExitCode))
{
    ORIGAPI(NKTerminateThread)(dwExitCode);
    NKDbgPrintf(APIHOOKFMT L" NKTerminateThread(%08lx)\n", APIHOOKARGS(),
            dwExitCode);
}
// f000fe90    0   92
DECLARE_HOOK(CloseAllHandles, void, (void))
{
    ORIGAPI(CloseAllHandles)();
    NKDbgPrintf(APIHOOKFMT L" CloseAllHandles\n", APIHOOKARGS());
}
// f000fda4    0  151
DECLARE_HOOK(KillThreadIfNeeded, void, (void))
{
    ORIGAPI(KillThreadIfNeeded)();
    NKDbgPrintf(APIHOOKFMT L" KillThreadIfNeeded\n", APIHOOKARGS());
}
// f000ff28    0   54
DECLARE_HOOK(CreateThread, HANDLE, (LPSECURITY_ATTRIBUTES lpsa, DWORD cbStack, LPTHREAD_START_ROUTINE lpStartAddr, LPVOID lpvThreadParm, DWORD fdwCreate, LPDWORD lpIDThread))
{
    HANDLE result= ORIGAPI(CreateThread)(lpsa, cbStack, lpStartAddr, lpvThreadParm, fdwCreate, lpIDThread);
    NKDbgPrintf(APIHOOKFMT L" CreateThread(%08lx, %08lx, %08lx, %08lx, %08lx, %08lx)= %08lx\n", APIHOOKARGS(),
        lpsa, cbStack, lpStartAddr, lpvThreadParm, fdwCreate, lpIDThread, result);
    return result;
}
// f000ffdc    0    9 
DECLARE_HOOK(FreeLibrary, BOOL, (HANDLE hInst, LPDWORD pdwCnt))
{
    BOOL result= ORIGAPI(FreeLibrary)(hInst, pdwCnt);
    NKDbgPrintf(APIHOOKFMT L" FreeLibrary(%08lx, %08lx:%08lx)=%08lx\n", APIHOOKARGS(),
            hInst, pdwCnt, pdwCnt?*pdwCnt:0, result);
                
    return result;
}

// f000fbdc    1    9
DECLARE_HOOK(TerminateThread,BOOL,(HANDLE hThread, DWORD dwExitCode))
{
    BOOL result= ORIGAPI(TerminateThread)(hThread, dwExitCode);
    NKDbgPrintf(APIHOOKFMT L" TerminateThread(%08lx,%08lx)=%08lx\n", APIHOOKARGS(),
            hThread, dwExitCode, result);
    return result;
}
// f000f800    2    0
DECLARE_HOOK(ProcCloseHandle,BOOL,(HANDLE hProc))
{
    BOOL result= ORIGAPI(ProcCloseHandle)(hProc);
    NKDbgPrintf(APIHOOKFMT L" ProcCloseHandle(%08lx)=%08lx\n", APIHOOKARGS(),
            hProc, result);
    return result;
}
// f000f7f8    2    2
DECLARE_HOOK(TerminateProcess,BOOL,(HANDLE hProc, DWORD dwExitCode))
{
    BOOL result= ORIGAPI(TerminateProcess)(hProc, dwExitCode);
    NKDbgPrintf(APIHOOKFMT L" TerminateProcess(%08lx,%08lx)=%08lx\n", APIHOOKARGS(),
            hProc, dwExitCode, result);
    return result;
}
// f000ffc8    0   14
DECLARE_HOOK(OutputDebugString, void, (const WCHAR*msg))
{
#ifdef BUFFERLOGGING
    logmsg(msg);
#endif
    ORIGAPI(OutputDebugString)(msg);
}


// f000ffa4    0   23 
DECLARE_HOOK(NKvDbgPrintf, void, (const WCHAR*msg, const void *lpParms))
{
#ifdef BUFFERLOGGING
    vlogmsg(msg, lpParms);
#endif
    ORIGAPI(NKvDbgPrintf)(msg, lpParms);
}
#ifdef BUFFERLOGGING
// f000fed8    0   74
DECLARE_HOOK(ReadDebugLog, BOOL, (BYTE *pBuf, DWORD dwBufSize, DWORD *pnRead))
{
    // todo: translate pbuf and pnread to kspace
    //NKDbgPrintf(L"ReadDebugLog(%p, 0x%x, %p)\n", pBuf, dwBufSize, pnRead);
    return readlog(pBuf, dwBufSize, pnRead);
}
#endif
// f000fed8    7   11
DECLARE_HOOK(DeviceIoControl, BOOL, (HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuf, DWORD nInBufSize, LPVOID lpOutBuf, DWORD nOutBufSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped))
{
    // todo: translate pbuf and pnread to kspace
    //NKDbgPrintf(L"ReadDebugLog(%p, 0x%x, %p)\n", pBuf, dwBufSize, pnRead);
    DWORD t0= ORIGAPI(GetTickCount)();
    BOOL result= ORIGAPI(DeviceIoControl)(hDevice, dwIoControlCode, lpInBuf, nInBufSize, lpOutBuf, nOutBufSize, lpBytesReturned, lpOverlapped);

    NKDbgPrintf(APIHOOKFMT L" DeviceIoControl(%08lx,%08lx, %08x:%04x, %08x:%04x, %08x->%04x,  %08x)=%08lx -  %d msec\n", APIHOOKARGS(),
            hDevice, dwIoControlCode,
            lpInBuf, nInBufSize,
            lpOutBuf, nOutBufSize,
            lpBytesReturned, isValidPtr((DWORD)lpBytesReturned) ? *lpBytesReturned : 0,
            lpOverlapped, result, ORIGAPI(GetTickCount)()-t0);
    return result;

}



// see line 1020 in ~/sources/wince500/PRIVATE/WINCEOS/COREOS/NK/KERNEL/ARM/mdarm.c
const WCHAR akystring[]= {
'A','K','Y','=','%','8','.','8','l','x',' ','P','C','=','%','8','.','8','l','x','(','%','s','+','0','x','%','8','.','8','l','x',')',' ','R','A','=','%','8','.','8','l','x','(',
//'%','s','+','0','x','%','8','.','8','l','x',')',' ','B','V','A','=','%','8','.','8','l','x',' ','F','S','R','=','%','8','.','8','l','x',
};
template<typename T>
int compare(const T*p1, const T*p2, size_t n)
{
    while (n && *p1==*p2) {
        n--;
        p1++;
        p2++;
    }
    if (n==0) return 0;
    if (*p1<*p2) return -1;
    if (*p1>*p2) return +1;
    return 0;
}


void NKDbgPrintf(const WCHAR *msg, ...)
{
    va_list ap;
    va_start(ap, msg);
#ifdef BUFFERLOGGING
    vlogmsg(msg, (const void *)ap);
#endif
    if (ORIGAPI(NKvDbgPrintf))
        ORIGAPI(NKvDbgPrintf)(msg, ap);
    va_end(ap);
}

void set_api_method(int set, int mth, PFNVOID func)
{
    const_cast<PFNVOID>(SystemApiSets[set]->ppfnMethods[mth])= func;
}


//------- hd interface
ULONG 
MyHDException(
    PEXCEPTION_RECORD ExceptionRecord,
    CONTEXT *ContextRecord,
    BOOLEAN SecondChance
    ) 
{
    dumpstack(0);
    return FALSE;
}
/*
static void HDPageIn (DWORD dw, BOOL f)
{
}

static void HDModLoad (DWORD dw)
{
}

static void HDModUnload (DWORD dw)
{
}
*/

void init()
{

GETAPI(0, 88, SetLastError)
GETAPI(0, 89, GetLastError)
GETAPI(0, 57, LeaveCriticalSection)
GETAPI(0, 56, EnterCriticalSection)
GETAPI(0, 78, InitializeCriticalSection)
GETAPI(0,168, AllocPhysMem)
GETAPI(0, 68, GetCallerProcess)
GETAPI(0, 62, IsBadPtr)
GETAPI(0, 13, GetTickCount)


//    HOOKAPI(0,14,OutputDebugString);
#ifdef BUFFERLOGGING
    HOOKAPI(0,23,NKvDbgPrintf);
#else
    GETAPI(0,23,NKvDbgPrintf);
#endif

NKDbgPrintf(L"libklog: got many apis gettick=%08x  devioctl=%08x/%08x\n", ORIGAPI(GetTickCount), &ORIGAPI(DeviceIoControl), DeviceIoControl_hook);

#ifdef BUFFERLOGGING
    initlogging(0x800000);
#endif

//    MyCreateFile= (CreateFile_t)SystemApiSets[20]->ppfnMethods[9];
//    
//    problem: the following 3 ptrs are NULL
//   MyCloseHandle= (CloseHandle_t)SystemApiSets[7]->ppfnMethods[0];
//   MyWriteFile= (WriteFile_t)SystemApiSets[7]->ppfnMethods[3];
//   MySetFilePointer= (SetFilePointer_t)SystemApiSets[7]->ppfnMethods[5];

// see ~/sources/wince500/PRIVATE/WINCEOS/COREOS/NK/KERNEL/kwin32.c
#ifdef HOOK_APIS
    HOOKAPI(0,53,CreateProcess);
    HOOKAPI(0,148,LoadLibraryEx);
    HOOKAPI(0,9,FreeLibrary);
    HOOKAPI(0,10,GetProcAddress);
    HOOKAPI(0,95,CreateFileMapping);
    HOOKAPI(0,46,CloseProcOE);
    HOOKAPI(0,66,KillAllOtherThreads);
    HOOKAPI(0,87,NKTerminateThread);
    HOOKAPI(0,92,CloseAllHandles);
//    HOOKAPI(0,151,KillThreadIfNeeded);    ... called very often
    HOOKAPI(0,54,CreateThread);
#else
    GETAPIX(0,148,LoadLibraryEx);
#endif
#ifdef BUFFERLOGGING
    HOOKAPI(0,74,ReadDebugLog);
#endif

// NKDbgPrintf(L"libklog: hooked several apis\n");
//     HOOKAPI(7,11,DeviceIoControl);
// NKDbgPrintf(L"libklog: hooked devioctl\n");

    // hack: hook hd.dll exception handler, note: this only works
    // for the htc kaiser v1.82 rom
    if (((DWORD)ORIGAPI(LoadLibraryEx))==0x8003ABCC)
        *(DWORD*)0x80306050= (DWORD)MyHDException;
    // for the htc viva v1.27 rom
    else if (((DWORD)ORIGAPI(LoadLibraryEx))==0x8C12FB64)
        *(DWORD*)0x8C3D6050= (DWORD)MyHDException;
    // for the htc startrek v1.02 rom
    else if (((DWORD)ORIGAPI(LoadLibraryEx))==0x8C0AC178)
        *(DWORD*)0x8C2C6050= (DWORD)MyHDException;
    // for the htc herald v5.4.405.1 rom
    else if (((DWORD)ORIGAPI(LoadLibraryEx))==0x8C12DD40)
        *(DWORD*)0x8C386050= (DWORD)MyHDException;
    // for the htc photon, v1.32 rom
    else if (((DWORD)ORIGAPI(LoadLibraryEx))==0x815743fc)
        *(DWORD*)0x81916050= (DWORD)MyHDException;
    else {
        NKDbgPrintf(L"libklog::init: did not patch HDException: loadlib=%08lx nkdbgprintf=%08lx createproc=%08lx getproc=%08lx\n", ORIGAPI(LoadLibraryEx), ORIGAPI(NKvDbgPrintf), ORIGAPI(CreateProcess), ORIGAPI(GetProcAddress));
    }
}
