This commit is contained in:
iris
2026-04-13 23:58:36 +02:00
parent ad6007b254
commit 004dcee429
2 changed files with 264 additions and 237 deletions
+216 -190
View File
@@ -8,7 +8,9 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), emuThread(fpsCounter, settingsWindow), vulkanWidget(window.getHandle(), emuThread.rdramCopy.data()) { KaizenGui::KaizenGui() noexcept :
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), emuThread(fpsCounter, settingsWindow),
vulkanWidget(window.getHandle(), emuThread.rdramCopy.data()) {
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
SDL_InitSubSystem(SDL_INIT_GAMEPAD); SDL_InitSubSystem(SDL_INIT_GAMEPAD);
@@ -36,186 +38,193 @@ void KaizenGui::QueryDevices(const SDL_Event &event) {
if (gamepad) if (gamepad)
SDL_CloseGamepad(gamepad); SDL_CloseGamepad(gamepad);
break; break;
default: break; default:
break;
} }
} }
void KaizenGui::HandleInput(const SDL_Event &event) { void KaizenGui::HandleInput(const SDL_Event &event) {
const n64::Core& core = n64::Core::GetInstance(); const n64::Core &core = n64::Core::GetInstance();
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
switch(event.type) { switch (event.type) {
case SDL_EVENT_GAMEPAD_AXIS_MOTION: case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if(!gamepad) if (!gamepad)
break;
{
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(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<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
}
break; break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: {
case SDL_EVENT_GAMEPAD_BUTTON_UP: pif.UpdateButton(0, n64::Controller::Key::Z,
if(!gamepad) SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(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<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
}
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
if (!gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
{
const auto keys = SDL_GetKeyboardState(nullptr);
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true;
}
fastForward = keys[SDL_SCANCODE_SPACE];
if (!unlockFramerate)
core.parallel.SetFramerateUnlocked(fastForward);
if (core.romLoaded) {
if (keys[SDL_SCANCODE_P]) {
emuThread.TogglePause();
}
if (keys[SDL_SCANCODE_R]) {
emuThread.Reset();
}
if (keys[SDL_SCANCODE_Q]) {
emuThread.Stop();
}
}
if (gamepad)
break; break;
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
break; pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
case SDL_EVENT_KEY_DOWN: pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
case SDL_EVENT_KEY_UP: pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
{ pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
const auto keys = SDL_GetKeyboardState(nullptr); pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true;
}
fastForward = keys[SDL_SCANCODE_SPACE]; float x = 0, y = 0;
if(!unlockFramerate)
core.parallel.SetFramerateUnlocked(fastForward);
if(core.romLoaded) { if (keys[SDL_SCANCODE_UP])
if(keys[SDL_SCANCODE_P]) { y = 86;
emuThread.TogglePause(); if (keys[SDL_SCANCODE_DOWN])
} y = -86;
if (keys[SDL_SCANCODE_LEFT])
x = -86;
if (keys[SDL_SCANCODE_RIGHT])
x = 86;
if(keys[SDL_SCANCODE_R]) { pif.UpdateAxis(0, n64::Controller::Axis::X, x);
emuThread.Reset(); pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
} }
break;
if(keys[SDL_SCANCODE_Q]) { default:
emuThread.Stop(); break;
}
}
if(gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
float x = 0, y = 0;
if (keys[SDL_SCANCODE_UP]) y = 86;
if (keys[SDL_SCANCODE_DOWN]) y = -86;
if (keys[SDL_SCANCODE_LEFT]) x = -86;
if (keys[SDL_SCANCODE_RIGHT]) x = 86;
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
}
break;
default: break;
} }
} }
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() { std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
auto lastPC = Util::Error::GetLastPC(); auto lastPC = Util::Error::GetLastPC();
if(lastPC.has_value()) { if (lastPC.has_value()) {
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
} }
auto memoryAccess = Util::Error::GetMemoryAccess(); auto memoryAccess = Util::Error::GetMemoryAccess();
if(memoryAccess.has_value()) { if (memoryAccess.has_value()) {
const auto [is_write, size, address, written_val] = memoryAccess.value(); const auto [is_write, size, address, written_val] = memoryAccess.value();
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", ImGui::Text("%s",
static_cast<u8>(size), address, std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
is_write ? std::format(" (value = 0x{:X})", written_val) : "") address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
.c_str()); .c_str());
} }
return {lastPC, memoryAccess}; return {lastPC, memoryAccess};
} }
void KaizenGui::RenderUI() { void KaizenGui::RenderUI() {
n64::Core& core = n64::Core::GetInstance(); n64::Core &core = n64::Core::GetInstance();
gui::StartFrame(); gui::StartFrame();
if(ImGui::BeginMainMenuBar()) { if (ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("File")) { if (ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Open", "Ctrl-O")) { if (ImGui::MenuItem("Open", "Ctrl-O")) {
fileDialogOpen = true; fileDialogOpen = true;
} }
if(ImGui::MenuItem("Exit")) { if (ImGui::MenuItem("Exit")) {
quit = true; quit = true;
emuThread.Stop(); emuThread.Stop();
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
if(ImGui::BeginMenu("Emulation")) { if (ImGui::BeginMenu("Emulation")) {
ImGui::BeginDisabled(!core.romLoaded); ImGui::BeginDisabled(!core.romLoaded);
if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
emuThread.TogglePause(); emuThread.TogglePause();
} }
if(ImGui::MenuItem("Reset", "R")) { if (ImGui::MenuItem("Reset", "R")) {
emuThread.Reset(); emuThread.Reset();
} }
if(ImGui::MenuItem("Stop", "Q")) { if (ImGui::MenuItem("Stop", "Q")) {
emuThread.Stop(); emuThread.Stop();
core.romLoaded = false; core.romLoaded = false;
} }
if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
core.parallel.SetFramerateUnlocked(unlockFramerate); core.parallel.SetFramerateUnlocked(unlockFramerate);
} }
if(ImGui::MenuItem("Open Debugger")) { if (ImGui::MenuItem("Open Debugger")) {
debugger.Open(); debugger.Open();
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
if(ImGui::MenuItem("Options")) { if (ImGui::MenuItem("Options")) {
settingsWindow.isOpen = true; settingsWindow.isOpen = true;
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
if(ImGui::BeginMenu("Help")) { if (ImGui::BeginMenu("Help")) {
if(ImGui::MenuItem("About")) { if (ImGui::MenuItem("About")) {
aboutOpen = true; aboutOpen = true;
} }
@@ -224,15 +233,15 @@ void KaizenGui::RenderUI() {
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
if(!Util::Error::IsHandled()) { if (!Util::Error::IsHandled()) {
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
} }
if(settingsWindow.isOpen) { if (settingsWindow.isOpen) {
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
} }
if(aboutOpen) { if (aboutOpen) {
ImGui::OpenPopup("About Kaizen"); ImGui::OpenPopup("About Kaizen");
} }
@@ -250,7 +259,7 @@ void KaizenGui::RenderUI() {
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
ImGui::Separator(); ImGui::Separator();
if(ImGui::Button("OK")) { if (ImGui::Button("OK")) {
aboutOpen = false; aboutOpen = false;
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
@@ -262,8 +271,9 @@ void KaizenGui::RenderUI() {
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
emuThread.TogglePause(); emuThread.TogglePause();
switch(Util::Error::GetSeverity().as_enum) { switch (Util::Error::GetSeverity().as_enum) {
case Util::Error::Severity::WARN: { case Util::Error::Severity::WARN:
{
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
@@ -272,33 +282,37 @@ void KaizenGui::RenderUI() {
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
RenderErrorMessageDetails(); RenderErrorMessageDetails();
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); const bool ignore = ImGui::Button("Try continuing");
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); ImGui::SameLine();
const bool stop = ImGui::Button("Stop emulation");
ImGui::SameLine();
const bool chooseAnother = ImGui::Button("Choose another ROM"); const bool chooseAnother = ImGui::Button("Choose another ROM");
if(ignore || stop || chooseAnother) { if (ignore || stop || chooseAnother) {
Util::Error::SetHandled(); Util::Error::SetHandled();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
if(ignore) { if (ignore) {
emuThread.TogglePause(); emuThread.TogglePause();
} }
if(stop || chooseAnother) { if (stop || chooseAnother) {
emuThread.Stop(); emuThread.Stop();
} }
if(chooseAnother) { if (chooseAnother) {
fileDialogOpen = true; fileDialogOpen = true;
} }
break; break;
} }
if(ImGui::Button("OK")) if (ImGui::Button("OK"))
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} break; }
case Util::Error::Severity::UNRECOVERABLE: { break;
case Util::Error::Severity::UNRECOVERABLE:
{
emuThread.Stop(); emuThread.Stop();
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
@@ -308,10 +322,12 @@ void KaizenGui::RenderUI() {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
RenderErrorMessageDetails(); RenderErrorMessageDetails();
if(ImGui::Button("OK")) if (ImGui::Button("OK"))
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} break; }
case Util::Error::Severity::NON_FATAL: { break;
case Util::Error::Severity::NON_FATAL:
{
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
ImGui::Text("An error has occurred!"); ImGui::Text("An error has occurred!");
@@ -321,48 +337,54 @@ void KaizenGui::RenderUI() {
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); const bool ignore = ImGui::Button("Try continuing");
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); ImGui::SameLine();
const bool stop = ImGui::Button("Stop emulation");
ImGui::SameLine();
const bool chooseAnother = ImGui::Button("Choose another ROM"); const bool chooseAnother = ImGui::Button("Choose another ROM");
const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; const bool openInDebugger =
if(ignore || stop || chooseAnother || openInDebugger) { lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
if (ignore || stop || chooseAnother || openInDebugger) {
Util::Error::SetHandled(); Util::Error::SetHandled();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
if(ignore) { if (ignore) {
emuThread.TogglePause(); emuThread.TogglePause();
} }
if(stop || chooseAnother) { if (stop || chooseAnother) {
emuThread.Stop(); emuThread.Stop();
} }
if(chooseAnother) { if (chooseAnother) {
fileDialogOpen = true; fileDialogOpen = true;
} }
if(openInDebugger) { if (openInDebugger) {
if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
debugger.Open(); debugger.Open();
emuThread.Reset(); emuThread.Reset();
} }
} break; }
default: break; break;
default:
break;
} }
ImGui::EndPopup(); ImGui::EndPopup();
} }
if(ImGui::BeginMainStatusBar()) { if (ImGui::BeginMainStatusBar()) {
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::EndMainStatusBar(); ImGui::EndMainStatusBar();
} }
if (shouldDisplaySpinner) { if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
@@ -387,36 +409,40 @@ void KaizenGui::RenderUI() {
ImGui::RenderPlatformWindowsDefault(); ImGui::RenderPlatformWindowsDefault();
} }
if(fileDialogOpen) { if (fileDialogOpen) {
fileDialogOpen = false; fileDialogOpen = false;
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { {"Nintendo 64 executable", "n64;z64;v64"},
auto kaizen = static_cast<KaizenGui*>(userdata); {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog(
[](void *userdata, const char *const *filelist, int) {
auto kaizen = static_cast<KaizenGui *>(userdata);
if (!filelist) { if (!filelist) {
panic("An error occured: {}", SDL_GetError()); panic("An error occured: {}", SDL_GetError());
} }
if (!*filelist) { if (!*filelist) {
warn("The user did not select any file."); warn("The user did not select any file.");
warn("Most likely, the dialog was canceled."); warn("Most likely, the dialog was canceled.");
return; return;
} }
kaizen->fileToLoad = *filelist; kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true; kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen); std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach(); fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false); },
this, window.getHandle(), filters, 3, nullptr, false);
} }
if(minimized) if (minimized)
return; return;
if(core.romLoaded) { if (core.romLoaded) {
std::unique_lock<std::mutex> lk(emuThread.presentMutex); std::unique_lock<std::mutex> lk(emuThread.presentMutex);
if(emuThread.readyForPresentation.wait_for(lk, 16.6667ms) == std::cv_status::no_timeout) if (emuThread.readyForPresentation.wait_for(lk, 16.6667ms) == std::cv_status::no_timeout)
core.parallel.UpdateScreen<true>(); core.parallel.UpdateScreen<true>();
else else
core.parallel.UpdateScreen<false>(); core.parallel.UpdateScreen<false>();
@@ -427,7 +453,7 @@ void KaizenGui::RenderUI() {
} }
void KaizenGui::LoadROM(const std::string &path) noexcept { void KaizenGui::LoadROM(const std::string &path) noexcept {
n64::Core& core = n64::Core::GetInstance(); n64::Core &core = n64::Core::GetInstance();
core.LoadROM(path); core.LoadROM(path);
emuThread.create(); emuThread.create();
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
@@ -435,22 +461,23 @@ void KaizenGui::LoadROM(const std::string &path) noexcept {
} }
void KaizenGui::run() { void KaizenGui::run() {
while(!quit) { while (!quit) {
SDL_Event e; SDL_Event e;
while (SDL_PollEvent(&e)) { while (SDL_PollEvent(&e)) {
ImGui_ImplSDL3_ProcessEvent(&e); ImGui_ImplSDL3_ProcessEvent(&e);
switch(e.type) { switch (e.type) {
case SDL_EVENT_QUIT: case SDL_EVENT_QUIT:
quit = true; quit = true;
emuThread.Stop(); emuThread.Stop();
break; break;
case SDL_EVENT_WINDOW_MINIMIZED: case SDL_EVENT_WINDOW_MINIMIZED:
minimized = true; minimized = true;
break; break;
case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_RESTORED:
minimized = false; minimized = false;
break; break;
default: default:
break;
} }
QueryDevices(e); QueryDevices(e);
HandleInput(e); HandleInput(e);
@@ -461,6 +488,5 @@ void KaizenGui::run() {
} }
} }
void KaizenGui::LoadTAS(const std::string &path) noexcept { void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
n64::Core::GetInstance().LoadTAS(fs::path(path));
}
+32 -31
View File
@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include <fstream>
#include <common.hpp> #include <common.hpp>
#include <mini/ini.h> #include <mini/ini.h>
#include <log.hpp> #include <log.hpp>
@@ -8,39 +7,41 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
struct Options { struct Options {
Options() : file{"resources/options.ini"} { Options() : file{"resources/options.ini"} {
auto fileExists = fs::exists("resources/options.ini"); auto fileExists = fs::exists("resources/options.ini");
if(fileExists) { if (fileExists) {
file.read(structure); file.read(structure);
return; return;
}
structure["general"]["savePath"] = "saves";
fs::create_directory("saves");
structure["audio"]["volumeL"] = "0.5";
structure["audio"]["volumeR"] = "0.5";
structure["audio"]["lock"] = "true";
structure["cpu"]["type"] = "interpreter";
if(!file.generate(structure))
panic("Couldn't generate settings' INI!");
} }
static Options &GetInstance() { structure["general"]["savePath"] = "saves";
static Options instance; fs::create_directory("saves");
return instance; structure["audio"]["volumeL"] = "0.5";
} structure["audio"]["volumeR"] = "0.5";
structure["audio"]["lock"] = "true";
structure["cpu"]["type"] = "interpreter";
template <typename T> if (!file.generate(structure))
void SetValue(const std::string &key, const std::string &field, const T &value); panic("Couldn't generate settings' INI!");
template <typename T> }
T GetValue(const std::string &key, const std::string &field);
static Options &GetInstance() {
static Options instance;
return instance;
}
template <typename T>
void SetValue(const std::string &key, const std::string &field, const T &value);
template <typename T>
T GetValue(const std::string &key, const std::string &field);
void Apply() {
if (!file.write(structure))
panic("Could not modify options on disk!");
}
void Apply() {
if(!file.write(structure))
panic("Could not modify options on disk!");
}
private: private:
mINI::INIFile file; mINI::INIFile file;
mINI::INIStructure structure; mINI::INIStructure structure;
}; };