#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <commctrl.h>
#include <vector>
#include "misc.h"
#include "resource.h"
#include "readpak.h"
#include "gui_things.h"
#include "qbkey32.h"

// TODO: 4GB support (SetFilePointer mod)
// TODO: Free image list?

BOOL ReadPakFiles(HWND hDlg, PAKReturn *pakInfo)
{
	PAKEntry thisEntry;
	DWORD readBytes, entryStart, fileSize, i;

	// Get size
	fileSize = GetFileSize(pakInfo->pakFile, NULL);

	// Read entries
	while (1)
	{
		// Read header
		entryStart = SetFilePointer(pakInfo->pakFile, 0, 0, FILE_CURRENT);

		ReadFileBe(pakInfo->pakFile, &thisEntry.fileExt, 4, &readBytes, NULL, pakInfo->isBigEndian);
		thisEntry.containerFile = INVALID_HANDLE_VALUE;
		ReadFileBe(pakInfo->pakFile, &thisEntry.fileOffset, 4, &readBytes, NULL, pakInfo->isBigEndian);
		thisEntry.fileOffset += entryStart;

		// Request PAB if needed, and fix offset
		if (thisEntry.fileOffset >= fileSize)
		{
			if (pakInfo->pabFile == INVALID_HANDLE_VALUE)
			{
				if (RequestPakName(hDlg, pakInfo, NAME_PAB) == FALSE)
				{
					FreePakInfo(hDlg, pakInfo, CLEAN_ALL);
					return FALSE;
				}
			}
			thisEntry.fileOffset -= fileSize;
		}

		ReadFileBe(pakInfo->pakFile, &thisEntry.fileSize, 4, &readBytes, NULL, pakInfo->isBigEndian);
		ReadFileBe(pakInfo->pakFile, &thisEntry.pathId, 4, &readBytes, NULL, pakInfo->isBigEndian);
		ReadFileBe(pakInfo->pakFile, &thisEntry.fileId, 4, &readBytes, NULL, pakInfo->isBigEndian);
		ReadFileBe(pakInfo->pakFile, &thisEntry.titleId, 4, &readBytes, NULL, pakInfo->isBigEndian);
		ReadFileBe(pakInfo->pakFile, &thisEntry.reserved, 4, &readBytes, NULL, pakInfo->isBigEndian);
		ReadFileBe(pakInfo->pakFile, &thisEntry.flags, 4, &readBytes, NULL, pakInfo->isBigEndian);

		thisEntry.niceName = FALSE;

		// Display ID to get info from DBG
		if (pakInfo->platform == PLAT_PS2 || pakInfo->platform == PLAT_NGC)
			thisEntry.displayId = thisEntry.pathId;
		else if (pakInfo->platform == PLAT_XEN)
			thisEntry.displayId = thisEntry.fileId;

		// Filename
		if (thisEntry.flags & PAK_FLAGS_FILENAME)
		{
			// Filename in the PAK...
			ReadFile(pakInfo->pakFile, &thisEntry.inPakFileName, PAK_FILENAME_SIZE, &readBytes, NULL);

			thisEntry.niceName = TRUE;

			// Make display name
			memcpy(thisEntry.displayFileName, thisEntry.inPakFileName, PAK_FILENAME_SIZE);

			// Replace \ with / in display name
			for (i = 0; i < strlen(thisEntry.displayFileName); i++)
			{
				if (thisEntry.displayFileName[i] == '\\')
					thisEntry.displayFileName[i] = '/';
			}
		}
		else
		{
			// No filename
			sprintf(thisEntry.displayFileName, "%.8X", thisEntry.displayId);
		}

		pakInfo->files.push_back(thisEntry);

		if (thisEntry.fileExt == makeQBKey("last") || thisEntry.fileExt == makeQBKey(".last"))
			break;
	}

	return TRUE;
}

