
/*
 * UNPUBLISHED -- Rights  reserved  under  the  copyright  laws  of the 
 * United States.  Use  of a copyright notice is precautionary only and 
 * does not imply publication or disclosure.                            
 *                                                                      
 * THIS DOCUMENTATION CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION 
 * OF    VICARIOUS   VISIONS,  INC.    ANY  DUPLICATION,  MODIFICATION, 
 * DISTRIBUTION, OR DISCLOSURE IS STRICTLY PROHIBITED WITHOUT THE PRIOR 
 * EXPRESS WRITTEN PERMISSION OF VICARIOUS VISIONS, INC.
 */

#include "../server/exe_headers.h"
#include "../client/client.h"
#include "../win32/win_local.h"
#include "../qcommon/qcommon.h"
#include "../qcommon/fixedmap.h"
#include "../zlib/zlib.h"
#include "../qcommon/files.h"

/***********************************************
*
* WINDOWS/XBOX VERSION
*
* Build a translation table, CRC -> file name.  We have the memory.
*
************************************************/

#if defined(_WINDOWS)
#include <windows.h>
#elif defined(_XBOX)
#include <xtl.h>
#endif

struct FileInfo
{
	char* name;
	int size;
};
static VVFixedMap< FileInfo, unsigned int >* s_Files = NULL;
static byte* buffer;

HANDLE s_Mutex = INVALID_HANDLE_VALUE;

int _buildFileList(const char* path, bool insert, bool buildList)
{
	WIN32_FIND_DATA data;
	char spec[MAX_OSPATH];
	int count = 0;

	// Look for all files
	Com_sprintf(spec, sizeof(spec), "%s\\*.*", path);

	HANDLE h = FindFirstFile(spec, &data);
	while (h != INVALID_HANDLE_VALUE)
	{
		char full[MAX_OSPATH];
		Com_sprintf(full, sizeof(full), "%s\\%s", path, data.cFileName);

		if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			// Directory -- lets go recursive
			if (data.cFileName[0] != '.') {
				count += _buildFileList(full, insert, buildList);
			}
		}
		else
		{

			if(insert || buildList)
			{
				// Regular file -- add it to the table
				strlwr(full);
				unsigned int code = crc32(0, (const byte *)full, strlen(full));

				FileInfo info;
				info.name = CopyString(full);
				info.size = data.nFileSizeLow;

				if(insert)
				{
					s_Files->Insert(info, code);
				}

				if(buildList)
				{
					// get the length of the filename
					int len;
					len = strlen(info.name) + 1;

					// save the file code
					*(int*)buffer		=  code;
					buffer				+= sizeof(code);

					// save the name of the file
					strcpy((char*)buffer,info.name);
					buffer				+= len;

					// save the size of the file
					*(int*)buffer		= info.size;
					buffer				+= sizeof(info.size);
				}
			}

			count++;
		}

		// Continue the loop
		if (!FindNextFile(h, &data))
		{
			FindClose(h);
			return count;
		}
	}
	return count;
}

bool _buildFileListFromSavedList(void)
{
	// open the file up for reading
	FILE*	in;
	in = fopen("d:\\xbx_filelist","rb");
	if(!in)
	{
		return false;
	}

	// read in the number of files
	int count;
	if(!(fread(&count,sizeof(count),1,in)))
	{
		fclose(in);
		return false;
	}

	// allocate memory for a temp buffer
	byte*	baseAddr;
	int bufferSize;
	bufferSize	= count * ( 2 * sizeof(int) + MAX_OSPATH );
	buffer		= (byte*)Z_Malloc(bufferSize,TAG_TEMP_WORKSPACE,qtrue,32);
	baseAddr	= buffer;

	// read the rest of the file into a big buffer
	if(!(fread(buffer,bufferSize,1,in)))
	{
		fclose(in);
		Z_Free(baseAddr);
		return false;
	}

	// allocate some memory for s_Files
	s_Files = new VVFixedMap<FileInfo, unsigned int>(count);

	// loop through all the files write out the codes
	int i;
	for(i = 0; i < count; i++)
	{
		FileInfo info;
		unsigned int code;

		// read the code for the file
		code	=  *(int*)buffer;
		buffer	+= sizeof(code);

		// read the filename
		info.name = CopyString((char*)buffer);
		buffer	+= (strlen(info.name) + 1);

		// read the size of the file
		info.size	=  *(int*)buffer;
		buffer		+= sizeof(info.size);

		// save the data
		s_Files->Insert(info, code);
	}

	fclose(in);
	Z_Free(baseAddr);
	return true;
}

