From f1222bbcdcd4e652e5adeb6b88874373da748640 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:57:20 -0500 Subject: [PATCH 01/22] Add partial iOS support --- CMakeLists.txt | 78 +++++++++++++++++++++--- include/mk64.h | 4 ++ libultraship | 2 +- src/engine/RaceManager.cpp | 5 +- src/engine/editor/Collision.cpp | 8 +-- src/engine/mods/ModManager.cpp | 4 +- src/port/Engine.cpp | 5 ++ src/port/GameExtractor.cpp | 28 +++++++-- src/port/audio/{HMAS.cpp => HMAS.mm} | 2 +- src/port/resource/type/TrackSections.cpp | 8 ++- src/port/resource/type/TrackSections.h | 6 ++ src/racing/skybox_and_splitscreen.c | 8 +++ 12 files changed, 134 insertions(+), 24 deletions(-) rename src/port/audio/{HMAS.cpp => HMAS.mm} (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88fbca52c7..74f6f4c787 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ endif() message("Spaghetti Kart version: ${PROJECT_VERSION} ${PROJECT_PATCH_WORD}") if(APPLE) + enable_language(OBJC) enable_language(OBJCXX) endif() @@ -263,6 +264,7 @@ file(GLOB_RECURSE ALL_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/port/*.h" "src/port/*.c" "src/port/*.cpp" + "src/port/*.mm" "src/mods/*.h" "src/mods/*.c" "src/mods/*.cpp" @@ -301,10 +303,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "iOS") set(IOS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libultraship/ios) set(STORYBOARD_FILE ${IOS_DIR}/Launch.storyboard) - set(IMAGE_FILES ${IOS_DIR}/PoweredBy.png) - set(ICON_FILES ${IOS_DIR}/Icon.png) + set(ICON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/icon.png) - list(APPEND ALL_FILES ${STORYBOARD_FILE} ${IMAGE_FILES} ${ICON_FILES}) + list(APPEND ALL_FILES ${STORYBOARD_FILE} ${ICON_FILES}) add_executable(${PROJECT_NAME} ${ALL_FILES}) set_xcode_property(${PROJECT_NAME} PRODUCT_BUNDLE_IDENTIFIER ${PROJECT_ID} All) @@ -313,7 +314,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "iOS") PROPERTIES MACOSX_BUNDLE TRUE MACOSX_BUNDLE_INFO_PLIST ${IOS_DIR}/plist.in - RESOURCE "${IMAGE_FILES};${STORYBOARD_FILE};${ICON_FILES}" + RESOURCE "${STORYBOARD_FILE};${ICON_FILES}" ) else() add_executable(${PROJECT_NAME} ${ALL_FILES}) @@ -483,6 +484,52 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") target_include_directories(${PROJECT_NAME} PRIVATE ${DEVKITPRO}/portlibs/wiiu/include/ ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + set(BUILD_TESTING OFF CACHE BOOL "" FORCE) + set(INSTALL_DOCS OFF CACHE BOOL "" FORCE) + set(INSTALL_PKG_CONFIG_MODULE OFF CACHE BOOL "" FORCE) + + FetchContent_Declare( + libogg + GIT_REPOSITORY https://github.com/xiph/ogg.git + GIT_TAG v1.3.6 + ) + FetchContent_MakeAvailable(libogg) + + set(OGG_ROOT "${libogg_BINARY_DIR}" CACHE PATH "" FORCE) + set(OGG_INCLUDE_DIR "${libogg_SOURCE_DIR}/include" CACHE PATH "" FORCE) + set(OGG_LIBRARY "${libogg_BINARY_DIR}/libogg.a" CACHE FILEPATH "" FORCE) + + if(TARGET ogg AND NOT TARGET Ogg::ogg) + add_library(Ogg::ogg ALIAS ogg) + endif() + + FetchContent_Declare( + libvorbis + GIT_REPOSITORY https://github.com/xiph/vorbis.git + GIT_TAG v1.3.7 + ) + FetchContent_MakeAvailable(libvorbis) + + if(TARGET vorbis AND NOT TARGET Vorbis::vorbis) + add_library(Vorbis::vorbis ALIAS vorbis) + endif() + + if(TARGET vorbisenc AND NOT TARGET Vorbis::vorbisenc) + add_library(Vorbis::vorbisenc ALIAS vorbisenc) + endif() + + if(TARGET vorbisfile AND NOT TARGET Vorbis::vorbisfile) + add_library(Vorbis::vorbisfile ALIAS vorbisfile) + endif() + + set(ADDITIONAL_LIBRARY_DEPENDENCIES + "Ogg::ogg" + "Vorbis::vorbis" + "Vorbis::vorbisenc" + "Vorbis::vorbisfile" + ) else() find_package(Ogg REQUIRED) find_package(Vorbis REQUIRED) @@ -658,11 +705,20 @@ endif() add_custom_command( TARGET ${PROJECT_NAME} POST_BUILD COMMENT "Copying asset yamls..." - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "$/config.yml" - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "$/yamls/" - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/meta/" "$/meta/" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "$/config.yml" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "$/yamls/" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/meta/" "$/meta/" ) +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + add_custom_command( + TARGET ${PROJECT_NAME} POST_BUILD + COMMENT "Copying packaged O2R assets..." + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/mk64.o2r" "$/mk64.o2r" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/spaghetti.o2r" "$/spaghetti.o2r" + ) +endif() + if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") include(ExternalProject) ExternalProject_Add(TorchExternal @@ -687,7 +743,7 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") COMMAND ${TORCH_EXECUTABLE} pack assets spaghetti.o2r o2r COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/mk64.o2r" "${CMAKE_BINARY_DIR}/mk64.o2r" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/spaghetti.o2r" "${CMAKE_BINARY_DIR}/spaghetti.o2r" -) + ) add_custom_target( GenerateO2R @@ -695,7 +751,11 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${TORCH_EXECUTABLE} pack assets spaghetti.o2r o2r COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/spaghetti.o2r" "${CMAKE_BINARY_DIR}/spaghetti.o2r" -) + ) + + if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + add_dependencies(${PROJECT_NAME} ExtractAssets) + endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") install(FILES "${CMAKE_BINARY_DIR}/spaghetti.o2r" DESTINATION . COMPONENT ${PROJECT_NAME}) diff --git a/include/mk64.h b/include/mk64.h index b3001755a1..59999a2ac8 100644 --- a/include/mk64.h +++ b/include/mk64.h @@ -26,7 +26,11 @@ extern "C" { #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 +#ifdef TARGET_N64 #define STACKSIZE 0x2000 +#else +#define STACKSIZE 0x10000 +#endif // Border Height Define for NTSC Versions #define BORDER_HEIGHT 1 diff --git a/libultraship b/libultraship index 430aedccfb..dbffb114aa 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 430aedccfb727d476b7dbc6662311e41889fac5e +Subproject commit dbffb114aa6fe69da000be795e1d88037b04f79e diff --git a/src/engine/RaceManager.cpp b/src/engine/RaceManager.cpp index de8ed69842..0e5f5a86d2 100644 --- a/src/engine/RaceManager.cpp +++ b/src/engine/RaceManager.cpp @@ -89,7 +89,10 @@ void RaceManager::BeginPlay() { } } } - gEditor.AddLight("Sun", nullptr, D_800DC610[1].l->l.dir); + + if (gEditor.IsEnabled()) { + gEditor.AddLight("Sun", nullptr, D_800DC610[1].l->l.dir); + } track->BeginPlay(); } diff --git a/src/engine/editor/Collision.cpp b/src/engine/editor/Collision.cpp index d872963d11..f988c921db 100644 --- a/src/engine/editor/Collision.cpp +++ b/src/engine/editor/Collision.cpp @@ -30,14 +30,14 @@ namespace TrackEditor { lo = ptr->words.w0; hi = ptr->words.w1; - opcode = (EDITOR_GFX_GET_OPCODE(lo) >> 24); + opcode = (EDITOR_GFX_GET_OPCODE(ptr->words.w0) >> 24); switch(opcode) { case G_DL: GenerateCollisionMesh(object, (Gfx*)hi, scale); break; case G_DL_OTR_HASH: ptr++; - GenerateCollisionMesh(object, (Gfx*)ResourceGetDataByCrc(((uint64_t)(ptr->words.w0 << 32)) + ptr->words.w1), scale); + GenerateCollisionMesh(object, (Gfx*)ResourceGetDataByCrc(((uint64_t)ptr->words.w0 << 32) | (uint32_t)ptr->words.w1), scale); break; case G_DL_OTR_FILEPATH: // printf("otr filepath: %s\n", (const char*)hi); @@ -48,7 +48,7 @@ namespace TrackEditor { break; case G_VTX_OTR_HASH: { ptr++; - vtx = (Vtx*)ResourceGetDataByCrc(((uint64_t)(ptr->words.w0 << 32)) + ptr->words.w1); + vtx = (Vtx*)ResourceGetDataByCrc(((uint64_t)ptr->words.w0 << 32) | (uint32_t)ptr->words.w1); break; } case G_VTX_OTR_FILEPATH: { @@ -201,7 +201,7 @@ namespace TrackEditor { // Render triangles in batches of 3 for (size_t i = 0; i + 2 < vtxBuffer.size(); i += 3) { - gSPVertex(gDisplayListHead++, (uintptr_t)&vtxBuffer[i], 3, 0); + gSPVertex(gDisplayListHead++, (uint64_t)(uintptr_t)&vtxBuffer[i], 3, 0); gSP1Triangle(gDisplayListHead++, 0, 1, 2, 0); } diff --git a/src/engine/mods/ModManager.cpp b/src/engine/mods/ModManager.cpp index 137fb97434..e31e162ef7 100644 --- a/src/engine/mods/ModManager.cpp +++ b/src/engine/mods/ModManager.cpp @@ -63,7 +63,7 @@ void GenerateAssetsMods() { } std::vector ListMods() { - const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory(game_asset_file); + const std::string main_path = Ship::Context::LocateFileAcrossAppDirs(game_asset_file); const std::string assets_path = Ship::Context::LocateFileAcrossAppDirs(engine_asset_file); std::vector archiveFiles; @@ -203,7 +203,7 @@ void AddCoreDependencies() { } void CheckMK64O2RExists() { - const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory(game_asset_file); + const std::string main_path = Ship::Context::LocateFileAcrossAppDirs(game_asset_file); if (!std::filesystem::exists(main_path)) { GenerateAssetsMods(); diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 2cd2c99b9a..6fc5dc4e55 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -269,7 +269,12 @@ bool GameEngine::GenAssetFile() { auto extractor = new GameExtractor(); if (!extractor->SelectGameFromUI()) { +#ifdef __IOS__ + ShowMessage("ROM Required", + "Copy a supported Mario Kart 64 ROM to this app's Documents folder as baserom.us.z64, then relaunch the app."); +#else ShowMessage("Error", "No ROM selected.\n\nExiting..."); +#endif exit(1); } diff --git a/src/port/GameExtractor.cpp b/src/port/GameExtractor.cpp index 4e8b4dfaee..fdbf8196f2 100644 --- a/src/port/GameExtractor.cpp +++ b/src/port/GameExtractor.cpp @@ -30,6 +30,8 @@ std::unordered_map mGameList = { { "579c48e211ae952530ffc8738709f078d5dd215e", "Mario Kart 64 (US)" }, }; +static constexpr const char* kExpectedRomName = "baserom.us.z64"; + bool GameExtractor::SelectGameFromUI() { std::vector roms; GetRoms(roms); @@ -87,14 +89,15 @@ bool GameExtractor::SelectGameFromUI() { romPath = selection[0]; } #else - // Mobile: fallback to baserom.us.z64 - if (!foundGame && !std::filesystem::exists(Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64"))) { - SPDLOG_ERROR("baserom not found"); + // Mobile: fallback to baserom.us.z64 in the writable app directory. + const auto fallbackRomPath = Ship::Context::GetPathRelativeToAppDirectory(kExpectedRomName); + if (!foundGame && !std::filesystem::exists(fallbackRomPath)) { + SPDLOG_ERROR("{} not found in app Documents", kExpectedRomName); return false; } if (!foundGame) { - romPath = Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64"); + romPath = fallbackRomPath; } #endif @@ -135,6 +138,23 @@ void GameExtractor::GetRoms(std::vector& roms) { // if (h != nullptr) { // CloseHandle(h); //} +#elif defined(__IOS__) + const auto appDirectory = Ship::Context::GetAppDirectoryPath(); + + if (!std::filesystem::exists(appDirectory)) { + return; + } + + for (const auto& file : std::filesystem::directory_iterator(appDirectory)) { + if (!file.is_regular_file()) { + continue; + } + + const auto extension = file.path().extension().string(); + if (extension == ".z64" || extension == ".n64" || extension == ".v64") { + roms.push_back(file.path().string()); + } + } #elif unix // Open the directory of the app. DIR* d = opendir("."); diff --git a/src/port/audio/HMAS.cpp b/src/port/audio/HMAS.mm similarity index 99% rename from src/port/audio/HMAS.cpp rename to src/port/audio/HMAS.mm index 9d656a206f..6c7ea129f8 100644 --- a/src/port/audio/HMAS.cpp +++ b/src/port/audio/HMAS.mm @@ -265,4 +265,4 @@ extern "C" void HMAS_AddEffect(HMAS_ChannelId channelId, HMAS_EffectType type, H extern "C" bool HMAS_IsIDRegistered(HMAS_AudioId id) { return GameEngine::Instance->gHMAS->IsIDRegistered(id); -} \ No newline at end of file +} diff --git a/src/port/resource/type/TrackSections.cpp b/src/port/resource/type/TrackSections.cpp index 5fd89f5c87..4c59780cac 100644 --- a/src/port/resource/type/TrackSections.cpp +++ b/src/port/resource/type/TrackSections.cpp @@ -7,7 +7,9 @@ TrackSectionsClass::TrackSectionsClass() : Resource(std::shared_ptr { size_t GetPointerSize() override; std::vector TrackSectionsList; + + private: + std::vector PointerData; }; class TrackSectionsO2RClass : public Ship::Resource { @@ -28,6 +31,9 @@ class TrackSectionsO2RClass : public Ship::Resource { size_t GetPointerSize() override; std::vector TrackSectionsList; + + private: + std::vector PointerData; }; } // namespace MK64 diff --git a/src/racing/skybox_and_splitscreen.c b/src/racing/skybox_and_splitscreen.c index 825150f831..a8f8c442cd 100644 --- a/src/racing/skybox_and_splitscreen.c +++ b/src/racing/skybox_and_splitscreen.c @@ -687,11 +687,19 @@ void render_screens(ScreenContext* screen, s32 mode, s32 someId, s32 playerId) { } if (CVarGetInteger("gDrawCPPActors", true) == true) { +#ifdef __IOS__ + // These editor-backed objects still crash during race rendering on iOS. +#else CM_DrawActors(camera); +#endif } if (CVarGetInteger("gDrawStaticMeshActors", true) == true) { +#ifdef __IOS__ + // Static mesh actor rendering shares the same crash path on iOS for now. +#else CM_DrawStaticMeshActors(); +#endif } if (CVarGetInteger("gDrawObjects", true) == true) { From 490e1f55df8a4e5574381342c888a4c5eb5b7da1 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:44:57 -0500 Subject: [PATCH 02/22] Prefer MK64 Reloaded on iOS --- src/engine/editor/Collision.cpp | 8 +-- src/engine/mods/ModManager.cpp | 91 ++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/engine/editor/Collision.cpp b/src/engine/editor/Collision.cpp index f988c921db..d872963d11 100644 --- a/src/engine/editor/Collision.cpp +++ b/src/engine/editor/Collision.cpp @@ -30,14 +30,14 @@ namespace TrackEditor { lo = ptr->words.w0; hi = ptr->words.w1; - opcode = (EDITOR_GFX_GET_OPCODE(ptr->words.w0) >> 24); + opcode = (EDITOR_GFX_GET_OPCODE(lo) >> 24); switch(opcode) { case G_DL: GenerateCollisionMesh(object, (Gfx*)hi, scale); break; case G_DL_OTR_HASH: ptr++; - GenerateCollisionMesh(object, (Gfx*)ResourceGetDataByCrc(((uint64_t)ptr->words.w0 << 32) | (uint32_t)ptr->words.w1), scale); + GenerateCollisionMesh(object, (Gfx*)ResourceGetDataByCrc(((uint64_t)(ptr->words.w0 << 32)) + ptr->words.w1), scale); break; case G_DL_OTR_FILEPATH: // printf("otr filepath: %s\n", (const char*)hi); @@ -48,7 +48,7 @@ namespace TrackEditor { break; case G_VTX_OTR_HASH: { ptr++; - vtx = (Vtx*)ResourceGetDataByCrc(((uint64_t)ptr->words.w0 << 32) | (uint32_t)ptr->words.w1); + vtx = (Vtx*)ResourceGetDataByCrc(((uint64_t)(ptr->words.w0 << 32)) + ptr->words.w1); break; } case G_VTX_OTR_FILEPATH: { @@ -201,7 +201,7 @@ namespace TrackEditor { // Render triangles in batches of 3 for (size_t i = 0; i + 2 < vtxBuffer.size(); i += 3) { - gSPVertex(gDisplayListHead++, (uint64_t)(uintptr_t)&vtxBuffer[i], 3, 0); + gSPVertex(gDisplayListHead++, (uintptr_t)&vtxBuffer[i], 3, 0); gSP1Triangle(gDisplayListHead++, 0, 1, 2, 0); } diff --git a/src/engine/mods/ModManager.cpp b/src/engine/mods/ModManager.cpp index e31e162ef7..b226d6cbd6 100644 --- a/src/engine/mods/ModManager.cpp +++ b/src/engine/mods/ModManager.cpp @@ -5,8 +5,12 @@ #include "port/Engine.h" #include "semver.hpp" #include "utils/StringHelper.h" +#include +#include +#include #include #include +#include #include #include "ModManager.h" @@ -18,6 +22,74 @@ void DetectCyclicDependencies(); void DetectOutdatedDependencies(); void SortModsByDependencies(); +namespace { +constexpr const char* kMk64ReloadedUrl = "https://evilgames.eu/texture-packs/mk64-reloaded.htm#rt64"; + +bool IsArchivePath(const std::filesystem::path& path) { + const auto ext = path.extension().string(); + return StringHelper::IEquals(ext, ".zip") || StringHelper::IEquals(ext, ".o2r") || + std::filesystem::is_directory(path); +} + +bool IsMk64ReloadedPath(const std::filesystem::path& path) { + if (!path.has_filename()) { + return false; + } + + auto fileName = path.filename().string(); + std::transform(fileName.begin(), fileName.end(), fileName.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return fileName.find("mk64-reloaded") != std::string::npos; +} + +void AddUniqueArchive(std::vector& archiveFiles, std::set& seenArchivePaths, + const std::filesystem::path& path) { + if (!IsArchivePath(path)) { + return; + } + + const auto normalizedPath = path.lexically_normal().generic_string(); + if (seenArchivePaths.insert(normalizedPath).second) { + archiveFiles.push_back(normalizedPath); + } +} + +#ifdef __IOS__ +void AddIosDocumentsArchives(std::vector& archiveFiles, std::set& seenArchivePaths, + bool hasMk64Reloaded) { + const auto appDirectory = std::filesystem::path(Ship::Context::GetAppDirectoryPath()); + + if (!std::filesystem::exists(appDirectory) || !std::filesystem::is_directory(appDirectory)) { + return; + } + + for (const auto& entry : std::filesystem::directory_iterator(appDirectory)) { + if (!entry.is_regular_file()) { + continue; + } + + const auto entryPath = entry.path(); + if (!IsMk64ReloadedPath(entryPath)) { + continue; + } + + AddUniqueArchive(archiveFiles, seenArchivePaths, entryPath); + hasMk64Reloaded = true; + } + + if (!hasMk64Reloaded && !CVarGetInteger("gIosMk64ReloadedPromptShown", 0)) { + CVarSetInteger("gIosMk64ReloadedPromptShown", 1); + if (GameEngine::ShowYesNoBox( + "Recommended Mod", + "Installing the official MK64 Reloaded SpaghettiKart O2R currently avoids several iOS track crashes.\n\n" + "Open the download page now?") == IDYES) { + SDL_OpenURL(kMk64ReloadedUrl); + } + } +} +#endif +} // namespace + std::vector>> Mods = {}; void InitModsSystem() { @@ -67,15 +139,16 @@ std::vector ListMods() { const std::string assets_path = Ship::Context::LocateFileAcrossAppDirs(engine_asset_file); std::vector archiveFiles; + std::set seenArchivePaths; if (std::filesystem::exists(main_path)) { - archiveFiles.push_back(main_path); + AddUniqueArchive(archiveFiles, seenArchivePaths, main_path); } else { // should not happen, but just in case GenerateAssetsMods(); - archiveFiles.push_back(main_path); + AddUniqueArchive(archiveFiles, seenArchivePaths, main_path); } if (std::filesystem::exists(assets_path)) { - archiveFiles.push_back(assets_path); + AddUniqueArchive(archiveFiles, seenArchivePaths, assets_path); } const std::string mods_path = Ship::Context::GetPathRelativeToAppDirectory("mods"); @@ -88,13 +161,17 @@ std::vector ListMods() { if (std::filesystem::exists(mods_path) && std::filesystem::is_directory(mods_path)) { for (const auto& p : std::filesystem::directory_iterator(mods_path)) { - auto ext = p.path().extension().string(); - if (StringHelper::IEquals(ext, ".zip") || StringHelper::IEquals(ext, ".o2r") || std::filesystem::is_directory(p.path())) { - archiveFiles.push_back(p.path().generic_string()); - } + AddUniqueArchive(archiveFiles, seenArchivePaths, p.path()); } } +#ifdef __IOS__ + const bool hasMk64Reloaded = std::any_of( + archiveFiles.begin(), archiveFiles.end(), + [](const std::string& archivePath) { return IsMk64ReloadedPath(std::filesystem::path(archivePath)); }); + AddIosDocumentsArchives(archiveFiles, seenArchivePaths, hasMk64Reloaded); +#endif + return archiveFiles; } From 2f9c0a2a60f387e87386413aa0a2c95ec70d5d55 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:40:16 -0500 Subject: [PATCH 03/22] Remove iOS mod prompt experiment --- src/engine/mods/ModManager.cpp | 57 ---------------------------------- src/engine/mods/ModManager.h | 2 +- 2 files changed, 1 insertion(+), 58 deletions(-) diff --git a/src/engine/mods/ModManager.cpp b/src/engine/mods/ModManager.cpp index b226d6cbd6..27f8c36493 100644 --- a/src/engine/mods/ModManager.cpp +++ b/src/engine/mods/ModManager.cpp @@ -5,8 +5,6 @@ #include "port/Engine.h" #include "semver.hpp" #include "utils/StringHelper.h" -#include -#include #include #include #include @@ -23,25 +21,12 @@ void DetectOutdatedDependencies(); void SortModsByDependencies(); namespace { -constexpr const char* kMk64ReloadedUrl = "https://evilgames.eu/texture-packs/mk64-reloaded.htm#rt64"; - bool IsArchivePath(const std::filesystem::path& path) { const auto ext = path.extension().string(); return StringHelper::IEquals(ext, ".zip") || StringHelper::IEquals(ext, ".o2r") || std::filesystem::is_directory(path); } -bool IsMk64ReloadedPath(const std::filesystem::path& path) { - if (!path.has_filename()) { - return false; - } - - auto fileName = path.filename().string(); - std::transform(fileName.begin(), fileName.end(), fileName.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); - return fileName.find("mk64-reloaded") != std::string::npos; -} - void AddUniqueArchive(std::vector& archiveFiles, std::set& seenArchivePaths, const std::filesystem::path& path) { if (!IsArchivePath(path)) { @@ -54,40 +39,6 @@ void AddUniqueArchive(std::vector& archiveFiles, std::set& archiveFiles, std::set& seenArchivePaths, - bool hasMk64Reloaded) { - const auto appDirectory = std::filesystem::path(Ship::Context::GetAppDirectoryPath()); - - if (!std::filesystem::exists(appDirectory) || !std::filesystem::is_directory(appDirectory)) { - return; - } - - for (const auto& entry : std::filesystem::directory_iterator(appDirectory)) { - if (!entry.is_regular_file()) { - continue; - } - - const auto entryPath = entry.path(); - if (!IsMk64ReloadedPath(entryPath)) { - continue; - } - - AddUniqueArchive(archiveFiles, seenArchivePaths, entryPath); - hasMk64Reloaded = true; - } - - if (!hasMk64Reloaded && !CVarGetInteger("gIosMk64ReloadedPromptShown", 0)) { - CVarSetInteger("gIosMk64ReloadedPromptShown", 1); - if (GameEngine::ShowYesNoBox( - "Recommended Mod", - "Installing the official MK64 Reloaded SpaghettiKart O2R currently avoids several iOS track crashes.\n\n" - "Open the download page now?") == IDYES) { - SDL_OpenURL(kMk64ReloadedUrl); - } - } -} -#endif } // namespace std::vector>> Mods = {}; @@ -164,14 +115,6 @@ std::vector ListMods() { AddUniqueArchive(archiveFiles, seenArchivePaths, p.path()); } } - -#ifdef __IOS__ - const bool hasMk64Reloaded = std::any_of( - archiveFiles.begin(), archiveFiles.end(), - [](const std::string& archivePath) { return IsMk64ReloadedPath(std::filesystem::path(archivePath)); }); - AddIosDocumentsArchives(archiveFiles, seenArchivePaths, hasMk64Reloaded); -#endif - return archiveFiles; } diff --git a/src/engine/mods/ModManager.h b/src/engine/mods/ModManager.h index af0baa2e99..5a87e83630 100644 --- a/src/engine/mods/ModManager.h +++ b/src/engine/mods/ModManager.h @@ -1,4 +1,4 @@ #pragma once void InitModsSystem(); -void UnloadMods(); \ No newline at end of file +void UnloadMods(); From f32fe9ab73ede2db691944140d5fe344a557def9 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:48:08 -0500 Subject: [PATCH 04/22] Update libultraship submodule --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index dbffb114aa..b593948ed1 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit dbffb114aa6fe69da000be795e1d88037b04f79e +Subproject commit b593948ed16fecf4372fad23e7e28006936ecd14 From 72504a236bce49b9c98e8bf36d878d72e033b887 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:23:45 -0500 Subject: [PATCH 05/22] Fix clean iOS build dependencies --- CMakeLists.txt | 4 ++++ libultraship | 2 +- src/port/pak.cpp | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74f6f4c787..0311dd40e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) +if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0") + set(CMAKE_POLICY_VERSION_MINIMUM 3.5) +endif() + # Set the project version and language project(Spaghettify VERSION 1.0.0 LANGUAGES C CXX ASM) include(FetchContent) diff --git a/libultraship b/libultraship index b593948ed1..6a88c49d68 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit b593948ed16fecf4372fad23e7e28006936ecd14 +Subproject commit 6a88c49d682b36ab92745a9b03d52fa21e38146b diff --git a/src/port/pak.cpp b/src/port/pak.cpp index 4adedd6a01..eaea257fc0 100644 --- a/src/port/pak.cpp +++ b/src/port/pak.cpp @@ -2,10 +2,6 @@ #include #if __has_include() #include -#define fmt(...) fmt::format(__VA_ARGS__) -#else -#include -#define fmt(...) std::format(__VA_ARGS__) #endif #include @@ -23,7 +19,11 @@ typedef struct ControllerPak { } ControllerPak; std::string Pfs_PakFile_GetPath(u8 file_no) { - return Ship::Context::GetPathRelativeToAppDirectory(fmt("controllerPak_file_{}.sav", file_no)); +#if __has_include() + return Ship::Context::GetPathRelativeToAppDirectory(fmt::format("controllerPak_file_{}.sav", file_no)); +#else + return Ship::Context::GetPathRelativeToAppDirectory("controllerPak_file_" + std::to_string(file_no) + ".sav"); +#endif } std::string Pfs_PakHeader_GetPath() { From ec3f15f177afd5960c9f3df94f8980d611eac2a6 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:59:45 -0500 Subject: [PATCH 06/22] Document iOS build steps --- docs/BUILDING.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index da288947c8..d14d27c131 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -232,6 +232,58 @@ cpack cmake --build build-cmake --target clean ``` +## iOS +Requires: + * macOS with Xcode installed + * CMake and Ninja in `PATH` + * Homebrew packages: `cmake ninja sdl2 libpng glew nlohmann-json libzip vorbis-tools sdl2_net tinyxml2 pkg-config git` + * Your own supported Mario Kart 64 ROM + +Install the Homebrew dependencies: + +```bash +brew install cmake ninja sdl2 libpng glew nlohmann-json libzip vorbis-tools sdl2_net tinyxml2 pkg-config git +``` + +Build steps: + +```bash +# Clone the repo +git clone --recurse-submodules https://github.com/HarbourMasters/SpaghettiKart.git +cd SpaghettiKart + +# If needed later +git submodule update --init --recursive + +# Put your supported ROM at the repo root with this filename +ln -sf "/path/to/your/baserom.us.z64" baserom.us.z64 + +# Generate mk64.o2r and spaghetti.o2r from your own ROM +cmake -S . -B build-cmake -GNinja +cmake --build build-cmake --target ExtractAssets + +# Configure the iOS build +cmake -S . -B build-ios-make -DCMAKE_BUILD_TYPE=Release -DIOS=ON -DSIGN_LIBRARY=OFF + +# Build the app +cmake --build build-ios-make --config Release + +# Package an unsigned IPA +mkdir -p build-ios-make/Payload +rm -rf build-ios-make/Payload/Spaghettify.app +cp -R build-ios-make/Spaghettify.app build-ios-make/Payload/Spaghettify.app +rm -f build-ios-make/SpaghettiKart-unsigned.ipa +zip -qry build-ios-make/SpaghettiKart-unsigned.ipa build-ios-make/Payload + +# Output +ls build-ios-make/SpaghettiKart-unsigned.ipa +``` + +Notes: + * The IPA produced by these steps is unsigned. + * The build generates `mk64.o2r` and `spaghetti.o2r` from your own ROM and copies them into the iOS app bundle. + * If you are using the current iOS workaround branch, install MK64 Reloaded manually by copying its `.o2r` file into `Spaghettify/mods` on the device. + ## Getting CI to work on your fork The CI works via [Github Actions](https://github.com/features/actions) where we mostly make use of machines hosted by Github; except for the very first step of the CI process called "Extract assets". This steps extracts assets from the game file and generates an "assets" folder in `mm/`. From 92204b3eb97b0fcc86d9af0bc661cdb750438904 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:04:42 -0500 Subject: [PATCH 07/22] Update libultraship submodule --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index 6a88c49d68..37923731d9 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 6a88c49d682b36ab92745a9b03d52fa21e38146b +Subproject commit 37923731d9bd71bff5d652a96bcc7b356f55bd65 From d39bfed9c5a4997297c87e293f98c4e9af10dd67 Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:47:01 -0500 Subject: [PATCH 08/22] Fix iOS IPA packaging instructions --- docs/BUILDING.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index d14d27c131..aa0c1f6375 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -269,19 +269,20 @@ cmake -S . -B build-ios-make -DCMAKE_BUILD_TYPE=Release -DIOS=ON -DSIGN_LIBRARY= cmake --build build-ios-make --config Release # Package an unsigned IPA -mkdir -p build-ios-make/Payload -rm -rf build-ios-make/Payload/Spaghettify.app -cp -R build-ios-make/Spaghettify.app build-ios-make/Payload/Spaghettify.app -rm -f build-ios-make/SpaghettiKart-unsigned.ipa -zip -qry build-ios-make/SpaghettiKart-unsigned.ipa build-ios-make/Payload +# The IPA must contain Payload/ at the archive root. +rm -rf ipa-package +mkdir -p ipa-package/Payload +cp -R build-ios-make/Spaghettify.app ipa-package/Payload/Spaghettify.app +(cd ipa-package && zip -qry ../SpaghettiKart-unsigned.ipa Payload) # Output -ls build-ios-make/SpaghettiKart-unsigned.ipa +ls SpaghettiKart-unsigned.ipa ``` Notes: * The IPA produced by these steps is unsigned. * The build generates `mk64.o2r` and `spaghetti.o2r` from your own ROM and copies them into the iOS app bundle. + * Do not zip `build-ios-make/Payload` directly, or the archive will have the wrong root folder for signing tools. * If you are using the current iOS workaround branch, install MK64 Reloaded manually by copying its `.o2r` file into `Spaghettify/mods` on the device. ## Getting CI to work on your fork From b818a74f4dbadc8d77b52bd3611de74e1422128d Mon Sep 17 00:00:00 2001 From: ykr2byfrqb-wq <268458829+ykr2byfrqb-wq@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:53:48 -0500 Subject: [PATCH 09/22] Update iOS build documentation --- docs/BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index aa0c1f6375..62402df9a9 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -283,7 +283,7 @@ Notes: * The IPA produced by these steps is unsigned. * The build generates `mk64.o2r` and `spaghetti.o2r` from your own ROM and copies them into the iOS app bundle. * Do not zip `build-ios-make/Payload` directly, or the archive will have the wrong root folder for signing tools. - * If you are using the current iOS workaround branch, install MK64 Reloaded manually by copying its `.o2r` file into `Spaghettify/mods` on the device. + * No additional mod pack is required for the current iOS build flow. ## Getting CI to work on your fork From d08af7833b8edec586007044e2e875dab5b52ee6 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:29:48 +0200 Subject: [PATCH 10/22] fix error on linux --- CMakeLists.txt | 1 + libultraship | 2 +- src/port/audio/{HMAS.mm => HMAS.cpp} | 45 ++++++++++++++++------------ 3 files changed, 28 insertions(+), 20 deletions(-) rename src/port/audio/{HMAS.mm => HMAS.cpp} (84%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0311dd40e3..c98703c0e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,6 +192,7 @@ set(SKIP_XCODE_VERSION_CHECK ON) set(GFX_DEBUG_DISASSEMBLER OFF) # Add compile definitions for the target +add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=int-conversion>" "$<$:-Wno-error=changes-meaning>" "$<$:-Wno-error=narrowing>") add_compile_definitions( VERSION_US=1 ENABLE_RUMBLE=1 diff --git a/libultraship b/libultraship index 37923731d9..4eadaf990d 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 37923731d9bd71bff5d652a96bcc7b356f55bd65 +Subproject commit 4eadaf990d234148a250234eb44f3b01f6a3af90 diff --git a/src/port/audio/HMAS.mm b/src/port/audio/HMAS.cpp similarity index 84% rename from src/port/audio/HMAS.mm rename to src/port/audio/HMAS.cpp index 6c7ea129f8..6182376e3d 100644 --- a/src/port/audio/HMAS.mm +++ b/src/port/audio/HMAS.cpp @@ -10,8 +10,8 @@ ma_result result; ma_engine_config engine = ma_engine_config_init(); - engine.channels = 2; - engine.noDevice = MA_TRUE; + engine.channels = 2; + engine.noDevice = MA_TRUE; engine.sampleRate = GameEngine_GetSampleRate(); result = ma_engine_init(&engine, &gAudioEngine); @@ -33,7 +33,7 @@ return; } - if(info.loop.start != -1 && info.loop.end != -1) { + if (info.loop.start != -1 && info.loop.end != -1) { ma_data_source* source = ma_sound_get_data_source(&gRegistry[id].sound); ma_data_source_set_loop_point_in_pcm_frames(source, info.loop.start, info.loop.end); } @@ -48,14 +48,15 @@ return; } - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, ma_engine_get_channels(&gAudioEngine), ma_engine_get_sample_rate(&gAudioEngine)); + ma_decoder_config config = ma_decoder_config_init(ma_format_f32, ma_engine_get_channels(&gAudioEngine), + ma_engine_get_sample_rate(&gAudioEngine)); ma_result result = ma_decoder_init_memory(data, size, &config, &gRegistry[id].decoder); if (result != MA_SUCCESS) { SPDLOG_ERROR("Failed to initialize decoder from memory: {}", ma_result_description(result)); return; } - if(info.loop.start != -1 && info.loop.end != -1) { + if (info.loop.start != -1 && info.loop.end != -1) { ma_data_source_set_loop_point_in_pcm_frames(&gRegistry[id].decoder, info.loop.start, info.loop.end); } @@ -72,7 +73,7 @@ } void HMAS::Play(HMAS_ChannelId channelId, HMAS_AudioId id, bool loop) { - if(channelId == HMAS_ChannelId::HMAS_MUSIC){ + if (channelId == HMAS_ChannelId::HMAS_MUSIC) { this->Stop(channelId); } @@ -150,8 +151,8 @@ return; } - if(pause) { - ma_sound_get_cursor_in_pcm_frames(channel->sound, &channel->cursor); + if (pause) { + ma_sound_get_cursor_in_pcm_frames(channel->sound, (ma_uint64*) &channel->cursor); ma_sound_stop(channel->sound); } else { ma_result result = ma_sound_start(channel->sound); @@ -163,9 +164,10 @@ } } -void HMAS::AddEffect(HMAS_ChannelId channelId, HMAS_EffectType type, HMAS_EffectTransition transition, uint32_t frames, float target) { +void HMAS::AddEffect(HMAS_ChannelId channelId, HMAS_EffectType type, HMAS_EffectTransition transition, uint32_t frames, + float target) { auto& channel = gChannelSound[channelId]; - channel.effects.push_back({type, transition, frames, target}); + channel.effects.push_back({ type, transition, frames, target }); } bool HMAS::IsIDRegistered(HMAS_AudioId id) { @@ -173,7 +175,7 @@ } void HMAS::ProcessEffects() { - for (size_t i = 0; i < sizeof(gChannelSound) / sizeof(gChannelSound[0]); i++){ + for (size_t i = 0; i < sizeof(gChannelSound) / sizeof(gChannelSound[0]); i++) { auto& channel = gChannelSound[i]; if (channel.sound == nullptr) { @@ -189,14 +191,16 @@ effect.numFrames--; switch (effect.type) { case HMAS_EffectType::HMAS_EFFECT_VOLUME: { - float volume = effect.transition == HMAS_EffectTransition::HMAS_LINEAR ? - Lerp(channel.volume, effect.target, 1.0f / effect.numFrames) : effect.target; + float volume = effect.transition == HMAS_EffectTransition::HMAS_LINEAR + ? Lerp(channel.volume, effect.target, 1.0f / effect.numFrames) + : effect.target; this->SetVolume((HMAS_ChannelId) i, std::max(0.0f, volume)); break; } case HMAS_EffectType::HMAS_EFFECT_PITCH: { - float pitch = effect.transition == HMAS_EffectTransition::HMAS_LINEAR ? - Lerp(channel.pitch, effect.target, 1.0f / effect.numFrames) : effect.target; + float pitch = effect.transition == HMAS_EffectTransition::HMAS_LINEAR + ? Lerp(channel.pitch, effect.target, 1.0f / effect.numFrames) + : effect.target; this->SetPitch((HMAS_ChannelId) i, std::max(0.1f, pitch)); break; } @@ -213,15 +217,17 @@ break; } } else { - SPDLOG_DEBUG("Removing effect: Type={}, Frames={}, Target={}", static_cast(effect.type), effect.numFrames, effect.target); + SPDLOG_DEBUG("Removing effect: Type={}, Frames={}, Target={}", static_cast(effect.type), + effect.numFrames, effect.target); channel.effects.erase(channel.effects.begin()); } } } -void HMAS::CreateBuffer(uint8_t *samples, uint32_t bufferSizeInBytes) { +void HMAS::CreateBuffer(uint8_t* samples, uint32_t bufferSizeInBytes) { this->ProcessEffects(); - ma_uint32 bufferSizeInFrames = bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&gAudioEngine)); + ma_uint32 bufferSizeInFrames = + bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&gAudioEngine)); ma_engine_read_pcm_frames(&gAudioEngine, samples, bufferSizeInFrames, NULL); } @@ -259,7 +265,8 @@ GameEngine::Instance->gHMAS->SetPause(channelId, pause); } -extern "C" void HMAS_AddEffect(HMAS_ChannelId channelId, HMAS_EffectType type, HMAS_EffectTransition transition, uint32_t frames, float target) { +extern "C" void HMAS_AddEffect(HMAS_ChannelId channelId, HMAS_EffectType type, HMAS_EffectTransition transition, + uint32_t frames, float target) { GameEngine::Instance->gHMAS->AddEffect(channelId, type, transition, frames, target); } From 2d4951bdb909f3212306ba645013742b14c1272d Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:10:27 +0200 Subject: [PATCH 11/22] move ios file in spaghettikart and use default lus --- CMakeLists.txt | 2 +- ios/Launch.storyboard | 36 +++++++++++++++++++++++++++++++++ ios/plist.in | 47 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 ios/Launch.storyboard create mode 100644 ios/plist.in diff --git a/CMakeLists.txt b/CMakeLists.txt index c98703c0e4..9512275089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,7 +305,7 @@ list(FILTER ALL_FILES EXCLUDE REGEX ".*.inc.c") list(FILTER ALL_FILES EXCLUDE REGEX "./src/debug/crash_screen_enhancement.c") if (CMAKE_SYSTEM_NAME STREQUAL "iOS") - set(IOS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libultraship/ios) + set(IOS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ios) set(STORYBOARD_FILE ${IOS_DIR}/Launch.storyboard) set(ICON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/icon.png) diff --git a/ios/Launch.storyboard b/ios/Launch.storyboard new file mode 100644 index 0000000000..31d8dc42b8 --- /dev/null +++ b/ios/Launch.storyboard @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/plist.in b/ios/plist.in new file mode 100644 index 0000000000..3d9990b299 --- /dev/null +++ b/ios/plist.in @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + @PROJECT_NAME@ + CFBundleExecutable + @PROJECT_NAME@ + CFBundleIdentifier + @PROJECT_ID@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @PROJECT_NAME@ + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchStoryboardName + Launch + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + + + From 7682006ec247765e4af9c87871cf6216cb942270 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:10:55 +0200 Subject: [PATCH 12/22] Update libultraship --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index 4eadaf990d..7461ee015e 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 4eadaf990d234148a250234eb44f3b01f6a3af90 +Subproject commit 7461ee015e630c6c7b3b0e9b14ae2a20dc336503 From fd27f2461d5b4c1f1ab920815192349c03afb94b Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:13:26 +0200 Subject: [PATCH 13/22] Update CMakeLists.txt --- CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9512275089..7b90de2bd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,14 @@ set(SKIP_XCODE_VERSION_CHECK ON) set(GFX_DEBUG_DISASSEMBLER OFF) # Add compile definitions for the target -add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=int-conversion>" "$<$:-Wno-error=changes-meaning>" "$<$:-Wno-error=narrowing>") +include(CheckCCompilerFlag) +check_c_compiler_flag("-Wno-error=int-conversion" HAS_WNO_ERROR_INT_CONVERSION) + +if(HAS_WNO_ERROR_INT_CONVERSION) + add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=int-conversion>" "$<$:-Wno-error=changes-meaning>" "$<$:-Wno-error=narrowing>") +else() + add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=narrowing>") +endif() add_compile_definitions( VERSION_US=1 ENABLE_RUMBLE=1 From 5a6687932a163b8c033a58413f1535fb7fa0a953 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:22:25 +0200 Subject: [PATCH 14/22] Update CMakeLists.txt --- CMakeLists.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b90de2bd9..ea4af977ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ endif() message("Spaghetti Kart version: ${PROJECT_VERSION} ${PROJECT_PATCH_WORD}") if(APPLE) + set_source_files_properties(src/port/audio/HMAS.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") enable_language(OBJC) enable_language(OBJCXX) endif() @@ -193,12 +194,18 @@ set(GFX_DEBUG_DISASSEMBLER OFF) # Add compile definitions for the target include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) check_c_compiler_flag("-Wno-error=int-conversion" HAS_WNO_ERROR_INT_CONVERSION) +check_cxx_compiler_flag("-Wno-error=changes-meaning" HAS_WNO_ERROR_CHANGES_MEANING) + +add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=narrowing>") if(HAS_WNO_ERROR_INT_CONVERSION) - add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=int-conversion>" "$<$:-Wno-error=changes-meaning>" "$<$:-Wno-error=narrowing>") -else() - add_compile_options("$<$:-Wno-error=incompatible-pointer-types>" "$<$:-Wno-error=narrowing>") + add_compile_options("$<$:-Wno-error=int-conversion>") +endif() + +if(HAS_WNO_ERROR_CHANGES_MEANING) + add_compile_options("$<$:-Wno-error=changes-meaning>") endif() add_compile_definitions( VERSION_US=1 @@ -720,6 +727,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "$/config.yml" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "$/yamls/" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/meta/" "$/meta/" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/spaghetti.o2r" "$/spaghetti.o2r" ) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") From 3d8241660c451f3c210d33186dbfb416e0c260fe Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:35:26 +0200 Subject: [PATCH 15/22] fix cli error --- CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea4af977ae..61e2e5402b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -721,13 +721,15 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") endif() endif() +file(WRITE ${CMAKE_BINARY_DIR}/copy_if_exists.cmake "if(EXISTS \"\${SRC}\")\n execute_process(COMMAND \${CMAKE_COMMAND} -E copy_if_different \"\${SRC}\" \"\${DST}\")\nendif()\n") + add_custom_command( TARGET ${PROJECT_NAME} POST_BUILD COMMENT "Copying asset yamls..." COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "$/config.yml" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "$/yamls/" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/meta/" "$/meta/" - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/spaghetti.o2r" "$/spaghetti.o2r" + COMMAND ${CMAKE_COMMAND} -DSRC="${CMAKE_SOURCE_DIR}/spaghetti.o2r" -DDST="$/spaghetti.o2r" -P "${CMAKE_BINARY_DIR}/copy_if_exists.cmake" ) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") @@ -735,7 +737,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") TARGET ${PROJECT_NAME} POST_BUILD COMMENT "Copying packaged O2R assets..." COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/mk64.o2r" "$/mk64.o2r" - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/spaghetti.o2r" "$/spaghetti.o2r" + COMMAND ${CMAKE_COMMAND} -DSRC="${CMAKE_BINARY_DIR}/spaghetti.o2r" -DDST="$/spaghetti.o2r" -P "${CMAKE_BINARY_DIR}/copy_if_exists.cmake" ) endif() @@ -778,7 +780,7 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - install(FILES "${CMAKE_BINARY_DIR}/spaghetti.o2r" DESTINATION . COMPONENT ${PROJECT_NAME}) + install(FILES "${CMAKE_BINARY_DIR}/spaghetti.o2r" DESTINATION . COMPONENT ${PROJECT_NAME} OPTIONAL) endif() if(CMAKE_SYSTEM_NAME MATCHES "Darwin") From 6e772162970fdc140baa6abc21ffb1fc26c054b2 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Mon, 20 Apr 2026 16:54:37 +0200 Subject: [PATCH 16/22] update build instruction and lus --- docs/BUILDING.md | 34 ++++++++++++++++++++++++++++++++++ libultraship | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 62402df9a9..c057e83dfc 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -285,6 +285,40 @@ Notes: * Do not zip `build-ios-make/Payload` directly, or the archive will have the wrong root folder for signing tools. * No additional mod pack is required for the current iOS build flow. +## iOS Simulator (macOS) +To run the project on the iOS Simulator on a macOS host (Apple Silicon `arm64`), use the following steps. This assumes you have already extracted the assets as shown in the iOS or macOS sections. + +### Build steps + +```bash +# Clean previous builds to avoid conflicts +rm -rf build-ios-make + +# Configure the project for the simulator +# This ensures we target the simulator's arm64 architecture instead of physical devices +cmake -B build-ios-make -G "Unix Makefiles" \ + -DCMAKE_TOOLCHAIN_FILE=cmake/ios.toolchain.cmake \ + -DPLATFORM=SIMULATORARM64 \ + -DCMAKE_OSX_SYSROOT=iphonesimulator \ + -DCMAKE_OSX_ARCHITECTURES=arm64 + +# Compile the project +cmake --build build-ios-make -j8 +``` + +### Running on the Simulator + +```bash +# Boot your preferred simulator (e.g., iPhone 16 Pro) +xcrun simctl boot "iPhone 16 Pro" + +# Install the app onto the booted simulator +xcrun simctl install booted build-ios-make/Spaghettify.app + +# Launch the app +xcrun simctl launch booted dev.net64.game +``` + ## Getting CI to work on your fork The CI works via [Github Actions](https://github.com/features/actions) where we mostly make use of machines hosted by Github; except for the very first step of the CI process called "Extract assets". This steps extracts assets from the game file and generates an "assets" folder in `mm/`. diff --git a/libultraship b/libultraship index 7461ee015e..09426197f5 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 7461ee015e630c6c7b3b0e9b14ae2a20dc336503 +Subproject commit 09426197f53810b3547a7696f0d50349bdbb35a2 From b3ebd51dd98274f513d3f6a963a20f33688242db Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:20:04 +0200 Subject: [PATCH 17/22] revert some weird change --- include/mk64.h | 4 ---- src/port/resource/type/TrackSections.cpp | 8 ++------ src/port/resource/type/TrackSections.h | 6 ------ 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/include/mk64.h b/include/mk64.h index 59999a2ac8..b3001755a1 100644 --- a/include/mk64.h +++ b/include/mk64.h @@ -26,11 +26,7 @@ extern "C" { #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 -#ifdef TARGET_N64 #define STACKSIZE 0x2000 -#else -#define STACKSIZE 0x10000 -#endif // Border Height Define for NTSC Versions #define BORDER_HEIGHT 1 diff --git a/src/port/resource/type/TrackSections.cpp b/src/port/resource/type/TrackSections.cpp index 4c59780cac..5fd89f5c87 100644 --- a/src/port/resource/type/TrackSections.cpp +++ b/src/port/resource/type/TrackSections.cpp @@ -7,9 +7,7 @@ TrackSectionsClass::TrackSectionsClass() : Resource(std::shared_ptr { size_t GetPointerSize() override; std::vector TrackSectionsList; - - private: - std::vector PointerData; }; class TrackSectionsO2RClass : public Ship::Resource { @@ -31,9 +28,6 @@ class TrackSectionsO2RClass : public Ship::Resource { size_t GetPointerSize() override; std::vector TrackSectionsList; - - private: - std::vector PointerData; }; } // namespace MK64 From c21bc0a0f9b04fd31c307a2380f66a2845d48ca4 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:20:24 +0200 Subject: [PATCH 18/22] allow two way of loading mk64.o2r --- src/engine/mods/ModManager.cpp | 59 ++++++++++++++-------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/src/engine/mods/ModManager.cpp b/src/engine/mods/ModManager.cpp index 27f8c36493..b467722981 100644 --- a/src/engine/mods/ModManager.cpp +++ b/src/engine/mods/ModManager.cpp @@ -20,27 +20,6 @@ void DetectCyclicDependencies(); void DetectOutdatedDependencies(); void SortModsByDependencies(); -namespace { -bool IsArchivePath(const std::filesystem::path& path) { - const auto ext = path.extension().string(); - return StringHelper::IEquals(ext, ".zip") || StringHelper::IEquals(ext, ".o2r") || - std::filesystem::is_directory(path); -} - -void AddUniqueArchive(std::vector& archiveFiles, std::set& seenArchivePaths, - const std::filesystem::path& path) { - if (!IsArchivePath(path)) { - return; - } - - const auto normalizedPath = path.lexically_normal().generic_string(); - if (seenArchivePaths.insert(normalizedPath).second) { - archiveFiles.push_back(normalizedPath); - } -} - -} // namespace - std::vector>> Mods = {}; void InitModsSystem() { @@ -86,20 +65,23 @@ void GenerateAssetsMods() { } std::vector ListMods() { - const std::string main_path = Ship::Context::LocateFileAcrossAppDirs(game_asset_file); + const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory(game_asset_file); + const std::string main_path_local_app = Ship::Context::LocateFileAcrossAppDirs(game_asset_file); const std::string assets_path = Ship::Context::LocateFileAcrossAppDirs(engine_asset_file); std::vector archiveFiles; std::set seenArchivePaths; if (std::filesystem::exists(main_path)) { - AddUniqueArchive(archiveFiles, seenArchivePaths, main_path); + archiveFiles.push_back(main_path); + } else if (std::filesystem::exists(main_path_local_app)) { + archiveFiles.push_back(main_path_local_app); } else { // should not happen, but just in case GenerateAssetsMods(); - AddUniqueArchive(archiveFiles, seenArchivePaths, main_path); + archiveFiles.push_back(main_path); } if (std::filesystem::exists(assets_path)) { - AddUniqueArchive(archiveFiles, seenArchivePaths, assets_path); + archiveFiles.push_back(assets_path); } const std::string mods_path = Ship::Context::GetPathRelativeToAppDirectory("mods"); @@ -112,7 +94,11 @@ std::vector ListMods() { if (std::filesystem::exists(mods_path) && std::filesystem::is_directory(mods_path)) { for (const auto& p : std::filesystem::directory_iterator(mods_path)) { - AddUniqueArchive(archiveFiles, seenArchivePaths, p.path()); + auto ext = p.path().extension().string(); + if (StringHelper::IEquals(ext, ".zip") || StringHelper::IEquals(ext, ".o2r") || + std::filesystem::is_directory(p.path())) { + archiveFiles.push_back(p.path().generic_string()); + } } } return archiveFiles; @@ -124,9 +110,8 @@ std::optional> CheckCyclicDependencies() { for (const auto& [metaB, _] : Mods) { if (metaA.name != metaB.name) { // If A depends on B and B depends on A, we have a cycle - if (metaA.dependencies.contains(metaB.name) && - metaB.dependencies.contains(metaA.name)) { - list.push_back(metaA.name + " <-> " + metaB.name); + if (metaA.dependencies.contains(metaB.name) && metaB.dependencies.contains(metaA.name)) { + list.push_back(metaA.name + " <-> " + metaB.name); } } } @@ -146,7 +131,8 @@ std::optional> CheckOutdatedDependencies(const ModMetad found = true; auto range = depVersion.first; if (!range.contains(otherMeta.version)) { - list.push_back(depName + " (required: " + depVersion.second + ", found: " + otherMeta.version.to_string() + ")"); + list.push_back(depName + " (required: " + depVersion.second + + ", found: " + otherMeta.version.to_string() + ")"); } break; } @@ -216,16 +202,18 @@ void AddCoreDependencies() { semver::range_set assetsVer; semver::parse("1.0.0-alpha1", assetsVer); meta.dependencies = { - {"mk64-assets", {mk64Ver, "1.0.0-alpha1"}}, - {"extended-assets", {assetsVer, "1.0.0-alpha1"}}, + { "mk64-assets", { mk64Ver, "1.0.0-alpha1" } }, + { "extended-assets", { assetsVer, "1.0.0-alpha1" } }, }; AddModMetadata(meta, nullptr); } void CheckMK64O2RExists() { - const std::string main_path = Ship::Context::LocateFileAcrossAppDirs(game_asset_file); + const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory(game_asset_file); + const std::string main_path_local_app = Ship::Context::LocateFileAcrossAppDirs(game_asset_file); - if (!std::filesystem::exists(main_path)) { + // some build can prefer have mk64.o2r in the app application + if (!std::filesystem::exists(main_path) && !std::filesystem::exists(main_path_local_app)) { GenerateAssetsMods(); } } @@ -265,7 +253,8 @@ void FindAndLoadMods() { } metadata.name = std::filesystem::path(path).stem().string(); semver::parse("0.0.0", metadata.version); - SPDLOG_WARN("The mod at path {} is missing a mods.toml file. Using default metadata:\n{}", path, metadata.ToString()); + SPDLOG_WARN("The mod at path {} is missing a mods.toml file. Using default metadata:\n{}", path, + metadata.ToString()); } AddModMetadata(metadata, archive); From ff5e32fec8a5e4cf8ed1a1a31e9a848d607a5e9e Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:57:27 +0200 Subject: [PATCH 19/22] Update BUILDING.md --- docs/BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index c057e83dfc..b23df376c5 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -303,7 +303,7 @@ cmake -B build-ios-make -G "Unix Makefiles" \ -DCMAKE_OSX_ARCHITECTURES=arm64 # Compile the project -cmake --build build-ios-make -j8 +cmake --build build-ios-make ``` ### Running on the Simulator From 29818780543dcf4d52776fd7ba11e53fbb17ea67 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:57:37 +0200 Subject: [PATCH 20/22] clean up a little the cmake --- CMakeLists.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61e2e5402b..c4b750b0d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -729,15 +729,13 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "$/config.yml" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "$/yamls/" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/meta/" "$/meta/" - COMMAND ${CMAKE_COMMAND} -DSRC="${CMAKE_SOURCE_DIR}/spaghetti.o2r" -DDST="$/spaghetti.o2r" -P "${CMAKE_BINARY_DIR}/copy_if_exists.cmake" ) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") add_custom_command( TARGET ${PROJECT_NAME} POST_BUILD COMMENT "Copying packaged O2R assets..." - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/mk64.o2r" "$/mk64.o2r" - COMMAND ${CMAKE_COMMAND} -DSRC="${CMAKE_BINARY_DIR}/spaghetti.o2r" -DDST="$/spaghetti.o2r" -P "${CMAKE_BINARY_DIR}/copy_if_exists.cmake" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/spaghetti.o2r" "$/spaghetti.o2r" ) endif() @@ -758,7 +756,7 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") add_custom_target( ExtractAssets - DEPENDS TorchExternal + DEPENDS ${TORCH_EXECUTABLE} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${TORCH_EXECUTABLE} header -o baserom.us.z64 COMMAND ${TORCH_EXECUTABLE} o2r baserom.us.z64 --additional-files meta/mods.toml @@ -769,15 +767,13 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") add_custom_target( GenerateO2R - DEPENDS TorchExternal + DEPENDS TorchExternal "${CMAKE_SOURCE_DIR}/assets" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${TORCH_EXECUTABLE} pack assets spaghetti.o2r o2r COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/spaghetti.o2r" "${CMAKE_BINARY_DIR}/spaghetti.o2r" ) - if(CMAKE_SYSTEM_NAME STREQUAL "iOS") - add_dependencies(${PROJECT_NAME} ExtractAssets) - endif() + add_dependencies(${PROJECT_NAME} GenerateO2R) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") install(FILES "${CMAKE_BINARY_DIR}/spaghetti.o2r" DESTINATION . COMPONENT ${PROJECT_NAME} OPTIONAL) From 4f57ed7059e0a25114c85da58049b3b2303bb3d1 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:58:55 +0200 Subject: [PATCH 21/22] add the definition of __IOS__ --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4b750b0d0..b2aa198fd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,6 +326,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "iOS") list(APPEND ALL_FILES ${STORYBOARD_FILE} ${ICON_FILES}) + add_compile_definitions(__IOS__) + + add_executable(${PROJECT_NAME} ${ALL_FILES}) set_xcode_property(${PROJECT_NAME} PRODUCT_BUNDLE_IDENTIFIER ${PROJECT_ID} All) set_target_properties( From 3e5f8cac64eda764bf90d4d366c93e6041a3d5fc Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:59:30 +0200 Subject: [PATCH 22/22] fix import --- src/port/SpaghettiGui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/port/SpaghettiGui.cpp b/src/port/SpaghettiGui.cpp index d9c957d3f2..592e6326a7 100644 --- a/src/port/SpaghettiGui.cpp +++ b/src/port/SpaghettiGui.cpp @@ -21,7 +21,7 @@ #endif #if defined(__ANDROID__) || defined(__IOS__) -#include "port/mobile/MobileImpl.h" +#include "ship/port/mobile/MobileImpl.h" #endif #ifdef ENABLE_OPENGL