From c2b021130408c5ff7ebd4e8a6e31ac9b15786bfb Mon Sep 17 00:00:00 2001 From: It's Rhew! <158854414+SnepOMatic@users.noreply.github.com> Date: Sat, 15 Nov 2025 22:51:19 +0000 Subject: [PATCH] Switch port (#731) * - Set defaults if ini exists, but empty. - Create a new dictionary object, not use null object. * Switch Port Todo: Fix: Misc crashes when interacting on island. Todo: Fix: Joystick pointer is slow to move. Todo: Fix: Touch doesn't behave. * Fix: base GetPathInfo fixup in the SDL implementation, with check for null SDL_PathInfo. Fix: Disable imgui shell functions. Todo: Display imgui debug if enabled. * Fix: Correction to suite style guidelines * - Switch build homebrew NRO - CI/CD to build Switch * Fix typo in CI * clang-format fix * Clang-format against isleapp.cpp * Use correct docker container for CI/CD * Remove SDL fixups and use an SDL3 port with fixes. Fixes crash bug and world issues! * Fix: use Clang-Format 17 instead of 21 --------- Co-authored-by: SnepOMatic (Rhew) --- .github/workflows/ci.yml | 11 ++++++- CMakeLists.txt | 63 ++++++++++++++++++++++++++++++++++++-- ISLE/isleapp.cpp | 9 ++++++ ISLE/switch/config.cpp | 12 ++++++++ ISLE/switch/config.h | 8 +++++ miniwin/CMakeLists.txt | 6 ++++ packaging/switch/isle.jpg | Bin 0 -> 15011 bytes 7 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 ISLE/switch/config.cpp create mode 100644 ISLE/switch/config.h create mode 100644 packaging/switch/isle.jpg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f4b04c9..c2ac64d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,7 @@ jobs: - { name: 'iOS', os: 'macos-15', generator: 'Xcode', dx5: false, config: false, brew: true, werror: true, clang-tidy: false, cmake-args: '-DCMAKE_SYSTEM_NAME=iOS', ios: true } - { name: 'Emscripten', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' } - { name: 'Nintendo 3DS', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, n3ds: true, werror: true, clang-tidy: false, container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' } + - { name: 'Nintendo Switch', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, nx: true, werror: true, clang-tidy: false, container: 'devkitpro/devkita64:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake' } - { name: 'Xbox One', os: 'windows-latest', generator: 'Visual Studio 17 2022', dx5: false, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64', cmake-args: '-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.26100.0', xbox-one: true} - { name: 'Android', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, android: true, werror: true, clang-tidy: false,} - { name: 'Vita', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, vita: true, werror: true, clang-tidy: false, cmake-args: '--toolchain /usr/local/vitasdk/share/vita.toolchain.cmake'} @@ -190,7 +191,7 @@ jobs: run: cmake --build build --verbose --config Release - name: Package (CPack) - if: ${{ !matrix.n3ds && !matrix.android && !matrix.vita }} + if: ${{ !matrix.n3ds && !matrix.nx && !matrix.android && !matrix.vita }} run: | cd build success=0 @@ -238,6 +239,13 @@ jobs: mkdir dist mv *.3dsx dist/ mv *.cia dist/ + + - name: Package (Switch) + if: ${{ matrix.nx }} + run: | + cd build + mkdir dist + mv *.nro dist/ - name: Package (Vita) if: ${{ matrix.vita }} @@ -265,6 +273,7 @@ jobs: build/dist/*.AppImage build/dist/*.3dsx build/dist/*.cia + build/dist/*.nro build/dist/*.apk build/dist/*.vpk diff --git a/CMakeLists.txt b/CMakeLists.txt index 60e742d0..c310108f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,14 @@ if (EMSCRIPTEN) set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE) endif() +if (NINTENDO_SWITCH) + set(CMAKE_TOOLCHAIN_FILE "${DEVKITPRO}/cmake/Switch.cmake" CACHE PATH "toolchain file") + add_compile_definitions(__SWITCH__) + add_compile_definitions(SDL_PLATFORM_SWITCH) + add_compile_definitions(SDL_VIDEO_DRIVER_SWITCH) + add_compile_definitions(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +endif() + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") include(CheckCXXSourceCompiles) @@ -50,7 +58,7 @@ cmake_dependent_option(ISLE_DEBUG "Enable imgui debug" ON "NOT VITA" OFF) cmake_dependent_option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" "${NOT_MINGW}" "WIN32;CMAKE_SIZEOF_VOID_P EQUAL 4" OFF) cmake_dependent_option(ISLE_MINIWIN "Use miniwin" ON "NOT ISLE_USE_DX5" OFF) cmake_dependent_option(ISLE_EXTENSIONS "Use extensions" ON "NOT ISLE_USE_DX5;NOT WINDOWS_STORE" OFF) -cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS;NOT WINDOWS_STORE;NOT VITA" OFF) +cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS;NOT NINTENDO_SWITCH;NOT WINDOWS_STORE;NOT VITA" OFF) cmake_dependent_option(ISLE_COMPILE_SHADERS "Compile shaders" ON "SDL_SHADERCROSS_BIN;TARGET Python3::Interpreter" OFF) cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON "NOT VITA" OFF) option(ENABLE_CLANG_TIDY "Enable clang-tidy") @@ -85,6 +93,18 @@ if (DOWNLOAD_DEPENDENCIES) GIT_TAG "main" EXCLUDE_FROM_ALL ) + elseif (NINTENDO_SWITCH) + FetchContent_Declare( + SDL3 + # Official repo missing Threads support? + #GIT_REPOSITORY "https://github.com/devkitPro/SDL.git" + #GIT_TAG "switch-sdl-3.2" + #GIT_REPOSITORY "https://github.com/vs49688/SDL.git" + GIT_REPOSITORY "https://github.com/SnepOMatic/SDL3-Switch.git" + GIT_TAG "switch-sdl-3.2.14" + EXCLUDE_FROM_ALL + ) + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON CACHE BOOL "Disable PCH globally" FORCE) else() FetchContent_Declare( SDL3 @@ -178,7 +198,8 @@ target_include_directories(Vec::Vec INTERFACE "${CMAKE_SOURCE_DIR}/3rdparty/vec" add_library(lego1 LEGO1/main.cpp ) -target_precompile_headers(lego1 PRIVATE "LEGO1/lego1_pch.h") +target_include_directories(lego1 PRIVATE "${CMAKE_SOURCE_DIR}/LEGO1") +target_precompile_headers(lego1 PRIVATE "") set_property(TARGET lego1 PROPERTY DEFINE_SYMBOL "LEGO1_DLL") target_include_directories(lego1 PUBLIC "$") target_include_directories(lego1 PUBLIC "$") @@ -585,6 +606,11 @@ if (ISLE_BUILD_APP) ISLE/3ds/config.cpp ) endif() + if(NINTENDO_SWITCH) + target_sources(isle PRIVATE + ISLE/switch/config.cpp + ) + endif() if(WINDOWS_STORE) target_sources(isle PRIVATE ISLE/xbox_one_series/config.cpp @@ -880,6 +906,39 @@ if(NINTENDO_3DS) endif() install(FILES "$/isle.3dsx" DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() +if(NINTENDO_SWITCH) + find_program(NACPTOOL NAMES nacptool) + find_program(ELF2NRO NAMES elf2nro) + + add_custom_command( + OUTPUT "isle.nacp" + COMMAND "${NACPTOOL}" + --create + "LEGO Island" + "isledecomp/isle-portable" + "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.0" + "isle.nacp" + VERBATIM + ) + + add_custom_command( + OUTPUT "isle.nro" + COMMAND "${ELF2NRO}" + "$" + "$/isle.nro" + "--icon=${CMAKE_SOURCE_DIR}/packaging/switch/isle.jpg" + "--nacp=${CMAKE_BINARY_DIR}/isle.nacp" + DEPENDS "${CMAKE_SOURCE_DIR}/packaging/switch/isle.jpg" "${CMAKE_BINARY_DIR}/isle.nacp" + VERBATIM + ) + + add_custom_target(switch-nro + ALL + DEPENDS "${CMAKE_BINARY_DIR}/isle.nro" + COMMENT "Build switch NRO from ELF" + ) + +endif() if(WINDOWS_STORE) install( DIRECTORY diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 87321226..72c11a7b 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -65,6 +65,12 @@ #include "3ds/config.h" #endif +#ifdef __SWITCH__ +#include "switch/config.h" + +#include +#endif + #ifdef WINDOWS_STORE #include "xbox_one_series/config.h" #endif @@ -1144,6 +1150,9 @@ bool IsleApp::LoadConfig() #ifdef __3DS__ N3DS_SetupDefaultConfigOverrides(dict); #endif +#ifdef __SWITCH__ + NX_SetupDefaultConfigOverrides(dict); +#endif #ifdef WINDOWS_STORE XBONE_SetupDefaultConfigOverrides(dict); #endif diff --git a/ISLE/switch/config.cpp b/ISLE/switch/config.cpp new file mode 100644 index 00000000..9f7f1b56 --- /dev/null +++ b/ISLE/switch/config.cpp @@ -0,0 +1,12 @@ +#include "config.h" + +#include +#include + +void NX_SetupDefaultConfigOverrides(dictionary* p_dictionary) +{ + iniparser_set(p_dictionary, "isle:diskpath", "sdmc:/switch/isle/LEGO/"); + iniparser_set(p_dictionary, "isle:cdpath", "sdmc:/switch/isle/"); + iniparser_set(p_dictionary, "isle:savepath", "sdmc:/switch/isle/"); + iniparser_set(p_dictionary, "isle:Cursor Sensitivity", "16.000000"); +} diff --git a/ISLE/switch/config.h b/ISLE/switch/config.h new file mode 100644 index 00000000..7545de51 --- /dev/null +++ b/ISLE/switch/config.h @@ -0,0 +1,8 @@ +#ifndef NX_CONFIG_H +#define NX_CONFIG_H + +#include "dictionary.h" + +void NX_SetupDefaultConfigOverrides(dictionary* p_dictionary); + +#endif // NX_CONFIG_H diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index 4293780e..a7b348e1 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -69,6 +69,12 @@ if(NOT (VITA OR WINDOWS_STORE)) endif() endif() +if(NINTENDO_SWITCH) + # Remove USE_OPENGL1 as incompatible. + # Remove everything else as not needed. + list(REMOVE_ITEM GRAPHICS_BACKENDS USE_SOFTWARE_RENDER USE_OPENGL1 USE_OPENGLES2) #USE_SDL_GPU +endif() + if(VITA) add_subdirectory(src/d3drm/backends/gxm/shaders) diff --git a/packaging/switch/isle.jpg b/packaging/switch/isle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ddaf3569356fac9c55a62fbc293f3f25d9af4419 GIT binary patch literal 15011 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 Di~*GR literal 0 HcmV?d00001