The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
    # Win32::API::Callback - Perl Win32 API Import Facility
    #
    # Version: 0.40
    # Date: 07 Mar 2003
    # Author: Aldo Calpini <dada@perl.it>
	# $Id: Callback.xs,v 1.0 2002/03/19 10:25:00 dada Exp $
 */

#define  WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <memory.h>

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define CROAK croak

#include "../API.h"

#pragma optimize("", off)

/*
 * some Perl macros for backward compatibility
 */
#ifdef NT_BUILD_NUMBER
#define boolSV(b) ((b) ? &sv_yes : &sv_no)
#endif

#ifndef PL_na
#	define PL_na na
#endif

#ifndef SvPV_nolen
#	define SvPV_nolen(sv) SvPV(sv, PL_na)
#endif

#ifndef call_pv
#	define call_pv(name, flags) perl_call_pv(name, flags)
#endif

#ifndef call_sv
#	define call_sv(name, flags) perl_call_sv(name, flags)
#endif

int PerformCallback(SV* self, int nparams, APIPARAM* params);

SV* fakesv;
int fakeint;
char *fakepointer;

int RelocateCode(unsigned char* cursor, unsigned int displacement) {
	int skip;
	unsigned int reladdr;

	switch(*cursor) {

		// skip +0
		case 0x50:	// push eax
		case 0x51:	// push ecx
		case 0x55:	// push ebp
		case 0x56:	// push esi
		case 0x59:	// pop ecx
		case 0x5E:	// pop esi
		case 0xC3:	// ret
		case 0xC9:	// leave
			skip = 0;
			break;

		// skip +1
		case 0x33:	// xor
		case 0x3B:	// cmp
		case 0x6A:	// push (1 byte)
		case 0x74:	// je
		case 0x75:	// jne
		case 0x7D:	// jge
		case 0x7E:	// jle
		case 0x85:	// test
		case 0xEB:	// jmp
			skip = 1;
			break;

		// skip +1/+2
		case 0x2B:
			if(*(cursor+1) == 0x30	// sub esi, dword ptr [eax]
			) {
				skip = 1;
				break;
			} else
			if (*(cursor+1) == 0x45	// sub eax, dword ptr [ebp+1 byte]
			) {
				skip = 2;
				break;
			}

		// skip +1/+2
		case 0x89:

			if(*(cursor+1) == 0x01	// mov dword ptr [ecx], eax
			|| *(cursor+1) == 0x08	// mov dword ptr [eax], ecx
			|| *(cursor+1) == 0x30	// mov dword ptr [eax], esi
			) {
				skip = 1;
				break;
			}
			if(*(cursor+1) == 0x45	// mov dword ptr [ebp+1 byte], eax
			|| *(cursor+1) == 0x4D	// mov dword ptr [ebp+1 byte], ecx
			|| *(cursor+1) == 0x04	// mov dword ptr [edx+ecx*1 byte], eax
			) {
				skip = 2;
				break;
			}

		// skip +1/+2
		case 0x8B:

			if(*(cursor+1) == 0x00	// mov eax,dword ptr [eax]
			|| *(cursor+1) == 0x09	// mov ecx,dword ptr [ecx]
			|| *(cursor+1) == 0x0E	// mov ecx,dword ptr [esi]
			|| *(cursor+1) == 0xEC	// mov ebp,esp
			|| *(cursor+1) == 0xF0	// mov esi,eax
			) {
				skip = 1;
				break;
			} else
			if(*(cursor+1) == 0x40	// mov eax,dword ptr [eax+1 byte]
			|| *(cursor+1) == 0x45	// mov eax,dword ptr [ebp+1 byte]
			|| *(cursor+1) == 0x4D	// mov ecx,dword ptr [ebp+1 byte]
			|| *(cursor+1) == 0x55	// mov edx,dword ptr [ebp+1 byte]
			|| *(cursor+1) == 0x75	// mov esi,dword ptr [ebp+1 byte]
			) {
				skip = 2;
				break;
			}

		case 0xFF:
			if(*(cursor+1) == 0x30	// push dword ptr [eax]
			) {
				skip = 1;
				break;
			} else
			if(*(cursor+1) == 0x75	// push dword ptr [epb+1 byte]
			|| *(cursor+1) == 0x34	// push dword ptr [ecx+eax*4]
			) {
				skip = 2;
				break;
			} else
			if(*(cursor+1) == 0x15	// call dword ptr ds:(4 byte)
			|| *(cursor+1) == 0x35	// push dword ptr ds:(4 byte)
			) {
				skip = 5;
				break;
			}

		// skip +2
		case 0xC1:	// sar
			skip = 2;
			break;

		// skip +2/+3
		case 0x83:
			if(*(cursor+1) != 0x65	// add|sub|cmp
			) {
				skip = 2;
				break;
			} else
			if(*(cursor+1) == 0x65	// add|sub|cmp
			) {
				skip = 3;
				break;
			}

		// skip +4
		case 0x25:	// and
		case 0x68:	// push (4 bytes)
			skip = 4;
			break;


		// skip +6
		case 0xC7:	// mov dword ptr (ebp+1 byte), (4 byte)
			skip = 6;
			break;

		case 0xE8:
			// we relocate here!
			reladdr = *((int*)(cursor + 1));
			*((int*)(cursor + 1)) = (unsigned int) (reladdr - displacement);
			skip = 4;
			break;

		default:
#ifdef WIN32_API_DEBUG
			printf("(C)RelocateCode: %08X ????    0x%x\n", cursor, *cursor);
#endif
			skip = 0;
			break;
	}
#ifdef WIN32_API_DEBUG
	{
		int i;
		printf("(C)RelocateCode: %08X skip +%1d ", cursor, skip);
		for(i = 0; i <= skip; i++) {
			printf("%02X ", *(cursor+i));
		}
		printf("\n");
	}
#endif
	return 1 + skip;
}


