start this from scratch
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
|
||||
add_library(backend ${SOURCES} ${HEADERS})
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(netplay)
|
||||
|
||||
add_library(backend ${SOURCES} ${HEADERS})
|
||||
target_link_libraries(backend PRIVATE core netplay)
|
||||
@@ -1,4 +1,11 @@
|
||||
file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
|
||||
add_library(core ${SOURCES} ${HEADERS})
|
||||
add_subdirectory(interpreter)
|
||||
add_subdirectory(mem)
|
||||
add_subdirectory(mmio)
|
||||
add_subdirectory(registers)
|
||||
add_subdirectory(rsp)
|
||||
|
||||
add_library(core ${SOURCES} ${HEADERS})
|
||||
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <log.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <core/Audio.hpp>
|
||||
#include <Audio.hpp>
|
||||
|
||||
namespace n64 {
|
||||
void AI::Reset() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <PIF.hpp>
|
||||
#include <MupenMovie.hpp>
|
||||
#include <PIF/MupenMovie.hpp>
|
||||
#include <Netplay.hpp>
|
||||
#include <log.hpp>
|
||||
#include <SDL2/SDL_keyboard.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <MupenMovie.hpp>
|
||||
#include <PIF/MupenMovie.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
struct TASMovieHeader {
|
||||
@@ -2,3 +2,4 @@ file(GLOB_RECURSE SOURCES *.cpp)
|
||||
file(GLOB_RECURSE HEADERS *.cpp)
|
||||
|
||||
add_library(registers ${SOURCES} ${HEADERS})
|
||||
target_link_libraries(registers PRIVATE interpreter)
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
#include <App.hpp>
|
||||
#include <nfd.hpp>
|
||||
|
||||
App::App() : window(core) {
|
||||
DiscordEventHandlers handlers{};
|
||||
Discord_Initialize("1049669178124148806", &handlers, 1, nullptr);
|
||||
Util::UpdateRPC(Util::Idling);
|
||||
}
|
||||
|
||||
void App::Run() {
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||
n64::SI& si = core.cpu->mem.mmio.si;
|
||||
|
||||
while (!window.done) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
switch(event.type) {
|
||||
case SDL_QUIT:
|
||||
window.done = true;
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
window.handleEvents(event, core);
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEADDED: {
|
||||
const int index = event.cdevice.which;
|
||||
si.pif.gamepad = SDL_GameControllerOpen(index);
|
||||
si.pif.gamepadConnected = false;
|
||||
if (!si.pif.gamepad) {
|
||||
Util::warn("[WARN]: Could not initialize gamepad: {}", SDL_GetError());
|
||||
} else {
|
||||
si.pif.gamepadConnected = true;
|
||||
}
|
||||
} break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
SDL_GameControllerClose(si.pif.gamepad);
|
||||
si.pif.gamepadConnected = false;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_o: {
|
||||
OpenROMDialog(window, core);
|
||||
} break;
|
||||
case SDLK_F1: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 0;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F2: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 1;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F3: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 2;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F4: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 3;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F5: {
|
||||
if(core.romLoaded) {
|
||||
if(event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 4;
|
||||
} else {
|
||||
core.Deserialize();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SDLK_F6: {
|
||||
if(core.romLoaded) {
|
||||
if(event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 5;
|
||||
} else {
|
||||
core.Serialize();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SDLK_F7: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 6;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F8: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 7;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F9: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 8;
|
||||
}
|
||||
} break;
|
||||
case SDLK_F10: {
|
||||
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
|
||||
core.slot = 9;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
case SDL_DROPFILE: {
|
||||
char *droppedDir = event.drop.file;
|
||||
if (droppedDir) {
|
||||
window.LoadROM(core, droppedDir);
|
||||
SDL_free(droppedDir);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
if(core.romLoaded) {
|
||||
if(!core.pause) {
|
||||
core.Run(window.settings.GetVolumeL(), window.settings.GetVolumeR());
|
||||
}
|
||||
if(core.render) {
|
||||
UpdateScreenParallelRdp(core, window, core.GetVI());
|
||||
}
|
||||
} else {
|
||||
if(core.render) {
|
||||
UpdateScreenParallelRdpNoGame(core, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include <imgui/Window.hpp>
|
||||
#include <Discord.hpp>
|
||||
|
||||
struct App {
|
||||
App();
|
||||
~App() { Util::ClearRPC(); }
|
||||
void Run();
|
||||
n64::Core core;
|
||||
Window window;
|
||||
};
|
||||
@@ -1,4 +1,62 @@
|
||||
file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(kaizen-qt)
|
||||
|
||||
add_library(frontend ${SOURCES} ${HEADERS})
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(mio REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
../
|
||||
../utils
|
||||
../backend
|
||||
../backend/core
|
||||
../backend/core/mmio
|
||||
../backend/core/registers
|
||||
../backend/core/rsp
|
||||
../../external/
|
||||
../../external/xbyak
|
||||
../../external/parallel-rdp
|
||||
../../external/parallel-rdp/parallel-rdp-standalone/parallel-rdp
|
||||
../../external/parallel-rdp/parallel-rdp-standalone/volk
|
||||
../../external/parallel-rdp/parallel-rdp-standalone/spirv-cross
|
||||
../../external/parallel-rdp/parallel-rdp-standalone/vulkan
|
||||
../../external/parallel-rdp/parallel-rdp-standalone/vulkan-headers/include
|
||||
../../external/parallel-rdp/parallel-rdp-standalone/util
|
||||
../../external/nativefiledialog-extended/src/include
|
||||
../../external/imgui/imgui
|
||||
../../external/imgui/imgui/backends
|
||||
../../external/discord-rpc/include
|
||||
../../external/unarr
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_subdirectory(../backend backend)
|
||||
add_subdirectory(../../external/discord-rpc discord-rpc)
|
||||
add_subdirectory(../../external/nativefiledialog-extended nfd)
|
||||
add_subdirectory(../../external/parallel-rdp parallel-rdp)
|
||||
add_subdirectory(../../external/unarr unarr)
|
||||
|
||||
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF)
|
||||
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF)
|
||||
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
add_executable(kaizen-qt
|
||||
main.cpp)
|
||||
|
||||
target_link_libraries(kaizen-qt PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets SDL2::SDL2 fmt::fmt mio::mio nlohmann_json::nlohmann_json
|
||||
discord-rpc nfd parallel-rdp backend)
|
||||
|
||||
file(COPY ../../resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/)
|
||||
file(REMOVE
|
||||
${PROJECT_BINARY_DIR}/resources/mario.png
|
||||
${PROJECT_BINARY_DIR}/resources/shader.frag
|
||||
${PROJECT_BINARY_DIR}/resources/shader.vert)
|
||||
@@ -1,105 +0,0 @@
|
||||
#pragma once
|
||||
#include <log.hpp>
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
namespace Language {
|
||||
|
||||
enum StringID {
|
||||
MENU_FILE,
|
||||
FILE_ITEM_OPEN,
|
||||
FILE_ITEM_EXIT,
|
||||
MENU_EMULATION,
|
||||
EMULATION_ITEM_RESET,
|
||||
EMULATION_ITEM_STOP,
|
||||
EMULATION_ITEM_PAUSE,
|
||||
EMULATION_ITEM_RESUME,
|
||||
EMULATION_ITEM_SETTINGS,
|
||||
EMULATION_ITEM_LOAD_STATE,
|
||||
EMULATION_ITEM_SAVE_STATE,
|
||||
EMULATION_MENU_STATES,
|
||||
STATES_ITEM_SLOT,
|
||||
SETTINGS_CATEGORY_CPU,
|
||||
SETTINGS_CATEGORY_AUDIO,
|
||||
SETTINGS_CATEGORY_INTERFACE,
|
||||
SETTINGS_OPTION_MUTE,
|
||||
SETTINGS_OPTION_VOLUME_L,
|
||||
SETTINGS_OPTION_VOLUME_R,
|
||||
SETTINGS_OPTION_LOCK_CHANNELS,
|
||||
SETTINGS_OPTION_ENABLE_JIT,
|
||||
SETTINGS_OPTION_LANGUAGE,
|
||||
SETTINGS_CLOSE,
|
||||
STRING_COUNT
|
||||
};
|
||||
|
||||
static const std::map <StringID, const char*> english = {
|
||||
{MENU_FILE, "File"},
|
||||
{FILE_ITEM_OPEN, "Open"},
|
||||
{FILE_ITEM_EXIT, "Exit"},
|
||||
{MENU_EMULATION, "Emulation"},
|
||||
{EMULATION_ITEM_RESET, "Reset"},
|
||||
{EMULATION_ITEM_STOP, "Stop"},
|
||||
{EMULATION_ITEM_PAUSE, "Pause"},
|
||||
{EMULATION_ITEM_RESUME, "Resume"},
|
||||
{EMULATION_ITEM_SETTINGS, "Settings"},
|
||||
{EMULATION_ITEM_LOAD_STATE, "Load state..."},
|
||||
{EMULATION_ITEM_SAVE_STATE, "Save state..."},
|
||||
{EMULATION_MENU_STATES, "Select save slot"},
|
||||
{STATES_ITEM_SLOT, "Slot {}"},
|
||||
{SETTINGS_CATEGORY_CPU, "CPU"},
|
||||
{SETTINGS_CATEGORY_AUDIO, "Audio"},
|
||||
{SETTINGS_CATEGORY_INTERFACE, "Interface"},
|
||||
{SETTINGS_OPTION_MUTE, "Mute"},
|
||||
{SETTINGS_OPTION_VOLUME_L, "Volume L"},
|
||||
{SETTINGS_OPTION_VOLUME_R, "Volume R"},
|
||||
{SETTINGS_OPTION_LOCK_CHANNELS, "Lock channels"},
|
||||
{SETTINGS_OPTION_ENABLE_JIT, "Enable JIT"},
|
||||
{SETTINGS_OPTION_LANGUAGE, "Language"},
|
||||
{SETTINGS_CLOSE, "Close"}
|
||||
};
|
||||
|
||||
static const std::map <StringID, const char*> italian = {
|
||||
{MENU_FILE, "File"},
|
||||
{FILE_ITEM_OPEN, "Apri"},
|
||||
{FILE_ITEM_EXIT, "Esci"},
|
||||
{MENU_EMULATION, "Emulazione"},
|
||||
{EMULATION_ITEM_RESET, "Reset"},
|
||||
{EMULATION_ITEM_STOP, "Stop"},
|
||||
{EMULATION_ITEM_PAUSE, "Pausa"},
|
||||
{EMULATION_ITEM_RESUME, "Riprendi"},
|
||||
{EMULATION_ITEM_SETTINGS, "Opzioni"},
|
||||
{EMULATION_ITEM_LOAD_STATE, "Carica stato..."},
|
||||
{EMULATION_ITEM_SAVE_STATE, "Salva stato..."},
|
||||
{EMULATION_MENU_STATES, "Seleziona slot"},
|
||||
{STATES_ITEM_SLOT, "Slot {}"},
|
||||
{SETTINGS_CATEGORY_CPU, "CPU"},
|
||||
{SETTINGS_CATEGORY_AUDIO, "Audio"},
|
||||
{SETTINGS_CATEGORY_INTERFACE, "Interfaccia"},
|
||||
{SETTINGS_OPTION_MUTE, "Muta"},
|
||||
{SETTINGS_OPTION_VOLUME_L, "Volume L"},
|
||||
{SETTINGS_OPTION_VOLUME_R, "Volume R"},
|
||||
{SETTINGS_OPTION_LOCK_CHANNELS, "Blocca canali"},
|
||||
{SETTINGS_OPTION_ENABLE_JIT, "Abilita JIT"},
|
||||
{SETTINGS_OPTION_LANGUAGE, "Lingua"},
|
||||
{SETTINGS_CLOSE, "Chiudi"}
|
||||
};
|
||||
|
||||
enum AvailableLangs {
|
||||
ENGLISH,
|
||||
ITALIAN,
|
||||
AVAILABLE_LANGS_COUNT
|
||||
};
|
||||
|
||||
static const std::array<const char*, AVAILABLE_LANGS_COUNT> languages = {
|
||||
"English",
|
||||
"Italiano"
|
||||
};
|
||||
|
||||
static FORCE_INLINE void SetLanguage(std::map<StringID, const char*>& lang, int selectedLang) {
|
||||
switch (selectedLang) {
|
||||
case AvailableLangs::ENGLISH: lang = english; break;
|
||||
case AvailableLangs::ITALIAN: lang = italian; break;
|
||||
default: Util::panic("Language not supported, index {}\n", selectedLang);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
|
||||
if(WIN32)
|
||||
add_compile_definitions(SDL_MAIN_HANDLED)
|
||||
endif()
|
||||
|
||||
add_library(frontend-imgui ${SOURCES} ${HEADERS})
|
||||
@@ -1,183 +0,0 @@
|
||||
#include <Settings.hpp>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <Widgets.hpp>
|
||||
#include <Core.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
#define GET_TRANSLATED_STRING(x) languageStrings[(x)]
|
||||
|
||||
#define checknestedjsonentry(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)
|
||||
|
||||
#define checkjsonentry(name, type, param, defaultVal) \
|
||||
do { \
|
||||
auto name##Entry = settings[param]; \
|
||||
if(!name##Entry.empty()) { \
|
||||
auto value = name##Entry.get<type>(); \
|
||||
(name) = value; \
|
||||
} else { \
|
||||
settingsFile.clear(); \
|
||||
settings[param] = defaultVal; \
|
||||
settingsFile << settings; \
|
||||
(name) = defaultVal; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
Settings::Settings(n64::Core& core) {
|
||||
auto fileExists = fs::exists("resources/settings.json");
|
||||
std::fstream settingsFile;
|
||||
if(fileExists) {
|
||||
settingsFile = std::fstream("resources/settings.json", std::fstream::in | std::fstream::out);
|
||||
settings = json::parse(settingsFile);
|
||||
|
||||
checknestedjsonentry(oldVolumeL, float, "audio", "volumeL", 0.5);
|
||||
checknestedjsonentry(oldVolumeR, float, "audio", "volumeR", 0.5);
|
||||
checknestedjsonentry(mute, bool, "audio", "mute", false);
|
||||
volumeL = mute ? 0 : oldVolumeL;
|
||||
volumeR = mute ? 0 : oldVolumeR;
|
||||
checknestedjsonentry(lockChannels, bool, "audio", "lockChannels", true);
|
||||
checknestedjsonentry(jit, bool, "cpu", "enableJIT", false);
|
||||
checkjsonentry(selectedLanguage, int, "language", Language::ENGLISH);
|
||||
} else {
|
||||
settingsFile = std::fstream("resources/settings.json", std::fstream::trunc | std::fstream::in | std::fstream::out);
|
||||
settings["audio"]["volumeR"] = 0.5;
|
||||
settings["audio"]["volumeL"] = 0.5;
|
||||
settings["audio"]["lockChannels"] = true;
|
||||
settings["audio"]["mute"] = false;
|
||||
settings["cpu"]["enableJIT"] = false;
|
||||
settings["language"] = Language::ENGLISH;
|
||||
|
||||
oldVolumeR = volumeR = 0.5;
|
||||
oldVolumeL = volumeL = 0.5;
|
||||
lockChannels = true;
|
||||
mute = false;
|
||||
jit = false;
|
||||
|
||||
settingsFile << settings;
|
||||
}
|
||||
|
||||
Language::SetLanguage(languageStrings, selectedLanguage);
|
||||
|
||||
if(jit) {
|
||||
core.cpu = std::make_unique<n64::JIT>();
|
||||
} else {
|
||||
core.cpu = std::make_unique<n64::Interpreter>();
|
||||
}
|
||||
settingsFile.close();
|
||||
}
|
||||
|
||||
Settings::~Settings() {
|
||||
auto fileExists = fs::exists("resources/settings.json");
|
||||
std::fstream settingsFile;
|
||||
if(fileExists) {
|
||||
settingsFile = std::fstream("resources/settings.json", std::fstream::trunc | std::fstream::out);
|
||||
} else {
|
||||
settingsFile = std::fstream("resources/settings.json", std::fstream::out);
|
||||
}
|
||||
|
||||
settings["audio"]["volumeR"] = oldVolumeR;
|
||||
settings["audio"]["volumeL"] = oldVolumeL;
|
||||
settings["audio"]["lockChannels"] = lockChannels;
|
||||
settings["audio"]["mute"] = mute;
|
||||
settings["cpu"]["enableJIT"] = jit;
|
||||
settings["language"] = selectedLanguage;
|
||||
settingsFile << settings;
|
||||
|
||||
settingsFile.close();
|
||||
}
|
||||
|
||||
void Settings::RenderWidget(const int& mWw, const int& mWh, bool& show) {
|
||||
if(show) {
|
||||
ImGui::OpenPopup("##settings");
|
||||
const float posX = (float)mWw * (1.f / 32.f), posY = (float)mWh * (1.f / 32.f) + 20;
|
||||
const float sizeX = (float)mWw * (30.f / 32.f), sizeY = (float)mWh * (30.f / 32.f) - 20;
|
||||
ImGui::SetNextWindowPos({ posX, posY });
|
||||
ImGui::SetNextWindowSize({ sizeX, sizeY });
|
||||
if(ImGui::BeginPopupModal("##settings", &show, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
|
||||
if (ImGui::BeginTabBar("##categories")) {
|
||||
if (ImGui::BeginTabItem(GET_TRANSLATED_STRING(Language::SETTINGS_CATEGORY_CPU))) {
|
||||
ImGui::Checkbox(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_ENABLE_JIT), &jit);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem(GET_TRANSLATED_STRING(Language::SETTINGS_CATEGORY_AUDIO))) {
|
||||
ImGui::Checkbox(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_LOCK_CHANNELS), &lockChannels);
|
||||
ImGui::Checkbox(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_MUTE), &mute);
|
||||
if (mute) {
|
||||
volumeL = 0;
|
||||
volumeR = 0;
|
||||
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::SliderFloat(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_VOLUME_L), &oldVolumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
if (lockChannels) {
|
||||
oldVolumeR = oldVolumeL;
|
||||
}
|
||||
ImGui::SliderFloat(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_VOLUME_R), &oldVolumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else {
|
||||
volumeL = oldVolumeL;
|
||||
volumeR = oldVolumeR;
|
||||
|
||||
ImGui::SliderFloat(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_VOLUME_L), &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
if (!lockChannels) {
|
||||
ImGui::SliderFloat(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_VOLUME_R), &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
}
|
||||
else {
|
||||
volumeR = volumeL;
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::SliderFloat(GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_VOLUME_R), &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
oldVolumeL = volumeL;
|
||||
oldVolumeR = volumeR;
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem(GET_TRANSLATED_STRING(Language::SETTINGS_CATEGORY_INTERFACE))) {
|
||||
static auto currentLang = selectedLanguage;
|
||||
const char* languages[Language::AVAILABLE_LANGS_COUNT] = {
|
||||
Language::languages[Language::ENGLISH],
|
||||
Language::languages[Language::ITALIAN]
|
||||
};
|
||||
ImGui::Text("%s:", GET_TRANSLATED_STRING(Language::SETTINGS_OPTION_LANGUAGE));
|
||||
CreateComboList("##language", (int*)&selectedLanguage, languages, (int)Language::AVAILABLE_LANGS_COUNT);
|
||||
ImGui::Separator();
|
||||
|
||||
if (currentLang != selectedLanguage) {
|
||||
currentLang = selectedLanguage;
|
||||
Language::SetLanguage(languageStrings, selectedLanguage);
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
const auto style = ImGui::GetStyle();
|
||||
ImGui::SetCursorPos({
|
||||
ImGui::GetWindowWidth() - ImGui::CalcTextSize(GET_TRANSLATED_STRING(Language::SETTINGS_CLOSE)).x - style.FramePadding.x * 5,
|
||||
ImGui::GetWindowHeight() - ImGui::CalcTextSize(GET_TRANSLATED_STRING(Language::SETTINGS_CLOSE)).y - style.FramePadding.y * 5,
|
||||
});
|
||||
if (ImGui::Button(GET_TRANSLATED_STRING(Language::SETTINGS_CLOSE))) show = false;
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <Language.hpp>
|
||||
|
||||
namespace n64 { struct Core; }
|
||||
using namespace nlohmann;
|
||||
|
||||
struct Settings {
|
||||
explicit Settings(n64::Core& core);
|
||||
~Settings();
|
||||
|
||||
[[nodiscard]] FORCE_INLINE float GetVolumeL() const { return volumeL; };
|
||||
[[nodiscard]] FORCE_INLINE float GetVolumeR() const { return volumeR; };
|
||||
|
||||
void RenderWidget(const int& mWw, const int& mWh, bool& show);
|
||||
std::map<Language::StringID, const char*> languageStrings{};
|
||||
private:
|
||||
bool jit = false;
|
||||
float volumeL, volumeR;
|
||||
float oldVolumeL, oldVolumeR;
|
||||
bool lockChannels = true;
|
||||
bool mute = false;
|
||||
int selectedLanguage = Language::ENGLISH;
|
||||
json settings;
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
|
||||
FORCE_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;
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
#include <filesystem>
|
||||
#include <Window.hpp>
|
||||
#include <Core.hpp>
|
||||
#include <Audio.hpp>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <Discord.hpp>
|
||||
|
||||
VkInstance instance{};
|
||||
namespace fs = std::filesystem;
|
||||
#define GET_TRANSLATED_STRING(x) settings.languageStrings[(x)]
|
||||
|
||||
Window::Window(n64::Core& core) : settings(core) {
|
||||
InitSDL();
|
||||
InitParallelRDP(core.cpu->mem.GetRDRAM(), window);
|
||||
InitImgui();
|
||||
NFD::Init();
|
||||
}
|
||||
|
||||
void Window::handleEvents(SDL_Event event, n64::Core& core) {
|
||||
done = event.window.event == (u8)SDL_WINDOWEVENT_CLOSE
|
||||
&& event.window.windowID == SDL_GetWindowID(window);
|
||||
|
||||
bool minimized = SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED;
|
||||
core.pause = event.window.event == (u8)SDL_WINDOWEVENT_FOCUS_LOST || minimized;
|
||||
core.render = !minimized;
|
||||
}
|
||||
|
||||
static void check_vk_result(VkResult err) {
|
||||
if (err != VK_SUCCESS) {
|
||||
Util::panic("[vulkan] Error: VkResult = {}", static_cast<int>(err));
|
||||
}
|
||||
}
|
||||
|
||||
void Window::InitSDL() {
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
n64::InitAudio();
|
||||
|
||||
windowTitle = "Kaizen";
|
||||
window = SDL_CreateWindow(
|
||||
"Kaizen",
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
1024, 768,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
|
||||
);
|
||||
|
||||
if(!window) {
|
||||
Util::panic("Could not create SDL window: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
check_vk_result(volkInitialize());
|
||||
}
|
||||
|
||||
void Window::InitImgui() {
|
||||
VkResult err;
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
(void)io;
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
instance = GetVkInstance();
|
||||
physicalDevice = GetVkPhysicalDevice();
|
||||
device = GetVkDevice();
|
||||
queueFamily = GetVkGraphicsQueueFamily();
|
||||
queue = GetGraphicsQueue();
|
||||
pipelineCache = nullptr;
|
||||
descriptorPool = nullptr;
|
||||
allocator = nullptr;
|
||||
minImageCount = 2;
|
||||
|
||||
ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(instance, function_name); });
|
||||
|
||||
{
|
||||
VkDescriptorPoolSize poolSizes[] = {
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo poolInfo{};
|
||||
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
poolInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes);
|
||||
poolInfo.poolSizeCount = (uint32_t)IM_ARRAYSIZE(poolSizes);
|
||||
poolInfo.pPoolSizes = poolSizes;
|
||||
err = vkCreateDescriptorPool(device, &poolInfo, allocator, &descriptorPool);
|
||||
check_vk_result(err);
|
||||
}
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForVulkan(window);
|
||||
ImGui_ImplVulkan_InitInfo initInfo = {};
|
||||
initInfo.Instance = instance;
|
||||
initInfo.PhysicalDevice = physicalDevice;
|
||||
initInfo.Device = device;
|
||||
initInfo.QueueFamily = queueFamily;
|
||||
initInfo.Queue = queue;
|
||||
initInfo.PipelineCache = pipelineCache;
|
||||
initInfo.DescriptorPool = descriptorPool;
|
||||
initInfo.Allocator = allocator;
|
||||
initInfo.MinImageCount = minImageCount;
|
||||
initInfo.ImageCount = 2;
|
||||
initInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
initInfo.CheckVkResultFn = check_vk_result;
|
||||
ImGui_ImplVulkan_Init(&initInfo, GetVkRenderPass());
|
||||
|
||||
int displayIndex = SDL_GetWindowDisplayIndex(window);
|
||||
float ddpi, hdpi, vdpi;
|
||||
SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi);
|
||||
|
||||
ddpi /= 96.f;
|
||||
|
||||
uiFont = io.Fonts->AddFontFromFileTTF("resources/OpenSans.ttf", 16.f * ddpi);
|
||||
|
||||
ImGui::GetStyle().ScaleAllSizes(ddpi);
|
||||
|
||||
{
|
||||
VkCommandBuffer commandBuffer = GetVkCommandBuffer();
|
||||
ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
|
||||
SubmitRequestedVkCommandBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
Window::~Window() {
|
||||
auto err = vkDeviceWaitIdle(device);
|
||||
check_vk_result(err);
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_DestroyWindow(g_Window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
ImDrawData* Window::Present(n64::Core& core) {
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame(window);
|
||||
ImGui::NewFrame();
|
||||
|
||||
Render(core);
|
||||
|
||||
ImGui::Render();
|
||||
return ImGui::GetDrawData();
|
||||
}
|
||||
|
||||
void Window::LoadROM(n64::Core& core, const std::string &path) {
|
||||
if(!path.empty()) {
|
||||
core.LoadROM(path);
|
||||
gameName = core.cpu->mem.rom.gameNameDB;
|
||||
|
||||
if(gameName.empty()) {
|
||||
gameName = fs::path(path).stem().string();
|
||||
}
|
||||
|
||||
Util::UpdateRPC(Util::Playing, gameName);
|
||||
windowTitle = "Kaizen - " + gameName;
|
||||
shadowWindowTitle = windowTitle;
|
||||
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Window::RenderMainMenuBar(n64::Core &core) {
|
||||
ImGui::BeginMainMenuBar();
|
||||
|
||||
if (ImGui::BeginMenu(GET_TRANSLATED_STRING(Language::MENU_FILE))) {
|
||||
if (ImGui::MenuItem(GET_TRANSLATED_STRING(Language::FILE_ITEM_OPEN), "O")) {
|
||||
OpenROMDialog(*this, core);
|
||||
}
|
||||
if (ImGui::MenuItem("Dump RDRAM")) {
|
||||
core.cpu->mem.DumpRDRAM();
|
||||
}
|
||||
if (ImGui::MenuItem("Dump IMEM")) {
|
||||
core.cpu->mem.DumpIMEM();
|
||||
}
|
||||
if (ImGui::MenuItem("Dump DMEM")) {
|
||||
core.cpu->mem.DumpDMEM();
|
||||
}
|
||||
if (ImGui::MenuItem(GET_TRANSLATED_STRING(Language::FILE_ITEM_EXIT))) {
|
||||
done = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu(GET_TRANSLATED_STRING(Language::MENU_EMULATION))) {
|
||||
if (ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_RESET))) {
|
||||
LoadROM(core, core.rom);
|
||||
}
|
||||
if (ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_STOP))) {
|
||||
windowTitle = "Kaizen";
|
||||
core.rom.clear();
|
||||
Util::UpdateRPC(Util::Idling);
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
core.Stop();
|
||||
}
|
||||
if (ImGui::MenuItem(core.pause ? GET_TRANSLATED_STRING(Language::EMULATION_ITEM_RESUME)
|
||||
: GET_TRANSLATED_STRING(Language::EMULATION_ITEM_PAUSE), nullptr, false, core.romLoaded)) {
|
||||
core.TogglePause();
|
||||
if(core.pause) {
|
||||
shadowWindowTitle = windowTitle;
|
||||
windowTitle += " | Paused";
|
||||
Util::UpdateRPC(Util::Paused, gameName);
|
||||
} else {
|
||||
windowTitle = shadowWindowTitle;
|
||||
Util::UpdateRPC(Util::Playing, gameName);
|
||||
}
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
}
|
||||
|
||||
if(ImGui::BeginMenu(GET_TRANSLATED_STRING(Language::EMULATION_MENU_STATES))) {
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 0).c_str())) { core.slot = 0; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 1).c_str())) { core.slot = 1; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 2).c_str())) { core.slot = 2; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 3).c_str())) { core.slot = 3; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 4).c_str())) { core.slot = 4; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 5).c_str())) { core.slot = 5; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 6).c_str())) { core.slot = 6; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 7).c_str())) { core.slot = 7; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 8).c_str())) { core.slot = 8; }
|
||||
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 9).c_str())) { core.slot = 9; }
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_LOAD_STATE), "F5", false, core.romLoaded)) {
|
||||
core.Deserialize();
|
||||
}
|
||||
if(ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_SAVE_STATE), "F6", false, core.romLoaded)) {
|
||||
core.Serialize();
|
||||
}
|
||||
if (ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_SETTINGS))) {
|
||||
showSettings = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
void Window::Render(n64::Core& core) {
|
||||
ImGui::PushFont(uiFont);
|
||||
|
||||
u32 ticks = SDL_GetTicks();
|
||||
static u32 lastFrame = 0;
|
||||
if(!core.pause && core.romLoaded && lastFrame < ticks - 1000) {
|
||||
lastFrame = ticks;
|
||||
windowTitle += fmt::format(" | {:02d} VI/s", core.cpu->mem.mmio.vi.swaps);
|
||||
core.cpu->mem.mmio.vi.swaps = 0;
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
windowTitle = shadowWindowTitle;
|
||||
}
|
||||
|
||||
if(SDL_GetMouseFocus()) {
|
||||
RenderMainMenuBar(core);
|
||||
}
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
|
||||
settings.RenderWidget(w, h, showSettings);
|
||||
|
||||
ImGui::PopFont();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
#include <ParallelRDPWrapper.hpp>
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_sdl2.h>
|
||||
#include <imgui_impl_vulkan.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vector>
|
||||
#include <frontend/imgui/Settings.hpp>
|
||||
#include <nfd.hpp>
|
||||
#include "Discord.hpp"
|
||||
|
||||
struct Window {
|
||||
explicit Window(n64::Core& core);
|
||||
~Window();
|
||||
ImDrawData* Present(n64::Core& core);
|
||||
|
||||
void handleEvents(SDL_Event event, n64::Core&);
|
||||
ImFont *uiFont{};
|
||||
Settings settings;
|
||||
void LoadROM(n64::Core& core, const std::string& path);
|
||||
bool done = false;
|
||||
std::string gameName{};
|
||||
private:
|
||||
bool showSettings = false;
|
||||
SDL_Window* window{};
|
||||
std::string windowTitle{"Kaizen"};
|
||||
std::string shadowWindowTitle{windowTitle};
|
||||
void InitSDL();
|
||||
void InitImgui();
|
||||
void Render(n64::Core& core);
|
||||
void RenderMainMenuBar(n64::Core& core);
|
||||
|
||||
VkPhysicalDevice physicalDevice{};
|
||||
VkDevice device{};
|
||||
uint32_t queueFamily{uint32_t(-1)};
|
||||
VkQueue queue{};
|
||||
VkPipelineCache pipelineCache{};
|
||||
VkDescriptorPool descriptorPool{};
|
||||
VkAllocationCallbacks* allocator{};
|
||||
|
||||
u32 minImageCount = 2;
|
||||
};
|
||||
|
||||
static void FORCE_INLINE OpenROMDialog(Window& window, n64::Core& core) {
|
||||
nfdchar_t *outpath;
|
||||
const nfdu8filteritem_t filter{"Nintendo 64 roms/archives", "n64,z64,v64,N64,Z64,V64,zip,tar,rar,7z"};
|
||||
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
||||
if (result == NFD_OKAY) {
|
||||
window.LoadROM(core, outpath);
|
||||
NFD_FreePath(outpath);
|
||||
}
|
||||
}
|
||||
16
src/frontend/main.cpp
Normal file
16
src/frontend/main.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
app.setStyle("fusion");
|
||||
QCoreApplication::setOrganizationName("kaizen");
|
||||
QCoreApplication::setApplicationName("Kaizen");
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
parser.addPositionalArgument("rom", "Rom to launch from command-line");
|
||||
parser.process(app);
|
||||
return app.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user