
/*
  File: include/basic/filesys.h
  Description: Operating-system specific file system manipulation classes
  Part of the Mantis project
  (c) 2002 Lycos Inc.

  This file is distributed for the sole purpose of allowing you to write
  software to interface with Sonique 2 and other Mantis programs. It is
  not to be modified or used for any other purpose without written
  authorization.

  If you redistribute this file, it must be in the original package
  complete with all other files provided, including the usage agreement.

  Do not remove this notice.
*/

#ifndef _FILESYS_H_
#error "filesys.h" not yet included!
#endif

#ifndef	_WIN32_FILESYSPORT_H__
#define _WIN32_FILESYSPORT_H__

#ifdef DEBUG_INCLUDE_ORDER
#pragma message("Including " __FILE__ "\n" )
#endif

#if _MSC_VER > 1000
#pragma once
#endif

#define WIN32_LEAN_AND_MEAN
#include <stdlib.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "typesport.h"
#include "win_ostest.h"
#include "filesys.h"
#include "shellapi.h"
#include "shlobj.h"

/*
extern  char*		GetCurImagePathA();
extern  wchar_t*	GetCurImagePath();
*/


#define SCARAB_NAME_MAX	MAX_PATH
#define NAME_MAX		MAX_PATH

#define DIR_DIVIDER_STRING	"\\"
#define LDIR_DIVIDER_STRING	L"\\"
#define DIR_DIVIDER_CHAR	'\\'
#define LDIR_DIVIDER_CHAR	L'\\'
#define EXTENSION_DIVIDER_STRING	"."
#define LEXTENSION_DIVIDER_STRING	L"."
#define EXTENSION_DIVIDER_CHAR		'.'
#define LEXTENSION_DIVIDER_CHAR		L'.'
#define DIR_DIVIDER_WSTRING	LDIR_DIVIDER_STRING
#define DIR_DIVIDER_WCHAR	LDIR_DIVIDER_CHAR
#define END_OF_LINE			"\r\n"
#define LEND_OF_LINE		L"\r\n"
#define END_OF_LINE_WIDE	LENDOFLINE
#define FILENAMESARECASESENSITIVE false

inline bool IOObject::MakeMemoryPtr( uint8** ppMemory, uint32 *size )
{
	if (!IsOpen())
		return false;

	if( pOpenMemory )
	{
		*ppMemory	= pOpenMemory;
		*size		= lMemSize;
		return true;
	}
	else
	{
		if ( pAllocatedMemory )
		{
			*ppMemory = pAllocatedMemory->pAllocatedMemoryMap;
			*size = pAllocatedMemory->nAllocatedMemoryLength;
			return true;
		} else {
			uint32 len;

			GetLength(len);

			pAllocatedMemory = (IOObjectMemoryTracker*)new uint8[sizeof(IOObjectMemoryTracker)+len];

			if( pAllocatedMemory )
			{
				pAllocatedMemory->nAllocatedMemoryRefCount = 1;
				pAllocatedMemory->nAllocatedMemoryLength = len;
				if( Read( &pAllocatedMemory->pAllocatedMemoryMap, len, size ) )
				{
					*ppMemory = pAllocatedMemory->pAllocatedMemoryMap;
					return true;
				} else {
					delete pAllocatedMemory;
					pAllocatedMemory = NULL;
				}
			}
		}
	}

	return false;
};

#ifdef _DEBUG
inline	void IOObject::FreeMemoryPtr( uint8* pMem )
#else
inline	void IOObject::FreeMemoryPtr( uint8* /* pMem*/ )
#endif
{
	if( !pOpenMemory ) // do something
	{
		ASSERT(pAllocatedMemory && pMem == pAllocatedMemory->pAllocatedMemoryMap);
		if (--pAllocatedMemory->nAllocatedMemoryRefCount == 0)
		{
			delete pAllocatedMemory;
			pAllocatedMemory = NULL;
		}
//			delete pMem;
	}
}


