Create function to read just the rom header so the rom list loads up way quicker (instantaneous on my shitty work laptop)

This commit is contained in:
2026-06-16 10:58:12 +02:00
parent 44dea81bf2
commit e6e33eef17
9 changed files with 143 additions and 49 deletions
+16
View File
@@ -12,6 +12,22 @@ static inline std::vector<u8> read_file_binary(const std::string &path) {
return {std::istreambuf_iterator{file}, {}}; return {std::istreambuf_iterator{file}, {}};
} }
static inline std::vector<u8> read_file_binary(const std::string &path, uint32_t size, uint32_t offset = 0) {
FILE *file = fopen(path.c_str(), "rb");
if (!file)
return {};
std::vector<u8> res;
fseek(file, offset, SEEK_SET);
res.resize(size);
fread(res.data(), 1, size, file);
fclose(file);
return res;
}
static inline void write_file_binary(const std::vector<u8> &data, const std::string &path) { static inline void write_file_binary(const std::vector<u8> &data, const std::string &path) {
std::ofstream file(path, std::ios::binary); std::ofstream file(path, std::ios::binary);
std::copy(data.begin(), data.end(), std::ostreambuf_iterator{file}); std::copy(data.begin(), data.end(), std::ostreambuf_iterator{file});
+1 -1
View File
@@ -32,7 +32,7 @@ void Core::LoadROM(const std::string &romFilename) {
const bool isArchive = std::ranges::any_of(archive_types, [&extension](const auto &e) { return e == extension; }); const bool isArchive = std::ranges::any_of(archive_types, [&extension](const auto &e) { return e == extension; });
auto rom = Mem::LoadROM(isArchive, romPath); auto rom = Mem::LoadROM(isArchive, romPath);
GameDB::match(rom); GameDB::match(rom.header);
mem->rom = rom; mem->rom = rom;
if (rom.gameNameDB.empty()) { if (rom.gameNameDB.empty()) {
rom.gameNameDB = fs::path(romPath).stem().string(); rom.gameNameDB = fs::path(romPath).stem().string();
+23 -15
View File
@@ -48,43 +48,51 @@ const char *GameDB::regionCodeToReadable(char r) {
} }
} }
std::string GameDB::match(ROM &rom) { GameDBMatchResults GameDB::match(const ROMHeader &header) {
std::string result = ""; GameDBMatchResults result;
char codeFromHeader[] = {
(char)header.categoryCode,
header.uniqueCode[0],
header.uniqueCode[1],
'\0',
};
for (const auto &[code, regions, saveType, name] : gamedb) { for (const auto &[code, regions, saveType, name] : gamedb) {
bool matchesRegion = false; bool matchesRegion = false;
if (code != rom.code) if (code != codeFromHeader)
continue; continue;
for (int j = 0; j < regions.size(); j++) { for (int j = 0; j < regions.size(); j++) {
if (j == regions.size() - 1) if (j == regions.size() - 1)
result += regionCodeToReadable(regions[j]); result.regions += regionCodeToReadable(regions[j]);
else else
result += std::string(regionCodeToReadable(regions[j])) + ", "; result.regions += std::string(regionCodeToReadable(regions[j])) + ", ";
if (regions[j] == rom.header.countryCode) if (regions[j] == header.countryCode)
matchesRegion = true; matchesRegion = true;
} }
if (matchesRegion) { if (matchesRegion) {
rom.saveType = saveType; result.saveType = saveType;
rom.gameNameDB = name; result.resolvedName = name;
return result; return result;
} }
ircolib::info("Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has " ircolib::info("Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has "
"region {}", "region {}",
name, regions, rom.header.countryCode); name, regions, header.countryCode);
rom.saveType = saveType; result.saveType = saveType;
rom.gameNameDB = name; result.resolvedName = name;
return result; return result;
} }
ircolib::info("Did not match any Game DB entries. Code: {} Region: {}", rom.code, rom.header.countryCode); ircolib::info("Did not match any Game DB entries. Code: {} Region: {}", codeFromHeader, header.countryCode);
rom.gameNameDB = ""; result.resolvedName = "";
rom.saveType = SAVE_NONE; result.saveType = SAVE_NONE;
return "Unknown"; result.regions = "Unknown";
return result;
} }
} // namespace n64 } // namespace n64
+7 -2
View File
@@ -3,7 +3,7 @@
namespace n64 { namespace n64 {
enum SaveType { SAVE_NONE, SAVE_EEPROM_4k, SAVE_EEPROM_16k, SAVE_FLASH_1m, SAVE_SRAM_256k }; enum SaveType { SAVE_NONE, SAVE_EEPROM_4k, SAVE_EEPROM_16k, SAVE_FLASH_1m, SAVE_SRAM_256k };
struct ROM; struct ROMHeader;
struct GameDBEntry { struct GameDBEntry {
std::string code; std::string code;
@@ -12,8 +12,13 @@ struct GameDBEntry {
const char *name; const char *name;
}; };
struct GameDBMatchResults {
SaveType saveType;
std::string regions, resolvedName;
};
namespace GameDB { namespace GameDB {
std::string match(ROM &); GameDBMatchResults match(const ROMHeader &);
const char *regionCodeToReadable(char); const char *regionCodeToReadable(char);
} // namespace GameDB } // namespace GameDB
+26 -12
View File
@@ -90,6 +90,31 @@ FORCE_INLINE void SetROMCIC(u32 checksum, ROM &rom) {
} }
} }
ROMHeader Mem::ReadROMHeader(bool isArchive, const std::string &filename) {
ROMHeader res;
u32 endianness;
std::vector<u8> buf{};
if (isArchive) {
buf = Util::ExtractROMHeaderFromArchive(filename);
} else {
buf = Util::OpenROMHeader(filename);
}
endianness = std::byteswap(ircolib::read_access<u32>(buf, 0));
Util::SwapN64Rom<true>(buf, endianness);
memcpy(&res, buf.data(), sizeof(ROMHeader));
res.clockRate = std::byteswap(res.clockRate);
res.programCounter = std::byteswap(res.programCounter);
res.release = std::byteswap(res.release);
res.unknown = std::byteswap(res.unknown);
res.unknown2 = std::byteswap(res.unknown2);
return res;
}
ROM Mem::LoadROM(const bool isArchive, const std::string &filename) { ROM Mem::LoadROM(const bool isArchive, const std::string &filename) {
ROM res; ROM res;
res.cart.resize(CART_SIZE); res.cart.resize(CART_SIZE);
@@ -99,7 +124,7 @@ ROM Mem::LoadROM(const bool isArchive, const std::string &filename) {
size_t sizeAdjusted; size_t sizeAdjusted;
std::vector<u8> buf{}; std::vector<u8> buf{};
if (isArchive) { if (isArchive) {
buf = Util::OpenArchive(filename, sizeAdjusted); buf = Util::ExtractROMFromArchive(filename, sizeAdjusted);
} else { } else {
buf = Util::OpenROM(filename, sizeAdjusted); buf = Util::OpenROM(filename, sizeAdjusted);
} }
@@ -108,10 +133,8 @@ ROM Mem::LoadROM(const bool isArchive, const std::string &filename) {
Util::SwapN64Rom<true>(buf, endianness); Util::SwapN64Rom<true>(buf, endianness);
std::ranges::copy(buf, res.cart.begin()); std::ranges::copy(buf, res.cart.begin());
res.mask = sizeAdjusted - 1;
memcpy(&res.header, buf.data(), sizeof(ROMHeader)); memcpy(&res.header, buf.data(), sizeof(ROMHeader));
} }
memcpy(res.gameNameCart, res.header.imageName, sizeof(res.header.imageName));
res.header.clockRate = std::byteswap(res.header.clockRate); res.header.clockRate = std::byteswap(res.header.clockRate);
res.header.programCounter = std::byteswap(res.header.programCounter); res.header.programCounter = std::byteswap(res.header.programCounter);
@@ -119,15 +142,6 @@ ROM Mem::LoadROM(const bool isArchive, const std::string &filename) {
res.header.unknown = std::byteswap(res.header.unknown); res.header.unknown = std::byteswap(res.header.unknown);
res.header.unknown2 = std::byteswap(res.header.unknown2); res.header.unknown2 = std::byteswap(res.header.unknown2);
res.code[0] = res.header.categoryCode;
res.code[1] = res.header.uniqueCode[0];
res.code[2] = res.header.uniqueCode[1];
res.code[3] = '\0';
for (int i = sizeof(res.header.imageName) - 1; res.gameNameCart[i] == ' '; i--) {
res.gameNameCart[i] = '\0';
}
const u32 checksum = SDL_crc32(0, &res.cart[0x40], 0x9C0); const u32 checksum = SDL_crc32(0, &res.cart[0x40], 0x9C0);
SetROMCIC(checksum, res); SetROMCIC(checksum, res);
endianness = std::byteswap(ircolib::read_access<u32>(res.cart, 0)); endianness = std::byteswap(ircolib::read_access<u32>(res.cart, 0));
+3 -5
View File
@@ -26,11 +26,8 @@ struct ROMHeader {
struct ROM { struct ROM {
bool pal; bool pal;
char gameNameCart[20];
char code[4];
ROMHeader header; ROMHeader header;
SaveType saveType = SAVE_NONE; SaveType saveType = SAVE_NONE;
size_t mask;
CICType cicType; CICType cicType;
std::vector<u8> cart; std::vector<u8> cart;
std::string gameNameDB; std::string gameNameDB;
@@ -80,6 +77,7 @@ struct Mem {
void Reset(); void Reset();
void LoadSRAM(SaveType, fs::path); void LoadSRAM(SaveType, fs::path);
static ROM LoadROM(bool, const std::string &); static ROM LoadROM(bool, const std::string &);
static ROMHeader ReadROMHeader(bool, const std::string &);
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); } [[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
@@ -136,8 +134,8 @@ struct Mem {
mio::mmap_sink saveData{}; mio::mmap_sink saveData{};
[[nodiscard]] static FORCE_INLINE bool IsROMPAL(ROM &rom) { [[nodiscard]] static FORCE_INLINE bool IsROMPAL(ROM &rom) {
static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'}; return std::ranges::any_of(std::array{'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y', 'Z'},
return std::ranges::any_of(pal_codes, [&rom](char a) { return rom.cart[0x3d] == a; }); [&rom](uint8_t a) { return rom.header.countryCode == a; });
} }
}; };
} // namespace n64 } // namespace n64
+9 -9
View File
@@ -54,18 +54,18 @@ void RomsListTable::populate(const std::string &romsPath) {
if (!isArchive && !isPlain) if (!isArchive && !isPlain)
continue; continue;
auto rom = n64::Mem::LoadROM(isArchive, filename); auto header = n64::Mem::ReadROMHeader(isArchive, filename);
auto regions = n64::GameDB::match(rom); auto [saveType, regions, gameNameDB] = n64::GameDB::match(header);
if (rom.gameNameDB.empty()) if (gameNameDB.empty())
rom.gameNameDB = fs::path(filename).stem().string(); gameNameDB = fs::path(filename).stem().string();
insertRow(i); insertRow(i);
setItem(i, 0, setItem(
new QTableWidgetItem(std::format("{} ({}) (Rev {})", rom.gameNameDB, i, 0,
n64::GameDB::regionCodeToReadable(rom.header.countryCode), new QTableWidgetItem(std::format("{} ({}) (Rev {})", gameNameDB,
rom.header.version) n64::GameDB::regionCodeToReadable(header.countryCode), header.version)
.c_str())); .c_str()));
setItem(i, 1, new QTableWidgetItem(regions.c_str())); setItem(i, 1, new QTableWidgetItem(regions.c_str()));
setItem(i, 2, new QTableWidgetItem("Never")); setItem(i, 2, new QTableWidgetItem("Never"));
setItem(i, 3, new QTableWidgetItem("0h 0m 0s")); setItem(i, 3, new QTableWidgetItem("0h 0m 0s"));
+55 -4
View File
@@ -1,5 +1,6 @@
#include <File.hpp> #include <File.hpp>
#include <algorithm> #include <algorithm>
#include <array>
#include <unarr.h> #include <unarr.h>
namespace Util { namespace Util {
@@ -9,7 +10,12 @@ std::vector<u8> OpenROM(const std::string &filename, size_t &sizeAdjusted) {
return buf; return buf;
} }
std::vector<u8> OpenArchive(const std::string &path, size_t &sizeAdjusted) { std::vector<u8> OpenROMHeader(const std::string &filename) {
auto buf = ircolib::read_file_binary(filename, 0x40);
return buf;
}
std::vector<u8> ExtractROMFromArchive(const std::string &path, size_t &sizeAdjusted) {
const auto stream = ar_open_file(fs::path(path).string().c_str()); const auto stream = ar_open_file(fs::path(path).string().c_str());
if (!stream) { if (!stream) {
@@ -33,13 +39,12 @@ std::vector<u8> OpenArchive(const std::string &path, size_t &sizeAdjusted) {
std::vector<u8> buf{}; std::vector<u8> buf{};
std::vector<std::string> rom_exts{".n64", ".z64", ".v64", ".N64", ".Z64", ".V64"};
while (ar_parse_entry(archive)) { while (ar_parse_entry(archive)) {
auto filename = ar_entry_get_name(archive); auto filename = ar_entry_get_name(archive);
auto extension = fs::path(filename).extension(); auto extension = fs::path(filename).extension();
if (std::ranges::any_of(rom_exts, [&](const auto &x) { return extension == x; })) { if (std::ranges::any_of(std::array{".n64", ".z64", ".v64", ".N64", ".Z64", ".V64"},
[&](const auto &x) { return extension == x; })) {
const auto size = ar_entry_get_size(archive); const auto size = ar_entry_get_size(archive);
sizeAdjusted = ircolib::next_pow2(size); sizeAdjusted = ircolib::next_pow2(size);
buf.resize(sizeAdjusted); buf.resize(sizeAdjusted);
@@ -56,4 +61,50 @@ std::vector<u8> OpenArchive(const std::string &path, size_t &sizeAdjusted) {
ar_close(stream); ar_close(stream);
return buf; return buf;
} }
std::vector<u8> ExtractROMHeaderFromArchive(const std::string &path) {
const auto stream = ar_open_file(fs::path(path).string().c_str());
if (!stream) {
ircolib::panic("Could not open archive! Are you sure it's an archive?");
}
ar_archive *archive = ar_open_zip_archive(stream, false);
if (!archive)
archive = ar_open_rar_archive(stream);
if (!archive)
archive = ar_open_7z_archive(stream);
if (!archive)
archive = ar_open_tar_archive(stream);
if (!archive) {
ar_close(stream);
ircolib::panic(
"Could not open archive! Are you sure it's a supported archive? (7z, zip, rar and tar are supported)");
}
std::vector<u8> buf{};
while (ar_parse_entry(archive)) {
auto filename = ar_entry_get_name(archive);
auto extension = fs::path(filename).extension();
if (std::ranges::any_of(std::array{".n64", ".z64", ".v64", ".N64", ".Z64", ".V64"},
[&](const auto &x) { return extension == x; })) {
const auto size = 0x40;
buf.resize(size);
ar_entry_uncompress(archive, buf.data(), size);
break;
}
ar_close_archive(archive);
ar_close(stream);
ircolib::panic("Could not find any rom image in the archive!");
}
ar_close_archive(archive);
ar_close(stream);
return buf;
}
} // namespace Util } // namespace Util
+3 -1
View File
@@ -9,6 +9,8 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace Util { namespace Util {
std::vector<u8> OpenROMHeader(const std::string &filename);
std::vector<u8> ExtractROMHeaderFromArchive(const std::string &path);
std::vector<u8> OpenROM(const std::string &filename, size_t &sizeAdjusted); std::vector<u8> OpenROM(const std::string &filename, size_t &sizeAdjusted);
std::vector<u8> OpenArchive(const std::string &path, size_t &sizeAdjusted); std::vector<u8> ExtractROMFromArchive(const std::string &path, size_t &sizeAdjusted);
} // namespace Util } // namespace Util