From 2971a3b06cac69e47c46e47d1f351b6e1fa8728f Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Fri, 15 Aug 2025 23:51:26 +0900 Subject: [PATCH 1/6] Fix iOS port build (#677) * fix: ios launchscreen build error * fix: github no longer provides ios sdk * fix: wow awesome a typo * fix: use latest xcode --- .github/workflows/ci.yml | 8 +++- packaging/ios/CMakeLists.txt | 2 +- packaging/ios/isle/Info.plist.in | 2 +- packaging/ios/isle/LaunchScreen.storyboard | 48 ---------------------- 4 files changed, 9 insertions(+), 51 deletions(-) delete mode 100644 packaging/ios/isle/LaunchScreen.storyboard diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0ae04c8..d84f6ab4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - { name: 'msys2 mingw32', os: 'windows-latest', generator: 'Ninja', dx5: false, config: false, mingw: true, werror: true, clang-tidy: true, msystem: 'mingw32', msys-env: 'mingw-w64-i686', shell: 'msys2 {0}' } - { name: 'msys2 mingw64', os: 'windows-latest', generator: 'Ninja', dx5: false, config: true, mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' } - { name: 'macOS', os: 'macos-latest', generator: 'Ninja', dx5: false, config: true, brew: true, werror: true, clang-tidy: false } - - { name: 'iOS', os: 'macos-15', generator: 'Xcode', dx5: false, config: false, brew: true, werror: true, clang-tidy: false, cmake-args: '-DCMAKE_SYSTEM_NAME=iOS' } + - { 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: '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} @@ -95,6 +95,12 @@ jobs: brew update brew install cmake ninja llvm qt6 echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV + + - name: Use latest Xcode + if: ${{ matrix.ios }} + uses: maxim-lobanov/setup-xcode@v1.6.0 + with: + xcode-version: latest-stable - name: Setup Emscripten uses: mymindstorm/setup-emsdk@master diff --git a/packaging/ios/CMakeLists.txt b/packaging/ios/CMakeLists.txt index 418ad518..834b75d6 100644 --- a/packaging/ios/CMakeLists.txt +++ b/packaging/ios/CMakeLists.txt @@ -15,7 +15,7 @@ if(ISLE_BUILD_APP) "${CMAKE_CURRENT_BINARY_DIR}/isle/Info.plist" @ONLY ) - set(RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/isle/LaunchScreen.storyboard" "${CMAKE_CURRENT_SOURCE_DIR}/isle/Assets.xcassets") + set(RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/isle/Assets.xcassets") target_sources(${ISLE_TARGET_NAME} PRIVATE ${RESOURCE_FILES}) set_source_files_properties(${RESOURCE_FILES} TARGET_DIRECTORY isle diff --git a/packaging/ios/isle/Info.plist.in b/packaging/ios/isle/Info.plist.in index 714324a3..c5d5a80b 100644 --- a/packaging/ios/isle/Info.plist.in +++ b/packaging/ios/isle/Info.plist.in @@ -27,7 +27,7 @@ CFBundleVersion @MACOSX_BUNDLE_BUNDLE_VERSION@ UILaunchStoryboardName - LaunchScreen + NSHighResolutionCapable CSResourcesFileMapped diff --git a/packaging/ios/isle/LaunchScreen.storyboard b/packaging/ios/isle/LaunchScreen.storyboard deleted file mode 100644 index ad167a8a..00000000 --- a/packaging/ios/isle/LaunchScreen.storyboard +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From f06731784e96c90849207e6bc9bd6c6a43fee4da Mon Sep 17 00:00:00 2001 From: Cookie <52550063+Covkie@users.noreply.github.com> Date: Fri, 15 Aug 2025 11:12:35 -0400 Subject: [PATCH 2/6] Android port (#656) * initial files * get building * proper paths & default to virtual mouse * Wrap gradle under cmake & build SDL3.aar run cmake with -DANDROID_NDK_PATH=/path/to/ndk to generate the .aar & run the gradle build if using Android Studio you will have to run the aar script manually(or do the cmake cmd) then put the normal cmake args inside '-PcmakeArgs="-DCMAKE_BUILD_TYPE=Release ...."' at `Settings > Build, Execution, Deployment > Gradle-Android Compiler > Command-line Options` Check CMakeLists.txt Android build block for more info * workflow and format * dont cmake -> gradle -> cmake * icons * prevent first start crash due to missing isle.ini dont force require gles3 (vulkan & software are options) * cleanup * cmake script & ci cleanup use a cmake script to downoad SDL3 sources for aar building * script cleanups & gradle stub for android studio handle the env var setting for the sdl3 aar build script * signing needed repository secrets: - keystore : key.jks in base64 - keystorePassword : Key store password - keyAlias : Key alias, ex key0 - keyPassword : Key Password * conditionally run action without keys * compile SDL once & cross-platformize gradle tasks * suggestions * use find_package over manual configuration * use MxString --------- Co-authored-by: Anders Jenbo Co-authored-by: Christian Semmler --- .github/workflows/ci.yml | 45 ++++- .gitignore | 2 + CMakeLists.txt | 40 ++-- ISLE/android/config.cpp | 30 +++ ISLE/android/config.h | 8 + ISLE/isleapp.cpp | 14 ++ android-project/.gitignore | 5 + android-project/app/build.gradle | 101 ++++++++++ .../app/src/main/AndroidManifest.xml | 60 ++++++ .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 20483 bytes .../org/legoisland/isle/IsleActivity.java | 9 + .../res/drawable/ic_launcher_foreground.xml | 141 +++++++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1996 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 3690 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 1342 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 2520 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 2592 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 5142 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 3652 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 7670 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 4574 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 10746 bytes .../res/values/ic_launcher_background.xml | 4 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 8 + android-project/build.gradle | 4 + android-project/downloadSDL3.cmake | 13 ++ android-project/gradle.properties | 21 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + android-project/gradlew | 185 ++++++++++++++++++ android-project/gradlew.bat | 89 +++++++++ android-project/settings.gradle | 16 ++ miniwin/CMakeLists.txt | 2 +- settings.gradle | 1 + 37 files changed, 803 insertions(+), 13 deletions(-) create mode 100644 ISLE/android/config.cpp create mode 100644 ISLE/android/config.h create mode 100644 android-project/.gitignore create mode 100644 android-project/app/build.gradle create mode 100644 android-project/app/src/main/AndroidManifest.xml create mode 100644 android-project/app/src/main/ic_launcher-playstore.png create mode 100644 android-project/app/src/main/java/org/legoisland/isle/IsleActivity.java create mode 100644 android-project/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 android-project/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 android-project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 android-project/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 android-project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 android-project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 android-project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 android-project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 android-project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 android-project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 android-project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 android-project/app/src/main/res/values/ic_launcher_background.xml create mode 100644 android-project/app/src/main/res/values/strings.xml create mode 100644 android-project/app/src/main/res/values/styles.xml create mode 100644 android-project/build.gradle create mode 100644 android-project/downloadSDL3.cmake create mode 100644 android-project/gradle.properties create mode 100644 android-project/gradle/wrapper/gradle-wrapper.jar create mode 100644 android-project/gradle/wrapper/gradle-wrapper.properties create mode 100755 android-project/gradlew create mode 100644 android-project/gradlew.bat create mode 100644 android-project/settings.gradle create mode 100644 settings.gradle diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d84f6ab4..9e284eee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: - { 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: '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,} steps: - name: Setup vcvars if: ${{ !!matrix.msvc }} @@ -112,7 +113,41 @@ jobs: - uses: actions/checkout@v4 + - name: Setup Java (Android) + if: ${{ matrix.android }} + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Get CMake (Android) + if: ${{ matrix.android }} + uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.30.5 + + - name: Build (Android) + if: ${{ matrix.android }} + env: + SIGNING_KEY_ALIAS: ${{ secrets.keyAlias }} + SIGNING_KEY_PASSWORD: ${{ secrets.keyPassword }} + SIGNING_STORE_PASSWORD: ${{ secrets.keystorePassword }} + SIGNING_STORE_FILE: ${{ github.workspace }}/release.keystore + run: | + echo "${{ secrets.keystore }}" | base64 -d > release.keystore + cd android-project + ./gradlew $([ -n "$SIGNING_KEY_ALIAS" ] && echo packageRelease || echo assembleDebug ) \ + --info \ + -PcmakeArgs="-DCMAKE_BUILD_TYPE=Release \ + -DISLE_USE_DX5=${{ !!matrix.dx5 }} \ + -DISLE_BUILD_CONFIG=${{ !!matrix.config }} \ + -DENABLE_CLANG_TIDY=${{ !!matrix.clang-tidy }} \ + -DISLE_WERROR=${{ !!matrix.werror }} \ + -DISLE_DEBUG=${{ matrix.debug || 'OFF' }} \ + -Werror=dev" + - name: Configure (CMake) + if: ${{ !matrix.android }} run: | ${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -G "${{ matrix.generator }}" \ ${{ matrix.cmake-args || '' }} \ @@ -126,10 +161,11 @@ jobs: -Werror=dev - name: Build (CMake) + if: ${{ !matrix.android }} run: cmake --build build --verbose --config Release - name: Package (CPack) - if: ${{ !matrix.n3ds }} + if: ${{ !matrix.n3ds && !matrix.android }} run: | cd build success=0 @@ -178,6 +214,12 @@ jobs: mv *.3dsx dist/ mv *.cia dist/ + - name: Package (Android) + if: ${{ matrix.android }} + run: | + mkdir -p build/dist + mv android-project/app/build/outputs/apk/*/*.apk build/dist/ + - name: Package Assets Separately if: matrix.build-assets run: (cd build/assets && zip -r ../dist/isle-assets.zip .) @@ -191,6 +233,7 @@ jobs: build/dist/*.AppImage build/dist/*.3dsx build/dist/*.cia + build/dist/*.apk flatpak: name: "Flatpak (${{ matrix.arch }})" diff --git a/.gitignore b/.gitignore index 1610ba3d..a4bd8a41 100644 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,14 @@ Release/ /.idea .env .venv +.gradle env/ venv/ ENV/ VENV/ env.bak/ venv.bak/ +local.properties /build/ /build_debug/ /legobin/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 87cb124f..47c9b9c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,22 +73,28 @@ if (DOWNLOAD_DEPENDENCIES) # FetchContent downloads and configures dependencies message(STATUS "Fetching SDL3 and iniparser. This might take a while...") include(FetchContent) - if (WINDOWS_STORE) - FetchContent_Declare( - SDL3 - GIT_REPOSITORY "https://github.com/Helloyunho/SDL3-uwp.git" - GIT_TAG "main" - EXCLUDE_FROM_ALL - ) + + if(ANDROID) + # Built by Gradle + find_package(SDL3 REQUIRED CONFIG COMPONENTS Shared) else() - FetchContent_Declare( + if (WINDOWS_STORE) + FetchContent_Declare( + SDL3 + GIT_REPOSITORY "https://github.com/Helloyunho/SDL3-uwp.git" + GIT_TAG "main" + EXCLUDE_FROM_ALL + ) + else() + FetchContent_Declare( SDL3 GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git" GIT_TAG "main" EXCLUDE_FROM_ALL - ) + ) + endif() + FetchContent_MakeAvailable(SDL3) endif() - FetchContent_MakeAvailable(SDL3) FetchContent_Declare( iniparser @@ -501,6 +507,13 @@ if (ISLE_EXTENSIONS) endif() if (ISLE_BUILD_APP) + + if (ANDROID) + function(add_executable TARGET PLATFORM) + add_library(${TARGET} SHARED ${ARGN}) + endfunction() + endif() + add_executable(isle WIN32 ISLE/res/isle.rc ISLE/isleapp.cpp @@ -580,6 +593,11 @@ if (ISLE_BUILD_APP) ISLE/ios/config.cpp ) endif() + if (ANDROID) + target_sources(isle PRIVATE + ISLE/android/config.cpp + ) + endif() if(Python3_FOUND) if(NOT DEFINED PYTHON_PIL_AVAILABLE) execute_process( @@ -746,7 +764,7 @@ endif() set(install_extra_targets) if(DOWNLOAD_DEPENDENCIES) get_property(sdl3_type TARGET SDL3::SDL3 PROPERTY TYPE) - if(sdl3_type STREQUAL "SHARED_LIBRARY") + if(sdl3_type STREQUAL "SHARED_LIBRARY" AND NOT ANDROID) list(APPEND install_extra_targets "SDL3-shared") endif() endif() diff --git a/ISLE/android/config.cpp b/ISLE/android/config.cpp new file mode 100644 index 00000000..e93cb02e --- /dev/null +++ b/ISLE/android/config.cpp @@ -0,0 +1,30 @@ +#include "config.h" + +#include "mxstring.h" + +#include +#include +#include +#include + +void Android_SetupDefaultConfigOverrides(dictionary* p_dictionary) +{ + SDL_Log("Overriding default config for Android"); + + const char* data = SDL_GetAndroidExternalStoragePath(); + MxString savedata = MxString(data) + "/saves/"; + + if (!SDL_GetPathInfo(savedata.GetData(), NULL)) { + SDL_CreateDirectory(savedata.GetData()); + } + + iniparser_set(p_dictionary, "isle:diskpath", data); + iniparser_set(p_dictionary, "isle:cdpath", data); + iniparser_set(p_dictionary, "isle:mediapath", data); + + iniparser_set(p_dictionary, "isle:savepath", savedata.GetData()); + + // Default to Virtual Mouse + char buf[16]; + iniparser_set(p_dictionary, "isle:Touch Scheme", SDL_itoa(0, buf, 10)); +} diff --git a/ISLE/android/config.h b/ISLE/android/config.h new file mode 100644 index 00000000..5b4ac3d0 --- /dev/null +++ b/ISLE/android/config.h @@ -0,0 +1,8 @@ +#ifndef ANDROID_CONFIG_H +#define ANDROID_CONFIG_H + +#include "dictionary.h" + +void Android_SetupDefaultConfigOverrides(dictionary* p_dictionary); + +#endif // ANDROID_CONFIG_H diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 83a7927c..0d3eee28 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -72,6 +72,10 @@ #include "ios/config.h" #endif +#ifdef ANDROID +#include "android/config.h" +#endif + DECOMP_SIZE_ASSERT(IsleApp, 0x8c) // GLOBAL: ISLE 0x410030 @@ -1027,6 +1031,13 @@ bool IsleApp::LoadConfig() { #ifdef IOS const char* prefPath = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS); +#elif defined(ANDROID) + // SDL_GetAndroidExternalStoragePath() returns without a trailing / resulting in "filesisle.ini" :( + const char* androidPath = SDL_GetAndroidExternalStoragePath(); + size_t len = SDL_strlen(androidPath) + 2; + char* prefPath = new char[len]; + SDL_strlcpy(prefPath, androidPath, len); + SDL_strlcat(prefPath, "/", len); #else char* prefPath = SDL_GetPrefPath("isledecomp", "isle"); #endif @@ -1128,6 +1139,9 @@ bool IsleApp::LoadConfig() #endif #ifdef IOS IOS_SetupDefaultConfigOverrides(dict); +#endif +#ifdef ANDROID + Android_SetupDefaultConfigOverrides(dict); #endif iniparser_dump_ini(dict, iniFP); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig); diff --git a/android-project/.gitignore b/android-project/.gitignore new file mode 100644 index 00000000..bcd50fa6 --- /dev/null +++ b/android-project/.gitignore @@ -0,0 +1,5 @@ +.cxx +.gradle +local.properties +build +*.aar diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle new file mode 100644 index 00000000..dbdb337b --- /dev/null +++ b/android-project/app/build.gradle @@ -0,0 +1,101 @@ +plugins { + id 'com.android.application' +} +def androidProject = projectDir.parentFile + +android { + namespace "org.legoisland.isle" + compileSdk 35 + + defaultConfig { + applicationId 'org.legoisland.isle' + minSdk 21 + targetSdk 35 + versionCode 1 + versionName '1.0' + + externalNativeBuild { + cmake { + arguments '-DANDROID_STL=c++_shared', "-DFETCHCONTENT_BASE_DIR=${androidProject}/build/_deps" + if (project.hasProperty('cmakeArgs')) { + project.cmakeArgs.split(" ").each {arg -> arguments arg} + } + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + } + } + } + externalNativeBuild { + cmake { + version "3.30.5" + path '../../CMakeLists.txt' + } + } + + signingConfigs { + register("release") { + enableV4Signing = true + keyAlias = System.getenv("SIGNING_KEY_ALIAS") + keyPassword = System.getenv("SIGNING_KEY_PASSWORD") + System.getenv("SIGNING_STORE_FILE")?.with { storeFile = file(it) } + storePassword = System.getenv("SIGNING_STORE_PASSWORD") + } + } + buildTypes { + release { + minifyEnabled true + signingConfig = signingConfigs.getByName("release") +// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + prefab true + } +} + +afterEvaluate { + def androidPath = System.getenv("ANDROID_HOME") ?: android.sdkDirectory + def ndkPath = System.getenv("ANDROID_NDK_HOME") ?: android.ndkDirectory + + tasks.named('compileSDL3AndroidArchive').configure { + environment "ANDROID_HOME", androidPath + environment "ANDROID_NDK_HOME", ndkPath + } +} + +def aarDest = project.providers.provider { file("${projectDir}/libs/SDL3.aar") } +tasks.register('downloadSDL3', Exec) { + workingDir = androidProject + commandLine 'cmake', '-P', 'downloadSDL3.cmake' +} +tasks.register('compileSDL3AndroidArchive', Exec) { + workingDir = androidProject + dependsOn(downloadSDL3) + + def sdl3Dir = "build/_deps/sdl3-src" + commandLine 'python', "${sdl3Dir}/build-scripts/build-release.py", + '--actions', 'android', + '--fast', '--force', + "--root=${sdl3Dir}" + + doLast { + def aarFile = file("${androidProject}/${sdl3Dir}/build-android").listFiles().find { + it.name.endsWith(".aar") + } + aarDest.get().parentFile.mkdirs() + aarFile.renameTo(aarDest.get()) + } +} +tasks.register('ensureSDL3') { + // if DOWNLOAD_DEPENDENCIES=OFF download the version appropriate + // 'devel android zip' and place the .aar at `aarDest` + if (aarDest.get().exists()) { return false } + dependsOn(compileSDL3AndroidArchive) +} + +dependencies { + implementation files('libs/SDL3.aar').builtBy(ensureSDL3) +} diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..7d76ba78 --- /dev/null +++ b/android-project/app/src/main/AndroidManifest.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android-project/app/src/main/ic_launcher-playstore.png b/android-project/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..d38af7d8bbad86334b406d81f1bbeb73a759eef4 GIT binary patch literal 20483 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 + + + + + + + + + + + + + + + + + + + + + + diff --git a/android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..7353dbd1 --- /dev/null +++ b/android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..7353dbd1 --- /dev/null +++ b/android-project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android-project/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android-project/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..f4d86bca905aebbd0f9806c95a6e4cda54d3fed1 GIT binary patch literal 1996 zcmV;-2Q&CmNk&G*2LJ$9MM6+kP&iDu2LJ#sN5Byf)lj3h&5S0!W81cETmNhuYwy^$ zr`^e?tZm!2&9UutrP6t8^)p*%yM}9yJ$oZT<(_ z_V?mr{Eltgw*BQxGO;0t+O}hIp4l;y`Tu`rF*bJa`@`DamSv1CC!K9pN#)K)?WD9_ zrLrBUkpLiUF3ikeqj&?7xytJLu25UWpqN1=oq-0xb?68Lz}?f01!0)pxI|Sg{GAc4+|#A9RG1r zW=N{BYVM-~K)^xi7ArExems~PVN#ENqT4fFq{o7h!yepZxJA9%LkEEHRM9*UtR}mM zGWq&{9aCg-%Fd7!^+wNRg`82y#B`p~=K^MPwjoHygg+cS;ovEM4Ng^V{iaAMk92Rf zEL>*|SS|8q-PdwJI8}rZ%{h9>xkf)<=NLs%e2!Mjx|H%mL6a1N@L*<|XhO94v7QYq zwKWNwyBdzUI`_5JaVLxDnUO`bCd;NO{q?eGUVSqv2BgW#4hOg>gXilF%f8-J2VIu_ z(JWiLCH^>H3%vz*Hyk0fc`?_mG$(%8rdn@N=jwXR7@zu-}&zh3?szgD#=>6~tzDze;!!a6OSDDJ91;Z5s+ zxlYp?bEB@!f8zd@eO{dzb9K&ZikNb7${&yQt~bNlZ$(saNBtRsmKQTand0lLn$LK> z=A`^aIn`U@`kRCqp;wRWvVzlsx~)-}>>fuK=WV!PB!61J|G8sXsKNPW7a394w0;7!@L^3wb(0W-nmE9yEPAG+ge@Z6A>AsTMH^bT=$Z(6Ca2Tkab*_grTpqK5hi6<0+;6-kSeApHjD;J-s3Q=ONHVby<~O5 zVM!oP#)LSh|EcN)mqd^J!wr?D!EJQC216Lt+*EPhmDz8I3B`u|;&lIUgJsS28egt4 zhEkE=f_SP`!i8@VIX@$d6e1djR# zvBOZg5&~*P+aU$KwH#b5vfJy<69Qe83RtrQd?j_lWmn)6wNpRC+gOyCW!UYt7od?5 zp-kPQz-eTy;XWOS;FoYXD@3Fu&WKdf(Ab1-neC4N{6z+^oyMhCfdCKy@B?{kq9y+5 zR=OOTsZzgm&KIB+J z>(&nvyTSetwyJf(z<`YrnTkEM;sznt5abzvU@+$}M2|xXI|S8|IzcO11iJ3bK_eqd z*|pnQ*Cvpib$G&AFoE7#%O{+bG=aP|XC+Muv!tMj60+tl5Np87SZl!M_L_?@KlcoF zd);~O*dl1z46TxkQDV+&m`GAWcUBEe_HuWPw#AbO&ss z0zhM|!Fc!JCf^@s(D%CsrpjLcXagYhhe@jm8w&v-UuwuV9-1tFO&16^;dZ0v!JJJX z2BucN3r`5M->a5)hD~}|$CGgYGyyPb*_hfmIk)k#S7!OCM^ycev?%f;m zZKvD-e{W2vS4L^^>u+0kZOb?vOPD$U5J*28N&=t`fDQn<0O+Cj(-#1!rBUls00@}6 em1sev=s5DZ=TOd}qKaO?`rh=bpBQAmhsp$NjObVZ literal 0 HcmV?d00001 diff --git a/android-project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android-project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..60a5c4175766c3734934792ac91bb4a20bea6bdc GIT binary patch literal 3690 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 A$p8QV literal 0 HcmV?d00001 diff --git a/android-project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android-project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..047b4028b1a6787591e3a7aabc9531f9bc03951e GIT binary patch literal 2520 zcmV;}2`BbaNk&G{2><|BMM6+kP&iD)2><{uFTe{BHHU$;Z5IEwx8FiUOh7uCPnBA2 z|6?h?4xogbWd@7PIp>^nj+@9iZJfm7XoAwx{m!Nb)IbHR;o7#XI@$U2 z&{OK!=i1Y_yL(dMP(nK71E7W-$+l``-oxG99j@Eid#{5Qhu}n&@1UxKWv(4s74JWW$#iz7=i( z?^VqyDmAZ3yr~qZ3^4Nf@okBhEe*J3j#n;Tsq&ex-TJA;tV*DY2rlS z!=`jDFe~bP-ogr?6VhP%Q20&JY&Xk1)>zr2PDnl28Kr~v1FFSP*N~zKVc7we=`#c{ zaE(?2WL0k;Dxb}#0bKw{T>wbVfefbT*G*7d+ISR(Hh-=KR9G%A{63d5B4t%MYRU+e zh}+2KSnvH=c%{dNN|jlkuW6cg;6dNKnGU}>BxA&{5NeOslf@`0zy+%uI%&a9*yaD-hOzkoEO>!qoE;FrVujcyS>;&)rxQxg zRKuz#r+>I@KKA$cdw&|6DTZ}xb`l)_6u0HXmjy+Y6Dad_!z|VYpCPRpN}La%#pVgb z?JSZtU}8EI3r#B!$3Lp9I^}f6IaB>sZKrxVcc#k>^Nc$OA9aineCdW!GKfVsfWZVA zuOY9!tP~$& zZnnegsV!62nx;eK0#A;orG1{eds6NO+31Y}0s|Rz@3x%!>PnK8-%3^x)}MI9Bh5{c zDjlivIL^TOi=Z$}L35!Nce8Xhp9D!VMKqOai`2!9{DVUdQL4>6 zd+38u$k{{wbiZ_ilWSogG5iuY}IjcV*|R*3#F`fW5nE@a_b>Gv`$bWmiSL6mP0N+32pedHPN_3 zB-*FjriU9HQ1SJalVJSZhy-gDJp3P`xdbb)xPQ>%9^CR?$m`dxg34$jQ=;**{KuIP zAV7-tY0HREM+*q>hem)24nLt!5g10vA@H{YhGEod2gmPb2=EI$N$ROW5RcFLJpxqN z1n?7+G-?7zA22E#RJBq+1l$BwRh!g;lQ&bg_z+d=rU(%1TLdsswYHcYM@Q(xc@S&G z<)bMkN0UG$b?DPCsY&&~@@!_1^|oAph5fBF3dNPWOW2cbFFo zFJ3gNhv3Wb;>C+@#o#OwSkkQt$#940pi`v2ivVy?L8|b=& zFR%td*Y!VEX#m)KLZ(1Th|b)+)BL01r!Bt62uLO-&1$fE=}J3!L~`B|xfwx(|;x>Bj<_HXjC&O8bu;(1!mo+D$*z?{8z)#t{M_1_JD! zgZDN`9r!aud@J2E9&Zi0OkTQP+6M3W+BI+lq=Vb_bbGYE&Ip5qVbfs<$!1Ruc1!Np zE#^`ezB2lbga~LVb5&OUcoj!W-Ys0Hvv9GNwC2oq`~Vsoxtu`x2xvo#jlWZC3F!KF z>zR1#@KDkC^Rp@a7aqo2gFgLgj(~n`5{yk+OF-Re_SQ8m*f(n;)J(yDA(^K}_T7H@ z(w(7?MxDjqA*NQoKT8-_Nwjrt%ZQ~T$;(>k>FYa;@p4Owc0*bN7kD4snPWO zRaOC859ev|&IIEyHBQe_^=5t9g8OTOFzc%z0SW;T0aNMElb|-6m%4$SVs3b)+Z&&b z=ToBTYHG0iy*XP)RQkOK`)?936A)zv!-`;i`*R5qVCH1_bN#P46!|&uDRDmZz47-eU%heD~M^KOae3ljLRvRp|o2`-Zui)1nZap literal 0 HcmV?d00001 diff --git a/android-project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android-project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..22fb68532b3f65bce7b24fd4a5c0491cabeeb70e GIT binary patch literal 2592 zcmV+*3g7ioNk&E(3IG6CMM6+kP&iBr3IG5vU%(d-HPO|!?X>Ou+qP}nwr$(CZSRF` zk2$w(+qP}n-qBif%rP?l5mqCWHG)1>TK%!Abg5kB7}h$AsLm1CJOLg-DpWZI9)eZw zDt0=n+ugcGpW4P5*ml3$SXC<9s#Y00sZytqb6eZ-O!wHfr!qQJo{qI`+x`Le^E@xY zmvQphwryLnA&1tsLz3RMZQH8)ySnPHS$m`5Ze-iGGTXMj(Pp-7dm|eMjx_)TmLJ)+ zZM()^7cfrKYiw_Bs?*q-Y@440w~-XdS>9uYA22hs7W>n*P(Hb1m z`$FxyeK)x6u5}n$lFJxsQkV5V-Qyz>4~ux1qC?VI{|kHR=&A-%i?U2+I-AD-PxqyH zh<;&&{_wE)OM}jyRXPLBA4ExHGZ|SjFidA3!!Z5kA^MMK(M=c;zcUj-GM>zluo@F0 zArZ~P!m2bzs(ukoIt7Vq*iGAYjs#{k;(0Q4v6P4CEy9raM;GrsmP*Op1xicQF|vdJ zAQ;tFY`}>4LMN$Il~>>Ssm@!80YE5{Io(Jrsg&G{&zwltLBs$c9M+a>rj?Xx{?%Jv zxJPORPeDLDQ&wEe=n zRZhFALec|*l`ww8Clv%29wSunlq5ACrF)-G&Job`8mxxbv15>O@y`u$6m{$Aq+!_x zg#?Z4Oco+815@F)Bp`|<2PKpkdWQ`Zg=M_vhh?I8u>6GAyk@ROW$SSqHzBGrZ?o!! z@~YhqBeJ9MUNglZsel=Am-@+$krR`UACKV?$|pX82+BwcoLBe&0IMx_Uh~7AcT*Om+^?W_STCNg1kwqWiF;#|-$&Beuvxi; zXUZo&Rw41Btc1wHq}9wjs!L}N<05(-$93-PEgMyWhzb{po)JBz zLAl^5DR9W-=d2o6tkR)DI%95s&>HddL0a||LbvB)soZ=^6RM-pXyr<2*A2E495*`Xl#jC`4W(3-xv20_$v84cunBsBMYkYxHt2xV3ZjIt&m0<8K< z74hUUgv@@iU|FRDa?uA0kc_V_F)YifeW{|+rFOGO7GXd#knfgI;E>rn3oC#H6_KSl zu5HUWe8Ndk!_Zu)#=%IA};+eSmV3QI-4H^pES1&K_v>IJwa zI~kf=KVvB&Hy{ycMI0n3pT?ou#?CYje85taLc3JZ1xb`ErvGEmL`A>hmI_HK2*qwM zQQiFyOIgZ$hfi0QLZ&L;`@$0Q@23QfaY2A;MLI#9hg9D+-nBJ_z!z0XZ>fwhIJImq{UB8%eZ2W^I}#ISi8e#XE46 zHOlW*SdxSFzE7Pm9-on_Uhyu}8R3a2C~84cSeENKP-l>xLs0`KsJcb_tV(Rg*zdaZ z`dyur&?(s#dQH5P#CypIk&vjvFukJ_adr9LLs77u*lJSx2Mw8-D1M7Z|I0`x5}AGN zW7>Qv4|2yaLsY=BawGsGCl;9bEJVb^;x~rHe|o?${XZr>!wVOeURY#Lh8O@OSZp_S zh-%1j7D64vN5ap0S+MvAz4tIee~#HUo?JLJ*=y(lfJ6`VxhP_{+O9(a0OUKV@{K2y zN~b%b$7qmrI+hnlUp%u{ CMyn_Q literal 0 HcmV?d00001 diff --git a/android-project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android-project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..043c7aa724cb5e6f8bcef0079596dbb1069b90c3 GIT binary patch literal 5142 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 literal 0 HcmV?d00001 diff --git a/android-project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android-project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..baea92cd1b8d472c20fb631936d8df0c36c70b07 GIT binary patch literal 3652 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

3YbVf`1%j2cvRvvYDv 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 I$uHm{87xDr3jhEB literal 0 HcmV?d00001 diff --git a/android-project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android-project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..320691a8c4224b1014f068f0e33415b758e10128 GIT binary patch literal 10746 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 + + #39A0D9 + \ No newline at end of file diff --git a/android-project/app/src/main/res/values/strings.xml b/android-project/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..85a6dca0 --- /dev/null +++ b/android-project/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Lego Island + diff --git a/android-project/app/src/main/res/values/styles.xml b/android-project/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..ff6c9d2c --- /dev/null +++ b/android-project/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/android-project/build.gradle b/android-project/build.gradle new file mode 100644 index 00000000..586a2bbd --- /dev/null +++ b/android-project/build.gradle @@ -0,0 +1,4 @@ +plugins { + id 'com.android.application' version '8.7.0' apply false + id 'com.android.library' version '8.7.0' apply false +} diff --git a/android-project/downloadSDL3.cmake b/android-project/downloadSDL3.cmake new file mode 100644 index 00000000..77479e86 --- /dev/null +++ b/android-project/downloadSDL3.cmake @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR) + +include(FetchContent) + +set(FETCHCONTENT_BASE_DIR "build/_deps") + +FetchContent_Populate( + SDL3 + GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git" + GIT_TAG "main" + SOURCE_DIR "build/_deps/sdl3-src" + BINARY_DIR "build/_deps/sdl3-build" +) diff --git a/android-project/gradle.properties b/android-project/gradle.properties new file mode 100644 index 00000000..d5e8af31 --- /dev/null +++ b/android-project/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/android-project/gradle/wrapper/gradle-wrapper.jar b/android-project/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ zw`1S}(D?mUXu_C{wupRS-jiX~w=Uqqhf|Vb3Cm9L=T+w91Cu^ z*&Ty%sN?x*h~mJc4g~k{xD4ZmF%FXZNC;oVDwLZ_WvrnzY|{v8hc1nmx4^}Z;yriXsAf+Lp+OFLbR!&Ox?xABwl zu8w&|5pCxmu#$?Cv2_-Vghl2LZ6m7}VLEfR5o2Ou$x02uA-%QB2$c(c1rH3R9hesc zfpn#oqpbKuVsdfV#cv@5pV4^f_!WS+F>SV6N0JQ9E!T90EX((_{bSSFv9ld%I0&}9 zH&Jd4MEX1e0iqDtq~h?DBrxQX1iI0lIs<|kB$Yrh&cpeK0-^K%=FBsCBT46@h#yi!AyDq1V(#V}^;{{V*@T4WJ&U-NTq43w=|K>z8%pr_nC>%C(Wa_l78Ufib$r8Od)IIN=u>417 z`Hl{9A$mI5A(;+-Q&$F&h-@;NR>Z<2U;Y21>>Z;s@0V@SbkMQQj%_;~+qTuQ?c|AV zcWm3XZQHhP&R%QWarS%mJ!9R^&!_)*s(v+VR@I#QrAT}`17Y+l<`b-nvmDNW`De%y zrwTZ9EJrj1AFA>B`1jYDow}~*dfPs}IZMO3=a{Fy#IOILc8F0;JS4x(k-NSpbN@qM z`@aE_e}5{!$v3+qVs7u?sOV(y@1Os*Fgu`fCW9=G@F_#VQ%xf$hj0~wnnP0$hFI+@ zkQj~v#V>xn)u??YutKsX>pxKCl^p!C-o?+9;!Nug^ z{rP!|+KsP5%uF;ZCa5F;O^9TGac=M|=V z_H(PfkV1rz4jl?gJ(ArXMyWT4y(86d3`$iI4^l9`vLdZkzpznSd5Ikfrs8qcSy&>z zTIZgWZGXw0n9ibQxYWE@gI0(3#KA-dAdPcsL_|hg2@~C!VZDM}5;v_Nykfq!*@*Zf zE_wVgx82GMDryKO{U{D>vSzSc%B~|cjDQrt5BN=Ugpsf8H8f1lR4SGo#hCuXPL;QQ z#~b?C4MoepT3X`qdW2dNn& zo8)K}%Lpu>0tQei+{>*VGErz|qjbK#9 zvtd8rcHplw%YyQCKR{kyo6fgg!)6tHUYT(L>B7er5)41iG`j$qe*kSh$fY!PehLcD zWeKZHn<492B34*JUQh=CY1R~jT9Jt=k=jCU2=SL&&y5QI2uAG2?L8qd2U(^AW#{(x zThSy=C#>k+QMo^7caQcpU?Qn}j-`s?1vXuzG#j8(A+RUAY})F@=r&F(8nI&HspAy4 z4>(M>hI9c7?DCW8rw6|23?qQMSq?*Vx?v30U%luBo)B-k2mkL)Ljk5xUha3pK>EEj z@(;tH|M@xkuN?gsz;*bygizwYR!6=(Xgcg^>WlGtRYCozY<rFX2E>kaZo)O<^J7a`MX8Pf`gBd4vrtD|qKn&B)C&wp0O-x*@-|m*0egT=-t@%dD zgP2D+#WPptnc;_ugD6%zN}Z+X4=c61XNLb7L1gWd8;NHrBXwJ7s0ce#lWnnFUMTR& z1_R9Fin4!d17d4jpKcfh?MKRxxQk$@)*hradH2$3)nyXep5Z;B z?yX+-Bd=TqO2!11?MDtG0n(*T^!CIiF@ZQymqq1wPM_X$Iu9-P=^}v7npvvPBu!d$ z7K?@CsA8H38+zjA@{;{kG)#AHME>Ix<711_iQ@WWMObXyVO)a&^qE1GqpP47Q|_AG zP`(AD&r!V^MXQ^e+*n5~Lp9!B+#y3#f8J^5!iC@3Y@P`;FoUH{G*pj*q7MVV)29+j z>BC`a|1@U_v%%o9VH_HsSnM`jZ-&CDvbiqDg)tQEnV>b%Ptm)T|1?TrpIl)Y$LnG_ zzKi5j2Fx^K^PG1=*?GhK;$(UCF-tM~^=Z*+Wp{FSuy7iHt9#4n(sUuHK??@v+6*|10Csdnyg9hAsC5_OrSL;jVkLlf zHXIPukLqbhs~-*oa^gqgvtpgTk_7GypwH><53riYYL*M=Q@F-yEPLqQ&1Sc zZB%w}T~RO|#jFjMWcKMZccxm-SL)s_ig?OC?y_~gLFj{n8D$J_Kw%{r0oB8?@dWzn zB528d-wUBQzrrSSLq?fR!K%59Zv9J4yCQhhDGwhptpA5O5U?Hjqt>8nOD zi{)0CI|&Gu%zunGI*XFZh(ix)q${jT8wnnzbBMPYVJc4HX*9d^mz|21$=R$J$(y7V zo0dxdbX3N#=F$zjstTf*t8vL)2*{XH!+<2IJ1VVFa67|{?LP&P41h$2i2;?N~RA30LV`BsUcj zfO9#Pg1$t}7zpv#&)8`mis3~o+P(DxOMgz-V*(?wWaxi?R=NhtW}<#^Z?(BhSwyar zG|A#Q7wh4OfK<|DAcl9THc-W4*>J4nTevsD%dkj`U~wSUCh15?_N@uMdF^Kw+{agk zJ`im^wDqj`Ev)W3k3stasP`88-M0ZBs7;B6{-tSm3>I@_e-QfT?7|n0D~0RRqDb^G zyHb=is;IwuQ&ITzL4KsP@Z`b$d%B0Wuhioo1CWttW8yhsER1ZUZzA{F*K=wmi-sb#Ju+j z-l@In^IKnb{bQG}Ps>+Vu_W#grNKNGto+yjA)?>0?~X`4I3T@5G1)RqGUZuP^NJCq&^HykuYtMDD8qq+l8RcZNJsvN(10{ zQ1$XcGt}QH-U^WU!-wRR1d--{B$%vY{JLWIV%P4-KQuxxDeJaF#{eu&&r!3Qu{w}0f--8^H|KwE>)ORrcR+2Qf zb})DRcH>k0zWK8@{RX}NYvTF;E~phK{+F;MkIP$)T$93Ba2R2TvKc>`D??#mv9wg$ zd~|-`Qx5LwwsZ2hb*Rt4S9dsF%Cny5<1fscy~)d;0m2r$f=83<->c~!GNyb!U)PA; zq^!`@@)UaG)Ew(9V?5ZBq#c%dCWZrplmuM`o~TyHjAIMh0*#1{B>K4po-dx$Tk-Cq z=WZDkP5x2W&Os`N8KiYHRH#UY*n|nvd(U>yO=MFI-2BEp?x@=N<~CbLJBf6P)}vLS?xJXYJ2^<3KJUdrwKnJnTp{ zjIi|R=L7rn9b*D#Xxr4*R<3T5AuOS+#U8hNlfo&^9JO{VbH!v9^JbK=TCGR-5EWR@ zN8T-_I|&@A}(hKeL4_*eb!1G8p~&_Im8|wc>Cdir+gg90n1dw?QaXcx6Op_W1r=axRw>4;rM*UOpT#Eb9xU1IiWo@h?|5uP zka>-XW0Ikp@dIe;MN8B01a7+5V@h3WN{J=HJ*pe0uwQ3S&MyWFni47X32Q7SyCTNQ z+sR!_9IZa5!>f&V$`q!%H8ci!a|RMx5}5MA_kr+bhtQy{-^)(hCVa@I!^TV4RBi zAFa!Nsi3y37I5EK;0cqu|9MRj<^r&h1lF}u0KpKQD^5Y+LvFEwM zLU@@v4_Na#Axy6tn3P%sD^5P#<7F;sd$f4a7LBMk zGU^RZHBcxSA%kCx*eH&wgA?Qwazm8>9SCSz_!;MqY-QX<1@p$*T8lc?@`ikEqJ>#w zcG``^CoFMAhdEXT9qt47g0IZkaU)4R7wkGs^Ax}usqJ5HfDYAV$!=6?>J6+Ha1I<5 z|6=9soU4>E))tW$<#>F ziZ$6>KJf0bPfbx_)7-}tMINlc=}|H+$uX)mhC6-Hz+XZxsKd^b?RFB6et}O#+>Wmw9Ec9) z{q}XFWp{3@qmyK*Jvzpyqv57LIR;hPXKsrh{G?&dRjF%Zt5&m20Ll?OyfUYC3WRn{cgQ?^V~UAv+5 z&_m#&nIwffgX1*Z2#5^Kl4DbE#NrD&Hi4|7SPqZ}(>_+JMz=s|k77aEL}<=0Zfb)a z%F(*L3zCA<=xO)2U3B|pcTqDbBoFp>QyAEU(jMu8(jLA61-H!ucI804+B!$E^cQQa z)_ERrW3g!B9iLb3nn3dlkvD7KsY?sRvls3QC0qPi>o<)GHx%4Xb$5a3GBTJ(k@`e@ z$RUa^%S15^1oLEmA=sayrP5;9qtf!Z1*?e$ORVPsXpL{jL<6E)0sj&swP3}NPmR%FM?O>SQgN5XfHE< zo(4#Cv11(%Nnw_{_Ro}r6=gKd{k?NebJ~<~Kv0r(r0qe4n3LFx$5%x(BKvrz$m?LG zjLIc;hbj0FMdb9aH9Lpsof#yG$(0sG2%RL;d(n>;#jb!R_+dad+K;Ccw!|RY?uS(a zj~?=&M!4C(5LnlH6k%aYvz@7?xRa^2gml%vn&eKl$R_lJ+e|xsNfXzr#xuh(>`}9g zLHSyiFwK^-p!;p$yt7$F|3*IfO3Mlu9e>Dpx8O`37?fA`cj`C0B-m9uRhJjs^mRp# zWB;Aj6|G^1V6`jg7#7V9UFvnB4((nIwG?k%c7h`?0tS8J3Bn0t#pb#SA}N-|45$-j z$R>%7cc2ebAClXc(&0UtHX<>pd)akR3Kx_cK+n<}FhzmTx!8e9^u2e4%x{>T6pQ`6 zO182bh$-W5A3^wos0SV_TgPmF4WUP-+D25KjbC{y_6W_9I2_vNKwU(^qSdn&>^=*t z&uvp*@c8#2*paD!ZMCi3;K{Na;I4Q35zw$YrW5U@Kk~)&rw;G?d7Q&c9|x<Hg|CNMsxovmfth*|E*GHezPTWa^Hd^F4!B3sF;)? z(NaPyAhocu1jUe(!5Cy|dh|W2=!@fNmuNOzxi^tE_jAtzNJ0JR-avc_H|ve#KO}#S z#a(8secu|^Tx553d4r@3#6^MHbH)vmiBpn0X^29xEv!Vuh1n(Sr5I0V&`jA2;WS|Y zbf0e}X|)wA-Pf5gBZ>r4YX3Mav1kKY(ulAJ0Q*jB)YhviHK)w!TJsi3^dMa$L@^{` z_De`fF4;M87vM3Ph9SzCoCi$#Fsd38u!^0#*sPful^p5oI(xGU?yeYjn;Hq1!wzFk zG&2w}W3`AX4bxoVm03y>ts{KaDf!}b&7$(P4KAMP=vK5?1In^-YYNtx1f#}+2QK@h zeSeAI@E6Z8a?)>sZ`fbq9_snl6LCu6g>o)rO;ijp3|$vig+4t} zylEo7$SEW<_U+qgVcaVhk+4k+C9THI5V10qV*dOV6pPtAI$)QN{!JRBKh-D zk2^{j@bZ}yqW?<#VVuI_27*cI-V~sJiqQv&m07+10XF+#ZnIJdr8t`9s_EE;T2V;B z4UnQUH9EdX%zwh-5&wflY#ve!IWt0UE-My3?L#^Bh%kcgP1q{&26eXLn zTkjJ*w+(|_>Pq0v8{%nX$QZbf)tbJaLY$03;MO=Ic-uqYUmUCuXD>J>o6BCRF=xa% z3R4SK9#t1!K4I_d>tZgE>&+kZ?Q}1qo4&h%U$GfY058s%*=!kac{0Z+4Hwm!)pFLR zJ+5*OpgWUrm0FPI2ib4NPJ+Sk07j(`diti^i#kh&f}i>P4~|d?RFb#!JN)~D@)beox}bw?4VCf^y*`2{4`-@%SFTry2h z>9VBc9#JxEs1+0i2^LR@B1J`B9Ac=#FW=(?2;5;#U$0E0UNag_!jY$&2diQk_n)bT zl5Me_SUvqUjwCqmVcyb`igygB_4YUB*m$h5oeKv3uIF0sk}~es!{D>4r%PC*F~FN3owq5e0|YeUTSG#Vq%&Gk7uwW z0lDo#_wvflqHeRm*}l?}o;EILszBt|EW*zNPmq#?4A+&i0xx^?9obLyY4xx=Y9&^G;xYXYPxG)DOpPg!i_Ccl#3L}6xAAZzNhPK1XaC_~ z!A|mlo?Be*8Nn=a+FhgpOj@G7yYs(Qk(8&|h@_>w8Y^r&5nCqe0V60rRz?b5%J;GYeBqSAjo|K692GxD4` zRZyM2FdI+-jK2}WAZTZ()w_)V{n5tEb@>+JYluDozCb$fA4H)$bzg(Ux{*hXurjO^ zwAxc+UXu=&JV*E59}h3kzQPG4M)X8E*}#_&}w*KEgtX)cU{vm9b$atHa;s>| z+L6&cn8xUL*OSjx4YGjf6{Eq+Q3{!ZyhrL&^6Vz@jGbI%cAM9GkmFlamTbcQGvOlL zmJ?(FI)c86=JEs|*;?h~o)88>12nXlpMR4@yh%qdwFNpct;vMlc=;{FSo*apJ;p}! zAX~t;3tb~VuP|ZW;z$=IHf->F@Ml)&-&Bnb{iQyE#;GZ@C$PzEf6~q}4D>9jic@mTO5x76ulDz@+XAcm35!VSu zT*Gs>;f0b2TNpjU_BjHZ&S6Sqk6V1370+!eppV2H+FY!q*n=GHQ!9Rn6MjY!Jc77A zG7Y!lFp8?TIHN!LXO?gCnsYM-gQxsm=Ek**VmZu7vnuufD7K~GIxfxbsQ@qv2T zPa`tvHB$fFCyZl>3oYg?_wW)C>^_iDOc^B7klnTOoytQH18WkOk)L2BSD0r%xgRSW zQS9elF^?O=_@|58zKLK;(f77l-Zzu}4{fXed2saq!5k#UZAoDBqYQS{sn@j@Vtp|$ zG%gnZ$U|9@u#w1@11Sjl8ze^Co=)7yS(}=;68a3~g;NDe_X^}yJj;~s8xq9ahQ5_r zxAlTMnep*)w1e(TG%tWsjo3RR;yVGPEO4V{Zp?=a_0R#=V^ioQu4YL=BO4r0$$XTX zZfnw#_$V}sDAIDrezGQ+h?q24St0QNug_?{s-pI(^jg`#JRxM1YBV;a@@JQvH8*>> zIJvku74E0NlXkYe_624>znU0J@L<-c=G#F3k4A_)*;ky!C(^uZfj%WB3-*{*B$?9+ zDm$WFp=0(xnt6`vDQV3Jl5f&R(Mp};;q8d3I%Kn>Kx=^;uSVCw0L=gw53%Bp==8Sw zxtx=cs!^-_+i{2OK`Q;913+AXc_&Z5$@z3<)So0CU3;JAv=H?@Zpi~riQ{z-zLtVL z!oF<}@IgJp)Iyz1zVJ42!SPHSkjYNS4%ulVVIXdRuiZ@5Mx8LJS}J#qD^Zi_xQ@>DKDr-_e#>5h3dtje*NcwH_h;i{Sx7}dkdpuW z(yUCjckQsagv*QGMSi9u1`Z|V^}Wjf7B@q%j2DQXyd0nOyqg%m{CK_lAoKlJ7#8M} z%IvR?Vh$6aDWK2W!=i?*<77q&B8O&3?zP(Cs@kapc)&p7En?J;t-TX9abGT#H?TW? ztO5(lPKRuC7fs}zwcUKbRh=7E8wzTsa#Z{a`WR}?UZ%!HohN}d&xJ=JQhpO1PI#>X zHkb>pW04pU%Bj_mf~U}1F1=wxdBZu1790>3Dm44bQ#F=T4V3&HlOLsGH)+AK$cHk6 zia$=$kog?)07HCL*PI6}DRhpM^*%I*kHM<#1Se+AQ!!xyhcy6j7`iDX7Z-2i73_n# zas*?7LkxS-XSqv;YBa zW_n*32D(HTYQ0$feV_Fru1ZxW0g&iwqixPX3=9t4o)o|kOo79V$?$uh?#8Q8e>4e)V6;_(x&ViUVxma+i25qea;d-oK7ouuDsB^ab{ zu1qjQ%`n56VtxBE#0qAzb7lph`Eb-}TYpXB!H-}3Ykqyp`otprp7{VEuW*^IR2n$Fb99*nAtqT&oOFIf z@w*6>YvOGw@Ja?Pp1=whZqydzx@9X4n^2!n83C5{C?G@|E?&$?p*g68)kNvUTJ)I6 z1Q|(#UuP6pj78GUxq11m-GSszc+)X{C2eo-?8ud9sB=3(D47v?`JAa{V(IF zPZQ_0AY*9M97>Jf<o%#O_%Wq}8>YM=q0|tGY+hlXcpE=Z4Od z`NT7Hu2hnvRoqOw@g1f=bv`+nba{GwA$Ak0INlqI1k<9!x_!sL()h?hEWoWrdU3w` zZ%%)VR+Bc@_v!C#koM1p-3v_^L6)_Ktj4HE>aUh%2XZE@JFMOn)J~c`_7VWNb9c-N z2b|SZMR4Z@E7j&q&9(6H3yjEu6HV7{2!1t0lgizD;mZ9$r(r7W5G$ky@w(T_dFnOD z*p#+z$@pKE+>o@%eT(2-p_C}wbQ5s(%Sn_{$HDN@MB+Ev?t@3dPy`%TZ!z}AThZSu zN<1i$siJhXFdjV zP*y|V<`V8t=h#XTRUR~5`c`Z9^-`*BZf?WAehGdg)E2Je)hqFa!k{V(u+(hTf^Yq& zoruUh2(^3pe)2{bvt4&4Y9CY3js)PUHtd4rVG57}uFJL)D(JfSIo^{P=7liFXG zq5yqgof0V8paQcP!gy+;^pp-DA5pj=gbMN0eW=-eY+N8~y+G>t+x}oa!5r>tW$xhI zPQSv=pi;~653Gvf6~*JcQ%t1xOrH2l3Zy@8AoJ+wz@daW@m7?%LXkr!bw9GY@ns3e zSfuWF_gkWnesv?s3I`@}NgE2xwgs&rj?kH-FEy82=O8`+szN ziHch`vvS`zNfap14!&#i9H@wF7}yIPm=UB%(o(}F{wsZ(wA0nJ2aD^@B41>>o-_U6 zUqD~vdo48S8~FTb^+%#zcbQiiYoDKYcj&$#^;Smmb+Ljp(L=1Kt_J!;0s%1|JK}Wi z;={~oL!foo5n8=}rs6MmUW~R&;SIJO3TL4Ky?kh+b2rT9B1Jl4>#Uh-Bec z`Hsp<==#UEW6pGPhNk8H!!DUQR~#F9jEMI6T*OWfN^Ze&X(4nV$wa8QUJ>oTkruH# zm~O<`J7Wxseo@FqaZMl#Y(mrFW9AHM9Kb|XBMqaZ2a)DvJgYipkDD_VUF_PKd~dT7 z#02}bBfPn9a!X!O#83=lbJSK#E}K&yx-HI#T6ua)6o0{|={*HFusCkHzs|Fn&|C3H zBck1cmfcWVUN&i>X$YU^Sn6k2H;r3zuXbJFz)r5~3$d$tUj(l1?o={MM){kjgqXRO zc5R*#{;V7AQh|G|)jLM@wGAK&rm2~@{Pewv#06pHbKn#wL0P6F1!^qw9g&cW3Z=9} zj)POhOlwsh@eF=>z?#sIs*C-Nl(yU!#DaiaxhEs#iJqQ8w%(?+6lU02MYSeDkr!B- zPjMv+on6OLXgGnAtl(ao>|X2Y8*Hb}GRW5}-IzXnoo-d0!m4Vy$GS!XOLy>3_+UGs z2D|YcQx@M#M|}TDOetGi{9lGo9m-=0-^+nKE^*?$^uHkxZh}I{#UTQd;X!L+W@jm( zDg@N4+lUqI92o_rNk{3P>1gxAL=&O;x)ZT=q1mk0kLlE$WeWuY_$0`0jY-Kkt zP*|m3AF}Ubd=`<>(Xg0har*_@x2YH}bn0Wk*OZz3*e5;Zc;2uBdnl8?&XjupbkOeNZsNh6pvsq_ydmJI+*z**{I{0K)-;p1~k8cpJXL$^t!-`E}=*4G^-E8>H!LjTPxSx zcF+cS`ommfKMhNSbas^@YbTpH1*RFrBuATUR zt{oFWSk^$xU&kbFQ;MCX22RAN5F6eq9UfR$ut`Jw--p2YX)A*J69m^!oYfj2y7NYcH6&r+0~_sH^c^nzeN1AU4Ga7=FlR{S|Mm~MpzY0$Z+p2W(a={b-pR9EO1Rs zB%KY|@wLcAA@)KXi!d2_BxrkhDn`DT1=Dec}V!okd{$+wK z4E{n8R*xKyci1(CnNdhf$Dp2(Jpof0-0%-38X=Dd9PQgT+w%Lshx9+loPS~MOm%ZT zt%2B2iL_KU_ita%N>xjB!#71_3=3c}o zgeW~^U_ZTJQ2!PqXulQd=3b=XOQhwATK$y(9$#1jOQ4}4?~l#&nek)H(04f(Sr=s| zWv7Lu1=%WGk4FSw^;;!8&YPM)pQDCY9DhU`hMty1@sq1=Tj7bFsOOBZOFlpR`W>-J$-(kezWJj;`?x-v>ev{*8V z8p|KXJPV$HyQr1A(9LVrM47u-XpcrIyO`yWvx1pVYc&?154aneRpLqgx)EMvRaa#|9?Wwqs2+W8n5~79G z(}iCiLk;?enn}ew`HzhG+tu+Ru@T+K5juvZN)wY;x6HjvqD!&!)$$;1VAh~7fg0K| zEha#aN=Yv|3^~YFH}cc38ovVb%L|g@9W6fo(JtT6$fa?zf@Ct88e}m?i)b*Jgc{fl zExfdvw-BYDmH6>(4QMt#p0;FUIQqkhD}aH?a7)_%JtA~soqj{ppP_82yi9kaxuK>~ ze_)Zt>1?q=ZH*kF{1iq9sr*tVuy=u>Zev}!gEZx@O6-fjyu9X00gpIl-fS_pzjpqJ z1yqBmf9NF!jaF<+YxgH6oXBdK)sH(>VZ)1siyA$P<#KDt;8NT*l_0{xit~5j1P)FN zI8hhYKhQ)i z37^aP13B~u65?sg+_@2Kr^iWHN=U;EDSZ@2W2!5ALhGNWXnFBY%7W?1 z=HI9JzQ-pLKZDYTv<0-lt|6c-RwhxZ)mU2Os{bsX_i^@*fKUj8*aDO5pks=qn3Dv6 zwggpKLuyRCTVPwmw1r}B#AS}?X7b837UlXwp~E2|PJw2SGVueL7){Y&z!jL!XN=0i zU^Eig`S2`{+gU$68aRdWx?BZ{sU_f=8sn~>s~M?GU~`fH5kCc; z8ICp+INM3(3{#k32RZdv6b9MQYdZXNuk7ed8;G?S2nT+NZBG=Tar^KFl2SvhW$bGW#kdWL-I)s_IqVnCDDM9fm8g;P;8 z7t4yZn3^*NQfx7SwmkzP$=fwdC}bafQSEF@pd&P8@H#`swGy_rz;Z?Ty5mkS%>m#% zp_!m9e<()sfKiY(nF<1zBz&&`ZlJf6QLvLhl`_``%RW&{+O>Xhp;lwSsyRqGf=RWd zpftiR`={2(siiPAS|p}@q=NhVc0ELprt%=fMXO3B)4ryC2LT(o=sLM7hJC!}T1@)E zA3^J$3&1*M6Xq>03FX`R&w*NkrZE?FwU+Muut;>qNhj@bX17ZJxnOlPSZ=Zeiz~T_ zOu#yc3t6ONHB;?|r4w+pI)~KGN;HOGC)txxiUN8#mexj+W(cz%9a4sx|IRG=}ia zuEBuba3AHsV2feqw-3MvuL`I+2|`Ud4~7ZkN=JZ;L20|Oxna5vx1qbIh#k2O4$RQF zo`tL()zxaqibg^GbB+BS5#U{@K;WWQj~GcB1zb}zJkPwH|5hZ9iH2308!>_;%msji zJHSL~s)YHBR=Koa1mLEOHos*`gp=s8KA-C zu0aE+W!#iJ*0xqKm3A`fUGy#O+X+5W36myS>Uh2!R*s$aCU^`K&KKLCCDkejX2p=5 z%o7-fl03x`gaSNyr?3_JLv?2RLS3F*8ub>Jd@^Cc17)v8vYEK4aqo?OS@W9mt%ITJ z9=S2%R8M){CugT@k~~0x`}Vl!svYqX=E)c_oU6o}#Hb^%G1l3BudxA{F*tbjG;W_>=xV73pKY53v%>I)@D36I_@&p$h|Aw zonQS`07z_F#@T-%@-Tb|)7;;anoD_WH>9ewFy(ZcEOM$#Y)8>qi7rCnsH9GO-_7zF zu*C87{Df1P4TEOsnzZ@H%&lvV(3V@;Q!%+OYRp`g05PjY^gL$^$-t0Y>H*CDDs?FZly*oZ&dxvsxaUWF!{em4{A>n@vpXg$dwvt@_rgmHF z-MER`ABa8R-t_H*kv>}CzOpz;!>p^^9ztHMsHL|SRnS<-y5Z*r(_}c4=fXF`l^-i}>e7v!qs_jv zqvWhX^F=2sDNWA9c@P0?lUlr6ecrTKM%pNQ^?*Lq?p-0~?_j50xV%^(+H>sMul#Tw zeciF*1=?a7cI(}352%>LO96pD+?9!fNyl^9v3^v&Y4L)mNGK0FN43&Xf8jUlxW1Bw zyiu2;qW-aGNhs=zbuoxnxiwZ3{PFZM#Kw)9H@(hgX23h(`Wm~m4&TvoZoYp{plb^> z_#?vXcxd>r7K+1HKJvhed>gtK`TAbJUazUWQY6T~t2af%#<+Veyr%7-#*A#@&*;@g58{i|E%6yC_InGXCOd{L0;$)z#?n7M`re zh!kO{6=>7I?*}czyF7_frt#)s1CFJ_XE&VrDA?Dp3XbvF{qsEJgb&OLSNz_5g?HpK z9)8rsr4JN!Af3G9!#Qn(6zaUDqLN(g2g8*M)Djap?WMK9NKlkC)E2|-g|#-rp%!Gz zAHd%`iq|81efi93m3yTBw3g0j#;Yb2X{mhRAI?&KDmbGqou(2xiRNb^sV}%%Wu0?< z?($L>(#BO*)^)rSgyNRni$i`R4v;GhlCZ8$@e^ROX(p=2_v6Y!%^As zu022)fHdv_-~Yu_H6WVPLpHQx!W%^6j)cBhS`O3QBW#x(eX54d&I22op(N59b*&$v zFiSRY6rOc^(dgSV1>a7-5C;(5S5MvKcM2Jm-LD9TGqDpP097%52V+0>Xqq!! zq4e3vj53SE6i8J`XcQB|MZPP8j;PAOnpGnllH6#Ku~vS42xP*Nz@~y%db7Xi8s09P z1)e%8ys6&M8D=Dt6&t`iKG_4X=!kgRQoh%Z`dc&mlOUqXk-k`jKv9@(a^2-Upw>?< zt5*^DV~6Zedbec4NVl($2T{&b)zA@b#dUyd>`2JC0=xa_fIm8{5um zr-!ApXZhC8@=vC2WyxO|!@0Km)h8ep*`^he92$@YwP>VcdoS5OC^s38e#7RPsg4j+ zbVGG}WRSET&ZfrcR(x~k8n1rTP%CnfUNKUonD$P?FtNFF#cn!wEIab-;jU=B1dHK@ z(;(yAQJ`O$sMn>h;pf^8{JISW%d+@v6@CnXh9n5TXGC}?FI9i-D0OMaIg&mAg=0Kn zNJ7oz5*ReJukD55fUsMuaP+H4tDN&V9zfqF@ zr=#ecUk9wu{0;!+gl;3Bw=Vn^)z$ahVhhw)io!na&9}LmWurLb0zubxK=UEnU*{5P z+SP}&*(iBKSO4{alBHaY^)5Q=mZ+2OwIooJ7*Q5XJ+2|q`9#f?6myq!&oz?klihLq z4C)$XP!BNS0G_Z1&TM>?Jk{S~{F3n83ioli=IO6f%wkvCl(RFFw~j0tb{GvXTx>*sB0McY0s&SNvj4+^h`9nJ_wM>F!Uc>X}9PifQekn0sKI2SAJP!a4h z5cyGTuCj3ZBM^&{dRelIlT^9zcfaAuL5Y~bl!ppSf`wZbK$z#6U~rdclk``e+!qhe z6Qspo*%<)eu6?C;Bp<^VuW6JI|Ncvyn+LlSl;Mp22Bl7ARQ0Xc24%29(ZrdsIPw&-=yHQ7_Vle|5h>AST0 zUGX2Zk34vp?U~IHT|;$U86T+UUHl_NE4m|}>E~6q``7hccCaT^#y+?wD##Q%HwPd8 zV3x4L4|qqu`B$4(LXqDJngNy-{&@aFBvVsywt@X^}iH7P%>bR?ciC$I^U-4Foa`YKI^qDyGK7k%E%c_P=yzAi`YnxGA%DeNd++j3*h^ z=rn>oBd0|~lZ<6YvmkKY*ZJlJ;Im0tqgWu&E92eqt;+NYdxx`eS(4Hw_Jb5|yVvBg z*tbdY^!AN;luEyN4VRhS@-_DC{({ziH{&Z}iGElSV~qvT>L-8G%+yEL zX#MFOhj{InyKG=mvW-<1B@c-}x$vA(nU?>S>0*eN#!SLzQ)Ex7fvQ)S4D<8|I#N$3 zT5Ei`Z?cxBODHX8(Xp73v`IsAYC@9b;t}z0wxVuQSY1J^GRwDPN@qbM-ZF48T$GZ< z8WU+;Pqo?{ghI-KZ-i*ydXu`Ep0Xw^McH_KE9J0S7G;x8Fe`DVG?j3Pv=0YzJ}yZR z%2=oqHiUjvuk0~Ca>Kol4CFi0_xQT~;_F?=u+!kIDl-9g`#ZNZ9HCy17Ga1v^Jv9# z{T4Kb1-AzUxq*MutfOWWZgD*HnFfyYg0&e9f(5tZ>krPF6{VikNeHoc{linPPt#Si z&*g>(c54V8rT_AX!J&bNm-!umPvOR}vDai#`CX___J#=zeB*{4<&2WpaDncZsOkp* zsg<%@@rbrMkR_ux9?LsQxzoBa1s%$BBn6vk#{&&zUwcfzeCBJUwFYSF$08qDsB;gWQN*g!p8pxjofWbqNSZOEKOaTx@+* zwdt5*Q47@EOZ~EZL9s?1o?A%9TJT=Ob_13yyugvPg*e&ZU(r6^k4=2+D-@n=Hv5vu zSXG|hM(>h9^zn=eQ=$6`JO&70&2|%V5Lsx>)(%#;pcOfu>*nk_3HB_BNaH$`jM<^S zcSftDU1?nL;jy)+sfonQN}(}gUW?d_ikr*3=^{G)=tjBtEPe>TO|0ddVB zTklrSHiW+!#26frPXQQ(YN8DG$PZo?(po(QUCCf_OJC`pw*uey00%gmH!`WJkrKXj2!#6?`T25mTu9OJp2L8z3! z=arrL$ZqxuE{%yV)14Kd>k}j7pxZ6#$Dz8$@WV5p8kTqN<-7W)Q7Gt2{KoOPK_tZ| zf2WG~O5@{qPI+W<4f_;reuFVdO^5`ADC1!JQE|N`s3cq@(0WB!n0uh@*c{=LAd;~} zyGK@hbF-Oo+!nN)@i*O(`@FA#u?o=~e{`4O#5}z&=UkU*50fOrzi11D^&FOqe>wii z?*k+2|EcUs;Gx{!@KBT~>PAwLrIDT7Th=Utu?~?np@t^gFs?zgX=D${RwOY^WGh-+ z+#4$066ISh8eYW#FXWp~S`<*%O^ZuItL1Tyqt8#tZ zY120E;^VG`!lZn&3sPd$RkdHpU#|w+bYV)pJC|SH9g%|5IkxVTQcBA4CL0}$&}ef@ zW^Vtj%M;;_1xxP9x#ex17&4N*{ksO*_4O}xYu(p*JkL#yr}@7b)t5X?%CY<+s5_MJ zuiqt+N_;A(_)%lumoyRFixWa-M7qK_9s6<1X?JDa9fP!+_6u~~M$5L=ipB=7(j#f< zZ34J%=bs549%~_mA(|={uZNs_0?o7;-LBP(ZRnkd{-^|2|=4vUTmtByHL8 zEph`(LSEzQj68a+`d$V<45J7cyv^#|^|%fD#si1Nx!4NW*`l*{->HEWNh6-|g>-=r zXmQ|-i}Ku$ndUeHQ^&ieT!Lf}vf6GaqW9$DJ2NWrqwPY%%4nip$@vK$nRp*_C-v<| zuKz~ZyN&<%!NS26&x?jhy+@awJipMQ-8(X4#Ae5??U<1QMt1l9R=w9fAnEF}NYu$2 z>6}Vkc zIb*A?G*z8^IvibmBKn_u^5&T_1oey0gZS2~obf(#xk=erZGTEdQnt3DMGM+0oPwss zj5zXD;(oWhB_T@~Ig#9@v)AKtXu3>Inmgf@A|-lD-1U>cNyl3h?ADD9)GG4}zUGPk zZzaXe!~Kf?<~@$G?Uql3t8jy9{2!doq4=J}j9ktTxss{p6!9UdjyDERlA*xZ!=Q)KDs5O)phz>Vq3BNGoM(H|=1*Q4$^2fTZw z(%nq1P|5Rt81}SYJpEEzMPl5VJsV5&4e)ZWKDyoZ>1EwpkHx-AQVQc8%JMz;{H~p{=FXV>jIxvm4X*qv52e?Y-f%DJ zxEA165GikEASQ^fH6K#d!Tpu2HP{sFs%E=e$gYd$aj$+xue6N+Wc(rAz~wUsk2`(b z8Kvmyz%bKQxpP}~baG-rwYcYCvkHOi zlkR<=>ZBTU*8RF_d#Bl@zZsRIhx<%~Z@Z=ik z>adw3!DK(8R|q$vy{FTxw%#xliD~6qXmY^7_9kthVPTF~Xy1CfBqbU~?1QmxmU=+k z(ggxvEuA;0e&+ci-zQR{-f7aO{O(Pz_OsEjLh_K>MbvoZ4nxtk5u{g@nPv)cgW_R} z9}EA4K4@z0?7ue}Z(o~R(X&FjejUI2g~08PH1E4w>9o{)S(?1>Z0XMvTb|;&EuyOE zGvWNpYX)Nv<8|a^;1>bh#&znEcl-r!T#pn= z4$?Yudha6F%4b>*8@=BdtXXY4N+`U4Dmx$}>HeVJk-QdTG@t!tVT#0(LeV0gvqyyw z2sEp^9eY0N`u10Tm4n8No&A=)IeEC|gnmEXoNSzu!1<4R<%-9kY_8~5Ej?zRegMn78wuMs#;i&eUA0Zk_RXQ3b&TT} z;SCI=7-FUB@*&;8|n>(_g^HGf3@QODE3LpmX~ELnymQm{Sx9xrKS zK29p~?v@R$0=v6Dr5aW>-!{+h@?Q58|Kz8{{W`%J+lDAdb&M5VHrX_mDY;1-JLnf)ezmPau$)1;=`-FU=-r-83tX=C`S#}GZufju zQ>sXNT0Ny=k@nc%cFnvA_i4SC)?_ORXHq8B4D%el1uPX`c~uG#S1M7C+*MMqLw78E zhY2dI8@+N^qrMI1+;TUda(vGqGSRyU{Fnm`aqrr7bz42c5xsOO-~oZpkzorD1g}Y<6rk&3>PsSGy}W?MtqFky@A(X# zIuNZK0cK?^=;PUAu>j0#HtjbHCV*6?jzA&OoE$*Jlga*}LF`SF?WLhv1O|zqC<>*> zYB;#lsYKx0&kH@BFpW8n*yDcc6?;_zaJs<-jPSkCsSX-!aV=P5kUgF@Nu<{a%#K*F z134Q{9|YX7X(v$62_cY3^G%t~rD>Q0z@)1|zs)vjJ6Jq9;7#Ki`w+eS**En?7;n&7 zu==V3T&eFboN3ZiMx3D8qYc;VjFUk_H-WWCau(VFXSQf~viH0L$gwD$UfFHqNcgN`x}M+YQ6RnN<+@t>JUp#)9YOkqst-Ga?{FsDpEeX0(5v{0J~SEbWiL zXC2}M4?UH@u&|;%0y`eb33ldo4~z-x8zY!oVmV=c+f$m?RfDC35mdQ2E>Pze7KWP- z>!Bh<&57I+O_^s}9Tg^k)h7{xx@0a0IA~GAOt2yy!X%Q$1rt~LbTB6@Du!_0%HV>N zlf)QI1&gvERKwso23mJ!Ou6ZS#zCS5W`gxE5T>C#E|{i<1D35C222I33?Njaz`On7 zi<+VWFP6D{e-{yiN#M|Jgk<44u1TiMI78S5W`Sdb5f+{zu34s{CfWN7a3Cf^@L%!& zN$?|!!9j2c)j$~+R6n#891w-z8(!oBpL2K=+%a$r2|~8-(vQj5_XT`<0Ksf;oP+tz z9CObS!0m)Tgg`K#xBM8B(|Z)Wb&DYL{WTYv`;A=q6~Nnx2+!lTIXtj8J7dZE!P_{z z#f8w6F}^!?^KE#+ZDv+xd5O&3EmomZzsv?>E-~ygGum45fk!SBN&|eo1rKw^?aZJ4 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/android-project/gradle/wrapper/gradle-wrapper.properties b/android-project/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..8c558f26 --- /dev/null +++ b/android-project/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/android-project/gradlew b/android-project/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/android-project/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/android-project/gradlew.bat b/android-project/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/android-project/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android-project/settings.gradle b/android-project/settings.gradle new file mode 100644 index 00000000..36e93e45 --- /dev/null +++ b/android-project/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "legoisland" +include ':app' diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index 615832cf..452d5144 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -44,7 +44,7 @@ if(NOT WINDOWS_STORE) message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL") endif() - find_library(OPENGL_ES3_LIBRARY NAMES GLESv2) + find_library(OPENGL_ES3_LIBRARY NAMES GLESv3 GLESv2) if(EMSCRIPTEN OR OPENGL_ES3_LIBRARY) message(STATUS "Found OpenGL: enabling OpenGL ES 3.x renderer") target_sources(miniwin PRIVATE src/d3drm/backends/opengles3/renderer.cpp) diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..9bee1096 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +includeBuild('android-project') From 72e2848876c314f4e022277e46736fa71763ae54 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 15 Aug 2025 17:16:21 +0200 Subject: [PATCH 3/6] Update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 95237c85..4e7eaef0 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Please note: this project is primarily dedicated to achieving platform independe | Nintendo 3DS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | Xbox One | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | iOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | +| Android | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | We are actively working to support more platforms. If you have experience with a particular platform, we encourage you to contribute to `isle-portable`. You can find a [list of ongoing efforts](https://github.com/isledecomp/isle-portable/wiki/Work%E2%80%90in%E2%80%90progress-ports) in our Wiki. From 94c2d1618002f4b7083ae9da9cd23e329706e323 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 15 Aug 2025 18:26:06 +0200 Subject: [PATCH 4/6] Add OpenGL ES 2.0 renderer (#678) --- README.md | 2 +- miniwin/CMakeLists.txt | 12 + .../src/d3drm/backends/opengles2/renderer.cpp | 898 ++++++++++++++++++ miniwin/src/d3drm/d3drmrenderer.cpp | 11 + .../src/internal/d3drmrenderer_opengles2.h | 128 +++ 5 files changed, 1050 insertions(+), 1 deletion(-) create mode 100644 miniwin/src/d3drm/backends/opengles2/renderer.cpp create mode 100644 miniwin/src/internal/d3drmrenderer_opengles2.h diff --git a/README.md b/README.md index 4e7eaef0..a6bd8eff 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl | WinMM, DirectSound (Audio) | [SDL3](https://www.libsdl.org/), [miniaudio](https://miniaud.io/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aaudio%5D%22&type=code) | | DirectDraw (2D video) | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A2d%5D%22&type=code) | | [Smacker](https://github.com/isledecomp/isle/tree/master/3rdparty/smacker) | [libsmacker](https://github.com/foxtacles/libsmacker) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable%20%22%2F%2F%20%5Blibrary%3Alibsmacker%5D%22&type=code) | -| Direct3D (3D video) | [SDL3 (Vulkan, Metal, D3D12)](https://www.libsdl.org/), D3D9, OpenGL, OpenGL ES, Software | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A3d%5D%22&type=code) | +| Direct3D (3D video) | [SDL3 (Vulkan, Metal, D3D12)](https://www.libsdl.org/), D3D9, OpenGL 1.1, OpenGL ES 2.0, OpenGL ES 3.0, Software | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A3d%5D%22&type=code) | | Direct3D Retained Mode | Custom re-implementation | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aretained%5D%22&type=code) | | [SmartHeap](https://github.com/isledecomp/isle/tree/master/3rdparty/smartheap) | Default memory allocator | - | - | diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index 452d5144..07f40b04 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -44,6 +44,18 @@ if(NOT WINDOWS_STORE) message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL") endif() + find_library(OPENGL_ES2_LIBRARY NAMES GLESv2) + if(EMSCRIPTEN OR OPENGL_ES2_LIBRARY) + message(STATUS "Found OpenGL: enabling OpenGL ES 2.x renderer") + target_sources(miniwin PRIVATE src/d3drm/backends/opengles2/renderer.cpp) + list(APPEND GRAPHICS_BACKENDS USE_OPENGLES2) + if(OPENGL_ES2_LIBRARY) + target_link_libraries(miniwin PRIVATE ${OPENGL_ES2_LIBRARY}) + endif() + else() + message(STATUS "🧩 OpenGL ES 2.x support not enabled") + endif() + find_library(OPENGL_ES3_LIBRARY NAMES GLESv3 GLESv2) if(EMSCRIPTEN OR OPENGL_ES3_LIBRARY) message(STATUS "Found OpenGL: enabling OpenGL ES 3.x renderer") diff --git a/miniwin/src/d3drm/backends/opengles2/renderer.cpp b/miniwin/src/d3drm/backends/opengles2/renderer.cpp new file mode 100644 index 00000000..2b46190b --- /dev/null +++ b/miniwin/src/d3drm/backends/opengles2/renderer.cpp @@ -0,0 +1,898 @@ +#include "d3drmrenderer_opengles2.h" +#include "meshutils.h" + +#include +#include +#include +#include +#include + +static GLuint CompileShader(GLenum type, const char* source) +{ + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLint logLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + std::vector log(logLength); + glGetShaderInfoLog(shader, logLength, nullptr, log.data()); + SDL_Log("Shader compile error: %s", log.data()); + } + else { + SDL_Log("CompileShader (%s)", SDL_GetError()); + } + glDeleteShader(shader); + return 0; + } + return shader; +} + +struct SceneLightGLES2 { + float color[4]; + float position[4]; + float direction[4]; +}; + +Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height, float anisotropic) +{ + // We have to reset the attributes here after having enumerated the + // OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE + // call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH. + SDL_GL_ResetAttributes(); + // But ResetAttributes resets it to 16. + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + + if (!DDWindow) { + SDL_Log("No window handler"); + return nullptr; + } + + SDL_GLContext context = SDL_GL_CreateContext(DDWindow); + if (!context) { + SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError()); + return nullptr; + } + + if (!SDL_GL_MakeCurrent(DDWindow, context)) { + SDL_GL_DestroyContext(context); + return nullptr; + } + + glDepthFunc(GL_LEQUAL); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CW); + + const char* vertexShaderSource = R"( + attribute vec3 a_position; + attribute vec3 a_normal; + attribute vec2 a_texCoord; + + uniform mat4 u_modelViewMatrix; + uniform mat3 u_normalMatrix; + uniform mat4 u_projectionMatrix; + + varying vec3 v_viewPos; + varying vec3 v_normal; + varying vec2 v_texCoord; + + void main() { + vec4 viewPos = u_modelViewMatrix * vec4(a_position, 1.0); + gl_Position = u_projectionMatrix * viewPos; + v_viewPos = viewPos.xyz; + v_normal = normalize(u_normalMatrix * a_normal); + v_texCoord = a_texCoord; + } + )"; + + const char* fragmentShaderSource = R"( + precision mediump float; + + struct SceneLight { + vec4 color; + vec4 position; + vec4 direction; + }; + + uniform SceneLight u_lights[3]; + uniform int u_lightCount; + + varying vec3 v_viewPos; + varying vec3 v_normal; + varying vec2 v_texCoord; + + uniform float u_shininess; + uniform vec4 u_color; + uniform int u_useTexture; + uniform sampler2D u_texture; + + void main() { + vec3 diffuse = vec3(0.0); + vec3 specular = vec3(0.0); + + for (int i = 0; i < 3; ++i) { + if (i >= u_lightCount) break; + + vec3 lightColor = u_lights[i].color.rgb; + + if (u_lights[i].position.w == 0.0 && u_lights[i].direction.w == 0.0) { + diffuse += lightColor; + continue; + } + + vec3 lightVec; + if (u_lights[i].direction.w == 1.0) { + lightVec = -normalize(u_lights[i].direction.xyz); + } + else { + lightVec = u_lights[i].position.xyz - v_viewPos; + } + lightVec = normalize(lightVec); + + float dotNL = max(dot(v_normal, lightVec), 0.0); + if (dotNL > 0.0) { + // Diffuse contribution + diffuse += dotNL * lightColor; + + // Specular + if (u_shininess > 0.0 && u_lights[i].direction.w == 1.0) { + vec3 viewVec = normalize(-v_viewPos); + vec3 H = normalize(lightVec + viewVec); + float dotNH = max(dot(v_normal, H), 0.0); + float spec = pow(dotNH, u_shininess); + specular += spec * lightColor; + } + } + } + + vec4 finalColor = u_color; + finalColor.rgb = clamp(diffuse * u_color.rgb + specular, 0.0, 1.0); + if (u_useTexture != 0) { + vec4 texel = texture2D(u_texture, v_texCoord); + finalColor.rgb = clamp(texel.rgb * finalColor.rgb, 0.0, 1.0); + finalColor.a = texel.a; + } + + gl_FragColor = finalColor; + } + )"; + + GLuint vs = CompileShader(GL_VERTEX_SHADER, vertexShaderSource); + GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); + + GLuint shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vs); + glAttachShader(shaderProgram, fs); + glBindAttribLocation(shaderProgram, 0, "a_position"); + glBindAttribLocation(shaderProgram, 1, "a_normal"); + glBindAttribLocation(shaderProgram, 2, "a_texCoord"); + glLinkProgram(shaderProgram); + glDeleteShader(vs); + glDeleteShader(fs); + + return new OpenGLES2Renderer(width, height, anisotropic, context, shaderProgram); +} + +GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup, bool forceUV = false) +{ + GLES2MeshCacheEntry cache{&meshGroup, meshGroup.version}; + + cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT; + + std::vector vertices; + if (cache.flat) { + FlattenSurfaces( + meshGroup.vertices.data(), + meshGroup.vertices.size(), + meshGroup.indices.data(), + meshGroup.indices.size(), + meshGroup.texture != nullptr || forceUV, + vertices, + cache.indices + ); + } + else { + vertices = meshGroup.vertices; + cache.indices.resize(meshGroup.indices.size()); + std::transform(meshGroup.indices.begin(), meshGroup.indices.end(), cache.indices.begin(), [](DWORD index) { + return static_cast(index); + }); + } + + std::vector texcoords; + if (meshGroup.texture || forceUV) { + texcoords.resize(vertices.size()); + std::transform(vertices.begin(), vertices.end(), texcoords.begin(), [](const D3DRMVERTEX& v) { + return v.texCoord; + }); + } + std::vector positions(vertices.size()); + std::transform(vertices.begin(), vertices.end(), positions.begin(), [](const D3DRMVERTEX& v) { + return v.position; + }); + std::vector normals(vertices.size()); + std::transform(vertices.begin(), vertices.end(), normals.begin(), [](const D3DRMVERTEX& v) { return v.normal; }); + + glGenBuffers(1, &cache.vboPositions); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); + glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(D3DVECTOR), positions.data(), GL_STATIC_DRAW); + + glGenBuffers(1, &cache.vboNormals); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); + glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(D3DVECTOR), normals.data(), GL_STATIC_DRAW); + + if (meshGroup.texture || forceUV) { + glGenBuffers(1, &cache.vboTexcoords); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); + glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(TexCoord), texcoords.data(), GL_STATIC_DRAW); + } + + glGenBuffers(1, &cache.ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + cache.indices.size() * sizeof(cache.indices[0]), + cache.indices.data(), + GL_STATIC_DRAW + ); + + return cache; +} + +bool OpenGLES2Renderer::UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUI) +{ + SDL_Surface* surf = source; + if (source->format != SDL_PIXELFORMAT_RGBA32) { + surf = SDL_ConvertSurface(source, SDL_PIXELFORMAT_RGBA32); + if (!surf) { + return false; + } + } + + glGenTextures(1, &outTexId); + glBindTexture(GL_TEXTURE_2D, outTexId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + + if (isUI) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (m_anisotropic > 1.0f) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_anisotropic); + } + glGenerateMipmap(GL_TEXTURE_2D); + } + + if (surf != source) { + SDL_DestroySurface(surf); + } + + return true; +} + +OpenGLES2Renderer::OpenGLES2Renderer( + DWORD width, + DWORD height, + float anisotropic, + SDL_GLContext context, + GLuint shaderProgram +) + : m_context(context), m_shaderProgram(shaderProgram), m_anisotropic(anisotropic) +{ + glGenFramebuffers(1, &m_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + bool anisoAvailable = SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic"); + GLfloat maxAniso = 0.0f; + if (anisoAvailable) { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); + } + if (m_anisotropic > maxAniso) { + m_anisotropic = maxAniso; + } + SDL_Log( + "Anisotropic is %s. Requested: %f, active: %f, max aniso: %f", + m_anisotropic > 1.0f ? "on" : "off", + anisotropic, + m_anisotropic, + maxAniso + ); + + m_virtualWidth = width; + m_virtualHeight = height; + ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f}; + Resize(width, height, viewportTransform); + + SDL_Surface* dummySurface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA32); + if (!dummySurface) { + SDL_Log("Failed to create surface: %s", SDL_GetError()); + return; + } + if (!SDL_LockSurface(dummySurface)) { + SDL_Log("Failed to lock surface: %s", SDL_GetError()); + SDL_DestroySurface(dummySurface); + return; + } + ((Uint32*) dummySurface->pixels)[0] = 0xFFFFFFFF; + SDL_UnlockSurface(dummySurface); + + UploadTexture(dummySurface, m_dummyTexture, false); + if (!m_dummyTexture) { + SDL_DestroySurface(dummySurface); + SDL_Log("Failed to create surface: %s", SDL_GetError()); + return; + } + SDL_DestroySurface(dummySurface); + + m_posLoc = glGetAttribLocation(m_shaderProgram, "a_position"); + m_normLoc = glGetAttribLocation(m_shaderProgram, "a_normal"); + m_texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord"); + m_colorLoc = glGetUniformLocation(m_shaderProgram, "u_color"); + m_shinLoc = glGetUniformLocation(m_shaderProgram, "u_shininess"); + m_lightCountLoc = glGetUniformLocation(m_shaderProgram, "u_lightCount"); + m_useTextureLoc = glGetUniformLocation(m_shaderProgram, "u_useTexture"); + m_textureLoc = glGetUniformLocation(m_shaderProgram, "u_texture"); + for (int i = 0; i < 3; ++i) { + std::string base = "u_lights[" + std::to_string(i) + "]"; + u_lightLocs[i][0] = glGetUniformLocation(m_shaderProgram, (base + ".color").c_str()); + u_lightLocs[i][1] = glGetUniformLocation(m_shaderProgram, (base + ".position").c_str()); + u_lightLocs[i][2] = glGetUniformLocation(m_shaderProgram, (base + ".direction").c_str()); + } + m_modelViewMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_modelViewMatrix"); + m_normalMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_normalMatrix"); + m_projectionMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"); + + m_uiMesh.vertices = { + {{0.0f, 0.0f, 0.0f}, {0, 0, -1}, {0.0f, 0.0f}}, + {{1.0f, 0.0f, 0.0f}, {0, 0, -1}, {1.0f, 0.0f}}, + {{1.0f, 1.0f, 0.0f}, {0, 0, -1}, {1.0f, 1.0f}}, + {{0.0f, 1.0f, 0.0f}, {0, 0, -1}, {0.0f, 1.0f}} + }; + m_uiMesh.indices = {0, 1, 2, 0, 2, 3}; + m_uiMeshCache = GLES2UploadMesh(m_uiMesh, true); + + glUseProgram(m_shaderProgram); +} + +OpenGLES2Renderer::~OpenGLES2Renderer() +{ + SDL_DestroySurface(m_renderedImage); + glDeleteTextures(1, &m_dummyTexture); + glDeleteProgram(m_shaderProgram); + glDeleteTextures(1, &m_colorTarget); + glDeleteRenderbuffers(1, &m_depthTarget); + glDeleteFramebuffers(1, &m_fbo); + SDL_GL_DestroyContext(m_context); +} + +void OpenGLES2Renderer::PushLights(const SceneLight* lightsArray, size_t count) +{ + if (count > 3) { + SDL_Log("Unsupported number of lights (%d)", static_cast(count)); + count = 3; + } + + m_lights.assign(lightsArray, lightsArray + count); +} + +void OpenGLES2Renderer::SetFrustumPlanes(const Plane* frustumPlanes) +{ +} + +void OpenGLES2Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) +{ + memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); +} + +struct TextureDestroyContextGLS2 { + OpenGLES2Renderer* renderer; + Uint32 textureId; +}; + +void OpenGLES2Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) +{ + auto* ctx = new TextureDestroyContextGLS2{this, id}; + texture->AddDestroyCallback( + [](IDirect3DRMObject* obj, void* arg) { + auto* ctx = static_cast(arg); + auto& cache = ctx->renderer->m_textures[ctx->textureId]; + if (cache.glTextureId != 0) { + glDeleteTextures(1, &cache.glTextureId); + cache.glTextureId = 0; + cache.texture = nullptr; + } + delete ctx; + }, + ctx + ); +} + +Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUI, float scaleX, float scaleY) +{ + SDL_GL_MakeCurrent(DDWindow, m_context); + auto texture = static_cast(iTexture); + auto surface = static_cast(texture->m_surface); + + for (Uint32 i = 0; i < m_textures.size(); ++i) { + auto& tex = m_textures[i]; + if (tex.texture == texture) { + if (tex.version != texture->m_version) { + glDeleteTextures(1, &tex.glTextureId); + if (UploadTexture(surface->m_surface, tex.glTextureId, isUI)) { + tex.version = texture->m_version; + } + } + return i; + } + } + + GLuint texId; + if (!UploadTexture(surface->m_surface, texId, isUI)) { + return NO_TEXTURE_ID; + } + + for (Uint32 i = 0; i < m_textures.size(); ++i) { + auto& tex = m_textures[i]; + if (!tex.texture) { + tex.texture = texture; + tex.version = texture->m_version; + tex.glTextureId = texId; + tex.width = surface->m_surface->w; + tex.height = surface->m_surface->h; + AddTextureDestroyCallback(i, texture); + return i; + } + } + + m_textures.push_back( + {texture, texture->m_version, texId, (uint16_t) surface->m_surface->w, (uint16_t) surface->m_surface->h} + ); + AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); + return (Uint32) (m_textures.size() - 1); +} + +struct GLES2MeshDestroyContext { + OpenGLES2Renderer* renderer; + Uint32 id; +}; + +void OpenGLES2Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) +{ + auto* ctx = new GLES2MeshDestroyContext{this, id}; + mesh->AddDestroyCallback( + [](IDirect3DRMObject*, void* arg) { + auto* ctx = static_cast(arg); + auto& cache = ctx->renderer->m_meshs[ctx->id]; + cache.meshGroup = nullptr; + glDeleteBuffers(1, &cache.vboPositions); + glDeleteBuffers(1, &cache.vboNormals); + glDeleteBuffers(1, &cache.vboTexcoords); + glDeleteBuffers(1, &cache.ibo); + delete ctx; + }, + ctx + ); +} + +Uint32 OpenGLES2Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) +{ + for (Uint32 i = 0; i < m_meshs.size(); ++i) { + auto& cache = m_meshs[i]; + if (cache.meshGroup == meshGroup) { + if (cache.version != meshGroup->version) { + cache = std::move(GLES2UploadMesh(*meshGroup)); + } + return i; + } + } + + auto newCache = GLES2UploadMesh(*meshGroup); + + for (Uint32 i = 0; i < m_meshs.size(); ++i) { + auto& cache = m_meshs[i]; + if (!cache.meshGroup) { + cache = std::move(newCache); + AddMeshDestroyCallback(i, mesh); + return i; + } + } + + m_meshs.push_back(std::move(newCache)); + AddMeshDestroyCallback((Uint32) (m_meshs.size() - 1), mesh); + return (Uint32) (m_meshs.size() - 1); +} + +HRESULT OpenGLES2Renderer::BeginFrame() +{ + SDL_GL_MakeCurrent(DDWindow, m_context); + m_dirty = true; + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + + SceneLightGLES2 lightData[3]; + int lightCount = std::min(static_cast(m_lights.size()), 3); + + for (int i = 0; i < lightCount; ++i) { + const auto& src = m_lights[i]; + lightData[i].color[0] = src.color.r; + lightData[i].color[1] = src.color.g; + lightData[i].color[2] = src.color.b; + lightData[i].color[3] = src.color.a; + + lightData[i].position[0] = src.position.x; + lightData[i].position[1] = src.position.y; + lightData[i].position[2] = src.position.z; + lightData[i].position[3] = src.positional; + + lightData[i].direction[0] = src.direction.x; + lightData[i].direction[1] = src.direction.y; + lightData[i].direction[2] = src.direction.z; + lightData[i].direction[3] = src.directional; + } + + for (int i = 0; i < lightCount; ++i) { + glUniform4fv(u_lightLocs[i][0], 1, lightData[i].color); + glUniform4fv(u_lightLocs[i][1], 1, lightData[i].position); + glUniform4fv(u_lightLocs[i][2], 1, lightData[i].direction); + } + glUniform1i(m_lightCountLoc, lightCount); + return DD_OK; +} + +void OpenGLES2Renderer::EnableTransparency() +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); +} + +void OpenGLES2Renderer::SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, + const Matrix3x3& normalMatrix, + const Appearance& appearance +) +{ + auto& mesh = m_meshs[meshId]; + + glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelViewMatrix[0][0]); + glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &normalMatrix[0][0]); + glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &m_projection[0][0]); + + glUniform4f( + m_colorLoc, + appearance.color.r / 255.0f, + appearance.color.g / 255.0f, + appearance.color.b / 255.0f, + appearance.color.a / 255.0f + ); + + glUniform1f(m_shinLoc, appearance.shininess); + + if (appearance.textureId != NO_TEXTURE_ID) { + glUniform1i(m_useTextureLoc, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textures[appearance.textureId].glTextureId); + glUniform1i(m_textureLoc, 0); + } + else { + glUniform1i(m_useTextureLoc, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_dummyTexture); + glUniform1i(m_textureLoc, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); + glEnableVertexAttribArray(m_posLoc); + glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals); + glEnableVertexAttribArray(m_normLoc); + glVertexAttribPointer(m_normLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + if (appearance.textureId != NO_TEXTURE_ID) { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords); + glEnableVertexAttribArray(m_texLoc); + glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo); + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); + + glDisableVertexAttribArray(m_normLoc); + glDisableVertexAttribArray(m_texLoc); +} + +HRESULT OpenGLES2Renderer::FinalizeFrame() +{ + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + return DD_OK; +} + +void OpenGLES2Renderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + SDL_GL_MakeCurrent(DDWindow, m_context); + m_width = width; + m_height = height; + m_viewportTransform = viewportTransform; + if (m_renderedImage) { + SDL_DestroySurface(m_renderedImage); + } + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); + + if (m_colorTarget) { + glDeleteTextures(1, &m_colorTarget); + m_colorTarget = 0; + } + if (m_depthTarget) { + glDeleteRenderbuffers(1, &m_depthTarget); + m_depthTarget = 0; + } + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + // Create color texture + glGenTextures(1, &m_colorTarget); + glBindTexture(GL_TEXTURE_2D, m_colorTarget); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTarget, 0); + + // Create depth renderbuffer + glGenRenderbuffers(1, &m_depthTarget); + glBindRenderbuffer(GL_RENDERBUFFER, m_depthTarget); + + if (SDL_GL_ExtensionSupported("GL_OES_depth24")) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, width, height); + } + else if (SDL_GL_ExtensionSupported("GL_OES_depth32")) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32_OES, width, height); + } + else { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + } + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthTarget); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + SDL_Log("FBO incomplete: 0x%X", status); + } + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glViewport(0, 0, m_width, m_height); +} + +void OpenGLES2Renderer::Clear(float r, float g, float b) +{ + SDL_GL_MakeCurrent(DDWindow, m_context); + m_dirty = true; + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glClearColor(r, g, b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void OpenGLES2Renderer::Flip() +{ + SDL_GL_MakeCurrent(DDWindow, m_context); + if (!m_dirty) { + return; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glDisable(GL_DEPTH_TEST); + glFrontFace(GL_CCW); + glDepthMask(GL_FALSE); + + glUniform4f(m_colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1f(m_shinLoc, 0.0f); + + float ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + float blank[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glUniform4fv(u_lightLocs[0][0], 1, ambient); + glUniform4fv(u_lightLocs[0][1], 1, blank); + glUniform4fv(u_lightLocs[0][2], 1, blank); + glUniform1i(m_lightCountLoc, 1); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_colorTarget); + glUniform1i(m_textureLoc, 0); + glUniform1i(m_useTextureLoc, 1); + + D3DRMMATRIX4D projection; + D3DRMMATRIX4D modelViewMatrix = { + {(float) m_width, 0.0f, 0.0f, 0.0f}, + {0.0f, (float) -m_height, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, (float) m_height, 0.0f, 1.0f} + }; + glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelViewMatrix[0][0]); + Matrix3x3 identity = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}; + glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &identity[0][0]); + CreateOrthographicProjection((float) m_width, (float) m_height, projection); + glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &projection[0][0]); + + glDisable(GL_SCISSOR_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboPositions); + glEnableVertexAttribArray(m_posLoc); + glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboTexcoords); + glEnableVertexAttribArray(m_texLoc); + glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uiMeshCache.ibo); + glDrawElements(GL_TRIANGLES, static_cast(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr); + + glDisableVertexAttribArray(m_texLoc); + + SDL_GL_SwapWindow(DDWindow); + glFrontFace(GL_CW); + m_dirty = false; +} + +void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color) +{ + SDL_GL_MakeCurrent(DDWindow, m_context); + m_dirty = true; + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + float ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + float blank[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glUniform4fv(u_lightLocs[0][0], 1, ambient); + glUniform4fv(u_lightLocs[0][1], 1, blank); + glUniform4fv(u_lightLocs[0][2], 1, blank); + glUniform1i(m_lightCountLoc, 1); + + glUniform4f(m_colorLoc, color.r, color.g, color.b, color.a); + glUniform1f(m_shinLoc, 0.0f); + + SDL_Rect expandedDstRect; + if (textureId != NO_TEXTURE_ID) { + const GLES2TextureCacheEntry& texture = m_textures[textureId]; + float scaleX = static_cast(dstRect.w) / srcRect.w; + float scaleY = static_cast(dstRect.h) / srcRect.h; + expandedDstRect = { + static_cast(std::round(dstRect.x - srcRect.x * scaleX)), + static_cast(std::round(dstRect.y - srcRect.y * scaleY)), + static_cast(std::round(texture.width * scaleX)), + static_cast(std::round(texture.height * scaleY)) + }; + + glUniform1i(m_useTextureLoc, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture.glTextureId); + glUniform1i(m_textureLoc, 0); + } + else { + expandedDstRect = dstRect; + glUniform1i(m_useTextureLoc, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_dummyTexture); + glUniform1i(m_textureLoc, 0); + } + + D3DRMMATRIX4D modelView, projection; + Create2DTransformMatrix( + expandedDstRect, + m_viewportTransform.scale, + m_viewportTransform.offsetX, + m_viewportTransform.offsetY, + modelView + ); + + glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelView[0][0]); + Matrix3x3 identity = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}; + glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &identity[0][0]); + CreateOrthographicProjection((float) m_width, (float) m_height, projection); + glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &projection[0][0]); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_SCISSOR_TEST); + glScissor( + static_cast(std::round(dstRect.x * m_viewportTransform.scale + m_viewportTransform.offsetX)), + m_height - static_cast( + std::round((dstRect.y + dstRect.h) * m_viewportTransform.scale + m_viewportTransform.offsetY) + ), + static_cast(std::round(dstRect.w * m_viewportTransform.scale)), + static_cast(std::round(dstRect.h * m_viewportTransform.scale)) + ); + + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboPositions); + glEnableVertexAttribArray(m_posLoc); + glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboTexcoords); + glEnableVertexAttribArray(m_texLoc); + glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uiMeshCache.ibo); + glDrawElements(GL_TRIANGLES, static_cast(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr); + + glDisableVertexAttribArray(m_texLoc); + glDisable(GL_SCISSOR_TEST); +} + +void OpenGLES2Renderer::Download(SDL_Surface* target) +{ + glFinish(); + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); + + SDL_Rect srcRect = { + static_cast(m_viewportTransform.offsetX), + static_cast(m_viewportTransform.offsetY), + static_cast(target->w * m_viewportTransform.scale), + static_cast(target->h * m_viewportTransform.scale), + }; + + SDL_Surface* bufferClone = SDL_CreateSurface(target->w, target->h, SDL_PIXELFORMAT_RGBA32); + if (!bufferClone) { + SDL_Log("SDL_CreateSurface: %s", SDL_GetError()); + return; + } + + SDL_BlitSurfaceScaled(m_renderedImage, &srcRect, bufferClone, nullptr, SDL_SCALEMODE_NEAREST); + + // Flip image vertically into target + SDL_Rect rowSrc = {0, 0, bufferClone->w, 1}; + SDL_Rect rowDst = {0, 0, bufferClone->w, 1}; + for (int y = 0; y < bufferClone->h; ++y) { + rowSrc.y = y; + rowDst.y = bufferClone->h - 1 - y; + SDL_BlitSurface(bufferClone, &rowSrc, target, &rowDst); + } + + SDL_DestroySurface(bufferClone); +} + +void OpenGLES2Renderer::SetDither(bool dither) +{ + if (dither) { + glEnable(GL_DITHER); + } + else { + glDisable(GL_DITHER); + } +} diff --git a/miniwin/src/d3drm/d3drmrenderer.cpp b/miniwin/src/d3drm/d3drmrenderer.cpp index 8fe210b5..f1e1fb8b 100644 --- a/miniwin/src/d3drm/d3drmrenderer.cpp +++ b/miniwin/src/d3drm/d3drmrenderer.cpp @@ -2,6 +2,9 @@ #ifdef USE_OPENGL1 #include "d3drmrenderer_opengl1.h" #endif +#ifdef USE_OPENGLES2 +#include "d3drmrenderer_opengles2.h" +#endif #ifdef USE_OPENGLES3 #include "d3drmrenderer_opengles3.h" #endif @@ -44,6 +47,11 @@ Direct3DRMRenderer* CreateDirect3DRMRenderer( ); } #endif +#ifdef USE_OPENGLES2 + if (SDL_memcmp(guid, &OpenGLES2_GUID, sizeof(GUID)) == 0) { + return OpenGLES2Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight, d3d->GetAnisotropic()); + } +#endif #ifdef USE_OPENGL1 if (SDL_memcmp(guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { return OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight, d3d->GetMSAASamples()); @@ -70,6 +78,9 @@ void Direct3DRMRenderer_EnumDevices(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICE #ifdef USE_OPENGLES3 OpenGLES3Renderer_EnumDevice(d3d, cb, ctx); #endif +#ifdef USE_OPENGLES2 + OpenGLES2Renderer_EnumDevice(d3d, cb, ctx); +#endif #ifdef USE_OPENGL1 OpenGL1Renderer_EnumDevice(d3d, cb, ctx); #endif diff --git a/miniwin/src/internal/d3drmrenderer_opengles2.h b/miniwin/src/internal/d3drmrenderer_opengles2.h new file mode 100644 index 00000000..6a95d73f --- /dev/null +++ b/miniwin/src/internal/d3drmrenderer_opengles2.h @@ -0,0 +1,128 @@ +#pragma once + +#include "d3drmrenderer.h" +#include "d3drmtexture_impl.h" +#include "ddraw_impl.h" + +#include +#include +#include + +DEFINE_GUID(OpenGLES2_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06); + +struct GLES2TextureCacheEntry { + IDirect3DRMTexture* texture; + Uint32 version; + GLuint glTextureId; + uint16_t width; + uint16_t height; +}; + +struct GLES2MeshCacheEntry { + const MeshGroup* meshGroup; + int version; + bool flat; + + std::vector indices; + GLuint vboPositions; + GLuint vboNormals; + GLuint vboTexcoords; + GLuint ibo; +}; + +class OpenGLES2Renderer : public Direct3DRMRenderer { +public: + static Direct3DRMRenderer* Create(DWORD width, DWORD height, float anisotropic); + OpenGLES2Renderer(DWORD width, DWORD height, float anisotropic, SDL_GLContext context, GLuint shaderProgram); + ~OpenGLES2Renderer() override; + + void PushLights(const SceneLight* lightsArray, size_t count) override; + void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; + void SetFrustumPlanes(const Plane* frustumPlanes) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUI, float scaleX, float scaleY) override; + Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; + HRESULT BeginFrame() override; + void EnableTransparency() override; + void SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, + const Matrix3x3& normalMatrix, + const Appearance& appearance + ) override; + HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color) override; + void Download(SDL_Surface* target) override; + void SetDither(bool dither) override; + +private: + void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); + void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); + bool UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUI); + + MeshGroup m_uiMesh; + GLES2MeshCacheEntry m_uiMeshCache; + std::vector m_textures; + std::vector m_meshs; + D3DRMMATRIX4D m_projection; + SDL_Surface* m_renderedImage = nullptr; + bool m_dirty = false; + std::vector m_lights; + SDL_GLContext m_context; + float m_anisotropic; + GLuint m_fbo; + GLuint m_colorTarget; + GLuint m_depthTarget; + GLuint m_shaderProgram; + GLuint m_dummyTexture; + GLint m_posLoc; + GLint m_normLoc; + GLint m_texLoc; + GLint m_colorLoc; + GLint m_shinLoc; + GLint m_lightCountLoc; + GLint m_useTextureLoc; + GLint m_textureLoc; + GLint u_lightLocs[3][3]; + GLint m_modelViewMatrixLoc; + GLint m_normalMatrixLoc; + GLint m_projectionMatrixLoc; + ViewportTransform m_viewportTransform; +}; + +inline static void OpenGLES2Renderer_EnumDevice(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICESCALLBACK cb, void* ctx) +{ + Direct3DRMRenderer* device = OpenGLES2Renderer::Create(640, 480, d3d->GetAnisotropic()); + if (!device) { + return; + } + + D3DDEVICEDESC halDesc = {}; + halDesc.dcmColorModel = D3DCOLOR_RGB; + halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc.dwDeviceZBufferBitDepth = DDBD_16; + halDesc.dwDeviceRenderBitDepth = DDBD_32; + halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + const char* extensions = (const char*) glGetString(GL_EXTENSIONS); + if (extensions) { + if (strstr(extensions, "GL_OES_depth24")) { + halDesc.dwDeviceZBufferBitDepth |= DDBD_24; + } + if (strstr(extensions, "GL_OES_depth32")) { + halDesc.dwDeviceZBufferBitDepth |= DDBD_32; + } + } + + delete device; + + D3DDEVICEDESC helDesc = {}; + + EnumDevice(cb, ctx, "OpenGL ES 2.0 HAL", &halDesc, &helDesc, OpenGLES2_GUID); +} From 73c6be7abf61b6b3d09d18c89c4707e5784ba2bb Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 15 Aug 2025 09:56:36 -0700 Subject: [PATCH 5/6] Clean up config code (#679) * Clean up config code * Export function * Fix --- ISLE/android/config.cpp | 1 - ISLE/ios/config.cpp | 16 ++--- ISLE/isleapp.cpp | 107 ++++++++++++---------------------- ISLE/isleapp.h | 2 +- LEGO1/omni/include/mxstring.h | 2 +- 5 files changed, 46 insertions(+), 82 deletions(-) diff --git a/ISLE/android/config.cpp b/ISLE/android/config.cpp index e93cb02e..4883a239 100644 --- a/ISLE/android/config.cpp +++ b/ISLE/android/config.cpp @@ -21,7 +21,6 @@ void Android_SetupDefaultConfigOverrides(dictionary* p_dictionary) iniparser_set(p_dictionary, "isle:diskpath", data); iniparser_set(p_dictionary, "isle:cdpath", data); iniparser_set(p_dictionary, "isle:mediapath", data); - iniparser_set(p_dictionary, "isle:savepath", savedata.GetData()); // Default to Virtual Mouse diff --git a/ISLE/ios/config.cpp b/ISLE/ios/config.cpp index ee9349fa..22a17657 100644 --- a/ISLE/ios/config.cpp +++ b/ISLE/ios/config.cpp @@ -1,5 +1,7 @@ #include "config.h" +#include "mxstring.h" + #include #include #include @@ -11,15 +13,13 @@ void IOS_SetupDefaultConfigOverrides(dictionary* p_dictionary) // Use DevelopmentFiles path for disk and cd paths // It's good to use that path since user can easily // connect through SMB and copy the files - const char* documentFolder = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS); - char* diskPath = new char[strlen(documentFolder) + strlen("isle") + 1](); - strcpy(diskPath, documentFolder); - strcat(diskPath, "isle"); + MxString documentFolder = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS); + documentFolder += "isle"; - if (!SDL_GetPathInfo(diskPath, NULL)) { - SDL_CreateDirectory(diskPath); + if (!SDL_GetPathInfo(documentFolder.GetData(), NULL)) { + SDL_CreateDirectory(documentFolder.GetData()); } - iniparser_set(p_dictionary, "isle:diskpath", diskPath); - iniparser_set(p_dictionary, "isle:cdpath", diskPath); + iniparser_set(p_dictionary, "isle:diskpath", documentFolder.GetData()); + iniparser_set(p_dictionary, "isle:cdpath", documentFolder.GetData()); } diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 0d3eee28..1b1af42a 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #define SDL_MAIN_USE_CALLBACKS @@ -172,6 +173,7 @@ IsleApp::IsleApp() LegoOmni::CreateInstance(); + m_mediaPath = NULL; m_iniPath = NULL; m_maxLod = RealtimeView::GetUserMaxLOD(); m_maxAllowedExtras = m_islandQuality <= 1 ? 10 : 20; @@ -198,25 +200,11 @@ IsleApp::~IsleApp() MxOmni::DestroyInstance(); } - if (m_hdPath) { - delete[] m_hdPath; - } - - if (m_cdPath) { - delete[] m_cdPath; - } - - if (m_deviceId) { - delete[] m_deviceId; - } - - if (m_savePath) { - delete[] m_savePath; - } - - if (m_mediaPath) { - delete[] m_mediaPath; - } + SDL_free(m_hdPath); + SDL_free(m_cdPath); + SDL_free(m_deviceId); + SDL_free(m_savePath); + SDL_free(m_mediaPath); } // FUNCTION: ISLE 0x401260 @@ -1032,39 +1020,32 @@ bool IsleApp::LoadConfig() #ifdef IOS const char* prefPath = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS); #elif defined(ANDROID) - // SDL_GetAndroidExternalStoragePath() returns without a trailing / resulting in "filesisle.ini" :( - const char* androidPath = SDL_GetAndroidExternalStoragePath(); - size_t len = SDL_strlen(androidPath) + 2; - char* prefPath = new char[len]; - SDL_strlcpy(prefPath, androidPath, len); - SDL_strlcat(prefPath, "/", len); -#else - char* prefPath = SDL_GetPrefPath("isledecomp", "isle"); -#endif - char* iniConfig; - -#ifdef __EMSCRIPTEN__ + MxString androidPath = MxString(SDL_GetAndroidExternalStoragePath()) + "/"; + const char* prefPath = androidPath.GetData(); +#elif defined(EMSCRIPTEN) if (m_iniPath && !Emscripten_SetupConfig(m_iniPath)) { m_iniPath = NULL; } + char* prefPath = SDL_GetPrefPath("isledecomp", "isle"); +#else + char* prefPath = SDL_GetPrefPath("isledecomp", "isle"); #endif + MxString iniConfig; if (m_iniPath) { - iniConfig = new char[strlen(m_iniPath) + 1]; - strcpy(iniConfig, m_iniPath); + iniConfig = m_iniPath; } else if (prefPath) { - iniConfig = new char[strlen(prefPath) + strlen("isle.ini") + 1](); - strcat(iniConfig, prefPath); - strcat(iniConfig, "isle.ini"); + iniConfig = prefPath; + iniConfig += "isle.ini"; } else { - iniConfig = new char[strlen("isle.ini") + 1]; - strcpy(iniConfig, "isle.ini"); + iniConfig = "isle.ini"; } - SDL_Log("Reading configuration from \"%s\"", iniConfig); - dictionary* dict = iniparser_load(iniConfig); + SDL_Log("Reading configuration from \"%s\"", iniConfig.GetData()); + + dictionary* dict = iniparser_load(iniConfig.GetData()); // [library:config] // Load sane defaults if dictionary failed to load @@ -1075,20 +1056,20 @@ bool IsleApp::LoadConfig() } SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading sane defaults"); - FILE* iniFP = fopen(iniConfig, "wb"); + FILE* iniFP = fopen(iniConfig.GetData(), "wb"); if (!iniFP) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "Failed to write config at '%s': %s", - iniConfig, + iniConfig.GetData(), strerror(errno) ); return false; } char buf[32]; - dict = iniparser_load(iniConfig); + dict = iniparser_load(iniConfig.GetData()); iniparser_set(dict, "isle", NULL); iniparser_set(dict, "isle:diskpath", SDL_GetBasePath()); @@ -1143,8 +1124,9 @@ bool IsleApp::LoadConfig() #ifdef ANDROID Android_SetupDefaultConfigOverrides(dict); #endif + iniparser_dump_ini(dict, iniFP); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig.GetData()); fclose(iniFP); } @@ -1152,20 +1134,10 @@ bool IsleApp::LoadConfig() Emscripten_SetupDefaultConfigOverrides(dict); #endif - const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath()); - m_hdPath = new char[strlen(hdPath) + 1]; - strcpy(m_hdPath, hdPath); - MxOmni::SetHD(m_hdPath); - - const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD()); - m_cdPath = new char[strlen(cdPath) + 1]; - strcpy(m_cdPath, cdPath); - MxOmni::SetCD(m_cdPath); - - const char* mediaPath = iniparser_getstring(dict, "isle:mediapath", hdPath); - m_mediaPath = new char[strlen(mediaPath) + 1]; - strcpy(m_mediaPath, mediaPath); - + MxOmni::SetHD((m_hdPath = SDL_strdup(iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath())))); + MxOmni::SetCD((m_cdPath = SDL_strdup(iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD())))); + m_savePath = SDL_strdup(iniparser_getstring(dict, "isle:savepath", prefPath)); + m_mediaPath = SDL_strdup(iniparser_getstring(dict, "isle:mediapath", m_hdPath)); m_flipSurfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flipSurfaces); m_fullScreen = iniparser_getboolean(dict, "isle:Full Screen", m_fullScreen); m_exclusiveFullScreen = iniparser_getboolean(dict, "isle:Exclusive Full Screen", m_exclusiveFullScreen); @@ -1212,17 +1184,9 @@ bool IsleApp::LoadConfig() const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL); if (deviceId != NULL) { - m_deviceId = new char[strlen(deviceId) + 1]; - strcpy(m_deviceId, deviceId); + m_deviceId = SDL_strdup(deviceId); } - // [library:config] - // The original game does not save any data if no savepath is given. - // Instead, we use SDLs prefPath as a default fallback and always save data. - const char* savePath = iniparser_getstring(dict, "isle:savepath", prefPath); - m_savePath = new char[strlen(savePath) + 1]; - strcpy(m_savePath, savePath); - #ifdef EXTENSIONS for (const char* key : Extensions::availableExtensions) { if (iniparser_getboolean(dict, key, 0)) { @@ -1242,11 +1206,12 @@ bool IsleApp::LoadConfig() #endif iniparser_freedict(dict); - delete[] iniConfig; -#ifndef IOS - SDL_free(prefPath); -#endif + [](auto path) { + if constexpr (std::is_same_v) { + SDL_free(path); + } + }(prefPath); return true; } diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index 470a5a72..0e0700e8 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -101,7 +101,7 @@ class IsleApp { MxFloat m_cursorSensitivity; void DisplayArgumentHelp(const char* p_execName); - char* m_iniPath; + const char* m_iniPath; MxFloat m_maxLod; MxU32 m_maxAllowedExtras; MxTransitionManager::TransitionType m_transitionType; diff --git a/LEGO1/omni/include/mxstring.h b/LEGO1/omni/include/mxstring.h index b4b25262..fad2e4ad 100644 --- a/LEGO1/omni/include/mxstring.h +++ b/LEGO1/omni/include/mxstring.h @@ -9,7 +9,7 @@ // SIZE 0x10 class MxString : public MxCore { public: - MxString(); + LEGO1_EXPORT MxString(); MxString(const MxString& p_str); LEGO1_EXPORT MxString(const char* p_str); MxString(const char* p_str, MxU16 p_maxlen); From d10ac7e77cf51704a5d81ddb72cc42e5cda6caa5 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sat, 16 Aug 2025 12:21:39 -0700 Subject: [PATCH 6/6] Add `hdmusic.si` asset pack, extend SiLoader with `Replace` directive (#671) * Replace SI objects * Changes * Fix * Remove obsolete file * Add delete handler * Updates * Update assets * Configure git LFS * Add HD music * Move files to LFS * chmod files * fix gitattributes * Fixes * Fixes * Add other HD music * Fix file perms * Add comment * Minor update * Refactor * Fix * Add LFS flag * Add check for file replace * Exclude assets from CPack * Attempt fix * Update libweaver * Fix MxDSBuffer leak * Add .lfsconfig * Set LFS URL * Fix LFS CI * Fix * Space --- .gitattributes | 2 + .github/workflows/ci.yml | 5 + .lfsconfig | 2 + 3rdparty/CMakeLists.txt | 4 +- 3rdparty/libweaver | 2 +- CMakeLists.txt | 4 - .../src/audio/mxbackgroundaudiomanager.cpp | 16 +- LEGO1/lego/legoomni/src/common/legoutils.cpp | 16 +- LEGO1/lego/legoomni/src/entity/legoworld.cpp | 9 + LEGO1/lego/legoomni/src/main/legomain.cpp | 20 +- LEGO1/omni/include/mxdsbuffer.h | 7 + LEGO1/omni/src/stream/mxdsbuffer.cpp | 31 ++- assets/hdmusic/Act3Music_HD.wav | 3 + assets/hdmusic/BeachBlvd_Music_HD.wav | 3 + assets/hdmusic/Beach_Music_HD.wav | 3 + assets/hdmusic/BrickHunt_HD.wav | 3 + assets/hdmusic/BrickstrChase_HD.wav | 3 + assets/hdmusic/Cave_Music_HD.wav | 3 + assets/hdmusic/CentralRoads_Music_HD.wav | 3 + assets/hdmusic/Hospital_Music_HD.wav | 3 + .../hdmusic/InfoCenter_3rd_Floor_Music_HD.wav | 3 + assets/hdmusic/InformationCenter_Music_HD.wav | 3 + assets/hdmusic/JBMusic1_HD.wav | 3 + assets/hdmusic/JBMusic2_HD.wav | 3 + assets/hdmusic/JBMusic3_HD.wav | 3 + assets/hdmusic/JBMusic4_HD.wav | 3 + assets/hdmusic/JBMusic5_HD.wav | 3 + assets/hdmusic/JBMusic6_HD.wav | 3 + assets/hdmusic/Jail_Music_HD.wav | 3 + assets/hdmusic/JetskiRace_Music_HD.wav | 3 + assets/hdmusic/Park_Music_HD.wav | 3 + assets/hdmusic/PoliceStation_Music_HD.wav | 3 + assets/hdmusic/RaceTrackRoad_Music_HD.wav | 3 + assets/hdmusic/ResidentalArea_Music_HD.wav | 3 + assets/main.cpp | 191 ++++++++++++++++-- .../widescreen/GaraDoor_Background_Wide.bmp | 3 + assets/widescreen/garadoor.bmp | Bin 538678 -> 0 bytes extensions/include/extensions/siloader.h | 25 ++- extensions/src/siloader.cpp | 123 +++++++++-- 39 files changed, 465 insertions(+), 61 deletions(-) create mode 100644 .lfsconfig create mode 100644 assets/hdmusic/Act3Music_HD.wav create mode 100644 assets/hdmusic/BeachBlvd_Music_HD.wav create mode 100644 assets/hdmusic/Beach_Music_HD.wav create mode 100644 assets/hdmusic/BrickHunt_HD.wav create mode 100644 assets/hdmusic/BrickstrChase_HD.wav create mode 100644 assets/hdmusic/Cave_Music_HD.wav create mode 100644 assets/hdmusic/CentralRoads_Music_HD.wav create mode 100644 assets/hdmusic/Hospital_Music_HD.wav create mode 100644 assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav create mode 100644 assets/hdmusic/InformationCenter_Music_HD.wav create mode 100644 assets/hdmusic/JBMusic1_HD.wav create mode 100644 assets/hdmusic/JBMusic2_HD.wav create mode 100644 assets/hdmusic/JBMusic3_HD.wav create mode 100644 assets/hdmusic/JBMusic4_HD.wav create mode 100644 assets/hdmusic/JBMusic5_HD.wav create mode 100644 assets/hdmusic/JBMusic6_HD.wav create mode 100644 assets/hdmusic/Jail_Music_HD.wav create mode 100644 assets/hdmusic/JetskiRace_Music_HD.wav create mode 100644 assets/hdmusic/Park_Music_HD.wav create mode 100644 assets/hdmusic/PoliceStation_Music_HD.wav create mode 100644 assets/hdmusic/RaceTrackRoad_Music_HD.wav create mode 100644 assets/hdmusic/ResidentalArea_Music_HD.wav create mode 100644 assets/widescreen/GaraDoor_Background_Wide.bmp delete mode 100755 assets/widescreen/garadoor.bmp diff --git a/.gitattributes b/.gitattributes index 04926c6b..430dfc15 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,5 @@ **/*.png binary **/*.svg text eol=lf **/*.desktop text eol=lf +assets/widescreen/** filter=lfs diff=lfs merge=lfs -text +assets/hdmusic/** filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e284eee..fe7ce886 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,6 +113,11 @@ jobs: - uses: actions/checkout@v4 + - name: Checkout LFS + if: ${{ matrix.build-assets }} + run: | + git lfs pull + - name: Setup Java (Android) if: ${{ matrix.android }} uses: actions/setup-java@v4 diff --git a/.lfsconfig b/.lfsconfig new file mode 100644 index 00000000..98f14551 --- /dev/null +++ b/.lfsconfig @@ -0,0 +1,2 @@ +[lfs] + url = https://f9622702b3bbcac0705052a10e62a5a5:31adabe20fe975a919bd1909a6f598f37b0d6486225efa84b38da1aaf27cbd35@assets.isle.pizza/a70a70ae5ebee06d2333bb1132711de1.r2.cloudflarestorage.com/assets diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 28b5aa15..54259e63 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -78,8 +78,8 @@ if(DOWNLOAD_DEPENDENCIES) include(FetchContent) FetchContent_Populate( libweaver - URL https://github.com/isledecomp/SIEdit/archive/6da93b2072c41c41d526b8b9df7d4292be1f0f55.tar.gz - URL_MD5 ae59007fcb9efadc06c67621e1e107cb + URL https://github.com/isledecomp/SIEdit/archive/ae447259389f3bf8273c7e7a4844743faf7cbdb8.tar.gz + URL_MD5 dee68424fde8db6d5cef3b9034a8151f ) else() set(libweaver_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libweaver") diff --git a/3rdparty/libweaver b/3rdparty/libweaver index 6da93b20..ae447259 160000 --- a/3rdparty/libweaver +++ b/3rdparty/libweaver @@ -1 +1 @@ -Subproject commit 6da93b2072c41c41d526b8b9df7d4292be1f0f55 +Subproject commit ae447259389f3bf8273c7e7a4844743faf7cbdb8 diff --git a/CMakeLists.txt b/CMakeLists.txt index 47c9b9c8..deebda7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -691,10 +691,6 @@ if(ISLE_BUILD_ASSETS) add_custom_target(build_assets ALL DEPENDS ${GENERATED_ASSETS_DIR}/.stamp ) - - install(DIRECTORY ${GENERATED_ASSETS_DIR}/ - DESTINATION assets - ) endif() if (ISLE_MINIWIN) diff --git a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp index 2822e95a..a5bb51b7 100644 --- a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp +++ b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp @@ -119,7 +119,12 @@ void MxBackgroundAudioManager::FadeInPendingPresenter() MxS32 compare, volume; if (m_activePresenter == NULL) { - if (m_pendingPresenter) { + if (m_pendingPresenter && m_pendingPresenter->GetCurrentTickleState() >= MxPresenter::e_streaming) { + if (!m_pendingPresenter->IsEnabled()) { + m_pendingPresenter->Enable(TRUE); + m_pendingPresenter->SetTickleState(MxPresenter::e_streaming); + } + if (m_volumeSuppressionAmount != 0) { compare = 30; } @@ -221,6 +226,12 @@ void MxBackgroundAudioManager::StartAction(MxParam& p_param) m_action2.SetObjectId(m_pendingPresenter->GetAction()->GetObjectId()); m_targetVolume = ((MxDSSound*) (m_pendingPresenter->GetAction()))->GetVolume(); m_pendingPresenter->SetVolume(0); + + // Disabling the action here and starting it later once the actively presented music has been faded out. + // This was not necessary in retail because the streaming layer would implicitly not start another action + // before the previous one has ended (since it's all coming from JUKEBOX.SI), however since we now + // allow loading music from multiple SI files this would cause the new music to start immediately. + m_pendingPresenter->GetAction()->SetFlags(m_pendingPresenter->GetAction()->GetFlags() & ~MxDSAction::c_enabled); } // FUNCTION: LEGO1 0x1007f200 @@ -254,7 +265,8 @@ MxResult MxBackgroundAudioManager::PlayMusic( return SUCCESS; } - if (m_action2.GetObjectId() == -1 && m_action1.GetObjectId() != p_action.GetObjectId()) { + if (m_action2.GetObjectId() == -1 && + (m_action1.GetObjectId() != p_action.GetObjectId() || m_action1.GetAtomId() != p_action.GetAtomId())) { MxDSAction action; action.SetAtomId(GetCurrentAction().GetAtomId()); action.SetObjectId(GetCurrentAction().GetObjectId()); diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp index c43e4dc9..f8ef1bab 100644 --- a/LEGO1/lego/legoomni/src/common/legoutils.cpp +++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp @@ -503,6 +503,12 @@ MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id) { LegoWorld* world = CurrentWorld(); + auto result = + Extension::Call(HandleRemove, SiLoader::StreamObject{p_atomId, p_id}, world).value_or(std::nullopt); + if (result) { + return result.value(); + } + if (world) { MxCore* object = world->Find(p_atomId, p_id); @@ -518,8 +524,6 @@ MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id) } ((MxPresenter*) object)->EndAction(); - - Extension::Call(RemoveWith, SiLoader::StreamObject{p_atomId, p_id}, world); } return TRUE; @@ -539,6 +543,12 @@ MxBool RemoveFromWorld( { LegoWorld* world = FindWorld(p_worldAtom, p_worldEntityId); + auto result = Extension::Call(HandleRemove, SiLoader::StreamObject{p_entityAtom, p_entityId}, world) + .value_or(std::nullopt); + if (result) { + return result.value(); + } + if (world) { MxCore* object = world->Find(p_entityAtom, p_entityId); @@ -554,8 +564,6 @@ MxBool RemoveFromWorld( } ((MxPresenter*) object)->EndAction(); - - Extension::Call(RemoveWith, SiLoader::StreamObject{p_entityAtom, p_entityId}, world); } return TRUE; diff --git a/LEGO1/lego/legoomni/src/entity/legoworld.cpp b/LEGO1/lego/legoomni/src/entity/legoworld.cpp index 1ba828cb..fe4bbc63 100644 --- a/LEGO1/lego/legoomni/src/entity/legoworld.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoworld.cpp @@ -1,6 +1,7 @@ #include "legoworld.h" #include "anim/legoanim.h" +#include "extensions/siloader.h" #include "legoanimationmanager.h" #include "legoanimpresenter.h" #include "legobuildingmanager.h" @@ -32,6 +33,8 @@ DECOMP_SIZE_ASSERT(LegoEntityListCursor, 0x10) DECOMP_SIZE_ASSERT(LegoCacheSoundList, 0x18) DECOMP_SIZE_ASSERT(LegoCacheSoundListCursor, 0x10) +using namespace Extensions; + // FUNCTION: LEGO1 0x1001ca40 LegoWorld::LegoWorld() : m_pathControllerList(TRUE) { @@ -636,6 +639,12 @@ MxCore* LegoWorld::Find(const char* p_class, const char* p_name) // FUNCTION: BETA10 0x100db3de MxCore* LegoWorld::Find(const MxAtomId& p_atom, MxS32 p_entityId) { + auto result = + Extension::Call(HandleFind, SiLoader::StreamObject{p_atom, p_entityId}, this).value_or(std::nullopt); + if (result) { + return result.value(); + } + LegoEntityListCursor entityCursor(m_entityList); LegoEntity* entity; diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp index 975933ff..604701eb 100644 --- a/LEGO1/lego/legoomni/src/main/legomain.cpp +++ b/LEGO1/lego/legoomni/src/main/legomain.cpp @@ -474,6 +474,11 @@ LegoWorld* LegoOmni::FindWorld(const MxAtomId& p_atom, MxS32 p_entityid) // STUB: BETA10 0x1008e93e void LegoOmni::DeleteObject(MxDSAction& p_dsAction) { + auto result = Extension::Call(HandleDelete, p_dsAction).value_or(std::nullopt); + if (result && result.value()) { + return; + } + if (p_dsAction.GetAtomId().GetInternal() != NULL) { LegoWorld* world = FindWorld(p_dsAction.GetAtomId(), p_dsAction.GetObjectId()); if (world) { @@ -663,6 +668,13 @@ void LegoOmni::CreateBackgroundAudio() // FUNCTION: BETA10 0x1008f7e0 MxResult LegoOmni::Start(MxDSAction* p_dsAction) { + { + auto result = Extension::Call(HandleStart, *p_dsAction).value_or(std::nullopt); + if (result) { + return result.value(); + } + } + MxResult result = MxOmni::Start(p_dsAction); #ifdef BETA10 this->m_action = *p_dsAction; @@ -673,14 +685,6 @@ MxResult LegoOmni::Start(MxDSAction* p_dsAction) this->m_action.SetObjectId(p_dsAction->GetObjectId()); this->m_action.SetUnknown24(p_dsAction->GetUnknown24()); #endif - - if (result == SUCCESS) { - Extension::Call( - StartWith, - SiLoader::StreamObject{p_dsAction->GetAtomId(), p_dsAction->GetObjectId()} - ); - } - return result; } diff --git a/LEGO1/omni/include/mxdsbuffer.h b/LEGO1/omni/include/mxdsbuffer.h index a988ca57..09f6f826 100644 --- a/LEGO1/omni/include/mxdsbuffer.h +++ b/LEGO1/omni/include/mxdsbuffer.h @@ -91,6 +91,12 @@ class MxDSBuffer : public MxCore { void SetUnk30(MxDSStreamingAction* p_unk0x30) { m_unk0x30 = p_unk0x30; } + void SetSourceBuffer(MxDSBuffer* p_sourceBuffer) + { + m_sourceBuffer = p_sourceBuffer; + m_sourceBuffer->AddRef(NULL); + } + // SYNTHETIC: LEGO1 0x100c6510 // SYNTHETIC: BETA10 0x10158530 // MxDSBuffer::`scalar deleting destructor' @@ -107,6 +113,7 @@ class MxDSBuffer : public MxCore { MxU32 m_writeOffset; // 0x28 MxU32 m_bytesRemaining; // 0x2c MxDSStreamingAction* m_unk0x30; // 0x30 + MxDSBuffer* m_sourceBuffer; }; #endif // MXDSBUFFER_H diff --git a/LEGO1/omni/src/stream/mxdsbuffer.cpp b/LEGO1/omni/src/stream/mxdsbuffer.cpp index 0d49741a..732f9fac 100644 --- a/LEGO1/omni/src/stream/mxdsbuffer.cpp +++ b/LEGO1/omni/src/stream/mxdsbuffer.cpp @@ -28,6 +28,7 @@ MxDSBuffer::MxDSBuffer() m_bytesRemaining = 0; m_mode = e_preallocated; m_unk0x30 = 0; + m_sourceBuffer = NULL; } // FUNCTION: LEGO1 0x100c6530 @@ -36,6 +37,10 @@ MxDSBuffer::~MxDSBuffer() { assert(m_referenceCount == 0); + if (m_sourceBuffer) { + m_sourceBuffer->ReleaseRef(NULL); + } + if (m_pBuffer != NULL) { switch (m_mode) { case e_allocate: @@ -267,6 +272,28 @@ MxResult MxDSBuffer::ParseChunk( return FAILURE; } + // START FIX: Ref-Counting Backpressure for Split Chunks + // + // PROBLEM: When a `DS_CHUNK_SPLIT` is found, the temporary `MxStreamChunk` + // header that holds a reference to the source buffer is immediately + // destroyed. This prematurely releases the reference, causing the source + // buffer's ref-count to drop to zero. + // + // EFFECT: The source buffer is freed immediately instead of being kept + // alive on the m_list0x74 "keep-alive" list. This breaks the natural + // ref-counting backpressure mechanism, as the controller is blind to the + // downstream workload and keeps reading new data from the stream at full + // speed, eventually leading to a memory leak. + // + // SOLUTION: We explicitly link the new reassembly buffer to the original + // source buffer. We then add an artificial reference to the source buffer. + // This reference is designed to be released by the reassembly buffer's + // destructor, ensuring the source buffer is kept alive for the correct + // duration and that the backpressure system functions as intended. + if (p_header->GetBuffer()) { + buffer->SetSourceBuffer(p_header->GetBuffer()); + } + MxU16* flags = MxStreamChunk::IntoFlags(buffer->GetBuffer()); *flags = p_header->GetChunkFlags() & ~DS_CHUNK_SPLIT; @@ -409,9 +436,7 @@ MxU8 MxDSBuffer::ReleaseRef(MxDSChunk*) // FUNCTION: LEGO1 0x100c6ee0 void MxDSBuffer::AddRef(MxDSChunk* p_chunk) { - if (p_chunk) { - m_referenceCount++; - } + m_referenceCount++; } // FUNCTION: LEGO1 0x100c6ef0 diff --git a/assets/hdmusic/Act3Music_HD.wav b/assets/hdmusic/Act3Music_HD.wav new file mode 100644 index 00000000..0480014b --- /dev/null +++ b/assets/hdmusic/Act3Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c3e3080ea1af626bc202d091ead654ef38ac8e27779c2908dca390f3106ced3 +size 14201934 diff --git a/assets/hdmusic/BeachBlvd_Music_HD.wav b/assets/hdmusic/BeachBlvd_Music_HD.wav new file mode 100644 index 00000000..9385be34 --- /dev/null +++ b/assets/hdmusic/BeachBlvd_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1143a85549ca76ec8edccc164e8c54d803d7eb9a5bddd5c0387ce102b5397f2c +size 26919040 diff --git a/assets/hdmusic/Beach_Music_HD.wav b/assets/hdmusic/Beach_Music_HD.wav new file mode 100644 index 00000000..a198fec1 --- /dev/null +++ b/assets/hdmusic/Beach_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e84733243a6d1d3044aab8032cf5d2b5769b1e3e3f0787c3b3bf18689c9f535 +size 22490016 diff --git a/assets/hdmusic/BrickHunt_HD.wav b/assets/hdmusic/BrickHunt_HD.wav new file mode 100644 index 00000000..4aaa8721 --- /dev/null +++ b/assets/hdmusic/BrickHunt_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfbd44517186635ad270a971e9328f65890b94258c456aafda6658830147dce2 +size 33980544 diff --git a/assets/hdmusic/BrickstrChase_HD.wav b/assets/hdmusic/BrickstrChase_HD.wav new file mode 100644 index 00000000..f7222876 --- /dev/null +++ b/assets/hdmusic/BrickstrChase_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a158faeb0b651da3c0663cfa871851ad226dae02577be97d049bc4f84e4d9251 +size 14614784 diff --git a/assets/hdmusic/Cave_Music_HD.wav b/assets/hdmusic/Cave_Music_HD.wav new file mode 100644 index 00000000..b02ed9da --- /dev/null +++ b/assets/hdmusic/Cave_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ae808110329a64ae06a96b08210bea8938f92ac0006b850a62b941541339359 +size 12214400 diff --git a/assets/hdmusic/CentralRoads_Music_HD.wav b/assets/hdmusic/CentralRoads_Music_HD.wav new file mode 100644 index 00000000..43c0e5ee --- /dev/null +++ b/assets/hdmusic/CentralRoads_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39bd028528e1c9e2c9cea0b090c3c54f78b1889135a863c09080b90d3b13b3d2 +size 34113102 diff --git a/assets/hdmusic/Hospital_Music_HD.wav b/assets/hdmusic/Hospital_Music_HD.wav new file mode 100644 index 00000000..718d46c7 --- /dev/null +++ b/assets/hdmusic/Hospital_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f253c05dcf77d5b7d5109f2e95ac5af85ee7a699a8689d8c276951617bbfd0e6 +size 40701386 diff --git a/assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav b/assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav new file mode 100644 index 00000000..0320b078 --- /dev/null +++ b/assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de7bf7bc1512aa6430b35e94c952c9b815b13ceded533f458ff9806a3915f948 +size 20201550 diff --git a/assets/hdmusic/InformationCenter_Music_HD.wav b/assets/hdmusic/InformationCenter_Music_HD.wav new file mode 100644 index 00000000..f896b392 --- /dev/null +++ b/assets/hdmusic/InformationCenter_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c1ade0044cef699f526898cd44c34f426f3f057312f8d457ebc4b454096bdb0 +size 27256398 diff --git a/assets/hdmusic/JBMusic1_HD.wav b/assets/hdmusic/JBMusic1_HD.wav new file mode 100644 index 00000000..a07f7354 --- /dev/null +++ b/assets/hdmusic/JBMusic1_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c11ed75397b677a069cd9302bec3e1178067b030c42a47ef6853cb9212ff716 +size 22200448 diff --git a/assets/hdmusic/JBMusic2_HD.wav b/assets/hdmusic/JBMusic2_HD.wav new file mode 100644 index 00000000..cdb64575 --- /dev/null +++ b/assets/hdmusic/JBMusic2_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8964ca0fb9698abab90398bfa62f04e52830b647af8b4014a129edd9fbfdae47 +size 28735604 diff --git a/assets/hdmusic/JBMusic3_HD.wav b/assets/hdmusic/JBMusic3_HD.wav new file mode 100644 index 00000000..cf22253b --- /dev/null +++ b/assets/hdmusic/JBMusic3_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98b02da1c3900a9e906b88692ecb2087f2091b4af7303565eaeff242cdf7c313 +size 21653144 diff --git a/assets/hdmusic/JBMusic4_HD.wav b/assets/hdmusic/JBMusic4_HD.wav new file mode 100644 index 00000000..acb73671 --- /dev/null +++ b/assets/hdmusic/JBMusic4_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13c787e61647e22a1f4535d2491cbad5638c835142c075ebe6a39964ff769466 +size 24699000 diff --git a/assets/hdmusic/JBMusic5_HD.wav b/assets/hdmusic/JBMusic5_HD.wav new file mode 100644 index 00000000..69ca50f6 --- /dev/null +++ b/assets/hdmusic/JBMusic5_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b744d204a6bcb3c659f490fc9caa6a2bff96e6c91ab7ba8770508b208f0fffd +size 12828932 diff --git a/assets/hdmusic/JBMusic6_HD.wav b/assets/hdmusic/JBMusic6_HD.wav new file mode 100644 index 00000000..a5235fce --- /dev/null +++ b/assets/hdmusic/JBMusic6_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8779860718e07a6093c979d77ec1723dcef93762bc7759e960e70ee730b66fb3 +size 10059690 diff --git a/assets/hdmusic/Jail_Music_HD.wav b/assets/hdmusic/Jail_Music_HD.wav new file mode 100644 index 00000000..a9f323c3 --- /dev/null +++ b/assets/hdmusic/Jail_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b39644468a172178738aa8854b514580f2b098a810dba127b2f5ae25534fe87e +size 12140672 diff --git a/assets/hdmusic/JetskiRace_Music_HD.wav b/assets/hdmusic/JetskiRace_Music_HD.wav new file mode 100644 index 00000000..eac69605 --- /dev/null +++ b/assets/hdmusic/JetskiRace_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82ccad0d9877ccf915fc050d9b245785cf6bcb2406a6fcb9f6bdb9a75a3fc8c4 +size 11367404 diff --git a/assets/hdmusic/Park_Music_HD.wav b/assets/hdmusic/Park_Music_HD.wav new file mode 100644 index 00000000..83c4f4da --- /dev/null +++ b/assets/hdmusic/Park_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b645676140604d078be9cb1dc7d5f0ee17700878ff50a5280b426616898be64 +size 16089216 diff --git a/assets/hdmusic/PoliceStation_Music_HD.wav b/assets/hdmusic/PoliceStation_Music_HD.wav new file mode 100644 index 00000000..6a4dd58c --- /dev/null +++ b/assets/hdmusic/PoliceStation_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59c5ad73804bf813a852a5486e6ab13739a16542d06c736c28c164694ed424fd +size 10070544 diff --git a/assets/hdmusic/RaceTrackRoad_Music_HD.wav b/assets/hdmusic/RaceTrackRoad_Music_HD.wav new file mode 100644 index 00000000..813d33b3 --- /dev/null +++ b/assets/hdmusic/RaceTrackRoad_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:002c09354642fd8757ea999d62badf0b346f5ab1d4fb6b6ce31e471b0a6bdb1a +size 33341560 diff --git a/assets/hdmusic/ResidentalArea_Music_HD.wav b/assets/hdmusic/ResidentalArea_Music_HD.wav new file mode 100644 index 00000000..923ee0dc --- /dev/null +++ b/assets/hdmusic/ResidentalArea_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f24f80d0979e1794d42000b55e95f0674be196af94c30abfa2a8d971e106a309 +size 15794304 diff --git a/assets/main.cpp b/assets/main.cpp index e1189012..d239e013 100644 --- a/assets/main.cpp +++ b/assets/main.cpp @@ -16,30 +16,182 @@ si::MemoryBuffer mxHd; void CreateWidescreen() { + std::string result = out + "/widescreen.si"; + struct AssetView { + std::string name; + std::string extra; + }; + const AssetView widescreenBitmaps[] = { + {"GaraDoor_Background_Wide", + "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161"} + }; + si::Interleaf si; - std::string file = out + "/widescreen.si"; mxHd.seek(0, si::MemoryBuffer::SeekStart); si.Read(&mxHd); - si::Object GaraDoor_Wide; - const char extra[] = - "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161"; - GaraDoor_Wide.type_ = si::MxOb::Bitmap; - GaraDoor_Wide.flags_ = MxDSAction::c_enabled | MxDSAction::c_bit4; - GaraDoor_Wide.duration_ = -1; - GaraDoor_Wide.loops_ = 1; - GaraDoor_Wide.extra_ = si::bytearray(extra, sizeof(extra)); - GaraDoor_Wide.presenter_ = "MxStillPresenter"; - GaraDoor_Wide.name_ = "GaraDoor_Wide"; - GaraDoor_Wide.filetype_ = si::MxOb::STL; - GaraDoor_Wide.location_.x = -240.0; - GaraDoor_Wide.location_.z = -1.0; - GaraDoor_Wide.up_.y = 1.0; - GaraDoor_Wide.ReplaceWithFile("widescreen/garadoor.bmp"); - si.AppendChild(&GaraDoor_Wide); - depfile << file << ": " << (std::filesystem::current_path() / "widescreen/garadoor.bmp").string() << std::endl; + int i = 0; + for (const AssetView& asset : widescreenBitmaps) { + si::Object* object = new si::Object; + std::string file = std::string("widescreen/") + asset.name + ".bmp"; - si.Write(file.c_str()); + object->id_ = i; + object->type_ = si::MxOb::Bitmap; + object->flags_ = MxDSAction::c_enabled | MxDSAction::c_bit4; + object->duration_ = -1; + object->loops_ = 1; + object->extra_ = si::bytearray(asset.extra.c_str(), asset.extra.length() + 1); + object->presenter_ = "MxStillPresenter"; + object->name_ = asset.name; + object->filetype_ = si::MxOb::STL; + object->location_ = si::Vector3(-240.0, 0.0, -1.0); + object->direction_ = si::Vector3(0, 0, 0); + object->up_ = si::Vector3(0, 1.0, 0); + + if (!object->ReplaceWithFile(file.c_str())) { + abort(); + } + + si.AppendChild(object); + depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl; + i++; + } + + si.Write(result.c_str()); +} + +void CreateHDMusic() +{ + std::string result = out + "/hdmusic.si"; + struct AssetView { + std::string name; + std::string extra; + uint32_t duration; + uint32_t loops; + uint32_t flags; + }; + const AssetView wavAudio[] = { + {"BrickstrChase_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;3", + 82850, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"BrickHunt_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;4", + 192630, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"ResidentalArea_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;5", + 89540, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"BeachBlvd_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;6", + 152600, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Cave_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;7", + 69240, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"CentralRoads_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;8", + 193380, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Jail_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;9", + 68820, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Hospital_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;10", + 211990, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"InformationCenter_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;11", + 154510, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"PoliceStation_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;12", + 57090, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Park_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;13", + 91210, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"RaceTrackRoad_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;16", + 189000, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Beach_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;17", + 127490, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"JetskiRace_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;19", + 64440, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Act3Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;20", + 80510, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"JBMusic1_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;55", 125850, 1, MxDSAction::c_enabled}, + {"JBMusic2_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;56", 162900, 1, MxDSAction::c_enabled}, + {"JBMusic3_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;57", 122750, 1, MxDSAction::c_enabled}, + {"JBMusic4_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;58", 140000, 1, MxDSAction::c_enabled}, + {"JBMusic5_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;59", 72720, 1, MxDSAction::c_enabled}, + {"JBMusic6_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;60", 57030, 1, MxDSAction::c_enabled}, + {"InfoCenter_3rd_Floor_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;61", + 114520, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3} + }; + + si::Interleaf si; + mxHd.seek(0, si::MemoryBuffer::SeekStart); + si.Read(&mxHd); + + int i = 0; + for (const AssetView& asset : wavAudio) { + si::Object* object = new si::Object; + std::string file = std::string("hdmusic/") + asset.name + ".wav"; + + object->id_ = i; + object->type_ = si::MxOb::Sound; + object->flags_ = asset.flags; + object->duration_ = asset.duration * asset.loops; + object->loops_ = asset.loops; + object->extra_ = si::bytearray(asset.extra.c_str(), asset.extra.length() + 1); + object->presenter_ = "MxWavePresenter"; + object->name_ = asset.name; + object->filetype_ = si::MxOb::WAV; + object->location_ = si::Vector3(0, 0, 0); + object->direction_ = si::Vector3(0, 0, 1); + object->up_ = si::Vector3(0, 1, 0); + object->volume_ = 79; + + if (!object->ReplaceWithFile(file.c_str())) { + abort(); + } + + si.AppendChild(object); + depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl; + i++; + } + + si.Write(result.c_str()); } int main(int argc, char* argv[]) @@ -54,5 +206,6 @@ int main(int argc, char* argv[]) mxHd.WriteU32(bufferCount); CreateWidescreen(); + CreateHDMusic(); return 0; } diff --git a/assets/widescreen/GaraDoor_Background_Wide.bmp b/assets/widescreen/GaraDoor_Background_Wide.bmp new file mode 100644 index 00000000..8f557e6b --- /dev/null +++ b/assets/widescreen/GaraDoor_Background_Wide.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d245fde39c95f788a9406f2c4bbc94680d566cef925f03883a0824e3485c23d +size 538678 diff --git a/assets/widescreen/garadoor.bmp b/assets/widescreen/garadoor.bmp deleted file mode 100755 index 8fc4c08c120a54e7901ea383bf57a4673818b21e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538678 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

    oslVKXF`5bwN%CkrNwR9 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 diff --git a/extensions/include/extensions/siloader.h b/extensions/include/extensions/siloader.h index 3fc7ab0a..ab3e69bc 100644 --- a/extensions/include/extensions/siloader.h +++ b/extensions/include/extensions/siloader.h @@ -7,6 +7,11 @@ #include #include +namespace si +{ +class Core; +} + namespace Extensions { class SiLoader { @@ -15,8 +20,10 @@ class SiLoader { static void Initialize(); static bool Load(); - static bool StartWith(StreamObject p_object); - static bool RemoveWith(StreamObject p_object, LegoWorld* world); + static std::optional HandleFind(StreamObject p_object, LegoWorld* world); + static std::optional HandleStart(MxDSAction& p_action); + static std::optional HandleRemove(StreamObject p_object, LegoWorld* world); + static std::optional HandleDelete(MxDSAction& p_action); static std::map options; static std::vector files; @@ -25,17 +32,23 @@ class SiLoader { private: static std::vector> startWith; static std::vector> removeWith; + static std::vector> replace; static bool LoadFile(const char* p_file); + static void ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom = MxAtomId()); }; #ifdef EXTENSIONS constexpr auto Load = &SiLoader::Load; -constexpr auto StartWith = &SiLoader::StartWith; -constexpr auto RemoveWith = &SiLoader::RemoveWith; +constexpr auto HandleFind = &SiLoader::HandleFind; +constexpr auto HandleStart = &SiLoader::HandleStart; +constexpr auto HandleRemove = &SiLoader::HandleRemove; +constexpr auto HandleDelete = &SiLoader::HandleDelete; #else constexpr decltype(&SiLoader::Load) Load = nullptr; -constexpr decltype(&SiLoader::StartWith) StartWith = nullptr; -constexpr decltype(&SiLoader::RemoveWith) RemoveWith = nullptr; +constexpr decltype(&SiLoader::HandleFind) HandleFind = nullptr; +constexpr decltype(&SiLoader::HandleStart) HandleStart = nullptr; +constexpr decltype(&SiLoader::HandleRemove) HandleRemove = nullptr; +constexpr decltype(&SiLoader::HandleDelete) HandleDelete = nullptr; #endif }; // namespace Extensions diff --git a/extensions/src/siloader.cpp b/extensions/src/siloader.cpp index bfbddd88..ab23b4a7 100644 --- a/extensions/src/siloader.cpp +++ b/extensions/src/siloader.cpp @@ -13,6 +13,7 @@ std::map SiLoader::options; std::vector SiLoader::files; std::vector> SiLoader::startWith; std::vector> SiLoader::removeWith; +std::vector> SiLoader::replace; bool SiLoader::enabled = false; void SiLoader::Initialize() @@ -36,21 +37,52 @@ bool SiLoader::Load() return true; } -bool SiLoader::StartWith(StreamObject p_object) +std::optional SiLoader::HandleFind(StreamObject p_object, LegoWorld* world) { - for (const auto& key : startWith) { + for (const auto& key : replace) { if (key.first == p_object) { + return world->Find(key.second.first, key.second.second); + } + } + + return std::nullopt; +} + +std::optional SiLoader::HandleStart(MxDSAction& p_action) +{ + StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + + for (const auto& key : startWith) { + if (key.first == object) { MxDSAction action; action.SetAtomId(key.second.first); action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); Start(&action); } } - return true; + for (const auto& key : replace) { + if (key.first == object) { + MxDSAction action; + action.SetAtomId(key.second.first); + action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); + + MxResult result = Start(&action); + p_action.SetUnknown24(action.GetUnknown24()); + return result; + } + } + + return std::nullopt; } -bool SiLoader::RemoveWith(StreamObject p_object, LegoWorld* world) +std::optional SiLoader::HandleRemove(StreamObject p_object, LegoWorld* world) { for (const auto& key : removeWith) { if (key.first == p_object) { @@ -58,7 +90,47 @@ bool SiLoader::RemoveWith(StreamObject p_object, LegoWorld* world) } } - return true; + for (const auto& key : replace) { + if (key.first == p_object) { + return RemoveFromWorld(key.second.first, key.second.second, world->GetAtomId(), world->GetEntityId()); + } + } + + return std::nullopt; +} + +std::optional SiLoader::HandleDelete(MxDSAction& p_action) +{ + StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + + for (const auto& key : removeWith) { + if (key.first == object) { + MxDSAction action; + action.SetAtomId(key.second.first); + action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); + DeleteObject(action); + } + } + + for (const auto& key : replace) { + if (key.first == object) { + MxDSAction action; + action.SetAtomId(key.second.first); + action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); + + DeleteObject(action); + p_action.SetUnknown24(action.GetUnknown24()); + return TRUE; + } + } + + return std::nullopt; } bool SiLoader::LoadFile(const char* p_file) @@ -68,10 +140,11 @@ bool SiLoader::LoadFile(const char* p_file) MxString path = MxString(MxOmni::GetHD()) + p_file; path.MapPathToFilesystem(); - if (si.Read(path.GetData()) != si::Interleaf::ERROR_SUCCESS) { + if (si.Read(path.GetData(), si::Interleaf::ObjectsOnly | si::Interleaf::NoInfo) != si::Interleaf::ERROR_SUCCESS) { path = MxString(MxOmni::GetCD()) + p_file; path.MapPathToFilesystem(); - if (si.Read(path.GetData()) != si::Interleaf::ERROR_SUCCESS) { + if (si.Read(path.GetData(), si::Interleaf::ObjectsOnly | si::Interleaf::NoInfo) != + si::Interleaf::ERROR_SUCCESS) { SDL_Log("Could not parse SI file %s", p_file); return false; } @@ -82,7 +155,15 @@ bool SiLoader::LoadFile(const char* p_file) return false; } - for (si::Core* child : si.GetChildren()) { + ParseDirectives(controller->GetAtom(), &si); + return true; +} + +void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom) +{ + for (si::Core* child : p_core->GetChildren()) { + MxAtomId replacedAtom = p_parentReplacedAtom; + if (si::Object* object = dynamic_cast(child)) { if (object->type() != si::MxOb::Null) { std::string extra(object->extra_.data(), object->extra_.size()); @@ -94,7 +175,7 @@ bool SiLoader::LoadFile(const char* p_file) if (SDL_sscanf(directive, "StartWith:%255[^;];%d", atom, &id) == 2) { startWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, - StreamObject{controller->GetAtom(), object->id_} + StreamObject{p_atom, object->id_} ); } } @@ -103,13 +184,31 @@ bool SiLoader::LoadFile(const char* p_file) if (SDL_sscanf(directive, "RemoveWith:%255[^;];%d", atom, &id) == 2) { removeWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, - StreamObject{controller->GetAtom(), object->id_} + StreamObject{p_atom, object->id_} ); } } + + if (p_parentReplacedAtom.GetInternal()) { + replace.emplace_back( + StreamObject{p_parentReplacedAtom, object->id_}, + StreamObject{p_atom, object->id_} + ); + } + else { + if ((directive = SDL_strstr(extra.c_str(), "Replace:"))) { + if (SDL_sscanf(directive, "Replace:%255[^;];%d", atom, &id) == 2) { + replace.emplace_back( + StreamObject{MxAtomId{atom, e_lowerCase2}, id}, + StreamObject{p_atom, object->id_} + ); + replacedAtom = replace.back().first.first; + } + } + } } } - } - return true; + ParseDirectives(p_atom, child, replacedAtom); + } }