inline bool IOObject::OpenFile( const char *szFilename, long flags )
{
	ASSERT(szFilename && !strchr(" \t\n",szFilename[strlen(szFilename)-1]) ); // assert that there isn't any trailing whitespace, (this sometimes happens, and is annoying )
	TRACE1( "Warning: Using ansi fileopen for %s!\n", szFilename );
	Close();

	HANDLE hTmp = CreateFileA(	szFilename,
								(flags & access_read ? GENERIC_READ : 0)   |  (flags & access_write ? GENERIC_WRITE : 0),
								(flags & share_read  ? FILE_SHARE_READ : 0 )  |  (flags & share_write  ? FILE_SHARE_WRITE : 0 ),
								NULL,
								(flags & open_always)==create_new ? CREATE_NEW : 
									(flags & open_always)==create_always ? CREATE_ALWAYS : 
										(flags & open_always)==open_existing ? OPEN_EXISTING :
											(flags & open_always)==open_always ? OPEN_ALWAYS  : 
												0,
								flags & hint_random ? FILE_FLAG_RANDOM_ACCESS : flags & hint_sequential ? FILE_FLAG_SEQUENTIAL_SCAN : 0,
								NULL );
 		


	
	if (hTmp == INVALID_HANDLE_VALUE)
	{
#ifdef _DEBUG
		uint32 i = GetLastError();
		LPVOID lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			i,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL 
		);
		TRACE2("open file \"%s\" failed - reason = %s\n", szFilename, lpMsgBuf);
		LocalFree( lpMsgBuf );
#endif		
		return false;
	}
	bReadOnly = !(flags & access_write);
	wsCurrentFilename = szFilename;

	ASSERT(!pHandle);
	pHandle = new IOObjectHandleTracker;
	if ( !wsCurrentFilename.Len() || !pHandle )
	{
		CloseHandle(hTmp);
		wsCurrentFilename = (char*)NULL;
		return false;
	}

	pHandle->nCount = 1;
	pHandle->hFile = hTmp;
	return true;
};

inline bool IOObject::OpenFile( const wchar_t *wcFilename, long flags )
{
	if( !IsUnicodeFileSystem() )
		return OpenFile( AString(wcFilename), flags );

	ASSERT(wcFilename && !wcschr(L" \t\n",wcFilename[wcslen(wcFilename)-1]) ); // assert that there isn't any trailing whitespace, (this sometimes happens, and is annoying )
	Close();

	HANDLE hTmp = CreateFileW(	wcFilename,//szBuffer,
									(flags & access_read ? GENERIC_READ : 0)   |  (flags & access_write ? GENERIC_WRITE : 0),
									(flags & share_read  ? FILE_SHARE_READ : 0 )  |  (flags & share_write  ? FILE_SHARE_WRITE : 0 ),
									NULL,
									(flags & open_always)==create_new ? CREATE_NEW : 
										(flags & open_always)==create_always ? CREATE_ALWAYS : 
											(flags & open_always)==open_existing ? OPEN_EXISTING :
												(flags & open_always)==open_always ? OPEN_ALWAYS  : 
													0,
									flags & hint_random ? FILE_FLAG_RANDOM_ACCESS : flags & hint_sequential ? FILE_FLAG_SEQUENTIAL_SCAN : 0,
									NULL );
	
	if ( hTmp== INVALID_HANDLE_VALUE)
	{
#ifdef _DEBUG
		uint32 i = GetLastError();
		LPVOID lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			i,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL 
		);
		TRACE2("open file \"%s\" failed - reason = %s\n", (const char*)AString(wcFilename),lpMsgBuf);
		LocalFree( lpMsgBuf );
#endif
		return false;
	}

 	bReadOnly = !(flags & access_write);
	wsCurrentFilename = wcFilename;

	ASSERT(!pHandle);
	pHandle = new IOObjectHandleTracker;
	if ( !wsCurrentFilename.Len() || !pHandle )
	{
		CloseHandle(hTmp);
		wsCurrentFilename = (char*)NULL;
		return false;
	}

	pHandle->nCount = 1;
	pHandle->hFile = hTmp;

	return true;
};

inline bool IOObject::Close()
{
	bool bRet = false;

	if( pHandle != NULL )
	{
		if (!--pHandle->nCount)
		{
			CloseHandle( pHandle->hFile );
			delete pHandle;
		}
		pHandle = (IOObjectHandleTracker*)NULL;
		bRet = true;
	}
	if( pOpenMemory )
	{
		pOpenMemory = NULL;
		bRet = true;
	}
	if (pAllocatedMemory)
	{
		if (--pAllocatedMemory->nAllocatedMemoryRefCount == 0)
			delete pAllocatedMemory;
		pAllocatedMemory = NULL;
	}
	wsCurrentFilename = (wchar_t*)NULL;

	return bRet;

};