BOOL MakeTree(HINSTANCE hInstance, HWND hDlg, PAKReturn *pakInfo)
{
	TVINSERTSTRUCT insertNode;
	TVITEM insertItem, getInfo;
	HTREEITEM parentItem, getInfoItem;

	HIMAGELIST imageList;
	HBITMAP image;

	TCHAR *directory, *filename, tempBuffer[DEFAULT_BUFFER], fileName[DEFAULT_BUFFER];
	DWORD i;
	BOOL isNewItem;

	// Clean tree
	SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_DELETEITEM, 0, (LPARAM) TVI_ROOT);

	// Make and set image list
	imageList = ImageList_Create(16, 16, ILC_COLOR8, 5, 0);
	
	image = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_FOLDER_CLOSE));
	ImageList_Add(imageList, image, NULL);
	DeleteObject(image);

	image = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_FILE_NORMAL));
	ImageList_Add(imageList, image, NULL);
	DeleteObject(image);

	image = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DEBUG_FILE));
	ImageList_Add(imageList, image, NULL);
	DeleteObject(image);

	image = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DATABASE_FILE));
	ImageList_Add(imageList, image, NULL);
	DeleteObject(image);

	image = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_IMAGE));
	ImageList_Add(imageList, image, NULL);
	DeleteObject(image);

	SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_SETIMAGELIST, 0, (LPARAM) imageList);

	// Set constant info...
	insertItem.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
	insertNode.hInsertAfter = TVI_FIRST;

	// Put each file/folder
	for (i = 0; i < pakInfo->files.size() - 1; i++) // -1 because I don't want to see LAST
	{
		parentItem = NULL;
		strcpy(fileName, pakInfo->files[i].displayFileName);

		// Get filename
		filename = strrchr(fileName, '/');

		// Add directories
		if (filename != NULL)
		{
			filename[0] = '\0';
			filename++;

			// Parse directories
			directory = strtok(fileName, "/");

			while (directory != NULL)
			{
				// Set variable info
				insertItem.pszText = directory;
				insertItem.iImage = 0;
				insertItem.iSelectedImage = 0;
				insertItem.lParam = 0;
				insertNode.hParent = parentItem;
				insertNode.item = insertItem;

				// Get existing strings if present
				isNewItem = TRUE;

				getInfoItem = (HTREEITEM) SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM) parentItem); // Get first item of the node
				while (getInfoItem != NULL) // Continue while there are items
				{
					// Put the info to search
					getInfo.hItem = getInfoItem;
					getInfo.mask = TVIF_TEXT;
					getInfo.pszText = tempBuffer;
					getInfo.cchTextMax = DEFAULT_BUFFER;

					// Search
					SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_GETITEM, 0, (LPARAM) &getInfo);

					// See if it's the string and break
					if (strcmp(getInfo.pszText, directory) == 0)
					{
						isNewItem = FALSE;
						parentItem = getInfoItem;
						break;
					}

					// Find more...
					getInfoItem = (HTREEITEM) SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM) getInfoItem);
				}

				// Add new item if not already present
				if (isNewItem == TRUE)
				{
					parentItem = (HTREEITEM) SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_INSERTITEM, 0, (LPARAM) &insertNode);
				}

				directory = strtok(NULL, "/");
			}
		}
		else
			filename = fileName;

		// Add filename...
		insertItem.pszText = filename;
		insertItem.lParam = i;

		if (pakInfo->files[i].fileExt == makeQBKey(".dbg"))
			insertItem.iImage = 2;
		else if (pakInfo->files[i].fileExt == makeQBKey(".qb") || pakInfo->files[i].fileExt == makeQBKey(".sqb"))
			insertItem.iImage = 3;
		else if (pakInfo->files[i].fileExt == makeQBKey(".img"))
			insertItem.iImage = 4;
		else
			insertItem.iImage = 1;

		insertItem.iSelectedImage = insertItem.iImage;
		insertNode.hParent = parentItem;
		insertNode.item = insertItem;
		SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_INSERTITEM, 0, (LPARAM) &insertNode);
	}

	return TRUE;
}

