From 63f9cfe0c24e93b53d0dbab5b87560a4a21fbbdb Mon Sep 17 00:00:00 2001 From: offtkp Date: Wed, 26 Jul 2023 16:36:37 +0300 Subject: [PATCH 1/6] Add load_rom http server command --- include/httpserver.hpp | 4 +++- src/emulator.cpp | 6 ++--- src/httpserver.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 9d3cb818..25bc9c7c 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,7 @@ #include "helpers.hpp" -enum class HttpActionType { None, Screenshot, Key }; +enum class HttpActionType { None, Screenshot, Key, LoadRom }; class Emulator; namespace httplib { @@ -42,6 +43,7 @@ class HttpAction { static std::unique_ptr createScreenshotAction(DeferredResponseWrapper& response); static std::unique_ptr createKeyAction(uint32_t key, bool state); + static std::unique_ptr createLoadRomAction(std::filesystem::path path, bool paused); }; struct HttpServer { diff --git a/src/emulator.cpp b/src/emulator.cpp index 9d933bd7..5e42c086 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -114,6 +114,9 @@ void Emulator::render() {} void Emulator::run() { while (running) { +#ifdef PANDA3DS_ENABLE_HTTP_SERVER + httpServer.processActions(); +#endif runFrame(); HIDService& hid = kernel.getServiceManager().getHID(); @@ -345,9 +348,6 @@ void Emulator::run() { void Emulator::runFrame() { if (romType != ROMType::None) { -#ifdef PANDA3DS_ENABLE_HTTP_SERVER - httpServer.processActions(); -#endif cpu.runFrame(); // Run 1 frame of instructions gpu.display(); // Display graphics diff --git a/src/httpserver.cpp b/src/httpserver.cpp index d8841807..2def1887 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -30,12 +31,27 @@ class HttpActionKey : public HttpAction { bool getState() const { return state; } }; +class HttpActionLoadRom : public HttpAction { + std::filesystem::path path; + bool paused; + + public: + HttpActionLoadRom(std::filesystem::path path, bool paused) : HttpAction(HttpActionType::LoadRom), path(path), paused(paused) {} + + std::filesystem::path getPath() const { return path; } + bool getPaused() const { return paused; } +}; + std::unique_ptr HttpAction::createScreenshotAction(DeferredResponseWrapper& response) { return std::make_unique(response); } std::unique_ptr HttpAction::createKeyAction(u32 key, bool state) { return std::make_unique(key, state); } +std::unique_ptr HttpAction::createLoadRomAction(std::filesystem::path path, bool paused) { + return std::make_unique(path, paused); +} + HttpServer::HttpServer(Emulator* emulator) : emulator(emulator), server(std::make_unique()), keyMap({ {"A", {HID::Keys::A}}, @@ -110,6 +126,37 @@ void HttpServer::startHttpServer() { server->Get("/status", [this](const httplib::Request&, httplib::Response& response) { response.set_content(status(), "text/plain"); }); + server->Get("/load_rom", [this](const httplib::Request& request, httplib::Response& response) { + printf("load_rom\n"); + auto it = request.params.find("path"); + if (it == request.params.end()) { + response.set_content("error", "text/plain"); + return; + } + + std::filesystem::path romPath = it->second; + if (romPath.empty()) { + response.set_content("error", "text/plain"); + return; + } else { + std::error_code error; + if (!std::filesystem::is_regular_file(romPath, error)) { + std::string message = "error: " + error.message(); + response.set_content(message, "text/plain"); + return; + } + } + + bool paused = false; + it = request.params.find("paused"); + if (it != request.params.end()) { + paused = (it->second == "1"); + } + + pushAction(HttpAction::createLoadRomAction(romPath, paused)); + response.set_content("ok", "text/plain"); + }); + // TODO: ability to specify host and port printf("Starting HTTP server on port 1234\n"); server->listen("localhost", 1234); @@ -165,6 +212,13 @@ void HttpServer::processActions() { break; } + case HttpActionType::LoadRom: { + HttpActionLoadRom* loadRomAction = static_cast(action.get()); + emulator->loadROM(loadRomAction->getPath()); + paused = loadRomAction->getPaused(); + break; + } + default: break; } } From 804e43f8c9db3095e472fb49fa3d5770564199a3 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:25:01 +0300 Subject: [PATCH 2/6] Fix ExeFS offset derp --- src/core/loader/ncch.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c1ce1b98..9e07f228 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -159,8 +159,9 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn // Read ExeFS if (hasExeFS()) { - u64 exeFSOffset = fileOffset + exeFS.offset; // Offset of ExeFS in the file = exeFS offset + ncch offset - printf("ExeFS offset: %08llX, size: %08llX (Offset in file = %08llX)\n", exeFS.offset, exeFS.size, exeFSOffset); + // Offset of ExeFS in the file = exeFS offset + NCCH offset + // exeFS.offset has already been offset by the NCCH offset + printf("ExeFS offset: %08llX, size: %08llX (Offset in file = %08llX)\n", exeFS.offset - info.offset, exeFS.size, exeFS.offset); constexpr size_t exeFSHeaderSize = 0x200; u8 exeFSHeader[exeFSHeaderSize]; From 3653eb538f8290c561f7c9ecce9a07a9f21777db Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:38:38 +0300 Subject: [PATCH 3/6] Fix struct & class confusion --- include/emulator.hpp | 2 +- include/httpserver.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index b508338e..0e03bbba 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -61,7 +61,7 @@ class Emulator { #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; - friend class HttpServer; + friend struct HttpServer; #endif // Keep the handle for the ROM here to reload when necessary and to prevent deleting it diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 82aee2a4..3bb26e2f 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -18,7 +18,7 @@ enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset, LoadRom } class Emulator; namespace httplib { class Server; - class Response; + struct Response; } // Wrapper for httplib::Response that allows the HTTP server to wait for the response to be ready From 630d423a4d774e80c6e666d76c805228964426ed Mon Sep 17 00:00:00 2001 From: offtkp Date: Thu, 27 Jul 2023 13:01:38 +0300 Subject: [PATCH 4/6] Make load_rom use DeferredResponseWrapper --- include/httpserver.hpp | 2 +- src/httpserver.cpp | 45 ++++++++++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 3bb26e2f..13caf3e4 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -43,7 +43,7 @@ class HttpAction { static std::unique_ptr createScreenshotAction(DeferredResponseWrapper& response); static std::unique_ptr createKeyAction(uint32_t key, bool state); - static std::unique_ptr createLoadRomAction(std::filesystem::path path, bool paused); + static std::unique_ptr createLoadRomAction(DeferredResponseWrapper& response, const std::filesystem::path& path, bool paused); static std::unique_ptr createTogglePauseAction(); static std::unique_ptr createResetAction(); }; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 4f9e0fe2..ca04d3d6 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -42,13 +42,16 @@ class HttpActionKey : public HttpAction { }; class HttpActionLoadRom : public HttpAction { - std::filesystem::path path; + DeferredResponseWrapper& response; + const std::filesystem::path& path; bool paused; public: - HttpActionLoadRom(std::filesystem::path path, bool paused) : HttpAction(HttpActionType::LoadRom), path(path), paused(paused) {} + HttpActionLoadRom(DeferredResponseWrapper& response, const std::filesystem::path& path, bool paused) + : HttpAction(HttpActionType::LoadRom), response(response), path(path), paused(paused) {} - std::filesystem::path getPath() const { return path; } + DeferredResponseWrapper& getResponse() { return response; } + const std::filesystem::path& getPath() const { return path; } bool getPaused() const { return paused; } }; @@ -60,8 +63,8 @@ std::unique_ptr HttpAction::createKeyAction(u32 key, bool state) { r std::unique_ptr HttpAction::createTogglePauseAction() { return std::make_unique(); } std::unique_ptr HttpAction::createResetAction() { return std::make_unique(); } -std::unique_ptr HttpAction::createLoadRomAction(std::filesystem::path path, bool paused) { - return std::make_unique(path, paused); +std::unique_ptr HttpAction::createLoadRomAction(DeferredResponseWrapper& response, const std::filesystem::path& path, bool paused) { + return std::make_unique(response, path, paused); } HttpServer::HttpServer(Emulator* emulator) @@ -99,6 +102,7 @@ void HttpServer::startHttpServer() { server->Get("/ping", [](const httplib::Request&, httplib::Response& response) { response.set_content("pong", "text/plain"); }); server->Get("/screen", [this](const httplib::Request&, httplib::Response& response) { + // TODO: make the below a DeferredResponseWrapper function DeferredResponseWrapper wrapper(response); // Lock the mutex before pushing the action to ensure that the condition variable is not notified before we wait on it std::unique_lock lock(wrapper.mutex); @@ -139,7 +143,6 @@ void HttpServer::startHttpServer() { server->Get("/status", [this](const httplib::Request&, httplib::Response& response) { response.set_content(status(), "text/plain"); }); server->Get("/load_rom", [this](const httplib::Request& request, httplib::Response& response) { - printf("load_rom\n"); auto it = request.params.find("path"); if (it == request.params.end()) { response.set_content("error", "text/plain"); @@ -165,8 +168,11 @@ void HttpServer::startHttpServer() { paused = (it->second == "1"); } - pushAction(HttpAction::createLoadRomAction(romPath, paused)); + DeferredResponseWrapper wrapper(response); + std::unique_lock lock(wrapper.mutex); + pushAction(HttpAction::createLoadRomAction(wrapper, romPath, paused)); response.set_content("ok", "text/plain"); + wrapper.cv.wait(lock, [&wrapper] { return wrapper.ready; }); }); server->Get("/togglepause", [this](const httplib::Request&, httplib::Response& response) { @@ -236,12 +242,31 @@ void HttpServer::processActions() { case HttpActionType::LoadRom: { HttpActionLoadRom* loadRomAction = static_cast(action.get()); - emulator->loadROM(loadRomAction->getPath()); - paused = loadRomAction->getPaused(); + DeferredResponseWrapper& response = loadRomAction->getResponse(); + bool loaded = emulator->loadROM(loadRomAction->getPath()); + + if (!loaded) { + response.inner_response.set_content("error", "text/plain"); + } else { + response.inner_response.set_content("ok", "text/plain"); + } + + std::unique_lock lock(response.mutex); + response.ready = true; + response.cv.notify_one(); + + if (loaded) { + paused = loadRomAction->getPaused(); + if (paused) { + emulator->pause(); + } else { + emulator->resume(); + } + } break; } - case HttpActionType::TogglePause: emulator->togglePause(); break; + case HttpActionType::TogglePause: emulator->togglePause(); paused ^= true; break; case HttpActionType::Reset: emulator->reset(Emulator::ReloadOption::Reload); break; default: break; From 420abd26d0339cad7b3e29602eb708455c1ea53d Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:49:13 +0300 Subject: [PATCH 5/6] Format --- include/httpserver.hpp | 2 +- src/httpserver.cpp | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 13caf3e4..08e98bbf 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -42,7 +42,7 @@ class HttpAction { HttpActionType getType() const { return type; } static std::unique_ptr createScreenshotAction(DeferredResponseWrapper& response); - static std::unique_ptr createKeyAction(uint32_t key, bool state); + static std::unique_ptr createKeyAction(u32 key, bool state); static std::unique_ptr createLoadRomAction(DeferredResponseWrapper& response, const std::filesystem::path& path, bool paused); static std::unique_ptr createTogglePauseAction(); static std::unique_ptr createResetAction(); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ca04d3d6..cccd69d9 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -171,9 +171,9 @@ void HttpServer::startHttpServer() { DeferredResponseWrapper wrapper(response); std::unique_lock lock(wrapper.mutex); pushAction(HttpAction::createLoadRomAction(wrapper, romPath, paused)); - response.set_content("ok", "text/plain"); + response.set_content("ok", "text/plain"); wrapper.cv.wait(lock, [&wrapper] { return wrapper.ready; }); - }); + }); server->Get("/togglepause", [this](const httplib::Request&, httplib::Response& response) { pushAction(HttpAction::createTogglePauseAction()); @@ -245,11 +245,7 @@ void HttpServer::processActions() { DeferredResponseWrapper& response = loadRomAction->getResponse(); bool loaded = emulator->loadROM(loadRomAction->getPath()); - if (!loaded) { - response.inner_response.set_content("error", "text/plain"); - } else { - response.inner_response.set_content("ok", "text/plain"); - } + response.inner_response.set_content(loaded ? "ok" : "error", "text/plain"); std::unique_lock lock(response.mutex); response.ready = true; @@ -266,7 +262,10 @@ void HttpServer::processActions() { break; } - case HttpActionType::TogglePause: emulator->togglePause(); paused ^= true; break; + case HttpActionType::TogglePause: + emulator->togglePause(); + paused = !paused; + break; case HttpActionType::Reset: emulator->reset(Emulator::ReloadOption::Reload); break; default: break; From 177e265d9f7afe00ab2d7839b4b25fd00ea8cde0 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:53:13 +0300 Subject: [PATCH 6/6] Create build_http.yml --- .github/workflows/build_http.yml | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/build_http.yml diff --git a/.github/workflows/build_http.yml b/.github/workflows/build_http.yml new file mode 100644 index 00000000..46210509 --- /dev/null +++ b/.github/workflows/build_http.yml @@ -0,0 +1,40 @@ +name: HTTP server build + +on: + push: + branches: + - master + pull_request: + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Fetch submodules + run: git submodule update --init --recursive + + - name: Setup Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.0 + with: + vulkan-query-version: latest + vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_USER_BUILD=ON -DENABLE_HTTP_SERVER=ON + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}