inline bool IOObject::PortFileRead( void* pData, uint32 numBytes, uint32* numBytesRead ) 
{
 	if( !pHandle || pHandle->hFile == INVALID_HANDLE_VALUE )
 		return false;
	
	if( ReadFile( pHandle->hFile, pData, numBytes, numBytesRead, 0 ) )
		return true;
	else
		return false;
};

inline bool IOObject::PortFileWrite( const void* pData, uint32 numBytes, uint32* numBytesWritten )
{
	if( !pHandle || pHandle->hFile == INVALID_HANDLE_VALUE )
 		return false;

	if( WriteFile( pHandle->hFile, pData, numBytes, numBytesWritten, 0 ) )
		return true;
	else
		return false;

};


inline uint32 IOObject::PortFileGetCurPos() 
{
	return Seek(0,seek_cur);
}


inline bool IOObject::PortFileGetLength(uint32 & len) 
{
	int32 FileLength = GetFileSize(pHandle->hFile, NULL);
	if(FileLength == -1)
	{
		len = 0;
		return(false);
	}

	len = FileLength;
	return(true);
}

#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
#endif
inline uint32 IOObject::PortFileSeek( long offset, IOObjectSeekModes sm )
{
	if( !pHandle || pHandle->hFile == INVALID_HANDLE_VALUE )
		return (uint32)-1;

	uint32 tmp = SetFilePointer( pHandle->hFile, offset, NULL, sm == seek_set ? FILE_BEGIN : sm == seek_cur ? FILE_CURRENT : sm == seek_end ? FILE_END : 0 );
	return (INVALID_SET_FILE_POINTER == tmp) ? -1 : tmp;
};

inline uint32 IOObject::PortFileTruncate( )
{
	if ( !pHandle || pHandle->hFile == INVALID_HANDLE_VALUE )
		return 0;

	SetEndOfFile( pHandle->hFile );
	return GetFileSize( pHandle->hFile, NULL );
};

inline bool IOObject::GetLastModified(tm& tmModified)
{

	bool bWorked;

	if (IsOpen() && !pOpenMemory)
	{
		_BY_HANDLE_FILE_INFORMATION bhfi;
		SYSTEMTIME st;

		GetFileInformationByHandle(pHandle->hFile, &bhfi);
		FileTimeToSystemTime(&bhfi.ftLastWriteTime, &st);

		tmModified.tm_sec = st.wSecond;
		tmModified.tm_min = st.wMinute;
		tmModified.tm_hour = st.wHour;
		tmModified.tm_mday = st.wDay;
		tmModified.tm_mon = st.wMonth - 1;
		tmModified.tm_year = st.wYear;
		tmModified.tm_isdst = tmModified.tm_wday = tmModified.tm_yday = 0; // not implemented

		bWorked = true;
	}
	else
		bWorked = false;

	return bWorked;

};
 
namespace filesys
{

template <class CHARTYPE>
bool RenameFile( const CHARTYPE* szNewName, const CHARTYPE* szOldName )
{
#ifdef _DEBUG
	DWORD dwErr;
	LPVOID lpMsgBuf;
#endif
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
	{
		if ( MoveFileA( AString(szOldName), AString(szNewName) ) )
			return true;
	}
	else
	{
		if ( MoveFileW( (const wchar_t*)szOldName, (const wchar_t*)szNewName)  )
			return true;

	}
	
#ifdef _DEBUG
	dwErr = GetLastError();
	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dwErr,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL 
	);
	TRACE3("rename file \"%s\" to \"%s\" failed - reason = %s\n", (const char*)AString(szOldName), (const char*)AString(szNewName), lpMsgBuf);
	LocalFree( lpMsgBuf );
#endif

	return false;
}