// SV* CallbackMakeStruct(SV* self, int nparam, char *addr) {
int CallbackMakeStruct(SV* self, int nparam, char *addr) {
#ifdef dTHX
	dTHX;
#endif
	dSP;
	SV* structobj = NULL;
	char ikey[80];
/*
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackMakeStruct: got self='%s'\n", SvPV_nolen(self));
	sv_dump(self);
	if(SvROK(self)) sv_dump(SvRV(self));
	printf("(C)CallbackMakeStruct: got nparam=%d\n", nparam);
	printf("(C)CallbackMakeStruct: got addr=0x%08x\n", addr);
	// memcpy( SvPV_nolen(self), 0, 1000);
#endif
*/
	ENTER;
	SAVETMPS;
	// XPUSHs(sv_2mortal(newSVrv(self, "Win32::API::Callback")));
	PUSHMARK(SP);
	XPUSHs(sv_2mortal(newSVsv(self)));
	XPUSHs(sv_2mortal(newSViv(nparam)));
	XPUSHs(sv_2mortal(newSViv((long) addr)));
	PUTBACK;
	call_pv("Win32::API::Callback::MakeStruct", G_SCALAR);
	SPAGAIN;
	structobj = newSVsv(POPs);

	itoa(nparam, ikey, 10);
	hv_store( (HV*)SvRV(self), ikey, strlen(ikey), structobj, 0 );
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: self{'%s'}='%s'\n", ikey, SvPV_nolen(*(hv_fetch( (HV*)SvRV(self), ikey, strlen(ikey), 0 ))));
#endif
/*
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackMakeStruct: structobj='%s'\n", SvPV_nolen(structobj));
	sv_dump(structobj);
	if(SvROK(structobj)) sv_dump(SvRV(structobj));
#endif
*/
	PUTBACK;
	FREETMPS;
	LEAVE;
	return 1;
	// return structobj;
}

