code:
#define WITHWINDOWS
#include "common.h"
#ifdef PS2_MENU
#include "crossplatform.h"
#include "MemoryCard.h"
#include "main.h"
#include "DMAudio.h"
#include "AudioScriptObject.h"
#include "Camera.h"
#include "CarGen.h"
#include "Cranes.h"
#include "Clock.h"
#include "MBlur.h"
#include "Date.h"
#include "Font.h"
#include "FileMgr.h"
#include "Game.h"
#include "GameLogic.h"
#include "Gangs.h"
#include "Garages.h"
#include "GenericGameStorage.h"
#include "Pad.h"
#include "Particle.h"
#include "ParticleObject.h"
#include "PathFind.h"
#include "PCSave.h"
#include "Phones.h"
#include "Pickups.h"
#include "PlayerPed.h"
#include "ProjectileInfo.h"
#include "Pools.h"
#include "Radar.h"
#include "Restart.h"
#include "Script.h"
#include "Stats.h"
#include "Streaming.h"
#include "Sprite2d.h"
#include "Timer.h"
#include "TimeStep.h"
#include "Weather.h"
#include "World.h"
#include "Zones.h"
#include "Frontend_PS2.h"

CMemoryCard TheMemoryCard;

char icon_one[16] = "slime1.ico";
char icon_two[16] = "slime2.ico";
char icon_three[16] = "slime3.ico";
char HostFileLocationOfIcons[64] = "icons\\";
char TheGameRootDirectory[64] = "/BESLES-50330GTA30000";

#define ReadDataFromBufferPointer(buf, to) memcpy(&to, buf, sizeof(to)); buf += align4bytes(sizeof(to));
#define WriteDataToBufferPointer(buf, from) memcpy(buf, &from, sizeof(from)); buf += align4bytes(sizeof(from));

static int
align4bytes(int32 size)
{
	return (size + 3) & 0xFFFFFFFC;
}

unsigned short ascii_table[3][2] =
{
	{ 0x824F, 0x30 }, /* 0-9  */
	{ 0x8260, 0x41 }, /* A-Z  */
	{ 0x8281, 0x61 }, /* a-z  */
};

unsigned short ascii_special[33][2] =
{
	{0x8140, 0x20}, /* " " */
	{0x8149, 0x21}, /* "!" */
	{0x8168, 0x22}, /* """ */
	{0x8194, 0x23}, /* "#" */
	{0x8190, 0x24}, /* "$" */
	{0x8193, 0x25}, /* "%" */
	{0x8195, 0x26}, /* "&" */
	{0x8166, 0x27}, /* "'" */
	{0x8169, 0x28}, /* "(" */
	{0x816A, 0x29}, /* ")" */
	{0x8196, 0x2A}, /* "*" */
	{0x817B, 0x2B}, /* "+" */
	{0x8143, 0x2C}, /* "," */
	{0x817C, 0x2D}, /* "-" */
	{0x8144, 0x2E}, /* "." */
	{0x815E, 0x2F}, /* "/" */
	{0x8146, 0x3A}, /* ":" */
	{0x8147, 0x3B}, /* ";" */
	{0x8171, 0x3C}, /* "<" */
	{0x8181, 0x3D}, /* "=" */
	{0x8172, 0x3E}, /* ">" */
	{0x8148, 0x3F}, /* "?" */
	{0x8197, 0x40}, /* "@" */
	{0x816D, 0x5B}, /* "[" */
	{0x818F, 0x5C}, /* "\" */
	{0x816E, 0x5D}, /* "]" */
	{0x814F, 0x5E}, /* "^" */
	{0x8151, 0x5F}, /* "_" */
	{0x8165, 0x60}, /* "`" */
	{0x816F, 0x7B}, /* "{" */
	{0x8162, 0x7C}, /* "|" */
	{0x8170, 0x7D}, /* "}" */
	{0x8150, 0x7E}, /* "~" */
};

unsigned short
Ascii2Sjis(unsigned char ascii_code)
{
	unsigned short sjis_code = 0;
	unsigned char stmp;
	unsigned char stmp2 = 0;

	if ((ascii_code >= 0x20) && (ascii_code <= 0x2f))
		stmp2 = 1;
	else
	if ((ascii_code >= 0x30) && (ascii_code <= 0x39))
		stmp = 0;
	else
	if ((ascii_code >= 0x3a) && (ascii_code <= 0x40))
		stmp2 = 11;
	else
	if ((ascii_code >= 0x41) && (ascii_code <= 0x5a))
		stmp = 1;
	else
	if ((ascii_code >= 0x5b) && (ascii_code <= 0x60))
		stmp2 = 37;
	else
	if ((ascii_code >= 0x61) && (ascii_code <= 0x7a))
		stmp = 2;
	else
	if ((ascii_code >= 0x7b) && (ascii_code <= 0x7e))
		stmp2 = 63;
	else {
		printf("bad ASCII code 0x%x\n", ascii_code);
		return(0);
	}

	if (stmp2)
		sjis_code = ascii_special[ascii_code - 0x20 - (stmp2 - 1)][0];
	else
		sjis_code = ascii_table[stmp][0] + ascii_code - ascii_table[stmp][1];

	return(sjis_code);
}

#if defined(GTA_PC)

extern "C"
{
	extern void HandleExit();
}

char CardCurDir[MAX_CARDS][260] = { "", "" };
char PCCardsPath[260];
char PCCardDir[MAX_CARDS][12] = { "memcard1", "memcard2" };

const char* _psGetUserFilesFolder();
void _psCreateFolder(LPCSTR path);

void
PCMCInit()
{
	sprintf(PCCardsPath, "%s", _psGetUserFilesFolder());

	char path[512];

	sprintf(path, "%s\\%s", PCCardsPath, PCCardDir[CARD_ONE]);
	_psCreateFolder(path);

	sprintf(path, "%s\\%s", PCCardsPath, PCCardDir[CARD_TWO]);
	_psCreateFolder(path);
}
#endif

CMemoryCardInfo::CMemoryCardInfo(void)
{
	type = 0;
	free = 0;
	format = 0;

	for ( int32 i = 0; i < sizeof(dir); i++ )
		dir[i] = '\0';

	strncpy(dir, TheGameRootDirectory, sizeof(dir) - 1);
}

int32
CMemoryCard::Init(void)
{
#if defined(PS2)
	if ( sceMcInit() == sceMcIniSucceed )
	{
		printf("Memory card initialsed\n");
		return RES_SUCCESS;
	}

	printf("Memory Card not being initialised\n");
	return RES_FAILED;
#else
	PCMCInit();
	printf("Memory card initialsed\n");
	return RES_SUCCESS;
#endif
}

CMemoryCard::CMemoryCard(void)
{
	_unk0 = 0;
	CurrentCard = CARD_ONE;
	Cards[CARD_ONE].port = 0;
	Cards[CARD_TWO].port = 1;

	for ( int32 i = 0; i < sizeof(_unkName3); i++ )
		_unkName3[i] = '\0';

	m_bWantToLoad = false;
	_bunk2 = false;
	_bunk7 = false;
	JustLoadedDontFadeInYet = false;
	StillToFadeOut = false;
	TimeStartedCountingForFade = 0;
	TimeToStayFadedBeforeFadeOut = 1750;
	b_FoundRecentSavedGameWantToLoad = false;

	char date[64];
	char time[64];
	char day[8];
	char month[8];
	char year[8];
	char hour[8];
	char minute[8];
	char second[8];

	strncpy(date, "Oct  7 2001", 62);
	strncpy(time, "15:48:32",    62);

	strncpy(month, date, 3);
	month[3] = '\0';

	strncpy(day, &date[4], 2);
	day[2] = '\0';

	strncpy(year, &date[7], 4);
	year[4] = '\0';

	strncpy(hour, time, 2);
	hour[2] = '\0';

	strncpy(minute, &time[3], 2);
	minute[2] = '\0';

	strncpy(second, &time[6], 2);
	second[2] = '\0';

	#define _CMP(m) strncmp(month, m, sizeof(m)-1)

	if ( !_CMP("Jan") ) CompileDateAndTime.m_nMonth = 1;
	else
	if ( !_CMP("Feb") ) CompileDateAndTime.m_nMonth = 2;
	else
	if ( !_CMP("Mar") ) CompileDateAndTime.m_nMonth = 3;
	else
	if ( !_CMP("Apr") ) CompileDateAndTime.m_nMonth = 4;
	else
	if ( !_CMP("May") ) CompileDateAndTime.m_nMonth = 5;
	else
	if ( !_CMP("Jun") ) CompileDateAndTime.m_nMonth = 6;
	else
	if ( !_CMP("Jul") ) CompileDateAndTime.m_nMonth = 7;
	else
	if ( !_CMP("Aug") ) CompileDateAndTime.m_nMonth = 8;
	else
	if ( !_CMP("Oct") ) CompileDateAndTime.m_nMonth = 9;  // BUG: oct and sep is swapped here
	else
	if ( !_CMP("Sep") ) CompileDateAndTime.m_nMonth = 10;
	else
	if ( !_CMP("Nov") ) CompileDateAndTime.m_nMonth = 11;
	else
	if ( !_CMP("Dec") ) CompileDateAndTime.m_nMonth = 12;

	#undef _CMP

	CompileDateAndTime.m_nDay    = atoi(day);
	CompileDateAndTime.m_nYear   = atoi(year);
	CompileDateAndTime.m_nHour   = atoi(hour);
	CompileDateAndTime.m_nMinute = atoi(minute);
	CompileDateAndTime.m_nSecond = atoi(second);
}

int32
CMemoryCard::RestoreForStartLoad(void)
{
	uint8 buf[30];

	int32 file = OpenMemCardFileForReading(CurrentCard, LoadFileName);
	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	ReadFromMemCard(file, buf, sizeof(buf) - 1);
	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	uint8 *pBuf = buf + sizeof(uint32) + sizeof(uint32);
	ReadDataFromBufferPointer(pBuf, CGame::currLevel);
	ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().x);
	ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().y);
	ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().z);

	if ( CGame::currLevel != LEVEL_INDUSTRIAL )
		CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL);

	if ( CGame::currLevel != LEVEL_COMMERCIAL )
		CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL);

	if ( CGame::currLevel != LEVEL_SUBURBAN )
		CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN);

	CStreaming::RemoveIslandsNotUsed(CGame::currLevel);
	CCollision::SortOutCollisionAfterLoad();
	CStreaming::RequestBigBuildings(CGame::currLevel);
	CStreaming::LoadAllRequestedModels(false);
	CStreaming::HaveAllBigBuildingsLoaded(CGame::currLevel);
	CGame::TidyUpMemory(true, false);

	CloseMemCardFile(file);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	return RES_SUCCESS;
}

int32
CMemoryCard::LoadSavedGame(void)
{
	CheckSum = 0;
	CDate date;

	int32 saveSize = 0;
	uint32 size = 0;

	int32 oldLang = CMenuManager::m_PrefsLanguage;

	CPad::ResetCheats();

	ChangeDirectory(CurrentCard, Cards[CurrentCard].dir);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	int32 file = OpenMemCardFileForReading(CurrentCard, LoadFileName);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	#define LoadSaveDataBlock()\
		do {\
			ReadFromMemCard(file, &size, sizeof(size)); \
			if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; \
			size = align4bytes(size); \
			ReadFromMemCard(file, work_buff, size); \
			if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; \
			buf = work_buff; \
		} while (0)

	uint8 *buf;

	LoadSaveDataBlock();

	ReadDataFromBufferPointer(buf, saveSize);
	ReadDataFromBufferPointer(buf, CGame::currLevel);
	ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().x);
	ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().y);
	ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().z);
	ReadDataFromBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute);
	ReadDataFromBufferPointer(buf, CClock::ms_nLastClockTick);
	ReadDataFromBufferPointer(buf, CClock::ms_nGameClockHours);
	ReadDataFromBufferPointer(buf, CClock::ms_nGameClockMinutes);
	ReadDataFromBufferPointer(buf, CPad::GetPad(0)->Mode);
	ReadDataFromBufferPointer(buf, CTimer::m_snTimeInMilliseconds);
	ReadDataFromBufferPointer(buf, CTimer::ms_fTimeScale);
	ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStep);
	ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStepNonClipped);
	ReadDataFromBufferPointer(buf, CTimer::m_FrameCounter);
	ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeStep);
	ReadDataFromBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate);
	ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeScale);
	ReadDataFromBufferPointer(buf, CWeather::OldWeatherType);
	ReadDataFromBufferPointer(buf, CWeather::NewWeatherType);
	ReadDataFromBufferPointer(buf, CWeather::ForcedWeatherType);
	ReadDataFromBufferPointer(buf, CWeather::InterpolationValue);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsMusicVolume);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsSfxVolume);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsControllerConfig);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsUseVibration);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsStereoMono);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsRadioStation);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsBrightness);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsShowTrails);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsShowSubtitles);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsLanguage);
	ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsUseWideScreen);
	ReadDataFromBufferPointer(buf, CPad::GetPad(0)->Mode);
