start this from scratch
This commit is contained in:
@@ -1,45 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.20)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
project(kaizen)
|
|
||||||
|
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
find_package(SDL2 REQUIRED)
|
|
||||||
find_package(fmt REQUIRED)
|
|
||||||
find_package(mio REQUIRED)
|
|
||||||
find_package(nlohmann_json REQUIRED)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
include_directories(
|
|
||||||
src
|
|
||||||
src/utils
|
|
||||||
src/frontend
|
|
||||||
src/frontend/imgui
|
|
||||||
src/backend
|
|
||||||
src/backend/core
|
|
||||||
src/backend/core/mmio
|
|
||||||
src/backend/core/registers
|
|
||||||
src/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_compile_definitions(SIMD_SUPPORT)
|
add_compile_definitions(SIMD_SUPPORT)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_definitions(-DNOMINMAX)
|
add_definitions(-DNOMINMAX)
|
||||||
@@ -56,39 +18,13 @@ if (HAS_SSSE3 AND HAS_SSE4_1)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
|
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
|
||||||
|
add_compile_definitions(VULKAN_DEBUG)
|
||||||
#add_compile_options(-fsanitize=address -fsanitize=undefined)
|
#add_compile_options(-fsanitize=address -fsanitize=undefined)
|
||||||
#add_link_options(-fsanitize=address -fsanitize=undefined)
|
#add_link_options(-fsanitize=address -fsanitize=undefined)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src/frontend)
|
find_package(Qt6 COMPONENTS Core Gui Widgets)
|
||||||
add_subdirectory(src/frontend/imgui)
|
|
||||||
add_subdirectory(src/backend)
|
|
||||||
add_subdirectory(src/backend/netplay)
|
|
||||||
add_subdirectory(src/backend/core)
|
|
||||||
add_subdirectory(src/backend/core/interpreter)
|
|
||||||
add_subdirectory(src/backend/core/JIT)
|
|
||||||
add_subdirectory(src/backend/core/mem)
|
|
||||||
add_subdirectory(src/backend/core/mmio)
|
|
||||||
add_subdirectory(src/backend/core/registers)
|
|
||||||
add_subdirectory(src/backend/core/rsp)
|
|
||||||
add_subdirectory(external/discord-rpc)
|
|
||||||
add_subdirectory(external/imgui)
|
|
||||||
add_subdirectory(external/nativefiledialog-extended)
|
|
||||||
add_subdirectory(external/parallel-rdp)
|
|
||||||
add_subdirectory(external/unarr)
|
|
||||||
|
|
||||||
add_executable(kaizen main.cpp)
|
if (Qt6_FOUND)
|
||||||
|
add_subdirectory(src/frontend)
|
||||||
if(MSVC)
|
endif()
|
||||||
target_compile_options(parallel-rdp PUBLIC /EHa)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(COPY ${PROJECT_SOURCE_DIR}/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)
|
|
||||||
|
|
||||||
target_link_libraries(kaizen PUBLIC frontend frontend-imgui
|
|
||||||
discord-rpc imgui nfd parallel-rdp backend fmt::fmt mio::mio nlohmann_json::nlohmann_json core registers jit interpreter mem unarr mmio rsp SDL2::SDL2main SDL2::SDL2)
|
|
||||||
target_compile_options(kaizen PUBLIC -Wextra)
|
|
||||||
26
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
26
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
@@ -6,7 +6,6 @@
|
|||||||
#include <File.hpp>
|
#include <File.hpp>
|
||||||
#include <SDL2/SDL_vulkan.h>
|
#include <SDL2/SDL_vulkan.h>
|
||||||
#include <imgui_impl_vulkan.h>
|
#include <imgui_impl_vulkan.h>
|
||||||
#include <imgui/Window.hpp>
|
|
||||||
|
|
||||||
using namespace Vulkan;
|
using namespace Vulkan;
|
||||||
using namespace RDP;
|
using namespace RDP;
|
||||||
@@ -70,13 +69,13 @@ void SetFramerateUnlocked(bool unlocked) {
|
|||||||
|
|
||||||
class SDLWSIPlatform final : public Vulkan::WSIPlatform {
|
class SDLWSIPlatform final : public Vulkan::WSIPlatform {
|
||||||
public:
|
public:
|
||||||
SDLWSIPlatform() = default;
|
SDLWSIPlatform(SDL_Window* window) : window(window) {}
|
||||||
|
|
||||||
std::vector<const char *> get_instance_extensions() override {
|
std::vector<const char *> get_instance_extensions() override {
|
||||||
const char* extensions[64];
|
const char* extensions[64];
|
||||||
unsigned int num_extensions = 64;
|
unsigned int num_extensions = 64;
|
||||||
|
|
||||||
if (!SDL_Vulkan_GetInstanceExtensions(g_Window, &num_extensions, extensions)) {
|
if (!SDL_Vulkan_GetInstanceExtensions(window, &num_extensions, extensions)) {
|
||||||
Util::panic("SDL_Vulkan_GetInstanceExtensions failed: {}", SDL_GetError());
|
Util::panic("SDL_Vulkan_GetInstanceExtensions failed: {}", SDL_GetError());
|
||||||
}
|
}
|
||||||
auto vec = std::vector<const char*>();
|
auto vec = std::vector<const char*>();
|
||||||
@@ -90,7 +89,7 @@ public:
|
|||||||
|
|
||||||
VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override {
|
VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override {
|
||||||
VkSurfaceKHR vk_surface;
|
VkSurfaceKHR vk_surface;
|
||||||
if (!SDL_Vulkan_CreateSurface(g_Window, instance, &vk_surface)) {
|
if (!SDL_Vulkan_CreateSurface(window, instance, &vk_surface)) {
|
||||||
Util::panic("Failed to create Vulkan window surface: {}", SDL_GetError());
|
Util::panic("Failed to create Vulkan window surface: {}", SDL_GetError());
|
||||||
}
|
}
|
||||||
return vk_surface;
|
return vk_surface;
|
||||||
@@ -120,6 +119,8 @@ public:
|
|||||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||||
.apiVersion = VK_API_VERSION_1_1
|
.apiVersion = VK_API_VERSION_1_1
|
||||||
};
|
};
|
||||||
|
private:
|
||||||
|
SDL_Window* window;
|
||||||
};
|
};
|
||||||
|
|
||||||
Program* fullscreen_quad_program;
|
Program* fullscreen_quad_program;
|
||||||
@@ -184,8 +185,7 @@ void LoadParallelRDP(const u8* rdram) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InitParallelRDP(const u8* rdram, SDL_Window* window) {
|
void InitParallelRDP(const u8* rdram, SDL_Window* window) {
|
||||||
g_Window = window;
|
LoadWSIPlatform(new SDLWSIPlatform(window), std::make_unique<SDLParallelRdpWindowInfo>(window));
|
||||||
LoadWSIPlatform(new SDLWSIPlatform(), std::make_unique<SDLParallelRdpWindowInfo>());
|
|
||||||
LoadParallelRDP(rdram);
|
LoadParallelRDP(rdram);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ void DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image, Util::Intrusive
|
|||||||
cmd->draw(3, 1);
|
cmd->draw(3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateScreen(n64::Core& core, Window& imguiWindow, Util::IntrusivePtr<Image> image) {
|
void UpdateScreen(n64::Core& core, Util::IntrusivePtr<Image> image) {
|
||||||
wsi->begin_frame();
|
wsi->begin_frame();
|
||||||
|
|
||||||
if (!image) {
|
if (!image) {
|
||||||
@@ -253,18 +253,14 @@ void UpdateScreen(n64::Core& core, Window& imguiWindow, Util::IntrusivePtr<Image
|
|||||||
Util::IntrusivePtr<CommandBuffer> cmd = wsi->get_device().request_command_buffer();
|
Util::IntrusivePtr<CommandBuffer> cmd = wsi->get_device().request_command_buffer();
|
||||||
|
|
||||||
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
|
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
|
||||||
ImDrawData* drawData = imguiWindow.Present(core);
|
|
||||||
|
|
||||||
DrawFullscreenTexturedQuad(image, cmd);
|
DrawFullscreenTexturedQuad(image, cmd);
|
||||||
|
|
||||||
ImGui_ImplVulkan_RenderDrawData(drawData, cmd->get_command_buffer());
|
|
||||||
|
|
||||||
cmd->end_render_pass();
|
cmd->end_render_pass();
|
||||||
wsi->get_device().submit(cmd);
|
wsi->get_device().submit(cmd);
|
||||||
wsi->end_frame();
|
wsi->end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateScreenParallelRdp(n64::Core& core, Window& imguiWindow, n64::VI& vi) {
|
void UpdateScreenParallelRdp(n64::Core& core, n64::VI& vi) {
|
||||||
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
||||||
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
||||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||||
@@ -290,12 +286,12 @@ void UpdateScreenParallelRdp(n64::Core& core, Window& imguiWindow, n64::VI& vi)
|
|||||||
opts.downscale_steps = true;
|
opts.downscale_steps = true;
|
||||||
opts.crop_overscan_pixels = true;
|
opts.crop_overscan_pixels = true;
|
||||||
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
||||||
UpdateScreen(core, imguiWindow, image);
|
UpdateScreen(core, image);
|
||||||
command_processor->begin_frame_context();
|
command_processor->begin_frame_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateScreenParallelRdpNoGame(n64::Core& core, Window& imguiWindow) {
|
void UpdateScreenParallelRdpNoGame(n64::Core& core) {
|
||||||
UpdateScreen(core, imguiWindow, static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
UpdateScreen(core, static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRdpEnqueueCommand(int command_length, u32* buffer) {
|
void ParallelRdpEnqueueCommand(int command_length, u32* buffer) {
|
||||||
|
|||||||
12
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
12
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
@@ -2,9 +2,6 @@
|
|||||||
#include <backend/Core.hpp>
|
#include <backend/Core.hpp>
|
||||||
#include <wsi.hpp>
|
#include <wsi.hpp>
|
||||||
|
|
||||||
struct Window;
|
|
||||||
static SDL_Window* g_Window;
|
|
||||||
|
|
||||||
class ParallelRdpWindowInfo {
|
class ParallelRdpWindowInfo {
|
||||||
public:
|
public:
|
||||||
struct CoordinatePair {
|
struct CoordinatePair {
|
||||||
@@ -16,9 +13,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class SDLParallelRdpWindowInfo : public ParallelRdpWindowInfo {
|
class SDLParallelRdpWindowInfo : public ParallelRdpWindowInfo {
|
||||||
|
SDL_Window* window;
|
||||||
|
public:
|
||||||
|
SDLParallelRdpWindowInfo(SDL_Window* window) : window(window) {}
|
||||||
CoordinatePair get_window_size() {
|
CoordinatePair get_window_size() {
|
||||||
int width, height;
|
int width, height;
|
||||||
SDL_GetWindowSize(g_Window, &width, &height);
|
SDL_GetWindowSize(window, &width, &height);
|
||||||
return CoordinatePair{ static_cast<float>(width), static_cast<float>(height) };
|
return CoordinatePair{ static_cast<float>(width), static_cast<float>(height) };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -33,9 +33,9 @@ VkFormat GetVkFormat();
|
|||||||
VkCommandBuffer GetVkCommandBuffer();
|
VkCommandBuffer GetVkCommandBuffer();
|
||||||
void SubmitRequestedVkCommandBuffer();
|
void SubmitRequestedVkCommandBuffer();
|
||||||
void InitParallelRDP(const u8* rdram, SDL_Window* window);
|
void InitParallelRDP(const u8* rdram, SDL_Window* window);
|
||||||
void UpdateScreenParallelRdp(n64::Core& core, Window& imguiWindow, n64::VI& vi);
|
void UpdateScreenParallelRdp(n64::Core& core, n64::VI& vi);
|
||||||
void ParallelRdpEnqueueCommand(int command_length, u32* buffer);
|
void ParallelRdpEnqueueCommand(int command_length, u32* buffer);
|
||||||
void ParallelRdpOnFullSync();
|
void ParallelRdpOnFullSync();
|
||||||
void UpdateScreenParallelRdpNoGame(n64::Core& core, Window& imguiWindow);
|
void UpdateScreenParallelRdpNoGame(n64::Core& core);
|
||||||
bool IsFramerateUnlocked();
|
bool IsFramerateUnlocked();
|
||||||
void SetFramerateUnlocked(bool unlocked);
|
void SetFramerateUnlocked(bool unlocked);
|
||||||
|
|||||||
17
main.cpp
17
main.cpp
@@ -1,17 +0,0 @@
|
|||||||
#include <App.hpp>
|
|
||||||
#include <MupenMovie.hpp>
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
App app;
|
|
||||||
|
|
||||||
if(argc > 1) {
|
|
||||||
if(argc > 2) {
|
|
||||||
LoadTAS(argv[2]);
|
|
||||||
}
|
|
||||||
app.window.LoadROM(app.core, argv[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
file(GLOB SOURCES *.cpp)
|
file(GLOB SOURCES *.cpp)
|
||||||
file(GLOB HEADERS *.hpp)
|
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 SOURCES *.cpp)
|
||||||
file(GLOB HEADERS *.hpp)
|
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 <log.hpp>
|
||||||
#include <core/Mem.hpp>
|
#include <core/Mem.hpp>
|
||||||
#include <core/registers/Registers.hpp>
|
#include <core/registers/Registers.hpp>
|
||||||
#include <core/Audio.hpp>
|
#include <Audio.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
void AI::Reset() {
|
void AI::Reset() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <PIF.hpp>
|
#include <PIF.hpp>
|
||||||
#include <MupenMovie.hpp>
|
#include <PIF/MupenMovie.hpp>
|
||||||
#include <Netplay.hpp>
|
#include <Netplay.hpp>
|
||||||
#include <log.hpp>
|
#include <log.hpp>
|
||||||
#include <SDL2/SDL_keyboard.h>
|
#include <SDL2/SDL_keyboard.h>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include <MupenMovie.hpp>
|
#include <PIF/MupenMovie.hpp>
|
||||||
#include <log.hpp>
|
#include <log.hpp>
|
||||||
|
|
||||||
struct TASMovieHeader {
|
struct TASMovieHeader {
|
||||||
@@ -2,3 +2,4 @@ file(GLOB_RECURSE SOURCES *.cpp)
|
|||||||
file(GLOB_RECURSE HEADERS *.cpp)
|
file(GLOB_RECURSE HEADERS *.cpp)
|
||||||
|
|
||||||
add_library(registers ${SOURCES} ${HEADERS})
|
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)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
file(GLOB HEADERS *.hpp)
|
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