flycast/core/nullDC.cpp

316 lines
6.9 KiB
C++

#ifndef LIBRETRO
#include "types.h"
#include "emulator.h"
#include "hw/mem/addrspace.h"
#include "cfg/cfg.h"
#include "cfg/option.h"
#include "log/LogManager.h"
#include "ui/gui.h"
#include "oslib/oslib.h"
#include "oslib/directory.h"
#include "debug/gdb_server.h"
#include "archive/rzip.h"
#include "ui/mainui.h"
#include "input/gamepad_device.h"
#include "lua/lua.h"
#include "stdclass.h"
#include "serialize.h"
#include <time.h>
struct SavestateHeader
{
void init()
{
memcpy(magic, MAGIC, sizeof(magic));
creationDate = time(nullptr);
version = Deserializer::Current;
pngSize = 0;
}
bool isValid() const {
return !memcmp(magic, MAGIC, sizeof(magic));
}
char magic[8];
u64 creationDate;
u32 version;
u32 pngSize;
// png data
static constexpr const char *MAGIC = "FLYSAVE1";
};
int flycast_init(int argc, char* argv[])
{
#if defined(TEST_AUTOMATION)
setbuf(stdout, 0);
setbuf(stderr, 0);
settings.aica.muteAudio = true;
#endif
if (!addrspace::reserve())
{
ERROR_LOG(VMEM, "Failed to alloc mem");
return -1;
}
ParseCommandLine(argc, argv);
if (cfgLoadInt("naomi", "BoardId", 0) != 0)
{
settings.naomi.multiboard = true;
settings.naomi.slave = true;
}
settings.naomi.drivingSimSlave = cfgLoadInt("naomi", "DrivingSimSlave", 0);
config::Settings::instance().reset();
LogManager::Shutdown();
if (!cfgOpen())
{
LogManager::Init();
NOTICE_LOG(BOOT, "Config directory is not set. Starting onboarding");
gui_open_onboarding();
}
else
{
LogManager::Init();
config::Settings::instance().load(false);
}
gui_init();
os_CreateWindow();
os_SetupInput();
if(config::GDB)
debugger::init(config::GDBPort);
lua::init();
if(config::ProfilerEnabled)
LogManager::GetInstance()->SetEnable(LogTypes::PROFILER, true);
return 0;
}
void dc_exit()
{
try {
emu.unloadGame();
} catch (...) { }
mainui_stop();
}
void SaveSettings()
{
config::Settings::instance().save();
GamepadDevice::SaveMaplePorts();
#ifdef __ANDROID__
void SaveAndroidSettings();
SaveAndroidSettings();
#endif
}
void flycast_term()
{
gui_cancel_load();
lua::term();
emu.term();
os_DestroyWindow();
gui_term();
os_TermInput();
}
void dc_savestate(int index, const u8 *pngData, u32 pngSize)
{
if (settings.network.online)
return;
Serializer ser;
dc_serialize(ser);
void *data = malloc(ser.size());
if (data == nullptr)
{
WARN_LOG(SAVESTATE, "Failed to save state - could not malloc %d bytes", (int)ser.size());
os_notify("Save state failed - memory full", 5000);
return;
}
ser = Serializer(data, ser.size());
dc_serialize(ser);
std::string filename = hostfs::getSavestatePath(index, true);
FILE *f = nowide::fopen(filename.c_str(), "wb");
if (f == nullptr)
{
WARN_LOG(SAVESTATE, "Failed to save state - could not open %s for writing", filename.c_str());
os_notify("Cannot open save file", 5000);
free(data);
return;
}
RZipFile zipFile;
SavestateHeader header;
header.init();
header.pngSize = pngSize;
if (std::fwrite(&header, sizeof(header), 1, f) != 1)
goto fail;
if (pngSize > 0 && std::fwrite(pngData, 1, pngSize, f) != pngSize)
goto fail;
#if 0
// Uncompressed savestate
std::fwrite(data, 1, ser.size(), f);
std::fclose(f);
#else
if (!zipFile.Open(f, true))
goto fail;
if (zipFile.Write(data, ser.size()) != ser.size())
goto fail;
zipFile.Close();
#endif
free(data);
NOTICE_LOG(SAVESTATE, "Saved state to %s size %d", filename.c_str(), (int)ser.size());
os_notify("State saved", 2000);
return;
fail:
WARN_LOG(SAVESTATE, "Failed to save state - error writing %s", filename.c_str());
os_notify("Error saving state", 5000);
if (zipFile.rawFile() != nullptr)
zipFile.Close();
else
std::fclose(f);
free(data);
// delete failed savestate?
}
void dc_loadstate(int index)
{
if (settings.raHardcoreMode)
return;
u32 total_size = 0;
std::string filename = hostfs::getSavestatePath(index, false);
FILE *f = nowide::fopen(filename.c_str(), "rb");
if (f == nullptr)
{
WARN_LOG(SAVESTATE, "Failed to load state - could not open %s for reading", filename.c_str());
os_notify("Save state not found", 2000);
return;
}
SavestateHeader header;
if (std::fread(&header, sizeof(header), 1, f) == 1)
{
if (!header.isValid())
// seek to beginning of file if this isn't a valid header (legacy savestate)
std::fseek(f, 0, SEEK_SET);
else
// skip png data
std::fseek(f, header.pngSize, SEEK_CUR);
}
else {
// probably not a valid savestate but we'll fail later
std::fseek(f, 0, SEEK_SET);
}
if (index == -1 && config::GGPOEnable)
{
long pos = std::ftell(f);
MD5Sum().add(f)
.getDigest(settings.network.md5.savestate);
std::fseek(f, pos, SEEK_SET);
}
RZipFile zipFile;
if (zipFile.Open(f, false)) {
total_size = (u32)zipFile.Size();
}
else
{
long pos = std::ftell(f);
std::fseek(f, 0, SEEK_END);
total_size = (u32)std::ftell(f) - pos;
std::fseek(f, pos, SEEK_SET);
}
void *data = malloc(total_size);
if (data == nullptr)
{
WARN_LOG(SAVESTATE, "Failed to load state - could not malloc %d bytes", total_size);
os_notify("Failed to load state - memory full", 5000);
if (zipFile.rawFile() == nullptr)
std::fclose(f);
else
zipFile.Close();
return;
}
size_t read_size;
if (zipFile.rawFile() != nullptr)
{
read_size = zipFile.Read(data, total_size);
zipFile.Close();
}
else
{
read_size = std::fread(data, 1, total_size, f);
std::fclose(f);
}
if (read_size != total_size)
{
WARN_LOG(SAVESTATE, "Failed to load state - I/O error");
os_notify("Failed to load state - I/O error", 5000);
free(data);
return;
}
try {
Deserializer deser(data, total_size);
dc_loadstate(deser);
NOTICE_LOG(SAVESTATE, "Loaded state ver %d from %s size %d", deser.version(), filename.c_str(), total_size);
if (deser.size() != total_size)
// Note: this isn't true for RA savestates
WARN_LOG(SAVESTATE, "Savestate size %d but only %d bytes used", total_size, (int)deser.size());
} catch (const Deserializer::Exception& e) {
ERROR_LOG(SAVESTATE, "%s", e.what());
}
free(data);
EventManager::event(Event::LoadState);
}
time_t dc_getStateCreationDate(int index)
{
std::string filename = hostfs::getSavestatePath(index, false);
FILE *f = nowide::fopen(filename.c_str(), "rb");
if (f == nullptr)
return 0;
SavestateHeader header;
if (std::fread(&header, sizeof(header), 1, f) != 1 || !header.isValid())
{
std::fclose(f);
struct stat st;
if (flycast::stat(filename.c_str(), &st) == 0)
return st.st_mtime;
else
return 0;
}
std::fclose(f);
return (time_t)header.creationDate;
}
void dc_getStateScreenshot(int index, std::vector<u8>& pngData)
{
pngData.clear();
std::string filename = hostfs::getSavestatePath(index, false);
FILE *f = nowide::fopen(filename.c_str(), "rb");
if (f == nullptr)
return;
SavestateHeader header;
if (std::fread(&header, sizeof(header), 1, f) == 1 && header.isValid() && header.pngSize != 0)
{
pngData.resize(header.pngSize);
if (std::fread(pngData.data(), 1, pngData.size(), f) != pngData.size())
pngData.clear();
}
std::fclose(f);
}
#endif