#ifdef PS2
	ReadDataFromBufferPointer(buf, BlurOn);
#else
	ReadDataFromBufferPointer(buf, CMBlur::BlurOn);
#endif
	ReadDataFromBufferPointer(buf, date.m_nSecond);
	ReadDataFromBufferPointer(buf, date.m_nMinute);
	ReadDataFromBufferPointer(buf, date.m_nHour);
	ReadDataFromBufferPointer(buf, date.m_nDay);
	ReadDataFromBufferPointer(buf, date.m_nMonth);
	ReadDataFromBufferPointer(buf, date.m_nYear);
	ReadDataFromBufferPointer(buf, CWeather::WeatherTypeInList);
	ReadDataFromBufferPointer(buf, TheCamera.CarZoomIndicator);
	ReadDataFromBufferPointer(buf, TheCamera.PedZoomIndicator);

	if ( date > CompileDateAndTime )
		;
	else
	if ( date < CompileDateAndTime )
		;

	#define ReadDataFromBlock(load_func)\
		do {\
			ReadDataFromBufferPointer(buf, size);\
			load_func(buf, size);\
			size = align4bytes(size);\
			buf += size;\
		} while (0)

	printf("Loading Scripts \n");
	ReadDataFromBlock(CTheScripts::LoadAllScripts);

	printf("Loading PedPool \n");
	ReadDataFromBlock(CPools::LoadPedPool);

	printf("Loading Garages \n");
	ReadDataFromBlock(CGarages::Load);

	printf("Loading Vehicles \n");
	ReadDataFromBlock(CPools::LoadVehiclePool);

	LoadSaveDataBlock();

	CProjectileInfo::RemoveAllProjectiles();
	CObject::DeleteAllTempObjects();

	printf("Loading Objects \n");
	ReadDataFromBlock(CPools::LoadObjectPool);

	printf("Loading Paths \n");
	ReadDataFromBlock(ThePaths.Load);

	printf("Loading Cranes \n");
	ReadDataFromBlock(CCranes::Load);

	LoadSaveDataBlock();

	printf("Loading Pickups \n");
	ReadDataFromBlock(CPickups::Load);

	printf("Loading Phoneinfo \n");
	ReadDataFromBlock(gPhoneInfo.Load);

	printf("Loading Restart \n");
	ReadDataFromBlock(CRestart::LoadAllRestartPoints);

	printf("Loading Radar Blips \n");
	ReadDataFromBlock(CRadar::LoadAllRadarBlips);

	printf("Loading Zones \n");
	ReadDataFromBlock(CTheZones::LoadAllZones);

	printf("Loading Gang Data \n");
	ReadDataFromBlock(CGangs::LoadAllGangData);

	printf("Loading Car Generators \n");
	ReadDataFromBlock(CTheCarGenerators::LoadAllCarGenerators);

	printf("Loading Particles \n");
	ReadDataFromBlock(CParticleObject::LoadParticle);

	printf("Loading AudioScript Objects \n");
	ReadDataFromBlock(cAudioScriptObject::LoadAllAudioScriptObjects);

	printf("Loading Player Info \n");
	ReadDataFromBlock(CWorld::Players[CWorld::PlayerInFocus].LoadPlayerInfo);

	printf("Loading Stats \n");
	ReadDataFromBlock(CStats::LoadStats);

	printf("Loading Streaming Stuff \n");
	ReadDataFromBlock(CStreaming::MemoryCardLoad);

	printf("Loading PedType Stuff \n");
	ReadDataFromBlock(CPedType::Load);

	#undef LoadSaveDataBlock
	#undef ReadDataFromBlock

	FrontEndMenuManager.SetSoundLevelsForMusicMenu();
	FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame();

	CloseMemCardFile(file);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	if ( oldLang != CMenuManager::m_PrefsLanguage )
	{
		TheText.Unload();
		TheText.Load();
	}

	JustLoadedDontFadeInYet = true;
	StillToFadeOut = true;

	CTheScripts::Process();

	printf("Game sucessfully loaded \n");

	return RES_SUCCESS;
}

int32
CMemoryCard::CheckCardInserted(int32 cardID)
{
#if defined(PS2)
	int cmd  = sceMcFuncNoCardInfo;
	int type = sceMcTypeNoCard;

	CTimer::Stop();

	while ( sceMcGetInfo(Cards[cardID].port, 0, &type, 0, 0) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( type == sceMcTypePS2 )
	{
		if ( result == sceMcResChangedCard || result == sceMcResSucceed )
		{
			nError = NO_ERR_SUCCESS;
			return nError;
		}
		else if ( result == sceMcResNoFormat )
		{
			nError = ERR_NOFORMAT;
			return nError;
		}
	}

	printf("Memory card %i not present\n", cardID);

	nError = ERR_NONE;
	return nError;
#else
	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::PopulateCardFlags(int32 cardID, bool bSlotFlag, bool bTypeFlag, bool bFreeFlag, bool bFormatFlag)
{
#if defined(PS2)
	int cmd = sceMcFuncNoCardInfo;
	int type = sceMcTypeNoCard;
	int free = 0;
	int format = 0;

	CTimer::Stop();

	Cards[cardID].type = 0;
	Cards[cardID].free = 0;
	Cards[cardID].format = 0;

	while ( sceMcGetInfo(Cards[cardID].port, 0, &type, &free, &format) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( type == sceMcTypePS2 )
	{
		if ( result == sceMcResChangedCard || result == sceMcResSucceed )
		{
			if ( bSlotFlag )
				Cards[cardID].slot = 0;

			//if ( bTypeFlag )
				Cards[cardID].type = type;

			if ( bFreeFlag )
				Cards[cardID].free = free;

			if ( bFormatFlag )
				Cards[cardID].format = format;

			printf("Memory card %i present\n", cardID);

			nError = NO_ERR_SUCCESS;
			return nError;
		}
		else if ( result == sceMcResNoFormat )
		{
			nError = ERR_NOFORMAT;
			return nError;
		}
	}

	printf("Memory card %i not present\n", cardID);

	nError = ERR_NONE;
	return nError;
#else
	CTimer::Stop();

	Cards[cardID].type = 0;
	Cards[cardID].free = 0;
	Cards[cardID].format = 0;

	if ( bSlotFlag )
		Cards[cardID].slot = 0;

	//if ( bTypeFlag )
		Cards[cardID].type = 0;

	if ( bFreeFlag )
		Cards[cardID].free = 1024 * 1024 * 4;

	if ( bFormatFlag )
		Cards[cardID].format = 0;

	printf("Memory card %i present\n", cardID);

	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::FormatCard(int32 cardID)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoFormat;

	int32 r = CheckCardInserted(cardID);
	if ( r == NO_ERR_SUCCESS )
	{
		while ( sceMcFormat(Cards[cardID].port, 0) != sceMcResSucceed )
			;

		int result;
		sceMcSync(0, &cmd, &result);

		if ( result < sceMcResSucceed )
		{
			printf("Memory card %i could not be formatted\n", cardID);

			nError = ERR_FORMATFAILED;
			return nError;
		}

		printf("Memory card %i present and formatted\n", cardID);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	return r;
#else
	printf("Memory card %i present and formatted\n", cardID);

	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::PopulateFileTable(int32 cardID)
{
	CTimer::Stop();

#if defined (PS2)
	int cmd = sceMcFuncNoGetDir;

	ClearFileTableBuffer(cardID);

	while ( sceMcGetDir(Cards[cardID].port, 0, "*", 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result >= sceMcResSucceed )
	{
		printf("Memory card %i present PopulateFileTables function successfull \n", cardID);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory card %i PopulateFileTables function successfull. MemoryCard not Formatted  \n", cardID);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory card %i  PopulateFileTables function unsuccessfull. Path does not exist  \n", cardID);

		nError = ERR_FILETABLENOENTRY;
		return nError;
	}

	printf("Memory card %i not Present\n", cardID);

	nError = ERR_NONE;
	return nError;
#else
	ClearFileTableBuffer(cardID);

	char path[512];
	sprintf(path, "%s\\%s\\%s\\*", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID]);

	memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table));
	WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0;
	if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE )
	{
		printf("Memory card %i not Present\n", cardID);
		nError = ERR_NONE;
		return nError;
	}
	do
	{
		SYSTEMTIME st;
		FileTimeToSystemTime(&fd.ftCreationTime, &st);
		Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear;
		FileTimeToSystemTime(&fd.ftLastWriteTime, &st);
		Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear;
		Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow;
		strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1);
		num++;
	} while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) );
	FindClose(hFind);

	//todo errors

	printf("Memory card %i present PopulateFileTables function successfull \n", cardID);

	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::CreateRootDirectory(int32 cardID)
{
	CTimer::Stop();
#if defined(PS2)
	int cmd = sceMcFuncNoMkdir;

	while ( sceMcMkdir(Cards[cardID].port, 0, Cards[cardID].dir) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result == sceMcResSucceed )
	{
		printf("Memory card %i present. RootDirectory Created\n", cardID);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory card %i RootDirectory not created card unformatted\n", cardID);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResFullDevice )
	{
		printf("Memory card %i RootDirectory not created due to insufficient memory card capacity\n", cardID);

		nError = ERR_DIRFULLDEVICE;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory card %i RootDirectory not created due to problem with pathname\n", cardID);

		nError = ERR_DIRBADENTRY;
		return nError;
	}

	printf("Memory card %i not present so RootDirectory not created \n", cardID);

	nError = ERR_NONE;
	return nError;
#else
	char path[512];
	sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], Cards[cardID].dir);
	_psCreateFolder(path);

	printf("Memory card %i present. RootDirectory Created\n", cardID);

	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::ChangeDirectory(int32 cardID, char *dir)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoChDir;

	while ( sceMcChdir(Cards[cardID].port, 0, dir, 0) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result == sceMcResSucceed )
	{
		printf("Memory Card %i. Changed to the directory %s \n", cardID, dir);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory card %i. Couldn't change to the directory %s. MemoryCard not Formatted  \n", cardID, dir);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory card %i  Couldn't change to the directory %s. Path does not exist  \n", cardID, dir);

		nError = ERR_DIRNOENTRY;
		return nError;
	}

	printf("Memory card %i not Present. So could not change to directory %s.\n", cardID, dir);

	nError = ERR_NONE;
	return nError;
#else

	if ( !strcmp(dir, "/" ) )
	{
		strncpy(CardCurDir[cardID], dir, sizeof(CardCurDir[cardID]) - 1);
		printf("Memory Card %i. Changed to the directory %s \n", cardID, dir);
		nError = NO_ERR_SUCCESS;
		return nError;
	}

	char path[512];
	sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], dir);

	WIN32_FIND_DATA fd; HANDLE hFind;
	if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE )
	{
		printf("Memory card %i  Couldn't change to the directory %s. Path does not exist  \n", cardID, dir);

		nError = ERR_DIRNOENTRY;
		return nError;
	}

	FindClose(hFind);

	strncpy(CardCurDir[cardID], dir, sizeof(CardCurDir[cardID]) - 1);
	printf("Memory Card %i. Changed to the directory %s \n", cardID, dir);
	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::CreateIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three)
{
#if defined(PS2)
	sceMcIconSys icon;
	static sceVu0IVECTOR bgcolor[4] = {
		{ 0x80,    0,    0, 0 },
		{    0, 0x80,    0, 0 },
		{    0,    0, 0x80, 0 },
		{ 0x80, 0x80, 0x80, 0 },
	};
	static sceVu0FVECTOR lightdir[3] = {
		{ 0.5, 0.5, 0.5, 0.0 },
		{ 0.0,-0.4,-0.1, 0.0 },
		{-0.5,-0.5, 0.5, 0.0 },
	};
	static sceVu0FVECTOR lightcol[3] = {
		{ 0.48, 0.48, 0.03, 0.00 },
		{ 0.50, 0.33, 0.20, 0.00 },
		{ 0.14, 0.14, 0.38, 0.00 },
	};
	static sceVu0FVECTOR ambient = { 0.50, 0.50, 0.50, 0.00 };
	char head[8]  = "PS2D";
	char title[8] = "GTA3";

	memset(&icon, 0, sizeof(icon));

	memcpy(icon.BgColor,    bgcolor,  sizeof(bgcolor));
	memcpy(icon.LightDir,   lightdir, sizeof(lightdir));
	memcpy(icon.LightColor, lightcol, sizeof(lightcol));
	memcpy(icon.Ambient,    ambient,  sizeof(ambient));

	icon.OffsLF = 24;
	icon.TransRate = 0x60;

	unsigned short *titleName = (unsigned short *)icon.TitleName;

	uint32 titlec = 0;
	while ( titlec < strlen(title) )
	{
		unsigned short sjis = Ascii2Sjis(title[titlec]);
		titleName[titlec] = (sjis << 8) | (sjis >> 8);
		titlec++;
	}

	titleName[titlec] = L'\0';

	char icon1[80];
	char icon2[80];
	char icon3[80];

	strncpy(icon1, icon_one,   sizeof(icon1) - 1);
	strncpy(icon2, icon_two,   sizeof(icon2) - 1);
	strncpy(icon3, icon_three, sizeof(icon3) - 1);

	strncpy((char *)icon.FnameView, icon1, sizeof(icon.FnameView) - 1);
	strncpy((char *)icon.FnameCopy, icon2, sizeof(icon.FnameCopy) - 1);
	strncpy((char *)icon.FnameDel,  icon3, sizeof(icon.FnameDel) - 1);
	strncpy((char *)icon.Head, head, sizeof(icon.Head));

	int32 iconFile = CreateMemCardFileReadWrite(Cards[cardID].port, "icon.sys");

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	WritetoMemCard(iconFile, &icon, sizeof(icon));

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	CloseMemCardFile(iconFile);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	if ( LoadIconFiles(Cards[cardID].port, icon_one, icon_two, icon_three) == RES_SUCCESS )
	{
		printf("All Icon files Created and loaded. \n");
		return RES_SUCCESS;
	}

	printf("Could not load all the icon files \n");

	return RES_FAILED;
#else
	return RES_SUCCESS;
#endif
}