int CALLBACK CallbackTemplate() {
	SV* myself = (SV*) 0xC0DE0001; 	// checkpoint_SELFPOS
	int nparams = 0xC0DE0002; 		// checkpoint_NPARAMS
	APIPARAM* params;
	unsigned int checkpoint;
	int i, r;

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: nparams=%d\n", nparams);
//	printf("(C)CallbackTemplate: myself='%s'\n", SvPV_nolen(myself));
//	sv_dump(myself);
//	if(SvROK(myself)) sv_dump(SvRV(myself));
#endif

	params = (APIPARAM*) safemalloc(  nparams * sizeof(APIPARAM) );
	checkpoint = 0xC0DE0010;		// checkpoint_PUSHI
	i = 0xC0DE0003;					// checkpoint_IPOS
	params[i].t = T_INTEGER;
	params[i].l = fakeint;
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: PUSHI(%d)=", i);
	printf("%d\n", params[i].l);
#endif
	checkpoint = 0xC0DE0020;		// checkpoint_PUSHL
	i = 0xC0DE0003;					// checkpoint_IPOS
	params[i].t = T_NUMBER;
	params[i].l = fakeint;
	checkpoint = 0xC0DE0030;		// checkpoint_PUSHP
	i = 0xC0DE0003;					// checkpoint_IPOS
	params[i].t = T_POINTER;
	params[i].p = (char*) fakeint;
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: PUSHP(%d)=", i);
	printf("%08x (%s)\n", params[i].p, params[i].p);
#endif
	checkpoint = 0xC0DE0040;		// checkpoint_PUSHS
	i = 0xC0DE0003;					// checkpoint_IPOS
	params[i].t = T_STRUCTURE;
	params[i].p = (char*) fakeint;
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: PUSHS(%d)=", i);
	printf("%08x\n", params[i].p);
#endif

/*
	checkpoint = 0xC0DE0050;		// checkpoint_PUSHD
	i = 0xC0DE0003;					// checkpoint_IPOS
	params[i].t = T_DOUBLE;
	params[i].d = fakedouble;
*/

	checkpoint = 0xC0DE9999;		// checkpoint_END
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: Calling PerformCallback...\n");
#endif
	r = PerformCallback(myself, nparams, params);
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: r=%d\n", r);
#endif
	safefree(params);
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackTemplate: RETURNING\n");
#endif
	return r;
}

int PerformCallback(SV* self, int nparams, APIPARAM* params) {
	SV* mycode;
	int i = 0;
	char ikey[80];
	unsigned int checkpoint;
	I32 size;
	int r;
#ifdef dTHX
	dTHX;
#endif
	dSP;

	mycode = *(hv_fetch((HV*)SvRV(self), "sub", 3, FALSE));

	for(i=0; i < nparams; i++) {
		if(params[i].t == T_STRUCTURE) {
			CallbackMakeStruct(self, i, params[i].p);
		}
	}

	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	for(i=0; i < nparams; i++) {
		switch(params[i].t) {
		case T_STRUCTURE:
			itoa(i, ikey, 10);
			XPUSHs(sv_2mortal(*(hv_fetch((HV*)SvRV(self), ikey, strlen(ikey), 0))));
			break;
		case T_POINTER:
			XPUSHs(sv_2mortal(newSVpv((char *) params[i].p, 0)));
			break;
		case T_INTEGER:
		case T_NUMBER:
			XPUSHs(sv_2mortal(newSViv((int) params[i].l)));
			break;
		}
	}

	PUTBACK;
	call_sv(mycode, G_EVAL | G_SCALAR);
	SPAGAIN;
	r = POPi;
	PUTBACK;
	FREETMPS;
	LEAVE;
	return r;
}

unsigned char * CallbackCreate(int nparams, APIPARAM *params, SV* self, SV* callback) {

	unsigned char * code;
	unsigned char * cursor;
	unsigned int i, j, r, toalloc, displacement;
	unsigned char ebpcounter = 8;
	unsigned char * source = (unsigned char *) (void *) CallbackTemplate;
	BOOL done = FALSE;
	unsigned int distance = 0;
	BOOL added_INIT_STRUCT = FALSE;
	int N_structs = 0;

	unsigned int
		checkpoint_PUSHI = 0,
		checkpoint_PUSHL = 0,
		checkpoint_PUSHP = 0,
		checkpoint_PUSHS = 0,
		checkpoint_END = 0,
		checkpoint_DONE = 0;

	unsigned int
		section_START,
		section_PUSHI,
		section_PUSHL,
		section_PUSHP,
		section_PUSHS,
		section_END;

	cursor = source;

	while(!done) {

		if(*(cursor+0) == 0x10
		&& *(cursor+1) == 0x00
		&& *(cursor+2) == 0xDE
		&& *(cursor+3) == 0xC0
		) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate.Study: checkpoint_PUSHI=%d\n", distance);
#endif
			checkpoint_PUSHI = distance - 3;
		}

		if(*(cursor+0) == 0x20
		&& *(cursor+1) == 0x00
		&& *(cursor+2) == 0xDE
		&& *(cursor+3) == 0xC0
		) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate.Study: checkpoint_PUSHL=%d\n", distance);
#endif
			checkpoint_PUSHL = distance - 3;
		}

		if(*(cursor+0) == 0x30
		&& *(cursor+1) == 0x00
		&& *(cursor+2) == 0xDE
		&& *(cursor+3) == 0xC0
		) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate.Study: checkpoint_PUSHP=%d\n", distance);
