From e6e33eef17f5a91e12fd3da0f238c4984ef8b464 Mon Sep 17 00:00:00 2001 From: iris Date: Tue, 16 Jun 2026 10:58:12 +0200 Subject: [PATCH] Create function to read just the rom header so the rom list loads up way quicker (instantaneous on my shitty work laptop) --- external/ircolib/file.hpp | 16 +++++++++++ src/backend/Core.cpp | 2 +- src/backend/GameDB.cpp | 38 +++++++++++++++---------- src/backend/GameDB.hpp | 9 ++++-- src/backend/core/Mem.cpp | 38 +++++++++++++++++-------- src/backend/core/Mem.hpp | 8 ++---- src/frontend/RomsList.cpp | 18 ++++++------ src/utils/File.cpp | 59 ++++++++++++++++++++++++++++++++++++--- src/utils/File.hpp | 4 ++- 9 files changed, 143 insertions(+), 49 deletions(-) diff --git a/external/ircolib/file.hpp b/external/ircolib/file.hpp index b89c4b2..20d0dfb 100644 --- a/external/ircolib/file.hpp +++ b/external/ircolib/file.hpp @@ -12,6 +12,22 @@ static inline std::vector read_file_binary(const std::string &path) { return {std::istreambuf_iterator{file}, {}}; } +static inline std::vector 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 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 &data, const std::string &path) { std::ofstream file(path, std::ios::binary); std::copy(data.begin(), data.end(), std::ostreambuf_iterator{file}); diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 87c7cde..678b231 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -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; }); auto rom = Mem::LoadROM(isArchive, romPath); - GameDB::match(rom); + GameDB::match(rom.header); mem->rom = rom; if (rom.gameNameDB.empty()) { rom.gameNameDB = fs::path(romPath).stem().string(); diff --git a/src/backend/GameDB.cpp b/src/backend/GameDB.cpp index 4549cf9..401f2a4 100644 --- a/src/backend/GameDB.cpp +++ b/src/backend/GameDB.cpp @@ -48,43 +48,51 @@ const char *GameDB::regionCodeToReadable(char r) { } } -std::string GameDB::match(ROM &rom) { - std::string result = ""; +GameDBMatchResults GameDB::match(const ROMHeader &header) { + GameDBMatchResults result; + + char codeFromHeader[] = { + (char)header.categoryCode, + header.uniqueCode[0], + header.uniqueCode[1], + '\0', + }; for (const auto &[code, regions, saveType, name] : gamedb) { bool matchesRegion = false; - if (code != rom.code) + if (code != codeFromHeader) continue; for (int j = 0; j < regions.size(); j++) { if (j == regions.size() - 1) - result += regionCodeToReadable(regions[j]); + result.regions += regionCodeToReadable(regions[j]); 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; } if (matchesRegion) { - rom.saveType = saveType; - rom.gameNameDB = name; + result.saveType = saveType; + result.resolvedName = name; return result; } ircolib::info("Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has " "region {}", - name, regions, rom.header.countryCode); - rom.saveType = saveType; - rom.gameNameDB = name; + name, regions, header.countryCode); + result.saveType = saveType; + result.resolvedName = name; 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 = ""; - rom.saveType = SAVE_NONE; - return "Unknown"; + result.resolvedName = ""; + result.saveType = SAVE_NONE; + result.regions = "Unknown"; + return result; } } // namespace n64 diff --git a/src/backend/GameDB.hpp b/src/backend/GameDB.hpp index 3b443ec..6944668 100644 --- a/src/backend/GameDB.hpp +++ b/src/backend/GameDB.hpp @@ -3,7 +3,7 @@ namespace n64 { enum SaveType { SAVE_NONE, SAVE_EEPROM_4k, SAVE_EEPROM_16k, SAVE_FLASH_1m, SAVE_SRAM_256k }; -struct ROM; +struct ROMHeader; struct GameDBEntry { std::string code; @@ -12,8 +12,13 @@ struct GameDBEntry { const char *name; }; +struct GameDBMatchResults { + SaveType saveType; + std::string regions, resolvedName; +}; + namespace GameDB { - std::string match(ROM &); + GameDBMatchResults match(const ROMHeader &); const char *regionCodeToReadable(char); } // namespace GameDB diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index c098c31..2d41c9e 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -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 buf{}; + if (isArchive) { + buf = Util::ExtractROMHeaderFromArchive(filename); + } else { + buf = Util::OpenROMHeader(filename); + } + + endianness = std::byteswap(ircolib::read_access(buf, 0)); + Util::SwapN64Rom(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 res; res.cart.resize(CART_SIZE); @@ -99,7 +124,7 @@ ROM Mem::LoadROM(const bool isArchive, const std::string &filename) { size_t sizeAdjusted; std::vector buf{}; if (isArchive) { - buf = Util::OpenArchive(filename, sizeAdjusted); + buf = Util::ExtractROMFromArchive(filename, sizeAdjusted); } else { buf = Util::OpenROM(filename, sizeAdjusted); } @@ -108,10 +133,8 @@ ROM Mem::LoadROM(const bool isArchive, const std::string &filename) { Util::SwapN64Rom(buf, endianness); std::ranges::copy(buf, res.cart.begin()); - res.mask = sizeAdjusted - 1; 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.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.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); SetROMCIC(checksum, res); endianness = std::byteswap(ircolib::read_access(res.cart, 0)); diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 6513986..71ecd7c 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -26,11 +26,8 @@ struct ROMHeader { struct ROM { bool pal; - char gameNameCart[20]; - char code[4]; ROMHeader header; SaveType saveType = SAVE_NONE; - size_t mask; CICType cicType; std::vector cart; std::string gameNameDB; @@ -80,6 +77,7 @@ struct Mem { void Reset(); void LoadSRAM(SaveType, fs::path); static ROM LoadROM(bool, const std::string &); + static ROMHeader ReadROMHeader(bool, const std::string &); [[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); } @@ -136,8 +134,8 @@ struct Mem { mio::mmap_sink saveData{}; [[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(pal_codes, [&rom](char a) { return rom.cart[0x3d] == a; }); + return std::ranges::any_of(std::array{'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y', 'Z'}, + [&rom](uint8_t a) { return rom.header.countryCode == a; }); } }; } // namespace n64 diff --git a/src/frontend/RomsList.cpp b/src/frontend/RomsList.cpp index 619f430..dfaed61 100644 --- a/src/frontend/RomsList.cpp +++ b/src/frontend/RomsList.cpp @@ -54,18 +54,18 @@ void RomsListTable::populate(const std::string &romsPath) { if (!isArchive && !isPlain) continue; - auto rom = n64::Mem::LoadROM(isArchive, filename); - auto regions = n64::GameDB::match(rom); + auto header = n64::Mem::ReadROMHeader(isArchive, filename); + auto [saveType, regions, gameNameDB] = n64::GameDB::match(header); - if (rom.gameNameDB.empty()) - rom.gameNameDB = fs::path(filename).stem().string(); + if (gameNameDB.empty()) + gameNameDB = fs::path(filename).stem().string(); insertRow(i); - setItem(i, 0, - new QTableWidgetItem(std::format("{} ({}) (Rev {})", rom.gameNameDB, - n64::GameDB::regionCodeToReadable(rom.header.countryCode), - rom.header.version) - .c_str())); + setItem( + i, 0, + new QTableWidgetItem(std::format("{} ({}) (Rev {})", gameNameDB, + n64::GameDB::regionCodeToReadable(header.countryCode), header.version) + .c_str())); setItem(i, 1, new QTableWidgetItem(regions.c_str())); setItem(i, 2, new QTableWidgetItem("Never")); setItem(i, 3, new QTableWidgetItem("0h 0m 0s")); diff --git a/src/utils/File.cpp b/src/utils/File.cpp index ae64a8c..89471f8 100644 --- a/src/utils/File.cpp +++ b/src/utils/File.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace Util { @@ -9,7 +10,12 @@ std::vector OpenROM(const std::string &filename, size_t &sizeAdjusted) { return buf; } -std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted) { +std::vector OpenROMHeader(const std::string &filename) { + auto buf = ircolib::read_file_binary(filename, 0x40); + return buf; +} + +std::vector ExtractROMFromArchive(const std::string &path, size_t &sizeAdjusted) { const auto stream = ar_open_file(fs::path(path).string().c_str()); if (!stream) { @@ -33,13 +39,12 @@ std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted) { std::vector buf{}; - std::vector rom_exts{".n64", ".z64", ".v64", ".N64", ".Z64", ".V64"}; - while (ar_parse_entry(archive)) { auto filename = ar_entry_get_name(archive); 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); sizeAdjusted = ircolib::next_pow2(size); buf.resize(sizeAdjusted); @@ -56,4 +61,50 @@ std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted) { ar_close(stream); return buf; } + +std::vector 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 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 diff --git a/src/utils/File.hpp b/src/utils/File.hpp index f43a051..eb83317 100644 --- a/src/utils/File.hpp +++ b/src/utils/File.hpp @@ -9,6 +9,8 @@ namespace fs = std::filesystem; namespace Util { +std::vector OpenROMHeader(const std::string &filename); +std::vector ExtractROMHeaderFromArchive(const std::string &path); std::vector OpenROM(const std::string &filename, size_t &sizeAdjusted); -std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted); +std::vector ExtractROMFromArchive(const std::string &path, size_t &sizeAdjusted); } // namespace Util