int32
CMemoryCard::LoadIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three)
{
#if defined(PS2)
	const uint32 size = 50968;
	uint8 *data = new uint8[size];

	char icon1_path[80];
	char icon2_path[80];
	char icon3_path[80];
	char icon1[32];
	char icon2[32];
	char icon3[32];

	strncpy(icon1, icon_one, sizeof(icon1) - 1);
	strncpy(icon2, icon_two, sizeof(icon2) - 1);
	strncpy(icon3, icon_three, sizeof(icon3) - 1);

	int hostlen = strlen(HostFileLocationOfIcons);

	strncpy(icon1_path, HostFileLocationOfIcons, sizeof(icon1_path) - 1);
	strncpy(icon2_path, HostFileLocationOfIcons, sizeof(icon2_path) - 1);
	strncpy(icon3_path, HostFileLocationOfIcons, sizeof(icon3_path) - 1);

	strncpy(icon1_path+hostlen, icon_one,   sizeof(icon1_path) - 1 - hostlen);
	strncpy(icon2_path+hostlen, icon_two,   sizeof(icon2_path) - 1 - hostlen);
	strncpy(icon3_path+hostlen, icon_three, sizeof(icon3_path) - 1 - hostlen);

	// ico1 copy
	int32 ico1file = CFileMgr::OpenFile(icon1_path);
	CFileMgr::Read(ico1file, (char *)data, size);
	CFileMgr::CloseFile(ico1file);

	int32 ico1mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon1);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	WritetoMemCard(ico1mc, data, size);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	CloseMemCardFile(ico1mc);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	// ico2 copy
	int32 ico2file = CFileMgr::OpenFile(icon2_path);
	CFileMgr::Read(ico2file, (char *)data, size);
	CFileMgr::CloseFile(ico2file);

	int32 ico2mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon2);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	WritetoMemCard(ico2mc, data, size);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	CloseMemCardFile(ico2mc);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	// ico3 copy
	int32 ico3file = CFileMgr::OpenFile(icon3_path);
	CFileMgr::Read(ico3file, (char *)data, size);
	CFileMgr::CloseFile(ico3file);

	int32 ico3mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon3);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	WritetoMemCard(ico3mc, data, size);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	CloseMemCardFile(ico3mc);

	if ( nError != NO_ERR_SUCCESS )
	{
		delete [] data;
		return RES_FAILED;
	}

	delete [] data;

	return RES_SUCCESS;
#else
	return RES_SUCCESS;
#endif
}

int32
CMemoryCard::CloseMemCardFile(int32 file)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoClose;

	while ( sceMcClose(file) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result == sceMcResSucceed )
	{
		printf("File %i closed\n", file);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory Card is Unformatted");

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory Card File Handle %i has not been opened", file);

		nError = ERR_OPENNOENTRY;
		return nError;
	}

	nError = ERR_NONE;
	return nError;
#else
	CFileMgr::CloseFile(file);
	printf("File %i closed\n", file);

	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::CreateMemCardFileReadWrite(int32 cardID, char *filename)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoOpen;

	char buff[255];

	strncpy(buff, filename, sizeof(buff));

	while ( sceMcOpen(Cards[cardID].port, 0, buff, SCE_RDWR|SCE_CREAT) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result >= sceMcResSucceed )
	{
		printf("%s File Created for MemoryCard. Its File handle is %i. \n", buff, result);

		nError = NO_ERR_SUCCESS;
		return result;
	}

	if ( result == sceMcResNoFormat )
	{
		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResFullDevice )
	{
		nError = ERR_FILEFULLDEVICE;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		nError = ERR_FILENOPATHENTRY;
		return nError;
	}

	if ( result == sceMcResDeniedPermit )
	{
		nError = ERR_FILEDENIED;
		return nError;
	}

	if ( result == sceMcResUpLimitHandle )
	{
		nError = ERR_FILEUPLIMIT;
		return nError;
	}

	printf("File %s not created on memory card.\n", buff);

	return ERR_NONE;
#else
	char path[512];
	sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename);
	int32 file = CFileMgr::OpenFile(path, "wb+");
	if (file == 0)
	{
		sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename);
		file = CFileMgr::OpenFile(path, "wb+");
	}

	if ( file )
	{
		printf("%s File Created for MemoryCard. Its File handle is %i. \n", filename, file);
		nError = NO_ERR_SUCCESS;
		return file;
	}

	printf("File %s not created on memory card.\n", path);

	nError = ERR_NONE;
	return 0;
#endif
}

int32
CMemoryCard::OpenMemCardFileForReading(int32 cardID, char *filename)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoOpen;

	char buff[255];

	strncpy(buff, filename, sizeof(buff));

	while ( sceMcOpen(Cards[cardID].port, 0, buff, SCE_RDONLY) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result >= sceMcResSucceed )
	{
		printf("%s File Created for MemoryCard. Its File handle is %i. \n", buff, result);

		nError = NO_ERR_SUCCESS;
		return result;
	}

	if ( result == sceMcResNoFormat )
	{
		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResFullDevice )
	{
		nError = ERR_FILEFULLDEVICE;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		nError = ERR_FILENOPATHENTRY;
		return nError;
	}

	if ( result == sceMcResDeniedPermit )
	{
		nError = ERR_FILEDENIED;
		return nError;
	}

	if ( result == sceMcResUpLimitHandle )
	{
		nError = ERR_FILEUPLIMIT;
		return nError;
	}

	printf("File %s not created on memory card.\n", buff);
	nError = ERR_NONE;
	return nError;
#else
	char path[512];
	sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename);
	int32 file = CFileMgr::OpenFile(path, "rb");
	if (file == 0)
	{
		sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename);
		file = CFileMgr::OpenFile(path, "rb");
	}

	if ( file )
	{
		printf("%s File Created for MemoryCard. Its File handle is %i. \n", filename, file);
		nError = NO_ERR_SUCCESS;
		return file;
	}

	printf("File %s not created on memory card.\n", path);
	nError = ERR_NONE;
	return 0;
#endif
}

int32
CMemoryCard::ReadFromMemCard(int32 file, void *buff, int32 size)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoRead;

	while ( sceMcRead(file, buff, size) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result >= sceMcResSucceed )
	{
		if ( size >= result )
		{
			printf("%i Bytes Read for Filehandle %i \n", result, file);

			nError = NO_ERR_SUCCESS;
			return result;
		}
	}

	if ( result == sceMcResNoFormat )
	{
		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		nError = ERR_READNOENTRY;
		return nError;
	}

	if ( result == sceMcResDeniedPermit )
	{
		nError = ERR_READDENIED;
		return nError;
	}

	printf("No Bytes Read for Filehandle %i \n", file);

	nError = ERR_NONE;
	return result;
#else
	int32 s = CFileMgr::Read(file, (const char *)buff, size);
	if ( s == size )
	{
		printf("%i Bytes Read for Filehandle %i \n", s, file);

		nError = NO_ERR_SUCCESS;
		return s;
	}

	printf("No Bytes Read for Filehandle %i \n", file);

	nError = ERR_NONE;
	return s;
#endif
}

int32
CMemoryCard::DeleteMemoryCardFile(int32 cardID, char *filename)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoDelete;

	while ( sceMcDelete(Cards[cardID].port, 0, filename) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result == sceMcResSucceed )
	{
		printf("Memory Card %i, %s NO_ERR_SUCCESSfully deleted", cardID, filename);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory Card %i, %s not deleted as memory Card is unformatted", cardID, filename);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory Card %i, %s attempt made to delete non existing file", cardID, filename);

		nError = ERR_DELETENOENTRY;
		return nError;
	}

	if ( result == sceMcResDeniedPermit )
	{
		printf("Memory Card %i, %s not deleted as file is in use or write protected", cardID, filename);

		nError = ERR_DELETEDENIED;
		return nError;
	}

	if ( result == sceMcResNotEmpty )
	{
		printf("Memory Card %i, %s not deleted. Entries remain in subdirectory", cardID, filename);

		nError = ERR_DELETEFAILED;
		return nError;
	}

	nError = ERR_NONE;
	return nError;
#else
	char path[512];
	sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename);
	int32 file = CFileMgr::OpenFile(path, "rb");
	if (file == 0)
	{
		sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename);
		file = CFileMgr::OpenFile(path, "rb");
	}

	if ( file )
	{
		CFileMgr::CloseFile(file);

		DeleteFile(path);

		printf("Memory Card %i, %s NO_ERR_SUCCESSfully deleted", cardID, filename);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	printf("Memory Card %i, %s attempt made to delete non existing file", cardID, filename);
	nError = ERR_DELETENOENTRY;

	//nError = ERR_NONE;
	return nError;

#endif
}

void
CMemoryCard::PopulateErrorMessage()
{
	switch ( nError )
	{
		case ERR_WRITEFULLDEVICE:
		case ERR_DIRFULLDEVICE:
			pErrorMsg = TheText.Get("SLONDR"); break; // Insufficient space to save. Please insert a Memory Card (PS2) with at least 500KB of free space available into MEMORY CARD slot 1.
		case ERR_FORMATFAILED:
			pErrorMsg = TheText.Get("SLONFM"); break; // Error formatting Memory Card (PS2) in MEMORY CARD slot 1.
		case ERR_SAVEFAILED:
			pErrorMsg = TheText.Get("SLNSP");  break; // Insufficient space to save. Please insert a Memory Card (PS2) with at least 200KB of free space available into MEMORY CARD slot 1.
		case ERR_DELETEDENIED:
		case ERR_NOFORMAT:
			pErrorMsg = TheText.Get("SLONNF"); break; // Memory Card (PS2) in MEMORY CARD slot 1 is unformatted.
		case ERR_NONE:
			pErrorMsg = TheText.Get("SLONNO"); break; // No Memory Card (PS2) in MEMORY CARD slot 1.
	}
}

int32
CMemoryCard::WritetoMemCard(int32 file, void *buff, int32 size)
{
#if defined(PS2)
	int cmd = sceMcFuncNoWrite;
	int result = sceMcResSucceed;
	int result1 = sceMcResSucceed;

	CTimer::Stop();

	while ( sceMcWrite(file, buff, size) != sceMcResSucceed )
		;

	sceMcSync(0, &cmd, &result);

	if ( result == sceMcResNoFormat )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResFullDevice )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_WRITEFULLDEVICE;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_WRITENOENTRY;
		return nError;
	}

	if ( result == sceMcResDeniedPermit )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_WRITEDENIED;
		return nError;
	}

	if ( result == sceMcResFailReplace )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_WRITEFAILED;
		return nError;
	}

	if ( result <= -10 )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_NONE;
		return nError;
	}

	cmd = sceMcFuncNoFlush;

	while ( sceMcFlush(file) != sceMcResSucceed )
		;

	sceMcSync(0, &cmd, &result1);

	if ( result1 == sceMcResNoFormat )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result1 == sceMcResNoEntry )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_FLUSHNOENTRY;
		return nError;
	}

	if ( result1 <= -10 )
	{
		printf("No Bytes written for Filehandle %i \n", file);

		nError = ERR_NONE;
		return nError;
	}

	if ( result > 0 && result1 == sceMcResSucceed )
	{
		printf("%i Bytes written for Filehandle %i \n", result, file);
	}
	else if ( result == sceMcResSucceed && result1 == sceMcResSucceed )
	{
		printf("Filehandle %i was flushed\n", file);
	}

	nError = NO_ERR_SUCCESS;
	return nError;
