From 183efa3d8f0766f66414286121d977630972e406 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 21 May 2025 19:54:22 +0200 Subject: [PATCH] More debug features: pause/step/resume (#140) * More debug features: pause/step/resume * Fix palette order (I think) * Revert "Fix palette order (I think)" This reverts commit a81de08f7889398285e44a9798a604826b720c4f. --- .github/workflows/ci.yml | 4 + ISLE/isleapp.cpp | 15 +- ISLE/isledebug.cpp | 222 +++++++++++++++++- ISLE/isledebug.h | 34 ++- .../legoomni/include/legobuildingmanager.h | 2 + .../lego/legoomni/include/legoplantmanager.h | 2 + .../lego/legoomni/include/legovideomanager.h | 2 + LEGO1/lego/sources/roi/legoroi.h | 2 + LEGO1/omni/include/mxticklemanager.h | 2 + 9 files changed, 267 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 970232db..fa7753c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,10 @@ name: CI on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + jobs: clang-format: name: 'clang-format' diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index cd5201d2..03f2801e 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -353,13 +353,13 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) switch (event->type) { case SDL_EVENT_WINDOW_FOCUS_GAINED: - if (!g_debugEnabled) { + if (!IsleDebug_Enabled()) { g_isle->SetWindowActive(TRUE); Lego()->Resume(); } break; case SDL_EVENT_WINDOW_FOCUS_LOST: - if (!g_debugEnabled) { + if (!IsleDebug_Enabled()) { g_isle->SetWindowActive(FALSE); Lego()->Pause(); } @@ -722,6 +722,10 @@ inline bool IsleApp::Tick() // GLOBAL: ISLE 0x4101bc static MxS32 g_startupDelay = 200; + if (IsleDebug_Paused() && IsleDebug_StepModeEnabled()) { + IsleDebug_SetPaused(false); + } + if (!m_windowActive) { SDL_Delay(1); return true; @@ -752,6 +756,11 @@ inline bool IsleApp::Tick() } g_lastFrameTime = currentTime; + if (IsleDebug_StepModeEnabled()) { + IsleDebug_SetPaused(true); + IsleDebug_ResetStepMode(); + } + if (g_startupDelay == 0) { return true; } @@ -845,7 +854,7 @@ MxResult IsleApp::ParseArguments(int argc, char** argv) } #ifdef ISLE_DEBUG else if (strcmp(argv[i], "--debug") == 0) { - g_debugEnabled = true; + IsleDebug_SetEnabled(true); consumed = 1; } #endif diff --git a/ISLE/isledebug.cpp b/ISLE/isledebug.cpp index b2b52752..cd75ab3a 100644 --- a/ISLE/isledebug.cpp +++ b/ISLE/isledebug.cpp @@ -1,22 +1,150 @@ #include "isledebug.h" #include "isleapp.h" +#include "lego/sources/roi/legoroi.h" #include "legobuildingmanager.h" +#include "legoentity.h" #include "legogamestate.h" #include "legoplantmanager.h" #include "legosoundmanager.h" #include "legovideomanager.h" #include "misc.h" +#include "mxticklemanager.h" #include #include #include #include -bool g_debugEnabled; -bool g_debugPaused; -SDL_Window* g_debugWindow; -SDL_Renderer* g_debugRenderer; +#define SCANCODE_KEY_PAUSE SDL_SCANCODE_KP_0 +#define SCANCODE_KEY_RESUME SDL_SCANCODE_KP_PERIOD + +static bool g_debugEnabled; +static bool g_debugPaused; +static bool g_debugDoStep; +static SDL_Window* g_debugWindow; +static SDL_Renderer* g_debugRenderer; + +static SDL_Texture* g_videoPalette; + +class DebugViewer { +public: + static void InsidePlantManager() + { + LegoPlantManager* plantManager = Lego()->GetPlantManager(); + ImGui::Value("#plants", plantManager->GetNumPlants()); + ImGui::Value("#entries", plantManager->m_numEntries); + if (plantManager->m_numEntries) { + if (ImGui::BeginTable("Animated Entries", 4, ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("ROI Name"); + ImGui::TableSetupColumn("ROI m_unk0x100"); + ImGui::TableSetupColumn("Entity Name"); + ImGui::TableSetupColumn("Time"); + ImGui::TableHeadersRow(); + for (MxS8 i = 0; i < plantManager->m_numEntries; i++) { + const auto* entry = plantManager->m_entries[i]; + ImGui::TableNextRow(); + ImGui::Text("%s", entry->m_roi->m_name); + ImGui::TableNextColumn(); + ImGui::Text("%d", entry->m_roi->m_unk0x100); + ImGui::TableNextColumn(); + ImGui::Text("%s", entry->m_roi->m_entity->ClassName()); + ImGui::TableNextColumn(); + ImGui::Text("%d", entry->m_time); + } + ImGui::EndTable(); + } + } + } + static void InsideBuildingManager() + { + auto buildingManager = Lego()->GetBuildingManager(); + ImGui::Text("nextVariant: %d", buildingManager->m_nextVariant); + ImGui::Text("m_unk0x09: %d", buildingManager->m_unk0x09); + ImGui::Text("m_unk0x28: %d", buildingManager->m_unk0x28); + ImGui::Text("#Animated Entries", buildingManager->m_numEntries); + if (buildingManager->m_numEntries) { + if (ImGui::BeginTable("Animated Entries", 6, ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("ROI Name"); + ImGui::TableSetupColumn("ROI m_unk0x100"); + ImGui::TableSetupColumn("Entity Name"); + ImGui::TableSetupColumn("Time"); + ImGui::TableSetupColumn("unk_0x0c"); + ImGui::TableSetupColumn("Muted"); + ImGui::TableHeadersRow(); + for (MxS8 i = 0; i < buildingManager->m_numEntries; i++) { + const auto* entry = buildingManager->m_entries[i]; + ImGui::TableNextRow(); + ImGui::Text("%s", entry->m_roi->m_name); + ImGui::TableNextColumn(); + ImGui::Text("%d", entry->m_roi->m_unk0x100); + ImGui::TableNextColumn(); + ImGui::Text("%s", entry->m_roi->m_entity->ClassName()); + ImGui::TableNextColumn(); + ImGui::Text("%d", entry->m_time); + ImGui::TableNextColumn(); + ImGui::Text("%d", entry->m_unk0x0c); + ImGui::TableNextColumn(); + ImGui::Text("%d", entry->m_muted); + } + ImGui::EndTable(); + } + } + } + static void InsideTickleManager() + { + auto tickleManager = Lego()->GetTickleManager(); + ImGui::Value("#clients", static_cast(tickleManager->m_clients.size())); + if (ImGui::BeginTable("Clients", 3, ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("Client"); + ImGui::TableSetupColumn("Interval"); + ImGui::TableSetupColumn("Flags"); + ImGui::TableHeadersRow(); + for (const auto* ticleClient : tickleManager->m_clients) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%s", ticleClient->GetClient()->ClassName()); + ImGui::TableNextColumn(); + ImGui::Text("%d", ticleClient->GetTickleInterval()); + ImGui::TableNextColumn(); + ImGui::Text("0x%x", ticleClient->GetFlags()); + } + ImGui::EndTable(); + } + } + static void InsideVideoManager() + { + auto videoManager = Lego()->GetVideoManager(); + SDL_UpdateTexture(g_videoPalette, NULL, videoManager->m_paletteEntries, 4 * 16); + ImGui::Text("Elapsed: %gs", static_cast(videoManager->GetElapsedSeconds())); + ImGui::Text("Render3D: %d", videoManager->GetRender3D()); + ImGui::Text("unk0xe5: %d", videoManager->m_unk0xe5); + ImGui::Text("unk0xe5: %d", videoManager->m_unk0xe6); + ImGui::Text("unk0x54c: %f", videoManager->m_unk0x54c); + ImGui::Text("unk0x54c: %f", videoManager->m_unk0x550); + ImGui::Text("unk0x54c: %d", videoManager->m_unk0x554); + ImGui::Text("unk0x70: %u", videoManager->m_unk0x70); + ImGui::Text("Dither: %d", videoManager->m_dither); + ImGui::Text("BufferCount: %u", videoManager->m_bufferCount); + ImGui::Text("Paused: %f", videoManager->m_paused); + ImGui::Text("back: %g", videoManager->m_back); + ImGui::Text("front: %g", videoManager->m_front); + ImGui::Text("cameraWidth: %g", videoManager->m_cameraWidth); + ImGui::Text("cameraHeight: %g", videoManager->m_cameraHeight); + ImGui::Text("fov: %g", videoManager->m_fov); + ImVec2 uv_min = ImVec2(0.0f, 0.0f); + ImVec2 uv_max = ImVec2(1.0f, 1.0f); + ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, SDL_max(1.0f, ImGui::GetStyle().ImageBorderSize)); + ImGui::ImageWithBg( + (ImTextureID) (uintptr_t) g_videoPalette, + ImVec2(200, 200), + uv_min, + uv_max, + ImVec4(0.0f, 0.0f, 0.0f, 1.0f) + ); + ImGui::PopStyleVar(); + } +}; void IsleDebug_Init() { @@ -38,11 +166,15 @@ void IsleDebug_Init() IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGui::StyleColorsDark(); + if (!ImGui_ImplSDL3_InitForSDLRenderer(g_debugWindow, g_debugRenderer)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ImGui_ImplSDL3_InitForSDLRenderer failed"); g_debugEnabled = false; break; } + g_videoPalette = + SDL_CreateTexture(g_debugRenderer, SDL_PIXELFORMAT_RGBX32, SDL_TEXTUREACCESS_STREAMING, 16, 16); + SDL_SetTextureScaleMode(g_videoPalette, SDL_SCALEMODE_PIXELART); if (!ImGui_ImplSDLRenderer3_Init(g_debugRenderer)) { g_debugEnabled = false; break; @@ -65,6 +197,24 @@ bool IsleDebug_Event(SDL_Event* event) if (!g_debugEnabled) { return false; } + if (event->type == SDL_EVENT_KEY_DOWN) { + if (event->key.scancode == SCANCODE_KEY_PAUSE) { + if (!g_debugPaused) { + IsleDebug_SetPaused(true); + } + else { + g_debugDoStep = true; + } + return true; + } + if (event->key.scancode == SCANCODE_KEY_RESUME) { + g_debugDoStep = false; + if (g_debugPaused) { + IsleDebug_SetPaused(false); + } + return true; + } + } if (SDL_GetWindowFromEvent(event) != g_debugWindow) { return false; } @@ -97,6 +247,7 @@ void IsleDebug_Render() } } ImGui::EndMainMenuBar(); + ImGui::ShowDemoWindow(nullptr); LegoOmni* lego = Lego(); if (ImGui::Begin("LEGO")) { if (ImGui::TreeNode("Game State")) { @@ -121,24 +272,24 @@ void IsleDebug_Render() ImGui::TreePop(); } if (ImGui::TreeNode("Video Manager")) { - LegoVideoManager* videoManager = lego->GetVideoManager(); - ImGui::Text("Elapsed: %g", static_cast(videoManager->GetElapsedSeconds())); - ImGui::Value("Render3D", videoManager->GetRender3D()); + DebugViewer::InsideVideoManager(); ImGui::TreePop(); } if (ImGui::TreeNode("Plant Manager")) { - LegoPlantManager* plantManager = lego->GetPlantManager(); - ImGui::Value("#plants", plantManager->GetNumPlants()); + DebugViewer::InsidePlantManager(); ImGui::TreePop(); } if (ImGui::TreeNode("Building Manager")) { - LegoBuildingManager* buildingManager = lego->GetBuildingManager(); + DebugViewer::InsideBuildingManager(); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Tickle Manager")) { + DebugViewer::InsideTickleManager(); ImGui::TreePop(); } } ImGui::End(); } - ImGui::ShowDemoWindow(nullptr); ImGui::Render(); SDL_RenderClear(g_debugRenderer); @@ -153,3 +304,52 @@ void IsleDebug_Render() ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), g_debugRenderer); SDL_RenderPresent(g_debugRenderer); } + +bool IsleDebug_Enabled() +{ + return g_debugEnabled; +} + +void IsleDebug_SetEnabled(bool v) +{ + if (v) { + SDL_Log( + "Press \"%s\" for pausing/stepping the game", + SDL_GetKeyName(SDL_GetKeyFromScancode(SCANCODE_KEY_PAUSE, 0, false)) + ); + SDL_Log( + "Press \"%s\" for resuming the game", + SDL_GetKeyName(SDL_GetKeyFromScancode(SCANCODE_KEY_RESUME, 0, false)) + ); + } + g_debugEnabled = v; +} + +void IsleDebug_SetPaused(bool v) +{ + SDL_assert(g_debugPaused == !v); + g_debugPaused = v; + if (g_debugPaused) { + g_isle->SetWindowActive(false); + Lego()->Pause(); + } + else { + g_isle->SetWindowActive(true); + Lego()->Resume(); + } +} + +bool IsleDebug_Paused() +{ + return g_debugPaused; +} + +void IsleDebug_ResetStepMode() +{ + g_debugDoStep = false; +} + +bool IsleDebug_StepModeEnabled() +{ + return g_debugDoStep; +} diff --git a/ISLE/isledebug.h b/ISLE/isledebug.h index e3f2bfbb..a2d01f97 100644 --- a/ISLE/isledebug.h +++ b/ISLE/isledebug.h @@ -3,29 +3,55 @@ #if defined(ISLE_DEBUG) -extern bool g_debugEnabled; - typedef union SDL_Event SDL_Event; +extern bool IsleDebug_Enabled(); + +extern void IsleDebug_SetEnabled(bool); + extern void IsleDebug_Init(); extern bool IsleDebug_Event(SDL_Event* event); extern void IsleDebug_Render(); +extern void IsleDebug_SetPaused(bool v); + +extern bool IsleDebug_Paused(); + +extern bool IsleDebug_StepModeEnabled(); + +extern void IsleDebug_ResetStepMode(); + #else +#define IsleDebug_Enabled() (false) + +#define IsleDebug_SetEnabled(V) \ + do { \ + } while (0) + #define IsleDebug_Init() \ do { \ } while (0) -#define IsleDebug_Event(EVENT) false +#define IsleDebug_Event(EVENT) (false) #define IsleDebug_Render() \ do { \ } while (0) -#define g_debugEnabled false +#define IsleDebug_SetPaused(X) \ + do { \ + } while (0) + +#define IsleDebug_Paused() (false) + +#define IsleDebug_StepModeEnabled() (false) + +#define IsleDebug_ResetStepMode() \ + do { \ + } while (0) #endif diff --git a/LEGO1/lego/legoomni/include/legobuildingmanager.h b/LEGO1/lego/legoomni/include/legobuildingmanager.h index a529a8d3..8133f98e 100644 --- a/LEGO1/lego/legoomni/include/legobuildingmanager.h +++ b/LEGO1/lego/legoomni/include/legobuildingmanager.h @@ -108,6 +108,8 @@ class LegoBuildingManager : public MxCore { LegoCacheSound* m_sound; // 0x24 MxBool m_unk0x28; // 0x28 LegoWorld* m_world; // 0x2c + + friend class DebugViewer; }; #endif // LEGOBUILDINGMANAGER_H diff --git a/LEGO1/lego/legoomni/include/legoplantmanager.h b/LEGO1/lego/legoomni/include/legoplantmanager.h index a494b5a2..d5007770 100644 --- a/LEGO1/lego/legoomni/include/legoplantmanager.h +++ b/LEGO1/lego/legoomni/include/legoplantmanager.h @@ -82,6 +82,8 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore { AnimEntry* m_entries[5]; // 0x10 MxS8 m_numEntries; // 0x24 LegoWorld* m_world; // 0x28 + + friend class DebugViewer; }; #endif // LEGOPLANTMANAGER_H diff --git a/LEGO1/lego/legoomni/include/legovideomanager.h b/LEGO1/lego/legoomni/include/legovideomanager.h index cd7b8973..23da06e9 100644 --- a/LEGO1/lego/legoomni/include/legovideomanager.h +++ b/LEGO1/lego/legoomni/include/legovideomanager.h @@ -124,6 +124,8 @@ class LegoVideoManager : public MxVideoManager { D3DRMRENDERMODE m_rendermode; // 0x584 BOOL m_dither; // 0x588 DWORD m_bufferCount; // 0x58c + + friend class DebugViewer; }; // SYNTHETIC: LEGO1 0x1007ab20 diff --git a/LEGO1/lego/sources/roi/legoroi.h b/LEGO1/lego/sources/roi/legoroi.h index e5767a50..8b59cfb6 100644 --- a/LEGO1/lego/sources/roi/legoroi.h +++ b/LEGO1/lego/sources/roi/legoroi.h @@ -89,6 +89,8 @@ class LegoROI : public ViewROI { BoundingSphere m_sphere; // 0xe8 undefined m_unk0x100; // 0x100 LegoEntity* m_entity; // 0x104 + + friend class DebugViewer; }; // VTABLE: LEGO1 0x100dbea8 diff --git a/LEGO1/omni/include/mxticklemanager.h b/LEGO1/omni/include/mxticklemanager.h index 1fae7a43..c941d58d 100644 --- a/LEGO1/omni/include/mxticklemanager.h +++ b/LEGO1/omni/include/mxticklemanager.h @@ -55,6 +55,8 @@ class MxTickleManager : public MxCore { private: MxTickleClientPtrList m_clients; // 0x08 + + friend class DebugViewer; }; #define TICKLE_MANAGER_NOT_FOUND 0x80000000