Game list
This commit is contained in:
@@ -1,158 +0,0 @@
|
|||||||
#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"] = "interpreter";
|
|
||||||
settingsFile << settings;
|
|
||||||
core.cpuType = n64::CpuType::Interpreter;
|
|
||||||
}
|
|
||||||
|
|
||||||
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"] = "interpreter";
|
|
||||||
settings["audio"]["volumeR"] = 0.5;
|
|
||||||
settings["audio"]["volumeL"] = 0.5;
|
|
||||||
settings["audio"]["lockChannels"] = true;
|
|
||||||
|
|
||||||
core.cpuType = n64::CpuType::Interpreter;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
154
src/frontend/imgui/GameList.cpp
Normal file
154
src/frontend/imgui/GameList.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#include <GameList.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <Mem.hpp>
|
||||||
|
#include <execution>
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
using namespace std::filesystem;
|
||||||
|
|
||||||
|
inline std::string CountryCodeToStr(u8 code) {
|
||||||
|
switch(code) {
|
||||||
|
case 0x37: return "Beta";
|
||||||
|
case 0x41: return "Asian (NTSC)";
|
||||||
|
case 0x42: return "Brazilian";
|
||||||
|
case 0x43: return "Chinese";
|
||||||
|
case 0x44: return "German";
|
||||||
|
case 0x45: return "North America";
|
||||||
|
case 0x46: return "French";
|
||||||
|
case 0x47: return "Gateway 64 (NTSC)";
|
||||||
|
case 0x48: return "Dutch";
|
||||||
|
case 0x49: return "Italian";
|
||||||
|
case 0x4A: return "Japanese";
|
||||||
|
case 0x4B: return "Korean";
|
||||||
|
case 0x4C: return "Gateway 64 (PAL)";
|
||||||
|
case 0x4E: return "Canadian";
|
||||||
|
case 0x50: return "European (basic spec.)";
|
||||||
|
case 0x53: return "Spanish";
|
||||||
|
case 0x55: return "Australian";
|
||||||
|
case 0x57: return "Scandinavian";
|
||||||
|
case 0x58: case 0x59: return "European";
|
||||||
|
default: return "Unrecognized";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameList::GameList(const std::string& path) {
|
||||||
|
if(!path.empty()) {
|
||||||
|
std::for_each(std::execution::par_unseq, begin(recursive_directory_iterator{path}), end(recursive_directory_iterator{path}), [&](const auto& p) {
|
||||||
|
if(p.path().extension() == ".n64" || p.path().extension() == ".z64" || p.path().extension() == ".v64" ||
|
||||||
|
p.path().extension() == ".N64" || p.path().extension() == ".Z64" || p.path().extension() == ".V64") {
|
||||||
|
std::ifstream file(p.path().string(), std::ios::binary);
|
||||||
|
file.unsetf(std::ios::skipws);
|
||||||
|
|
||||||
|
if(!file.is_open()) {
|
||||||
|
util::panic("Unable to open {}!", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
auto size = file.tellg();
|
||||||
|
auto sizeAdjusted = util::NextPow2(size);
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<u8> cart{};
|
||||||
|
|
||||||
|
std::fill(cart.begin(), cart.end(), 0);
|
||||||
|
cart.resize(sizeAdjusted);
|
||||||
|
cart.insert(cart.begin(), std::istream_iterator<u8>(file), std::istream_iterator<u8>());
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
u32 crc, dummy;
|
||||||
|
util::SwapN64Rom(sizeAdjusted, cart.data(), crc, dummy);
|
||||||
|
|
||||||
|
u8 countryCode = 0;
|
||||||
|
countryCode = cart[0x3D];
|
||||||
|
|
||||||
|
std::ifstream gameDbFile("resources/game_db.json");
|
||||||
|
json gameDb = json::parse(gameDbFile);
|
||||||
|
auto entry = gameDb[fmt::format("{:08x}", crc)]["name"];
|
||||||
|
|
||||||
|
if(!entry.empty()) {
|
||||||
|
gamesList.push_back({entry.get<std::string>(), CountryCodeToStr(countryCode), fmt::format("{:.2f} MiB", float(size) / 1024 / 1024), p.path().string()});
|
||||||
|
} else {
|
||||||
|
gamesList.push_back({p.path().stem().string(), CountryCodeToStr(countryCode), fmt::format("{:.2f} MiB", float(size) / 1024 / 1024), p.path().string()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameList::RenderWidget(bool showMainMenuBar, float mainMenuBarHeight, std::string& rom) {
|
||||||
|
const auto windowSize = ImGui::GetIO().DisplaySize;
|
||||||
|
if (showMainMenuBar) {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0, mainMenuBarHeight));
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(windowSize.x, windowSize.y - mainMenuBarHeight));
|
||||||
|
} else {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||||
|
ImGui::SetNextWindowSize(windowSize);
|
||||||
|
}
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.f, 0.f));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
|
||||||
|
|
||||||
|
ImGui::Begin(
|
||||||
|
"Games list",
|
||||||
|
nullptr,
|
||||||
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
|
||||||
|
ImGuiWindowFlags_NoBringToFrontOnFocus
|
||||||
|
);
|
||||||
|
|
||||||
|
static ImGuiTableFlags flags =
|
||||||
|
ImGuiTableFlags_Resizable
|
||||||
|
| ImGuiTableFlags_RowBg
|
||||||
|
| ImGuiTableFlags_BordersOuterV
|
||||||
|
| ImGuiTableFlags_SizingStretchProp;
|
||||||
|
|
||||||
|
bool toOpen = false;
|
||||||
|
if (ImGui::BeginTable("Games List", 3, flags)) {
|
||||||
|
ImGui::TableSetupColumn("Title");
|
||||||
|
ImGui::TableSetupColumn("Region");
|
||||||
|
ImGui::TableSetupColumn("Size");
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
for (int row = 0; row < gamesList.size(); row++) {
|
||||||
|
GameInfo entry = gamesList[row];
|
||||||
|
|
||||||
|
ImGui::TableNextRow(ImGuiTableRowFlags_None);
|
||||||
|
ImGui::PushID(row);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.0f, 0.5f));
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
|
||||||
|
if (ImGui::Selectable(entry.name.c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap, ImVec2(0.0f, 20.f))) {
|
||||||
|
toOpen = true;
|
||||||
|
rom = entry.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
|
|
||||||
|
if (ImGui::Selectable(entry.region.c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap, ImVec2(0.0f, 20.f))) {
|
||||||
|
toOpen = true;
|
||||||
|
rom = entry.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(2);
|
||||||
|
|
||||||
|
if (ImGui::Selectable(entry.size.c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap, ImVec2(0.0f, 20.f))) {
|
||||||
|
toOpen = true;
|
||||||
|
rom = entry.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
return toOpen;
|
||||||
|
}
|
||||||
18
src/frontend/imgui/GameList.hpp
Normal file
18
src/frontend/imgui/GameList.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct GameInfo {
|
||||||
|
std::string name, region, size, path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GameList {
|
||||||
|
GameList(const std::string&);
|
||||||
|
~GameList() = default;
|
||||||
|
|
||||||
|
bool RenderWidget(bool, float, std::string&);
|
||||||
|
|
||||||
|
std::vector<GameInfo> GetGamesList() const { return gamesList; }
|
||||||
|
private:
|
||||||
|
std::vector<GameInfo> gamesList{};
|
||||||
|
};
|
||||||
124
src/frontend/imgui/Settings.cpp
Normal file
124
src/frontend/imgui/Settings.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#include <Settings.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <utilities.hpp>
|
||||||
|
#include <nfd.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"] = "interpreter";
|
||||||
|
settingsFile << settings;
|
||||||
|
core.cpuType = n64::CpuType::Interpreter;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkjsonentry(volumeR, float, "audio", "volumeR", 0.5);
|
||||||
|
checkjsonentry(volumeL, float, "audio", "volumeL", 0.5);
|
||||||
|
checkjsonentry(lockChannels, bool, "audio", "lockChannels", true);
|
||||||
|
checkjsonentry(gamesDir, std::string, "general", "gamesDir", "");
|
||||||
|
} else {
|
||||||
|
settings["general"]["gamesDir"] = "";
|
||||||
|
settings["cpu"]["type"] = "interpreter";
|
||||||
|
settings["audio"]["volumeR"] = 0.5;
|
||||||
|
settings["audio"]["volumeL"] = 0.5;
|
||||||
|
settings["audio"]["lockChannels"] = true;
|
||||||
|
|
||||||
|
core.cpuType = n64::CpuType::Interpreter;
|
||||||
|
volumeR = 0.5;
|
||||||
|
volumeL = 0.5;
|
||||||
|
lockChannels = true;
|
||||||
|
gamesDir = "";
|
||||||
|
|
||||||
|
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["general"]["gamesDir"] = gamesDir;
|
||||||
|
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)) {
|
||||||
|
const char *categories[] = {"General", "CPU", "Audio"};
|
||||||
|
enum Category { General, CPU, Audio };
|
||||||
|
static int category = General;
|
||||||
|
CreateComboList("", &category, categories, 3);
|
||||||
|
ImGui::Separator();
|
||||||
|
switch (category) {
|
||||||
|
case General:
|
||||||
|
ImGui::Text("Games directory: %s", gamesDir.c_str());
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Button("Select...")) {
|
||||||
|
nfdchar_t *outpath;
|
||||||
|
nfdresult_t result = NFD_PickFolderN(&outpath, nullptr);
|
||||||
|
if (result == NFD_OKAY) {
|
||||||
|
gamesDir = outpath;
|
||||||
|
NFD_FreePath(outpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CPU: {
|
||||||
|
const char *cpuTypes[] = {"JIT", "Interpreter"};
|
||||||
|
static int currentType = 0;
|
||||||
|
if (cpuType == "interpreter") currentType = 1;
|
||||||
|
|
||||||
|
if (CreateComboList("Core type", ¤tType, cpuTypes, 2)) {
|
||||||
|
if(currentType == 0) cpuType = "dynarec";
|
||||||
|
if(currentType == 1) cpuType = "interpreter";
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 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();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,15 +12,11 @@ struct Settings {
|
|||||||
float GetVolumeR() const { return volumeR; };
|
float GetVolumeR() const { return volumeR; };
|
||||||
bool GetLockChannels() const { return lockChannels; }
|
bool GetLockChannels() const { return lockChannels; }
|
||||||
std::string GetCpuType() const { return cpuType; }
|
std::string GetCpuType() const { return cpuType; }
|
||||||
|
std::string GetGamesDir() const { return gamesDir; }
|
||||||
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);
|
void RenderWidget(bool& show);
|
||||||
private:
|
private:
|
||||||
std::string cpuType = "interpreter";
|
std::string cpuType = "interpreter", gamesDir = "";
|
||||||
float volumeL = 0.0, volumeR = 0.0;
|
float volumeL = 0.0, volumeR = 0.0;
|
||||||
bool lockChannels = true;
|
bool lockChannels = true;
|
||||||
json settings;
|
json settings;
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
VkInstance instance{};
|
VkInstance instance{};
|
||||||
|
|
||||||
Window::Window(n64::Core& core) : settings(core) {
|
Window::Window(n64::Core& core) : settings(core), gameList(settings.GetGamesDir()) {
|
||||||
InitSDL();
|
InitSDL();
|
||||||
InitParallelRDP(core.mem.GetRDRAM(), window);
|
InitParallelRDP(core.mem.GetRDRAM(), window);
|
||||||
InitImgui();
|
InitImgui();
|
||||||
@@ -167,6 +167,7 @@ void Window::LoadROM(n64::Core& core, const std::string &path) {
|
|||||||
gameName = std::filesystem::path(path).stem().string();
|
gameName = std::filesystem::path(path).stem().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util::UpdateRPC(util::Playing, gameName);
|
||||||
windowTitle = "Gadolinium - " + gameName;
|
windowTitle = "Gadolinium - " + gameName;
|
||||||
shadowWindowTitle = windowTitle;
|
shadowWindowTitle = windowTitle;
|
||||||
|
|
||||||
@@ -187,10 +188,13 @@ void Window::Render(n64::Core& core) {
|
|||||||
windowTitle = shadowWindowTitle;
|
windowTitle = shadowWindowTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool renderGameList = true;
|
||||||
static bool showSettings = false;
|
static bool showSettings = false;
|
||||||
bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus());
|
bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus());
|
||||||
|
static float mainMenuBarHeight = 0;
|
||||||
if(showMainMenuBar) {
|
if(showMainMenuBar) {
|
||||||
ImGui::BeginMainMenuBar();
|
ImGui::BeginMainMenuBar();
|
||||||
|
mainMenuBarHeight = ImGui::GetWindowSize().y;
|
||||||
if (ImGui::BeginMenu("File")) {
|
if (ImGui::BeginMenu("File")) {
|
||||||
if (ImGui::MenuItem("Open", "O")) {
|
if (ImGui::MenuItem("Open", "O")) {
|
||||||
nfdchar_t *outpath;
|
nfdchar_t *outpath;
|
||||||
@@ -200,6 +204,7 @@ void Window::Render(n64::Core& core) {
|
|||||||
LoadROM(core, outpath);
|
LoadROM(core, outpath);
|
||||||
util::UpdateRPC(util::Playing, gameName);
|
util::UpdateRPC(util::Playing, gameName);
|
||||||
NFD_FreePath(outpath);
|
NFD_FreePath(outpath);
|
||||||
|
renderGameList = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Dump RDRAM")) {
|
if (ImGui::MenuItem("Dump RDRAM")) {
|
||||||
@@ -219,8 +224,10 @@ void Window::Render(n64::Core& core) {
|
|||||||
if (ImGui::BeginMenu("Emulation")) {
|
if (ImGui::BeginMenu("Emulation")) {
|
||||||
if (ImGui::MenuItem("Reset")) {
|
if (ImGui::MenuItem("Reset")) {
|
||||||
LoadROM(core, core.rom);
|
LoadROM(core, core.rom);
|
||||||
|
renderGameList = false;
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Stop")) {
|
if (ImGui::MenuItem("Stop")) {
|
||||||
|
renderGameList = true;
|
||||||
windowTitle = "Gadolinium";
|
windowTitle = "Gadolinium";
|
||||||
core.rom.clear();
|
core.rom.clear();
|
||||||
util::UpdateRPC(util::Idling);
|
util::UpdateRPC(util::Idling);
|
||||||
@@ -247,6 +254,11 @@ void Window::Render(n64::Core& core) {
|
|||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string rom{};
|
||||||
|
if(renderGameList && gameList.RenderWidget(showMainMenuBar, mainMenuBarHeight, rom)) {
|
||||||
|
LoadROM(core, rom);
|
||||||
|
renderGameList = false;
|
||||||
|
}
|
||||||
settings.RenderWidget(showSettings);
|
settings.RenderWidget(showSettings);
|
||||||
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Settings.hpp>
|
#include <Settings.hpp>
|
||||||
|
#include <GameList.hpp>
|
||||||
|
|
||||||
struct Window {
|
struct Window {
|
||||||
explicit Window(n64::Core& core);
|
explicit Window(n64::Core& core);
|
||||||
@@ -17,6 +18,7 @@ struct Window {
|
|||||||
ImFont *uiFont{}, *codeFont{};
|
ImFont *uiFont{}, *codeFont{};
|
||||||
u32 windowID{};
|
u32 windowID{};
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
GameList gameList;
|
||||||
void LoadROM(n64::Core& core, const std::string& path);
|
void LoadROM(n64::Core& core, const std::string& path);
|
||||||
private:
|
private:
|
||||||
SDL_Window* window{};
|
SDL_Window* window{};
|
||||||
|
|||||||
21
src/frontend/imgui/utilities.hpp
Normal file
21
src/frontend/imgui/utilities.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
inline bool CreateComboList(const char* label, int* index, const char** items, int items_count) {
|
||||||
|
if (ImGui::BeginCombo(label, items[*index])) {
|
||||||
|
for (int n = 0; n < items_count; n++) {
|
||||||
|
const bool is_selected = (*index == n);
|
||||||
|
if (ImGui::Selectable(items[n], is_selected)) {
|
||||||
|
*index = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
14
src/util.hpp
14
src/util.hpp
@@ -302,4 +302,18 @@ inline void UpdateRPC(State state, const std::string& game = "") {
|
|||||||
inline void ClearRPC() {
|
inline void ClearRPC() {
|
||||||
Discord_ClearPresence();
|
Discord_ClearPresence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define checkjsonentry(name, type, param1, param2, defaultVal) \
|
||||||
|
do { \
|
||||||
|
auto name##Entry = settings[param1][param2]; \
|
||||||
|
if(!name##Entry.empty()) { \
|
||||||
|
auto value = name##Entry.get<type>(); \
|
||||||
|
name = value; \
|
||||||
|
} else { \
|
||||||
|
settingsFile.clear(); \
|
||||||
|
settings[param1][param2] = defaultVal; \
|
||||||
|
settingsFile << settings; \
|
||||||
|
name = defaultVal; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user