add more shit to EmuInstance

This commit is contained in:
Arisotura 2024-05-11 10:18:05 +02:00
parent faa6cfec48
commit b96f1bd734
5 changed files with 570 additions and 16 deletions

View File

@ -726,12 +726,13 @@ void Array::SetString(const int id, const std::string& val)
}
Table::Table() : Data()
/*Table::Table()// : Data(toml::value())
{
Data = toml::value();
PathPrefix = "";
}
}*/
Table::Table(toml::value& data, std::string path) : Data(data)
Table::Table(toml::value& data, const std::string& path) : Data(data)
{
if (path.empty())
PathPrefix = "";
@ -739,10 +740,12 @@ Table::Table(toml::value& data, std::string path) : Data(data)
PathPrefix = path + ".";
}
Table Table::operator=(Table b)
Table& Table::operator=(const Table& b)
{
Data = b.Data;
PathPrefix = b.PathPrefix;
return *this;
}
Array Table::GetArray(const std::string& path)

View File

@ -138,11 +138,11 @@ private:
class Table
{
public:
Table();
Table(toml::value& data, std::string path);
//Table();
Table(toml::value& data, const std::string& path);
~Table() {}
Table operator=(Table b);
Table& operator=(const Table& b);
Array GetArray(const std::string& path);
Table GetTable(const std::string& path);
@ -223,7 +223,7 @@ extern int FirmwareBirthdayDay;
extern int FirmwareFavouriteColour;
extern std::string FirmwareMessage;
extern std::string FirmwareMAC;
extern std::string WifiSettingsPath;
//extern std::string WifiSettingsPath;
extern int MPAudioMode;
extern int MPRecvTimeout;

View File

@ -59,14 +59,13 @@ using namespace melonDS::Platform;
MainWindow* topWindow = nullptr;
const string kWifiSettingsPath = "wfcsettings.bin";
EmuInstance::EmuInstance(int inst)
EmuInstance::EmuInstance(int inst) : instanceID(inst),
globalCfg(Config::GetGlobalTable()),
localCfg(Config::GetLocalTable(inst))
{
instanceID = inst;
globalCfg = Config::GetGlobalTable();
localCfg = Config::GetLocalTable(inst);
emuThread = new EmuThread();
numWindows = 0;
@ -360,3 +359,519 @@ QString EmuInstance::verifySetup()
return "";
}
std::string EmuInstance::getEffectiveFirmwareSavePath()
{
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{
return kWifiSettingsPath;
}
if (nds->ConsoleType == 1)
{
return globalCfg.GetString("DSi.FirmwarePath");
}
else
{
return globalCfg.GetString("DS.FirmwarePath");
}
}
// Initializes the firmware save manager with the selected firmware image's path
// OR the path to the wi-fi settings.
void EmuInstance::initFirmwareSaveManager() noexcept
{
firmwareSave = std::make_unique<SaveManager>(getEffectiveFirmwareSavePath());
}
std::string EmuInstance::getSavestateName(int slot)
{
std::string ext = ".ml";
ext += (char)('0'+slot);
return getAssetPath(false, Config::SavestatePath, ext);
}
bool EmuInstance::savestateExists(int slot)
{
std::string ssfile = getSavestateName(slot);
return Platform::FileExists(ssfile);
}
bool EmuInstance::loadState(const std::string& filename)
{
FILE* file = fopen(filename.c_str(), "rb");
if (file == nullptr)
{ // If we couldn't open the state file...
Platform::Log(Platform::LogLevel::Error, "Failed to open state file \"%s\"\n", filename.c_str());
return false;
}
std::unique_ptr<Savestate> backup = std::make_unique<Savestate>(Savestate::DEFAULT_SIZE);
if (backup->Error)
{ // If we couldn't allocate memory for the backup...
Platform::Log(Platform::LogLevel::Error, "Failed to allocate memory for state backup\n");
fclose(file);
return false;
}
if (!nds->DoSavestate(backup.get()) || backup->Error)
{ // Back up the emulator's state. If that failed...
Platform::Log(Platform::LogLevel::Error, "Failed to back up state, aborting load (from \"%s\")\n", filename.c_str());
fclose(file);
return false;
}
// We'll store the backup once we're sure that the state was loaded.
// Now that we know the file and backup are both good, let's load the new state.
// Get the size of the file that we opened
if (fseek(file, 0, SEEK_END) != 0)
{
Platform::Log(Platform::LogLevel::Error, "Failed to seek to end of state file \"%s\"\n", filename.c_str());
fclose(file);
return false;
}
size_t size = ftell(file);
rewind(file); // reset the filebuf's position
// Allocate exactly as much memory as we need for the savestate
std::vector<u8> buffer(size);
if (fread(buffer.data(), size, 1, file) == 0)
{ // Read the state file into the buffer. If that failed...
Platform::Log(Platform::LogLevel::Error, "Failed to read %u-byte state file \"%s\"\n", size, filename.c_str());
fclose(file);
return false;
}
fclose(file); // done with the file now
// Get ready to load the state from the buffer into the emulator
std::unique_ptr<Savestate> state = std::make_unique<Savestate>(buffer.data(), size, false);
if (!nds->DoSavestate(state.get()) || state->Error)
{ // If we couldn't load the savestate from the buffer...
Platform::Log(Platform::LogLevel::Error, "Failed to load state file \"%s\" into emulator\n", filename.c_str());
return false;
}
// The backup was made and the state was loaded, so we can store the backup now.
backupState = std::move(backup); // This will clean up any existing backup
assert(backup == nullptr);
if (Config::SavestateRelocSRAM && ndsSave)
{
previousSaveFile = ndsSave->GetPath();
std::string savefile = filename.substr(lastSep(filename)+1);
savefile = getAssetPath(false, Config::SaveFilePath, ".sav", savefile);
savefile += Platform::InstanceFileSuffix();
ndsSave->SetPath(savefile, true);
}
savestateLoaded = true;
return true;
}
bool EmuInstance::saveState(const std::string& filename)
{
FILE* file = fopen(filename.c_str(), "wb");
if (file == nullptr)
{ // If the file couldn't be opened...
return false;
}
Savestate state;
if (state.Error)
{ // If there was an error creating the state (and allocating its memory)...
fclose(file);
return false;
}
// Write the savestate to the in-memory buffer
nds->DoSavestate(&state);
if (state.Error)
{
fclose(file);
return false;
}
if (fwrite(state.Buffer(), state.Length(), 1, file) == 0)
{ // Write the Savestate buffer to the file. If that fails...
Platform::Log(Platform::Error,
"Failed to write %d-byte savestate to %s\n",
state.Length(),
filename.c_str()
);
fclose(file);
return false;
}
fclose(file);
if (Config::SavestateRelocSRAM && ndsSave)
{
std::string savefile = filename.substr(lastSep(filename)+1);
savefile = getAssetPath(false, Config::SaveFilePath, ".sav", savefile);
savefile += Platform::InstanceFileSuffix();
ndsSave->SetPath(savefile, false);
}
return true;
}
void EmuInstance::undoStateLoad()
{
if (!savestateLoaded || !backupState) return;
// Rewind the backup state and put it in load mode
backupState->Rewind(false);
// pray that this works
// what do we do if it doesn't???
// but it should work.
nds->DoSavestate(backupState.get());
if (ndsSave && (!previousSaveFile.empty()))
{
ndsSave->SetPath(previousSaveFile, true);
}
}
void EmuInstance::unloadCheats()
{
if (cheatFile)
{
delete cheatFile;
cheatFile = nullptr;
nds->AREngine.SetCodeFile(nullptr);
}
}
void EmuInstance::loadCheats()
{
unloadCheats();
std::string filename = getAssetPath(false, Config::CheatFilePath, ".mch");
// TODO: check for error (malformed cheat file, ...)
cheatFile = new ARCodeFile(filename);
nds->AREngine.SetCodeFile(cheatsOn ? cheatFile : nullptr);
}
std::optional<std::array<u8, ARM9BIOSSize>> EmuInstance::loadARM9BIOS() noexcept
{
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{
return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_optional(bios_arm9_bin) : std::nullopt;
}
string path = globalCfg.GetString("DS.BIOS9Path");
if (FileHandle* f = OpenLocalFile(path, Read))
{
std::array<u8, ARM9BIOSSize> bios {};
FileRewind(f);
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
Log(Info, "ARM9 BIOS loaded from %s\n", path.c_str());
return bios;
}
Log(Warn, "ARM9 BIOS not found\n");
return std::nullopt;
}
std::optional<std::array<u8, ARM7BIOSSize>> EmuInstance::loadARM7BIOS() noexcept
{
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{
return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_optional(bios_arm7_bin) : std::nullopt;
}
string path = globalCfg.GetString("DS.BIOS7Path");
if (FileHandle* f = OpenLocalFile(path, Read))
{
std::array<u8, ARM7BIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
Log(Info, "ARM7 BIOS loaded from %s\n", path.c_str());
return bios;
}
Log(Warn, "ARM7 BIOS not found\n");
return std::nullopt;
}
std::optional<std::array<u8, DSiBIOSSize>> EmuInstance::loadDSiARM9BIOS() noexcept
{
string path = globalCfg.GetString("DSi.BIOS9Path");
if (FileHandle* f = OpenLocalFile(path, Read))
{
std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
if (!globalCfg.GetBool("DSi.FullBIOSBoot"))
{
// herp
*(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector
// TODO!!!!
// hax the upper 32K out of the goddamn DSi
// done that :) -pcy
}
Log(Info, "ARM9i BIOS loaded from %s\n", path.c_str());
return bios;
}
Log(Warn, "ARM9i BIOS not found\n");
return std::nullopt;
}
std::optional<std::array<u8, DSiBIOSSize>> EmuInstance::loadDSiARM7BIOS() noexcept
{
string path = globalCfg.GetString("DSi.BIOS7Path");
if (FileHandle* f = OpenLocalFile(path, Read))
{
std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
if (!globalCfg.GetBool("DSi.FullBIOSBoot"))
{
// herp
*(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector
// TODO!!!!
// hax the upper 32K out of the goddamn DSi
// done that :) -pcy
}
Log(Info, "ARM7i BIOS loaded from %s\n", path.c_str());
return bios;
}
Log(Warn, "ARM7i BIOS not found\n");
return std::nullopt;
}
Firmware EmuInstance::generateFirmware(int type) noexcept
{
// Construct the default firmware...
string settingspath;
Firmware firmware = Firmware(type);
assert(firmware.Buffer() != nullptr);
// If using generated firmware, we keep the wi-fi settings on the host disk separately.
// Wi-fi access point data includes Nintendo WFC settings,
// and if we didn't keep them then the player would have to reset them in each session.
// We don't need to save the whole firmware, just the part that may actually change.
if (FileHandle* f = OpenLocalFile(kWifiSettingsPath, Read))
{// If we have Wi-fi settings to load...
constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(Firmware::WifiAccessPoint) + sizeof(Firmware::ExtendedWifiAccessPoint));
if (!FileRead(firmware.GetExtendedAccessPointPosition(), TOTAL_WFC_SETTINGS_SIZE, 1, f))
{ // If we couldn't read the Wi-fi settings from this file...
Log(Warn, "Failed to read Wi-fi settings from \"%s\"; using defaults instead\n", kWifiSettingsPath.c_str());
// The access point and extended access point segments might
// be in different locations depending on the firmware revision,
// but our generated firmware always keeps them next to each other.
// (Extended access points first, then regular ones.)
firmware.GetAccessPoints() = {
Firmware::WifiAccessPoint(type),
Firmware::WifiAccessPoint(),
Firmware::WifiAccessPoint(),
};
firmware.GetExtendedAccessPoints() = {
Firmware::ExtendedWifiAccessPoint(),
Firmware::ExtendedWifiAccessPoint(),
Firmware::ExtendedWifiAccessPoint(),
};
firmware.UpdateChecksums();
CloseFile(f);
}
}
customizeFirmware(firmware);
// If we don't have Wi-fi settings to load,
// then the defaults will have already been populated by the constructor.
return firmware;
}
std::optional<Firmware> EmuInstance::loadFirmware(int type) noexcept
{
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{ // If we're using built-in firmware...
if (type == 1)
{
Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n");
return std::nullopt;
}
return generateFirmware(type);
}
//const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
string firmwarepath;
if (type == 1)
firmwarepath = globalCfg.GetString("DSi.FirmwarePath");
else
firmwarepath = globalCfg.GetString("DS.FirmwarePath");
Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str());
FileHandle* file = OpenLocalFile(firmwarepath, Read);
if (!file)
{
Log(Error, "SPI firmware: couldn't open firmware file!\n");
return std::nullopt;
}
Firmware firmware(file);
CloseFile(file);
if (!firmware.Buffer())
{
Log(Error, "SPI firmware: couldn't read firmware file!\n");
return std::nullopt;
}
if (Config::FirmwareOverrideSettings)
{
customizeFirmware(firmware);
}
return firmware;
}
std::optional<DSi_NAND::NANDImage> EmuInstance::loadNAND(const std::array<u8, DSiBIOSSize>& arm7ibios) noexcept
{
string path = globalCfg.GetString("DSi.NANDPath");
FileHandle* nandfile = OpenLocalFile(path, ReadWriteExisting);
if (!nandfile)
return std::nullopt;
DSi_NAND::NANDImage nandImage(nandfile, &arm7ibios[0x8308]);
if (!nandImage)
{
Log(Error, "Failed to parse DSi NAND\n");
return std::nullopt;
// the NANDImage takes ownership of the FileHandle, no need to clean it up here
}
// scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage
{
auto mount = DSi_NAND::NANDMount(nandImage);
if (!mount)
{
Log(Error, "Failed to mount DSi NAND\n");
return std::nullopt;
}
DSi_NAND::DSiFirmwareSystemSettings settings {};
if (!mount.ReadUserData(settings))
{
Log(Error, "Failed to read DSi NAND user data\n");
return std::nullopt;
}
// override user settings, if needed
if (Config::FirmwareOverrideSettings)
{
// we store relevant strings as UTF-8, so we need to convert them to UTF-16
auto converter = wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{};
// setting up username
std::u16string username = converter.from_bytes(Config::FirmwareUsername);
size_t usernameLength = std::min(username.length(), (size_t) 10);
memset(&settings.Nickname, 0, sizeof(settings.Nickname));
memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t));
// setting language
settings.Language = static_cast<Firmware::Language>(Config::FirmwareLanguage);
// setting up color
settings.FavoriteColor = Config::FirmwareFavouriteColour;
// setting up birthday
settings.BirthdayMonth = Config::FirmwareBirthdayMonth;
settings.BirthdayDay = Config::FirmwareBirthdayDay;
// setup message
std::u16string message = converter.from_bytes(Config::FirmwareMessage);
size_t messageLength = std::min(message.length(), (size_t) 26);
memset(&settings.Message, 0, sizeof(settings.Message));
memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t));
// TODO: make other items configurable?
}
// fix touchscreen coords
settings.TouchCalibrationADC1 = {0, 0};
settings.TouchCalibrationPixel1 = {0, 0};
settings.TouchCalibrationADC2 = {255 << 4, 191 << 4};
settings.TouchCalibrationPixel2 = {255, 191};
settings.UpdateHash();
if (!mount.ApplyUserData(settings))
{
Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n");
return std::nullopt;
}
}
return nandImage;
}
constexpr u64 MB(u64 i)
{
return i * 1024 * 1024;
}
constexpr u64 imgsizes[] = {0, MB(256), MB(512), MB(1024), MB(2048), MB(4096)};
std::optional<FATStorageArgs> EmuInstance::getSDCardArgs(const string& key) noexcept
{
// key = DSi.SD or DLDI
Config::Table sdopt = globalCfg.GetTable(key);
if (!sdopt.GetBool("Enable"))
return std::nullopt;
return FATStorageArgs {
sdopt.GetString("ImagePath"),
imgsizes[sdopt.GetInt("ImageSize")],
sdopt.GetBool("ReadOnly"),
sdopt.GetBool("FolderSync") ? std::make_optional(sdopt.GetString("FolderPath")) : std::nullopt
};
}
std::optional<FATStorage> EmuInstance::loadSDCard(const string& key) noexcept
{
auto args = getSDCardArgs(key);
if (!args.has_value())
return std::nullopt;
return FATStorage(args.value());
}
void EmuInstance::enableCheats(bool enable)
{
cheatsOn = enable;
if (cheatFile)
nds->AREngine.SetCodeFile(cheatsOn ? cheatFile : nullptr);
}
ARCodeFile* EmuInstance::getCheatFile()
{
return cheatFile;
}

