mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-06-28 09:59:41 -06:00
Custom path support (#1333)
also including: * getting rid of shitty strings * all new, cleaner ROM handling code * base for DSi savestates * GBA slot addons (for now, memory cart)
This commit is contained in:
843
src/frontend/qt_sdl/ROMManager.cpp
Normal file
843
src/frontend/qt_sdl/ROMManager.cpp
Normal file
@ -0,0 +1,843 @@
|
||||
/*
|
||||
Copyright 2016-2021 Arisotura
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS 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.
|
||||
|
||||
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#ifdef ARCHIVE_SUPPORT_ENABLED
|
||||
#include "ArchiveUtil.h"
|
||||
#endif
|
||||
#include "ROMManager.h"
|
||||
#include "Config.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#include "NDS.h"
|
||||
#include "DSi.h"
|
||||
|
||||
|
||||
namespace ROMManager
|
||||
{
|
||||
|
||||
int CartType = -1;
|
||||
std::string BaseROMDir = "";
|
||||
std::string BaseROMName = "";
|
||||
std::string BaseAssetName = "";
|
||||
|
||||
int GBACartType = -1;
|
||||
std::string BaseGBAROMDir = "";
|
||||
std::string BaseGBAROMName = "";
|
||||
std::string BaseGBAAssetName = "";
|
||||
|
||||
SaveManager* NDSSave = nullptr;
|
||||
SaveManager* GBASave = nullptr;
|
||||
|
||||
bool SavestateLoaded = false;
|
||||
std::string PreviousSaveFile = "";
|
||||
|
||||
ARCodeFile* CheatFile = nullptr;
|
||||
bool CheatsOn = false;
|
||||
|
||||
|
||||
int LastSep(std::string path)
|
||||
{
|
||||
int i = path.length() - 1;
|
||||
while (i >= 0)
|
||||
{
|
||||
if (path[i] == '/' || path[i] == '\\')
|
||||
return i;
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string GetAssetPath(bool gba, std::string configpath, std::string ext, std::string file="")
|
||||
{
|
||||
if (configpath.empty())
|
||||
configpath = gba ? BaseGBAROMDir : BaseROMDir;
|
||||
|
||||
if (file.empty())
|
||||
{
|
||||
file = gba ? BaseGBAAssetName : BaseAssetName;
|
||||
if (file.empty())
|
||||
file = "firmware";
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int i = configpath.length() - 1;
|
||||
if (configpath[i] == '/' || configpath[i] == '\\')
|
||||
configpath = configpath.substr(0, i);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!configpath.empty())
|
||||
configpath += "/";
|
||||
|
||||
return configpath + file + ext;
|
||||
}
|
||||
|
||||
|
||||
QString VerifyDSBIOS()
|
||||
{
|
||||
FILE* f;
|
||||
long len;
|
||||
|
||||
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
|
||||
if (!f) return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
if (len != 0x1000)
|
||||
{
|
||||
fclose(f);
|
||||
return "DS ARM9 BIOS is not a valid BIOS dump.";
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
|
||||
if (!f) return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
if (len != 0x4000)
|
||||
{
|
||||
fclose(f);
|
||||
return "DS ARM7 BIOS is not a valid BIOS dump.";
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
QString VerifyDSiBIOS()
|
||||
{
|
||||
FILE* f;
|
||||
long len;
|
||||
|
||||
// TODO: check the first 32 bytes
|
||||
|
||||
f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb");
|
||||
if (!f) return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
if (len != 0x10000)
|
||||
{
|
||||
fclose(f);
|
||||
return "DSi ARM9 BIOS is not a valid BIOS dump.";
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
|
||||
if (!f) return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
if (len != 0x10000)
|
||||
{
|
||||
fclose(f);
|
||||
return "DSi ARM7 BIOS is not a valid BIOS dump.";
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
QString VerifyDSFirmware()
|
||||
{
|
||||
FILE* f;
|
||||
long len;
|
||||
|
||||
f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
|
||||
if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
if (len == 0x20000)
|
||||
{
|
||||
// 128KB firmware, not bootable
|
||||
fclose(f);
|
||||
// TODO report it somehow? detect in core?
|
||||
return "";
|
||||
}
|
||||
else if (len != 0x40000 && len != 0x80000)
|
||||
{
|
||||
fclose(f);
|
||||
return "DS firmware is not a valid firmware dump.";
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
QString VerifyDSiFirmware()
|
||||
{
|
||||
FILE* f;
|
||||
long len;
|
||||
|
||||
f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb");
|
||||
if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
if (len != 0x20000)
|
||||
{
|
||||
// not 128KB
|
||||
// TODO: check whether those work
|
||||
fclose(f);
|
||||
return "DSi firmware is not a valid firmware dump.";
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
QString VerifyDSiNAND()
|
||||
{
|
||||
FILE* f;
|
||||
long len;
|
||||
|
||||
f = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
|
||||
if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
// TODO: some basic checks
|
||||
// check that it has the nocash footer, and all
|
||||
|
||||
fclose(f);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
QString VerifySetup()
|
||||
{
|
||||
QString res;
|
||||
|
||||
if (Config::ExternalBIOSEnable)
|
||||
{
|
||||
res = VerifyDSBIOS();
|
||||
if (!res.isEmpty()) return res;
|
||||
}
|
||||
|
||||
if (Config::ConsoleType == 1)
|
||||
{
|
||||
res = VerifyDSiBIOS();
|
||||
if (!res.isEmpty()) return res;
|
||||
|
||||
if (Config::ExternalBIOSEnable)
|
||||
{
|
||||
res = VerifyDSiFirmware();
|
||||
if (!res.isEmpty()) return res;
|
||||
}
|
||||
|
||||
res = VerifyDSiNAND();
|
||||
if (!res.isEmpty()) return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Config::ExternalBIOSEnable)
|
||||
{
|
||||
res = VerifyDSFirmware();
|
||||
if (!res.isEmpty()) return res;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
std::string GetSavestateName(int slot)
|
||||
{
|
||||
std::string ext = ".ml";
|
||||
ext += (char)('0'+slot);
|
||||
return GetAssetPath(false, Config::SavestatePath, ext);
|
||||
}
|
||||
|
||||
bool SavestateExists(int slot)
|
||||
{
|
||||
std::string ssfile = GetSavestateName(slot);
|
||||
return Platform::FileExists(ssfile);
|
||||
}
|
||||
|
||||
bool LoadState(std::string filename)
|
||||
{
|
||||
// backup
|
||||
Savestate* backup = new Savestate("timewarp.mln", true);
|
||||
NDS::DoSavestate(backup);
|
||||
delete backup;
|
||||
|
||||
bool failed = false;
|
||||
|
||||
Savestate* state = new Savestate(filename, false);
|
||||
if (state->Error)
|
||||
{
|
||||
delete state;
|
||||
|
||||
// current state might be crapoed, so restore from sane backup
|
||||
state = new Savestate("timewarp.mln", false);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
bool res = NDS::DoSavestate(state);
|
||||
delete state;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
failed = true;
|
||||
state = new Savestate("timewarp.mln", false);
|
||||
NDS::DoSavestate(state);
|
||||
delete state;
|
||||
}
|
||||
|
||||
if (failed) return false;
|
||||
|
||||
if (Config::SavestateRelocSRAM && NDSSave)
|
||||
{
|
||||
PreviousSaveFile = NDSSave->GetPath();
|
||||
|
||||
std::string savefile = filename.substr(LastSep(filename)+1);
|
||||
savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
|
||||
NDSSave->SetPath(savefile, true);
|
||||
}
|
||||
|
||||
SavestateLoaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveState(std::string filename)
|
||||
{
|
||||
Savestate* state = new Savestate(filename, true);
|
||||
if (state->Error)
|
||||
{
|
||||
delete state;
|
||||
return false;
|
||||
}
|
||||
|
||||
NDS::DoSavestate(state);
|
||||
delete state;
|
||||
|
||||
if (Config::SavestateRelocSRAM && NDSSave)
|
||||
{
|
||||
std::string savefile = filename.substr(LastSep(filename)+1);
|
||||
savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
|
||||
NDSSave->SetPath(savefile, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UndoStateLoad()
|
||||
{
|
||||
if (!SavestateLoaded) return;
|
||||
|
||||
// pray that this works
|
||||
// what do we do if it doesn't???
|
||||
// but it should work.
|
||||
Savestate* backup = new Savestate("timewarp.mln", false);
|
||||
NDS::DoSavestate(backup);
|
||||
delete backup;
|
||||
|
||||
if (NDSSave && (!PreviousSaveFile.empty()))
|
||||
{
|
||||
NDSSave->SetPath(PreviousSaveFile, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UnloadCheats()
|
||||
{
|
||||
if (CheatFile)
|
||||
{
|
||||
delete CheatFile;
|
||||
CheatFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadCheats()
|
||||
{
|
||||
UnloadCheats();
|
||||
|
||||
std::string filename = GetAssetPath(false, Config::CheatFilePath, ".mch");
|
||||
|
||||
// TODO: check for error (malformed cheat file, ...)
|
||||
CheatFile = new ARCodeFile(filename);
|
||||
|
||||
AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
|
||||
}
|
||||
|
||||
void EnableCheats(bool enable)
|
||||
{
|
||||
CheatsOn = enable;
|
||||
if (CheatFile)
|
||||
AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
|
||||
}
|
||||
|
||||
ARCodeFile* GetCheatFile()
|
||||
{
|
||||
return CheatFile;
|
||||
}
|
||||
|
||||
|
||||
void Reset()
|
||||
{
|
||||
NDS::SetConsoleType(Config::ConsoleType);
|
||||
if (Config::ConsoleType == 1) EjectGBACart();
|
||||
NDS::Reset();
|
||||
|
||||
if ((CartType != -1) && NDSSave)
|
||||
{
|
||||
std::string oldsave = NDSSave->GetPath();
|
||||
std::string newsave = GetAssetPath(false, Config::SaveFilePath, ".sav");
|
||||
if (oldsave != newsave)
|
||||
NDSSave->SetPath(newsave, false);
|
||||
}
|
||||
|
||||
if ((GBACartType != -1) && GBASave)
|
||||
{
|
||||
std::string oldsave = GBASave->GetPath();
|
||||
std::string newsave = GetAssetPath(true, Config::SaveFilePath, ".sav");
|
||||
if (oldsave != newsave)
|
||||
GBASave->SetPath(newsave, false);
|
||||
}
|
||||
|
||||
if (!BaseROMName.empty())
|
||||
{
|
||||
if (Config::DirectBoot || NDS::NeedsDirectBoot())
|
||||
{
|
||||
NDS::SetupDirectBoot(BaseROMName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LoadBIOS()
|
||||
{
|
||||
NDS::SetConsoleType(Config::ConsoleType);
|
||||
|
||||
if (NDS::NeedsDirectBoot())
|
||||
return false;
|
||||
|
||||
/*if (NDSSave) delete NDSSave;
|
||||
NDSSave = nullptr;
|
||||
|
||||
CartType = -1;
|
||||
BaseROMDir = "";
|
||||
BaseROMName = "";
|
||||
BaseAssetName = "";*/
|
||||
|
||||
NDS::Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LoadROM(QStringList filepath, bool reset)
|
||||
{
|
||||
if (filepath.empty()) return false;
|
||||
|
||||
u8* filedata;
|
||||
u32 filelen;
|
||||
|
||||
std::string basepath;
|
||||
std::string romname;
|
||||
|
||||
int num = filepath.count();
|
||||
if (num == 1)
|
||||
{
|
||||
// regular file
|
||||
|
||||
std::string filename = filepath.at(0).toStdString();
|
||||
FILE* f = Platform::OpenFile(filename, "rb", true);
|
||||
if (!f) return false;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long len = ftell(f);
|
||||
if (len > 0x40000000)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
filedata = new u8[len];
|
||||
size_t nread = fread(filedata, (size_t)len, 1, f);
|
||||
if (nread != 1)
|
||||
{
|
||||
fclose(f);
|
||||
delete[] filedata;
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
filelen = (u32)len;
|
||||
|
||||
int pos = LastSep(filename);
|
||||
basepath = filename.substr(0, pos);
|
||||
romname = filename.substr(pos+1);
|
||||
}
|
||||
#ifdef ARCHIVE_SUPPORT_ENABLED
|
||||
else if (num == 2)
|
||||
{
|
||||
// file inside archive
|
||||
|
||||
u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
|
||||
if (lenread < 0) return false;
|
||||
if (!filedata) return false;
|
||||
if (lenread != filelen)
|
||||
{
|
||||
delete[] filedata;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string std_archivepath = filepath.at(0).toStdString();
|
||||
basepath = std_archivepath.substr(0, LastSep(std_archivepath));
|
||||
|
||||
std::string std_romname = filepath.at(1).toStdString();
|
||||
romname = std_romname.substr(LastSep(std_romname)+1);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return false;
|
||||
|
||||
if (NDSSave) delete NDSSave;
|
||||
NDSSave = nullptr;
|
||||
|
||||
BaseROMDir = basepath;
|
||||
BaseROMName = romname;
|
||||
BaseAssetName = romname.substr(0, romname.rfind('.'));
|
||||
|
||||
if (reset)
|
||||
{
|
||||
NDS::SetConsoleType(Config::ConsoleType);
|
||||
NDS::Reset();
|
||||
}
|
||||
|
||||
u32 savelen = 0;
|
||||
u8* savedata = nullptr;
|
||||
|
||||
std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
|
||||
FILE* sav = Platform::OpenFile(savname, "rb", true);
|
||||
if (sav)
|
||||
{
|
||||
fseek(sav, 0, SEEK_END);
|
||||
savelen = (u32)ftell(sav);
|
||||
|
||||
fseek(sav, 0, SEEK_SET);
|
||||
savedata = new u8[savelen];
|
||||
fread(savedata, savelen, 1, sav);
|
||||
fclose(sav);
|
||||
}
|
||||
|
||||
bool res = NDS::LoadCart(filedata, filelen, savedata, savelen);
|
||||
if (res && reset)
|
||||
{
|
||||
if (Config::DirectBoot || NDS::NeedsDirectBoot())
|
||||
{
|
||||
NDS::SetupDirectBoot(romname);
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
CartType = 0;
|
||||
NDSSave = new SaveManager(savname);
|
||||
|
||||
LoadCheats();
|
||||
}
|
||||
|
||||
if (savedata) delete[] savedata;
|
||||
delete[] filedata;
|
||||
return res;
|
||||
}
|
||||
|
||||
void EjectCart()
|
||||
{
|
||||
if (NDSSave) delete NDSSave;
|
||||
NDSSave = nullptr;
|
||||
|
||||
UnloadCheats();
|
||||
|
||||
NDS::EjectCart();
|
||||
|
||||
CartType = -1;
|
||||
BaseROMDir = "";
|
||||
BaseROMName = "";
|
||||
BaseAssetName = "";
|
||||
}
|
||||
|
||||
bool CartInserted()
|
||||
{
|
||||
return CartType != -1;
|
||||
}
|
||||
|
||||
QString CartLabel()
|
||||
{
|
||||
if (CartType == -1)
|
||||
return "(none)";
|
||||
|
||||
QString ret = QString::fromStdString(BaseROMName);
|
||||
|
||||
int maxlen = 32;
|
||||
if (ret.length() > maxlen)
|
||||
ret = ret.left(maxlen-6) + "..." + ret.right(3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool LoadGBAROM(QStringList filepath)
|
||||
{
|
||||
if (Config::ConsoleType == 1) return false;
|
||||
if (filepath.empty()) return false;
|
||||
|
||||
u8* filedata;
|
||||
u32 filelen;
|
||||
|
||||
std::string basepath;
|
||||
std::string romname;
|
||||
|
||||
int num = filepath.count();
|
||||
if (num == 1)
|
||||
{
|
||||
// regular file
|
||||
|
||||
std::string filename = filepath.at(0).toStdString();
|
||||
FILE* f = Platform::OpenFile(filename, "rb", true);
|
||||
if (!f) return false;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long len = ftell(f);
|
||||
if (len > 0x40000000)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
filedata = new u8[len];
|
||||
size_t nread = fread(filedata, (size_t)len, 1, f);
|
||||
if (nread != 1)
|
||||
{
|
||||
fclose(f);
|
||||
delete[] filedata;
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
filelen = (u32)len;
|
||||
|
||||
int pos = LastSep(filename);
|
||||
basepath = filename.substr(0, pos);
|
||||
romname = filename.substr(pos+1);
|
||||
}
|
||||
#ifdef ARCHIVE_SUPPORT_ENABLED
|
||||
else if (num == 2)
|
||||
{
|
||||
// file inside archive
|
||||
|
||||
u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
|
||||
if (lenread < 0) return false;
|
||||
if (!filedata) return false;
|
||||
if (lenread != filelen)
|
||||
{
|
||||
delete[] filedata;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string std_archivepath = filepath.at(0).toStdString();
|
||||
basepath = std_archivepath.substr(0, LastSep(std_archivepath));
|
||||
|
||||
std::string std_romname = filepath.at(1).toStdString();
|
||||
romname = std_romname.substr(LastSep(std_romname)+1);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return false;
|
||||
|
||||
if (GBASave) delete GBASave;
|
||||
GBASave = nullptr;
|
||||
|
||||
BaseGBAROMDir = basepath;
|
||||
BaseGBAROMName = romname;
|
||||
BaseGBAAssetName = romname.substr(0, romname.rfind('.'));
|
||||
|
||||
u32 savelen = 0;
|
||||
u8* savedata = nullptr;
|
||||
|
||||
std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
|
||||
FILE* sav = Platform::OpenFile(savname, "rb", true);
|
||||
if (sav)
|
||||
{
|
||||
fseek(sav, 0, SEEK_END);
|
||||
savelen = (u32)ftell(sav);
|
||||
|
||||
fseek(sav, 0, SEEK_SET);
|
||||
savedata = new u8[savelen];
|
||||
fread(savedata, savelen, 1, sav);
|
||||
fclose(sav);
|
||||
}
|
||||
|
||||
bool res = NDS::LoadGBACart(filedata, filelen, savedata, savelen);
|
||||
|
||||
if (res)
|
||||
{
|
||||
GBACartType = 0;
|
||||
GBASave = new SaveManager(savname);
|
||||
}
|
||||
|
||||
if (savedata) delete[] savedata;
|
||||
delete[] filedata;
|
||||
return res;
|
||||
}
|
||||
|
||||
void LoadGBAAddon(int type)
|
||||
{
|
||||
if (Config::ConsoleType == 1) return;
|
||||
|
||||
if (GBASave) delete GBASave;
|
||||
GBASave = nullptr;
|
||||
|
||||
NDS::LoadGBAAddon(type);
|
||||
|
||||
GBACartType = type;
|
||||
BaseGBAROMDir = "";
|
||||
BaseGBAROMName = "";
|
||||
BaseGBAAssetName = "";
|
||||
}
|
||||
|
||||
void EjectGBACart()
|
||||
{
|
||||
if (GBASave) delete GBASave;
|
||||
GBASave = nullptr;
|
||||
|
||||
NDS::EjectGBACart();
|
||||
|
||||
GBACartType = -1;
|
||||
BaseGBAROMDir = "";
|
||||
BaseGBAROMName = "";
|
||||
BaseGBAAssetName = "";
|
||||
}
|
||||
|
||||
bool GBACartInserted()
|
||||
{
|
||||
return GBACartType != -1;
|
||||
}
|
||||
|
||||
QString GBACartLabel()
|
||||
{
|
||||
if (Config::ConsoleType == 1) return "none (DSi)";
|
||||
|
||||
switch (GBACartType)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
QString ret = QString::fromStdString(BaseGBAROMName);
|
||||
|
||||
int maxlen = 32;
|
||||
if (ret.length() > maxlen)
|
||||
ret = ret.left(maxlen-6) + "..." + ret.right(3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
case NDS::GBAAddon_RAMExpansion:
|
||||
return "Memory expansion";
|
||||
}
|
||||
|
||||
return "(none)";
|
||||
}
|
||||
|
||||
|
||||
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
|
||||
{
|
||||
int index = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
for (int k = 0; k < 8; k++)
|
||||
{
|
||||
for (int l = 0; l < 8; l++)
|
||||
{
|
||||
u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F;
|
||||
u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31;
|
||||
u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31;
|
||||
u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31;
|
||||
u8 a = pal_index ? 255: 0;
|
||||
u32* row = &iconRef[256 * i + 32 * k + 8 * j];
|
||||
row[l] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15)
|
||||
#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14)
|
||||
#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11)
|
||||
#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
|
||||
#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
|
||||
|
||||
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
if (!sequence[i])
|
||||
break;
|
||||
u32* frame = &animatedTexRef[32 * 32 * i];
|
||||
ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame);
|
||||
|
||||
if (SEQ_FLIPH(sequence[i]))
|
||||
{
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
for (int y = 0; y < 32/2; y++)
|
||||
{
|
||||
std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SEQ_FLIPV(sequence[i]))
|
||||
{
|
||||
for (int x = 0; x < 32/2; x++)
|
||||
{
|
||||
for (int y = 0; y < 32; y++)
|
||||
{
|
||||
std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < SEQ_DUR(sequence[i]); j++)
|
||||
animatedSequenceRef.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user