From 2509e0112577d9bebbd5176abb04e13f9e4543dd Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Wed, 2 Apr 2025 04:17:26 +0200 Subject: [PATCH] Initial aurora setup, doesn't build yet --- CMakeLists.txt | 30 +++++ README.md | 18 +-- configure.py | 3 + include/port/imgui.h | 18 +++ {include => libc}/ctype.h | 0 {include => libc}/float.h | 0 {include => libc}/math.h | 0 {include => libc}/stdarg.h | 0 {include => libc}/stddef.h | 0 {include => libc}/stdint.h | 0 {include => libc}/stdio.h | 0 {include => libc}/stdlib.h | 0 {include => libc}/string.h | 0 src/dolphin/os/OSAlloc.c | 7 + src/game/init.c | 3 + src/game/main.c | 93 ++++++++++++- src/port/imgui.cpp | 267 +++++++++++++++++++++++++++++++++++++ src/port/stubs.c | 0 18 files changed, 430 insertions(+), 9 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 include/port/imgui.h rename {include => libc}/ctype.h (100%) rename {include => libc}/float.h (100%) rename {include => libc}/math.h (100%) rename {include => libc}/stdarg.h (100%) rename {include => libc}/stddef.h (100%) rename {include => libc}/stdint.h (100%) rename {include => libc}/stdio.h (100%) rename {include => libc}/stdlib.h (100%) rename {include => libc}/string.h (100%) create mode 100644 src/port/imgui.cpp create mode 100644 src/port/stubs.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..e6455cd8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.13) +project(marioparty4 LANGUAGES C CXX) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) + +if (CMAKE_SYSTEM_NAME STREQUAL Linux) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-parameter") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -fsanitize=address -fsanitize-address-use-after-scope") + set(CMAKE_PREFIX_PATH /usr) + set(CMAKE_LIBRARY_ARCHITECTURE i386-linux-gnu) + set(CMAKE_LIBRARY_PATH "/usr/lib32" CACHE PATH "") + set(CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX 32) +endif () + +add_subdirectory(extern/aurora EXCLUDE_FROM_ALL) + +add_executable(marioparty4 + src/game/main.c + src/game/init.c +# libraries/dolphin_pc/GX.c +# libraries/dolphin_pc/vi.c +# libraries/dolphin_pc/pad_evdev.c +# libraries/dolphin_pc/pad_dinput.c +# src/port/byteswap.cpp + src/port/imgui.cpp + src/port/stubs.c + ) +target_compile_definitions(marioparty4 PRIVATE TARGET_PC VERSION=0) +target_include_directories(marioparty4 PRIVATE include) +target_link_libraries(marioparty4 PRIVATE aurora::aurora aurora::main) diff --git a/README.md b/README.md index f1594c57..06a57925 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Linux - For non-x86(_64) platforms: Install wine from your package manager. - For x86(_64), [wibo](https://github.com/decompals/wibo), a minimal 32-bit Windows binary wrapper, will be automatically downloaded and used. -Building +Building the game for the GameCube ======== - Clone the repository: @@ -89,11 +89,13 @@ Building ninja ``` -Diffing -======= +Building the game for PC +===== +After you got the GameCube build up and running for `GMPE01_00`: +- Generate project files using CMake: + ``` + cmake -B build/port -G "Visual Studio 17 2022" -A Win32 + ``` +Linux and MacOS, and x64 support is coming later. -Once the initial build succeeds, an `objdiff.json` should exist in the project root. - -Download the latest release from [encounter/objdiff](https://github.com/encounter/objdiff). Under project settings, set `Project directory`. The configuration should be loaded automatically. - -Select an object from the left sidebar to begin diffing. Changes to the project will rebuild automatically: changes to source files, headers, `configure.py`, `splits.txt` or `symbols.txt`. +- Open the solution in Visual Studio and build. diff --git a/configure.py b/configure.py index 3721f4c9..bc3a4268 100644 --- a/configure.py +++ b/configure.py @@ -160,6 +160,7 @@ config.asflags = [ "-mgekko", "--strip-local-absolute", "-I include", + "-I libc", f"-I build/{config.version}/include", f"--defsym version={version_num}", ] @@ -192,6 +193,7 @@ cflags_base = [ "-fp_contract on", "-str reuse", "-i include", + "-i libc", "-i extern/musyx/include", f"-i build/{config.version}/include", "-multibyte", @@ -270,6 +272,7 @@ cflags_musyx = [ "-nodefaults", "-nosyspath", "-i include", + "-i libc", "-i extern/musyx/include", "-inline auto", "-O4,p", diff --git a/include/port/imgui.h b/include/port/imgui.h new file mode 100644 index 00000000..5c761fb0 --- /dev/null +++ b/include/port/imgui.h @@ -0,0 +1,18 @@ +#ifndef _SRC_IMGUI_H_ +#define _SRC_IMGUI_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + void imgui_main(const AuroraInfo* info); + void frame_limiter(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ctype.h b/libc/ctype.h similarity index 100% rename from include/ctype.h rename to libc/ctype.h diff --git a/include/float.h b/libc/float.h similarity index 100% rename from include/float.h rename to libc/float.h diff --git a/include/math.h b/libc/math.h similarity index 100% rename from include/math.h rename to libc/math.h diff --git a/include/stdarg.h b/libc/stdarg.h similarity index 100% rename from include/stdarg.h rename to libc/stdarg.h diff --git a/include/stddef.h b/libc/stddef.h similarity index 100% rename from include/stddef.h rename to libc/stddef.h diff --git a/include/stdint.h b/libc/stdint.h similarity index 100% rename from include/stdint.h rename to libc/stdint.h diff --git a/include/stdio.h b/libc/stdio.h similarity index 100% rename from include/stdio.h rename to libc/stdio.h diff --git a/include/stdlib.h b/libc/stdlib.h similarity index 100% rename from include/stdlib.h rename to libc/stdlib.h diff --git a/include/string.h b/libc/string.h similarity index 100% rename from include/string.h rename to libc/string.h diff --git a/src/dolphin/os/OSAlloc.c b/src/dolphin/os/OSAlloc.c index 32515afd..7f00ee63 100644 --- a/src/dolphin/os/OSAlloc.c +++ b/src/dolphin/os/OSAlloc.c @@ -137,6 +137,9 @@ static long DLSize(struct Cell *list) void *OSAllocFromHeap(int heap, unsigned long size) { +#ifdef TARGET_PC + return malloc(size); +#endif struct HeapDesc *hd; struct Cell *cell; struct Cell *newCell; @@ -297,6 +300,9 @@ void *OSAllocFixed(void **rstart, void **rend) void OSFreeToHeap(int heap, void *ptr) { +#ifdef TARGET_PC + free(ptr); +#else struct HeapDesc *hd; struct Cell *cell; @@ -310,6 +316,7 @@ void OSFreeToHeap(int heap, void *ptr) ASSERTMSG1(0x247, DLLookup(hd->allocated, cell), "OSFreeToHeap(): invalid pointer."); hd->allocated = DLExtract(hd->allocated, cell); hd->free = DLInsert(hd->free, cell); +#endif } int OSSetCurrentHeap(int heap) diff --git a/src/game/init.c b/src/game/init.c index 39e190f2..8fcc7b8b 100644 --- a/src/game/init.c +++ b/src/game/init.c @@ -50,6 +50,9 @@ void HuSysInit(GXRenderModeObj *mode) OSInit(); DVDInit(); VIInit(); + #if TARGET_PC + VISetWindowTitle("Mario Party 4"); + #endif PADInit(); #if VERSION_NTSC if(OSGetProgressiveMode() == 1 && VIGetDTVStatus() == 1) { diff --git a/src/game/main.c b/src/game/main.c index 4fc6c048..a7001979 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -16,6 +16,13 @@ #include "game/gamework.h" #include "game/sreset.h" +#ifdef TARGET_PC +#include "port/imgui.h" +#include +#include +#include +#endif + extern FileListEntry _ovltbl[]; u32 GlobalCounter; static u32 vcheck; @@ -40,8 +47,54 @@ static u32 fi_req; s32 HuDvdErrWait; s32 SystemInitF; -void main(void) +#ifdef TARGET_PC +#include +void aurora_log_callback(AuroraLogLevel level, const char *message, unsigned int len) { + const char *levelStr = "??"; + FILE *out = stdout; + switch (level) + { + case LOG_DEBUG: + levelStr = "DEBUG"; + break; + case LOG_INFO: + levelStr = "INFO"; + break; + case LOG_WARNING: + levelStr = "WARNING"; + break; + case LOG_ERROR: + levelStr = "ERROR"; + out = stderr; + break; + case LOG_FATAL: + levelStr = "FATAL"; + out = stderr; + break; + } + fprintf(out, "[%s: %s]\n", levelStr, message); + if (level == LOG_FATAL) + { + fflush(out); + abort(); + } +} +#endif + +#ifdef TARGET_PC +int main(int argc, char* argv[]) +#else +void main(void) +#endif +{ +#ifdef AURORA + const AuroraInfo auroraInfo = aurora_initialize(argc, argv, + &(AuroraConfig){ + .appName = "Mario Party 4", + .logCallback = &aurora_log_callback, + }); +#endif u32 met0; u32 met1; s16 i; @@ -82,12 +135,33 @@ void main(void) VIWaitForRetrace(); } while (1) { +#ifdef TARGET_PC + const AuroraEvent *event = aurora_update(); + bool exiting = false; + while (event != NULL && event->type != AURORA_NONE) + { + if (event->type == AURORA_EXIT) + { + exiting = true; + break; + } + ++event; + } + if (exiting) + { + break; + } +#endif retrace = VIGetRetraceCount(); if (HuSoftResetButtonCheck() != 0 || HuDvdErrWait != 0) { continue; } HuPerfZero(); + HuPerfBegin(2); +#ifdef TARGET_PC + aurora_begin_frame(); +#endif HuSysBeforeRender(); GXSetGPMetric(GX_PERF0_CLIP_VTX, GX_PERF1_VERTICES); GXClearGPMetric(); @@ -95,10 +169,12 @@ void main(void) GXClearVCacheMetric(); GXClearPixMetric(); GXClearMemMetric(); + HuPerfBegin(0); Hu3DPreProc(); HuPadRead(); pfClsScr(); + HuPrcCall(1); MGSeqMain(); HuPerfBegin(1); @@ -106,8 +182,15 @@ void main(void) HuDvdErrorWatch(); WipeExecAlways(); HuPerfEnd(0); + pfDrawFonts(); HuPerfEnd(1); + +#ifdef TARGET_PC + imgui_main(&auroraInfo); + aurora_end_frame(); +#endif + msmMusFdoutEnd(); HuSysDoneRender(retrace); GXReadGPMetric(&met0, &met1); @@ -116,7 +199,15 @@ void main(void) GXReadMemMetric(&cp_req, &tc_req, &cpu_rd_req, &cpu_wr_req, &dsp_req, &io_req, &vi_req, &pe_req, &rf_req, &fi_req); HuPerfEnd(2); GlobalCounter++; + +#ifdef TARGET_PC + frame_limiter(); +#endif } + +#ifdef TARGET_PC + aurora_shutdown(); +#endif } void HuSysVWaitSet(s16 vcount) diff --git a/src/port/imgui.cpp b/src/port/imgui.cpp new file mode 100644 index 00000000..abfb7168 --- /dev/null +++ b/src/port/imgui.cpp @@ -0,0 +1,267 @@ +extern "C" +{ +#include "port/imgui.h" +} + +#include +#include +#include +#include +#include +#include +#include +#include + +static bool m_frameRate = true; +static bool m_pipelineInfo = true; +static bool m_graphicsBackend = true; +static int m_debugOverlayCorner = 0; // top-left + +using namespace std::string_literals; +using namespace std::string_view_literals; + +namespace aurora::gfx +{ +extern std::atomic_uint32_t queuedPipelines; +extern std::atomic_uint32_t createdPipelines; + +extern size_t g_drawCallCount; +extern size_t g_mergedDrawCallCount; +extern size_t g_lastVertSize; +extern size_t g_lastUniformSize; +extern size_t g_lastIndexSize; +extern size_t g_lastStorageSize; +} // namespace aurora::gfx + +static void SetOverlayWindowLocation(int corner) +{ + const ImGuiViewport *viewport = ImGui::GetMainViewport(); + ImVec2 workPos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! + ImVec2 workSize = viewport->WorkSize; + ImVec2 windowPos; + ImVec2 windowPosPivot; + constexpr float padding = 10.0f; + windowPos.x = (corner & 1) != 0 ? (workPos.x + workSize.x - padding) : (workPos.x + padding); + windowPos.y = (corner & 2) != 0 ? (workPos.y + workSize.y - padding) : (workPos.y + padding); + windowPosPivot.x = (corner & 1) != 0 ? 1.0f : 0.0f; + windowPosPivot.y = (corner & 2) != 0 ? 1.0f : 0.0f; + ImGui::SetNextWindowPos(windowPos, ImGuiCond_Always, windowPosPivot); +} + +static void ImGuiStringViewText(std::string_view text) +{ + // begin()/end() do not work on MSVC + ImGui::TextUnformatted(text.data(), text.data() + text.size()); +} + +static std::string BytesToString(size_t bytes) +{ + constexpr std::array suffixes{"B"sv, "KB"sv, "MB"sv, "GB"sv, "TB"sv, "PB"sv, "EB"sv}; + uint32_t s = 0; + auto count = static_cast(bytes); + while (count >= 1024.0 && s < 7) + { + s++; + count /= 1024.0; + } + if (count - floor(count) == 0.0) + { + return fmt::format(FMT_STRING("{}{}"), static_cast(count), suffixes[s]); + } + return fmt::format(FMT_STRING("{:.1f}{}"), count, suffixes[s]); +} + +void imgui_main(const AuroraInfo *info) +{ + + ImGuiIO &io = ImGui::GetIO(); + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + if (m_debugOverlayCorner != -1) + { + SetOverlayWindowLocation(m_debugOverlayCorner); + windowFlags |= ImGuiWindowFlags_NoMove; + } + ImGui::SetNextWindowBgAlpha(0.65f); + if (ImGui::Begin("Debug Overlay", nullptr, windowFlags)) + { + bool hasPrevious = false; + if (m_frameRate) + { + if (hasPrevious) + { + ImGui::Separator(); + } + hasPrevious = true; + + ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.1f}\n"), io.Framerate)); + } + if (m_graphicsBackend) + { + if (hasPrevious) + { + ImGui::Separator(); + } + hasPrevious = true; + + std::string_view backendString = "Unknown"sv; + switch (info->backend) + { + case BACKEND_D3D12: + backendString = "D3D12"sv; + break; + case BACKEND_METAL: + backendString = "Metal"sv; + break; + case BACKEND_VULKAN: + backendString = "Vulkan"sv; + break; + case BACKEND_OPENGL: + backendString = "OpenGL"sv; + break; + case BACKEND_OPENGLES: + backendString = "OpenGL ES"sv; + break; + case BACKEND_WEBGPU: + backendString = "WebGPU"sv; + break; + case BACKEND_NULL: + backendString = "Null"sv; + break; + } + ImGuiStringViewText(fmt::format(FMT_STRING("Backend: {}\n"), backendString)); + } + if (m_pipelineInfo) + { + if (hasPrevious) + { + ImGui::Separator(); + } + hasPrevious = true; + + ImGuiStringViewText( + fmt::format(FMT_STRING("Queued pipelines: {}\n"), aurora::gfx::queuedPipelines)); + ImGuiStringViewText( + fmt::format(FMT_STRING("Done pipelines: {}\n"), aurora::gfx::createdPipelines)); + ImGuiStringViewText( + fmt::format(FMT_STRING("Draw call count: {}\n"), aurora::gfx::g_drawCallCount)); + ImGuiStringViewText(fmt::format(FMT_STRING("Merged draw calls: {}\n"), + aurora::gfx::g_mergedDrawCallCount)); + ImGuiStringViewText(fmt::format(FMT_STRING("Vertex size: {}\n"), + BytesToString(aurora::gfx::g_lastVertSize))); + ImGuiStringViewText(fmt::format(FMT_STRING("Uniform size: {}\n"), + BytesToString(aurora::gfx::g_lastUniformSize))); + ImGuiStringViewText(fmt::format(FMT_STRING("Index size: {}\n"), + BytesToString(aurora::gfx::g_lastIndexSize))); + ImGuiStringViewText(fmt::format(FMT_STRING("Storage size: {}\n"), + BytesToString(aurora::gfx::g_lastStorageSize))); + ImGuiStringViewText(fmt::format( + FMT_STRING("Total: {}\n"), + BytesToString(aurora::gfx::g_lastVertSize + aurora::gfx::g_lastUniformSize + + aurora::gfx::g_lastIndexSize + aurora::gfx::g_lastStorageSize))); + } + } + ImGui::End(); +} + +class Limiter +{ + using delta_clock = std::chrono::high_resolution_clock; + using duration_t = std::chrono::nanoseconds; + + public: + void Reset() + { + m_oldTime = delta_clock::now(); + } + + void Sleep(duration_t targetFrameTime) + { + if (targetFrameTime.count() == 0) + { + return; + } + + auto start = delta_clock::now(); + duration_t adjustedSleepTime = SleepTime(targetFrameTime); + if (adjustedSleepTime.count() > 0) + { + NanoSleep(adjustedSleepTime); + duration_t overslept = TimeSince(start) - adjustedSleepTime; + if (overslept < duration_t{targetFrameTime}) + { + m_overheadTimes[m_overheadTimeIdx] = overslept; + m_overheadTimeIdx = (m_overheadTimeIdx + 1) % m_overheadTimes.size(); + } + } + Reset(); + } + + duration_t SleepTime(duration_t targetFrameTime) + { + const auto sleepTime = duration_t{targetFrameTime} - TimeSince(m_oldTime); + m_overhead = std::accumulate(m_overheadTimes.begin(), m_overheadTimes.end(), duration_t{}) / + m_overheadTimes.size(); + if (sleepTime > m_overhead) + { + return sleepTime - m_overhead; + } + return duration_t{0}; + } + + private: + delta_clock::time_point m_oldTime; + std::array m_overheadTimes{}; + size_t m_overheadTimeIdx = 0; + duration_t m_overhead = duration_t{0}; + + duration_t TimeSince(delta_clock::time_point start) + { + return std::chrono::duration_cast(delta_clock::now() - start); + } + +#if _WIN32 + bool m_initialized; + double m_countPerNs; + + void NanoSleep(const duration_t duration) + { + if (!m_initialized) + { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + m_countPerNs = static_cast(freq.QuadPart) / 1000000000.0; + m_initialized = true; + } + + DWORD ms = std::chrono::duration_cast(duration).count(); + auto tickCount = + static_cast(static_cast(duration.count()) * m_countPerNs); + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + if (ms > 10) + { + // Adjust for Sleep overhead + ::Sleep(ms - 10); + } + auto end = count.QuadPart + tickCount; + do + { + QueryPerformanceCounter(&count); + } while (count.QuadPart < end); + } +#else + void NanoSleep(const duration_t duration) + { + std::this_thread::sleep_for(duration); + } +#endif +}; + +static Limiter g_frameLimiter; +void frame_limiter() +{ + g_frameLimiter.Sleep( + std::chrono::duration_cast(std::chrono::seconds{1}) / 60); +} diff --git a/src/port/stubs.c b/src/port/stubs.c new file mode 100644 index 00000000..e69de29b