#endif
			checkpoint_PUSHP = distance - 3;
		}

		if(*(cursor+0) == 0x40
		&& *(cursor+1) == 0x00
		&& *(cursor+2) == 0xDE
		&& *(cursor+3) == 0xC0
		) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate.Study: checkpoint_PUSHS=%d\n", distance);
#endif
			checkpoint_PUSHS = distance - 3;
		}

		if(*(cursor+0) == 0x99
		&& *(cursor+1) == 0x99
		&& *(cursor+2) == 0xDE
		&& *(cursor+3) == 0xC0
		) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate.Study: checkpoint_END=%d\n", distance);
#endif
			checkpoint_END = distance - 3;
		}


#ifdef WIN32_API_DEBUG
		if(checkpoint_END > 0) {
			printf("(C)CallbackCreate.Study: after END got 0x%02X at %d\n", *cursor, distance);
		}
#endif
	
		
		

		if(*(cursor+0) == 0xC9	// leave
		&& *(cursor+1) == 0xC3	// ret
		) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate.Study: checkpoint_DONE=%d\n", distance);
#endif
			checkpoint_DONE = distance + 2;
			done = TRUE;
		}

		if(cursor >= (unsigned char *) PerformCallback) {
			checkpoint_DONE = distance;
		 	done = TRUE;
		}
		// TODO: add fallback (eg. if cursor >= CallbackCreate then done)

		cursor++;
		distance++;
	}

	section_START 	= checkpoint_PUSHI;
	section_PUSHI	= checkpoint_PUSHL	- checkpoint_PUSHI;
	section_PUSHL	= checkpoint_PUSHP	- checkpoint_PUSHL;
	section_PUSHP	= checkpoint_PUSHS	- checkpoint_PUSHP;
	section_PUSHS	= checkpoint_END 	- checkpoint_PUSHS;
	section_END		= checkpoint_DONE	- checkpoint_END;

	toalloc = section_START;

	toalloc += section_END;
	toalloc += 3; // we'll need 3 extra bytes for the callback epilogue

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: toalloc=%d\n", toalloc);
#endif

	for(i=0; i<nparams; i++) {

		if(params[i].t == T_NUMBER) {
			toalloc += section_PUSHI;
		}

		if(params[i].t == T_POINTER) {
			toalloc += section_PUSHP;
		}

		if(params[i].t == T_STRUCTURE) {
			toalloc += section_PUSHS;
		}

#ifdef WIN32_API_DEBUG
		printf("(C)CallbackCreate: summing param[%d] (%d), toalloc=%d\n", i, params[i].t, toalloc);
#endif

	}

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: fakeint          is at: 0x%08X\n", &fakeint);
	printf("(C)CallbackCreate: fakepointer      is at: 0x%08X\n", &fakepointer);
	printf("(C)CallbackCreate: fakesv           is at: 0x%08X\n", &fakesv);
	printf("(C)CallbackCreate: CallbackTemplate is at: 0x%08X\n", CallbackTemplate);
	printf("(C)CallbackCreate: allocating %d bytes\n", toalloc);
#endif
	code = (unsigned char *) malloc(toalloc);

	if(code == NULL) {
		printf("can't allocate callback code, aborting!\n");
		return 0;
	}
	cursor = code;

	displacement = code - source;