bool Sys_SaveFileCodes(void)
{
	bool ret;
	int res;

	// get the number of files
	int count;
	count = _buildFileList(Sys_Cwd(), false, false);

	// open a file for writing
	FILE* out;
	out = fopen("d:\\xbx_filelist","wb");
	if(!out)
	{
		return false;
	}

	// allocate a buffer for writing
	byte*	baseAddr;
	int		bufferSize;
	
	bufferSize	= sizeof(int) + ( count * ( 2 * sizeof(int) + MAX_OSPATH ) );
	baseAddr	= (byte*)Z_Malloc(bufferSize,TAG_TEMP_WORKSPACE,qtrue,32);
	buffer		= baseAddr;

	// write the number of files to the buffer
	*(int*)buffer	=  count;
	buffer			+= sizeof(count);

	// fill up the rest of the buffer
	ret = _buildFileList(Sys_Cwd(), false, true);

	if(!ret)
	{
		// there was a problem
		fclose(out);
		Z_Free(baseAddr);
		return false;
	}

	// attempt to write out the data
	if(!(fwrite(baseAddr,bufferSize,1,out)))
	{
		// there was a problem
		fclose(out);
		Z_Free(baseAddr);
		return false;
	}

	// everything went ok
	fclose(out);
	Z_Free(baseAddr);
	return true;
}

void Sys_InitFileCodes(void)
{
	bool ret;
	int count = 0;

	// First: try to load an existing filecode cache
	ret = _buildFileListFromSavedList();

	// if we had trouble building the list that way
	// we need to do it by searching the files
	if( !ret )
	{
		// There was no filelist cache, make one
		if( !Sys_SaveFileCodes() )
			Com_Error( ERR_DROP, "ERROR: Couldn't create filecode cache\n" );

		// Now re-read it
		if( !_buildFileListFromSavedList() )
			Com_Error( ERR_DROP, "ERROR: Couldn't re-read filecode cache\n" );
	}
	s_Files->Sort();

	// make it thread safe
	s_Mutex = CreateMutex(NULL, FALSE, NULL);
}

void Sys_ShutdownFileCodes(void)
{
	FileInfo*	info = NULL;

	info = s_Files->Pop();
	while(info)
	{
		Z_Free(info->name);
		info->name = NULL;
		info = s_Files->Pop();
	}

	delete s_Files;
	s_Files = NULL;

	CloseHandle(s_Mutex);
}

int Sys_GetFileCode(const char* name)
{
	WaitForSingleObject(s_Mutex, INFINITE);

	// Get system level path
	char* osname = FS_BuildOSPath(name);
	
	// Generate hash for file name
	strlwr(osname);
	unsigned int code = crc32(0, (const byte *)osname, strlen(osname));
	
	// Check if the file exists
	if (!s_Files->Find(code))
	{
		ReleaseMutex(s_Mutex);
		return -1;
	}

	ReleaseMutex(s_Mutex);
	return code;
}

const char* Sys_GetFileCodeName(int code)
{
	WaitForSingleObject(s_Mutex, INFINITE);

	FileInfo *entry = s_Files->Find(code);
	if (entry)
	{
		ReleaseMutex(s_Mutex);
		return entry->name;
	}
	
	ReleaseMutex(s_Mutex);
	return NULL;
}

int Sys_GetFileCodeSize(int code)
{
	WaitForSingleObject(s_Mutex, INFINITE);

	FileInfo *entry = s_Files->Find(code);
	if (entry)
	{
		ReleaseMutex(s_Mutex);
		return entry->size;
	}
	
	ReleaseMutex(s_Mutex);
	return -1;
}
// Quick function to re-scan for new files, update the filecode
// table, and dump the new one to disk
void Sys_FilecodeScan_f( void )
{
	// Make an updated filecode cache
	if( !Sys_SaveFileCodes() )
		Com_Error( ERR_DROP, "ERROR: Couldn't create filecode cache\n" );

	// Throw out our current list
	Sys_ShutdownFileCodes();

	// Re-init, which should use the new list we just made
	Sys_InitFileCodes();
}
