// Memory card support module

#include "burner.h"

static TCHAR szMemoryCardFile[MAX_PATH];

int nMemoryCardStatus = 0;
int nMemoryCardSize;

static int nMinVersion;
static bool bMemCardFC1Format;

static int MakeOfn()
{
	memset(&ofn, 0, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = hScrnWnd;
	ofn.lpstrFile = szMemoryCardFile;
	ofn.nMaxFile = sizeof(szMemoryCardFile);
	ofn.lpstrInitialDir = _T(".");
	ofn.Flags = OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = _T("fc");

	return 0;
}

static int MemCardRead(TCHAR* szFilename, unsigned char* pData, int nSize)
{
	const char* szHeader  = "FB1 FC1 ";				// File + chunk identifier
	char szReadHeader[8] = "";

	bMemCardFC1Format = false;

	FILE* fp = _tfopen(szFilename, _T("rb"));
	if (fp == NULL) {
		return 1;
	}
	
	fread(szReadHeader, 1, 8, fp);					// Read identifiers
	if (memcmp(szReadHeader, szHeader, 8) == 0) {

		// FB Alpha memory card file

		int nChunkSize = 0;
		int nVersion = 0;

		bMemCardFC1Format = true;

		fread(&nChunkSize, 1, 4, fp);				// Read chunk size
		if (nSize < nChunkSize - 32) {
			fclose(fp);
			return 1;
		}

		fread(&nVersion, 1, 4, fp);					// Read version
		if (nVersion < nMinVersion) {
			fclose(fp);
			return 1;
		}
		fread(&nVersion, 1, 4, fp);
#if 0
		if (nVersion < nBurnVer) {
			fclose(fp);
			return 1;
		}
#endif

		fseek(fp, 0x0C, SEEK_CUR);					// Move file pointer to the start of the data block

		fread(pData, 1, nChunkSize - 32, fp);		// Read the data
	} else {

		// MAME or old FB Alpha memory card file

		unsigned char* pTemp = (unsigned char*)malloc(nSize >> 1);

		memset(pData, 0, nSize);
		fseek(fp, 0x00, SEEK_SET);

		if (pTemp) {
			fread(pTemp, 1, nSize >> 1, fp);

			for (int i = 1; i < nSize; i += 2) {
				pData[i] = pTemp[i >> 1];
			}

			free(pTemp);
		}
	}

	fclose(fp);

	return 0;
}

static int MemCardWrite(TCHAR* szFilename, unsigned char* pData, int nSize)
{
	FILE* fp = _tfopen(szFilename, _T("wb"));
	if (fp == NULL) {
		return 1;
	}

	if (bMemCardFC1Format) {

		// FB Alpha memory card file

		const char* szFileHeader  = "FB1 ";				// File identifier
		const char* szChunkHeader = "FC1 ";				// Chunk identifier
		const int nZero = 0;

		int nChunkSize = nSize + 32;

		fwrite(szFileHeader, 1, 4, fp);
		fwrite(szChunkHeader, 1, 4, fp);

		fwrite(&nChunkSize, 1, 4, fp);					// Chunk size

		fwrite(&nBurnVer, 1, 4, fp);					// Version of FBA this was saved from
		fwrite(&nMinVersion, 1, 4, fp);					// Min version of FBA data will work with

		fwrite(&nZero, 1, 4, fp);						// Reserved
		fwrite(&nZero, 1, 4, fp);						//
		fwrite(&nZero, 1, 4, fp);						//

		fwrite(pData, 1, nSize, fp);
	} else {

		// MAME or old FB Alpha memory card file

		unsigned char* pTemp = (unsigned char*)malloc(nSize >> 1);
		if (pTemp) {
			for (int i = 1; i < nSize; i += 2) {
				pTemp[i >> 1] = pData[i];
			}

			fwrite(pTemp, 1, nSize >> 1, fp);

			free(pTemp);
		}
	}

	fclose(fp);

	return 0;
}

static int __cdecl MemCardDoGetSize(struct BurnArea* pba)
{
	nMemoryCardSize = pba->nLen;

	return 0;
}

static int MemCardGetSize()
{
	BurnAcb = MemCardDoGetSize;
	BurnAreaScan(ACB_MEMCARD, &nMinVersion);

	return 0;
}

int	MemCardCreate()
{
	TCHAR szFilter[1024];
	int nRet;

	_stprintf(szFilter, FBALoadStringEx(hAppInst, IDS_DISK_FILE_CARD, true), _T(APP_TITLE));
	memcpy(szFilter + _tcslen(szFilter), _T(" (*.fc)\0*.fc\0\0"), 14 * sizeof(TCHAR));

	_stprintf (szMemoryCardFile, _T("memorycard"));
	MakeOfn();
	ofn.lpstrTitle = FBALoadStringEx(hAppInst, IDS_MEMCARD_CREATE, true);
	ofn.lpstrFilter = szFilter;
	ofn.Flags |= OFN_OVERWRITEPROMPT;

	int bOldPause = bRunPause;
	bRunPause = 1;
	nRet = GetSaveFileName(&ofn);
	bRunPause = bOldPause;

	if (nRet == 0) {
		return 1;
	}

	{
		unsigned char* pCard;

		MemCardGetSize();

		pCard = (unsigned char*)malloc(nMemoryCardSize);
		memset(pCard, 0, nMemoryCardSize);

		bMemCardFC1Format = true;

		if (MemCardWrite(szMemoryCardFile, pCard, nMemoryCardSize)) {
			return 1;
		}

		free(pCard);
	}

	nMemoryCardStatus = 1;
	MenuEnableItems();

	return 0;
}

int	MemCardSelect()
{
	TCHAR szFilter[1024];
	TCHAR* pszTemp = szFilter;
	int nRet;

	pszTemp += _stprintf(pszTemp, FBALoadStringEx(hAppInst, IDS_DISK_ALL_CARD, true));
	memcpy(pszTemp, _T(" (*.fc, MEMCARD.\?\?\?)\0*.fc;MEMCARD.\?\?\?\0"), 38 * sizeof(TCHAR));
	pszTemp += 38;
	pszTemp += _stprintf(pszTemp, FBALoadStringEx(hAppInst, IDS_DISK_FILE_CARD, true), _T(APP_TITLE));
	memcpy(pszTemp, _T(" (*.fc)\0*.fc\0"), 13 * sizeof(TCHAR));
	pszTemp += 13;
	pszTemp += _stprintf(pszTemp, FBALoadStringEx(hAppInst, IDS_DISK_FILE_CARD, true), _T("MAME"));
	memcpy(pszTemp, _T(" (MEMCARD.\?\?\?)\0MEMCARD.\?\?\?\0\0"), 28 * sizeof(TCHAR));

	MakeOfn();
	ofn.lpstrTitle = FBALoadStringEx(hAppInst, IDS_MEMCARD_SELECT, true);
	ofn.lpstrFilter = szFilter;

	int bOldPause = bRunPause;
	bRunPause = 1;
	nRet = GetOpenFileName(&ofn);
	bRunPause = bOldPause;

	if (nRet == 0) {
		return 1;
	}

	MemCardGetSize();

	if (nMemoryCardSize <= 0) {
		return 1;
	}

	nMemoryCardStatus = 1;
	MenuEnableItems();

	return 0;
}

static int __cdecl MemCardDoInsert(struct BurnArea* pba)
{
	if (MemCardRead(szMemoryCardFile, (unsigned char*)pba->Data, pba->nLen)) {
		return 1;
	}

	nMemoryCardStatus |= 2;
	MenuEnableItems();

	return 0;
}

int	MemCardInsert()
{
	if ((nMemoryCardStatus & 1) && (nMemoryCardStatus & 2) == 0) {
		BurnAcb = MemCardDoInsert;
		BurnAreaScan(ACB_WRITE | ACB_MEMCARD, &nMinVersion);
	}

	return 0;
}

static int __cdecl MemCardDoEject(struct BurnArea* pba)
{
	if (MemCardWrite(szMemoryCardFile, (unsigned char*)pba->Data, pba->nLen) == 0) {
		nMemoryCardStatus &= ~2;
		MenuEnableItems();

		return 0;
	}

	return 1;
}

int	MemCardEject()
{
	if ((nMemoryCardStatus & 1) && (nMemoryCardStatus & 2)) {
		BurnAcb = MemCardDoEject;
		nMinVersion = 0;
		BurnAreaScan(ACB_READ | ACB_MEMCARD, &nMinVersion);
	}

	return 0;
}

int	MemCardToggle()
{
	if (nMemoryCardStatus & 1) {
		if (nMemoryCardStatus & 2) {
			return MemCardEject();
		} else {
			return MemCardInsert();
		}
	}

	return 1;
}