#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: source       = 0x%x\n", source);
	printf("(C)CallbackCreate: code         = 0x%x\n", code);
#endif

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: COPYING SECTION section_START (%d bytes)\n", section_START);
#endif
	memcpy( (void *) cursor, source, section_START);

	for(i=0; i < section_START; i++) {
		r = RelocateCode(cursor, displacement);
		if(r == 7
		&& *(cursor+3) == 0xDE
		&& *(cursor+4) == 0xC0
		&& *(cursor+5) == 0xFF
		&& *(cursor+6) == 0xFF) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate:     FOUND CODE at 0x%x...\n", cursor+3);
			printf("(C)CallbackCreate:     callback    = 0x%x\n", callback);
#endif
			*((int*)(cursor+3)) = (int) callback;

#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate:     CODE now is = 0x%x\n", *((int*)(cursor+3)));
#endif
		}
		if(r == 7
		&& *(cursor+3) == 0x01
		&& *(cursor+4) == 0x00
		&& *(cursor+5) == 0xDE
		&& *(cursor+6) == 0xC0) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate:     FOUND SELF at 0x%08x...\n", cursor+3);
			printf("(C)CallbackCreate:     self        = 0x%08x\n", self);
#endif
			hv_store((HV*) SvRV(self), "selfpos", 7, newSViv((long) cursor+3), 0);
			*((int*)(cursor+3)) = (int) self;

#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate:     SELF now is = 0x%08x\n", *((int*)(cursor+3)));
#endif
		}
		if(r == 7
		&& *(cursor+3) == 0x02
		&& *(cursor+4) == 0x00
		&& *(cursor+5) == 0xDE
		&& *(cursor+6) == 0xC0) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate:     FOUND NPARAMS 0x%08x...\n", cursor+3);
			printf("(C)CallbackCreate:     NPARAMS     = 0x%08x\n", nparams);
#endif
			*((int*)(cursor+3)) = nparams;

#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate:     NPARAMS now = 0x%08x\n", *((int*)(cursor+3)));
#endif
		}

		cursor += r;
		if(r > 1) i += (r-1);
	}

	for(j=0; j<nparams; j++) {

		if(params[j].t == T_STRUCTURE) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate: COPYING SECTION section_PUSHS (%d bytes)\n", section_PUSHS);
#endif
			memcpy( (void *) cursor, source + checkpoint_PUSHS, section_PUSHS );
			displacement = cursor - (source + checkpoint_PUSHS);

			for(i=0; i < section_PUSHS; i++) {

				if(*(cursor+0) == 0x8B
				&& *(cursor+1) == 0x15
				&& *((int*)(cursor+2)) == (int) &fakeint
				) {
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     FOUND THE SVPV at 0x%x\n", cursor);
					printf("(C)CallbackCreate:     writing EBP+%02Xh\n", ebpcounter);
#endif
					*(cursor+0) = 0x8B;
					*(cursor+1) = 0x55;
					*(cursor+2) = ebpcounter;
					*(cursor+3) = 0x90;		// nop
					*(cursor+4) = 0x90;		// nop
					*(cursor+5) = 0x90;		// nop
					cursor += 5;
					i += 4;
				} else
				if(*(cursor+0) == 0xC7
				&& *(cursor+1) == 0x45
				&& *(cursor+2) == 0xEC
				&& *((int*)(cursor+3)) == 0xC0DE0003
				) {
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     FOUND NPARAM   at 0x%x\n", cursor);
					printf("(C)CallbackCreate:     writing         = 0x%08X\n", j);
#endif
					*((int*)(cursor+3)) = j;
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     NPARAM now is   = 0x%08X\n", *((int*)(cursor+3)));
#endif
					cursor += 6;
					i += 5;
				} else {
					r = RelocateCode(cursor, displacement);
					cursor += r;
					if(r > 1) i += (r-1);
				}

			}
		}

		if(params[j].t == T_NUMBER) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate: COPYING SECTION section_PUSHI (%d bytes)\n", section_PUSHI);
