From fc306967f91a55c59d7f4bb63a85fead228238db Mon Sep 17 00:00:00 2001 From: iris Date: Thu, 11 Jun 2026 22:58:00 +0200 Subject: [PATCH] Wow the ROM Header was just completely busted. Game list view works now --- src/backend/GameDB.cpp | 41 ++++++++-------- src/backend/GameDB.hpp | 41 ++++++++-------- src/backend/core/Mem.cpp | 52 +++++++++----------- src/backend/core/Mem.hpp | 14 +++--- src/frontend/KaizenGui.cpp | 99 +++++++++++++++++++++++++++++++++++--- src/frontend/KaizenGui.hpp | 60 +++++++++++------------ 6 files changed, 192 insertions(+), 115 deletions(-) diff --git a/src/backend/GameDB.cpp b/src/backend/GameDB.cpp index 499123b..2d019ef 100644 --- a/src/backend/GameDB.cpp +++ b/src/backend/GameDB.cpp @@ -2,7 +2,7 @@ #include namespace n64 { -const char *regionCodeToReadable(char r) { +const char *GameDB::regionCodeToReadable(char r) { switch (r) { case 0: return "Region-free"; @@ -52,35 +52,36 @@ std::string GameDB::match(ROM &rom) { std::string result = ""; for (const auto &[code, regions, saveType, name] : gamedb) { - bool matches_region = false; + bool matchesRegion = 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 (code != rom.code) + continue; - if (regions[j] == rom.header.countryCode[0]) - matches_region = true; - } + 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 (matches_region) { - rom.saveType = saveType; - rom.gameNameDB = name; - return result; - } + if (regions[j] == rom.header.countryCode) + matchesRegion = true; + } - info("Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has " - "region {}", - name, regions, rom.header.countryCode[0]); + if (matchesRegion) { 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); + rom.saveType = saveType; + rom.gameNameDB = name; + return result; } - info("Did not match any Game DB entries. Code: {} Region: {}", rom.code, rom.header.countryCode[0]); + info("Did not match any Game DB entries. Code: {} Region: {}", rom.code, rom.header.countryCode); rom.gameNameDB = ""; rom.saveType = SAVE_NONE; diff --git a/src/backend/GameDB.hpp b/src/backend/GameDB.hpp index 8a62bc1..3b443ec 100644 --- a/src/backend/GameDB.hpp +++ b/src/backend/GameDB.hpp @@ -14,23 +14,24 @@ struct GameDBEntry { namespace GameDB { std::string match(ROM &); -} + const char *regionCodeToReadable(char); +} // namespace GameDB 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)"}, + {"CFZ", "EJ", SAVE_SRAM_256k, "F-Zero X"}, + {"CLB", "EJ", SAVE_EEPROM_4k, "Mario Party"}, + {"CP2", "J", SAVE_FLASH_1m, "Pokémon Stadium 2"}, + {"CPS", "J", SAVE_SRAM_256k, "Pokémon Stadium"}, + {"CZL", "EJ", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time"}, {"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)"}, + {"NAD", "E", SAVE_EEPROM_4k, "Worms Armageddon"}, {"NAF", "J", SAVE_FLASH_1m, "Doubutsu no Mori"}, {"NAG", "EJP", SAVE_EEPROM_4k, "AeroGauge"}, {"NAL", "EJPU", SAVE_SRAM_256k, "Super Smash Bros"}, @@ -67,7 +68,7 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NER", "E", SAVE_EEPROM_4k, "AeroFighters Assault"}, {"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"}, @@ -76,7 +77,7 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NFZ", "P", SAVE_SRAM_256k, "F-Zero X"}, {"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"}, @@ -85,10 +86,10 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NHA", "J", SAVE_EEPROM_4k, "Bomber Man 64"}, {"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)"}, + {"NHY", "J", SAVE_SRAM_256k, "Hybrid Heaven"}, {"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"}, @@ -106,7 +107,7 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NLB", "P", SAVE_EEPROM_4k, "Mario Party"}, {"NLL", "J", SAVE_EEPROM_4k, "Last Legion UX"}, {"NLR", "EJP", SAVE_EEPROM_4k, "Lode Runner 3D"}, {"NM6", "E", SAVE_FLASH_1m, "Mega Man 64"}, @@ -139,7 +140,7 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NPO", "DEFIPS", SAVE_FLASH_1m, "Pokémon Stadium"}, {"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"}, @@ -155,7 +156,7 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NSA", "JP", SAVE_EEPROM_4k, "AeroFighters Assault"}, {"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"}, @@ -175,30 +176,30 @@ static const GameDBEntry gamedb[] = { {"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)"}, + {"NTR", "JP", SAVE_EEPROM_4k, "Top Gear Rally"}, {"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)"}, + {"NVL", "EP", SAVE_EEPROM_4k, "V-Rally 99"}, {"NVP", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 64"}, - {"NVY", "J", SAVE_EEPROM_4k, "V-Rally 99 (Japan)"}, + {"NVY", "J", SAVE_EEPROM_4k, "V-Rally 99"}, {"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)"}, + {"NWT", "J", SAVE_EEPROM_4k, "Wetrix"}, + {"NWU", "P", SAVE_EEPROM_4k, "Worms Armageddon"}, {"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)"}, + {"NZL", "P", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time"}, {"NZS", "EJP", SAVE_FLASH_1m, "Legend of Zelda: Majora's Mask"}, }; } // namespace n64 diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 29fb0cf..7573f24 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -91,9 +91,9 @@ FORCE_INLINE void SetROMCIC(u32 checksum, ROM &rom) { } ROM Mem::LoadROM(const bool isArchive, const std::string &filename) { - ROM rom; - rom.cart.resize(CART_SIZE); - std::ranges::fill(rom.cart, 0); + ROM res; + res.cart.resize(CART_SIZE); + std::ranges::fill(res.cart, 0); u32 endianness; { size_t sizeAdjusted; @@ -107,37 +107,33 @@ ROM Mem::LoadROM(const bool isArchive, const std::string &filename) { endianness = std::byteswap(ircolib::ReadAccess(buf, 0)); Util::SwapN64Rom(buf, endianness); - std::ranges::copy(buf, rom.cart.begin()); - rom.mask = sizeAdjusted - 1; - memcpy(&rom.header, buf.data(), sizeof(ROMHeader)); + std::ranges::copy(buf, res.cart.begin()); + res.mask = sizeAdjusted - 1; + memcpy(&res.header, buf.data(), sizeof(ROMHeader)); } - memcpy(rom.gameNameCart, rom.header.imageName, sizeof(rom.header.imageName)); + memcpy(res.gameNameCart, res.header.imageName, sizeof(res.header.imageName)); - rom.header.clockRate = std::byteswap(rom.header.clockRate); - rom.header.programCounter = std::byteswap(rom.header.programCounter); - rom.header.release = std::byteswap(rom.header.release); - rom.header.crc1 = std::byteswap(rom.header.crc1); - rom.header.crc2 = std::byteswap(rom.header.crc2); - rom.header.unknown = std::byteswap(rom.header.unknown); - rom.header.unknown2 = std::byteswap(rom.header.unknown2); - rom.header.manufacturerId = std::byteswap(rom.header.manufacturerId); - rom.header.cartridgeId = std::byteswap(rom.header.cartridgeId); + res.header.clockRate = std::byteswap(res.header.clockRate); + res.header.programCounter = std::byteswap(res.header.programCounter); + res.header.release = std::byteswap(res.header.release); + res.header.unknown = std::byteswap(res.header.unknown); + res.header.unknown2 = std::byteswap(res.header.unknown2); - rom.code[0] = rom.header.manufacturerId & 0xFF; - rom.code[1] = (rom.header.cartridgeId >> 8) & 0xFF; - rom.code[2] = rom.header.cartridgeId & 0xFF; - rom.code[3] = '\0'; + 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(rom.header.imageName) - 1; rom.gameNameCart[i] == ' '; i--) { - rom.gameNameCart[i] = '\0'; + for (int i = sizeof(res.header.imageName) - 1; res.gameNameCart[i] == ' '; i--) { + res.gameNameCart[i] = '\0'; } - const u32 checksum = SDL_crc32(0, &rom.cart[0x40], 0x9C0); - SetROMCIC(checksum, rom); - endianness = std::byteswap(ircolib::ReadAccess(rom.cart, 0)); - Util::SwapN64Rom(rom.cart, endianness); - rom.pal = IsROMPAL(rom); - return rom; + const u32 checksum = SDL_crc32(0, &res.cart[0x40], 0x9C0); + SetROMCIC(checksum, res); + endianness = std::byteswap(ircolib::ReadAccess(res.cart, 0)); + Util::SwapN64Rom(res.cart, endianness); + res.pal = IsROMPAL(res); + return res; } template <> diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index f3e056e..a6039c2 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -11,17 +11,17 @@ namespace n64 { struct ROMHeader { u8 initialValues[4]; - char imageName[20]; - char countryCode[2]; - u16 cartridgeId; u32 clockRate; u32 programCounter; u32 release; - u32 crc1; - u32 crc2; - u32 unknown2; - u32 manufacturerId; u64 unknown; + u64 unknown2; + char imageName[20]; + char unknown3[7]; + u8 categoryCode; + char uniqueCode[2]; + u8 countryCode; + u8 version; }; struct ROM { diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 1b26a0f..3ec1a45 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -21,7 +21,7 @@ void KaizenGui::populateRomsList(const std::string &romsPath) { if (!file.is_regular_file()) continue; - auto filename = file.path().lexically_normal().string(); + auto filename = file.path().string(); bool isPlain = std::ranges::any_of(std::array{".n64", ".z64", ".v64"}, [&](const std::string &ext) { return file.path().extension() == ext; }); @@ -36,9 +36,18 @@ void KaizenGui::populateRomsList(const std::string &romsPath) { auto rom = n64::Mem::LoadROM(isArchive, filename); auto regions = n64::GameDB::match(rom); + if (rom.gameNameDB.empty()) + rom.gameNameDB = fs::path(filename).stem(); + romsListPaths.push_back(filename); romsList->insertRow(i); - romsList->setItem(i, 0, new QTableWidgetItem(core.mem->rom.gameNameDB.c_str())); + + romsList->setItem( + i, 0, + new QTableWidgetItem(std::format("{} ({}) (Rev {})", rom.gameNameDB, + n64::GameDB::regionCodeToReadable(rom.header.countryCode), + rom.header.version) + .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")); @@ -51,6 +60,7 @@ void KaizenGui::populateRomsList(const std::string &romsPath) { KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::UserScope) { SDL_InitSubSystem(SDL_INIT_GAMEPAD); + SDL_AddGamepadMapping(gamecontrollerdb_str); hide(); romsList->verticalHeader()->hide(); @@ -60,10 +70,10 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User romsList->setEditTriggers(QAbstractItemView::NoEditTriggers); romsList->setSortingEnabled(true); romsList->setColumnCount(4); - romsList->setHorizontalHeaderItem(0, romName); - romsList->setHorizontalHeaderItem(1, romRegions); - romsList->setHorizontalHeaderItem(2, romLastPlayed); - romsList->setHorizontalHeaderItem(3, romTimePlayed); + romsList->setHorizontalHeaderItem(0, new QTableWidgetItem("Name")); + romsList->setHorizontalHeaderItem(1, new QTableWidgetItem("Regions")); + romsList->setHorizontalHeaderItem(2, new QTableWidgetItem("Last played")); + romsList->setHorizontalHeaderItem(3, new QTableWidgetItem("Time played")); vulkanWidget = new RenderWidget(); vulkanWidget->hide(); @@ -105,9 +115,36 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User setAcceptDrops(true); + SDLeventsTimer = new QTimer(); + SDLeventsTimer->setInterval(1); statusBarTimer = new QTimer(); statusBarTimer->setInterval(500); + connect(SDLeventsTimer, &QTimer::timeout, this, [&] { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_GAMEPAD_ADDED) { + if (!gamepad) { + const auto index = e.gdevice.which; + + gamepad = SDL_OpenGamepad(index); + warn("Found controller!"); + warn("Name: {}", SDL_GetGamepadName(gamepad)); + warn("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + } + } + + if (e.type == SDL_EVENT_GAMEPAD_REMOVED) { + if (gamepad) { + SDL_CloseGamepad(gamepad); + gamepad = nullptr; + } + } + } + }); + + SDLeventsTimer->start(); + connect(statusBarTimer, &QTimer::timeout, this, [&] { pause->setText("Pause"); fpsLabel->setText(std::format("FPS: {:.2f}", 1000.f / elapsed).c_str()); @@ -234,6 +271,28 @@ KaizenGui::~KaizenGui() { cleanup(); } void KaizenGui::updateKeys() { auto &pif = core.mem->mmio.si.pif; + if (gamepad) { + pif.UpdateButton(0, n64::Controller::Z, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); + pif.UpdateButton(0, n64::Controller::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); + pif.UpdateButton(0, n64::Controller::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::CUp, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) == SDL_JOYSTICK_AXIS_MIN); + pif.UpdateButton(0, n64::Controller::CDown, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::CLeft, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) == SDL_JOYSTICK_AXIS_MIN); + pif.UpdateButton(0, n64::Controller::CRight, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) == SDL_JOYSTICK_AXIS_MAX); + return; + } pif.UpdateButton(0, n64::Controller::Z, pressedKeys.test(0)); pif.UpdateButton(0, n64::Controller::A, pressedKeys.test(1)); pif.UpdateButton(0, n64::Controller::B, pressedKeys.test(2)); @@ -251,8 +310,34 @@ void KaizenGui::updateKeys() { } void KaizenGui::updateAxis() { - s16 x = 0, y = 0; auto &pif = core.mem->mmio.si.pif; + + if (gamepad) { + float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + if (xclamped < 0) { + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); + } else { + xclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + xclamped *= 86; + + float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + if (yclamped < 0) { + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); + } else { + yclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + yclamped *= 86; + + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); + + return; + } + + s16 x = 0, y = 0; if (pressedKeys.test(14)) // up y += 86; diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index e166c3b..5250e4e 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -19,6 +19,33 @@ class KaizenGui final : QMainWindow { void updateAxis(); bool eventFilter(QObject *, QEvent *) override; void populateRomsList(const std::string &); + bool fastForward = false; + bool minimized = false; + + std::vector romsListPaths{}; + + SDL_Gamepad *gamepad = nullptr; + bool gamepadConnected; + float elapsed = 0.f; + n64::Core &core = n64::Core::GetInstance(); + std::bitset<18> pressedKeys{}; + std::atomic_bool unlockFrameratePressed = false; + + QWidget *currentHomeWidget; + QLabel *romPathNotSet = new QLabel(R"(Kaizen could not find any ROMs. Set the path in "Settings -> General")"); + QTableWidget *romsList = new QTableWidget(); + QStackedWidget *centralWidget = new QStackedWidget(); + SettingsWindow *settingsWindow; + RenderWidget *vulkanWidget; + QSettings settings; + QThread *emuThread; + QTimer *statusBarTimer, *SDLeventsTimer; + QLabel *fpsLabel; + QLabel *cpuTypeLabel; + QLabel *idleSkipLabel; + QAction *pause = new QAction("Pause"); + QAction *reset = new QAction("Reset"); + QAction *stop = new QAction("Stop"); public: explicit KaizenGui() noexcept; @@ -30,41 +57,8 @@ class KaizenGui final : QMainWindow { void keyPressEvent(QKeyEvent *) override; void keyReleaseEvent(QKeyEvent *) override; - 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(); - SettingsWindow *settingsWindow; - RenderWidget *vulkanWidget; - QSettings settings; - QThread *emuThread; - QTimer *statusBarTimer; - QLabel *fpsLabel; - QLabel *cpuTypeLabel; - QLabel *idleSkipLabel; - QAction *pause = new QAction("Pause"); - QAction *reset = new QAction("Reset"); - QAction *stop = new QAction("Stop"); - - SDL_Gamepad *gamepad = nullptr; - void LoadTAS(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept; signals: void paused(); - - private: - float elapsed = 0.f; - n64::Core &core = n64::Core::GetInstance(); - std::bitset<18> pressedKeys{}; - std::atomic_bool unlockFrameratePressed = false; };