#else
	CTimer::Stop();

	int32 s = CFileMgr::Write(file, (const char *)buff, size);
	if ( s == size )
	{
		printf("%i Bytes written for Filehandle %i \n", s, file);
		nError = NO_ERR_SUCCESS;
		return s;
	}

	nError = ERR_NONE;
	return s;
#endif
}

static inline void
MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize)
{
	presize = buf;
	buf += sizeof(uint32);
	postsize = buf;
}

static inline void
CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size)
{
	memcpy(buf, &size, sizeof(size));
	size = align4bytes(size);
	postbuf2 += size;
	postbuf = postbuf2;
}

bool
CMemoryCard::SaveGame(void)
{
	uint32 saveSize = 0;
	uint32 totalSize = 0;

	CurrentCard = CARD_ONE;

	CheckSum = 0;

	CGameLogic::PassTime(360);
	CPlayerPed *ped = FindPlayerPed();
	ped->m_fCurrentStamina = ped->m_fMaxStamina;
	CGame::TidyUpMemory(true, false);

	saveSize = SAVE_FILE_SIZE;
	int32 minfree = 198;

	PopulateCardFlags(CurrentCard, false, false, true, false);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	minfree += GetClusterAmountForFileCreation(CurrentCard);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	if ( Cards[CurrentCard].free < 200 )
	{
		CTimer::Update();
		uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode();

		while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 )
		{
			for ( int32 i = 0; i < 1000; i++ )
				powf(3.33f, 3.444f);

			CTimer::Update();
		}

		nError = ERR_SAVEFAILED;
		return false;
	}

	if ( Cards[CurrentCard].free < minfree )
	{
		CTimer::Update();
		uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode();

		while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 )
		{
			for ( int32 i = 0; i < 1000; i++ )
				powf(3.33f, 3.444f);

			CTimer::Update();
		}

		nError = ERR_SAVEFAILED;
		return false;
	}

	uint32 size;
	uint8 *buf = work_buff;
	uint32 reserved = 0;

	int32 file = CreateMemCardFileReadWrite(CurrentCard, ValidSaveName);

	if ( nError != NO_ERR_SUCCESS )
	{
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1);
		return false;
	}

	WriteDataToBufferPointer(buf, saveSize);
	WriteDataToBufferPointer(buf, CGame::currLevel);
	WriteDataToBufferPointer(buf, TheCamera.GetPosition().x);
	WriteDataToBufferPointer(buf, TheCamera.GetPosition().y);
	WriteDataToBufferPointer(buf, TheCamera.GetPosition().z);
	WriteDataToBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute);
	WriteDataToBufferPointer(buf, CClock::ms_nLastClockTick);
	WriteDataToBufferPointer(buf, CClock::ms_nGameClockHours);
	WriteDataToBufferPointer(buf, CClock::ms_nGameClockMinutes);
	WriteDataToBufferPointer(buf, CPad::GetPad(0)->Mode);
	WriteDataToBufferPointer(buf, CTimer::m_snTimeInMilliseconds);
	WriteDataToBufferPointer(buf, CTimer::ms_fTimeScale);
	WriteDataToBufferPointer(buf, CTimer::ms_fTimeStep);
	WriteDataToBufferPointer(buf, CTimer::ms_fTimeStepNonClipped);
	WriteDataToBufferPointer(buf, CTimer::m_FrameCounter);
	WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeStep);
	WriteDataToBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate);
	WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeScale);
	WriteDataToBufferPointer(buf, CWeather::OldWeatherType);
	WriteDataToBufferPointer(buf, CWeather::NewWeatherType);
	WriteDataToBufferPointer(buf, CWeather::ForcedWeatherType);
	WriteDataToBufferPointer(buf, CWeather::InterpolationValue);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsMusicVolume);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsSfxVolume);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsControllerConfig);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsUseVibration);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsStereoMono);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsRadioStation);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsBrightness);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsShowTrails);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsShowSubtitles);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsLanguage);
	WriteDataToBufferPointer(buf, CMenuManager::m_PrefsUseWideScreen);
	WriteDataToBufferPointer(buf, CPad::GetPad(0)->Mode);
#ifdef PS2
	WriteDataToBufferPointer(buf, BlurOn);
#else
	WriteDataToBufferPointer(buf, CMBlur::BlurOn);
#endif
	WriteDataToBufferPointer(buf, CompileDateAndTime.m_nSecond);
	WriteDataToBufferPointer(buf, CompileDateAndTime.m_nMinute);
	WriteDataToBufferPointer(buf, CompileDateAndTime.m_nHour);
	WriteDataToBufferPointer(buf, CompileDateAndTime.m_nDay);
	WriteDataToBufferPointer(buf, CompileDateAndTime.m_nMonth);
	WriteDataToBufferPointer(buf, CompileDateAndTime.m_nYear);
	WriteDataToBufferPointer(buf, CWeather::WeatherTypeInList);
	WriteDataToBufferPointer(buf, TheCamera.CarZoomIndicator);
	WriteDataToBufferPointer(buf, TheCamera.PedZoomIndicator);

	if ( nError != NO_ERR_SUCCESS )
	{
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1);
		return false;
	}

	uint8 *presize;
	uint8 *postsize;

	#define WriteSaveDataBlock(save_func)\
		do {\
			MakeSpaceForSizeInBufferPointer(presize, buf, postsize);\
			save_func(buf, &size);\
			CopySizeAndPreparePointer(presize, buf, postsize, reserved, size);\
		} while (0)

	WriteSaveDataBlock(CTheScripts::SaveAllScripts);
	printf("Script Save Size %d, \n", size);

	WriteSaveDataBlock(CPools::SavePedPool);
	printf("PedPool Save Size %d, \n", size);

	WriteSaveDataBlock(CGarages::Save);
	printf("Garage Save Size %d, \n", size);

	WriteSaveDataBlock(CPools::SaveVehiclePool);
	printf("Vehicle Save Size %d, \n", size);

	DoClassSaveRoutine(file, work_buff, buf - work_buff);
	totalSize += buf - work_buff;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	buf = work_buff;
	reserved = 0;

	WriteSaveDataBlock(CPools::SaveObjectPool);
	printf("Object Save Size %d, \n", size);

	WriteSaveDataBlock(ThePaths.Save);
	printf("The Paths Save Size %d, \n", size);

	WriteSaveDataBlock(CCranes::Save);
	printf("Cranes Save Size %d, \n", size);

	DoClassSaveRoutine(file, work_buff, buf - work_buff);
	totalSize += buf - work_buff;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	buf = work_buff;
	reserved = 0;

	WriteSaveDataBlock(CPickups::Save);
	printf("Pick Ups Save Size %d, \n", size);

	WriteSaveDataBlock(gPhoneInfo.Save);
	printf("Phones Save Size %d, \n", size);

	WriteSaveDataBlock(CRestart::SaveAllRestartPoints);
	printf("RestartPoints Save Size %d, \n", size);

	WriteSaveDataBlock(CRadar::SaveAllRadarBlips);
	printf("Radar Save Size %d, \n", size);

	WriteSaveDataBlock(CTheZones::SaveAllZones);
	printf("Save Size %d, \n", size);

	WriteSaveDataBlock(CGangs::SaveAllGangData);
	printf("Gangs Save Size %d, \n", size);

	WriteSaveDataBlock(CTheCarGenerators::SaveAllCarGenerators);
	printf("Car Gens Save Size %d, \n", size);

	WriteSaveDataBlock(CParticleObject::SaveParticle);
	printf("Particles Save Size %d, \n", size);

	WriteSaveDataBlock(cAudioScriptObject::SaveAllAudioScriptObjects);
	printf("Audio Script Save Size %d, \n", size);

	WriteSaveDataBlock(CWorld::Players[CWorld::PlayerInFocus].SavePlayerInfo);
	printf("Player Info Save Size %d, \n", size);

	WriteSaveDataBlock(CStats::SaveStats);
	printf("Stats Save Size %d, \n", size);

	WriteSaveDataBlock(CStreaming::MemoryCardSave);
	printf("Streaming Save Size %d, \n", size);

	WriteSaveDataBlock(CPedType::Save);
	printf("PedType Save Size %d, \n", size);

	DoClassSaveRoutine(file, work_buff, buf - work_buff);
	totalSize += buf - work_buff;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	buf = work_buff;
	reserved = 0;

	for (int32 i = 0; i < 3; i++)
	{
		size = align4bytes(saveSize - totalSize - 4);
		if (size > sizeof(work_buff))
			size = sizeof(work_buff);
		if (size > 4) {
			DoClassSaveRoutine(file, work_buff, size);
			totalSize += size;
		}
	}

	WritetoMemCard(file, &CheckSum, sizeof(CheckSum));

	CloseMemCardFile(file);

	#undef WriteSaveDataBlock

	if ( nError != NO_ERR_SUCCESS )
	{
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1);
		DoHackRoundSTUPIDSonyDateTimeStuff(CARD_ONE, ValidSaveName);
		return false;
	}

	DoHackRoundSTUPIDSonyDateTimeStuff(CARD_ONE, ValidSaveName);
	strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1);
	return true;
}

bool
CMemoryCard::DoHackRoundSTUPIDSonyDateTimeStuff(int32 port, char *filename)
{
#if defined(PS2)
	int cmd = sceMcFuncNoFileInfo;
	int result = sceMcResSucceed;

	sceCdCLOCK rtc;
	sceCdReadClock(&rtc);

	sceScfGetLocalTimefromRTC(&rtc);

	#define ROUNDHACK(a) ( ((a) & 15) + ( ( ( ((a) >> 4) << 2 ) + ((a) >> 4) ) << 1 ) )

	sceMcTblGetDir info;

	info._Create.Sec   = ROUNDHACK(rtc.second);
	info._Create.Min   = ROUNDHACK(rtc.minute);
	info._Create.Hour  = ROUNDHACK(rtc.hour);
	info._Create.Day   = ROUNDHACK(rtc.day);
	info._Create.Month = ROUNDHACK(rtc.month);
	info._Create.Year  = ROUNDHACK(rtc.year) + 2000;

	#undef ROUNDHACK

	while ( sceMcSetFileInfo(port, 0, filename, (char *)&info, sceMcFileInfoCreate) != sceMcResSucceed )
		;

	sceMcSync(0, &cmd, &result);

	return sceMcResSucceed >= result;
#else
	return true;
#endif
}

int32
CMemoryCard::LookForRootDirectory(int32 cardID)
{
	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoGetDir;

	while ( sceMcGetDir(Cards[cardID].port, 0, Cards[cardID].dir, 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result == 0 )
	{
		nError = NO_ERR_SUCCESS;
		return ERR_NOROOTDIR;
	}

	if ( result > sceMcResSucceed )
	{
		printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID);

		nError = NO_ERR_SUCCESS;
		return nError;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. MemoryCard not Formatted  \n", cardID);

		nError = ERR_NOFORMAT;
		return nError;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory card %i  PopulateFileTables function unNO_ERR_SUCCESSfull. Path does not exist  \n", cardID);

		nError = ERR_FILETABLENOENTRY;
		return nError;
	}

	printf("Memory card %i not Present\n", cardID);

	nError = ERR_NONE;
	return nError;
#else
	char path[512];
	sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], Cards[cardID].dir);

	memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table));
	WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0;
	if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE )
	{
		nError = NO_ERR_SUCCESS;
		return ERR_NOROOTDIR;
	}
	do
	{
		SYSTEMTIME st;
		FileTimeToSystemTime(&fd.ftCreationTime, &st);
		Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear;
		FileTimeToSystemTime(&fd.ftLastWriteTime, &st);
		Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear;
		Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow;
		strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1);
		num++;
	} while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) );
	FindClose(hFind);

	if ( num == 0 )
	{
		nError = NO_ERR_SUCCESS;
		return ERR_NOROOTDIR;
	}

	//todo errors

	printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID);

	nError = NO_ERR_SUCCESS;
	return nError;
#endif
}

int32
CMemoryCard::FillFirstFileWithGuff(int32 cardID)
{
	CTimer::Stop();

	char buff[80];
	strncpy(buff, Cards[cardID].dir+1, sizeof(buff) - 1);

	int32 file = CreateMemCardFileReadWrite(Cards[cardID].port, buff);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	const int32 kBlockSize = GUFF_FILE_SIZE / 3;

	work_buff[kBlockSize-1] = 5;
	WritetoMemCard(file, work_buff, kBlockSize);
	WritetoMemCard(file, work_buff, kBlockSize);
	WritetoMemCard(file, work_buff, kBlockSize);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	CloseMemCardFile(file);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	return RES_SUCCESS;
}

