Commit 434a58aa authored by Libretro-Admin's avatar Libretro-Admin
Browse files

Update

parent 6cf2796b
Pipeline #1062 failed with stages
in 1 minute and 54 seconds
// Driver Save State module
#include "burner.h"
// from dynhuff.cpp
INT32 FreezeDecode(UINT8 **buffer, INT32 *size);
INT32 UnfreezeDecode(const UINT8* buffer, INT32 size);
INT32 FreezeEncode(UINT8 **buffer, INT32 *size);
INT32 UnfreezeEncode(const UINT8* buffer, INT32 size);
// from replay.cpp
INT32 FreezeInput(UINT8** buf, int* size);
INT32 UnfreezeInput(const UINT8* buf, INT32 size);
extern INT32 nReplayStatus;
extern bool bReplayReadOnly;
extern INT32 nReplayUndoCount;
extern UINT32 nReplayCurrentFrame;
extern UINT32 nStartFrame;
// If bAll=0 save/load all non-volatile ram to .fs
// If bAll=1 save/load all ram to .fs
// ------------ State len --------------------
static INT32 nTotalLen = 0;
static INT32 __cdecl StateLenAcb(struct BurnArea* pba)
{
nTotalLen += pba->nLen;
return 0;
}
static INT32 StateInfo(int* pnLen, int* pnMinVer, INT32 bAll)
{
INT32 nMin = 0;
nTotalLen = 0;
BurnAcb = StateLenAcb;
BurnAreaScan(ACB_NVRAM, &nMin); // Scan nvram
if (bAll) {
INT32 m;
BurnAreaScan(ACB_MEMCARD, &m); // Scan memory card
if (m > nMin) { // Up the minimum, if needed
nMin = m;
}
BurnAreaScan(ACB_VOLATILE, &m); // Scan volatile ram
if (m > nMin) { // Up the minimum, if needed
nMin = m;
}
}
*pnLen = nTotalLen;
*pnMinVer = nMin;
return 0;
}
// State load
INT32 BurnStateLoadEmbed(FILE* fp, INT32 nOffset, INT32 bAll, INT32 (*pLoadGame)())
{
const char* szHeader = "FS1 "; // Chunk identifier
INT32 nLen = 0;
INT32 nMin = 0, nFileVer = 0, nFileMin = 0;
INT32 t1 = 0, t2 = 0;
char ReadHeader[4];
char szForName[33];
INT32 nChunkSize = 0;
UINT8 *Def = NULL;
INT32 nDefLen = 0; // Deflated version
INT32 nRet = 0;
if (nOffset >= 0) {
fseek(fp, nOffset, SEEK_SET);
} else {
if (nOffset == -2) {
fseek(fp, 0, SEEK_END);
} else {
fseek(fp, 0, SEEK_CUR);
}
}
memset(ReadHeader, 0, 4);
fread(ReadHeader, 1, 4, fp); // Read identifier
if (memcmp(ReadHeader, szHeader, 4)) { // Not the right file type
return -2;
}
fread(&nChunkSize, 1, 4, fp);
if (nChunkSize <= 0x40) { // Not big enough
return -1;
}
INT32 nChunkData = ftell(fp);
fread(&nFileVer, 1, 4, fp); // Version of FB that this file was saved from
fread(&t1, 1, 4, fp); // Min version of FB that NV data will work with
fread(&t2, 1, 4, fp); // Min version of FB that All data will work with
if (bAll) { // Get the min version number which applies to us
nFileMin = t2;
} else {
nFileMin = t1;
}
fread(&nDefLen, 1, 4, fp); // Get the size of the compressed data block
memset(szForName, 0, sizeof(szForName));
fread(szForName, 1, 32, fp);
if (nBurnVer < nFileMin) { // Error - emulator is too old to load this state
return -5;
}
// Check the game the savestate is for, and load it if needed.
{
bool bLoadGame = false;
if (nBurnDrvActive < nBurnDrvCount) {
if (strcmp(szForName, BurnDrvGetTextA(DRV_NAME))) { // The save state is for the wrong game
bLoadGame = true;
}
} else { // No game loaded
bLoadGame = true;
}
if (bLoadGame) {
UINT32 nCurrentGame = nBurnDrvActive;
UINT32 i;
for (i = 0; i < nBurnDrvCount; i++) {
nBurnDrvActive = i;
if (strcmp(szForName, BurnDrvGetTextA(DRV_NAME)) == 0) {
break;
}
}
if (i == nBurnDrvCount) {
nBurnDrvActive = nCurrentGame;
return -3;
} else {
if (nCurrentGame != nBurnDrvActive) {
INT32 nOldActive = nBurnDrvActive; // Exit current game if loading a state from another game
nBurnDrvActive = nCurrentGame;
DrvExit();
nBurnDrvActive = nOldActive;
}
if (pLoadGame == NULL) {
return -1;
}
if (pLoadGame()) {
return -1;
}
}
}
}
StateInfo(&nLen, &nMin, bAll);
if (nLen <= 0) { // No memory to load
return -1;
}
// Check if the save state is okay
if (nFileVer < nMin) { // Error - this state is too old and cannot be loaded.
return -4;
}
fseek(fp, nChunkData + 0x30, SEEK_SET); // Read current frame
fread(&nReplayCurrentFrame, 1, 4, fp);
nCurrentFrame = nStartFrame + nReplayCurrentFrame;
fseek(fp, 0x0C, SEEK_CUR); // Move file pointer to the start of the compressed block
Def = (UINT8*)malloc(nDefLen);
if (Def == NULL) {
return -1;
}
memset(Def, 0, nDefLen);
fread(Def, 1, nDefLen, fp); // Read in deflated block
nRet = BurnStateDecompress(Def, nDefLen, bAll); // Decompress block into driver
free(Def); // free deflated block
fseek(fp, nChunkData + nChunkSize, SEEK_SET);
if (nRet == 0) { // Force the palette to recalculate on state load
BurnRecalcPal();
}
if (nRet) {
return -1;
} else {
return 0;
}
}
// State load
INT32 BurnStateLoad(TCHAR* szName, INT32 bAll, INT32 (*pLoadGame)())
{
const char szHeader[] = "FB1 "; // File identifier
char szReadHeader[4] = "";
INT32 nRet = 0;
FILE* fp = _tfopen(szName, _T("rb"));
if (fp == NULL) {
return 1;
}
fread(szReadHeader, 1, 4, fp); // Read identifier
if (memcmp(szReadHeader, szHeader, 4) == 0) { // Check filetype
nRet = BurnStateLoadEmbed(fp, -1, bAll, pLoadGame);
}
// load movie extra info
if(nReplayStatus)
{
const char szMovieExtra[] = "MOV ";
const char szDecodeChunk[] = "HUFF";
const char szInputChunk[] = "INP ";
INT32 nChunkSize;
UINT8* buf=NULL;
do
{
fread(szReadHeader, 1, 4, fp);
if(memcmp(szReadHeader, szMovieExtra, 4)) { nRet = -1; break; }
fread(&nChunkSize, 1, 4, fp);
fread(szReadHeader, 1, 4, fp);
if(memcmp(szReadHeader, szDecodeChunk, 4)) { nRet = -1; break; }
fread(&nChunkSize, 1, 4, fp);
if((buf=(UINT8*)malloc(nChunkSize))==NULL) { nRet = -1; break; }
fread(buf, 1, nChunkSize, fp);
INT32 ret=-1;
if(nReplayStatus == 1)
{
ret = UnfreezeEncode(buf, nChunkSize);
++nReplayUndoCount;
}
else if(nReplayStatus == 2)
{
ret = UnfreezeDecode(buf, nChunkSize);
}
if(ret) { nRet = -1; break; }
free(buf);
buf = NULL;
fread(szReadHeader, 1, 4, fp);
if(memcmp(szReadHeader, szInputChunk, 4)) { nRet = -1; break; }
fread(&nChunkSize, 1, 4, fp);
if((buf=(UINT8*)malloc(nChunkSize))==NULL) { nRet = -1; break; }
fread(buf, 1, nChunkSize, fp);
if(UnfreezeInput(buf, nChunkSize)) { nRet = -1; break; }
free(buf);
buf = NULL;
}
while(false);
if(buf) free(buf);
}
fclose(fp);
if (nRet < 0) {
return -nRet;
} else {
return 0;
}
}
// Write a savestate as a chunk of an "FB1 " file
// nOffset is the absolute offset from the beginning of the file
// -1: Append at current position
// -2: Append at EOF
INT32 BurnStateSaveEmbed(FILE* fp, INT32 nOffset, INT32 bAll)
{
const char* szHeader = "FS1 "; // Chunk identifier
INT32 nLen = 0;
INT32 nNvMin = 0, nAMin = 0;
INT32 nZero = 0;
char szGame[33];
UINT8 *Def = NULL;
INT32 nDefLen = 0; // Deflated version
INT32 nRet = 0;
if (fp == NULL) {
return -1;
}
StateInfo(&nLen, &nNvMin, 0); // Get minimum version for NV part
nAMin = nNvMin;
if (bAll) { // Get minimum version for All data
StateInfo(&nLen, &nAMin, 1);
}
if (nLen <= 0) { // No memory to save
return -1;
}
if (nOffset >= 0) {
fseek(fp, nOffset, SEEK_SET);
} else {
if (nOffset == -2) {
fseek(fp, 0, SEEK_END);
} else {
fseek(fp, 0, SEEK_CUR);
}
}
fwrite(szHeader, 1, 4, fp); // Chunk identifier
INT32 nSizeOffset = ftell(fp); // Reserve space to write the size of this chunk
fwrite(&nZero, 1, 4, fp); //
fwrite(&nBurnVer, 1, 4, fp); // Version of FB this was saved from
fwrite(&nNvMin, 1, 4, fp); // Min version of FB NV data will work with
fwrite(&nAMin, 1, 4, fp); // Min version of FB All data will work with
fwrite(&nZero, 1, 4, fp); // Reserve space to write the compressed data size
memset(szGame, 0, sizeof(szGame)); // Game name
sprintf(szGame, "%.32s", BurnDrvGetTextA(DRV_NAME)); //
fwrite(szGame, 1, 32, fp); //
nReplayCurrentFrame = GetCurrentFrame() - nStartFrame;
fwrite(&nReplayCurrentFrame, 1, 4, fp); // Current frame
fwrite(&nZero, 1, 4, fp); // Reserved
fwrite(&nZero, 1, 4, fp); //
fwrite(&nZero, 1, 4, fp); //
nRet = BurnStateCompress(&Def, &nDefLen, bAll); // Compress block from driver and return deflated buffer
if (Def == NULL) {
return -1;
}
nRet = fwrite(Def, 1, nDefLen, fp); // Write block to disk
free(Def); // free deflated block and close file
if (nRet != nDefLen) { // error writing block to disk
return -1;
}
if (nDefLen & 3) { // Chunk size must be a multiple of 4
fwrite(&nZero, 1, 4 - (nDefLen & 3), fp); // Pad chunk if needed
}
fseek(fp, nSizeOffset + 0x10, SEEK_SET); // Write size of the compressed data
fwrite(&nDefLen, 1, 4, fp); //
nDefLen = (nDefLen + 0x43) & ~3; // Add for header size and align
fseek(fp, nSizeOffset, SEEK_SET); // Write size of the chunk
fwrite(&nDefLen, 1, 4, fp); //
fseek (fp, 0, SEEK_END); // Set file pointer to the end of the chunk
return nDefLen;
}
#ifdef BUILD_WIN32
INT32 FileExists(const TCHAR *fileName)
{
DWORD dwAttrib = GetFileAttributes(fileName);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
#endif
#define MAX_STATEBACKUPS 10
// SaveState Undo - restores the last savestate backup file. Windows-only at the moment.
INT32 BurnStateUNDO(TCHAR* szName)
{
#ifdef BUILD_WIN32
/*
Savestate Undo
derp.fs.backup0 -> derp.fs
derp.fs.backup1 -> derp.fs.backup0
derp.fs.backup2 -> derpfs.backup1
derp.fs.backup3 -> derpfs.backup2
*/
INT32 cantundo = 0;
for (INT32 i = 0; i <= MAX_STATEBACKUPS; i++) {
TCHAR szBackupNameTo[1024] = _T("");
TCHAR szBackupNameFrom[1024] = _T("");
TCHAR szBackupNamePrev[1024] = _T("");
if (i == 0) {
_stprintf(szBackupNameTo, _T("%s.UNDO"), szName);// game.fs -> game.fs.UNDO
_stprintf(szBackupNamePrev, _T("%s.backup0"), szName);
if (FileExists(szName) && FileExists(szBackupNamePrev)) {
DeleteFileW(szBackupNameTo);
MoveFileW(szName, szBackupNameTo);
} else {
cantundo = 1;
}
_stprintf(szBackupNameTo, _T("%s"), szName);// game.fs
} else {
_stprintf(szBackupNameTo, _T("%s.backup%d"), szName, i - 1); //game.fs.backup0
}
_stprintf(szBackupNameFrom, _T("%s.backup%d"), szName, i); //game.fs.backup1
//bprintf(0, _T("%d: %s -> %s\n"), i, szBackupNameFrom, szBackupNameTo);
MoveFileW(szBackupNameFrom, szBackupNameTo);
}
return cantundo;
#else
return 0;
#endif
}
// State save
INT32 BurnStateSave(TCHAR* szName, INT32 bAll)
{
const char szHeader[] = "FB1 "; // File identifier
INT32 nLen = 0, nVer = 0;
INT32 nRet = 0;
if (bAll) { // Get amount of data
StateInfo(&nLen, &nVer, 1);
} else {
StateInfo(&nLen, &nVer, 0);
}
if (nLen <= 0) { // No data, so exit without creating a savestate
return 0; // Don't return an error code
}
#ifdef BUILD_WIN32
/*
Save State backups - used in conjunction with BurnStateUNDO();
derp.fs -> derp.fs.backup
derp.fs.backup -> derp.fs.backup1
derp.fs.backup1 -> derpfs.backup2
derp.fs.backup3 -> derpfs.backup4
*/
if (_tcsstr(szName, _T(" slot "))) {
for (INT32 i=MAX_STATEBACKUPS;i>=0;i--) {
TCHAR szBackupNameTo[1024] = _T("");
TCHAR szBackupNameFrom[1024] = _T("");
_stprintf(szBackupNameTo, _T("%s.backup%d"), szName, i + 1);
_stprintf(szBackupNameFrom, _T("%s.backup%d"), szName, i);
if (i == MAX_STATEBACKUPS) {
DeleteFileW(szBackupNameFrom); // make sure there is only MAX_STATEBACKUPS :)
} else {
MoveFileW(szBackupNameFrom, szBackupNameTo); //derp.fs.backup0 -> derp.fs.backup1
if (i == 0) {
MoveFileW(szName, szBackupNameFrom); //derp.fs -> derp.fs.backup0
}
}
}
}
#endif
FILE* fp = _tfopen(szName, _T("wb"));
if (fp == NULL) {
return 1;
}
fwrite(&szHeader, 1, 4, fp);
nRet = BurnStateSaveEmbed(fp, -1, bAll);
// save movie extra info
if(nReplayStatus)
{
UINT8* huff_buf = NULL;
INT32 huff_size;
UINT8* input_buf = NULL;
INT32 input_size;
INT32 ret=-1;
if(nReplayStatus == 1)
{
ret = FreezeEncode(&huff_buf, &huff_size);
}
else if(nReplayStatus == 2)
{
ret = FreezeDecode(&huff_buf, &huff_size);
}
if(!ret &&
!FreezeInput(&input_buf, &input_size))
{
const char szMovieExtra[] = "MOV ";
const char szDecodeChunk[] = "HUFF";
const char szInputChunk[] = "INP ";
INT32 nZero = 0;
INT32 nAlign = 0;
INT32 nChkLen = 0;
INT32 nMovChunkLen = 0;
fwrite(szMovieExtra, 1, 4, fp);
INT32 nSizeOffset = ftell(fp);
fwrite(&nZero, 1, 4, fp); // Leave room for the chunk size
nMovChunkLen = 0;
// write Decode block
nAlign = (huff_size&3) ? (4 - (huff_size&3)) : 0;
nChkLen = huff_size + nAlign;
fwrite(szDecodeChunk, 1, 4, fp);
fwrite(&nChkLen, 1, 4, fp);
fwrite(huff_buf, 1, huff_size, fp);
if(nAlign)
{
fwrite(&nZero, 1, nAlign, fp);
}
nMovChunkLen += nChkLen + 8;
// write Input block
nAlign = (input_size&3) ? (4 - (input_size&3)) : 0;
nChkLen = input_size + nAlign;
fwrite(szInputChunk, 1, 4, fp);
fwrite(&nChkLen, 1, 4, fp);
fwrite(input_buf, 1, input_size, fp);
if(nAlign)
{
fwrite(&nZero, 1, nAlign, fp);
}
nMovChunkLen += nChkLen + 8;
fseek(fp, nSizeOffset, SEEK_SET);
fwrite(&nMovChunkLen, 1, 4, fp);
fseek(fp, nMovChunkLen, SEEK_CUR);
}
if(huff_buf) free(huff_buf);
if(input_buf) free(input_buf);
}
fclose(fp);
if (nRet < 0) {
return 1;
} else {
return 0;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment