Compare commits
252 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d204de47bf | |||
| 23dc0cdbf0 | |||
| 861efaa053 | |||
| 8e41e0195e | |||
| e9359a92d7 | |||
| 0c78376ba8 | |||
| 8c001f7968 | |||
| ef43b94370 | |||
| 76efa02beb | |||
| aeeb1ccdd2 | |||
| 93c7d0d64d | |||
| 1b76b2650c | |||
| 45196886b0 | |||
| 80af15c95b | |||
| 4c5e3b933e | |||
| 5eddcb9653 | |||
| 6dd50c955c | |||
| 4db65b9845 | |||
| ede6827369 | |||
| 2c9b20841d | |||
| 2b9ed729a3 | |||
| f39195c5e0 | |||
| a0f42c0c80 | |||
| d1e9d5af2f | |||
| 61b2e6ce4d | |||
| e73244bca5 | |||
| a4f25ecb28 | |||
| 39b546b81f | |||
| 1bd4585994 | |||
| c896bb39ea | |||
| 3366613354 | |||
| 79b1f4ab4d | |||
| 157f4f9df2 | |||
| 9d5d8dd13a | |||
| b0f1fbee1c | |||
| 40e3f7d057 | |||
| 764fc0b96f | |||
| 1b4a842eec | |||
| 08c7261262 | |||
| 07b440a1c9 | |||
| e7ab978a30 | |||
| 3a02e129e7 | |||
| 93c6106770 | |||
| b08d994e32 | |||
| 0665a78c84 | |||
| 4db8b51f24 | |||
| 800da8dbff | |||
| a0ecdb1735 | |||
| c948bffd32 | |||
| 4bcf4ca354 | |||
| bfd9917ca1 | |||
| 7f6212f9b7 | |||
| 80245387f3 | |||
| 0a1fea4bc7 | |||
| 4ec7b01213 | |||
| 5187fe90c3 | |||
| a86fa9c162 | |||
| 4ec8c1aaee | |||
| 97d032f8b5 | |||
| 286532904a | |||
| 04b5861f29 | |||
| 453e958068 | |||
| e7d2fbcc0b | |||
| 8f71c70d14 | |||
| df23edcb69 | |||
| daff157027 | |||
| 0c23bd4332 | |||
| 7562486449 | |||
| 5e08b810fc | |||
| c66cccf660 | |||
| 3b1118229b | |||
| 491da372a1 | |||
| a2f463d146 | |||
| 63b3ce4849 | |||
| 8280ac00a0 | |||
| 45ef0d72b1 | |||
| ad9c460ec9 | |||
| 23dc9bc39a | |||
| bf23d44389 | |||
| d0b9b6d10f | |||
| 2f83753260 | |||
| eeb0ad77a4 | |||
| 594cadcf7d | |||
| 4290726691 | |||
| 80dd5ff278 | |||
| 08efb9a3cf | |||
| d8a1dd1da4 | |||
| 13dd3c3932 | |||
| dd7885da9c | |||
| 5a05433a2b | |||
| c3ff3884d7 | |||
| e42c4d3174 | |||
| 06c77a6818 | |||
| 4d4a80891f | |||
| 71c892368d | |||
| d2a1dda523 | |||
| 78179eb93f | |||
| 34e10d3844 | |||
| 65e8577253 | |||
| a4fcc10f5f | |||
| a2c2988666 | |||
| abec043249 | |||
| 699d069b0a | |||
| 4d67033ff8 | |||
| 84ffd67622 | |||
| 1c85ee63eb | |||
| 81c7213993 | |||
| be82e606b2 | |||
| 44da1a9f7d | |||
| d0f8ea56f9 | |||
| e472b36cef | |||
| 3934e09c8f | |||
| 3136816ce9 | |||
| 6fd3762ffc | |||
| 97a1190713 | |||
| 73a3bd9ae8 | |||
| fc533dbdc7 | |||
| 6217e071d2 | |||
| 673ca7f686 | |||
| 29a1cff7ea | |||
| 3c5152a67b | |||
| 1c1a849095 | |||
| 89d393e57a | |||
| 2f27687d80 | |||
| 167a50c01d | |||
| 313f03f5e5 | |||
| 6cfdc3d8a3 | |||
| 9c24a0bc4b | |||
| b99ad920c4 | |||
| 912b18eca1 | |||
| 9e651a51db | |||
| c8b6e997a7 | |||
| 928e187524 | |||
| cf12d19860 | |||
| e3a3ac56fb | |||
| d15ed81cd5 | |||
| ff054f6f47 | |||
| 667cf70fa0 | |||
| 73eb401c93 | |||
| b11f3add06 | |||
| 92f888a152 | |||
| 78ed5cc716 | |||
| 3968b67c5d | |||
| 119830c979 | |||
| 647394c875 | |||
| c50d777309 | |||
| 4954685cca | |||
| 39d43a8d8f | |||
| b4761cc54e | |||
| 9aee2e9cfe | |||
| 7c7c8b228e | |||
| ca7714dafd | |||
| 1ad5ab8632 | |||
| c4e7838089 | |||
| 7313712263 | |||
| aa48d95f24 | |||
| c5baabbe9c | |||
| 1d2716139f | |||
| 742ad2c150 | |||
| 18eb0692f0 | |||
| b5f98f69db | |||
| 47593d0eb4 | |||
| 4404fce369 | |||
| 72c20f4dd0 | |||
| 3240885bfd | |||
| 7f0955f022 | |||
| c21bce0093 | |||
| 4e23472ed5 | |||
| cfe1f2304b | |||
| de6568d750 | |||
| 3aafe7fa16 | |||
| c1e65d19e7 | |||
| 5fdf954994 | |||
| 230868af3c | |||
| 66154d9de8 | |||
| 93ec3c7dbd | |||
| 945ce3e4bc | |||
| 782a8573e9 | |||
| 4e0ca51159 | |||
| 3cd160e1b2 | |||
| 93a236a9d2 | |||
| eaf3bc2f40 | |||
| 5ca0a2ba06 | |||
| ccd2bdbaac | |||
| 08321699cd | |||
| 7300c0e0f5 | |||
| 7e562824fe | |||
| ed8b5c96b9 | |||
| 14bccdffa6 | |||
| 39e465bcab | |||
| e53bb3a12d | |||
| 50fccd393f | |||
| 7993740ac8 | |||
| 8e5bb8ae59 | |||
| 8fefdd4114 | |||
| 64c8cee21b | |||
| 25fe686573 | |||
| 1c1ea98fdd | |||
| e098104f8f | |||
| 3c5ade5565 | |||
| b2ad75027e | |||
| fdfbf83b88 | |||
| 1b9ca0949e | |||
| 827037f0fa | |||
| d84c5790f5 | |||
| 49eb2282af | |||
| 741f9ecfab | |||
| 37b8122962 | |||
| 5f2cf68e80 | |||
| 74f2c58b29 | |||
| 208433047a | |||
| 2c01430035 | |||
| 5121437bcf | |||
| f61bd3e5ad | |||
| 835e409b32 | |||
| 010bdb7e25 | |||
| 7ba22b7714 | |||
| efcb19a3d0 | |||
| 55455bb1b5 | |||
| e49be12297 | |||
| 75f4940f5e | |||
| 8047330952 | |||
| 9105dcb078 | |||
| b8e38e03e2 | |||
| 331352878e | |||
| 62a88f1e9a | |||
| 43b603e70b | |||
| 95e6ac54cf | |||
| c4b2e2e501 | |||
| dccba23980 | |||
| bf27d10519 | |||
| 6c27011e32 | |||
| 6220990dc5 | |||
| 93e9767c9f | |||
| c774f53dad | |||
| 7fbfe5ad88 | |||
| ef02037990 | |||
| 23cc18ba0e | |||
| 924dbc7715 | |||
| 742f4938f2 | |||
| 02e0f586d3 | |||
| 5717aeef85 | |||
| da9b99f650 | |||
| 901ce2ee4c | |||
| dd2b993cd5 | |||
| 83577d3b82 | |||
| 44f3828f68 | |||
| 3f560b060c | |||
| eaae8b6137 | |||
| 24b468f2a2 | |||
| d9a0ef760f | |||
| 95470b830f |
@@ -8,7 +8,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# SCCACHE_GHA_ENABLED: "true"
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
RUSTC_WRAPPER: "sccache"
|
RUSTC_WRAPPER: "sccache"
|
||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: GCC x86_64
|
- name: GCC x86_64
|
||||||
runner: [self-hosted, Linux]
|
runner: ubuntu-latest
|
||||||
preset: gcc
|
preset: gcc
|
||||||
artifact_arch: x86_64
|
artifact_arch: x86_64
|
||||||
# - name: GCC aarch64
|
# - name: GCC aarch64
|
||||||
@@ -41,7 +41,6 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: 'false' # disabled for self-hosted
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install ninja-build clang lld openssl libcurl4-openssl-dev \
|
sudo apt-get -y install ninja-build clang lld openssl libcurl4-openssl-dev \
|
||||||
@@ -51,8 +50,7 @@ jobs:
|
|||||||
libxss-dev libfuse2 libusb-1.0-0-dev libdecor-0-dev libpipewire-0.3-dev libunwind-dev
|
libxss-dev libfuse2 libusb-1.0-0-dev libdecor-0-dev libpipewire-0.3-dev libunwind-dev
|
||||||
|
|
||||||
- name: Setup sccache
|
- name: Setup sccache
|
||||||
if: 'false' # disabled for self-hosted
|
uses: mozilla-actions/sccache-action@v0.0.10
|
||||||
uses: mozilla-actions/sccache-action@v0.0.9
|
|
||||||
|
|
||||||
- name: Print sccache stats
|
- name: Print sccache stats
|
||||||
run: sccache --show-stats
|
run: sccache --show-stats
|
||||||
@@ -67,17 +65,16 @@ jobs:
|
|||||||
run: ci/build-appimage.sh
|
run: ci/build-appimage.sh
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-linux-${{matrix.preset}}-${{matrix.artifact_arch}}
|
name: dusklight-${{env.DUSK_VERSION}}-linux-${{matrix.preset}}-${{matrix.artifact_arch}}
|
||||||
path: |
|
path: |
|
||||||
build/install/Dusk-*.AppImage
|
build/install/Dusklight-*.AppImage
|
||||||
build/install/debug.tar.*
|
build/install/debug.tar.*
|
||||||
|
|
||||||
build-apple:
|
build-apple:
|
||||||
name: Build Apple (${{matrix.name}})
|
name: Build Apple (${{matrix.name}})
|
||||||
runs-on: [self-hosted, macOS]
|
runs-on: macos-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -86,14 +83,14 @@ jobs:
|
|||||||
platform: macos
|
platform: macos
|
||||||
preset: x-macos-ci-arm64
|
preset: x-macos-ci-arm64
|
||||||
artifact_name: macos-appleclang-arm64
|
artifact_name: macos-appleclang-arm64
|
||||||
# - name: AppleClang macOS x86_64
|
- name: AppleClang macOS x86_64
|
||||||
# platform: macos
|
platform: macos
|
||||||
# preset: x-macos-ci-x86_64
|
preset: x-macos-ci-x86_64
|
||||||
# artifact_name: macos-appleclang-x86_64
|
artifact_name: macos-appleclang-x86_64
|
||||||
# - name: AppleClang iOS arm64
|
- name: AppleClang iOS arm64
|
||||||
# platform: ios
|
platform: ios
|
||||||
# preset: x-ios-ci
|
preset: x-ios-ci
|
||||||
# artifact_name: ios-appleclang-arm64
|
artifact_name: ios-appleclang-arm64
|
||||||
# - name: AppleClang tvOS arm64
|
# - name: AppleClang tvOS arm64
|
||||||
# platform: tvos
|
# platform: tvos
|
||||||
# preset: x-tvos-ci
|
# preset: x-tvos-ci
|
||||||
@@ -106,7 +103,6 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: 'false'
|
|
||||||
run: brew install cmake ninja
|
run: brew install cmake ninja
|
||||||
|
|
||||||
- name: Install Rust iOS target
|
- name: Install Rust iOS target
|
||||||
@@ -128,7 +124,7 @@ jobs:
|
|||||||
rustup target add x86_64-apple-darwin
|
rustup target add x86_64-apple-darwin
|
||||||
|
|
||||||
- name: Setup sccache
|
- name: Setup sccache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.9
|
uses: mozilla-actions/sccache-action@v0.0.10
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
run: cmake --preset ${{matrix.preset}}
|
run: cmake --preset ${{matrix.preset}}
|
||||||
@@ -137,14 +133,80 @@ jobs:
|
|||||||
run: cmake --build --preset ${{matrix.preset}}
|
run: cmake --build --preset ${{matrix.preset}}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-${{matrix.artifact_name}}
|
name: dusklight-${{env.DUSK_VERSION}}-${{matrix.artifact_name}}
|
||||||
path: |
|
path: |
|
||||||
build/install/Dusk.app
|
build/install/Dusklight.app
|
||||||
build/install/debug.tar.*
|
build/install/debug.tar.*
|
||||||
|
|
||||||
|
build-android:
|
||||||
|
name: Build Android (${{matrix.name}})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: Clang arm64-v8a
|
||||||
|
preset: x-android-ci-arm64
|
||||||
|
abi: arm64-v8a
|
||||||
|
artifact_arch: arm64
|
||||||
|
rust_target: aarch64-linux-android
|
||||||
|
|
||||||
|
env:
|
||||||
|
ANDROID_NDK_VERSION: "29.0.14206865"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get -y install ninja-build
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v5
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 17
|
||||||
|
|
||||||
|
- name: Setup Android SDK
|
||||||
|
uses: android-actions/setup-android@v4
|
||||||
|
|
||||||
|
- name: Install Android SDK packages
|
||||||
|
run: sdkmanager "platforms;android-36" "build-tools;36.1.0" "ndk;${ANDROID_NDK_VERSION}"
|
||||||
|
|
||||||
|
- name: Install Rust Android target
|
||||||
|
run: |
|
||||||
|
rustup toolchain install stable
|
||||||
|
rustup target add ${{matrix.rust_target}}
|
||||||
|
|
||||||
|
- name: Setup sccache
|
||||||
|
uses: mozilla-actions/sccache-action@v0.0.10
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: cmake --preset ${{matrix.preset}}
|
||||||
|
|
||||||
|
- name: Build native library
|
||||||
|
run: cmake --build --preset ${{matrix.preset}} --target dusklight
|
||||||
|
|
||||||
|
- name: Stage stripped JNI library
|
||||||
|
run: ANDROID_STAGE_ABIS="${{matrix.abi}}" platforms/android/scripts/stage-jni-libs.sh
|
||||||
|
|
||||||
|
- name: Build APK
|
||||||
|
working-directory: platforms/android
|
||||||
|
run: ./gradlew :app:assembleRelease --rerun-tasks
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: dusklight-${{env.DUSK_VERSION}}-android-${{matrix.artifact_arch}}
|
||||||
|
path: platforms/android/app/build/outputs/apk/release/app-${{matrix.abi}}-release-unsigned.apk
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
name: Build Windows (${{matrix.name}})
|
name: Build Windows (${{matrix.name}})
|
||||||
runs-on: ${{matrix.runner}}
|
runs-on: ${{matrix.runner}}
|
||||||
@@ -154,7 +216,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: MSVC x86_64
|
- name: MSVC x86_64
|
||||||
runner: [self-hosted, Windows]
|
runner: windows-latest
|
||||||
preset: msvc
|
preset: msvc
|
||||||
msvc_arch: amd64
|
msvc_arch: amd64
|
||||||
vcpkg_arch: x64
|
vcpkg_arch: x64
|
||||||
@@ -191,7 +253,6 @@ jobs:
|
|||||||
uses: mozilla-actions/sccache-action@v0.0.9
|
uses: mozilla-actions/sccache-action@v0.0.9
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: 'false' # disabled for self-hosted
|
|
||||||
run: |
|
run: |
|
||||||
choco install ninja
|
choco install ninja
|
||||||
vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static
|
vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static
|
||||||
@@ -203,10 +264,9 @@ jobs:
|
|||||||
run: cmake --build --preset x-windows-ci-${{matrix.preset}}
|
run: cmake --build --preset x-windows-ci-${{matrix.preset}}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-win32-msvc-${{matrix.artifact_arch}}
|
name: dusklight-${{env.DUSK_VERSION}}-win32-msvc-${{matrix.artifact_arch}}
|
||||||
path: |
|
path: |
|
||||||
build/install/*.exe
|
build/install/*.exe
|
||||||
build/install/*.dll
|
build/install/*.dll
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ compile_commands.json
|
|||||||
# MacOS
|
# MacOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# direnv / nix
|
||||||
|
.direnv/
|
||||||
|
.envrc
|
||||||
|
|
||||||
# ISOs
|
# ISOs
|
||||||
*.iso
|
*.iso
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "(gdb) Launch Dusk MSVC",
|
"name": "(gdb) Launch Dusklight MSVC",
|
||||||
"type": "cppvsdbg",
|
"type": "cppvsdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${command:cmake.launchTargetPath}",
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cmake.buildDirectory": "${workspaceFolder}/build/dusk/${buildType}/${variant:tp_version}",
|
"cmake.buildDirectory": "${workspaceFolder}/build/dusklight/${buildType}/${variant:tp_version}",
|
||||||
"cmake.generator": "Ninja",
|
"cmake.generator": "Ninja",
|
||||||
"cmake.configureSettings": {
|
"cmake.configureSettings": {
|
||||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||||
|
|||||||
@@ -48,13 +48,15 @@ else ()
|
|||||||
message(STATUS "Unable to find git, commit information will not be available")
|
message(STATUS "Unable to find git, commit information will not be available")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)(-([0-9]+).*)?$")
|
if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)([-+].*)?$")
|
||||||
set(DUSK_SHORT_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
|
set(DUSK_SHORT_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
|
||||||
if (CMAKE_MATCH_5)
|
set(DUSK_VERSION_TWEAK "0")
|
||||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${CMAKE_MATCH_5}")
|
if (DUSK_WC_DESCRIBE MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+-([0-9]+)(-dirty)?$")
|
||||||
else ()
|
set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}")
|
||||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.0")
|
elseif (DUSK_WC_DESCRIBE MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+-[0-9A-Za-z.-]+-([0-9]+)(-dirty)?$")
|
||||||
|
set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}")
|
||||||
endif ()
|
endif ()
|
||||||
|
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${DUSK_VERSION_TWEAK}")
|
||||||
else ()
|
else ()
|
||||||
set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION")
|
set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION")
|
||||||
set(DUSK_VERSION_STRING "0.0.0.0")
|
set(DUSK_VERSION_STRING "0.0.0.0")
|
||||||
@@ -65,11 +67,11 @@ endif ()
|
|||||||
if(DEFINED ENV{GITHUB_ENV})
|
if(DEFINED ENV{GITHUB_ENV})
|
||||||
file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n")
|
file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Dusk version set to ${DUSK_WC_DESCRIBE}")
|
message(STATUS "Dusklight version set to ${DUSK_WC_DESCRIBE}")
|
||||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
project(dusk LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING})
|
project(dusklight LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING})
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
enable_language(OBJC)
|
enable_language(OBJC OBJCXX)
|
||||||
endif ()
|
endif ()
|
||||||
if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS)
|
if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS)
|
||||||
# ios.toolchain.cmake hack for SDL
|
# ios.toolchain.cmake hack for SDL
|
||||||
@@ -102,31 +104,39 @@ set(AURORA_ENABLE_DVD ON CACHE BOOL "Enable DVD API support" FORCE)
|
|||||||
set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE)
|
set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE)
|
||||||
set(AURORA_ENABLE_RMLUI ON CACHE BOOL "Enable RmlUi UI support" FORCE)
|
set(AURORA_ENABLE_RMLUI ON CACHE BOOL "Enable RmlUi UI support" FORCE)
|
||||||
add_subdirectory(extern/aurora EXCLUDE_FROM_ALL)
|
add_subdirectory(extern/aurora EXCLUDE_FROM_ALL)
|
||||||
|
target_compile_definitions(aurora_mtx PRIVATE MTX_USE_PS=1)
|
||||||
|
|
||||||
add_subdirectory(libs/freeverb)
|
add_subdirectory(libs/freeverb)
|
||||||
|
|
||||||
option(DUSK_BUILD_WARNINGS "Enable compiler warnings (off by default)")
|
option(DUSK_BUILD_WARNINGS "Enable compiler warnings (off by default)")
|
||||||
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
|
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
|
||||||
option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON)
|
option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON)
|
||||||
|
option(DUSK_ENABLE_UPDATE_CHECKER "Enable update checking support" ON)
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
set(DUSK_MOVIE_SUPPORT OFF)
|
set(DUSK_MOVIE_SUPPORT OFF)
|
||||||
set(NOD_COMPRESS_BZIP2 OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NOD_COMPRESS_LZMA OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NOD_COMPRESS_ZLIB OFF CACHE BOOL "" FORCE)
|
|
||||||
set(NOD_COMPRESS_ZSTD OFF CACHE BOOL "" FORCE)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
|
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
|
||||||
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
|
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
|
||||||
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
|
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
|
||||||
|
|
||||||
|
# Edit & Continue
|
||||||
|
if (MSVC)
|
||||||
|
if ("${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT}" STREQUAL "" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")
|
||||||
|
endif ()
|
||||||
|
if (CMAKE_MSVC_DEBUG_INFORMATION_FORMAT STREQUAL "EditAndContinue")
|
||||||
|
add_link_options("/INCREMENTAL")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (DUSK_MOVIE_SUPPORT)
|
if (DUSK_MOVIE_SUPPORT)
|
||||||
find_package(libjpeg-turbo 3.0 CONFIG QUIET)
|
find_package(libjpeg-turbo 3.0 CONFIG QUIET)
|
||||||
if (libjpeg-turbo_FOUND)
|
if (libjpeg-turbo_FOUND)
|
||||||
message(STATUS "dusk: Using system libjpeg-turbo")
|
message(STATUS "dusklight: Using system libjpeg-turbo")
|
||||||
else ()
|
else ()
|
||||||
message(STATUS "dusk: Fetching libjpeg-turbo")
|
message(STATUS "dusklight: Fetching libjpeg-turbo")
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
set(_jpeg_install_dir ${CMAKE_BINARY_DIR}/libjpeg-turbo-install)
|
set(_jpeg_install_dir ${CMAKE_BINARY_DIR}/libjpeg-turbo-install)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -150,6 +160,7 @@ if (DUSK_MOVIE_SUPPORT)
|
|||||||
CMAKE_C_COMPILER_LAUNCHER
|
CMAKE_C_COMPILER_LAUNCHER
|
||||||
CMAKE_MAKE_PROGRAM
|
CMAKE_MAKE_PROGRAM
|
||||||
CMAKE_MSVC_RUNTIME_LIBRARY
|
CMAKE_MSVC_RUNTIME_LIBRARY
|
||||||
|
CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
||||||
CMAKE_OSX_ARCHITECTURES
|
CMAKE_OSX_ARCHITECTURES
|
||||||
DEPLOYMENT_TARGET
|
DEPLOYMENT_TARGET
|
||||||
ENABLE_ARC
|
ENABLE_ARC
|
||||||
@@ -215,13 +226,13 @@ endif ()
|
|||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
# Declare all dependencies first so CMake can download them in parallel
|
# Declare all dependencies first so CMake can download them in parallel
|
||||||
message(STATUS "dusk: Fetching cxxopts")
|
message(STATUS "dusklight: Fetching cxxopts")
|
||||||
FetchContent_Declare(cxxopts
|
FetchContent_Declare(cxxopts
|
||||||
URL https://github.com/jarro2783/cxxopts/archive/refs/tags/v3.3.1.tar.gz
|
URL https://github.com/jarro2783/cxxopts/archive/refs/tags/v3.3.1.tar.gz
|
||||||
URL_HASH SHA256=3bfc70542c521d4b55a46429d808178916a579b28d048bd8c727ee76c39e2072
|
URL_HASH SHA256=3bfc70542c521d4b55a46429d808178916a579b28d048bd8c727ee76c39e2072
|
||||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||||
)
|
)
|
||||||
message(STATUS "dusk: Fetching nlohmann/json")
|
message(STATUS "dusklight: Fetching nlohmann/json")
|
||||||
FetchContent_Declare(json
|
FetchContent_Declare(json
|
||||||
URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
|
URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
|
||||||
URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa
|
URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa
|
||||||
@@ -230,7 +241,7 @@ FetchContent_Declare(json
|
|||||||
FetchContent_MakeAvailable(cxxopts json)
|
FetchContent_MakeAvailable(cxxopts json)
|
||||||
|
|
||||||
if (DUSK_ENABLE_SENTRY_NATIVE)
|
if (DUSK_ENABLE_SENTRY_NATIVE)
|
||||||
message(STATUS "dusk: Fetching sentry-native")
|
message(STATUS "dusklight: Fetching sentry-native")
|
||||||
set(SENTRY_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
set(SENTRY_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
set(SENTRY_BACKEND crashpad CACHE STRING "" FORCE)
|
set(SENTRY_BACKEND crashpad CACHE STRING "" FORCE)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -275,17 +286,17 @@ include(files.cmake)
|
|||||||
|
|
||||||
# TODO: version handling for res includes
|
# TODO: version handling for res includes
|
||||||
|
|
||||||
set(DUSK_BUNDLE_NAME Dusk)
|
set(DUSK_BUNDLE_NAME Dusklight)
|
||||||
set(DUSK_BUNDLE_IDENTIFIER dev.twilitrealm.dusk)
|
set(DUSK_BUNDLE_IDENTIFIER dev.twilitrealm.dusk)
|
||||||
set(DUSK_COMPANY_NAME "Twilit Realm")
|
set(DUSK_COMPANY_NAME "Twilit Realm")
|
||||||
set(DUSK_FILE_DESCRIPTION "Dusk")
|
set(DUSK_FILE_DESCRIPTION "Dusklight")
|
||||||
set(DUSK_PRODUCT_NAME "Dusk")
|
set(DUSK_PRODUCT_NAME "Dusklight")
|
||||||
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
|
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
|
||||||
|
|
||||||
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
|
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
|
||||||
source_group("dusk" FILES ${DUSK_FILES})
|
source_group("dusklight" FILES ${DUSK_FILES} ${DUSK_HTTP_BACKEND_FILES})
|
||||||
|
|
||||||
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0)
|
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1)
|
||||||
|
|
||||||
set(GAME_INCLUDE_DIRS
|
set(GAME_INCLUDE_DIRS
|
||||||
include
|
include
|
||||||
@@ -297,8 +308,10 @@ set(GAME_INCLUDE_DIRS
|
|||||||
extern
|
extern
|
||||||
${CMAKE_BINARY_DIR})
|
${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
set(GAME_LIBS aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd
|
set(GAME_LIBS aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd
|
||||||
aurora::card freeverb cxxopts::cxxopts absl::flat_hash_map nlohmann_json::nlohmann_json TracyClient fmt::fmt)
|
aurora::card freeverb cxxopts::cxxopts absl::flat_hash_map nlohmann_json::nlohmann_json TracyClient fmt::fmt
|
||||||
|
Threads::Threads)
|
||||||
|
|
||||||
list(APPEND GAME_LIBS libzstd_static)
|
list(APPEND GAME_LIBS libzstd_static)
|
||||||
|
|
||||||
@@ -311,6 +324,41 @@ if (WIN32)
|
|||||||
list(APPEND GAME_LIBS Ws2_32)
|
list(APPEND GAME_LIBS Ws2_32)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/no_backend.cpp)
|
||||||
|
if (DUSK_ENABLE_UPDATE_CHECKER)
|
||||||
|
list(APPEND GAME_COMPILE_DEFS DUSK_ENABLE_UPDATE_CHECKER=1)
|
||||||
|
if (WIN32)
|
||||||
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/winhttp.cpp)
|
||||||
|
list(APPEND GAME_LIBS winhttp)
|
||||||
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_WINHTTP=1)
|
||||||
|
message(STATUS "dusklight: Enabled update checker (WinHTTP)")
|
||||||
|
elseif (ANDROID)
|
||||||
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/android.cpp)
|
||||||
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_ANDROID=1)
|
||||||
|
message(STATUS "dusklight: Enabled update checker (Android)")
|
||||||
|
elseif (APPLE)
|
||||||
|
find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED)
|
||||||
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/url_session.mm)
|
||||||
|
set_source_files_properties(src/dusk/http/url_session.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
list(APPEND GAME_LIBS ${FOUNDATION_FRAMEWORK})
|
||||||
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_URLSESSION=1)
|
||||||
|
message(STATUS "dusklight: Enabled update checker (NSURLSession)")
|
||||||
|
elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||||
|
find_package(CURL QUIET OPTIONAL_COMPONENTS HTTPS SSL)
|
||||||
|
if (CURL_FOUND AND CURL_HTTPS_FOUND AND CURL_SSL_FOUND)
|
||||||
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/curl.cpp)
|
||||||
|
list(APPEND GAME_LIBS CURL::libcurl)
|
||||||
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_LIBCURL=1)
|
||||||
|
message(STATUS "dusklight: Enabled update checker (libcurl)")
|
||||||
|
else ()
|
||||||
|
message(STATUS "dusklight: Disabled update checker (libcurl + HTTPS/SSL not found)")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
message(STATUS "dusklight: Disabled update checker (unsupported platform)")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
list(APPEND DUSK_FILES ${DUSK_HTTP_BACKEND_SOURCE})
|
||||||
|
|
||||||
if (DUSK_MOVIE_SUPPORT)
|
if (DUSK_MOVIE_SUPPORT)
|
||||||
if (TARGET libjpeg-turbo::turbojpeg-static)
|
if (TARGET libjpeg-turbo::turbojpeg-static)
|
||||||
list(APPEND GAME_LIBS libjpeg-turbo::turbojpeg-static)
|
list(APPEND GAME_LIBS libjpeg-turbo::turbojpeg-static)
|
||||||
@@ -320,56 +368,13 @@ if (DUSK_MOVIE_SUPPORT)
|
|||||||
list(APPEND GAME_COMPILE_DEFS MOVIE_SUPPORT=1)
|
list(APPEND GAME_COMPILE_DEFS MOVIE_SUPPORT=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
option(DUSK_ENABLE_DISCORD_RPC "Enable Discord Rich Presence support" ON)
|
set(DUSK_ENABLE_DISCORD_DEFAULT ON)
|
||||||
if (DUSK_ENABLE_DISCORD_RPC AND NOT ANDROID AND NOT IOS AND NOT TVOS)
|
if (DEFINED DUSK_ENABLE_DISCORD_RPC AND NOT DEFINED DUSK_ENABLE_DISCORD)
|
||||||
|
set(DUSK_ENABLE_DISCORD_DEFAULT ${DUSK_ENABLE_DISCORD_RPC})
|
||||||
FetchContent_Populate(discord_rpc
|
|
||||||
URL https://github.com/discord/discord-rpc/archive/refs/tags/v3.4.0.tar.gz
|
|
||||||
URL_HASH SHA256=e13427019027acd187352dacba6c65953af66fdf3c35fcf38fc40b454a9d7855
|
|
||||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
||||||
)
|
|
||||||
# RapidJSON is a git submodule absent from the discord-rpc tarball; fetch separately.
|
|
||||||
FetchContent_Populate(rapidjson
|
|
||||||
URL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.tar.gz
|
|
||||||
URL_HASH SHA256=bf7ced29704a1e696fbccf2a2b4ea068e7774fa37f6d7dd4039d0787f8bed98e
|
|
||||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
||||||
)
|
|
||||||
|
|
||||||
if (NOT TARGET discord-rpc)
|
|
||||||
set(_drpc ${discord_rpc_SOURCE_DIR}/src)
|
|
||||||
set(_drpc_src
|
|
||||||
${_drpc}/discord_rpc.cpp
|
|
||||||
${_drpc}/rpc_connection.cpp
|
|
||||||
${_drpc}/serialization.cpp
|
|
||||||
)
|
|
||||||
if (WIN32)
|
|
||||||
list(APPEND _drpc_src ${_drpc}/connection_win.cpp ${_drpc}/discord_register_win.cpp)
|
|
||||||
elseif (APPLE)
|
|
||||||
list(APPEND _drpc_src ${_drpc}/connection_unix.cpp ${_drpc}/discord_register_osx.m)
|
|
||||||
else ()
|
|
||||||
list(APPEND _drpc_src ${_drpc}/connection_unix.cpp ${_drpc}/discord_register_linux.cpp)
|
|
||||||
endif ()
|
|
||||||
add_library(discord-rpc STATIC ${_drpc_src})
|
|
||||||
target_include_directories(discord-rpc PUBLIC
|
|
||||||
${discord_rpc_SOURCE_DIR}/include
|
|
||||||
${rapidjson_SOURCE_DIR}/include
|
|
||||||
)
|
|
||||||
if (UNIX)
|
|
||||||
target_link_libraries(discord-rpc PUBLIC pthread)
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
list(APPEND GAME_LIBS discord-rpc)
|
|
||||||
list(APPEND GAME_COMPILE_DEFS DUSK_DISCORD_RPC=1)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Edit & Continue
|
|
||||||
if (MSVC)
|
|
||||||
if ("${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT}" STREQUAL "" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")
|
|
||||||
endif ()
|
|
||||||
if (CMAKE_MSVC_DEBUG_INFORMATION_FORMAT STREQUAL "EditAndContinue")
|
|
||||||
add_link_options("/INCREMENTAL")
|
|
||||||
endif ()
|
endif ()
|
||||||
|
option(DUSK_ENABLE_DISCORD "Enable Discord Rich Presence support" ${DUSK_ENABLE_DISCORD_DEFAULT})
|
||||||
|
if (DUSK_ENABLE_DISCORD AND NOT ANDROID AND NOT IOS AND NOT TVOS)
|
||||||
|
list(APPEND GAME_COMPILE_DEFS DUSK_DISCORD=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
@@ -427,31 +432,37 @@ endif ()
|
|||||||
|
|
||||||
set(DUSK_FILES src/dusk/main.cpp ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
|
set(DUSK_FILES src/dusk/main.cpp ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
add_library(dusk SHARED ${DUSK_FILES})
|
add_library(dusklight SHARED ${DUSK_FILES})
|
||||||
set_target_properties(dusk PROPERTIES OUTPUT_NAME main)
|
set_target_properties(dusklight PROPERTIES OUTPUT_NAME main)
|
||||||
else ()
|
else ()
|
||||||
add_executable(dusk ${DUSK_FILES})
|
add_executable(dusklight ${DUSK_FILES})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_compile_definitions(dusk PRIVATE ${GAME_COMPILE_DEFS})
|
target_compile_definitions(dusklight PRIVATE ${GAME_COMPILE_DEFS})
|
||||||
target_include_directories(dusk PRIVATE ${GAME_INCLUDE_DIRS})
|
target_include_directories(dusklight PRIVATE ${GAME_INCLUDE_DIRS})
|
||||||
target_link_libraries(dusk PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBRARIES})
|
target_link_libraries(dusklight PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBRARIES})
|
||||||
target_precompile_headers(dusk PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
|
target_precompile_headers(dusklight PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
|
||||||
if (TARGET crashpad_handler)
|
if (TARGET crashpad_handler)
|
||||||
add_dependencies(dusk crashpad_handler)
|
add_dependencies(dusklight crashpad_handler)
|
||||||
|
add_custom_command(TARGET dusklight POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"$<TARGET_FILE:crashpad_handler>"
|
||||||
|
"$<TARGET_FILE_DIR:dusklight>"
|
||||||
|
COMMENT "Copying crashpad handler"
|
||||||
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
# SDLActivity loads SDL_main via dlsym on Android. Since aurora::main is a static
|
# SDLActivity loads SDL_main via dlsym on Android. Since aurora::main is a static
|
||||||
# archive, force an undefined reference so the linker keeps the SDL_main object.
|
# archive, force an undefined reference so the linker keeps the SDL_main object.
|
||||||
target_link_options(dusk PRIVATE "-Wl,-u,SDL_main")
|
target_link_options(dusklight PRIVATE "-Wl,-u,SDL_main")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
add_custom_command(TARGET dusk POST_BUILD
|
add_custom_command(TARGET dusklight POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
"${CMAKE_SOURCE_DIR}/res"
|
"${CMAKE_SOURCE_DIR}/res"
|
||||||
"$<TARGET_FILE_DIR:dusk>/res"
|
"$<TARGET_FILE_DIR:dusklight>/res"
|
||||||
COMMENT "Copying resources"
|
COMMENT "Copying resources"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
@@ -459,9 +470,9 @@ endif ()
|
|||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(DUSK_WINDOWS_RESOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/platforms/windows)
|
set(DUSK_WINDOWS_RESOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/platforms/windows)
|
||||||
set(DUSK_WINDOWS_ICON_PNG ${CMAKE_CURRENT_SOURCE_DIR}/res/icon.png)
|
set(DUSK_WINDOWS_ICON_PNG ${CMAKE_CURRENT_SOURCE_DIR}/res/icon.png)
|
||||||
set(DUSK_WINDOWS_ICON_ICO ${CMAKE_CURRENT_BINARY_DIR}/dusk.ico)
|
set(DUSK_WINDOWS_ICON_ICO ${CMAKE_CURRENT_BINARY_DIR}/dusklight.ico)
|
||||||
set(DUSK_WINDOWS_RC ${CMAKE_CURRENT_BINARY_DIR}/dusk.rc)
|
set(DUSK_WINDOWS_RC ${CMAKE_CURRENT_BINARY_DIR}/dusklight.rc)
|
||||||
set(DUSK_WINDOWS_MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/dusk.manifest)
|
set(DUSK_WINDOWS_MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/dusklight.manifest)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${DUSK_WINDOWS_ICON_ICO}
|
OUTPUT ${DUSK_WINDOWS_ICON_ICO}
|
||||||
@@ -474,14 +485,14 @@ if (WIN32)
|
|||||||
COMMENT "Generating Windows icon"
|
COMMENT "Generating Windows icon"
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusk.manifest.in ${DUSK_WINDOWS_MANIFEST} @ONLY)
|
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusklight.manifest.in ${DUSK_WINDOWS_MANIFEST} @ONLY)
|
||||||
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusk.rc.in ${DUSK_WINDOWS_RC} @ONLY)
|
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusklight.rc.in ${DUSK_WINDOWS_RC} @ONLY)
|
||||||
|
|
||||||
target_sources(dusk PRIVATE ${DUSK_WINDOWS_ICON_ICO} ${DUSK_WINDOWS_RC})
|
target_sources(dusklight PRIVATE ${DUSK_WINDOWS_ICON_ICO} ${DUSK_WINDOWS_RC})
|
||||||
set_target_properties(dusk PROPERTIES WIN32_EXECUTABLE TRUE)
|
set_target_properties(dusklight PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_options(dusk PRIVATE /MANIFEST:NO)
|
target_link_options(dusklight PRIVATE /MANIFEST:NO)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -497,10 +508,10 @@ if (APPLE)
|
|||||||
file(GLOB_RECURSE DUSK_RESOURCE_FILES
|
file(GLOB_RECURSE DUSK_RESOURCE_FILES
|
||||||
"${DUSK_RESOURCE_DIR}/Assets.car"
|
"${DUSK_RESOURCE_DIR}/Assets.car"
|
||||||
"${DUSK_RESOURCE_DIR}/Base.lproj/*"
|
"${DUSK_RESOURCE_DIR}/Base.lproj/*"
|
||||||
"${DUSK_RESOURCE_DIR}/Dusk.icns")
|
"${DUSK_RESOURCE_DIR}/Dusklight.icns")
|
||||||
file(GLOB_RECURSE DUSK_APP_RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/*")
|
file(GLOB_RECURSE DUSK_APP_RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/*")
|
||||||
target_sources(dusk PRIVATE ${DUSK_RESOURCE_FILES})
|
target_sources(dusklight PRIVATE ${DUSK_RESOURCE_FILES})
|
||||||
target_sources(dusk PRIVATE ${DUSK_APP_RESOURCE_FILES})
|
target_sources(dusklight PRIVATE ${DUSK_APP_RESOURCE_FILES})
|
||||||
foreach (FILE ${DUSK_RESOURCE_FILES})
|
foreach (FILE ${DUSK_RESOURCE_FILES})
|
||||||
file(RELATIVE_PATH NEW_FILE "${DUSK_RESOURCE_DIR}" ${FILE})
|
file(RELATIVE_PATH NEW_FILE "${DUSK_RESOURCE_DIR}" ${FILE})
|
||||||
get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
|
get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
|
||||||
@@ -512,29 +523,36 @@ if (APPLE)
|
|||||||
set_property(SOURCE ${FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${NEW_FILE_PATH}")
|
set_property(SOURCE ${FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${NEW_FILE_PATH}")
|
||||||
endforeach ()
|
endforeach ()
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
dusk PROPERTIES
|
dusklight PROPERTIES
|
||||||
MACOSX_BUNDLE TRUE
|
MACOSX_BUNDLE TRUE
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME ${DUSK_BUNDLE_NAME}
|
MACOSX_BUNDLE_BUNDLE_NAME ${DUSK_BUNDLE_NAME}
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER ${DUSK_BUNDLE_IDENTIFIER}
|
MACOSX_BUNDLE_GUI_IDENTIFIER ${DUSK_BUNDLE_IDENTIFIER}
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${DUSK_VERSION_STRING}
|
MACOSX_BUNDLE_BUNDLE_VERSION ${DUSK_VERSION_STRING}
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${DUSK_SHORT_VERSION_STRING}
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${DUSK_SHORT_VERSION_STRING}
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${DUSK_INFO_PLIST}
|
MACOSX_BUNDLE_INFO_PLIST ${DUSK_INFO_PLIST}
|
||||||
OUTPUT_NAME Dusk
|
OUTPUT_NAME Dusklight
|
||||||
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES"
|
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES"
|
||||||
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "YES"
|
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "YES"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (APPLE AND NOT IOS AND NOT TVOS)
|
||||||
|
find_library(APPKIT_FRAMEWORK AppKit REQUIRED)
|
||||||
|
target_sources(dusklight PRIVATE src/dusk/file_select_macos.mm)
|
||||||
|
set_source_files_properties(src/dusk/file_select_macos.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
target_link_libraries(dusklight PRIVATE ${APPKIT_FRAMEWORK})
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (IOS)
|
if (IOS)
|
||||||
find_library(UIKIT_FRAMEWORK UIKit REQUIRED)
|
find_library(UIKIT_FRAMEWORK UIKit REQUIRED)
|
||||||
find_library(UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK UniformTypeIdentifiers REQUIRED)
|
find_library(UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK UniformTypeIdentifiers REQUIRED)
|
||||||
target_sources(dusk PRIVATE src/dusk/ios/FileSelectDialog.m)
|
target_sources(dusklight PRIVATE src/dusk/ios/FileSelectDialog.m)
|
||||||
set_source_files_properties(src/dusk/ios/FileSelectDialog.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
set_source_files_properties(src/dusk/ios/FileSelectDialog.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
target_link_libraries(dusk PRIVATE ${UIKIT_FRAMEWORK} ${UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK})
|
target_link_libraries(dusklight PRIVATE ${UIKIT_FRAMEWORK} ${UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
include(extern/aurora/cmake/AuroraCopyRuntimeDLLs.cmake)
|
include(extern/aurora/cmake/AuroraCopyRuntimeDLLs.cmake)
|
||||||
aurora_copy_runtime_dlls(dusk)
|
aurora_copy_runtime_dlls(dusklight)
|
||||||
|
|
||||||
if (DUSK_SELECTED_OPT)
|
if (DUSK_SELECTED_OPT)
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
@@ -572,13 +590,13 @@ function(get_target_prefix target result_var)
|
|||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
endfunction()
|
endfunction()
|
||||||
list(APPEND BINARY_TARGETS dusk)
|
list(APPEND BINARY_TARGETS dusklight)
|
||||||
set(EXTRA_TARGETS "")
|
set(EXTRA_TARGETS "")
|
||||||
if (TARGET crashpad_handler)
|
if (TARGET crashpad_handler)
|
||||||
list(APPEND EXTRA_TARGETS crashpad_handler)
|
list(APPEND EXTRA_TARGETS crashpad_handler)
|
||||||
endif ()
|
endif ()
|
||||||
install(TARGETS ${BINARY_TARGETS} ${EXTRA_TARGETS} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
install(TARGETS ${BINARY_TARGETS} ${EXTRA_TARGETS} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||||
aurora_install_runtime_dlls(dusk ${CMAKE_INSTALL_PREFIX})
|
aurora_install_runtime_dlls(dusklight ${CMAKE_INSTALL_PREFIX})
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res DESTINATION ${CMAKE_INSTALL_PREFIX})
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@@ -352,6 +352,31 @@
|
|||||||
"ANDROID_ABI": "x86_64"
|
"ANDROID_ABI": "x86_64"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "x-android-ci",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": [
|
||||||
|
"android-base",
|
||||||
|
"ci"
|
||||||
|
],
|
||||||
|
"cacheVariables": {
|
||||||
|
"DUSK_ENABLE_SENTRY_NATIVE": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-android-ci-arm64",
|
||||||
|
"binaryDir": "${sourceDir}/build/android-arm64",
|
||||||
|
"inherits": [
|
||||||
|
"x-android-ci"
|
||||||
|
],
|
||||||
|
"cacheVariables": {
|
||||||
|
"ANDROID_ABI": "arm64-v8a",
|
||||||
|
"Rust_CARGO_TARGET": "aarch64-linux-android"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "x-linux-ci",
|
"name": "x-linux-ci",
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
@@ -412,6 +437,7 @@
|
|||||||
"x-macos-ci"
|
"x-macos-ci"
|
||||||
],
|
],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
|
"AURORA_DAWN_PROVIDER": "vendor",
|
||||||
"CMAKE_OSX_ARCHITECTURES": "x86_64",
|
"CMAKE_OSX_ARCHITECTURES": "x86_64",
|
||||||
"Rust_CARGO_TARGET": "x86_64-apple-darwin"
|
"Rust_CARGO_TARGET": "x86_64-apple-darwin"
|
||||||
}
|
}
|
||||||
@@ -525,7 +551,7 @@
|
|||||||
"description": "iOS release build with debug info",
|
"description": "iOS release build with debug info",
|
||||||
"displayName": "iOS RelWithDebInfo",
|
"displayName": "iOS RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -534,7 +560,7 @@
|
|||||||
"description": "tvOS release build with debug info",
|
"description": "tvOS release build with debug info",
|
||||||
"displayName": "tvOS RelWithDebInfo",
|
"displayName": "tvOS RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -543,7 +569,7 @@
|
|||||||
"description": "Android arm64-v8a release build with debug info",
|
"description": "Android arm64-v8a release build with debug info",
|
||||||
"displayName": "Android arm64-v8a RelWithDebInfo",
|
"displayName": "Android arm64-v8a RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -552,7 +578,16 @@
|
|||||||
"description": "Android x86_64 release build with debug info",
|
"description": "Android x86_64 release build with debug info",
|
||||||
"displayName": "Android x86_64 RelWithDebInfo",
|
"displayName": "Android x86_64 RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-android-ci-arm64",
|
||||||
|
"configurePreset": "x-android-ci-arm64",
|
||||||
|
"description": "(Internal) Android CI arm64-v8a",
|
||||||
|
"displayName": "(Internal) Android CI arm64-v8a",
|
||||||
|
"targets": [
|
||||||
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,37 +1,69 @@
|
|||||||

|
<div align="center">
|
||||||
|
<img src="res/logo.png" alt="Logo" width="640">
|
||||||
|
|
||||||
- ### **[Official Website](https://twilitrealm.dev)**
|
<p align="center">
|
||||||
- ### **[Discord](https://discord.gg/QACynxeyna)**
|
<a href="https://twilitrealm.dev">Official Website</a>
|
||||||
|
•
|
||||||
|
<a href="https://discord.gg/6NpMhefCK9">Discord</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
Dusk is a reverse-engineered reimplementation of Twilight Princess.
|
|
||||||
|
Dusklight is a reverse-engineered reimplementation of Twilight Princess.
|
||||||
|
|
||||||
It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience.
|
It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience.
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
**⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
|
|
||||||
|
|
||||||
### 1. Verify your ROM dump
|
> [!IMPORTANT]
|
||||||
First make sure your dump of the game is clean and supported by Dusk. You can do this by checking the sha1 hash of your dump against this list of supported versions.
|
> Dusklight does *not* provide any copyrighted assets. You must provide your own copy of the original game.
|
||||||
|
|
||||||
| Version | sha1 hash |
|
> [!IMPORTANT]
|
||||||
|--------------| ---------------------------------------- |
|
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
||||||
| GameCube USA | 75edd3ddff41f125d1b4ce1a40378f1b565519e7 |
|
|
||||||
| GameCube PAL | 2601822a488eeb86fb89db16ca8f29c2c953e1ca |
|
|
||||||
|
|
||||||
### 2. Download [Dusk](https://github.com/TwilitRealm/dusk/releases)
|
### 1. Verify your dump
|
||||||
|
|
||||||
|
First, make sure your dump of the game is clean and supported by Dusklight. You can do this by checking the SHA-1 hash of your dump against this list of supported versions:
|
||||||
|
|
||||||
|
| Version | SHA-1 hash |
|
||||||
|
|--------------| ------------------------------------------ |
|
||||||
|
| GameCube USA | `75edd3ddff41f125d1b4ce1a40378f1b565519e7` |
|
||||||
|
| GameCube EUR | `2601822a488eeb86fb89db16ca8f29c2c953e1ca` |
|
||||||
|
|
||||||
|
*Support for other versions of the game is planned in the future.
|
||||||
|
|
||||||
|
### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases)
|
||||||
|
|
||||||
### 3. Setup the game
|
### 3. Setup the game
|
||||||
- Extract the zip folder
|
**Windows / macOS / Linux**
|
||||||
- Launch Dusk
|
- Extract the .zip file
|
||||||
- Select Options, then set the ISO Path to your supported game dump
|
- Launch Dusklight
|
||||||
- Press Start Game to play!
|
- Press **Select Disc Image** and provide the path to your supported game dump
|
||||||
|
- Press **Play**!
|
||||||
|
|
||||||

|
**iOS**
|
||||||
|
- Follow the [iOS setup guide](docs/ios-install-altstore.md)
|
||||||
|
|
||||||
|
**Android**
|
||||||
|
- Install the Dusklight APK
|
||||||
|
- Launch Dusklight
|
||||||
|
- Press **Select Disc Image** and provide the path to your supported game dump
|
||||||
|
- Press **Play**!
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
If you'd like to build Dusk from source, please read the [build instructions](docs/building.md).
|
|
||||||
|
|
||||||
Pull Requests are welcomed! Note that we do not accept contributions that are primarily AI generated and will close your PR if we suspect as much.
|
If you'd like to build Dusklight from source, please read the [build instructions](docs/building.md).
|
||||||
|
|
||||||
|
Pull requests are welcomed! Note that we do not accept contributions that are primarily AI-generated and will close your PR if we suspect as much. Please also see the [code conventions](docs/code-conventions.md).
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors).
|
|
||||||
|
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusklight/graphs/contributors).
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://github.com/encounter/aurora">
|
||||||
|
<img src="assets/aurora-powered.png" alt="Powered by Aurora" width="800">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 85 KiB |
@@ -0,0 +1,66 @@
|
|||||||
|
<svg width="600" height="600" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="150" cy="150" r="105" fill="none" stroke="white" stroke-width="4"/>
|
||||||
|
<circle cx="150" cy="150" r="95" fill="none" stroke="white" stroke-width="4"/>
|
||||||
|
<circle cx="150" cy="150" r="60" fill="none" stroke="white" stroke-width="4"/>
|
||||||
|
<circle cx="150" cy="150" r="75" fill="none" stroke="white" stroke-width="4"/>
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
<line id="ray" x1="150" y1="55" x2="150" y2="45"/>
|
||||||
|
<clipPath id="zigzag-clip">
|
||||||
|
<circle cx="150" cy="150" r="75"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<g stroke="white" stroke-width="3">
|
||||||
|
<use href="#ray"/>
|
||||||
|
<use href="#ray" transform="rotate(18 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(36 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(54 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(72 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(90 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(108 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(126 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(144 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(162 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(180 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(198 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(216 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(234 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(252 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(270 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(288 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(306 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(324 150 150)"/>
|
||||||
|
<use href="#ray" transform="rotate(342 150 150)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<polygon fill="none" stroke="white" stroke-width="4" opacity="1" clip-path="url(#zigzag-clip)"
|
||||||
|
points="
|
||||||
|
126.82,78.67
|
||||||
|
150,90
|
||||||
|
173.18,78.67
|
||||||
|
185.27,101.46
|
||||||
|
210.68,105.92
|
||||||
|
207.06,131.46
|
||||||
|
225,150
|
||||||
|
207.06,168.54
|
||||||
|
210.68,194.08
|
||||||
|
185.27,198.54
|
||||||
|
173.18,221.33
|
||||||
|
150,210
|
||||||
|
126.82,221.33
|
||||||
|
114.73,198.54
|
||||||
|
89.32,194.08
|
||||||
|
92.94,168.54
|
||||||
|
75,150
|
||||||
|
92.94,131.46
|
||||||
|
89.32,105.92
|
||||||
|
114.73,101.46
|
||||||
|
"/>
|
||||||
|
|
||||||
|
<g fill="none" stroke="white" stroke-width="4">
|
||||||
|
<polygon points="150,105 130,140 170,140"/>
|
||||||
|
<polygon points="130,140 110,175 150,175"/>
|
||||||
|
<polygon points="170,140 150,175 190,175"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -1,18 +1,26 @@
|
|||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
shopt -s extglob
|
|
||||||
|
if [[ -n "${GITHUB_WORKSPACE:-}" ]]; then
|
||||||
|
cd "$GITHUB_WORKSPACE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
build_dir="$PWD/build"
|
||||||
|
linuxdeploy="$build_dir/linuxdeploy-$(uname -m).AppImage"
|
||||||
|
|
||||||
# Get linuxdeploy
|
# Get linuxdeploy
|
||||||
cd "$RUNNER_WORKSPACE"
|
mkdir -p "$build_dir"
|
||||||
curl -fOL https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$(uname -m).AppImage
|
curl -fL "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$(uname -m).AppImage" -o "$linuxdeploy"
|
||||||
chmod +x linuxdeploy-$(uname -m).AppImage
|
chmod +x "$linuxdeploy"
|
||||||
|
|
||||||
# Build AppImage
|
# Build AppImage
|
||||||
cd "$GITHUB_WORKSPACE"
|
|
||||||
mkdir -p build/appdir/usr/{bin,share/{applications,icons/hicolor}}
|
mkdir -p build/appdir/usr/{bin,share/{applications,icons/hicolor}}
|
||||||
cp -r build/install/!(*.*) build/appdir/usr/bin
|
for install_path in build/install/*; do
|
||||||
|
[[ "$(basename "$install_path")" == *.* ]] && continue
|
||||||
|
cp -r "$install_path" build/appdir/usr/bin
|
||||||
|
done
|
||||||
cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor
|
cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor
|
||||||
cp platforms/freedesktop/dusk.desktop build/appdir/usr/share/applications
|
cp platforms/freedesktop/dusklight.desktop build/appdir/usr/share/applications
|
||||||
|
|
||||||
cd build/install
|
cd build/install
|
||||||
VERSION="$DUSK_VERSION" NO_STRIP=1 "$RUNNER_WORKSPACE"/linuxdeploy-$(uname -m).AppImage \
|
VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \
|
||||||
-l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$GITHUB_WORKSPACE"/build/appdir --output appimage
|
-l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$build_dir/appdir" --output appimage
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
sudo dnf groupinstall "Development Tools" "Development Libraries"
|
sudo dnf groupinstall "Development Tools" "Development Libraries"
|
||||||
```
|
```
|
||||||
#### Setup
|
#### Setup
|
||||||
Clone and initialize the Dusk repository
|
Clone and initialize the Dusklight repository
|
||||||
```sh
|
```sh
|
||||||
git clone --recursive https://github.com/TwilitRealm/dusk.git
|
git clone --recursive https://github.com/TwilitRealm/dusklight.git
|
||||||
cd dusk
|
cd dusklight
|
||||||
git pull
|
git pull
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
```
|
```
|
||||||
@@ -93,6 +93,6 @@ Alternate presets available:
|
|||||||
#### Running
|
#### Running
|
||||||
Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
|
Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
|
||||||
```sh
|
```sh
|
||||||
build/{preset}/dusk /path/to/game.rvz
|
build/{preset}/dusklight/path/to/game.rvz
|
||||||
```
|
```
|
||||||
If no path is specified, Dusk defaults to `game.iso` in the current working directory.
|
If no path is specified, Dusklight defaults to `game.iso` in the current working directory.
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Code conventions for Dusk
|
||||||
|
|
||||||
|
## Upstream when appropriate
|
||||||
|
|
||||||
|
Bug fixes, documentation improvements, code cleanup, etc that also apply to the [original decompilation project](https://github.com/zeldaret/tp) should preferably be PR'd there.
|
||||||
|
|
||||||
|
## Properly indicate Dusk-modified code
|
||||||
|
|
||||||
|
When modifying original game code (i.e. in decomp) for Dusk's purposes, please clearly delineate such code as being Dusk-specific. Generally, this can be done by using `#if TARGET_PC` and keeping the original code in place. Use `#if AVOID_UB` for Undefined Behavior fixes to the original codebase.
|
||||||
|
|
||||||
|
## Miscellaneous things
|
||||||
|
|
||||||
|
* The original codebase makes heavy use of global `operator new` and similar overloads to allocate into a strict tree of heaps. This would cause many linkage headaches for us, so effectively all uses of `new` or `delete` in the original game code have been replaced with `JKR_NEW`, `JKR_DELETE`, or similar macros. See `JKRHeap.h` for the full list.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Installing Dusklight on iOS via AltStore
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Mac with Homebrew installed
|
||||||
|
- iPhone connected via USB
|
||||||
|
- Dusklight IPA file (download the latest `Dusklight-vX.X.X-ios-arm64.ipa` from the [releases page](https://github.com/TwilitRealm/dusk/releases))
|
||||||
|
- Game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL)
|
||||||
|
|
||||||
|
## 1. Install AltServer
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brew install altserver
|
||||||
|
open -a AltServer
|
||||||
|
```
|
||||||
|
|
||||||
|
AltServer will appear in your menu bar.
|
||||||
|
|
||||||
|
## 2. Enable Developer Mode (iOS 16+)
|
||||||
|
|
||||||
|
- On your iPhone, go to **Settings > Privacy & Security > Developer Mode**
|
||||||
|
- Toggle it on and restart when prompted
|
||||||
|
|
||||||
|
## 3. Install AltStore on Your iPhone
|
||||||
|
|
||||||
|
- Click AltServer in the menu bar
|
||||||
|
- Click **Install AltStore > [Your iPhone]**
|
||||||
|
- Enter your Apple ID credentials when prompted
|
||||||
|
- On your iPhone, go to **Settings > General > VPN & Device Management**
|
||||||
|
- Tap your Apple ID under "Developer App" and tap **Trust**
|
||||||
|
|
||||||
|
## 4. Copy Files to Your iPhone
|
||||||
|
|
||||||
|
Transfer the IPA and game disc to your iPhone so they're accessible in the Files app. A few ways to do this:
|
||||||
|
|
||||||
|
- **AirDrop** - Right-click the files on your Mac and choose Share > AirDrop
|
||||||
|
- **iCloud Drive** - Place files in iCloud Drive on your Mac and they'll sync to Files on your iPhone
|
||||||
|
- **USB transfer** - Connect your iPhone and drag files via Finder's sidebar
|
||||||
|
- **Cloud storage** - Upload to Google Drive, Dropbox, etc. and download on your iPhone
|
||||||
|
|
||||||
|
## 5. Install via AltStore
|
||||||
|
|
||||||
|
- Open **AltStore** on your iPhone
|
||||||
|
- Go to the **My Apps** tab
|
||||||
|
- Tap the **+** button (top left)
|
||||||
|
- Open the **Files** app and select the `.ipa` file
|
||||||
@@ -1411,6 +1411,7 @@ set(DOLPHIN_FILES
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(DUSK_FILES
|
set(DUSK_FILES
|
||||||
|
include/dusk/action_bindings.h
|
||||||
include/dusk/endian_gx.hpp
|
include/dusk/endian_gx.hpp
|
||||||
include/dusk/config.hpp
|
include/dusk/config.hpp
|
||||||
include/dusk/dvd_asset.hpp
|
include/dusk/dvd_asset.hpp
|
||||||
@@ -1420,6 +1421,8 @@ set(DUSK_FILES
|
|||||||
src/dusk/asserts.cpp
|
src/dusk/asserts.cpp
|
||||||
src/dusk/config.cpp
|
src/dusk/config.cpp
|
||||||
src/dusk/crash_reporting.cpp
|
src/dusk/crash_reporting.cpp
|
||||||
|
src/dusk/data.cpp
|
||||||
|
src/dusk/data.hpp
|
||||||
src/dusk/endian.cpp
|
src/dusk/endian.cpp
|
||||||
src/dusk/extras.c
|
src/dusk/extras.c
|
||||||
src/dusk/file_select.cpp
|
src/dusk/file_select.cpp
|
||||||
@@ -1430,53 +1433,59 @@ set(DUSK_FILES
|
|||||||
src/dusk/gyro.cpp
|
src/dusk/gyro.cpp
|
||||||
src/dusk/gamepad_color.cpp
|
src/dusk/gamepad_color.cpp
|
||||||
src/dusk/autosave.cpp
|
src/dusk/autosave.cpp
|
||||||
|
src/dusk/http/http.hpp
|
||||||
src/dusk/io.cpp
|
src/dusk/io.cpp
|
||||||
src/dusk/layout.cpp
|
src/dusk/layout.cpp
|
||||||
src/dusk/logging.cpp
|
src/dusk/logging.cpp
|
||||||
src/dusk/settings.cpp
|
src/dusk/settings.cpp
|
||||||
|
src/dusk/speedrun.cpp
|
||||||
src/dusk/stubs.cpp
|
src/dusk/stubs.cpp
|
||||||
|
src/dusk/update_check.cpp
|
||||||
|
src/dusk/update_check.hpp
|
||||||
#src/dusk/m_Do_ext_dusk.cpp
|
#src/dusk/m_Do_ext_dusk.cpp
|
||||||
src/dusk/imgui/ImGuiConfig.hpp
|
src/dusk/imgui/ImGuiConfig.hpp
|
||||||
src/dusk/imgui/ImGuiConsole.hpp
|
src/dusk/imgui/ImGuiConsole.hpp
|
||||||
src/dusk/imgui/ImGuiConsole.cpp
|
src/dusk/imgui/ImGuiConsole.cpp
|
||||||
src/dusk/imgui/ImGuiEngine.cpp
|
src/dusk/imgui/ImGuiEngine.cpp
|
||||||
src/dusk/imgui/ImGuiEngine.hpp
|
src/dusk/imgui/ImGuiEngine.hpp
|
||||||
src/dusk/imgui/ImGuiMenuGame.cpp
|
|
||||||
src/dusk/imgui/ImGuiMenuGame.hpp
|
|
||||||
src/dusk/imgui/ImGuiBloomWindow.cpp
|
src/dusk/imgui/ImGuiBloomWindow.cpp
|
||||||
src/dusk/imgui/ImGuiBloomWindow.hpp
|
src/dusk/imgui/ImGuiBloomWindow.hpp
|
||||||
|
src/dusk/imgui/ImGuiEnhancedLightingWindow.cpp
|
||||||
|
src/dusk/imgui/ImGuiEnhancedLightingWindow.hpp
|
||||||
src/dusk/imgui/ImGuiMenuTools.cpp
|
src/dusk/imgui/ImGuiMenuTools.cpp
|
||||||
src/dusk/imgui/ImGuiMenuTools.hpp
|
src/dusk/imgui/ImGuiMenuTools.hpp
|
||||||
src/dusk/imgui/ImGuiPreLaunchWindow.cpp
|
src/dusk/imgui/ImGuiActorSpawner.cpp
|
||||||
src/dusk/imgui/ImGuiPreLaunchWindow.hpp
|
|
||||||
src/dusk/imgui/ImGuiFirstRunPreset.hpp
|
|
||||||
src/dusk/imgui/ImGuiFirstRunPreset.cpp
|
|
||||||
src/dusk/imgui/ImGuiProcessOverlay.cpp
|
src/dusk/imgui/ImGuiProcessOverlay.cpp
|
||||||
src/dusk/imgui/ImGuiCameraOverlay.cpp
|
src/dusk/imgui/ImGuiCameraOverlay.cpp
|
||||||
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
||||||
src/dusk/imgui/ImGuiDebugPad.cpp
|
|
||||||
src/dusk/imgui/ImGuiControllerOverlay.cpp
|
src/dusk/imgui/ImGuiControllerOverlay.cpp
|
||||||
src/dusk/imgui/ImGuiStubLog.cpp
|
src/dusk/imgui/ImGuiStubLog.cpp
|
||||||
src/dusk/imgui/ImGuiMapLoader.cpp
|
src/dusk/imgui/ImGuiMapLoader.cpp
|
||||||
src/dusk/imgui/ImGuiSaveEditor.cpp
|
src/dusk/imgui/ImGuiSaveEditor.cpp
|
||||||
src/dusk/imgui/ImGuiStateShare.hpp
|
src/dusk/imgui/ImGuiStateShare.hpp
|
||||||
src/dusk/imgui/ImGuiStateShare.cpp
|
src/dusk/imgui/ImGuiStateShare.cpp
|
||||||
src/dusk/imgui/ImGuiAchievements.hpp
|
src/dusk/ui/achievements.cpp
|
||||||
src/dusk/imgui/ImGuiAchievements.cpp
|
src/dusk/ui/achievements.hpp
|
||||||
src/dusk/ui/bool_button.cpp
|
src/dusk/ui/bool_button.cpp
|
||||||
src/dusk/ui/bool_button.hpp
|
src/dusk/ui/bool_button.hpp
|
||||||
src/dusk/ui/button.cpp
|
src/dusk/ui/button.cpp
|
||||||
src/dusk/ui/button.hpp
|
src/dusk/ui/button.hpp
|
||||||
src/dusk/ui/component.cpp
|
src/dusk/ui/component.cpp
|
||||||
src/dusk/ui/component.hpp
|
src/dusk/ui/component.hpp
|
||||||
|
src/dusk/ui/controller_config.cpp
|
||||||
|
src/dusk/ui/controller_config.hpp
|
||||||
src/dusk/ui/document.cpp
|
src/dusk/ui/document.cpp
|
||||||
src/dusk/ui/document.hpp
|
src/dusk/ui/document.hpp
|
||||||
src/dusk/ui/editor.cpp
|
src/dusk/ui/editor.cpp
|
||||||
src/dusk/ui/editor.hpp
|
src/dusk/ui/editor.hpp
|
||||||
src/dusk/ui/event.cpp
|
src/dusk/ui/event.cpp
|
||||||
src/dusk/ui/event.hpp
|
src/dusk/ui/event.hpp
|
||||||
|
src/dusk/ui/graphics_tuner.cpp
|
||||||
|
src/dusk/ui/graphics_tuner.hpp
|
||||||
src/dusk/ui/input.cpp
|
src/dusk/ui/input.cpp
|
||||||
src/dusk/ui/input.hpp
|
src/dusk/ui/input.hpp
|
||||||
|
src/dusk/ui/modal.cpp
|
||||||
|
src/dusk/ui/modal.hpp
|
||||||
src/dusk/ui/nav_types.hpp
|
src/dusk/ui/nav_types.hpp
|
||||||
src/dusk/ui/number_button.cpp
|
src/dusk/ui/number_button.cpp
|
||||||
src/dusk/ui/number_button.hpp
|
src/dusk/ui/number_button.hpp
|
||||||
@@ -1484,12 +1493,14 @@ set(DUSK_FILES
|
|||||||
src/dusk/ui/overlay.hpp
|
src/dusk/ui/overlay.hpp
|
||||||
src/dusk/ui/pane.cpp
|
src/dusk/ui/pane.cpp
|
||||||
src/dusk/ui/pane.hpp
|
src/dusk/ui/pane.hpp
|
||||||
src/dusk/ui/popup.cpp
|
src/dusk/ui/menu_bar.cpp
|
||||||
src/dusk/ui/popup.hpp
|
src/dusk/ui/menu_bar.hpp
|
||||||
src/dusk/ui/prelaunch.cpp
|
src/dusk/ui/prelaunch.cpp
|
||||||
src/dusk/ui/prelaunch.hpp
|
src/dusk/ui/prelaunch.hpp
|
||||||
src/dusk/ui/prelaunch_options.cpp
|
src/dusk/ui/preset.cpp
|
||||||
src/dusk/ui/prelaunch_options.hpp
|
src/dusk/ui/preset.hpp
|
||||||
|
src/dusk/ui/reporting.cpp
|
||||||
|
src/dusk/ui/reporting.hpp
|
||||||
src/dusk/ui/select_button.cpp
|
src/dusk/ui/select_button.cpp
|
||||||
src/dusk/ui/select_button.hpp
|
src/dusk/ui/select_button.hpp
|
||||||
src/dusk/ui/settings.cpp
|
src/dusk/ui/settings.cpp
|
||||||
@@ -1510,6 +1521,16 @@ set(DUSK_FILES
|
|||||||
src/dusk/OSReport.cpp
|
src/dusk/OSReport.cpp
|
||||||
src/dusk/OSThread.cpp
|
src/dusk/OSThread.cpp
|
||||||
src/dusk/OSMutex.cpp
|
src/dusk/OSMutex.cpp
|
||||||
|
src/dusk/discord.cpp
|
||||||
|
src/dusk/discord.hpp
|
||||||
src/dusk/discord_presence.cpp
|
src/dusk/discord_presence.cpp
|
||||||
src/dusk/version.cpp
|
src/dusk/version.cpp
|
||||||
|
src/dusk/action_bindings.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(DUSK_HTTP_BACKEND_FILES
|
||||||
|
src/dusk/http/no_backend.cpp
|
||||||
|
src/dusk/http/curl.cpp
|
||||||
|
src/dusk/http/winhttp.cpp
|
||||||
|
src/dusk/http/url_session.mm
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,10 +4,86 @@
|
|||||||
};
|
};
|
||||||
outputs = { self, nixpkgs }:
|
outputs = { self, nixpkgs }:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
supportedSystems = [
|
||||||
dusk = pkgs.stdenv.mkDerivation {
|
"x86_64-linux"
|
||||||
name = "dusk";
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
|
pkgsFor = system: import nixpkgs { inherit system; };
|
||||||
|
|
||||||
|
# Dependencies that are not packaged in nixpkgs (used by the Linux package build):
|
||||||
|
buildSources = pkgs: {
|
||||||
|
aurora-src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "encounter";
|
||||||
|
repo = "aurora";
|
||||||
|
rev = "63606a43265a3bc18dafd500ab4d7a2108f109e6";
|
||||||
|
hash = "sha256-xBvnAwGwNzav67Ac6oUz7RqDUwqgL2bsME3OOMn8Tqw=";
|
||||||
|
};
|
||||||
|
dawn-src = pkgs.fetchzip {
|
||||||
|
url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz";
|
||||||
|
hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs=";
|
||||||
|
stripRoot = false;
|
||||||
|
};
|
||||||
|
nod-src = pkgs.fetchzip {
|
||||||
|
url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz";
|
||||||
|
hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE=";
|
||||||
|
stripRoot = false;
|
||||||
|
};
|
||||||
|
# The version of imgui on nixpkgs does not map cleanly.
|
||||||
|
imgui-src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "ocornut";
|
||||||
|
repo = "imgui";
|
||||||
|
rev = "v1.91.9b-docking";
|
||||||
|
hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ=";
|
||||||
|
};
|
||||||
|
sqlite-src = pkgs.fetchzip {
|
||||||
|
url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip";
|
||||||
|
hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM=";
|
||||||
|
};
|
||||||
|
rmlui-src = pkgs.fetchzip {
|
||||||
|
url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz";
|
||||||
|
hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg=";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Dusklight Actual (Linux x86_64 only — relies on prebuilt dawn/nod binaries)
|
||||||
|
mkDusklight = pkgs:
|
||||||
|
let srcs = buildSources pkgs; in
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "dusklight";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
postUnpack = ''
|
||||||
|
mkdir -p $sourceRoot/extern/aurora
|
||||||
|
cp -r ${srcs.aurora-src}/. $sourceRoot/extern/aurora/
|
||||||
|
chmod -R u+w $sourceRoot/extern/aurora
|
||||||
|
sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt
|
||||||
|
'';
|
||||||
|
# Remove last line to re-enable tests
|
||||||
|
cmakeFlags = [
|
||||||
|
"-DFETCHCONTENT_FULLY_DISCONNECTED=ON"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${srcs.dawn-src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}"
|
||||||
|
"-DAURORA_SDL3_PROVIDER=system"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${srcs.nod-src}"
|
||||||
|
"-DAURORA_NOD_PROVIDER=package"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${srcs.sqlite-src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_IMGUI=${srcs.imgui-src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_RMLUI=${srcs.rmlui-src}"
|
||||||
|
"-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it.
|
||||||
|
];
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp dusklight $out/bin/dusklight
|
||||||
|
cp -r ./res $out/bin/res
|
||||||
|
'';
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.cmake
|
pkgs.cmake
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
@@ -25,9 +101,115 @@
|
|||||||
pkgs.libjpeg8
|
pkgs.libjpeg8
|
||||||
pkgs.libxkbcommon
|
pkgs.libxkbcommon
|
||||||
pkgs.libglvnd
|
pkgs.libglvnd
|
||||||
|
pkgs.cxxopts
|
||||||
|
pkgs.abseil-cpp
|
||||||
|
pkgs.sdl3
|
||||||
|
pkgs.fmt
|
||||||
|
pkgs.tracy
|
||||||
|
pkgs.freetype
|
||||||
|
pkgs.zstd
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Tooling common to every supported host (Linux and macOS).
|
||||||
|
commonDevTools = pkgs: [
|
||||||
|
pkgs.cmake
|
||||||
|
pkgs.ninja
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.git
|
||||||
|
pkgs.python3
|
||||||
|
pkgs.python3Packages.markupsafe
|
||||||
|
pkgs.rustc
|
||||||
|
pkgs.cargo
|
||||||
|
pkgs.sccache
|
||||||
|
];
|
||||||
|
|
||||||
|
# Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml
|
||||||
|
# so the cmake presets resolve the same set of headers as CI.
|
||||||
|
linuxDevDeps = pkgs: [
|
||||||
|
# Compilers / linkers
|
||||||
|
pkgs.clang
|
||||||
|
pkgs.lld
|
||||||
|
# C/C++ utilities
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.openssl
|
||||||
|
pkgs.zlib
|
||||||
|
pkgs.libpng
|
||||||
|
pkgs.libjpeg_turbo
|
||||||
|
pkgs.freetype
|
||||||
|
pkgs.zstd
|
||||||
|
pkgs.fmt
|
||||||
|
pkgs.tracy
|
||||||
|
pkgs.cxxopts
|
||||||
|
pkgs.abseil-cpp
|
||||||
|
pkgs.sdl3
|
||||||
|
pkgs.ncurses
|
||||||
|
pkgs.libunwind
|
||||||
|
pkgs.libusb1
|
||||||
|
pkgs.fuse
|
||||||
|
# Wayland / display server
|
||||||
|
pkgs.wayland
|
||||||
|
pkgs.wayland-protocols
|
||||||
|
pkgs.libxkbcommon
|
||||||
|
pkgs.libdecor
|
||||||
|
# OpenGL / Vulkan
|
||||||
|
pkgs.libGL
|
||||||
|
pkgs.libGLU
|
||||||
|
pkgs.libglvnd
|
||||||
|
pkgs.vulkan-headers
|
||||||
|
pkgs.vulkan-loader
|
||||||
|
# X11
|
||||||
|
pkgs.libX11
|
||||||
|
pkgs.libxcb
|
||||||
|
pkgs.libXcursor
|
||||||
|
pkgs.libxi
|
||||||
|
pkgs.libxrandr
|
||||||
|
pkgs.libxscrnsaver
|
||||||
|
pkgs.libxtst
|
||||||
|
pkgs.libxinerama
|
||||||
|
# Audio
|
||||||
|
pkgs.alsa-lib
|
||||||
|
pkgs.libpulseaudio
|
||||||
|
pkgs.pipewire
|
||||||
|
# System integration
|
||||||
|
pkgs.dbus
|
||||||
|
pkgs.udev
|
||||||
|
pkgs.gtk3
|
||||||
|
];
|
||||||
|
|
||||||
|
# On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up
|
||||||
|
# Apple Clang and the Xcode SDK directly, matching the macOS CI workflow.
|
||||||
|
mkDarwinShell = pkgs:
|
||||||
|
pkgs.mkShellNoCC {
|
||||||
|
packages = commonDevTools pkgs;
|
||||||
|
shellHook = ''
|
||||||
|
echo "Dusklight dev shell (macOS)"
|
||||||
|
echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK."
|
||||||
|
echo "Configure: cmake --preset macos-default-relwithdebinfo"
|
||||||
|
echo "Build: cmake --build --preset macos-default-relwithdebinfo"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mkLinuxShell = pkgs:
|
||||||
|
pkgs.mkShell {
|
||||||
|
packages = (commonDevTools pkgs) ++ (linuxDevDeps pkgs);
|
||||||
|
shellHook = ''
|
||||||
|
echo "Dusklight dev shell (Linux)"
|
||||||
|
echo "Configure: cmake --preset linux-default-relwithdebinfo"
|
||||||
|
echo " cmake --preset linux-clang-relwithdebinfo"
|
||||||
|
echo "Build: cmake --build --preset <preset>"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mkDevShell = pkgs:
|
||||||
|
if pkgs.stdenv.isDarwin
|
||||||
|
then mkDarwinShell pkgs
|
||||||
|
else mkLinuxShell pkgs;
|
||||||
in {
|
in {
|
||||||
packages.x86_64-linux.default = dusk;
|
packages.x86_64-linux.default = mkDusklight (pkgsFor "x86_64-linux");
|
||||||
|
|
||||||
|
devShells = forAllSystems (system: {
|
||||||
|
default = mkDevShell (pkgsFor system);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4552,6 +4552,18 @@ public:
|
|||||||
void handleWolfHowl();
|
void handleWolfHowl();
|
||||||
void handleQuickTransform();
|
void handleQuickTransform();
|
||||||
bool checkGyroAimContext();
|
bool checkGyroAimContext();
|
||||||
|
|
||||||
|
void onIronBallChainInterpCallback();
|
||||||
|
|
||||||
|
static const int IRON_BALL_CHAIN_COUNT = 102;
|
||||||
|
cXyz mIBChainInterpPrevPos[IRON_BALL_CHAIN_COUNT];
|
||||||
|
cXyz mIBChainInterpCurrPos[IRON_BALL_CHAIN_COUNT];
|
||||||
|
csXyz mIBChainInterpPrevAngle[IRON_BALL_CHAIN_COUNT];
|
||||||
|
csXyz mIBChainInterpCurrAngle[IRON_BALL_CHAIN_COUNT];
|
||||||
|
cXyz mIBChainInterpPrevHandRoot;
|
||||||
|
cXyz mIBChainInterpCurrHandRoot;
|
||||||
|
bool mIBChainInterpPrevValid;
|
||||||
|
bool mIBChainInterpCurrValid;
|
||||||
#endif
|
#endif
|
||||||
}; // Size: 0x385C
|
}; // Size: 0x385C
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ public:
|
|||||||
/* 0x125C */ u32 field_0x125c;
|
/* 0x125C */ u32 field_0x125c;
|
||||||
/* 0x1260 */ u8 field_0x1260[0x126C - 0x1260];
|
/* 0x1260 */ u8 field_0x1260[0x126C - 0x1260];
|
||||||
/* 0x126C */ u8 HIOInit;
|
/* 0x126C */ u8 HIOInit;
|
||||||
|
#if TARGET_PC
|
||||||
|
cXyz mStalkLineInterpPrev[12];
|
||||||
|
cXyz mStalkLineInterpCurr[12];
|
||||||
|
bool mStalkLineInterpPrevValid;
|
||||||
|
bool mStalkLineInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(e_db_class) == 0x1270);
|
STATIC_ASSERT(sizeof(e_db_class) == 0x1270);
|
||||||
|
|||||||
@@ -73,6 +73,12 @@ public:
|
|||||||
/* 0x124C */ f32 field_0x124c;
|
/* 0x124C */ f32 field_0x124c;
|
||||||
/* 0x1250 */ u8 field_0x1250[0x1264 - 0x1250];
|
/* 0x1250 */ u8 field_0x1250[0x1264 - 0x1250];
|
||||||
/* 0x1264 */ u8 HIOInit;
|
/* 0x1264 */ u8 HIOInit;
|
||||||
|
#if TARGET_PC
|
||||||
|
cXyz mStalkLineInterpPrev[12];
|
||||||
|
cXyz mStalkLineInterpCurr[12];
|
||||||
|
bool mStalkLineInterpPrevValid;
|
||||||
|
bool mStalkLineInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(e_hb_class) == 0x1268);
|
STATIC_ASSERT(sizeof(e_hb_class) == 0x1268);
|
||||||
|
|||||||
@@ -81,6 +81,15 @@ public:
|
|||||||
/* 0x306D */ u8 field_0x306D[0x307C - 0x306D];
|
/* 0x306D */ u8 field_0x306D[0x307C - 0x306D];
|
||||||
/* 0x307C */ u32 mBodyEffEmtrID;
|
/* 0x307C */ u32 mBodyEffEmtrID;
|
||||||
/* 0x3080 */ u8 mInitHIO;
|
/* 0x3080 */ u8 mInitHIO;
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
static const int HAIR_STRAND_COUNT = 22;
|
||||||
|
static const int HAIR_SEGMENT_COUNT = 16;
|
||||||
|
cXyz mHairInterpPrev[HAIR_STRAND_COUNT * HAIR_SEGMENT_COUNT];
|
||||||
|
cXyz mHairInterpCurr[HAIR_STRAND_COUNT * HAIR_SEGMENT_COUNT];
|
||||||
|
bool mHairInterpPrevValid;
|
||||||
|
bool mHairInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(e_s1_class) == 0x3084);
|
STATIC_ASSERT(sizeof(e_s1_class) == 0x3084);
|
||||||
|
|||||||
@@ -74,6 +74,12 @@ public:
|
|||||||
/* 0x1250 */ f32 field_0x1250;
|
/* 0x1250 */ f32 field_0x1250;
|
||||||
/* 0x1254 */ u8 field_0x1254[0x1268 - 0x1254];
|
/* 0x1254 */ u8 field_0x1254[0x1268 - 0x1254];
|
||||||
/* 0x1268 */ u8 field_0x1268;
|
/* 0x1268 */ u8 field_0x1268;
|
||||||
|
#if TARGET_PC
|
||||||
|
cXyz mLineMatInterpPrev[12];
|
||||||
|
cXyz mLineMatInterpCurr[12];
|
||||||
|
bool mLineMatInterpPrevValid;
|
||||||
|
bool mLineMatInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(e_yd_class) == 0x126c);
|
STATIC_ASSERT(sizeof(e_yd_class) == 0x126c);
|
||||||
|
|||||||
@@ -63,6 +63,15 @@ public:
|
|||||||
/* 0x0BB4 */ yg_ke_s mYgKes[13];
|
/* 0x0BB4 */ yg_ke_s mYgKes[13];
|
||||||
/* 0x1880 */ mDoExt_3DlineMat0_c mLineMat;
|
/* 0x1880 */ mDoExt_3DlineMat0_c mLineMat;
|
||||||
/* 0x189C */ u8 mIsFirstSpawn;
|
/* 0x189C */ u8 mIsFirstSpawn;
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
static const int TENTACLE_STRAND_COUNT = 13;
|
||||||
|
static const int TENTACLE_SEGMENT_COUNT = 10;
|
||||||
|
cXyz mTentacleInterpPrev[TENTACLE_STRAND_COUNT * TENTACLE_SEGMENT_COUNT];
|
||||||
|
cXyz mTentacleInterpCurr[TENTACLE_STRAND_COUNT * TENTACLE_SEGMENT_COUNT];
|
||||||
|
bool mTentacleInterpPrevValid;
|
||||||
|
bool mTentacleInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(e_yg_class) == 0x18a0);
|
STATIC_ASSERT(sizeof(e_yg_class) == 0x18a0);
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ public:
|
|||||||
/* 0x1260 */ u32 field_0x1260;
|
/* 0x1260 */ u32 field_0x1260;
|
||||||
/* 0x1260 */ u8 field_0x1264[0x1270 - 0x1264];
|
/* 0x1260 */ u8 field_0x1264[0x1270 - 0x1264];
|
||||||
/* 0x1270 */ bool mIsHIOOwner;
|
/* 0x1270 */ bool mIsHIOOwner;
|
||||||
|
#if TARGET_PC
|
||||||
|
cXyz mLineInterpPrev[12];
|
||||||
|
cXyz mLineInterpCurr[12];
|
||||||
|
bool mLineInterpPrevValid;
|
||||||
|
bool mLineInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(e_yh_class) == 0x1274);
|
STATIC_ASSERT(sizeof(e_yh_class) == 0x1274);
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ public:
|
|||||||
csXyz* getAngle() { return field_0x8a4; }
|
csXyz* getAngle() { return field_0x8a4; }
|
||||||
J3DModelData* getModelData() { return mModelData; }
|
J3DModelData* getModelData() { return mModelData; }
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void onInterpCallback();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* 0x568 */ request_of_phase_process_class mPhase;
|
/* 0x568 */ request_of_phase_process_class mPhase;
|
||||||
/* 0x570 */ J3DModelData* mModelData;
|
/* 0x570 */ J3DModelData* mModelData;
|
||||||
@@ -42,6 +46,14 @@ private:
|
|||||||
/* 0x694 */ cXyz field_0x694[22];
|
/* 0x694 */ cXyz field_0x694[22];
|
||||||
/* 0x79C */ cXyz field_0x79c[22];
|
/* 0x79C */ cXyz field_0x79c[22];
|
||||||
/* 0x8A4 */ csXyz field_0x8a4[22];
|
/* 0x8A4 */ csXyz field_0x8a4[22];
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
static const int CHAIN_COUNT = 22;
|
||||||
|
cXyz mChainInterpPrev[CHAIN_COUNT];
|
||||||
|
cXyz mChainInterpCurr[CHAIN_COUNT];
|
||||||
|
bool mChainInterpPrevValid;
|
||||||
|
bool mChainInterpCurrValid;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(daObjFchain_c) == 0x928);
|
STATIC_ASSERT(sizeof(daObjFchain_c) == 0x928);
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ public:
|
|||||||
int Draw();
|
int Draw();
|
||||||
int Delete();
|
int Delete();
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void onInterpCallback();
|
||||||
|
#endif
|
||||||
|
|
||||||
enum Param_e {
|
enum Param_e {
|
||||||
LOCK_e = (1 << 6), NO_BASE_DISP = (1 << 7)
|
LOCK_e = (1 << 6), NO_BASE_DISP = (1 << 7)
|
||||||
};
|
};
|
||||||
@@ -50,6 +54,13 @@ private:
|
|||||||
/* 0x1020 */ dCcD_Cyl mCylinderCollider;
|
/* 0x1020 */ dCcD_Cyl mCylinderCollider;
|
||||||
/* 0x115C */ s32 mStopSwingingFrames;
|
/* 0x115C */ s32 mStopSwingingFrames;
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
cXyz mChainInterpPrev[64];
|
||||||
|
cXyz mChainInterpCurr[64];
|
||||||
|
bool mChainInterpPrevValid;
|
||||||
|
bool mChainInterpCurrValid;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Number of chain models
|
// Number of chain models
|
||||||
u32 getArg0() {
|
u32 getArg0() {
|
||||||
return fopAcM_GetParamBit(this, 0, 6);
|
return fopAcM_GetParamBit(this, 0, 6);
|
||||||
|
|||||||
@@ -118,6 +118,18 @@ class camera_class;
|
|||||||
class dCamera_c;
|
class dCamera_c;
|
||||||
typedef bool (dCamera_c::*engine_fn)(s32);
|
typedef bool (dCamera_c::*engine_fn)(s32);
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
struct DebugFlyCam {
|
||||||
|
bool initialized;
|
||||||
|
f32 pitch;
|
||||||
|
f32 yaw;
|
||||||
|
cXyz savedCenter;
|
||||||
|
cXyz savedEye;
|
||||||
|
f32 savedFovy;
|
||||||
|
cSAngle savedBank;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
class dCamera_c {
|
class dCamera_c {
|
||||||
public:
|
public:
|
||||||
class dCamInfo_c {
|
class dCamInfo_c {
|
||||||
@@ -1028,6 +1040,8 @@ public:
|
|||||||
bool test2Camera(s32);
|
bool test2Camera(s32);
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
bool freeCamera();
|
bool freeCamera();
|
||||||
|
bool executeDebugFlyCam();
|
||||||
|
void deactivateDebugFlyCam();
|
||||||
#endif
|
#endif
|
||||||
bool towerCamera(s32);
|
bool towerCamera(s32);
|
||||||
bool hookshotCamera(s32);
|
bool hookshotCamera(s32);
|
||||||
@@ -1376,6 +1390,10 @@ public:
|
|||||||
/* 0x970 */ dCamSetup_c mCamSetup;
|
/* 0x970 */ dCamSetup_c mCamSetup;
|
||||||
/* 0xAEC */ dCamParam_c mCamParam;
|
/* 0xAEC */ dCamParam_c mCamParam;
|
||||||
/* 0xB0C */ u8 field_0xb0c;
|
/* 0xB0C */ u8 field_0xb0c;
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
DebugFlyCam mDebugFlyCam;
|
||||||
|
#endif
|
||||||
}; // Size: 0xB10
|
}; // Size: 0xB10
|
||||||
|
|
||||||
dCamera_c* dCam_getBody();
|
dCamera_c* dCam_getBody();
|
||||||
|
|||||||
@@ -1845,6 +1845,12 @@ inline void dComIfGs_addDeathCount() {
|
|||||||
g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().addDeathCount();
|
g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().addDeathCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
inline u16 dComIfGs_getDeathCount() {
|
||||||
|
return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getDeathCount();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
inline char* dComIfGs_getPlayerName() {
|
inline char* dComIfGs_getPlayerName() {
|
||||||
return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getPlayerName();
|
return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getPlayerName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,11 @@ public:
|
|||||||
/* 0x108 */ int mSkipTimer;
|
/* 0x108 */ int mSkipTimer;
|
||||||
/* 0x10C */ int mSkipParameter;
|
/* 0x10C */ int mSkipParameter;
|
||||||
/* 0x110 */ BOOL mIsSkipFade;
|
/* 0x110 */ BOOL mIsSkipFade;
|
||||||
|
#if TARGET_PC
|
||||||
|
/* 0x114 */ char mSkipEventName[21];
|
||||||
|
#else
|
||||||
/* 0x114 */ char mSkipEventName[20];
|
/* 0x114 */ char mSkipEventName[20];
|
||||||
|
#endif
|
||||||
/* 0x128 */ u8 mCompulsory;
|
/* 0x128 */ u8 mCompulsory;
|
||||||
/* 0x129 */ bool mRoomInfoSet;
|
/* 0x129 */ bool mRoomInfoSet;
|
||||||
/* 0x12C */ int mRoomNo;
|
/* 0x12C */ int mRoomNo;
|
||||||
|
|||||||
@@ -223,6 +223,9 @@ private:
|
|||||||
/* 0x8F */ u8 field_0x8f;
|
/* 0x8F */ u8 field_0x8f;
|
||||||
/* 0x90 */ u8 field_0x90;
|
/* 0x90 */ u8 field_0x90;
|
||||||
/* 0x91 */ u8 field_0x91;
|
/* 0x91 */ u8 field_0x91;
|
||||||
|
#if TARGET_PC
|
||||||
|
bool previousMirror;
|
||||||
|
#endif
|
||||||
}; // Size: 0x94
|
}; // Size: 0x94
|
||||||
|
|
||||||
class dMap_HIO_list_c : public dMpath_HIO_n::hioList_c {
|
class dMap_HIO_list_c : public dMpath_HIO_n::hioList_c {
|
||||||
|
|||||||
@@ -169,6 +169,12 @@ public:
|
|||||||
|
|
||||||
void mapBlink() {}
|
void mapBlink() {}
|
||||||
|
|
||||||
|
#if PLATFORM_WII || TARGET_PC
|
||||||
|
f32 getMirrorPosX(f32 param_0, f32 param_1) {
|
||||||
|
return (field_0x11dc * 2.0f - (param_0 + param_1)) - param_1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Unknown name
|
// Unknown name
|
||||||
struct RegionTexData {
|
struct RegionTexData {
|
||||||
/* 0x00 */ float mMinX;
|
/* 0x00 */ float mMinX;
|
||||||
|
|||||||
@@ -66,6 +66,16 @@ public:
|
|||||||
_c90 = param_2;
|
_c90 = param_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WII || TARGET_PC
|
||||||
|
f32 getMirrorCenterPosX(f32 param_0, f32 param_1) {
|
||||||
|
if (_c90) {
|
||||||
|
return (mCenterPosX * 2.0f - (param_0 + param_1)) - param_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return param_0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Stage_c {
|
struct Stage_c {
|
||||||
// Incomplete class
|
// Incomplete class
|
||||||
|
|
||||||
|
|||||||
@@ -486,6 +486,9 @@ public:
|
|||||||
mDeathCount++;
|
mDeathCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if TARGET_PC
|
||||||
|
u16 getDeathCount() const { return mDeathCount; }
|
||||||
|
#endif
|
||||||
char* getPlayerName() const { return const_cast<char*>(mPlayerName); }
|
char* getPlayerName() const { return const_cast<char*>(mPlayerName); }
|
||||||
void setPlayerName(const char* i_name) {
|
void setPlayerName(const char* i_name) {
|
||||||
#if AVOID_UB
|
#if AVOID_UB
|
||||||
|
|||||||
@@ -12,9 +12,8 @@
|
|||||||
namespace dusk {
|
namespace dusk {
|
||||||
|
|
||||||
enum class AchievementCategory : uint8_t {
|
enum class AchievementCategory : uint8_t {
|
||||||
Story,
|
|
||||||
Collection,
|
|
||||||
Challenge,
|
Challenge,
|
||||||
|
Collection,
|
||||||
Minigame,
|
Minigame,
|
||||||
Misc,
|
Misc,
|
||||||
Glitched
|
Glitched
|
||||||
@@ -50,8 +49,6 @@ public:
|
|||||||
bool hasSignal(const char* key) const;
|
bool hasSignal(const char* key) const;
|
||||||
|
|
||||||
std::vector<Achievement> getAchievements() const;
|
std::vector<Achievement> getAchievements() const;
|
||||||
bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); }
|
|
||||||
std::string consumePendingUnlock();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Entry {
|
struct Entry {
|
||||||
@@ -68,7 +65,6 @@ private:
|
|||||||
std::unordered_set<std::string_view> m_signals;
|
std::unordered_set<std::string_view> m_signals;
|
||||||
bool m_loaded = false;
|
bool m_loaded = false;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
std::queue<std::string> m_pendingUnlocks;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dusk
|
} // namespace dusk
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "dusk/config_var.hpp"
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
|
||||||
|
enum class ActionBinds {
|
||||||
|
FIRST_PERSON_CAMERA,
|
||||||
|
CALL_MIDNA,
|
||||||
|
OPEN_DUSKLIGHT_MENU,
|
||||||
|
TURBO_SPEED_BUTTON,
|
||||||
|
COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActionBindData {
|
||||||
|
std::array<config::ActionBindConfigVar, 4>* configVars{};
|
||||||
|
std::string actionName{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActionBindPressData {
|
||||||
|
bool pressedCurFrame{false};
|
||||||
|
bool pressedPrevFrame{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
using ActionBindsMap = std::unordered_map<ActionBinds, ActionBindData>;
|
||||||
|
|
||||||
|
ActionBindsMap& getActionBinds();
|
||||||
|
|
||||||
|
bool isActionBound(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
void updateActionBindings();
|
||||||
|
|
||||||
|
bool getActionBindTrig(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
bool getActionBindHold(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
bool getActionBindHoldAnyPort(ActionBinds action);
|
||||||
|
|
||||||
|
int getActionBindButton(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,12 @@ namespace dusk {
|
|||||||
*
|
*
|
||||||
* This gets used for file paths and such, and cannot be changed!
|
* This gets used for file paths and such, and cannot be changed!
|
||||||
*/
|
*/
|
||||||
constexpr auto AppName = "Dusk";
|
constexpr auto AppName = "Dusklight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous AppName to migrate data from.
|
||||||
|
*/
|
||||||
|
constexpr auto LegacyAppName = "Dusk";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief The internal organization name for the game.
|
* \brief The internal organization name for the game.
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ void enterAutoSave();
|
|||||||
void autoSaving();
|
void autoSaving();
|
||||||
void waitingForWrite();
|
void waitingForWrite();
|
||||||
void endAutoSave();
|
void endAutoSave();
|
||||||
|
void toggleAutoSave(bool enabled);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef DUSK_CONFIG_HPP
|
#ifndef DUSK_CONFIG_HPP
|
||||||
#define DUSK_CONFIG_HPP
|
#define DUSK_CONFIG_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
#include "config_var.hpp"
|
#include "config_var.hpp"
|
||||||
@@ -111,6 +112,18 @@ void Save();
|
|||||||
*/
|
*/
|
||||||
ConfigVarBase* GetConfigVar(std::string_view name);
|
ConfigVarBase* GetConfigVar(std::string_view name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resets all custom action bindings for a specific port to nothing
|
||||||
|
*
|
||||||
|
* @param port The port to be cleared of action bindings
|
||||||
|
*/
|
||||||
|
void ClearAllActionBindings(int port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Call a function on every registered CVar.
|
||||||
|
*/
|
||||||
|
void EnumerateRegistered(std::function<void(ConfigVarBase&)> callback);
|
||||||
|
|
||||||
template <ConfigValue T>
|
template <ConfigValue T>
|
||||||
const ConfigImplBase* GetConfigImpl() {
|
const ConfigImplBase* GetConfigImpl() {
|
||||||
static ConfigImpl<T> config;
|
static ConfigImpl<T> config;
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ enum class ConfigVarLayer : u8 {
|
|||||||
* Will not get saved to config.
|
* Will not get saved to config.
|
||||||
*/
|
*/
|
||||||
Override,
|
Override,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CVar is temporarily overridden by speedrun mode.
|
||||||
|
* Will not get saved to config. Cleared when speedrun mode is disabled.
|
||||||
|
* Lower priority than Override, so launch args still win.
|
||||||
|
*/
|
||||||
|
Speedrun,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigImplBase;
|
class ConfigImplBase;
|
||||||
@@ -113,6 +120,12 @@ public:
|
|||||||
* This is necessary to make it legal to access.
|
* This is necessary to make it legal to access.
|
||||||
*/
|
*/
|
||||||
void markRegistered();
|
void markRegistered();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a speedrun-mode override if one is active on this CVar.
|
||||||
|
* Safe to call on any CVar, no-op if not at the Speedrun layer.
|
||||||
|
*/
|
||||||
|
virtual void clearSpeedrunOverride() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -189,6 +202,7 @@ public:
|
|||||||
case ConfigVarLayer::Value:
|
case ConfigVarLayer::Value:
|
||||||
return value;
|
return value;
|
||||||
case ConfigVarLayer::Override:
|
case ConfigVarLayer::Override:
|
||||||
|
case ConfigVarLayer::Speedrun:
|
||||||
return overrideValue;
|
return overrideValue;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
@@ -239,8 +253,42 @@ public:
|
|||||||
overrideValue = std::move(newValue);
|
overrideValue = std::move(newValue);
|
||||||
layer = ConfigVarLayer::Override;
|
layer = ConfigVarLayer::Override;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Give a CVar a speedrun-mode override value.
|
||||||
|
*
|
||||||
|
* Lower priority than a launch-arg override. Cleared when speedrun mode is disabled.
|
||||||
|
* The overridden value will not get saved to config.
|
||||||
|
*
|
||||||
|
* @param newValue The new value the CVar will get.
|
||||||
|
*/
|
||||||
|
void setSpeedrunValue(T newValue) {
|
||||||
|
checkRegistered();
|
||||||
|
if (layer != ConfigVarLayer::Override) {
|
||||||
|
overrideValue = std::move(newValue);
|
||||||
|
layer = ConfigVarLayer::Speedrun;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearOverride() {
|
||||||
|
checkRegistered();
|
||||||
|
if (layer == ConfigVarLayer::Override) {
|
||||||
|
overrideValue = {};
|
||||||
|
layer = ConfigVarLayer::Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearSpeedrunOverride() override {
|
||||||
|
checkRegistered();
|
||||||
|
if (layer == ConfigVarLayer::Speedrun) {
|
||||||
|
overrideValue = {};
|
||||||
|
layer = ConfigVarLayer::Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ActionBindConfigVar = ConfigVar<int>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DUSK_CONFIG_VAR_HPP
|
#endif // DUSK_CONFIG_VAR_HPP
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk::crash_reporting {
|
||||||
|
|
||||||
void InitializeCrashReporting();
|
enum class Consent {
|
||||||
void ShutdownCrashReporting();
|
Unavailable,
|
||||||
|
Unknown,
|
||||||
|
Given,
|
||||||
|
Revoked,
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace dusk
|
void initialize();
|
||||||
|
void shutdown();
|
||||||
|
Consent get_consent();
|
||||||
|
void set_consent(bool enabled);
|
||||||
|
|
||||||
|
} // namespace dusk::crash_reporting
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef DUSK_DISCORD_RPC
|
#ifdef DUSK_DISCORD
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk::discord {
|
||||||
namespace discord {
|
|
||||||
|
|
||||||
void Initialize();
|
void initialize();
|
||||||
|
void run_callbacks();
|
||||||
|
void update_presence();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
void RunCallbacks();
|
} // namespace dusk::discord
|
||||||
|
|
||||||
void UpdatePresence();
|
#endif // DUSK_DISCORD
|
||||||
|
|
||||||
void Shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DUSK_DISCORD_RPC
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ void rollgoalTableOffset(s16& out_ax, s16& out_az);
|
|||||||
extern bool s_sensor_keep_alive;
|
extern bool s_sensor_keep_alive;
|
||||||
bool get_sensor_keep_alive();
|
bool get_sensor_keep_alive();
|
||||||
void set_sensor_keep_alive(bool value);
|
void set_sensor_keep_alive(bool value);
|
||||||
|
bool rollgoal_gyro_enabled();
|
||||||
} // namespace dusk::gyro
|
} // namespace dusk::gyro
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef DUSK_IO_HPP
|
#ifndef DUSK_IO_HPP
|
||||||
#define DUSK_IO_HPP
|
#define DUSK_IO_HPP
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// I can't believe it's 2026 and neither SDL (no error codes) nor
|
// I can't believe it's 2026 and neither SDL (no error codes) nor
|
||||||
@@ -15,7 +16,7 @@ namespace dusk::io {
|
|||||||
* Methods on this class throw appropriate C++ exceptions when an error occurs.
|
* Methods on this class throw appropriate C++ exceptions when an error occurs.
|
||||||
*/
|
*/
|
||||||
class FileStream {
|
class FileStream {
|
||||||
void* file;
|
FILE* file;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileStream() noexcept;
|
FileStream() noexcept;
|
||||||
@@ -23,7 +24,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* \brief Take ownership of a FILE* handle.
|
* \brief Take ownership of a FILE* handle.
|
||||||
*/
|
*/
|
||||||
explicit FileStream(void* file);
|
explicit FileStream(FILE* file);
|
||||||
FileStream(const FileStream& other) = delete;
|
FileStream(const FileStream& other) = delete;
|
||||||
FileStream(FileStream&& other) noexcept;
|
FileStream(FileStream&& other) noexcept;
|
||||||
|
|
||||||
@@ -34,6 +35,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
static FileStream OpenRead(const char* utf8Path);
|
static FileStream OpenRead(const char* utf8Path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Open a file for reading at the given path.
|
||||||
|
*/
|
||||||
|
static FileStream OpenRead(const std::filesystem::path& path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Create a file for writing.
|
* \brief Create a file for writing.
|
||||||
*
|
*
|
||||||
@@ -41,16 +47,33 @@ public:
|
|||||||
*/
|
*/
|
||||||
static FileStream Create(const char* utf8Path);
|
static FileStream Create(const char* utf8Path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Create a file for writing.
|
||||||
|
*
|
||||||
|
* If there is an existing file, its contents are demolished.
|
||||||
|
*/
|
||||||
|
static FileStream Create(const std::filesystem::path& path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Read the byte contents of a file directly into a vector.
|
* \brief Read the byte contents of a file directly into a vector.
|
||||||
*/
|
*/
|
||||||
static std::vector<u8> ReadAllBytes(const char* utf8Path);
|
static std::vector<u8> ReadAllBytes(const char* utf8Path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read the byte contents of a file directly into a vector.
|
||||||
|
*/
|
||||||
|
static std::vector<u8> ReadAllBytes(const std::filesystem::path& path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Read the byte contents of a file directly into a vector.
|
* \brief Read the byte contents of a file directly into a vector.
|
||||||
*/
|
*/
|
||||||
static void WriteAllText(const char* utf8Path, std::string_view text);
|
static void WriteAllText(const char* utf8Path, std::string_view text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read the byte contents of a file directly into a vector.
|
||||||
|
*/
|
||||||
|
static void WriteAllText(const std::filesystem::path& path, std::string_view text);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Read the remaining contents of the file directly into a vector.
|
* \brief Read the remaining contents of the file directly into a vector.
|
||||||
*/
|
*/
|
||||||
@@ -59,17 +82,24 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Get direct access to the underlying FILE* handle.
|
* Get direct access to the underlying FILE* handle.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] void* GetFileHandle() const noexcept {
|
[[nodiscard]] void* GetFileHandle() const noexcept { return file; }
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write data to the file.
|
* Write data to the file.
|
||||||
*/
|
*/
|
||||||
void Write(const char* data, size_t dataLen);
|
void Write(const char* data, size_t dataLen);
|
||||||
|
|
||||||
|
FILE* ToInner();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a std::filesystem::path to a std::string, UTF-8, without exploding on Windows.
|
||||||
|
*/
|
||||||
|
inline std::string fs_path_to_string(const std::filesystem::path& path) {
|
||||||
|
const auto u8str = path.u8string();
|
||||||
|
return {reinterpret_cast<const char*>(u8str.c_str())};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::io
|
||||||
|
|
||||||
#endif // DUSK_IO_HPP
|
#endif // DUSK_IO_HPP
|
||||||
|
|||||||
@@ -4,11 +4,22 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
|
|
||||||
extern bool IsRunning;
|
extern bool IsRunning;
|
||||||
extern bool IsShuttingDown;
|
extern bool IsShuttingDown;
|
||||||
extern bool IsGameLaunched;
|
extern bool IsGameLaunched;
|
||||||
extern bool IsFocusPaused;
|
extern bool RestartRequested;
|
||||||
extern std::filesystem::path ConfigPath;
|
extern std::filesystem::path ConfigPath;
|
||||||
}
|
|
||||||
|
#if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \
|
||||||
|
(defined(TARGET_OS_TV) && TARGET_OS_TV)
|
||||||
|
inline constexpr bool SupportsProcessRestart = false;
|
||||||
|
#else
|
||||||
|
inline constexpr bool SupportsProcessRestart = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void RequestRestart() noexcept;
|
||||||
|
|
||||||
|
} // namespace dusk
|
||||||
|
|
||||||
#endif // DUSK_MAIN_H
|
#endif // DUSK_MAIN_H
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
struct RoomEntry {
|
struct RoomEntry {
|
||||||
u8 roomNo;
|
u8 roomNo;
|
||||||
std::vector<s16> roomPoints = {};
|
std::vector<s16> roomPoints = {};
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
#define _SRC_DUSK_MATH_H_
|
#define _SRC_DUSK_MATH_H_
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <array>
|
|
||||||
#include <limits>
|
|
||||||
#include <bit>
|
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846f
|
#define M_PI 3.14159265358979323846f
|
||||||
@@ -19,139 +16,6 @@ inline float i_cosf(float x) { return cos(x); }
|
|||||||
inline float i_tanf(float x) { return tan(x); }
|
inline float i_tanf(float x) { return tan(x); }
|
||||||
inline float i_acosf(float x) { return acos(x); }
|
inline float i_acosf(float x) { return acos(x); }
|
||||||
|
|
||||||
|
#include <dolphin/ppc_math.h>
|
||||||
// frsqrte matching courtesy of Geotale, with reference to https://achurch.org/cpu-tests/ppc750cl.s
|
|
||||||
|
|
||||||
struct BaseAndDec32 {
|
|
||||||
uint32_t base;
|
|
||||||
int32_t dec;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BaseAndDec64 {
|
|
||||||
uint64_t base;
|
|
||||||
int64_t dec;
|
|
||||||
};
|
|
||||||
|
|
||||||
union c32 {
|
|
||||||
constexpr c32(const float p) {
|
|
||||||
f = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr c32(const uint32_t p) {
|
|
||||||
u = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t u;
|
|
||||||
float f;
|
|
||||||
};
|
|
||||||
|
|
||||||
union c64 {
|
|
||||||
constexpr c64(const double p) {
|
|
||||||
f = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr c64(const uint64_t p) {
|
|
||||||
u = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t u;
|
|
||||||
double f;
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr uint64_t EXPONENT_SHIFT_F64 = 52;
|
|
||||||
static constexpr uint64_t MANTISSA_MASK_F64 = 0x000fffffffffffffULL;
|
|
||||||
static constexpr uint64_t EXPONENT_MASK_F64 = 0x7ff0000000000000ULL;
|
|
||||||
static constexpr uint64_t SIGN_MASK_F64 = 0x8000000000000000ULL;
|
|
||||||
|
|
||||||
static constexpr std::array<BaseAndDec64, 32> RSQRTE_TABLE = {{
|
|
||||||
{0x69fa000000000ULL, -0x15a0000000LL},
|
|
||||||
{0x5f2e000000000ULL, -0x13cc000000LL},
|
|
||||||
{0x554a000000000ULL, -0x1234000000LL},
|
|
||||||
{0x4c30000000000ULL, -0x10d4000000LL},
|
|
||||||
{0x43c8000000000ULL, -0x0f9c000000LL},
|
|
||||||
{0x3bfc000000000ULL, -0x0e88000000LL},
|
|
||||||
{0x34b8000000000ULL, -0x0d94000000LL},
|
|
||||||
{0x2df0000000000ULL, -0x0cb8000000LL},
|
|
||||||
{0x2794000000000ULL, -0x0bf0000000LL},
|
|
||||||
{0x219c000000000ULL, -0x0b40000000LL},
|
|
||||||
{0x1bfc000000000ULL, -0x0aa0000000LL},
|
|
||||||
{0x16ae000000000ULL, -0x0a0c000000LL},
|
|
||||||
{0x11a8000000000ULL, -0x0984000000LL},
|
|
||||||
{0x0ce6000000000ULL, -0x090c000000LL},
|
|
||||||
{0x0862000000000ULL, -0x0898000000LL},
|
|
||||||
{0x0416000000000ULL, -0x082c000000LL},
|
|
||||||
{0xffe8000000000ULL, -0x1e90000000LL},
|
|
||||||
{0xf0a4000000000ULL, -0x1c00000000LL},
|
|
||||||
{0xe2a8000000000ULL, -0x19c0000000LL},
|
|
||||||
{0xd5c8000000000ULL, -0x17c8000000LL},
|
|
||||||
{0xc9e4000000000ULL, -0x1610000000LL},
|
|
||||||
{0xbedc000000000ULL, -0x1490000000LL},
|
|
||||||
{0xb498000000000ULL, -0x1330000000LL},
|
|
||||||
{0xab00000000000ULL, -0x11f8000000LL},
|
|
||||||
{0xa204000000000ULL, -0x10e8000000LL},
|
|
||||||
{0x9994000000000ULL, -0x0fe8000000LL},
|
|
||||||
{0x91a0000000000ULL, -0x0f08000000LL},
|
|
||||||
{0x8a1c000000000ULL, -0x0e38000000LL},
|
|
||||||
{0x8304000000000ULL, -0x0d78000000LL},
|
|
||||||
{0x7c48000000000ULL, -0x0cc8000000LL},
|
|
||||||
{0x75e4000000000ULL, -0x0c28000000LL},
|
|
||||||
{0x6fd0000000000ULL, -0x0b98000000LL},
|
|
||||||
}};
|
|
||||||
|
|
||||||
[[nodiscard]] static inline double frsqrte(const double val) {
|
|
||||||
c64 bits(val);
|
|
||||||
|
|
||||||
uint64_t mantissa = bits.u & MANTISSA_MASK_F64;
|
|
||||||
int64_t exponent = bits.u & EXPONENT_MASK_F64;
|
|
||||||
bool sign = (bits.u & SIGN_MASK_F64) != 0;
|
|
||||||
|
|
||||||
// Handle 0 case
|
|
||||||
if (mantissa == 0 && exponent == 0) {
|
|
||||||
return std::copysign(std::numeric_limits<double>::infinity(), bits.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle NaN-like
|
|
||||||
if (exponent == EXPONENT_MASK_F64) {
|
|
||||||
if (mantissa == 0) {
|
|
||||||
return sign ? std::numeric_limits<double>::quiet_NaN() : 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle negative inputs
|
|
||||||
if (sign) {
|
|
||||||
return std::numeric_limits<double>::quiet_NaN();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exponent == 0) {
|
|
||||||
// Shift so one bit goes to where the exponent would be,
|
|
||||||
// then clear that bit to mimic a not-subnormal number!
|
|
||||||
// Aka, if there are 12 leading zeroes, shift left once
|
|
||||||
uint32_t shift = std::countl_zero(mantissa) - static_cast<uint32_t>(63 - EXPONENT_SHIFT_F64);
|
|
||||||
|
|
||||||
mantissa <<= shift;
|
|
||||||
mantissa &= MANTISSA_MASK_F64;
|
|
||||||
// The shift is subtracted by 1 because denormals by default
|
|
||||||
// are offset by 1 (exponent 0 doesn't have implied 1 bit)
|
|
||||||
exponent -= static_cast<int64_t>(shift - 1) << EXPONENT_SHIFT_F64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In reality this doesn't get the full exponent -- Only the least significant bit
|
|
||||||
// Only that's needed because square roots of higher exponent bits simply multiply the
|
|
||||||
// result by 2!!
|
|
||||||
uint32_t key = static_cast<uint32_t>((static_cast<uint64_t>(exponent) | mantissa) >> 37);
|
|
||||||
uint64_t new_exp =
|
|
||||||
(static_cast<uint64_t>((0xbfcLL << EXPONENT_SHIFT_F64) - exponent) >> 1) & EXPONENT_MASK_F64;
|
|
||||||
|
|
||||||
// Remove the bits relating to anything higher than the LSB of the exponent
|
|
||||||
const auto &entry = RSQRTE_TABLE[0x1f & (key >> 11)];
|
|
||||||
|
|
||||||
// The result is given by an estimate then an adjustment based on the original
|
|
||||||
// key that was computed
|
|
||||||
uint64_t new_mantissa = static_cast<uint64_t>(entry.base + entry.dec * static_cast<int64_t>(key & 0x7ff));
|
|
||||||
|
|
||||||
return c64(new_exp | new_mantissa).f;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _SRC_DUSK_MATH_H_
|
#endif // _SRC_DUSK_MATH_H_
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef DUSK_CONFIG_H
|
#ifndef DUSK_CONFIG_H
|
||||||
#define DUSK_CONFIG_H
|
#define DUSK_CONFIG_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "dusk/config_var.hpp"
|
#include "dusk/config_var.hpp"
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
@@ -21,6 +23,17 @@ enum class GameLanguage : u8 {
|
|||||||
Italian = OS_LANGUAGE_ITALIAN,
|
Italian = OS_LANGUAGE_ITALIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DiscVerificationState : u8 {
|
||||||
|
Unknown = 0,
|
||||||
|
Success,
|
||||||
|
HashMismatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GyroMode : u8 {
|
||||||
|
Sensor = 0,
|
||||||
|
Mouse = 1,
|
||||||
|
};
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
template <>
|
template <>
|
||||||
struct ConfigEnumRange<BloomMode> {
|
struct ConfigEnumRange<BloomMode> {
|
||||||
@@ -33,6 +46,18 @@ struct ConfigEnumRange<GameLanguage> {
|
|||||||
static constexpr auto min = GameLanguage::English;
|
static constexpr auto min = GameLanguage::English;
|
||||||
static constexpr auto max = GameLanguage::Italian;
|
static constexpr auto max = GameLanguage::Italian;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ConfigEnumRange<DiscVerificationState> {
|
||||||
|
static constexpr auto min = DiscVerificationState::Unknown;
|
||||||
|
static constexpr auto max = DiscVerificationState::HashMismatch;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ConfigEnumRange<GyroMode> {
|
||||||
|
static constexpr auto min = GyroMode::Sensor;
|
||||||
|
static constexpr auto max = GyroMode::Mouse;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persistent user settings
|
// Persistent user settings
|
||||||
@@ -45,6 +70,8 @@ struct UserSettings {
|
|||||||
ConfigVar<bool> enableFullscreen;
|
ConfigVar<bool> enableFullscreen;
|
||||||
ConfigVar<bool> enableVsync;
|
ConfigVar<bool> enableVsync;
|
||||||
ConfigVar<bool> lockAspectRatio;
|
ConfigVar<bool> lockAspectRatio;
|
||||||
|
ConfigVar<bool> enableFpsOverlay;
|
||||||
|
ConfigVar<int> fpsOverlayCorner;
|
||||||
} video;
|
} video;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -56,6 +83,7 @@ struct UserSettings {
|
|||||||
ConfigVar<int> fanfareVolume;
|
ConfigVar<int> fanfareVolume;
|
||||||
ConfigVar<bool> enableReverb;
|
ConfigVar<bool> enableReverb;
|
||||||
ConfigVar<bool> enableHrtf;
|
ConfigVar<bool> enableHrtf;
|
||||||
|
ConfigVar<bool> menuSounds;
|
||||||
} audio;
|
} audio;
|
||||||
|
|
||||||
// Game settings
|
// Game settings
|
||||||
@@ -66,13 +94,11 @@ struct UserSettings {
|
|||||||
// QoL
|
// QoL
|
||||||
ConfigVar<bool> enableQuickTransform;
|
ConfigVar<bool> enableQuickTransform;
|
||||||
ConfigVar<bool> hideTvSettingsScreen;
|
ConfigVar<bool> hideTvSettingsScreen;
|
||||||
ConfigVar<bool> skipWarningScreen;
|
|
||||||
ConfigVar<bool> biggerWallets;
|
ConfigVar<bool> biggerWallets;
|
||||||
ConfigVar<bool> noReturnRupees;
|
ConfigVar<bool> noReturnRupees;
|
||||||
ConfigVar<bool> disableRupeeCutscenes;
|
ConfigVar<bool> disableRupeeCutscenes;
|
||||||
ConfigVar<bool> noSwordRecoil;
|
ConfigVar<bool> noSwordRecoil;
|
||||||
ConfigVar<int> damageMultiplier;
|
ConfigVar<int> damageMultiplier;
|
||||||
ConfigVar<bool> hyperEnemies;
|
|
||||||
ConfigVar<bool> noHeartDrops;
|
ConfigVar<bool> noHeartDrops;
|
||||||
ConfigVar<bool> instantDeath;
|
ConfigVar<bool> instantDeath;
|
||||||
ConfigVar<bool> fastClimbing;
|
ConfigVar<bool> fastClimbing;
|
||||||
@@ -86,13 +112,21 @@ struct UserSettings {
|
|||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
ConfigVar<bool> enableMirrorMode;
|
ConfigVar<bool> enableMirrorMode;
|
||||||
ConfigVar<bool> disableMainHUD;
|
ConfigVar<bool> minimalHUD;
|
||||||
ConfigVar<bool> pauseOnFocusLost;
|
ConfigVar<bool> pauseOnFocusLost;
|
||||||
ConfigVar<bool> enableLinkDollRotation;
|
ConfigVar<bool> enableLinkDollRotation;
|
||||||
ConfigVar<bool> enableAchievementNotifications;
|
ConfigVar<bool> enableAchievementToasts;
|
||||||
|
ConfigVar<bool> enableControllerToasts;
|
||||||
|
ConfigVar<bool> enableDiscordPresence;
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
|
ConfigVar<bool> enhancedLighting;
|
||||||
|
ConfigVar<bool> enableSpecularLighting;
|
||||||
|
ConfigVar<bool> enableRimLighting;
|
||||||
|
ConfigVar<float> specularIntensity;
|
||||||
|
ConfigVar<float> rimIntensity;
|
||||||
|
ConfigVar<float> ambientLightMultiplier;
|
||||||
|
ConfigVar<float> diffuseLightMultiplier;
|
||||||
ConfigVar<BloomMode> bloomMode;
|
ConfigVar<BloomMode> bloomMode;
|
||||||
ConfigVar<float> bloomMultiplier;
|
ConfigVar<float> bloomMultiplier;
|
||||||
ConfigVar<bool> disableWaterRefraction;
|
ConfigVar<bool> disableWaterRefraction;
|
||||||
@@ -101,12 +135,14 @@ struct UserSettings {
|
|||||||
ConfigVar<int> shadowResolutionMultiplier;
|
ConfigVar<int> shadowResolutionMultiplier;
|
||||||
ConfigVar<bool> enableDepthOfField;
|
ConfigVar<bool> enableDepthOfField;
|
||||||
ConfigVar<bool> enableMapBackground;
|
ConfigVar<bool> enableMapBackground;
|
||||||
|
ConfigVar<bool> disableCutscenePillarboxing;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
ConfigVar<bool> noLowHpSound;
|
ConfigVar<bool> noLowHpSound;
|
||||||
ConfigVar<bool> midnasLamentNonStop;
|
ConfigVar<bool> midnasLamentNonStop;
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
|
ConfigVar<GyroMode> gyroMode;
|
||||||
ConfigVar<bool> enableGyroAim;
|
ConfigVar<bool> enableGyroAim;
|
||||||
ConfigVar<bool> enableGyroRollgoal;
|
ConfigVar<bool> enableGyroRollgoal;
|
||||||
ConfigVar<float> gyroSensitivityX;
|
ConfigVar<float> gyroSensitivityX;
|
||||||
@@ -119,7 +155,12 @@ struct UserSettings {
|
|||||||
ConfigVar<bool> freeCamera;
|
ConfigVar<bool> freeCamera;
|
||||||
ConfigVar<bool> invertCameraXAxis;
|
ConfigVar<bool> invertCameraXAxis;
|
||||||
ConfigVar<bool> invertCameraYAxis;
|
ConfigVar<bool> invertCameraYAxis;
|
||||||
|
ConfigVar<bool> invertFirstPersonXAxis;
|
||||||
|
ConfigVar<bool> invertFirstPersonYAxis;
|
||||||
ConfigVar<float> freeCameraSensitivity;
|
ConfigVar<float> freeCameraSensitivity;
|
||||||
|
ConfigVar<bool> debugFlyCam;
|
||||||
|
ConfigVar<bool> debugFlyCamLockEvents;
|
||||||
|
ConfigVar<bool> allowBackgroundInput;
|
||||||
|
|
||||||
// Cheats
|
// Cheats
|
||||||
ConfigVar<bool> infiniteHearts;
|
ConfigVar<bool> infiniteHearts;
|
||||||
@@ -134,6 +175,7 @@ struct UserSettings {
|
|||||||
ConfigVar<bool> alwaysGreatspin;
|
ConfigVar<bool> alwaysGreatspin;
|
||||||
ConfigVar<bool> enableFastIronBoots;
|
ConfigVar<bool> enableFastIronBoots;
|
||||||
ConfigVar<bool> canTransformAnywhere;
|
ConfigVar<bool> canTransformAnywhere;
|
||||||
|
ConfigVar<bool> fastRoll;
|
||||||
ConfigVar<bool> fastSpinner;
|
ConfigVar<bool> fastSpinner;
|
||||||
ConfigVar<bool> freeMagicArmor;
|
ConfigVar<bool> freeMagicArmor;
|
||||||
|
|
||||||
@@ -142,22 +184,36 @@ struct UserSettings {
|
|||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
ConfigVar<bool> enableTurboKeybind;
|
ConfigVar<bool> enableTurboKeybind;
|
||||||
|
ConfigVar<bool> enableResetKeybind;
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
ConfigVar<bool> speedrunMode;
|
ConfigVar<bool> speedrunMode;
|
||||||
ConfigVar<bool> liveSplitEnabled;
|
ConfigVar<bool> liveSplitEnabled;
|
||||||
|
ConfigVar<bool> showSpeedrunRTATimer;
|
||||||
|
ConfigVar<bool> recordingMode;
|
||||||
|
ConfigVar<bool> showInputViewer;
|
||||||
|
ConfigVar<bool> showInputViewerGyro;
|
||||||
} game;
|
} game;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
ConfigVar<std::string> isoPath;
|
ConfigVar<std::string> isoPath;
|
||||||
|
ConfigVar<DiscVerificationState> isoVerification;
|
||||||
ConfigVar<std::string> graphicsBackend;
|
ConfigVar<std::string> graphicsBackend;
|
||||||
ConfigVar<bool> skipPreLaunchUI;
|
ConfigVar<bool> skipPreLaunchUI;
|
||||||
ConfigVar<bool> showPipelineCompilation;
|
ConfigVar<bool> showPipelineCompilation;
|
||||||
ConfigVar<bool> wasPresetChosen;
|
ConfigVar<bool> wasPresetChosen;
|
||||||
ConfigVar<bool> enableCrashReporting;
|
ConfigVar<bool> checkForUpdates;
|
||||||
ConfigVar<bool> duskMenuOpen;
|
|
||||||
ConfigVar<int> cardFileType;
|
ConfigVar<int> cardFileType;
|
||||||
|
ConfigVar<bool> enableAdvancedSettings;
|
||||||
} backend;
|
} backend;
|
||||||
|
|
||||||
|
// Arrays of size 4 for 4 ports
|
||||||
|
struct {
|
||||||
|
std::array<ActionBindConfigVar, 4> firstPersonCamera;
|
||||||
|
std::array<ActionBindConfigVar, 4> callMidna;
|
||||||
|
std::array<ActionBindConfigVar, 4> openDusklightMenu;
|
||||||
|
std::array<ActionBindConfigVar, 4> turboSpeedButton;
|
||||||
|
} actionBindings;
|
||||||
};
|
};
|
||||||
|
|
||||||
UserSettings& getSettings();
|
UserSettings& getSettings();
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <aurora/aurora.h>
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
|
||||||
|
struct SpeedrunInfo {
|
||||||
|
void startRun() {
|
||||||
|
m_isRunStarted = true;
|
||||||
|
m_startTimestamp = OSGetTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopRun() {
|
||||||
|
m_isRunStarted = false;
|
||||||
|
m_endTimestamp = OSGetTime() - m_startTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
m_isRunStarted = false;
|
||||||
|
m_startTimestamp = 0;
|
||||||
|
m_endTimestamp = 0;
|
||||||
|
m_isPauseIGT = false;
|
||||||
|
m_loadStartTimestamp = 0;
|
||||||
|
m_totalLoadTime = 0;
|
||||||
|
m_igtTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isRunStarted = false;
|
||||||
|
OSTime m_startTimestamp = 0;
|
||||||
|
OSTime m_endTimestamp = 0;
|
||||||
|
|
||||||
|
bool m_isPauseIGT = false;
|
||||||
|
OSTime m_loadStartTimestamp = 0;
|
||||||
|
OSTime m_totalLoadTime = 0;
|
||||||
|
OSTime m_igtTimer = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SpeedrunInfo m_speedrunInfo;
|
||||||
|
|
||||||
|
void resetForSpeedrunMode();
|
||||||
|
|
||||||
|
} // namespace dusk
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "Z2AudioLib/Z2EnvSeMgr.h"
|
#include "Z2AudioLib/Z2EnvSeMgr.h"
|
||||||
#include "Z2AudioLib/Z2LinkMgr.h"
|
#include "Z2AudioLib/Z2LinkMgr.h"
|
||||||
#include "dusk/audio.h"
|
#include "dusk/audio.h"
|
||||||
|
#include "dusk/settings.h"
|
||||||
|
|
||||||
class mDoAud_zelAudio_c : public Z2AudioMgr {
|
class mDoAud_zelAudio_c : public Z2AudioMgr {
|
||||||
public:
|
public:
|
||||||
@@ -132,6 +133,18 @@ inline void mDoAud_seStart(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_re
|
|||||||
-1.0f, -1.0f, 0);
|
-1.0f, -1.0f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
inline void mDoAud_seStartMenu(u32 i_sfxID) {
|
||||||
|
if (!mDoAud_zelAudio_c::isInitFlag()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dusk::getSettings().audio.menuSounds.getValue()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mDoAud_seStart(i_sfxID, nullptr, 0, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
inline void mDoAud_seStartLevel(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_reverb) {
|
inline void mDoAud_seStartLevel(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_reverb) {
|
||||||
DUSK_AUDIO_SKIP()
|
DUSK_AUDIO_SKIP()
|
||||||
Z2AudioMgr::getInterface()->seStartLevel(i_sfxID, i_sePos, param_2, i_reverb, 1.0f, 1.0f,
|
Z2AudioMgr::getInterface()->seStartLevel(i_sfxID, i_sePos, param_2, i_reverb, 1.0f, 1.0f,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#ifndef J3DSTRUCT_H
|
#ifndef J3DSTRUCT_H
|
||||||
#define J3DSTRUCT_H
|
#define J3DSTRUCT_H
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <gx.h>
|
#include <gx.h>
|
||||||
#include <mtx.h>
|
#include <mtx.h>
|
||||||
#include <mtx.h>
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "JSystem/JMath/JMath.h"
|
#include "JSystem/JMath/JMath.h"
|
||||||
|
|
||||||
|
|||||||
@@ -556,8 +556,8 @@ void J3DModelLoader::readVertexData(const J3DVertexBlock& block, J3DVertexData&
|
|||||||
|
|
||||||
if (attr == GX_VA_POS) {
|
if (attr == GX_VA_POS) {
|
||||||
// can be a little off due to 0x20 alignment, account for that
|
// can be a little off due to 0x20 alignment, account for that
|
||||||
u32 expect = ((data.mVtxNum * vertStride) + 0x1F) & ~0x1F;
|
// u32 expect = ((data.mVtxNum * vertStride) + 0x1F) & ~0x1F;
|
||||||
JUT_ASSERT(1234, expect == addrDiff);
|
// JUT_ASSERT(1234, expect == addrDiff);
|
||||||
} else if (attr == GX_VA_NRM) {
|
} else if (attr == GX_VA_NRM) {
|
||||||
data.mNrmNum = num;
|
data.mNrmNum = num;
|
||||||
} else if (attr == GX_VA_CLR0) {
|
} else if (attr == GX_VA_CLR0) {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#include "JSystem/JUtility/JUTFader.h"
|
#include "JSystem/JUtility/JUTFader.h"
|
||||||
#include "JSystem/J2DGraph/J2DOrthoGraph.h"
|
#include "JSystem/J2DGraph/J2DOrthoGraph.h"
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include <algorithm>
|
||||||
|
#endif
|
||||||
|
|
||||||
JUTFader::JUTFader(int x, int y, int width, int height, JUtility::TColor pColor)
|
JUTFader::JUTFader(int x, int y, int width, int height, JUtility::TColor pColor)
|
||||||
: mColor(pColor), mBox(x, y, x + width, y + height) {
|
: mColor(pColor), mBox(x, y, x + width, y + height) {
|
||||||
mStatus = None;
|
mStatus = None;
|
||||||
@@ -63,14 +67,24 @@ void JUTFader::advance() {
|
|||||||
|
|
||||||
void JUTFader::control() {
|
void JUTFader::control() {
|
||||||
advance();
|
advance();
|
||||||
#ifndef TARGET_PC
|
|
||||||
// FRAME INTERP NOTE: Draw is called by JFWDisplay when interpolation is active
|
|
||||||
draw();
|
draw();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JUTFader::draw() {
|
void JUTFader::draw() {
|
||||||
if (mColor.a != 0) {
|
if (mColor.a != 0) {
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
if (dusk::frame_interp::is_enabled() && mDuration != 0) {
|
||||||
|
const auto step = dusk::frame_interp::get_interpolation_step();
|
||||||
|
const auto progress = static_cast<f32>(mTimer) / static_cast<f32>(mDuration);
|
||||||
|
const auto timer = mTimer - 1 + step + progress;
|
||||||
|
auto alpha = timer / mDuration;
|
||||||
|
if (mStatus == FadeIn) {
|
||||||
|
alpha = 1.0f - alpha;
|
||||||
|
}
|
||||||
|
alpha = std::clamp(alpha, 0.0f, 1.0f);
|
||||||
|
mColor.a = static_cast<u8>(alpha * 255.0f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
J2DOrthoGraph orthograph;
|
J2DOrthoGraph orthograph;
|
||||||
orthograph.setColor(mColor);
|
orthograph.setColor(mColor);
|
||||||
orthograph.fillBox(mBox);
|
orthograph.fillBox(mBox);
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "os_report.h"
|
#include "os_report.h"
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
u32 JUTGamePad::CRumble::sChannelMask[4] = {
|
u32 JUTGamePad::CRumble::sChannelMask[4] = {
|
||||||
PAD_CHAN0_BIT,
|
PAD_CHAN0_BIT,
|
||||||
PAD_CHAN1_BIT,
|
PAD_CHAN1_BIT,
|
||||||
@@ -85,6 +89,9 @@ u32 JUTGamePad::sRumbleSupported;
|
|||||||
|
|
||||||
u32 JUTGamePad::read() {
|
u32 JUTGamePad::read() {
|
||||||
sRumbleSupported = PADRead(mPadStatus);
|
sRumbleSupported = PADRead(mPadStatus);
|
||||||
|
#if TARGET_PC
|
||||||
|
dusk::updateActionBindings();
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (sClampMode) {
|
switch (sClampMode) {
|
||||||
case EClampStick:
|
case EClampStick:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Android Shell
|
# Android Shell
|
||||||
|
|
||||||
This directory contains a minimal SDLActivity-based Android app wrapper for Dusk.
|
This directory contains a minimal SDLActivity-based Android app wrapper for Dusklight.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -66,12 +66,11 @@ Output APK:
|
|||||||
You can pass command-line args through the activity intent:
|
You can pass command-line args through the activity intent:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb shell am start -n com.twilitrealm.dusk/.DuskActivity \
|
adb shell am start -n dev.twilitrealm.dusk/.DuskActivity \
|
||||||
--es dusk_args "'/sdcard/Download/The Legend of Zelda: Twilight Princess (USA).iso'"
|
--es dusk_args "--backend vulkan"
|
||||||
```
|
```
|
||||||
|
|
||||||
Supported extras:
|
Supported extras:
|
||||||
|
|
||||||
- `dusk_args`: single shell-like argument string
|
- `dusk_args`: single shell-like argument string
|
||||||
- `dusk_argv`: string-array argv
|
- `dusk_argv`: string-array argv
|
||||||
- `dusk_disc`: compatibility shortcut (single ISO path)
|
|
||||||
|
|||||||
@@ -2,12 +2,22 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def duskRepoDir = rootProject.projectDir.parentFile.parentFile
|
||||||
|
def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusklight').get().asFile
|
||||||
|
def syncDuskAssets = tasks.register('syncDuskAssets', Sync) {
|
||||||
|
from(new File(duskRepoDir, 'res')) {
|
||||||
|
into 'res'
|
||||||
|
exclude '**/.DS_Store'
|
||||||
|
}
|
||||||
|
into duskGeneratedAssetsDir
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.twilitrealm.dusk'
|
namespace 'dev.twilitrealm.dusk'
|
||||||
compileSdk 36
|
compileSdk 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'com.twilitrealm.dusk'
|
applicationId 'dev.twilitrealm.dusk'
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
@@ -27,7 +37,7 @@ android {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
jniLibs.srcDirs = ['src/main/jniLibs']
|
jniLibs.srcDirs = ['src/main/jniLibs']
|
||||||
assets.srcDirs = ['../../assets']
|
assets.srcDirs = [duskGeneratedAssetsDir]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,3 +58,10 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.configureEach { task ->
|
||||||
|
if ((task.name.startsWith('merge') && task.name.endsWith('Assets')) ||
|
||||||
|
task.name.toLowerCase().contains('lint')) {
|
||||||
|
task.dependsOn(syncDuskAssets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
# Keep SDL activity and related JNI bridge methods.
|
# Keep SDL activity and related JNI bridge methods.
|
||||||
-keep class org.libsdl.app.** { *; }
|
-keep class org.libsdl.app.** { *; }
|
||||||
|
-keep class dev.twilitrealm.dusk.DuskHttpClient { *; }
|
||||||
|
-keep class dev.twilitrealm.dusk.DuskHttpClient$Response { *; }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<uses-feature android:name="android.hardware.type.pc" android:required="false" />
|
<uses-feature android:name="android.hardware.type.pc" android:required="false" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@@ -23,8 +24,19 @@
|
|||||||
<meta-data android:name="android.game_mode_config"
|
<meta-data android:name="android.game_mode_config"
|
||||||
android:resource="@xml/game_mode_config" />
|
android:resource="@xml/game_mode_config" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="dev.twilitrealm.dusk.DuskDocumentsProvider"
|
||||||
|
android:authorities="dev.twilitrealm.dusk.documents"
|
||||||
|
android:exported="true"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
android:permission="android.permission.MANAGE_DOCUMENTS">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
||||||
|
</intent-filter>
|
||||||
|
</provider>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.twilitrealm.dusk.DuskActivity"
|
android:name="dev.twilitrealm.dusk.DuskActivity"
|
||||||
android:alwaysRetainTaskState="true"
|
android:alwaysRetainTaskState="true"
|
||||||
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -1,18 +1,39 @@
|
|||||||
package com.twilitrealm.dusk;
|
package dev.twilitrealm.dusk;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.ClipData;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
|
import android.view.WindowInsetsController;
|
||||||
|
|
||||||
import org.libsdl.app.SDLActivity;
|
import org.libsdl.app.SDLActivity;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DuskActivity extends SDLActivity {
|
public class DuskActivity extends SDLActivity {
|
||||||
|
private static final String TAG = "DuskActivity";
|
||||||
|
private static final int FOLDER_DIALOG_REQUEST_CODE = 0x4455;
|
||||||
|
private static final String EXTERNAL_STORAGE_AUTHORITY =
|
||||||
|
"com.android.externalstorage.documents";
|
||||||
|
|
||||||
|
private long folderDialogUserdata = 0;
|
||||||
|
|
||||||
|
private static native void nativeFolderDialogResult(long userdata, String path, String error);
|
||||||
|
|
||||||
private static String[] splitArgs(String raw) {
|
private static String[] splitArgs(String raw) {
|
||||||
List<String> out = new ArrayList<>();
|
List<String> out = new ArrayList<>();
|
||||||
StringBuilder current = new StringBuilder();
|
StringBuilder current = new StringBuilder();
|
||||||
@@ -61,20 +82,48 @@ public class DuskActivity extends SDLActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
hideSystemBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
hideSystemBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
if (hasFocus) {
|
||||||
|
hideSystemBars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideSystemBars() {
|
||||||
|
Window window = getWindow();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
|
window.setDecorFitsSystemWindows(false);
|
||||||
|
WindowInsetsController ctrl = window.getDecorView().getWindowInsetsController();
|
||||||
|
if (ctrl != null) {
|
||||||
|
ctrl.setSystemBarsBehavior(
|
||||||
|
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||||
|
ctrl.hide(WindowInsets.Type.systemBars());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
View decorView = getWindow().getDecorView();
|
View decorView = window.getDecorView();
|
||||||
// Hide the status bar.
|
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||||
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||||
decorView.setSystemUiVisibility(uiOptions);
|
decorView.setSystemUiVisibility(uiOptions);
|
||||||
// Remember that you should never show the action bar if the
|
|
||||||
// status bar is hidden, so hide that too if necessary.
|
|
||||||
ActionBar actionBar = getActionBar();
|
ActionBar actionBar = getActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
actionBar.hide();
|
actionBar.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getLibraries() {
|
protected String[] getLibraries() {
|
||||||
@@ -100,12 +149,241 @@ public class DuskActivity extends SDLActivity {
|
|||||||
return splitArgs(trimmed);
|
return splitArgs(trimmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String discPath = intent.getStringExtra("dusk_disc");
|
|
||||||
if (discPath != null && !discPath.isEmpty()) {
|
|
||||||
return new String[] { discPath };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
persistUriPermissions(data);
|
||||||
|
}
|
||||||
|
if (requestCode == FOLDER_DIALOG_REQUEST_CODE) {
|
||||||
|
finishFolderDialog(resultCode, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean showFolderDialog(long userdata) {
|
||||||
|
if (userdata == 0 || folderDialogUserdata != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
folderDialogUserdata = userdata;
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivityForResult(intent, FOLDER_DIALOG_REQUEST_CODE);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.w(TAG, "Unable to open folder dialog.", e);
|
||||||
|
finishFolderDialog(Activity.RESULT_CANCELED, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishFolderDialog(int resultCode, Intent data) {
|
||||||
|
long userdata = folderDialogUserdata;
|
||||||
|
folderDialogUserdata = 0;
|
||||||
|
if (userdata == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode == RESULT_OK && data != null && data.getData() != null) {
|
||||||
|
String path = getRealPathForUri(data.getData());
|
||||||
|
if (path != null && !path.isEmpty()) {
|
||||||
|
nativeFolderDialogResult(userdata, path, null);
|
||||||
|
} else {
|
||||||
|
nativeFolderDialogResult(
|
||||||
|
userdata, null, "Selected folder is not available as a filesystem path");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeFolderDialogResult(userdata, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRealPathForUri(Uri uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String scheme = uri.getScheme();
|
||||||
|
if ("file".equals(scheme)) {
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"content".equals(scheme) ||
|
||||||
|
!EXTERNAL_STORAGE_AUTHORITY.equals(uri.getAuthority()) ||
|
||||||
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getExternalStoragePathForDocumentId(getExternalStorageDocumentId(uri));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.w(TAG, "Unable to resolve URI: " + uri, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExternalStorageDocumentId(Uri uri) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isTreeDocumentUri(uri)) {
|
||||||
|
return DocumentsContract.getTreeDocumentId(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DocumentsContract.getDocumentId(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTreeDocumentUri(Uri uri) {
|
||||||
|
List<String> segments = uri.getPathSegments();
|
||||||
|
return segments.size() >= 2 && "tree".equals(segments.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getExternalStoragePathForDocumentId(String documentId) {
|
||||||
|
if (documentId == null || documentId.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (documentId.startsWith("raw:")) {
|
||||||
|
return documentId.substring("raw:".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = documentId.split(":", 2);
|
||||||
|
String volumeId = parts[0];
|
||||||
|
String relativePath = parts.length > 1 ? parts[1] : "";
|
||||||
|
|
||||||
|
File root = getExternalStorageRoot(volumeId);
|
||||||
|
if (root == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return relativePath.isEmpty()
|
||||||
|
? root.getAbsolutePath()
|
||||||
|
: new File(root, relativePath).getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getExternalStorageRoot(String volumeId) {
|
||||||
|
if ("primary".equalsIgnoreCase(volumeId)) {
|
||||||
|
return Environment.getExternalStorageDirectory();
|
||||||
|
}
|
||||||
|
if ("home".equalsIgnoreCase(volumeId)) {
|
||||||
|
return new File(
|
||||||
|
Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOCUMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] externalFilesDirs = getExternalFilesDirs(null);
|
||||||
|
if (externalFilesDirs != null) {
|
||||||
|
for (File externalFilesDir : externalFilesDirs) {
|
||||||
|
File root = getStorageRootForExternalFilesDir(externalFilesDir);
|
||||||
|
if (root != null && volumeId.equalsIgnoreCase(root.getName())) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File fallback = new File("/storage", volumeId);
|
||||||
|
return fallback.exists() ? fallback : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getStorageRootForExternalFilesDir(File externalFilesDir) {
|
||||||
|
if (externalFilesDir == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = externalFilesDir.getAbsolutePath();
|
||||||
|
int androidDir = path.indexOf("/Android/");
|
||||||
|
if (androidDir <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File(path.substring(0, androidDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistUriPermissions(Intent data) {
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int permissionFlags =
|
||||||
|
data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
if (permissionFlags == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uri = data.getData();
|
||||||
|
if (uri != null) {
|
||||||
|
persistUriPermission(uri, permissionFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipData clipData = data.getClipData();
|
||||||
|
if (clipData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < clipData.getItemCount(); ++i) {
|
||||||
|
Uri itemUri = clipData.getItemAt(i).getUri();
|
||||||
|
if (itemUri != null) {
|
||||||
|
persistUriPermission(itemUri, permissionFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistUriPermission(Uri uri, int permissionFlags) {
|
||||||
|
if ((permissionFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
|
||||||
|
persistUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, "read");
|
||||||
|
}
|
||||||
|
if ((permissionFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
|
||||||
|
persistUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, "write");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistUriPermission(Uri uri, int permissionFlag, String permissionName) {
|
||||||
|
try {
|
||||||
|
getContentResolver().takePersistableUriPermission(uri, permissionFlag);
|
||||||
|
} catch (SecurityException | IllegalArgumentException e) {
|
||||||
|
Log.w(TAG, "Unable to persist " + permissionName + " URI permission for " + uri, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayNameForUri(String uriString) {
|
||||||
|
if (uriString == null || uriString.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(uriString);
|
||||||
|
if ("content".equals(uri.getScheme())) {
|
||||||
|
try (Cursor cursor = getContentResolver().query(
|
||||||
|
uri, new String[] { OpenableColumns.DISPLAY_NAME }, null, null, null))
|
||||||
|
{
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int displayNameColumn = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||||
|
if (displayNameColumn >= 0) {
|
||||||
|
String displayName = cursor.getString(displayNameColumn);
|
||||||
|
if (displayName != null && !displayName.isEmpty()) {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SecurityException | IllegalArgumentException e) {
|
||||||
|
Log.w(TAG, "Unable to query display name for " + uri, e);
|
||||||
|
}
|
||||||
|
} else if ("file".equals(uri.getScheme())) {
|
||||||
|
String path = uri.getPath();
|
||||||
|
if (path != null && !path.isEmpty()) {
|
||||||
|
String name = new File(path).getName();
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String lastSegment = uri.getLastPathSegment();
|
||||||
|
return lastSegment != null ? lastSegment : "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,467 @@
|
|||||||
|
package dev.twilitrealm.dusk;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.res.AssetFileDescriptor;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.DocumentsContract.Document;
|
||||||
|
import android.provider.DocumentsContract.Root;
|
||||||
|
import android.provider.DocumentsProvider;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class DuskDocumentsProvider extends DocumentsProvider {
|
||||||
|
public static final String AUTHORITY = "dev.twilitrealm.dusk.documents";
|
||||||
|
|
||||||
|
private static final String ROOT_ID = "dusk";
|
||||||
|
private static final String ROOT_DOCUMENT_ID = "root";
|
||||||
|
private static final String LOCATION_DESCRIPTOR_NAME = "data_location.json";
|
||||||
|
private static final String DIRECTORY_MIME_TYPE = Document.MIME_TYPE_DIR;
|
||||||
|
|
||||||
|
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
|
||||||
|
Root.COLUMN_ROOT_ID,
|
||||||
|
Root.COLUMN_FLAGS,
|
||||||
|
Root.COLUMN_TITLE,
|
||||||
|
Root.COLUMN_DOCUMENT_ID,
|
||||||
|
Root.COLUMN_ICON,
|
||||||
|
Root.COLUMN_AVAILABLE_BYTES,
|
||||||
|
Root.COLUMN_SUMMARY
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
|
||||||
|
Document.COLUMN_DOCUMENT_ID,
|
||||||
|
Document.COLUMN_DISPLAY_NAME,
|
||||||
|
Document.COLUMN_FLAGS,
|
||||||
|
Document.COLUMN_MIME_TYPE,
|
||||||
|
Document.COLUMN_LAST_MODIFIED,
|
||||||
|
Document.COLUMN_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
if (!isCustomDataPathEnabled()) {
|
||||||
|
ensureUserDirectories();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
|
||||||
|
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
|
||||||
|
if (isCustomDataPathEnabled()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File root = getRootDirectory();
|
||||||
|
final MatrixCursor.RowBuilder row = result.newRow();
|
||||||
|
|
||||||
|
row.add(Root.COLUMN_ROOT_ID, ROOT_ID);
|
||||||
|
row.add(Root.COLUMN_FLAGS,
|
||||||
|
Root.FLAG_LOCAL_ONLY |
|
||||||
|
Root.FLAG_SUPPORTS_CREATE |
|
||||||
|
Root.FLAG_SUPPORTS_IS_CHILD);
|
||||||
|
row.add(Root.COLUMN_TITLE, getContext().getString(R.string.app_name));
|
||||||
|
row.add(Root.COLUMN_DOCUMENT_ID, ROOT_DOCUMENT_ID);
|
||||||
|
row.add(Root.COLUMN_ICON, R.mipmap.icon);
|
||||||
|
row.add(Root.COLUMN_AVAILABLE_BYTES, root.getFreeSpace());
|
||||||
|
row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.documents_provider_summary));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
|
||||||
|
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
|
||||||
|
includeDocument(result, documentId, getFileForDocumentId(documentId));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
|
||||||
|
throws FileNotFoundException
|
||||||
|
{
|
||||||
|
return queryChildDocumentsInternal(parentDocumentId, projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, Bundle queryArgs)
|
||||||
|
throws FileNotFoundException
|
||||||
|
{
|
||||||
|
return queryChildDocumentsInternal(parentDocumentId, projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cursor queryChildDocumentsInternal(String parentDocumentId, String[] projection)
|
||||||
|
throws FileNotFoundException
|
||||||
|
{
|
||||||
|
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
|
||||||
|
final File parent = getFileForDocumentId(parentDocumentId);
|
||||||
|
final File[] files = parent.listFiles();
|
||||||
|
result.setNotificationUri(getContext().getContentResolver(), getChildDocumentsUri(parentDocumentId));
|
||||||
|
|
||||||
|
if (files == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File file : files) {
|
||||||
|
includeDocument(result, getDocumentIdForFile(file), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChildDocument(String parentDocumentId, String documentId) {
|
||||||
|
try {
|
||||||
|
final File parent = getFileForDocumentId(parentDocumentId);
|
||||||
|
final File child = getFileForDocumentId(documentId);
|
||||||
|
return isInside(parent, child);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
||||||
|
throws FileNotFoundException
|
||||||
|
{
|
||||||
|
final File parent = getFileForDocumentId(parentDocumentId);
|
||||||
|
if (!parent.isDirectory()) {
|
||||||
|
throw new FileNotFoundException("Parent is not a directory: " + parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String safeDisplayName = sanitizeDisplayName(displayName);
|
||||||
|
final File file = buildUniqueFile(parent, safeDisplayName);
|
||||||
|
final boolean created;
|
||||||
|
if (DIRECTORY_MIME_TYPE.equals(mimeType)) {
|
||||||
|
created = file.mkdir();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
created = file.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw asFileNotFound("Unable to create document", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!created) {
|
||||||
|
throw new FileNotFoundException("Unable to create document: " + displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyChildrenChanged(parentDocumentId);
|
||||||
|
return getDocumentIdForFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renameDocument(String documentId, String displayName) throws FileNotFoundException {
|
||||||
|
final File file = getFileForDocumentId(documentId);
|
||||||
|
if (ROOT_DOCUMENT_ID.equals(documentId)) {
|
||||||
|
throw new FileNotFoundException("Cannot rename root document");
|
||||||
|
}
|
||||||
|
|
||||||
|
final File target = buildUniqueFile(file.getParentFile(), sanitizeDisplayName(displayName));
|
||||||
|
final String parentDocumentId = getDocumentIdForFile(file.getParentFile());
|
||||||
|
if (!file.renameTo(target)) {
|
||||||
|
throw new FileNotFoundException("Unable to rename document: " + documentId);
|
||||||
|
}
|
||||||
|
notifyDocumentChanged(documentId);
|
||||||
|
notifyDocumentChanged(getDocumentIdForFile(target));
|
||||||
|
notifyChildrenChanged(parentDocumentId);
|
||||||
|
return getDocumentIdForFile(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteDocument(String documentId) throws FileNotFoundException {
|
||||||
|
if (ROOT_DOCUMENT_ID.equals(documentId)) {
|
||||||
|
throw new FileNotFoundException("Cannot delete root document");
|
||||||
|
}
|
||||||
|
|
||||||
|
final File file = getFileForDocumentId(documentId);
|
||||||
|
final String parentDocumentId = getDocumentIdForFile(file.getParentFile());
|
||||||
|
deleteRecursively(file);
|
||||||
|
notifyDocumentChanged(documentId);
|
||||||
|
notifyChildrenChanged(parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal)
|
||||||
|
throws FileNotFoundException
|
||||||
|
{
|
||||||
|
return ParcelFileDescriptor.open(getFileForDocumentId(documentId), modeToParcelMode(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssetFileDescriptor openDocumentThumbnail(String documentId, android.graphics.Point sizeHint,
|
||||||
|
CancellationSignal signal) throws FileNotFoundException
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("Thumbnails are not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void includeDocument(MatrixCursor result, String documentId, File file) throws FileNotFoundException {
|
||||||
|
final MatrixCursor.RowBuilder row = result.newRow();
|
||||||
|
final boolean isDirectory = file.isDirectory();
|
||||||
|
final String displayName = ROOT_DOCUMENT_ID.equals(documentId)
|
||||||
|
? getContext().getString(R.string.documents_provider_root_name)
|
||||||
|
: file.getName();
|
||||||
|
|
||||||
|
int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_RENAME;
|
||||||
|
if (isDirectory) {
|
||||||
|
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||||
|
} else if (file.canWrite()) {
|
||||||
|
flags |= Document.FLAG_SUPPORTS_WRITE;
|
||||||
|
}
|
||||||
|
if (ROOT_DOCUMENT_ID.equals(documentId)) {
|
||||||
|
flags &= ~(Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_RENAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
row.add(Document.COLUMN_DOCUMENT_ID, documentId);
|
||||||
|
row.add(Document.COLUMN_DISPLAY_NAME, displayName);
|
||||||
|
row.add(Document.COLUMN_FLAGS, flags);
|
||||||
|
row.add(Document.COLUMN_MIME_TYPE, isDirectory ? DIRECTORY_MIME_TYPE : getMimeType(file));
|
||||||
|
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||||
|
row.add(Document.COLUMN_SIZE, isDirectory ? null : file.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getRootDirectory() throws FileNotFoundException {
|
||||||
|
if (isCustomDataPathEnabled()) {
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"Dusk DocumentsProvider is disabled while a custom data path is configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
final File root = getContext().getFilesDir();
|
||||||
|
if (root == null) {
|
||||||
|
throw new FileNotFoundException("Dusklight files directory is unavailable");
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getFileForDocumentId(String documentId) throws FileNotFoundException {
|
||||||
|
final File root = getRootDirectory();
|
||||||
|
if (ROOT_DOCUMENT_ID.equals(documentId)) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
if (!documentId.startsWith(ROOT_DOCUMENT_ID + "/")) {
|
||||||
|
throw new FileNotFoundException("Invalid document id: " + documentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String relativePath = documentId.substring(ROOT_DOCUMENT_ID.length() + 1);
|
||||||
|
final File file = new File(root, relativePath);
|
||||||
|
if (!isInside(root, file)) {
|
||||||
|
throw new FileNotFoundException("Document escapes Dusklight files directory: " + documentId);
|
||||||
|
}
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new FileNotFoundException("Document does not exist: " + documentId);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDocumentIdForFile(File file) throws FileNotFoundException {
|
||||||
|
final File root = getRootDirectory();
|
||||||
|
if (sameFile(root, file)) {
|
||||||
|
return ROOT_DOCUMENT_ID;
|
||||||
|
}
|
||||||
|
if (!isInside(root, file)) {
|
||||||
|
throw new FileNotFoundException("File escapes Dusklight files directory: " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String rootPath = canonicalPath(root);
|
||||||
|
final String filePath = canonicalPath(file);
|
||||||
|
return ROOT_DOCUMENT_ID + "/" + filePath.substring(rootPath.length() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureUserDirectories() {
|
||||||
|
final File root = getContext().getFilesDir();
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new File(root, "texture_replacements").mkdirs();
|
||||||
|
new File(root, "USA/Card A").mkdirs();
|
||||||
|
new File(root, "EUR/Card A").mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCustomDataPathEnabled() {
|
||||||
|
if (getContext() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File filesDir = getContext().getFilesDir();
|
||||||
|
if (filesDir == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File descriptor = new File(filesDir, LOCATION_DESCRIPTOR_NAME);
|
||||||
|
if (!descriptor.isFile()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final JSONObject json = new JSONObject(readText(descriptor));
|
||||||
|
return "custom".equals(json.optString("mode", "default"));
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readText(File file) throws IOException {
|
||||||
|
try (FileInputStream input = new FileInputStream(file);
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream())
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = input.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
return output.toString(StandardCharsets.UTF_8.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] resolveRootProjection(String[] projection) {
|
||||||
|
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] resolveDocumentProjection(String[] projection) {
|
||||||
|
return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sanitizeDisplayName(String displayName) throws FileNotFoundException {
|
||||||
|
if (displayName == null) {
|
||||||
|
throw new FileNotFoundException("Document name is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String sanitized = displayName.trim();
|
||||||
|
if (sanitized.isEmpty() || ".".equals(sanitized) || "..".equals(sanitized) ||
|
||||||
|
sanitized.contains("/") || sanitized.contains("\\"))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("Invalid document name: " + displayName);
|
||||||
|
}
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File buildUniqueFile(File parent, String displayName) {
|
||||||
|
File file = new File(parent, displayName);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int dot = displayName.lastIndexOf('.');
|
||||||
|
final String baseName = dot > 0 ? displayName.substring(0, dot) : displayName;
|
||||||
|
final String extension = dot > 0 ? displayName.substring(dot) : "";
|
||||||
|
for (int i = 1; i < 100; ++i) {
|
||||||
|
file = new File(parent, baseName + " (" + i + ")" + extension);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new File(parent, baseName + " (" + System.currentTimeMillis() + ")" + extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int modeToParcelMode(String mode) {
|
||||||
|
if ("r".equals(mode)) {
|
||||||
|
return ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
}
|
||||||
|
if ("w".equals(mode) || "wt".equals(mode)) {
|
||||||
|
return ParcelFileDescriptor.MODE_WRITE_ONLY |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE |
|
||||||
|
ParcelFileDescriptor.MODE_TRUNCATE;
|
||||||
|
}
|
||||||
|
if ("wa".equals(mode)) {
|
||||||
|
return ParcelFileDescriptor.MODE_WRITE_ONLY |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE |
|
||||||
|
ParcelFileDescriptor.MODE_APPEND;
|
||||||
|
}
|
||||||
|
if ("rw".equals(mode)) {
|
||||||
|
return ParcelFileDescriptor.MODE_READ_WRITE |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE;
|
||||||
|
}
|
||||||
|
if ("rwt".equals(mode)) {
|
||||||
|
return ParcelFileDescriptor.MODE_READ_WRITE |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE |
|
||||||
|
ParcelFileDescriptor.MODE_TRUNCATE;
|
||||||
|
}
|
||||||
|
return ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getMimeType(File file) {
|
||||||
|
final int dot = file.getName().lastIndexOf('.');
|
||||||
|
if (dot >= 0) {
|
||||||
|
final String extension = file.getName().substring(dot + 1).toLowerCase();
|
||||||
|
final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||||
|
if (mimeType != null) {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uri getChildDocumentsUri(String parentDocumentId) {
|
||||||
|
return DocumentsContract.buildChildDocumentsUri(AUTHORITY, parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyChildrenChanged(String parentDocumentId) {
|
||||||
|
final ContentResolver resolver = getContext().getContentResolver();
|
||||||
|
resolver.notifyChange(getChildDocumentsUri(parentDocumentId), null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyDocumentChanged(String documentId) {
|
||||||
|
final ContentResolver resolver = getContext().getContentResolver();
|
||||||
|
resolver.notifyChange(DocumentsContract.buildDocumentUri(AUTHORITY, documentId), null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteRecursively(File file) throws FileNotFoundException {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
final File[] children = file.listFiles();
|
||||||
|
if (children != null) {
|
||||||
|
for (File child : children) {
|
||||||
|
deleteRecursively(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file.delete()) {
|
||||||
|
throw new FileNotFoundException("Unable to delete document: " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInside(File parent, File child) {
|
||||||
|
try {
|
||||||
|
final String parentPath = canonicalPath(parent);
|
||||||
|
final String childPath = canonicalPath(child);
|
||||||
|
return childPath.equals(parentPath) || childPath.startsWith(parentPath + File.separator);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean sameFile(File a, File b) {
|
||||||
|
try {
|
||||||
|
return canonicalPath(a).equals(canonicalPath(b));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String canonicalPath(File file) throws FileNotFoundException {
|
||||||
|
try {
|
||||||
|
return file.getCanonicalPath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw asFileNotFound("Unable to resolve path", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FileNotFoundException asFileNotFound(String message, IOException cause) {
|
||||||
|
final FileNotFoundException exception = new FileNotFoundException(message + ": " + cause.getMessage());
|
||||||
|
exception.initCause(cause);
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
package dev.twilitrealm.dusk;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
|
public final class DuskHttpClient {
|
||||||
|
public static final int ERROR_NONE = 0;
|
||||||
|
public static final int ERROR_INVALID_URL = 1;
|
||||||
|
public static final int ERROR_UNSUPPORTED_SCHEME = 2;
|
||||||
|
public static final int ERROR_TIMEOUT = 3;
|
||||||
|
public static final int ERROR_TOO_LARGE = 4;
|
||||||
|
public static final int ERROR_NETWORK = 5;
|
||||||
|
|
||||||
|
private static final int MAX_REDIRECTS = 5;
|
||||||
|
|
||||||
|
public static final class Response {
|
||||||
|
public int error;
|
||||||
|
public String message;
|
||||||
|
public int statusCode;
|
||||||
|
public String[] headerNames;
|
||||||
|
public String[] headerValues;
|
||||||
|
public byte[] body;
|
||||||
|
|
||||||
|
Response(int error, String message, int statusCode, String[] headerNames,
|
||||||
|
String[] headerValues, byte[] body) {
|
||||||
|
this.error = error;
|
||||||
|
this.message = message;
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.headerNames = headerNames != null ? headerNames : new String[0];
|
||||||
|
this.headerValues = headerValues != null ? headerValues : new String[0];
|
||||||
|
this.body = body != null ? body : new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DuskHttpClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Response get(String url, String[] headerNames, String[] headerValues,
|
||||||
|
int timeoutMs, long maxBodyBytes) {
|
||||||
|
if (url == null || url.isEmpty()) {
|
||||||
|
return fail(ERROR_INVALID_URL, "URL is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL currentUrl = new URL(url);
|
||||||
|
if (!isHttps(currentUrl)) {
|
||||||
|
return fail(ERROR_UNSUPPORTED_SCHEME, "Only https:// URLs are supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int redirect = 0; redirect <= MAX_REDIRECTS; ++redirect) {
|
||||||
|
HttpsURLConnection connection =
|
||||||
|
(HttpsURLConnection) currentUrl.openConnection();
|
||||||
|
try {
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.setConnectTimeout(timeoutMs);
|
||||||
|
connection.setReadTimeout(timeoutMs);
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
connection.setInstanceFollowRedirects(false);
|
||||||
|
applyHeaders(connection, headerNames, headerValues);
|
||||||
|
|
||||||
|
int statusCode = connection.getResponseCode();
|
||||||
|
if (isRedirect(statusCode)) {
|
||||||
|
String location = connection.getHeaderField("Location");
|
||||||
|
if (location == null || location.isEmpty()) {
|
||||||
|
return fail(ERROR_NETWORK, "Redirect response did not include Location",
|
||||||
|
statusCode, connection, new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
URL nextUrl = new URL(currentUrl, location);
|
||||||
|
if (!isHttps(nextUrl)) {
|
||||||
|
return fail(ERROR_UNSUPPORTED_SCHEME,
|
||||||
|
"Only https:// redirects are supported", statusCode,
|
||||||
|
connection, new byte[0]);
|
||||||
|
}
|
||||||
|
currentUrl = nextUrl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] body = readBody(connection, statusCode, maxBodyBytes);
|
||||||
|
return success(statusCode, connection, body);
|
||||||
|
} catch (ResponseTooLargeException e) {
|
||||||
|
return fail(ERROR_TOO_LARGE, "Response body exceeded the configured limit",
|
||||||
|
safeStatusCode(connection), connection, e.partialBody);
|
||||||
|
} finally {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fail(ERROR_NETWORK, "Too many redirects");
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return fail(ERROR_INVALID_URL, "Failed to parse URL");
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
return fail(ERROR_TIMEOUT, "Request timed out");
|
||||||
|
} catch (IOException e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
return fail(ERROR_NETWORK, message != null ? message : e.toString());
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
return fail(ERROR_UNSUPPORTED_SCHEME, "Only https:// URLs are supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyHeaders(HttpsURLConnection connection, String[] names,
|
||||||
|
String[] values) {
|
||||||
|
if (names == null || values == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = Math.min(names.length, values.length);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (names[i] != null && values[i] != null) {
|
||||||
|
connection.setRequestProperty(names[i], values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isHttps(URL url) {
|
||||||
|
return "https".equalsIgnoreCase(url.getProtocol());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRedirect(int statusCode) {
|
||||||
|
return statusCode == HttpURLConnection.HTTP_MOVED_PERM ||
|
||||||
|
statusCode == HttpURLConnection.HTTP_MOVED_TEMP ||
|
||||||
|
statusCode == HttpURLConnection.HTTP_SEE_OTHER ||
|
||||||
|
statusCode == 307 ||
|
||||||
|
statusCode == 308;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readBody(HttpsURLConnection connection, int statusCode,
|
||||||
|
long maxBodyBytes) throws IOException,
|
||||||
|
ResponseTooLargeException {
|
||||||
|
InputStream stream = statusCode >= HttpURLConnection.HTTP_BAD_REQUEST ?
|
||||||
|
connection.getErrorStream() : connection.getInputStream();
|
||||||
|
if (stream == null) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream bodyStream = stream;
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long total = 0;
|
||||||
|
while (true) {
|
||||||
|
int read = bodyStream.read(buffer);
|
||||||
|
if (read < 0) {
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
if (read == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (read > maxBodyBytes || total > maxBodyBytes - read) {
|
||||||
|
throw new ResponseTooLargeException(out.toByteArray());
|
||||||
|
}
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
total += read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int safeStatusCode(HttpsURLConnection connection) {
|
||||||
|
try {
|
||||||
|
return connection.getResponseCode();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Response success(int statusCode, HttpsURLConnection connection, byte[] body) {
|
||||||
|
HeaderLists headers = readHeaders(connection);
|
||||||
|
return new Response(ERROR_NONE, "", statusCode, headers.names, headers.values, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Response fail(int error, String message) {
|
||||||
|
return new Response(error, message, 0, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Response fail(int error, String message, int statusCode,
|
||||||
|
HttpsURLConnection connection, byte[] body) {
|
||||||
|
HeaderLists headers = readHeaders(connection);
|
||||||
|
return new Response(error, message, statusCode, headers.names, headers.values, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HeaderLists readHeaders(HttpsURLConnection connection) {
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
List<String> values = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<String, List<String>> headerFields = connection.getHeaderFields();
|
||||||
|
if (headerFields == null) {
|
||||||
|
return new HeaderLists(new String[0], new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
if (name == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<String> entryValues = entry.getValue();
|
||||||
|
if (entryValues == null || entryValues.isEmpty()) {
|
||||||
|
names.add(name);
|
||||||
|
values.add("");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (String value : entryValues) {
|
||||||
|
names.add(name);
|
||||||
|
values.add(value != null ? value : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HeaderLists(names.toArray(new String[0]), values.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class HeaderLists {
|
||||||
|
final String[] names;
|
||||||
|
final String[] values;
|
||||||
|
|
||||||
|
HeaderLists(String[] names, String[] values) {
|
||||||
|
this.names = names;
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ResponseTooLargeException extends Exception {
|
||||||
|
final byte[] partialBody;
|
||||||
|
|
||||||
|
ResponseTooLargeException(byte[] partialBody) {
|
||||||
|
this.partialBody = partialBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -256,6 +256,7 @@ public class HIDDeviceManager {
|
|||||||
0x24c6, // PowerA
|
0x24c6, // PowerA
|
||||||
0x2c22, // Qanba
|
0x2c22, // Qanba
|
||||||
0x2dc8, // 8BitDo
|
0x2dc8, // 8BitDo
|
||||||
|
0x37d7, // Flydigi
|
||||||
0x9886, // ASTRO Gaming
|
0x9886, // ASTRO Gaming
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||||||
private static final String TAG = "SDL";
|
private static final String TAG = "SDL";
|
||||||
private static final int SDL_MAJOR_VERSION = 3;
|
private static final int SDL_MAJOR_VERSION = 3;
|
||||||
private static final int SDL_MINOR_VERSION = 4;
|
private static final int SDL_MINOR_VERSION = 4;
|
||||||
private static final int SDL_MICRO_VERSION = 2;
|
private static final int SDL_MICRO_VERSION = 4;
|
||||||
/*
|
/*
|
||||||
// Display InputType.SOURCE/CLASS of events and devices
|
// Display InputType.SOURCE/CLASS of events and devices
|
||||||
//
|
//
|
||||||
@@ -2032,7 +2032,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||||||
try {
|
try {
|
||||||
ParcelFileDescriptor pfd = mSingleton.getContentResolver().openFileDescriptor(Uri.parse(uri), mode);
|
ParcelFileDescriptor pfd = mSingleton.getContentResolver().openFileDescriptor(Uri.parse(uri), mode);
|
||||||
return pfd != null ? pfd.detachFd() : -1;
|
return pfd != null ? pfd.detachFd() : -1;
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException | SecurityException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2227,4 +2227,3 @@ class SDLClipboardHandler implements
|
|||||||
SDLActivity.onNativeClipboardChanged();
|
SDLActivity.onNativeClipboardChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ class SDLInputConnection extends BaseInputConnection
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||||
if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) {
|
|
||||||
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
|
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
|
||||||
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
||||||
if (beforeLength > 0 && afterLength == 0) {
|
if (beforeLength > 0 && afterLength == 0) {
|
||||||
@@ -75,7 +74,6 @@ class SDLInputConnection extends BaseInputConnection
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
|
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
|
||||||
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
|
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
|
||||||
|
|
||||||
|
private static native void auroraNativeSetSurfaceReady(boolean ready);
|
||||||
|
|
||||||
// Sensors
|
// Sensors
|
||||||
protected SensorManager mSensorManager;
|
protected SensorManager mSensorManager;
|
||||||
protected Display mDisplay;
|
protected Display mDisplay;
|
||||||
@@ -96,6 +98,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
@Override
|
@Override
|
||||||
public void surfaceCreated(SurfaceHolder holder) {
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
Log.v("SDL", "surfaceCreated()");
|
Log.v("SDL", "surfaceCreated()");
|
||||||
|
auroraNativeSetSurfaceReady(false);
|
||||||
SDLActivity.onNativeSurfaceCreated();
|
SDLActivity.onNativeSurfaceCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +106,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
Log.v("SDL", "surfaceDestroyed()");
|
Log.v("SDL", "surfaceDestroyed()");
|
||||||
|
auroraNativeSetSurfaceReady(false);
|
||||||
|
|
||||||
// Transition to pause, if needed
|
// Transition to pause, if needed
|
||||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||||
@@ -192,6 +196,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
/* Surface is ready */
|
/* Surface is ready */
|
||||||
mIsSurfaceReady = true;
|
mIsSurfaceReady = true;
|
||||||
|
auroraNativeSetSurfaceReady(true);
|
||||||
|
|
||||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||||
SDLActivity.handleNativeState();
|
SDLActivity.handleNativeState();
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Dusk</string>
|
<string name="app_name">Dusklight</string>
|
||||||
|
<string name="documents_provider_root_name">Dusklight Data</string>
|
||||||
|
<string name="documents_provider_summary">Saves, texture packs, settings, and logs</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -14,9 +14,22 @@ if [[ -z "$ANDROID_NDK_VER" ]] && [[ -d "$ANDROID_HOME_DIR/ndk" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$ANDROID_NDK_VER" ]]; then
|
if [[ -n "$ANDROID_NDK_VER" ]]; then
|
||||||
TOOLCHAIN_BIN="$ANDROID_HOME_DIR/ndk/$ANDROID_NDK_VER/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
case "$(uname -s)" in
|
||||||
if [[ -x "$TOOLCHAIN_BIN/llvm-strip" ]]; then
|
Darwin) HOST_TAG="darwin-x86_64" ;;
|
||||||
STRIP_TOOL="$TOOLCHAIN_BIN/llvm-strip"
|
Linux) HOST_TAG="linux-x86_64" ;;
|
||||||
|
*) HOST_TAG="" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
PREBUILT_DIR="$ANDROID_HOME_DIR/ndk/$ANDROID_NDK_VER/toolchains/llvm/prebuilt"
|
||||||
|
if [[ -n "$HOST_TAG" && -x "$PREBUILT_DIR/$HOST_TAG/bin/llvm-strip" ]]; then
|
||||||
|
STRIP_TOOL="$PREBUILT_DIR/$HOST_TAG/bin/llvm-strip"
|
||||||
|
else
|
||||||
|
for candidate in "$PREBUILT_DIR"/*/bin/llvm-strip; do
|
||||||
|
if [[ -x "$candidate" ]]; then
|
||||||
|
STRIP_TOOL="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -25,29 +38,35 @@ copy_lib() {
|
|||||||
local src="$2"
|
local src="$2"
|
||||||
local dst_dir="$APP_DIR/$abi"
|
local dst_dir="$APP_DIR/$abi"
|
||||||
local dst="$dst_dir/libmain.so"
|
local dst="$dst_dir/libmain.so"
|
||||||
|
local tmp="$dst_dir/.libmain.so.$$"
|
||||||
|
if [[ ! -f "$src" ]]; then
|
||||||
|
echo "Missing native library for $abi: $src" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p "$dst_dir"
|
mkdir -p "$dst_dir"
|
||||||
cp -f "$src" "$dst"
|
cp -f "$src" "$tmp"
|
||||||
if [[ "$ANDROID_STAGE_STRIP" != "0" ]] && [[ -n "$STRIP_TOOL" ]]; then
|
if [[ "$ANDROID_STAGE_STRIP" != "0" ]] && [[ -n "$STRIP_TOOL" ]]; then
|
||||||
"$STRIP_TOOL" --strip-debug "$dst"
|
"$STRIP_TOOL" --strip-unneeded "$tmp"
|
||||||
echo "Staged and stripped $src -> $dst"
|
mv -f "$tmp" "$dst"
|
||||||
|
echo "Stripped and staged $src -> $dst"
|
||||||
else
|
else
|
||||||
|
mv -f "$tmp" "$dst"
|
||||||
echo "Staged $src -> $dst (strip disabled or strip tool unavailable)"
|
echo "Staged $src -> $dst (strip disabled or strip tool unavailable)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
declare -A ABI_TO_LIB=(
|
|
||||||
["arm64-v8a"]="$ROOT_DIR/build/android-arm64/libmain.so"
|
|
||||||
["x86_64"]="$ROOT_DIR/build/android-x86_64/libmain.so"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Drop any previously staged ABI directories to avoid stale APK contents.
|
# Drop any previously staged ABI directories to avoid stale APK contents.
|
||||||
rm -rf "$APP_DIR/x86" "$APP_DIR/arm64-v8a" "$APP_DIR/x86_64"
|
rm -rf "$APP_DIR/x86" "$APP_DIR/arm64-v8a" "$APP_DIR/x86_64"
|
||||||
|
|
||||||
for abi in $ANDROID_STAGE_ABIS; do
|
for abi in $ANDROID_STAGE_ABIS; do
|
||||||
src="${ABI_TO_LIB[$abi]:-}"
|
case "$abi" in
|
||||||
if [[ -z "$src" ]]; then
|
arm64-v8a) src="$ROOT_DIR/build/android-arm64/libmain.so" ;;
|
||||||
|
x86_64) src="$ROOT_DIR/build/android-x86_64/libmain.so" ;;
|
||||||
|
*)
|
||||||
echo "Unsupported ABI '$abi'. Supported ABIs: arm64-v8a x86_64" >&2
|
echo "Unsupported ABI '$abi'. Supported ABIs: arm64-v8a x86_64" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
copy_lib "$abi" "$src"
|
copy_lib "$abi" "$src"
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "dusk-android"
|
rootProject.name = "dusklight-android"
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 928 KiB |
|
Before Width: | Height: | Size: 306 B |
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 79 B |
|
After Width: | Height: | Size: 1014 B |
|
Before Width: | Height: | Size: 761 B |
|
After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 99 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 123 B |
|
After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 179 B |
|
After Width: | Height: | Size: 8.7 KiB |
@@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=Dusk
|
|
||||||
GenericName=Dusk
|
|
||||||
Comment=The Legend of Zelda: Twilight Princess
|
|
||||||
Exec=dusk
|
|
||||||
Icon=dusk
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Graphics;3DGraphics;Game
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=Dusklight
|
||||||
|
GenericName=Dusklight
|
||||||
|
Comment=PC port of a classic adventure game
|
||||||
|
Exec=dusklight
|
||||||
|
Icon=dusklight
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Game;
|
||||||
@@ -79,5 +79,11 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>UIFileSharingEnabled</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSSupportsGameMode</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>Dusk</string>
|
<string>Dusklight</string>
|
||||||
<key>CFBundleIconName</key>
|
<key>CFBundleIconName</key>
|
||||||
<string>Dusk</string>
|
<string>Dusk</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -28,5 +28,7 @@
|
|||||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>LSSupportsGameMode</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -45,5 +45,7 @@
|
|||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIUserInterfaceStyle</key>
|
<key>UIUserInterfaceStyle</key>
|
||||||
<string>Automatic</string>
|
<string>Automatic</string>
|
||||||
|
<key>LSSupportsGameMode</key>
|
||||||
|
<true />
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ BEGIN
|
|||||||
VALUE "CompanyName", "@DUSK_COMPANY_NAME@\0"
|
VALUE "CompanyName", "@DUSK_COMPANY_NAME@\0"
|
||||||
VALUE "FileDescription", "@DUSK_FILE_DESCRIPTION@\0"
|
VALUE "FileDescription", "@DUSK_FILE_DESCRIPTION@\0"
|
||||||
VALUE "FileVersion", "@DUSK_VERSION_STRING@\0"
|
VALUE "FileVersion", "@DUSK_VERSION_STRING@\0"
|
||||||
VALUE "InternalName", "dusk\0"
|
VALUE "InternalName", "dusklight\0"
|
||||||
VALUE "LegalCopyright", "@DUSK_COPYRIGHT@\0"
|
VALUE "LegalCopyright", "@DUSK_COPYRIGHT@\0"
|
||||||
VALUE "OriginalFilename", "dusk.exe\0"
|
VALUE "OriginalFilename", "dusklight.exe\0"
|
||||||
VALUE "ProductName", "@DUSK_PRODUCT_NAME@\0"
|
VALUE "ProductName", "@DUSK_PRODUCT_NAME@\0"
|
||||||
VALUE "ProductVersion", "@DUSK_VERSION_STRING@\0"
|
VALUE "ProductVersion", "@DUSK_VERSION_STRING@\0"
|
||||||
END
|
END
|
||||||
|
Before Width: | Height: | Size: 457 KiB |
|
Before Width: | Height: | Size: 340 KiB After Width: | Height: | Size: 642 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 13 KiB |