bool
CMemoryCard::FindMostRecentFileName(int32 cardID, char *filename)
{
	CDate date1, date2;

	CTimer::Stop();

#if defined(PS2)
	int cmd = sceMcFuncNoGetDir;

	ClearFileTableBuffer(cardID);

	while ( sceMcGetDir(Cards[cardID].port, 0, "*", 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed )
		;

	int result;
	sceMcSync(0, &cmd, &result);

	if ( result >= sceMcResSucceed )
	{
		printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID);
		nError = NO_ERR_SUCCESS;

		for ( int32 entry = 7; entry < ARRAY_SIZE(Cards[CARD_ONE].table); entry++ )
		{
			bool found = false;

			if (   Cards[CARD_ONE].table[entry]._Modify.Sec   != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Min   != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Hour  != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Day   != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Month != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Year  != 0 )
			{
				date1.m_nSecond = Cards[CARD_ONE].table[entry]._Modify.Sec;
				date1.m_nMinute = Cards[CARD_ONE].table[entry]._Modify.Min;
				date1.m_nHour   = Cards[CARD_ONE].table[entry]._Modify.Hour;
				date1.m_nDay    = Cards[CARD_ONE].table[entry]._Modify.Day;
				date1.m_nMonth  = Cards[CARD_ONE].table[entry]._Modify.Month;
				date1.m_nYear   = Cards[CARD_ONE].table[entry]._Modify.Year;

				if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0
					&& Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed
					&& Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE )
				{
					found = true;
				}
			}
			else
			if (   Cards[CARD_ONE].table[entry]._Create.Sec   != 0
				|| Cards[CARD_ONE].table[entry]._Create.Min   != 0
				|| Cards[CARD_ONE].table[entry]._Create.Hour  != 0
				|| Cards[CARD_ONE].table[entry]._Create.Day   != 0
				|| Cards[CARD_ONE].table[entry]._Create.Month != 0
				|| Cards[CARD_ONE].table[entry]._Create.Year  != 0 )
			{
				date1.m_nSecond = Cards[CARD_ONE].table[entry]._Create.Sec;
				date1.m_nMinute = Cards[CARD_ONE].table[entry]._Create.Min;
				date1.m_nHour   = Cards[CARD_ONE].table[entry]._Create.Hour;
				date1.m_nDay    = Cards[CARD_ONE].table[entry]._Create.Day;
				date1.m_nMonth  = Cards[CARD_ONE].table[entry]._Create.Month;
				date1.m_nYear   = Cards[CARD_ONE].table[entry]._Create.Year;

				if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0
					&& Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed
					&& Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE )
				{
					found = true;
				}
			}

			if ( found )
			{
				int32 d;
				if ( date1 > date2 )      d = 1;
				else if ( date1 < date2 ) d = 2;
				else                      d = 0;

				if ( d == 1 )
				{
					char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName;

					date2 = date1;
					strncpy(filename, entryname, 28);
				}
				else
				{
					int32 d;
					if ( date1 > date2 )      d = 1;
					else if ( date1 < date2 ) d = 2;
					else                      d = 0;

					if ( d == 0 )
					{
						char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName;
						date2 = date1;
						strncpy(filename, entryname, 28);
					}
				}
			}
		}

		if (   date2.m_nSecond != 0
			|| date2.m_nMinute != 0
			|| date2.m_nHour   != 0
			|| date2.m_nDay    != 0
			|| date2.m_nMonth  != 0
			|| date2.m_nYear   != 0 )
		{
			return true;
		}

		return false;
	}

	if ( result == sceMcResNoFormat )
	{
		printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. MemoryCard not Formatted  \n", cardID);
		nError = ERR_NOFORMAT;
		return false;
	}

	if ( result == sceMcResNoEntry )
	{
		printf("Memory card %i  PopulateFileTables function unNO_ERR_SUCCESSfull. Path does not exist  \n", cardID);
		nError = ERR_FILETABLENOENTRY;
		return false;
	}

	printf("Memory card %i not Present\n", cardID);
	nError = ERR_NONE;
	return false;
#else
	ClearFileTableBuffer(cardID);

	char path[512];
	sprintf(path, "%s\\%s\\%s\\*", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID]);

	memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table));
	WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0;
	if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE )
	{
		printf("Memory card %i not Present\n", cardID);
		nError = ERR_NONE;
		return nError;
	}
	do
	{
		SYSTEMTIME st;
		FileTimeToSystemTime(&fd.ftCreationTime, &st);
		Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear;
		FileTimeToSystemTime(&fd.ftLastWriteTime, &st);
		Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear;
		Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow;
		strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1);
		num++;
	} while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) );
	FindClose(hFind);

	if ( num > 0 )
	{
		printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID);
		nError = NO_ERR_SUCCESS;

		for ( int32 entry = 0; entry < ARRAY_SIZE(Cards[CARD_ONE].table); entry++ )
		{
			bool found = false;

			if (   Cards[CARD_ONE].table[entry]._Modify.Sec   != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Min   != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Hour  != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Day   != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Month != 0
				|| Cards[CARD_ONE].table[entry]._Modify.Year  != 0 )
			{
				date1.m_nSecond = Cards[CARD_ONE].table[entry]._Modify.Sec;
				date1.m_nMinute = Cards[CARD_ONE].table[entry]._Modify.Min;
				date1.m_nHour   = Cards[CARD_ONE].table[entry]._Modify.Hour;
				date1.m_nDay    = Cards[CARD_ONE].table[entry]._Modify.Day;
				date1.m_nMonth  = Cards[CARD_ONE].table[entry]._Modify.Month;
				date1.m_nYear   = Cards[CARD_ONE].table[entry]._Modify.Year;

				if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0
					&& Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE )
				{
					found = true;
				}
			}
			else
			if (   Cards[CARD_ONE].table[entry]._Create.Sec   != 0
				|| Cards[CARD_ONE].table[entry]._Create.Min   != 0
				|| Cards[CARD_ONE].table[entry]._Create.Hour  != 0
				|| Cards[CARD_ONE].table[entry]._Create.Day   != 0
				|| Cards[CARD_ONE].table[entry]._Create.Month != 0
				|| Cards[CARD_ONE].table[entry]._Create.Year  != 0 )
			{
				date1.m_nSecond = Cards[CARD_ONE].table[entry]._Create.Sec;
				date1.m_nMinute = Cards[CARD_ONE].table[entry]._Create.Min;
				date1.m_nHour   = Cards[CARD_ONE].table[entry]._Create.Hour;
				date1.m_nDay    = Cards[CARD_ONE].table[entry]._Create.Day;
				date1.m_nMonth  = Cards[CARD_ONE].table[entry]._Create.Month;
				date1.m_nYear   = Cards[CARD_ONE].table[entry]._Create.Year;

				if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0
					&& Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE )
				{
					found = true;
				}
			}

			if ( found )
			{
				int32 d;
				if ( date1 > date2 )      d = 1;
				else if ( date1 < date2 ) d = 2;
				else                      d = 0;

				if ( d == 1 )
				{
					char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName;

					date2 = date1;
					strncpy(filename, entryname, 28);
				}
				else
				{
					int32 d;
					if ( date1 > date2 )      d = 1;
					else if ( date1 < date2 ) d = 2;
					else                      d = 0;

					if ( d == 0 )
					{
						char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName;
						date2 = date1;
						strncpy(filename, entryname, 28);
					}
				}
			}
		}

		if (   date2.m_nSecond != 0
			|| date2.m_nMinute != 0
			|| date2.m_nHour   != 0
			|| date2.m_nDay    != 0
			|| date2.m_nMonth  != 0
			|| date2.m_nYear   != 0 )
		{
			return true;
		}

		return false;
	}

	//todo errors

	nError = ERR_NONE;
	return false;
#endif
}

void
CMemoryCard::ClearFileTableBuffer(int32 cardID)
{
	for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table); i++ )
	{
		Cards[cardID].table[i].FileSizeByte = 0;
		strncpy((char *)Cards[cardID].table[i].EntryName, " ", sizeof(Cards[cardID].table[i].EntryName) - 1);
	}
}

int32
CMemoryCard::GetClusterAmountForFileCreation(int32 port)
{
#if defined(PS2)
	int cmd = sceMcFuncNoEntSpace;
	int result = 0;

	CTimer::Stop();

	while ( sceMcGetEntSpace(port, 0, TheGameRootDirectory) != sceMcResSucceed )
		;

	sceMcSync(0, &cmd, &result);

	if ( result >= sceMcResSucceed )
	{
		nError = NO_ERR_SUCCESS;
		return result;
	}

	if ( result == sceMcResNoFormat )
	{
		nError = ERR_NOFORMAT;
		return nError;
	}

	nError = ERR_NONE;
	return nError;
#else
	CTimer::Stop();
	nError = NO_ERR_SUCCESS;
	return 0;
#endif
}

bool
CMemoryCard::DeleteEverythingInGameRoot(int32 cardID)
{
	CTimer::Stop();

	ChangeDirectory(CurrentCard, Cards[CurrentCard].dir);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	PopulateFileTable(cardID);

	for ( int32 i = ARRAY_SIZE(Cards[cardID].table) - 1; i >= 0; i--)
		DeleteMemoryCardFile(cardID, (char *)Cards[cardID].table[i].EntryName);

	ChangeDirectory(CurrentCard, "/");

	DeleteMemoryCardFile(cardID, Cards[CurrentCard].dir);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	return true;
}

int32
CMemoryCard::CheckDataNotCorrupt(char *filename)
{
	CheckSum = 0;

	int32 lang = 0;
	int32 level = 0;

	LastBlockSize = 0;

	char buf[100*4];

	for ( int32 i = 0; i < sizeof(buf); i++ )
		buf[i] = '\0';

	strncpy(buf, Cards[CurrentCard].dir, sizeof(buf) - 1);
	strncat(buf, "/", sizeof(buf) - 1);
	strcat (buf, filename);

	ChangeDirectory(CurrentCard, Cards[CurrentCard].dir);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	int32 file = OpenMemCardFileForReading(CurrentCard, buf);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	int32 bytes_processed = 0;
	int32 blocknum = 0;
	int32 lastblocksize;

	while ( SAVE_FILE_SIZE - sizeof(int32) > bytes_processed && blocknum < 8 )
	{
		int32 size;

		ReadFromMemCard(file, &size, sizeof(size));

		if ( nError != NO_ERR_SUCCESS )
			return RES_FAILED;

		lastblocksize = ReadFromMemCard(file, work_buff, align4bytes(size));

		if ( nError != NO_ERR_SUCCESS )
			return RES_FAILED;

		uint8 sizebuff[4];
		memcpy(sizebuff, &size, sizeof(size));

		for ( int32 i = 0; i < ARRAY_SIZE(sizebuff); i++ )
			CheckSum += sizebuff[i];

		uint8 *pWork_buf = work_buff;
		for ( int32 i = 0; i < lastblocksize; i++ )
		{
			CheckSum += *pWork_buf++;
			bytes_processed++;
		}

		if ( blocknum == 0 )
		{
			uint8 *pBuf = work_buff + sizeof(uint32);
			ReadDataFromBufferPointer(pBuf, level);
			pBuf += sizeof(uint32) * 29;
			ReadDataFromBufferPointer(pBuf, lang);
		}

		blocknum++;
	}

	int32 checkSum;
	ReadFromMemCard(file, &checkSum, sizeof(checkSum));
	CloseMemCardFile(file);

	if ( nError != NO_ERR_SUCCESS )
		return RES_FAILED;

	if ( CheckSum == checkSum )
	{
		m_LevelToLoad    = level;
		m_LanguageToLoad = lang;
		LastBlockSize    = lastblocksize;

		return RES_SUCCESS;
	}

	nError = ERR_DATACORRUPTED;
	return RES_FAILED;
}

int32
CMemoryCard::GetLanguageToLoad(void)
{
	return m_LanguageToLoad;
}

int32
CMemoryCard::GetLevelToLoad(void)
{
	return m_LevelToLoad;
}

bool
CMemoryCard::CreateGameDirectoryFromScratch(int32 cardID)
{
	TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true);

	int32 err = RES_SUCCESS;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	if ( Cards[CurrentCard].free < 500 )
	{
		CTimer::Update();
		uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode();

		while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 )
		{
			for ( int32 i = 0; i < 1000; i++ )
				powf(3.33f, 3.444f);

			CTimer::Update();
		}

		nError = ERR_DIRFULLDEVICE;
		return false;
	}

	TheMemoryCard.ChangeDirectory(CARD_ONE, "/");

	if ( nError != NO_ERR_SUCCESS )
		return false;

	int32 r = LookForRootDirectory(CARD_ONE);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	if ( r == ERR_NOROOTDIR )
	{
		CreateRootDirectory(CARD_ONE);

		if ( nError != NO_ERR_SUCCESS )
			DeleteEverythingInGameRoot(CARD_ONE);
	}

	ChangeDirectory(CARD_ONE, Cards[CARD_ONE].dir);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	PopulateFileTable(CARD_ONE);

	if ( nError != NO_ERR_SUCCESS )
		return false;

