// gta_dll.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "gta_dll.h"

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); }


}

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);
}

FILE * logfile;
FILE * data;

// .ASI loader
void loadASI () {
	WIN32_FIND_DATA fd;

	HANDLE asiFile = FindFirstFile ("*.asi", &fd);
	if (asiFile == INVALID_HANDLE_VALUE)
		return;

	do {
		if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 
			LoadLibrary (fd.cFileName);
///		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 
///			fprintf (log, "File %s is directory, skipped\n", fd.cFileName);
///		else {
///			fprintf (log, "Loading %s ...", fd.cFileName);
///			fflush (log); // на случай, если мы сейчас порушимся с грохотом
///			HMODULE hlib = LoadLibrary (fd.cFileName);
///			if (hlib) 
///				fprintf (log, "done, %08x.\n", (DWORD)hlib);
///			else
///				fprintf (log, "failed.\n");
///		}

	} while (FindNextFile (asiFile, &fd));
	FindClose (asiFile);
}

// общий метод для подключения заменяемых фкункций
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;

		if (!attachDll ()) {
			debug ("AttachDLL failed, exiting\n");
			return false;
		}

		//data = fopen ("data.bin", "wb");
		//fwrite ((void *)0x401000, 1, 4*1024*1024, data);
		//fclose (data);
		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;
}

/*
__declspec(dllexport) bool mintest (float a, float b) {
	return a < b;
}

__declspec(dllexport) bool maxtest (float a, float b) {
	return a > b;
}

__declspec(dllexport) float mintest2 (float a, float b) {
	return a< b ? a:b;
}
*/
/*
// This is an example of an exported variable
GTA_DLL_API int ngta_dll=0;

// This is an example of an exported function.
GTA_DLL_API int fngta_dll(void)
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see gta_dll.h for the class definition
Cgta_dll::Cgta_dll()
{
	return;
}
*/