// *** OpenGTA3 ***
// GXT support module
#include "stdafx.h"
#include <tchar.h>
#include "ogFile.h"
#include "ogText.h"
#include "ogUtil.h"

// INJECTOR start
// TODO: move to injector module
// >> 6A0050
const TCHAR * __stdcall textLocate (char * key) { return gxt.locate (key); }
// >> 6A01A0
// TODO: reload file only if language changed
void __stdcall textLoad (bool noMpack) { return gxt.load (noMpack); }
// >> 69FBF0
void __stdcall textLoadMission (char * missionName) { return gxt.loadMission (missionName); }
// >> 69FF20
void __stdcall textClear (bool __unused) { gxt.clear (); }	// do nothing
// >> 69FBD0
void __stdcall textGetMissionName (char * pDst) { gxt.getMissionName (pDst); }

// TODO: сделать зассив заменяемых функций и функцию, которая будет патчить все функции, на которые есть ссылки в этом массиве
void injectFunction (DWORD address, DWORD function) {
	BYTE * patch = (BYTE *)address;
	*patch = 0xE9;	// JMP
	*(DWORD *)(patch+1) = (function-(address+5));
}

void textInject () {
	injectFunction (0x6A0050, (DWORD)textLocate);
	injectFunction (0x6A01A0, (DWORD)textLoad);
	injectFunction (0x69FBF0, (DWORD)textLoadMission);
	injectFunction (0x69FF20, (DWORD)textClear);
	injectFunction (0x69FBD0, (DWORD)textGetMissionName);
}


void (__cdecl  *imp_clearScreenText)(bool clear);

// TODO: поставить ссылки на переменные внутри .exe (и, далее, на замененные объекты)
BYTE language = 0;	// TODO: menu.language
TCHAR * gamePath = "c:/games/gta-sanandreas";
BYTE mpackNumber = 0;
// INJECTOR end

extern FILE * logfile;

CText gxt;

#define MAX_LANGUAGE 0	// TODO: only english supported yet
static TCHAR * gxtFiles[5] = { "american", "french", "german", "italian", "spanish" };
#define GXT_PATH "%s/text/%s.gxt"

// constructor
CText::CText() {
	m_gxtFile = new CFile (NULL);
	m_mpackFile = new CFile (NULL);

	m_missionName[0] = '\0';
	m_loadedLanguage = 0xFF;	// force load file

	m_fileDirectory = NULL;
	m_fileDirectorySize = 0;

	m_mainKey = m_missionKey = NULL;
	m_mainKeySize = m_missionKeySize = 0;
	m_mainText = m_missionText = NULL;

	imp_clearScreenText = (void (__cdecl *)(bool))0x69EDC0;
}

CText::~CText() {
	if (m_mpackFile) 
		delete m_mpackFile;
	if (m_gxtFile)
		delete m_gxtFile;
}

extern void printMenu ();
static bool menuDone = false;

void CText::load (bool noMpack) {
	clear ();	

	if (language != m_loadedLanguage) {	// (re)open .gxt		
		BYTE lang = language;
		if (lang > MAX_LANGUAGE)
			lang = MAX_LANGUAGE;
		m_loadedLanguage = 0xFF;	// not initialized
		m_missionName[0] = '\0';
		
		TCHAR filenameBuffer[MAX_PATH];	
		_stprintf_s (filenameBuffer, MAX_PATH, GXT_PATH, gamePath, gxtFiles[lang]); // STANDALONE		

		m_gxtFile->reopenFile (filenameBuffer);
		m_gxtFile->mapFile ();

		DWORD offset = 0;
		// process file directory
		Tag * tag = (Tag *)m_gxtFile->getPtr();
		if (!tag) 
			return;	// file open error
		
		// GTA3/VC or SA file ?
		// skip 4 byte fo SA
		if (tag->tag != 0x4C424154) {
			tag = (Tag *)(m_gxtFile->getPtr()+4);
			offset = 4;
		}
		
		if (tag->tag != 0x4C424154) 
			return;	// 'TABL' not found	(TODO: GTA3 have no TABL)

		m_fileDirectory = (TablEntry *)(m_gxtFile->getPtr()+offset+sizeof (Tag));
		m_fileDirectorySize = tag->length/sizeof (TablEntry);
		offset += sizeof (Tag)+tag->length;

		// TODO: load mission 'MAIN' and initialize main ptrs from mission ptrs 		

		// process main 'TKEY' and 'TDAT'
		tag = (Tag *)(m_gxtFile->getPtr()+offset);
		offset += sizeof (Tag);
		if (tag->tag != 0x59454B54)	 
			return; // main 'TKEY' not found
		
		
		m_mainKey = (TKeyEntry *)(m_gxtFile->getPtr ()+offset);
		m_mainKeySize = tag->length/sizeof (TKeyEntry);
		offset += tag->length;

		tag = (Tag *)(m_gxtFile->getPtr()+offset);
		if (tag->tag != 0x54414454) // 'TDAT'
			return; // main 'TDAT' not found
		m_mainText = (char *)(m_gxtFile->getPtr()+offset+sizeof (Tag));
		
		m_loadedLanguage = lang;	// .gxt loaded
	}
	
	// TODO: process mpack


}