#endif
			memcpy( (void *) cursor, source + checkpoint_PUSHI, section_PUSHI );
			displacement = cursor - (source + checkpoint_PUSHI);

			for(i=0; i < section_PUSHI; i++) {

				if(*(cursor+0) == 0x8B
				&& *(cursor+1) == 0x15
				&& *((int*)(cursor+2)) == (int) &fakeint
				) {
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     FOUND THE SVIV at 0x%x\n", cursor);
					printf("(C)CallbackCreate:     writing EBP+%02Xh\n", ebpcounter);
#endif
					*(cursor+0) = 0x8B;
					*(cursor+1) = 0x55;
					*(cursor+2) = ebpcounter;
					*(cursor+3) = 0x90;		// push ecx
					*(cursor+4) = 0x90;		// push esi
					*(cursor+5) = 0x90;		// pop esi
					cursor += 5;
					i += 4;
				} else
				if(*(cursor+0) == 0xC7
				&& *(cursor+1) == 0x45
				&& *(cursor+2) == 0xEC
				&& *((int*)(cursor+3)) == 0xC0DE0003
				) {
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     FOUND NPARAM   at 0x%x\n", cursor);
					printf("(C)CallbackCreate:     writing         = 0x%08X\n", j);
#endif
					*((int*)(cursor+3)) = j;
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     NPARAM now is   = 0x%08X\n", *((int*)(cursor+3)));
#endif
					cursor += 6;
					i += 5;
				} else {
					r = RelocateCode(cursor, displacement);
					cursor += r;
					if(r > 1) i += (r-1);
				}

			}
		}


		if(params[j].t == T_POINTER) {
#ifdef WIN32_API_DEBUG
			printf("(C)CallbackCreate: COPYING SECTION section_PUSHP (%d bytes)\n", section_PUSHP);
#endif
			memcpy( (void *) cursor, source + checkpoint_PUSHP, section_PUSHP );
			displacement = cursor - (source + checkpoint_PUSHP);

			for(i=0; i < section_PUSHP; i++) {

				if(*(cursor+0) == 0x8B
				&& *(cursor+1) == 0x15
				&& *((int*)(cursor+2)) == (int) &fakeint
				) {
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     FOUND THE SVPV at 0x%x\n", cursor);
					printf("(C)CallbackCreate:     writing EBP+%02Xh\n", ebpcounter);
#endif
					*(cursor+0) = 0x8B;
					*(cursor+1) = 0x55;
					*(cursor+2) = ebpcounter;
					*(cursor+3) = 0x90;		// nop
					*(cursor+4) = 0x90;		// nop
					*(cursor+5) = 0x90;		// nop
					cursor += 5;
					i += 4;
				} else
				if(*(cursor+0) == 0xC7
				&& *(cursor+1) == 0x45
				&& *(cursor+2) == 0xEC
				&& *((int*)(cursor+3)) == 0xC0DE0003
				) {
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     FOUND NPARAM   at 0x%x\n", cursor);
					printf("(C)CallbackCreate:     writing         = 0x%08X\n", j);
#endif
					*((int*)(cursor+3)) = j;
#ifdef WIN32_API_DEBUG
					printf("(C)CallbackCreate:     NPARAM now is   = 0x%08X\n", *((int*)(cursor+3)));
#endif
					cursor += 6;
					i += 5;
				} else {
					r = RelocateCode(cursor, displacement);
					cursor += r;
					if(r > 1) i += (r-1);
				}

			}
		}

		ebpcounter += 4;
	}

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: COPYING SECTION section_END (%d bytes) at 0x%08x\n", section_END, cursor);
#endif
	memcpy( (void *) cursor, source + checkpoint_END, section_END );

	displacement = cursor - (source + checkpoint_END);

	for(i=0; i < section_END; i++) {
		r = RelocateCode(cursor, displacement);
		cursor += r;
		if(r > 1) i += (r-1);
	}

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: adjusting callback epilogue...\n");
#endif

	// #### back up two bytes (leave/ret)
	cursor -= 2;

	// #### insert the callback epilogue
	*(cursor+0) = 0x8B; // mov esp,ebp
	*(cursor+1) = 0xE5;
	*(cursor+2) = 0x5D; // pop ebp
	*(cursor+3) = 0xC2; // ret + 2 bytes
	*(cursor+4) = ebpcounter - 8;
	*(cursor+5) = 0x00;