View File

@ -19,9 +19,11 @@
#ifndef EMUINSTANCE_H
#define EMUINSTANCE_H
#include "NDS.h"
#include "EmuThread.h"
#include "Window.h"
#include "Config.h"
#include "SaveManager.h"
const int kMaxWindows = 16;
@ -46,6 +48,27 @@ private:
QString verifyDSiFirmware();
QString verifyDSiNAND();
std::string getEffectiveFirmwareSavePath();
void initFirmwareSaveManager() noexcept;
std::string getSavestateName(int slot);
bool savestateExists(int slot);
bool loadState(const std::string& filename);
bool saveState(const std::string& filename);
void undoStateLoad();
void unloadCheats();
void loadCheats();
std::optional<std::array<melonDS::u8, melonDS::ARM9BIOSSize>> loadARM9BIOS() noexcept;
std::optional<std::array<melonDS::u8, melonDS::ARM7BIOSSize>> loadARM7BIOS() noexcept;
std::optional<std::array<melonDS::u8, melonDS::DSiBIOSSize>> loadDSiARM9BIOS() noexcept;
std::optional<std::array<melonDS::u8, melonDS::DSiBIOSSize>> loadDSiARM7BIOS() noexcept;
melonDS::Firmware generateFirmware(int type) noexcept;
std::optional<melonDS::Firmware> loadFirmware(int type) noexcept;
std::optional<melonDS::DSi_NAND::NANDImage> loadNAND(const std::array<melonDS::u8, melonDS::DSiBIOSSize>& arm7ibios) noexcept;
std::optional<melonDS::FATStorageArgs> getSDCardArgs(const std::string& key) noexcept;
std::optional<melonDS::FATStorage> loadSDCard(const std::string& key) noexcept;
void enableCheats(bool enable);
melonDS::ARCodeFile* getCheatFile();
int instanceID;
EmuThread* emuThread;
@ -57,6 +80,8 @@ private:
Config::Table globalCfg;
Config::Table localCfg;
melonDS::NDS* nds;
int cartType;
std::string baseROMDir;
std::string baseROMName;
@ -66,6 +91,17 @@ private:
std::string baseGBAROMDir;
std::string baseGBAROMName;
std::string baseGBAAssetName;
std::unique_ptr<SaveManager> ndsSave;
std::unique_ptr<SaveManager> gbaSave;
std::unique_ptr<SaveManager> firmwareSave;
std::unique_ptr<melonDS::Savestate> backupState;
bool savestateLoaded;
std::string previousSaveFile;
melonDS::ARCodeFile* cheatFile;
bool cheatsOn;
};
#endif //EMUINSTANCE_H

View File

@ -325,7 +325,7 @@ QString VerifySetup()
return "";
}
#endif
std::string GetEffectiveFirmwareSavePath(EmuThread* thread)
{
if (!Config::ExternalBIOSEnable)
@ -846,7 +846,7 @@ ARCodeFile* GetCheatFile()
{
return CheatFile;
}
#endif
void SetBatteryLevels(NDS& nds)
{