Initial aurora setup, doesn't build yet

This commit is contained in:
dbalatoni13 2025-04-02 04:17:26 +02:00
parent ba0d7ef58c
commit 2509e01125
18 changed files with 430 additions and 9 deletions

30
CMakeLists.txt Normal file
View file

@ -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)

View file

@ -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.

View file

@ -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",

18
include/port/imgui.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef _SRC_IMGUI_H_
#define _SRC_IMGUI_H_
#include <aurora/aurora.h>
#ifdef __cplusplus
extern "C"
{
#endif
void imgui_main(const AuroraInfo* info);
void frame_limiter();
#ifdef __cplusplus
}
#endif
#endif

View file

@ -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)

View file

@ -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) {

View file

@ -16,6 +16,13 @@
#include "game/gamework.h"
#include "game/sreset.h"
#ifdef TARGET_PC
#include "port/imgui.h"
#include <aurora/aurora.h>
#include <aurora/event.h>
#include <aurora/main.h>
#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 <stdio.h>
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)

267
src/port/imgui.cpp Normal file
View file

@ -0,0 +1,267 @@
extern "C"
{
#include "port/imgui.h"
}
#include <array>
#include <atomic>
#include <chrono>
#include <cmath>
#include <fmt/format.h>
#include <imgui.h>
#include <numeric>
#include <thread>
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<double>(bytes);
while (count >= 1024.0 && s < 7)
{
s++;
count /= 1024.0;
}
if (count - floor(count) == 0.0)
{
return fmt::format(FMT_STRING("{}{}"), static_cast<size_t>(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<duration_t, 4> 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<duration_t>(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<double>(freq.QuadPart) / 1000000000.0;
m_initialized = true;
}
DWORD ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
auto tickCount =
static_cast<LONGLONG>(static_cast<double>(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::nanoseconds>(std::chrono::seconds{1}) / 60);
}

0
src/port/stubs.c Normal file
View file