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:
Vendored
+16
@@ -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});
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user