Compare commits
430 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 1cb8b19520 | |||
| d109cd891e | |||
| 63c4002ca6 | |||
| 6954efcd15 | |||
| 78c2147771 | |||
| c938d5468e | |||
| 35590c5312 | |||
| e976b10e2a | |||
| 3949706b28 | |||
| ce55916845 | |||
| 9b252cbdd2 | |||
| 97fa09f6ee | |||
| 0e7a7cccb9 | |||
| 1c0cdcc176 | |||
| 280305c2ba | |||
| d1f1d579bc | |||
| eeeb3ffe25 | |||
| 1ee0f862e1 | |||
| 6b327c9f61 | |||
| f148e0ebc1 | |||
| 6f20f4d629 | |||
| 94670270d6 | |||
| 8e21247a97 | |||
| aa84004cb4 | |||
| e1c201f4bd | |||
| 425a1d15a0 | |||
| e3ad41792a | |||
| c1ba10fc8b | |||
| 1c5686f71b | |||
| b9e0f2b9ca | |||
| ede175f141 | |||
| a301874e30 | |||
| 9b4a9a6bd9 | |||
| 04274b1884 | |||
| ca783b8424 | |||
| 2e4c2b5b46 | |||
| 4c09d8b910 | |||
| fb1b260d09 | |||
| 98eb8f718e | |||
| 68b2e0ee2d | |||
| 81771a0522 | |||
| 93c8bcc210 | |||
| 176bf5f0c4 | |||
| 65a5945778 | |||
| 8f7b9cdfdd | |||
| acecba7ff7 | |||
| 507e0aadbc | |||
| 8406d9b192 | |||
| 6f3170cb56 | |||
| 835582224e | |||
| 52879f50f0 | |||
| 2b505f1be4 | |||
| f089f9024d | |||
| 43fb421a93 | |||
| f06d6b50a9 | |||
| 2c987b0211 | |||
| 3d860ad454 | |||
| b5bf19569b | |||
| 5a7f5cb4a7 | |||
| 1affefbbfc | |||
| e4ff38a712 | |||
| f2ac4d6f44 | |||
| 48b98a5432 | |||
| a4752154f7 | |||
| 0d973a497b | |||
| ab4eccf1df | |||
| e0c449f28e | |||
| ce9a5c06d5 | |||
| 2bbba1e4e8 | |||
| 39298f8d8f | |||
| 32b4c0567a | |||
| 7ff1b5332e | |||
| 5e77a60bd6 | |||
| 9629c000bd | |||
| 9dc5fed686 | |||
| 2cc9db77dd | |||
| 8aa08c9443 | |||
| dca3e2eba6 | |||
| 57061fba38 | |||
| cee6a24309 | |||
| 62edb3abc6 | |||
| 1c9e1c0a66 | |||
| a06aeb10c1 | |||
| b84b309e00 | |||
| 62eecb3ccd | |||
| ec07572ced | |||
| b45e2fa34d | |||
| 289a718446 | |||
| 292a2a6c34 | |||
| 2e8415b950 | |||
| dbf1f6e354 | |||
| 9a2fe9745d | |||
| b5ca343fac | |||
| 88c7ff63ff | |||
| 7a438ad30f | |||
| 89649de7c3 | |||
| a1960eaa33 | |||
| d6820c9233 | |||
| 5c4bb8d33d | |||
| e3ce1f01c9 | |||
| 9b6b344ecf | |||
| 3db85d5b44 | |||
| b5871d72d9 | |||
| b70a714f88 | |||
| cad5a8d1bc | |||
| 93f8a5fa8f | |||
| b0809ea78c | |||
| b0e9033736 | |||
| 1ac6df8de7 | |||
| 5899b2157a | |||
| 44f3828f68 | |||
| 3f560b060c | |||
| 3185f578fb | |||
| 4fc09799b6 | |||
| fe0e3cad72 | |||
| 37d1aa7f40 | |||
| 4a12554bf4 | |||
| fecd1d5683 | |||
| bce9bf6fd9 | |||
| fbf63b075a | |||
| b86d6e90e2 | |||
| 6425b452e7 | |||
| 1657fe8083 | |||
| ecd74a4cbd | |||
| 36dc43c602 | |||
| d92515f0d4 | |||
| f147dcac0c | |||
| ee4c84f39b | |||
| b8a83c6f59 | |||
| 4462c0ef69 | |||
| c803bfb545 | |||
| 2623c44cab | |||
| 24dd02fc81 | |||
| a97602b6dc | |||
| e2943e90dc | |||
| 3cb7fbd030 | |||
| afe54f22ab | |||
| e15f5bcee9 | |||
| 1e372a856d | |||
| b26896cad5 | |||
| f75faf6b06 | |||
| b48d9aa052 | |||
| d899706208 | |||
| 3e05789b58 | |||
| 9a7b62cbc6 | |||
| 8e0f0e878e | |||
| 79344edf0d | |||
| e59bfd1a9c | |||
| 4d12cc8ea2 | |||
| 2ed2268579 | |||
| 94a99e8da0 | |||
| ddaf50c01d | |||
| 7566949b42 | |||
| bb6db3caea | |||
| 2dc494dc1c | |||
| 3c25633ee9 | |||
| d99ed2729b | |||
| 782455d48b | |||
| 47863b34c2 | |||
| 92391d5eb8 | |||
| 0d37cb4e54 | |||
| ea528ed9d9 | |||
| f7b880c5ea | |||
| ff78bc8d6c | |||
| 6503b4e7eb | |||
| 30b7087f30 | |||
| 0c1372f986 | |||
| 8fb4ba8924 | |||
| 5f675c6f2b | |||
| b3333241c5 | |||
| e39079c0f8 | |||
| c3317d9232 | |||
| a4777045fe | |||
| bbe8ea6aa6 | |||
| d662db69f0 | |||
| 9b259143be | |||
| 025cb58493 | |||
| b3dee825e8 | |||
| f6c5aac3c8 | |||
| 25e9064d09 | |||
| 5f33489465 | |||
| 3e1e8f1244 | |||
| 23a91a37be | |||
| eaae8b6137 | |||
| a886f8a2df | |||
| 6df7b1e9cd | |||
| 206c02b527 | |||
| a05d1a9ee6 | |||
| ed0df01b8d | |||
| 0f9d563a3e | |||
| f3fb65495e | |||
| 196f33005e | |||
| 90c0bdded0 | |||
| f856f871bb | |||
| e75ea18ef0 | |||
| 50e2d9d1a7 | |||
| 5c84978c3c | |||
| 3859f39729 | |||
| 3498ded9d9 | |||
| 8784958c40 | |||
| c1231885fe | |||
| 6cf94b4491 | |||
| 30aa2dd527 | |||
| 8e121a7e51 | |||
| 0bf663141a | |||
| 17949e526b | |||
| d7dced7ddf | |||
| 78b0563c0e | |||
| 871d18e294 | |||
| c157564da6 | |||
| ecc3b00c51 | |||
| 8afb1141ab | |||
| 8c5673d9b8 | |||
| 916dfcd9da | |||
| d9a0ef760f | |||
| 842210e539 | |||
| 39d951d0cb | |||
| a4be0841e5 | |||
| 95470b830f | |||
| 0d76b90144 | |||
| a6690c2052 |
@@ -2,7 +2,7 @@
|
||||
Language: Cpp
|
||||
Standard: C++03
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignOperands: true
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
# SCCACHE_GHA_ENABLED: "true"
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- name: GCC x86_64
|
||||
runner: [self-hosted, Linux]
|
||||
runner: ubuntu-latest
|
||||
preset: gcc
|
||||
artifact_arch: x86_64
|
||||
# - name: GCC aarch64
|
||||
@@ -41,7 +41,6 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
if: 'false' # disabled for self-hosted
|
||||
run: |
|
||||
sudo apt-get update
|
||||
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
|
||||
|
||||
- name: Setup sccache
|
||||
if: 'false' # disabled for self-hosted
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
|
||||
- name: Print sccache stats
|
||||
run: sccache --show-stats
|
||||
@@ -67,7 +65,6 @@ jobs:
|
||||
run: ci/build-appimage.sh
|
||||
|
||||
- name: Upload artifacts
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: dusk-${{env.DUSK_VERSION}}-linux-${{matrix.preset}}-${{matrix.artifact_arch}}
|
||||
@@ -77,7 +74,7 @@ jobs:
|
||||
|
||||
build-apple:
|
||||
name: Build Apple (${{matrix.name}})
|
||||
runs-on: [self-hosted, macOS]
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -86,14 +83,14 @@ jobs:
|
||||
platform: macos
|
||||
preset: x-macos-ci-arm64
|
||||
artifact_name: macos-appleclang-arm64
|
||||
# - name: AppleClang macOS x86_64
|
||||
# platform: macos
|
||||
# preset: x-macos-ci-x86_64
|
||||
# artifact_name: macos-appleclang-x86_64
|
||||
# - name: AppleClang iOS arm64
|
||||
# platform: ios
|
||||
# preset: x-ios-ci
|
||||
# artifact_name: ios-appleclang-arm64
|
||||
- name: AppleClang macOS x86_64
|
||||
platform: macos
|
||||
preset: x-macos-ci-x86_64
|
||||
artifact_name: macos-appleclang-x86_64
|
||||
- name: AppleClang iOS arm64
|
||||
platform: ios
|
||||
preset: x-ios-ci
|
||||
artifact_name: ios-appleclang-arm64
|
||||
# - name: AppleClang tvOS arm64
|
||||
# platform: tvos
|
||||
# preset: x-tvos-ci
|
||||
@@ -106,7 +103,6 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
if: 'false'
|
||||
run: brew install cmake ninja
|
||||
|
||||
- name: Install Rust iOS target
|
||||
@@ -128,7 +124,7 @@ jobs:
|
||||
rustup target add x86_64-apple-darwin
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --preset ${{matrix.preset}}
|
||||
@@ -137,7 +133,6 @@ jobs:
|
||||
run: cmake --build --preset ${{matrix.preset}}
|
||||
|
||||
- name: Upload artifacts
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: dusk-${{env.DUSK_VERSION}}-${{matrix.artifact_name}}
|
||||
@@ -145,6 +140,73 @@ jobs:
|
||||
build/install/Dusk.app
|
||||
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 dusk
|
||||
|
||||
- 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: dusk-${{env.DUSK_VERSION}}-android-${{matrix.artifact_arch}}
|
||||
path: platforms/android/app/build/outputs/apk/release/app-${{matrix.abi}}-release-unsigned.apk
|
||||
|
||||
build-windows:
|
||||
name: Build Windows (${{matrix.name}})
|
||||
runs-on: ${{matrix.runner}}
|
||||
@@ -154,7 +216,7 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- name: MSVC x86_64
|
||||
runner: [self-hosted, Windows]
|
||||
runner: windows-latest
|
||||
preset: msvc
|
||||
msvc_arch: amd64
|
||||
vcpkg_arch: x64
|
||||
@@ -191,7 +253,6 @@ jobs:
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Install dependencies
|
||||
if: 'false' # disabled for self-hosted
|
||||
run: |
|
||||
choco install ninja
|
||||
vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static
|
||||
@@ -203,7 +264,6 @@ jobs:
|
||||
run: cmake --build --preset x-windows-ci-${{matrix.preset}}
|
||||
|
||||
- name: Upload artifacts
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: dusk-${{env.DUSK_VERSION}}-win32-msvc-${{matrix.artifact_arch}}
|
||||
|
||||
@@ -48,13 +48,15 @@ else ()
|
||||
message(STATUS "Unable to find git, commit information will not be available")
|
||||
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}")
|
||||
if (CMAKE_MATCH_5)
|
||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${CMAKE_MATCH_5}")
|
||||
else ()
|
||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.0")
|
||||
set(DUSK_VERSION_TWEAK "0")
|
||||
if (DUSK_WC_DESCRIBE MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+-([0-9]+)(-dirty)?$")
|
||||
set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}")
|
||||
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 ()
|
||||
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${DUSK_VERSION_TWEAK}")
|
||||
else ()
|
||||
set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION")
|
||||
set(DUSK_VERSION_STRING "0.0.0.0")
|
||||
@@ -69,7 +71,7 @@ message(STATUS "Dusk version set to ${DUSK_WC_DESCRIBE}")
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
project(dusk LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING})
|
||||
if (APPLE)
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJC OBJCXX)
|
||||
endif ()
|
||||
if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS)
|
||||
# ios.toolchain.cmake hack for SDL
|
||||
@@ -100,26 +102,35 @@ if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||
endif ()
|
||||
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_RMLUI ON CACHE BOOL "Enable RmlUi UI support" FORCE)
|
||||
add_subdirectory(extern/aurora EXCLUDE_FROM_ALL)
|
||||
target_compile_definitions(aurora_mtx PRIVATE MTX_USE_PS=1)
|
||||
|
||||
add_subdirectory(libs/freeverb)
|
||||
|
||||
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_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)
|
||||
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 ()
|
||||
|
||||
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
|
||||
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
|
||||
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)
|
||||
find_package(libjpeg-turbo 3.0 CONFIG QUIET)
|
||||
if (libjpeg-turbo_FOUND)
|
||||
@@ -149,6 +160,7 @@ if (DUSK_MOVIE_SUPPORT)
|
||||
CMAKE_C_COMPILER_LAUNCHER
|
||||
CMAKE_MAKE_PROGRAM
|
||||
CMAKE_MSVC_RUNTIME_LIBRARY
|
||||
CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
||||
CMAKE_OSX_ARCHITECTURES
|
||||
DEPLOYMENT_TARGET
|
||||
ENABLE_ARC
|
||||
@@ -282,9 +294,9 @@ set(DUSK_PRODUCT_NAME "Dusk")
|
||||
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
|
||||
|
||||
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
|
||||
source_group("dusk" FILES ${DUSK_FILES})
|
||||
source_group("dusk" 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
|
||||
include
|
||||
@@ -296,8 +308,10 @@ set(GAME_INCLUDE_DIRS
|
||||
extern
|
||||
${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
|
||||
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)
|
||||
|
||||
@@ -306,6 +320,45 @@ if (DUSK_ENABLE_SENTRY_NATIVE)
|
||||
list(APPEND GAME_COMPILE_DEFS DUSK_ENABLE_SENTRY_NATIVE=1 SENTRY_BUILD_STATIC=1)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
list(APPEND GAME_LIBS Ws2_32)
|
||||
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 "dusk: 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 "dusk: 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 "dusk: 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 "dusk: Enabled update checker (libcurl)")
|
||||
else ()
|
||||
message(STATUS "dusk: Disabled update checker (libcurl + HTTPS/SSL not found)")
|
||||
endif ()
|
||||
else ()
|
||||
message(STATUS "dusk: Disabled update checker (unsupported platform)")
|
||||
endif ()
|
||||
endif ()
|
||||
list(APPEND DUSK_FILES ${DUSK_HTTP_BACKEND_SOURCE})
|
||||
|
||||
if (DUSK_MOVIE_SUPPORT)
|
||||
if (TARGET libjpeg-turbo::turbojpeg-static)
|
||||
list(APPEND GAME_LIBS libjpeg-turbo::turbojpeg-static)
|
||||
@@ -315,56 +368,13 @@ if (DUSK_MOVIE_SUPPORT)
|
||||
list(APPEND GAME_COMPILE_DEFS MOVIE_SUPPORT=1)
|
||||
endif ()
|
||||
|
||||
option(DUSK_ENABLE_DISCORD_RPC "Enable Discord Rich Presence support" ON)
|
||||
if (DUSK_ENABLE_DISCORD_RPC AND NOT ANDROID AND NOT IOS AND NOT TVOS)
|
||||
|
||||
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)
|
||||
set(DUSK_ENABLE_DISCORD_DEFAULT ON)
|
||||
if (DEFINED DUSK_ENABLE_DISCORD_RPC AND NOT DEFINED DUSK_ENABLE_DISCORD)
|
||||
set(DUSK_ENABLE_DISCORD_DEFAULT ${DUSK_ENABLE_DISCORD_RPC})
|
||||
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 ()
|
||||
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 ()
|
||||
|
||||
if(ANDROID)
|
||||
@@ -434,6 +444,12 @@ target_link_libraries(dusk PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBR
|
||||
target_precompile_headers(dusk PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
|
||||
if (TARGET crashpad_handler)
|
||||
add_dependencies(dusk crashpad_handler)
|
||||
add_custom_command(TARGET dusk POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<TARGET_FILE:crashpad_handler>"
|
||||
"$<TARGET_FILE_DIR:dusk>"
|
||||
COMMENT "Copying crashpad handler"
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (ANDROID)
|
||||
|
||||
@@ -352,6 +352,31 @@
|
||||
"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",
|
||||
"hidden": true,
|
||||
@@ -412,6 +437,7 @@
|
||||
"x-macos-ci"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"AURORA_DAWN_PROVIDER": "vendor",
|
||||
"CMAKE_OSX_ARCHITECTURES": "x86_64",
|
||||
"Rust_CARGO_TARGET": "x86_64-apple-darwin"
|
||||
}
|
||||
@@ -555,6 +581,15 @@
|
||||
"dusk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "x-android-ci-arm64",
|
||||
"configurePreset": "x-android-ci-arm64",
|
||||
"description": "(Internal) Android CI arm64-v8a",
|
||||
"displayName": "(Internal) Android CI arm64-v8a",
|
||||
"targets": [
|
||||
"dusk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"configurePreset": "windows-msvc-debug",
|
||||
|
||||
@@ -1,31 +1,69 @@
|
||||

|
||||
<div align="center">
|
||||
<img src="res/logo-mascot.png" alt="Logo" width="640">
|
||||
|
||||
- ### **[Official Website](https://twilitrealm.dev)**
|
||||
- ### **[Discord](https://discord.gg/QACynxeyna)**
|
||||
<p align="center">
|
||||
<a href="https://twilitrealm.dev">Official Website</a>
|
||||
•
|
||||
<a href="https://discord.gg/dusktp">Discord</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
# Overview
|
||||
|
||||
Dusk 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.
|
||||
|
||||
# Setup
|
||||
**⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
|
||||
|
||||
### 1. Verify your ROM dump
|
||||
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.
|
||||
> [!IMPORTANT]
|
||||
> Dusk does *not* provide any copyrighted assets. You must provide your own copy of the original game.
|
||||
|
||||
| Version | sha1 hash |
|
||||
|--------------| ---------------------------------------- |
|
||||
| GameCube USA | 75edd3ddff41f125d1b4ce1a40378f1b565519e7 |
|
||||
| GameCube PAL | 2601822a488eeb86fb89db16ca8f29c2c953e1ca |
|
||||
> [!IMPORTANT]
|
||||
> At a minimum, Dusk 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 likelyhood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
||||
|
||||
### 1. Verify your dump
|
||||
|
||||
First, make sure your dump of the game is clean and supported by Dusk. 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 [Dusk](https://github.com/TwilitRealm/dusk/releases)
|
||||
|
||||
### 3. Setup the game
|
||||
- Extract the zip folder
|
||||
**Windows / macOS / Linux**
|
||||
- Extract the .zip file
|
||||
- Launch Dusk
|
||||
- Select Options, then set the ISO Path to your supported game dump
|
||||
- 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 Dusk apk
|
||||
- Launch Dusk
|
||||
- Press **Select Disc Image** and provide the path to your supported game dump
|
||||
- Press **Play**!
|
||||
|
||||
# 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. Please also see the [code conventions](docs/code-conventions.md).
|
||||
|
||||
# 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).
|
||||
|
||||
<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
|
||||
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
|
||||
cd "$RUNNER_WORKSPACE"
|
||||
curl -fOL https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$(uname -m).AppImage
|
||||
chmod +x linuxdeploy-$(uname -m).AppImage
|
||||
mkdir -p "$build_dir"
|
||||
curl -fL "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$(uname -m).AppImage" -o "$linuxdeploy"
|
||||
chmod +x "$linuxdeploy"
|
||||
|
||||
# Build AppImage
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
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 platforms/freedesktop/dusk.desktop build/appdir/usr/share/applications
|
||||
|
||||
cd build/install
|
||||
VERSION="$DUSK_VERSION" NO_STRIP=1 "$RUNNER_WORKSPACE"/linuxdeploy-$(uname -m).AppImage \
|
||||
-l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$GITHUB_WORKSPACE"/build/appdir --output appimage
|
||||
VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \
|
||||
-l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$build_dir/appdir" --output appimage
|
||||
|
||||
@@ -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 Dusk on iOS via AltStore
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Mac with Homebrew installed
|
||||
- iPhone connected via USB
|
||||
- Dusk IPA file (download the latest `Dusk-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
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
set(DOLZEL_FILES
|
||||
src/m_Do/m_Do_main.cpp
|
||||
src/m_Do/m_Do_printf.cpp
|
||||
#src/m_Do/m_Do_printf.cpp
|
||||
src/m_Do/m_Do_audio.cpp
|
||||
src/m_Do/m_Do_controller_pad.cpp
|
||||
#src/m_Do/m_Re_controller_pad.cpp
|
||||
@@ -1429,11 +1429,15 @@ set(DUSK_FILES
|
||||
src/dusk/globals.cpp
|
||||
src/dusk/gyro.cpp
|
||||
src/dusk/gamepad_color.cpp
|
||||
src/dusk/autosave.cpp
|
||||
src/dusk/http/http.hpp
|
||||
src/dusk/io.cpp
|
||||
src/dusk/layout.cpp
|
||||
src/dusk/logging.cpp
|
||||
src/dusk/settings.cpp
|
||||
src/dusk/stubs.cpp
|
||||
src/dusk/update_check.cpp
|
||||
src/dusk/update_check.hpp
|
||||
#src/dusk/m_Do_ext_dusk.cpp
|
||||
src/dusk/imgui/ImGuiConfig.hpp
|
||||
src/dusk/imgui/ImGuiConsole.hpp
|
||||
@@ -1446,28 +1450,82 @@ set(DUSK_FILES
|
||||
src/dusk/imgui/ImGuiBloomWindow.hpp
|
||||
src/dusk/imgui/ImGuiMenuTools.cpp
|
||||
src/dusk/imgui/ImGuiMenuTools.hpp
|
||||
src/dusk/imgui/ImGuiPreLaunchWindow.cpp
|
||||
src/dusk/imgui/ImGuiPreLaunchWindow.hpp
|
||||
src/dusk/imgui/ImGuiFirstRunPreset.hpp
|
||||
src/dusk/imgui/ImGuiFirstRunPreset.cpp
|
||||
src/dusk/imgui/ImGuiActorSpawner.cpp
|
||||
src/dusk/imgui/ImGuiProcessOverlay.cpp
|
||||
src/dusk/imgui/ImGuiCameraOverlay.cpp
|
||||
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
||||
src/dusk/imgui/ImGuiDebugPad.cpp
|
||||
src/dusk/imgui/ImGuiControllerOverlay.cpp
|
||||
src/dusk/imgui/ImGuiStubLog.cpp
|
||||
src/dusk/imgui/ImGuiMapLoader.cpp
|
||||
src/dusk/imgui/ImGuiSaveEditor.cpp
|
||||
src/dusk/imgui/ImGuiStateShare.hpp
|
||||
src/dusk/imgui/ImGuiStateShare.cpp
|
||||
src/dusk/imgui/ImGuiAchievements.hpp
|
||||
src/dusk/imgui/ImGuiAchievements.cpp
|
||||
src/dusk/ui/achievements.cpp
|
||||
src/dusk/ui/achievements.hpp
|
||||
src/dusk/ui/bool_button.cpp
|
||||
src/dusk/ui/bool_button.hpp
|
||||
src/dusk/ui/button.cpp
|
||||
src/dusk/ui/button.hpp
|
||||
src/dusk/ui/component.cpp
|
||||
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.hpp
|
||||
src/dusk/ui/editor.cpp
|
||||
src/dusk/ui/editor.hpp
|
||||
src/dusk/ui/event.cpp
|
||||
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.hpp
|
||||
src/dusk/ui/modal.cpp
|
||||
src/dusk/ui/modal.hpp
|
||||
src/dusk/ui/nav_types.hpp
|
||||
src/dusk/ui/number_button.cpp
|
||||
src/dusk/ui/number_button.hpp
|
||||
src/dusk/ui/overlay.cpp
|
||||
src/dusk/ui/overlay.hpp
|
||||
src/dusk/ui/pane.cpp
|
||||
src/dusk/ui/pane.hpp
|
||||
src/dusk/ui/menu_bar.cpp
|
||||
src/dusk/ui/menu_bar.hpp
|
||||
src/dusk/ui/prelaunch.cpp
|
||||
src/dusk/ui/prelaunch.hpp
|
||||
src/dusk/ui/preset.cpp
|
||||
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.hpp
|
||||
src/dusk/ui/settings.cpp
|
||||
src/dusk/ui/settings.hpp
|
||||
src/dusk/ui/string_button.cpp
|
||||
src/dusk/ui/string_button.hpp
|
||||
src/dusk/ui/tab_bar.cpp
|
||||
src/dusk/ui/tab_bar.hpp
|
||||
src/dusk/ui/ui.cpp
|
||||
src/dusk/ui/ui.hpp
|
||||
src/dusk/ui/window.cpp
|
||||
src/dusk/ui/window.hpp
|
||||
src/dusk/achievements.cpp
|
||||
src/dusk/iso_validate.cpp
|
||||
src/dusk/livesplit.cpp
|
||||
src/dusk/offset_ptr.cpp
|
||||
src/dusk/OSContext.cpp
|
||||
src/dusk/OSReport.cpp
|
||||
src/dusk/OSThread.cpp
|
||||
src/dusk/OSMutex.cpp
|
||||
src/dusk/discord.cpp
|
||||
src/dusk/discord.hpp
|
||||
src/dusk/discord_presence.cpp
|
||||
src/dusk/version.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
|
||||
)
|
||||
|
||||
@@ -5,9 +5,73 @@
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
|
||||
# Dependencies that are not packaged in nixpkgs:
|
||||
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=";
|
||||
};
|
||||
# Dusk Actual
|
||||
dusk = pkgs.stdenv.mkDerivation {
|
||||
name = "dusk";
|
||||
src = ./.;
|
||||
postUnpack = ''
|
||||
mkdir -p $sourceRoot/extern/aurora
|
||||
cp -r ${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=${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=${nod-src}"
|
||||
"-DAURORA_NOD_PROVIDER=package"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${sqlite-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_IMGUI=${imgui-src}"
|
||||
"-DFETCHCONTENT_SOURCE_DIR_RMLUI=${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 dusk $out/bin/dusk
|
||||
cp -r ./res $out/bin/res
|
||||
'';
|
||||
nativeBuildInputs = [
|
||||
pkgs.cmake
|
||||
pkgs.pkg-config
|
||||
@@ -25,6 +89,13 @@
|
||||
pkgs.libjpeg8
|
||||
pkgs.libxkbcommon
|
||||
pkgs.libglvnd
|
||||
pkgs.cxxopts
|
||||
pkgs.abseil-cpp
|
||||
pkgs.sdl3
|
||||
pkgs.fmt
|
||||
pkgs.tracy
|
||||
pkgs.freetype
|
||||
pkgs.zstd
|
||||
];
|
||||
};
|
||||
in {
|
||||
|
||||
@@ -4552,6 +4552,18 @@ public:
|
||||
void handleWolfHowl();
|
||||
void handleQuickTransform();
|
||||
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
|
||||
}; // Size: 0x385C
|
||||
|
||||
|
||||
@@ -80,6 +80,12 @@ public:
|
||||
/* 0x125C */ u32 field_0x125c;
|
||||
/* 0x1260 */ u8 field_0x1260[0x126C - 0x1260];
|
||||
/* 0x126C */ u8 HIOInit;
|
||||
#if TARGET_PC
|
||||
cXyz mStalkLineInterpPrev[12];
|
||||
cXyz mStalkLineInterpCurr[12];
|
||||
bool mStalkLineInterpPrevValid;
|
||||
bool mStalkLineInterpCurrValid;
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(e_db_class) == 0x1270);
|
||||
|
||||
@@ -73,6 +73,12 @@ public:
|
||||
/* 0x124C */ f32 field_0x124c;
|
||||
/* 0x1250 */ u8 field_0x1250[0x1264 - 0x1250];
|
||||
/* 0x1264 */ u8 HIOInit;
|
||||
#if TARGET_PC
|
||||
cXyz mStalkLineInterpPrev[12];
|
||||
cXyz mStalkLineInterpCurr[12];
|
||||
bool mStalkLineInterpPrevValid;
|
||||
bool mStalkLineInterpCurrValid;
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(e_hb_class) == 0x1268);
|
||||
|
||||
@@ -81,6 +81,15 @@ public:
|
||||
/* 0x306D */ u8 field_0x306D[0x307C - 0x306D];
|
||||
/* 0x307C */ u32 mBodyEffEmtrID;
|
||||
/* 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);
|
||||
|
||||
@@ -74,6 +74,12 @@ public:
|
||||
/* 0x1250 */ f32 field_0x1250;
|
||||
/* 0x1254 */ u8 field_0x1254[0x1268 - 0x1254];
|
||||
/* 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);
|
||||
|
||||
@@ -63,6 +63,15 @@ public:
|
||||
/* 0x0BB4 */ yg_ke_s mYgKes[13];
|
||||
/* 0x1880 */ mDoExt_3DlineMat0_c mLineMat;
|
||||
/* 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);
|
||||
|
||||
@@ -77,6 +77,12 @@ public:
|
||||
/* 0x1260 */ u32 field_0x1260;
|
||||
/* 0x1260 */ u8 field_0x1264[0x1270 - 0x1264];
|
||||
/* 0x1270 */ bool mIsHIOOwner;
|
||||
#if TARGET_PC
|
||||
cXyz mLineInterpPrev[12];
|
||||
cXyz mLineInterpCurr[12];
|
||||
bool mLineInterpPrevValid;
|
||||
bool mLineInterpCurrValid;
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(e_yh_class) == 0x1274);
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
/* 0x17C */ cXyz mViewScale;
|
||||
#if TARGET_PC
|
||||
bool mbReset = false;
|
||||
bool mbHadEntry = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@ public:
|
||||
csXyz* getAngle() { return field_0x8a4; }
|
||||
J3DModelData* getModelData() { return mModelData; }
|
||||
|
||||
#if TARGET_PC
|
||||
void onInterpCallback();
|
||||
#endif
|
||||
|
||||
private:
|
||||
/* 0x568 */ request_of_phase_process_class mPhase;
|
||||
/* 0x570 */ J3DModelData* mModelData;
|
||||
@@ -42,6 +46,14 @@ private:
|
||||
/* 0x694 */ cXyz field_0x694[22];
|
||||
/* 0x79C */ cXyz field_0x79c[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);
|
||||
|
||||
@@ -25,6 +25,10 @@ public:
|
||||
int Draw();
|
||||
int Delete();
|
||||
|
||||
#if TARGET_PC
|
||||
void onInterpCallback();
|
||||
#endif
|
||||
|
||||
enum Param_e {
|
||||
LOCK_e = (1 << 6), NO_BASE_DISP = (1 << 7)
|
||||
};
|
||||
@@ -50,6 +54,13 @@ private:
|
||||
/* 0x1020 */ dCcD_Cyl mCylinderCollider;
|
||||
/* 0x115C */ s32 mStopSwingingFrames;
|
||||
|
||||
#if TARGET_PC
|
||||
cXyz mChainInterpPrev[64];
|
||||
cXyz mChainInterpCurr[64];
|
||||
bool mChainInterpPrevValid;
|
||||
bool mChainInterpCurrValid;
|
||||
#endif
|
||||
|
||||
// Number of chain models
|
||||
u32 getArg0() {
|
||||
return fopAcM_GetParamBit(this, 0, 6);
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
void setNextPoint();
|
||||
int Draw();
|
||||
int Delete();
|
||||
#if TARGET_PC
|
||||
friend void daL8Lift_interp_callback(bool isSimFrame, void* pUserWork);
|
||||
#endif
|
||||
|
||||
u8 getPthID() { return fopAcM_GetParamBit(this, 0, 8); }
|
||||
u8 getMoveSpeed() { return fopAcM_GetParamBit(this, 8, 4); }
|
||||
|
||||
@@ -118,6 +118,18 @@ class camera_class;
|
||||
class dCamera_c;
|
||||
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 {
|
||||
public:
|
||||
class dCamInfo_c {
|
||||
@@ -1028,6 +1040,8 @@ public:
|
||||
bool test2Camera(s32);
|
||||
#if TARGET_PC
|
||||
bool freeCamera();
|
||||
bool executeDebugFlyCam();
|
||||
void deactivateDebugFlyCam();
|
||||
#endif
|
||||
bool towerCamera(s32);
|
||||
bool hookshotCamera(s32);
|
||||
@@ -1376,6 +1390,10 @@ public:
|
||||
/* 0x970 */ dCamSetup_c mCamSetup;
|
||||
/* 0xAEC */ dCamParam_c mCamParam;
|
||||
/* 0xB0C */ u8 field_0xb0c;
|
||||
|
||||
#if TARGET_PC
|
||||
DebugFlyCam mDebugFlyCam;
|
||||
#endif
|
||||
}; // Size: 0xB10
|
||||
|
||||
dCamera_c* dCam_getBody();
|
||||
|
||||
@@ -1845,6 +1845,12 @@ inline void dComIfGs_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() {
|
||||
return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getPlayerName();
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ public:
|
||||
void calcCursor();
|
||||
void drawCursor();
|
||||
|
||||
#if TARGET_PC
|
||||
void dMapBgWide();
|
||||
#endif
|
||||
|
||||
void setDPDFloorSelCurPos(s8 i_pos) { field_0xdd6 = i_pos; }
|
||||
|
||||
f32 getMapWidth() { return mMapWidth; }
|
||||
|
||||
@@ -81,6 +81,10 @@ public:
|
||||
void calcDrawPriority();
|
||||
void setArrowPosAxis(f32, f32);
|
||||
|
||||
#if TARGET_PC
|
||||
void fMapBackWide();
|
||||
#endif
|
||||
|
||||
virtual void draw();
|
||||
virtual ~dMenu_Fmap2DBack_c();
|
||||
|
||||
@@ -165,6 +169,12 @@ public:
|
||||
|
||||
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
|
||||
struct RegionTexData {
|
||||
/* 0x00 */ float mMinX;
|
||||
@@ -330,6 +340,10 @@ public:
|
||||
void setHIO(bool);
|
||||
bool isWarpAccept();
|
||||
|
||||
#if TARGET_PC
|
||||
void fMapTopWide();
|
||||
#endif
|
||||
|
||||
virtual void draw();
|
||||
virtual ~dMenu_Fmap2DTop_c();
|
||||
|
||||
|
||||
@@ -66,6 +66,16 @@ public:
|
||||
_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 {
|
||||
// Incomplete class
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ public:
|
||||
bool isStaffMessage();
|
||||
bool isSaveMessage();
|
||||
bool isTalkMessage();
|
||||
#if TARGET_PC
|
||||
bool isShopItemMessage();
|
||||
#endif
|
||||
const char* getSmellName();
|
||||
const char* getPortalName();
|
||||
const char* getBombName();
|
||||
|
||||
@@ -486,6 +486,9 @@ public:
|
||||
mDeathCount++;
|
||||
}
|
||||
}
|
||||
#if TARGET_PC
|
||||
u16 getDeathCount() const { return mDeathCount; }
|
||||
#endif
|
||||
char* getPlayerName() const { return const_cast<char*>(mPlayerName); }
|
||||
void setPlayerName(const char* i_name) {
|
||||
#if AVOID_UB
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
enum class AchievementCategory : uint8_t {
|
||||
Story,
|
||||
Collection,
|
||||
Challenge,
|
||||
Collection,
|
||||
Minigame,
|
||||
Misc,
|
||||
Glitched
|
||||
};
|
||||
|
||||
@@ -40,10 +42,13 @@ public:
|
||||
void save();
|
||||
void tick();
|
||||
void clearAll();
|
||||
void clearOne(const char* key);
|
||||
|
||||
// Signals are visible to all achievement checks within the same tick, then cleared.
|
||||
void signal(const char* key);
|
||||
bool hasSignal(const char* key) const;
|
||||
|
||||
std::vector<Achievement> getAchievements() const;
|
||||
bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); }
|
||||
std::string consumePendingUnlock();
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
@@ -57,9 +62,9 @@ private:
|
||||
void processEntry(Entry& e);
|
||||
|
||||
std::vector<Entry> m_entries;
|
||||
std::unordered_set<std::string_view> m_signals;
|
||||
bool m_loaded = false;
|
||||
bool m_dirty = false;
|
||||
std::queue<std::string> m_pendingUnlocks;
|
||||
};
|
||||
|
||||
} // namespace dusk
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef AUTOSAVE_H
|
||||
#define AUTOSAVE_H
|
||||
|
||||
#include <m_Do/m_Do_MemCardRWmng.h>
|
||||
#include <m_Do/m_Do_MemCard.h>
|
||||
|
||||
void noAutoSave();
|
||||
void triggerAutoSave();
|
||||
void updateAutoSave();
|
||||
void enterAutoSave();
|
||||
void autoSaving();
|
||||
void waitingForWrite();
|
||||
void endAutoSave();
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk {
|
||||
namespace dusk::crash_reporting {
|
||||
|
||||
void InitializeCrashReporting();
|
||||
void ShutdownCrashReporting();
|
||||
enum class Consent {
|
||||
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
|
||||
|
||||
#ifdef DUSK_DISCORD_RPC
|
||||
#ifdef DUSK_DISCORD
|
||||
|
||||
namespace dusk {
|
||||
namespace discord {
|
||||
namespace dusk::discord {
|
||||
|
||||
void Initialize();
|
||||
void initialize();
|
||||
void run_callbacks();
|
||||
void update_presence();
|
||||
void shutdown();
|
||||
|
||||
void RunCallbacks();
|
||||
} // namespace dusk::discord
|
||||
|
||||
void UpdatePresence();
|
||||
|
||||
void Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DUSK_DISCORD_RPC
|
||||
#endif // DUSK_DISCORD
|
||||
|
||||
@@ -12,6 +12,7 @@ void rollgoalTableOffset(s16& out_ax, s16& out_az);
|
||||
extern bool s_sensor_keep_alive;
|
||||
bool get_sensor_keep_alive();
|
||||
void set_sensor_keep_alive(bool value);
|
||||
bool rollgoal_gyro_enabled();
|
||||
} // namespace dusk::gyro
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef DUSK_IO_HPP
|
||||
#define DUSK_IO_HPP
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
// 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.
|
||||
*/
|
||||
class FileStream {
|
||||
void* file;
|
||||
FILE* file;
|
||||
|
||||
public:
|
||||
FileStream() noexcept;
|
||||
@@ -23,7 +24,7 @@ public:
|
||||
/**
|
||||
* \brief Take ownership of a FILE* handle.
|
||||
*/
|
||||
explicit FileStream(void* file);
|
||||
explicit FileStream(FILE* file);
|
||||
FileStream(const FileStream& other) = delete;
|
||||
FileStream(FileStream&& other) noexcept;
|
||||
|
||||
@@ -34,6 +35,11 @@ public:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -41,16 +47,33 @@ public:
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -59,17 +82,24 @@ public:
|
||||
/**
|
||||
* Get direct access to the underlying FILE* handle.
|
||||
*/
|
||||
[[nodiscard]] void* GetFileHandle() const noexcept {
|
||||
return file;
|
||||
}
|
||||
[[nodiscard]] void* GetFileHandle() const noexcept { return file; }
|
||||
|
||||
/**
|
||||
* Write data to the file.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dusk::speedrun {
|
||||
void onGameFrame();
|
||||
uint64_t getFrameCount();
|
||||
void start();
|
||||
void reset();
|
||||
void connectLiveSplit(const char* host = "127.0.0.1", int port = 16834);
|
||||
void disconnectLiveSplit();
|
||||
bool consumeConnectedEvent();
|
||||
bool consumeDisconnectedEvent();
|
||||
void updateLiveSplit();
|
||||
void shutdown();
|
||||
}
|
||||
@@ -1,14 +1,36 @@
|
||||
#ifndef DUSK_MAIN_H
|
||||
#define DUSK_MAIN_H
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#if defined(_WIN32) || \
|
||||
(defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \
|
||||
(defined(__linux__) && !defined(__ANDROID__))
|
||||
#define DUSK_CAN_OPEN_DATA_FOLDER 1
|
||||
#else
|
||||
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
||||
#endif
|
||||
|
||||
namespace dusk {
|
||||
extern bool IsRunning;
|
||||
extern bool IsShuttingDown;
|
||||
extern bool IsGameLaunched;
|
||||
extern bool IsFocusPaused;
|
||||
extern bool RestartRequested;
|
||||
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;
|
||||
bool OpenDataFolder();
|
||||
}
|
||||
|
||||
#endif // DUSK_MAIN_H
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
struct RoomEntry {
|
||||
u8 roomNo;
|
||||
std::vector<s16> roomPoints = {};
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
#define _SRC_DUSK_MATH_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <bit>
|
||||
|
||||
#ifndef M_PI
|
||||
#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_acosf(float x) { return acos(x); }
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
#include <dolphin/ppc_math.h>
|
||||
|
||||
#endif // _SRC_DUSK_MATH_H_
|
||||
|
||||
@@ -21,6 +21,17 @@ enum class GameLanguage : u8 {
|
||||
Italian = OS_LANGUAGE_ITALIAN,
|
||||
};
|
||||
|
||||
enum class DiscVerificationState : u8 {
|
||||
Unknown = 0,
|
||||
Success,
|
||||
HashMismatch,
|
||||
};
|
||||
|
||||
enum class GyroMode : u8 {
|
||||
Sensor = 0,
|
||||
Mouse = 1,
|
||||
};
|
||||
|
||||
namespace config {
|
||||
template <>
|
||||
struct ConfigEnumRange<BloomMode> {
|
||||
@@ -33,6 +44,18 @@ struct ConfigEnumRange<GameLanguage> {
|
||||
static constexpr auto min = GameLanguage::English;
|
||||
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
|
||||
@@ -45,6 +68,8 @@ struct UserSettings {
|
||||
ConfigVar<bool> enableFullscreen;
|
||||
ConfigVar<bool> enableVsync;
|
||||
ConfigVar<bool> lockAspectRatio;
|
||||
ConfigVar<bool> enableFpsOverlay;
|
||||
ConfigVar<int> fpsOverlayCorner;
|
||||
} video;
|
||||
|
||||
struct {
|
||||
@@ -55,6 +80,8 @@ struct UserSettings {
|
||||
ConfigVar<int> soundEffectsVolume;
|
||||
ConfigVar<int> fanfareVolume;
|
||||
ConfigVar<bool> enableReverb;
|
||||
ConfigVar<bool> enableHrtf;
|
||||
ConfigVar<bool> menuSounds;
|
||||
} audio;
|
||||
|
||||
// Game settings
|
||||
@@ -65,7 +92,6 @@ struct UserSettings {
|
||||
// QoL
|
||||
ConfigVar<bool> enableQuickTransform;
|
||||
ConfigVar<bool> hideTvSettingsScreen;
|
||||
ConfigVar<bool> skipWarningScreen;
|
||||
ConfigVar<bool> biggerWallets;
|
||||
ConfigVar<bool> noReturnRupees;
|
||||
ConfigVar<bool> disableRupeeCutscenes;
|
||||
@@ -76,17 +102,19 @@ struct UserSettings {
|
||||
ConfigVar<bool> fastClimbing;
|
||||
ConfigVar<bool> noMissClimbing;
|
||||
ConfigVar<bool> fastTears;
|
||||
ConfigVar<bool> no2ndFishForCat;
|
||||
ConfigVar<bool> instantSaves;
|
||||
ConfigVar<bool> instantText;
|
||||
ConfigVar<bool> sunsSong;
|
||||
ConfigVar<bool> autoSave;
|
||||
|
||||
// Preferences
|
||||
ConfigVar<bool> enableMirrorMode;
|
||||
ConfigVar<bool> disableMainHUD;
|
||||
ConfigVar<bool> minimalHUD;
|
||||
ConfigVar<bool> pauseOnFocusLost;
|
||||
ConfigVar<bool> enableLinkDollRotation;
|
||||
ConfigVar<bool> enableAchievementNotifications;
|
||||
|
||||
ConfigVar<bool> enableAchievementToasts;
|
||||
ConfigVar<bool> enableControllerToasts;
|
||||
|
||||
// Graphics
|
||||
ConfigVar<BloomMode> bloomMode;
|
||||
@@ -96,12 +124,14 @@ struct UserSettings {
|
||||
ConfigVar<int> internalResolutionScale;
|
||||
ConfigVar<int> shadowResolutionMultiplier;
|
||||
ConfigVar<bool> enableDepthOfField;
|
||||
ConfigVar<bool> enableMapBackground;
|
||||
|
||||
// Audio
|
||||
ConfigVar<bool> noLowHpSound;
|
||||
ConfigVar<bool> midnasLamentNonStop;
|
||||
|
||||
// Input
|
||||
ConfigVar<GyroMode> gyroMode;
|
||||
ConfigVar<bool> enableGyroAim;
|
||||
ConfigVar<bool> enableGyroRollgoal;
|
||||
ConfigVar<float> gyroSensitivityX;
|
||||
@@ -114,7 +144,12 @@ struct UserSettings {
|
||||
ConfigVar<bool> freeCamera;
|
||||
ConfigVar<bool> invertCameraXAxis;
|
||||
ConfigVar<bool> invertCameraYAxis;
|
||||
ConfigVar<bool> invertFirstPersonXAxis;
|
||||
ConfigVar<bool> invertFirstPersonYAxis;
|
||||
ConfigVar<float> freeCameraSensitivity;
|
||||
ConfigVar<bool> debugFlyCam;
|
||||
ConfigVar<bool> debugFlyCamLockEvents;
|
||||
ConfigVar<bool> allowBackgroundInput;
|
||||
|
||||
// Cheats
|
||||
ConfigVar<bool> infiniteHearts;
|
||||
@@ -137,17 +172,23 @@ struct UserSettings {
|
||||
|
||||
// Controls
|
||||
ConfigVar<bool> enableTurboKeybind;
|
||||
|
||||
// Tools
|
||||
ConfigVar<bool> speedrunMode;
|
||||
ConfigVar<bool> liveSplitEnabled;
|
||||
ConfigVar<bool> recordingMode;
|
||||
} game;
|
||||
|
||||
struct {
|
||||
ConfigVar<std::string> isoPath;
|
||||
ConfigVar<DiscVerificationState> isoVerification;
|
||||
ConfigVar<std::string> graphicsBackend;
|
||||
ConfigVar<bool> skipPreLaunchUI;
|
||||
ConfigVar<bool> showPipelineCompilation;
|
||||
ConfigVar<bool> wasPresetChosen;
|
||||
ConfigVar<bool> enableCrashReporting;
|
||||
ConfigVar<bool> duskMenuOpen;
|
||||
ConfigVar<bool> checkForUpdates;
|
||||
ConfigVar<int> cardFileType;
|
||||
ConfigVar<bool> enableAdvancedSettings;
|
||||
} backend;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Z2AudioLib/Z2EnvSeMgr.h"
|
||||
#include "Z2AudioLib/Z2LinkMgr.h"
|
||||
#include "dusk/audio.h"
|
||||
#include "dusk/settings.h"
|
||||
|
||||
class mDoAud_zelAudio_c : public Z2AudioMgr {
|
||||
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);
|
||||
}
|
||||
|
||||
#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) {
|
||||
DUSK_AUDIO_SKIP()
|
||||
Z2AudioMgr::getInterface()->seStartLevel(i_sfxID, i_sePos, param_2, i_reverb, 1.0f, 1.0f,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef J3DSTRUCT_H
|
||||
#define J3DSTRUCT_H
|
||||
|
||||
#include <cstring>
|
||||
#include <gx.h>
|
||||
#include <mtx.h>
|
||||
#include <mtx.h>
|
||||
#include "global.h"
|
||||
#include "JSystem/JMath/JMath.h"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DLightInfo {
|
||||
bool operator==(J3DLightInfo& other) const;
|
||||
@@ -28,7 +28,7 @@ struct J3DLightInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTextureSRTInfo {
|
||||
// NOTE: Big endian when loaded from file!
|
||||
@@ -79,7 +79,7 @@ enum J3DTexMtxMode {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTexMtxInfo {
|
||||
bool operator==(J3DTexMtxInfo& other) const;
|
||||
@@ -97,7 +97,7 @@ struct J3DTexMtxInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DIndTexMtxInfo {
|
||||
J3DIndTexMtxInfo& operator=(J3DIndTexMtxInfo const&);
|
||||
@@ -107,7 +107,7 @@ struct J3DIndTexMtxInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DFogInfo {
|
||||
bool operator==(J3DFogInfo&) const;
|
||||
@@ -126,7 +126,7 @@ struct J3DFogInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DNBTScaleInfo {
|
||||
bool operator==(const J3DNBTScaleInfo& other) const;
|
||||
@@ -153,7 +153,7 @@ struct J3DIndTexOrderInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevSwapModeInfo {
|
||||
/* 0x0 */ u8 mRasSel;
|
||||
@@ -164,7 +164,7 @@ struct J3DTevSwapModeInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevSwapModeTableInfo {
|
||||
/* 0x0 */ u8 field_0x0;
|
||||
@@ -175,7 +175,7 @@ struct J3DTevSwapModeTableInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevStageInfo {
|
||||
/* 0x0 */ u8 field_0x0;
|
||||
@@ -202,7 +202,7 @@ struct J3DTevStageInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DIndTevStageInfo {
|
||||
/* 0x0 */ u8 mIndStage;
|
||||
@@ -219,7 +219,7 @@ struct J3DIndTevStageInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTexCoordInfo {
|
||||
/* 0x0 */ u8 mTexGenType;
|
||||
@@ -265,7 +265,7 @@ struct J3DBlendInfo {
|
||||
|
||||
/**
|
||||
* @ingroup jsystem-j3d
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct J3DTevOrderInfo {
|
||||
void operator=(const J3DTevOrderInfo& other) {
|
||||
|
||||
@@ -59,6 +59,9 @@ public:
|
||||
bool isActive() const { return mSeqList.getNumLinks() != 0; }
|
||||
int getNumActiveSeqs() const { return mSeqList.getNumLinks(); }
|
||||
void pause(bool paused) { mActivity.field_0x0.flags.flag2 = paused; }
|
||||
#if TARGET_PC
|
||||
JSUList<JAISeq>* getSeqList() { return &mSeqList; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
/* 0x08 */ JAIAudience* mAudience;
|
||||
|
||||
@@ -207,4 +207,11 @@ void JPARegistAlphaEnv(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
void JPARegistPrmAlpha(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
void JPARegistPrmAlphaEnv(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
|
||||
#if TARGET_PC
|
||||
void JPAInterpBillboard(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
void JPAInterpRotBillboard(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
void JPAInterpDirection(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
void JPAInterpRotDirection(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
#endif
|
||||
|
||||
#endif /* JPABASESHAPE_H */
|
||||
|
||||
@@ -24,6 +24,9 @@ public:
|
||||
void init_c(JPAEmitterWorkData*, JPABaseParticle*);
|
||||
bool calc_p(JPAEmitterWorkData*);
|
||||
bool calc_c(JPAEmitterWorkData*);
|
||||
#if TARGET_PC
|
||||
void interp(JPAEmitterWorkData*, void const* drawFunc);
|
||||
#endif
|
||||
bool canCreateChild(JPAEmitterWorkData*);
|
||||
f32 getWidth(JPABaseEmitter const*) const;
|
||||
f32 getHeight(JPABaseEmitter const*) const;
|
||||
|
||||
@@ -40,6 +40,9 @@ public:
|
||||
JUTTransparency getTransparency() const { return JUTTransparency(mTransparency); }
|
||||
u16 getNumColors() const { return mNumColors; }
|
||||
ResTLUT* getColorTable() const { return mColorTable; }
|
||||
#if TARGET_PC
|
||||
void dataUploaded();
|
||||
#endif
|
||||
|
||||
private:
|
||||
/* 0x00 */ GXTlutObj mTlutObj;
|
||||
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
s32 getTransparency() const { return mTexInfo->alphaEnabled; }
|
||||
s32 getWidth() const { return mTexInfo->width; }
|
||||
s32 getHeight() const { return mTexInfo->height; }
|
||||
JUTPalette* getPalette() const { return mPalette; }
|
||||
void setCaptureFlag(bool flag) { mFlags &= 2 | flag; }
|
||||
bool getCaptureFlag() const { return mFlags & 1; }
|
||||
bool getEmbPaletteDelFlag() const { return mFlags & 2; }
|
||||
@@ -82,7 +83,7 @@ public:
|
||||
int getTlutName() const { return mTlutName; }
|
||||
bool operator==(const JUTTexture& other) {
|
||||
return mTexInfo == other.mTexInfo
|
||||
&& field_0x2c == other.field_0x2c
|
||||
&& mPalette == other.mPalette
|
||||
&& mWrapS == other.mWrapS
|
||||
&& mWrapT == other.mWrapT
|
||||
&& mMinFilter == other.mMinFilter
|
||||
@@ -100,7 +101,7 @@ private:
|
||||
/* 0x20 */ const ResTIMG* mTexInfo;
|
||||
/* 0x24 */ void* mTexData;
|
||||
/* 0x28 */ JUTPalette* mEmbPalette;
|
||||
/* 0x2C */ JUTPalette* field_0x2c;
|
||||
/* 0x2C */ JUTPalette* mPalette;
|
||||
/* 0x30 */ u8 mWrapS;
|
||||
/* 0x31 */ u8 mWrapT;
|
||||
/* 0x32 */ u8 mMinFilter;
|
||||
|
||||
@@ -556,8 +556,8 @@ void J3DModelLoader::readVertexData(const J3DVertexBlock& block, J3DVertexData&
|
||||
|
||||
if (attr == GX_VA_POS) {
|
||||
// can be a little off due to 0x20 alignment, account for that
|
||||
u32 expect = ((data.mVtxNum * vertStride) + 0x1F) & ~0x1F;
|
||||
JUT_ASSERT(1234, expect == addrDiff);
|
||||
// u32 expect = ((data.mVtxNum * vertStride) + 0x1F) & ~0x1F;
|
||||
// JUT_ASSERT(1234, expect == addrDiff);
|
||||
} else if (attr == GX_VA_NRM) {
|
||||
data.mNrmNum = num;
|
||||
} else if (attr == GX_VA_CLR0) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "JSystem/JSystem.h" // IWYU pragma: keep
|
||||
|
||||
#include "JSystem/JAudio2/JASChannel.h"
|
||||
#if TARGET_PC
|
||||
#include "dusk/audio/DuskDsp.hpp"
|
||||
#endif
|
||||
#include "JSystem/JAudio2/JASAiCtrl.h"
|
||||
#include "JSystem/JAudio2/JASCalc.h"
|
||||
#include "JSystem/JAudio2/JASDriverIF.h"
|
||||
@@ -170,7 +173,12 @@ void JASChannel::updateEffectorParam(JASDsp::TChannel* i_channel, u16* i_mixerVo
|
||||
|
||||
f32 pan = 0.5f;
|
||||
f32 dolby = 0.0f;
|
||||
switch (JASDriver::getOutputMode()) {
|
||||
#if TARGET_PC
|
||||
u32 effectiveOutputMode = dusk::audio::EnableHrtf ? JAS_OUTPUT_SURROUND : JASDriver::getOutputMode();
|
||||
#else
|
||||
u32 effectiveOutputMode = JASDriver::getOutputMode();
|
||||
#endif
|
||||
switch (effectiveOutputMode) {
|
||||
case JAS_OUTPUT_MONO:
|
||||
break;
|
||||
case JAS_OUTPUT_STEREO:
|
||||
|
||||
@@ -302,7 +302,6 @@ void JASKernel::setupRootHeap(JKRSolidHeap* heap, u32 size) {
|
||||
JKRHEAP_NAME(sSystemHeap, "JASKernel::sSystemHeap");
|
||||
JUT_ASSERT(787, sSystemHeap);
|
||||
sCommandHeap = JKR_NEW_ARGS (heap, 0) JASMemChunkPool<1024, JASThreadingModel::ObjectLevelLockable>;
|
||||
JKRHEAP_NAME(sSystemHeap, "JASKernel::sCommandHeap");
|
||||
JUT_ASSERT(790, sCommandHeap);
|
||||
JASDram = heap;
|
||||
}
|
||||
|
||||
@@ -442,6 +442,7 @@ static JAUSectionHeap* JAUNewSectionHeap(JKRSolidHeap* heap, bool param_1) {
|
||||
JAUSectionHeap* JAUNewSectionHeap(bool param_0) {
|
||||
s32 freeSize = JASDram->getFreeSize();
|
||||
JKRSolidHeap* sectionHeap = JKRCreateSolidHeap(freeSize, JASDram, true);
|
||||
JKRHEAP_NAME(sectionHeap, "sectionHeap");
|
||||
JUT_ASSERT(821, sectionHeap);
|
||||
return JAUNewSectionHeap(sectionHeap, param_0);
|
||||
}
|
||||
|
||||
@@ -222,16 +222,11 @@ void* JKRExpHeap::do_alloc(u32 size, int alignment) {
|
||||
OSReport_Error("Free block list as follows:\n");
|
||||
OSReport_Error("Start | End | Size \n");
|
||||
|
||||
int i = 0;
|
||||
for (const CMemBlock* block = mHeadFreeList; block; block = block->mNext) {
|
||||
if (block->mMagic) {
|
||||
// Allocated, ignore.
|
||||
continue;
|
||||
}
|
||||
if (i++ > 10) {
|
||||
OSReport_Error("<more>\n");
|
||||
break;
|
||||
}
|
||||
|
||||
auto blockStart = (uintptr_t)block - (uintptr_t)mStart;
|
||||
auto blockEnd = (uintptr_t)block + block->size - (uintptr_t)mStart;
|
||||
@@ -239,6 +234,14 @@ void* JKRExpHeap::do_alloc(u32 size, int alignment) {
|
||||
OSReport_Error("%08X | %08X | %08X\n", (u32) blockStart, (u32) blockEnd, (u32) blockSize);
|
||||
}
|
||||
|
||||
OSReport_Error("Child heaps as follows:\n");
|
||||
OSReport_Error("Start | End | Name \n");
|
||||
|
||||
const JSUTree<JKRHeap>& tree = getHeapTree();
|
||||
for (JSUTreeIterator iter(tree.getFirstChild()); iter != tree.getEndChild(); ++iter) {
|
||||
OSReport_Error("%08X | %08X | %s\n", iter->getStartAddr(), iter->getEndAddr(), iter->getName());
|
||||
}
|
||||
|
||||
CRASH("Aborting due to allocation failure!");
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <mtx.h>
|
||||
#include <gx.h>
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#endif
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
void JPASetPointSize(JPAEmitterWorkData* work) {
|
||||
@@ -418,50 +421,95 @@ static projectionFunc p_prj[3] = {
|
||||
loadPrjAnm,
|
||||
};
|
||||
|
||||
void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
||||
#if TARGET_PC
|
||||
void JPAInterpBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
Mtx ptclPosMtx;
|
||||
MTXTrans(ptclPosMtx, ptcl->mPosition.x, ptcl->mPosition.y, ptcl->mPosition.z);
|
||||
dusk::frame_interp::record_final_mtx(ptclPosMtx, ptcl);
|
||||
}
|
||||
|
||||
void JPAInterpRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
Mtx ptclPosMtx;
|
||||
f32 sinRot = JMASSin(ptcl->mRotateAngle);
|
||||
f32 cosRot = JMASCos(ptcl->mRotateAngle);
|
||||
MTXTrans(ptclPosMtx, ptcl->mPosition.x, ptcl->mPosition.y, ptcl->mPosition.z);
|
||||
ptclPosMtx[0][0] = cosRot;
|
||||
ptclPosMtx[0][1] = -sinRot;
|
||||
ptclPosMtx[1][0] = sinRot;
|
||||
ptclPosMtx[1][1] = cosRot;
|
||||
dusk::frame_interp::record_final_mtx(ptclPosMtx, ptcl);
|
||||
}
|
||||
#endif
|
||||
|
||||
void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JGeometry::TVec3<f32> local_48;
|
||||
MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48);
|
||||
Mtx local_38;
|
||||
local_38[0][0] = work->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
||||
local_38[0][3] = local_48.x;
|
||||
local_38[1][1] = work->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
||||
local_38[1][3] = local_48.y;
|
||||
local_38[2][2] = 1.0f;
|
||||
local_38[2][3] = local_48.z;
|
||||
local_38[0][1] = local_38[0][2] = local_38[1][0] = local_38[1][2] = local_38[2][0] = local_38[2][1] = 0.0f;
|
||||
GXLoadPosMtxImm(local_38, 0);
|
||||
p_prj[work->mPrjType](work, local_38);
|
||||
JGeometry::TVec3<f32> pos;
|
||||
#if TARGET_PC
|
||||
Mtx ptclPosMtx;
|
||||
if (dusk::frame_interp::lookup_replacement(ptcl, ptclPosMtx)) {
|
||||
pos.set(ptclPosMtx[0][3], ptclPosMtx[1][3], ptclPosMtx[2][3]);
|
||||
MTXMultVec(work->mPosCamMtx, &pos, &pos);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
MTXMultVec(work->mPosCamMtx, &ptcl->mPosition, &pos);
|
||||
}
|
||||
Mtx posMtx;
|
||||
posMtx[0][0] = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||
posMtx[0][3] = pos.x;
|
||||
posMtx[1][1] = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||
posMtx[1][3] = pos.y;
|
||||
posMtx[2][2] = 1.0f;
|
||||
posMtx[2][3] = pos.z;
|
||||
posMtx[0][1] = posMtx[0][2] = posMtx[1][0] = posMtx[1][2] = posMtx[2][0] = posMtx[2][1] = 0.0f;
|
||||
GXLoadPosMtxImm(posMtx, GX_PNMTX0);
|
||||
p_prj[work->mPrjType](work, posMtx);
|
||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||
}
|
||||
|
||||
void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
||||
void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JGeometry::TVec3<f32> local_48;
|
||||
MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48);
|
||||
f32 sinRot = JMASSin(param_1->mRotateAngle);
|
||||
f32 cosRot = JMASCos(param_1->mRotateAngle);
|
||||
f32 particleX = work->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
||||
f32 particleY = work->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
||||
if (work->mpRes->getUsrIdx() == 0x89d7) {
|
||||
int a = 0;
|
||||
}
|
||||
|
||||
Mtx local_38;
|
||||
local_38[0][0] = cosRot * particleX;
|
||||
local_38[0][1] = -sinRot * particleY;
|
||||
local_38[0][3] = local_48.x;
|
||||
local_38[1][0] = sinRot * particleX;
|
||||
local_38[1][1] = cosRot * particleY;
|
||||
local_38[1][3] = local_48.y;
|
||||
local_38[2][2] = 1.0f;
|
||||
local_38[2][3] = local_48.z;
|
||||
local_38[0][2] = local_38[1][2] = local_38[2][0] = local_38[2][1] = 0.0f;
|
||||
GXLoadPosMtxImm(local_38, 0);
|
||||
p_prj[work->mPrjType](work, local_38);
|
||||
JGeometry::TVec3<f32> pos;
|
||||
f32 sinRot, cosRot;
|
||||
#if TARGET_PC
|
||||
Mtx ptclPosMtx;
|
||||
MTXTrans(ptclPosMtx, ptcl->mPosition.x, ptcl->mPosition.y, ptcl->mPosition.z);
|
||||
if (dusk::frame_interp::lookup_replacement(ptcl, ptclPosMtx)) {
|
||||
pos.set(ptclPosMtx[0][3], ptclPosMtx[1][3], ptclPosMtx[2][3]);
|
||||
sinRot = ptclPosMtx[1][0];
|
||||
cosRot = ptclPosMtx[0][0];
|
||||
MTXMultVec(work->mPosCamMtx, &pos, &pos);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
MTXMultVec(work->mPosCamMtx, &ptcl->mPosition, &pos);
|
||||
sinRot = JMASSin(ptcl->mRotateAngle);
|
||||
cosRot = JMASCos(ptcl->mRotateAngle);
|
||||
}
|
||||
f32 particleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||
f32 particleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||
Mtx posMtx;
|
||||
posMtx[0][0] = cosRot * particleX;
|
||||
posMtx[0][1] = -sinRot * particleY;
|
||||
posMtx[0][3] = pos.x;
|
||||
posMtx[1][0] = sinRot * particleX;
|
||||
posMtx[1][1] = cosRot * particleY;
|
||||
posMtx[1][3] = pos.y;
|
||||
posMtx[2][2] = 1.0f;
|
||||
posMtx[2][3] = pos.z;
|
||||
posMtx[0][2] = posMtx[1][2] = posMtx[2][0] = posMtx[2][1] = 0.0f;
|
||||
GXLoadPosMtxImm(posMtx, GX_PNMTX0);
|
||||
p_prj[work->mPrjType](work, posMtx);
|
||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||
}
|
||||
|
||||
@@ -484,7 +532,7 @@ void JPADrawYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
local_38[2][2] = work->mYBBCamMtx[2][2];
|
||||
local_38[2][3] = local_48.z;
|
||||
local_38[0][1] = local_38[0][2] = local_38[1][0] = local_38[2][0] = 0.0f;
|
||||
GXLoadPosMtxImm(local_38, 0);
|
||||
GXLoadPosMtxImm(local_38, GX_PNMTX0);
|
||||
p_prj[work->mPrjType](work, local_38);
|
||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||
}
|
||||
@@ -517,7 +565,7 @@ void JPADrawRotYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
local_38[2][1] = local_94 * fVar1;
|
||||
local_38[2][2] = local_90;
|
||||
local_38[2][3] = local_48.z;
|
||||
GXLoadPosMtxImm(local_38, 0);
|
||||
GXLoadPosMtxImm(local_38, GX_PNMTX0);
|
||||
p_prj[work->mPrjType](work, local_38);
|
||||
GXCallDisplayList(jpa_dl, sizeof(jpa_dl));
|
||||
}
|
||||
@@ -681,103 +729,197 @@ static u8* p_dl[2] = {
|
||||
jpa_dl_x,
|
||||
};
|
||||
|
||||
void JPADrawDirection(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
||||
#if TARGET_PC
|
||||
void JPAInterpDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
JGeometry::TVec3<f32> axisY;
|
||||
JGeometry::TVec3<f32> axisZ;
|
||||
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||
|
||||
if (axisY.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
axisY.normalize();
|
||||
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||
|
||||
JGeometry::TVec3<f32> local_6c;
|
||||
JGeometry::TVec3<f32> local_78;
|
||||
p_direction[param_0->mDirType](param_0, param_1, &local_6c);
|
||||
|
||||
if (local_6c.isZero()) {
|
||||
if (axisZ.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
local_6c.normalize();
|
||||
local_78.cross(param_1->mBaseAxis, local_6c);
|
||||
|
||||
if (local_78.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
local_78.normalize();
|
||||
param_1->mBaseAxis.cross(local_6c, local_78);
|
||||
param_1->mBaseAxis.normalize();
|
||||
Mtx local_60;
|
||||
f32 fVar1 = param_0->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
||||
f32 fVar2 = param_0->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
||||
local_60[0][0] = param_1->mBaseAxis.x;
|
||||
local_60[0][1] = local_6c.x;
|
||||
local_60[0][2] = local_78.x;
|
||||
local_60[0][3] = param_1->mPosition.x;
|
||||
local_60[1][0] = param_1->mBaseAxis.y;
|
||||
local_60[1][1] = local_6c.y;
|
||||
local_60[1][2] = local_78.y;
|
||||
local_60[1][3] = param_1->mPosition.y;
|
||||
local_60[2][0] = param_1->mBaseAxis.z;
|
||||
local_60[2][1] = local_6c.z;
|
||||
local_60[2][2] = local_78.z;
|
||||
local_60[2][3] = param_1->mPosition.z;
|
||||
p_plane[param_0->mPlaneType](local_60, fVar1, fVar2);
|
||||
MTXConcat(param_0->mPosCamMtx, local_60, local_60);
|
||||
GXLoadPosMtxImm(local_60, 0);
|
||||
p_prj[param_0->mPrjType](param_0, local_60);
|
||||
GXCallDisplayList(p_dl[param_0->mDLType], sizeof(jpa_dl));
|
||||
axisZ.normalize();
|
||||
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||
ptcl->mBaseAxis.normalize();
|
||||
Mtx posMtx;
|
||||
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||
posMtx[0][0] = ptcl->mBaseAxis.x;
|
||||
posMtx[0][1] = axisY.x;
|
||||
posMtx[0][2] = axisZ.x;
|
||||
posMtx[0][3] = ptcl->mPosition.x;
|
||||
posMtx[1][0] = ptcl->mBaseAxis.y;
|
||||
posMtx[1][1] = axisY.y;
|
||||
posMtx[1][2] = axisZ.y;
|
||||
posMtx[1][3] = ptcl->mPosition.y;
|
||||
posMtx[2][0] = ptcl->mBaseAxis.z;
|
||||
posMtx[2][1] = axisY.z;
|
||||
posMtx[2][2] = axisZ.z;
|
||||
posMtx[2][3] = ptcl->mPosition.z;
|
||||
p_plane[work->mPlaneType](posMtx, scaleX, scaleY);
|
||||
dusk::frame_interp::record_final_mtx(posMtx, ptcl);
|
||||
}
|
||||
|
||||
void JPADrawRotDirection(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
||||
if (param_1->checkStatus(JPAPtclStts_Invisible)) {
|
||||
void JPAInterpRotDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
f32 sinRot = JMASSin(ptcl->mRotateAngle);
|
||||
f32 cosRot = JMASCos(ptcl->mRotateAngle);
|
||||
JGeometry::TVec3<f32> axisY;
|
||||
JGeometry::TVec3<f32> axisZ;
|
||||
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||
|
||||
if (axisY.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
axisY.normalize();
|
||||
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||
|
||||
if (axisZ.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
axisZ.normalize();
|
||||
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||
ptcl->mBaseAxis.normalize();
|
||||
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||
Mtx mtx1;
|
||||
Mtx mtx2;
|
||||
p_rot[work->mRotType](sinRot, cosRot, mtx1);
|
||||
p_plane[work->mPlaneType](mtx1, scaleX, scaleY);
|
||||
mtx2[0][0] = ptcl->mBaseAxis.x;
|
||||
mtx2[0][1] = axisY.x;
|
||||
mtx2[0][2] = axisZ.x;
|
||||
mtx2[0][3] = ptcl->mPosition.x;
|
||||
mtx2[1][0] = ptcl->mBaseAxis.y;
|
||||
mtx2[1][1] = axisY.y;
|
||||
mtx2[1][2] = axisZ.y;
|
||||
mtx2[1][3] = ptcl->mPosition.y;
|
||||
mtx2[2][0] = ptcl->mBaseAxis.z;
|
||||
mtx2[2][1] = axisY.z;
|
||||
mtx2[2][2] = axisZ.z;
|
||||
mtx2[2][3] = ptcl->mPosition.z;
|
||||
MTXConcat(mtx2, mtx1, mtx1);
|
||||
dusk::frame_interp::record_final_mtx(mtx1, ptcl);
|
||||
}
|
||||
#endif
|
||||
|
||||
void JPADrawDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
|
||||
f32 sinRot = JMASSin(param_1->mRotateAngle);
|
||||
f32 cosRot = JMASCos(param_1->mRotateAngle);
|
||||
JGeometry::TVec3<f32> local_6c;
|
||||
JGeometry::TVec3<f32> local_78;
|
||||
p_direction[param_0->mDirType](param_0, param_1, &local_6c);
|
||||
Mtx posMtx;
|
||||
#if TARGET_PC
|
||||
if (!dusk::frame_interp::lookup_replacement(ptcl, posMtx))
|
||||
#endif
|
||||
{
|
||||
JGeometry::TVec3<f32> axisY;
|
||||
JGeometry::TVec3<f32> axisZ;
|
||||
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||
|
||||
if (local_6c.isZero()) {
|
||||
if (axisY.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
axisY.normalize();
|
||||
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||
|
||||
if (axisZ.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
axisZ.normalize();
|
||||
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||
ptcl->mBaseAxis.normalize();
|
||||
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||
posMtx[0][0] = ptcl->mBaseAxis.x;
|
||||
posMtx[0][1] = axisY.x;
|
||||
posMtx[0][2] = axisZ.x;
|
||||
posMtx[0][3] = ptcl->mPosition.x;
|
||||
posMtx[1][0] = ptcl->mBaseAxis.y;
|
||||
posMtx[1][1] = axisY.y;
|
||||
posMtx[1][2] = axisZ.y;
|
||||
posMtx[1][3] = ptcl->mPosition.y;
|
||||
posMtx[2][0] = ptcl->mBaseAxis.z;
|
||||
posMtx[2][1] = axisY.z;
|
||||
posMtx[2][2] = axisZ.z;
|
||||
posMtx[2][3] = ptcl->mPosition.z;
|
||||
p_plane[work->mPlaneType](posMtx, scaleX, scaleY);
|
||||
}
|
||||
|
||||
MTXConcat(work->mPosCamMtx, posMtx, posMtx);
|
||||
GXLoadPosMtxImm(posMtx, GX_PNMTX0);
|
||||
p_prj[work->mPrjType](work, posMtx);
|
||||
GXCallDisplayList(p_dl[work->mDLType], sizeof(jpa_dl));
|
||||
}
|
||||
|
||||
void JPADrawRotDirection(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
if (ptcl->checkStatus(JPAPtclStts_Invisible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
local_6c.normalize();
|
||||
local_78.cross(param_1->mBaseAxis, local_6c);
|
||||
ZoneScoped;
|
||||
|
||||
if (local_78.isZero()) {
|
||||
return;
|
||||
Mtx mtx1;
|
||||
Mtx mtx2;
|
||||
#if TARGET_PC
|
||||
if (!dusk::frame_interp::lookup_replacement(ptcl, mtx1))
|
||||
#endif
|
||||
{
|
||||
f32 sinRot = JMASSin(ptcl->mRotateAngle);
|
||||
f32 cosRot = JMASCos(ptcl->mRotateAngle);
|
||||
JGeometry::TVec3<f32> axisY;
|
||||
JGeometry::TVec3<f32> axisZ;
|
||||
p_direction[work->mDirType](work, ptcl, &axisY);
|
||||
|
||||
if (axisY.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
axisY.normalize();
|
||||
axisZ.cross(ptcl->mBaseAxis, axisY);
|
||||
|
||||
if (axisZ.isZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
axisZ.normalize();
|
||||
ptcl->mBaseAxis.cross(axisY, axisZ);
|
||||
ptcl->mBaseAxis.normalize();
|
||||
f32 scaleX = work->mGlobalPtclScl.x * ptcl->mParticleScaleX;
|
||||
f32 scaleY = work->mGlobalPtclScl.y * ptcl->mParticleScaleY;
|
||||
p_rot[work->mRotType](sinRot, cosRot, mtx1);
|
||||
p_plane[work->mPlaneType](mtx1, scaleX, scaleY);
|
||||
mtx2[0][0] = ptcl->mBaseAxis.x;
|
||||
mtx2[0][1] = axisY.x;
|
||||
mtx2[0][2] = axisZ.x;
|
||||
mtx2[0][3] = ptcl->mPosition.x;
|
||||
mtx2[1][0] = ptcl->mBaseAxis.y;
|
||||
mtx2[1][1] = axisY.y;
|
||||
mtx2[1][2] = axisZ.y;
|
||||
mtx2[1][3] = ptcl->mPosition.y;
|
||||
mtx2[2][0] = ptcl->mBaseAxis.z;
|
||||
mtx2[2][1] = axisY.z;
|
||||
mtx2[2][2] = axisZ.z;
|
||||
mtx2[2][3] = ptcl->mPosition.z;
|
||||
MTXConcat(mtx2, mtx1, mtx1);
|
||||
}
|
||||
|
||||
local_78.normalize();
|
||||
param_1->mBaseAxis.cross(local_6c, local_78);
|
||||
param_1->mBaseAxis.normalize();
|
||||
f32 particleX = param_0->mGlobalPtclScl.x * param_1->mParticleScaleX;
|
||||
f32 particleY = param_0->mGlobalPtclScl.y * param_1->mParticleScaleY;
|
||||
Mtx auStack_80;
|
||||
Mtx local_60;
|
||||
p_rot[param_0->mRotType](sinRot, cosRot, auStack_80);
|
||||
p_plane[param_0->mPlaneType](auStack_80, particleX, particleY);
|
||||
local_60[0][0] = param_1->mBaseAxis.x;
|
||||
local_60[0][1] = local_6c.x;
|
||||
local_60[0][2] = local_78.x;
|
||||
local_60[0][3] = param_1->mPosition.x;
|
||||
local_60[1][0] = param_1->mBaseAxis.y;
|
||||
local_60[1][1] = local_6c.y;
|
||||
local_60[1][2] = local_78.y;
|
||||
local_60[1][3] = param_1->mPosition.y;
|
||||
local_60[2][0] = param_1->mBaseAxis.z;
|
||||
local_60[2][1] = local_6c.z;
|
||||
local_60[2][2] = local_78.z;
|
||||
local_60[2][3] = param_1->mPosition.z;
|
||||
MTXConcat(local_60, auStack_80, auStack_80);
|
||||
MTXConcat(param_0->mPosCamMtx, auStack_80, local_60);
|
||||
GXLoadPosMtxImm(local_60, 0);
|
||||
p_prj[param_0->mPrjType](param_0, local_60);
|
||||
GXCallDisplayList(p_dl[param_0->mDLType], sizeof(jpa_dl));
|
||||
MTXConcat(work->mPosCamMtx, mtx1, mtx2);
|
||||
GXLoadPosMtxImm(mtx2, GX_PNMTX0);
|
||||
p_prj[work->mPrjType](work, mtx2);
|
||||
GXCallDisplayList(p_dl[work->mDLType], sizeof(jpa_dl));
|
||||
}
|
||||
|
||||
void JPADrawDBillboard(JPAEmitterWorkData* param_0, JPABaseParticle* param_1) {
|
||||
|
||||
@@ -204,6 +204,28 @@ void JPABaseParticle::init_c(JPAEmitterWorkData* work, JPABaseParticle* parent)
|
||||
mTexAnmIdx = 0;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
void JPABaseParticle::interp(JPAEmitterWorkData* work, void const* drawFunc) {
|
||||
static bool enable = false;
|
||||
if (!enable)
|
||||
return;
|
||||
|
||||
// don't interpolate the first frame
|
||||
if (mAge == 0)
|
||||
return;
|
||||
|
||||
if (drawFunc == JPADrawBillboard) {
|
||||
JPAInterpBillboard(work, this);
|
||||
} else if (drawFunc == JPADrawRotBillboard) {
|
||||
JPAInterpRotBillboard(work, this);
|
||||
} else if (drawFunc == JPADrawDirection) {
|
||||
JPAInterpDirection(work, this);
|
||||
} else if (drawFunc == JPADrawRotDirection) {
|
||||
JPAInterpRotDirection(work, this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool JPABaseParticle::calc_p(JPAEmitterWorkData* work) {
|
||||
if (++mAge >= mLifeTime) {
|
||||
return true;
|
||||
@@ -247,6 +269,17 @@ bool JPABaseParticle::calc_p(JPAEmitterWorkData* work) {
|
||||
mOffsetPosition.y + mLocalPosition.y * work->mPublicScale.y,
|
||||
mOffsetPosition.z + mLocalPosition.z * work->mPublicScale.z);
|
||||
|
||||
#if TARGET_PC
|
||||
JPABaseShape* pBsp = work->mpRes->getBsp();
|
||||
work->mGlobalPtclScl.x = work->mpEmtr->mGlobalPScl.x * pBsp->getBaseSizeX();
|
||||
work->mGlobalPtclScl.y = work->mpEmtr->mGlobalPScl.y * pBsp->getBaseSizeY();
|
||||
work->mDirType = pBsp->getDirType();
|
||||
work->mRotType = pBsp->getRotType();
|
||||
work->mDLType = pBsp->getType() == 4 || pBsp->getType() == 8;
|
||||
work->mPlaneType = work->mDLType ? 2 : pBsp->getBasePlaneType();
|
||||
interp(work, (void const*)work->mpRes->mpDrawParticleFuncList[0]);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -289,6 +322,23 @@ bool JPABaseParticle::calc_c(JPAEmitterWorkData* work) {
|
||||
mOffsetPosition.y + mLocalPosition.y * work->mPublicScale.y,
|
||||
mOffsetPosition.z + mLocalPosition.z * work->mPublicScale.z);
|
||||
|
||||
#if TARGET_PC
|
||||
JPABaseShape* pBsp = work->mpRes->getBsp();
|
||||
JPAChildShape* pCsp = work->mpRes->getCsp();
|
||||
if (pCsp->isScaleInherited()) {
|
||||
work->mGlobalPtclScl.x = work->mpEmtr->mGlobalPScl.x * pBsp->getBaseSizeX();
|
||||
work->mGlobalPtclScl.y = work->mpEmtr->mGlobalPScl.y * pBsp->getBaseSizeY();
|
||||
} else {
|
||||
work->mGlobalPtclScl.x = work->mpEmtr->mGlobalPScl.x * pCsp->getScaleX();
|
||||
work->mGlobalPtclScl.y = work->mpEmtr->mGlobalPScl.y * pCsp->getScaleY();
|
||||
}
|
||||
work->mDirType = pCsp->getDirType();
|
||||
work->mRotType = pCsp->getRotType();
|
||||
work->mDLType = pCsp->getType() == 4 || pCsp->getType() == 8;
|
||||
work->mPlaneType = work->mDLType ? 2 : pCsp->getBasePlaneType();
|
||||
interp(work, (void const*)work->mpRes->mpDrawParticleChildFuncList[0]);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "JSystem/JUtility/JUTFader.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)
|
||||
: mColor(pColor), mBox(x, y, x + width, y + height) {
|
||||
mStatus = None;
|
||||
@@ -63,14 +67,24 @@ void JUTFader::advance() {
|
||||
|
||||
void JUTFader::control() {
|
||||
advance();
|
||||
#ifndef TARGET_PC
|
||||
// FRAME INTERP NOTE: Draw is called by JFWDisplay when interpolation is active
|
||||
draw();
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUTFader::draw() {
|
||||
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;
|
||||
orthograph.setColor(mColor);
|
||||
orthograph.fillBox(mBox);
|
||||
|
||||
@@ -38,3 +38,9 @@ bool JUTPalette::load() {
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
void JUTPalette::dataUploaded() {
|
||||
GXInitTlutObjData(&mTlutObj, (void*)mColorTable);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,7 @@ void JUTTexture::storeTIMG(ResTIMG const* param_0, u8 param_1) {
|
||||
mTexData = (void*)((intptr_t)mTexInfo + 0x20);
|
||||
}
|
||||
|
||||
field_0x2c = NULL;
|
||||
mPalette = NULL;
|
||||
mTlutName = 0;
|
||||
mWrapS = mTexInfo->wrapS;
|
||||
mWrapT = mTexInfo->wrapT;
|
||||
@@ -95,7 +95,7 @@ void JUTTexture::storeTIMG(ResTIMG const* param_0, JUTPalette* param_1, GXTlut p
|
||||
}
|
||||
mEmbPalette = param_1;
|
||||
setEmbPaletteDelFlag(false);
|
||||
field_0x2c = NULL;
|
||||
mPalette = NULL;
|
||||
if (param_1 != NULL) {
|
||||
mTlutName = param_2;
|
||||
if (param_2 != param_1->getTlutName()) {
|
||||
@@ -120,11 +120,11 @@ void JUTTexture::storeTIMG(ResTIMG const* param_0, JUTPalette* param_1, GXTlut p
|
||||
void JUTTexture::attachPalette(JUTPalette* param_0) {
|
||||
if (mTexInfo->indexTexture) {
|
||||
if (param_0 == NULL && mEmbPalette != NULL) {
|
||||
field_0x2c = mEmbPalette;
|
||||
mPalette = mEmbPalette;
|
||||
} else {
|
||||
field_0x2c = param_0;
|
||||
mPalette = param_0;
|
||||
}
|
||||
initTexObj(field_0x2c->getTlutName());
|
||||
initTexObj(mPalette->getTlutName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +133,9 @@ void JUTTexture::init() {
|
||||
initTexObj();
|
||||
} else {
|
||||
if (mEmbPalette != NULL) {
|
||||
field_0x2c = mEmbPalette;
|
||||
mPalette = mEmbPalette;
|
||||
|
||||
initTexObj(field_0x2c->getTlutName());
|
||||
initTexObj(mPalette->getTlutName());
|
||||
} else {
|
||||
OS_REPORT("This texture is CI-Format, but EmbPalette is NULL.\n");
|
||||
}
|
||||
@@ -179,8 +179,8 @@ void JUTTexture::initTexObj(GXTlut param_0) {
|
||||
}
|
||||
|
||||
void JUTTexture::load(GXTexMapID param_0) {
|
||||
if (field_0x2c) {
|
||||
field_0x2c->load();
|
||||
if (mPalette) {
|
||||
mPalette->load();
|
||||
}
|
||||
GXLoadTexObj(&mTexObj, param_0);
|
||||
}
|
||||
|
||||
@@ -66,12 +66,11 @@ Output APK:
|
||||
You can pass command-line args through the activity intent:
|
||||
|
||||
```bash
|
||||
adb shell am start -n com.twilitrealm.dusk/.DuskActivity \
|
||||
--es dusk_args "'/sdcard/Download/The Legend of Zelda: Twilight Princess (USA).iso'"
|
||||
adb shell am start -n dev.twilitrealm.dusk/.DuskActivity \
|
||||
--es dusk_args "--backend vulkan"
|
||||
```
|
||||
|
||||
Supported extras:
|
||||
|
||||
- `dusk_args`: single shell-like argument string
|
||||
- `dusk_argv`: string-array argv
|
||||
- `dusk_disc`: compatibility shortcut (single ISO path)
|
||||
|
||||
@@ -2,12 +2,22 @@ plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
def duskRepoDir = rootProject.projectDir.parentFile.parentFile
|
||||
def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusk').get().asFile
|
||||
def syncDuskAssets = tasks.register('syncDuskAssets', Sync) {
|
||||
from(new File(duskRepoDir, 'res')) {
|
||||
into 'res'
|
||||
exclude '**/.DS_Store'
|
||||
}
|
||||
into duskGeneratedAssetsDir
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.twilitrealm.dusk'
|
||||
namespace 'dev.twilitrealm.dusk'
|
||||
compileSdk 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.twilitrealm.dusk'
|
||||
applicationId 'dev.twilitrealm.dusk'
|
||||
minSdk 26
|
||||
targetSdk 36
|
||||
versionCode 1
|
||||
@@ -27,7 +37,7 @@ android {
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['src/main/jniLibs']
|
||||
assets.srcDirs = ['../../assets']
|
||||
assets.srcDirs = [duskGeneratedAssetsDir]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,3 +58,10 @@ android {
|
||||
dependencies {
|
||||
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 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-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -23,8 +24,19 @@
|
||||
<meta-data android:name="android.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
|
||||
android:name="com.twilitrealm.dusk.DuskActivity"
|
||||
android:name="dev.twilitrealm.dusk.DuskActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:exported="true"
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
package com.twilitrealm.dusk;
|
||||
package dev.twilitrealm.dusk;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
|
||||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DuskActivity extends SDLActivity {
|
||||
private static final String TAG = "DuskActivity";
|
||||
|
||||
private static String[] splitArgs(String raw) {
|
||||
List<String> out = new ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
@@ -61,18 +71,46 @@ public class DuskActivity extends SDLActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle 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) {
|
||||
getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
|
||||
}else {
|
||||
View decorView = getWindow().getDecorView();
|
||||
// Hide the status bar.
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
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 {
|
||||
View decorView = window.getDecorView();
|
||||
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);
|
||||
// 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.hide();
|
||||
if (actionBar != null) {
|
||||
actionBar.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,12 +138,96 @@ public class DuskActivity extends SDLActivity {
|
||||
return splitArgs(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
String discPath = intent.getStringExtra("dusk_disc");
|
||||
if (discPath != null && !discPath.isEmpty()) {
|
||||
return new String[] { discPath };
|
||||
}
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
persistUriPermissions(data);
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
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,413 @@
|
||||
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 java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
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 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() {
|
||||
ensureUserDirectories();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
|
||||
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
|
||||
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 {
|
||||
final File root = getContext().getFilesDir();
|
||||
if (root == null) {
|
||||
throw new FileNotFoundException("Dusk 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 Dusk 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 Dusk 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 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
|
||||
0x2c22, // Qanba
|
||||
0x2dc8, // 8BitDo
|
||||
0x37d7, // Flydigi
|
||||
0x9886, // ASTRO Gaming
|
||||
};
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
private static final String TAG = "SDL";
|
||||
private static final int SDL_MAJOR_VERSION = 3;
|
||||
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
|
||||
//
|
||||
@@ -2032,7 +2032,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
try {
|
||||
ParcelFileDescriptor pfd = mSingleton.getContentResolver().openFileDescriptor(Uri.parse(uri), mode);
|
||||
return pfd != null ? pfd.detachFd() : -1;
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (FileNotFoundException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
@@ -2227,4 +2227,3 @@ class SDLClipboardHandler implements
|
||||
SDLActivity.onNativeClipboardChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,17 +65,15 @@ class SDLInputConnection extends BaseInputConnection
|
||||
|
||||
@Override
|
||||
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
|
||||
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
||||
if (beforeLength > 0 && afterLength == 0) {
|
||||
// backspace(s)
|
||||
while (beforeLength-- > 0) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 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
|
||||
if (beforeLength > 0 && afterLength == 0) {
|
||||
// backspace(s)
|
||||
while (beforeLength-- > 0) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
||||
return false;
|
||||
|
||||
@@ -35,6 +35,8 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
|
||||
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
|
||||
|
||||
private static native void auroraNativeSetSurfaceReady(boolean ready);
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
@@ -96,6 +98,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
auroraNativeSetSurfaceReady(false);
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
@@ -103,6 +106,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
auroraNativeSetSurfaceReady(false);
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
@@ -192,6 +196,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
auroraNativeSetSurfaceReady(true);
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Dusk</string>
|
||||
<string name="documents_provider_root_name">Dusk Data</string>
|
||||
<string name="documents_provider_summary">Saves, texture packs, settings, and logs</string>
|
||||
</resources>
|
||||
|
||||
@@ -14,9 +14,22 @@ if [[ -z "$ANDROID_NDK_VER" ]] && [[ -d "$ANDROID_HOME_DIR/ndk" ]]; then
|
||||
fi
|
||||
|
||||
if [[ -n "$ANDROID_NDK_VER" ]]; then
|
||||
TOOLCHAIN_BIN="$ANDROID_HOME_DIR/ndk/$ANDROID_NDK_VER/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
if [[ -x "$TOOLCHAIN_BIN/llvm-strip" ]]; then
|
||||
STRIP_TOOL="$TOOLCHAIN_BIN/llvm-strip"
|
||||
case "$(uname -s)" in
|
||||
Darwin) HOST_TAG="darwin-x86_64" ;;
|
||||
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
|
||||
|
||||
@@ -25,29 +38,35 @@ copy_lib() {
|
||||
local src="$2"
|
||||
local dst_dir="$APP_DIR/$abi"
|
||||
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"
|
||||
cp -f "$src" "$dst"
|
||||
cp -f "$src" "$tmp"
|
||||
if [[ "$ANDROID_STAGE_STRIP" != "0" ]] && [[ -n "$STRIP_TOOL" ]]; then
|
||||
"$STRIP_TOOL" --strip-debug "$dst"
|
||||
echo "Staged and stripped $src -> $dst"
|
||||
"$STRIP_TOOL" --strip-unneeded "$tmp"
|
||||
mv -f "$tmp" "$dst"
|
||||
echo "Stripped and staged $src -> $dst"
|
||||
else
|
||||
mv -f "$tmp" "$dst"
|
||||
echo "Staged $src -> $dst (strip disabled or strip tool unavailable)"
|
||||
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.
|
||||
rm -rf "$APP_DIR/x86" "$APP_DIR/arm64-v8a" "$APP_DIR/x86_64"
|
||||
|
||||
for abi in $ANDROID_STAGE_ABIS; do
|
||||
src="${ABI_TO_LIB[$abi]:-}"
|
||||
if [[ -z "$src" ]]; then
|
||||
echo "Unsupported ABI '$abi'. Supported ABIs: arm64-v8a x86_64" >&2
|
||||
exit 1
|
||||
fi
|
||||
case "$abi" in
|
||||
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
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
copy_lib "$abi" "$src"
|
||||
done
|
||||
|
||||
|
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 |
@@ -6,4 +6,4 @@ Exec=dusk
|
||||
Icon=dusk
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Graphics;3DGraphics;Game
|
||||
Categories=Game;
|
||||
|
||||
@@ -79,5 +79,11 @@
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>LSSupportsGameMode</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -28,5 +28,7 @@
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>LSSupportsGameMode</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -45,5 +45,7 @@
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>LSSupportsGameMode</key>
|
||||
<true />
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
After Width: | Height: | Size: 457 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 48 KiB |