#if defined(PS2)
	bool entryExist;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ )
		{
			if ( !strcmp("icon.sys", (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		err = RES_FAILED;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ )
		{
			if ( !strcmp(icon_one, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		err = RES_FAILED;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ )
		{
			if ( !strcmp(icon_two, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		err = RES_FAILED;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ )
		{
			if ( !strcmp(icon_three, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		err = RES_FAILED;

	if ( nError != NO_ERR_SUCCESS )
		return false;

	if ( err != RES_SUCCESS )
	{
		int32 icon = CreateIconFiles(CARD_ONE, icon_one, icon_two, icon_three);

		if ( nError != NO_ERR_SUCCESS )
			return false;

		if ( icon != RES_SUCCESS )
			DeleteEverythingInGameRoot(CARD_ONE);
	}
#endif

	int32 guff = FillFirstFileWithGuff(CARD_ONE);

	if ( nError != NO_ERR_SUCCESS )
		return false;

	if ( guff == RES_SUCCESS )
	{
		printf("Game Default directory present");
		return true;
	}

	DeleteEverythingInGameRoot(CARD_ONE);

	return false;
}

bool
CMemoryCard::CheckGameDirectoryThere(int32 cardID)
{
	TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true);

	if ( TheMemoryCard.nError != NO_ERR_SUCCESS )
		return false;

	TheMemoryCard.ChangeDirectory(cardID, Cards[CARD_ONE].dir);

	if ( TheMemoryCard.nError != NO_ERR_SUCCESS )
		return false;

	PopulateFileTable(cardID);

	if ( TheMemoryCard.nError != NO_ERR_SUCCESS )
		return false;

	bool entryExist;

#if defined(PS2)
	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ )
		{
			if ( !strcmp("icon.sys", (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		return false;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ )
		{
			if ( !strcmp(icon_one, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		return false;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ )
		{
			if ( !strcmp(icon_two, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		return false;

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ )
		{
			if ( !strcmp(icon_three, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		return false;
#endif

	char buff[80];

	strncpy(buff, Cards[cardID].dir+1, sizeof(buff) - 1);

	entryExist = false;
	if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS )
	{
		for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ )
		{
			if ( !strcmp(buff, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) )
			{
				entryExist = true;
				break;
			}
		}
	}

	if ( !entryExist )
		return false;

	printf("Game directory present");

	return true;
}

void
CMemoryCard::PopulateSlotInfo(int32 cardID)
{
	CTimer::Stop();

	for ( int32 i = 0; i < MAX_SLOTS; i++ )
	{
		Slots[i] = SLOT_NOTPRESENT;

		for ( int32 j = 0; j < ARRAY_SIZE(SlotFileName[i]); j++ )
			SlotFileName[i][j] = L'\0';

		for ( int32 j = 0; j < ARRAY_SIZE(SlotSaveDate[i]); j++ )
			SlotSaveDate[i][j] = L'\0';

		UnicodeStrcpy(SlotSaveDate[i], TheText.Get("DEFDT"));
	}

	TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true);

	if ( nError != NO_ERR_SUCCESS )
		return;

	TheMemoryCard.ChangeDirectory(cardID, TheMemoryCard.Cards[CARD_ONE].dir);

	if ( nError != NO_ERR_SUCCESS && nError != ERR_DIRNOENTRY )
		return;

	PopulateFileTable(cardID);

	if ( nError != NO_ERR_SUCCESS && nError != ERR_FILETABLENOENTRY )
		return;

	for ( int32 slot = 0; slot < MAX_SLOTS; slot++ )
	{
#if defined(PS2)
		for ( int32 entry = 7; entry < ARRAY_SIZE(Cards[cardID].table); entry++ )
#else
		for ( int32 entry = 0; entry < ARRAY_SIZE(Cards[cardID].table); entry++ )
#endif
		{
			if ( TheMemoryCard.Cards[CARD_ONE].table[entry].FileSizeByte != 0 )
			{
				char slotnum[30];
				char slotname[30];
				char slotdate[30];

				if (
#if defined(PS2)
					TheMemoryCard.Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed &&
#endif
					TheMemoryCard.Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE )
				{
					char *entryname = (char *)Cards[cardID].table[entry].EntryName;

					bool bFound = false;
#if defined(PS2)
					for ( int32 i = 7; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ )
#else
					for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ )
#endif
					{
						sprintf(slotnum, "%i ", slot+1);

						for ( int32 j = 0; j < sizeof(slotname); j++ )
							slotname[j] = '\0';

						strncat(slotname, slotnum, sizeof(slotnum)-1);

						if ( !strncmp(slotname, entryname, 1) )
						{
							bFound = true;

							Slots[slot] = SLOT_PRESENT;
							AsciiToUnicode(entryname, SlotFileName[slot]);

							int32 sec   = Cards[CARD_ONE].table[entry]._Create.Sec;
							int32 month = Cards[CARD_ONE].table[entry]._Create.Month;
							int32 year  = Cards[CARD_ONE].table[entry]._Create.Year;
							int32 min   = Cards[CARD_ONE].table[entry]._Create.Min;
							int32 hour  = Cards[CARD_ONE].table[entry]._Create.Hour;
							int32 day   = Cards[CARD_ONE].table[entry]._Create.Day;

							for ( int32 j = 0; j < ARRAY_SIZE(SlotSaveDate[slot]); j++ )
								SlotSaveDate[slot][j] = L'\0';

							for ( int32 j = 0; j < ARRAY_SIZE(slotdate); j++ )
								slotdate[j] = '\0';

							char *monthstr;
							switch ( month )
							{
								case  1: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JAN")); break;
								case  2: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("FEB")); break;
								case  3: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("MAR")); break;
								case  4: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("APR")); break;
								case  5: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("MAY")); break;
								case  6: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JUN")); break;
								case  7: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JUL")); break;
								case  8: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("AUG")); break;
								case  9: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("SEP")); break;
								case 10: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("OCT")); break;
								case 11: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("NOV")); break;
								case 12: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("DEC")); break;
							}

							sprintf(slotdate, "%02d %s %04d %02d:%02d:%02d", day, monthstr, year, hour, min, sec);
							AsciiToUnicode(slotdate, SlotSaveDate[slot]);
						}
					}
				}
				else
				{
					char *entryname = (char *)Cards[cardID].table[entry].EntryName;

					bool bFound = false;
#if defined(PS2)
					for ( int32 i = 7; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) // again ...
#else
					for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) // again ...
#endif
					{
						sprintf(slotnum, "%i ", slot+1);

						for ( int32 j = 0; j < sizeof(slotname); j++ )
							slotname[j] = '\0';

						strncat(slotname, slotnum, sizeof(slotnum)-1);

						if ( !strncmp(slotname, entryname, 1) )
						{
							bFound = true;

							Slots[slot] = SLOT_CORRUPTED;
							AsciiToUnicode(entryname, SlotFileName[slot]);
						}
					}
				}
			}
		}
	}

	nError = NO_ERR_SUCCESS;
	return;
}

int32
CMemoryCard::GetInfoOnSpecificSlot(int32 slotID)
{
	return Slots[slotID];
}

wchar *
CMemoryCard::GetDateAndTimeOfSavedGame(int32 slotID)
{
	return SlotSaveDate[slotID];
}

int32
CMemoryCard::CheckCardStateAtGameStartUp(int32 cardID)
{
	CheckCardInserted(cardID);
	if ( nError == ERR_NOFORMAT )
		return MCSTATE_OK;
	if ( nError == ERR_NONE )
		return MCSTATE_NOCARD;

	if ( !CheckGameDirectoryThere(cardID) )
	{
		if ( nError == ERR_NONE )
			return MCSTATE_NOCARD;

		DeleteEverythingInGameRoot(cardID);
		if ( nError == ERR_NONE )
			return MCSTATE_NOCARD;

		TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true);
		if ( nError == ERR_NONE )
			return MCSTATE_NOCARD;

		if ( Cards[CurrentCard].free < 500 )
			return MCSTATE_NEED_500KB;

		return MCSTATE_OK;
	}

	TheMemoryCard.CheckCardInserted(CARD_ONE);

	if ( nError == NO_ERR_SUCCESS )
	{
		if ( TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CARD_ONE].dir) != ERR_NONE )
		{
			if ( TheMemoryCard.FindMostRecentFileName(CARD_ONE, MostRecentFile) == true )
			{
				if ( TheMemoryCard.CheckDataNotCorrupt(MostRecentFile) == RES_FAILED )
				{
					TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true);
					if ( Cards[CurrentCard].free < 200 )
						return MCSTATE_NEED_200KB;
				}
			}
			else
			{
				TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true);
				if ( Cards[CurrentCard].free < 200 )
					return MCSTATE_NEED_200KB;
			}
		}
	}

	if ( TheMemoryCard.CheckCardInserted(CARD_ONE) != NO_ERR_SUCCESS )
		return MCSTATE_NOCARD;

	return MCSTATE_OK;
}

void
CMemoryCard::SaveSlot(int32 slotID)
{
	bool bSave = true;

	for ( int32 j = 0; j < sizeof(ValidSaveName); j++ )
		ValidSaveName[j] = '\0';

	char buff[100];

	sprintf(buff, "%i ", slotID+1);
	strncat(ValidSaveName, buff, sizeof(ValidSaveName) - 1);

	if ( CStats::LastMissionPassedName[0] != '\0' )
	{
		char mission[100];

		strcpy(mission, UnicodeToAsciiForMemoryCard(TheText.Get(CStats::LastMissionPassedName)));

#ifdef FIX_BUGS
		strncat(ValidSaveName, mission, sizeof(ValidSaveName)-1);
#else
		strncat(ValidSaveName, mission, 21);
		strncat(ValidSaveName, "...", strlen("..."));
#endif
	}

	if ( !CheckGameDirectoryThere(CARD_ONE) )
	{
		DeleteEverythingInGameRoot(CARD_ONE);
		bSave = CreateGameDirectoryFromScratch(CARD_ONE);
	}

	if ( bSave )
	{
		if ( Slots[slotID] == SLOT_PRESENT )
		{
			TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir);
			if ( nError == NO_ERR_SUCCESS )
				TheMemoryCard.DeleteMemoryCardFile(CARD_ONE, UnicodeToAsciiForMemoryCard(SlotFileName[slotID]));
		}

		SaveGame();
	}

	CTimer::Stop();
	CStreaming::FlushRequestList();
	CStreaming::DeleteRwObjectsAfterDeath(FindPlayerPed()->GetPosition());
	CStreaming::RemoveUnusedModelsInLoadedList();
	CGame::DrasticTidyUpMemory(false);
	CTimer::Update();
}

void
CMemoryCard::DeleteSlot(int32 slotID)
{
	TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir);

	if ( nError == NO_ERR_SUCCESS )
		TheMemoryCard.DeleteMemoryCardFile(CARD_ONE, UnicodeToAsciiForMemoryCard(SlotFileName[slotID]));
}

void
CMemoryCard::LoadSlotToBuffer(int32 slotID)
{
	CStreaming::DeleteAllRwObjects();

	strcpy(LoadFileName, UnicodeToAsciiForMemoryCard(SlotFileName[slotID]));

	TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir);

	if ( nError == NO_ERR_SUCCESS )
		TheMemoryCard.CheckDataNotCorrupt(LoadFileName);
}

wchar *
CMemoryCard::GetNameOfSavedGame(int32 slotID)
{
	return SlotFileName[slotID];
}

int32
CMemoryCard::DoClassSaveRoutine(int32 file, uint8 *data, uint32 size)
{
	WritetoMemCard(file, &size, sizeof(size));

	if ( nError != NO_ERR_SUCCESS )
	{
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1);
		return ERR_NONE;
	}

	WritetoMemCard(file, data, align4bytes(size));

	uint8 sizebuff[4];
	memcpy(sizebuff, &size, sizeof(size));

	for ( int32 i = 0; i < ARRAY_SIZE(sizebuff); i++ )
		CheckSum += sizebuff[i];

	for ( int32 i = 0; i < align4bytes(size); i++ )
		CheckSum += *data++;

	if ( nError != NO_ERR_SUCCESS )
	{
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1);
		return ERR_NONE;
	}

	return nError;
}

#endif


code:
#pragma once
#include "common.h"
#ifdef PS2_MENU
#include "Date.h"

#if defined(PS2)
#include <libcdvd.h>
#include <sifdev.h>
#include <libvu0.h>
#endif

enum
{
	CARD_ONE = 0,
	CARD_TWO,
	MAX_CARDS,
};

