/* Software License Agreement
 * 
 *     Copyright(C) 1994-2020 David Lindauer, (LADSoft)
 * 
 *     This file is part of the Orange C Compiler package.
 * 
 *     The Orange C Compiler package is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     The Orange C Compiler package is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with Orange C.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *     As a special exception, if other files instantiate templates or
 *     use macros or inline functions from this file, or you compile
 *     this file and link it with other works to produce a work based
 *     on this file, this file does not by itself cause the resulting
 *     work to be covered by the GNU General Public License. However
 *     the source code for this file must still be made available in
 *     accordance with section (3) of the GNU General Public License.
 *     
 *     This exception does not invalidate any other reasons why a work
 *     based on this file might be covered by the GNU General Public
 *     License.
 * 
 *     contact information:
 *         email: TouchStone222@runbox.com <David Lindauer>
 * 
 */

#include <windows.h>
int PASCAL DllMain(
 	HINSTANCE hinstDLL,
	DWORD fdwReason,
	LPVOID lpvReserved
	)
 {
    return TRUE;
 }
#define MAX_HASH 64

struct hash {
	struct hash *next;
	void *funcptr;
	unsigned char thunk[1];
} *table[MAX_HASH];

#pragma pack(1)

static char *dataPointer;
static int left;
static void *NextRecord(int n)
{
    if (left < n)
    {
        dataPointer = VirtualAlloc(NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (dataPointer)
        {
            left = 4096;
        }
        else
        {
            left = 0;
        }
    }
    if (left)
    {
        left-= n;
        dataPointer += n;
        return dataPointer - n;
    }
    return NULL;
}
static struct hash *GetThunkRecord(void *proc, int n)
{
    int hashval = (((int)proc)>>2) % MAX_HASH;
    struct hash *link = table[hashval];
    while (link)
    {
        if (link->funcptr == proc)
            return link;
        link = link->next;
    }
    link = NextRecord(n);
    if (link)
    {
        link->next = table[hashval];
        table[hashval] = link;
        return link;
    }
    return NULL;
}
#ifdef __ORANGEC__
__export
#else
__declspec(dllexport) 
#endif
void * __OCCMSIL_GetProcThunkToManaged(void *proc, unsigned char *pdata)
 {
	static struct thunk1 {       /* must be <= 16 bytes, not sure if this is an MSIL limitation or not */
		unsigned char code[10];
		int saveAddr;
	} thunk1 = {
		0x8B, 0x4C, 0x24, 0x04,     /* mov ecx,DWORD PTR [esp+4] */
		0x8B, 0x54, 0x24, 0x08,     /* mov edx,DWORD PTR [esp+8] */
        0x8f, 0x05,                 /* pop dword ptr [xxxxx] */
        0
    };
    static struct thunk2 {
        unsigned char code[3];
        int offset;
    } thunk2 = 
    {
        0xff, 0xb4, 0x24,             /* push dword ptr [esp + xxxxxx] */
        0
    };
    static struct thunk3 {
        unsigned char code0[1];
        int offset;
        unsigned char code1[1];
        int saveAddr;
        unsigned char code2[1];
        short retsize;
    } thunk3 = 
    {
        0xe8,
        0,
        0x68,                       /* push dword const */
        0,
        0xc2, 
        0
    };
    struct hash * record;
    int n = ((pdata[0] & 0x7f) << 8) + pdata[1], n1 = n;
    BOOL isstdcall = pdata[2];
    int ts;
    if (n < 2)
        n = 0;
    else
        n -= 2;
    
    ts = sizeof(thunk1) + n * sizeof(thunk2) + sizeof(thunk3);
    record = GetThunkRecord(proc, ts);
    if (record)
    {
        if (!record->funcptr)
        {
            struct thunk1 *t1;
            struct thunk2 *t2;
            struct thunk3 *t3;
            int i;
            record->funcptr = proc;
            t1 = (struct thunk1 *)record->thunk;
            t2 = (struct thunk1 *)((char *)t1 + sizeof(thunk1));
            t3 = (struct thunk3 *)((char *)t2 + n * sizeof(thunk2));
            memcpy(t1, &thunk1, sizeof(thunk1));
            for (i=0; i < n; i++)
                memcpy((char *)t2 + i * sizeof(thunk2), &thunk2, sizeof(thunk2));
            memcpy(t3, &thunk3, sizeof(thunk3));
            t1->saveAddr = (int)& t3->saveAddr;
            pdata += 3;
            for (i=0; i < n;)
            {
                int val = *pdata++;
                int j;
                if (val & 0x80)
                {
                    val = ((val &0x80) << 8) + *pdata++;;
                }
                for (j = 0; j < val; j++)
                    t2[i + j].offset = 8 + (i) * 8 + (val - 1) *4;
                i += val;
            }
            t3->offset = (int)((char *)proc - (((char *)&t3->offset) + 4));
            t3->retsize =  isstdcall ? n1 * 4 : 0;
        }
        return record->thunk;
    }
    return NULL;
 }
#ifdef __ORANGEC__
__export
#else
__declspec(dllexport) 
#endif
void *__OCCMSIL_GetProcThunkToUnmanaged(void *proc)
 {
	static struct thunk {       /* must be <= 16 bytes, not sure if this is an MSIL limitation or not */
		unsigned char code[3];
		int offset;
		unsigned char retcode[4];
	} thunk = {
		0x52,                       /* push	edx */
		0x51,                       /* push ecx */
		0xE8,                       /* call rel32 */
		0,
		0x83, 0xC4, 0x08,           /* add esp,8 */
		0xC3                        /* ret */
	};
    struct hash * record = GetThunkRecord(proc, sizeof(thunk));
    if (record)
    {
        if (!record->funcptr)
        {
            record->funcptr = proc;
            memcpy(&record->thunk, &thunk, sizeof(thunk));
            *(int *)(record->thunk + 3) = (char *)proc - ((char *)record->thunk + 3 + 4);
        }
        return record->thunk;
    }
    return NULL;
 }
 /*
 __declspec(dllexport) void _stdcall testfunc(void *a)
 {
    void (*_stdcall b)(int a, int b, int c, int d, long long e, int f, int g,int h, int i) = a;
    (*b)(1,2,3,4,5,6,7,8,9);
    printf("try");
 }
 */