BizHawk/waterbox/melon/BizPlatform/BizSaveManager.cpp

153 lines
4.0 KiB
C++

#include "NDS.h"
#include "NDSCart.h"
#include "GBACart.h"
#include "DSi.h"
#include "DSi_NAND.h"
#include "Platform.h"
namespace melonDS::Platform
{
constexpr u32 DSIWARE_CATEGORY = 0x00030004;
static bool NdsSaveRamIsDirty = false;
static bool GbaSaveRamIsDirty = false;
ECL_EXPORT void PutSaveRam(melonDS::NDS* nds, u8* data, u32 len)
{
const u32 ndsSaveLen = nds->GetNDSSaveLength();
const u32 gbaSaveLen = nds->GetGBASaveLength();
if (len >= ndsSaveLen)
{
nds->SetNDSSave(data, len);
NdsSaveRamIsDirty = false;
if (gbaSaveLen && len >= (ndsSaveLen + gbaSaveLen))
{
// don't use SetGBASave! it will re-allocate the save buffer (bad!)
// SetNDSSave is fine (and should be used)
memcpy(nds->GetGBASave(), data + ndsSaveLen, gbaSaveLen);
GbaSaveRamIsDirty = false;
}
}
}
ECL_EXPORT void GetSaveRam(melonDS::NDS* nds, u8* data)
{
const u32 ndsSaveLen = nds->GetNDSSaveLength();
const u32 gbaSaveLen = nds->GetGBASaveLength();
if (ndsSaveLen)
{
memcpy(data, nds->GetNDSSave(), ndsSaveLen);
NdsSaveRamIsDirty = false;
}
if (gbaSaveLen)
{
memcpy(data + ndsSaveLen, nds->GetGBASave(), gbaSaveLen);
GbaSaveRamIsDirty = false;
}
}
ECL_EXPORT u32 GetSaveRamLength(melonDS::NDS* nds)
{
return nds->GetNDSSaveLength() + nds->GetGBASaveLength();
}
ECL_EXPORT bool SaveRamIsDirty()
{
return NdsSaveRamIsDirty || GbaSaveRamIsDirty;
}
ECL_EXPORT void ImportDSiWareSavs(melonDS::DSi* dsi, u32 titleId)
{
if (auto& nand = dsi->GetNAND())
{
if (auto mount = melonDS::DSi_NAND::NANDMount(nand))
{
mount.ImportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PublicSav, "public.sav");
mount.ImportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PrivateSav, "private.sav");
mount.ImportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_BannerSav, "banner.sav");
}
}
}
ECL_EXPORT void ExportDSiWareSavs(melonDS::DSi* dsi, u32 titleId)
{
if (auto& nand = dsi->GetNAND())
{
if (auto mount = melonDS::DSi_NAND::NANDMount(nand))
{
mount.ExportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PublicSav, "public.sav");
mount.ExportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PrivateSav, "private.sav");
mount.ExportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_BannerSav, "banner.sav");
}
}
}
ECL_EXPORT void DSiWareSavsLength(melonDS::DSi* dsi, u32 titleId, u32* publicSavSize, u32* privateSavSize, u32* bannerSavSize)
{
*publicSavSize = *privateSavSize = *bannerSavSize = 0;
if (auto& nand = dsi->GetNAND())
{
if (auto mount = melonDS::DSi_NAND::NANDMount(nand))
{
u32 version;
melonDS::NDSHeader header{};
mount.GetTitleInfo(DSIWARE_CATEGORY, titleId, version, &header, nullptr);
*publicSavSize = header.DSiPublicSavSize;
*privateSavSize = header.DSiPrivateSavSize;
*bannerSavSize = (header.AppFlags & 0x04) ? 0x4000 : 0;
}
}
}
// TODO - I don't like this approach with NAND
// Perhaps instead it would be better to use FileFlush to write to disk
// (guarded by frontend determinism switch, of course)
ECL_EXPORT u32 GetNANDSize(melonDS::DSi* dsi)
{
if (auto& nand = dsi->GetNAND())
{
return nand.GetLength();
}
return 0;
}
ECL_EXPORT void GetNANDData(melonDS::DSi* dsi, u8* buf)
{
if (auto& nand = dsi->GetNAND())
{
auto len = nand.GetLength();
auto file = nand.GetFile();
melonDS::Platform::FileRewind(file);
melonDS::Platform::FileRead(buf, 1, len, file);
}
}
void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen)
{
NdsSaveRamIsDirty = true;
}
void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen)
{
GbaSaveRamIsDirty = true;
}
void WriteFirmware(const melonDS::Firmware& firmware, u32 writeoffset, u32 writelen)
{
}
void WriteDateTime(int year, int month, int day, int hour, int minute, int second)
{
}
}