template<class CHARTYPE>
inline bool DeleteFile( const CHARTYPE* ctFileName ) 
{
#ifdef _DEBUG
	bool bWorked;
	DWORD dwError;
	
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
	{
		bWorked = (::DeleteFileA( AString(ctFileName)) != 0);
		if (!bWorked)
		{
			dwError = GetLastError();
			TRACE1("DeleteFile failed with error %u\n", dwError);
		}
	}
	else
	{
		bWorked = (::DeleteFileW( WString(ctFileName)) != 0);
		if (!bWorked)
		{
			dwError = GetLastError();
			TRACE1("DeleteFile failed with error %u\n", dwError);
		}
	}
	return bWorked;
#else
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
		return 0!=::DeleteFileA( AString(ctFileName) );
	else
		return 0!=::DeleteFileW( (const wchar_t*)ctFileName );
#endif
}

inline int CreatePipe(int pipe[2], int buffersize)
{
	return _pipe(pipe, buffersize, _O_TEXT);
}
inline int HookFD(int fdnum, int newfd, int* oldfd)
{
	if( oldfd )
		*oldfd = _dup(fdnum);
	_dup2(newfd, fdnum);
	return 0;
}

// jmc110802 WString!!
//inline AString GetSharedDataDirectory(void)
inline WString GetSharedDataDirectory(void)
{
	WString ret;
	int size;

	if ( IsUnicodeFileSystem() )
	{
		size = GetSystemDirectoryW(NULL, 0);

		// lame OS (windows) requires this.
		wchar_t* wcBuffer = new wchar_t[max(MAX_PATH, size)];

		if( GetSystemDirectoryW( wcBuffer, size) )
			ret = wcBuffer;

		// delete the temp
		delete wcBuffer;
	}
	else
	{
		size = GetSystemDirectoryA(NULL, 0);

		// lame OS (windows) requires this.
		char* acBuffer = new char[max(MAX_PATH, size)];

		if( GetSystemDirectoryA( acBuffer, size) )
			ret = acBuffer;

		// delete the temp
		delete acBuffer;
	}
	
	return ret;
}

template <class CHARTYPE>
inline bool GetExecutablePath(TString<CHARTYPE> &ToHere)
{

	if (!IsUnicodeFileSystem() && sizeof(CHARTYPE) == sizeof(wchar_t))
	{
		AString foo;
		bool b = GetExecutablePath(foo);
		ToHere = foo;
		return b;
	}

	CHARTYPE *p = new CHARTYPE[4000];
	if ((sizeof(CHARTYPE) == sizeof(wchar_t))?GetModuleFileNameW(NULL,(wchar_t*)p, 3999) : GetModuleFileNameA(NULL, (char*)p, 3999))
	{
		ToHere = TString<CHARTYPE>(p);
		delete[] p;
		return true;
	}
	else ToHere = NULL;
	delete[] p;
	return false;
}


