fuck this shit

This commit is contained in:
2026-06-11 14:37:02 +02:00
parent 2b59e5f461
commit bad1691ee2
13 changed files with 414 additions and 317 deletions
+15 -13
View File
@@ -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;
+1 -1
View File
@@ -47,7 +47,7 @@ struct Core {
int slot = 0;
size_t memSize{}, cpuSize{}, verSize{};
CPUType cpuType;
std::string rom;
std::string romPath;
std::set<s64> breakpoints{};
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
Registers regs;
+69 -19
View File
@@ -2,38 +2,88 @@
#include <Core.hpp>
namespace n64 {
void GameDB::match() {
n64::Mem& mem = n64::Core::GetMem();
const ROM &rom = mem.rom;
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";
}
}
std::string GameDB::match(ROM &rom) {
std::string result = "";
for (const auto &[code, regions, saveType, name] : gamedb) {
const bool matches_code = code == rom.code;
bool matches_region = false;
for (int j = 0; j < regions.size() && !matches_region; j++) {
if (regions[j] == rom.header.countryCode[0]) {
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_code) {
if (matches_region) {
mem.saveType = saveType;
mem.rom.gameNameDB = name;
return;
rom.saveType = saveType;
rom.gameNameDB = name;
return result;
}
warn(
"Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has region {}",
info("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;
rom.saveType = saveType;
rom.gameNameDB = name;
return result;
}
}
warn("Did not match any Game DB entries. Code: {} Region: {}", mem.rom.code, mem.rom.header.countryCode[0]);
info("Did not match any Game DB entries. Code: {} Region: {}", rom.code, rom.header.countryCode[0]);
mem.rom.gameNameDB = "";
mem.saveType = SAVE_NONE;
rom.gameNameDB = "";
rom.saveType = SAVE_NONE;
return "Unknown";
}
} // namespace n64
+3 -2
View File
@@ -3,6 +3,7 @@
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;
@@ -12,7 +13,7 @@ struct GameDBEntry {
};
namespace GameDB {
void match();
std::string match(ROM &);
}
static const GameDBEntry gamedb[] = {
@@ -200,4 +201,4 @@ static const GameDBEntry gamedb[] = {
{"NZL", "P", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (PAL)"},
{"NZS", "EJP", SAVE_FLASH_1m, "Legend of Zelda: Majora's Mask"},
};
}
} // namespace n64
+1 -1
View File
@@ -45,7 +45,7 @@ void Scheduler::HandleEvents() {
break;
case STOP:
core.Stop();
core.rom = {};
core.romPath = {};
break;
case RESET:
core.Reset();
+10 -6
View File
@@ -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<u32>(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<u32>(const u32 addr) {
switch (saveType) {
switch (rom.saveType) {
case SAVE_NONE:
return 0;
case SAVE_EEPROM_4k:
@@ -469,7 +473,7 @@ u32 Mem::BackupRead<u32>(const u32 addr) {
template <>
u8 Mem::BackupRead<u8>(const u32 addr) {
switch (saveType) {
switch (rom.saveType) {
case SAVE_NONE:
return 0;
case SAVE_EEPROM_4k:
@@ -492,7 +496,7 @@ u8 Mem::BackupRead<u8>(const u32 addr) {
template <>
void Mem::BackupWrite<u32>(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<u32>(const u32 addr, const u32 val) {
template <>
void Mem::BackupWrite<u8>(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;
+4 -4
View File
@@ -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<u8> 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
+2 -2
View File
@@ -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);
}
+2 -2
View File
@@ -507,7 +507,7 @@ void PI::DMA<false>() {
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<true>() {
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);
}
+4 -4
View File
@@ -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);
}
+55 -50
View File
@@ -11,43 +11,71 @@
#include <Options.hpp>
#include <Scheduler.hpp>
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;
vulkanWidget = new RenderWidget();
vulkanWidget->hide();
centralWidget->addWidget(vulkanWidget);
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);
}
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");
+6
View File
@@ -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<std::string> 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();
+38 -9
View File
@@ -7,12 +7,12 @@
#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 <LogLevel messageType = Info, class... Args>
@@ -28,11 +28,40 @@ void print(const std::format_string<Args...> fmt, Args... args) {
}
}
#define panic(fmt, ...) do { Util::print<Util::Error>("[FATAL] " fmt __VA_OPT__(,) __VA_ARGS__); exit(-1); } while(0)
#define error(fmt, ...) do { Util::print<Util::Error>("[ERROR] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0)
#define warn(fmt, ...) do { Util::print<Util::Warn>("[WARN] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0)
#define info(fmt, ...) do { Util::print<Util::Info>("[INFO] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0)
#define debug(fmt, ...) do { Util::print<Util::Debug>("[DEBUG] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0)
#define trace(fmt, ...) do { Util::print<Util::Trace>("[TRACE] " fmt __VA_OPT__(,) __VA_ARGS__); } while(0)
#define always(fmt, ...) do { Util::print<Util::Always>(fmt __VA_OPT__(,) __VA_ARGS__); } while(0)
#define panic(fmt, ...) \
do { \
Util::print<Util::Error>("[FATAL] " fmt __VA_OPT__(, ) __VA_ARGS__); \
exit(-1); \
} \
while (0)
#define error(fmt, ...) \
do { \
Util::print<Util::Error>("[ERROR] " fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
while (0)
#define warn(fmt, ...) \
do { \
Util::print<Util::Warn>("[WARN] " fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
while (0)
#define info(fmt, ...) \
do { \
Util::print<Util::Info>("[INFO] " fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
while (0)
#define debug(fmt, ...) \
do { \
Util::print<Util::Debug>("[DEBUG] " fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
while (0)
#define trace(fmt, ...) \
do { \
Util::print<Util::Trace>("[TRACE] " fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
while (0)
#define always(fmt, ...) \
do { \
Util::print<Util::Always>(fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
while (0)
} // namespace Util