BOOL ExploreSelectedItem(HWND hDlg, PAKReturn *pakInfo, DWORD mode)
{
	HTREEITEM selectedItem;
	TVITEM selectedInfo;

	// Get info about the selected item
	selectedItem = (HTREEITEM) SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_GETNEXTITEM, TVGN_CARET, 0);
	
	if (selectedItem == NULL)
		return FALSE;

	selectedInfo.hItem = selectedItem;
	selectedInfo.mask = TVIF_IMAGE | TVIF_PARAM;
	SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_GETITEM, 0, (LPARAM) &selectedInfo);

	// Extract
	if (selectedInfo.iImage == 0) // It's a folder
	{
		MessageBox(hDlg, "Sorry, folders not supported yet!", NULL, MB_OK | MB_ICONERROR);
	}
	else // It's a file
	{
		if (mode == SELECT_MODE_EXTRACT)
			return ExtractSingleFile(hDlg, pakInfo, selectedInfo.lParam);
		else if (mode == SELECT_MODE_REPLACE)
			return ReplaceSingleFile(hDlg, pakInfo, selectedInfo.lParam);
	}

	return FALSE;
}

BOOL ExtractSingleFile(HWND hDlg, PAKReturn *pakInfo, LPARAM numItem)
{
	HANDLE readFile, outFile;
	PAKEntry *selEntry; // Quick pointer to make code cleaner
	OPENFILENAME saveFile;
	TCHAR saveFileName[DEFAULT_BUFFER], *pointerName;

	BYTE bufferSave[FILE_TMP_BUFFER];
	DWORD bytesRead, bytesWrite, bytesRemain;

	// Quick pointer
	selEntry = &pakInfo->files[numItem];

	// Get the file to read
	if (selEntry->containerFile == INVALID_HANDLE_VALUE)
	{
		if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
			readFile = pakInfo->pabFile;
		else
			readFile = pakInfo->pakFile;
	}
	else
		readFile = selEntry->containerFile;

	// Seek to position
	SetFilePointer(readFile, selEntry->fileOffset, 0, FILE_BEGIN);
	
	// Get file name from path
	pointerName = strrchr(selEntry->displayFileName, '/');

	if (pointerName == NULL)
	{
		strcpy(saveFileName, selEntry->displayFileName);
	}
	else
	{
		pointerName++;
		strcpy(saveFileName, pointerName);
	}

	// Request file
	saveFile.lStructSize = sizeof(OPENFILENAME);
	saveFile.hwndOwner = hDlg;
	saveFile.hInstance = NULL;
	saveFile.lpstrFilter = "All files\0*.*\0\0";
	saveFile.lpstrCustomFilter = NULL;
	saveFile.nMaxCustFilter = 0;
	saveFile.nFilterIndex = 1;
	saveFile.lpstrFile = saveFileName;
	saveFile.nMaxFile = DEFAULT_BUFFER;
	saveFile.lpstrFileTitle = NULL;
	saveFile.nMaxFileTitle = 0;
	saveFile.lpstrInitialDir = NULL;
	saveFile.lpstrTitle = "Extract file";
	saveFile.Flags = OFN_OVERWRITEPROMPT;
	saveFile.nFileOffset = 0;
	saveFile.nFileExtension = 0;
	saveFile.lpstrDefExt = NULL;
	saveFile.lCustData = 0;
	saveFile.lpfnHook = NULL;
	saveFile.lpTemplateName = 0;

	if (GetSaveFileName(&saveFile) == FALSE)
		return FALSE;

	// Open output file
	outFile = CreateFile(saveFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (outFile == INVALID_HANDLE_VALUE)
	{
		DisplayError(hDlg, "Can't open the specified file!");
		return FALSE;
	}
		
	// Truncate
	SetEndOfFile(outFile);

	// Save
	bytesRemain = selEntry->fileSize;
	while (bytesRemain != 0)
	{
		if (ReadFile(readFile, bufferSave, FILE_TMP_BUFFER, &bytesRead, NULL) == TRUE)
		{
			if (bytesRemain >= FILE_TMP_BUFFER)
			{
				WriteFile(outFile, bufferSave, FILE_TMP_BUFFER, &bytesWrite, NULL);
				bytesRemain -= FILE_TMP_BUFFER;
			}
			else
			{
				WriteFile(outFile, bufferSave, bytesRemain, &bytesWrite, NULL);
				bytesRemain = 0;
			}
		}
		else
		{
			DisplayError(hDlg, "Can't read data from the file!");
			CloseHandle(outFile);
			return FALSE;
		}
	}

	// Close
	CloseHandle(outFile);

	return TRUE;
}

BOOL FreePakInfo(HWND hDlg, PAKReturn *pakInfo, DWORD cleanFlags)
{
	DWORD i;

	// Clean tree
	if ((cleanFlags & CLEAN_TREE) == CLEAN_TREE)
		SendDlgItemMessage(hDlg, IDC_PAKFILES, TVM_DELETEITEM, 0, (LPARAM) TVI_ROOT);

	// Close file handles

	if ((cleanFlags & CLEAN_PAKINFO) == CLEAN_PAKINFO)
	{
		for (i = 0; i < pakInfo->files.size(); i++)
		{
			if (pakInfo->files[i].containerFile != INVALID_HANDLE_VALUE)
				CloseHandle(pakInfo->files[i].containerFile);
		}

		// Free files
		pakInfo->files.clear();
	}

	if ((cleanFlags & CLEAN_FILES) == CLEAN_FILES)
	{
		// Close PAK/PAB
		CloseHandle(pakInfo->pakFile);

		if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
			CloseHandle(pakInfo->pabFile);

		pakInfo->pakFile = INVALID_HANDLE_VALUE;
		pakInfo->pakFileName[0] = '\0';
		pakInfo->pabFile = INVALID_HANDLE_VALUE;
		pakInfo->pabFileName[0] = '\0';
	}

	if ((cleanFlags & CLEAN_ALL) == CLEAN_ALL)
	{
		pakInfo->platform = PLAT_PS2;
		pakInfo->isBigEndian = FALSE;
	}
	return TRUE;
}

BOOL ReplaceSingleFile(HWND hDlg, PAKReturn *pakInfo, LPARAM numItem)
{
	OPENFILENAME openReplace;
	PAKEntry *selEntry;
	TCHAR openReplaceName[DEFAULT_BUFFER] = "";
	HANDLE newFile;
	DWORD newFileSize;

	selEntry = &pakInfo->files[numItem]; // Quick pointer

	// Request file
	openReplace.lStructSize = sizeof(OPENFILENAME);
	openReplace.hwndOwner = hDlg;
	openReplace.hInstance = NULL;
	openReplace.lpstrFilter = "All files\0*.*\0\0";
	openReplace.lpstrCustomFilter = NULL;
	openReplace.nMaxCustFilter = 0;
	openReplace.nFilterIndex = 1;
	openReplace.lpstrFile = openReplaceName;
	openReplace.nMaxFile = DEFAULT_BUFFER;
	openReplace.lpstrFileTitle = NULL;
	openReplace.nMaxFileTitle = 0;
	openReplace.lpstrInitialDir = NULL;
	openReplace.lpstrTitle = "Open file to replace";
	openReplace.Flags = 0;
	openReplace.nFileOffset = 0;
	openReplace.nFileExtension = 0;
	openReplace.lpstrDefExt = NULL;
	openReplace.lCustData = 0;
	openReplace.lpfnHook = NULL;
	openReplace.lpTemplateName = 0;

	if (GetOpenFileName(&openReplace) == FALSE)
		return FALSE;

	// Open file
	newFile = CreateFile(openReplaceName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	
	if (newFile == INVALID_HANDLE_VALUE)
	{
		DisplayError(hDlg, "Can't open the specified file!");
		return FALSE;
	}

	// Get size
	newFileSize = GetFileSize(newFile, NULL);

	// Modify existing entry...

	// Close existing handle if there's any
	if (selEntry->containerFile != INVALID_HANDLE_VALUE)
		CloseHandle(selEntry->containerFile);

	// Put file
	selEntry->containerFile = newFile;
	selEntry->fileOffset = 0;
	selEntry->fileSize = newFileSize;

	return TRUE;
}

BOOL SavePakFiles(HWND hDlg, PAKReturn *pakInfo)
{
	DWORD i, newHeaderSize, bytesWrite, currentOffset, finalOffset, bytesRemain, bytesRead, numPadding;
	BYTE zero = 0;
	BYTE bufferSave[FILE_TMP_BUFFER];
	HANDLE tempPakFile, readFrom;
	TCHAR tempPakName[DEFAULT_BUFFER + 5], tempPabName[DEFAULT_BUFFER + 5], bakPakName[DEFAULT_BUFFER + 5], bakPabName[DEFAULT_BUFFER + 5];

	// Get padding to apply
	if (pakInfo->platform == PLAT_PS2)
		numPadding = 16;
	else if (pakInfo->platform == PLAT_NGC)
		numPadding = 32;
	else if (pakInfo->platform == PLAT_XEN)
		numPadding = 4096;

	// Make temporal file names
	strcpy(tempPakName, pakInfo->pakFileName);
	strcat(tempPakName, ".tmp");

	strcpy(bakPakName, pakInfo->pakFileName);
	strcat(bakPakName, ".bak");

	if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
	{
		strcpy(tempPabName, pakInfo->pabFileName);
		strcat(tempPabName, ".tmp");

		strcpy(bakPabName, pakInfo->pabFileName);
		strcat(bakPabName, ".bak");
	}

	// Calculate new header size
	newHeaderSize = 0;

	for (i = 0; i < pakInfo->files.size(); i++) // +1 for LAST entry
	{
		newHeaderSize += 32; // 32 bytes per entry

		if (pakInfo->files[i].flags & PAK_FLAGS_FILENAME) // 160 per filename
			newHeaderSize += PAK_FILENAME_SIZE;
	}

	newHeaderSize += 16; // +16 for null entry

	// XEN header padding
	while ((newHeaderSize % numPadding) != 0)
		newHeaderSize++;

	// Open temporal PAK file
	tempPakFile = CreateFile(tempPakName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	
	if (tempPakFile == INVALID_HANDLE_VALUE)
	{
		DisplayError(hDlg, "Can't open the temporal PAK file!");
		return FALSE;
	}

	// Write header
	currentOffset = newHeaderSize;

	for (i = 0; i < pakInfo->files.size(); i++) // +1 for LAST entry
	{
		finalOffset = currentOffset - SetFilePointer(tempPakFile, 0, 0, FILE_CURRENT);

		WriteFileBe(tempPakFile, &pakInfo->files[i].fileExt, 4, &bytesWrite, NULL, pakInfo->isBigEndian);
		WriteFileBe(tempPakFile, &finalOffset, 4, &bytesWrite, NULL, pakInfo->isBigEndian);

		currentOffset += pakInfo->files[i].fileSize;
		while((currentOffset % numPadding) != 0)
			currentOffset++;

		WriteFileBe(tempPakFile, &pakInfo->files[i].fileSize, 4, &bytesWrite, NULL, pakInfo->isBigEndian);
		WriteFileBe(tempPakFile, &pakInfo->files[i].pathId, 4, &bytesWrite, NULL, pakInfo->isBigEndian);
		WriteFileBe(tempPakFile, &pakInfo->files[i].fileId, 4, &bytesWrite, NULL, pakInfo->isBigEndian);
		WriteFileBe(tempPakFile, &pakInfo->files[i].titleId, 4, &bytesWrite, NULL, pakInfo->isBigEndian);
		WriteFileBe(tempPakFile, &pakInfo->files[i].reserved, 4, &bytesWrite, NULL, pakInfo->isBigEndian);
		WriteFileBe(tempPakFile, &pakInfo->files[i].flags, 4, &bytesWrite, NULL, pakInfo->isBigEndian);

		if (pakInfo->files[i].flags & PAK_FLAGS_FILENAME) // Filename
			WriteFile(tempPakFile, &pakInfo->files[i].inPakFileName, PAK_FILENAME_SIZE, &bytesWrite, NULL);
	}

	for (i = 0; i < 16; i++) // 16 extra bytes at the end of header
		WriteFile(tempPakFile, &zero, 1, &bytesWrite, NULL);


	while ((SetFilePointer(tempPakFile, 0, 0, FILE_CURRENT) % numPadding) != 0)
		WriteFile(tempPakFile, &zero, 1, &bytesWrite, NULL);



	// Change the file to the temporal PAB is required
	if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(tempPakFile);
		tempPakFile = CreateFile(tempPabName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if (tempPakFile == INVALID_HANDLE_VALUE)
		{
			DisplayError(hDlg, "Can't open the temporal PAB file!");
			return FALSE;
		}
	}

	// Write files
	for (i = 0; i < pakInfo->files.size(); i++) // +1 for LAST entry
	{
		// Get the file to read from
		if (pakInfo->files[i].containerFile == INVALID_HANDLE_VALUE)
		{
			if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
				readFrom = pakInfo->pabFile;
			else
				readFrom = pakInfo->pakFile;
		}
		else
			readFrom = pakInfo->files[i].containerFile;

		// Seek file
		SetFilePointer(readFrom, pakInfo->files[i].fileOffset, 0, FILE_BEGIN);

		// Copy file
		bytesRemain = pakInfo->files[i].fileSize;

		while (bytesRemain != 0)
		{
			if (ReadFile(readFrom, bufferSave, FILE_TMP_BUFFER, &bytesRead, NULL) == TRUE)
			{
				if (bytesRemain >= FILE_TMP_BUFFER)
				{
					WriteFile(tempPakFile, bufferSave, FILE_TMP_BUFFER, &bytesWrite, NULL);
					bytesRemain -= FILE_TMP_BUFFER;
				}
				else
				{
					WriteFile(tempPakFile, bufferSave, bytesRemain, &bytesWrite, NULL);
					bytesRemain = 0;
				}
			}
			else
			{
				DisplayError(hDlg, "Can't read the file!");

				CloseHandle(tempPakFile);

				if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
					CloseHandle(tempPakFile);
				return FALSE;
			}
		}

		// Add padding
		while ((SetFilePointer(tempPakFile, 0, 0, FILE_CURRENT) % numPadding) != 0)
			WriteFile(tempPakFile, &zero, 1, &bytesWrite, NULL);
	}

	// Close temporal PAK/PAB file
	CloseHandle(tempPakFile);

	// Replace the PAK and the PAB file with the new files
	CloseHandle(pakInfo->pakFile);

	if (ReplaceFile(pakInfo->pakFileName, tempPakName, bakPakName, REPLACEFILE_WRITE_THROUGH, NULL, NULL) == FALSE)
	{
		DisplayError(hDlg, "Can't replace the temporal PAK file with the actual PAK file!, program will close");
		ExitProcess(-1);
		return FALSE;
	}

	if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(pakInfo->pabFile);
		if (ReplaceFile(pakInfo->pabFileName, tempPabName, bakPabName, REPLACEFILE_WRITE_THROUGH, NULL, NULL) == FALSE)
		{
			DisplayError(hDlg, "Can't replace the temporal PAB file with the actual PAB file (restore your backup of the PAK file!), program will close");
			ExitProcess(-1);
			return FALSE;
		}
	}
	
	return TRUE;
}

BOOL ReOpenPakAndPab(HWND hDlg, PAKReturn *pakInfo)
{
	pakInfo->pakFile = CreateFile(pakInfo->pakFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (pakInfo->pakFile == INVALID_HANDLE_VALUE)
	{
		DisplayError(hDlg, "Can't reopen the PAK file!");
		return FALSE;
	}

	if (pakInfo->pabFile != INVALID_HANDLE_VALUE)
	{
		pakInfo->pabFile = CreateFile(pakInfo->pabFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

		if (pakInfo->pabFile == INVALID_HANDLE_VALUE)
		{
			DisplayError(hDlg, "Can't reopen the PAB file!");
			return FALSE;
		}
	}

	return TRUE;
}

int searchQbKeyDbg(const void *a, const void *b)
{
	DBGBinEntry *x, *y;

	// Convert void * to the original types
	x = (DBGBinEntry *) a;
	y = (DBGBinEntry *) b;

	// Compare
	if (x->qbKey > y->qbKey)
		return 1;
	else if (x->qbKey == y->qbKey)
		return 0;
	else if (x->qbKey < y->qbKey)
		return -1;

	return 0; // Just to avoid an stupid VC warning >_>
}

BOOL ReadDbgInfo(HWND hDlg, PAKReturn *pakInfo)
{
	HANDLE dbgFile;
	DWORD numEntries, bytesRead, i;
	DBGBinEntry *entries, *foundEntry;
	OPENFILENAME openDebug;
	TCHAR openDebugName[DEFAULT_BUFFER] = "";

	// Select file dialog
	openDebug.lStructSize = sizeof(OPENFILENAME);
	openDebug.hwndOwner = hDlg;
	openDebug.hInstance = NULL;
	openDebug.lpstrFilter = "All files\0*.*\0\0";
	openDebug.lpstrCustomFilter = NULL;
	openDebug.nMaxCustFilter = 0;
	openDebug.nFilterIndex = 1;
	openDebug.lpstrFile = openDebugName;
	openDebug.nMaxFile = DEFAULT_BUFFER;
	openDebug.lpstrFileTitle = NULL;
	openDebug.nMaxFileTitle = 0;
	openDebug.lpstrInitialDir = NULL;
	openDebug.lpstrTitle = "Open DBG file";
	openDebug.Flags = 0;
	openDebug.nFileOffset = 0;
	openDebug.nFileExtension = 0;
	openDebug.lpstrDefExt = NULL;
	openDebug.lCustData = 0;
	openDebug.lpfnHook = NULL;
	openDebug.lpTemplateName = 0;

	if (GetOpenFileName(&openDebug) == FALSE)
		return FALSE;

	// Open DBG
	dbgFile = CreateFile(openDebugName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (dbgFile == INVALID_HANDLE_VALUE)
	{
		DisplayError(hDlg, "Can't open the specified file!");
		return FALSE;
	}

	ReadFile(dbgFile, &numEntries, 4, &bytesRead, NULL);

	// Get memory
	entries = (DBGBinEntry *) malloc(numEntries * sizeof(DBGBinEntry));
	if (entries == NULL)
	{
		MessageBox(hDlg, "Can't get enough memory for the DBG file", NULL, 0);
		return FALSE;
	}

	// Read entries
	for (i = 0; i < numEntries; i++)
	{
		ReadFile(dbgFile, &entries[i].qbKey, 4, &bytesRead, NULL);
		ReadFile(dbgFile, &entries[i].pointerString, 4, &bytesRead, NULL);
	}

	// Find entries
	for (i = 0; i < pakInfo->files.size(); i++)
	{
		if (pakInfo->files[i].niceName == FALSE)
		{
			foundEntry = (DBGBinEntry *) bsearch(&pakInfo->files[i].displayId, entries, numEntries, sizeof(DBGBinEntry), searchQbKeyDbg);

			if (foundEntry != NULL)
			{
				pakInfo->files[i].niceName = TRUE; // Nice name :)

				// Obtain the name from the DBG.BIN
				SetFilePointer(dbgFile, foundEntry->pointerString, 0, FILE_BEGIN);
				ReadFile(dbgFile, &pakInfo->files[i].displayFileName, PAK_FILENAME_SIZE, &bytesRead, NULL);
				pakInfo->files[i].displayFileName[PAK_FILENAME_SIZE - 1] = '\0';
			}
		}
	}

	// Clean
	free(entries);
	CloseHandle(dbgFile);

	return TRUE;
}