class CMemoryCardInfo
{
public:
	int port;
	int slot;
	int type;
	int free;
	int format;
	char dir[40];
#if defined(PS2)
	sceMcTblGetDir table[15];
#else
	struct
	{
		typedef struct {unsigned char Sec,Min,Hour; unsigned char Day,Month; unsigned short Year;} _time;
		_time _Create;
		_time _Modify;
		unsigned int FileSizeByte;
		unsigned short AttrFile;
		unsigned char EntryName[32];
	}table[15];
#endif
	CMemoryCardInfo(void);
};

#define GUFF_FILE_SIZE 147096
#define SAVE_FILE_SIZE 201729

class CMemoryCard
{
public:
	enum
	{
		MAX_SLOTS = 8,
	};

	enum MCSTATE
	{
		MCSTATE_OK = 0,
		MCSTATE_NEED_500KB,
		MCSTATE_NEED_200KB,
		MCSTATE_NOCARD,
	};

	enum SLOTINFO
	{
		SLOT_PRESENT = 0,
		SLOT_NOTPRESENT,
		SLOT_CORRUPTED,
	};

	int             _unk0;
	int             _unk1;
	bool            m_bWantToLoad;
	bool            JustLoadedDontFadeInYet;
	bool            StillToFadeOut;
	bool            b_FoundRecentSavedGameWantToLoad;
	uint32          TimeStartedCountingForFade;
	uint32          TimeToStayFadedBeforeFadeOut;
	uint32          LastBlockSize;
	bool            _bunk2;
	char            ValidSaveName        [30];
	char            MostRecentFile       [30];
	char            _unkName3            [30];
	char            SaveFileNameJustSaved[30];
	char _pad0[3];
	wchar          *pErrorMsg;
	char            _unk4[32];
	bool            _bunk5;
	bool            _bunk6;
	bool            _bunk7;
	bool            _bunk8;
	int             nError;
	wchar           _unk9[30];
	char            LoadFileName[30];
	char _pad1[2];
	CDate           CompileDateAndTime;
	int             m_LanguageToLoad;
	int             m_LevelToLoad;
	int             CurrentCard;
	CMemoryCardInfo Cards       [MAX_CARDS];
	int             Slots       [MAX_SLOTS];
	wchar           SlotFileName[MAX_SLOTS][30];
	wchar           SlotSaveDate[MAX_SLOTS][30];
	char            _unk10[32];

	enum
	{
		ERR_NONE = 0,
		ERR_NOFORMAT = 1,
		ERR_DIRNOENTRY = 2,
		ERR_OPENNOENTRY = 3,
		ERR_DELETENOENTRY = 4,
		ERR_DELETEDENIED = 5,
		ERR_DELETEFAILED = 6,
		ERR_WRITEFULLDEVICE = 7,
		ERR_WRITENOENTRY = 8,
		ERR_WRITEDENIED = 9,
		ERR_FLUSHNOENTRY,
		ERR_WRITEFAILED,
		ERR_FORMATFAILED = 12,
		ERR_FILETABLENOENTRY = 13,
		ERR_DIRFULLDEVICE = 14,
		ERR_DIRBADENTRY = 15,
		ERR_FILEFULLDEVICE = 16,
		ERR_FILENOPATHENTRY = 17,
		ERR_FILEDENIED = 18,
		ERR_FILEUPLIMIT = 19,
		ERR_READNOENTRY = 20,
		ERR_READDENIED = 21,
		ERR_LOADFAILED = 22, // unused
		ERR_SAVEFAILED = 23,
		ERR_DATACORRUPTED = 24,
		ERR_NOROOTDIR = 25,
		NO_ERR_SUCCESS = 26,
	};

	enum
	{
		RES_SUCCESS = 1,
		RES_FAILED  = -1,
	};

	int32 GetError()
	{
		return nError;
	}

	wchar *GetErrorMessage()
	{
		return pErrorMsg;
	}

	int32 Init(void);
	CMemoryCard(void);
	int32 RestoreForStartLoad(void);
	int32 LoadSavedGame(void);
	int32 CheckCardInserted(int32 cardID);
	int32 PopulateCardFlags(int32 cardID, bool bSlotFlag, bool bTypeFlag, bool bFreeFlag, bool bFormatFlag);
	int32 FormatCard(int32 cardID);
	int32 PopulateFileTable(int32 cardID);
	int32 CreateRootDirectory(int32 cardID);
	int32 ChangeDirectory(int32 cardID, char *dir);
	int32 CreateIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three);
	int32 LoadIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three);
	int32 CloseMemCardFile(int32 file);
	int32 CreateMemCardFileReadWrite(int32 cardID, char *filename);
	int32 OpenMemCardFileForReading(int32 cardID, char *filename);
	int32 ReadFromMemCard(int32 file, void *buff, int32 size);
	int32 DeleteMemoryCardFile(int32 cardID, char *filename);
	void PopulateErrorMessage();
	int32 WritetoMemCard(int32 file, void *buff, int32 size);
	bool SaveGame(void);
	bool DoHackRoundSTUPIDSonyDateTimeStuff(int32 port, char *filename);
	int32 LookForRootDirectory(int32 cardID);
	int32 FillFirstFileWithGuff(int32 cardID);
	bool FindMostRecentFileName(int32 cardID, char *filename);
	void ClearFileTableBuffer(int32 cardID);
	int32 GetClusterAmountForFileCreation(int32 port);
	bool DeleteEverythingInGameRoot(int32 cardID);
	int32 CheckDataNotCorrupt(char *filename);
	int32 GetLanguageToLoad(void);
	int32 GetLevelToLoad(void);
	bool CreateGameDirectoryFromScratch(int32 cardID);
	bool CheckGameDirectoryThere(int32 cardID);
	void PopulateSlotInfo(int32 cardID);
	int32 GetInfoOnSpecificSlot(int32 slotID);
	wchar *GetDateAndTimeOfSavedGame(int32 slotID);
	int32 CheckCardStateAtGameStartUp(int32 cardID);
	void SaveSlot(int32 slotID);
	void DeleteSlot(int32 slotID);
	void LoadSlotToBuffer(int32 slotID);
	wchar *GetNameOfSavedGame(int32 slotID);
	int32 DoClassSaveRoutine(int32 file, uint8 *data, uint32 size);
};

extern CMemoryCard TheMemoryCard;
#endif

license: mit


code:
#define WITHWINDOWS
#include "common.h"
#include "crossplatform.h"

#include "FileMgr.h"
#include "Font.h"
#ifdef MORE_LANGUAGES
#include "Game.h"
#endif
#include "GenericGameStorage.h"
#include "Messages.h"
#include "PCSave.h"
#include "Text.h"

const char* _psGetUserFilesFolder();

C_PcSave PcSaveHelper;

void
C_PcSave::SetSaveDirectory(const char *path)
{
	sprintf(DefaultPCSaveFileName, "%s\\%s", path, "GTA3sf");
}

bool
C_PcSave::DeleteSlot(int32 slot)
{
#ifdef FIX_BUGS
	char FileName[MAX_PATH];
#else
	char FileName[200];
#endif

	PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL;
	sprintf(FileName, "%s%i.b", DefaultPCSaveFileName, slot + 1);
	DeleteFile(FileName);
	SlotSaveDate[slot][0] = '\0';
	return true;
}

bool
C_PcSave::SaveSlot(int32 slot)
{
	MakeValidSaveName(slot);
	PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL;
	_psGetUserFilesFolder();
	int file = CFileMgr::OpenFile(ValidSaveName, "wb");
	if (file != 0) {
#ifdef MISSION_REPLAY
		if (!IsQuickSave)
#endif
			DoGameSpecificStuffBeforeSave();
		if (GenericSave(file)) {
			if (!!CFileMgr::CloseFile(file))
				nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE;
			return true;
		}

		return false;
	}
	PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CREATE;
	return false;
}

bool
C_PcSave::PcClassSaveRoutine(int32 file, uint8 *data, uint32 size)
{
	CFileMgr::Write(file, (const char*)&size, sizeof(size));
	if (CFileMgr::GetErrorReadWrite(file)) {
		nErrorCode = SAVESTATUS_ERR_SAVE_WRITE;
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1);
		return false;
	}

	CFileMgr::Write(file, (const char*)data, align4bytes(size));
	CheckSum += (uint8) size;
	CheckSum += (uint8) (size >> 8);
	CheckSum += (uint8) (size >> 16);
	CheckSum += (uint8) (size >> 24);
	for (int i = 0; i < align4bytes(size); i++) {
		CheckSum += *data++;
	}
	if (CFileMgr::GetErrorReadWrite(file)) {
		nErrorCode = SAVESTATUS_ERR_SAVE_WRITE;
		strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1);
		return false;
	}

	return true;
}

void
C_PcSave::PopulateSlotInfo()
{
	for (int i = 0; i < SLOT_COUNT; i++) {
		Slots[i + 1] = SLOT_EMPTY;
		SlotFileName[i][0] = '\0';
		SlotSaveDate[i][0] = '\0';
	}
	for (int i = 0; i < SLOT_COUNT; i++) {
#ifdef FIX_BUGS
		char savename[MAX_PATH];
#else
		char savename[52];
#endif
		struct {
			int size;
			wchar FileName[24];
			SYSTEMTIME SaveDateTime;
		} header;
		sprintf(savename, "%s%i%s", DefaultPCSaveFileName, i + 1, ".b");
		int file = CFileMgr::OpenFile(savename, "rb");
		if (file != 0) {
			CFileMgr::Read(file, (char*)&header, sizeof(header));
			if (strncmp((char*)&header, TopLineEmptyFile, sizeof(TopLineEmptyFile)-1) != 0) {
				Slots[i + 1] = SLOT_OK;
				memcpy(SlotFileName[i], &header.FileName, sizeof(header.FileName));

				SlotFileName[i][24] = '\0';
			}
			CFileMgr::CloseFile(file);
		}
		if (Slots[i + 1] == SLOT_OK) {
			if (CheckDataNotCorrupt(i, savename)) {
#ifdef FIX_INCOMPATIBLE_SAVES
				if (!FixSave(i, GetSaveType(savename))) {
					CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]);
					Slots[i + 1] = SLOT_CORRUPTED;
					continue;
				}
#endif
				SYSTEMTIME st;
				memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME));
				const char *month;
				switch (st.wMonth)
				{
				case 1: month = "JAN"; break;
				case 2: month = "FEB"; break;
				case 3: month = "MAR"; break;
				case 4: month = "APR"; break;
				case 5: month = "MAY"; break;
				case 6: month = "JUN"; break;
				case 7: month = "JUL"; break;
				case 8: month = "AUG"; break;
				case 9: month = "SEP"; break;
				case 10: month = "OCT"; break;
				case 11: month = "NOV"; break;
				case 12: month = "DEC"; break;
				default: assert(0);
				}
				char date[70];
#ifdef MORE_LANGUAGES
				if (CGame::japaneseGame)
					sprintf(date, "%02d %02d %04d %02d:%02d:%02d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond);
				else
#endif // MORE_LANGUAGES
					sprintf(date, "%02d %s %04d %02d:%02d:%02d", st.wDay, UnicodeToAsciiForSaveLoad(TheText.Get(month)), st.wYear, st.wHour, st.wMinute, st.wSecond);
				AsciiToUnicode(date, SlotSaveDate[i]);

			} else {
				CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]);
				Slots[i + 1] = SLOT_CORRUPTED;
			}
		}
	}
}


code:
#pragma once

enum eSaveStatus
{
	SAVESTATUS_SUCCESSFUL = 0,
	SAVESTATUS_ERR_SAVE_CREATE,
	SAVESTATUS_ERR_SAVE_WRITE,
	SAVESTATUS_ERR_SAVE_CLOSE,
	SAVESTATUS_ERR_LOAD_OPEN,
	SAVESTATUS_ERR_LOAD_READ,
	SAVESTATUS_ERR_LOAD_CLOSE,
	SAVESTATUS_ERR_DATA_INVALID,

	// unused
	SAVESTATUS_DELETEFAILED8,
	SAVESTATUS_DELETEFAILED9,
	SAVESTATUS_DELETEFAILED10,
};

enum
{
	SLOT_OK = 0,
	SLOT_EMPTY,
	SLOT_CORRUPTED
};

class C_PcSave
{
public:
	eSaveStatus nErrorCode;

	C_PcSave() : nErrorCode(SAVESTATUS_SUCCESSFUL) {}
	void PopulateSlotInfo();
	bool DeleteSlot(int32 slot);
	bool SaveSlot(int32 slot);
	bool PcClassSaveRoutine(int32 file, uint8 *data, uint32 size);
	static void SetSaveDirectory(const char *path);
};

extern C_PcSave PcSaveHelper;


code:
#pragma once