#ifdef WIN32_API_DEBUG
	printf("(C)CallbackCreate: DONE!\n");
#endif

	return code;
}


MODULE = Win32::API::Callback   PACKAGE = Win32::API::Callback

PROTOTYPES: DISABLE

unsigned int
CallbackCreate(self)
	SV* self
PREINIT:
    APIPARAM *params;
    int    iParam;
    long   lParam;
    float  fParam;
    double dParam;
	char   cParam;
    char  *pParam;
    LPBYTE ppParam;
    HV*		obj;
    SV**	obj_sub;
    SV*		sub;
    SV**	obj_proto;
    SV**	obj_in;
    SV**	obj_out;
    SV**	obj_intypes;
    SV**	in_type;
    AV*		inlist;
    AV*		intypes;
    int nin, tout, i;
CODE:
#ifdef WIN32_API_DEBUG
	printf("(XS)CallbackCreate: got self='%s'\n", SvPV_nolen(self));
	printf("(XS)CallbackCreate: self dump:\n");
	sv_dump(self);
	if(SvROK(self)) sv_dump(SvRV(self));
#endif
    obj = (HV*) SvRV(self);
    obj_in = hv_fetch(obj, "in", 2, FALSE);
    obj_out = hv_fetch(obj, "out", 3, FALSE);
    inlist = (AV*) SvRV(*obj_in);
    nin  = av_len(inlist);
#ifdef WIN32_API_DEBUG
	printf("(XS)CallbackCreate: nin=%d\n", nin);
#endif
    tout = SvIV(*obj_out);
#ifdef WIN32_API_DEBUG
	printf("(XS)CallbackCreate: tout=%d\n", tout);
#endif
	obj_sub = hv_fetch(obj, "sub", 3, FALSE);
	sub = *obj_sub;
#ifdef WIN32_API_DEBUG
	printf("(XS)CallbackCreate: self.sub='%s'\n", SvPV_nolen(sub));
	printf("(XS)CallbackCreate: self.sub dump:\n");
	sv_dump(sub);
	if(SvROK(sub)) sv_dump(SvRV(sub));
#endif
    EXTEND(SP, 1);
    if(nin >= 0) {
        params = (APIPARAM *) safemalloc((nin+1) * sizeof(APIPARAM));
        for(i = 0; i <= nin; i++) {
            in_type = av_fetch(inlist, i, 0);
            params[i].t = SvIV(*in_type);
            // params[i].t = T_NUMBER;
        }
	}
	RETVAL = (unsigned int) CallbackCreate(nin+1, params, self, sub);
#ifdef WIN32_API_DEBUG
	printf("(XS)CallbackCreate: got RETVAL=0x%08x\n", RETVAL);
#endif
	if(nin > 0) safefree(params);
#ifdef WIN32_API_DEBUG
	printf("(XS)CallbackCreate: returning to caller\n");
#endif
OUTPUT:
	RETVAL


void
PushSelf(self)
	SV* self
PREINIT:
	HV*		obj;
	SV**	obj_selfpos;
	unsigned char *selfpos;
CODE:
#ifdef WIN32_API_DEBUG
	printf("(XS)PushSelf: got self='%s' (SV=0x%08x)\n", SvPV_nolen(self), self);
#endif
	obj = (HV*) SvRV(self);
	obj_selfpos = hv_fetch(obj, "selfpos", 7, FALSE);
	if(obj_selfpos != NULL) {
#ifdef WIN32_API_DEBUG
		printf("(XS)PushSelf: obj_selfpos=0x%08x\n", SvIV(*obj_selfpos));
#endif
		*((int*)SvIV(*obj_selfpos)) = (int) self;
	}

void
DESTROY(self)
	SV* self
PREINIT:
    HV*		obj;
    SV**	obj_code;
CODE:
	obj = (HV*) SvRV(self);
	obj_code = hv_fetch(obj, "code", 4, FALSE);
	if(obj_code != NULL) free((unsigned char *) SvIV(*obj_code));