diff --git a/.gitmodules b/.gitmodules index 30c0036e..1fb0fcca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "third_party/glm"] path = third_party/glm url = https://github.com/g-truc/glm +[submodule "third_party/discord-rpc"] + path = third_party/discord-rpc + url = https://github.com/discord/discord-rpc diff --git a/CMakeLists.txt b/CMakeLists.txt index c5725ff0..ac557f89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,10 @@ -cmake_minimum_required(VERSION 3.10) +# We need to be able to use enable_language(OBJC) on Mac, so we need CMake 3.16 vs the 3.10 we use otherwise. Blame Apple. +if (APPLE) + cmake_minimum_required(VERSION 3.16) +else() + cmake_minimum_required(VERSION 3.10) +endif() + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -7,12 +13,16 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION endif() if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) + set(CMAKE_BUILD_TYPE Release) endif() project(Alber) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +if(APPLE) + enable_language(OBJC) +endif() + if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security") endif() @@ -24,6 +34,7 @@ option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON) option(ENABLE_LTO "Enable link-time optimization" OFF) option(ENABLE_USER_BUILD "Make a user-facing build. These builds have various assertions disabled, LTO, and more" OFF) option(ENABLE_HTTP_SERVER "Enable HTTP server. Used for Discord bot support" OFF) +option(ENABLE_DISCORD_RPC "Compile with Discord RPC support (disabled by default)" ON) include_directories(${PROJECT_SOURCE_DIR}/include/) include_directories(${PROJECT_SOURCE_DIR}/include/kernel) @@ -43,6 +54,11 @@ add_compile_definitions(NOMINMAX) # Make windows.h not define min/ma add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything add_compile_definitions(SDL_MAIN_HANDLED) +if(ENABLE_DISCORD_RPC) + add_subdirectory(third_party/discord-rpc) + include_directories(third_party/discord-rpc/include) +endif() + set(SDL_STATIC ON CACHE BOOL "" FORCE) set(SDL_SHARED OFF CACHE BOOL "" FORCE) set(SDL_TEST OFF CACHE BOOL "" FORCE) @@ -100,6 +116,7 @@ set(SOURCE_FILES src/main.cpp src/emulator.cpp src/io_file.cpp src/config.cpp src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp + src/discord_rpc.cpp ) set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp) set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp @@ -157,7 +174,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/crypto/aes_engine.hpp include/metaprogramming.hpp include/PICA/pica_vertex.hpp include/config.hpp include/services/ir_user.hpp include/http_server.hpp include/cheats.hpp include/action_replay.hpp include/renderer_sw/renderer_sw.hpp include/compiler_builtins.hpp - include/fs/romfs.hpp include/fs/ivfc.hpp + include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp @@ -251,11 +268,16 @@ endif() add_executable(Alber ${ALL_SOURCES}) if(ENABLE_LTO OR ENABLE_USER_BUILD) - set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) + set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() target_link_libraries(Alber PRIVATE dynarmic SDL2-static cryptopp glad) +if(ENABLE_DISCORD_RPC) + target_compile_definitions(Alber PUBLIC "PANDA3DS_ENABLE_DISCORD_RPC=1") + target_link_libraries(Alber PRIVATE discord-rpc) +endif() + if(ENABLE_OPENGL) target_compile_definitions(Alber PUBLIC "PANDA3DS_ENABLE_OPENGL=1") target_link_libraries(Alber PRIVATE resources_renderer_gl) diff --git a/include/config.hpp b/include/config.hpp index 6bccdad6..631ada81 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -6,6 +6,7 @@ // Remember to initialize every field here to its default value otherwise bad things will happen struct EmulatorConfig { bool shaderJitEnabled = false; + bool discordRpcEnabled = false; RendererType rendererType = RendererType::OpenGL; EmulatorConfig(const std::filesystem::path& path); diff --git a/include/discord_rpc.hpp b/include/discord_rpc.hpp new file mode 100644 index 00000000..9b244faf --- /dev/null +++ b/include/discord_rpc.hpp @@ -0,0 +1,23 @@ +#pragma once + +#ifdef PANDA3DS_ENABLE_DISCORD_RPC +#include + +#include +#include + +namespace Discord { + enum class RPCStatus { Idling, Playing }; + + class RPC { + std::uint64_t startTimestamp; + bool enabled = false; + + public: + void init(); + void update(RPCStatus status, const std::string& title); + void stop(); + }; +} // namespace Discord + +#endif \ No newline at end of file diff --git a/include/emulator.hpp b/include/emulator.hpp index e34e93dc..a3ab09a5 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -11,6 +11,7 @@ #include "config.hpp" #include "cpu.hpp" #include "crypto/aes_engine.hpp" +#include "discord_rpc.hpp" #include "io_file.hpp" #include "memory.hpp" @@ -64,6 +65,11 @@ class Emulator { friend struct HttpServer; #endif +#ifdef PANDA3DS_ENABLE_DISCORD_RPC + Discord::RPC discordRpc; +#endif + void updateDiscord(); + // Keep the handle for the ROM here to reload when necessary and to prevent deleting it // This is currently only used for ELFs, NCSDs use the IOFile API instead std::ifstream loadedELF; diff --git a/src/config.cpp b/src/config.cpp index a5e9330c..b194ab09 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -29,6 +29,15 @@ void EmulatorConfig::load(const std::filesystem::path& path) { return; } + if (data.contains("General")) { + auto generalResult = toml::expect(data.at("General")); + if (generalResult.is_ok()) { + auto general = generalResult.unwrap(); + + discordRpcEnabled = toml::find_or(general, "EnableDiscordRPC", false); + } + } + if (data.contains("GPU")) { auto gpuResult = toml::expect(data.at("GPU")); if (gpuResult.is_ok()) { @@ -68,6 +77,7 @@ void EmulatorConfig::save(const std::filesystem::path& path) { printf("Saving new configuration file %s\n", path.string().c_str()); } + data["General"]["EnableDiscordRPC"] = discordRpcEnabled; data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); diff --git a/src/discord_rpc.cpp b/src/discord_rpc.cpp new file mode 100644 index 00000000..018b1dcf --- /dev/null +++ b/src/discord_rpc.cpp @@ -0,0 +1,41 @@ +#ifdef PANDA3DS_ENABLE_DISCORD_RPC + +#include "discord_rpc.hpp" + +#include +#include + +void Discord::RPC::init() { + DiscordEventHandlers handlers{}; + Discord_Initialize("1138176975865909360", &handlers, 1, nullptr); + + startTimestamp = time(nullptr); + enabled = true; +} + +void Discord::RPC::update(Discord::RPCStatus status, const std::string& game) { + DiscordRichPresence rpc{}; + + if (status == Discord::RPCStatus::Playing) { + rpc.details = "Playing a game"; + rpc.state = game.c_str(); + } else { + rpc.details = "Idle"; + } + + rpc.largeImageKey = "pand"; + rpc.largeImageText = "Panda3DS is a 3DS emulator for Windows, MacOS and Linux"; + rpc.startTimestamp = startTimestamp; + + Discord_UpdatePresence(&rpc); +} + +void Discord::RPC::stop() { + if (enabled) { + enabled = false; + Discord_ClearPresence(); + Discord_Shutdown(); + } +} + +#endif \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 2e7cd521..9825cd47 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -13,7 +13,7 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1; Emulator::Emulator() : config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config), - memory(cpu.getTicksRef()), cheats(memory, kernel.getServiceManager().getHID()) + memory(cpu.getTicksRef()), cheats(memory, kernel.getServiceManager().getHID()), running(false), programRunning(false) #ifdef PANDA3DS_ENABLE_HTTP_SERVER , httpServer(this) #endif @@ -34,6 +34,13 @@ Emulator::Emulator() needOpenGL = needOpenGL || (config.rendererType == RendererType::OpenGL); #endif +#ifdef PANDA3DS_ENABLE_DISCORD_RPC + if (config.discordRpcEnabled) { + discordRpc.init(); + updateDiscord(); + } +#endif + if (needOpenGL) { // Demand 3.3 core for software renderer, or 4.1 core for OpenGL renderer (max available on MacOS) // MacOS gets mad if we don't explicitly demand a core profile @@ -75,12 +82,16 @@ Emulator::Emulator() } } - running = false; - programRunning = false; reset(ReloadOption::NoReload); } -Emulator::~Emulator() { config.save(std::filesystem::current_path() / "config.toml"); } +Emulator::~Emulator() { + config.save(std::filesystem::current_path() / "config.toml"); + +#ifdef PANDA3DS_ENABLE_DISCORD_RPC + discordRpc.stop(); +#endif +} void Emulator::reset(ReloadOption reload) { cpu.reset(); @@ -121,6 +132,7 @@ void Emulator::run() { #ifdef PANDA3DS_ENABLE_HTTP_SERVER httpServer.processActions(); #endif + runFrame(); HIDService& hid = kernel.getServiceManager().getHID(); @@ -431,6 +443,9 @@ bool Emulator::loadROM(const std::filesystem::path& path) { if (success) { romPath = path; +#ifdef PANDA3DS_ENABLE_DISCORD_RPC + updateDiscord(); +#endif } else { romPath = std::nullopt; romType = ROMType::None; @@ -487,3 +502,18 @@ bool Emulator::loadELF(std::ifstream& file) { // Reset our graphics context and initialize the GPU's graphics context void Emulator::initGraphicsContext() { gpu.initGraphicsContext(window); } + +#ifdef PANDA3DS_ENABLE_DISCORD_RPC +void Emulator::updateDiscord() { + if (config.discordRpcEnabled) { + if (romType != ROMType::None) { + const auto name = romPath.value().stem(); + discordRpc.update(Discord::RPCStatus::Playing, name.string()); + } else { + discordRpc.update(Discord::RPCStatus::Idling, ""); + } + } +} +#else +void Emulator::updateDiscord() {} +#endif \ No newline at end of file diff --git a/third_party/discord-rpc b/third_party/discord-rpc new file mode 160000 index 00000000..963aa9f3 --- /dev/null +++ b/third_party/discord-rpc @@ -0,0 +1 @@ +Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33