void CText::clear() {
	// TODO: _69EDC0 (false); // clearScreenText (); еще не разобрана
	imp_clearScreenText (false);

	// если нужно освободить ресурсы - это будет сделано в деструкторе
	// если нужно перезагузить файл - в load
}

void CText::loadMission (char * missionName) {
	if (mpackNumber > 0)
		return;	// если загружен MPACK, обычные миссии не загружаются

	// clear mission data
	imp_clearScreenText (true);	
	m_missionKey = NULL;
	m_missionKeySize = 0;
	m_missionText = NULL;
	m_missionName[0] = '\0';

	// locate mission in file directory ('TABL')
	DWORD i;
	for (i = 0; i < m_fileDirectorySize; i++)
		if (!strcmp (m_fileDirectory[i].name, missionName))
			break;
	if (i >= m_fileDirectorySize)
		return ;	// mission not found

	// check block tags
	// TKEY
	DWORD offset = m_fileDirectory[i].offset+8; // TODO: SA only ?
	if (m_gxtFile->getLength() < offset+sizeof (Tag))
		return;	// premature end of file

	Tag * tag = (Tag *)(m_gxtFile->getPtr()+offset);
	if (tag->tag != 0x59454B54) // 'TKEY'
		return;	// mission TKEY not found
	offset += sizeof (Tag);
	if (m_gxtFile->getLength() < offset+tag->length)
		return;	// premature end of file
	m_missionKey = (TKeyEntry *)(m_gxtFile->getPtr()+offset);
	m_missionKeySize = tag->length/sizeof (TKeyEntry);
	offset += tag->length;

	// TDAT	
	if (m_gxtFile->getLength() < offset+sizeof (Tag))
		return;	// premature end of file
	tag = (Tag *)(m_gxtFile->getPtr()+offset);
	if (tag->tag != 0x54414454) 
		return; // mission TDAT not found
	offset += sizeof (Tag);
	if (m_gxtFile->getLength() < offset+tag->length)
		return;	// premature end of file
	m_missionText = (char *)(m_gxtFile->getPtr()+offset);

	strcpy_s (m_missionName, 8, missionName);
}

// search hash in TKEY block
DWORD CText::locateKey (DWORD hash, CText::TKeyEntry * key, DWORD size) {
	int low = 0;
	int high = (int)(size-1);

	while (low <= high) {
		int idx = (low+high)/2;
		if (key[idx].hash == hash) 			
			return key[idx].offset;
		if (key[idx].hash < hash)
			low = idx+1;
		else
			high = idx-1;
	}
	return 0xFFFFFFFF;
}

const TCHAR * CText::locate(char *key) {
	if (!key || *key == '\0' || *key == ' ')
		return " ";

	DWORD hash = crc32FromUpcaseString ((BYTE *)key);
	DWORD offset = locateKey (hash, m_mainKey, m_mainKeySize);
	if (offset != 0xFFFFFFFF) 
		return m_mainText+offset;
	
	if (m_missionText) {
		offset = locateKey (hash, m_missionKey, m_missionKeySize);
		if (offset != 0xFFFFFFFF)
			return m_missionText+offset;
	}

	return " NOT FOUND ";	// TODO:
}

