diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index c67ba61..ba091c1 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -22,26 +22,28 @@ void Core::Reset() { void Core::LoadTAS(const fs::path &path) const { mem->mmio.si.pif.movie.Load(path); } -void Core::LoadROM(const std::string &rom_) { +void Core::LoadROM(const std::string &romFilename) { Stop(); - rom = rom_; + romPath = romFilename; std::string archive_types[] = {".zip", ".7z", ".rar", ".tar"}; - auto extension = fs::path(rom).extension().string(); + auto extension = fs::path(romPath).extension().string(); const bool isArchive = std::ranges::any_of(archive_types, [&extension](const auto &e) { return e == extension; }); - mem->LoadROM(isArchive, rom); - GameDB::match(); - if (mem->rom.gameNameDB.empty()) { - mem->rom.gameNameDB = fs::path(rom).stem().string(); + auto rom = Mem::LoadROM(isArchive, romPath); + GameDB::match(rom); + mem->rom = rom; + if (rom.gameNameDB.empty()) { + rom.gameNameDB = fs::path(romPath).stem().string(); + mem->rom.gameNameDB = rom.gameNameDB; } - mem->mmio.vi.isPal = mem->IsROMPAL(); - mem->mmio.si.pif.InitDevices(mem->saveType); - mem->mmio.si.pif.mempakPath = rom; - mem->mmio.si.pif.LoadEeprom(mem->saveType, rom); - mem->flash.Load(mem->saveType, rom); - mem->LoadSRAM(mem->saveType, rom); + mem->mmio.vi.isPal = Mem::IsROMPAL(rom); + mem->mmio.si.pif.InitDevices(mem->rom.saveType); + mem->mmio.si.pif.mempakPath = romPath; + mem->mmio.si.pif.LoadEeprom(mem->rom.saveType, romPath); + mem->flash.Load(mem->rom.saveType, romPath); + mem->LoadSRAM(mem->rom.saveType, romPath); mem->mmio.si.pif.Execute(); pause = false; romLoaded = true; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 0025106..17bbd5b 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -47,7 +47,7 @@ struct Core { int slot = 0; size_t memSize{}, cpuSize{}, verSize{}; CPUType cpuType; - std::string rom; + std::string romPath; std::set breakpoints{}; std::unique_ptr mem = std::make_unique(); Registers regs; diff --git a/src/backend/GameDB.cpp b/src/backend/GameDB.cpp index 7905197..499123b 100644 --- a/src/backend/GameDB.cpp +++ b/src/backend/GameDB.cpp @@ -2,38 +2,88 @@ #include namespace n64 { -void GameDB::match() { - n64::Mem& mem = n64::Core::GetMem(); - const ROM &rom = mem.rom; - for (const auto &[code, regions, saveType, name] : gamedb) { - const bool matches_code = code == rom.code; - bool matches_region = false; +const char *regionCodeToReadable(char r) { + switch (r) { + case 0: + return "Region-free"; + case 'A': + return "All regions"; + case 'B': + return "Brazil"; + case 'C': + return "China"; + case 'D': + return "Germany"; + case 'E': + return "North America"; + case 'F': + return "France"; + case 'G': + return "Gateway 64 (NTSC)"; + case 'H': + return "Netherlands"; + case 'I': + return "Italy"; + case 'J': + return "Japan"; + case 'K': + return "Korea"; + case 'L': + return "Gateway 64 (PAL)"; + case 'N': + return "Canada"; + case 'S': + return "Spain"; + case 'U': + return "Australia"; + case 'W': + return "Scandinavia"; + case 'P': + case 'X': + case 'Y': + case 'Z': + return "Europe"; + default: + return "Unknown"; + } +} - for (int j = 0; j < regions.size() && !matches_region; j++) { - if (regions[j] == rom.header.countryCode[0]) { - matches_region = true; - } +std::string GameDB::match(ROM &rom) { + std::string result = ""; + + for (const auto &[code, regions, saveType, name] : gamedb) { + bool matches_region = false; + + if (code == rom.code) { + for (int j = 0; j < regions.size(); j++) { + if (j == regions.size() - 1) + result += regionCodeToReadable(regions[j]); + else + result += std::string(regionCodeToReadable(regions[j])) + ", "; + + if (regions[j] == rom.header.countryCode[0]) + matches_region = true; + } + + if (matches_region) { + rom.saveType = saveType; + rom.gameNameDB = name; + return result; + } + + info("Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has " + "region {}", + name, regions, rom.header.countryCode[0]); + rom.saveType = saveType; + rom.gameNameDB = name; + return result; + } } - if (matches_code) { - if (matches_region) { - mem.saveType = saveType; - mem.rom.gameNameDB = name; - return; - } + info("Did not match any Game DB entries. Code: {} Region: {}", rom.code, rom.header.countryCode[0]); - warn( - "Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has region {}", - name, regions, rom.header.countryCode[0]); - mem.saveType = saveType; - mem.rom.gameNameDB = name; - return; - } - } - - warn("Did not match any Game DB entries. Code: {} Region: {}", mem.rom.code, mem.rom.header.countryCode[0]); - - mem.rom.gameNameDB = ""; - mem.saveType = SAVE_NONE; + rom.gameNameDB = ""; + rom.saveType = SAVE_NONE; + return "Unknown"; } } // namespace n64 diff --git a/src/backend/GameDB.hpp b/src/backend/GameDB.hpp index 36ecc3b..8a62bc1 100644 --- a/src/backend/GameDB.hpp +++ b/src/backend/GameDB.hpp @@ -3,201 +3,202 @@ namespace n64 { enum SaveType { SAVE_NONE, SAVE_EEPROM_4k, SAVE_EEPROM_16k, SAVE_FLASH_1m, SAVE_SRAM_256k }; +struct ROM; struct GameDBEntry { - std::string code; - std::string regions; - SaveType saveType; - const char *name; + std::string code; + std::string regions; + SaveType saveType; + const char *name; }; namespace GameDB { - void match(); + std::string match(ROM &); } static const GameDBEntry gamedb[] = { - {"NNM", "E", SAVE_NONE, "Namco Museum 64"}, - {"NDM", "E", SAVE_NONE, "Doom 64"}, - {"NGN", "E", SAVE_EEPROM_4k, "GoldenEye 007"}, - // Copied from CEN64 with small edits: https://github.com/n64dev/cen64/blob/master/device/cart_db.c - {"CFZ", "EJ", SAVE_SRAM_256k, "F-Zero X (NTSC)"}, - {"CLB", "EJ", SAVE_EEPROM_4k, "Mario Party (NTSC)"}, - {"CP2", "J", SAVE_FLASH_1m, "Pokémon Stadium 2 (Japan)"}, - {"CPS", "J", SAVE_SRAM_256k, "Pokémon Stadium (Japan)"}, - {"CZL", "EJ", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (NTSC)"}, - {"N3D", "J", SAVE_EEPROM_16k, "Doraemon 3: Nobita no Machi SOS!"}, - {"N3H", "J", SAVE_SRAM_256k, "Ganbare! Nippon! Olympics 2000"}, - {"NA2", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 2"}, - {"NAB", "JP", SAVE_EEPROM_4k, "Air Boarder 64"}, - {"NAD", "E", SAVE_EEPROM_4k, "Worms Armageddon (USA)"}, - {"NAF", "J", SAVE_FLASH_1m, "Doubutsu no Mori"}, - {"NAG", "EJP", SAVE_EEPROM_4k, "AeroGauge"}, - {"NAL", "EJPU", SAVE_SRAM_256k, "Super Smash Bros"}, - {"NB5", "J", SAVE_SRAM_256k, "Biohazard 2"}, - {"NB6", "J", SAVE_EEPROM_4k, "Super B-Daman: Battle Phoenix 64"}, - {"NB7", "EJPU", SAVE_EEPROM_16k, "Banjo-Tooie"}, - {"NBC", "EJP", SAVE_EEPROM_4k, "Blast Corps"}, - {"NBD", "EJP", SAVE_EEPROM_4k, "Bomberman Hero"}, - {"NBH", "EP", SAVE_EEPROM_4k, "Body Harvest"}, - {"NBK", "EJP", SAVE_EEPROM_4k, "Banjo-Kazooie"}, - {"NBM", "EJP", SAVE_EEPROM_4k, "Bomberman 64"}, - {"NBN", "J", SAVE_EEPROM_4k, "Bakuretsu Muteki Bangaioh"}, - {"NBV", "EJ", SAVE_EEPROM_4k, "Bomberman 64: The Second Attack!"}, - {"NCC", "DEP", SAVE_FLASH_1m, "Command & Conquer"}, - {"NCG", "J", SAVE_EEPROM_4k, "Choro Q 64 2: Hacha-Mecha Grand Prix Race"}, - {"NCH", "EP", SAVE_EEPROM_4k, "Chopper Attack"}, - {"NCK", "E", SAVE_FLASH_1m, "NBA Courtside 2"}, - {"NCR", "EJP", SAVE_EEPROM_4k, "Penny Racers"}, - {"NCT", "EJP", SAVE_EEPROM_4k, "Chameleon Twist"}, - {"NCU", "EP", SAVE_EEPROM_4k, "Cruis'n USA"}, - {"NCW", "EP", SAVE_EEPROM_16k, "Cruis'n World"}, - {"NCX", "J", SAVE_EEPROM_4k, "Custom Robo"}, - {"NCZ", "J", SAVE_EEPROM_16k, "Custom Robo V2"}, - {"ND2", "J", SAVE_EEPROM_16k, "Doraemon 2: Nobita to Hikari no Shinden"}, - {"ND3", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku"}, - {"ND4", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku Gaiden: Legend of Cornell"}, - {"ND6", "J", SAVE_EEPROM_16k, "Densha de Go! 64"}, - {"NDA", "J", SAVE_FLASH_1m, "Derby Stallion 64"}, - {"NDK", "J", SAVE_EEPROM_4k, "Space Dynamites"}, - {"NDO", "EJP", SAVE_EEPROM_16k, "Donkey Kong 64"}, - {"NDP", "E", SAVE_FLASH_1m, "Dinosaur Planet"}, - {"NDR", "J", SAVE_EEPROM_4k, "Doraemon: Nobita to 3tsu no Seireiseki"}, - {"NDU", "EP", SAVE_EEPROM_4k, "Duck Dodgers"}, - {"NDY", "EJP", SAVE_EEPROM_4k, "Diddy Kong Racing"}, - {"NEA", "EP", SAVE_EEPROM_4k, "PGA European Tour"}, - {"NEP", "EJP", SAVE_EEPROM_16k, "Star Wars Episode I: Racer"}, - {"NER", "E", SAVE_EEPROM_4k, "AeroFighters Assault (USA)"}, - {"NEV", "J", SAVE_EEPROM_16k, "Neon Genesis Evangelion"}, - {"NF2", "P", SAVE_EEPROM_4k, "F-1 World Grand Prix II"}, - {"NFG", "E", SAVE_EEPROM_4k, "Fighter Destiny 2"}, - {"NFH", "EP", SAVE_EEPROM_4k, "Bass Hunter 64"}, - {"NFU", "EP", SAVE_EEPROM_16k, "Conker's Bad Fur Day"}, - {"NFW", "DEFJP", SAVE_EEPROM_4k, "F-1 World Grand Prix"}, - {"NFX", "EJPU", SAVE_EEPROM_4k, "Star Fox 64"}, - {"NFY", "J", SAVE_EEPROM_4k, "Kakutou Denshou: F-Cup Maniax"}, - {"NFZ", "P", SAVE_SRAM_256k, "F-Zero X (PAL)"}, - {"NG6", "J", SAVE_SRAM_256k, "Ganbare Goemon: Dero Dero Douchuu Obake Tenkomori"}, - {"NGC", "EP", SAVE_EEPROM_16k, "GT 64: Championship Edition"}, - {"NGE", "EJP", SAVE_EEPROM_4k, "GoldenEye 007"}, - {"NGL", "J", SAVE_EEPROM_4k, "Getter Love!!"}, - {"NGP", "J", SAVE_SRAM_256k, "Goemon: Mononoke Sugoroku"}, - {"NGT", "J", SAVE_EEPROM_16k, "City-Tour GP: Zen-Nihon GT Senshuken"}, - {"NGU", "J", SAVE_EEPROM_4k, "Tsumi to Batsu: Hoshi no Keishousha"}, - {"NGV", "EP", SAVE_EEPROM_4k, "Glover"}, - {"NHA", "J", SAVE_EEPROM_4k, "Bomber Man 64 (Japan)"}, - {"NHF", "J", SAVE_EEPROM_4k, "64 Hanafuda: Tenshi no Yakusoku"}, - {"NHP", "J", SAVE_EEPROM_4k, "Heiwa Pachinko World 64"}, - {"NHY", "J", SAVE_SRAM_256k, "Hybrid Heaven (Japan)"}, - {"NIB", "J", SAVE_SRAM_256k, "Itoi Shigesato no Bass Tsuri No. 1 Kettei Ban!"}, - {"NIC", "E", SAVE_EEPROM_4k, "Indy Racing 2000"}, - {"NIJ", "EP", SAVE_EEPROM_4k, "Indiana Jones and the Infernal Machine"}, - {"NIM", "J", SAVE_EEPROM_16k, "Ide Yosuke no Mahjong Juku"}, - {"NIR", "J", SAVE_EEPROM_4k, "Utchan Nanchan no Hono no Challenger: Denryuu Ira Ira Bou"}, - {"NJ5", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 5"}, - {"NJD", "E", SAVE_FLASH_1m, "Jet Force Gemini (Kiosk Demo)"}, - {"NJF", "EJP", SAVE_FLASH_1m, "Jet Force Gemini"}, - {"NJG", "J", SAVE_SRAM_256k, "Jinsei Game 64"}, - {"NJM", "EP", SAVE_EEPROM_4k, "Earthworm Jim 3D"}, - {"NK2", "EJP", SAVE_EEPROM_4k, "Snowboard Kids 2"}, - {"NK4", "EJP", SAVE_EEPROM_16k, "Kirby 64: The Crystal Shards"}, - {"NKA", "DEFJP", SAVE_EEPROM_4k, "Fighters Destiny"}, - {"NKG", "EP", SAVE_SRAM_256k, "MLB featuring Ken Griffey Jr."}, - {"NKI", "EP", SAVE_EEPROM_4k, "Killer Instinct Gold"}, - {"NKJ", "E", SAVE_FLASH_1m, "Ken Griffey Jr.'s Slugfest"}, - {"NKT", "EJP", SAVE_EEPROM_4k, "Mario Kart 64"}, - {"NLB", "P", SAVE_EEPROM_4k, "Mario Party (PAL)"}, - {"NLL", "J", SAVE_EEPROM_4k, "Last Legion UX"}, - {"NLR", "EJP", SAVE_EEPROM_4k, "Lode Runner 3D"}, - {"NM6", "E", SAVE_FLASH_1m, "Mega Man 64"}, - {"NM8", "EJP", SAVE_EEPROM_16k, "Mario Tennis"}, - {"NMF", "EJP", SAVE_SRAM_256k, "Mario Golf"}, - {"NMG", "DEP", SAVE_EEPROM_4k, "Monaco Grand Prix"}, - {"NMI", "DEFIPS", SAVE_EEPROM_4k, "Mission: Impossible"}, - {"NML", "EJP", SAVE_EEPROM_4k, "Mickey's Speedway USA"}, - {"NMO", "E", SAVE_EEPROM_4k, "Monopoly"}, - {"NMQ", "EJP", SAVE_FLASH_1m, "Paper Mario"}, - {"NMR", "EJP", SAVE_EEPROM_4k, "Multi Racing Championship"}, - {"NMS", "J", SAVE_EEPROM_4k, "Morita Shougi 64"}, - {"NMU", "E", SAVE_EEPROM_4k, "Big Mountain 2000"}, - {"NMV", "EJP", SAVE_EEPROM_16k, "Mario Party 3"}, - {"NMW", "EJP", SAVE_EEPROM_4k, "Mario Party 2"}, - {"NMX", "EJP", SAVE_EEPROM_16k, "Excitebike 64"}, - {"NN6", "E", SAVE_EEPROM_4k, "Dr. Mario 64"}, - {"NNA", "EP", SAVE_EEPROM_4k, "Star Wars Episode I: Battle for Naboo"}, - {"NNB", "EP", SAVE_EEPROM_16k, "Kobe Bryant in NBA Courtside"}, - {"NOB", "EJ", SAVE_SRAM_256k, "Ogre Battle 64: Person of Lordly Caliber"}, - {"NOS", "J", SAVE_EEPROM_4k, "64 Oozumou"}, - {"NP2", "J", SAVE_EEPROM_4k, "Chou Kuukan Nighter Pro Yakyuu King 2"}, - {"NP3", "DEFIJPS", SAVE_FLASH_1m, "Pokémon Stadium 2"}, - {"NP6", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 6"}, - {"NPA", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 2000"}, - {"NPD", "EJP", SAVE_EEPROM_16k, "Perfect Dark"}, - {"NPE", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu Basic Ban 2001"}, - {"NPF", "DEFIJPSU", SAVE_FLASH_1m, "Pokémon Snap"}, - {"NPG", "EJ", SAVE_EEPROM_4k, "Hey You, Pikachu!"}, - {"NPH", "E", SAVE_FLASH_1m, "Pokémon Snap Station (Kiosk Demo)"}, - {"NPM", "P", SAVE_SRAM_256k, "Premier Manager 64"}, - {"NPN", "DEFP", SAVE_FLASH_1m, "Pokémon Puzzle League"}, - {"NPO", "DEFIPS", SAVE_FLASH_1m, "Pokémon Stadium (USA, PAL)"}, - {"NPP", "J", SAVE_EEPROM_16k, "Parlor! Pro 64: Pachinko Jikki Simulation Game"}, - {"NPS", "J", SAVE_SRAM_256k, "Jikkyou J.League 1999: Perfect Striker 2"}, - {"NPT", "J", SAVE_EEPROM_4k, "Puyo Puyon Party"}, - {"NPW", "EJP", SAVE_EEPROM_4k, "Pilotwings 64"}, - {"NPY", "J", SAVE_EEPROM_4k, "Puyo Puyo Sun 64"}, - {"NR7", "J", SAVE_EEPROM_16k, "Robot Poncots 64: 7tsu no Umi no Caramel"}, - {"NRA", "J", SAVE_EEPROM_4k, "Rally '99"}, - {"NRC", "EJP", SAVE_EEPROM_4k, "Top Gear Overdrive"}, - {"NRE", "EP", SAVE_SRAM_256k, "Resident Evil 2"}, - {"NRH", "J", SAVE_FLASH_1m, "Rockman Dash"}, - {"NRI", "EP", SAVE_SRAM_256k, "The New Tetris"}, - {"NRS", "EJP", SAVE_EEPROM_4k, "Star Wars: Rogue Squadron"}, - {"NRZ", "EP", SAVE_EEPROM_16k, "Ridge Racer 64"}, - {"NS4", "J", SAVE_SRAM_256k, "Super Robot Taisen 64"}, - {"NS6", "EJ", SAVE_EEPROM_4k, "Star Soldier: Vanishing Earth"}, - {"NSA", "JP", SAVE_EEPROM_4k, "AeroFighters Assault (PAL, Japan)"}, - {"NSC", "EP", SAVE_EEPROM_4k, "Starshot: Space Circus Fever"}, - {"NSI", "J", SAVE_SRAM_256k, "Fushigi no Dungeon: Fuurai no Shiren 2"}, - {"NSM", "EJP", SAVE_EEPROM_4k, "Super Mario 64"}, - {"NSN", "J", SAVE_EEPROM_4k, "Snow Speeder"}, - {"NSQ", "EP", SAVE_FLASH_1m, "StarCraft 64"}, - {"NSS", "J", SAVE_EEPROM_4k, "Super Robot Spirits"}, - {"NSU", "EP", SAVE_EEPROM_4k, "Rocket: Robot on Wheels"}, - {"NSV", "EP", SAVE_EEPROM_4k, "SpaceStation Silicon Valley"}, - {"NSW", "EJP", SAVE_EEPROM_4k, "Star Wars: Shadows of the Empire"}, - {"NT3", "J", SAVE_SRAM_256k, "Toukon Road 2"}, - {"NT6", "J", SAVE_EEPROM_4k, "Tetris 64"}, - {"NT9", "EP", SAVE_FLASH_1m, "Tigger's Honey Hunt"}, - {"NTB", "J", SAVE_EEPROM_4k, "Transformers: Beast Wars Metals 64"}, - {"NTC", "J", SAVE_EEPROM_4k, "64 Trump Collection"}, - {"NTE", "AP", SAVE_SRAM_256k, "1080 Snowboarding"}, - {"NTJ", "EP", SAVE_EEPROM_4k, "Tom and Jerry in Fists of Furry"}, - {"NTM", "EJP", SAVE_EEPROM_4k, "Mischief Makers"}, - {"NTN", "EP", SAVE_EEPROM_4k, "All-Star Tennis 99"}, - {"NTP", "EP", SAVE_EEPROM_4k, "Tetrisphere"}, - {"NTR", "JP", SAVE_EEPROM_4k, "Top Gear Rally (PAL, Japan)"}, - {"NTW", "J", SAVE_EEPROM_4k, "64 de Hakken!! Tamagotchi"}, - {"NTX", "EP", SAVE_EEPROM_4k, "Taz Express"}, - {"NUB", "J", SAVE_EEPROM_16k, "PD Ultraman Battle Collection 64"}, - {"NUM", "J", SAVE_SRAM_256k, "Nushi Zuri 64: Shiokaze ni Notte"}, - {"NUT", "J", SAVE_SRAM_256k, "Nushi Zuri 64"}, - {"NVB", "J", SAVE_SRAM_256k, "Bass Rush: ECOGEAR PowerWorm Championship"}, - {"NVL", "EP", SAVE_EEPROM_4k, "V-Rally 99 (USA, PAL)"}, - {"NVP", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 64"}, - {"NVY", "J", SAVE_EEPROM_4k, "V-Rally 99 (Japan)"}, - {"NW2", "EP", SAVE_SRAM_256k, "WCW/nWo Revenge"}, - {"NW4", "EP", SAVE_FLASH_1m, "WWF No Mercy"}, - {"NWC", "J", SAVE_EEPROM_4k, "Wild Choppers"}, - {"NWL", "EP", SAVE_SRAM_256k, "Waialae Country Club: True Golf Classics"}, - {"NWQ", "E", SAVE_EEPROM_4k, "Rally Challenge 2000"}, - {"NWR", "EJP", SAVE_EEPROM_4k, "Wave Race 64"}, - {"NWT", "J", SAVE_EEPROM_4k, "Wetrix (Japan)"}, - {"NWU", "P", SAVE_EEPROM_4k, "Worms Armageddon (PAL)"}, - {"NWX", "EJP", SAVE_SRAM_256k, "WWF WrestleMania 2000"}, - {"NXO", "E", SAVE_EEPROM_4k, "Cruis'n Exotica"}, - {"NYK", "J", SAVE_EEPROM_4k, "Yakouchuu II: Satsujin Kouro"}, - {"NYS", "EJP", SAVE_EEPROM_16k, "Yoshi's Story"}, - {"NYW", "EJ", SAVE_SRAM_256k, "Harvest Moon 64"}, - {"NZL", "P", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (PAL)"}, - {"NZS", "EJP", SAVE_FLASH_1m, "Legend of Zelda: Majora's Mask"}, + {"NNM", "E", SAVE_NONE, "Namco Museum 64"}, + {"NDM", "E", SAVE_NONE, "Doom 64"}, + {"NGN", "E", SAVE_EEPROM_4k, "GoldenEye 007"}, + // Copied from CEN64 with small edits: https://github.com/n64dev/cen64/blob/master/device/cart_db.c + {"CFZ", "EJ", SAVE_SRAM_256k, "F-Zero X (NTSC)"}, + {"CLB", "EJ", SAVE_EEPROM_4k, "Mario Party (NTSC)"}, + {"CP2", "J", SAVE_FLASH_1m, "Pokémon Stadium 2 (Japan)"}, + {"CPS", "J", SAVE_SRAM_256k, "Pokémon Stadium (Japan)"}, + {"CZL", "EJ", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (NTSC)"}, + {"N3D", "J", SAVE_EEPROM_16k, "Doraemon 3: Nobita no Machi SOS!"}, + {"N3H", "J", SAVE_SRAM_256k, "Ganbare! Nippon! Olympics 2000"}, + {"NA2", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 2"}, + {"NAB", "JP", SAVE_EEPROM_4k, "Air Boarder 64"}, + {"NAD", "E", SAVE_EEPROM_4k, "Worms Armageddon (USA)"}, + {"NAF", "J", SAVE_FLASH_1m, "Doubutsu no Mori"}, + {"NAG", "EJP", SAVE_EEPROM_4k, "AeroGauge"}, + {"NAL", "EJPU", SAVE_SRAM_256k, "Super Smash Bros"}, + {"NB5", "J", SAVE_SRAM_256k, "Biohazard 2"}, + {"NB6", "J", SAVE_EEPROM_4k, "Super B-Daman: Battle Phoenix 64"}, + {"NB7", "EJPU", SAVE_EEPROM_16k, "Banjo-Tooie"}, + {"NBC", "EJP", SAVE_EEPROM_4k, "Blast Corps"}, + {"NBD", "EJP", SAVE_EEPROM_4k, "Bomberman Hero"}, + {"NBH", "EP", SAVE_EEPROM_4k, "Body Harvest"}, + {"NBK", "EJP", SAVE_EEPROM_4k, "Banjo-Kazooie"}, + {"NBM", "EJP", SAVE_EEPROM_4k, "Bomberman 64"}, + {"NBN", "J", SAVE_EEPROM_4k, "Bakuretsu Muteki Bangaioh"}, + {"NBV", "EJ", SAVE_EEPROM_4k, "Bomberman 64: The Second Attack!"}, + {"NCC", "DEP", SAVE_FLASH_1m, "Command & Conquer"}, + {"NCG", "J", SAVE_EEPROM_4k, "Choro Q 64 2: Hacha-Mecha Grand Prix Race"}, + {"NCH", "EP", SAVE_EEPROM_4k, "Chopper Attack"}, + {"NCK", "E", SAVE_FLASH_1m, "NBA Courtside 2"}, + {"NCR", "EJP", SAVE_EEPROM_4k, "Penny Racers"}, + {"NCT", "EJP", SAVE_EEPROM_4k, "Chameleon Twist"}, + {"NCU", "EP", SAVE_EEPROM_4k, "Cruis'n USA"}, + {"NCW", "EP", SAVE_EEPROM_16k, "Cruis'n World"}, + {"NCX", "J", SAVE_EEPROM_4k, "Custom Robo"}, + {"NCZ", "J", SAVE_EEPROM_16k, "Custom Robo V2"}, + {"ND2", "J", SAVE_EEPROM_16k, "Doraemon 2: Nobita to Hikari no Shinden"}, + {"ND3", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku"}, + {"ND4", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku Gaiden: Legend of Cornell"}, + {"ND6", "J", SAVE_EEPROM_16k, "Densha de Go! 64"}, + {"NDA", "J", SAVE_FLASH_1m, "Derby Stallion 64"}, + {"NDK", "J", SAVE_EEPROM_4k, "Space Dynamites"}, + {"NDO", "EJP", SAVE_EEPROM_16k, "Donkey Kong 64"}, + {"NDP", "E", SAVE_FLASH_1m, "Dinosaur Planet"}, + {"NDR", "J", SAVE_EEPROM_4k, "Doraemon: Nobita to 3tsu no Seireiseki"}, + {"NDU", "EP", SAVE_EEPROM_4k, "Duck Dodgers"}, + {"NDY", "EJP", SAVE_EEPROM_4k, "Diddy Kong Racing"}, + {"NEA", "EP", SAVE_EEPROM_4k, "PGA European Tour"}, + {"NEP", "EJP", SAVE_EEPROM_16k, "Star Wars Episode I: Racer"}, + {"NER", "E", SAVE_EEPROM_4k, "AeroFighters Assault (USA)"}, + {"NEV", "J", SAVE_EEPROM_16k, "Neon Genesis Evangelion"}, + {"NF2", "P", SAVE_EEPROM_4k, "F-1 World Grand Prix II"}, + {"NFG", "E", SAVE_EEPROM_4k, "Fighter Destiny 2"}, + {"NFH", "EP", SAVE_EEPROM_4k, "Bass Hunter 64"}, + {"NFU", "EP", SAVE_EEPROM_16k, "Conker's Bad Fur Day"}, + {"NFW", "DEFJP", SAVE_EEPROM_4k, "F-1 World Grand Prix"}, + {"NFX", "EJPU", SAVE_EEPROM_4k, "Star Fox 64"}, + {"NFY", "J", SAVE_EEPROM_4k, "Kakutou Denshou: F-Cup Maniax"}, + {"NFZ", "P", SAVE_SRAM_256k, "F-Zero X (PAL)"}, + {"NG6", "J", SAVE_SRAM_256k, "Ganbare Goemon: Dero Dero Douchuu Obake Tenkomori"}, + {"NGC", "EP", SAVE_EEPROM_16k, "GT 64: Championship Edition"}, + {"NGE", "EJP", SAVE_EEPROM_4k, "GoldenEye 007"}, + {"NGL", "J", SAVE_EEPROM_4k, "Getter Love!!"}, + {"NGP", "J", SAVE_SRAM_256k, "Goemon: Mononoke Sugoroku"}, + {"NGT", "J", SAVE_EEPROM_16k, "City-Tour GP: Zen-Nihon GT Senshuken"}, + {"NGU", "J", SAVE_EEPROM_4k, "Tsumi to Batsu: Hoshi no Keishousha"}, + {"NGV", "EP", SAVE_EEPROM_4k, "Glover"}, + {"NHA", "J", SAVE_EEPROM_4k, "Bomber Man 64 (Japan)"}, + {"NHF", "J", SAVE_EEPROM_4k, "64 Hanafuda: Tenshi no Yakusoku"}, + {"NHP", "J", SAVE_EEPROM_4k, "Heiwa Pachinko World 64"}, + {"NHY", "J", SAVE_SRAM_256k, "Hybrid Heaven (Japan)"}, + {"NIB", "J", SAVE_SRAM_256k, "Itoi Shigesato no Bass Tsuri No. 1 Kettei Ban!"}, + {"NIC", "E", SAVE_EEPROM_4k, "Indy Racing 2000"}, + {"NIJ", "EP", SAVE_EEPROM_4k, "Indiana Jones and the Infernal Machine"}, + {"NIM", "J", SAVE_EEPROM_16k, "Ide Yosuke no Mahjong Juku"}, + {"NIR", "J", SAVE_EEPROM_4k, "Utchan Nanchan no Hono no Challenger: Denryuu Ira Ira Bou"}, + {"NJ5", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 5"}, + {"NJD", "E", SAVE_FLASH_1m, "Jet Force Gemini (Kiosk Demo)"}, + {"NJF", "EJP", SAVE_FLASH_1m, "Jet Force Gemini"}, + {"NJG", "J", SAVE_SRAM_256k, "Jinsei Game 64"}, + {"NJM", "EP", SAVE_EEPROM_4k, "Earthworm Jim 3D"}, + {"NK2", "EJP", SAVE_EEPROM_4k, "Snowboard Kids 2"}, + {"NK4", "EJP", SAVE_EEPROM_16k, "Kirby 64: The Crystal Shards"}, + {"NKA", "DEFJP", SAVE_EEPROM_4k, "Fighters Destiny"}, + {"NKG", "EP", SAVE_SRAM_256k, "MLB featuring Ken Griffey Jr."}, + {"NKI", "EP", SAVE_EEPROM_4k, "Killer Instinct Gold"}, + {"NKJ", "E", SAVE_FLASH_1m, "Ken Griffey Jr.'s Slugfest"}, + {"NKT", "EJP", SAVE_EEPROM_4k, "Mario Kart 64"}, + {"NLB", "P", SAVE_EEPROM_4k, "Mario Party (PAL)"}, + {"NLL", "J", SAVE_EEPROM_4k, "Last Legion UX"}, + {"NLR", "EJP", SAVE_EEPROM_4k, "Lode Runner 3D"}, + {"NM6", "E", SAVE_FLASH_1m, "Mega Man 64"}, + {"NM8", "EJP", SAVE_EEPROM_16k, "Mario Tennis"}, + {"NMF", "EJP", SAVE_SRAM_256k, "Mario Golf"}, + {"NMG", "DEP", SAVE_EEPROM_4k, "Monaco Grand Prix"}, + {"NMI", "DEFIPS", SAVE_EEPROM_4k, "Mission: Impossible"}, + {"NML", "EJP", SAVE_EEPROM_4k, "Mickey's Speedway USA"}, + {"NMO", "E", SAVE_EEPROM_4k, "Monopoly"}, + {"NMQ", "EJP", SAVE_FLASH_1m, "Paper Mario"}, + {"NMR", "EJP", SAVE_EEPROM_4k, "Multi Racing Championship"}, + {"NMS", "J", SAVE_EEPROM_4k, "Morita Shougi 64"}, + {"NMU", "E", SAVE_EEPROM_4k, "Big Mountain 2000"}, + {"NMV", "EJP", SAVE_EEPROM_16k, "Mario Party 3"}, + {"NMW", "EJP", SAVE_EEPROM_4k, "Mario Party 2"}, + {"NMX", "EJP", SAVE_EEPROM_16k, "Excitebike 64"}, + {"NN6", "E", SAVE_EEPROM_4k, "Dr. Mario 64"}, + {"NNA", "EP", SAVE_EEPROM_4k, "Star Wars Episode I: Battle for Naboo"}, + {"NNB", "EP", SAVE_EEPROM_16k, "Kobe Bryant in NBA Courtside"}, + {"NOB", "EJ", SAVE_SRAM_256k, "Ogre Battle 64: Person of Lordly Caliber"}, + {"NOS", "J", SAVE_EEPROM_4k, "64 Oozumou"}, + {"NP2", "J", SAVE_EEPROM_4k, "Chou Kuukan Nighter Pro Yakyuu King 2"}, + {"NP3", "DEFIJPS", SAVE_FLASH_1m, "Pokémon Stadium 2"}, + {"NP6", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 6"}, + {"NPA", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 2000"}, + {"NPD", "EJP", SAVE_EEPROM_16k, "Perfect Dark"}, + {"NPE", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu Basic Ban 2001"}, + {"NPF", "DEFIJPSU", SAVE_FLASH_1m, "Pokémon Snap"}, + {"NPG", "EJ", SAVE_EEPROM_4k, "Hey You, Pikachu!"}, + {"NPH", "E", SAVE_FLASH_1m, "Pokémon Snap Station (Kiosk Demo)"}, + {"NPM", "P", SAVE_SRAM_256k, "Premier Manager 64"}, + {"NPN", "DEFP", SAVE_FLASH_1m, "Pokémon Puzzle League"}, + {"NPO", "DEFIPS", SAVE_FLASH_1m, "Pokémon Stadium (USA, PAL)"}, + {"NPP", "J", SAVE_EEPROM_16k, "Parlor! Pro 64: Pachinko Jikki Simulation Game"}, + {"NPS", "J", SAVE_SRAM_256k, "Jikkyou J.League 1999: Perfect Striker 2"}, + {"NPT", "J", SAVE_EEPROM_4k, "Puyo Puyon Party"}, + {"NPW", "EJP", SAVE_EEPROM_4k, "Pilotwings 64"}, + {"NPY", "J", SAVE_EEPROM_4k, "Puyo Puyo Sun 64"}, + {"NR7", "J", SAVE_EEPROM_16k, "Robot Poncots 64: 7tsu no Umi no Caramel"}, + {"NRA", "J", SAVE_EEPROM_4k, "Rally '99"}, + {"NRC", "EJP", SAVE_EEPROM_4k, "Top Gear Overdrive"}, + {"NRE", "EP", SAVE_SRAM_256k, "Resident Evil 2"}, + {"NRH", "J", SAVE_FLASH_1m, "Rockman Dash"}, + {"NRI", "EP", SAVE_SRAM_256k, "The New Tetris"}, + {"NRS", "EJP", SAVE_EEPROM_4k, "Star Wars: Rogue Squadron"}, + {"NRZ", "EP", SAVE_EEPROM_16k, "Ridge Racer 64"}, + {"NS4", "J", SAVE_SRAM_256k, "Super Robot Taisen 64"}, + {"NS6", "EJ", SAVE_EEPROM_4k, "Star Soldier: Vanishing Earth"}, + {"NSA", "JP", SAVE_EEPROM_4k, "AeroFighters Assault (PAL, Japan)"}, + {"NSC", "EP", SAVE_EEPROM_4k, "Starshot: Space Circus Fever"}, + {"NSI", "J", SAVE_SRAM_256k, "Fushigi no Dungeon: Fuurai no Shiren 2"}, + {"NSM", "EJP", SAVE_EEPROM_4k, "Super Mario 64"}, + {"NSN", "J", SAVE_EEPROM_4k, "Snow Speeder"}, + {"NSQ", "EP", SAVE_FLASH_1m, "StarCraft 64"}, + {"NSS", "J", SAVE_EEPROM_4k, "Super Robot Spirits"}, + {"NSU", "EP", SAVE_EEPROM_4k, "Rocket: Robot on Wheels"}, + {"NSV", "EP", SAVE_EEPROM_4k, "SpaceStation Silicon Valley"}, + {"NSW", "EJP", SAVE_EEPROM_4k, "Star Wars: Shadows of the Empire"}, + {"NT3", "J", SAVE_SRAM_256k, "Toukon Road 2"}, + {"NT6", "J", SAVE_EEPROM_4k, "Tetris 64"}, + {"NT9", "EP", SAVE_FLASH_1m, "Tigger's Honey Hunt"}, + {"NTB", "J", SAVE_EEPROM_4k, "Transformers: Beast Wars Metals 64"}, + {"NTC", "J", SAVE_EEPROM_4k, "64 Trump Collection"}, + {"NTE", "AP", SAVE_SRAM_256k, "1080 Snowboarding"}, + {"NTJ", "EP", SAVE_EEPROM_4k, "Tom and Jerry in Fists of Furry"}, + {"NTM", "EJP", SAVE_EEPROM_4k, "Mischief Makers"}, + {"NTN", "EP", SAVE_EEPROM_4k, "All-Star Tennis 99"}, + {"NTP", "EP", SAVE_EEPROM_4k, "Tetrisphere"}, + {"NTR", "JP", SAVE_EEPROM_4k, "Top Gear Rally (PAL, Japan)"}, + {"NTW", "J", SAVE_EEPROM_4k, "64 de Hakken!! Tamagotchi"}, + {"NTX", "EP", SAVE_EEPROM_4k, "Taz Express"}, + {"NUB", "J", SAVE_EEPROM_16k, "PD Ultraman Battle Collection 64"}, + {"NUM", "J", SAVE_SRAM_256k, "Nushi Zuri 64: Shiokaze ni Notte"}, + {"NUT", "J", SAVE_SRAM_256k, "Nushi Zuri 64"}, + {"NVB", "J", SAVE_SRAM_256k, "Bass Rush: ECOGEAR PowerWorm Championship"}, + {"NVL", "EP", SAVE_EEPROM_4k, "V-Rally 99 (USA, PAL)"}, + {"NVP", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 64"}, + {"NVY", "J", SAVE_EEPROM_4k, "V-Rally 99 (Japan)"}, + {"NW2", "EP", SAVE_SRAM_256k, "WCW/nWo Revenge"}, + {"NW4", "EP", SAVE_FLASH_1m, "WWF No Mercy"}, + {"NWC", "J", SAVE_EEPROM_4k, "Wild Choppers"}, + {"NWL", "EP", SAVE_SRAM_256k, "Waialae Country Club: True Golf Classics"}, + {"NWQ", "E", SAVE_EEPROM_4k, "Rally Challenge 2000"}, + {"NWR", "EJP", SAVE_EEPROM_4k, "Wave Race 64"}, + {"NWT", "J", SAVE_EEPROM_4k, "Wetrix (Japan)"}, + {"NWU", "P", SAVE_EEPROM_4k, "Worms Armageddon (PAL)"}, + {"NWX", "EJP", SAVE_SRAM_256k, "WWF WrestleMania 2000"}, + {"NXO", "E", SAVE_EEPROM_4k, "Cruis'n Exotica"}, + {"NYK", "J", SAVE_EEPROM_4k, "Yakouchuu II: Satsujin Kouro"}, + {"NYS", "EJP", SAVE_EEPROM_16k, "Yoshi's Story"}, + {"NYW", "EJ", SAVE_SRAM_256k, "Harvest Moon 64"}, + {"NZL", "P", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (PAL)"}, + {"NZS", "EJP", SAVE_FLASH_1m, "Legend of Zelda: Majora's Mask"}, }; -} \ No newline at end of file +} // namespace n64 diff --git a/src/backend/Scheduler.cpp b/src/backend/Scheduler.cpp index e4b8788..45c0bc4 100644 --- a/src/backend/Scheduler.cpp +++ b/src/backend/Scheduler.cpp @@ -45,7 +45,7 @@ void Scheduler::HandleEvents() { break; case STOP: core.Stop(); - core.rom = {}; + core.romPath = {}; break; case RESET: core.Reset(); diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 7391202..29fb0cf 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -90,7 +90,10 @@ FORCE_INLINE void SetROMCIC(u32 checksum, ROM &rom) { } } -void Mem::LoadROM(const bool isArchive, const std::string &filename) { +ROM Mem::LoadROM(const bool isArchive, const std::string &filename) { + ROM rom; + rom.cart.resize(CART_SIZE); + std::ranges::fill(rom.cart, 0); u32 endianness; { size_t sizeAdjusted; @@ -133,7 +136,8 @@ void Mem::LoadROM(const bool isArchive, const std::string &filename) { SetROMCIC(checksum, rom); endianness = std::byteswap(ircolib::ReadAccess(rom.cart, 0)); Util::SwapN64Rom(rom.cart, endianness); - rom.pal = IsROMPAL(); + rom.pal = IsROMPAL(rom); + return rom; } template <> @@ -451,7 +455,7 @@ void Mem::Write(const u32 paddr, u64 val) { template <> u32 Mem::BackupRead(const u32 addr) { - switch (saveType) { + switch (rom.saveType) { case SAVE_NONE: return 0; case SAVE_EEPROM_4k: @@ -469,7 +473,7 @@ u32 Mem::BackupRead(const u32 addr) { template <> u8 Mem::BackupRead(const u32 addr) { - switch (saveType) { + switch (rom.saveType) { case SAVE_NONE: return 0; case SAVE_EEPROM_4k: @@ -492,7 +496,7 @@ u8 Mem::BackupRead(const u32 addr) { template <> void Mem::BackupWrite(const u32 addr, const u32 val) { - switch (saveType) { + switch (rom.saveType) { case SAVE_NONE: warn("Accessing cartridge with save type SAVE_NONE in write word"); break; @@ -511,7 +515,7 @@ void Mem::BackupWrite(const u32 addr, const u32 val) { template <> void Mem::BackupWrite(const u32 addr, const u8 val) { - switch (saveType) { + switch (rom.saveType) { case SAVE_NONE: warn("Accessing cartridge with save type SAVE_NONE in write word"); break; diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 8279868..f3e056e 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -29,6 +29,7 @@ struct ROM { char gameNameCart[20]; char code[4]; ROMHeader header; + SaveType saveType = SAVE_NONE; size_t mask; CICType cicType; std::vector cart; @@ -78,7 +79,7 @@ struct Mem { ~Mem() = default; void Reset(); void LoadSRAM(SaveType, fs::path); - void LoadROM(bool, const std::string &); + static ROM LoadROM(bool, const std::string &); [[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); } @@ -119,7 +120,6 @@ struct Mem { MMIO mmio; ROM rom; - SaveType saveType = SAVE_NONE; Flash flash; private: @@ -135,9 +135,9 @@ struct Mem { std::string sramPath{}; mio::mmap_sink saveData{}; - [[nodiscard]] FORCE_INLINE bool IsROMPAL() const { + [[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, [this](char a) { return rom.cart[0x3d] == a; }); + return std::ranges::any_of(pal_codes, [&rom](char a) { return rom.cart[0x3d] == a; }); } }; } // namespace n64 diff --git a/src/backend/core/mmio/AI.cpp b/src/backend/core/mmio/AI.cpp index 9129c83..2914b29 100644 --- a/src/backend/core/mmio/AI.cpp +++ b/src/backend/core/mmio/AI.cpp @@ -64,8 +64,8 @@ void AI::Write(const u32 addr, const u32 val) { { const u32 oldDacFreq = dac.freq; dacRate = val & 0x3FFF; - dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.IsROMPAL()) / (dacRate + 1)) * 1.037; - dac.period = GetVideoFrequency(mem.IsROMPAL()) / dac.freq; + dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.rom.pal) / (dacRate + 1)) * 1.037; + dac.period = GetVideoFrequency(mem.rom.pal) / dac.freq; if (oldDacFreq != dac.freq) { device.AdjustSampleRate(dac.freq); } diff --git a/src/backend/core/mmio/PI.cpp b/src/backend/core/mmio/PI.cpp index 54f9fd7..3f2f1c1 100644 --- a/src/backend/core/mmio/PI.cpp +++ b/src/backend/core/mmio/PI.cpp @@ -507,7 +507,7 @@ void PI::DMA() { n64::Mem &mem = n64::Core::GetMem(); const s32 len = rdLen + 1; trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr); - if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) { + if (mem.rom.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) { cartAddr = SREGION_PI_SRAM | ((cartAddr & (1_mb - 1)) << 1); } @@ -535,7 +535,7 @@ void PI::DMA() { const s32 len = wrLen + 1; trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr); - if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) { + if (mem.rom.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) { cartAddr = SREGION_PI_SRAM | ((cartAddr & (1_mb - 1)) << 1); } diff --git a/src/backend/core/mmio/PIF.cpp b/src/backend/core/mmio/PIF.cpp index ace3aa2..62e73e6 100644 --- a/src/backend/core/mmio/PIF.cpp +++ b/src/backend/core/mmio/PIF.cpp @@ -347,10 +347,10 @@ void PIF::MempakWrite(u8 *cmd, u8 *res) { void PIF::EepromRead(const u8 *cmd, u8 *res) const { n64::Mem &mem = n64::Core::GetMem(); - assert(mem.saveType == SAVE_EEPROM_4k || mem.saveType == SAVE_EEPROM_16k); + assert(mem.rom.saveType == SAVE_EEPROM_4k || mem.rom.saveType == SAVE_EEPROM_16k); if (channel == 4) { const u8 offset = cmd[3]; - if ((offset * 8) >= GetSaveSize(mem.saveType)) { + if ((offset * 8) >= GetSaveSize(mem.rom.saveType)) { panic("Out of range EEPROM read! offset: {:02X}", offset); } @@ -362,10 +362,10 @@ void PIF::EepromRead(const u8 *cmd, u8 *res) const { void PIF::EepromWrite(const u8 *cmd, u8 *res) { n64::Mem &mem = n64::Core::GetMem(); - assert(mem.saveType == SAVE_EEPROM_4k || mem.saveType == SAVE_EEPROM_16k); + assert(mem.rom.saveType == SAVE_EEPROM_4k || mem.rom.saveType == SAVE_EEPROM_16k); if (channel == 4) { const u8 offset = cmd[3]; - if ((offset * 8) >= GetSaveSize(mem.saveType)) { + if ((offset * 8) >= GetSaveSize(mem.rom.saveType)) { panic("Out of range EEPROM write! offset: {:02X}", offset); } diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index b83536d..1b26a0f 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -11,43 +11,71 @@ #include #include +void KaizenGui::populateRomsList(const std::string &romsPath) { + centralWidget->setCurrentWidget(romPathNotSet); + if (!romsPath.empty()) { + int i = 0; + centralWidget->setCurrentWidget(romsList); + + for (const auto &file : fs::recursive_directory_iterator{romsPath}) { + if (!file.is_regular_file()) + continue; + + auto filename = file.path().lexically_normal().string(); + + bool isPlain = std::ranges::any_of(std::array{".n64", ".z64", ".v64"}, + [&](const std::string &ext) { return file.path().extension() == ext; }); + + bool isArchive = + std::ranges::any_of(std::array{".zip", ".7z", ".rar", ".tar"}, + [&](const std::string &ext) { return file.path().extension() == ext; }); + + if (!isArchive && !isPlain) + continue; + + auto rom = n64::Mem::LoadROM(isArchive, filename); + auto regions = n64::GameDB::match(rom); + + romsListPaths.push_back(filename); + romsList->insertRow(i); + romsList->setItem(i, 0, new QTableWidgetItem(core.mem->rom.gameNameDB.c_str())); + romsList->setItem(i, 1, new QTableWidgetItem(regions.c_str())); + romsList->setItem(i, 2, new QTableWidgetItem("Never")); + romsList->setItem(i, 3, new QTableWidgetItem("0h 0m 0s")); + i++; + } + } + currentHomeWidget = centralWidget->currentWidget(); +} + KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::UserScope) { SDL_InitSubSystem(SDL_INIT_GAMEPAD); hide(); - auto romsNotFoundLabel = new QLabel(R"(Kaizen could not find any ROMs. Set the path in "Settings -> General")"); - romsList->verticalHeader()->hide(); + romsList->setSizePolicy({QSizePolicy::Maximum, QSizePolicy::Maximum}); + romsList->setSelectionMode(QAbstractItemView::SingleSelection); romsList->setSelectionBehavior(QAbstractItemView::SelectRows); romsList->setEditTriggers(QAbstractItemView::NoEditTriggers); romsList->setSortingEnabled(true); - romsList->setColumnCount(3); + romsList->setColumnCount(4); romsList->setHorizontalHeaderItem(0, romName); - romsList->setHorizontalHeaderItem(1, romLastPlayed); - romsList->setHorizontalHeaderItem(2, romTimePlayed); + romsList->setHorizontalHeaderItem(1, romRegions); + romsList->setHorizontalHeaderItem(2, romLastPlayed); + romsList->setHorizontalHeaderItem(3, romTimePlayed); - auto romsPath = Options::GetRomsPath(); - if (!romsPath.empty()) { - int i = 0; - centralWidget->addWidget(romsList); - for (const auto &file : fs::recursive_directory_iterator{romsPath}) { - if (file.is_regular_file() && - std::ranges::any_of(std::array{".n64", ".z64", ".v64"}, - [&](const std::string &ext) { return file.path().extension() == ext; })) { - romsList->insertRow(i); - romsList->setItem(i, 0, new QTableWidgetItem(file.path().stem().string().c_str())); - romsList->setItem(i, 1, new QTableWidgetItem("Never")); - romsList->setItem(i, 2, new QTableWidgetItem("0h 0m 0s")); - i++; - } - } - } else { - centralWidget->addWidget(romsNotFoundLabel); - } + vulkanWidget = new RenderWidget(); + vulkanWidget->hide(); + + centralWidget->addWidget(vulkanWidget); + centralWidget->addWidget(romsList); + centralWidget->addWidget(romPathNotSet); + + populateRomsList(Options::GetRomsPath()); connect(romsList, &QTableWidget::cellDoubleClicked, this, [&](int row, int) { - auto fileToLoad = fs::path(romsPath) / romsList->item(row, 0)->text().toStdString(); + auto fileToLoad = fs::path(Options::GetRomsPath()) / romsListPaths[row]; std::println("{}", fileToLoad.string()); LoadROM(fileToLoad.string()); }); @@ -57,11 +85,6 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("windowState").toByteArray()); - vulkanWidget = new RenderWidget(); - vulkanWidget->hide(); - - centralWidget->addWidget(vulkanWidget); - cpuTypeLabel = new QLabel("Interpreter"); if (Options::GetCpuType() == n64::CachedInterpreter) cpuTypeLabel->setText("Cached Interpreter"); @@ -108,7 +131,7 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User auto open = fileMenu->addAction("Open"); open->setShortcut(QKeyCombination(Qt::CTRL, Qt::Key_O)); connect(open, &QAction::triggered, this, [&] { - auto originPath = romsPath.empty() ? QDir::currentPath() : romsPath.c_str(); + auto originPath = Options::GetRomsPath().empty() ? QDir::currentPath() : Options::GetRomsPath().c_str(); auto fileToLoad = QFileDialog::getOpenFileName(this, "Select a Nintendo 64 ROM", originPath, "N64 ROM (*.z64 *.n64 *.v64)") .toStdString(); @@ -123,26 +146,8 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User auto settingsMenu = emulationMenu->addAction("Settings"); settingsWindow = new SettingsWindow(); connect(settingsMenu, &QAction::triggered, settingsWindow, &SettingsWindow::show); - connect(settingsWindow->general, &GeneralSettings::romFolderSelected, this, [&] { - romsPath = Options::GetRomsPath(); - if (!romsPath.empty()) { - int i = 0; - centralWidget->addWidget(romsList); - for (const auto &file : fs::recursive_directory_iterator{romsPath}) { - if (file.is_regular_file() && - std::ranges::any_of(std::array{".n64", ".z64", ".v64"}, - [&](const std::string &ext) { return file.path().extension() == ext; })) { - romsList->insertRow(i); - romsList->setItem(i, 0, new QTableWidgetItem(file.path().stem().string().c_str())); - romsList->setItem(i, 1, new QTableWidgetItem("Never")); - romsList->setItem(i, 2, new QTableWidgetItem("0h 0m 0s")); - i++; - } - } - } else { - centralWidget->addWidget(romsNotFoundLabel); - } - }); + connect(settingsWindow->general, &GeneralSettings::romFolderSelected, this, + [&] { populateRomsList(Options::GetRomsPath()); }); connect(settingsWindow->cpu, &CPUSettings::cpuTypeChanged, this, [&] { core.cpuType = Options::GetCpuType(); @@ -185,7 +190,7 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User emulationMenu->addAction(stop); connect(stop, &QAction::triggered, this, [&] { Scheduler::GetInstance().EnqueueRelative(0, STOP); - centralWidget->setCurrentWidget(romsList); + centralWidget->setCurrentWidget(currentHomeWidget); }); auto helpMenu = menuBar()->addMenu("Help"); diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index 61d7acb..e166c3b 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -18,6 +18,7 @@ class KaizenGui final : QMainWindow { void updateKeys(); void updateAxis(); bool eventFilter(QObject *, QEvent *) override; + void populateRomsList(const std::string &); public: explicit KaizenGui() noexcept; @@ -32,8 +33,13 @@ class KaizenGui final : QMainWindow { bool fastForward = false; bool minimized = false; + std::vector romsListPaths{}; + + QWidget *currentHomeWidget; + QLabel *romPathNotSet = new QLabel(R"(Kaizen could not find any ROMs. Set the path in "Settings -> General")"); QTableWidget *romsList = new QTableWidget(); QTableWidgetItem *romName = new QTableWidgetItem("Name"); + QTableWidgetItem *romRegions = new QTableWidgetItem("Regions"); QTableWidgetItem *romLastPlayed = new QTableWidgetItem("Last played"); QTableWidgetItem *romTimePlayed = new QTableWidgetItem("Time played"); QStackedWidget *centralWidget = new QStackedWidget(); diff --git a/src/utils/log.hpp b/src/utils/log.hpp index 966fd6f..3163564 100644 --- a/src/utils/log.hpp +++ b/src/utils/log.hpp @@ -7,32 +7,61 @@ #endif namespace Util { -enum LogLevel : u8 { Trace, Debug, Warn, Info, Error, Always }; +enum LogLevel : u8 { Trace, Debug, Info, Warn, Error, Always }; #ifndef NDEBUG static constexpr auto globalLogLevel = Debug; #else -static constexpr auto globalLogLevel = Info; +static constexpr auto globalLogLevel = Warn; #endif template void print(const std::format_string fmt, Args... args) { - if (messageType >= globalLogLevel) { - if (messageType <= Debug) { + if (messageType >= globalLogLevel) { + if (messageType <= Debug) { #ifndef NDEBUG - std::println(fmt, std::forward(args)...); + std::println(fmt, std::forward(args)...); #endif - } else { - std::println(fmt, std::forward(args)...); + } else { + std::println(fmt, std::forward(args)...); + } } - } } -#define panic(fmt, ...) do { Util::print("[FATAL] " fmt __VA_OPT__(,) __VA_ARGS__); exit(-1); } while(0) -#define error(fmt, ...) do { Util::print("[ERROR] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0) -#define warn(fmt, ...) do { Util::print("[WARN] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0) -#define info(fmt, ...) do { Util::print("[INFO] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0) -#define debug(fmt, ...) do { Util::print("[DEBUG] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0) -#define trace(fmt, ...) do { Util::print("[TRACE] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0) -#define always(fmt, ...) do { Util::print(fmt __VA_OPT__(,) __VA_ARGS__); } while(0) +#define panic(fmt, ...) \ + do { \ + Util::print("[FATAL] " fmt __VA_OPT__(, ) __VA_ARGS__); \ + exit(-1); \ + } \ + while (0) +#define error(fmt, ...) \ + do { \ + Util::print("[ERROR] " fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + while (0) +#define warn(fmt, ...) \ + do { \ + Util::print("[WARN] " fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + while (0) +#define info(fmt, ...) \ + do { \ + Util::print("[INFO] " fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + while (0) +#define debug(fmt, ...) \ + do { \ + Util::print("[DEBUG] " fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + while (0) +#define trace(fmt, ...) \ + do { \ + Util::print("[TRACE] " fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + while (0) +#define always(fmt, ...) \ + do { \ + Util::print(fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + while (0) } // namespace Util