// ### OpenGTA3 ###

#include "stdafx.h"
#include <stdio.h>
#include <varargs.h>
#include <time.h>

//=============================================================================
// vorbisfile connector
extern "C" {
typedef struct {
  size_t (*read_func)  (void *ptr, size_t size, size_t nmemb, void *datasource);
  int    (*seek_func)  (void *datasource, long long offset, int whence);
  int    (*close_func) (void *datasource);
  long   (*tell_func)  (void *datasource);
} ov_callbacks;

int (* __ov_open_callbacks)(void *datasource, void *vf, char *initial, long ibytes, ov_callbacks callbacks);
__declspec(dllexport) int ov_open_callbacks(void *datasource, void *vf, char *initial, long ibytes, ov_callbacks callbacks) 
{ return __ov_open_callbacks (datasource, vf, initial, ibytes, callbacks); }

int (*__ov_clear)(void * vf);
__declspec(dllexport) int ov_clear(void *vf)
{ return __ov_clear (vf); }

double (*__ov_time_total)(void *vf, int i);	
__declspec(dllexport) double ov_time_total (void * vf, int i) 
{ return __ov_time_total (vf, i); }

double (*__ov_time_tell)(void *vf);
__declspec(dllexport) double ov_time_tell(void *vf) 
{ return __ov_time_tell (vf); }

long (*__ov_read)(void *vf,char *buffer,int length, int bigendianp, int word, int sgned, int *bitstream);
__declspec(dllexport) long ov_read(void *vf,char *buffer,int length, int bigendianp, int word, int sgned, int *bitstream) 
{ return __ov_read (vf, buffer, length, bigendianp, word, sgned, bitstream); }

void * (*__ov_info)(void *vf,int link); 
__declspec(dllexport) void * ov_info(void *vf,int link) 
{ return __ov_info (vf, link); }

int (*__ov_time_seek)(void *vf, double pos) ;
__declspec(dllexport) int ov_time_seek(void *vf, double pos) 
{ return __ov_time_seek (vf, pos); }

}	// extern "C"

HMODULE vorbisHooked = NULL;

// подключение оригинальной vorbisFile.dll
void vorbisAttach () {
	vorbisHooked = LoadLibrary ("vorbisHooked.dll");
	if (!vorbisHooked)
		return;

	__ov_open_callbacks = (int (__cdecl *)(void *, void *, char *, long, ov_callbacks))GetProcAddress (vorbisHooked, "ov_open_callbacks");
	__ov_clear = (int (__cdecl *)(void *))GetProcAddress (vorbisHooked, "ov_clear");
	__ov_time_total = (double (__cdecl *)(void *, int))GetProcAddress (vorbisHooked, "ov_time_total");
	__ov_time_tell = (double (__cdecl *)(void *))GetProcAddress (vorbisHooked, "ov_time_tell");
	__ov_read = (long (__cdecl *)(void *, char *, int, int, int, int, int *))GetProcAddress (vorbisHooked, "ov_read");
	__ov_info = (void *(_cdecl *)(void *, int))GetProcAddress (vorbisHooked, "ov_info");
	__ov_time_seek = (int (__cdecl *)(void *, double))GetProcAddress (vorbisHooked, "ov_time_seek");
}

void vorbisDetach () {
	FreeLibrary (vorbisHooked);
}

//=============================================================================
// 
static FILE * logfile;
void debug (char * message, ...) {
	if (!logfile)
		return;
	SYSTEMTIME	t;
	GetLocalTime (&t);	
	fprintf (logfile, "%02d/%02d/%04d %02d:%02d:%02d.%03d ", t.wDay, t.wMonth, t.wYear, t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
	va_list	arg;	
	va_start (arg, message);
	vfprintf (logfile, message, arg);

	//fflush (logfile);

	va_end (arg); 
}

void injectFunction (DWORD address, DWORD function) {
	BYTE * patch = (BYTE *)address;
	*patch = 0xE9;	// JMP
	*(DWORD *)(patch+1) = (function-(address+5));	
}


// общий метод для подключения заменяемых фкункций
// extern bool attachDll ();
extern bool attachDll ();

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 ) {
	switch (ul_reason_for_call) {
	case DLL_PROCESS_ATTACH:
		logfile = fopen ("gta_trace.log", "ab");
		if (!logfile)
			return false;
		debug ("Log started\n");

///		loadASI ();

		vorbisAttach ();
		if (!vorbisHooked)
			return FALSE;

		DWORD oldProtect;
		if (!VirtualProtect ((LPVOID)0x401000, 0x456000, PAGE_EXECUTE_READWRITE, &oldProtect)) {
			debug ("Virtual protect failed, exiting...\n");
			return false;
		}

		if (!attachDll ()) {
			debug ("AttachDLL failed, exiting\n");
			return false;
		}
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		if (logfile) {
			debug ("Log closed\n");
			fclose (logfile);
			logfile = NULL;
		}

		// detachDll ();

		if (vorbisHooked) 
			vorbisDetach ();
		break;
	}
    return TRUE;
}