#ifdef VALIDATE_SAVE_SIZE
extern int32 _saveBufCount;
#define INITSAVEBUF _saveBufCount = 0;
#define VALIDATESAVEBUF(b) assert(_saveBufCount == b);
#else
#define INITSAVEBUF
#define VALIDATESAVEBUF(b)
#endif

inline void
SkipSaveBuf(uint8 *&buf, int32 skip)
{
	buf += skip;
#ifdef VALIDATE_SAVE_SIZE
	_saveBufCount += skip;
#endif
}

template <typename T>
inline void
ReadSaveBuf(T* out, uint8 *&buf)
{
	*out = *(T *)buf;
	SkipSaveBuf(buf, sizeof(T));
}

template <typename T>
inline T *
WriteSaveBuf(uint8 *&buf, const T &value)
{
	T *p = (T *)buf;
	*p = value;
	SkipSaveBuf(buf, sizeof(T));
	return p;
}

#ifdef COMPATIBLE_SAVES
inline void
ZeroSaveBuf(uint8 *&buf, uint32 length)
{
	memset(buf, 0, length);
	SkipSaveBuf(buf, length);
}
#endif

#define SAVE_HEADER_SIZE (4 * sizeof(char) + sizeof(uint32))

#define WriteSaveHeader(buf, a, b, c, d, size) \
	WriteSaveBuf(buf, a); \
	WriteSaveBuf(buf, b); \
	WriteSaveBuf(buf, c); \
	WriteSaveBuf(buf, d); \
	WriteSaveBuf(buf, (uint32)(size));

#ifdef VALIDATE_SAVE_SIZE
#define CheckSaveHeader(buf, a, b, c, d, size) do { \
	char _c; uint32 _size;\
	ReadSaveBuf(&_c, buf);\
	assert(_c == a);\
	ReadSaveBuf(&_c, buf);\
	assert(_c == b);\
	ReadSaveBuf(&_c, buf);\
	assert(_c == c);\
	ReadSaveBuf(&_c, buf);\
	assert(_c == d);\
	ReadSaveBuf(&_size, buf);\
	assert(_size == size);\
	} while(0)
#else
#define CheckSaveHeader(buf, a, b, c, d, size) SkipSaveBuf(buf, 8);
#endif


code:
#include "common.h"
#include "crossplatform.h"

// Codes compatible with Windows and Linux
#ifndef _WIN32

// For internal use
// wMilliseconds is not needed
void tmToSystemTime(const tm *tm, SYSTEMTIME *out) {
    out->wYear = tm->tm_year + 1900;
    out->wMonth = tm->tm_mon + 1;
    out->wDayOfWeek = tm->tm_wday;
    out->wDay = tm->tm_mday;
    out->wHour = tm->tm_hour;
    out->wMinute = tm->tm_min;
    out->wSecond = tm->tm_sec;
}

void GetLocalTime_CP(SYSTEMTIME *out) {
    time_t timestamp = time(nil);
    tm *localTm = localtime(&timestamp);
    tmToSystemTime(localTm, out);
}
#endif

// Compatible with Linux/POSIX and MinGW on Windows
#ifndef _WIN32
HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) {
	char pathCopy[MAX_PATH];
	strcpy(pathCopy, pathname);

	char *folder = strtok(pathCopy, "*");
	char *extension = strtok(NULL, "*");

	// because I remember like strtok might not return NULL for last delimiter
	if (extension && extension - folder == strlen(pathname))
		extension = nil;

	// Case-sensitivity and backslashes...
	// Will be freed at the bottom
	char *realFolder = casepath(folder);
	if (realFolder) {
		folder = realFolder;
	}

	strncpy(firstfile->folder, folder, sizeof(firstfile->folder));

	if (extension)
		strncpy(firstfile->extension, extension, sizeof(firstfile->extension));
	else
		firstfile->extension[0] = '\0';

	if (realFolder)
		free(realFolder);

	HANDLE d;
	if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile))
		return NULL;

	return d;
}

bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) {
	dirent *file;
	static struct stat fileStats;
	static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1];
	int extensionLen = strlen(finddata->extension);
	while ((file = readdir((DIR*)d)) != NULL) {

		// We only want "DT_REG"ular Files, but reportedly some FS and OSes gives DT_UNKNOWN as type.
		if ((file->d_type == DT_UNKNOWN || file->d_type == DT_REG || file->d_type == DT_LNK) &&
			(extensionLen == 0 || strncasecmp(&file->d_name[strlen(file->d_name) - extensionLen], finddata->extension, extensionLen) == 0)) {

			sprintf(relativepath, "%s/%s", finddata->folder, file->d_name);
			realpath(relativepath, path);
			stat(path, &fileStats);
			strncpy(finddata->cFileName, file->d_name, sizeof(finddata->cFileName));
			finddata->ftLastWriteTime = fileStats.st_mtime;
			return true;
		}
	}
	return false;
}

void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char* out, int size) {
	tm linuxTime;
	linuxTime.tm_year = in->wYear - 1900;
	linuxTime.tm_mon = in->wMonth - 1;
	linuxTime.tm_wday = in->wDayOfWeek;
	linuxTime.tm_mday = in->wDay;
	linuxTime.tm_hour = in->wHour;
	linuxTime.tm_min = in->wMinute;
	linuxTime.tm_sec = in->wSecond;
	strftime(out, size, nl_langinfo(D_FMT), &linuxTime);
}

void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) {
	tm *ptm = gmtime(writeTime);
	tmToSystemTime(ptm, out);
}
#endif

// Because wchar length differs between platforms.
wchar*
AllocUnicode(const char* src)
{
	wchar *dst = (wchar*)malloc(strlen(src)*2 + 2);
	wchar *i = dst;
	while((*i++ = (unsigned char)*src++) != '\0');
	return dst;
}

// Funcs/features from Windows that we need on other platforms
#ifndef _WIN32
char *strupr(char *s) {
    char* tmp = s;

    for (;*tmp;++tmp) {
        *tmp = toupper((unsigned char) *tmp);
    }

    return s;
}
char *strlwr(char *s) {
    char* tmp = s;

    for (;*tmp;++tmp) {
        *tmp = tolower((unsigned char) *tmp);
    }

    return s;
}

char *trim(char *s) {
    char *ptr;
    if (!s)
        return NULL;   // handle NULL string
    if (!*s)
        return s;      // handle empty string
    for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr);
    ptr[1] = '\0';
    return s;
}

FILE* _fcaseopen(char const* filename, char const* mode)
{
    FILE* result;
    char* real = casepath(filename);
    if (!real)
        result = fopen(filename, mode);
    else {
        result = fopen(real, mode);
        free(real);
    }
    return result;
}

int _caserename(const char *old_filename, const char *new_filename)
{
    int result;
    char *real_old = casepath(old_filename);
    char *real_new = casepath(new_filename);

    // hack so we don't even try to rename it to new_filename if it already exists
    if (!real_new) {
        free(real_old);
        return -1;
    }

    if (!real_old)
        result = rename(old_filename, real_new);
    else
        result = rename(real_old, real_new);

    free(real_old);
    free(real_new);

    return result;
}

// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
// Returned string should freed manually (if exists)
char* casepath(char const* path, bool checkPathFirst)
{
    if (checkPathFirst && access(path, F_OK) != -1) {
        // File path is correct
        return nil;
    }

    size_t l = strlen(path);
    char* p = (char*)alloca(l + 1);
    char* out = (char*)malloc(l + 3); // for extra ./
    strcpy(p, path);

    // my addon: linux doesn't handle filenames with spaces at the end nicely
    p = trim(p);

    size_t rl = 0;

    DIR* d;
    char* c;

    #if defined(__SWITCH__) || defined(PSP2)
    if( (c = strstr(p, ":/")) != NULL) // scheme used by some environments, eg. switch, vita
    {
        size_t deviceNameOffset = c - p + 3;
        char* deviceNamePath = (char*)alloca(deviceNameOffset + 1);
        strlcpy(deviceNamePath, p, deviceNameOffset);
        deviceNamePath[deviceNameOffset] = 0;
        d = opendir(deviceNamePath);
        p = c + 1;
    }
    else
    #endif
    if (p[0] == '/' || p[0] == '\\')
    {
        d = opendir("/");
    }
    else
    {
        d = opendir(".");
        out[0] = '.';
        out[1] = 0;
        rl = 1;
    }

    bool cantProceed = false; // just convert slashes in what's left in string, don't correct case of letters(because we can't)
    bool mayBeTrailingSlash = false;

    while (c = strsep(&p, "/\\"))
    {
        // May be trailing slash(allow), slash at the start(avoid), or multiple slashes(avoid)
        if (*c == '\0')
        {
            mayBeTrailingSlash = true;
            continue;
        } else {
            mayBeTrailingSlash = false;
        }

        out[rl] = '/';
        rl += 1;
        out[rl] = 0;

        if (cantProceed)
        {
            strcpy(out + rl, c);
            rl += strlen(c);
            continue;
        }

        struct dirent* e;
        while (e = readdir(d))
        {
            if (strcasecmp(c, e->d_name) == 0)
            {
                strcpy(out + rl, e->d_name);
                int reportedLen = (int)strlen(e->d_name);
                rl += reportedLen;
                assert(reportedLen == strlen(c) && "casepath: This is not good at all");

                closedir(d);
                d = opendir(out);

                // Either it wasn't a folder, or permission error, I/O error etc.
                if (!d) {
                    cantProceed = true;
                }

                break;
            }
        }

        if (!e)
        {
            printf("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path);
            // No match, add original name and continue converting further slashes.
            strcpy(out + rl, c);
            rl += strlen(c);
            cantProceed = true;
        }
    }

    if (d) closedir(d);
    if (mayBeTrailingSlash) {
        out[rl] = '/';  rl += 1;
        out[rl] = '\0';
    }

    if (rl > l + 2) {
        printf("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%zu chars)\n\tCorrected: %s (%zu chars)\n\n", path, l, out, rl);
    }
    return out;
}
#endif

#ifdef __SWITCH__
/* Taken from glibc */
char *realpath(const char *name, char *resolved)
{
   char *rpath, *dest = NULL;
   const char *start, *end, *rpath_limit;
   long int path_max;

   /* As per Single Unix Specification V2 we must return an error if
      either parameter is a null pointer.  We extend this to allow
      the RESOLVED parameter to be NULL in case the we are expected to
      allocate the room for the return value.  */
   if (!name)
      return NULL;

   /* As per Single Unix Specification V2 we must return an error if
      the name argument points to an empty string.  */
   if (name[0] == '\0')
      return NULL;

#ifdef PATH_MAX
   path_max = PATH_MAX;
#else
   path_max = pathconf(name, _PC_PATH_MAX);
   if (path_max <= 0)
      path_max = 1024;
#endif

   if (!resolved)
   {
      rpath = (char*)malloc(path_max);
      if (!rpath)
         return NULL;
   }
   else
      rpath = resolved;
   rpath_limit = rpath + path_max;

   if (name[0] != '/')
   {
      if (!getcwd(rpath, path_max))
      {
         rpath[0] = '\0';
         goto error;
      }
      dest = (char*)memchr(rpath, '\0', path_max);
   }
   else
   {
      rpath[0] = '/';
      dest = rpath + 1;
   }

   for (start = end = name; *start; start = end)
   {
      /* Skip sequence of multiple path-separators.  */
      while (*start == '/')
         ++start;

      /* Find end of path component.  */
      for (end = start; *end && *end != '/'; ++end)
         /* Nothing.  */;

      if (end - start == 0)
         break;
      else if (end - start == 1 && start[0] == '.')
         /* nothing */;
      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
      {
         /* Back up to previous component, ignore if at root already.  */
         if (dest > rpath + 1)
            while ((--dest)[-1] != '/')
               ;
      }
      else
      {
         size_t new_size;

         if (dest[-1] != '/')
            *dest++ = '/';

         if (dest + (end - start) >= rpath_limit)
         {
            ptrdiff_t dest_offset = dest - rpath;
            char *new_rpath;

            if (resolved)
            {
               if (dest > rpath + 1)
                  dest--;
               *dest = '\0';
               goto error;
            }
            new_size = rpath_limit - rpath;
            if (end - start + 1 > path_max)
               new_size += end - start + 1;
            else
               new_size += path_max;
            new_rpath = (char *)realloc(rpath, new_size);
            if (!new_rpath)
               goto error;
            rpath = new_rpath;
            rpath_limit = rpath + new_size;

            dest = rpath + dest_offset;
         }

         dest = (char*)memcpy(dest, start, end - start);
         *dest = '\0';
      }
   }
   if (dest > rpath + 1 && dest[-1] == '/')
      --dest;
   *dest = '\0';

   return rpath;

error:
   if (!resolved)
      free(rpath);
   return NULL;
}

ssize_t readlink (const char * __path, char * __buf, size_t __buflen)
{
   errno = ENOSYS;
   return -1;
}
#endif