We do a little saving (Widget + json serialization)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -15,4 +15,5 @@ disasm.txt
|
|||||||
log.txt
|
log.txt
|
||||||
.vs/
|
.vs/
|
||||||
CMakeSettings.json
|
CMakeSettings.json
|
||||||
out/
|
out/
|
||||||
|
settings.json
|
||||||
@@ -12,7 +12,7 @@ void App::Run() {
|
|||||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||||
|
|
||||||
while (!core.done) {
|
while (!core.done) {
|
||||||
core.Run(window, window.volumeL, window.volumeR);
|
core.Run(window, window.settings.GetVolumeL(), window.settings.GetVolumeL());
|
||||||
core.UpdateController(state);
|
core.UpdateController(state);
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
|||||||
158
src/frontend/Settings.cpp
Normal file
158
src/frontend/Settings.cpp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#include <Settings.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
using namespace std::filesystem;
|
||||||
|
|
||||||
|
Settings::Settings(n64::Core& core) {
|
||||||
|
auto modes = std::fstream::in | std::fstream::out;
|
||||||
|
auto fileExists = exists("resources/settings.json");
|
||||||
|
if(!fileExists) {
|
||||||
|
modes |= std::fstream::trunc;
|
||||||
|
}
|
||||||
|
std::fstream settingsFile{"resources/settings.json", modes};
|
||||||
|
if(fileExists) {
|
||||||
|
settings = json::parse(settingsFile);
|
||||||
|
auto entryCpuType = settings["cpu"]["type"];
|
||||||
|
if(!entryCpuType.empty()) {
|
||||||
|
cpuType = entryCpuType.get<std::string>();
|
||||||
|
if(cpuType == "dynarec") {
|
||||||
|
core.cpuType = n64::CpuType::Dynarec;
|
||||||
|
} else if(cpuType == "interpreter") {
|
||||||
|
core.cpuType = n64::CpuType::Interpreter;
|
||||||
|
} else {
|
||||||
|
util::panic("Unrecognized cpu type: {}\n", cpuType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
settingsFile.clear();
|
||||||
|
settings["cpu"]["type"] = "dynarec";
|
||||||
|
settingsFile << settings;
|
||||||
|
core.cpuType = n64::CpuType::Dynarec;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto volumeREntry = settings["audio"]["volumeR"];
|
||||||
|
if(!volumeREntry.empty()) {
|
||||||
|
auto value = volumeREntry.get<float>();
|
||||||
|
volumeR = value;
|
||||||
|
} else {
|
||||||
|
settingsFile.clear();
|
||||||
|
settings["audio"]["volumeR"] = 0.5;
|
||||||
|
settingsFile << settings;
|
||||||
|
volumeR = 0.5;
|
||||||
|
}
|
||||||
|
auto volumeLEntry = settings["audio"]["volumeL"];
|
||||||
|
if(!volumeLEntry.empty()) {
|
||||||
|
auto value = volumeLEntry.get<float>();
|
||||||
|
volumeL = value;
|
||||||
|
} else {
|
||||||
|
settingsFile.clear();
|
||||||
|
settings["audio"]["volumeL"] = 0.5;
|
||||||
|
settingsFile << settings;
|
||||||
|
volumeL = 0.5;
|
||||||
|
}
|
||||||
|
auto lockChannelsEntry = settings["audio"]["lockChannels"];
|
||||||
|
if(!lockChannelsEntry.empty()) {
|
||||||
|
auto value = lockChannelsEntry.get<bool>();
|
||||||
|
lockChannels = value;
|
||||||
|
} else {
|
||||||
|
settingsFile.clear();
|
||||||
|
settings["audio"]["lockChannels"] = true;
|
||||||
|
settingsFile << settings;
|
||||||
|
lockChannels = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
settings["cpu"]["type"] = "dynarec";
|
||||||
|
settings["audio"]["volumeR"] = 0.5;
|
||||||
|
settings["audio"]["volumeL"] = 0.5;
|
||||||
|
settings["audio"]["lockChannels"] = true;
|
||||||
|
|
||||||
|
core.cpuType = n64::CpuType::Dynarec;
|
||||||
|
volumeR = 0.5;
|
||||||
|
volumeL = 0.5;
|
||||||
|
lockChannels = true;
|
||||||
|
|
||||||
|
settingsFile << settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::~Settings() {
|
||||||
|
auto modes = std::fstream::out;
|
||||||
|
auto fileExists = exists("resources/settings.json");
|
||||||
|
if(fileExists) {
|
||||||
|
modes |= std::fstream::trunc;
|
||||||
|
|
||||||
|
std::fstream settingsFile{"resources/settings.json", modes};
|
||||||
|
|
||||||
|
settings["cpu"]["type"] = cpuType;
|
||||||
|
settings["audio"]["volumeR"] = volumeR;
|
||||||
|
settings["audio"]["volumeL"] = volumeL;
|
||||||
|
settings["audio"]["lockChannels"] = lockChannels;
|
||||||
|
settingsFile << settings;
|
||||||
|
settingsFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::RenderWidget(bool& show) {
|
||||||
|
if(show) {
|
||||||
|
ImGui::OpenPopup("Settings");
|
||||||
|
if(ImGui::BeginPopupModal("Settings", &show)) {
|
||||||
|
static enum { CPU, Audio } category = CPU;
|
||||||
|
if(ImGui::Button("CPU")) {
|
||||||
|
category = CPU;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Button("Audio")) {
|
||||||
|
category = Audio;
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
if(category == Audio) {
|
||||||
|
ImGui::Checkbox("Lock channels", &lockChannels);
|
||||||
|
ImGui::SliderFloat("Volume L", &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||||
|
if (!lockChannels) {
|
||||||
|
ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||||
|
} else {
|
||||||
|
volumeR = volumeL;
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
} else if(category == CPU) {
|
||||||
|
const char* items[] = { "JIT", "Interpreter" };
|
||||||
|
static int currentIndex = [this]() {
|
||||||
|
if(cpuType == "dynarec") return 0;
|
||||||
|
else if(cpuType == "interpreter") return 1;
|
||||||
|
return 0;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if(ImGui::BeginCombo("CPU type", items[currentIndex])) {
|
||||||
|
for (int n = 0; n < 2; n++) {
|
||||||
|
const bool is_selected = (currentIndex == n);
|
||||||
|
if (ImGui::Selectable(items[n], is_selected)) {
|
||||||
|
currentIndex = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentIndex == 0) {
|
||||||
|
cpuType = "dynarec";
|
||||||
|
}
|
||||||
|
if(currentIndex == 1) {
|
||||||
|
cpuType = "interpreter";
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLockChannels(lockChannels);
|
||||||
|
SetVolumeL(volumeL);
|
||||||
|
SetVolumeR(volumeR);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/frontend/Settings.hpp
Normal file
27
src/frontend/Settings.hpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <Core.hpp>
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
Settings(n64::Core&);
|
||||||
|
~Settings();
|
||||||
|
|
||||||
|
float GetVolumeL() const { return volumeL; };
|
||||||
|
float GetVolumeR() const { return volumeR; };
|
||||||
|
bool GetLockChannels() const { return lockChannels; }
|
||||||
|
std::string GetCpuType() const { return cpuType; }
|
||||||
|
|
||||||
|
void SetVolumeL(float v) { volumeL = v; };
|
||||||
|
void SetVolumeR(float v) { volumeR = v; };
|
||||||
|
void SetLockChannels(bool v) { lockChannels = v; }
|
||||||
|
void SetCpuType(std::string v) { cpuType = v; }
|
||||||
|
|
||||||
|
void RenderWidget(bool& show);
|
||||||
|
private:
|
||||||
|
std::string cpuType = "interpreter";
|
||||||
|
float volumeL = 0.0, volumeR = 0.0;
|
||||||
|
bool lockChannels = true;
|
||||||
|
json settings;
|
||||||
|
};
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
|
#include <filesystem>
|
||||||
#include <Window.hpp>
|
#include <Window.hpp>
|
||||||
#include <nfd.hpp>
|
#include <nfd.hpp>
|
||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <Audio.hpp>
|
#include <Audio.hpp>
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
VkInstance instance{};
|
VkInstance instance{};
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
Window::Window(n64::Core& core) {
|
Window::Window(n64::Core& core) : settings(core) {
|
||||||
InitSDL();
|
InitSDL();
|
||||||
LoadSettings(core);
|
|
||||||
InitParallelRDP(core.mem.GetRDRAM(), window);
|
InitParallelRDP(core.mem.GetRDRAM(), window);
|
||||||
InitImgui();
|
InitImgui();
|
||||||
NFD::Init();
|
NFD::Init();
|
||||||
@@ -22,34 +19,6 @@ Window::Window(n64::Core& core) {
|
|||||||
&& event.window.windowID == SDL_GetWindowID(window);
|
&& event.window.windowID == SDL_GetWindowID(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::LoadSettings(n64::Core &core) {
|
|
||||||
settingsFile.open("settings.json", std::fstream::in | std::fstream::out);
|
|
||||||
if(settingsFile.is_open()) {
|
|
||||||
settings = json::parse(settingsFile);
|
|
||||||
auto entryCpuType = settings["cpu_type"];
|
|
||||||
if(!entryCpuType.empty()) {
|
|
||||||
auto cpuType = entryCpuType.get<std::string>();
|
|
||||||
if(cpuType == "dynarec") {
|
|
||||||
core.cpuType = n64::CpuType::Dynarec;
|
|
||||||
} else if(cpuType == "interpreter") {
|
|
||||||
core.cpuType = n64::CpuType::Interpreter;
|
|
||||||
} else {
|
|
||||||
util::panic("Unrecognized cpu type: {}\n", cpuType);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
settings["cpu_type"] = "dynarec";
|
|
||||||
settingsFile << settings;
|
|
||||||
core.cpuType = n64::CpuType::Dynarec;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
settings["cpu_type"] = "dynarec";
|
|
||||||
settingsFile << settings;
|
|
||||||
core.cpuType = n64::CpuType::Dynarec;
|
|
||||||
}
|
|
||||||
|
|
||||||
settingsFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::InitSDL() {
|
void Window::InitSDL() {
|
||||||
SDL_Init(SDL_INIT_EVERYTHING);
|
SDL_Init(SDL_INIT_EVERYTHING);
|
||||||
n64::InitAudio();
|
n64::InitAudio();
|
||||||
@@ -217,6 +186,7 @@ void Window::Render(n64::Core& core) {
|
|||||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||||
windowTitle = shadowWindowTitle;
|
windowTitle = shadowWindowTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool showSettings = false;
|
static bool showSettings = false;
|
||||||
bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus());
|
bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus());
|
||||||
if(showMainMenuBar) {
|
if(showMainMenuBar) {
|
||||||
@@ -277,21 +247,7 @@ void Window::Render(n64::Core& core) {
|
|||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(showSettings) {
|
settings.RenderWidget(showSettings);
|
||||||
ImGui::OpenPopup("Settings");
|
|
||||||
if(ImGui::BeginPopupModal("Settings", &showSettings)) {
|
|
||||||
ImGui::Checkbox("Lock channels", &lockVolume);
|
|
||||||
ImGui::SliderFloat("Volume L", &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
|
||||||
if (!lockVolume) {
|
|
||||||
ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
|
||||||
} else {
|
|
||||||
volumeR = volumeL;
|
|
||||||
ImGui::BeginDisabled();
|
|
||||||
ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <nlohmann/json.hpp>
|
#include <Settings.hpp>
|
||||||
|
|
||||||
using namespace nlohmann;
|
|
||||||
|
|
||||||
struct Window {
|
struct Window {
|
||||||
explicit Window(n64::Core& core);
|
explicit Window(n64::Core& core);
|
||||||
@@ -18,12 +16,9 @@ struct Window {
|
|||||||
[[nodiscard]] bool gotClosed(SDL_Event event);
|
[[nodiscard]] bool gotClosed(SDL_Event event);
|
||||||
ImFont *uiFont{}, *codeFont{};
|
ImFont *uiFont{}, *codeFont{};
|
||||||
u32 windowID{};
|
u32 windowID{};
|
||||||
float volumeL = 0.0, volumeR = 0.0;
|
Settings settings;
|
||||||
void LoadROM(n64::Core& core, const std::string& path);
|
void LoadROM(n64::Core& core, const std::string& path);
|
||||||
private:
|
private:
|
||||||
json settings;
|
|
||||||
std::fstream settingsFile;
|
|
||||||
bool lockVolume = true;
|
|
||||||
SDL_Window* window{};
|
SDL_Window* window{};
|
||||||
std::string windowTitle{"Gadolinium"};
|
std::string windowTitle{"Gadolinium"};
|
||||||
std::string shadowWindowTitle{windowTitle};
|
std::string shadowWindowTitle{windowTitle};
|
||||||
@@ -31,7 +26,6 @@ private:
|
|||||||
void InitSDL();
|
void InitSDL();
|
||||||
void InitImgui();
|
void InitImgui();
|
||||||
void Render(n64::Core& core);
|
void Render(n64::Core& core);
|
||||||
void LoadSettings(n64::Core&);
|
|
||||||
|
|
||||||
VkPhysicalDevice physicalDevice{};
|
VkPhysicalDevice physicalDevice{};
|
||||||
VkDevice device{};
|
VkDevice device{};
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ CartInfo Core::LoadROM(const std::string& rom_) {
|
|||||||
void Core::Run(Window& window, float volumeL, float volumeR) {
|
void Core::Run(Window& window, float volumeL, float volumeR) {
|
||||||
MMIO& mmio = mem.mmio;
|
MMIO& mmio = mem.mmio;
|
||||||
Controller& controller = mmio.si.controller;
|
Controller& controller = mmio.si.controller;
|
||||||
|
Registers& regs = CpuGetRegs();
|
||||||
|
|
||||||
for(int field = 0; field < mmio.vi.numFields; field++) {
|
for(int field = 0; field < mmio.vi.numFields; field++) {
|
||||||
int frameCycles = 0;
|
int frameCycles = 0;
|
||||||
@@ -41,33 +42,33 @@ void Core::Run(Window& window, float volumeL, float volumeR) {
|
|||||||
mmio.vi.current = (i << 1) + field;
|
mmio.vi.current = (i << 1) + field;
|
||||||
|
|
||||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||||
InterruptRaise(mmio.mi, CpuGetRegs(), Interrupt::VI);
|
InterruptRaise(mmio.mi, regs, Interrupt::VI);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(;cycles <= mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
|
for(;cycles <= mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
|
||||||
CpuStep(mem);
|
CpuStep(mem);
|
||||||
if(!mmio.rsp.spStatus.halt) {
|
if(!mmio.rsp.spStatus.halt) {
|
||||||
cpu.regs.steps++;
|
regs.steps++;
|
||||||
if(cpu.regs.steps > 2) {
|
if(regs.steps > 2) {
|
||||||
mmio.rsp.steps += 2;
|
mmio.rsp.steps += 2;
|
||||||
cpu.regs.steps -= 3;
|
regs.steps -= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(mmio.rsp.steps > 0) {
|
while(mmio.rsp.steps > 0) {
|
||||||
mmio.rsp.steps--;
|
mmio.rsp.steps--;
|
||||||
mmio.rsp.Step(CpuGetRegs(), mem);
|
mmio.rsp.Step(regs, mem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mmio.ai.Step(mem, CpuGetRegs(), 1, volumeL, volumeR);
|
mmio.ai.Step(mem, regs, 1, volumeL, volumeR);
|
||||||
scheduler.tick(1, mem, CpuGetRegs());
|
scheduler.tick(1, mem, regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
cycles -= mmio.vi.cyclesPerHalfline;
|
cycles -= mmio.vi.cyclesPerHalfline;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||||
InterruptRaise(mmio.mi, CpuGetRegs(), Interrupt::VI);
|
InterruptRaise(mmio.mi, regs, Interrupt::VI);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateScreenParallelRdp(*this, window, GetVI());
|
UpdateScreenParallelRdp(*this, window, GetVI());
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <n64/core/RSP.hpp>
|
#include <n64/core/RSP.hpp>
|
||||||
#include <util.hpp>
|
#include <util.hpp>
|
||||||
#include <n64/core/Mem.hpp>
|
#include <n64/core/Mem.hpp>
|
||||||
#include <n64/core/cpu/Registers.hpp>
|
#include <n64/core/registers/Registers.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
RSP::RSP() {
|
RSP::RSP() {
|
||||||
|
|||||||
@@ -778,28 +778,28 @@ void Interpreter::trap(bool cond) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::mtc2(u32 instr) {
|
void Interpreter::mtc2(u32 instr) {
|
||||||
cop2Latch = regs.gpr[RT(instr)];
|
cop2Latch = regs.gpr[RT(instr)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::mfc2(u32 instr) {
|
void Interpreter::mfc2(u32 instr) {
|
||||||
s32 value = cop2Latch;
|
s32 value = cop2Latch;
|
||||||
regs.gpr[RT(instr)] = value;
|
regs.gpr[RT(instr)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::dmtc2(u32 instr) {
|
void Interpreter::dmtc2(u32 instr) {
|
||||||
cop2Latch = regs.gpr[RT(instr)];
|
cop2Latch = regs.gpr[RT(instr)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::dmfc2(u32 instr) {
|
void Interpreter::dmfc2(u32 instr) {
|
||||||
regs.gpr[RT(instr)] = cop2Latch;
|
regs.gpr[RT(instr)] = cop2Latch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::ctc2(u32) {
|
void Interpreter::ctc2(u32) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::cfc2(u32) {
|
void Interpreter::cfc2(u32) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user