mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 09:35:21 +02:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca4a20f524 | |||
| 3b145909e8 | |||
| 6eb3367f1b | |||
| 03f83fe917 | |||
| c4771f91e0 | |||
| bdd12352e1 | |||
| 224bd2df1d | |||
| 412f928781 | |||
| 779ef9fe77 | |||
| 048d1d662f | |||
| cf3a27cecf | |||
| b8c3556df6 | |||
| 9e337295ca | |||
| 8cb57811a6 | |||
| 82c02461f8 | |||
| 26253b5ed7 | |||
| f520aeab82 | |||
| edb6960d4f | |||
| a488f0d91a | |||
| 73d895a64a | |||
| 805bcba249 | |||
| 66abd14c3b | |||
| d856499f59 | |||
| bc3dd4d39a | |||
| 5a91d09f9e | |||
| b09f8bf891 | |||
| 8ee10e40ad | |||
| f8a7153fdd | |||
| a592d0eb84 | |||
| 28aa8ea280 | |||
| cb87f79710 | |||
| be117214c3 | |||
| bb056a77f4 |
@@ -8,6 +8,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# - package-ecosystem: "cargo"
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
|
||||
+30
-17
@@ -4,11 +4,11 @@ permissions:
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
branches: [ "main" ]
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -40,26 +40,31 @@ jobs:
|
||||
rustflags: ""
|
||||
cache: ${{ github.ref_type != 'tag' }}
|
||||
- name: Set LLVM_VERSION
|
||||
run: echo "LLVM_VERSION=$(rustc --version --verbose | grep "LLVM version" | cut -d':' -f2 | xargs)" >> $GITHUB_ENV
|
||||
run: echo "LLVM_VERSION=$(rustc --version --verbose | grep "LLVM version" | cut
|
||||
-d':' -f2 | xargs)" >> $GITHUB_ENV
|
||||
- name: Install SDL Dependencies
|
||||
# https://wiki.libsdl.org/SDL3/README-linux#build-dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1
|
||||
with:
|
||||
packages: build-essential git make \
|
||||
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
|
||||
libaudio-dev libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
|
||||
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
|
||||
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
|
||||
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev \
|
||||
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev libfreetype-dev libfontconfig-dev
|
||||
packages: build-essential git make pkg-config cmake ninja-build
|
||||
gnome-desktop-testing libasound2-dev libpulse-dev libaudio-dev
|
||||
libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev
|
||||
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev
|
||||
libxtst-dev libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev
|
||||
libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev
|
||||
libudev-dev libthai-dev libpipewire-0.3-dev libwayland-dev
|
||||
libdecor-0-dev liburing-dev libfreetype-dev libfontconfig-dev
|
||||
version: ${{ matrix.target }}-1.0
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: ${{ env.LLVM_VERSION }}
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-Linux-${{ matrix.llvm }}.tar.xz
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{
|
||||
env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-Linux-${{
|
||||
matrix.llvm }}.tar.xz
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --no-default-features -v --target=${{ matrix.target }}
|
||||
cargo build --release -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/release/gopher64 output/gopher64-${{ matrix.arch }}
|
||||
@@ -104,9 +109,12 @@ jobs:
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: ${{ env.LLVM_VERSION }}
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-${{ matrix.llvm }}.exe
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{
|
||||
env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-${{ matrix.llvm
|
||||
}}.exe
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --no-default-features -v --target=${{ matrix.target }}
|
||||
cargo build --release -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/release/gopher64.exe output/gopher64-${{ matrix.arch }}.exe
|
||||
@@ -163,16 +171,20 @@ jobs:
|
||||
rustflags: ""
|
||||
cache: ${{ github.ref_type != 'tag' }}
|
||||
- name: Set LLVM_VERSION
|
||||
run: echo "LLVM_VERSION=$(rustc --version --verbose | grep "LLVM version" | cut -d':' -f2 | xargs)" >> $GITHUB_ENV
|
||||
run: echo "LLVM_VERSION=$(rustc --version --verbose | grep "LLVM version" | cut
|
||||
-d':' -f2 | xargs)" >> $GITHUB_ENV
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: ${{ env.LLVM_VERSION }}
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-macOS-${{ matrix.llvm }}.tar.xz
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{
|
||||
env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-macOS-${{
|
||||
matrix.llvm }}.tar.xz
|
||||
- name: Build
|
||||
run: |
|
||||
cargo install cargo-bundle
|
||||
brew install molten-vk
|
||||
cargo build --no-default-features -v --target=${{ matrix.target }}
|
||||
cargo bundle --release --target=${{ matrix.target }} --format osx
|
||||
install_name_tool -change /opt/homebrew/opt/freetype/lib/libfreetype.6.dylib \
|
||||
@executable_path/../Frameworks/libfreetype.6.dylib target/${{ matrix.target }}/release/bundle/osx/Gopher64.app/Contents/MacOS/gopher64
|
||||
@@ -180,11 +192,11 @@ jobs:
|
||||
@executable_path/../Frameworks/libpng16.16.dylib target/${{ matrix.target }}/release/bundle/osx/Gopher64.app/Contents/Frameworks/libfreetype.6.dylib
|
||||
env:
|
||||
NETPLAY_ID: ${{ secrets.NETPLAY_ID }}
|
||||
MACOSX_DEPLOYMENT_TARGET: "15.0"
|
||||
MACOSX_DEPLOYMENT_TARGET: "15.7"
|
||||
- name: Sign Bundle Dev
|
||||
if: ${{ github.ref_type != 'tag' }}
|
||||
run: |
|
||||
codesign -f --entitlements data/entitlements_dev.plist --deep --timestamp --options runtime -s - target/${{ matrix.target }}/release/bundle/osx/Gopher64.app
|
||||
codesign -f --entitlements data/macos/entitlements_dev.plist --deep --timestamp --options runtime -s - target/${{ matrix.target }}/release/bundle/osx/Gopher64.app
|
||||
- name: Import Codesign Certs
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
uses: apple-actions/import-codesign-certs@v6
|
||||
@@ -194,7 +206,8 @@ jobs:
|
||||
- name: Sign Bundle Release
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
run: |
|
||||
codesign -f --entitlements data/entitlements_prod.plist --deep --timestamp --options runtime -s "${{ secrets.APPLE_DEVELOPER_ID }}" target/${{ matrix.target }}/release/bundle/osx/Gopher64.app
|
||||
cp data/macos/embedded.provisionprofile target/${{ matrix.target }}/release/bundle/osx/Gopher64.app/Contents/embedded.provisionprofile
|
||||
codesign -f --entitlements data/macos/entitlements_prod.plist --deep --timestamp --options runtime -s "${{ secrets.APPLE_DEVELOPER_ID }}" target/${{ matrix.target }}/release/bundle/osx/Gopher64.app
|
||||
zip -r gopher64-${{ matrix.arch }}.zip target/${{ matrix.target }}/release/bundle/osx/Gopher64.app
|
||||
xcrun notarytool submit --wait --apple-id "${{ secrets.APPLE_ID }}" --team-id "${{ secrets.APPLE_DEVELOPER_ID }}" --password "${{ secrets.APPLE_APP_PASSWORD }}" gopher64-${{ matrix.arch }}.zip
|
||||
xcrun stapler staple target/${{ matrix.target }}/release/bundle/osx/Gopher64.app
|
||||
|
||||
+15
-11
@@ -2,9 +2,9 @@ name: Rust-lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "main" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -23,26 +23,30 @@ jobs:
|
||||
with:
|
||||
components: clippy, rustfmt
|
||||
- name: Set LLVM_VERSION
|
||||
run: echo "LLVM_VERSION=$(rustc --version --verbose | grep "LLVM version" | cut -d':' -f2 | xargs)" >> $GITHUB_ENV
|
||||
run: echo "LLVM_VERSION=$(rustc --version --verbose | grep "LLVM version" | cut
|
||||
-d':' -f2 | xargs)" >> $GITHUB_ENV
|
||||
- name: Install SDL Dependencies
|
||||
# https://wiki.libsdl.org/SDL3/README-linux#build-dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1
|
||||
with:
|
||||
packages: build-essential git make \
|
||||
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
|
||||
libaudio-dev libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
|
||||
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
|
||||
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
|
||||
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev \
|
||||
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev libfreetype-dev libfontconfig-dev
|
||||
packages: build-essential git make pkg-config cmake ninja-build
|
||||
gnome-desktop-testing libasound2-dev libpulse-dev libaudio-dev
|
||||
libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev
|
||||
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev
|
||||
libxtst-dev libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev
|
||||
libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev
|
||||
libudev-dev libthai-dev libpipewire-0.3-dev libwayland-dev
|
||||
libdecor-0-dev liburing-dev libfreetype-dev libfontconfig-dev
|
||||
version: lint-1.0
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: ${{ env.LLVM_VERSION }}
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-Linux-X64.tar.xz
|
||||
force-url: https://github.com/llvm/llvm-project/releases/download/llvmorg-${{
|
||||
env.LLVM_VERSION }}/LLVM-${{ env.LLVM_VERSION }}-Linux-X64.tar.xz
|
||||
- name: Rustfmt Check
|
||||
uses: actions-rust-lang/rustfmt@v1
|
||||
- name: Lint
|
||||
run: |
|
||||
cargo clippy -- -Dwarnings
|
||||
cargo clippy --no-default-features -- -Dwarnings
|
||||
|
||||
Generated
+358
-73
@@ -18,6 +18,95 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||
|
||||
[[package]]
|
||||
name = "accesskit"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eca13c82f9a5cd813120b2e9b6a5d10532c6e4cd140c295cebd1f770095c8a5"
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_atspi_common"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eb9cc46b7fb6987c4f891f0301b230b29d9e69b4854f060a0cf41fbc407ab77"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
"atspi-common",
|
||||
"serde",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69d880a613f29621c90e801feec40f5dd61d837d7e20bf9b67676d45e7364a36"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0ddfc3fe3d457d11cc1c4989105986a03583a1d54d0c25053118944b62e100"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
"hashbrown 0.16.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_unix"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d552169ef018149966ed139bb0311c6947b3343e9140d1b9f88d69da9528fd"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_atspi_common",
|
||||
"async-channel",
|
||||
"async-executor",
|
||||
"async-task",
|
||||
"atspi",
|
||||
"futures-lite",
|
||||
"futures-util",
|
||||
"serde",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_windows"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d277279d0a3b0c0021dd110b55aa1fe326b09ee2cbc338df28f847c7daf94e25"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
"hashbrown 0.16.1",
|
||||
"static_assertions",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_winit"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db08dff285306264a1de127ea07bb9e7a1ed71bd8593c168d0731caa782516c9"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_macos",
|
||||
"accesskit_unix",
|
||||
"accesskit_windows",
|
||||
"raw-window-handle",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
@@ -372,6 +461,43 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "atspi"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d"
|
||||
dependencies = [
|
||||
"atspi-common",
|
||||
"atspi-proxies",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atspi-common"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zbus",
|
||||
"zbus-lockstep",
|
||||
"zbus-lockstep-macros",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atspi-proxies"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc"
|
||||
dependencies = [
|
||||
"atspi-common",
|
||||
"serde",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auto_enums"
|
||||
version = "0.8.8"
|
||||
@@ -783,7 +909,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1128,10 +1254,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor-lite"
|
||||
version = "0.1.2"
|
||||
name = "ctor"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300"
|
||||
checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf"
|
||||
dependencies = [
|
||||
"dtor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
@@ -1333,6 +1462,12 @@ dependencies = [
|
||||
"linux-raw-sys 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtor"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b"
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
@@ -1627,8 +1762,8 @@ dependencies = [
|
||||
"read-fonts",
|
||||
"roxmltree",
|
||||
"smallvec",
|
||||
"windows",
|
||||
"windows-core",
|
||||
"windows 0.62.2",
|
||||
"windows-core 0.62.2",
|
||||
"yeslogic-fontconfig-sys",
|
||||
]
|
||||
|
||||
@@ -1814,7 +1949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
|
||||
dependencies = [
|
||||
"rustix 1.1.4",
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1976,7 +2111,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gopher64"
|
||||
version = "1.1.16"
|
||||
version = "1.1.17"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
@@ -2013,9 +2148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "grid"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220"
|
||||
checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
@@ -2303,6 +2438,8 @@ version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90f7fddf48d12b366f352f8fb78d59665c490cf279786772f1d1592e143edd0d"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_winit",
|
||||
"block2 0.6.2",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
@@ -2332,7 +2469,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"webbrowser",
|
||||
"windows",
|
||||
"windows 0.62.2",
|
||||
"winit",
|
||||
"zbus",
|
||||
]
|
||||
@@ -2475,7 +2612,7 @@ dependencies = [
|
||||
"softbuffer",
|
||||
"unicode-segmentation",
|
||||
"vtable",
|
||||
"windows",
|
||||
"windows 0.62.2",
|
||||
"write-fonts",
|
||||
]
|
||||
|
||||
@@ -2511,7 +2648,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2890,7 +3027,7 @@ dependencies = [
|
||||
"simd_cesu8",
|
||||
"thiserror 2.0.18",
|
||||
"walkdir",
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3004,9 +3141,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
checksum = "b3a6a8c165077efc8f3a971534c50ea6a1a18b329ef4a66e897a7e3a1494565f"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
@@ -3031,7 +3168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3042,12 +3179,11 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.44"
|
||||
version = "0.1.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
|
||||
checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3233,9 +3369,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.48"
|
||||
version = "0.1.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8"
|
||||
checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
@@ -3862,9 +3998,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.3.3"
|
||||
version = "5.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc"
|
||||
checksum = "9f3bab717c29a857abf75fcef718d441ec7cb2725f937343c734740a985d37fd"
|
||||
dependencies = [
|
||||
"is-wsl",
|
||||
"libc",
|
||||
@@ -3885,9 +4021,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "orbclient"
|
||||
version = "0.3.51"
|
||||
version = "0.3.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6"
|
||||
checksum = "12c6933ddbbd16539a7672e697bb8d41ac3a4e99ac43eeb40c07236bd7fcb2dd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
@@ -4220,9 +4356,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.28"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d"
|
||||
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
@@ -4239,6 +4375,16 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.39.2"
|
||||
@@ -4734,9 +4880,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.38"
|
||||
version = "0.23.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21"
|
||||
checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"once_cell",
|
||||
@@ -4797,9 +4943,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.12"
|
||||
version = "0.103.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06"
|
||||
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
@@ -5173,7 +5319,7 @@ checksum = "6a71c01d325d40b1031dee67d251a5e0132e79e2a9ec272149a4f4a0d4b8b3be"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"skia-bindings",
|
||||
"windows",
|
||||
"windows 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5200,6 +5346,7 @@ checksum = "364a1e8fae7f1969224acc59ba729f06aeaf808123796389eae458c396119ef0"
|
||||
dependencies = [
|
||||
"const-field-offset",
|
||||
"i-slint-backend-selector",
|
||||
"i-slint-backend-winit",
|
||||
"i-slint-common",
|
||||
"i-slint-core",
|
||||
"i-slint-core-macros",
|
||||
@@ -5430,6 +5577,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strict-num"
|
||||
version = "0.1.1"
|
||||
@@ -5726,12 +5879,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tiny-xlib"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e"
|
||||
checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"ctor-lite",
|
||||
"ctor",
|
||||
"libloading",
|
||||
"pkg-config",
|
||||
"tracing",
|
||||
@@ -5765,14 +5918,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.52.0"
|
||||
version = "1.52.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776"
|
||||
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -5851,7 +6005,7 @@ dependencies = [
|
||||
"toml_datetime 1.1.1+spec-1.1.0",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow 1.0.1",
|
||||
"winnow 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5882,7 +6036,7 @@ dependencies = [
|
||||
"toml_datetime 1.1.1+spec-1.1.0",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow 1.0.1",
|
||||
"winnow 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5891,7 +6045,7 @@ version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow 1.0.1",
|
||||
"winnow 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6026,9 +6180,9 @@ checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
||||
|
||||
[[package]]
|
||||
name = "udev"
|
||||
@@ -6267,11 +6421,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
version = "1.0.3+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
"wit-bindgen 0.57.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6280,7 +6434,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
"wit-bindgen 0.51.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6491,7 +6645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quick-xml 0.39.2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
@@ -6529,9 +6683,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webbrowser"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16"
|
||||
checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.1",
|
||||
"jni 0.22.4",
|
||||
@@ -6567,16 +6721,38 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
dependencies = [
|
||||
"windows-collections 0.2.0",
|
||||
"windows-core 0.61.2",
|
||||
"windows-future 0.2.1",
|
||||
"windows-link 0.1.3",
|
||||
"windows-numerics 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-numerics",
|
||||
"windows-collections 0.3.2",
|
||||
"windows-core 0.62.2",
|
||||
"windows-future 0.3.2",
|
||||
"windows-numerics 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6585,7 +6761,20 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link 0.1.3",
|
||||
"windows-result 0.3.4",
|
||||
"windows-strings 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6596,9 +6785,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
"windows-link 0.1.3",
|
||||
"windows-threading 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6607,9 +6807,9 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
"windows-core 0.62.2",
|
||||
"windows-link 0.2.1",
|
||||
"windows-threading 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6634,20 +6834,36 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-core 0.62.2",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6656,9 +6872,18 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6667,7 +6892,16 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6676,7 +6910,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6730,7 +6964,7 @@ version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6785,7 +7019,7 @@ version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
@@ -6796,13 +7030,22 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7048,9 +7291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
|
||||
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -7074,6 +7317,12 @@ dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
@@ -7353,6 +7602,30 @@ dependencies = [
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus-lockstep"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863"
|
||||
dependencies = [
|
||||
"zbus_xml",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus-lockstep-macros"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zbus-lockstep",
|
||||
"zbus_xml",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.14.0"
|
||||
@@ -7379,6 +7652,18 @@ dependencies = [
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_xml"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac"
|
||||
dependencies = [
|
||||
"quick-xml 0.38.4",
|
||||
"serde",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeno"
|
||||
version = "0.3.3"
|
||||
|
||||
+9
-7
@@ -1,10 +1,12 @@
|
||||
[package]
|
||||
name = "gopher64"
|
||||
version = "1.1.16"
|
||||
version = "1.1.17"
|
||||
edition = "2024"
|
||||
rust-version = "1.95.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
default = ["gui"]
|
||||
gui = ["dep:rfd", "dep:slint", "dep:slint-build"]
|
||||
|
||||
[dependencies]
|
||||
dirs = "6.0"
|
||||
@@ -14,13 +16,13 @@ chrono = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde-big-array = "0.5"
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-winit", "renderer-skia"] }
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "unstable-winit-030", "backend-winit", "renderer-skia", "accessibility"], optional = true }
|
||||
open = "5.3"
|
||||
sha2 = "0.11"
|
||||
sdl3-sys = { version = "0.6", features = ["build-from-source-static"] }
|
||||
sdl3-ttf-sys = { version = "0.6", features = ["build-from-source-static", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] }
|
||||
rfd = "0.17"
|
||||
tokio = {version = "1.46", features = ["rt-multi-thread", "macros", "fs"] }
|
||||
rfd = {version = "0.17", optional = true }
|
||||
tokio = {version = "1.46", features = ["rt-multi-thread", "macros", "fs", "process"] }
|
||||
spin_sleep = "1.3"
|
||||
encoding_rs = "0.8"
|
||||
mimalloc = "0.1"
|
||||
@@ -38,7 +40,7 @@ reqwest = { version = "0.13", features = ["json", "query"] }
|
||||
[build-dependencies]
|
||||
winresource = "0.1"
|
||||
bindgen = "0.72"
|
||||
slint-build = "1.12"
|
||||
slint-build = { version = "1.12", optional = true }
|
||||
cc = { version = "1.2", features = ["parallel"] }
|
||||
|
||||
[profile.release]
|
||||
@@ -58,4 +60,4 @@ identifier = "io.github.gopher64.gopher64"
|
||||
category = "public.app-category.games"
|
||||
icon = ["data/icon/gopher64_128x128.png","data/icon/gopher64_256x256.png","data/icon/gopher64_512x512.png"]
|
||||
osx_frameworks = ["/opt/homebrew/opt/molten-vk/lib/libMoltenVK.dylib","/opt/homebrew/opt/freetype/lib/libfreetype.6.dylib","/opt/homebrew/opt/libpng/lib/libpng16.16.dylib"]
|
||||
osx_minimum_system_version = "15.0"
|
||||
osx_minimum_system_version = "15.7"
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
# gopher64
|
||||
Gopher64 is a cross-platform N64 emulator. Some notable features:
|
||||
* Netplay
|
||||
* Homebrew support
|
||||
* Upscaling
|
||||
* CRT shader
|
||||
* Emulate CPU overclocking
|
||||
* Cheats
|
||||
* Savestates
|
||||
* RetroAchievements
|
||||
|
||||
## download
|
||||
|
||||
Windows: https://github.com/gopher64/gopher64/releases/latest/download/gopher64-windows-x86_64.exe
|
||||
|
||||
@@ -3,8 +3,11 @@ fn main() {
|
||||
println!("cargo::rerun-if-changed=retroachievements");
|
||||
println!("cargo::rerun-if-changed=src/compat");
|
||||
|
||||
let slint_config = slint_build::CompilerConfiguration::new();
|
||||
slint_build::compile_with_config("src/ui/gui/appwindow.slint", slint_config).unwrap();
|
||||
#[cfg(feature = "gui")]
|
||||
{
|
||||
let slint_config = slint_build::CompilerConfiguration::new();
|
||||
slint_build::compile_with_config("src/ui/gui/appwindow.slint", slint_config).unwrap();
|
||||
}
|
||||
|
||||
let mut simd_build = cc::Build::new();
|
||||
let mut volk_build = cc::Build::new();
|
||||
@@ -62,11 +65,10 @@ fn main() {
|
||||
.include("parallel-rdp/parallel-rdp-standalone/vulkan-headers/include")
|
||||
.include("parallel-rdp/parallel-rdp-standalone/util")
|
||||
.include(
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_OUT_DIR").to_owned().unwrap())
|
||||
.join("include"),
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_OUT_DIR").unwrap()).join("include"),
|
||||
)
|
||||
.include(
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_TTF_OUT_DIR").to_owned().unwrap())
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_TTF_OUT_DIR").unwrap())
|
||||
.join("include"),
|
||||
);
|
||||
|
||||
@@ -144,6 +146,9 @@ fn main() {
|
||||
println!("cargo:rustc-link-search=native={}", runtime_dir);
|
||||
println!("cargo:rustc-link-lib=static=clang_rt.osx");
|
||||
}
|
||||
if cfg!(not(feature = "gui")) && os == "linux" {
|
||||
println!("cargo:rustc-link-lib=dylib=freetype");
|
||||
}
|
||||
|
||||
volk_build.flag("-flto=thin");
|
||||
rdp_build.flag("-flto=thin");
|
||||
@@ -156,26 +161,32 @@ fn main() {
|
||||
|
||||
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
|
||||
let retroachievements_bindings = bindgen::Builder::default()
|
||||
let mut retroachievements_builder = bindgen::Builder::default()
|
||||
.header("retroachievements/retroachievements.h")
|
||||
.allowlist_function("ra_init_client")
|
||||
.allowlist_function("ra_welcome")
|
||||
.allowlist_function("ra_shutdown_client")
|
||||
.allowlist_function("ra_get_hardcore")
|
||||
.allowlist_function("ra_load_game")
|
||||
.allowlist_function("ra_unload_game")
|
||||
.allowlist_function("ra_set_dmem")
|
||||
.allowlist_function("ra_do_frame")
|
||||
.allowlist_function("ra_do_idle")
|
||||
.allowlist_function("ra_http_callback")
|
||||
.allowlist_function("ra_logout_user")
|
||||
.allowlist_function("ra_login_user")
|
||||
.allowlist_function("ra_login_token_user")
|
||||
.allowlist_function("ra_is_user_logged_in")
|
||||
.allowlist_function("ra_get_username")
|
||||
.allowlist_function("ra_get_token")
|
||||
.allowlist_function("ra_state_size")
|
||||
.allowlist_function("ra_save_state")
|
||||
.allowlist_function("ra_load_state")
|
||||
.allowlist_function("ra_load_state");
|
||||
|
||||
if cfg!(feature = "gui") {
|
||||
retroachievements_builder = retroachievements_builder
|
||||
.allowlist_function("ra_logout_user")
|
||||
.allowlist_function("ra_get_username")
|
||||
.allowlist_function("ra_get_token");
|
||||
}
|
||||
|
||||
let retroachievements_bindings = retroachievements_builder
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
@@ -199,6 +210,7 @@ fn main() {
|
||||
.allowlist_function("rdp_state_size")
|
||||
.allowlist_function("rdp_save_state")
|
||||
.allowlist_function("rdp_load_state")
|
||||
.allowlist_function("rdp_set_fps")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
@@ -265,12 +277,24 @@ fn main() {
|
||||
.compile("simd");
|
||||
}
|
||||
|
||||
let git_output = std::process::Command::new("git")
|
||||
.args(["describe", "--always", "--dirty"])
|
||||
.output()
|
||||
.unwrap();
|
||||
let mut git_output = std::process::Command::new("git");
|
||||
git_output.args(["describe", "--dirty"]);
|
||||
|
||||
let git_describe = String::from_utf8(git_output.stdout).unwrap();
|
||||
let git_describe = if let Ok(git_output) = git_output.output()
|
||||
&& git_output.status.success()
|
||||
{
|
||||
String::from_utf8(git_output.stdout).unwrap()
|
||||
} else if let Ok(git_output) = git_output.args(["--always"]).output()
|
||||
&& git_output.status.success()
|
||||
{
|
||||
format!(
|
||||
"v{}-{}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
String::from_utf8(git_output.stdout).unwrap()
|
||||
)
|
||||
} else {
|
||||
panic!("Failed to get git describe");
|
||||
};
|
||||
println!("cargo:rustc-env=GIT_DESCRIBE={git_describe}");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=NETPLAY_ID");
|
||||
|
||||
Binary file not shown.
@@ -12,7 +12,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.device.bluetooth</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -75,6 +75,11 @@ typedef struct {
|
||||
FrameBufferInfo frame_buffer_info;
|
||||
} RDP_DEVICE;
|
||||
|
||||
typedef struct {
|
||||
uint32_t fps;
|
||||
uint32_t vis;
|
||||
} FPS_DATA;
|
||||
|
||||
typedef struct {
|
||||
std::string message;
|
||||
Vulkan::ImageHandle image;
|
||||
@@ -106,6 +111,9 @@ static Vulkan::ImageHandle achievement_progress_indicator_image;
|
||||
static std::map<uint32_t, std::string> leaderboard_trackers;
|
||||
static bool display_challenge_indicator;
|
||||
|
||||
static bool display_fps;
|
||||
static Vulkan::ImageHandle fps_image;
|
||||
|
||||
#define MESSAGE_TIME 3000 // 3 seconds
|
||||
|
||||
typedef struct {
|
||||
@@ -147,6 +155,9 @@ bool sdl_event_filter(void *userdata, SDL_Event *event) {
|
||||
if (gfx_info.fullscreen)
|
||||
callback.emu_running = false;
|
||||
break;
|
||||
case SDL_SCANCODE_F1:
|
||||
display_fps = !display_fps;
|
||||
break;
|
||||
case SDL_SCANCODE_F4:
|
||||
crop_letterbox = !crop_letterbox;
|
||||
break;
|
||||
@@ -275,13 +286,17 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, const void *font,
|
||||
fragment_size = sizeof(plain_fragment_spirv);
|
||||
}
|
||||
|
||||
bool window_vsync = 0;
|
||||
wsi = new WSI;
|
||||
wsi_platform = new SDL_WSIPlatform;
|
||||
wsi_platform->set_window(window);
|
||||
wsi->set_platform(wsi_platform);
|
||||
wsi->set_present_mode(window_vsync ? PresentMode::SyncToVBlank
|
||||
: PresentMode::UnlockedMaybeTear);
|
||||
if (gfx_info.vsync) {
|
||||
// VK_PRESENT_MODE_MAILBOX_KHR, fallback to VK_PRESENT_MODE_FIFO_KHR
|
||||
wsi->set_present_mode(PresentMode::UnlockedNoTearing);
|
||||
} else {
|
||||
// VK_PRESENT_MODE_MAILBOX_KHR, fallback to VK_PRESENT_MODE_IMMEDIATE_KHR
|
||||
wsi->set_present_mode(PresentMode::UnlockedMaybeTear);
|
||||
}
|
||||
wsi->set_backbuffer_srgb(false);
|
||||
Context::SystemHandles handles = {};
|
||||
if (!::Vulkan::Context::init_loader(
|
||||
@@ -326,12 +341,15 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, const void *font,
|
||||
leaderboard_trackers.clear();
|
||||
achievement_challenge_indicator_image = Vulkan::ImageHandle();
|
||||
achievement_progress_indicator_image = Vulkan::ImageHandle();
|
||||
fps_image = Vulkan::ImageHandle();
|
||||
display_fps = false;
|
||||
}
|
||||
|
||||
void rdp_close() {
|
||||
messages = std::queue<Message>();
|
||||
achievement_challenge_indicator_image = Vulkan::ImageHandle();
|
||||
achievement_progress_indicator_image = Vulkan::ImageHandle();
|
||||
fps_image = Vulkan::ImageHandle();
|
||||
|
||||
if (wsi)
|
||||
wsi->end_frame();
|
||||
@@ -403,8 +421,7 @@ static void calculate_viewport(float *x, float *y, float *width, float *height,
|
||||
}
|
||||
|
||||
static void draw_indicator(CommandBufferHandle cmd,
|
||||
Vulkan::ImageHandle indicator_image,
|
||||
VkViewport &vp) {
|
||||
Vulkan::ImageHandle indicator_image, VkViewport vp) {
|
||||
cmd->set_texture(0, 0, indicator_image->get_view(),
|
||||
Vulkan::StockSampler::NearestClamp);
|
||||
vp.x = vp.x + vp.width - indicator_image->get_width();
|
||||
@@ -416,6 +433,17 @@ static void draw_indicator(CommandBufferHandle cmd,
|
||||
cmd->draw(3);
|
||||
}
|
||||
|
||||
static void draw_fps(CommandBufferHandle cmd, VkViewport vp) {
|
||||
cmd->set_texture(0, 0, fps_image->get_view(),
|
||||
Vulkan::StockSampler::NearestClamp);
|
||||
vp.y = vp.y + vp.height - fps_image->get_height();
|
||||
vp.height = fps_image->get_height();
|
||||
vp.width = fps_image->get_width();
|
||||
cmd->set_viewport(vp);
|
||||
|
||||
cmd->draw(3);
|
||||
}
|
||||
|
||||
static void render_frame(Vulkan::Device &device) {
|
||||
RDP::ScanoutOptions options = {};
|
||||
options.persist_frame_on_invalid_input = true;
|
||||
@@ -512,6 +540,10 @@ static void render_frame(Vulkan::Device &device) {
|
||||
display_challenge_indicator) {
|
||||
draw_indicator(cmd, achievement_challenge_indicator_image, vp);
|
||||
}
|
||||
|
||||
if (messages.empty() && display_fps && fps_image) {
|
||||
draw_fps(cmd, vp);
|
||||
}
|
||||
}
|
||||
|
||||
cmd->end_render_pass();
|
||||
@@ -817,6 +849,20 @@ uint64_t rdp_process_commands() {
|
||||
return interrupt_timer;
|
||||
}
|
||||
|
||||
void rdp_set_fps_callback(void *userdata) {
|
||||
FPS_DATA *data = (FPS_DATA *)userdata;
|
||||
fps_image = create_message_image(
|
||||
wsi->get_device(), 0, achievement_challenge_indicator_font,
|
||||
std::format("FPS: {} VI/S: {}", data->fps, data->vis).c_str());
|
||||
}
|
||||
|
||||
void rdp_set_fps(uint32_t fps, uint32_t vis) {
|
||||
if (display_fps) {
|
||||
FPS_DATA data = {fps, vis};
|
||||
SDL_RunOnMainThread(rdp_set_fps_callback, &data, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_challenge_indicator() {
|
||||
std::string message;
|
||||
for (const auto &leaderboard_tracker : leaderboard_trackers) {
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef struct {
|
||||
bool PAL;
|
||||
bool widescreen;
|
||||
bool fullscreen;
|
||||
bool vsync;
|
||||
bool integer_scaling;
|
||||
uint32_t upscale;
|
||||
bool crt;
|
||||
@@ -50,6 +51,7 @@ void rdp_check_framebuffers(uint32_t address, uint32_t length);
|
||||
size_t rdp_state_size();
|
||||
void rdp_save_state(uint8_t *state);
|
||||
void rdp_load_state(const uint8_t *state);
|
||||
void rdp_set_fps(uint32_t fps, uint32_t vis);
|
||||
|
||||
void achievement_challenge_indicator_add(const char *achievement_title);
|
||||
void achievement_challenge_indicator_remove(const char *achievement_title);
|
||||
|
||||
@@ -18,13 +18,8 @@ void notify_load_game(void *userdata);
|
||||
static rc_client_t *g_client = NULL;
|
||||
static const uint8_t *g_dmem = NULL;
|
||||
static size_t g_dmem_size = 0;
|
||||
static bool g_game_loaded = false;
|
||||
static bool g_user_logged_in = false;
|
||||
static bool g_challenge = false;
|
||||
static bool g_leaderboard = false;
|
||||
static const char *g_username = NULL;
|
||||
static const char *g_token = NULL;
|
||||
static char load_game_error_message[512];
|
||||
static rc_client_leaderboard_list_t *g_leaderboard_list = NULL;
|
||||
|
||||
static uint32_t read_memory(uint32_t address, uint8_t *buffer,
|
||||
@@ -73,41 +68,33 @@ static void login_callback(int result, const char *error_message,
|
||||
rc_client_t *client, void *userdata) {
|
||||
// If not successful, just report the error and bail.
|
||||
if (result != RC_OK) {
|
||||
snprintf(load_game_error_message, sizeof(load_game_error_message),
|
||||
"RA login failed: %s", error_message);
|
||||
if (client && !rc_client_get_userdata(client))
|
||||
rc_client_set_userdata(client, strdup(error_message));
|
||||
store_retroachievements_credentials(NULL, NULL, userdata);
|
||||
return;
|
||||
} else {
|
||||
memset(load_game_error_message, 0, sizeof(load_game_error_message));
|
||||
}
|
||||
|
||||
// Login was successful. Capture the token for future logins so we don't have
|
||||
// to store the password anywhere.
|
||||
const rc_client_user_t *user = rc_client_get_user_info(client);
|
||||
store_retroachievements_credentials(user->username, user->token, userdata);
|
||||
|
||||
g_username = user->username;
|
||||
g_token = user->token;
|
||||
g_user_logged_in = true;
|
||||
}
|
||||
|
||||
bool ra_is_user_logged_in() { return g_user_logged_in; }
|
||||
|
||||
const char *ra_get_username() { return g_username; }
|
||||
const char *ra_get_token() { return g_token; }
|
||||
|
||||
void ra_logout_user() {
|
||||
g_user_logged_in = false;
|
||||
g_username = NULL;
|
||||
g_token = NULL;
|
||||
rc_client_logout(g_client);
|
||||
const char *ra_get_username() {
|
||||
const rc_client_user_t *user = rc_client_get_user_info(g_client);
|
||||
return user ? user->username : NULL;
|
||||
}
|
||||
const char *ra_get_token() {
|
||||
const rc_client_user_t *user = rc_client_get_user_info(g_client);
|
||||
return user ? user->token : NULL;
|
||||
}
|
||||
|
||||
void ra_logout_user() { rc_client_logout(g_client); }
|
||||
|
||||
void ra_login_user(const char *username, const char *password, void *userdata) {
|
||||
// This will generate an HTTP payload and call the server_call chain above.
|
||||
// Eventually, login_callback will be called to let us know if the login was
|
||||
// successful.
|
||||
g_user_logged_in = false;
|
||||
rc_client_begin_login_with_password(g_client, username, password,
|
||||
login_callback, userdata);
|
||||
}
|
||||
@@ -117,7 +104,6 @@ void ra_login_token_user(const char *username, const char *token,
|
||||
// This is exactly the same functionality as
|
||||
// rc_client_begin_login_with_password, but uses the token captured from the
|
||||
// first login instead of a password. Note that it uses the same callback.
|
||||
g_user_logged_in = false;
|
||||
rc_client_begin_login_with_token(g_client, username, token, login_callback,
|
||||
userdata);
|
||||
}
|
||||
@@ -125,13 +111,11 @@ void ra_login_token_user(const char *username, const char *token,
|
||||
static void load_game_callback(int result, const char *error_message,
|
||||
rc_client_t *client, void *userdata) {
|
||||
if (result != RC_OK) {
|
||||
if (client && !rc_client_get_userdata(client))
|
||||
rc_client_set_userdata(client, strdup(error_message));
|
||||
rc_client_set_hardcore_enabled(client, false);
|
||||
snprintf(load_game_error_message, sizeof(load_game_error_message),
|
||||
"RA load failed: %s", error_message);
|
||||
notify_load_game(userdata);
|
||||
return;
|
||||
} else {
|
||||
memset(load_game_error_message, 0, sizeof(load_game_error_message));
|
||||
}
|
||||
|
||||
if (!rc_client_is_processing_required(client)) {
|
||||
@@ -141,20 +125,26 @@ static void load_game_callback(int result, const char *error_message,
|
||||
g_leaderboard_list = rc_client_create_leaderboard_list(
|
||||
client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
|
||||
g_game_loaded = true;
|
||||
notify_load_game(userdata);
|
||||
}
|
||||
|
||||
void ra_welcome() {
|
||||
if (strlen(load_game_error_message) > 0) {
|
||||
rdp_onscreen_message(load_game_error_message, true);
|
||||
}
|
||||
if (!g_game_loaded)
|
||||
if (!g_client)
|
||||
return;
|
||||
|
||||
char buffer[512];
|
||||
|
||||
char *error_message = rc_client_get_userdata(g_client);
|
||||
if (error_message) {
|
||||
snprintf(buffer, sizeof(buffer), "RA Error: %s", error_message);
|
||||
rdp_onscreen_message(buffer, true);
|
||||
free(error_message);
|
||||
rc_client_set_userdata(g_client, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
const rc_client_game_t *game = rc_client_get_game_info(g_client);
|
||||
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(g_client, &summary);
|
||||
|
||||
@@ -174,16 +164,19 @@ void ra_welcome() {
|
||||
}
|
||||
|
||||
void ra_load_game(const uint8_t *rom, size_t rom_size, void *userdata) {
|
||||
if (!g_user_logged_in) {
|
||||
notify_load_game(userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_NINTENDO_64, NULL,
|
||||
rom, rom_size, load_game_callback,
|
||||
userdata);
|
||||
}
|
||||
|
||||
void ra_unload_game() {
|
||||
if (g_leaderboard_list) {
|
||||
rc_client_destroy_leaderboard_list(g_leaderboard_list);
|
||||
g_leaderboard_list = NULL;
|
||||
}
|
||||
rc_client_unload_game(g_client);
|
||||
}
|
||||
|
||||
void ra_set_dmem(const uint8_t *dmem, size_t dmem_size) {
|
||||
g_dmem = dmem;
|
||||
g_dmem_size = dmem_size;
|
||||
@@ -323,8 +316,6 @@ void ra_init_client(bool hardcore, bool challenge, bool leaderboard) {
|
||||
// Create the client instance (using a global variable simplifies this
|
||||
// example)
|
||||
g_client = rc_client_create(read_memory, server_call);
|
||||
g_game_loaded = false;
|
||||
g_user_logged_in = false;
|
||||
g_dmem = NULL;
|
||||
g_dmem_size = 0;
|
||||
|
||||
@@ -339,17 +330,9 @@ void ra_init_client(bool hardcore, bool challenge, bool leaderboard) {
|
||||
g_leaderboard = leaderboard;
|
||||
}
|
||||
|
||||
bool ra_get_hardcore() {
|
||||
if (!g_user_logged_in)
|
||||
return false;
|
||||
return rc_client_get_hardcore_enabled(g_client);
|
||||
}
|
||||
bool ra_get_hardcore() { return rc_client_get_hardcore_enabled(g_client); }
|
||||
|
||||
void ra_shutdown_client() {
|
||||
if (g_leaderboard_list) {
|
||||
rc_client_destroy_leaderboard_list(g_leaderboard_list);
|
||||
g_leaderboard_list = NULL;
|
||||
}
|
||||
if (g_client) {
|
||||
// Release resources associated to the client instance
|
||||
rc_client_destroy(g_client);
|
||||
@@ -357,30 +340,16 @@ void ra_shutdown_client() {
|
||||
}
|
||||
}
|
||||
|
||||
void ra_do_frame() {
|
||||
if (!g_game_loaded)
|
||||
return;
|
||||
void ra_do_frame() { rc_client_do_frame(g_client); }
|
||||
|
||||
rc_client_do_frame(g_client);
|
||||
}
|
||||
void ra_do_idle() { rc_client_idle(g_client); }
|
||||
|
||||
void ra_do_idle() {
|
||||
if (!g_game_loaded)
|
||||
return;
|
||||
|
||||
rc_client_idle(g_client);
|
||||
}
|
||||
|
||||
size_t ra_state_size() {
|
||||
if (!g_game_loaded)
|
||||
return 0;
|
||||
|
||||
return rc_client_progress_size(g_client);
|
||||
}
|
||||
size_t ra_state_size() { return rc_client_progress_size(g_client); }
|
||||
|
||||
void ra_save_state(uint8_t *state, size_t state_size) {
|
||||
if (!g_game_loaded)
|
||||
if (!rc_client_is_game_loaded(g_client))
|
||||
return;
|
||||
|
||||
if (rc_client_serialize_progress_sized(g_client, state, state_size) !=
|
||||
RC_OK) {
|
||||
printf("RetroAchievements: Failed to serialize progress\n");
|
||||
@@ -388,8 +357,9 @@ void ra_save_state(uint8_t *state, size_t state_size) {
|
||||
}
|
||||
|
||||
void ra_load_state(const uint8_t *state, size_t state_size) {
|
||||
if (!g_game_loaded)
|
||||
if (!rc_client_is_game_loaded(g_client))
|
||||
return;
|
||||
|
||||
if (rc_client_deserialize_progress_sized(g_client, state, state_size) !=
|
||||
RC_OK) {
|
||||
printf("RetroAchievements: Failed to deserialize progress\n");
|
||||
@@ -397,7 +367,7 @@ void ra_load_state(const uint8_t *state, size_t state_size) {
|
||||
}
|
||||
|
||||
void ra_display_inprogress_achievements(void *userdata) {
|
||||
if (!g_game_loaded)
|
||||
if (!rc_client_is_game_loaded(g_client))
|
||||
return;
|
||||
|
||||
rc_client_achievement_list_t *list = rc_client_create_achievement_list(
|
||||
|
||||
@@ -7,6 +7,7 @@ void ra_shutdown_client();
|
||||
void ra_welcome();
|
||||
bool ra_get_hardcore();
|
||||
void ra_load_game(const uint8_t *rom, size_t rom_size, void *userdata);
|
||||
void ra_unload_game();
|
||||
void ra_set_dmem(const uint8_t *dmem, size_t dmem_size);
|
||||
void ra_do_frame();
|
||||
void ra_do_idle();
|
||||
@@ -16,7 +17,6 @@ void ra_logout_user();
|
||||
void ra_login_user(const char *username, const char *password, void *userdata);
|
||||
void ra_login_token_user(const char *username, const char *token,
|
||||
void *userdata);
|
||||
bool ra_is_user_logged_in();
|
||||
const char *ra_get_username();
|
||||
const char *ra_get_token();
|
||||
size_t ra_state_size();
|
||||
|
||||
+14
-6
@@ -15,16 +15,24 @@ pub struct Cheats {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone)]
|
||||
pub struct CheatData {
|
||||
pub note: String,
|
||||
pub data: Vec<String>,
|
||||
pub options: Option<std::collections::BTreeMap<String, String>>,
|
||||
}
|
||||
|
||||
pub type Cheat = std::collections::BTreeMap<String, std::collections::BTreeMap<String, CheatData>>;
|
||||
|
||||
pub fn init(
|
||||
device: &mut device::Device,
|
||||
cheat_settings: std::collections::HashMap<String, Option<String>>,
|
||||
) {
|
||||
let cheats =
|
||||
serde_json::from_slice::<ui::cheats::Cheats>(include_bytes!("../data/cheats.json"))
|
||||
.unwrap()
|
||||
.get(&ui::storage::get_game_crc(&device.cart.rom))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let cheats = serde_json::from_slice::<Cheat>(include_bytes!("../data/cheats.json"))
|
||||
.unwrap()
|
||||
.get(&ui::storage::get_game_crc(&device.cart.rom))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let re = regex::Regex::new(r"\?+").unwrap();
|
||||
for cheat_setting in cheat_settings.iter() {
|
||||
|
||||
+15
-7
@@ -44,7 +44,7 @@ pub mod tlb;
|
||||
pub mod unmapped;
|
||||
pub mod vi;
|
||||
|
||||
pub fn run_game(device: &mut Device, rom_contents: &[u8], game_settings: ui::gui::GameSettings) {
|
||||
pub fn run_game(device: &mut Device, rom_contents: &[u8], game_settings: ui::GameSettings) {
|
||||
device.cpu.overclock = game_settings.overclock;
|
||||
if game_settings.disable_expansion_pak {
|
||||
device.rdram.size = 0x400000;
|
||||
@@ -89,8 +89,9 @@ pub fn run_game(device: &mut Device, rom_contents: &[u8], game_settings: ui::gui
|
||||
|
||||
cpu::run(device);
|
||||
|
||||
retroachievements::unload_game();
|
||||
|
||||
ui::storage::write_saves(device);
|
||||
retroachievements::shutdown_client();
|
||||
ui::input::close(&mut device.ui);
|
||||
ui::audio::close(&mut device.ui);
|
||||
ui::video::close(&device.ui);
|
||||
@@ -140,7 +141,11 @@ fn swap_rom(contents: Vec<u8>) -> Option<Vec<u8>> {
|
||||
|
||||
pub fn get_rom_contents(file_path: &std::path::Path) -> Option<Vec<u8>> {
|
||||
let mut contents = vec![];
|
||||
if file_path.extension().unwrap().eq_ignore_ascii_case("zip") {
|
||||
if file_path
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.eq_ignore_ascii_case("zip")
|
||||
{
|
||||
let zip_file = fs::File::open(file_path).unwrap();
|
||||
let mut archive = zip::ZipArchive::new(zip_file).unwrap();
|
||||
for i in 0..archive.len() {
|
||||
@@ -157,7 +162,11 @@ pub fn get_rom_contents(file_path: &std::path::Path) -> Option<Vec<u8>> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if file_path.extension().unwrap().eq_ignore_ascii_case("7z") {
|
||||
} else if file_path
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.eq_ignore_ascii_case("7z")
|
||||
{
|
||||
let mut archive =
|
||||
sevenz_rust2::ArchiveReader::open(file_path, sevenz_rust2::Password::empty()).unwrap();
|
||||
|
||||
@@ -182,7 +191,8 @@ pub fn get_rom_contents(file_path: &std::path::Path) -> Option<Vec<u8>> {
|
||||
)
|
||||
.expect("ok");
|
||||
} else {
|
||||
contents = fs::read(file_path).expect("Should have been able to read the file");
|
||||
contents = fs::read(file_path)
|
||||
.unwrap_or_else(|_| panic!("Could not read ROM file: {}", file_path.display()));
|
||||
}
|
||||
|
||||
if contents.is_empty() {
|
||||
@@ -482,8 +492,6 @@ impl Device {
|
||||
enable_speed_limiter: true,
|
||||
next_pace_deadline: None,
|
||||
vi_counter: 0,
|
||||
last_origin: 0,
|
||||
internal_frame_counter: 0,
|
||||
min_wait_time: std::time::Duration::from_secs(1),
|
||||
frame_time: 0.0,
|
||||
limit_freq: 2,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::{device, ui};
|
||||
use crate::device;
|
||||
#[cfg(feature = "gui")]
|
||||
use crate::ui;
|
||||
use std::collections::HashMap;
|
||||
|
||||
const JCMD_VRU_READ: u8 = 0x09;
|
||||
@@ -179,7 +181,10 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = 0;
|
||||
}
|
||||
JCMD_VRU_READ => {
|
||||
#[cfg(feature = "gui")]
|
||||
let index = ui::vru::prompt_for_match(&device.vru.words, device.vi.frame_time);
|
||||
#[cfg(not(feature = "gui"))]
|
||||
let index = 0x7FFF;
|
||||
let num_results = if index == 0x7FFF { 0 } else { 1 };
|
||||
let data: HashMap<usize, u16> = HashMap::from([
|
||||
(0, 0x8000),
|
||||
|
||||
+4
-13
@@ -27,8 +27,6 @@ pub struct Vi {
|
||||
pub count_per_scanline: u64,
|
||||
pub enable_speed_limiter: bool,
|
||||
pub vi_counter: u64,
|
||||
pub last_origin: u32,
|
||||
pub internal_frame_counter: u64,
|
||||
pub min_wait_time: std::time::Duration,
|
||||
pub frame_time: f64,
|
||||
pub limit_freq: u64,
|
||||
@@ -110,10 +108,10 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
}
|
||||
}
|
||||
VI_ORIGIN_REG => {
|
||||
let current_origin = device.vi.regs[reg as usize];
|
||||
device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask);
|
||||
if device.vi.regs[reg as usize] != device.vi.last_origin {
|
||||
device.vi.last_origin = device.vi.regs[reg as usize];
|
||||
device.vi.internal_frame_counter += 1;
|
||||
if current_origin != device.vi.regs[reg as usize] {
|
||||
let _ = device.ui.video.fps_tx.try_send(true);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -129,6 +127,7 @@ pub fn vertical_interrupt_event(device: &mut device::Device) {
|
||||
}
|
||||
|
||||
ui::video::render_frame();
|
||||
let _ = device.ui.video.vis_tx.try_send(true);
|
||||
|
||||
retroachievements::do_frame();
|
||||
|
||||
@@ -159,14 +158,6 @@ pub fn vertical_interrupt_event(device: &mut device::Device) {
|
||||
|
||||
unsafe { sdl3_sys::events::SDL_PumpEvents() };
|
||||
|
||||
/*
|
||||
let vis = if device.cart.pal { 50 } else { 60 };
|
||||
if device.vi.vi_counter % vis == 0 {
|
||||
println!("FPS: {}", device.vi.internal_frame_counter);
|
||||
device.vi.internal_frame_counter = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/* toggle vi field if in interlaced mode */
|
||||
device.vi.field ^= (device.vi.regs[VI_STATUS_REG as usize] >> 6) & 0x1;
|
||||
|
||||
|
||||
+46
-29
@@ -11,11 +11,10 @@ mod savestates;
|
||||
mod ui;
|
||||
use clap::Parser;
|
||||
use std::io::Error;
|
||||
use ui::gui;
|
||||
|
||||
/// N64 emulator
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(author, version=env!("GIT_DESCRIBE"), about, long_about = None, arg_required_else_help = if cfg!(feature = "gui") { false } else { true })]
|
||||
struct Args {
|
||||
game: Option<String>,
|
||||
#[arg(short, long)]
|
||||
@@ -30,10 +29,6 @@ struct Args {
|
||||
netplay_peer_addr: Option<String>,
|
||||
#[arg(long, value_name = "NETPLAY_PLAYER_NUMBER", hide = true)]
|
||||
netplay_player_number: Option<u8>,
|
||||
#[arg(long, value_name = "GB_ROM_PATH", hide = true)]
|
||||
gb_rom: Option<Vec<String>>,
|
||||
#[arg(long, value_name = "GB_RAM_PATH", hide = true)]
|
||||
gb_ram: Option<Vec<String>>,
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "PROFILE_NAME",
|
||||
@@ -143,6 +138,11 @@ async fn main() -> std::io::Result<()> {
|
||||
}
|
||||
|
||||
let mut device = device::Device::new();
|
||||
|
||||
device.ui.config.recent_roms.retain(|x| *x != game);
|
||||
device.ui.config.recent_roms.insert(0, game);
|
||||
device.ui.config.recent_roms.truncate(5);
|
||||
|
||||
if args.fullscreen {
|
||||
device.ui.video.fullscreen = true;
|
||||
} else {
|
||||
@@ -168,15 +168,6 @@ async fn main() -> std::io::Result<()> {
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
if cfg!(ra_hardcore_enabled) {
|
||||
retroachievements::init_client(
|
||||
args.ra_hardcore,
|
||||
args.ra_challenge,
|
||||
args.ra_leaderboard,
|
||||
);
|
||||
} else {
|
||||
retroachievements::init_client(false, args.ra_challenge, args.ra_leaderboard);
|
||||
}
|
||||
let mut shutdown_tx = None;
|
||||
|
||||
if let Some(peer_addr) = args.netplay_peer_addr
|
||||
@@ -184,16 +175,15 @@ async fn main() -> std::io::Result<()> {
|
||||
{
|
||||
device.netplay = Some(netplay::init(peer_addr.parse().unwrap(), player_number));
|
||||
} else {
|
||||
if let Some(gb_roms) = args.gb_rom
|
||||
&& let Some(gb_rams) = args.gb_ram
|
||||
{
|
||||
for i in 0..4 {
|
||||
if let Some(gb_rom) = gb_roms.get(i)
|
||||
&& let Some(gb_ram) = gb_rams.get(i)
|
||||
{
|
||||
device.transferpaks[i].cart.rom = std::fs::read(gb_rom).unwrap();
|
||||
device.transferpaks[i].cart.ram = std::fs::read(gb_ram).unwrap();
|
||||
}
|
||||
for i in 0..4 {
|
||||
if device.ui.config.input.transfer_pak[i]
|
||||
&& !device.ui.config.input.gb_rom_path[i].is_empty()
|
||||
&& !device.ui.config.input.gb_ram_path[i].is_empty()
|
||||
{
|
||||
device.transferpaks[i].cart.rom =
|
||||
std::fs::read(&device.ui.config.input.gb_rom_path[i]).unwrap();
|
||||
device.transferpaks[i].cart.ram =
|
||||
std::fs::read(&device.ui.config.input.gb_ram_path[i]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +192,16 @@ async fn main() -> std::io::Result<()> {
|
||||
}
|
||||
|
||||
if let Some(username) = args.ra_username {
|
||||
retroachievements::init_client(
|
||||
if cfg!(ra_hardcore_enabled) {
|
||||
args.ra_hardcore
|
||||
} else {
|
||||
false
|
||||
},
|
||||
args.ra_challenge,
|
||||
args.ra_leaderboard,
|
||||
);
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<bool>();
|
||||
if let Some(password) = args.ra_password {
|
||||
retroachievements::login_user(username, password, tx);
|
||||
@@ -219,16 +219,30 @@ async fn main() -> std::io::Result<()> {
|
||||
device::run_game(
|
||||
&mut device,
|
||||
&rom_contents,
|
||||
ui::gui::GameSettings {
|
||||
ui::GameSettings {
|
||||
overclock,
|
||||
disable_expansion_pak,
|
||||
cheats,
|
||||
load_savestate_slot: args.load_state,
|
||||
},
|
||||
);
|
||||
retroachievements::shutdown_client();
|
||||
|
||||
if device.netplay.is_some() {
|
||||
netplay::close(&mut device);
|
||||
} else {
|
||||
for i in 0..4 {
|
||||
if device.ui.config.input.transfer_pak[i]
|
||||
&& !device.ui.config.input.gb_ram_path[i].is_empty()
|
||||
{
|
||||
tokio::fs::write(
|
||||
&device.ui.config.input.gb_ram_path[i],
|
||||
&device.transferpaks[i].cart.ram,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(shutdown_tx) = &shutdown_tx {
|
||||
ui::usb::close(shutdown_tx);
|
||||
@@ -274,9 +288,12 @@ async fn main() -> std::io::Result<()> {
|
||||
ui::input::bind_input_profile(&mut ui, profile, port);
|
||||
}
|
||||
} else {
|
||||
retroachievements::init_client(false, false, false);
|
||||
gui::app_window();
|
||||
retroachievements::shutdown_client();
|
||||
#[cfg(feature = "gui")]
|
||||
{
|
||||
retroachievements::init_client(false, false, false);
|
||||
ui::gui::app_window();
|
||||
retroachievements::shutdown_client();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ pub fn send_save(netplay: &mut Netplay, save_type: &str, save_data: &[u8], size:
|
||||
request.extend_from_slice(&(size as u32).to_be_bytes());
|
||||
|
||||
if size > 0 {
|
||||
request.extend(save_data.to_owned());
|
||||
request.extend_from_slice(save_data);
|
||||
}
|
||||
netplay.tcp_stream.write_all(&request).unwrap();
|
||||
}
|
||||
|
||||
+27
-124
@@ -1,7 +1,6 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/retroachievements_bindings.rs"));
|
||||
|
||||
use crate::ui;
|
||||
use slint::ComponentHandle;
|
||||
|
||||
static WEB_CLIENT: std::sync::LazyLock<reqwest::Client> = std::sync::LazyLock::new(|| {
|
||||
reqwest::Client::builder()
|
||||
@@ -144,115 +143,6 @@ pub extern "C" fn rust_server_call(
|
||||
});
|
||||
}
|
||||
|
||||
fn set_current_user_message(app: &ui::gui::AppWindow, rx: tokio::sync::oneshot::Receiver<bool>) {
|
||||
app.set_ra_current_user_message("Logging in...".into());
|
||||
let weak_app = app.as_weak();
|
||||
tokio::spawn(async move {
|
||||
rx.await.unwrap();
|
||||
weak_app
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
if is_user_logged_in() {
|
||||
handle.set_ra_current_user_message(
|
||||
format!("Logged in as {}", handle.get_ra_username()).into(),
|
||||
);
|
||||
handle.set_ra_show_profile(true);
|
||||
} else {
|
||||
handle.set_ra_current_user_message("Login failed".into());
|
||||
handle.set_ra_show_profile(false);
|
||||
}
|
||||
handle.set_ra_logging_in(false);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn ra_window(app: &ui::gui::AppWindow) {
|
||||
let mut token = String::new();
|
||||
if let Ok(ra_config) = std::fs::read(ui::get_dirs().config_dir.join("retroachievements.json"))
|
||||
&& let Ok(result) = serde_json::from_slice::<RAConfig>(ra_config.as_ref())
|
||||
{
|
||||
app.set_ra_username(result.username.into());
|
||||
app.set_ra_enabled(result.enabled);
|
||||
app.set_ra_hardcore(result.hardcore);
|
||||
app.set_ra_challenge(result.challenge);
|
||||
app.set_ra_leaderboard(result.leaderboard);
|
||||
token = result.token;
|
||||
} else {
|
||||
app.set_ra_hardcore(true);
|
||||
}
|
||||
|
||||
if app.get_ra_enabled() && !app.get_ra_username().is_empty() {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<bool>();
|
||||
login_token_user(app.get_ra_username().to_string(), token, tx);
|
||||
set_current_user_message(app, rx);
|
||||
} else {
|
||||
app.set_ra_current_user_message("Not currently logged in".into());
|
||||
}
|
||||
|
||||
let weak_app2 = app.as_weak();
|
||||
app.on_ra_button_clicked(move |password| {
|
||||
weak_app2
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<bool>();
|
||||
login_user(
|
||||
handle.get_ra_username().to_string(),
|
||||
password.to_string(),
|
||||
tx,
|
||||
);
|
||||
|
||||
set_current_user_message(&handle, rx);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
app.on_ra_toggled(move |enabled, hardcore, challenge, leaderboard| {
|
||||
let file_path = ui::get_dirs().config_dir.join("retroachievements.json");
|
||||
let raconfig = if let Ok(ra_config) = std::fs::read(&file_path)
|
||||
&& let Ok(result) = serde_json::from_slice::<RAConfig>(ra_config.as_ref())
|
||||
{
|
||||
if !enabled {
|
||||
unsafe { ra_logout_user() };
|
||||
RAConfig {
|
||||
username: "".into(),
|
||||
token: "".into(),
|
||||
enabled,
|
||||
hardcore,
|
||||
challenge,
|
||||
leaderboard,
|
||||
}
|
||||
} else {
|
||||
RAConfig {
|
||||
username: result.username,
|
||||
token: result.token,
|
||||
enabled,
|
||||
hardcore,
|
||||
challenge,
|
||||
leaderboard,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RAConfig {
|
||||
username: "".into(),
|
||||
token: "".into(),
|
||||
enabled,
|
||||
hardcore,
|
||||
challenge,
|
||||
leaderboard,
|
||||
}
|
||||
};
|
||||
let f = std::fs::File::create(&file_path).unwrap();
|
||||
serde_json::to_writer_pretty(f, &raconfig).unwrap();
|
||||
});
|
||||
|
||||
app.on_ra_games_clicked(move || {
|
||||
open::that_detached("https://retroachievements.org/system/2-nintendo-64/games").unwrap();
|
||||
});
|
||||
|
||||
app.on_ra_show_profile_clicked(move |username| {
|
||||
open::that_detached(format!("https://retroachievements.org/user/{}", username)).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn load_game(rom: &[u8], rom_size: usize) {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<bool>();
|
||||
unsafe {
|
||||
@@ -262,6 +152,10 @@ pub async fn load_game(rom: &[u8], rom_size: usize) {
|
||||
rx.await.unwrap();
|
||||
}
|
||||
|
||||
pub fn unload_game() {
|
||||
unsafe { ra_unload_game() };
|
||||
}
|
||||
|
||||
pub fn welcome() {
|
||||
unsafe { ra_welcome() };
|
||||
}
|
||||
@@ -299,6 +193,11 @@ pub fn login_user(username: String, password: String, tx: tokio::sync::oneshot::
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "gui")]
|
||||
pub fn logout_user() {
|
||||
unsafe { ra_logout_user() };
|
||||
}
|
||||
|
||||
pub fn login_token_user(username: String, token: String, tx: tokio::sync::oneshot::Sender<bool>) {
|
||||
unsafe {
|
||||
let tx_ptr = Box::into_raw(Box::new(tx)) as *mut std::ffi::c_void;
|
||||
@@ -308,29 +207,33 @@ pub fn login_token_user(username: String, token: String, tx: tokio::sync::onesho
|
||||
};
|
||||
}
|
||||
|
||||
pub fn is_user_logged_in() -> bool {
|
||||
unsafe { ra_is_user_logged_in() }
|
||||
}
|
||||
|
||||
pub fn get_username() -> &'static str {
|
||||
#[cfg(feature = "gui")]
|
||||
pub fn get_username() -> Option<String> {
|
||||
let c_username = unsafe { ra_get_username() };
|
||||
if c_username.is_null() {
|
||||
"unknown"
|
||||
None
|
||||
} else {
|
||||
unsafe { std::ffi::CStr::from_ptr(c_username) }
|
||||
.to_str()
|
||||
.unwrap()
|
||||
Some(
|
||||
unsafe { std::ffi::CStr::from_ptr(c_username) }
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_token() -> &'static str {
|
||||
#[cfg(feature = "gui")]
|
||||
pub fn get_token() -> Option<String> {
|
||||
let c_token = unsafe { ra_get_token() };
|
||||
if c_token.is_null() {
|
||||
"unknown"
|
||||
None
|
||||
} else {
|
||||
unsafe { std::ffi::CStr::from_ptr(c_token) }
|
||||
.to_str()
|
||||
.unwrap()
|
||||
Some(
|
||||
unsafe { std::ffi::CStr::from_ptr(c_token) }
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
pub mod audio;
|
||||
#[cfg(feature = "gui")]
|
||||
pub mod cheats;
|
||||
pub mod config;
|
||||
#[cfg(feature = "gui")]
|
||||
pub mod gui;
|
||||
pub mod input;
|
||||
#[cfg(feature = "gui")]
|
||||
pub mod netplay;
|
||||
#[cfg(feature = "gui")]
|
||||
pub mod retroachievements;
|
||||
pub mod storage;
|
||||
pub mod usb;
|
||||
pub mod video;
|
||||
#[cfg(feature = "gui")]
|
||||
pub mod vru;
|
||||
|
||||
pub struct Dirs {
|
||||
@@ -36,6 +42,10 @@ pub struct Storage {
|
||||
pub struct Video {
|
||||
pub window: *mut sdl3_sys::video::SDL_Window,
|
||||
pub fullscreen: bool,
|
||||
pub fps_tx: tokio::sync::mpsc::Sender<bool>,
|
||||
pub fps_rx: Option<tokio::sync::mpsc::Receiver<bool>>,
|
||||
pub vis_tx: tokio::sync::mpsc::Sender<bool>,
|
||||
pub vis_rx: Option<tokio::sync::mpsc::Receiver<bool>>,
|
||||
}
|
||||
|
||||
pub struct Usb {
|
||||
@@ -43,6 +53,14 @@ pub struct Usb {
|
||||
pub cart_rx: Option<tokio::sync::broadcast::Receiver<usb::UsbData>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GameSettings {
|
||||
pub overclock: bool,
|
||||
pub disable_expansion_pak: bool,
|
||||
pub cheats: std::collections::HashMap<String, Option<String>>,
|
||||
pub load_savestate_slot: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct Ui {
|
||||
pub dirs: Dirs,
|
||||
pub config: config::Config,
|
||||
@@ -113,6 +131,8 @@ impl Ui {
|
||||
fn construct_ui(joysticks: Vec<sdl3_sys::everything::SDL_JoystickID>, with_sdl: bool) -> Ui {
|
||||
let dirs = get_dirs();
|
||||
|
||||
let (fps_tx, fps_rx) = tokio::sync::mpsc::channel(1000);
|
||||
let (vis_tx, vis_rx) = tokio::sync::mpsc::channel(1000);
|
||||
Ui {
|
||||
input: Input {
|
||||
controllers: [
|
||||
@@ -190,6 +210,10 @@ impl Ui {
|
||||
video: Video {
|
||||
window: std::ptr::null_mut(),
|
||||
fullscreen: false,
|
||||
fps_tx,
|
||||
fps_rx: Some(fps_rx),
|
||||
vis_tx,
|
||||
vis_rx: Some(vis_rx),
|
||||
},
|
||||
usb: Usb {
|
||||
usb_tx: None,
|
||||
|
||||
+7
-32
@@ -1,18 +1,10 @@
|
||||
use crate::cheats;
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
use crate::ui::gui::AppWindow;
|
||||
use crate::ui::gui::ErrorDialog;
|
||||
use slint::ComponentHandle;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone)]
|
||||
pub struct CheatData {
|
||||
note: String,
|
||||
pub data: Vec<String>,
|
||||
pub options: Option<std::collections::BTreeMap<String, String>>,
|
||||
}
|
||||
|
||||
pub type Cheats = std::collections::BTreeMap<String, std::collections::BTreeMap<String, CheatData>>;
|
||||
|
||||
pub fn cheats_window(app: &AppWindow) {
|
||||
let weak = app.as_weak();
|
||||
app.on_cheats_select_rom_clicked(move |rom_dir| {
|
||||
@@ -38,7 +30,7 @@ pub fn cheats_window(app: &AppWindow) {
|
||||
handle.set_cheat_game_name(game_name.into());
|
||||
})
|
||||
.unwrap();
|
||||
let cheats: Cheats =
|
||||
let cheats: cheats::Cheat =
|
||||
serde_json::from_slice(include_bytes!("../../data/cheats.json")).unwrap();
|
||||
if let Some(cheat) = cheats.get(&game_crc) {
|
||||
let cheat = cheat.clone();
|
||||
@@ -77,16 +69,7 @@ pub fn cheats_window(app: &AppWindow) {
|
||||
options,
|
||||
));
|
||||
}
|
||||
#[allow(clippy::type_complexity)]
|
||||
let cheats_model: std::rc::Rc<
|
||||
slint::VecModel<(
|
||||
slint::SharedString,
|
||||
bool,
|
||||
slint::SharedString,
|
||||
slint::ModelRc<(bool, slint::SharedString)>,
|
||||
)>,
|
||||
> = std::rc::Rc::new(cheats_vec);
|
||||
handle.set_cheats(slint::ModelRc::from(cheats_model));
|
||||
handle.set_cheats(slint::ModelRc::from(std::rc::Rc::new(cheats_vec)));
|
||||
})
|
||||
.unwrap();
|
||||
} else {
|
||||
@@ -137,19 +120,11 @@ pub fn cheats_window(app: &AppWindow) {
|
||||
|
||||
fn clear_cheats(weak: &slint::Weak<AppWindow>, clear_name: bool) {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
let cheats_vec = slint::VecModel::default();
|
||||
#[allow(clippy::type_complexity)]
|
||||
let cheats_model: std::rc::Rc<
|
||||
slint::VecModel<(
|
||||
slint::SharedString,
|
||||
bool,
|
||||
slint::SharedString,
|
||||
slint::ModelRc<(bool, slint::SharedString)>,
|
||||
)>,
|
||||
> = std::rc::Rc::new(cheats_vec);
|
||||
handle.set_cheats(slint::ModelRc::from(cheats_model));
|
||||
handle.set_cheats(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::default(),
|
||||
)));
|
||||
if clear_name {
|
||||
handle.set_cheat_game_name("".into());
|
||||
handle.set_cheat_game_name(String::new().into());
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -39,6 +39,8 @@ pub struct Input {
|
||||
pub controller_assignment: [Option<String>; 4],
|
||||
pub controller_enabled: [bool; 4],
|
||||
pub transfer_pak: [bool; 4],
|
||||
pub gb_rom_path: [String; 4],
|
||||
pub gb_ram_path: [String; 4],
|
||||
pub emulate_vru: bool,
|
||||
}
|
||||
|
||||
@@ -48,6 +50,7 @@ pub struct Video {
|
||||
pub integer_scaling: bool,
|
||||
pub fullscreen: bool,
|
||||
pub widescreen: bool,
|
||||
pub vsync: bool,
|
||||
pub crt: bool,
|
||||
}
|
||||
|
||||
@@ -70,6 +73,7 @@ pub struct Config {
|
||||
pub video: Video,
|
||||
pub emulation: Emulation,
|
||||
pub rom_dir: std::path::PathBuf,
|
||||
pub recent_roms: Vec<String>,
|
||||
}
|
||||
|
||||
impl Drop for Cheats {
|
||||
@@ -140,6 +144,8 @@ impl Config {
|
||||
input_profiles,
|
||||
controller_enabled: [true, false, false, false],
|
||||
transfer_pak: [false, false, false, false],
|
||||
gb_rom_path: [String::new(), String::new(), String::new(), String::new()],
|
||||
gb_ram_path: [String::new(), String::new(), String::new(), String::new()],
|
||||
emulate_vru: false,
|
||||
},
|
||||
video: Video {
|
||||
@@ -147,6 +153,7 @@ impl Config {
|
||||
integer_scaling: false,
|
||||
fullscreen: false,
|
||||
widescreen: false,
|
||||
vsync: true,
|
||||
crt: false,
|
||||
},
|
||||
emulation: Emulation {
|
||||
@@ -155,6 +162,7 @@ impl Config {
|
||||
usb: false,
|
||||
},
|
||||
rom_dir: std::path::PathBuf::new(),
|
||||
recent_roms: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+236
-120
@@ -1,6 +1,7 @@
|
||||
use crate::retroachievements;
|
||||
use crate::ui;
|
||||
use slint::Model;
|
||||
use slint::winit_030::WinitWindowAccessor;
|
||||
|
||||
slint::include_modules!();
|
||||
|
||||
@@ -18,19 +19,6 @@ pub struct NetplayDevice {
|
||||
pub player_number: u8,
|
||||
}
|
||||
|
||||
pub struct GbPaths {
|
||||
pub rom: [Option<std::path::PathBuf>; 4],
|
||||
pub ram: [Option<std::path::PathBuf>; 4],
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GameSettings {
|
||||
pub overclock: bool,
|
||||
pub disable_expansion_pak: bool,
|
||||
pub cheats: std::collections::HashMap<String, Option<String>>,
|
||||
pub load_savestate_slot: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RASettings {
|
||||
pub enabled: bool,
|
||||
@@ -66,12 +54,92 @@ fn check_latest_version(weak: slint::Weak<AppWindow>) {
|
||||
});
|
||||
}
|
||||
|
||||
fn local_game_window(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
let dirs = ui::get_dirs();
|
||||
fn run_with_path(
|
||||
weak: slint::Weak<AppWindow>,
|
||||
path: std::path::PathBuf,
|
||||
controller_paths: &[Option<String>],
|
||||
) {
|
||||
let controller_paths = controller_paths.to_owned();
|
||||
let weak2 = weak.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
if handle.get_game_running() {
|
||||
return;
|
||||
}
|
||||
save_settings(&handle, &controller_paths);
|
||||
|
||||
run_rom(
|
||||
path,
|
||||
ui::GameSettings {
|
||||
overclock: handle.get_overclock_n64_cpu(),
|
||||
disable_expansion_pak: handle.get_disable_expansion_pak(),
|
||||
cheats: std::collections::HashMap::new(), // will be filled in later
|
||||
load_savestate_slot: None,
|
||||
},
|
||||
None,
|
||||
RASettings {
|
||||
enabled: handle.get_ra_enabled(),
|
||||
hardcore: handle.get_ra_hardcore(),
|
||||
challenge: handle.get_ra_challenge(),
|
||||
leaderboard: handle.get_ra_leaderboard(),
|
||||
},
|
||||
weak2,
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn file_dropped(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
let weak = app.as_weak();
|
||||
let controller_paths = controller_paths.to_owned();
|
||||
app.window()
|
||||
.on_winit_window_event(move |_winit_window, event| {
|
||||
if let slint::winit_030::winit::event::WindowEvent::DroppedFile(path) = event {
|
||||
run_with_path(weak.clone(), path.to_path_buf(), &controller_paths);
|
||||
}
|
||||
slint::winit_030::EventResult::Propagate
|
||||
});
|
||||
}
|
||||
|
||||
fn local_game_window(
|
||||
app: &AppWindow,
|
||||
config: &ui::config::Config,
|
||||
controller_paths: &[Option<String>],
|
||||
) {
|
||||
let dirs = ui::get_dirs();
|
||||
|
||||
app.set_recent_roms(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
config
|
||||
.recent_roms
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
if let Ok(exists) = std::fs::exists(x)
|
||||
&& exists
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|x| {
|
||||
(
|
||||
x.into(),
|
||||
std::path::Path::new(x)
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(slint::SharedString, slint::SharedString)>>(),
|
||||
),
|
||||
)));
|
||||
|
||||
let weak = app.as_weak();
|
||||
let owned_controller_paths = controller_paths.to_owned();
|
||||
app.on_open_rom_button_clicked(move || {
|
||||
let controller_paths = controller_paths.clone();
|
||||
let controller_paths = owned_controller_paths.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
save_settings(&handle, &controller_paths);
|
||||
open_rom(&handle)
|
||||
@@ -79,10 +147,25 @@ fn local_game_window(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let weak = app.as_weak();
|
||||
let owned_controller_paths = controller_paths.to_owned();
|
||||
app.on_recent_rom_button_clicked(move |rom| {
|
||||
let controller_paths = owned_controller_paths.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
run_with_path(
|
||||
handle.as_weak(),
|
||||
std::path::PathBuf::from(rom.to_string()),
|
||||
&controller_paths,
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let saves_path = dirs.data_dir.join("saves");
|
||||
app.on_saves_folder_button_clicked(move || {
|
||||
open::that_detached(saves_path.clone()).unwrap();
|
||||
});
|
||||
file_dropped(app, controller_paths);
|
||||
}
|
||||
|
||||
fn input_profiles(config: &ui::config::Config) -> Vec<String> {
|
||||
@@ -97,6 +180,7 @@ fn settings_window(app: &AppWindow, config: &ui::config::Config) {
|
||||
app.set_integer_scaling(config.video.integer_scaling);
|
||||
app.set_fullscreen(config.video.fullscreen);
|
||||
app.set_widescreen(config.video.widescreen);
|
||||
app.set_vsync(config.video.vsync);
|
||||
app.set_apply_crt_shader(config.video.crt);
|
||||
app.set_overclock_n64_cpu(config.emulation.overclock);
|
||||
app.set_disable_expansion_pak(config.emulation.disable_expansion_pak);
|
||||
@@ -133,17 +217,17 @@ fn update_input_profiles(weak: &slint::Weak<AppWindow>, config: &ui::config::Con
|
||||
profile_bindings.push(position.unwrap() as i32);
|
||||
}
|
||||
|
||||
let input_profiles = slint::VecModel::default();
|
||||
for profile in profiles {
|
||||
input_profiles.push(profile.into());
|
||||
}
|
||||
let input_profiles_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(input_profiles);
|
||||
handle.set_input_profiles(slint::ModelRc::from(input_profiles_model));
|
||||
handle.set_input_profiles(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
profiles
|
||||
.iter()
|
||||
.map(|x| x.into())
|
||||
.collect::<Vec<slint::SharedString>>(),
|
||||
),
|
||||
)));
|
||||
|
||||
let input_profile_binding_model: std::rc::Rc<slint::VecModel<i32>> =
|
||||
std::rc::Rc::new(profile_bindings);
|
||||
handle.set_selected_profile_binding(slint::ModelRc::from(input_profile_binding_model));
|
||||
handle
|
||||
.set_selected_profile_binding(slint::ModelRc::from(std::rc::Rc::new(profile_bindings)));
|
||||
|
||||
// this is a workaround to make the input profile combobox update
|
||||
handle.set_blank_profiles(true);
|
||||
@@ -161,53 +245,65 @@ fn update_input_profiles(weak: &slint::Weak<AppWindow>, config: &ui::config::Con
|
||||
fn controller_window(
|
||||
app: &AppWindow,
|
||||
config: &ui::config::Config,
|
||||
controller_names: &Vec<String>,
|
||||
controller_names: &[String],
|
||||
controller_paths: &[Option<String>],
|
||||
) {
|
||||
let controller_enabled_model: std::rc::Rc<slint::VecModel<bool>> = std::rc::Rc::new(
|
||||
slint::VecModel::from(config.input.controller_enabled.to_vec()),
|
||||
);
|
||||
app.set_emulate_vru(config.input.emulate_vru);
|
||||
|
||||
app.set_controller_enabled(slint::ModelRc::from(controller_enabled_model));
|
||||
app.set_controller_enabled(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(config.input.controller_enabled.to_vec()),
|
||||
)));
|
||||
|
||||
let transferpak_enabled_model: std::rc::Rc<slint::VecModel<bool>> =
|
||||
std::rc::Rc::new(slint::VecModel::from(config.input.transfer_pak.to_vec()));
|
||||
app.set_transferpak(slint::ModelRc::from(transferpak_enabled_model));
|
||||
app.set_transferpak(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(config.input.transfer_pak.to_vec()),
|
||||
)));
|
||||
|
||||
app.set_gb_rom_paths(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
config
|
||||
.input
|
||||
.gb_rom_path
|
||||
.iter()
|
||||
.map(|x| x.into())
|
||||
.collect::<Vec<slint::SharedString>>(),
|
||||
),
|
||||
)));
|
||||
|
||||
app.set_gb_ram_paths(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
config
|
||||
.input
|
||||
.gb_ram_path
|
||||
.iter()
|
||||
.map(|x| x.into())
|
||||
.collect::<Vec<slint::SharedString>>(),
|
||||
),
|
||||
)));
|
||||
|
||||
update_input_profiles(&app.as_weak(), config);
|
||||
|
||||
let controllers = slint::VecModel::default();
|
||||
for controller in controller_names {
|
||||
controllers.push(controller.into());
|
||||
}
|
||||
let controller_names_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(controllers);
|
||||
app.set_controller_names(slint::ModelRc::from(controller_names_model));
|
||||
app.set_controller_names(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
controller_names
|
||||
.iter()
|
||||
.map(|x| x.into())
|
||||
.collect::<Vec<slint::SharedString>>(),
|
||||
),
|
||||
)));
|
||||
|
||||
let controller_changed = slint::VecModel::default();
|
||||
let selected_controllers = slint::VecModel::default();
|
||||
for selected in config.input.controller_assignment.iter() {
|
||||
let mut found = false;
|
||||
for (i, path) in controller_paths.iter().enumerate() {
|
||||
if selected == path {
|
||||
selected_controllers.push(i as i32);
|
||||
found = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
selected_controllers.push(0);
|
||||
}
|
||||
let selected_index = controller_paths
|
||||
.iter()
|
||||
.position(|path| selected == path)
|
||||
.unwrap_or(0) as i32;
|
||||
selected_controllers.push(selected_index);
|
||||
controller_changed.push(false);
|
||||
}
|
||||
let selected_controllers_model: std::rc::Rc<slint::VecModel<i32>> =
|
||||
std::rc::Rc::new(selected_controllers);
|
||||
app.set_selected_controller(slint::ModelRc::from(selected_controllers_model));
|
||||
app.set_selected_controller(slint::ModelRc::from(std::rc::Rc::new(selected_controllers)));
|
||||
|
||||
let controller_changed_model: std::rc::Rc<slint::VecModel<bool>> =
|
||||
std::rc::Rc::new(controller_changed);
|
||||
app.set_controller_changed(slint::ModelRc::from(controller_changed_model));
|
||||
app.set_controller_changed(slint::ModelRc::from(std::rc::Rc::new(controller_changed)));
|
||||
|
||||
let weak_app = app.as_weak();
|
||||
app.on_input_profile_button_clicked(move || {
|
||||
@@ -226,7 +322,7 @@ fn controller_window(
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut command =
|
||||
std::process::Command::new(std::env::current_exe().unwrap());
|
||||
tokio::process::Command::new(std::env::current_exe().unwrap());
|
||||
command.args([
|
||||
"--configure-input-profile",
|
||||
&profile_name,
|
||||
@@ -236,17 +332,62 @@ fn controller_window(
|
||||
if dinput {
|
||||
command.arg("--use-dinput");
|
||||
}
|
||||
if !command.status().unwrap().success() {
|
||||
if !command.status().await.unwrap().success() {
|
||||
eprintln!("Failed to configure input profile");
|
||||
}
|
||||
let game_ui = ui::Ui::new();
|
||||
update_input_profiles(&weak_app, &game_ui.config);
|
||||
let config = ui::config::Config::new();
|
||||
update_input_profiles(&weak_app, &config);
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
dialog.show().unwrap();
|
||||
});
|
||||
let weak_app2 = app.as_weak();
|
||||
app.on_transferpak_toggled(move |player, enabled| {
|
||||
if enabled {
|
||||
let select_gb_rom = rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("GB ROM P{}", player + 1))
|
||||
.add_filter("GB ROM files", &["gb", "gbc", "GB", "GBC"])
|
||||
.pick_file();
|
||||
let select_gb_ram = rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("GB RAM P{}", player + 1))
|
||||
.add_filter("GB RAM files", &["sav", "ram", "srm", "SAV", "RAM", "SRM"])
|
||||
.pick_file();
|
||||
let weak_app3 = weak_app2.clone();
|
||||
tokio::spawn(async move {
|
||||
if let (Some(gb_rom), Some(gb_ram)) = (select_gb_rom.await, select_gb_ram.await) {
|
||||
weak_app3
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
let rom_paths = handle.get_gb_rom_paths();
|
||||
let ram_paths = handle.get_gb_ram_paths();
|
||||
rom_paths.set_row_data(
|
||||
player as usize,
|
||||
gb_rom.path().to_str().unwrap().into(),
|
||||
);
|
||||
ram_paths.set_row_data(
|
||||
player as usize,
|
||||
gb_ram.path().to_str().unwrap().into(),
|
||||
);
|
||||
handle.set_gb_rom_paths(rom_paths);
|
||||
handle.set_gb_ram_paths(ram_paths);
|
||||
})
|
||||
.unwrap();
|
||||
} else {
|
||||
weak_app3
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
let rom_paths = handle.get_gb_rom_paths();
|
||||
let ram_paths = handle.get_gb_ram_paths();
|
||||
rom_paths.set_row_data(player as usize, String::new().into());
|
||||
ram_paths.set_row_data(player as usize, String::new().into());
|
||||
handle.set_gb_rom_paths(rom_paths);
|
||||
handle.set_gb_ram_paths(ram_paths);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn save_settings(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
@@ -254,6 +395,7 @@ pub fn save_settings(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
config.video.integer_scaling = app.get_integer_scaling();
|
||||
config.video.fullscreen = app.get_fullscreen();
|
||||
config.video.widescreen = app.get_widescreen();
|
||||
config.video.vsync = app.get_vsync();
|
||||
config.video.crt = app.get_apply_crt_shader();
|
||||
config.emulation.overclock = app.get_overclock_n64_cpu();
|
||||
config.emulation.disable_expansion_pak = app.get_disable_expansion_pak();
|
||||
@@ -267,6 +409,8 @@ pub fn save_settings(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
}
|
||||
for (i, transferpak_enabled) in app.get_transferpak().iter().enumerate() {
|
||||
config.input.transfer_pak[i] = transferpak_enabled;
|
||||
config.input.gb_rom_path[i] = app.get_gb_rom_paths().row_data(i).unwrap().to_string();
|
||||
config.input.gb_ram_path[i] = app.get_gb_ram_paths().row_data(i).unwrap().to_string();
|
||||
}
|
||||
for (i, input_profile_binding) in app.get_selected_profile_binding().iter().enumerate() {
|
||||
config.input.input_profile_binding[i] = app
|
||||
@@ -312,7 +456,7 @@ fn about_window(app: &AppWindow) {
|
||||
pub fn app_window() {
|
||||
let app = AppWindow::new().unwrap();
|
||||
about_window(&app);
|
||||
retroachievements::ra_window(&app);
|
||||
ui::retroachievements::ra_window(&app);
|
||||
let mut controller_paths;
|
||||
{
|
||||
let game_ui = ui::Ui::new();
|
||||
@@ -322,8 +466,8 @@ pub fn app_window() {
|
||||
controller_paths.insert(0, None);
|
||||
settings_window(&app, &game_ui.config);
|
||||
controller_window(&app, &game_ui.config, &controller_names, &controller_paths);
|
||||
local_game_window(&app, &game_ui.config, &controller_paths);
|
||||
}
|
||||
local_game_window(&app, &controller_paths);
|
||||
ui::netplay::netplay_window(&app, &controller_paths);
|
||||
ui::cheats::cheats_window(&app);
|
||||
app.run().unwrap();
|
||||
@@ -331,9 +475,8 @@ pub fn app_window() {
|
||||
}
|
||||
|
||||
pub fn run_rom(
|
||||
gb_paths: GbPaths,
|
||||
file_path: std::path::PathBuf,
|
||||
game_settings: GameSettings,
|
||||
game_settings: ui::GameSettings,
|
||||
netplay: Option<NetplayDevice>,
|
||||
ra_settings: RASettings,
|
||||
weak: slint::Weak<AppWindow>,
|
||||
@@ -342,7 +485,7 @@ pub fn run_rom(
|
||||
weak.upgrade_in_event_loop(move |handle| handle.set_game_running(true))
|
||||
.unwrap();
|
||||
|
||||
let mut command = std::process::Command::new(std::env::current_exe().unwrap());
|
||||
let mut command = tokio::process::Command::new(std::env::current_exe().unwrap());
|
||||
command.args([
|
||||
"--overclock",
|
||||
&game_settings.overclock.to_string(),
|
||||
@@ -366,9 +509,9 @@ pub fn run_rom(
|
||||
if ra_settings.enabled {
|
||||
command.args([
|
||||
"--ra-username",
|
||||
retroachievements::get_username(),
|
||||
&retroachievements::get_username().unwrap_or("unknown".into()),
|
||||
"--ra-token",
|
||||
retroachievements::get_token(),
|
||||
&retroachievements::get_token().unwrap_or("unknown".into()),
|
||||
]);
|
||||
if ra_settings.hardcore {
|
||||
command.args(["--ra-hardcore"]);
|
||||
@@ -381,23 +524,14 @@ pub fn run_rom(
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
if gb_paths.rom[i].is_some() && gb_paths.ram[i].is_some() {
|
||||
command.args([
|
||||
"--gb-rom",
|
||||
gb_paths.rom[i].as_ref().unwrap().to_str().unwrap(),
|
||||
"--gb-ram",
|
||||
gb_paths.ram[i].as_ref().unwrap().to_str().unwrap(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if !command
|
||||
let success = command
|
||||
.arg(file_path.to_str().unwrap())
|
||||
.status()
|
||||
.await
|
||||
.unwrap()
|
||||
.success()
|
||||
{
|
||||
.success();
|
||||
|
||||
if !success {
|
||||
eprintln!("Failed to run game");
|
||||
}
|
||||
|
||||
@@ -407,6 +541,24 @@ pub fn run_rom(
|
||||
if let Some(rom_dir) = file_path.parent().unwrap().to_str() {
|
||||
handle.set_rom_dir(rom_dir.into());
|
||||
}
|
||||
if success {
|
||||
let recent_roms = slint::VecModel::default();
|
||||
recent_roms.push((
|
||||
file_path.to_str().unwrap().into(),
|
||||
file_path.file_name().unwrap().to_str().unwrap().into(),
|
||||
));
|
||||
|
||||
for rom in handle.get_recent_roms().iter() {
|
||||
if rom.0 != file_path.to_str().unwrap()
|
||||
&& recent_roms.row_count() < 5
|
||||
&& let Ok(exists) = std::fs::exists(&rom.0)
|
||||
&& exists
|
||||
{
|
||||
recent_roms.push(rom);
|
||||
}
|
||||
}
|
||||
handle.set_recent_roms(slint::ModelRc::from(std::rc::Rc::new(recent_roms)));
|
||||
}
|
||||
handle.set_game_running(false);
|
||||
})
|
||||
.unwrap();
|
||||
@@ -426,25 +578,6 @@ fn open_rom(app: &AppWindow) {
|
||||
.set_title("Select ROM")
|
||||
.add_filter("ROM files", &N64_EXTENSIONS)
|
||||
.pick_file();
|
||||
let mut select_gb_rom = [None, None, None, None];
|
||||
let mut select_gb_ram = [None, None, None, None];
|
||||
|
||||
for (i, transfer_pak_enabled) in app.get_transferpak().iter().enumerate() {
|
||||
if transfer_pak_enabled {
|
||||
select_gb_rom[i] = Some(
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("GB ROM P{}", i + 1))
|
||||
.add_filter("GB ROM files", &["gb", "gbc", "GB", "GBC"])
|
||||
.pick_file(),
|
||||
);
|
||||
select_gb_ram[i] = Some(
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("GB RAM P{}", i + 1))
|
||||
.add_filter("GB RAM files", &["sav", "ram", "srm", "SAV", "RAM", "SRM"])
|
||||
.pick_file(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let overclock = app.get_overclock_n64_cpu();
|
||||
let disable_expansion_pak = app.get_disable_expansion_pak();
|
||||
@@ -456,26 +589,9 @@ fn open_rom(app: &AppWindow) {
|
||||
let weak = app.as_weak();
|
||||
tokio::spawn(async move {
|
||||
if let Some(file) = select_rom.await {
|
||||
let mut gb_rom_path = [None, None, None, None];
|
||||
let mut gb_ram_path = [None, None, None, None];
|
||||
|
||||
for i in 0..4 {
|
||||
if let (Some(gb_rom), Some(gb_ram)) =
|
||||
(select_gb_rom[i].as_mut(), select_gb_ram[i].as_mut())
|
||||
&& let (Some(gb_rom), Some(gb_ram)) = (gb_rom.await, gb_ram.await)
|
||||
{
|
||||
gb_rom_path[i] = Some(gb_rom.path().to_path_buf());
|
||||
gb_ram_path[i] = Some(gb_ram.path().to_path_buf());
|
||||
}
|
||||
}
|
||||
|
||||
run_rom(
|
||||
GbPaths {
|
||||
rom: gb_rom_path,
|
||||
ram: gb_ram_path,
|
||||
},
|
||||
file.path().to_path_buf(),
|
||||
GameSettings {
|
||||
ui::GameSettings {
|
||||
overclock,
|
||||
disable_expansion_pak,
|
||||
cheats: std::collections::HashMap::new(), // will be filled in later
|
||||
|
||||
@@ -22,12 +22,14 @@ export component AppWindow inherits Window {
|
||||
callback wiki_button_clicked;
|
||||
callback discord_button_clicked;
|
||||
callback newversion_button_clicked;
|
||||
callback recent_rom_button_clicked(string);
|
||||
callback patreon_button_clicked;
|
||||
callback github_sponsors_button_clicked;
|
||||
callback source_code_button_clicked;
|
||||
callback open_rom_button_clicked;
|
||||
callback saves_folder_button_clicked;
|
||||
callback input_profile_button_clicked;
|
||||
callback transferpak_toggled(int,bool);
|
||||
callback create_session_button_clicked;
|
||||
callback join_session_button_clicked;
|
||||
callback netplay_discord_button_clicked;
|
||||
@@ -37,7 +39,7 @@ export component AppWindow inherits Window {
|
||||
callback cheat_toggled(string, string, string, bool);
|
||||
callback ra_button_clicked(string);
|
||||
callback ra_games_clicked;
|
||||
callback ra_show_profile_clicked(string);
|
||||
callback ra_show_profile_clicked;
|
||||
callback ra_toggled(bool,bool,bool,bool);
|
||||
in property version <=> AboutData.version;
|
||||
in property has_update <=> AboutData.has_update;
|
||||
@@ -50,9 +52,11 @@ export component AppWindow inherits Window {
|
||||
in-out property ra_hardcore <=> RAData.hardcore;
|
||||
in-out property ra_challenge <=> RAData.challenge;
|
||||
in-out property ra_leaderboard <=> RAData.leaderboard;
|
||||
in-out property ra_softcore_only <=> RAData.ra_softcore_only;
|
||||
in-out property integer_scaling <=> SettingsData.integer_scaling;
|
||||
in-out property fullscreen <=> SettingsData.fullscreen;
|
||||
in-out property widescreen <=> SettingsData.widescreen;
|
||||
in-out property vsync <=> SettingsData.vsync;
|
||||
in-out property apply_crt_shader <=> SettingsData.apply_crt_shader;
|
||||
in-out property overclock_n64_cpu <=> SettingsData.overclock_n64_cpu;
|
||||
in-out property disable_expansion_pak <=> SettingsData.disable_expansion_pak;
|
||||
@@ -63,11 +67,14 @@ export component AppWindow inherits Window {
|
||||
in-out property controller_enabled <=> ControllerData.controller_enabled;
|
||||
in-out property controller_changed <=> ControllerData.controller_changed;
|
||||
in-out property transferpak <=> ControllerData.transferpak;
|
||||
in-out property gb_rom_paths <=> ControllerData.gb_rom_paths;
|
||||
in-out property gb_ram_paths <=> ControllerData.gb_ram_paths;
|
||||
in-out property selected_profile_binding <=> ControllerData.selected_profile_binding;
|
||||
in-out property input_profiles <=> ControllerData.input_profiles;
|
||||
in-out property controller_names <=> ControllerData.controller_names;
|
||||
in-out property selected_controller <=> ControllerData.selected_controller;
|
||||
in-out property game_running <=> State.game_running;
|
||||
in-out property recent_roms <=> State.recent_roms;
|
||||
in-out property blank_profiles <=> ControllerData.blank_profiles;
|
||||
in-out property cheat_game_name <=> CheatsData.cheat_game_name;
|
||||
in-out property cheat_game_crc <=> CheatsData.cheat_game_crc;
|
||||
@@ -101,11 +108,17 @@ export component AppWindow inherits Window {
|
||||
newversion_button_clicked => {
|
||||
newversion_button_clicked();
|
||||
}
|
||||
recent_rom_button_clicked(rom) => {
|
||||
recent_rom_button_clicked(rom);
|
||||
}
|
||||
}
|
||||
if(side-bar.current-item == 1): ControllerConfig {
|
||||
input_profile_button_clicked => {
|
||||
input_profile_button_clicked();
|
||||
}
|
||||
transferpak_toggled(player,enabled) => {
|
||||
transferpak_toggled(player,enabled);
|
||||
}
|
||||
}
|
||||
if(side-bar.current-item == 2): Netplay {
|
||||
create_session_button_clicked => {
|
||||
@@ -142,8 +155,8 @@ export component AppWindow inherits Window {
|
||||
ra_games_clicked => {
|
||||
ra_games_clicked();
|
||||
}
|
||||
ra_show_profile_clicked(username) => {
|
||||
ra_show_profile_clicked(username);
|
||||
ra_show_profile_clicked => {
|
||||
ra_show_profile_clicked();
|
||||
}
|
||||
}
|
||||
if(side-bar.current-item == 5): Settings { }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { ListView, HorizontalBox, VerticalBox, Button, GroupBox, CheckBox } from "std-widgets.slint";
|
||||
import { ListView, HorizontalBox, VerticalBox, Button, GroupBox, Switch, CheckBox } from "std-widgets.slint";
|
||||
import { Page } from "page.slint";
|
||||
import { SettingsData } from "settings_page.slint";
|
||||
|
||||
@@ -62,15 +62,14 @@ export component Cheats inherits Page {
|
||||
wrap: word-wrap;
|
||||
}
|
||||
|
||||
if data.options.length == 0: CheckBox {
|
||||
text: @tr("Enabled");
|
||||
if data.options.length == 0: Switch {
|
||||
checked: data.enabled;
|
||||
toggled => {
|
||||
data.enabled = self.checked;
|
||||
cheat_toggled(CheatsData.cheat_game_crc,data.cheat_name,"",self.checked);
|
||||
}
|
||||
}
|
||||
for item[index] in data.options: CheckBox {
|
||||
for item in data.options: CheckBox {
|
||||
text: item.option;
|
||||
enabled: !data.enabled || (data.enabled && item.enabled);
|
||||
checked: item.enabled;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { CheckBox, ComboBox, Button, HorizontalBox, VerticalBox, GroupBox } from "std-widgets.slint";
|
||||
import { Switch, ComboBox, Button, HorizontalBox, VerticalBox, GroupBox } from "std-widgets.slint";
|
||||
import { Page } from "page.slint";
|
||||
import { State } from "localgame_page.slint";
|
||||
|
||||
@@ -9,6 +9,8 @@ export global ControllerData {
|
||||
in-out property <bool> emulate_vru;
|
||||
in-out property <[bool]> controller_enabled;
|
||||
in-out property <[bool]> transferpak;
|
||||
in-out property <[string]> gb_rom_paths;
|
||||
in-out property <[string]> gb_ram_paths;
|
||||
in-out property <[int]> selected_profile_binding: [-1, -1, -1, -1];
|
||||
in-out property <[string]> input_profiles;
|
||||
in-out property <[string]> controller_names;
|
||||
@@ -19,6 +21,7 @@ export global ControllerData {
|
||||
|
||||
export component ControllerConfig inherits Page {
|
||||
callback input_profile_button_clicked;
|
||||
callback transferpak_toggled(int,bool);
|
||||
title: @tr("Controller Configuration");
|
||||
|
||||
VerticalBox {
|
||||
@@ -52,7 +55,7 @@ export component ControllerConfig inherits Page {
|
||||
title: @tr("Enabled");
|
||||
VerticalLayout {
|
||||
alignment: space-between;
|
||||
for player in [0, 1, 2, 3]: CheckBox {
|
||||
for player in [0, 1, 2, 3]: Switch {
|
||||
checked: ControllerData.controller_enabled[player];
|
||||
toggled => {
|
||||
ControllerData.controller_enabled[player] = self.checked;
|
||||
@@ -65,7 +68,7 @@ export component ControllerConfig inherits Page {
|
||||
title: @tr("Emulate VRU");
|
||||
VerticalLayout {
|
||||
alignment: space-between;
|
||||
for valid in [false, false, false, true]: CheckBox {
|
||||
for valid in [false, false, false, true]: Switch {
|
||||
enabled: valid;
|
||||
checked: valid && ControllerData.emulate_vru;
|
||||
toggled => {
|
||||
@@ -79,10 +82,11 @@ export component ControllerConfig inherits Page {
|
||||
title: @tr("Transfer Pak");
|
||||
VerticalLayout {
|
||||
alignment: space-between;
|
||||
for player in [0, 1, 2, 3]: CheckBox {
|
||||
for player in [0, 1, 2, 3]: Switch {
|
||||
checked: ControllerData.transferpak[player];
|
||||
toggled => {
|
||||
ControllerData.transferpak[player] = self.checked;
|
||||
transferpak_toggled(player,self.checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StandardButton, HorizontalBox, VerticalBox, Button, LineEdit, CheckBox, Slider } from "std-widgets.slint";
|
||||
import { StandardButton, HorizontalBox, VerticalBox, Button, LineEdit, Switch, Slider } from "std-widgets.slint";
|
||||
|
||||
export global InputProfileData {
|
||||
in-out property <bool> dinput;
|
||||
@@ -47,7 +47,7 @@ export component InputProfileDialog inherits Window {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
CheckBox {
|
||||
Switch {
|
||||
text: @tr("Use DirectInput");
|
||||
checked: InputProfileData.dinput;
|
||||
toggled => {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { Button, HorizontalBox, VerticalBox } from "std-widgets.slint";
|
||||
import { Button, HorizontalBox, VerticalBox, GroupBox } from "std-widgets.slint";
|
||||
import { Page } from "page.slint";
|
||||
import { AboutData, NewVersionButton } from "about_page.slint";
|
||||
|
||||
export global State {
|
||||
in-out property <bool> game_running;
|
||||
in-out property <[{a:string, b:string}]> recent_roms;
|
||||
}
|
||||
|
||||
export component LocalGame inherits Page {
|
||||
callback open_rom_button_clicked;
|
||||
callback saves_folder_button_clicked;
|
||||
callback newversion_button_clicked;
|
||||
callback recent_rom_button_clicked(string);
|
||||
title: @tr("Local Game");
|
||||
|
||||
VerticalBox {
|
||||
@@ -44,5 +46,22 @@ export component LocalGame inherits Page {
|
||||
}
|
||||
}
|
||||
Rectangle { }
|
||||
|
||||
if State.recent_roms.length>0:HorizontalBox {
|
||||
alignment: center;
|
||||
GroupBox {
|
||||
title: @tr("Recent ROMs");
|
||||
VerticalLayout {
|
||||
alignment: space-between;
|
||||
for rom in State.recent_roms: Button {
|
||||
text: rom.b;
|
||||
enabled: !State.game_running;
|
||||
clicked => {
|
||||
recent_rom_button_clicked(rom.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export component NetplayCreate inherits Window {
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder_text: @tr("Enter session name");
|
||||
placeholder-text: @tr("Enter session name");
|
||||
edited(text) => {
|
||||
NetplayData.session_name = text;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export component NetplayCreate inherits Window {
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder_text: @tr("Enter session password");
|
||||
placeholder-text: @tr("Enter session password");
|
||||
edited(text) => {
|
||||
NetplayData.password = text;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export component NetplayCreate inherits Window {
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder_text: @tr("Enter player name");
|
||||
placeholder-text: @tr("Enter player name");
|
||||
edited(text) => {
|
||||
NetplayData.player_name = text;
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ export component NetplayJoin inherits Window {
|
||||
HorizontalBox {
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Player Name:");
|
||||
text: @tr("Player Name (required):");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder_text: @tr("Enter player name");
|
||||
placeholder-text: @tr("Enter player name");
|
||||
edited(text) => {
|
||||
NetplayData.player_name = text;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export component NetplayWait inherits Window {
|
||||
|
||||
HorizontalBox {
|
||||
chat_message := LineEdit {
|
||||
placeholder_text: @tr("Enter chat message");
|
||||
placeholder-text: @tr("Enter chat message");
|
||||
key-released(event) => {
|
||||
if (event.text == Key.Return && chat_message.text != "") {
|
||||
send_chat_message(chat_message.text);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { LineEdit, CheckBox, Button, HorizontalBox, VerticalBox } from "std-widgets.slint";
|
||||
import { LineEdit, Switch, Button, HorizontalBox, VerticalBox } from "std-widgets.slint";
|
||||
import { Page } from "page.slint";
|
||||
|
||||
export global RAData {
|
||||
@@ -11,6 +11,7 @@ export global RAData {
|
||||
in-out property <bool> leaderboard;
|
||||
in-out property <bool> ra_logging_in;
|
||||
in-out property <bool> ra_show_profile;
|
||||
in-out property <bool> ra_softcore_only;
|
||||
in-out property <string> ra_username;
|
||||
in-out property <string> ra_password;
|
||||
in-out property <string> current_user_message;
|
||||
@@ -20,14 +21,14 @@ export component RetroAchievements inherits Page {
|
||||
callback ra_button_clicked(string);
|
||||
callback ra_toggled(bool,bool,bool,bool);
|
||||
callback ra_games_clicked;
|
||||
callback ra_show_profile_clicked(string);
|
||||
callback ra_show_profile_clicked;
|
||||
title: @tr("RetroAchievements");
|
||||
|
||||
VerticalBox {
|
||||
alignment: start;
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
CheckBox {
|
||||
alignment: start;
|
||||
Switch {
|
||||
text: @tr("Enabled");
|
||||
checked: RAData.enabled;
|
||||
toggled => {
|
||||
@@ -43,9 +44,9 @@ export component RetroAchievements inherits Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
CheckBox {
|
||||
if !RAData.ra_softcore_only:HorizontalBox {
|
||||
alignment: start;
|
||||
Switch {
|
||||
text: @tr("Hardcore mode");
|
||||
checked: RAData.hardcore;
|
||||
enabled: RAData.enabled;
|
||||
@@ -56,8 +57,8 @@ export component RetroAchievements inherits Page {
|
||||
}
|
||||
}
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
CheckBox {
|
||||
alignment: start;
|
||||
Switch {
|
||||
text: @tr("Challenge Indicators");
|
||||
checked: RAData.challenge;
|
||||
enabled: RAData.enabled;
|
||||
@@ -68,8 +69,8 @@ export component RetroAchievements inherits Page {
|
||||
}
|
||||
}
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
CheckBox {
|
||||
alignment: start;
|
||||
Switch {
|
||||
text: @tr("Leaderboard Trackers");
|
||||
checked: RAData.leaderboard;
|
||||
enabled: RAData.enabled;
|
||||
@@ -89,7 +90,7 @@ export component RetroAchievements inherits Page {
|
||||
enabled: RAData.enabled;
|
||||
text: @tr("View Profile");
|
||||
clicked => {
|
||||
ra_show_profile_clicked(RAData.ra_username);
|
||||
ra_show_profile_clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { ComboBox, HorizontalBox, VerticalBox, CheckBox } from "std-widgets.slint";
|
||||
import { ComboBox, HorizontalBox, VerticalBox, Switch } from "std-widgets.slint";
|
||||
import { Page } from "page.slint";
|
||||
|
||||
export global SettingsData {
|
||||
in-out property <bool> integer_scaling;
|
||||
in-out property <bool> fullscreen;
|
||||
in-out property <bool> widescreen;
|
||||
in-out property <bool> vsync;
|
||||
in-out property <bool> apply_crt_shader;
|
||||
in-out property <bool> overclock_n64_cpu;
|
||||
in-out property <bool> disable_expansion_pak;
|
||||
@@ -39,7 +40,7 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
Switch {
|
||||
text: @tr("Integer Scaling");
|
||||
checked: SettingsData.integer_scaling;
|
||||
toggled => {
|
||||
@@ -50,8 +51,8 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
text: @tr("Fullscreen (Esc closes game)");
|
||||
Switch {
|
||||
text: @tr("Fullscreen");
|
||||
checked: SettingsData.fullscreen;
|
||||
toggled => {
|
||||
SettingsData.fullscreen = self.checked;
|
||||
@@ -61,8 +62,8 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
text: @tr("Widescreen (strech)");
|
||||
Switch {
|
||||
text: @tr("Widescreen (stretch)");
|
||||
checked: SettingsData.widescreen;
|
||||
toggled => {
|
||||
SettingsData.widescreen = self.checked;
|
||||
@@ -72,7 +73,18 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
Switch {
|
||||
text: @tr("VSync");
|
||||
checked: SettingsData.vsync;
|
||||
toggled => {
|
||||
SettingsData.vsync = self.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
Switch {
|
||||
text: @tr("Apply CRT shader");
|
||||
checked: SettingsData.apply_crt_shader;
|
||||
toggled => {
|
||||
@@ -87,7 +99,7 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
Switch {
|
||||
text: @tr("Overclock N64 CPU (may cause bugs)");
|
||||
checked: SettingsData.overclock_n64_cpu;
|
||||
toggled => {
|
||||
@@ -98,7 +110,7 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
Switch {
|
||||
text: @tr("Disable Expansion Pak");
|
||||
checked: SettingsData.disable_expansion_pak;
|
||||
toggled => {
|
||||
@@ -109,7 +121,7 @@ export component Settings inherits Page {
|
||||
|
||||
HorizontalBox {
|
||||
alignment: start;
|
||||
CheckBox {
|
||||
Switch {
|
||||
text: @tr("Emulate UNFLoader");
|
||||
checked: SettingsData.emulate_usb;
|
||||
toggled => {
|
||||
|
||||
@@ -384,6 +384,7 @@ pub fn get_controller_names(game_ui: &ui::Ui) -> Vec<String> {
|
||||
controllers
|
||||
}
|
||||
|
||||
#[cfg(feature = "gui")]
|
||||
pub fn get_controller_paths(game_ui: &ui::Ui) -> Vec<Option<String>> {
|
||||
let mut controller_paths: Vec<Option<String>> = vec![];
|
||||
|
||||
|
||||
+34
-50
@@ -1,8 +1,8 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
use crate::ui::gui::{
|
||||
AppWindow, CustomNetplayServer, DispatcherDialog, ErrorDialog, GameSettings, GbPaths,
|
||||
NetplayCreate, NetplayDevice, NetplayJoin, NetplayWait, RASettings, run_rom, save_settings,
|
||||
AppWindow, CustomNetplayServer, DispatcherDialog, ErrorDialog, NetplayCreate, NetplayDevice,
|
||||
NetplayJoin, NetplayWait, RASettings, run_rom, save_settings,
|
||||
};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -159,12 +159,8 @@ fn populate_server_names<T: ComponentHandle + NetplayPages + 'static>(weak: slin
|
||||
}
|
||||
server_names.push("Custom".into());
|
||||
handle.refresh_sessions();
|
||||
let server_names_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(server_names);
|
||||
let server_urls_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(server_urls);
|
||||
handle.set_server_names(slint::ModelRc::from(server_names_model));
|
||||
handle.set_server_urls(slint::ModelRc::from(server_urls_model));
|
||||
handle.set_server_names(slint::ModelRc::from(std::rc::Rc::new(server_names)));
|
||||
handle.set_server_urls(slint::ModelRc::from(std::rc::Rc::new(server_urls)));
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
@@ -192,7 +188,7 @@ fn select_rom<T: ComponentHandle + NetplayPages + 'static>(
|
||||
let mut game_name = ui::storage::get_game_name(&rom_contents);
|
||||
let game_crc = ui::storage::get_game_crc(&rom_contents);
|
||||
let cheats = ui::config::Cheats::new();
|
||||
let mut parsed_cheats = "".to_string();
|
||||
let mut parsed_cheats = String::new();
|
||||
if let Some(game_cheats) = cheats.cheats.get(&game_crc)
|
||||
&& !game_cheats.is_empty()
|
||||
{
|
||||
@@ -224,10 +220,10 @@ fn select_rom<T: ComponentHandle + NetplayPages + 'static>(
|
||||
message_dialog.set_text("Could not read ROM".into());
|
||||
message_dialog.show().unwrap();
|
||||
|
||||
handle.set_game_name("".into());
|
||||
handle.set_game_hash("".into());
|
||||
handle.set_game_cheats("".into());
|
||||
handle.set_rom_path("".into());
|
||||
handle.set_game_name(String::new().into());
|
||||
handle.set_game_hash(String::new().into());
|
||||
handle.set_game_cheats(String::new().into());
|
||||
handle.set_rom_path(String::new().into());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
@@ -251,7 +247,7 @@ fn show_custom_url_dialog(weak: slint::Weak<NetplayCreate>, server_url: slint::S
|
||||
|
||||
pub fn setup_create_window(
|
||||
create_window: &NetplayCreate,
|
||||
game_settings: GameSettings,
|
||||
game_settings: ui::GameSettings,
|
||||
ra_settings: RASettings,
|
||||
rom_dir: slint::SharedString,
|
||||
weak_app: slint::Weak<AppWindow>,
|
||||
@@ -583,26 +579,19 @@ fn update_sessions(weak: slint::Weak<NetplayJoin>) {
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
let sessions_vec = slint::VecModel::default();
|
||||
for session in sessions.iter() {
|
||||
let session_vec = slint::VecModel::from(session.to_vec());
|
||||
let session_model: std::rc::Rc<
|
||||
slint::VecModel<slint::StandardListViewItem>,
|
||||
> = std::rc::Rc::new(session_vec);
|
||||
sessions_vec.push(slint::ModelRc::from(session_model));
|
||||
sessions_vec.push(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(session.to_vec()),
|
||||
)));
|
||||
}
|
||||
let rooms_model: std::rc::Rc<
|
||||
slint::VecModel<slint::ModelRc<slint::StandardListViewItem>>,
|
||||
> = std::rc::Rc::new(sessions_vec);
|
||||
handle.set_sessions(slint::ModelRc::from(rooms_model));
|
||||
handle.set_sessions(slint::ModelRc::from(std::rc::Rc::new(sessions_vec)));
|
||||
|
||||
let room_urls_vec = slint::VecModel::from(room_urls.to_vec());
|
||||
let room_urls_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(room_urls_vec);
|
||||
handle.set_room_urls(slint::ModelRc::from(room_urls_model));
|
||||
handle.set_room_urls(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(room_urls.to_vec()),
|
||||
)));
|
||||
|
||||
let room_ports_vec = slint::VecModel::from(room_ports.to_vec());
|
||||
let room_ports_model: std::rc::Rc<slint::VecModel<i32>> =
|
||||
std::rc::Rc::new(room_ports_vec);
|
||||
handle.set_room_ports(slint::ModelRc::from(room_ports_model));
|
||||
handle.set_room_ports(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(room_ports.to_vec()),
|
||||
)));
|
||||
|
||||
handle.set_current_session(-1);
|
||||
handle.set_pending_refresh(false);
|
||||
@@ -624,7 +613,7 @@ fn create_session(
|
||||
game_hash: String,
|
||||
game_cheats: String,
|
||||
password: String,
|
||||
game_settings: GameSettings,
|
||||
game_settings: ui::GameSettings,
|
||||
ra_settings: RASettings,
|
||||
weak_app: slint::Weak<AppWindow>,
|
||||
weak: slint::Weak<NetplayCreate>,
|
||||
@@ -714,7 +703,7 @@ fn create_session(
|
||||
handle.get_rom_path(),
|
||||
message.player_name.as_ref().unwrap().into(),
|
||||
session.port.unwrap(),
|
||||
GameSettings {
|
||||
ui::GameSettings {
|
||||
overclock: overclock.parse().unwrap(),
|
||||
disable_expansion_pak: disable_expansion_pak.parse().unwrap(),
|
||||
cheats: serde_json::from_str(cheats).unwrap(),
|
||||
@@ -837,7 +826,7 @@ fn join_session(
|
||||
handle.get_rom_path(),
|
||||
message.player_name.as_ref().unwrap().into(),
|
||||
session.port.unwrap(),
|
||||
GameSettings {
|
||||
ui::GameSettings {
|
||||
overclock: overclock.parse().unwrap(),
|
||||
disable_expansion_pak: disable_expansion_pak.parse().unwrap(),
|
||||
cheats: serde_json::from_str(cheats).unwrap(),
|
||||
@@ -883,7 +872,7 @@ fn setup_wait_window(
|
||||
rom_path: slint::SharedString,
|
||||
player_name: slint::SharedString,
|
||||
port: i32,
|
||||
game_settings: GameSettings,
|
||||
game_settings: ui::GameSettings,
|
||||
ra_settings: RASettings,
|
||||
peer_addr: slint::SharedString,
|
||||
weak_app: slint::Weak<AppWindow>,
|
||||
@@ -1062,15 +1051,14 @@ fn setup_wait_window(
|
||||
handle.set_can_start(false);
|
||||
}
|
||||
|
||||
let players_vec: slint::VecModel<slint::SharedString> =
|
||||
slint::VecModel::default();
|
||||
for player in player_names {
|
||||
players_vec.push(player.into());
|
||||
}
|
||||
let players_model: std::rc::Rc<
|
||||
slint::VecModel<slint::SharedString>,
|
||||
> = std::rc::Rc::new(players_vec);
|
||||
handle.set_players(slint::ModelRc::from(players_model));
|
||||
handle.set_players(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
player_names
|
||||
.iter()
|
||||
.map(|x| x.into())
|
||||
.collect::<Vec<slint::SharedString>>(),
|
||||
),
|
||||
)));
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
@@ -1101,12 +1089,8 @@ fn setup_wait_window(
|
||||
}
|
||||
|
||||
run_rom(
|
||||
GbPaths {
|
||||
rom: [None, None, None, None],
|
||||
ram: [None, None, None, None],
|
||||
},
|
||||
handle.get_rom_path().as_str().into(),
|
||||
GameSettings {
|
||||
ui::GameSettings {
|
||||
overclock: game_settings.overclock,
|
||||
disable_expansion_pak: game_settings.disable_expansion_pak,
|
||||
cheats: game_settings.cheats,
|
||||
@@ -1230,7 +1214,7 @@ pub fn netplay_window(app: &AppWindow, controller_paths: &[Option<String>]) {
|
||||
save_settings(&handle, &controller_paths);
|
||||
setup_create_window(
|
||||
&create_window,
|
||||
GameSettings {
|
||||
ui::GameSettings {
|
||||
overclock: handle.get_overclock_n64_cpu(),
|
||||
disable_expansion_pak: handle.get_disable_expansion_pak(),
|
||||
cheats: std::collections::HashMap::new(), // not used here
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
use crate::retroachievements;
|
||||
use crate::ui;
|
||||
use slint::ComponentHandle;
|
||||
|
||||
pub fn ra_window(app: &ui::gui::AppWindow) {
|
||||
let mut token = String::new();
|
||||
if let Ok(ra_config) = std::fs::read(ui::get_dirs().config_dir.join("retroachievements.json"))
|
||||
&& let Ok(result) =
|
||||
serde_json::from_slice::<retroachievements::RAConfig>(ra_config.as_ref())
|
||||
{
|
||||
app.set_ra_username(result.username.into());
|
||||
app.set_ra_enabled(result.enabled);
|
||||
app.set_ra_hardcore(result.hardcore);
|
||||
app.set_ra_challenge(result.challenge);
|
||||
app.set_ra_leaderboard(result.leaderboard);
|
||||
token = result.token;
|
||||
} else {
|
||||
app.set_ra_hardcore(true);
|
||||
}
|
||||
|
||||
if !cfg!(ra_hardcore_enabled) {
|
||||
app.set_ra_softcore_only(true);
|
||||
}
|
||||
|
||||
if app.get_ra_enabled() && !app.get_ra_username().is_empty() {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<bool>();
|
||||
retroachievements::login_token_user(app.get_ra_username().to_string(), token, tx);
|
||||
set_current_user_message(app, rx);
|
||||
} else {
|
||||
app.set_ra_current_user_message("Not currently logged in".into());
|
||||
}
|
||||
|
||||
let weak_app2 = app.as_weak();
|
||||
app.on_ra_button_clicked(move |password| {
|
||||
weak_app2
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<bool>();
|
||||
retroachievements::login_user(
|
||||
handle.get_ra_username().to_string(),
|
||||
password.to_string(),
|
||||
tx,
|
||||
);
|
||||
|
||||
set_current_user_message(&handle, rx);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
app.on_ra_toggled(move |enabled, hardcore, challenge, leaderboard| {
|
||||
let file_path = ui::get_dirs().config_dir.join("retroachievements.json");
|
||||
let raconfig = if let Ok(ra_config) = std::fs::read(&file_path)
|
||||
&& let Ok(result) =
|
||||
serde_json::from_slice::<retroachievements::RAConfig>(ra_config.as_ref())
|
||||
{
|
||||
if !enabled {
|
||||
retroachievements::logout_user();
|
||||
retroachievements::RAConfig {
|
||||
username: String::new(),
|
||||
token: String::new(),
|
||||
enabled,
|
||||
hardcore,
|
||||
challenge,
|
||||
leaderboard,
|
||||
}
|
||||
} else {
|
||||
retroachievements::RAConfig {
|
||||
username: result.username,
|
||||
token: result.token,
|
||||
enabled,
|
||||
hardcore,
|
||||
challenge,
|
||||
leaderboard,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
retroachievements::RAConfig {
|
||||
username: String::new(),
|
||||
token: String::new(),
|
||||
enabled,
|
||||
hardcore,
|
||||
challenge,
|
||||
leaderboard,
|
||||
}
|
||||
};
|
||||
let f = std::fs::File::create(&file_path).unwrap();
|
||||
serde_json::to_writer_pretty(f, &raconfig).unwrap();
|
||||
});
|
||||
|
||||
app.on_ra_games_clicked(move || {
|
||||
open::that_detached("https://retroachievements.org/system/2-nintendo-64/games").unwrap();
|
||||
});
|
||||
|
||||
app.on_ra_show_profile_clicked(move || {
|
||||
open::that_detached(format!(
|
||||
"https://retroachievements.org/user/{}",
|
||||
retroachievements::get_username().unwrap_or_default()
|
||||
))
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn set_current_user_message(app: &ui::gui::AppWindow, rx: tokio::sync::oneshot::Receiver<bool>) {
|
||||
app.set_ra_current_user_message("Logging in...".into());
|
||||
let weak_app = app.as_weak();
|
||||
tokio::spawn(async move {
|
||||
rx.await.unwrap();
|
||||
weak_app
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
if let Some(username) = retroachievements::get_username() {
|
||||
handle.set_ra_current_user_message(format!("Logged in as {}", username).into());
|
||||
handle.set_ra_show_profile(true);
|
||||
} else {
|
||||
handle.set_ra_current_user_message("Login failed".into());
|
||||
handle.set_ra_show_profile(false);
|
||||
}
|
||||
handle.set_ra_logging_in(false);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
+13
-16
@@ -135,13 +135,13 @@ pub fn get_game_crc(rom: &[u8]) -> String {
|
||||
}
|
||||
|
||||
pub fn get_game_name(rom: &[u8]) -> String {
|
||||
let mut game_name = "".to_owned();
|
||||
let header_value = std::str::from_utf8(&rom[0x20..0x34]);
|
||||
if let Ok(header_value) = header_value {
|
||||
let re = regex::Regex::new(r"[^a-zA-Z0-9_ -]").unwrap();
|
||||
game_name = re.replace_all(header_value, "").trim().replace('\0', "");
|
||||
re.replace_all(header_value, "").trim().replace('\0', "")
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
game_name
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui, rom: &[u8]) {
|
||||
@@ -163,49 +163,46 @@ pub fn init(ui: &mut ui::Ui, rom: &[u8]) {
|
||||
ui.storage
|
||||
.paths
|
||||
.eep_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".eep");
|
||||
.push(format!("{}-{}.eep", prefix, ui.game_hash));
|
||||
|
||||
ui.storage.paths.sra_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.sra_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".sra");
|
||||
.push(format!("{}-{}.sra", prefix, ui.game_hash));
|
||||
|
||||
ui.storage.paths.fla_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.fla_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".fla");
|
||||
.push(format!("{}-{}.fla", prefix, ui.game_hash));
|
||||
|
||||
ui.storage.paths.pak_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.pak_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".mpk");
|
||||
.push(format!("{}-{}.mpk", prefix, ui.game_hash));
|
||||
|
||||
ui.storage.paths.sdcard_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.sdcard_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".img");
|
||||
.push(format!("{}-{}.img", prefix, ui.game_hash));
|
||||
|
||||
ui.storage.paths.romsave_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.romsave_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".romsave");
|
||||
.push(format!("{}-{}.romsave", prefix, ui.game_hash));
|
||||
|
||||
ui.storage
|
||||
.paths
|
||||
.savestate_file_path
|
||||
.clone_from(&states_path);
|
||||
ui.storage.paths.savestate_file_path.push(
|
||||
prefix.to_owned()
|
||||
+ "-"
|
||||
+ &ui.game_hash
|
||||
+ ".state"
|
||||
+ ui.storage.save_state_slot.to_string().as_str(),
|
||||
);
|
||||
ui.storage.paths.savestate_file_path.push(format!(
|
||||
"{}-{}.state{}",
|
||||
prefix, ui.game_hash, ui.storage.save_state_slot
|
||||
));
|
||||
}
|
||||
|
||||
pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
|
||||
|
||||
@@ -21,6 +21,11 @@ fn build_gfx_info(device: &mut device::Device) -> GFX_INFO {
|
||||
PAL: device.cart.pal,
|
||||
widescreen: device.ui.config.video.widescreen,
|
||||
fullscreen: device.ui.video.fullscreen,
|
||||
vsync: if device.netplay.is_none() {
|
||||
device.ui.config.video.vsync
|
||||
} else {
|
||||
false
|
||||
},
|
||||
integer_scaling: device.ui.config.video.integer_scaling,
|
||||
upscale: device.ui.config.video.upscale,
|
||||
crt: device.ui.config.video.crt,
|
||||
@@ -101,6 +106,35 @@ pub fn init(device: &mut device::Device) {
|
||||
device.ui.storage.save_state_slot,
|
||||
)
|
||||
}
|
||||
|
||||
fps_counter(&mut device.ui);
|
||||
}
|
||||
|
||||
fn fps_counter(ui: &mut ui::Ui) {
|
||||
let mut fps_rx = ui.video.fps_rx.take().unwrap();
|
||||
let mut vis_rx = ui.video.vis_rx.take().unwrap();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let mut fps: u32 = 0;
|
||||
let mut vis: u32 = 0;
|
||||
loop {
|
||||
match fps_rx.try_recv() {
|
||||
Ok(_fps_update) => fps += 1,
|
||||
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
|
||||
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return,
|
||||
}
|
||||
}
|
||||
loop {
|
||||
match vis_rx.try_recv() {
|
||||
Ok(_vis_update) => vis += 1,
|
||||
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
|
||||
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return,
|
||||
}
|
||||
}
|
||||
unsafe { rdp_set_fps(fps, vis) };
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn close(ui: &ui::Ui) {
|
||||
|
||||
+32
-34
@@ -1,5 +1,5 @@
|
||||
use crate::gui;
|
||||
use crate::retroachievements;
|
||||
use crate::ui::gui;
|
||||
use slint::ComponentHandle;
|
||||
|
||||
pub fn prompt_for_match(words: &[String], frame_time: f64) -> u16 {
|
||||
@@ -8,43 +8,41 @@ pub fn prompt_for_match(words: &[String], frame_time: f64) -> u16 {
|
||||
dedup_words.dedup();
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
|
||||
tokio::spawn(async move {
|
||||
let vru_dialog = gui::VruDialog::new().unwrap();
|
||||
let vru_dialog_weak = vru_dialog.as_weak();
|
||||
|
||||
let tx_clicked = tx.clone();
|
||||
let vru_dialog = gui::VruDialog::new().unwrap();
|
||||
let vru_dialog_weak = vru_dialog.as_weak();
|
||||
|
||||
vru_dialog.on_vru_button_clicked(move |chosen_word| {
|
||||
tx_clicked.try_send(chosen_word.to_string()).unwrap();
|
||||
vru_dialog_weak.unwrap().window().hide().unwrap();
|
||||
});
|
||||
|
||||
vru_dialog.window().on_close_requested(move || {
|
||||
tx.try_send("".to_string()).unwrap();
|
||||
slint::CloseRequestResponse::HideWindow
|
||||
});
|
||||
|
||||
let words_vec = slint::VecModel::default();
|
||||
for word in dedup_words {
|
||||
words_vec.push(word.into());
|
||||
}
|
||||
let words_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(words_vec);
|
||||
vru_dialog.set_words(slint::ModelRc::from(words_model));
|
||||
|
||||
vru_dialog.run().unwrap();
|
||||
vru_dialog.on_vru_button_clicked(move |chosen_word| {
|
||||
tx.try_send(chosen_word.to_string()).unwrap();
|
||||
vru_dialog_weak.unwrap().window().hide().unwrap();
|
||||
});
|
||||
|
||||
let mut result = rx.try_recv();
|
||||
while result.is_err() {
|
||||
result = rx.try_recv();
|
||||
std::thread::sleep(std::time::Duration::from_secs_f64(frame_time));
|
||||
unsafe { sdl3_sys::events::SDL_PumpEvents() }; // so the OS doesn't complain about the game window being frozen
|
||||
retroachievements::do_idle();
|
||||
}
|
||||
for (i, v) in words.iter().enumerate() {
|
||||
if *v == *result.as_ref().unwrap() {
|
||||
return i as u16;
|
||||
vru_dialog.set_words(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
dedup_words
|
||||
.iter()
|
||||
.map(|x| x.into())
|
||||
.collect::<Vec<slint::SharedString>>(),
|
||||
),
|
||||
)));
|
||||
|
||||
let timer = slint::Timer::default();
|
||||
timer.start(
|
||||
slint::TimerMode::Repeated,
|
||||
std::time::Duration::from_secs_f64(frame_time),
|
||||
move || {
|
||||
unsafe { sdl3_sys::events::SDL_PumpEvents() }; // so the OS doesn't complain about the game window being frozen
|
||||
retroachievements::do_idle();
|
||||
},
|
||||
);
|
||||
|
||||
vru_dialog.run().unwrap();
|
||||
|
||||
if let Ok(result) = rx.try_recv() {
|
||||
for (i, v) in words.iter().enumerate() {
|
||||
if *v == result {
|
||||
return i as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
0x7FFF
|
||||
|
||||
Reference in New Issue
Block a user