mirror of
https://github.com/coderkei/akmenu-next.git
synced 2025-06-19 01:15:32 -04:00

- UTF8-ify where possible - Remove custom linkerscript - Update Makefiles - devkitPro/nds-examples@6afa09b205 - Comment out akloader binaries - This will be reworked soon™️ - Eradicate sdidentify - AKRPG specific - Eradicate libelm - Eradicate save64m - Eradicate file operations - Eradicate libio* - Eradicate crtsmall - Fix paths for new root drive naming in latest libfat - dsrom: fix type cast issue in homebrew check - MAX_FILENAME_LENGTH -> PATH_MAX - adapt directory listing operations to new dkP way - timer: unstaticify _factor - Remove all flashcart-specific bits - fix type of cPopMenu::itemBelowPoint - gbaloader: use updated vramSetPrimaryBanks function - Move arm9-specific headers to arm9
366 lines
8.8 KiB
C++
366 lines
8.8 KiB
C++
/*
|
||
savemngr.cpp
|
||
Copyright (C) 2007 Acekard, www.acekard.com
|
||
Copyright (C) 2007-2009 somebody
|
||
Copyright (C) 2009 yellow wood goblin
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
//<2F>
|
||
|
||
#include "savemngr.h"
|
||
#include "inifile.h"
|
||
#include "dsrom.h"
|
||
#include "dbgtool.h"
|
||
#include "systemfilenames.h"
|
||
#include "progresswnd.h"
|
||
#include "language.h"
|
||
#include "datetime.h"
|
||
#include "ui.h"
|
||
#include <fat.h>
|
||
#include <fcntl.h>
|
||
#include <unistd.h>
|
||
|
||
using namespace akui;
|
||
|
||
cSaveManager::cSaveManager()
|
||
{
|
||
}
|
||
|
||
cSaveManager::~cSaveManager()
|
||
{
|
||
}
|
||
|
||
static bool loadSaveList(const std::string& filename,std::vector<SAVE_INFO_EX>& buffer)
|
||
{
|
||
u8* data=NULL;
|
||
FILE* f=fopen(filename.c_str(),"rb");
|
||
if(NULL==f)
|
||
{
|
||
dbg_printf("fopen %s fail\n",filename.c_str());
|
||
return false;
|
||
}
|
||
bool res=false;
|
||
do
|
||
{
|
||
u32 saveCount=0;
|
||
SAVE_INFO_EX_HEADER header={SAVE_INFO_EX_HEADER_MAGIC,sizeof(SAVE_INFO),saveCount,0};
|
||
if(1!=fread(&header,sizeof(header),1,f)) break;
|
||
if(header.marker!=SAVE_INFO_EX_HEADER_MAGIC) break;
|
||
data=new u8[header.itemSize*header.itemCount];
|
||
u32 copiedSize=std::min((unsigned int)header.itemSize,sizeof(SAVE_INFO_EX));
|
||
if(header.itemCount!=fread(data,header.itemSize,header.itemCount,f)) break;
|
||
buffer.resize(header.itemCount);
|
||
for(u32 ii=0;ii<header.itemCount;ii++)
|
||
{
|
||
memset(&buffer[ii],0,sizeof(SAVE_INFO_EX));
|
||
memcpy(&buffer[ii],data+ii*header.itemSize,copiedSize);
|
||
}
|
||
res=true;
|
||
} while(false);
|
||
fclose(f);
|
||
delete[] data;
|
||
return res;
|
||
}
|
||
|
||
static bool loadOfficialSaveList(const std::string& filename,std::vector<SAVE_INFO>& buffer)
|
||
{
|
||
FILE* f=fopen(filename.c_str(),"rb");
|
||
if(NULL==f)
|
||
{
|
||
dbg_printf("fopen %s fail\n",filename.c_str());
|
||
return false;
|
||
}
|
||
fseek(f,0,SEEK_END);
|
||
u32 filesize=ftell(f);
|
||
if(0==filesize||0!=(filesize%19))
|
||
{
|
||
dbg_printf("%s size error %d\n", filename.c_str(), filesize );
|
||
fclose(f);
|
||
f=NULL;
|
||
return false;
|
||
}
|
||
|
||
u32 saveCount=filesize/19;
|
||
buffer.resize(saveCount);
|
||
|
||
fseek(f,0,SEEK_SET);
|
||
u32 readed=fread(&buffer[0],1,filesize,f);
|
||
if(filesize!=readed)
|
||
{
|
||
dbg_printf("%s read length error %d\n",filename.c_str(),readed);
|
||
fclose(f);
|
||
f=NULL;
|
||
return false;
|
||
}
|
||
fclose(f);
|
||
return true;
|
||
}
|
||
|
||
bool cSaveManager::importSaveList( const std::string & customFile, const std::string & officialFile )
|
||
{
|
||
loadSaveList(customFile,_customSaveList);
|
||
loadOfficialSaveList(officialFile,_saveList);
|
||
return true;
|
||
}
|
||
|
||
bool cSaveManager::exportCustomSaveList(const std::string& filename)
|
||
{
|
||
int f=open(filename.c_str(),O_WRONLY|O_CREAT|O_TRUNC);
|
||
if(f<0)
|
||
{
|
||
dbg_printf("fopen %s fail\n",filename.c_str());
|
||
return false;
|
||
}
|
||
bool res=false;
|
||
do
|
||
{
|
||
u32 count=_customSaveList.size();
|
||
SAVE_INFO_EX_HEADER header={SAVE_INFO_EX_HEADER_MAGIC,sizeof(SAVE_INFO_EX),count,0};
|
||
//libelm hack
|
||
lseek(f,sizeof(header)+sizeof(SAVE_INFO_EX)*count,SEEK_SET);
|
||
lseek(f,0,SEEK_SET);
|
||
//
|
||
ssize_t written=write(f,&header,sizeof(header));
|
||
if(written!=sizeof(header))
|
||
{
|
||
dbg_printf("%s write length error %d\n",filename.c_str(),written);
|
||
break;
|
||
}
|
||
written=write(f,&_customSaveList[0],sizeof(SAVE_INFO_EX)*count);
|
||
if(static_cast<ssize_t>(sizeof(SAVE_INFO_EX)*count)!=written)
|
||
{
|
||
dbg_printf("%s write length error %d\n",filename.c_str(),written);
|
||
break;
|
||
}
|
||
res=true;
|
||
} while(false);
|
||
close(f);
|
||
return res;
|
||
}
|
||
|
||
void cSaveManager::updateCustomSaveList( const SAVE_INFO_EX & aSaveInfo )
|
||
{
|
||
size_t i = 0;
|
||
for( i = 0; i < _customSaveList.size(); ++i )
|
||
{
|
||
if( 0 == memcmp( &_customSaveList[i], &aSaveInfo, SAVE_INFO_EX_COMPARE_SIZE ) ) {
|
||
_customSaveList[i] = aSaveInfo;
|
||
break;
|
||
}
|
||
}
|
||
if( i == _customSaveList.size() )
|
||
_customSaveList.push_back( aSaveInfo );
|
||
|
||
exportCustomSaveList( SFN_CUSTOM_SAVELIST );
|
||
}
|
||
|
||
bool cSaveManager::saveLastInfo( const std::string & romFilename )
|
||
{
|
||
CIniFile f;
|
||
f.SetString( "Save Info", "lastLoaded", romFilename );
|
||
if( !f.SaveIniFile( SFN_LAST_SAVEINFO ) )
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool cSaveManager::loadLastInfo( std::string & lastLoadedFilename )
|
||
{
|
||
CIniFile f;
|
||
if( !f.LoadIniFile( SFN_LAST_SAVEINFO ) ) {
|
||
lastLoadedFilename = "";
|
||
return false;
|
||
}
|
||
|
||
lastLoadedFilename = f.GetString( "Save Info", "lastLoaded", "" );
|
||
if( "" == lastLoadedFilename )
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool cSaveManager::clearLastInfo()
|
||
{
|
||
std::string loadLoadedFile;
|
||
if(loadLastInfo(loadLoadedFile))
|
||
{
|
||
return saveLastInfo("");
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool cSaveManager::initializeSaveFile(const std::string& romFilename,u8 slot,u32 size)
|
||
{
|
||
bool res=false;
|
||
std::string saveFilename=generateSaveName(romFilename,slot);
|
||
int f=open(saveFilename.c_str(),O_WRONLY|O_CREAT);
|
||
if(f>=0)
|
||
{
|
||
off_t filesize=lseek(f,0,SEEK_END);
|
||
if(filesize>=0)
|
||
{
|
||
if((u32)filesize>=size)
|
||
{
|
||
write(f,NULL,0); //update time stamp
|
||
res=true;
|
||
}
|
||
else
|
||
{
|
||
progressWnd().setTipText(LANG("progress window","processing save"));
|
||
progressWnd().show();
|
||
progressWnd().setPercent(0);
|
||
u32 tail=size-filesize;
|
||
const u32 bufferSize=128*1024; u32 ii=0;
|
||
u8* buffer=new u8[bufferSize];
|
||
memset(buffer,0,bufferSize);
|
||
res=true;
|
||
while(tail>0)
|
||
{
|
||
ssize_t writtenSize=(tail>bufferSize)?bufferSize:tail;
|
||
if(write(f,buffer,writtenSize)!=writtenSize)
|
||
{
|
||
res=false;
|
||
break;
|
||
}
|
||
tail-=writtenSize; ii++;
|
||
if(0==ii%8) progressWnd().setPercent((lseek(f,0,SEEK_END))*100/size);
|
||
}
|
||
progressWnd().setPercent(100);
|
||
progressWnd().hide();
|
||
}
|
||
}
|
||
close(f);
|
||
}
|
||
return res;
|
||
}
|
||
|
||
std::string cSaveManager::generateSaveName(const std::string& romFilename,u8 slot)
|
||
{
|
||
std::string saveFilename(romFilename);
|
||
if(gs().saveExt&&saveFilename.length()>3)
|
||
{
|
||
size_t nameLength=saveFilename.length()-4;
|
||
if(saveFilename[nameLength]=='.') saveFilename=saveFilename.substr(0,nameLength);
|
||
}
|
||
if(slot)
|
||
{
|
||
saveFilename+=".";
|
||
saveFilename+=(slot+'0');
|
||
}
|
||
saveFilename+=".sav";
|
||
return saveFilename;
|
||
}
|
||
|
||
SAVE_TYPE cSaveManager::getSaveTypeByFile( const std::string & romFilename )
|
||
{
|
||
return ST_UNKNOWN;
|
||
}
|
||
|
||
void cSaveManager::updateSaveInfoByInfo( SAVE_INFO_EX & gameInfo )
|
||
{
|
||
size_t saveCount = _customSaveList.size();
|
||
for( size_t i = 0; i < saveCount; ++i )
|
||
{
|
||
if( 0 == memcmp( &gameInfo, &_customSaveList[i], SAVE_INFO_EX_COMPARE_SIZE ) )
|
||
{
|
||
gameInfo=_customSaveList[i];
|
||
return;
|
||
}
|
||
}
|
||
|
||
gameInfo.defaults();
|
||
saveCount=_saveList.size();
|
||
for(size_t ii=0;ii<saveCount;ii++)
|
||
{
|
||
if(0==memcmp(&gameInfo,&_saveList[ii],SAVE_INFO_EX_COMPARE_SIZE))
|
||
{
|
||
u8 saveType=_saveList[ii].saveType;
|
||
if(saveType>ST_UNKNOWN&&saveType<=ST_8M) gameInfo.saveType=_saveList[ii].saveType;
|
||
return;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
DISPLAY_SAVE_TYPE cSaveManager::SaveTypeToDisplaySaveType(SAVE_TYPE aSaveType)
|
||
{
|
||
switch(aSaveType)
|
||
{
|
||
case ST_UNKNOWN:
|
||
return D_ST_UNKNOWN;
|
||
case ST_NOSAVE:
|
||
return D_ST_NOSAVE;
|
||
case ST_4K:
|
||
return D_ST_4K;
|
||
case ST_64K:
|
||
return D_ST_64K;
|
||
case ST_512K:
|
||
return D_ST_512K;
|
||
case ST_2M:
|
||
return D_ST_2M;
|
||
case ST_4M:
|
||
return D_ST_4M;
|
||
case ST_8M:
|
||
return D_ST_8M;
|
||
case ST_NEW:
|
||
return D_ST_UNKNOWN;
|
||
case ST_AUTO:
|
||
return D_ST_UNKNOWN;
|
||
case ST_1M:
|
||
return D_ST_1M;
|
||
case ST_16M:
|
||
return D_ST_16M;
|
||
case ST_32M:
|
||
return D_ST_32M;
|
||
case ST_64M:
|
||
return D_ST_64M;
|
||
default:
|
||
return D_ST_UNKNOWN;
|
||
}
|
||
}
|
||
|
||
SAVE_TYPE cSaveManager::DisplaySaveTypeToSaveType(DISPLAY_SAVE_TYPE aSaveType)
|
||
{
|
||
switch(aSaveType)
|
||
{
|
||
case D_ST_UNKNOWN:
|
||
return ST_UNKNOWN;
|
||
case D_ST_NOSAVE:
|
||
return ST_NOSAVE;
|
||
case D_ST_4K:
|
||
return ST_4K;
|
||
case D_ST_64K:
|
||
return ST_64K;
|
||
case D_ST_512K:
|
||
return ST_512K;
|
||
case D_ST_1M:
|
||
return ST_1M;
|
||
case D_ST_2M:
|
||
return ST_2M;
|
||
case D_ST_4M:
|
||
return ST_4M;
|
||
case D_ST_8M:
|
||
return ST_8M;
|
||
case D_ST_16M:
|
||
return ST_16M;
|
||
case D_ST_32M:
|
||
return ST_32M;
|
||
case D_ST_64M:
|
||
return ST_64M;
|
||
}
|
||
return ST_UNKNOWN;
|
||
}
|