inline WString GetPrivateDataDirectory(void)	// jmc080602 return WString instead of AString
{
    WString wsRet;

	/* jmc080502 - rewrote this to use the shell API calls instead of the registry */

	LPITEMIDLIST pidl = NULL;
	LPMALLOC pMalloc = NULL;
	wchar_t wcBuffer[_MAX_PATH];
	char acBuffer[_MAX_PATH];

	SHGetMalloc(&pMalloc);

	BOOL bSuccess = false;
	HRESULT hr = SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA, &pidl);
	if ((pidl != NULL) && (hr == NOERROR))
	{
		if( IsUnicodeFileSystem() )
		{
			if (SHGetPathFromIDListW(pidl, wcBuffer))
			{
				bSuccess = true;
				wsRet = wcBuffer;
			}
		}
		else
		{
			if (SHGetPathFromIDListA(pidl, acBuffer))
			{
				bSuccess = true;
				wsRet = acBuffer;
			}
		}			
		pMalloc->Free(pidl);
		pidl = NULL;
	}
	// if that failed, try the registry...
	unsigned long len;
	HKEY key;
	if ( !bSuccess && (RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", &key)  == ERROR_SUCCESS) )
	{
		DWORD Type;
		if (IsUnicodeFileSystem())
		{
			if( RegQueryValueExW(key, L"AppData", NULL, &Type, (BYTE*)NULL, &len) == ERROR_SUCCESS && Type == REG_SZ )
			{
				wsRet.Allocate(len-1);
				RegQueryValueExW(key, L"AppData", NULL, &Type, (BYTE*)wsRet.GetAt(), &len);
				bSuccess = true;
			}
		}
		else
		{
			if( RegQueryValueExA(key, "AppData", NULL, &Type, (BYTE*)NULL, &len) == ERROR_SUCCESS && Type == REG_SZ )
			{
				wsRet.Allocate(len-1);
				RegQueryValueExA(key, "AppData", NULL, &Type, (BYTE*)wsRet.GetAt(), &len);
				bSuccess = true;
			}
		}
		RegCloseKey(key);
	}
	

	pMalloc->Release();

	return wsRet;
}
// jmc110802 WString!!
//inline AString GetHomeDirectory(void)
inline WString GetHomeDirectory(void)
{
    WString wsRet;
	unsigned long len;
	HKEY key;


	LPITEMIDLIST pidl = NULL;
	LPMALLOC pMalloc = NULL;
	wchar_t wcBuffer[_MAX_PATH];
	char acBuffer[_MAX_PATH];

	SHGetMalloc(&pMalloc);

	BOOL bSuccess = false;
	HRESULT hr = SHGetSpecialFolderLocation(NULL,CSIDL_DESKTOPDIRECTORY, &pidl);
	if ((pidl != NULL) && (hr == NOERROR))
	{
		if( IsUnicodeFileSystem() )
		{
			if (SHGetPathFromIDListW(pidl, wcBuffer))
			{
				bSuccess = true;
				wsRet = wcBuffer;
			}
		}
		else
		{
			if (SHGetPathFromIDListA(pidl, acBuffer))
			{
				bSuccess = true;
				wsRet = acBuffer;
			}
		}			
		pMalloc->Free(pidl);
		pidl = NULL;
	}
	// if that failed, try the registry...
	if ( !bSuccess && (RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", &key)  == ERROR_SUCCESS) )
	{
        DWORD Type;
		if (IsUnicodeFileSystem())
		{
			if( RegQueryValueExW(key, L"Desktop", NULL, &Type, (BYTE*)NULL, &len) == ERROR_SUCCESS && Type == REG_SZ )
			{
				wsRet.Allocate(len-1);
				RegQueryValueExW(key, L"Desktop", NULL, &Type, (BYTE*)wsRet.GetAt(), &len);
			}
		}
		else
		{
			if( RegQueryValueExA(key, "Desktop", NULL, &Type, (BYTE*)NULL, &len) == ERROR_SUCCESS && Type == REG_SZ )
			{
				wsRet.Allocate(len-1);
				RegQueryValueExA(key, "Desktop", NULL, &Type, (BYTE*)wsRet.GetAt(), &len);
			}
		}
		RegCloseKey(key);
	}
	return wsRet;
}

inline FileAccess GetFileAccess( const wchar_t* FileName ) 
{
	int fa = 0;

	if( FileName )
	{
		DWORD dwReturn = (DWORD)-1;
		if( IsUnicodeFileSystem() ) // use wide version
		{
			/*
			fa |= (~(_waccess( FileName, 0 ))) & FileAccess_Exists;	// existence?
			fa |= (~(_waccess( FileName, 2 ))) & FileAccess_Read;	// readable?
			fa |= (~(_waccess( FileName, 4 ))) & FileAccess_Write;	// writable?
			*/

			dwReturn = GetFileAttributesW( FileName );
		}
		else // convert
		{
			
			AString asFile( FileName );
			/*
			
			fa |= (~(_access( asFile, 0 ))) & FileAccess_Exists;	// existence?
			fa |= (~(_access( asFile, 2 ))) & FileAccess_Read;		// readable?
			fa |= (~(_access( asFile, 4 ))) & FileAccess_Write;		// writable?
			*/
			dwReturn = GetFileAttributesA( asFile );
		}

		if( dwReturn != -1 ) // exists?
		{
			fa |= FileAccess_Exists;
			fa |= (dwReturn & FILE_ATTRIBUTE_READONLY) ? FileAccess_Read : FileAccess_Full;
		}
		else
			fa = FileAccess_None;
	}

	return (FileAccess)fa;
}

template<class CHARTYPE>
inline bool FileExists(const  CHARTYPE* ctFileName ) 
{
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
		return -1 != GetFileAttributesA( (const char*)AString(ctFileName) );
	else
		return -1 != GetFileAttributesW( (const wchar_t*)ctFileName );
}


inline bool IsFileSystemCaseSensitive() 
{
	return false;
}

template<class CHARTYPE>
inline bool GetCurrentPath( TString<CHARTYPE>& toHere ) 
{
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
	{
		AString str;
		uint32 nCount = GetCurrentDirectoryA(0,NULL);
		str.Allocate(nCount-1);
		
		uint32 nRet = GetCurrentDirectoryA( nCount, (char*)  str.GetAt()  );
		
		toHere = str;
		return nRet != 0;
	}
	else
	{
		// wchar_t

		uint32 nCount = GetCurrentDirectoryW(0,NULL);
		toHere.Allocate(nCount-1);
		
		uint32 nRet = GetCurrentDirectoryW( nCount, (wchar_t*) toHere.GetAt() );

		return nRet != 0;
	}
}

template<class CHARTYPE>
inline bool SetCurrentPath( const CHARTYPE* fromHere ) 
{
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
		return 0!=SetCurrentDirectoryA( (const char*)AString(fromHere) );
	else
		return 0!=SetCurrentDirectoryW( (const wchar_t*) fromHere );
}

template<class CHARTYPE>
inline bool GetLastModified( const CHARTYPE* ctFileName, tm& tmModified)
{
	FILETIME time;
	HANDLE hFind;

	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
	{
		WIN32_FIND_DATA Data;
		hFind = FindFirstFileA((const char*)AString(ctFileName), &Data);
		time = Data.ftLastWriteTime;
	} else {
		WIN32_FIND_DATAW Data;
		hFind = FindFirstFileW((const wchar_t*)ctFileName, &Data);
		time = Data.ftLastWriteTime;
	}

	if( hFind == INVALID_HANDLE_VALUE )
		return false;

	SYSTEMTIME st;
	FileTimeToSystemTime(&time, &st);

	tmModified.tm_sec = st.wSecond;
	tmModified.tm_min = st.wMinute;
	tmModified.tm_hour = st.wHour;
	tmModified.tm_mday = st.wDay;
	tmModified.tm_mon = st.wMonth - 1;
	tmModified.tm_year = st.wYear;
	tmModified.tm_isdst = tmModified.tm_wday = tmModified.tm_yday = 0; // not implemented

	FindClose(hFind);
	return true;
}

template<class CHARTYPE>
inline TString<CHARTYPE> FindTempName( const CHARTYPE* szBasePath, const CHARTYPE* szBaseName )
{
	TString<CHARTYPE> Temp;
	uint32 nCount = 0;
	uint32 len;

	do{
		Temp = szBasePath;
		if( sizeof(CHARTYPE) == sizeof(char) )
			len = strlen((const char*)szBasePath);
		else if( sizeof(CHARTYPE) == sizeof(wchar_t) )
			len = wcslen((const wchar_t*)szBasePath);
		else
			ASSERT(!"huh?");

		if( len == 0 || (szBasePath[len-1] != CONSTCHAR('/',CHARTYPE) && szBasePath[len-1] != CONSTCHAR('\\',CHARTYPE)) )
			Temp.Cat(CONSTSTR(DIR_DIVIDER_STRING,CHARTYPE));
		Temp.printa(CONSTSTR("%s%x",CHARTYPE), szBaseName, nCount++ );
	}
	while( filesys::FileExists( (const CHARTYPE*)Temp )  );

	return Temp;
}

template<class CHARTYPE>
inline TString<CHARTYPE> GetTemporaryFileName(const CHARTYPE* BaseName)
{
#if defined(_WIN32)
	TString<CHARTYPE> Path;

	uint32 nSize = GetTempPath(0,NULL);
	if( !nSize )
		return false;  // zero sized path? odd
		
	if( sizeof(CHARTYPE) == sizeof(char) || !IsUnicodeFileSystem() )
	{
        TString<char> asPath;
		asPath.Allocate(nSize-1);
		if( !GetTempPathA( nSize, (char*)asPath.GetAt() ) )
			return (const CHARTYPE*)NULL; // failed to get temp path
        Path = asPath;
	} else if( sizeof(CHARTYPE) == sizeof(wchar_t) ) {
		Path.Allocate(nSize-1);
		if( !GetTempPathW( nSize, (wchar_t*)Path.GetAt() ) )
			return (const CHARTYPE*)NULL; // failed to get temp path
	} else {
		ASSERT(!"what da heck?");
	}
#elif defined(_LINUX)
	Path = CONSTSTR("/tmp",CHARTYPE);
#else
#error foo
#endif

	return FindTempName( (const CHARTYPE*)Path, (const CHARTYPE*)BaseName );
}


extern char hostname[MAX_COMPUTERNAME_LENGTH+1];
extern wchar_t whostname[MAX_COMPUTERNAME_LENGTH+1];

template<class CHARTYPE>
inline bool IsRemotePath(const CHARTYPE* Path)
{
    if( sizeof(CHARTYPE) == sizeof(char) )
    {
        int32 lead;

        if( Path[0] == CONSTCHAR('\\',CHARTYPE) &&
            Path[1] == CONSTCHAR('\\',CHARTYPE) &&
            Path[2] == CONSTCHAR('\\',CHARTYPE) &&
            Path[3] == CONSTCHAR('\\',CHARTYPE) &&
            Path[4] == CONSTCHAR('.',CHARTYPE) )
            Path += 5;

        if( Path[0] == CONSTCHAR('\\',CHARTYPE) &&
            Path[1] == CONSTCHAR('\\',CHARTYPE) &&
            isalnum(Path[2]) )
        {
            lead = 2;
           IsLocalUNCA:
            if( !strnicmp((char*)Path+2, "localhost\\", strlenof("localhost\\")) ||
                !strnicmp((char*)Path+2, "127.0.0.1\\", strlenof("127.0.0.1\\")) ||
                (!strnicmp((char*)Path+2, hostname,      strlen(  hostname     )) && Path[2+strlen(hostname)] == CONSTCHAR('\\',CHARTYPE)) )
                return false;
            return true;
        }
        if( !strnicmp((char*)Path, "\\\\?\\UNC\\", strlenof("\\\\?\\UNC\\")) )
        {
            lead = strlenof("\\\\?\\UNC\\");
            goto IsLocalUNCA;
        }
        return false;
    } else if( sizeof(CHARTYPE) == sizeof(wchar_t) ) {
        int32 lead;

        if( Path[0] == CONSTCHAR('\\',CHARTYPE) &&
            Path[1] == CONSTCHAR('\\',CHARTYPE) &&
            Path[2] == CONSTCHAR('\\',CHARTYPE) &&
            Path[3] == CONSTCHAR('\\',CHARTYPE) &&
            Path[4] == CONSTCHAR('.',CHARTYPE) )
            Path += 5;

        if( Path[0] == CONSTCHAR('\\',CHARTYPE) &&
            Path[1] == CONSTCHAR('\\',CHARTYPE) &&
            iswalnum(Path[2]) )
        {
            lead = 2;
           IsLocalUNCW:
            if( !wcsnicmp((wchar_t*)Path+2, L"localhost\\", strlenof(L"localhost\\")) ||
                !wcsnicmp((wchar_t*)Path+2, L"127.0.0.1\\", strlenof(L"127.0.0.1\\")) ||
                (!wcsnicmp((wchar_t*)Path+2, whostname,      wcslen(  whostname     )) && Path[2+wcslen(whostname)] == CONSTCHAR('\\',CHARTYPE)) )
                return false;
            return true;
        }
        if( !wcsnicmp((wchar_t*)Path, L"\\\\?\\UNC\\", strlenof(L"\\\\?\\UNC\\")) )
        {
            lead = strlenof(L"\\\\?\\UNC\\");
            goto IsLocalUNCW;
        }
        return false;
    } else {
        ASSERT(!"what kind of whacky character type are you using?!@#");
        return false;
    }
}


template <class CHARTYPE>
inline bool MakeDirectoryT(const CHARTYPE* ctPath)
{
	CHARTYPE* cur;
    TString<CHARTYPE> tsPath(ctPath);
	
	cur = tsPath.GetAt();
	while( *cur )
	{
		if( *cur == CONSTCHAR('/',CHARTYPE) )
			*cur = CONSTCHAR('\\',CHARTYPE);
		cur++;
	}
	
	cur = tsPath.GetAt();
	int res = 0;
	while(true)
	{
		if( *cur == CONSTCHAR('\\',CHARTYPE) || *cur == CONSTCHAR('\0',CHARTYPE) )
		{
			CHARTYPE old = *cur;
			*cur = CONSTCHAR('\0',CHARTYPE);

            if( sizeof(CHARTYPE) == sizeof(char) )
			    res = CreateDirectoryA((const char*)tsPath.GetAt(),NULL);
            else if( sizeof(CHARTYPE) == sizeof(wchar_t) )
                res = CreateDirectoryW((const wchar_t*)tsPath.GetAt(),NULL);
            else
                ASSERT(0); // bad type

			if( !old )
				break;
			*cur = old;
		}
		cur++;
	}
			
//	return res != -1;
	return res != 0;
}

template <class CHARTYPE>
inline bool MakeDirectory(const CHARTYPE* ctPath)
{
    if( !IsUnicodeFileSystem( ) || sizeof(CHARTYPE) == sizeof(char) )
        return MakeDirectoryT<char>( (const char*)AString(ctPath) );
    else
        return MakeDirectoryT<wchar_t>( (const wchar_t*)WString(ctPath) );
}



inline IOObjectType QueryFileType(const char* szName)
{
	DWORD Result;

	Result = GetFileAttributesA(szName);
	if (Result == 0xFFFFFFFF)
	{
		ASSERT(0);  // jmc051402 added.
		return objecttype_unknown;
	}

	if (Result & FILE_ATTRIBUTE_DIRECTORY)
		return objecttype_directory;
	else
		return objecttype_file;
}


inline IOObjectType QueryFileType(const wchar_t* wcName)
{
	DWORD Result;
	if( !IsUnicodeFileSystem() )
	{
		return QueryFileType(AString(wcName));
	}
	Result = GetFileAttributesW(wcName);

	if (Result == 0xFFFFFFFF) // INVALID_FILE_ATTRIBUTES 
	{
#ifdef _DEBUG
		DWORD dwErr;
		LPVOID lpMsgBuf;
		dwErr = GetLastError();
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			dwErr,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL 
		);
		TRACE2("GetFileAttributesW file \"%s\" failed - reason = %s\n", (const char*)AString(wcName), lpMsgBuf);
		LocalFree( lpMsgBuf );
#endif
		return objecttype_unknown;
	}

	if (Result & FILE_ATTRIBUTE_DIRECTORY)
		return objecttype_directory;
	else
		return objecttype_file;
}

template <class CHARTYPE>
void MakeValidFileName(CHARTYPE* pFileName, CHARTYPE ReplaceChar)
{
    while( *pFileName )
    {
        if( *pFileName < 128 )
        {
            if( !isalnum(*pFileName) && !strchr("$%'-_@{}~`!#() .", *pFileName) )
                *pFileName = ReplaceChar;
        }
        pFileName++;
    }
}
template <class CHARTYPE>
void MakeValidFileName(TString<CHARTYPE>& FileName, CHARTYPE ReplaceChar)
{
    MakeValidFileName(FileName.GetAt(), ReplaceChar);
}

// jmc092602 added SystemExecute
inline bool SystemExecute(const WString wsPath)
{
	int iRet = 0;
	if( !IsUnicodeFileSystem() )
	{
		//iRet = (int)::ShellExecuteA( NULL, L"open", AString(wsPath), NULL, NULL, SW_SHOWNORMAL );
		iRet = (int)ShellExecuteA( NULL, NULL, AString(wsPath), NULL, NULL, SW_SHOWNORMAL );
	}
	else
	{
		//iRet = (int)::ShellExecuteW( NULL, L"open", wsPath, NULL, NULL, SW_SHOWNORMAL );
		iRet = (int)ShellExecuteW( NULL, NULL, wsPath, NULL, NULL, SW_SHOWNORMAL );
	}

	if (iRet > 32)
		return true;
	else
		return false;
}

} // end filesys




#ifdef DEBUG_INCLUDE_ORDER
#pragma message("Done including " __FILE__ "\n" )
#endif

#endif
