Import gopher64 upstream
Some checks failed
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (push) Waiting to run
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (push) Waiting to run
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (push) Waiting to run
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (push) Waiting to run
Rust / Collect release artifacts (push) Blocked by required conditions
Rust-lint / lint-linux (push) Failing after 16s
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (push) Failing after 2s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (push) Failing after 1s
27
.cargo/config.toml
Normal file
@ -0,0 +1,27 @@
|
||||
[target.'cfg(all(target_arch = "aarch64", target_os = "linux"))']
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
|
||||
|
||||
[target.'cfg(any(target_os = "windows", target_os = "macos"))']
|
||||
linker = "rust-lld"
|
||||
|
||||
[target.'cfg(target_os = "windows")']
|
||||
rustflags = ["-C", "link-arg=/STACK:8388608"]
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")']
|
||||
rustflags = ["-C", "target-cpu=x86-64-v3"]
|
||||
|
||||
[target.'cfg(all(target_arch = "aarch64", target_os = "macos"))']
|
||||
rustflags = [
|
||||
"-C", "target-cpu=apple-m1",
|
||||
"-C", "link-arg=-headerpad_max_install_names"
|
||||
]
|
||||
|
||||
[target.'cfg(all(target_arch = "aarch64", not(target_os = "macos")))']
|
||||
rustflags = ["-C", "target-cpu=cortex-a76"]
|
||||
|
||||
[env]
|
||||
CC = "clang"
|
||||
CXX = "clang++"
|
||||
AR = "llvm-ar"
|
||||
SDKROOT = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
|
||||
CMAKE_POLICY_VERSION_MINIMUM = "3.5" # for freetype
|
||||
2
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
github: loganmc10
|
||||
patreon: loganmc10
|
||||
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Bugs only! No feature requests
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
type: Bug
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Device (please complete the following information):**
|
||||
- OS: [e.g. Windows 11]
|
||||
- CPU: [e.g. Intel i7-1365U]
|
||||
- GPU: [e.g. Nvidia 5060 Ti]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
15
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "rust-toolchain"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# - package-ecosystem: "cargo"
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
# interval: "daily"
|
||||
300
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,300 @@
|
||||
name: Rust
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
merge_group:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RA_HARDCORE: ${{ github.ref_type == 'tag' && 'true' || 'false' }}
|
||||
NETPLAY_ID: ${{ github.ref_type == 'tag' && secrets.NETPLAY_ID || 'dev' }}
|
||||
BUILD_PROFILE: ${{ github.ref_type == 'tag' && 'release' || 'dev' }}
|
||||
BUILD_DIR: ${{ github.ref_type == 'tag' && 'release' || 'debug' }}
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
arch: linux-x86_64
|
||||
llvm: X64
|
||||
- os: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-gnu
|
||||
arch: linux-aarch64
|
||||
llvm: ARM64
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
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
|
||||
- name: Install SDL Dependencies
|
||||
# https://wiki.libsdl.org/SDL3/README-linux#build-dependencies
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y 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 libusb-1.0-0-dev \
|
||||
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev libfontconfig-dev
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
- 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
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --profile ${{ env.BUILD_PROFILE }} -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/${{ env.BUILD_DIR }}/gopher64 output/gopher64-${{ matrix.arch }}
|
||||
- name: Upload file
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: output/gopher64-${{ matrix.arch }}
|
||||
archive: false
|
||||
|
||||
build-windows:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2025
|
||||
target: x86_64-pc-windows-msvc
|
||||
arch: windows-x86_64
|
||||
llvm: win64
|
||||
- os: windows-11-arm
|
||||
target: aarch64-pc-windows-msvc
|
||||
arch: windows-aarch64
|
||||
llvm: woa64
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
rustflags: ""
|
||||
cache: ${{ github.ref_type != 'tag' }}
|
||||
- name: Set LLVM_VERSION
|
||||
run: |
|
||||
$llvm_version = rustc --version --verbose | Select-String "LLVM version" | ForEach-Object { $_.Line.Split(":")[1].Trim() }
|
||||
echo "LLVM_VERSION=$llvm_version" >> $env: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 }}-${{ matrix.llvm
|
||||
}}.exe
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --profile ${{ env.BUILD_PROFILE }} -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/${{ env.BUILD_DIR }}/gopher64.exe output/gopher64-${{ matrix.arch }}.exe
|
||||
env:
|
||||
CC: clang-cl
|
||||
CXX: clang-cl
|
||||
- name: Upload unsigned artifact
|
||||
id: upload-unsigned-artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: output/gopher64-${{ matrix.arch }}.exe
|
||||
archive: ${{ github.ref_type == 'tag' && true || false }}
|
||||
- name: SignPath
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
uses: signpath/github-action-submit-signing-request@v2
|
||||
with:
|
||||
api-token: "${{ secrets.SIGNPATH_API_TOKEN }}"
|
||||
organization-id: "${{ secrets.SIGNPATH_ORG_ID }}"
|
||||
project-slug: "gopher64"
|
||||
artifact-configuration-slug: "gopher64-${{ matrix.arch }}"
|
||||
signing-policy-slug: "release-signing"
|
||||
github-artifact-id: "${{ steps.upload-unsigned-artifact.outputs.artifact-id }}"
|
||||
wait-for-completion: true
|
||||
wait-for-completion-timeout-in-seconds: 3600
|
||||
output-artifact-directory: output-signed
|
||||
- name: Upload signed artifact
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: output-signed/gopher64-${{ matrix.arch }}.exe
|
||||
archive: false
|
||||
overwrite: true
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-apple-darwin
|
||||
arch: macos-aarch64
|
||||
llvm: ARM64
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
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
|
||||
- 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
|
||||
- name: Build
|
||||
run: |
|
||||
cargo install cargo-bundle
|
||||
brew install molten-vk
|
||||
cargo build --profile ${{ env.BUILD_PROFILE }} --no-default-features -v --target=${{ matrix.target }}
|
||||
mv target/${{ matrix.target }}/${{ env.BUILD_DIR }}/gopher64 target/${{ matrix.target }}/${{ env.BUILD_DIR }}/gopher64-cli
|
||||
cargo bundle --profile ${{ env.BUILD_PROFILE }} --target=${{ matrix.target }} --format osx
|
||||
cp target/${{ matrix.target }}/${{ env.BUILD_DIR }}/gopher64-cli target/${{ matrix.target }}/${{ env.BUILD_DIR }}/bundle/osx/Gopher64.app/Contents/MacOS/gopher64-cli
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "15.0"
|
||||
- name: Sign Bundle Dev
|
||||
if: ${{ github.ref_type != 'tag' }}
|
||||
run: |
|
||||
codesign -f --entitlements data/macos/entitlements_dev.plist --deep --timestamp --options runtime -s - target/${{ matrix.target }}/${{ env.BUILD_DIR }}/bundle/osx/Gopher64.app
|
||||
- name: Import Codesign Certs
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
uses: apple-actions/import-codesign-certs@v7
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.APPLE_CERTIFICATES_FILE_BASE64 }}
|
||||
p12-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
|
||||
- name: Sign Bundle Release
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
run: |
|
||||
codesign -f --timestamp --options runtime -s "${{ secrets.APPLE_DEVELOPER_ID }}" target/${{ matrix.target }}/${{ env.BUILD_DIR }}/bundle/osx/Gopher64.app/Contents/Frameworks/libMoltenVK.dylib
|
||||
codesign -f --entitlements data/macos/entitlements_child.plist --timestamp --options runtime -s "${{ secrets.APPLE_DEVELOPER_ID }}" target/${{ matrix.target }}/${{ env.BUILD_DIR }}/bundle/osx/Gopher64.app/Contents/MacOS/gopher64-cli
|
||||
codesign -f --entitlements data/macos/entitlements_prod.plist --timestamp --options runtime -s "${{ secrets.APPLE_DEVELOPER_ID }}" target/${{ matrix.target }}/${{ env.BUILD_DIR }}/bundle/osx/Gopher64.app
|
||||
zip -r gopher64-${{ matrix.arch }}.zip target/${{ matrix.target }}/${{ env.BUILD_DIR }}/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 }}/${{ env.BUILD_DIR }}/bundle/osx/Gopher64.app
|
||||
- name: Upload file
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: target/${{ matrix.target }}/${{ env.BUILD_DIR }}/bundle/osx
|
||||
|
||||
build-android:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
platform: 33
|
||||
ndk: 29.0.14206865
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
target: x86_64-linux-android,aarch64-linux-android
|
||||
rustflags: ""
|
||||
cache: ${{ github.ref_type != 'tag' }}
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v4
|
||||
with:
|
||||
packages: "platforms;android-${{ matrix.platform }} ndk;${{ matrix.ndk }}"
|
||||
- name: Decode keystore and create jks
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
working-directory: android-project
|
||||
run: |
|
||||
echo "${{ secrets.ANDROID_KEYSTORE }}" | base64 --decode > app/keystore.jks
|
||||
|
||||
cat <<EOF > keystore.properties
|
||||
storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||
keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
keyAlias=gopher64
|
||||
storeFile=keystore.jks
|
||||
EOF
|
||||
- name: Build
|
||||
working-directory: android-project
|
||||
run: |
|
||||
cargo install cargo-ndk
|
||||
./gradlew assemble${{ env.BUILD_PROFILE == 'release' && 'Release' || 'Debug' }}
|
||||
mkdir output
|
||||
cp app/build/outputs/apk/${{ env.BUILD_DIR }}/app-${{ env.BUILD_DIR }}.apk output/gopher64-android.apk
|
||||
- name: Upload file
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gopher64-android
|
||||
path: android-project/output/gopher64-android.apk
|
||||
archive: false
|
||||
|
||||
collect-artifacts:
|
||||
name: Collect release artifacts
|
||||
needs: [build-linux, build-windows, build-macos, build-android]
|
||||
if: github.ref_type == 'tag'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download all workflow artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
skip-decompress: true
|
||||
merge-multiple: true
|
||||
path: release-artifacts
|
||||
- name: Add files to release
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
draft: true
|
||||
files: release-artifacts/*
|
||||
- name: Setup butler
|
||||
uses: remarkablegames/setup-butler@v3
|
||||
- name: Upload to itch.io
|
||||
run: |
|
||||
butler push release-artifacts/gopher64-linux-x86_64 loganmc10/gopher64:linux-x86_64 --userversion ${GITHUB_REF_NAME#v}
|
||||
butler push release-artifacts/gopher64-linux-aarch64 loganmc10/gopher64:linux-aarch64 --userversion ${GITHUB_REF_NAME#v}
|
||||
butler push release-artifacts/gopher64-windows-x86_64.exe loganmc10/gopher64:windows-x86_64 --userversion ${GITHUB_REF_NAME#v}
|
||||
butler push release-artifacts/gopher64-windows-aarch64.exe loganmc10/gopher64:windows-aarch64 --userversion ${GITHUB_REF_NAME#v}
|
||||
butler push release-artifacts/gopher64-macos-aarch64.zip loganmc10/gopher64:macos-aarch64 --userversion ${GITHUB_REF_NAME#v}
|
||||
butler push release-artifacts/gopher64-android.apk loganmc10/gopher64:android --userversion ${GITHUB_REF_NAME#v}
|
||||
env:
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_API_KEY }}
|
||||
61
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: Rust-lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
lint-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
rustflags: ""
|
||||
components: clippy, rustfmt
|
||||
- name: Set LLVM_VERSION
|
||||
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
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y 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 libusb-1.0-0-dev \
|
||||
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev libfontconfig-dev gettext
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
- 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
|
||||
- name: Rustfmt Check
|
||||
uses: actions-rust-lang/rustfmt@v1
|
||||
- name: Lint
|
||||
run: |
|
||||
cargo install slint-tr-extractor
|
||||
cargo clippy -- -Dwarnings
|
||||
cargo clippy --no-default-features -- -Dwarnings
|
||||
find -name \*.slint | sort | xargs slint-tr-extractor -o /tmp/translations.pot
|
||||
msgcmp --use-untranslated /tmp/translations.pot data/translations/gopher64.pot
|
||||
msgcmp --use-untranslated data/translations/gopher64.pot /tmp/translations.pot
|
||||
find data/translations -name "*.po" | xargs -I {} msgcmp {} data/translations/gopher64.pot
|
||||
18
.github/workflows/sec.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Security audit
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
jobs:
|
||||
audit:
|
||||
permissions:
|
||||
issues: write
|
||||
checks: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: rustsec/audit-check@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
.vscode/settings.json
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
11
.gitmodules
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
[submodule "parallel-rdp/parallel-rdp-standalone"]
|
||||
path = parallel-rdp/parallel-rdp-standalone
|
||||
url = https://github.com/Themaister/parallel-rdp-standalone.git
|
||||
branch = master
|
||||
[submodule "src/compat/sse2neon"]
|
||||
path = src/compat/sse2neon
|
||||
url = https://github.com/DLTcollab/sse2neon.git
|
||||
branch = master
|
||||
[submodule "retroachievements/rcheevos"]
|
||||
path = retroachievements/rcheevos
|
||||
url = https://github.com/RetroAchievements/rcheevos.git
|
||||
9749
Cargo.lock
generated
Normal file
80
Cargo.toml
Normal file
@ -0,0 +1,80 @@
|
||||
[package]
|
||||
name = "gopher64"
|
||||
version = "1.1.36"
|
||||
edition = "2024"
|
||||
rust-version = "1.98.0"
|
||||
|
||||
[features]
|
||||
default = ["gui"]
|
||||
gui = ["dep:rfd", "dep:slint", "dep:open", "dep:slint-build"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
zip = "8.0"
|
||||
sevenz-rust2 = "0.22"
|
||||
chrono = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde-big-array = "0.5"
|
||||
sha2 = "0.11"
|
||||
tokio = {version = "1.46", features = ["rt-multi-thread", "fs", "process"] }
|
||||
spin_sleep = "1.3"
|
||||
encoding_rs = "0.8"
|
||||
mimalloc = "0.1"
|
||||
regex = "1.11"
|
||||
tokio-tungstenite = { version = "0.30", features = ["rustls-tls-webpki-roots"] }
|
||||
matchbox_socket = "0.14"
|
||||
ggrs = { version = "0.13", features = ["sync-send"] }
|
||||
rustc-hash = "2.1"
|
||||
futures = "0.3"
|
||||
rand = { version = "0.10", features = ["serde"] }
|
||||
fatfs = "0.3"
|
||||
semver = "1.0"
|
||||
postcard = { version = "1.1", features = ["use-std"] }
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "charset", "http2", "system-proxy", "json", "query"] }
|
||||
discord-rich-presence = "1.1"
|
||||
quick-xml = "0.42"
|
||||
obfstr = "0.4"
|
||||
rustls = { version = "0.23", default-features = false }
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "unstable-winit-030", "backend-winit", "renderer-femtovg-wgpu", "accessibility"], optional = true }
|
||||
rfd = {version = "0.17", optional = true }
|
||||
dirs = "6.0"
|
||||
open = { version = "5.3", optional = true }
|
||||
sdl3-sys = { version = "0.6", features = ["build-from-source-static"] }
|
||||
sdl3-ttf-sys = { version = "0.6", features = ["build-static-vendored", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true }
|
||||
jni = "0.22"
|
||||
sdl3-sys = { version = "0.6", features = ["build-from-source"] }
|
||||
sdl3-ttf-sys = { version = "0.6", features = ["build-from-source", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] }
|
||||
webpki-roots = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
winresource = "0.1"
|
||||
bindgen = "0.72"
|
||||
sdl3-src = "3.4"
|
||||
slint-build = { version = "1.12", optional = true }
|
||||
cc = { version = "1.2", features = ["parallel"] }
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
strip = true
|
||||
|
||||
[profile.release-with-debug]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
strip = false
|
||||
|
||||
[package.metadata.bundle]
|
||||
name = "Gopher64"
|
||||
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"]
|
||||
osx_minimum_system_version = "15.0"
|
||||
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
86
README.md
@ -1,3 +1,85 @@
|
||||
# gopher64-netplay
|
||||
# gopher64
|
||||
Gopher64 is a cross-platform N64 emulator. Some notable features:
|
||||
* Netplay
|
||||
* Homebrew support
|
||||
* Upscaling
|
||||
* CRT shader
|
||||
* Emulate CPU overclocking
|
||||
* Cheats
|
||||
* Savestates
|
||||
* RetroAchievements
|
||||
|
||||
Gopher64 fork with netplay session save/resume and desync auto-resync
|
||||
## download
|
||||
|
||||
<a href="https://loganmc10.itch.io/gopher64"><img src="https://static.itch.io/images/badge.svg" width="200" ></a>
|
||||
|
||||
Windows:
|
||||
* Standalone executable: [gopher64-windows-x86_64.exe](https://github.com/gopher64/gopher64/releases/latest/download/gopher64-windows-x86_64.exe)
|
||||
|
||||
MacOS:
|
||||
* App Bundle: [gopher64-macos-aarch64.zip](https://github.com/gopher64/gopher64/releases/latest/download/gopher64-macos-aarch64.zip)
|
||||
* Homebrew: `brew install --cask gopher64`
|
||||
|
||||
Linux:
|
||||
* Standalone executable: [gopher64-linux-x86_64](https://github.com/gopher64/gopher64/releases/latest/download/gopher64-linux-x86_64)
|
||||
* Flatpak: `flatpak install flathub io.github.gopher64.gopher64`
|
||||
|
||||
Android:
|
||||
* APK: [gopher64-android.apk](https://github.com/gopher64/gopher64/releases/latest/download/gopher64-android.apk)
|
||||
|
||||
## wiki
|
||||
|
||||
https://github.com/gopher64/gopher64/wiki
|
||||
|
||||
## discord
|
||||
|
||||
[Discord invite link](https://discord.gg/9RGXq8W8JQ)
|
||||
|
||||
## controls
|
||||
|
||||
Keys are mapped according to [these defaults](https://github.com/gopher64/gopher64/wiki/Default-Keyboard-Setup). Xbox-style controllers also have a [default mapping applied](https://github.com/gopher64/gopher64/wiki/Default-Gamepad-Setup).
|
||||
|
||||
## netplay
|
||||
|
||||
Gopher64 supports P2P netplay (online play with others).
|
||||
|
||||
Please read the [guide](https://github.com/gopher64/gopher64/wiki/Netplay-Guide) before trying out netplay.
|
||||
|
||||
## portable mode
|
||||
|
||||
If you would like to keep all the game data in the same folder as the executable, you just need to create a file called "portable.txt" in the same directory as the executable.
|
||||
|
||||
## flatpak
|
||||
|
||||
If you want to run the flatpak from the command line, you need to add the `--filesystem=host:ro` option, for example:
|
||||
|
||||
```
|
||||
flatpak run --filesystem=host:ro io.github.gopher64.gopher64 /path/to/rom.z64
|
||||
```
|
||||
|
||||
## building and usage
|
||||
|
||||
1. Linux only: [install the SDL3 dependencies](https://wiki.libsdl.org/SDL3/README-linux#build-dependencies)
|
||||
2. [Install rust](https://www.rust-lang.org/tools/install)
|
||||
3. `git clone --recursive https://github.com/gopher64/gopher64.git`
|
||||
4. `cd gopher64`
|
||||
5. `cargo build --release`
|
||||
6. `./target/release/gopher64 /path/to/rom.z64`
|
||||
|
||||
## contributing
|
||||
|
||||
PRs that are vibe coded and/or co-authored by AI agents (Plan/Agent mode) are not permitted. If you are going to submit a PR, it should be scoped to a single feature/improvement.
|
||||
|
||||
Please contact me via a GitHub issue or Discord (loganmc10) before doing substantial work on a PR.
|
||||
|
||||
## license
|
||||
|
||||
Gopher64 is licensed under the GPLv3 license. Many portions of gopher64 have been adapted from mupen64plus and/or ares. The license for mupen64plus can be found [here](https://github.com/mupen64plus/mupen64plus-core/blob/master/LICENSES). The license for ares can be found [here](https://github.com/ares-emulator/ares/blob/master/LICENSE).
|
||||
|
||||
## privacy and code signing policy
|
||||
|
||||
Free code signing for the Windows release is provided by [SignPath.io](https://about.signpath.io), certificate by [SignPath Foundation](https://signpath.org).
|
||||
|
||||
During online netplay sessions, the server logs your IP address and basic session information (game title and session name) for operational purposes. No additional personal data is collected or stored.
|
||||
|
||||
If you enable the RetroAchievements feature, some data is sent to their systems. Please see their terms [here](https://retroachievements.org/terms).
|
||||
|
||||
39
android-project/.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
jniLibs/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
keystore.properties
|
||||
|
||||
# Log/OS Files
|
||||
*.log
|
||||
|
||||
# Android Studio generated files and folders
|
||||
captures/
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
*.aab
|
||||
*.apk
|
||||
output-metadata.json
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/
|
||||
misc.xml
|
||||
deploymentTargetDropDown.xml
|
||||
render.experimental.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
*.keystore
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
google-services.json
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
app/src/main/java/org/libsdl/app/
|
||||
4
android-project/android.toolchain.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
set(ANDROID_PLATFORM $ENV{ANDROID_PLATFORM})
|
||||
set(ANDROID_ABI $ENV{ANDROID_ABI})
|
||||
set(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES ON)
|
||||
include("$ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake")
|
||||
161
android-project/app/build.gradle.kts
Normal file
@ -0,0 +1,161 @@
|
||||
import groovy.json.JsonSlurper
|
||||
import java.util.Properties
|
||||
import java.io.FileInputStream
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
val keystoreProperties = Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.github.gopher64.gopher64"
|
||||
compileSdk {
|
||||
version = release(36) {
|
||||
minorApiLevel = 1
|
||||
}
|
||||
}
|
||||
|
||||
ndkVersion = "29.0.14206865"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "io.github.gopher64.gopher64"
|
||||
minSdk = 33
|
||||
targetSdk = 36
|
||||
versionCode = semverToVersionCode(cargoPackageVersion())
|
||||
versionName = cargoPackageVersion()
|
||||
ndk {
|
||||
abiFilters.addAll(listOf("arm64-v8a", "x86_64"))
|
||||
}
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
create("release") {
|
||||
keyAlias = keystoreProperties["keyAlias"] as String
|
||||
keyPassword = keystoreProperties["keyPassword"] as String
|
||||
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||
storePassword = keystoreProperties["storePassword"] as String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signingConfig = signingConfigs["release"]
|
||||
}
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
packaging {
|
||||
jniLibs {
|
||||
excludes.add("lib/**/libsevenz_rust2*.so")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun cargoPackageVersion(packageName: String = "gopher64"): String {
|
||||
val repoRoot = rootDir.parentFile
|
||||
val jsonText = providers.exec {
|
||||
workingDir(repoRoot)
|
||||
commandLine("cargo", "metadata", "--format-version", "1", "--no-deps")
|
||||
}.standardOutput.asText.get()
|
||||
|
||||
val parsedJson = JsonSlurper().parseText(jsonText) as Map<String, Any>
|
||||
val packages = parsedJson["packages"] as List<Map<String, Any>>
|
||||
return packages.first { it["name"] == packageName }["version"] as String
|
||||
}
|
||||
|
||||
fun semverToVersionCode(version: String): Int {
|
||||
val parts = version.substringBefore('-').split(".")
|
||||
val (major, minor, patch) = Triple(parts[0].toInt(), parts[1].toInt(), parts[2].toInt())
|
||||
|
||||
return major * 1_000_000 + minor * 1_000 + patch
|
||||
}
|
||||
|
||||
val ndkBuild = tasks.register<Exec>("ndkBuild") {
|
||||
val isRelease = gradle.startParameter.taskNames.any { it.endsWith("Release", ignoreCase = true) }
|
||||
workingDir = rootDir.parentFile
|
||||
val toolchainPath = "$rootDir/android.toolchain.cmake"
|
||||
environment("CMAKE_TOOLCHAIN_FILE", toolchainPath)
|
||||
|
||||
var minSdk = android.defaultConfig.minSdk
|
||||
var ndkDir = androidComponents.sdkComponents.ndkDirectory.get().asFile.absolutePath
|
||||
environment("ANDROID_NDK_HOME", "$ndkDir")
|
||||
environment("ANDROID_NDK_ROOT", "$ndkDir")
|
||||
environment("LIBCLANG_PATH", "$ndkDir/toolchains/llvm/prebuilt/linux-x86_64/musl/lib")
|
||||
|
||||
val jniType = if (isRelease) "release" else "debug"
|
||||
val jniLibsFolder = "$rootDir/app/src/$jniType/jniLibs"
|
||||
|
||||
commandLine(
|
||||
"cargo", "ndk",
|
||||
"--link-libcxx-shared",
|
||||
"-P", "$minSdk",
|
||||
"-t", "arm64-v8a",
|
||||
"-t", "x86_64",
|
||||
"-o", jniLibsFolder,
|
||||
"build", "--lib",
|
||||
"--profile", if (isRelease) "release" else "dev",
|
||||
)
|
||||
}
|
||||
|
||||
val sdlLibsArm64 = tasks.register<Copy>("sdlLibsArm64") {
|
||||
val isRelease = gradle.startParameter.taskNames.any { it.endsWith("Release", ignoreCase = true) }
|
||||
val jniType = if (isRelease) "release" else "debug"
|
||||
val jniLibsFolder = "$rootDir/app/src/$jniType/jniLibs/arm64-v8a"
|
||||
|
||||
from("$rootDir/../target/aarch64-linux-android/$jniType")
|
||||
into(jniLibsFolder)
|
||||
include("libSDL*")
|
||||
}
|
||||
|
||||
val sdlLibsX64 = tasks.register<Copy>("sdlLibsX64") {
|
||||
val isRelease = gradle.startParameter.taskNames.any { it.endsWith("Release", ignoreCase = true) }
|
||||
val jniType = if (isRelease) "release" else "debug"
|
||||
val jniLibsFolder = "$rootDir/app/src/$jniType/jniLibs/x86_64"
|
||||
|
||||
from("$rootDir/../target/x86_64-linux-android/$jniType")
|
||||
into(jniLibsFolder)
|
||||
include("libSDL*")
|
||||
}
|
||||
|
||||
tasks.named("preBuild") {
|
||||
dependsOn(sdlLibsArm64)
|
||||
dependsOn(sdlLibsX64)
|
||||
}
|
||||
|
||||
tasks.named("sdlLibsArm64") {
|
||||
dependsOn(ndkBuild)
|
||||
}
|
||||
|
||||
tasks.named("sdlLibsX64") {
|
||||
dependsOn(ndkBuild)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.material)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
}
|
||||
82
android-project/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
|
||||
java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it
|
||||
java.lang.String clipboardGetText();
|
||||
boolean clipboardHasText();
|
||||
void clipboardSetText(java.lang.String);
|
||||
int createCustomCursor(int[], int, int, int, int);
|
||||
void destroyCustomCursor(int);
|
||||
android.app.Activity getContext();
|
||||
boolean getManifestEnvironmentVariables();
|
||||
android.view.Surface getNativeSurface();
|
||||
void initTouch();
|
||||
boolean isAndroidTV();
|
||||
boolean isChromebook();
|
||||
boolean isDeXMode();
|
||||
boolean isTablet();
|
||||
void manualBackButton();
|
||||
int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]);
|
||||
void minimizeWindow();
|
||||
boolean openURL(java.lang.String);
|
||||
void requestPermission(java.lang.String, int);
|
||||
boolean showToast(java.lang.String, int, int, int, int);
|
||||
boolean sendMessage(int, int);
|
||||
boolean setActivityTitle(java.lang.String);
|
||||
boolean setCustomCursor(int);
|
||||
void setOrientation(int, int, boolean, java.lang.String);
|
||||
boolean setRelativeMouseEnabled(boolean);
|
||||
boolean setSystemCursor(int);
|
||||
void setWindowStyle(boolean);
|
||||
boolean shouldMinimizeOnFocusLoss();
|
||||
boolean showTextInput(int, int, int, int, int);
|
||||
boolean supportsRelativeMouse();
|
||||
int openFileDescriptor(java.lang.String, java.lang.String);
|
||||
boolean showFileDialog(java.lang.String[], boolean, boolean, int);
|
||||
java.lang.String getPreferredLocales();
|
||||
java.lang.String formatLocale(java.util.Locale);
|
||||
}
|
||||
|
||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager {
|
||||
void closeDevice(int);
|
||||
boolean initialize(boolean, boolean);
|
||||
boolean openDevice(int);
|
||||
boolean readReport(int, byte[], boolean);
|
||||
int writeReport(int, byte[], boolean);
|
||||
}
|
||||
|
||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager {
|
||||
void registerAudioDeviceCallback();
|
||||
void unregisterAudioDeviceCallback();
|
||||
void audioSetThreadPriority(boolean, int);
|
||||
}
|
||||
|
||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager {
|
||||
void joystickSetSensorsEnabled(int, boolean);
|
||||
void pollInputDevices();
|
||||
void joystickSetLED(int, int, int, int);
|
||||
void pollHapticDevices();
|
||||
void hapticRun(int, float, int);
|
||||
void hapticRumble(int, float, float, int);
|
||||
void hapticStop(int);
|
||||
}
|
||||
43
android-project/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature android:name="android.hardware.vulkan.version" android:required="true"
|
||||
android:version="0x00401000" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.gamepad"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.usb.host"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Gopher64"
|
||||
android:appCategory="game"
|
||||
android:hardwareAccelerated="true">
|
||||
<activity android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:name=".SlintActivity" android:exported="true">
|
||||
<meta-data android:name="android.app.lib_name" android:value="gopher64" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".N64Activity"
|
||||
android:launchMode="singleTop"
|
||||
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:preferMinimalPostProcessing="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,48 @@
|
||||
package io.github.gopher64.gopher64
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.PowerManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import org.libsdl.app.SDLActivity
|
||||
|
||||
class N64Activity : SDLActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val powerManager = getContext().getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
|
||||
if (powerManager.isSustainedPerformanceModeSupported) {
|
||||
Log.v("SDL", "Enabling sustained performance mode")
|
||||
window.setSustainedPerformanceMode(true)
|
||||
} else {
|
||||
Log.v("SDL", "Sustained performance mode not supported")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<String> = arrayOf(
|
||||
"SDL3",
|
||||
"SDL3_ttf",
|
||||
"gopher64"
|
||||
)
|
||||
|
||||
override fun getMainFunction(): String = "gopher64_sdl_main"
|
||||
|
||||
override fun getArguments(): Array<String> {
|
||||
val intent = intent ?: return super.getArguments()
|
||||
val args = intent.getStringArrayExtra("args") ?: return super.getArguments()
|
||||
|
||||
val dataIntent = Intent()
|
||||
val file_path = intent.getStringExtra("file_path")
|
||||
if (file_path != null) {
|
||||
dataIntent.putExtra("file_path", file_path)
|
||||
}
|
||||
val cheats_path = intent.getStringExtra("cheats_path")
|
||||
if (cheats_path != null) {
|
||||
dataIntent.putExtra("cheats_path", cheats_path)
|
||||
}
|
||||
setResult(RESULT_OK, dataIntent)
|
||||
return args
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package io.github.gopher64.gopher64
|
||||
|
||||
import android.app.NativeActivity
|
||||
import android.content.Intent
|
||||
|
||||
class SlintActivity : NativeActivity() {
|
||||
companion object {
|
||||
init {
|
||||
System.loadLibrary("gopher64")
|
||||
}
|
||||
}
|
||||
private external fun nativeOnActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
nativeOnActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 47 KiB |
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#419567</color>
|
||||
</resources>
|
||||
4
android-project/build.gradle.kts
Normal file
@ -0,0 +1,4 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
}
|
||||
15
android-project/gradle.properties
Normal file
@ -0,0 +1,15 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
12
android-project/gradle/gradle-daemon-jvm.properties
Normal file
@ -0,0 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect
|
||||
toolchainVersion=21
|
||||
20
android-project/gradle/libs.versions.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[versions]
|
||||
agp = "9.2.1"
|
||||
coreKtx = "1.18.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
appcompat = "1.7.1"
|
||||
material = "1.14.0"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
BIN
android-project/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
9
android-project/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#Thu May 21 08:40:22 CEST 2026
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
251
android-project/gradlew
vendored
Executable file
@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
94
android-project/gradlew.bat
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
26
android-project/settings.gradle.kts
Normal file
@ -0,0 +1,26 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Gopher64"
|
||||
include(":app")
|
||||
334
build.rs
Normal file
@ -0,0 +1,334 @@
|
||||
fn main() {
|
||||
println!("cargo::rerun-if-changed=parallel-rdp");
|
||||
println!("cargo::rerun-if-changed=retroachievements");
|
||||
println!("cargo::rerun-if-changed=src/compat");
|
||||
println!("cargo::rerun-if-changed=data/translations");
|
||||
println!("cargo::rerun-if-changed=data/ui");
|
||||
|
||||
#[cfg(feature = "gui")]
|
||||
{
|
||||
let slint_config = slint_build::CompilerConfiguration::new()
|
||||
.with_bundled_translations("data/translations");
|
||||
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();
|
||||
volk_build
|
||||
.std("c17")
|
||||
.include("parallel-rdp/parallel-rdp-standalone/vulkan-headers/include")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/volk/volk.c");
|
||||
let mut rdp_build = cc::Build::new();
|
||||
rdp_build
|
||||
.cpp(true)
|
||||
.std("c++20")
|
||||
.flag("-Wno-unused-parameter")
|
||||
.flag("-Wno-missing-field-initializers")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/command_ring.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/rdp_device.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/rdp_dump_write.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/video_interface.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/buffer.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/buffer_pool.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/command_buffer.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/command_pool.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/context.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/cookie.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/descriptor_set.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/event_manager.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/fence.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/fence_manager.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/image.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/indirect_layout.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/memory_allocator.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/pipeline_event.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/query_pool.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/render_pass.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/sampler.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/semaphore.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/semaphore_manager.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/shader.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/texture/texture_format.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/vulkan/wsi.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/arena_allocator.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/logging.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/thread_id.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/aligned_alloc.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/timer.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/timeline_trace_file.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/environment.cpp")
|
||||
.file("parallel-rdp/parallel-rdp-standalone/util/thread_name.cpp")
|
||||
.file("parallel-rdp/interface.cpp")
|
||||
.file("parallel-rdp/wsi_platform.cpp")
|
||||
.include("parallel-rdp/parallel-rdp-standalone/parallel-rdp")
|
||||
.include("parallel-rdp/parallel-rdp-standalone/volk")
|
||||
.include("parallel-rdp/parallel-rdp-standalone/vulkan")
|
||||
.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").unwrap()).join("include"),
|
||||
)
|
||||
.include(
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_TTF_OUT_DIR").unwrap())
|
||||
.join("include"),
|
||||
);
|
||||
|
||||
let mut retroachievements_build = cc::Build::new();
|
||||
retroachievements_build
|
||||
.flag("-Wno-unused-parameter")
|
||||
.include("retroachievements/rcheevos/include")
|
||||
.flag("-DRC_CLIENT_SUPPORTS_HASH")
|
||||
.file("retroachievements/rcheevos/src/rc_client.c")
|
||||
.file("retroachievements/rcheevos/src/rc_compat.c")
|
||||
.file("retroachievements/rcheevos/src/rc_util.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/alloc.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/condition.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/condset.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/consoleinfo.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/format.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/lboard.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/memref.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/operand.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/richpresence.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/runtime.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/runtime_progress.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/trigger.c")
|
||||
.file("retroachievements/rcheevos/src/rcheevos/value.c")
|
||||
.file("retroachievements/rcheevos/src/rapi/rc_api_common.c")
|
||||
.file("retroachievements/rcheevos/src/rapi/rc_api_info.c")
|
||||
.file("retroachievements/rcheevos/src/rapi/rc_api_runtime.c")
|
||||
.file("retroachievements/rcheevos/src/rapi/rc_api_user.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/aes.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/cdreader.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/md5.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/hash.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/hash_disc.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/hash_encrypted.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/hash_rom.c")
|
||||
.file("retroachievements/rcheevos/src/rhash/hash_zip.c")
|
||||
.file("retroachievements/retroachievements.c");
|
||||
|
||||
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let opt_flag = if arch == "x86_64" {
|
||||
"-march=x86-64-v3"
|
||||
} else if arch == "aarch64" && os == "macos" {
|
||||
"-march=armv8.4-a"
|
||||
} else if arch == "aarch64" && os != "macos" {
|
||||
"-march=armv8.2-a"
|
||||
} else {
|
||||
panic!("unknown arch")
|
||||
};
|
||||
|
||||
volk_build.flag(opt_flag);
|
||||
rdp_build.flag(opt_flag);
|
||||
simd_build.flag(opt_flag);
|
||||
retroachievements_build.flag(opt_flag);
|
||||
|
||||
if os == "windows" {
|
||||
volk_build.flag("-DVK_USE_PLATFORM_WIN32_KHR");
|
||||
rdp_build.flag("-DVK_USE_PLATFORM_WIN32_KHR");
|
||||
|
||||
winresource::WindowsResource::new()
|
||||
.set_icon("data/icon/icon.ico")
|
||||
.compile()
|
||||
.unwrap();
|
||||
} else if os == "macos" {
|
||||
let output = std::process::Command::new("clang")
|
||||
.args(["--print-runtime-dir"])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let runtime_dir = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
|
||||
println!("cargo:rustc-link-search=native={}", runtime_dir);
|
||||
println!("cargo:rustc-link-lib=static=clang_rt.osx");
|
||||
}
|
||||
|
||||
volk_build.flag("-flto=thin");
|
||||
rdp_build.flag("-flto=thin");
|
||||
simd_build.flag("-flto=thin");
|
||||
retroachievements_build.flag("-flto=thin");
|
||||
|
||||
volk_build.compile("volk");
|
||||
rdp_build.compile("parallel-rdp");
|
||||
retroachievements_build.compile("retroachievements");
|
||||
|
||||
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
|
||||
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_rdram")
|
||||
.allowlist_function("ra_do_frame")
|
||||
.allowlist_function("ra_do_idle")
|
||||
.allowlist_function("ra_http_callback")
|
||||
.allowlist_function("ra_login_user")
|
||||
.allowlist_function("ra_login_token_user")
|
||||
.allowlist_function("ra_state_size")
|
||||
.allowlist_function("ra_save_state")
|
||||
.allowlist_function("ra_load_state")
|
||||
.allowlist_function("ra_get_rich_presence");
|
||||
|
||||
if cfg!(feature = "gui") {
|
||||
retroachievements_builder = retroachievements_builder
|
||||
.allowlist_function("ra_logout_user")
|
||||
.allowlist_function("ra_get_username");
|
||||
}
|
||||
|
||||
let retroachievements_bindings = retroachievements_builder
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
retroachievements_bindings
|
||||
.write_to_file(out_path.join("retroachievements_bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
let parallel_bindings = bindgen::Builder::default()
|
||||
.header("parallel-rdp/interface.hpp")
|
||||
.allowlist_function("rdp_init")
|
||||
.allowlist_function("rdp_close")
|
||||
.allowlist_function("rdp_set_vi_register")
|
||||
.allowlist_function("rdp_update_screen")
|
||||
.allowlist_function("rdp_render_frame")
|
||||
.allowlist_function("rdp_process_commands")
|
||||
.allowlist_function("rdp_onscreen_message")
|
||||
.allowlist_function("rdp_check_callback")
|
||||
.allowlist_function("rdp_check_framebuffers")
|
||||
.allowlist_function("rdp_state_size")
|
||||
.allowlist_function("rdp_save_state")
|
||||
.allowlist_function("rdp_load_state")
|
||||
.allowlist_function("rdp_idle")
|
||||
.allowlist_function("rdp_set_fps")
|
||||
.allowlist_function("get_joystick_event")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
parallel_bindings
|
||||
.write_to_file(out_path.join("parallel_bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
if arch == "aarch64" {
|
||||
let simd_bindings = bindgen::Builder::default()
|
||||
.header("src/compat/sse2neon/sse2neon.h")
|
||||
.allowlist_function("_mm_setzero_si128")
|
||||
.allowlist_function("_mm_set_epi8")
|
||||
.allowlist_function("_mm_movemask_epi8")
|
||||
.allowlist_function("_mm_shuffle_epi8")
|
||||
.allowlist_function("_mm_packs_epi16")
|
||||
.allowlist_function("_mm_set_epi16")
|
||||
.allowlist_function("_mm_cmpeq_epi8")
|
||||
.allowlist_function("_mm_and_si128")
|
||||
.allowlist_function("_mm_set1_epi8")
|
||||
.allowlist_function("_mm_mullo_epi16")
|
||||
.allowlist_function("_mm_cmpeq_epi16")
|
||||
.allowlist_function("_mm_add_epi16")
|
||||
.allowlist_function("_mm_slli_epi16")
|
||||
.allowlist_function("_mm_mulhi_epi16")
|
||||
.allowlist_function("_mm_srai_epi16")
|
||||
.allowlist_function("_mm_andnot_si128")
|
||||
.allowlist_function("_mm_or_si128")
|
||||
.allowlist_function("_mm_mulhi_epu16")
|
||||
.allowlist_function("_mm_sub_epi16")
|
||||
.allowlist_function("_mm_unpacklo_epi16")
|
||||
.allowlist_function("_mm_unpackhi_epi16")
|
||||
.allowlist_function("_mm_packs_epi32")
|
||||
.allowlist_function("_mm_adds_epu16")
|
||||
.allowlist_function("_mm_cmpgt_epi16")
|
||||
.allowlist_function("_mm_blendv_epi8")
|
||||
.allowlist_function("_mm_min_epi16")
|
||||
.allowlist_function("_mm_max_epi16")
|
||||
.allowlist_function("_mm_subs_epi16")
|
||||
.allowlist_function("_mm_adds_epi16")
|
||||
.allowlist_function("_mm_xor_si128")
|
||||
.allowlist_function("_mm_cmplt_epi16")
|
||||
.allowlist_function("_mm_subs_epu16")
|
||||
.allowlist_function("_mm_set1_epi32")
|
||||
.allowlist_function("_mm_set1_epi16")
|
||||
.blocklist_type("__m128i")
|
||||
.blocklist_type("int64x2_t")
|
||||
.wrap_static_fns(true)
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
simd_bindings
|
||||
.write_to_file(out_path.join("simd_bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
simd_build
|
||||
.std("c17")
|
||||
.flag("-D_POSIX_C_SOURCE=200112L")
|
||||
.flag("-DSSE2NEON_SUPPRESS_WARNINGS")
|
||||
.file("src/compat/aarch64.c")
|
||||
.file(std::env::temp_dir().join("bindgen").join("extern.c"))
|
||||
.include(".")
|
||||
.compile("simd");
|
||||
}
|
||||
|
||||
let mut git_output = std::process::Command::new("git");
|
||||
git_output.args(["describe", "--dirty"]);
|
||||
|
||||
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");
|
||||
let netplay_id = std::env::var("NETPLAY_ID").unwrap_or("gopher64".to_string());
|
||||
println!("cargo:rustc-env=NETPLAY_ID={netplay_id}");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=RA_HARDCORE");
|
||||
println!("cargo:rustc-check-cfg=cfg(ra_hardcore_enabled)");
|
||||
if let Ok(ra_hardcore) = std::env::var("RA_HARDCORE")
|
||||
&& ra_hardcore.to_lowercase() == "true"
|
||||
{
|
||||
println!("cargo:rustc-cfg=ra_hardcore_enabled");
|
||||
}
|
||||
if os == "android" {
|
||||
copy_dir_all(
|
||||
std::path::Path::new(sdl3_src::SOURCE_DIR)
|
||||
.join("android-project/app/src/main/java/org/libsdl/app"),
|
||||
"android-project/app/src/main/java/org/libsdl/app",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_dir_all(
|
||||
src: impl AsRef<std::path::Path>,
|
||||
dst: impl AsRef<std::path::Path>,
|
||||
) -> std::io::Result<()> {
|
||||
std::fs::create_dir_all(&dst)?;
|
||||
for entry in std::fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
} else {
|
||||
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
1
data/cheats.json
Normal file
6
data/icon/generate.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
magick gopher64.png -resize 512x512 gopher64_512x512.png
|
||||
magick gopher64.png -resize 256x256 gopher64_256x256.png
|
||||
magick gopher64.png -resize 128x128 gopher64_128x128.png
|
||||
magick gopher64.png -resize 256x256 icon.ico
|
||||
BIN
data/icon/gopher64.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
data/icon/gopher64_128x128.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
data/icon/gopher64_256x256.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
data/icon/gopher64_512x512.png
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
data/icon/icon.ico
Normal file
|
After Width: | Height: | Size: 64 KiB |
10
data/macos/entitlements_child.plist
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.inherit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
8
data/macos/entitlements_dev.plist
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
18
data/macos/entitlements_prod.plist
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.usb</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.bluetooth</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
135
data/shader/crt-aperture.frag
Normal file
@ -0,0 +1,135 @@
|
||||
#version 450
|
||||
|
||||
/*
|
||||
CRT Shader by EasyMode
|
||||
License: GPL
|
||||
*/
|
||||
|
||||
layout(push_constant) uniform Push
|
||||
{
|
||||
vec4 SourceSize;
|
||||
vec4 OutputSize;
|
||||
} params;
|
||||
|
||||
#define SHARPNESS_IMAGE 1.0
|
||||
#define SHARPNESS_EDGES 3.0
|
||||
#define GLOW_WIDTH 0.5
|
||||
#define GLOW_HEIGHT 0.5
|
||||
#define GLOW_HALATION 0.1
|
||||
#define GLOW_DIFFUSION 0.05
|
||||
#define MASK_COLORS 2.0
|
||||
#define MASK_STRENGTH 0.3
|
||||
#define MASK_SIZE 1.0
|
||||
#define SCANLINE_SIZE_MIN 0.5
|
||||
#define SCANLINE_SIZE_MAX 1.5
|
||||
#define SCANLINE_SHAPE 2.5
|
||||
#define SCANLINE_OFFSET 1.0
|
||||
#define GAMMA_INPUT 2.4
|
||||
#define GAMMA_OUTPUT 2.4
|
||||
#define BRIGHTNESS 1.5
|
||||
|
||||
layout(std140, set = 0, binding = 0) uniform UBO
|
||||
{
|
||||
mat4 MVP;
|
||||
} global;
|
||||
|
||||
#define FIX(c) max(abs(c), 1e-5)
|
||||
#define PI 3.141592653589
|
||||
#define TEX2D(c) pow(texture(tex, c).rgb, vec3(GAMMA_INPUT))
|
||||
#define saturate(c) clamp(c, 0.0, 1.0)
|
||||
|
||||
mat3x3 get_color_matrix(sampler2D tex, vec2 co, vec2 dx)
|
||||
{
|
||||
return mat3x3(TEX2D(co - dx), TEX2D(co), TEX2D(co + dx));
|
||||
}
|
||||
|
||||
vec3 blur(mat3 m, float dist, float rad)
|
||||
{
|
||||
vec3 x = vec3(dist - 1.0, dist, dist + 1.0) / rad;
|
||||
vec3 w = exp2(x * x * -1.0);
|
||||
|
||||
return (m[0] * w.x + m[1] * w.y + m[2] * w.z) / (w.x + w.y + w.z);
|
||||
}
|
||||
|
||||
vec3 filter_gaussian(sampler2D tex, vec2 co, vec2 tex_size)
|
||||
{
|
||||
vec2 dx = vec2(1.0 / tex_size.x, 0.0);
|
||||
vec2 dy = vec2(0.0, 1.0 / tex_size.y);
|
||||
vec2 pix_co = co * tex_size;
|
||||
vec2 tex_co = (floor(pix_co) + 0.5) / tex_size;
|
||||
vec2 dist = (fract(pix_co) - 0.5) * -1.0;
|
||||
|
||||
mat3x3 line0 = get_color_matrix(tex, tex_co - dy, dx);
|
||||
mat3x3 line1 = get_color_matrix(tex, tex_co, dx);
|
||||
mat3x3 line2 = get_color_matrix(tex, tex_co + dy, dx);
|
||||
mat3x3 column = mat3x3(blur(line0, dist.x, GLOW_WIDTH),
|
||||
blur(line1, dist.x, GLOW_WIDTH),
|
||||
blur(line2, dist.x, GLOW_WIDTH));
|
||||
|
||||
return blur(column, dist.y, GLOW_HEIGHT);
|
||||
}
|
||||
|
||||
vec3 filter_lanczos(sampler2D tex, vec2 co, vec2 tex_size, float sharp)
|
||||
{
|
||||
tex_size.x *= sharp;
|
||||
|
||||
vec2 dx = vec2(1.0 / tex_size.x, 0.0);
|
||||
vec2 pix_co = co * tex_size - vec2(0.5, 0.0);
|
||||
vec2 tex_co = (floor(pix_co) + vec2(0.5, 0.001)) / tex_size;
|
||||
vec2 dist = fract(pix_co);
|
||||
vec4 coef = PI * vec4(dist.x + 1.0, dist.x, dist.x - 1.0, dist.x - 2.0);
|
||||
|
||||
coef = FIX(coef);
|
||||
coef = 2.0 * sin(coef) * sin(coef / 2.0) / (coef * coef);
|
||||
coef /= dot(coef, vec4(1.0));
|
||||
|
||||
vec4 col1 = vec4(TEX2D(tex_co), 1.0);
|
||||
vec4 col2 = vec4(TEX2D(tex_co + dx), 1.0);
|
||||
|
||||
return (mat4x4(col1, col1, col2, col2) * coef).rgb;
|
||||
}
|
||||
|
||||
vec3 get_scanline_weight(float x, vec3 col)
|
||||
{
|
||||
vec3 beam = mix(vec3(SCANLINE_SIZE_MIN), vec3(SCANLINE_SIZE_MAX), pow(col, vec3(1.0 / SCANLINE_SHAPE)));
|
||||
vec3 x_mul = 2.0 / beam;
|
||||
vec3 x_offset = x_mul * 0.5;
|
||||
|
||||
return smoothstep(0.0, 1.0, 1.0 - abs(x * x_mul - x_offset)) * x_offset;
|
||||
}
|
||||
|
||||
vec3 get_mask_weight(float x)
|
||||
{
|
||||
float i = mod(floor(x * params.OutputSize.x * params.SourceSize.x / (params.SourceSize.x * MASK_SIZE)), MASK_COLORS);
|
||||
|
||||
if (i == 0.0) return mix(vec3(1.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), MASK_COLORS - 2.0);
|
||||
else if (i == 1.0) return vec3(0.0, 1.0, 0.0);
|
||||
else return vec3(0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#pragma stage fragment
|
||||
layout(location = 0) in vec2 vTexCoord;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
layout(set = 0, binding = 0) uniform sampler2D Source;
|
||||
|
||||
void main()
|
||||
{
|
||||
float scale = floor(params.OutputSize.y * params.SourceSize.w);
|
||||
float offset = 1.0 / scale * 0.5;
|
||||
|
||||
if (bool(mod(scale, 2.0))) offset = 0.0;
|
||||
|
||||
vec2 co = (vTexCoord * params.SourceSize.xy - vec2(0.0, offset * SCANLINE_OFFSET)) * params.SourceSize.zw;
|
||||
vec3 col_glow = filter_gaussian(Source, co, params.SourceSize.xy);
|
||||
vec3 col_soft = filter_lanczos(Source, co, params.SourceSize.xy, SHARPNESS_IMAGE);
|
||||
vec3 col_sharp = filter_lanczos(Source, co, params.SourceSize.xy, SHARPNESS_EDGES);
|
||||
vec3 col = sqrt(col_sharp * col_soft);
|
||||
|
||||
col *= get_scanline_weight(fract(co.y * params.SourceSize.y), col_soft);
|
||||
col_glow = saturate(col_glow - col);
|
||||
col += col_glow * col_glow * GLOW_HALATION;
|
||||
col = mix(col, col * get_mask_weight(vTexCoord.x) * MASK_COLORS, MASK_STRENGTH);
|
||||
col += col_glow * GLOW_DIFFUSION;
|
||||
col = pow(col * BRIGHTNESS, vec3(1.0 / GAMMA_OUTPUT));
|
||||
FragColor = vec4(col, 1.0);
|
||||
}
|
||||
453
data/shader/crt_frag.spv
Normal file
@ -0,0 +1,453 @@
|
||||
{0x07230203,0x00010300,0x000d000b,0x000004fd,
|
||||
0x00000000,0x00020011,0x00000001,0x0006000b,
|
||||
0x00000001,0x4c534c47,0x6474732e,0x3035342e,
|
||||
0x00000000,0x0003000e,0x00000000,0x00000001,
|
||||
0x0007000f,0x00000004,0x00000004,0x6e69616d,
|
||||
0x00000000,0x000001ba,0x0000021a,0x00030010,
|
||||
0x00000004,0x00000007,0x00030047,0x00000185,
|
||||
0x00000002,0x00050048,0x00000185,0x00000000,
|
||||
0x00000023,0x00000000,0x00050048,0x00000185,
|
||||
0x00000001,0x00000023,0x00000010,0x00040047,
|
||||
0x000001ba,0x0000001e,0x00000000,0x00040047,
|
||||
0x000001ca,0x00000021,0x00000000,0x00040047,
|
||||
0x000001ca,0x00000022,0x00000000,0x00040047,
|
||||
0x0000021a,0x0000001e,0x00000000,0x00020013,
|
||||
0x00000002,0x00030021,0x00000003,0x00000002,
|
||||
0x00030016,0x00000006,0x00000020,0x00090019,
|
||||
0x00000007,0x00000006,0x00000001,0x00000000,
|
||||
0x00000000,0x00000000,0x00000001,0x00000000,
|
||||
0x0003001b,0x00000008,0x00000007,0x00040020,
|
||||
0x00000009,0x00000000,0x00000008,0x00040017,
|
||||
0x0000000a,0x00000006,0x00000002,0x00040017,
|
||||
0x0000000c,0x00000006,0x00000003,0x00040017,
|
||||
0x00000037,0x00000006,0x00000004,0x0004002b,
|
||||
0x00000006,0x0000003a,0x4019999a,0x0006002c,
|
||||
0x0000000c,0x0000003b,0x0000003a,0x0000003a,
|
||||
0x0000003a,0x0004002b,0x00000006,0x00000049,
|
||||
0x3f800000,0x0004002b,0x00000006,0x0000004a,
|
||||
0x00000000,0x0004002b,0x00000006,0x00000068,
|
||||
0xbf800000,0x00040015,0x0000006b,0x00000020,
|
||||
0x00000001,0x0004002b,0x0000006b,0x0000006c,
|
||||
0x00000000,0x00040015,0x0000006f,0x00000020,
|
||||
0x00000000,0x0004002b,0x0000006f,0x00000070,
|
||||
0x00000000,0x0004002b,0x0000006b,0x00000074,
|
||||
0x00000001,0x0004002b,0x0000006f,0x00000077,
|
||||
0x00000001,0x0004002b,0x00000006,0x000000a1,
|
||||
0x3f000000,0x0005002c,0x0000000a,0x000000fc,
|
||||
0x000000a1,0x0000004a,0x0004002b,0x00000006,
|
||||
0x00000101,0x3a83126f,0x0005002c,0x0000000a,
|
||||
0x00000102,0x000000a1,0x00000101,0x0004002b,
|
||||
0x00000006,0x0000010b,0x40490fdb,0x0004002b,
|
||||
0x00000006,0x00000116,0x40000000,0x0004002b,
|
||||
0x00000006,0x0000011c,0x3727c5ac,0x0007002c,
|
||||
0x00000037,0x0000012c,0x00000049,0x00000049,
|
||||
0x00000049,0x00000049,0x00040018,0x00000149,
|
||||
0x00000037,0x00000004,0x0006002c,0x0000000c,
|
||||
0x00000165,0x000000a1,0x000000a1,0x000000a1,
|
||||
0x0004002b,0x00000006,0x00000166,0x3fc00000,
|
||||
0x0006002c,0x0000000c,0x00000167,0x00000166,
|
||||
0x00000166,0x00000166,0x0004002b,0x00000006,
|
||||
0x00000169,0x3ecccccd,0x0006002c,0x0000000c,
|
||||
0x0000016a,0x00000169,0x00000169,0x00000169,
|
||||
0x0004001e,0x00000185,0x00000037,0x00000037,
|
||||
0x00040020,0x00000186,0x00000009,0x00000185,
|
||||
0x0004003b,0x00000186,0x00000187,0x00000009,
|
||||
0x00040020,0x00000188,0x00000009,0x00000006,
|
||||
0x00020014,0x00000196,0x0006002c,0x0000000c,
|
||||
0x0000019a,0x00000049,0x0000004a,0x00000049,
|
||||
0x0006002c,0x0000000c,0x000001a1,0x0000004a,
|
||||
0x00000049,0x0000004a,0x0006002c,0x0000000c,
|
||||
0x000001a4,0x0000004a,0x0000004a,0x00000049,
|
||||
0x0004002b,0x0000006f,0x000001aa,0x00000003,
|
||||
0x00040020,0x000001b9,0x00000001,0x0000000a,
|
||||
0x0004003b,0x000001b9,0x000001ba,0x00000001,
|
||||
0x00040020,0x000001bc,0x00000009,0x00000037,
|
||||
0x0004003b,0x00000009,0x000001ca,0x00000000,
|
||||
0x0004002b,0x00000006,0x000001dc,0x40400000,
|
||||
0x0004002b,0x00000006,0x000001ff,0x3dcccccd,
|
||||
0x00040020,0x00000206,0x00000001,0x00000006,
|
||||
0x0004002b,0x00000006,0x0000020c,0x3e99999a,
|
||||
0x0004002b,0x00000006,0x00000210,0x3d4ccccd,
|
||||
0x0004002b,0x00000006,0x00000216,0x3ed55555,
|
||||
0x0006002c,0x0000000c,0x00000217,0x00000216,
|
||||
0x00000216,0x00000216,0x00040020,0x00000219,
|
||||
0x00000003,0x00000037,0x0004003b,0x00000219,
|
||||
0x0000021a,0x00000003,0x0005002c,0x0000000a,
|
||||
0x000004f3,0x000000a1,0x000000a1,0x0007002c,
|
||||
0x00000037,0x000004f4,0x0000011c,0x0000011c,
|
||||
0x0000011c,0x0000011c,0x0006002c,0x0000000c,
|
||||
0x000004f6,0x00000116,0x00000116,0x00000116,
|
||||
0x0006002c,0x0000000c,0x000004f7,0x00000049,
|
||||
0x00000049,0x00000049,0x0006002c,0x0000000c,
|
||||
0x000004f8,0x0000004a,0x0000004a,0x0000004a,
|
||||
0x0006002c,0x0000000c,0x000004f9,0x0000020c,
|
||||
0x0000020c,0x0000020c,0x0007002c,0x00000037,
|
||||
0x000004fa,0x000000a1,0x000000a1,0x000000a1,
|
||||
0x000000a1,0x0004002b,0x00000006,0x000004fb,
|
||||
0x3eaaaaab,0x00050036,0x00000002,0x00000004,
|
||||
0x00000000,0x00000003,0x000200f8,0x00000005,
|
||||
0x00060041,0x00000188,0x000001a8,0x00000187,
|
||||
0x00000074,0x00000077,0x0004003d,0x00000006,
|
||||
0x000001a9,0x000001a8,0x00060041,0x00000188,
|
||||
0x000001ab,0x00000187,0x0000006c,0x000001aa,
|
||||
0x0004003d,0x00000006,0x000001ac,0x000001ab,
|
||||
0x00050085,0x00000006,0x000001ad,0x000001a9,
|
||||
0x000001ac,0x0006000c,0x00000006,0x000001ae,
|
||||
0x00000001,0x00000008,0x000001ad,0x00050088,
|
||||
0x00000006,0x000001b2,0x000000a1,0x000001ae,
|
||||
0x0005008d,0x00000006,0x000001b4,0x000001ae,
|
||||
0x00000116,0x000500b7,0x00000196,0x000001b5,
|
||||
0x000001b4,0x0000004a,0x000600a9,0x00000006,
|
||||
0x000004fc,0x000001b5,0x0000004a,0x000001b2,
|
||||
0x0004003d,0x0000000a,0x000001bb,0x000001ba,
|
||||
0x00050041,0x000001bc,0x000001bd,0x00000187,
|
||||
0x0000006c,0x0004003d,0x00000037,0x000001be,
|
||||
0x000001bd,0x0007004f,0x0000000a,0x000001bf,
|
||||
0x000001be,0x000001be,0x00000000,0x00000001,
|
||||
0x00050085,0x0000000a,0x000001c0,0x000001bb,
|
||||
0x000001bf,0x00050050,0x0000000a,0x000001c3,
|
||||
0x0000004a,0x000004fc,0x00050083,0x0000000a,
|
||||
0x000001c4,0x000001c0,0x000001c3,0x0007004f,
|
||||
0x0000000a,0x000001c7,0x000001be,0x000001be,
|
||||
0x00000002,0x00000003,0x00050085,0x0000000a,
|
||||
0x000001c8,0x000001c4,0x000001c7,0x00050051,
|
||||
0x00000006,0x00000249,0x000001be,0x00000000,
|
||||
0x00050088,0x00000006,0x0000024a,0x00000049,
|
||||
0x00000249,0x00050050,0x0000000a,0x0000024b,
|
||||
0x0000024a,0x0000004a,0x00050051,0x00000006,
|
||||
0x0000024d,0x000001be,0x00000001,0x00050088,
|
||||
0x00000006,0x0000024e,0x00000049,0x0000024d,
|
||||
0x00050050,0x0000000a,0x0000024f,0x0000004a,
|
||||
0x0000024e,0x00050085,0x0000000a,0x00000252,
|
||||
0x000001c8,0x000001bf,0x0006000c,0x0000000a,
|
||||
0x00000254,0x00000001,0x00000008,0x00000252,
|
||||
0x00050081,0x0000000a,0x00000256,0x00000254,
|
||||
0x000004f3,0x00050088,0x0000000a,0x00000258,
|
||||
0x00000256,0x000001bf,0x0006000c,0x0000000a,
|
||||
0x0000025a,0x00000001,0x0000000a,0x00000252,
|
||||
0x00050083,0x0000000a,0x0000025c,0x0000025a,
|
||||
0x000004f3,0x0005008e,0x0000000a,0x0000025d,
|
||||
0x0000025c,0x00000068,0x00050083,0x0000000a,
|
||||
0x00000260,0x00000258,0x0000024f,0x0004003d,
|
||||
0x00000008,0x0000028a,0x000001ca,0x00050083,
|
||||
0x0000000a,0x0000028d,0x00000260,0x0000024b,
|
||||
0x00050057,0x00000037,0x0000028e,0x0000028a,
|
||||
0x0000028d,0x0008004f,0x0000000c,0x0000028f,
|
||||
0x0000028e,0x0000028e,0x00000000,0x00000001,
|
||||
0x00000002,0x0007000c,0x0000000c,0x00000290,
|
||||
0x00000001,0x0000001a,0x0000028f,0x0000003b,
|
||||
0x00050057,0x00000037,0x00000293,0x0000028a,
|
||||
0x00000260,0x0008004f,0x0000000c,0x00000294,
|
||||
0x00000293,0x00000293,0x00000000,0x00000001,
|
||||
0x00000002,0x0007000c,0x0000000c,0x00000295,
|
||||
0x00000001,0x0000001a,0x00000294,0x0000003b,
|
||||
0x00050081,0x0000000a,0x00000299,0x00000260,
|
||||
0x0000024b,0x00050057,0x00000037,0x0000029a,
|
||||
0x0000028a,0x00000299,0x0008004f,0x0000000c,
|
||||
0x0000029b,0x0000029a,0x0000029a,0x00000000,
|
||||
0x00000001,0x00000002,0x0007000c,0x0000000c,
|
||||
0x0000029c,0x00000001,0x0000001a,0x0000029b,
|
||||
0x0000003b,0x00050083,0x0000000a,0x000002af,
|
||||
0x00000258,0x0000024b,0x00050057,0x00000037,
|
||||
0x000002b0,0x0000028a,0x000002af,0x0008004f,
|
||||
0x0000000c,0x000002b1,0x000002b0,0x000002b0,
|
||||
0x00000000,0x00000001,0x00000002,0x0007000c,
|
||||
0x0000000c,0x000002b2,0x00000001,0x0000001a,
|
||||
0x000002b1,0x0000003b,0x00050057,0x00000037,
|
||||
0x000002b5,0x0000028a,0x00000258,0x0008004f,
|
||||
0x0000000c,0x000002b6,0x000002b5,0x000002b5,
|
||||
0x00000000,0x00000001,0x00000002,0x0007000c,
|
||||
0x0000000c,0x000002b7,0x00000001,0x0000001a,
|
||||
0x000002b6,0x0000003b,0x00050081,0x0000000a,
|
||||
0x000002bb,0x00000258,0x0000024b,0x00050057,
|
||||
0x00000037,0x000002bc,0x0000028a,0x000002bb,
|
||||
0x0008004f,0x0000000c,0x000002bd,0x000002bc,
|
||||
0x000002bc,0x00000000,0x00000001,0x00000002,
|
||||
0x0007000c,0x0000000c,0x000002be,0x00000001,
|
||||
0x0000001a,0x000002bd,0x0000003b,0x00050081,
|
||||
0x0000000a,0x00000268,0x00000258,0x0000024f,
|
||||
0x00050083,0x0000000a,0x000002d1,0x00000268,
|
||||
0x0000024b,0x00050057,0x00000037,0x000002d2,
|
||||
0x0000028a,0x000002d1,0x0008004f,0x0000000c,
|
||||
0x000002d3,0x000002d2,0x000002d2,0x00000000,
|
||||
0x00000001,0x00000002,0x0007000c,0x0000000c,
|
||||
0x000002d4,0x00000001,0x0000001a,0x000002d3,
|
||||
0x0000003b,0x00050057,0x00000037,0x000002d7,
|
||||
0x0000028a,0x00000268,0x0008004f,0x0000000c,
|
||||
0x000002d8,0x000002d7,0x000002d7,0x00000000,
|
||||
0x00000001,0x00000002,0x0007000c,0x0000000c,
|
||||
0x000002d9,0x00000001,0x0000001a,0x000002d8,
|
||||
0x0000003b,0x00050081,0x0000000a,0x000002dd,
|
||||
0x00000268,0x0000024b,0x00050057,0x00000037,
|
||||
0x000002de,0x0000028a,0x000002dd,0x0008004f,
|
||||
0x0000000c,0x000002df,0x000002de,0x000002de,
|
||||
0x00000000,0x00000001,0x00000002,0x0007000c,
|
||||
0x0000000c,0x000002e0,0x00000001,0x0000001a,
|
||||
0x000002df,0x0000003b,0x00050051,0x00000006,
|
||||
0x0000026d,0x0000025d,0x00000000,0x00050083,
|
||||
0x00000006,0x000002f3,0x0000026d,0x00000049,
|
||||
0x00050081,0x00000006,0x000002f6,0x0000026d,
|
||||
0x00000049,0x00060050,0x0000000c,0x000002f7,
|
||||
0x000002f3,0x0000026d,0x000002f6,0x00050085,
|
||||
0x0000000c,0x000002fa,0x000002f7,0x000004f6,
|
||||
0x00050085,0x0000000c,0x000002fd,0x000002fa,
|
||||
0x000002fa,0x0005008e,0x0000000c,0x000002fe,
|
||||
0x000002fd,0x00000068,0x0006000c,0x0000000c,
|
||||
0x000002ff,0x00000001,0x0000001d,0x000002fe,
|
||||
0x00050051,0x00000006,0x00000303,0x000002ff,
|
||||
0x00000000,0x0005008e,0x0000000c,0x00000304,
|
||||
0x00000290,0x00000303,0x00050051,0x00000006,
|
||||
0x00000308,0x000002ff,0x00000001,0x0005008e,
|
||||
0x0000000c,0x00000309,0x00000295,0x00000308,
|
||||
0x00050081,0x0000000c,0x0000030a,0x00000304,
|
||||
0x00000309,0x00050051,0x00000006,0x0000030e,
|
||||
0x000002ff,0x00000002,0x0005008e,0x0000000c,
|
||||
0x0000030f,0x0000029c,0x0000030e,0x00050081,
|
||||
0x0000000c,0x00000310,0x0000030a,0x0000030f,
|
||||
0x00050081,0x00000006,0x00000315,0x00000303,
|
||||
0x00000308,0x00050081,0x00000006,0x00000318,
|
||||
0x00000315,0x0000030e,0x00060050,0x0000000c,
|
||||
0x00000319,0x00000318,0x00000318,0x00000318,
|
||||
0x00050088,0x0000000c,0x0000031a,0x00000310,
|
||||
0x00000319,0x0005008e,0x0000000c,0x00000331,
|
||||
0x000002b2,0x00000303,0x0005008e,0x0000000c,
|
||||
0x00000336,0x000002b7,0x00000308,0x00050081,
|
||||
0x0000000c,0x00000337,0x00000331,0x00000336,
|
||||
0x0005008e,0x0000000c,0x0000033c,0x000002be,
|
||||
0x0000030e,0x00050081,0x0000000c,0x0000033d,
|
||||
0x00000337,0x0000033c,0x00050088,0x0000000c,
|
||||
0x00000347,0x0000033d,0x00000319,0x0005008e,
|
||||
0x0000000c,0x0000035e,0x000002d4,0x00000303,
|
||||
0x0005008e,0x0000000c,0x00000363,0x000002d9,
|
||||
0x00000308,0x00050081,0x0000000c,0x00000364,
|
||||
0x0000035e,0x00000363,0x0005008e,0x0000000c,
|
||||
0x00000369,0x000002e0,0x0000030e,0x00050081,
|
||||
0x0000000c,0x0000036a,0x00000364,0x00000369,
|
||||
0x00050088,0x0000000c,0x00000374,0x0000036a,
|
||||
0x00000319,0x00050051,0x00000006,0x00000286,
|
||||
0x0000025d,0x00000001,0x00050083,0x00000006,
|
||||
0x0000037a,0x00000286,0x00000049,0x00050081,
|
||||
0x00000006,0x0000037d,0x00000286,0x00000049,
|
||||
0x00060050,0x0000000c,0x0000037e,0x0000037a,
|
||||
0x00000286,0x0000037d,0x00050085,0x0000000c,
|
||||
0x00000381,0x0000037e,0x000004f6,0x00050085,
|
||||
0x0000000c,0x00000384,0x00000381,0x00000381,
|
||||
0x0005008e,0x0000000c,0x00000385,0x00000384,
|
||||
0x00000068,0x0006000c,0x0000000c,0x00000386,
|
||||
0x00000001,0x0000001d,0x00000385,0x00050051,
|
||||
0x00000006,0x0000038a,0x00000386,0x00000000,
|
||||
0x0005008e,0x0000000c,0x0000038b,0x0000031a,
|
||||
0x0000038a,0x00050051,0x00000006,0x0000038f,
|
||||
0x00000386,0x00000001,0x0005008e,0x0000000c,
|
||||
0x00000390,0x00000347,0x0000038f,0x00050081,
|
||||
0x0000000c,0x00000391,0x0000038b,0x00000390,
|
||||
0x00050051,0x00000006,0x00000395,0x00000386,
|
||||
0x00000002,0x0005008e,0x0000000c,0x00000396,
|
||||
0x00000374,0x00000395,0x00050081,0x0000000c,
|
||||
0x00000397,0x00000391,0x00000396,0x00050081,
|
||||
0x00000006,0x0000039c,0x0000038a,0x0000038f,
|
||||
0x00050081,0x00000006,0x0000039f,0x0000039c,
|
||||
0x00000395,0x00060050,0x0000000c,0x000003a0,
|
||||
0x0000039f,0x0000039f,0x0000039f,0x00050088,
|
||||
0x0000000c,0x000003a1,0x00000397,0x000003a0,
|
||||
0x00060052,0x0000000a,0x000004e1,0x00000249,
|
||||
0x000001bf,0x00000000,0x00050085,0x0000000a,
|
||||
0x000003b6,0x000001c8,0x000004e1,0x00050083,
|
||||
0x0000000a,0x000003b7,0x000003b6,0x000000fc,
|
||||
0x0006000c,0x0000000a,0x000003b9,0x00000001,
|
||||
0x00000008,0x000003b7,0x00050081,0x0000000a,
|
||||
0x000003ba,0x000003b9,0x00000102,0x00050088,
|
||||
0x0000000a,0x000003bc,0x000003ba,0x000004e1,
|
||||
0x0006000c,0x0000000a,0x000003be,0x00000001,
|
||||
0x0000000a,0x000003b7,0x00050051,0x00000006,
|
||||
0x000003c0,0x000003be,0x00000000,0x00050081,
|
||||
0x00000006,0x000003c1,0x000003c0,0x00000049,
|
||||
0x00050083,0x00000006,0x000003c6,0x000003c0,
|
||||
0x00000049,0x00050083,0x00000006,0x000003c9,
|
||||
0x000003c0,0x00000116,0x00070050,0x00000037,
|
||||
0x000003ca,0x000003c1,0x000003c0,0x000003c6,
|
||||
0x000003c9,0x0005008e,0x00000037,0x000003cb,
|
||||
0x000003ca,0x0000010b,0x0006000c,0x00000037,
|
||||
0x000003cd,0x00000001,0x00000004,0x000003cb,
|
||||
0x0007000c,0x00000037,0x000003cf,0x00000001,
|
||||
0x00000028,0x000003cd,0x000004f4,0x0006000c,
|
||||
0x00000037,0x000003d1,0x00000001,0x0000000d,
|
||||
0x000003cf,0x0005008e,0x00000037,0x000003d2,
|
||||
0x000003d1,0x00000116,0x00050085,0x00000037,
|
||||
0x000003d5,0x000003cf,0x000004fa,0x0006000c,
|
||||
0x00000037,0x000003d6,0x00000001,0x0000000d,
|
||||
0x000003d5,0x00050085,0x00000037,0x000003d7,
|
||||
0x000003d2,0x000003d6,0x00050085,0x00000037,
|
||||
0x000003da,0x000003cf,0x000003cf,0x00050088,
|
||||
0x00000037,0x000003db,0x000003d7,0x000003da,
|
||||
0x00050094,0x00000006,0x000003dd,0x000003db,
|
||||
0x0000012c,0x00070050,0x00000037,0x000003df,
|
||||
0x000003dd,0x000003dd,0x000003dd,0x000003dd,
|
||||
0x00050088,0x00000037,0x000003e0,0x000003db,
|
||||
0x000003df,0x00050057,0x00000037,0x000003e3,
|
||||
0x0000028a,0x000003bc,0x0008004f,0x0000000c,
|
||||
0x000003e4,0x000003e3,0x000003e3,0x00000000,
|
||||
0x00000001,0x00000002,0x0007000c,0x0000000c,
|
||||
0x000003e5,0x00000001,0x0000001a,0x000003e4,
|
||||
0x0000003b,0x00050051,0x00000006,0x000003e6,
|
||||
0x000003e5,0x00000000,0x00050051,0x00000006,
|
||||
0x000003e7,0x000003e5,0x00000001,0x00050051,
|
||||
0x00000006,0x000003e8,0x000003e5,0x00000002,
|
||||
0x00050081,0x0000000a,0x000003ed,0x000003bc,
|
||||
0x0000024b,0x00050057,0x00000037,0x000003ee,
|
||||
0x0000028a,0x000003ed,0x0008004f,0x0000000c,
|
||||
0x000003ef,0x000003ee,0x000003ee,0x00000000,
|
||||
0x00000001,0x00000002,0x0007000c,0x0000000c,
|
||||
0x000003f0,0x00000001,0x0000001a,0x000003ef,
|
||||
0x0000003b,0x00050051,0x00000006,0x000003f1,
|
||||
0x000003f0,0x00000000,0x00050051,0x00000006,
|
||||
0x000003f2,0x000003f0,0x00000001,0x00050051,
|
||||
0x00000006,0x000003f3,0x000003f0,0x00000002,
|
||||
0x00070050,0x00000037,0x00000407,0x000003e6,
|
||||
0x000003e7,0x000003e8,0x00000049,0x00070050,
|
||||
0x00000037,0x00000409,0x000003f1,0x000003f2,
|
||||
0x000003f3,0x00000049,0x00070050,0x00000149,
|
||||
0x0000040b,0x00000407,0x00000407,0x00000409,
|
||||
0x00000409,0x00050091,0x00000037,0x0000040d,
|
||||
0x0000040b,0x000003e0,0x0008004f,0x0000000c,
|
||||
0x0000040e,0x0000040d,0x0000040d,0x00000000,
|
||||
0x00000001,0x00000002,0x00050085,0x00000006,
|
||||
0x0000041b,0x00000249,0x000001dc,0x00060052,
|
||||
0x0000000a,0x000004e9,0x0000041b,0x000001bf,
|
||||
0x00000000,0x00050088,0x00000006,0x0000041f,
|
||||
0x000004fb,0x00000249,0x00050050,0x0000000a,
|
||||
0x00000420,0x0000041f,0x0000004a,0x00050085,
|
||||
0x0000000a,0x00000423,0x000001c8,0x000004e9,
|
||||
0x00050083,0x0000000a,0x00000424,0x00000423,
|
||||
0x000000fc,0x0006000c,0x0000000a,0x00000426,
|
||||
0x00000001,0x00000008,0x00000424,0x00050081,
|
||||
0x0000000a,0x00000427,0x00000426,0x00000102,
|
||||
0x00050088,0x0000000a,0x00000429,0x00000427,
|
||||
0x000004e9,0x0006000c,0x0000000a,0x0000042b,
|
||||
0x00000001,0x0000000a,0x00000424,0x00050051,
|
||||
0x00000006,0x0000042d,0x0000042b,0x00000000,
|
||||
0x00050081,0x00000006,0x0000042e,0x0000042d,
|
||||
0x00000049,0x00050083,0x00000006,0x00000433,
|
||||
0x0000042d,0x00000049,0x00050083,0x00000006,
|
||||
0x00000436,0x0000042d,0x00000116,0x00070050,
|
||||
0x00000037,0x00000437,0x0000042e,0x0000042d,
|
||||
0x00000433,0x00000436,0x0005008e,0x00000037,
|
||||
0x00000438,0x00000437,0x0000010b,0x0006000c,
|
||||
0x00000037,0x0000043a,0x00000001,0x00000004,
|
||||
0x00000438,0x0007000c,0x00000037,0x0000043c,
|
||||
0x00000001,0x00000028,0x0000043a,0x000004f4,
|
||||
0x0006000c,0x00000037,0x0000043e,0x00000001,
|
||||
0x0000000d,0x0000043c,0x0005008e,0x00000037,
|
||||
0x0000043f,0x0000043e,0x00000116,0x00050085,
|
||||
0x00000037,0x00000442,0x0000043c,0x000004fa,
|
||||
0x0006000c,0x00000037,0x00000443,0x00000001,
|
||||
0x0000000d,0x00000442,0x00050085,0x00000037,
|
||||
0x00000444,0x0000043f,0x00000443,0x00050085,
|
||||
0x00000037,0x00000447,0x0000043c,0x0000043c,
|
||||
0x00050088,0x00000037,0x00000448,0x00000444,
|
||||
0x00000447,0x00050094,0x00000006,0x0000044a,
|
||||
0x00000448,0x0000012c,0x00070050,0x00000037,
|
||||
0x0000044c,0x0000044a,0x0000044a,0x0000044a,
|
||||
0x0000044a,0x00050088,0x00000037,0x0000044d,
|
||||
0x00000448,0x0000044c,0x00050057,0x00000037,
|
||||
0x00000450,0x0000028a,0x00000429,0x0008004f,
|
||||
0x0000000c,0x00000451,0x00000450,0x00000450,
|
||||
0x00000000,0x00000001,0x00000002,0x0007000c,
|
||||
0x0000000c,0x00000452,0x00000001,0x0000001a,
|
||||
0x00000451,0x0000003b,0x00050051,0x00000006,
|
||||
0x00000453,0x00000452,0x00000000,0x00050051,
|
||||
0x00000006,0x00000454,0x00000452,0x00000001,
|
||||
0x00050051,0x00000006,0x00000455,0x00000452,
|
||||
0x00000002,0x00050081,0x0000000a,0x0000045a,
|
||||
0x00000429,0x00000420,0x00050057,0x00000037,
|
||||
0x0000045b,0x0000028a,0x0000045a,0x0008004f,
|
||||
0x0000000c,0x0000045c,0x0000045b,0x0000045b,
|
||||
0x00000000,0x00000001,0x00000002,0x0007000c,
|
||||
0x0000000c,0x0000045d,0x00000001,0x0000001a,
|
||||
0x0000045c,0x0000003b,0x00050051,0x00000006,
|
||||
0x0000045e,0x0000045d,0x00000000,0x00050051,
|
||||
0x00000006,0x0000045f,0x0000045d,0x00000001,
|
||||
0x00050051,0x00000006,0x00000460,0x0000045d,
|
||||
0x00000002,0x00070050,0x00000037,0x00000474,
|
||||
0x00000453,0x00000454,0x00000455,0x00000049,
|
||||
0x00070050,0x00000037,0x00000476,0x0000045e,
|
||||
0x0000045f,0x00000460,0x00000049,0x00070050,
|
||||
0x00000149,0x00000478,0x00000474,0x00000474,
|
||||
0x00000476,0x00000476,0x00050091,0x00000037,
|
||||
0x0000047a,0x00000478,0x0000044d,0x0008004f,
|
||||
0x0000000c,0x0000047b,0x0000047a,0x0000047a,
|
||||
0x00000000,0x00000001,0x00000002,0x00050085,
|
||||
0x0000000c,0x000001e8,0x0000047b,0x0000040e,
|
||||
0x0006000c,0x0000000c,0x000001e9,0x00000001,
|
||||
0x0000001f,0x000001e8,0x00050051,0x00000006,
|
||||
0x000001eb,0x000001c8,0x00000001,0x00060041,
|
||||
0x00000188,0x000001ec,0x00000187,0x0000006c,
|
||||
0x00000077,0x0004003d,0x00000006,0x000001ed,
|
||||
0x000001ec,0x00050085,0x00000006,0x000001ee,
|
||||
0x000001eb,0x000001ed,0x0006000c,0x00000006,
|
||||
0x000001ef,0x00000001,0x0000000a,0x000001ee,
|
||||
0x0007000c,0x0000000c,0x00000482,0x00000001,
|
||||
0x0000001a,0x0000040e,0x0000016a,0x0008000c,
|
||||
0x0000000c,0x00000483,0x00000001,0x0000002e,
|
||||
0x00000165,0x00000167,0x00000482,0x00050088,
|
||||
0x0000000c,0x00000486,0x000004f6,0x00000483,
|
||||
0x0005008e,0x0000000c,0x00000488,0x00000486,
|
||||
0x000000a1,0x0005008e,0x0000000c,0x0000048b,
|
||||
0x00000486,0x000001ef,0x00050083,0x0000000c,
|
||||
0x0000048d,0x0000048b,0x00000488,0x0006000c,
|
||||
0x0000000c,0x0000048e,0x00000001,0x00000004,
|
||||
0x0000048d,0x00050083,0x0000000c,0x00000490,
|
||||
0x000004f7,0x0000048e,0x0008000c,0x0000000c,
|
||||
0x00000493,0x00000001,0x00000031,0x000004f8,
|
||||
0x000004f7,0x00000490,0x00050085,0x0000000c,
|
||||
0x00000495,0x00000493,0x00000488,0x00050085,
|
||||
0x0000000c,0x000001f5,0x000001e9,0x00000495,
|
||||
0x00050083,0x0000000c,0x000001f8,0x000003a1,
|
||||
0x000001f5,0x0008000c,0x0000000c,0x000001fb,
|
||||
0x00000001,0x0000002b,0x000001f8,0x000004f8,
|
||||
0x000004f7,0x00050085,0x0000000c,0x000001fe,
|
||||
0x000001fb,0x000001fb,0x0005008e,0x0000000c,
|
||||
0x00000200,0x000001fe,0x000001ff,0x00050081,
|
||||
0x0000000c,0x00000202,0x000001f5,0x00000200,
|
||||
0x00050041,0x00000206,0x00000207,0x000001ba,
|
||||
0x00000070,0x0004003d,0x00000006,0x00000208,
|
||||
0x00000207,0x000300f7,0x000004b3,0x00000000,
|
||||
0x000300fb,0x00000070,0x0000049b,0x000200f8,
|
||||
0x0000049b,0x00060041,0x00000188,0x0000049d,
|
||||
0x00000187,0x00000074,0x00000070,0x0004003d,
|
||||
0x00000006,0x0000049e,0x0000049d,0x00050085,
|
||||
0x00000006,0x0000049f,0x00000208,0x0000049e,
|
||||
0x0006000c,0x00000006,0x000004a7,0x00000001,
|
||||
0x00000008,0x0000049f,0x0005008d,0x00000006,
|
||||
0x000004a8,0x000004a7,0x00000116,0x000500b4,
|
||||
0x00000196,0x000004aa,0x000004a8,0x0000004a,
|
||||
0x000300f7,0x000004b2,0x00000000,0x000400fa,
|
||||
0x000004aa,0x000004ab,0x000004ac,0x000200f8,
|
||||
0x000004ac,0x000500b4,0x00000196,0x000004ae,
|
||||
0x000004a8,0x00000049,0x000300f7,0x000004b1,
|
||||
0x00000000,0x000400fa,0x000004ae,0x000004af,
|
||||
0x000004b0,0x000200f8,0x000004b0,0x000200f9,
|
||||
0x000004b3,0x000200f8,0x000004af,0x000200f9,
|
||||
0x000004b3,0x000200f8,0x000004b1,0x000100ff,
|
||||
0x000200f8,0x000004ab,0x000200f9,0x000004b3,
|
||||
0x000200f8,0x000004b2,0x000100ff,0x000200f8,
|
||||
0x000004b3,0x000900f5,0x0000000c,0x000004f1,
|
||||
0x0000019a,0x000004ab,0x000001a1,0x000004af,
|
||||
0x000001a4,0x000004b0,0x00050085,0x0000000c,
|
||||
0x0000020a,0x00000202,0x000004f1,0x0005008e,
|
||||
0x0000000c,0x0000020b,0x0000020a,0x00000116,
|
||||
0x0008000c,0x0000000c,0x0000020e,0x00000001,
|
||||
0x0000002e,0x00000202,0x0000020b,0x000004f9,
|
||||
0x0005008e,0x0000000c,0x00000211,0x000001fb,
|
||||
0x00000210,0x00050081,0x0000000c,0x00000213,
|
||||
0x0000020e,0x00000211,0x0005008e,0x0000000c,
|
||||
0x00000215,0x00000213,0x00000166,0x0007000c,
|
||||
0x0000000c,0x00000218,0x00000001,0x0000001a,
|
||||
0x00000215,0x00000217,0x00050051,0x00000006,
|
||||
0x0000021c,0x00000218,0x00000000,0x00050051,
|
||||
0x00000006,0x0000021d,0x00000218,0x00000001,
|
||||
0x00050051,0x00000006,0x0000021e,0x00000218,
|
||||
0x00000002,0x00070050,0x00000037,0x0000021f,
|
||||
0x0000021c,0x0000021d,0x0000021e,0x00000049,
|
||||
0x0003003e,0x0000021a,0x0000021f,0x000100fd,
|
||||
0x00010038}
|
||||
10
data/shader/main.frag
Normal file
@ -0,0 +1,10 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
layout(set = 0, binding = 0) uniform sampler2D uTexture;
|
||||
layout(location = 0) in vec2 vUV;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = textureLod(uTexture, vUV, 0.0);
|
||||
}
|
||||
21
data/shader/main.vert
Normal file
@ -0,0 +1,21 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec2 vUV;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (gl_VertexIndex == 0)
|
||||
{
|
||||
gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);
|
||||
}
|
||||
else if (gl_VertexIndex == 1)
|
||||
{
|
||||
gl_Position = vec4(-1.0, +3.0, 0.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl_Position = vec4(+3.0, -1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vUV = gl_Position.xy * 0.5 + 0.5;
|
||||
}
|
||||
32
data/shader/main_frag.spv
Normal file
@ -0,0 +1,32 @@
|
||||
{0x07230203,0x00010300,0x000d000b,0x00000015,
|
||||
0x00000000,0x00020011,0x00000001,0x0006000b,
|
||||
0x00000001,0x4c534c47,0x6474732e,0x3035342e,
|
||||
0x00000000,0x0003000e,0x00000000,0x00000001,
|
||||
0x0007000f,0x00000004,0x00000004,0x6e69616d,
|
||||
0x00000000,0x00000009,0x00000011,0x00030010,
|
||||
0x00000004,0x00000007,0x00040047,0x00000009,
|
||||
0x0000001e,0x00000000,0x00040047,0x0000000d,
|
||||
0x00000021,0x00000000,0x00040047,0x0000000d,
|
||||
0x00000022,0x00000000,0x00040047,0x00000011,
|
||||
0x0000001e,0x00000000,0x00020013,0x00000002,
|
||||
0x00030021,0x00000003,0x00000002,0x00030016,
|
||||
0x00000006,0x00000020,0x00040017,0x00000007,
|
||||
0x00000006,0x00000004,0x00040020,0x00000008,
|
||||
0x00000003,0x00000007,0x0004003b,0x00000008,
|
||||
0x00000009,0x00000003,0x00090019,0x0000000a,
|
||||
0x00000006,0x00000001,0x00000000,0x00000000,
|
||||
0x00000000,0x00000001,0x00000000,0x0003001b,
|
||||
0x0000000b,0x0000000a,0x00040020,0x0000000c,
|
||||
0x00000000,0x0000000b,0x0004003b,0x0000000c,
|
||||
0x0000000d,0x00000000,0x00040017,0x0000000f,
|
||||
0x00000006,0x00000002,0x00040020,0x00000010,
|
||||
0x00000001,0x0000000f,0x0004003b,0x00000010,
|
||||
0x00000011,0x00000001,0x0004002b,0x00000006,
|
||||
0x00000013,0x00000000,0x00050036,0x00000002,
|
||||
0x00000004,0x00000000,0x00000003,0x000200f8,
|
||||
0x00000005,0x0004003d,0x0000000b,0x0000000e,
|
||||
0x0000000d,0x0004003d,0x0000000f,0x00000012,
|
||||
0x00000011,0x00070058,0x00000007,0x00000014,
|
||||
0x0000000e,0x00000012,0x00000002,0x00000013,
|
||||
0x0003003e,0x00000009,0x00000014,0x000100fd,
|
||||
0x00010038}
|
||||
72
data/shader/main_vert.spv
Normal file
@ -0,0 +1,72 @@
|
||||
{0x07230203,0x00010300,0x000d000b,0x00000034,
|
||||
0x00000000,0x00020011,0x00000001,0x0006000b,
|
||||
0x00000001,0x4c534c47,0x6474732e,0x3035342e,
|
||||
0x00000000,0x0003000e,0x00000000,0x00000001,
|
||||
0x0008000f,0x00000000,0x00000004,0x6e69616d,
|
||||
0x00000000,0x00000008,0x00000016,0x0000002b,
|
||||
0x00040047,0x00000008,0x0000000b,0x0000002a,
|
||||
0x00030047,0x00000014,0x00000002,0x00050048,
|
||||
0x00000014,0x00000000,0x0000000b,0x00000000,
|
||||
0x00050048,0x00000014,0x00000001,0x0000000b,
|
||||
0x00000001,0x00050048,0x00000014,0x00000002,
|
||||
0x0000000b,0x00000003,0x00050048,0x00000014,
|
||||
0x00000003,0x0000000b,0x00000004,0x00040047,
|
||||
0x0000002b,0x0000001e,0x00000000,0x00020013,
|
||||
0x00000002,0x00030021,0x00000003,0x00000002,
|
||||
0x00040015,0x00000006,0x00000020,0x00000001,
|
||||
0x00040020,0x00000007,0x00000001,0x00000006,
|
||||
0x0004003b,0x00000007,0x00000008,0x00000001,
|
||||
0x0004002b,0x00000006,0x0000000a,0x00000000,
|
||||
0x00020014,0x0000000b,0x00030016,0x0000000f,
|
||||
0x00000020,0x00040017,0x00000010,0x0000000f,
|
||||
0x00000004,0x00040015,0x00000011,0x00000020,
|
||||
0x00000000,0x0004002b,0x00000011,0x00000012,
|
||||
0x00000001,0x0004001c,0x00000013,0x0000000f,
|
||||
0x00000012,0x0006001e,0x00000014,0x00000010,
|
||||
0x0000000f,0x00000013,0x00000013,0x00040020,
|
||||
0x00000015,0x00000003,0x00000014,0x0004003b,
|
||||
0x00000015,0x00000016,0x00000003,0x0004002b,
|
||||
0x0000000f,0x00000017,0xbf800000,0x0004002b,
|
||||
0x0000000f,0x00000018,0x00000000,0x0004002b,
|
||||
0x0000000f,0x00000019,0x3f800000,0x0007002c,
|
||||
0x00000010,0x0000001a,0x00000017,0x00000017,
|
||||
0x00000018,0x00000019,0x00040020,0x0000001b,
|
||||
0x00000003,0x00000010,0x0004002b,0x00000006,
|
||||
0x0000001f,0x00000001,0x0004002b,0x0000000f,
|
||||
0x00000023,0x40400000,0x0007002c,0x00000010,
|
||||
0x00000024,0x00000017,0x00000023,0x00000018,
|
||||
0x00000019,0x0007002c,0x00000010,0x00000027,
|
||||
0x00000023,0x00000017,0x00000018,0x00000019,
|
||||
0x00040017,0x00000029,0x0000000f,0x00000002,
|
||||
0x00040020,0x0000002a,0x00000003,0x00000029,
|
||||
0x0004003b,0x0000002a,0x0000002b,0x00000003,
|
||||
0x0004002b,0x0000000f,0x0000002f,0x3f000000,
|
||||
0x0005002c,0x00000029,0x00000033,0x0000002f,
|
||||
0x0000002f,0x00050036,0x00000002,0x00000004,
|
||||
0x00000000,0x00000003,0x000200f8,0x00000005,
|
||||
0x0004003d,0x00000006,0x00000009,0x00000008,
|
||||
0x000500aa,0x0000000b,0x0000000c,0x00000009,
|
||||
0x0000000a,0x000300f7,0x0000000e,0x00000000,
|
||||
0x000400fa,0x0000000c,0x0000000d,0x0000001d,
|
||||
0x000200f8,0x0000000d,0x00050041,0x0000001b,
|
||||
0x0000001c,0x00000016,0x0000000a,0x0003003e,
|
||||
0x0000001c,0x0000001a,0x000200f9,0x0000000e,
|
||||
0x000200f8,0x0000001d,0x000500aa,0x0000000b,
|
||||
0x00000020,0x00000009,0x0000001f,0x000300f7,
|
||||
0x00000022,0x00000000,0x000400fa,0x00000020,
|
||||
0x00000021,0x00000026,0x000200f8,0x00000021,
|
||||
0x00050041,0x0000001b,0x00000025,0x00000016,
|
||||
0x0000000a,0x0003003e,0x00000025,0x00000024,
|
||||
0x000200f9,0x00000022,0x000200f8,0x00000026,
|
||||
0x00050041,0x0000001b,0x00000028,0x00000016,
|
||||
0x0000000a,0x0003003e,0x00000028,0x00000027,
|
||||
0x000200f9,0x00000022,0x000200f8,0x00000022,
|
||||
0x000200f9,0x0000000e,0x000200f8,0x0000000e,
|
||||
0x00050041,0x0000001b,0x0000002c,0x00000016,
|
||||
0x0000000a,0x0004003d,0x00000010,0x0000002d,
|
||||
0x0000002c,0x0007004f,0x00000029,0x0000002e,
|
||||
0x0000002d,0x0000002d,0x00000000,0x00000001,
|
||||
0x0005008e,0x00000029,0x00000030,0x0000002e,
|
||||
0x0000002f,0x00050081,0x00000029,0x00000032,
|
||||
0x00000030,0x00000033,0x0003003e,0x0000002b,
|
||||
0x00000032,0x000100fd,0x00010038}
|
||||
5
data/shader/output.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
glslc -O crt-aperture.frag -o crt_frag.spv --target-env=vulkan1.1 -mfmt=c
|
||||
glslc -O main.frag -o main_frag.spv --target-env=vulkan1.1 -mfmt=c
|
||||
glslc -O main.vert -o main_vert.spv --target-env=vulkan1.1 -mfmt=c
|
||||
26
data/translations/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Adding translations to Gopher64
|
||||
1. Copy and modify the `gopher64.pot` file in this directory with your translations.
|
||||
2. Rename it to `gopher64.po` and put it here:
|
||||
`data/translations/<lang>/LC_MESSAGES/gopher64.po`
|
||||
|
||||
See [here](https://docs.slint.dev/latest/docs/slint/guide/development/translations/#translating-strings) for more details on creating a .po file. I recommend using a program like [poedit](https://poedit.com) to create the .po file from the template.
|
||||
|
||||
---
|
||||
|
||||
For example, in the .pot file you will see:
|
||||
```
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr ""
|
||||
```
|
||||
|
||||
A translation will look something like this:
|
||||
```
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "Nova versão disponível! Clique aqui para baixar"
|
||||
```
|
||||
|
||||
The only line you modify is the `msgstr`. The `msgctxt` and `msgid` need to remain unchanged.
|
||||
644
data/translations/de/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,644 @@
|
||||
# gopher64 - German Translation
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gopher64\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 12:00+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "Neue Version verfügbar! Zum Herunterladen hier klicken"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "Über"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Discord-Server"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "Quellcode"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Spenden (über Patreon)"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "Spenden (über GitHub)"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "Lokales Spiel"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "Controller-Konfiguration"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "Cheats"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "Über"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "Cheats"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROM auswählen"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "Cheats für alle ROMs löschen"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "Keine Cheats verfügbar"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "Aktiviert"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "Controller-Konfiguration"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "Eingabeprofil konfigurieren"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "Controller-Einstellungen"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "Controller-Zuweisung"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "Eingabeprofil erstellen/bearbeiten"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "Profilname zum Erstellen oder Bearbeiten eingeben"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "Deadzone Größe in Prozent"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "DirectInput verwenden"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "Assistent zur Profilerstellung starten"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"Bei Verwendung von DirectInput und/oder Tastatur navigieren Sie im Dialog "
|
||||
"mit den Pfeiltasten.\n"
|
||||
"Drücken Sie Enter, um die Belegung einer Taste/Achse zu starten.\n"
|
||||
"Drücken Sie Entf, um eine Belegung zu entfernen.\n"
|
||||
"\n"
|
||||
"Bei Verwendung eines Controllers im PlayStation-Stil navigieren Sie im "
|
||||
"Dialog mit dem Steuerkreuz.\n"
|
||||
"Drücken Sie die Süd-/Ost-Taste, um die Belegung einer Taste/Achse zu "
|
||||
"starten.\n"
|
||||
"Drücken Sie die Nord-/West-Taste, um eine Belegung zu entfernen."
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "Lokales Spiel"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "ROM öffnen"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "Speicherordner öffnen"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "Zuletzt verwendete ROMs"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "Netplay: Sitzung erstellen"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "Sitzungsname:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "Sitzungsname eingeben"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "Passwort (optional):"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "Sitzungspasswort eingeben"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "Spielername:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "Spielername eingeben"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROM auswählen"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
"Für dieses Spiel sind Cheats aktiviert. Cheats werden mit anderen Spielern "
|
||||
"synchronisiert."
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "Sitzung erstellen"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "Netplay: Sitzung beitreten"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "Spielername (erforderlich):"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "Spielername eingeben"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "Passwort (falls erforderlich)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROM auswählen"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "Sitzungsliste wird aktualisiert, bitte warten …"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "Auf eine Sitzung klicken um diese auszuwählen"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr "Keine Sitzungen verfügbar. Bitte aktualisieren, um erneut zu prüfen."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "Sitzungsname"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "Spielname"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "Cheats"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "Sitzungsliste aktualisieren"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "Sitzung beitreten"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "Sitzung erstellen"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "Sitzung beitreten"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "Netplay-Discord-Kanal"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "Feedback geben"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "Netplay: Warteraum"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "Sitzungsname:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "Spielname:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "Spieler 1:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "Spieler 2:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "Spieler 3:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "Spieler 4:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "Chatnachricht eingeben"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "Nachricht senden"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"Eine zu niedrige Eingabeverzögerung führt zu Ruckeln im Spiel.\n"
|
||||
"Sie können die Eingabeverzögerung im Spiel mit Alt+[ verringern und mit "
|
||||
"Alt+] erhöhen."
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "Lokale Eingabeverzögerung:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "Empfohlen: "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "Spiel starten"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "Warten auf P2P-Mesh "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "Derzeit nicht angemeldet"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "Aktiviert"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "Hardcore-Modus"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "Challenge Tipps"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "Bestenlisten-Tracker"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Rich Presence für Discord"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "Profil anzeigen"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "Benutzername"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "Unterstützte Spiele anzeigen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "Spiel"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "Auflösung:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "SSAA-Herunterskalierung"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "Pixelgenaue Skalierung"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Vollbild"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "16:9 (gestreckt)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "VSync"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "CRT-Shader anwenden"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "N64-CPU übertakten (kann Fehler verursachen)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "Expansion Pak deaktivieren"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "Rewind aktivieren"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "UNFLoader emulieren"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "VRU emulieren"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "Benutzeroberfläche"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "Thema:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "Dunkel"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "Hell"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "Was möchten Sie sagen?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "Server:"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "Eigene Server-URL eingeben"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "Eigene Server-URL (optional):"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "Server"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Ping:"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr ""
|
||||
#~ "Server wird erstellt, bitte warten … Dies kann etwa 30 Sekunden dauern."
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "Port"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "Controller"
|
||||
642
data/translations/es/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,642 @@
|
||||
# gopher64 - Spanish Translation
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gopher64\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 12:00+0000\n"
|
||||
"Last-Translator: Gemini\n"
|
||||
"Language-Team: Spanish (Spain)\n"
|
||||
"Language: es_ES\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "¡Nueva versión disponible! Pulsa aquí para descargar"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "Acerca de"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Servidor de Discord"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "Código fuente"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Donar (a través de Patreon)"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "Donar (a través de GitHub)"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "Juego local"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "Config. del mando"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "Trucos"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "Ajustes"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "Acerca de"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "Trucos"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "Seleccionar ROM"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "Borrar trucos de todas las ROMs"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "No hay trucos disponibles"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "Activado"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "Perfil"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "Configuración del mando"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "Configurar perfil de entrada"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "Ajustes del mando"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "Asignación del mando"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "Crear/Modificar perfil de entrada"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "Introduce el nombre del perfil para crear o modificar"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "Porcentaje de zona muerta"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "Usar DirectInput"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "Iniciar asistente de creación de perfil"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"Al usar DirectInput y/o el teclado, navegue por el diálogo con las teclas de "
|
||||
"flecha.\n"
|
||||
"Pulse Intro para empezar a asignar un botón/eje.\n"
|
||||
"Pulse Suprimir para quitar una asignación.\n"
|
||||
"\n"
|
||||
"Al usar un mando estilo PlayStation, navegue por el diálogo con la cruceta.\n"
|
||||
"Pulse el botón Sur/Este para empezar a asignar un botón/eje.\n"
|
||||
"Pulse el botón Norte/Oeste para quitar una asignación."
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "Juego local"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "Abrir ROM"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "Abrir carpeta de guardados"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "ROMs recientes"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "Netplay: Crear sesión"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "Nombre de la sesión:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "Introduce el nombre de la sesión"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "Contraseña (Opcional):"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "Introduce la contraseña"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "Nombre del jugador:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "Introduce tu nombre"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "Seleccionar ROM"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
"Este juego tiene trucos activados. Los trucos se sincronizarán con los otros "
|
||||
"jugadores."
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "Crear sesión"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "Netplay: Unirse a la sesión"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "Nombre de jugador (requerido):"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "Introduce tu nombre"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "Contraseña (si es necesaria)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "Seleccionar ROM"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "Actualizando lista de sesiones, espera..."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "Pulsa una sesión para seleccionarla"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr "No hay sesiones disponibles. Actualiza para comprobar de nuevo."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "Nombre de la sesión"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "Nombre del juego"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "Trucos"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "Actualizar lista"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "Unirse a la sesión"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "Crear sesión"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "Unirse a la sesión"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "Canal de Discord de Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "Enviar comentarios"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "Netplay: Sala de espera"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "Nombre de la sesión:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "Nombre del juego:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "Jugador 1:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "Jugador 2:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "Jugador 3:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "Jugador 4:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "Escribe un mensaje en el chat"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "Enviar mensaje"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"Configurar un retardo de entrada demasiado bajo hará que el juego se "
|
||||
"ralentice.\n"
|
||||
"Puedes reducir el retardo de entrada en el juego pulsando Alt+[ y aumentarlo "
|
||||
"con Alt+]."
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "Retardo de entrada local:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "Recomendado: "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "Iniciar juego"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "Esperando malla P2P "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "Sesión no iniciada"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "Activado"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "Modo Hardcore"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "Indicadores de desafío"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "Indicadores de tabla de clasificación"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Rich Presence en Discord"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "Ver perfil"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "Usuario"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "Entrar"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "Ver juegos compatibles"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "Ajustes"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "Juego"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "Resolución:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "Reducción de escala SSAA"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "Escalado por enteros"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Pantalla completa"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "Pantalla ancha (estirado)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "Sincronización vertical (VSync)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "Aplicar sombreador CRT"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "Overclock CPU N64 (puede causar errores)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "Desactivar Expansion Pak"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "Activar rebobinado"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "Emular UNFLoader"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "Emular VRU"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "Interfaz"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "Tema:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "Oscuro"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "Claro"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "¿Qué te gustaría decir?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "Servidor:"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "Introduce la URL de servidor personalizado"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "URL de servidor personalizado (opcional):"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "Servidor"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Latencia (Ping):"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr "Creando servidor, espera... Puede tardar unos 30 segundos."
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "Puerto"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "Mando"
|
||||
645
data/translations/fr/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,645 @@
|
||||
# gopher64 - French (France) Translation
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gopher64\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 12:00+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French (France)\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "Nouvelle version disponible ! Cliquez ici pour télécharger"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "À propos"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Serveur Discord"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "Code source"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Faire un don (via Patreon)"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "Faire un don (via GitHub)"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "Jeu local"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "Configuration manette"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "Triche"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "Paramètres"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "À propos"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "Triche"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "Sélectionner une ROM"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "Effacer la triche pour toutes les ROMs"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "Aucune triche disponible"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "Activé"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "Configuration de la manette"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "Configurer le profil d'entrée"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "Paramètres de la manette"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "Attribution de la manette"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "Créer / modifier le profil d'entrée"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "Saisir le nom du profil à créer ou à modifier"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "Pourcentage de zone morte"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "Utiliser DirectInput"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "Lancer l'assistant de création de profil"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"Lorsque vous utilisez DirectInput et/ou le clavier, naviguez dans la boîte "
|
||||
"de dialogue avec les touches fléchées.\n"
|
||||
"Appuyez sur Entrée pour commencer à assigner un bouton/axe.\n"
|
||||
"Appuyez sur Suppr pour supprimer une assignation.\n"
|
||||
"\n"
|
||||
"Lorsque vous utilisez une manette de style PlayStation, naviguez dans la "
|
||||
"boîte de dialogue avec la croix directionnelle.\n"
|
||||
"Appuyez sur le bouton Sud/Est pour commencer à assigner un bouton/axe.\n"
|
||||
"Appuyez sur le bouton Nord/Ouest pour supprimer une assignation."
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "Jeu local"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "Ouvrir une ROM"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "Ouvrir le dossier des sauvegardes"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "ROMs récentes"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "Netplay : créer une session"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "Nom de la session :"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "Saisir le nom de la session"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "Mot de passe (facultatif) :"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "Saisir le mot de passe de la session"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "Nom du joueur :"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "Saisir le nom du joueur"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "Sélectionner une ROM"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
"La triche est activée pour ce jeu. Elle sera synchronisée avec les autres "
|
||||
"joueurs."
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "Créer la session"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "Netplay : rejoindre une session"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "Nom du joueur (obligatoire) :"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "Saisir le nom du joueur"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "Mot de passe (si requis)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "Sélectionner une ROM"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "Actualisation de la liste des sessions, veuillez patienter…"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "Cliquez sur une session ci-dessous pour la sélectionner"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr ""
|
||||
"Aucune session disponible. Veuillez actualiser pour vérifier à nouveau."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "Nom de la session"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "Nom du jeu"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "Mot de passe"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "Triche"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "Actualiser la liste"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "Rejoindre la session"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "Créer une session"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "Rejoindre une session"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "Salon Discord Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "Donner votre avis"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "Netplay : salle d'attente"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "Nom de la session :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "Nom du jeu :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "Joueur 1 :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "Joueur 2 :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "Joueur 3 :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "Joueur 4 :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "Saisir un message"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "Envoyer"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"Régler le délai d'entrée trop bas provoquera des ralentissements.\n"
|
||||
"Vous pouvez diminuer le délai d'entrée en jeu avec Alt+[ et l'augmenter avec "
|
||||
"Alt+]."
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "Délai d'entrée local :"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "Recommandé : "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "Lancer la partie"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "En attente du maillage P2P "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "Non connecté"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "Activé"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "Mode hardcore"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "Indicateurs de défi"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "Suivi des classements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Rich Presence Discord"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "Voir le profil"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "Nom d'utilisateur"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "Mot de passe"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "Connexion"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "Voir les jeux pris en charge"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "Paramètres"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "Jeu"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "Résolution :"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "Réduction SSAA"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "Mise à l'échelle par entiers"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Plein écran"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "Écran large (étiré)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "Synchro verticale"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "Appliquer le filtre CRT"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "Surcharger le CPU N64 (risque de bugs)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "Désactiver l'Expansion Pak"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "Activer le rembobinage"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "Émuler UNFLoader"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "Émuler le VRU"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "Interface"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "Thème :"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "Système"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "Sombre"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "Clair"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "Que souhaitez-vous dire ?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "Serveur :"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "Saisir l'URL personnalisée du serveur"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "URL personnalisée du serveur (facultatif) :"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "Serveur"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Ping :"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr ""
|
||||
#~ "Création du serveur, veuillez patienter… Cela peut prendre environ 30 "
|
||||
#~ "secondes."
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "Port"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "Manette"
|
||||
591
data/translations/gopher64.pot
Normal file
@ -0,0 +1,591 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\nPress Enter to begin binding a button/axis.\nPress Delete to remove a "
|
||||
"binding.\n\nWhen using a PlayStation-style controller, navigate the dialog "
|
||||
"using the D-pad.\nPress the South/East button to begin binding a button/axis."
|
||||
"\nPress the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\nYou can lower "
|
||||
"the input delay in-game by pressing Alt+[ and raise it with Alt+]."
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr ""
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr ""
|
||||
641
data/translations/ja/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,641 @@
|
||||
# gopher64 - Japanese Translation
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gopher64\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 12:00+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "新しいバージョンがあります。クリックしてダウンロード"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "バージョン情報"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Discordサーバー"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "ソースコード"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Patreonで寄付"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "GitHubで寄付"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "ローカルゲーム"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "コントローラー設定"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "ネットプレイ"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "チート"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "設定"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "バージョン情報"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "チート"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROMを選択"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "すべてのROMのチートをクリア"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "利用できるチートはありません"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "有効"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "プロファイル"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "コントローラー設定"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "入力プロファイルを設定"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "コントローラー詳細設定"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "コントローラー割り当て"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "入力プロファイルの作成/変更"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "作成または変更するプロファイル名を入力"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "デッドゾーン(%)"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "DirectInputを使用"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "プロファイル作成ウィザードを開始"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"DirectInputおよび/またはキーボードを使用する場合は、矢印キーでダイアログを"
|
||||
"操作します。\n"
|
||||
"Enterキーでボタン/軸の割り当てを開始します。\n"
|
||||
"Deleteキーで割り当てを解除します。\n"
|
||||
"\n"
|
||||
"PlayStationスタイルのコントローラーを使用する場合は、方向パッドでダイアログ"
|
||||
"を操作します。\n"
|
||||
"下/右ボタンでボタン/軸の割り当てを開始します。\n"
|
||||
"上/左ボタンで割り当てを解除します。"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "ローカルゲーム"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "ROMを開く"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "セーブフォルダーを開く"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "最近使ったROM"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "ネットプレイ:セッション作成"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "セッション名:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "セッション名を入力"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "パスワード(任意):"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "セッションのパスワードを入力"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "プレイヤー名:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "プレイヤー名を入力"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROMを選択"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr "このゲームではチートが有効です。チートは他のプレイヤーと同期されます。"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "セッションを作成"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "ネットプレイ:セッションに参加"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "プレイヤー名(必須):"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "プレイヤー名を入力"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "パスワード(必要な場合)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROMを選択"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "セッション一覧を更新しています。しばらくお待ちください…"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "下の一覧からセッションをクリックして選択"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr "利用できるセッションがありません。更新して再度確認してください。"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "セッション名"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "ゲーム名"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "パスワード"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "チート"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "セッション一覧を更新"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "セッションに参加"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "ネットプレイ"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "セッションを作成"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "セッションに参加"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "ネットプレイ用Discordチャンネル"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "フィードバックを送る"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "ネットプレイ:待機室"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "セッション名:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "ゲーム名:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "プレイヤー1:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "プレイヤー2:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "プレイヤー3:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "プレイヤー4:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "チャットメッセージを入力"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "送信"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"入力遅延を低く設定しすぎるとゲームが遅延します。\n"
|
||||
"Alt+[ でゲーム内の入力遅延を下げ、Alt+] で上げることができます。"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "ローカル入力遅延:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "推奨: "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "ゲーム開始"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "P2Pメッシュ接続待機中 "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "ログインしていません"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "有効"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "ハードコアモード"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "チャレンジ表示"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "リーダーボード追跡"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Discord のリッチプレゼンス"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "プロフィールを表示"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "ユーザー名"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "パスワード"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "ログイン"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "対応ゲームを表示"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "設定"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "ゲーム"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "解像度:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "SSAAダウンスケール"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "整数倍スケーリング"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "全画面表示"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "ワイド画面(伸ばす)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "垂直同期(VSync)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "CRTシェーダーを適用"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "N64のCPUをオーバークロック(不具合の原因になることがあります)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "拡張パックを無効にする"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "巻き戻しを有効にする"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "UNFLoaderをエミュレート"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "VRUをエミュレート"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "UI"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "テーマ:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "システム"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "ダーク"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "ライト"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "何と言いますか?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "サーバー:"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "カスタムサーバーURLを入力"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "カスタムサーバーURL(任意):"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "サーバー"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Ping:"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr ""
|
||||
#~ "サーバーを作成しています。しばらくお待ちください… 最大約30秒かかることがあ"
|
||||
#~ "ります。"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "ポート"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "コントローラー"
|
||||
643
data/translations/nl/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,643 @@
|
||||
# gopher64 - Dutch (Netherlands) Translation
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gopher64\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 12:00+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch (Netherlands)\n"
|
||||
"Language: nl_NL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "Nieuwe versie beschikbaar! Klik hier om te downloaden"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "Over"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Discord-server"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "Broncode"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Doneren (via Patreon)"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "Doneren (via GitHub)"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "Lokaal spel"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "Controllerconfiguratie"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "Cheats"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "Instellingen"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "Over"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "Cheats"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROM selecteren"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "Cheats wissen voor alle ROM's"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "Geen cheats beschikbaar"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "Ingeschakeld"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "Profiel"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "Controllerconfiguratie"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "Invoerprofiel configureren"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "Controllerinstellingen"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "Controllertoewijzing"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "Invoerprofiel maken/wijzigen"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "Profielnaam invoeren om aan te maken of te wijzigen"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "Dode-zonepercentage"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "DirectInput gebruiken"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "Wizard voor profiel maken starten"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"Bij gebruik van DirectInput en/of het toetsenbord navigeert u door het "
|
||||
"dialoogvenster met de pijltjestoetsen.\n"
|
||||
"Druk op Enter om te beginnen met het toewijzen van een knop/as.\n"
|
||||
"Druk op Delete om een toewijzing te verwijderen.\n"
|
||||
"\n"
|
||||
"Bij gebruik van een PlayStation-stijl controller navigeert u door het "
|
||||
"dialoogvenster met de D-pad.\n"
|
||||
"Druk op de Zuid/Oost-knop om te beginnen met het toewijzen van een knop/as.\n"
|
||||
"Druk op de Noord/West-knop om een toewijzing te verwijderen."
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "Lokaal spel"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "ROM openen"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "Map met opslagen openen"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "Recent gebruikte ROM's"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "Netplay: sessie aanmaken"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "Sessienaam:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "Sessienaam invoeren"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "Wachtwoord (optioneel):"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "Sessiewachtwoord invoeren"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "Spelernaam:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "Spelernaam invoeren"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROM selecteren"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
"Voor dit spel zijn cheats ingeschakeld. Cheats worden gesynchroniseerd met "
|
||||
"andere spelers."
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "Sessie aanmaken"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "Netplay: deelnemen aan sessie"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "Spelernaam (verplicht):"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "Spelernaam invoeren"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "Wachtwoord (indien vereist)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "ROM selecteren"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "Sessielijst wordt vernieuwd, even geduld…"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "Klik hieronder op een sessie om die te selecteren"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr "Geen sessies beschikbaar. Vernieuw om opnieuw te controleren."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "Sessienaam"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "Spelnaam"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "Wachtwoord"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "Cheats"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "Sessielijst vernieuwen"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "Deelnemen aan sessie"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "Sessie aanmaken"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "Deelnemen aan sessie"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "Netplay-Discordkanaal"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "Feedback geven"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "Netplay: wachtruimte"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "Sessienaam:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "Spelnaam:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "Speler 1:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "Speler 2:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "Speler 3:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "Speler 4:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "Chatbericht invoeren"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "Bericht verzenden"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"Een te lage invoervertraging zorgt ervoor dat het spel gaat haperen.\n"
|
||||
"Je kunt de invoervertraging in het spel verlagen met Alt+[ en verhogen met "
|
||||
"Alt+]."
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "Lokale invoervertraging:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "Aanbevolen: "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "Spel starten"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "Wachten op P2P-mesh "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "Momenteel niet ingelogd"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "Ingeschakeld"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "Hardcore-modus"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "Challenge-indicatoren"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "Leaderboard-trackers"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Discord Rich Presence"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "Profiel bekijken"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "Gebruikersnaam"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "Wachtwoord"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "Inloggen"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "Ondersteunde spellen bekijken"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "Instellingen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "Spel"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "Resolutie:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "SSAA-downscaling"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "Schalen in hele getallen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Volledig scherm"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "Breedbeeld (uitgerekt)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "VSync"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "CRT-shader toepassen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "N64-CPU overklokken (kan bugs veroorzaken)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "Expansion Pak uitschakelen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "Terugspoelen inschakelen"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "UNFLoader emuleren"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "VRU emuleren"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "Interface"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "Thema:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "Systeem"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "Donker"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "Licht"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "Wat wil je zeggen?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "Server:"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "Aangepaste server-URL invoeren"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "Aangepaste server-URL (optioneel):"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "Server"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Ping:"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr ""
|
||||
#~ "Server wordt aangemaakt, even geduld… Dit kan ongeveer 30 seconden duren."
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "Poort"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "Controller"
|
||||
644
data/translations/pt_BR/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,644 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 18:11-3000\n"
|
||||
"Last-Translator: EDUARDO CARVALHO <eduardotcarvalho@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: Brazilian Portuguese\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "Nova versão disponível! Clique aqui para baixar"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "Sobre"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Servidor do Discord"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "Código Fonte"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Doações (via Patreon)"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "Doações (via GitHub)"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "Jogo Local"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "Configurar Controle"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "Trapaças"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "Configurações"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "Sobre"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "Trapaças"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "Selecionar ROM"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "Limpar Trapaças para todas as ROMs"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "Sem trapaças disponíveis"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "Habilitado"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "Perfil"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "Configurar Controles"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "Configurar Perfil de Controle"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "Configurações do controle"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "Atribuição do controle"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "Criar/Modificar Perfil de Comandos"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "Insira o nome de perfil para criar ou modificar"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "Porcentagem de Zona-morta"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "Usar DirectInput"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "Iniciar Assistente de Criação de Perfil"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"Ao usar DirectInput e/ou o teclado, navegue pelo diálogo usando as teclas de "
|
||||
"seta.\n"
|
||||
"Pressione Enter para começar a vincular um botão/eixo.\n"
|
||||
"Pressione Delete para remover uma vinculação.\n"
|
||||
"\n"
|
||||
"Ao usar um controle no estilo PlayStation, navegue pelo diálogo usando o D-"
|
||||
"pad.\n"
|
||||
"Pressione o botão Sul/Leste para começar a vincular um botão/eixo.\n"
|
||||
"Pressione o botão Norte/Oeste para remover uma vinculação."
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "Jogo Local"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "Abrir ROM"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "Abrir Pasta de Saves"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "ROMs Recentes"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "Netplay: Criar Sessão"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "Nome da Sessão:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "Insira nome da sessão"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "Senha: (Opcional):"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "Insira a senha da sessão"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "Nome de Jogador:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "Insira nome de jogador"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "Selecionar ROM"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
"Este jogo tem trapaças habilitadas. As trapaças serão sincronizadas com "
|
||||
"outros jogadores."
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "Criar Sessão"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "Netplay: Entrar na sessão"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "Nome de Jogador (obrigatório):"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "Insira o nome de jogador"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "Senha (caso obrigatória)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "Selecionar ROM"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "Recarregando lista de sessões, por favor aguarde..."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "Clique em uma sessão abaixo para selecionar"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr ""
|
||||
"Sem sessões disponíveis. Por favor recarregue para verificar novamente."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "Nome da Sessão"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "Nome do Jogo"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "Senha"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "Trapaças"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "Recarregar Lista de Sessões"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "Entrar na Sessão"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "Netplay"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "Criar Sessão"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "Entrar na Sessão"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "Canal de Netplay do Discord"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "Fornecer Feedback"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "Netplay: Aguardando Sala"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "Nome da Sessão:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "Nome do Jogo:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "Jogador 1:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "Jogador 2:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "Jogador 3:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "Jogador 4:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "Escrever mensagem no chat"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "Enviar Mensagem"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"Definir o atraso de entrada muito baixo causará lag no jogo.\n"
|
||||
"Você pode diminuir o atraso de entrada no jogo pressionando Alt+[ e aumentá-"
|
||||
"lo com Alt+]."
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "Atraso de entrada local:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "Recomendado: "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "Iniciar jogo"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "Aguardando malha P2P "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "Não está logado."
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "Habilitado"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "Modo Hardcore"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "Indicadores de Desafio"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "Rastreadores de Tabela"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Rich Presence no Discord"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "Ver Perfil"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "Usuário"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "Senha"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "Login"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "Ver Jogos com Suporte"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "Configurações"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "Jogo"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "Resolução:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "Redução de SSAA"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "Proporção Íntegra"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Tela Cheia"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "Widescreen (esticar)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "VSync"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "Aplicar filtro de CRT"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "Overclock da CPU do N64 (pode causar bugs)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "Desabilitar Expansion Pak"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "Habilitar rebobinamento"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "Emular UNFLoader"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "Emular VRU"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "Interface"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "Tema:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "Escuro"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "Claro"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "O que você quer falar?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "Servidor:"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "Insira a URL personalizada do servidor"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "URL Personalizada de Servidor (opcional):"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "Servidor"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Ping:"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr ""
|
||||
#~ "Criando servidor, por favor aguarde...Isto poderá levar até 30 segundos."
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "Entrada"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "Controle"
|
||||
644
data/translations/ru/LC_MESSAGES/gopher64.po
Normal file
@ -0,0 +1,644 @@
|
||||
# gopher64 - Russian Translation
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gopher64\n"
|
||||
"POT-Creation-Date: 2026-08-03 15:22+0000\n"
|
||||
"PO-Revision-Date: 2026-05-11 12:00+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: src/ui/gui/about_page.slint:17
|
||||
msgctxt "NewVersionButton"
|
||||
msgid "New version available! Click here to download"
|
||||
msgstr "Доступна новая версия! Нажмите здесь для загрузки"
|
||||
|
||||
#: src/ui/gui/about_page.slint:33
|
||||
msgctxt "About"
|
||||
msgid "About"
|
||||
msgstr "О программе"
|
||||
|
||||
#: src/ui/gui/about_page.slint:40
|
||||
msgctxt "About"
|
||||
msgid "Wiki"
|
||||
msgstr "Вики"
|
||||
|
||||
#: src/ui/gui/about_page.slint:50
|
||||
msgctxt "About"
|
||||
msgid "Discord Server"
|
||||
msgstr "Сервер Discord"
|
||||
|
||||
#: src/ui/gui/about_page.slint:60
|
||||
msgctxt "About"
|
||||
msgid "Source Code"
|
||||
msgstr "Исходный код"
|
||||
|
||||
#: src/ui/gui/about_page.slint:70
|
||||
msgctxt "About"
|
||||
msgid "Donate (via Patreon)"
|
||||
msgstr "Пожертвовать (через Patreon)"
|
||||
|
||||
#: src/ui/gui/about_page.slint:77
|
||||
msgctxt "About"
|
||||
msgid "Donate (via GitHub)"
|
||||
msgstr "Пожертвовать (через GitHub)"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:177
|
||||
msgctxt "Menu"
|
||||
msgid "Local Game"
|
||||
msgstr "Локальная игра"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:181
|
||||
msgctxt "Menu"
|
||||
msgid "Controller Config"
|
||||
msgstr "Настройка контроллера"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:185
|
||||
msgctxt "Menu"
|
||||
msgid "Netplay"
|
||||
msgstr "Сетевая игра"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:189
|
||||
msgctxt "Menu"
|
||||
msgid "Cheats"
|
||||
msgstr "Читы"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:193
|
||||
msgctxt "Menu"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:197
|
||||
msgctxt "Menu"
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
#: src/ui/gui/appwindow.slint:201
|
||||
msgctxt "Menu"
|
||||
msgid "About"
|
||||
msgstr "О программе"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:28
|
||||
msgctxt "Cheats"
|
||||
msgid "Cheats"
|
||||
msgstr "Читы"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:38
|
||||
msgctxt "Cheats"
|
||||
msgid "Select ROM"
|
||||
msgstr "Выбрать ROM"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:54
|
||||
msgctxt "Cheats"
|
||||
msgid "Clear Cheats For All ROMs"
|
||||
msgstr "Очистить читы для всех ROMов"
|
||||
|
||||
#: src/ui/gui/cheat_page.slint:96
|
||||
msgctxt "Cheats"
|
||||
msgid "No Cheats Available"
|
||||
msgstr "Читы недоступны"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:87
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Enabled"
|
||||
msgstr "Включено"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:100
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Transfer Pak"
|
||||
msgstr "Transfer Pak"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:114
|
||||
msgctxt "ControllerSettingsGrid"
|
||||
msgid "Profile"
|
||||
msgstr "Профиль"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:164
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Configuration"
|
||||
msgstr "Настройка контроллера"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:178
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Configure Input Profile"
|
||||
msgstr "Настроить профиль ввода"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:187
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Settings"
|
||||
msgstr "Настройки контроллера"
|
||||
|
||||
#: src/ui/gui/controller_page.slint:197
|
||||
msgctxt "ControllerConfig"
|
||||
msgid "Controller Assignment"
|
||||
msgstr "Назначение контроллера"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:19
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Create/Modify Input Profile"
|
||||
msgstr "Создать/изменить профиль ввода"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:32
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Enter profile name to create or modify"
|
||||
msgstr "Введите имя профиля для создания или изменения"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:45
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Deadzone Percentage"
|
||||
msgstr "Процент мёртвой зоны"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:68
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Use DirectInput"
|
||||
msgstr "Использовать DirectInput"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:81
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Start Profile Creation Wizard"
|
||||
msgstr "Запустить мастер создания профиля"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:90
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: src/ui/gui/input_profile.slint:101
|
||||
msgctxt "InputProfileDialog"
|
||||
msgid ""
|
||||
"When using DirectInput and/or keyboard, navigate the dialog using the arrow "
|
||||
"keys.\n"
|
||||
"Press Enter to begin binding a button/axis.\n"
|
||||
"Press Delete to remove a binding.\n"
|
||||
"\n"
|
||||
"When using a PlayStation-style controller, navigate the dialog using the D-"
|
||||
"pad.\n"
|
||||
"Press the South/East button to begin binding a button/axis.\n"
|
||||
"Press the North/West button to remove a binding."
|
||||
msgstr ""
|
||||
"При использовании DirectInput и/или клавиатуры перемещайтесь по диалогу с "
|
||||
"помощью стрелок.\n"
|
||||
"Нажмите Enter, чтобы начать привязку кнопки/оси.\n"
|
||||
"Нажмите Delete, чтобы удалить привязку.\n"
|
||||
"\n"
|
||||
"При использовании контроллера в стиле PlayStation перемещайтесь по диалогу с "
|
||||
"помощью крестовины.\n"
|
||||
"Нажмите кнопку Юг/Восток, чтобы начать привязку кнопки/оси.\n"
|
||||
"Нажмите кнопку Север/Запад, чтобы удалить привязку."
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:24
|
||||
msgctxt "LocalGame"
|
||||
msgid "Local Game"
|
||||
msgstr "Локальная игра"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:31
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open ROM"
|
||||
msgstr "Открыть ROM"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:42
|
||||
msgctxt "LocalGame"
|
||||
msgid "Open Saves Folder"
|
||||
msgstr "Открыть папку сохранений"
|
||||
|
||||
#: src/ui/gui/localgame_page.slint:59
|
||||
msgctxt "LocalGame"
|
||||
msgid "Recent ROMs"
|
||||
msgstr "Недавние ROMы"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:16
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Netplay: Create Session"
|
||||
msgstr "Сетевая игра: Создать сессию"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:31
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Session Name:"
|
||||
msgstr "Имя сессии:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:35
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session name"
|
||||
msgstr "Введите имя сессии"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:45
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Password (Optional):"
|
||||
msgstr "Пароль (необязательно):"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:49
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter session password"
|
||||
msgstr "Введите пароль сессии"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:60
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Player Name:"
|
||||
msgstr "Имя игрока:"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:64
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Enter player name"
|
||||
msgstr "Введите имя игрока"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:75
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Select ROM"
|
||||
msgstr "Выбрать ROM"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:91
|
||||
msgctxt "NetplayCreate"
|
||||
msgid ""
|
||||
"This game has cheats enabled. Cheats will be synchronized with other players."
|
||||
msgstr ""
|
||||
"Для этой игры включены читы. Читы будут синхронизированы с другими игроками."
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:100
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Create Session"
|
||||
msgstr "Создать сессию"
|
||||
|
||||
#: src/ui/gui/netplay_create.slint:117
|
||||
msgctxt "NetplayCreate"
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:15
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Netplay: Join Session"
|
||||
msgstr "Сетевая игра: Присоединиться к сессии"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:26
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Player Name (required):"
|
||||
msgstr "Имя игрока (обязательно):"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:30
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Enter player name"
|
||||
msgstr "Введите имя игрока"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:39
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password (if required)"
|
||||
msgstr "Пароль (если требуется)"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:51
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Select ROM"
|
||||
msgstr "Выбрать ROM"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:66
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refreshing session list, please wait..."
|
||||
msgstr "Обновление списка сессий, пожалуйста, подождите..."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:71
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Click on a session below to select it"
|
||||
msgstr "Нажмите на сессию ниже, чтобы выбрать её"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:76
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "No sessions available. Please refresh to check again."
|
||||
msgstr "Нет доступных сессий. Пожалуйста, обновите, чтобы проверить снова."
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:82
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Session Name"
|
||||
msgstr "Имя сессии"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:83
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Game Name"
|
||||
msgstr "Название игры"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:84
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Password"
|
||||
msgstr "Пароль"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:85
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cheats"
|
||||
msgstr "Читы"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:99
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Refresh Session List"
|
||||
msgstr "Обновить список сессий"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:114
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Join Session"
|
||||
msgstr "Присоединиться к сессии"
|
||||
|
||||
#: src/ui/gui/netplay_join.slint:127
|
||||
msgctxt "NetplayJoin"
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:37
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay"
|
||||
msgstr "Сетевая игра"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:45
|
||||
msgctxt "Netplay"
|
||||
msgid "Create Session"
|
||||
msgstr "Создать сессию"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:56
|
||||
msgctxt "Netplay"
|
||||
msgid "Join Session"
|
||||
msgstr "Присоединиться к сессии"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:70
|
||||
msgctxt "Netplay"
|
||||
msgid "Netplay Discord Channel"
|
||||
msgstr "Канал Discord для сетевой игры"
|
||||
|
||||
#: src/ui/gui/netplay_page.slint:77
|
||||
msgctxt "Netplay"
|
||||
msgid "Provide Feedback"
|
||||
msgstr "Оставить отзыв"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:27
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Netplay: Waiting Room"
|
||||
msgstr "Сетевая игра: Комната ожидания"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:39
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Session Name:"
|
||||
msgstr "Имя сессии:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:45
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Game Name:"
|
||||
msgstr "Название игры:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:55
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 1:"
|
||||
msgstr "Игрок 1:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:59
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 2:"
|
||||
msgstr "Игрок 2:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:65
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 3:"
|
||||
msgstr "Игрок 3:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:69
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Player 4:"
|
||||
msgstr "Игрок 4:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:83
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Enter chat message"
|
||||
msgstr "Введите сообщение чата"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:94
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Send Message"
|
||||
msgstr "Отправить сообщение"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:104
|
||||
msgctxt "NetplayWait"
|
||||
msgid ""
|
||||
"Setting the input delay too low will cause the game to lag.\n"
|
||||
"You can lower the input delay in-game by pressing Alt+[ and raise it with "
|
||||
"Alt+]."
|
||||
msgstr ""
|
||||
"Слишком низкая задержка ввода вызовет лаги в игре.\n"
|
||||
"Вы можете уменьшить задержку ввода в игре, нажав Alt+[, и увеличить её с "
|
||||
"помощью Alt+]."
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:112
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Local Input Delay:"
|
||||
msgstr "Локальная задержка ввода:"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:129
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Recommended: "
|
||||
msgstr "Рекомендуется: "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Start Game"
|
||||
msgstr "Начать игру"
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:138
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Waiting for P2P mesh "
|
||||
msgstr "Ожидание P2P-сети "
|
||||
|
||||
#: src/ui/gui/netplay_wait.slint:147
|
||||
msgctxt "NetplayWait"
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:25
|
||||
msgctxt "RAData"
|
||||
msgid "Not currently logged in"
|
||||
msgstr "В данный момент не выполнен вход"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:33
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "RetroAchievements"
|
||||
msgstr "RetroAchievements"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:42
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Enabled"
|
||||
msgstr "Включено"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:58
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Hardcore mode"
|
||||
msgstr "Хардкорный режим"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:67
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Challenge Indicators"
|
||||
msgstr "Индикаторы испытаний"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:77
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Leaderboard Trackers"
|
||||
msgstr "Трекеры таблицы лидеров"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:87
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Discord Rich Presence"
|
||||
msgstr "Расширенный статус Discord"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:107
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Profile"
|
||||
msgstr "Просмотреть профиль"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:118
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Username"
|
||||
msgstr "Имя пользователя"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:128
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Password"
|
||||
msgstr "Пароль"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:138
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "Login"
|
||||
msgstr "Войти"
|
||||
|
||||
#: src/ui/gui/retroachievements_page.slint:156
|
||||
msgctxt "RetroAchievements"
|
||||
msgid "View Supported Games"
|
||||
msgstr "Просмотреть поддерживаемые игры"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:31
|
||||
msgctxt "Settings"
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:38
|
||||
msgctxt "Settings"
|
||||
msgid "Game"
|
||||
msgstr "Игра"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:44
|
||||
msgctxt "Settings"
|
||||
msgid "Resolution:"
|
||||
msgstr "Разрешение:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:58
|
||||
msgctxt "Settings"
|
||||
msgid "SSAA downscaling"
|
||||
msgstr "SSAA даунскейлинг"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:66
|
||||
msgctxt "Settings"
|
||||
msgid "Integer Scaling"
|
||||
msgstr "Целочисленное масштабирование"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:74
|
||||
msgctxt "Settings"
|
||||
msgid "Fullscreen"
|
||||
msgstr "Полноэкранный режим"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:82
|
||||
msgctxt "Settings"
|
||||
msgid "Widescreen (stretch)"
|
||||
msgstr "Широкоэкранный режим (растянуто)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:90
|
||||
msgctxt "Settings"
|
||||
msgid "VSync"
|
||||
msgstr "Вертикальная синхронизация"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:98
|
||||
msgctxt "Settings"
|
||||
msgid "Apply CRT shader"
|
||||
msgstr "Применить шейдер ЭЛТ"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:110
|
||||
msgctxt "Settings"
|
||||
msgid "Overclock N64 CPU (may cause bugs)"
|
||||
msgstr "Разгон процессора N64 (может вызвать ошибки)"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:118
|
||||
msgctxt "Settings"
|
||||
msgid "Disable Expansion Pak"
|
||||
msgstr "Отключить Expansion Pak"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:126
|
||||
msgctxt "Settings"
|
||||
msgid "Enable Rewind"
|
||||
msgstr "Включить перемотку назад"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:134
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate UNFLoader"
|
||||
msgstr "Эмулировать UNFLoader"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:142
|
||||
msgctxt "Settings"
|
||||
msgid "Emulate VRU"
|
||||
msgstr "Эмулировать VRU"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:152
|
||||
msgctxt "Settings"
|
||||
msgid "UI"
|
||||
msgstr "Интерфейс"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:158
|
||||
msgctxt "Settings"
|
||||
msgid "Theme:"
|
||||
msgstr "Тема:"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "System"
|
||||
msgstr "Система"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Dark"
|
||||
msgstr "Тёмная"
|
||||
|
||||
#: src/ui/gui/settings_page.slint:163
|
||||
msgctxt "Settings"
|
||||
msgid "Light"
|
||||
msgstr "Светлая"
|
||||
|
||||
#: src/ui/gui/vru_dialog.slint:7
|
||||
msgctxt "VruDialog"
|
||||
msgid "What Would You Like To Say?"
|
||||
msgstr "Что вы хотите сказать?"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Server:"
|
||||
#~ msgstr "Сервер:"
|
||||
|
||||
#~ msgctxt "CustomServerDialog"
|
||||
#~ msgid "Enter a custom server URL"
|
||||
#~ msgstr "Введите URL пользовательского сервера"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Custom Server URL (optional):"
|
||||
#~ msgstr "URL пользовательского сервера (необязательно):"
|
||||
|
||||
#~ msgctxt "NetplayJoin"
|
||||
#~ msgid "Server"
|
||||
#~ msgstr "Сервер"
|
||||
|
||||
#~ msgctxt "NetplayWait"
|
||||
#~ msgid "Ping:"
|
||||
#~ msgstr "Пинг:"
|
||||
|
||||
#~ msgctxt "NetplayCreate"
|
||||
#~ msgid "Creating server, please wait...This may take about 30 seconds."
|
||||
#~ msgstr ""
|
||||
#~ "Создание сервера, пожалуйста, подождите... Это может занять около 30 "
|
||||
#~ "секунд."
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Port"
|
||||
#~ msgstr "Порт"
|
||||
|
||||
#~ msgctxt "ControllerConfig"
|
||||
#~ msgid "Controller"
|
||||
#~ msgstr "Контроллер"
|
||||
22233
data/ui/Nintendo - Nintendo 64 (DB Export) (20260609-194259).xml
Normal file
BIN
data/ui/RobotoMono-Regular.ttf
Normal file
1
data/ui/icons/about.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M440-280h80v-240h-80v240Zm68.5-331.5Q520-623 520-640t-11.5-28.5Q497-680 480-680t-28.5 11.5Q440-657 440-640t11.5 28.5Q463-600 480-600t28.5-11.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 553 B |
1
data/ui/icons/achievements.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M280-120v-80h160v-124q-49-11-87.5-41.5T296-442q-75-9-125.5-65.5T120-640v-40q0-33 23.5-56.5T200-760h80v-80h400v80h80q33 0 56.5 23.5T840-680v40q0 76-50.5 132.5T664-442q-18 46-56.5 76.5T520-324v124h160v80H280Zm0-408v-152h-80v40q0 38 22 68.5t58 43.5Zm285 93q35-35 35-85v-240H360v240q0 50 35 85t85 35q50 0 85-35Zm115-93q36-13 58-43.5t22-68.5v-40h-80v152Zm-200-52Z"/></svg>
|
||||
|
After Width: | Height: | Size: 483 B |
1
data/ui/icons/cheats.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-160v-80h320v80H480ZM220-320l-56-56 183-184-183-184 56-56 240 240-240 240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 203 B |
1
data/ui/icons/controller.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M189-160q-60 0-102.5-43T42-307q0-9 1-18t3-18l84-336q14-54 57-87.5t98-33.5h390q55 0 98 33.5t57 87.5l84 336q2 9 3.5 18.5T919-306q0 61-43.5 103.5T771-160q-42 0-78-22t-54-60l-28-58q-5-10-15-15t-21-5H385q-11 0-21 5t-15 15l-28 58q-18 38-54 60t-78 22Zm3-80q19 0 34.5-10t23.5-27l28-57q15-31 44-48.5t63-17.5h190q34 0 63 18t45 48l28 57q8 17 23.5 27t34.5 10q28 0 48-18.5t21-46.5q0 1-2-19l-84-335q-7-27-28-44t-49-17H285q-28 0-49.5 17T208-659l-84 335q-2 6-2 18 0 28 20.5 47t49.5 19Zm376.5-291.5Q580-543 580-560t-11.5-28.5Q557-600 540-600t-28.5 11.5Q500-577 500-560t11.5 28.5Q523-520 540-520t28.5-11.5Zm80-80Q660-623 660-640t-11.5-28.5Q637-680 620-680t-28.5 11.5Q580-657 580-640t11.5 28.5Q603-600 620-600t28.5-11.5Zm0 160Q660-463 660-480t-11.5-28.5Q637-520 620-520t-28.5 11.5Q580-497 580-480t11.5 28.5Q603-440 620-440t28.5-11.5Zm80-80Q740-543 740-560t-11.5-28.5Q717-600 700-600t-28.5 11.5Q660-577 660-560t11.5 28.5Q683-520 700-520t28.5-11.5Zm-367 63Q370-477 370-490v-40h40q13 0 21.5-8.5T440-560q0-13-8.5-21.5T410-590h-40v-40q0-13-8.5-21.5T340-660q-13 0-21.5 8.5T310-630v40h-40q-13 0-21.5 8.5T240-560q0 13 8.5 21.5T270-530h40v40q0 13 8.5 21.5T340-460q13 0 21.5-8.5ZM480-480Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
data/ui/icons/local_game.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>
|
||||
|
After Width: | Height: | Size: 243 B |
1
data/ui/icons/netplay.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Zm0-80h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41Zm220-240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 424 B |
1
data/ui/icons/settings.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>
|
||||
|
After Width: | Height: | Size: 771 B |
1098
parallel-rdp/interface.cpp
Normal file
84
parallel-rdp/interface.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t *RDRAM;
|
||||
uint8_t *DMEM;
|
||||
uint32_t RDRAM_SIZE;
|
||||
uint32_t *DPC_CURRENT_REG;
|
||||
uint32_t *DPC_START_REG;
|
||||
uint32_t *DPC_END_REG;
|
||||
uint32_t *DPC_STATUS_REG;
|
||||
bool PAL;
|
||||
bool widescreen;
|
||||
bool fullscreen;
|
||||
bool vsync;
|
||||
bool integer_scaling;
|
||||
uint32_t upscale;
|
||||
bool ssaa;
|
||||
bool crt;
|
||||
} GFX_INFO;
|
||||
|
||||
typedef struct {
|
||||
bool emu_running;
|
||||
bool save_state;
|
||||
bool load_state;
|
||||
bool load_rewind;
|
||||
bool reset_game;
|
||||
bool enable_speedlimiter;
|
||||
bool lower_volume;
|
||||
bool raise_volume;
|
||||
bool decrease_input_delay;
|
||||
bool increase_input_delay;
|
||||
bool paused;
|
||||
bool frame_advance;
|
||||
uint32_t save_state_slot;
|
||||
} CALL_BACK;
|
||||
|
||||
typedef struct {
|
||||
uint32_t joystick_id;
|
||||
bool connected;
|
||||
} JoystickEvent;
|
||||
|
||||
typedef enum {
|
||||
MESSAGE_VERY_SHORT = 500,
|
||||
MESSAGE_SHORT = 3000,
|
||||
MESSAGE_LONG = 6000,
|
||||
} MESSAGE_LENGTH;
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, const void *font,
|
||||
size_t font_size, uint32_t save_state_slot);
|
||||
void rdp_close();
|
||||
void rdp_set_vi_register(uint32_t reg, uint32_t value);
|
||||
void rdp_update_screen();
|
||||
void rdp_render_frame();
|
||||
CALL_BACK rdp_check_callback();
|
||||
uint64_t rdp_process_commands();
|
||||
void rdp_idle();
|
||||
void rdp_onscreen_message(const char *message, MESSAGE_LENGTH milliseconds);
|
||||
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(GFX_INFO _gfx_info, const uint8_t *state);
|
||||
void rdp_set_fps(uint32_t fps, uint32_t vis);
|
||||
|
||||
JoystickEvent get_joystick_event();
|
||||
|
||||
void achievement_challenge_indicator_add(const char *achievement_title);
|
||||
void achievement_challenge_indicator_remove(const char *achievement_title);
|
||||
void achievement_progress_add(const char *achievement_title,
|
||||
const char *progress);
|
||||
void achievement_progress_remove();
|
||||
void leaderboard_tracker_add(uint32_t id, const char *title,
|
||||
const char *display);
|
||||
void leaderboard_tracker_remove(uint32_t id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1
parallel-rdp/parallel-rdp-standalone
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 388d70f5835b352d841d9d9e5a08c5de01470f41
|
||||
109
parallel-rdp/spirv.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
static const uint32_t vertex_spirv[] =
|
||||
{0x07230203, 0x00010300, 0x000d000b, 0x00000034,
|
||||
0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
|
||||
0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x0008000f, 0x00000000, 0x00000004, 0x6e69616d,
|
||||
0x00000000, 0x00000008, 0x00000016, 0x0000002b,
|
||||
0x00040047, 0x00000008, 0x0000000b, 0x0000002a,
|
||||
0x00030047, 0x00000014, 0x00000002, 0x00050048,
|
||||
0x00000014, 0x00000000, 0x0000000b, 0x00000000,
|
||||
0x00050048, 0x00000014, 0x00000001, 0x0000000b,
|
||||
0x00000001, 0x00050048, 0x00000014, 0x00000002,
|
||||
0x0000000b, 0x00000003, 0x00050048, 0x00000014,
|
||||
0x00000003, 0x0000000b, 0x00000004, 0x00040047,
|
||||
0x0000002b, 0x0000001e, 0x00000000, 0x00020013,
|
||||
0x00000002, 0x00030021, 0x00000003, 0x00000002,
|
||||
0x00040015, 0x00000006, 0x00000020, 0x00000001,
|
||||
0x00040020, 0x00000007, 0x00000001, 0x00000006,
|
||||
0x0004003b, 0x00000007, 0x00000008, 0x00000001,
|
||||
0x0004002b, 0x00000006, 0x0000000a, 0x00000000,
|
||||
0x00020014, 0x0000000b, 0x00030016, 0x0000000f,
|
||||
0x00000020, 0x00040017, 0x00000010, 0x0000000f,
|
||||
0x00000004, 0x00040015, 0x00000011, 0x00000020,
|
||||
0x00000000, 0x0004002b, 0x00000011, 0x00000012,
|
||||
0x00000001, 0x0004001c, 0x00000013, 0x0000000f,
|
||||
0x00000012, 0x0006001e, 0x00000014, 0x00000010,
|
||||
0x0000000f, 0x00000013, 0x00000013, 0x00040020,
|
||||
0x00000015, 0x00000003, 0x00000014, 0x0004003b,
|
||||
0x00000015, 0x00000016, 0x00000003, 0x0004002b,
|
||||
0x0000000f, 0x00000017, 0xbf800000, 0x0004002b,
|
||||
0x0000000f, 0x00000018, 0x00000000, 0x0004002b,
|
||||
0x0000000f, 0x00000019, 0x3f800000, 0x0007002c,
|
||||
0x00000010, 0x0000001a, 0x00000017, 0x00000017,
|
||||
0x00000018, 0x00000019, 0x00040020, 0x0000001b,
|
||||
0x00000003, 0x00000010, 0x0004002b, 0x00000006,
|
||||
0x0000001f, 0x00000001, 0x0004002b, 0x0000000f,
|
||||
0x00000023, 0x40400000, 0x0007002c, 0x00000010,
|
||||
0x00000024, 0x00000017, 0x00000023, 0x00000018,
|
||||
0x00000019, 0x0007002c, 0x00000010, 0x00000027,
|
||||
0x00000023, 0x00000017, 0x00000018, 0x00000019,
|
||||
0x00040017, 0x00000029, 0x0000000f, 0x00000002,
|
||||
0x00040020, 0x0000002a, 0x00000003, 0x00000029,
|
||||
0x0004003b, 0x0000002a, 0x0000002b, 0x00000003,
|
||||
0x0004002b, 0x0000000f, 0x0000002f, 0x3f000000,
|
||||
0x0005002c, 0x00000029, 0x00000033, 0x0000002f,
|
||||
0x0000002f, 0x00050036, 0x00000002, 0x00000004,
|
||||
0x00000000, 0x00000003, 0x000200f8, 0x00000005,
|
||||
0x0004003d, 0x00000006, 0x00000009, 0x00000008,
|
||||
0x000500aa, 0x0000000b, 0x0000000c, 0x00000009,
|
||||
0x0000000a, 0x000300f7, 0x0000000e, 0x00000000,
|
||||
0x000400fa, 0x0000000c, 0x0000000d, 0x0000001d,
|
||||
0x000200f8, 0x0000000d, 0x00050041, 0x0000001b,
|
||||
0x0000001c, 0x00000016, 0x0000000a, 0x0003003e,
|
||||
0x0000001c, 0x0000001a, 0x000200f9, 0x0000000e,
|
||||
0x000200f8, 0x0000001d, 0x000500aa, 0x0000000b,
|
||||
0x00000020, 0x00000009, 0x0000001f, 0x000300f7,
|
||||
0x00000022, 0x00000000, 0x000400fa, 0x00000020,
|
||||
0x00000021, 0x00000026, 0x000200f8, 0x00000021,
|
||||
0x00050041, 0x0000001b, 0x00000025, 0x00000016,
|
||||
0x0000000a, 0x0003003e, 0x00000025, 0x00000024,
|
||||
0x000200f9, 0x00000022, 0x000200f8, 0x00000026,
|
||||
0x00050041, 0x0000001b, 0x00000028, 0x00000016,
|
||||
0x0000000a, 0x0003003e, 0x00000028, 0x00000027,
|
||||
0x000200f9, 0x00000022, 0x000200f8, 0x00000022,
|
||||
0x000200f9, 0x0000000e, 0x000200f8, 0x0000000e,
|
||||
0x00050041, 0x0000001b, 0x0000002c, 0x00000016,
|
||||
0x0000000a, 0x0004003d, 0x00000010, 0x0000002d,
|
||||
0x0000002c, 0x0007004f, 0x00000029, 0x0000002e,
|
||||
0x0000002d, 0x0000002d, 0x00000000, 0x00000001,
|
||||
0x0005008e, 0x00000029, 0x00000030, 0x0000002e,
|
||||
0x0000002f, 0x00050081, 0x00000029, 0x00000032,
|
||||
0x00000030, 0x00000033, 0x0003003e, 0x0000002b,
|
||||
0x00000032, 0x000100fd, 0x00010038};
|
||||
|
||||
static const uint32_t plain_fragment_spirv[] =
|
||||
{0x07230203, 0x00010300, 0x000d000b, 0x00000015,
|
||||
0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
|
||||
0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x0007000f, 0x00000004, 0x00000004, 0x6e69616d,
|
||||
0x00000000, 0x00000009, 0x00000011, 0x00030010,
|
||||
0x00000004, 0x00000007, 0x00040047, 0x00000009,
|
||||
0x0000001e, 0x00000000, 0x00040047, 0x0000000d,
|
||||
0x00000021, 0x00000000, 0x00040047, 0x0000000d,
|
||||
0x00000022, 0x00000000, 0x00040047, 0x00000011,
|
||||
0x0000001e, 0x00000000, 0x00020013, 0x00000002,
|
||||
0x00030021, 0x00000003, 0x00000002, 0x00030016,
|
||||
0x00000006, 0x00000020, 0x00040017, 0x00000007,
|
||||
0x00000006, 0x00000004, 0x00040020, 0x00000008,
|
||||
0x00000003, 0x00000007, 0x0004003b, 0x00000008,
|
||||
0x00000009, 0x00000003, 0x00090019, 0x0000000a,
|
||||
0x00000006, 0x00000001, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000001, 0x00000000, 0x0003001b,
|
||||
0x0000000b, 0x0000000a, 0x00040020, 0x0000000c,
|
||||
0x00000000, 0x0000000b, 0x0004003b, 0x0000000c,
|
||||
0x0000000d, 0x00000000, 0x00040017, 0x0000000f,
|
||||
0x00000006, 0x00000002, 0x00040020, 0x00000010,
|
||||
0x00000001, 0x0000000f, 0x0004003b, 0x00000010,
|
||||
0x00000011, 0x00000001, 0x0004002b, 0x00000006,
|
||||
0x00000013, 0x00000000, 0x00050036, 0x00000002,
|
||||
0x00000004, 0x00000000, 0x00000003, 0x000200f8,
|
||||
0x00000005, 0x0004003d, 0x0000000b, 0x0000000e,
|
||||
0x0000000d, 0x0004003d, 0x0000000f, 0x00000012,
|
||||
0x00000011, 0x00070058, 0x00000007, 0x00000014,
|
||||
0x0000000e, 0x00000012, 0x00000002, 0x00000013,
|
||||
0x0003003e, 0x00000009, 0x00000014, 0x000100fd,
|
||||
0x00010038};
|
||||
456
parallel-rdp/spirv_crt.hpp
Normal file
@ -0,0 +1,456 @@
|
||||
#pragma once
|
||||
|
||||
static const uint32_t crt_fragment_spirv[] =
|
||||
{0x07230203, 0x00010300, 0x000d000b, 0x000004fd,
|
||||
0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
|
||||
0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
0x0007000f, 0x00000004, 0x00000004, 0x6e69616d,
|
||||
0x00000000, 0x000001ba, 0x0000021a, 0x00030010,
|
||||
0x00000004, 0x00000007, 0x00030047, 0x00000185,
|
||||
0x00000002, 0x00050048, 0x00000185, 0x00000000,
|
||||
0x00000023, 0x00000000, 0x00050048, 0x00000185,
|
||||
0x00000001, 0x00000023, 0x00000010, 0x00040047,
|
||||
0x000001ba, 0x0000001e, 0x00000000, 0x00040047,
|
||||
0x000001ca, 0x00000021, 0x00000000, 0x00040047,
|
||||
0x000001ca, 0x00000022, 0x00000000, 0x00040047,
|
||||
0x0000021a, 0x0000001e, 0x00000000, 0x00020013,
|
||||
0x00000002, 0x00030021, 0x00000003, 0x00000002,
|
||||
0x00030016, 0x00000006, 0x00000020, 0x00090019,
|
||||
0x00000007, 0x00000006, 0x00000001, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000001, 0x00000000,
|
||||
0x0003001b, 0x00000008, 0x00000007, 0x00040020,
|
||||
0x00000009, 0x00000000, 0x00000008, 0x00040017,
|
||||
0x0000000a, 0x00000006, 0x00000002, 0x00040017,
|
||||
0x0000000c, 0x00000006, 0x00000003, 0x00040017,
|
||||
0x00000037, 0x00000006, 0x00000004, 0x0004002b,
|
||||
0x00000006, 0x0000003a, 0x4019999a, 0x0006002c,
|
||||
0x0000000c, 0x0000003b, 0x0000003a, 0x0000003a,
|
||||
0x0000003a, 0x0004002b, 0x00000006, 0x00000049,
|
||||
0x3f800000, 0x0004002b, 0x00000006, 0x0000004a,
|
||||
0x00000000, 0x0004002b, 0x00000006, 0x00000068,
|
||||
0xbf800000, 0x00040015, 0x0000006b, 0x00000020,
|
||||
0x00000001, 0x0004002b, 0x0000006b, 0x0000006c,
|
||||
0x00000000, 0x00040015, 0x0000006f, 0x00000020,
|
||||
0x00000000, 0x0004002b, 0x0000006f, 0x00000070,
|
||||
0x00000000, 0x0004002b, 0x0000006b, 0x00000074,
|
||||
0x00000001, 0x0004002b, 0x0000006f, 0x00000077,
|
||||
0x00000001, 0x0004002b, 0x00000006, 0x000000a1,
|
||||
0x3f000000, 0x0005002c, 0x0000000a, 0x000000fc,
|
||||
0x000000a1, 0x0000004a, 0x0004002b, 0x00000006,
|
||||
0x00000101, 0x3a83126f, 0x0005002c, 0x0000000a,
|
||||
0x00000102, 0x000000a1, 0x00000101, 0x0004002b,
|
||||
0x00000006, 0x0000010b, 0x40490fdb, 0x0004002b,
|
||||
0x00000006, 0x00000116, 0x40000000, 0x0004002b,
|
||||
0x00000006, 0x0000011c, 0x3727c5ac, 0x0007002c,
|
||||
0x00000037, 0x0000012c, 0x00000049, 0x00000049,
|
||||
0x00000049, 0x00000049, 0x00040018, 0x00000149,
|
||||
0x00000037, 0x00000004, 0x0006002c, 0x0000000c,
|
||||
0x00000165, 0x000000a1, 0x000000a1, 0x000000a1,
|
||||
0x0004002b, 0x00000006, 0x00000166, 0x3fc00000,
|
||||
0x0006002c, 0x0000000c, 0x00000167, 0x00000166,
|
||||
0x00000166, 0x00000166, 0x0004002b, 0x00000006,
|
||||
0x00000169, 0x3ecccccd, 0x0006002c, 0x0000000c,
|
||||
0x0000016a, 0x00000169, 0x00000169, 0x00000169,
|
||||
0x0004001e, 0x00000185, 0x00000037, 0x00000037,
|
||||
0x00040020, 0x00000186, 0x00000009, 0x00000185,
|
||||
0x0004003b, 0x00000186, 0x00000187, 0x00000009,
|
||||
0x00040020, 0x00000188, 0x00000009, 0x00000006,
|
||||
0x00020014, 0x00000196, 0x0006002c, 0x0000000c,
|
||||
0x0000019a, 0x00000049, 0x0000004a, 0x00000049,
|
||||
0x0006002c, 0x0000000c, 0x000001a1, 0x0000004a,
|
||||
0x00000049, 0x0000004a, 0x0006002c, 0x0000000c,
|
||||
0x000001a4, 0x0000004a, 0x0000004a, 0x00000049,
|
||||
0x0004002b, 0x0000006f, 0x000001aa, 0x00000003,
|
||||
0x00040020, 0x000001b9, 0x00000001, 0x0000000a,
|
||||
0x0004003b, 0x000001b9, 0x000001ba, 0x00000001,
|
||||
0x00040020, 0x000001bc, 0x00000009, 0x00000037,
|
||||
0x0004003b, 0x00000009, 0x000001ca, 0x00000000,
|
||||
0x0004002b, 0x00000006, 0x000001dc, 0x40400000,
|
||||
0x0004002b, 0x00000006, 0x000001ff, 0x3dcccccd,
|
||||
0x00040020, 0x00000206, 0x00000001, 0x00000006,
|
||||
0x0004002b, 0x00000006, 0x0000020c, 0x3e99999a,
|
||||
0x0004002b, 0x00000006, 0x00000210, 0x3d4ccccd,
|
||||
0x0004002b, 0x00000006, 0x00000216, 0x3ed55555,
|
||||
0x0006002c, 0x0000000c, 0x00000217, 0x00000216,
|
||||
0x00000216, 0x00000216, 0x00040020, 0x00000219,
|
||||
0x00000003, 0x00000037, 0x0004003b, 0x00000219,
|
||||
0x0000021a, 0x00000003, 0x0005002c, 0x0000000a,
|
||||
0x000004f3, 0x000000a1, 0x000000a1, 0x0007002c,
|
||||
0x00000037, 0x000004f4, 0x0000011c, 0x0000011c,
|
||||
0x0000011c, 0x0000011c, 0x0006002c, 0x0000000c,
|
||||
0x000004f6, 0x00000116, 0x00000116, 0x00000116,
|
||||
0x0006002c, 0x0000000c, 0x000004f7, 0x00000049,
|
||||
0x00000049, 0x00000049, 0x0006002c, 0x0000000c,
|
||||
0x000004f8, 0x0000004a, 0x0000004a, 0x0000004a,
|
||||
0x0006002c, 0x0000000c, 0x000004f9, 0x0000020c,
|
||||
0x0000020c, 0x0000020c, 0x0007002c, 0x00000037,
|
||||
0x000004fa, 0x000000a1, 0x000000a1, 0x000000a1,
|
||||
0x000000a1, 0x0004002b, 0x00000006, 0x000004fb,
|
||||
0x3eaaaaab, 0x00050036, 0x00000002, 0x00000004,
|
||||
0x00000000, 0x00000003, 0x000200f8, 0x00000005,
|
||||
0x00060041, 0x00000188, 0x000001a8, 0x00000187,
|
||||
0x00000074, 0x00000077, 0x0004003d, 0x00000006,
|
||||
0x000001a9, 0x000001a8, 0x00060041, 0x00000188,
|
||||
0x000001ab, 0x00000187, 0x0000006c, 0x000001aa,
|
||||
0x0004003d, 0x00000006, 0x000001ac, 0x000001ab,
|
||||
0x00050085, 0x00000006, 0x000001ad, 0x000001a9,
|
||||
0x000001ac, 0x0006000c, 0x00000006, 0x000001ae,
|
||||
0x00000001, 0x00000008, 0x000001ad, 0x00050088,
|
||||
0x00000006, 0x000001b2, 0x000000a1, 0x000001ae,
|
||||
0x0005008d, 0x00000006, 0x000001b4, 0x000001ae,
|
||||
0x00000116, 0x000500b7, 0x00000196, 0x000001b5,
|
||||
0x000001b4, 0x0000004a, 0x000600a9, 0x00000006,
|
||||
0x000004fc, 0x000001b5, 0x0000004a, 0x000001b2,
|
||||
0x0004003d, 0x0000000a, 0x000001bb, 0x000001ba,
|
||||
0x00050041, 0x000001bc, 0x000001bd, 0x00000187,
|
||||
0x0000006c, 0x0004003d, 0x00000037, 0x000001be,
|
||||
0x000001bd, 0x0007004f, 0x0000000a, 0x000001bf,
|
||||
0x000001be, 0x000001be, 0x00000000, 0x00000001,
|
||||
0x00050085, 0x0000000a, 0x000001c0, 0x000001bb,
|
||||
0x000001bf, 0x00050050, 0x0000000a, 0x000001c3,
|
||||
0x0000004a, 0x000004fc, 0x00050083, 0x0000000a,
|
||||
0x000001c4, 0x000001c0, 0x000001c3, 0x0007004f,
|
||||
0x0000000a, 0x000001c7, 0x000001be, 0x000001be,
|
||||
0x00000002, 0x00000003, 0x00050085, 0x0000000a,
|
||||
0x000001c8, 0x000001c4, 0x000001c7, 0x00050051,
|
||||
0x00000006, 0x00000249, 0x000001be, 0x00000000,
|
||||
0x00050088, 0x00000006, 0x0000024a, 0x00000049,
|
||||
0x00000249, 0x00050050, 0x0000000a, 0x0000024b,
|
||||
0x0000024a, 0x0000004a, 0x00050051, 0x00000006,
|
||||
0x0000024d, 0x000001be, 0x00000001, 0x00050088,
|
||||
0x00000006, 0x0000024e, 0x00000049, 0x0000024d,
|
||||
0x00050050, 0x0000000a, 0x0000024f, 0x0000004a,
|
||||
0x0000024e, 0x00050085, 0x0000000a, 0x00000252,
|
||||
0x000001c8, 0x000001bf, 0x0006000c, 0x0000000a,
|
||||
0x00000254, 0x00000001, 0x00000008, 0x00000252,
|
||||
0x00050081, 0x0000000a, 0x00000256, 0x00000254,
|
||||
0x000004f3, 0x00050088, 0x0000000a, 0x00000258,
|
||||
0x00000256, 0x000001bf, 0x0006000c, 0x0000000a,
|
||||
0x0000025a, 0x00000001, 0x0000000a, 0x00000252,
|
||||
0x00050083, 0x0000000a, 0x0000025c, 0x0000025a,
|
||||
0x000004f3, 0x0005008e, 0x0000000a, 0x0000025d,
|
||||
0x0000025c, 0x00000068, 0x00050083, 0x0000000a,
|
||||
0x00000260, 0x00000258, 0x0000024f, 0x0004003d,
|
||||
0x00000008, 0x0000028a, 0x000001ca, 0x00050083,
|
||||
0x0000000a, 0x0000028d, 0x00000260, 0x0000024b,
|
||||
0x00050057, 0x00000037, 0x0000028e, 0x0000028a,
|
||||
0x0000028d, 0x0008004f, 0x0000000c, 0x0000028f,
|
||||
0x0000028e, 0x0000028e, 0x00000000, 0x00000001,
|
||||
0x00000002, 0x0007000c, 0x0000000c, 0x00000290,
|
||||
0x00000001, 0x0000001a, 0x0000028f, 0x0000003b,
|
||||
0x00050057, 0x00000037, 0x00000293, 0x0000028a,
|
||||
0x00000260, 0x0008004f, 0x0000000c, 0x00000294,
|
||||
0x00000293, 0x00000293, 0x00000000, 0x00000001,
|
||||
0x00000002, 0x0007000c, 0x0000000c, 0x00000295,
|
||||
0x00000001, 0x0000001a, 0x00000294, 0x0000003b,
|
||||
0x00050081, 0x0000000a, 0x00000299, 0x00000260,
|
||||
0x0000024b, 0x00050057, 0x00000037, 0x0000029a,
|
||||
0x0000028a, 0x00000299, 0x0008004f, 0x0000000c,
|
||||
0x0000029b, 0x0000029a, 0x0000029a, 0x00000000,
|
||||
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
|
||||
0x0000029c, 0x00000001, 0x0000001a, 0x0000029b,
|
||||
0x0000003b, 0x00050083, 0x0000000a, 0x000002af,
|
||||
0x00000258, 0x0000024b, 0x00050057, 0x00000037,
|
||||
0x000002b0, 0x0000028a, 0x000002af, 0x0008004f,
|
||||
0x0000000c, 0x000002b1, 0x000002b0, 0x000002b0,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
|
||||
0x0000000c, 0x000002b2, 0x00000001, 0x0000001a,
|
||||
0x000002b1, 0x0000003b, 0x00050057, 0x00000037,
|
||||
0x000002b5, 0x0000028a, 0x00000258, 0x0008004f,
|
||||
0x0000000c, 0x000002b6, 0x000002b5, 0x000002b5,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
|
||||
0x0000000c, 0x000002b7, 0x00000001, 0x0000001a,
|
||||
0x000002b6, 0x0000003b, 0x00050081, 0x0000000a,
|
||||
0x000002bb, 0x00000258, 0x0000024b, 0x00050057,
|
||||
0x00000037, 0x000002bc, 0x0000028a, 0x000002bb,
|
||||
0x0008004f, 0x0000000c, 0x000002bd, 0x000002bc,
|
||||
0x000002bc, 0x00000000, 0x00000001, 0x00000002,
|
||||
0x0007000c, 0x0000000c, 0x000002be, 0x00000001,
|
||||
0x0000001a, 0x000002bd, 0x0000003b, 0x00050081,
|
||||
0x0000000a, 0x00000268, 0x00000258, 0x0000024f,
|
||||
0x00050083, 0x0000000a, 0x000002d1, 0x00000268,
|
||||
0x0000024b, 0x00050057, 0x00000037, 0x000002d2,
|
||||
0x0000028a, 0x000002d1, 0x0008004f, 0x0000000c,
|
||||
0x000002d3, 0x000002d2, 0x000002d2, 0x00000000,
|
||||
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
|
||||
0x000002d4, 0x00000001, 0x0000001a, 0x000002d3,
|
||||
0x0000003b, 0x00050057, 0x00000037, 0x000002d7,
|
||||
0x0000028a, 0x00000268, 0x0008004f, 0x0000000c,
|
||||
0x000002d8, 0x000002d7, 0x000002d7, 0x00000000,
|
||||
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
|
||||
0x000002d9, 0x00000001, 0x0000001a, 0x000002d8,
|
||||
0x0000003b, 0x00050081, 0x0000000a, 0x000002dd,
|
||||
0x00000268, 0x0000024b, 0x00050057, 0x00000037,
|
||||
0x000002de, 0x0000028a, 0x000002dd, 0x0008004f,
|
||||
0x0000000c, 0x000002df, 0x000002de, 0x000002de,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
|
||||
0x0000000c, 0x000002e0, 0x00000001, 0x0000001a,
|
||||
0x000002df, 0x0000003b, 0x00050051, 0x00000006,
|
||||
0x0000026d, 0x0000025d, 0x00000000, 0x00050083,
|
||||
0x00000006, 0x000002f3, 0x0000026d, 0x00000049,
|
||||
0x00050081, 0x00000006, 0x000002f6, 0x0000026d,
|
||||
0x00000049, 0x00060050, 0x0000000c, 0x000002f7,
|
||||
0x000002f3, 0x0000026d, 0x000002f6, 0x00050085,
|
||||
0x0000000c, 0x000002fa, 0x000002f7, 0x000004f6,
|
||||
0x00050085, 0x0000000c, 0x000002fd, 0x000002fa,
|
||||
0x000002fa, 0x0005008e, 0x0000000c, 0x000002fe,
|
||||
0x000002fd, 0x00000068, 0x0006000c, 0x0000000c,
|
||||
0x000002ff, 0x00000001, 0x0000001d, 0x000002fe,
|
||||
0x00050051, 0x00000006, 0x00000303, 0x000002ff,
|
||||
0x00000000, 0x0005008e, 0x0000000c, 0x00000304,
|
||||
0x00000290, 0x00000303, 0x00050051, 0x00000006,
|
||||
0x00000308, 0x000002ff, 0x00000001, 0x0005008e,
|
||||
0x0000000c, 0x00000309, 0x00000295, 0x00000308,
|
||||
0x00050081, 0x0000000c, 0x0000030a, 0x00000304,
|
||||
0x00000309, 0x00050051, 0x00000006, 0x0000030e,
|
||||
0x000002ff, 0x00000002, 0x0005008e, 0x0000000c,
|
||||
0x0000030f, 0x0000029c, 0x0000030e, 0x00050081,
|
||||
0x0000000c, 0x00000310, 0x0000030a, 0x0000030f,
|
||||
0x00050081, 0x00000006, 0x00000315, 0x00000303,
|
||||
0x00000308, 0x00050081, 0x00000006, 0x00000318,
|
||||
0x00000315, 0x0000030e, 0x00060050, 0x0000000c,
|
||||
0x00000319, 0x00000318, 0x00000318, 0x00000318,
|
||||
0x00050088, 0x0000000c, 0x0000031a, 0x00000310,
|
||||
0x00000319, 0x0005008e, 0x0000000c, 0x00000331,
|
||||
0x000002b2, 0x00000303, 0x0005008e, 0x0000000c,
|
||||
0x00000336, 0x000002b7, 0x00000308, 0x00050081,
|
||||
0x0000000c, 0x00000337, 0x00000331, 0x00000336,
|
||||
0x0005008e, 0x0000000c, 0x0000033c, 0x000002be,
|
||||
0x0000030e, 0x00050081, 0x0000000c, 0x0000033d,
|
||||
0x00000337, 0x0000033c, 0x00050088, 0x0000000c,
|
||||
0x00000347, 0x0000033d, 0x00000319, 0x0005008e,
|
||||
0x0000000c, 0x0000035e, 0x000002d4, 0x00000303,
|
||||
0x0005008e, 0x0000000c, 0x00000363, 0x000002d9,
|
||||
0x00000308, 0x00050081, 0x0000000c, 0x00000364,
|
||||
0x0000035e, 0x00000363, 0x0005008e, 0x0000000c,
|
||||
0x00000369, 0x000002e0, 0x0000030e, 0x00050081,
|
||||
0x0000000c, 0x0000036a, 0x00000364, 0x00000369,
|
||||
0x00050088, 0x0000000c, 0x00000374, 0x0000036a,
|
||||
0x00000319, 0x00050051, 0x00000006, 0x00000286,
|
||||
0x0000025d, 0x00000001, 0x00050083, 0x00000006,
|
||||
0x0000037a, 0x00000286, 0x00000049, 0x00050081,
|
||||
0x00000006, 0x0000037d, 0x00000286, 0x00000049,
|
||||
0x00060050, 0x0000000c, 0x0000037e, 0x0000037a,
|
||||
0x00000286, 0x0000037d, 0x00050085, 0x0000000c,
|
||||
0x00000381, 0x0000037e, 0x000004f6, 0x00050085,
|
||||
0x0000000c, 0x00000384, 0x00000381, 0x00000381,
|
||||
0x0005008e, 0x0000000c, 0x00000385, 0x00000384,
|
||||
0x00000068, 0x0006000c, 0x0000000c, 0x00000386,
|
||||
0x00000001, 0x0000001d, 0x00000385, 0x00050051,
|
||||
0x00000006, 0x0000038a, 0x00000386, 0x00000000,
|
||||
0x0005008e, 0x0000000c, 0x0000038b, 0x0000031a,
|
||||
0x0000038a, 0x00050051, 0x00000006, 0x0000038f,
|
||||
0x00000386, 0x00000001, 0x0005008e, 0x0000000c,
|
||||
0x00000390, 0x00000347, 0x0000038f, 0x00050081,
|
||||
0x0000000c, 0x00000391, 0x0000038b, 0x00000390,
|
||||
0x00050051, 0x00000006, 0x00000395, 0x00000386,
|
||||
0x00000002, 0x0005008e, 0x0000000c, 0x00000396,
|
||||
0x00000374, 0x00000395, 0x00050081, 0x0000000c,
|
||||
0x00000397, 0x00000391, 0x00000396, 0x00050081,
|
||||
0x00000006, 0x0000039c, 0x0000038a, 0x0000038f,
|
||||
0x00050081, 0x00000006, 0x0000039f, 0x0000039c,
|
||||
0x00000395, 0x00060050, 0x0000000c, 0x000003a0,
|
||||
0x0000039f, 0x0000039f, 0x0000039f, 0x00050088,
|
||||
0x0000000c, 0x000003a1, 0x00000397, 0x000003a0,
|
||||
0x00060052, 0x0000000a, 0x000004e1, 0x00000249,
|
||||
0x000001bf, 0x00000000, 0x00050085, 0x0000000a,
|
||||
0x000003b6, 0x000001c8, 0x000004e1, 0x00050083,
|
||||
0x0000000a, 0x000003b7, 0x000003b6, 0x000000fc,
|
||||
0x0006000c, 0x0000000a, 0x000003b9, 0x00000001,
|
||||
0x00000008, 0x000003b7, 0x00050081, 0x0000000a,
|
||||
0x000003ba, 0x000003b9, 0x00000102, 0x00050088,
|
||||
0x0000000a, 0x000003bc, 0x000003ba, 0x000004e1,
|
||||
0x0006000c, 0x0000000a, 0x000003be, 0x00000001,
|
||||
0x0000000a, 0x000003b7, 0x00050051, 0x00000006,
|
||||
0x000003c0, 0x000003be, 0x00000000, 0x00050081,
|
||||
0x00000006, 0x000003c1, 0x000003c0, 0x00000049,
|
||||
0x00050083, 0x00000006, 0x000003c6, 0x000003c0,
|
||||
0x00000049, 0x00050083, 0x00000006, 0x000003c9,
|
||||
0x000003c0, 0x00000116, 0x00070050, 0x00000037,
|
||||
0x000003ca, 0x000003c1, 0x000003c0, 0x000003c6,
|
||||
0x000003c9, 0x0005008e, 0x00000037, 0x000003cb,
|
||||
0x000003ca, 0x0000010b, 0x0006000c, 0x00000037,
|
||||
0x000003cd, 0x00000001, 0x00000004, 0x000003cb,
|
||||
0x0007000c, 0x00000037, 0x000003cf, 0x00000001,
|
||||
0x00000028, 0x000003cd, 0x000004f4, 0x0006000c,
|
||||
0x00000037, 0x000003d1, 0x00000001, 0x0000000d,
|
||||
0x000003cf, 0x0005008e, 0x00000037, 0x000003d2,
|
||||
0x000003d1, 0x00000116, 0x00050085, 0x00000037,
|
||||
0x000003d5, 0x000003cf, 0x000004fa, 0x0006000c,
|
||||
0x00000037, 0x000003d6, 0x00000001, 0x0000000d,
|
||||
0x000003d5, 0x00050085, 0x00000037, 0x000003d7,
|
||||
0x000003d2, 0x000003d6, 0x00050085, 0x00000037,
|
||||
0x000003da, 0x000003cf, 0x000003cf, 0x00050088,
|
||||
0x00000037, 0x000003db, 0x000003d7, 0x000003da,
|
||||
0x00050094, 0x00000006, 0x000003dd, 0x000003db,
|
||||
0x0000012c, 0x00070050, 0x00000037, 0x000003df,
|
||||
0x000003dd, 0x000003dd, 0x000003dd, 0x000003dd,
|
||||
0x00050088, 0x00000037, 0x000003e0, 0x000003db,
|
||||
0x000003df, 0x00050057, 0x00000037, 0x000003e3,
|
||||
0x0000028a, 0x000003bc, 0x0008004f, 0x0000000c,
|
||||
0x000003e4, 0x000003e3, 0x000003e3, 0x00000000,
|
||||
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
|
||||
0x000003e5, 0x00000001, 0x0000001a, 0x000003e4,
|
||||
0x0000003b, 0x00050051, 0x00000006, 0x000003e6,
|
||||
0x000003e5, 0x00000000, 0x00050051, 0x00000006,
|
||||
0x000003e7, 0x000003e5, 0x00000001, 0x00050051,
|
||||
0x00000006, 0x000003e8, 0x000003e5, 0x00000002,
|
||||
0x00050081, 0x0000000a, 0x000003ed, 0x000003bc,
|
||||
0x0000024b, 0x00050057, 0x00000037, 0x000003ee,
|
||||
0x0000028a, 0x000003ed, 0x0008004f, 0x0000000c,
|
||||
0x000003ef, 0x000003ee, 0x000003ee, 0x00000000,
|
||||
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
|
||||
0x000003f0, 0x00000001, 0x0000001a, 0x000003ef,
|
||||
0x0000003b, 0x00050051, 0x00000006, 0x000003f1,
|
||||
0x000003f0, 0x00000000, 0x00050051, 0x00000006,
|
||||
0x000003f2, 0x000003f0, 0x00000001, 0x00050051,
|
||||
0x00000006, 0x000003f3, 0x000003f0, 0x00000002,
|
||||
0x00070050, 0x00000037, 0x00000407, 0x000003e6,
|
||||
0x000003e7, 0x000003e8, 0x00000049, 0x00070050,
|
||||
0x00000037, 0x00000409, 0x000003f1, 0x000003f2,
|
||||
0x000003f3, 0x00000049, 0x00070050, 0x00000149,
|
||||
0x0000040b, 0x00000407, 0x00000407, 0x00000409,
|
||||
0x00000409, 0x00050091, 0x00000037, 0x0000040d,
|
||||
0x0000040b, 0x000003e0, 0x0008004f, 0x0000000c,
|
||||
0x0000040e, 0x0000040d, 0x0000040d, 0x00000000,
|
||||
0x00000001, 0x00000002, 0x00050085, 0x00000006,
|
||||
0x0000041b, 0x00000249, 0x000001dc, 0x00060052,
|
||||
0x0000000a, 0x000004e9, 0x0000041b, 0x000001bf,
|
||||
0x00000000, 0x00050088, 0x00000006, 0x0000041f,
|
||||
0x000004fb, 0x00000249, 0x00050050, 0x0000000a,
|
||||
0x00000420, 0x0000041f, 0x0000004a, 0x00050085,
|
||||
0x0000000a, 0x00000423, 0x000001c8, 0x000004e9,
|
||||
0x00050083, 0x0000000a, 0x00000424, 0x00000423,
|
||||
0x000000fc, 0x0006000c, 0x0000000a, 0x00000426,
|
||||
0x00000001, 0x00000008, 0x00000424, 0x00050081,
|
||||
0x0000000a, 0x00000427, 0x00000426, 0x00000102,
|
||||
0x00050088, 0x0000000a, 0x00000429, 0x00000427,
|
||||
0x000004e9, 0x0006000c, 0x0000000a, 0x0000042b,
|
||||
0x00000001, 0x0000000a, 0x00000424, 0x00050051,
|
||||
0x00000006, 0x0000042d, 0x0000042b, 0x00000000,
|
||||
0x00050081, 0x00000006, 0x0000042e, 0x0000042d,
|
||||
0x00000049, 0x00050083, 0x00000006, 0x00000433,
|
||||
0x0000042d, 0x00000049, 0x00050083, 0x00000006,
|
||||
0x00000436, 0x0000042d, 0x00000116, 0x00070050,
|
||||
0x00000037, 0x00000437, 0x0000042e, 0x0000042d,
|
||||
0x00000433, 0x00000436, 0x0005008e, 0x00000037,
|
||||
0x00000438, 0x00000437, 0x0000010b, 0x0006000c,
|
||||
0x00000037, 0x0000043a, 0x00000001, 0x00000004,
|
||||
0x00000438, 0x0007000c, 0x00000037, 0x0000043c,
|
||||
0x00000001, 0x00000028, 0x0000043a, 0x000004f4,
|
||||
0x0006000c, 0x00000037, 0x0000043e, 0x00000001,
|
||||
0x0000000d, 0x0000043c, 0x0005008e, 0x00000037,
|
||||
0x0000043f, 0x0000043e, 0x00000116, 0x00050085,
|
||||
0x00000037, 0x00000442, 0x0000043c, 0x000004fa,
|
||||
0x0006000c, 0x00000037, 0x00000443, 0x00000001,
|
||||
0x0000000d, 0x00000442, 0x00050085, 0x00000037,
|
||||
0x00000444, 0x0000043f, 0x00000443, 0x00050085,
|
||||
0x00000037, 0x00000447, 0x0000043c, 0x0000043c,
|
||||
0x00050088, 0x00000037, 0x00000448, 0x00000444,
|
||||
0x00000447, 0x00050094, 0x00000006, 0x0000044a,
|
||||
0x00000448, 0x0000012c, 0x00070050, 0x00000037,
|
||||
0x0000044c, 0x0000044a, 0x0000044a, 0x0000044a,
|
||||
0x0000044a, 0x00050088, 0x00000037, 0x0000044d,
|
||||
0x00000448, 0x0000044c, 0x00050057, 0x00000037,
|
||||
0x00000450, 0x0000028a, 0x00000429, 0x0008004f,
|
||||
0x0000000c, 0x00000451, 0x00000450, 0x00000450,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
|
||||
0x0000000c, 0x00000452, 0x00000001, 0x0000001a,
|
||||
0x00000451, 0x0000003b, 0x00050051, 0x00000006,
|
||||
0x00000453, 0x00000452, 0x00000000, 0x00050051,
|
||||
0x00000006, 0x00000454, 0x00000452, 0x00000001,
|
||||
0x00050051, 0x00000006, 0x00000455, 0x00000452,
|
||||
0x00000002, 0x00050081, 0x0000000a, 0x0000045a,
|
||||
0x00000429, 0x00000420, 0x00050057, 0x00000037,
|
||||
0x0000045b, 0x0000028a, 0x0000045a, 0x0008004f,
|
||||
0x0000000c, 0x0000045c, 0x0000045b, 0x0000045b,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
|
||||
0x0000000c, 0x0000045d, 0x00000001, 0x0000001a,
|
||||
0x0000045c, 0x0000003b, 0x00050051, 0x00000006,
|
||||
0x0000045e, 0x0000045d, 0x00000000, 0x00050051,
|
||||
0x00000006, 0x0000045f, 0x0000045d, 0x00000001,
|
||||
0x00050051, 0x00000006, 0x00000460, 0x0000045d,
|
||||
0x00000002, 0x00070050, 0x00000037, 0x00000474,
|
||||
0x00000453, 0x00000454, 0x00000455, 0x00000049,
|
||||
0x00070050, 0x00000037, 0x00000476, 0x0000045e,
|
||||
0x0000045f, 0x00000460, 0x00000049, 0x00070050,
|
||||
0x00000149, 0x00000478, 0x00000474, 0x00000474,
|
||||
0x00000476, 0x00000476, 0x00050091, 0x00000037,
|
||||
0x0000047a, 0x00000478, 0x0000044d, 0x0008004f,
|
||||
0x0000000c, 0x0000047b, 0x0000047a, 0x0000047a,
|
||||
0x00000000, 0x00000001, 0x00000002, 0x00050085,
|
||||
0x0000000c, 0x000001e8, 0x0000047b, 0x0000040e,
|
||||
0x0006000c, 0x0000000c, 0x000001e9, 0x00000001,
|
||||
0x0000001f, 0x000001e8, 0x00050051, 0x00000006,
|
||||
0x000001eb, 0x000001c8, 0x00000001, 0x00060041,
|
||||
0x00000188, 0x000001ec, 0x00000187, 0x0000006c,
|
||||
0x00000077, 0x0004003d, 0x00000006, 0x000001ed,
|
||||
0x000001ec, 0x00050085, 0x00000006, 0x000001ee,
|
||||
0x000001eb, 0x000001ed, 0x0006000c, 0x00000006,
|
||||
0x000001ef, 0x00000001, 0x0000000a, 0x000001ee,
|
||||
0x0007000c, 0x0000000c, 0x00000482, 0x00000001,
|
||||
0x0000001a, 0x0000040e, 0x0000016a, 0x0008000c,
|
||||
0x0000000c, 0x00000483, 0x00000001, 0x0000002e,
|
||||
0x00000165, 0x00000167, 0x00000482, 0x00050088,
|
||||
0x0000000c, 0x00000486, 0x000004f6, 0x00000483,
|
||||
0x0005008e, 0x0000000c, 0x00000488, 0x00000486,
|
||||
0x000000a1, 0x0005008e, 0x0000000c, 0x0000048b,
|
||||
0x00000486, 0x000001ef, 0x00050083, 0x0000000c,
|
||||
0x0000048d, 0x0000048b, 0x00000488, 0x0006000c,
|
||||
0x0000000c, 0x0000048e, 0x00000001, 0x00000004,
|
||||
0x0000048d, 0x00050083, 0x0000000c, 0x00000490,
|
||||
0x000004f7, 0x0000048e, 0x0008000c, 0x0000000c,
|
||||
0x00000493, 0x00000001, 0x00000031, 0x000004f8,
|
||||
0x000004f7, 0x00000490, 0x00050085, 0x0000000c,
|
||||
0x00000495, 0x00000493, 0x00000488, 0x00050085,
|
||||
0x0000000c, 0x000001f5, 0x000001e9, 0x00000495,
|
||||
0x00050083, 0x0000000c, 0x000001f8, 0x000003a1,
|
||||
0x000001f5, 0x0008000c, 0x0000000c, 0x000001fb,
|
||||
0x00000001, 0x0000002b, 0x000001f8, 0x000004f8,
|
||||
0x000004f7, 0x00050085, 0x0000000c, 0x000001fe,
|
||||
0x000001fb, 0x000001fb, 0x0005008e, 0x0000000c,
|
||||
0x00000200, 0x000001fe, 0x000001ff, 0x00050081,
|
||||
0x0000000c, 0x00000202, 0x000001f5, 0x00000200,
|
||||
0x00050041, 0x00000206, 0x00000207, 0x000001ba,
|
||||
0x00000070, 0x0004003d, 0x00000006, 0x00000208,
|
||||
0x00000207, 0x000300f7, 0x000004b3, 0x00000000,
|
||||
0x000300fb, 0x00000070, 0x0000049b, 0x000200f8,
|
||||
0x0000049b, 0x00060041, 0x00000188, 0x0000049d,
|
||||
0x00000187, 0x00000074, 0x00000070, 0x0004003d,
|
||||
0x00000006, 0x0000049e, 0x0000049d, 0x00050085,
|
||||
0x00000006, 0x0000049f, 0x00000208, 0x0000049e,
|
||||
0x0006000c, 0x00000006, 0x000004a7, 0x00000001,
|
||||
0x00000008, 0x0000049f, 0x0005008d, 0x00000006,
|
||||
0x000004a8, 0x000004a7, 0x00000116, 0x000500b4,
|
||||
0x00000196, 0x000004aa, 0x000004a8, 0x0000004a,
|
||||
0x000300f7, 0x000004b2, 0x00000000, 0x000400fa,
|
||||
0x000004aa, 0x000004ab, 0x000004ac, 0x000200f8,
|
||||
0x000004ac, 0x000500b4, 0x00000196, 0x000004ae,
|
||||
0x000004a8, 0x00000049, 0x000300f7, 0x000004b1,
|
||||
0x00000000, 0x000400fa, 0x000004ae, 0x000004af,
|
||||
0x000004b0, 0x000200f8, 0x000004b0, 0x000200f9,
|
||||
0x000004b3, 0x000200f8, 0x000004af, 0x000200f9,
|
||||
0x000004b3, 0x000200f8, 0x000004b1, 0x000100ff,
|
||||
0x000200f8, 0x000004ab, 0x000200f9, 0x000004b3,
|
||||
0x000200f8, 0x000004b2, 0x000100ff, 0x000200f8,
|
||||
0x000004b3, 0x000900f5, 0x0000000c, 0x000004f1,
|
||||
0x0000019a, 0x000004ab, 0x000001a1, 0x000004af,
|
||||
0x000001a4, 0x000004b0, 0x00050085, 0x0000000c,
|
||||
0x0000020a, 0x00000202, 0x000004f1, 0x0005008e,
|
||||
0x0000000c, 0x0000020b, 0x0000020a, 0x00000116,
|
||||
0x0008000c, 0x0000000c, 0x0000020e, 0x00000001,
|
||||
0x0000002e, 0x00000202, 0x0000020b, 0x000004f9,
|
||||
0x0005008e, 0x0000000c, 0x00000211, 0x000001fb,
|
||||
0x00000210, 0x00050081, 0x0000000c, 0x00000213,
|
||||
0x0000020e, 0x00000211, 0x0005008e, 0x0000000c,
|
||||
0x00000215, 0x00000213, 0x00000166, 0x0007000c,
|
||||
0x0000000c, 0x00000218, 0x00000001, 0x0000001a,
|
||||
0x00000215, 0x00000217, 0x00050051, 0x00000006,
|
||||
0x0000021c, 0x00000218, 0x00000000, 0x00050051,
|
||||
0x00000006, 0x0000021d, 0x00000218, 0x00000001,
|
||||
0x00050051, 0x00000006, 0x0000021e, 0x00000218,
|
||||
0x00000002, 0x00070050, 0x00000037, 0x0000021f,
|
||||
0x0000021c, 0x0000021d, 0x0000021e, 0x00000049,
|
||||
0x0003003e, 0x0000021a, 0x0000021f, 0x000100fd,
|
||||
0x00010038};
|
||||
55
parallel-rdp/wsi_platform.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "wsi_platform.hpp"
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
VkSurfaceKHR SDL_WSIPlatform::create_surface(VkInstance instance,
|
||||
VkPhysicalDevice gpu) {
|
||||
VkSurfaceKHR surface = nullptr;
|
||||
bool result = SDL_Vulkan_CreateSurface(window, instance, NULL, &surface);
|
||||
if (result != true) {
|
||||
printf("Error creating surface\n");
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
void SDL_WSIPlatform::destroy_surface(VkInstance instance,
|
||||
VkSurfaceKHR surface) {
|
||||
SDL_Vulkan_DestroySurface(instance, surface, NULL);
|
||||
}
|
||||
|
||||
std::vector<const char *> SDL_WSIPlatform::get_instance_extensions() {
|
||||
|
||||
unsigned int extensionCount = 0;
|
||||
char const *const *extensions =
|
||||
SDL_Vulkan_GetInstanceExtensions(&extensionCount);
|
||||
if (extensions == NULL) {
|
||||
printf("Error getting instance extensions\n");
|
||||
}
|
||||
|
||||
std::vector<const char *> extensionNames;
|
||||
for (unsigned int i = 0; i < extensionCount; ++i) {
|
||||
extensionNames.push_back(extensions[i]);
|
||||
}
|
||||
return extensionNames;
|
||||
}
|
||||
|
||||
uint32_t SDL_WSIPlatform::get_surface_width() {
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
uint32_t SDL_WSIPlatform::get_surface_height() {
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
return h;
|
||||
}
|
||||
|
||||
bool SDL_WSIPlatform::alive(Vulkan::WSI &wsi) { return true; }
|
||||
|
||||
void SDL_WSIPlatform::poll_input() { SDL_PumpEvents(); }
|
||||
|
||||
void SDL_WSIPlatform::poll_input_async(Granite::InputTrackerHandler *handler) {}
|
||||
|
||||
void SDL_WSIPlatform::set_window(SDL_Window *_window) { window = _window; }
|
||||
|
||||
void SDL_WSIPlatform::do_resize() { resize = true; }
|
||||
22
parallel-rdp/wsi_platform.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "wsi.hpp"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
class SDL_WSIPlatform : public Vulkan::WSIPlatform {
|
||||
public:
|
||||
VkSurfaceKHR create_surface(VkInstance instance,
|
||||
VkPhysicalDevice gpu) override;
|
||||
void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override;
|
||||
std::vector<const char *> get_instance_extensions() override;
|
||||
uint32_t get_surface_width() override;
|
||||
uint32_t get_surface_height() override;
|
||||
bool alive(Vulkan::WSI &wsi) override;
|
||||
void poll_input() override;
|
||||
void poll_input_async(Granite::InputTrackerHandler *handler) override;
|
||||
void set_window(SDL_Window *_window);
|
||||
void do_resize();
|
||||
|
||||
private:
|
||||
SDL_Window *window;
|
||||
};
|
||||
1
retroachievements/rcheevos
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2ad0b8672f68a48148620164510b963039e49eb1
|
||||
421
retroachievements/retroachievements.c
Normal file
@ -0,0 +1,421 @@
|
||||
#include "retroachievements.h"
|
||||
#include "../parallel-rdp/interface.hpp"
|
||||
#include <rc_client.h>
|
||||
#include <rc_consoles.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void rust_server_call(const char *url, const char *post_data,
|
||||
const char *content_type,
|
||||
rc_client_server_callback_t callback,
|
||||
void *callback_data);
|
||||
void store_retroachievements_credentials(const char *username,
|
||||
const char *token, void *userdata);
|
||||
|
||||
void notify_load_game(void *userdata);
|
||||
|
||||
static rc_client_t *g_client = NULL;
|
||||
static const uint8_t *g_rdram = NULL;
|
||||
static size_t g_rdram_size = 0;
|
||||
static bool g_challenge = false;
|
||||
static bool g_leaderboard = false;
|
||||
static rc_client_leaderboard_list_t *g_leaderboard_list = NULL;
|
||||
|
||||
static char g_rich_presence_buffer[512];
|
||||
|
||||
static uint32_t read_memory(uint32_t address, uint8_t *buffer,
|
||||
uint32_t num_bytes, rc_client_t *client) {
|
||||
if (address + num_bytes > g_rdram_size)
|
||||
memset(buffer, 0, num_bytes);
|
||||
else
|
||||
memcpy(buffer, &g_rdram[address], num_bytes);
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
static void server_call(const rc_api_request_t *request,
|
||||
rc_client_server_callback_t callback,
|
||||
void *callback_data, rc_client_t *client) {
|
||||
rust_server_call(request->url, request->post_data, request->content_type,
|
||||
callback, callback_data);
|
||||
}
|
||||
|
||||
void ra_http_callback(const char *content, size_t content_size, int status_code,
|
||||
void *callback, void *callback_data) {
|
||||
// Prepare a data object to pass the HTTP response to the callback
|
||||
rc_api_server_response_t server_response;
|
||||
memset(&server_response, 0, sizeof(server_response));
|
||||
server_response.body = content;
|
||||
server_response.body_length = content_size;
|
||||
server_response.http_status_code = status_code;
|
||||
|
||||
// handle non-http errors (socket timeout, no internet available, etc)
|
||||
if (status_code == 0) {
|
||||
// Let rc_client know the error was not catastrophic and could be retried.
|
||||
// It may decide to retry or just immediately pass the error to the
|
||||
// callback. To prevent possible retries, use
|
||||
// RC_API_SERVER_RESPONSE_CLIENT_ERROR.
|
||||
server_response.http_status_code =
|
||||
RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR;
|
||||
}
|
||||
|
||||
((rc_client_server_callback_t)callback)(&server_response, callback_data);
|
||||
}
|
||||
|
||||
static void log_message(const char *message, const rc_client_t *client) {
|
||||
printf("RetroAchievements: %s\n", message);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (client && !rc_client_get_userdata(client))
|
||||
rc_client_set_userdata(client, strdup(error_message));
|
||||
store_retroachievements_credentials(NULL, NULL, userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
const char *ra_get_username() {
|
||||
const rc_client_user_t *user = rc_client_get_user_info(g_client);
|
||||
return user ? user->username : 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.
|
||||
rc_client_begin_login_with_password(g_client, username, password,
|
||||
login_callback, userdata);
|
||||
}
|
||||
|
||||
void ra_login_token_user(const char *username, const char *token,
|
||||
void *userdata) {
|
||||
// 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.
|
||||
rc_client_begin_login_with_token(g_client, username, token, login_callback,
|
||||
userdata);
|
||||
}
|
||||
|
||||
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);
|
||||
notify_load_game(userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rc_client_is_processing_required(client)) {
|
||||
rc_client_set_hardcore_enabled(client, false);
|
||||
}
|
||||
|
||||
g_leaderboard_list = rc_client_create_leaderboard_list(
|
||||
client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
|
||||
notify_load_game(userdata);
|
||||
}
|
||||
|
||||
void ra_welcome(const char **game_title, const char **game_image_url) {
|
||||
if (!g_client) {
|
||||
*game_title = NULL;
|
||||
*game_image_url = NULL;
|
||||
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, MESSAGE_LONG);
|
||||
free(error_message);
|
||||
rc_client_set_userdata(g_client, NULL);
|
||||
|
||||
*game_title = NULL;
|
||||
*game_image_url = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
const rc_client_game_t *game = rc_client_get_game_info(g_client);
|
||||
if (!game) {
|
||||
*game_title = NULL;
|
||||
*game_image_url = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(g_client, &summary);
|
||||
|
||||
int message_length = snprintf(
|
||||
buffer, sizeof(buffer), "%s\nMode: %s\n", game->title,
|
||||
rc_client_get_hardcore_enabled(g_client) ? "Hardcore" : "Casual");
|
||||
|
||||
if (summary.num_core_achievements != 0) {
|
||||
snprintf(buffer + message_length, sizeof(buffer) - message_length,
|
||||
"%u/%u achievements unlocked", summary.num_unlocked_achievements,
|
||||
summary.num_core_achievements);
|
||||
} else {
|
||||
snprintf(buffer + message_length, sizeof(buffer) - message_length,
|
||||
"Game has no achievements");
|
||||
}
|
||||
rdp_onscreen_message(buffer, MESSAGE_LONG);
|
||||
|
||||
*game_title = game->title;
|
||||
*game_image_url = game->badge_url;
|
||||
}
|
||||
|
||||
void ra_load_game(const uint8_t *rom, size_t rom_size, void *userdata) {
|
||||
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_rdram(const uint8_t *rdram, size_t rdram_size) {
|
||||
g_rdram = rdram;
|
||||
g_rdram_size = rdram_size;
|
||||
}
|
||||
|
||||
static void leaderboard_submitted(const rc_client_leaderboard_t *leaderboard) {
|
||||
char buffer[512];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Leaderboard submitted: %s - %s",
|
||||
leaderboard->title, leaderboard->tracker_value);
|
||||
rdp_onscreen_message(buffer, MESSAGE_SHORT);
|
||||
}
|
||||
|
||||
static void achievement_triggered(const rc_client_achievement_t *achievement) {
|
||||
char buffer[512];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Unlocked: %s", achievement->title);
|
||||
rdp_onscreen_message(buffer, MESSAGE_SHORT);
|
||||
}
|
||||
|
||||
static void game_completed(rc_client_t *client) {
|
||||
char buffer[512];
|
||||
const rc_client_game_t *game = rc_client_get_game_info(client);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%s: %s",
|
||||
rc_client_get_hardcore_enabled(client) ? "Mastered" : "Completed",
|
||||
game->title);
|
||||
rdp_onscreen_message(buffer, MESSAGE_SHORT);
|
||||
}
|
||||
|
||||
static void subset_completed(const rc_client_subset_t *subset,
|
||||
rc_client_t *client) {
|
||||
char buffer[512];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Subset %s: %s",
|
||||
rc_client_get_hardcore_enabled(client) ? "mastered" : "completed",
|
||||
subset->title);
|
||||
rdp_onscreen_message(buffer, MESSAGE_SHORT);
|
||||
}
|
||||
|
||||
static void server_error(const rc_client_server_error_t *server_error) {
|
||||
char buffer[512];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "RA server error: %s",
|
||||
server_error->error_message);
|
||||
rdp_onscreen_message(buffer, MESSAGE_SHORT);
|
||||
}
|
||||
|
||||
static const char *get_leaderboard_title(const char *display) {
|
||||
if (g_leaderboard_list == NULL)
|
||||
return NULL;
|
||||
|
||||
for (uint32_t i = 0; i < g_leaderboard_list->num_buckets; i++) {
|
||||
for (uint32_t j = 0; j < g_leaderboard_list->buckets[i].num_leaderboards;
|
||||
j++) {
|
||||
// this looks like a mistake, but it is intentional.
|
||||
// Leaderboard trackers don't contain the title,
|
||||
// but we take advantage of the fact that tracker_value and display point
|
||||
// to the same char* to find a matching leaderboard.
|
||||
if (g_leaderboard_list->buckets[i].leaderboards[j]->tracker_value ==
|
||||
display) {
|
||||
return g_leaderboard_list->buckets[i].leaderboards[j]->title;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void event_handler(const rc_client_event_t *event, rc_client_t *client) {
|
||||
switch (event->type) {
|
||||
case RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED:
|
||||
achievement_triggered(event->achievement);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_STARTED:
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_FAILED:
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED:
|
||||
if (g_leaderboard)
|
||||
leaderboard_submitted(event->leaderboard);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW:
|
||||
if (g_challenge)
|
||||
achievement_challenge_indicator_add(event->achievement->title);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE:
|
||||
if (g_challenge)
|
||||
achievement_challenge_indicator_remove(event->achievement->title);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW:
|
||||
achievement_progress_add(event->achievement->title,
|
||||
event->achievement->measured_progress);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE:
|
||||
achievement_progress_remove();
|
||||
break;
|
||||
case RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE:
|
||||
achievement_progress_add(event->achievement->title,
|
||||
event->achievement->measured_progress);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW:
|
||||
if (g_leaderboard)
|
||||
leaderboard_tracker_add(
|
||||
event->leaderboard_tracker->id,
|
||||
get_leaderboard_title(event->leaderboard_tracker->display),
|
||||
event->leaderboard_tracker->display);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE:
|
||||
if (g_leaderboard)
|
||||
leaderboard_tracker_remove(event->leaderboard_tracker->id);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE:
|
||||
if (g_leaderboard)
|
||||
leaderboard_tracker_add(
|
||||
event->leaderboard_tracker->id,
|
||||
get_leaderboard_title(event->leaderboard_tracker->display),
|
||||
event->leaderboard_tracker->display);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD:
|
||||
break;
|
||||
case RC_CLIENT_EVENT_GAME_COMPLETED:
|
||||
game_completed(client);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_SUBSET_COMPLETED:
|
||||
subset_completed(event->subset, client);
|
||||
break;
|
||||
case RC_CLIENT_EVENT_SERVER_ERROR:
|
||||
server_error(event->server_error);
|
||||
break;
|
||||
default:
|
||||
printf("RetroAchievements: Unhandled event %d\n", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ra_init_client(bool hardcore, bool challenge, bool leaderboard) {
|
||||
// Create the client instance (using a global variable simplifies this
|
||||
// example)
|
||||
if (!g_client)
|
||||
g_client = rc_client_create(read_memory, server_call);
|
||||
g_rdram = NULL;
|
||||
g_rdram_size = 0;
|
||||
|
||||
// Provide a logging function to simplify debugging
|
||||
rc_client_enable_logging(g_client, RC_CLIENT_LOG_LEVEL_WARN, log_message);
|
||||
|
||||
rc_client_set_event_handler(g_client, event_handler);
|
||||
|
||||
rc_client_set_hardcore_enabled(g_client, hardcore);
|
||||
|
||||
g_challenge = challenge;
|
||||
g_leaderboard = leaderboard;
|
||||
}
|
||||
|
||||
bool ra_get_hardcore() { return rc_client_get_hardcore_enabled(g_client); }
|
||||
|
||||
void ra_shutdown_client() {
|
||||
if (g_client) {
|
||||
// Release resources associated to the client instance
|
||||
rc_client_destroy(g_client);
|
||||
g_client = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ra_do_frame() { rc_client_do_frame(g_client); }
|
||||
|
||||
void ra_do_idle() { rc_client_idle(g_client); }
|
||||
|
||||
const char *ra_get_rich_presence() {
|
||||
size_t size = rc_client_get_rich_presence_message(
|
||||
g_client, g_rich_presence_buffer, sizeof(g_rich_presence_buffer));
|
||||
if (size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return g_rich_presence_buffer;
|
||||
}
|
||||
|
||||
size_t ra_state_size() { return rc_client_progress_size(g_client); }
|
||||
|
||||
void ra_save_state(uint8_t *state, size_t state_size) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void ra_load_state(const uint8_t *state, size_t state_size) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void ra_display_inprogress_achievements(void *userdata) {
|
||||
if (!rc_client_is_game_loaded(g_client))
|
||||
return;
|
||||
|
||||
rc_client_achievement_list_t *list = rc_client_create_achievement_list(
|
||||
g_client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_LOCK_STATE);
|
||||
|
||||
char buffer[1024] = {0};
|
||||
size_t buffer_length = 0;
|
||||
|
||||
for (uint32_t i = 0; i < list->num_buckets; i++) {
|
||||
if (list->buckets[i].bucket_type == RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED) {
|
||||
for (uint32_t j = 0; j < list->buckets[i].num_achievements; j++) {
|
||||
const rc_client_achievement_t *achievement =
|
||||
list->buckets[i].achievements[j];
|
||||
if (achievement->measured_percent > 0.0f) {
|
||||
if (buffer_length < sizeof(buffer) - 1) {
|
||||
buffer_length += snprintf(
|
||||
buffer + buffer_length, sizeof(buffer) - buffer_length,
|
||||
"%s: %s\n", achievement->title, achievement->measured_progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffer_length > 0) {
|
||||
rdp_onscreen_message(buffer, MESSAGE_SHORT);
|
||||
}
|
||||
rc_client_destroy_achievement_list(list);
|
||||
}
|
||||
33
retroachievements/retroachievements.h
Normal file
@ -0,0 +1,33 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void ra_init_client(bool hardcore, bool challenge, bool leaderboard);
|
||||
void ra_shutdown_client();
|
||||
void ra_welcome(const char **game_title, const char **game_image_url);
|
||||
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_rdram(const uint8_t *rdram, size_t rdram_size);
|
||||
void ra_do_frame();
|
||||
void ra_do_idle();
|
||||
void ra_http_callback(const char *content, size_t content_size, int status_code,
|
||||
void *callback, void *callback_data);
|
||||
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);
|
||||
const char *ra_get_username();
|
||||
size_t ra_state_size();
|
||||
void ra_save_state(uint8_t *state, size_t state_size);
|
||||
void ra_load_state(const uint8_t *state, size_t state_size);
|
||||
|
||||
const char *ra_get_rich_presence();
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void ra_display_inprogress_achievements(void *userdata);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
2
rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.98.0"
|
||||
204
src/cheats.rs
Normal file
@ -0,0 +1,204 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
|
||||
pub struct DecodedCheat {
|
||||
code_type: u8,
|
||||
address: u32,
|
||||
data: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Cheats {
|
||||
pub cheats: Vec<Vec<DecodedCheat>>,
|
||||
pub boot: bool,
|
||||
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: rustc_hash::FxHashMap<String, Option<String>>,
|
||||
) {
|
||||
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() {
|
||||
if let Some(cheat_data) = cheats.get(cheat_setting.0) {
|
||||
let mut option_value = None;
|
||||
if let Some(option) = cheat_setting.1 {
|
||||
if let Some(found_option_value) = cheat_data
|
||||
.options
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.get(option)
|
||||
{
|
||||
option_value = Some(found_option_value.to_string());
|
||||
} else {
|
||||
eprintln!("Cheat option: {option} not found");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"Cheat: {} Option: {}",
|
||||
cheat_setting.0,
|
||||
cheat_setting.1.clone().unwrap_or("none".to_string())
|
||||
);
|
||||
let mut decoded_cheat: Vec<DecodedCheat> = vec![];
|
||||
for code in cheat_data.data.iter() {
|
||||
let mut result = code.clone();
|
||||
if let Some(option_value) = option_value.as_ref() {
|
||||
result = re.replace_all(code, option_value).into_owned();
|
||||
}
|
||||
let mut split = result.split_whitespace();
|
||||
let first_part = u32::from_str_radix(split.next().unwrap(), 16).unwrap();
|
||||
if let Ok(data) = u16::from_str_radix(split.next().unwrap(), 16) {
|
||||
decoded_cheat.push(DecodedCheat {
|
||||
code_type: (first_part >> 24) as u8,
|
||||
address: first_part & 0x00FFFFFF,
|
||||
data,
|
||||
})
|
||||
} else {
|
||||
eprintln!("Could not parse data for: {}", cheat_setting.0);
|
||||
};
|
||||
}
|
||||
if !decoded_cheat.is_empty() {
|
||||
device.cheats.cheats.push(decoded_cheat);
|
||||
}
|
||||
} else {
|
||||
eprintln!("Could not find cheat: {}", cheat_setting.0);
|
||||
}
|
||||
}
|
||||
|
||||
if !device.cheats.cheats.is_empty() {
|
||||
device.cheats.enabled = true;
|
||||
ui::video::onscreen_message("Cheats enabled", ui::video::MESSAGE_LENGTH_MESSAGE_SHORT);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_byte(device: &mut device::Device, cheat_line: &DecodedCheat) {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(cheat_line.address as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = cheat_line.data as u8;
|
||||
}
|
||||
|
||||
fn write_half(device: &mut device::Device, cheat_line: &DecodedCheat) {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(cheat_line.address as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = (cheat_line.data >> 8) as u8;
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut((cheat_line.address + 1) as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = cheat_line.data as u8;
|
||||
}
|
||||
|
||||
fn equal_byte(device: &mut device::Device, cheat_line: &DecodedCheat) -> bool {
|
||||
let current_value = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get(cheat_line.address as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
current_value == (cheat_line.data as u8)
|
||||
}
|
||||
|
||||
fn equal_half(device: &mut device::Device, cheat_line: &DecodedCheat) -> bool {
|
||||
let current_value1 = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get(cheat_line.address as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
let current_value2 = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get((cheat_line.address + 1) as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
current_value1 == (cheat_line.data >> 8) as u8 && current_value2 == cheat_line.data as u8
|
||||
}
|
||||
|
||||
pub fn execute_cheats(device: &mut device::Device, cheats: Vec<Vec<DecodedCheat>>) {
|
||||
for cheat in cheats.iter() {
|
||||
let mut valid = true;
|
||||
let mut cheat_iter = cheat.iter().peekable();
|
||||
while let Some(cheat_line) = cheat_iter.next() {
|
||||
match cheat_line.code_type {
|
||||
0x50 => {
|
||||
if valid {
|
||||
let mut expanded_cheat: Vec<DecodedCheat> = vec![];
|
||||
let compressed_cheat = *cheat_iter.peek().unwrap();
|
||||
let count = (cheat_line.address & 0xFF00) >> 8;
|
||||
let offset = cheat_line.address & 0xFF;
|
||||
for i in 0..count {
|
||||
let line = DecodedCheat {
|
||||
code_type: compressed_cheat.code_type,
|
||||
address: compressed_cheat.address + (i * offset),
|
||||
data: compressed_cheat.data
|
||||
+ (i as i16 * cheat_line.data as i16) as u16,
|
||||
};
|
||||
expanded_cheat.push(line);
|
||||
}
|
||||
execute_cheats(device, [expanded_cheat].to_vec());
|
||||
}
|
||||
valid = false; // skip the next line
|
||||
}
|
||||
0x80 | 0xA0 => {
|
||||
if valid {
|
||||
write_byte(device, cheat_line);
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
0x81 | 0xA1 => {
|
||||
if valid {
|
||||
write_half(device, cheat_line);
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
0xF0 => {
|
||||
if device.cheats.boot && valid {
|
||||
write_byte(device, cheat_line);
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
0xF1 => {
|
||||
if device.cheats.boot && valid {
|
||||
write_half(device, cheat_line);
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
0xD0 => {
|
||||
valid = equal_byte(device, cheat_line);
|
||||
}
|
||||
0xD1 => {
|
||||
valid = equal_half(device, cheat_line);
|
||||
}
|
||||
0xD2 => {
|
||||
valid = !equal_byte(device, cheat_line);
|
||||
}
|
||||
0xD3 => {
|
||||
valid = !equal_half(device, cheat_line);
|
||||
}
|
||||
0x88 | 0x89 | 0xD8 | 0xD9 | 0xDA | 0xDB => { /* GS button not emulated */ }
|
||||
_ => panic!("Unknown cheat code type: {:X}", cheat_line.code_type),
|
||||
}
|
||||
}
|
||||
}
|
||||
device.cheats.boot = false;
|
||||
}
|
||||
197
src/compat/aarch64.c
Normal file
@ -0,0 +1,197 @@
|
||||
#include "src/compat/sse2neon/sse2neon.h"
|
||||
|
||||
// Static wrappers
|
||||
|
||||
__m128i _mm_srli_epi16__extern(__m128i __a, int __imm8) { return _mm_srli_epi16(__a, __imm8); }
|
||||
|
||||
__m128i _mm_insert_epi8__extern(__m128i __a, int __i, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_insert_epi8(__a, __i, 0);
|
||||
case 1:
|
||||
return _mm_insert_epi8(__a, __i, 1);
|
||||
case 2:
|
||||
return _mm_insert_epi8(__a, __i, 2);
|
||||
case 3:
|
||||
return _mm_insert_epi8(__a, __i, 3);
|
||||
case 4:
|
||||
return _mm_insert_epi8(__a, __i, 4);
|
||||
case 5:
|
||||
return _mm_insert_epi8(__a, __i, 5);
|
||||
case 6:
|
||||
return _mm_insert_epi8(__a, __i, 6);
|
||||
case 7:
|
||||
return _mm_insert_epi8(__a, __i, 7);
|
||||
case 8:
|
||||
return _mm_insert_epi8(__a, __i, 8);
|
||||
case 9:
|
||||
return _mm_insert_epi8(__a, __i, 9);
|
||||
case 10:
|
||||
return _mm_insert_epi8(__a, __i, 10);
|
||||
case 11:
|
||||
return _mm_insert_epi8(__a, __i, 11);
|
||||
case 12:
|
||||
return _mm_insert_epi8(__a, __i, 12);
|
||||
case 13:
|
||||
return _mm_insert_epi8(__a, __i, 13);
|
||||
case 14:
|
||||
return _mm_insert_epi8(__a, __i, 14);
|
||||
case 15:
|
||||
return _mm_insert_epi8(__a, __i, 15);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int _mm_extract_epi8__extern(__m128i __a, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_extract_epi8(__a, 0);
|
||||
case 1:
|
||||
return _mm_extract_epi8(__a, 1);
|
||||
case 2:
|
||||
return _mm_extract_epi8(__a, 2);
|
||||
case 3:
|
||||
return _mm_extract_epi8(__a, 3);
|
||||
case 4:
|
||||
return _mm_extract_epi8(__a, 4);
|
||||
case 5:
|
||||
return _mm_extract_epi8(__a, 5);
|
||||
case 6:
|
||||
return _mm_extract_epi8(__a, 6);
|
||||
case 7:
|
||||
return _mm_extract_epi8(__a, 7);
|
||||
case 8:
|
||||
return _mm_extract_epi8(__a, 8);
|
||||
case 9:
|
||||
return _mm_extract_epi8(__a, 9);
|
||||
case 10:
|
||||
return _mm_extract_epi8(__a, 10);
|
||||
case 11:
|
||||
return _mm_extract_epi8(__a, 11);
|
||||
case 12:
|
||||
return _mm_extract_epi8(__a, 12);
|
||||
case 13:
|
||||
return _mm_extract_epi8(__a, 13);
|
||||
case 14:
|
||||
return _mm_extract_epi8(__a, 14);
|
||||
case 15:
|
||||
return _mm_extract_epi8(__a, 15);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
__m128i _mm_insert_epi16__extern(__m128i __a, int __i, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_insert_epi16(__a, __i, 0);
|
||||
case 1:
|
||||
return _mm_insert_epi16(__a, __i, 1);
|
||||
case 2:
|
||||
return _mm_insert_epi16(__a, __i, 2);
|
||||
case 3:
|
||||
return _mm_insert_epi16(__a, __i, 3);
|
||||
case 4:
|
||||
return _mm_insert_epi16(__a, __i, 4);
|
||||
case 5:
|
||||
return _mm_insert_epi16(__a, __i, 5);
|
||||
case 6:
|
||||
return _mm_insert_epi16(__a, __i, 6);
|
||||
case 7:
|
||||
return _mm_insert_epi16(__a, __i, 7);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int _mm_extract_epi16__extern(__m128i __a, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_extract_epi16(__a, 0);
|
||||
case 1:
|
||||
return _mm_extract_epi16(__a, 1);
|
||||
case 2:
|
||||
return _mm_extract_epi16(__a, 2);
|
||||
case 3:
|
||||
return _mm_extract_epi16(__a, 3);
|
||||
case 4:
|
||||
return _mm_extract_epi16(__a, 4);
|
||||
case 5:
|
||||
return _mm_extract_epi16(__a, 5);
|
||||
case 6:
|
||||
return _mm_extract_epi16(__a, 6);
|
||||
case 7:
|
||||
return _mm_extract_epi16(__a, 7);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
__m128i _mm_insert_epi32__extern(__m128i __a, int __i, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_insert_epi32(__a, __i, 0);
|
||||
case 1:
|
||||
return _mm_insert_epi32(__a, __i, 1);
|
||||
case 2:
|
||||
return _mm_insert_epi32(__a, __i, 2);
|
||||
case 3:
|
||||
return _mm_insert_epi32(__a, __i, 3);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int _mm_extract_epi32__extern(__m128i __a, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_extract_epi32(__a, 0);
|
||||
case 1:
|
||||
return _mm_extract_epi32(__a, 1);
|
||||
case 2:
|
||||
return _mm_extract_epi32(__a, 2);
|
||||
case 3:
|
||||
return _mm_extract_epi32(__a, 3);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
__m128i _mm_insert_epi64__extern(__m128i __a, int64_t __i, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_insert_epi64(__a, __i, 0);
|
||||
case 1:
|
||||
return _mm_insert_epi64(__a, __i, 1);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t _mm_extract_epi64__extern(__m128i __a, const int __imm8)
|
||||
{
|
||||
switch (__imm8)
|
||||
{
|
||||
case 0:
|
||||
return _mm_extract_epi64(__a, 0);
|
||||
case 1:
|
||||
return _mm_extract_epi64(__a, 1);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
47
src/compat/aarch64.rs
Normal file
@ -0,0 +1,47 @@
|
||||
unsafe extern "C" {
|
||||
#[link_name = "_mm_srli_epi16__extern"]
|
||||
fn _mm_srli_epi16(__a: __m128i, __imm8: ::std::os::raw::c_int) -> __m128i;
|
||||
|
||||
#[link_name = "_mm_insert_epi8__extern"]
|
||||
fn _mm_insert_epi8(
|
||||
__a: __m128i,
|
||||
__i: ::std::os::raw::c_int,
|
||||
__imm8: ::std::os::raw::c_int,
|
||||
) -> __m128i;
|
||||
|
||||
#[link_name = "_mm_extract_epi8__extern"]
|
||||
fn _mm_extract_epi8(__a: __m128i, __imm8: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
||||
|
||||
#[link_name = "_mm_insert_epi16__extern"]
|
||||
fn _mm_insert_epi16(
|
||||
__a: __m128i,
|
||||
__i: ::std::os::raw::c_int,
|
||||
__imm8: ::std::os::raw::c_int,
|
||||
) -> __m128i;
|
||||
|
||||
#[link_name = "_mm_extract_epi16__extern"]
|
||||
fn _mm_extract_epi16(__a: __m128i, __imm8: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
||||
|
||||
#[link_name = "_mm_insert_epi32__extern"]
|
||||
fn _mm_insert_epi32(
|
||||
__a: __m128i,
|
||||
__i: ::std::os::raw::c_int,
|
||||
__imm8: ::std::os::raw::c_int,
|
||||
) -> __m128i;
|
||||
|
||||
#[link_name = "_mm_extract_epi32__extern"]
|
||||
fn _mm_extract_epi32(__a: __m128i, __imm8: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
|
||||
|
||||
#[link_name = "_mm_insert_epi64__extern"]
|
||||
fn _mm_insert_epi64(
|
||||
__a: __m128i,
|
||||
__i: ::std::os::raw::c_longlong,
|
||||
__imm8: ::std::os::raw::c_int,
|
||||
) -> __m128i;
|
||||
|
||||
#[link_name = "_mm_extract_epi64__extern"]
|
||||
fn _mm_extract_epi64(
|
||||
__a: __m128i,
|
||||
__imm8: ::std::os::raw::c_int,
|
||||
) -> ::std::os::raw::c_longlong;
|
||||
}
|
||||
1
src/compat/sse2neon
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b26afbb44df55ac05320f7003a8a0eef30e465c5
|
||||
610
src/device.rs
Normal file
@ -0,0 +1,610 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct __m128i(std::arch::aarch64::int64x2_t);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
include!(concat!(env!("OUT_DIR"), "/simd_bindings.rs"));
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
const N64_EXTENSIONS_UNCOMPRESSED: [&str; 8] =
|
||||
["n64", "v64", "z64", "bin", "N64", "V64", "Z64", "BIN"];
|
||||
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
use crate::{cheats, netplay, retroachievements, savestates, ui};
|
||||
use std::io::Read;
|
||||
|
||||
pub mod ai;
|
||||
pub mod cache;
|
||||
pub mod cart;
|
||||
pub mod controller;
|
||||
pub mod cop0;
|
||||
pub mod cop1;
|
||||
pub mod cop2;
|
||||
pub mod cpu;
|
||||
pub mod cpu_instructions;
|
||||
pub mod events;
|
||||
pub mod exceptions;
|
||||
pub mod fpu_instructions;
|
||||
pub mod is_viewer;
|
||||
pub mod memory;
|
||||
pub mod mi;
|
||||
pub mod pi;
|
||||
pub mod pif;
|
||||
pub mod rdp;
|
||||
pub mod rdram;
|
||||
pub mod ri;
|
||||
pub mod rsp_cpu;
|
||||
pub mod rsp_interface;
|
||||
pub mod rsp_su_instructions;
|
||||
pub mod rsp_vu_instructions;
|
||||
pub mod si;
|
||||
pub mod tlb;
|
||||
pub mod unmapped;
|
||||
pub mod vi;
|
||||
|
||||
pub fn run_game(
|
||||
device: &mut Device,
|
||||
rom_contents: &[u8],
|
||||
game_settings: ui::GameSettings,
|
||||
ra_config: retroachievements::RAConfig,
|
||||
netplay_config: Option<netplay::NetplayConfig>,
|
||||
) {
|
||||
device.cpu.overclock = game_settings.overclock;
|
||||
if game_settings.disable_expansion_pak {
|
||||
device.rdram.size = 0x400000;
|
||||
}
|
||||
if let Some(slot) = game_settings.load_savestate_slot {
|
||||
device.ui.storage.save_state_slot = slot;
|
||||
device.savestate.load_state = true;
|
||||
}
|
||||
|
||||
cart::rom::init(device, rom_contents); // cart needs to come before rdram
|
||||
|
||||
// rdram pointer is shared with parallel-rdp and retroachievements
|
||||
rdram::init(device);
|
||||
|
||||
ui::video::init(device, netplay_config.is_some());
|
||||
ui::audio::init(device);
|
||||
ui::input::init(&mut device.ui);
|
||||
|
||||
// must be after video init
|
||||
if let Some(netplay_config) = &netplay_config {
|
||||
device.netplay = netplay::init(
|
||||
device,
|
||||
netplay_config,
|
||||
cart::rom::is_system_pal(rom_contents),
|
||||
);
|
||||
if device.netplay.is_none() {
|
||||
ui::input::close(&mut device.ui);
|
||||
ui::audio::close(&mut device.ui);
|
||||
ui::video::close(&device.ui);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
init_rng_rtc(device);
|
||||
|
||||
// must be after video init
|
||||
let (discord_watch_tx, discord_handle) = if ra_config.enabled && device.netplay.is_none() {
|
||||
retroachievements::load_game(rom_contents, rom_contents.len(), ra_config.rich_presence)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
mi::init(device);
|
||||
pif::init(device);
|
||||
if device.ui.config.input.emulate_vru && device.netplay.is_none() {
|
||||
controller::vru::init(device);
|
||||
}
|
||||
memory::init(device);
|
||||
cache::init(device);
|
||||
rsp_interface::init(device);
|
||||
rdp::init(device);
|
||||
vi::init(device);
|
||||
cpu::init(device);
|
||||
|
||||
ui::storage::init(&mut device.ui, &device.cart.rom);
|
||||
ui::storage::load_saves(&mut device.ui, &mut device.netplay);
|
||||
ui::storage::format_saves(device);
|
||||
|
||||
if !retroachievements::get_hardcore() {
|
||||
cheats::init(device, game_settings.cheats);
|
||||
}
|
||||
|
||||
cpu::run(device);
|
||||
|
||||
retroachievements::unload_game(discord_watch_tx, discord_handle);
|
||||
|
||||
ui::storage::write_saves(device);
|
||||
|
||||
if let Some(netplay) = &mut device.netplay {
|
||||
netplay::close(netplay);
|
||||
}
|
||||
|
||||
ui::input::close(&mut device.ui);
|
||||
ui::audio::close(&mut device.ui);
|
||||
ui::video::close(&device.ui);
|
||||
}
|
||||
|
||||
fn set_rng() -> rand::rngs::Xoshiro256PlusPlus {
|
||||
rand::rngs::Xoshiro256PlusPlus::try_from_rng(&mut rand::rngs::SysRng).unwrap()
|
||||
}
|
||||
|
||||
fn init_rng_rtc(device: &mut Device) {
|
||||
let mut rng_seed = set_rng().next_u64();
|
||||
if let Some(netplay) = &mut device.netplay {
|
||||
if netplay.player_number == 0 {
|
||||
netplay::send_rng(netplay, rng_seed);
|
||||
} else {
|
||||
rng_seed = netplay::receive_rng(netplay);
|
||||
}
|
||||
}
|
||||
device.rng = rand::rngs::Xoshiro256PlusPlus::seed_from_u64(rng_seed);
|
||||
|
||||
device.cart.rtc_timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
if let Some(netplay) = &mut device.netplay {
|
||||
if netplay.player_number == 0 {
|
||||
netplay::send_rtc(netplay, device.cart.rtc_timestamp);
|
||||
} else {
|
||||
device.cart.rtc_timestamp = netplay::receive_rtc(netplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_rom(contents: Vec<u8>) -> Option<Vec<u8>> {
|
||||
let test = u32::from_be_bytes(contents[0..4].try_into().unwrap());
|
||||
if test == 0x80371240 {
|
||||
// z64
|
||||
Some(contents)
|
||||
} else if test == 0x37804012 {
|
||||
// v64
|
||||
let mut data: Vec<u8> = vec![0; contents.len()];
|
||||
for i in (0..contents.len()).step_by(2) {
|
||||
let temp = u16::from_ne_bytes(contents[i..i + 2].try_into().unwrap());
|
||||
data[i..i + 2].copy_from_slice(&temp.to_be_bytes());
|
||||
}
|
||||
Some(data)
|
||||
} else if test == 0x40123780 {
|
||||
// n64
|
||||
let mut data: Vec<u8> = vec![0; contents.len()];
|
||||
for i in (0..contents.len()).step_by(4) {
|
||||
let temp = u32::from_ne_bytes(contents[i..i + 4].try_into().unwrap());
|
||||
data[i..i + 4].copy_from_slice(&temp.to_be_bytes());
|
||||
}
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rom_contents(file_path: &std::path::PathBuf) -> Option<Vec<u8>> {
|
||||
let mut contents = vec![];
|
||||
if file_path
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.eq_ignore_ascii_case("zip")
|
||||
{
|
||||
#[cfg(target_os = "android")]
|
||||
let zip_file = ui::android::get_file_from_uri(file_path).unwrap();
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let zip_file = std::fs::File::open(file_path).unwrap();
|
||||
let mut archive = zip::ZipArchive::new(zip_file).unwrap();
|
||||
for i in 0..archive.len() {
|
||||
let mut file = archive.by_index(i).unwrap();
|
||||
if let Some(extension) = file.enclosed_name().unwrap().extension()
|
||||
&& N64_EXTENSIONS_UNCOMPRESSED.contains(&extension.to_str().unwrap())
|
||||
{
|
||||
file.read_to_end(&mut contents)
|
||||
.expect("could not read zip file");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if file_path
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.eq_ignore_ascii_case("7z")
|
||||
{
|
||||
#[cfg(target_os = "android")]
|
||||
let sevenz_file = ui::android::get_file_from_uri(file_path).unwrap();
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let sevenz_file = std::fs::File::open(file_path).unwrap();
|
||||
let mut archive =
|
||||
sevenz_rust2::ArchiveReader::new(sevenz_file, sevenz_rust2::Password::empty()).unwrap();
|
||||
|
||||
let mut found = false;
|
||||
archive
|
||||
.for_each_entries(
|
||||
&mut |entry: &sevenz_rust2::ArchiveEntry, reader: &mut dyn std::io::Read| {
|
||||
if !found
|
||||
&& let Some(extension) = std::path::PathBuf::from(entry.name()).extension()
|
||||
&& N64_EXTENSIONS_UNCOMPRESSED.contains(&extension.to_str().unwrap())
|
||||
{
|
||||
reader
|
||||
.read_to_end(&mut contents)
|
||||
.expect("could not read zip file");
|
||||
found = true;
|
||||
} else {
|
||||
//skip other files
|
||||
std::io::copy(reader, &mut std::io::sink())?;
|
||||
}
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.expect("ok");
|
||||
} else {
|
||||
#[cfg(target_os = "android")]
|
||||
let mut file = ui::android::get_file_from_uri(file_path).unwrap();
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let mut file = std::fs::File::open(file_path).unwrap();
|
||||
file.read_to_end(&mut contents).unwrap();
|
||||
}
|
||||
|
||||
if contents.is_empty() {
|
||||
None
|
||||
} else {
|
||||
swap_rom(contents)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SpeedLimiter {
|
||||
pub limit_freq: u64,
|
||||
#[serde(skip)]
|
||||
#[serde(default = "std::time::Instant::now")]
|
||||
pub limit_freq_check: std::time::Instant,
|
||||
pub frame_counter: u64,
|
||||
pub enabled: bool,
|
||||
pub min_wait_time: std::time::Duration,
|
||||
#[serde(skip)]
|
||||
pub next_pace_deadline: Option<std::time::Instant>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Device {
|
||||
#[serde(skip)]
|
||||
pub netplay: Option<netplay::Netplay>,
|
||||
#[serde(skip)]
|
||||
pub ui: ui::Ui,
|
||||
pub speed_limiter: SpeedLimiter,
|
||||
pub byte_swap: usize,
|
||||
pub savestate: savestates::Savestate,
|
||||
pub cpu: cpu::Cpu,
|
||||
pub pif: pif::Pif,
|
||||
pub cart: cart::Cart,
|
||||
pub memory: memory::Memory,
|
||||
pub rsp: rsp_interface::Rsp,
|
||||
pub rdp: rdp::Rdp,
|
||||
pub rdram: rdram::Rdram,
|
||||
pub mi: mi::Mi,
|
||||
pub pi: pi::Pi,
|
||||
pub vi: vi::Vi,
|
||||
pub ai: ai::Ai,
|
||||
pub si: si::Si,
|
||||
pub ri: ri::Ri,
|
||||
pub rng: rand::rngs::Xoshiro256PlusPlus,
|
||||
pub vru: controller::vru::Vru,
|
||||
pub transferpaks: [controller::transferpak::TransferPak; 4],
|
||||
pub cheats: cheats::Cheats,
|
||||
}
|
||||
|
||||
pub fn zero_m128i() -> __m128i {
|
||||
unsafe { _mm_setzero_si128() }
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn clone_state(&mut self, device: &Device) {
|
||||
self.rng.clone_from(&device.rng);
|
||||
self.cpu.clone_from(&device.cpu);
|
||||
self.pif.clone_from(&device.pif);
|
||||
self.cart.clone_from(&device.cart);
|
||||
self.memory.clone_from(&device.memory);
|
||||
self.rsp.clone_from(&device.rsp);
|
||||
self.rdp.clone_from(&device.rdp);
|
||||
self.rdram.clone_from(&device.rdram);
|
||||
self.mi.clone_from(&device.mi);
|
||||
self.pi.clone_from(&device.pi);
|
||||
self.vi.clone_from(&device.vi);
|
||||
self.ai.clone_from(&device.ai);
|
||||
self.si.clone_from(&device.si);
|
||||
self.ri.clone_from(&device.ri);
|
||||
self.vru.clone_from(&device.vru);
|
||||
self.transferpaks.clone_from(&device.transferpaks);
|
||||
self.cheats.clone_from(&device.cheats);
|
||||
}
|
||||
|
||||
pub fn new(with_ui: bool) -> Box<Device> {
|
||||
let mut byte_swap: usize = 0;
|
||||
let test: [u8; 4] = [1, 2, 3, 4];
|
||||
// if the host computer is little endian, that means the RDRAM will be stored as little endian
|
||||
// when accessing bytes in RDRAM, we need to swap them by XORing with the byte_swap value (3)
|
||||
if u32::from_le_bytes(test) == u32::from_ne_bytes(test) {
|
||||
byte_swap = 3;
|
||||
}
|
||||
Box::new(Device {
|
||||
netplay: None,
|
||||
ui: if with_ui {
|
||||
ui::Ui::new()
|
||||
} else {
|
||||
ui::Ui::default()
|
||||
},
|
||||
speed_limiter: SpeedLimiter {
|
||||
limit_freq: 2,
|
||||
limit_freq_check: std::time::Instant::now(),
|
||||
frame_counter: 0,
|
||||
enabled: true,
|
||||
min_wait_time: std::time::Duration::from_secs(1),
|
||||
next_pace_deadline: None,
|
||||
},
|
||||
byte_swap,
|
||||
savestate: savestates::Savestate {
|
||||
save_state: false,
|
||||
load_state: false,
|
||||
load_rewind: false,
|
||||
last_rewind_saved: 0.0,
|
||||
rewind_pool: std::sync::Arc::new(tokio::sync::Mutex::new(
|
||||
std::collections::BTreeMap::new(),
|
||||
)),
|
||||
},
|
||||
cpu: cpu::Cpu {
|
||||
cop0: cop0::Cop0 {
|
||||
regs: [0; cop0::COP0_REGS_COUNT],
|
||||
reg_write_masks: [0; cop0::COP0_REGS_COUNT],
|
||||
reg_latch: 0,
|
||||
is_event: false,
|
||||
instrs: [cop0::reserved; 32],
|
||||
instrs2: [cop0::reserved; 32],
|
||||
tlb_lut_w: rustc_hash::FxHashMap::default(),
|
||||
tlb_lut_r: rustc_hash::FxHashMap::default(),
|
||||
tlb_entries: [tlb::TlbEntry {
|
||||
mask: 0,
|
||||
vpn2: 0,
|
||||
region: 0,
|
||||
g: 0,
|
||||
asid: 0,
|
||||
pfn_even: 0,
|
||||
c_even: 0,
|
||||
d_even: 0,
|
||||
v_even: 0,
|
||||
pfn_odd: 0,
|
||||
c_odd: 0,
|
||||
d_odd: 0,
|
||||
v_odd: 0,
|
||||
start_even: 0,
|
||||
end_even: 0,
|
||||
phys_even: 0,
|
||||
start_odd: 0,
|
||||
end_odd: 0,
|
||||
phys_odd: 0,
|
||||
}; 32],
|
||||
},
|
||||
cop1: cop1::Cop1 {
|
||||
fcr0: 0,
|
||||
fcr31: 0,
|
||||
//#[cfg(target_arch = "x86_64")]
|
||||
//flush_mode: 0,
|
||||
fgr32: [[0; 4]; 32],
|
||||
fgr64: [[0; 8]; 32],
|
||||
instrs: [cop1::reserved; 32],
|
||||
b_instrs: [cop1::reserved; 4],
|
||||
s_instrs: [cop1::reserved; 64],
|
||||
d_instrs: [cop1::reserved; 64],
|
||||
w_instrs: [cop1::reserved; 64],
|
||||
l_instrs: [cop1::reserved; 64],
|
||||
},
|
||||
cop2: cop2::Cop2 {
|
||||
instrs: [cop2::reserved; 32],
|
||||
reg_latch: 0,
|
||||
},
|
||||
branch_state: cpu::BranchState {
|
||||
state: cpu::State::Step,
|
||||
pc: 0,
|
||||
},
|
||||
gpr: [0; 32],
|
||||
clock_rate: 0,
|
||||
pc: 0xBFC00000,
|
||||
pc_phys: 0,
|
||||
llbit: false,
|
||||
overclock: false,
|
||||
lo: 0,
|
||||
hi: 0,
|
||||
running: false,
|
||||
instrs: [cop0::reserved; 64],
|
||||
special_instrs: [cop0::reserved; 64],
|
||||
regimm_instrs: [cop0::reserved; 32],
|
||||
events: [events::Event {
|
||||
enabled: false,
|
||||
count: u64::MAX,
|
||||
}; events::EVENT_TYPE_COUNT],
|
||||
next_event_count: u64::MAX,
|
||||
next_event: events::EVENT_TYPE_NONE,
|
||||
},
|
||||
pif: pif::Pif {
|
||||
rom: [0; 1984],
|
||||
ram: [0; 64],
|
||||
channels: [pif::PifChannel {
|
||||
tx: None,
|
||||
tx_buf: None,
|
||||
rx: None,
|
||||
rx_buf: None,
|
||||
process: None,
|
||||
pak_handler: None,
|
||||
change_pak: controller::PakType::None,
|
||||
}; 5],
|
||||
},
|
||||
cart: cart::Cart {
|
||||
rom: Vec::new(),
|
||||
is_viewer_buffer: vec![0; 0xFFFF],
|
||||
pal: false,
|
||||
latch: 0,
|
||||
cic_seed: 0,
|
||||
rtc_timestamp: 0,
|
||||
rtc: cart::AfRtc { control: 0x0200 },
|
||||
sc64: cart::sc64::Sc64 {
|
||||
regs: [0; cart::sc64::SC64_REGS_COUNT],
|
||||
regs_locked: true,
|
||||
cfg: [0; cart::sc64::SC64_CFG_COUNT],
|
||||
sector: 0,
|
||||
buffer: vec![0; 8192],
|
||||
writeback_sector: vec![0; 256],
|
||||
usb_buffer: vec![],
|
||||
},
|
||||
flashram: cart::sram::Flashram {
|
||||
status: 0,
|
||||
erase_page: 0,
|
||||
page_buf: [0xff; 128],
|
||||
silicon_id: [cart::sram::FLASHRAM_TYPE_ID, cart::sram::MX29L1100_ID],
|
||||
mode: cart::sram::FlashramMode::ReadArray,
|
||||
},
|
||||
},
|
||||
memory: memory::Memory {
|
||||
fast_read: [unmapped::read_mem_fast; 0x2000],
|
||||
memory_map_read: [unmapped::read_mem; 0x2000],
|
||||
memory_map_write: [unmapped::write_mem; 0x2000],
|
||||
icache: [cache::ICache {
|
||||
valid: false,
|
||||
index: 0,
|
||||
tag: 0,
|
||||
words: [0; 8],
|
||||
instruction: [cop0::reserved; 8],
|
||||
}; 512],
|
||||
dcache: [cache::DCache {
|
||||
valid: false,
|
||||
dirty: false,
|
||||
tag: 0,
|
||||
index: 0,
|
||||
words: [0; 4],
|
||||
}; 512],
|
||||
},
|
||||
rdram: rdram::Rdram {
|
||||
mem: vec![],
|
||||
size: 0x800000,
|
||||
regs: [[0; rdram::RDRAM_REGS_COUNT]; 4],
|
||||
},
|
||||
rsp: rsp_interface::Rsp {
|
||||
cpu: rsp_cpu::Cpu {
|
||||
instructions: [rsp_cpu::Instructions {
|
||||
func: rsp_su_instructions::reserved,
|
||||
opcode: 0,
|
||||
}; 0x1000 / 4],
|
||||
last_instruction_type: rsp_cpu::InstructionType::Su,
|
||||
instruction_type: rsp_cpu::InstructionType::Su,
|
||||
pipeline_full: false,
|
||||
branch_state: rsp_cpu::BranchState {
|
||||
state: cpu::State::Step,
|
||||
pc: 0,
|
||||
},
|
||||
broken: false,
|
||||
running: false,
|
||||
halted: false,
|
||||
sync_point: false,
|
||||
cycle_counter: 0,
|
||||
gpr: [0; 32],
|
||||
vpr: unsafe { [_mm_setzero_si128(); 32] },
|
||||
reciprocals: [0; 512],
|
||||
inverse_square_roots: [0; 512],
|
||||
divdp: false,
|
||||
divin: 0,
|
||||
divout: 0,
|
||||
shuffle: unsafe { [_mm_setzero_si128(); 16] },
|
||||
vcol: unsafe { _mm_setzero_si128() },
|
||||
vcoh: unsafe { _mm_setzero_si128() },
|
||||
vccl: unsafe { _mm_setzero_si128() },
|
||||
vcch: unsafe { _mm_setzero_si128() },
|
||||
vce: unsafe { _mm_setzero_si128() },
|
||||
accl: unsafe { _mm_setzero_si128() },
|
||||
accm: unsafe { _mm_setzero_si128() },
|
||||
acch: unsafe { _mm_setzero_si128() },
|
||||
special_instrs: [rsp_su_instructions::reserved; 64],
|
||||
regimm_instrs: [rsp_su_instructions::reserved; 32],
|
||||
cop0_instrs: [rsp_su_instructions::reserved; 32],
|
||||
cop2_instrs: [rsp_su_instructions::reserved; 32],
|
||||
lwc2_instrs: [rsp_su_instructions::reserved; 32],
|
||||
swc2_instrs: [rsp_su_instructions::reserved; 32],
|
||||
instrs: [rsp_su_instructions::reserved; 64],
|
||||
vec_instrs: [rsp_vu_instructions::reserved; 64],
|
||||
},
|
||||
regs: [0; rsp_interface::SP_REGS_COUNT],
|
||||
regs2: [0; rsp_interface::SP_REGS2_COUNT],
|
||||
mem: [0; 0x2000],
|
||||
last_status_value: 0,
|
||||
run_after_dma: false,
|
||||
fifo: [rsp_interface::RspDma {
|
||||
dir: rsp_interface::DmaDir::None,
|
||||
length: 0,
|
||||
memaddr: 0,
|
||||
dramaddr: 0,
|
||||
}; 2],
|
||||
},
|
||||
rdp: rdp::Rdp {
|
||||
regs_dpc: [0; rdp::DPC_REGS_COUNT],
|
||||
regs_dps: [0; rdp::DPS_REGS_COUNT],
|
||||
wait_frozen: false,
|
||||
last_status_value: 0,
|
||||
},
|
||||
mi: mi::Mi {
|
||||
regs: [0; mi::MI_REGS_COUNT],
|
||||
},
|
||||
pi: pi::Pi {
|
||||
regs: [0; pi::PI_REGS_COUNT],
|
||||
},
|
||||
ai: ai::Ai {
|
||||
regs: [0; ai::AI_REGS_COUNT],
|
||||
last_read: 0,
|
||||
freq: 33600,
|
||||
delayed_carry: false,
|
||||
fifo: [ai::AiDma {
|
||||
address: 0,
|
||||
length: 0,
|
||||
duration: 0,
|
||||
}; 2],
|
||||
},
|
||||
si: si::Si {
|
||||
regs: [0; si::SI_REGS_COUNT],
|
||||
dma_dir: si::DmaDir::None,
|
||||
},
|
||||
ri: ri::Ri {
|
||||
regs: [0; ri::RI_REGS_COUNT],
|
||||
ram_init: false,
|
||||
},
|
||||
vi: vi::Vi {
|
||||
regs: [0; vi::VI_REGS_COUNT],
|
||||
clock: 0,
|
||||
field: 0,
|
||||
delay: 0,
|
||||
count_per_scanline: 0,
|
||||
frame_time: 0.0,
|
||||
elapsed_time: 0.0,
|
||||
},
|
||||
vru: controller::vru::Vru {
|
||||
status: 0,
|
||||
voice_state: 0,
|
||||
load_offset: 0,
|
||||
voice_init: 0,
|
||||
word_buffer: [0; 40],
|
||||
words: Vec::new(),
|
||||
talking: false,
|
||||
word_mappings: rustc_hash::FxHashMap::default(),
|
||||
},
|
||||
rng: set_rng(),
|
||||
transferpaks: [
|
||||
controller::transferpak::TransferPak::default(),
|
||||
controller::transferpak::TransferPak::default(),
|
||||
controller::transferpak::TransferPak::default(),
|
||||
controller::transferpak::TransferPak::default(),
|
||||
],
|
||||
cheats: cheats::Cheats {
|
||||
cheats: vec![],
|
||||
boot: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
170
src/device/ai.rs
Normal file
@ -0,0 +1,170 @@
|
||||
use crate::{device, ui};
|
||||
|
||||
const AI_DRAM_ADDR_REG: usize = 0;
|
||||
const AI_LEN_REG: usize = 1;
|
||||
// const AI_CONTROL_REG: usize = 2;
|
||||
pub const AI_STATUS_REG: usize = 3;
|
||||
const AI_DACRATE_REG: usize = 4;
|
||||
// const AI_BITRATE_REG: usize = 5;
|
||||
pub const AI_REGS_COUNT: usize = 6;
|
||||
|
||||
pub const AI_STATUS_BUSY: u32 = 0x40000000;
|
||||
const AI_STATUS_FULL: u32 = 0x80000000;
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Ai {
|
||||
pub regs: [u32; AI_REGS_COUNT],
|
||||
pub fifo: [AiDma; 2],
|
||||
pub last_read: u64,
|
||||
pub delayed_carry: bool,
|
||||
pub freq: u64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AiDma {
|
||||
pub address: u64,
|
||||
pub length: u64,
|
||||
pub duration: u64,
|
||||
}
|
||||
|
||||
fn get_remaining_dma_length(device: &device::Device) -> u64 {
|
||||
if device.ai.fifo[0].duration == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let next_ai_event = device::events::get_event(device, device::events::EVENT_TYPE_AI);
|
||||
if next_ai_event.is_none() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let remaining_dma_duration =
|
||||
next_ai_event.unwrap().count - device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG];
|
||||
|
||||
let dma_length = remaining_dma_duration * device.ai.fifo[0].length / device.ai.fifo[0].duration;
|
||||
dma_length & !7
|
||||
}
|
||||
|
||||
fn get_dma_duration(device: &device::Device) -> u64 {
|
||||
let bytes_per_sample = 4; /* XXX: assume 16bit stereo - should depends on bitrate instead */
|
||||
let length = (device.ai.regs[AI_LEN_REG] & !7) as u64;
|
||||
|
||||
(length * device.cpu.clock_rate) / (bytes_per_sample * device.ai.freq)
|
||||
}
|
||||
|
||||
fn do_dma(device: &mut device::Device) {
|
||||
device.ai.last_read = device.ai.fifo[0].length;
|
||||
|
||||
if device.ai.delayed_carry {
|
||||
device.ai.fifo[0].address += 0x2000;
|
||||
}
|
||||
|
||||
device.ai.delayed_carry =
|
||||
((device.ai.fifo[0].address + device.ai.fifo[0].length) & 0x1FFF) == 0;
|
||||
|
||||
/* schedule end of dma event */
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_AI,
|
||||
device.ai.fifo[0].duration,
|
||||
);
|
||||
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI);
|
||||
}
|
||||
|
||||
fn fifo_push(device: &mut device::Device) {
|
||||
let duration = get_dma_duration(device);
|
||||
|
||||
if (device.ai.regs[AI_STATUS_REG] & AI_STATUS_BUSY) != 0 {
|
||||
device.ai.fifo[1].address =
|
||||
device.ai.regs[AI_DRAM_ADDR_REG] as u64 & device::rdram::RDRAM_MASK as u64;
|
||||
device.ai.fifo[1].length = (device.ai.regs[AI_LEN_REG] & !7) as u64;
|
||||
device.ai.fifo[1].duration = duration;
|
||||
device.ai.regs[AI_STATUS_REG] |= AI_STATUS_FULL;
|
||||
} else {
|
||||
device.ai.fifo[0].address =
|
||||
device.ai.regs[AI_DRAM_ADDR_REG] as u64 & device::rdram::RDRAM_MASK as u64;
|
||||
device.ai.fifo[0].length = (device.ai.regs[AI_LEN_REG] & !7) as u64;
|
||||
device.ai.fifo[0].duration = duration;
|
||||
device.ai.regs[AI_STATUS_REG] |= AI_STATUS_BUSY;
|
||||
|
||||
do_dma(device);
|
||||
ui::audio::resume_game_audio(&mut device.ui);
|
||||
}
|
||||
}
|
||||
|
||||
fn fifo_pop(device: &mut device::Device) {
|
||||
if device.ai.regs[AI_STATUS_REG] & AI_STATUS_FULL != 0 {
|
||||
device.ai.fifo[0].address = device.ai.fifo[1].address;
|
||||
device.ai.fifo[0].length = device.ai.fifo[1].length;
|
||||
device.ai.fifo[0].duration = device.ai.fifo[1].duration;
|
||||
device.ai.regs[AI_STATUS_REG] &= !AI_STATUS_FULL;
|
||||
|
||||
do_dma(device);
|
||||
} else {
|
||||
device.ai.regs[AI_STATUS_REG] &= !AI_STATUS_BUSY;
|
||||
device.ai.delayed_carry = false;
|
||||
ui::audio::pause_game_audio(&mut device.ui);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_regs(
|
||||
device: &mut device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
let reg = (address & 0xFFFF) >> 2;
|
||||
let return_value = match reg as usize {
|
||||
AI_LEN_REG => {
|
||||
let value = get_remaining_dma_length(device);
|
||||
if value < device.ai.last_read {
|
||||
let diff = device.ai.fifo[0].length - device.ai.last_read;
|
||||
|
||||
ui::audio::play_audio(
|
||||
device,
|
||||
(device.ai.fifo[0].address + diff) as usize,
|
||||
device.ai.last_read - value,
|
||||
);
|
||||
|
||||
device.ai.last_read = value;
|
||||
}
|
||||
value as u32
|
||||
}
|
||||
_ => device.ai.regs[reg as usize],
|
||||
};
|
||||
device::cop0::add_cycles(device, 20);
|
||||
return_value
|
||||
}
|
||||
|
||||
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
let reg = (address & 0xFFFF) >> 2;
|
||||
match reg as usize {
|
||||
AI_LEN_REG => {
|
||||
device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask);
|
||||
if device.ai.regs[reg as usize] != 0 {
|
||||
fifo_push(device)
|
||||
}
|
||||
}
|
||||
AI_STATUS_REG => device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_AI),
|
||||
AI_DACRATE_REG => {
|
||||
if device.ai.regs[reg as usize] != value & mask {
|
||||
device.ai.freq = device.vi.clock / (1 + (value & mask)) as u64;
|
||||
ui::audio::update_freq(device);
|
||||
}
|
||||
device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask)
|
||||
}
|
||||
_ => device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dma_event(device: &mut device::Device) {
|
||||
if device.ai.last_read != 0 {
|
||||
let diff = device.ai.fifo[0].length - device.ai.last_read;
|
||||
ui::audio::play_audio(
|
||||
device,
|
||||
(device.ai.fifo[0].address + diff) as usize,
|
||||
device.ai.last_read,
|
||||
);
|
||||
device.ai.last_read = 0;
|
||||
}
|
||||
|
||||
fifo_pop(device);
|
||||
}
|
||||
159
src/device/cache.rs
Normal file
@ -0,0 +1,159 @@
|
||||
use crate::{device, savestates};
|
||||
|
||||
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ICache {
|
||||
pub valid: bool,
|
||||
pub tag: u32,
|
||||
pub index: u16,
|
||||
pub words: [u32; 8],
|
||||
#[serde(skip, default = "savestates::default_instructions")]
|
||||
pub instruction: [fn(&mut device::Device, u32); 8],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DCache {
|
||||
pub valid: bool,
|
||||
pub dirty: bool,
|
||||
pub tag: u32,
|
||||
pub index: u16,
|
||||
pub words: [u32; 4],
|
||||
}
|
||||
|
||||
pub fn icache_hit(device: &device::Device, line_index: usize, phys_address: u64) -> bool {
|
||||
device.memory.icache[line_index].valid
|
||||
&& (device.memory.icache[line_index].tag & 0x1ffffffc) == (phys_address & !0xFFF) as u32
|
||||
}
|
||||
|
||||
pub fn icache_writeback(device: &mut device::Device, line_index: usize) {
|
||||
device::cop0::add_cycles(device, device::rdram::rdram_calculate_cycles(32));
|
||||
|
||||
let cache_address = ((device.memory.icache[line_index].tag
|
||||
| (device.memory.icache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
for i in 0..8 {
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | (i * 4),
|
||||
device.memory.icache[line_index].words[i as usize],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icache_fill(device: &mut device::Device, line_index: usize, phys_address: u64) {
|
||||
device::cop0::add_cycles(device, 8);
|
||||
|
||||
device.memory.icache[line_index].valid = true;
|
||||
device.memory.icache[line_index].tag = (phys_address & !0xFFF) as u32;
|
||||
let cache_address = ((device.memory.icache[line_index].tag
|
||||
| (device.memory.icache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
for i in 0..8 {
|
||||
device.memory.icache[line_index].words[i as usize] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | (i * 4),
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
|
||||
device.memory.icache[line_index].instruction[i as usize] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[i as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icache_fetch(device: &mut device::Device, phys_address: u64) {
|
||||
let line_index = ((phys_address >> 5) & 0x1FF) as usize;
|
||||
if !icache_hit(device, line_index, phys_address) {
|
||||
icache_fill(device, line_index, phys_address)
|
||||
}
|
||||
let item = ((phys_address >> 2) & 7) as usize;
|
||||
device.memory.icache[line_index].instruction[item](
|
||||
device,
|
||||
device.memory.icache[line_index].words[item],
|
||||
);
|
||||
}
|
||||
|
||||
pub fn dcache_hit(device: &device::Device, line_index: usize, phys_address: u64) -> bool {
|
||||
device.memory.dcache[line_index].valid
|
||||
&& (device.memory.dcache[line_index].tag & 0x1ffffffc) == (phys_address & !0xFFF) as u32
|
||||
}
|
||||
|
||||
pub fn dcache_writeback(device: &mut device::Device, line_index: usize) {
|
||||
device::cop0::add_cycles(device, device::rdram::rdram_calculate_cycles(16));
|
||||
|
||||
device.memory.dcache[line_index].dirty = false;
|
||||
|
||||
let cache_address = ((device.memory.dcache[line_index].tag
|
||||
| (device.memory.dcache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
|
||||
for i in 0..4 {
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | (i * 4),
|
||||
device.memory.dcache[line_index].words[i as usize],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn dcache_fill(device: &mut device::Device, line_index: usize, phys_address: u64) {
|
||||
device::cop0::add_cycles(device, 7);
|
||||
|
||||
device.memory.dcache[line_index].valid = true;
|
||||
device.memory.dcache[line_index].dirty = false;
|
||||
|
||||
device.memory.dcache[line_index].tag = (phys_address & !0xFFF) as u32;
|
||||
let cache_address = ((device.memory.dcache[line_index].tag
|
||||
| (device.memory.dcache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
|
||||
for i in 0..4 {
|
||||
device.memory.dcache[line_index].words[i as usize] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | (i * 4),
|
||||
device::memory::AccessSize::Dcache,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dcache_read(device: &mut device::Device, phys_address: u64) -> u32 {
|
||||
let line_index = ((phys_address >> 4) & 0x1FF) as usize;
|
||||
if !dcache_hit(device, line_index, phys_address) {
|
||||
if device.memory.dcache[line_index].valid && device.memory.dcache[line_index].dirty {
|
||||
dcache_writeback(device, line_index)
|
||||
}
|
||||
dcache_fill(device, line_index, phys_address)
|
||||
} else {
|
||||
device::cop0::add_cycles(device, 1)
|
||||
}
|
||||
device.memory.dcache[line_index].words[((phys_address >> 2) & 3) as usize]
|
||||
}
|
||||
|
||||
pub fn dcache_write(device: &mut device::Device, phys_address: u64, value: u32, mask: u32) {
|
||||
let line_index = ((phys_address >> 4) & 0x1FF) as usize;
|
||||
if !dcache_hit(device, line_index, phys_address) {
|
||||
if device.memory.dcache[line_index].valid && device.memory.dcache[line_index].dirty {
|
||||
dcache_writeback(device, line_index)
|
||||
}
|
||||
dcache_fill(device, line_index, phys_address)
|
||||
} else {
|
||||
device::cop0::add_cycles(device, 1)
|
||||
}
|
||||
device::memory::masked_write_32(
|
||||
&mut device.memory.dcache[line_index].words[((phys_address >> 2) & 3) as usize],
|
||||
value,
|
||||
mask,
|
||||
);
|
||||
device.memory.dcache[line_index].dirty = true;
|
||||
}
|
||||
|
||||
pub fn init(device: &mut device::Device) {
|
||||
for (pos, i) in device.memory.icache.iter_mut().enumerate() {
|
||||
i.index = (pos << 5) as u16 & 0xFE0
|
||||
}
|
||||
for (pos, i) in device.memory.dcache.iter_mut().enumerate() {
|
||||
i.index = (pos << 4) as u16 & 0xFF0
|
||||
}
|
||||
}
|
||||
211
src/device/cart.rs
Normal file
@ -0,0 +1,211 @@
|
||||
use chrono::Datelike;
|
||||
use chrono::Timelike;
|
||||
|
||||
use crate::{device, ui};
|
||||
|
||||
pub mod rom;
|
||||
pub mod sc64;
|
||||
pub mod sram;
|
||||
|
||||
const JCMD_STATUS: u8 = 0x00;
|
||||
const JCMD_EEPROM_READ: u8 = 0x04;
|
||||
const JCMD_EEPROM_WRITE: u8 = 0x05;
|
||||
const JCMD_AF_RTC_STATUS: u8 = 0x06;
|
||||
const JCMD_AF_RTC_READ: u8 = 0x07;
|
||||
const JCMD_AF_RTC_WRITE: u8 = 0x08;
|
||||
const JCMD_RESET: u8 = 0xff;
|
||||
|
||||
const JDT_AF_RTC: u16 = 0x1000; /* RTC */
|
||||
const JDT_EEPROM_4K: u16 = 0x8000; /* 4k EEPROM */
|
||||
const JDT_EEPROM_16K: u16 = 0xc000; /* 16k EEPROM */
|
||||
const EEPROM_BLOCK_SIZE: usize = 8;
|
||||
pub const EEPROM_MAX_SIZE: usize = 0x800;
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AfRtc {
|
||||
pub control: u16,
|
||||
}
|
||||
|
||||
fn byte2bcd(mut n: u32) -> u8 {
|
||||
n %= 100;
|
||||
(((n / 10) << 4) | (n % 10)) as u8
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Cart {
|
||||
#[serde(skip)]
|
||||
pub rom: Vec<u8>,
|
||||
pub is_viewer_buffer: Vec<u8>,
|
||||
pub pal: bool,
|
||||
pub latch: u32,
|
||||
pub cic_seed: u8,
|
||||
pub rtc: AfRtc,
|
||||
pub sc64: sc64::Sc64,
|
||||
pub flashram: sram::Flashram,
|
||||
pub rtc_timestamp: i64,
|
||||
}
|
||||
|
||||
pub fn process(device: &mut device::Device, channel: usize) {
|
||||
let cmd = device.pif.ram[device.pif.channels[channel].tx_buf.unwrap()];
|
||||
|
||||
match cmd {
|
||||
JCMD_RESET | JCMD_STATUS => {
|
||||
let eeprom_type = if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Eeprom16k)
|
||||
{
|
||||
JDT_EEPROM_16K
|
||||
} else if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Eeprom4k)
|
||||
{
|
||||
JDT_EEPROM_4K
|
||||
} else {
|
||||
device.pif.ram[device.pif.channels[channel].rx.unwrap()] |= 0x80;
|
||||
return;
|
||||
};
|
||||
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = eeprom_type as u8;
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] =
|
||||
(eeprom_type >> 8) as u8;
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = 0;
|
||||
}
|
||||
JCMD_EEPROM_READ => {
|
||||
eeprom_read_block(
|
||||
device,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
||||
device.pif.channels[channel].rx_buf.unwrap(),
|
||||
);
|
||||
}
|
||||
JCMD_EEPROM_WRITE => eeprom_write_block(
|
||||
device,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 2,
|
||||
device.pif.channels[channel].rx_buf.unwrap(),
|
||||
),
|
||||
JCMD_AF_RTC_STATUS => {
|
||||
/* set type and status */
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = JDT_AF_RTC as u8;
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] =
|
||||
(JDT_AF_RTC >> 8) as u8;
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = 0x00;
|
||||
}
|
||||
JCMD_AF_RTC_READ => {
|
||||
af_rtc_read_block(
|
||||
device,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
||||
device.pif.channels[channel].rx_buf.unwrap(),
|
||||
device.pif.channels[channel].rx_buf.unwrap() + 8,
|
||||
);
|
||||
}
|
||||
JCMD_AF_RTC_WRITE => af_rtc_write_block(
|
||||
device,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 2,
|
||||
device.pif.channels[channel].rx_buf.unwrap(),
|
||||
),
|
||||
_ => {
|
||||
panic!("unknown cart command")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_eeprom(device: &mut device::Device) {
|
||||
if device.ui.storage.saves.eeprom.data.len() < EEPROM_MAX_SIZE {
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.eeprom
|
||||
.data
|
||||
.resize(EEPROM_MAX_SIZE, 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
fn eeprom_read_block(device: &mut device::Device, block: usize, offset: usize) {
|
||||
let address = device.pif.ram[block] as usize * EEPROM_BLOCK_SIZE;
|
||||
|
||||
device.pif.ram[offset..offset + EEPROM_BLOCK_SIZE].copy_from_slice(
|
||||
&device.ui.storage.saves.eeprom.data[address..address + EEPROM_BLOCK_SIZE],
|
||||
);
|
||||
}
|
||||
|
||||
fn eeprom_write_block(device: &mut device::Device, block: usize, offset: usize, status: usize) {
|
||||
let address = device.pif.ram[block] as usize * EEPROM_BLOCK_SIZE;
|
||||
|
||||
device.ui.storage.saves.eeprom.data[address..address + EEPROM_BLOCK_SIZE]
|
||||
.copy_from_slice(&device.pif.ram[offset..offset + EEPROM_BLOCK_SIZE]);
|
||||
|
||||
device.pif.ram[status] = 0x00;
|
||||
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Eeprom4k);
|
||||
}
|
||||
|
||||
fn time2data(device: &mut device::Device, offset: usize) {
|
||||
let timestamp = device.cart.rtc_timestamp + device.vi.elapsed_time as i64;
|
||||
let now = chrono::DateTime::from_timestamp(timestamp, 0)
|
||||
.unwrap()
|
||||
.with_timezone(&chrono::Local);
|
||||
|
||||
device.pif.ram[offset] = byte2bcd(now.second());
|
||||
device.pif.ram[offset + 1] = byte2bcd(now.minute());
|
||||
device.pif.ram[offset + 2] = 0x80 + byte2bcd(now.hour());
|
||||
device.pif.ram[offset + 3] = byte2bcd(now.day());
|
||||
device.pif.ram[offset + 4] = byte2bcd(now.weekday().num_days_from_sunday());
|
||||
device.pif.ram[offset + 5] = byte2bcd(now.month());
|
||||
device.pif.ram[offset + 6] = byte2bcd(now.year() as u32 - 1900);
|
||||
device.pif.ram[offset + 7] = byte2bcd((now.year() as u32 - 1900) / 100);
|
||||
}
|
||||
|
||||
fn af_rtc_read_block(device: &mut device::Device, block: usize, offset: usize, status: usize) {
|
||||
match device.pif.ram[block] {
|
||||
0 => {
|
||||
device.pif.ram[offset] = device.cart.rtc.control as u8;
|
||||
device.pif.ram[offset + 1] = (device.cart.rtc.control >> 8) as u8;
|
||||
device.pif.ram[status] = 0x00;
|
||||
}
|
||||
1 => {
|
||||
panic!("AF-RTC reading block 1 is not implemented !");
|
||||
}
|
||||
2 => {
|
||||
time2data(device, offset);
|
||||
device.pif.ram[status] = 0x00;
|
||||
}
|
||||
_ => {
|
||||
panic!("AF-RTC read invalid block");
|
||||
}
|
||||
}
|
||||
}
|
||||
fn af_rtc_write_block(device: &mut device::Device, block: usize, offset: usize, status: usize) {
|
||||
match device.pif.ram[block] {
|
||||
0 => {
|
||||
device.cart.rtc.control =
|
||||
((device.pif.ram[offset + 1] as u16) << 8) | device.pif.ram[offset] as u16;
|
||||
device.pif.ram[status] = 0x00;
|
||||
}
|
||||
1 => {
|
||||
/* block 1 read-only when control[0] is set */
|
||||
if (device.cart.rtc.control & 0x01) != 0 {
|
||||
return;
|
||||
}
|
||||
panic!("AF-RTC writing block 1 is not implemented !");
|
||||
}
|
||||
2 => {
|
||||
/* block 2 read-only when control[1] is set */
|
||||
if (device.cart.rtc.control & 0x02) != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: implement block 2 writes */
|
||||
panic!("AF-RTC writing block 2 is not implemented !");
|
||||
}
|
||||
|
||||
_ => {
|
||||
panic!("AF-RTC write invalid block");
|
||||
}
|
||||
}
|
||||
}
|
||||
185
src/device/cart/rom.rs
Normal file
@ -0,0 +1,185 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub const CART_MASK: usize = 0xFFFFFFF;
|
||||
|
||||
fn read_cart_word(device: &device::Device, address: usize) -> u32 {
|
||||
let romsave = &device.ui.storage.saves.romsave.data;
|
||||
let rom = &device.cart.rom;
|
||||
u32::from_be_bytes(std::array::from_fn(|i| {
|
||||
romsave
|
||||
.get(&((address + i) as u32))
|
||||
.copied()
|
||||
.unwrap_or_else(|| *rom.get(address + i).unwrap_or(&0))
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn read_mem_fast(
|
||||
device: &device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
let masked_address = address as usize & CART_MASK;
|
||||
read_cart_word(device, masked_address)
|
||||
}
|
||||
|
||||
pub fn read_mem(
|
||||
device: &mut device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
let cycles = device::pi::calculate_cycles(device, 1, 4);
|
||||
device::cop0::add_cycles(device, cycles);
|
||||
|
||||
// well known cart ROM oddity, if a read is performed while PI_STATUS_IO_BUSY is set, the latched value is returned rather than the data at the specified address
|
||||
if device.pi.regs[device::pi::PI_STATUS_REG] & device::pi::PI_STATUS_IO_BUSY != 0 {
|
||||
device.cart.latch
|
||||
} else {
|
||||
let masked_address = address as usize & CART_MASK;
|
||||
read_cart_word(device, masked_address)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
if device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] != 0 {
|
||||
let masked_address = address as usize & CART_MASK;
|
||||
let mut data = read_cart_word(device, masked_address);
|
||||
device::memory::masked_write_32(&mut data, value, mask);
|
||||
for (i, item) in data.to_be_bytes().iter().enumerate() {
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.romsave
|
||||
.data
|
||||
.insert((masked_address + i) as u32, *item);
|
||||
}
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Romsave);
|
||||
}
|
||||
|
||||
device.cart.latch = value & mask;
|
||||
|
||||
device.pi.regs[device::pi::PI_STATUS_REG] |= device::pi::PI_STATUS_IO_BUSY;
|
||||
|
||||
let cycles = device::pi::calculate_cycles(device, 1, 4);
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles);
|
||||
}
|
||||
|
||||
pub fn dma_read(
|
||||
device: &mut device::Device,
|
||||
mut cart_addr: u32,
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
if device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] != 0 {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= CART_MASK as u32;
|
||||
|
||||
for i in 0..length {
|
||||
device.ui.storage.saves.romsave.data.insert(
|
||||
cart_addr + i,
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get((dram_addr + i) as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0),
|
||||
);
|
||||
}
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Romsave);
|
||||
}
|
||||
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
}
|
||||
|
||||
// cart is big endian, rdram is native endian
|
||||
pub fn dma_write(
|
||||
device: &mut device::Device,
|
||||
mut cart_addr: u32,
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= CART_MASK as u32;
|
||||
let mut i = dram_addr;
|
||||
let mut j = cart_addr;
|
||||
while i < dram_addr + length {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(i as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = if let Some(value) = device.ui.storage.saves.romsave.data.get(&j) {
|
||||
*value
|
||||
} else {
|
||||
*device.cart.rom.get(j as usize).unwrap_or(&0)
|
||||
};
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
}
|
||||
|
||||
pub fn init(device: &mut device::Device, rom_file: &[u8]) {
|
||||
device.cart.sc64.cfg[device::cart::sc64::SC64_BOOTLOADER_SWITCH as usize] = 1;
|
||||
|
||||
device.cart.rom = rom_file.to_vec();
|
||||
device.cart.pal = is_system_pal(&device.cart.rom);
|
||||
set_cic(device);
|
||||
|
||||
device.ui.game_hash = calculate_hash(&device.cart.rom);
|
||||
|
||||
device.ui.game_id = String::from_utf8(device.cart.rom[0x3B..0x3E].to_vec()).unwrap();
|
||||
if device.ui.game_id.contains('\0') {
|
||||
device.ui.game_id = String::from("UNK");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_system_pal(rom_contents: &[u8]) -> bool {
|
||||
let pal_codes: [u8; 8] = *b"DFIPSUXY";
|
||||
for i in pal_codes {
|
||||
if rom_contents[0x3E] == i {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn set_cic(device: &mut device::Device) {
|
||||
let hash = calculate_hash(&device.cart.rom[0x40..0x1000]);
|
||||
match hash.as_str() {
|
||||
"B99F06C4802C2377E31E388435955EF3E99C618A6D55D24699D828EB1075F1EB" => {
|
||||
device.cart.cic_seed = 0x3F; // CicNus6101
|
||||
}
|
||||
"61E88238552C356C23D19409FE5570EE6910419586BC6FC740F638F761ADC46E" => {
|
||||
device.cart.cic_seed = 0x3F; // CicNus6102
|
||||
}
|
||||
"BF3620D30817007091EBE9BDDD1B88C23B8A0052170B3309CDE5B6B4238E45E7" => {
|
||||
device.cart.cic_seed = 0x78; // CicNus6103
|
||||
}
|
||||
"04B7BC6717A9F0EB724CF927E74AD3876C381CBB280D841736FC5E55580B756B" => {
|
||||
device.cart.cic_seed = 0x91; // CicNus6105
|
||||
}
|
||||
"36ADC40148AF56F0D78CD505EB6A90117D1FD6F11C6309E52ED36BC4C6BA340E" => {
|
||||
device.cart.cic_seed = 0x85; // CicNus6106
|
||||
}
|
||||
"53C0088FB777870D0AF32F0251E964030E2E8B72E830C26042FD191169508C05" => {
|
||||
device.cart.cic_seed = 0xdd; // CicNus5167
|
||||
device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] = 1;
|
||||
}
|
||||
_ => {
|
||||
device.cart.cic_seed = 0x3F; // CicNus6102
|
||||
//println!("unknown IPL3 {}", hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_hash(data: &[u8]) -> String {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(data);
|
||||
hasher
|
||||
.finalize()
|
||||
.iter()
|
||||
.map(|b| format!("{:02X}", b))
|
||||
.collect()
|
||||
}
|
||||
462
src/device/cart/sc64.rs
Normal file
@ -0,0 +1,462 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub const SDCARD_SIZE: usize = 0x4000000;
|
||||
|
||||
const SC64_SCR_REG: usize = 0;
|
||||
const SC64_DATA0_REG: usize = 1;
|
||||
const SC64_DATA1_REG: usize = 2;
|
||||
const SC64_IDENTIFIER_REG: usize = 3;
|
||||
const SC64_KEY_REG: usize = 4;
|
||||
//const SC64_IRQ_REG: usize = 5;
|
||||
//const SC64_AUX_REG: usize = 6;
|
||||
pub const SC64_REGS_COUNT: usize = 7;
|
||||
|
||||
pub const SC64_BOOTLOADER_SWITCH: u32 = 0;
|
||||
pub const SC64_ROM_WRITE_ENABLE: u32 = 1;
|
||||
pub const SC64_SAVE_TYPE: u32 = 6;
|
||||
pub const SC64_CFG_COUNT: usize = 15;
|
||||
|
||||
const SC64_BUFFER_MASK: usize = 0x1FFF;
|
||||
const SC64_EEPROM_MASK: usize = 0xFFF;
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Sc64 {
|
||||
pub buffer: Vec<u8>,
|
||||
pub regs: [u32; SC64_REGS_COUNT],
|
||||
pub regs_locked: bool,
|
||||
pub cfg: [u32; SC64_CFG_COUNT],
|
||||
pub sector: u32,
|
||||
pub writeback_sector: Vec<u32>,
|
||||
pub usb_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
fn format_sdcard(device: &mut device::Device) {
|
||||
if device.ui.storage.saves.sdcard.data.is_empty() {
|
||||
device.ui.storage.saves.sdcard.data.resize(SDCARD_SIZE, 0);
|
||||
let buf = std::io::Cursor::new(&mut device.ui.storage.saves.sdcard.data);
|
||||
fatfs::format_volume(buf, fatfs::FormatVolumeOptions::new()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_regs(
|
||||
device: &mut device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
device::cop0::add_cycles(device, 20);
|
||||
if device.cart.sc64.regs_locked {
|
||||
return 0;
|
||||
}
|
||||
let reg = (address & 0xFFFF) >> 2;
|
||||
match reg as usize {
|
||||
SC64_SCR_REG | SC64_DATA0_REG | SC64_DATA1_REG => device.cart.sc64.regs[reg as usize],
|
||||
SC64_IDENTIFIER_REG => 0x53437632,
|
||||
_ => panic!("unknown read reg {reg} address {address:#x}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
let reg = (address & 0xFFFF) >> 2;
|
||||
match reg as usize {
|
||||
SC64_KEY_REG => {
|
||||
device::memory::masked_write_32(&mut device.cart.sc64.regs[reg as usize], value, mask);
|
||||
if device.cart.sc64.regs[SC64_KEY_REG] == 0x4F434B5F {
|
||||
device.cart.sc64.regs_locked = false;
|
||||
} else if device.cart.sc64.regs[SC64_KEY_REG] == 0xFFFFFFFF {
|
||||
device.cart.sc64.regs_locked = true;
|
||||
}
|
||||
}
|
||||
SC64_DATA0_REG | SC64_DATA1_REG => {
|
||||
if !device.cart.sc64.regs_locked {
|
||||
device::memory::masked_write_32(
|
||||
&mut device.cart.sc64.regs[reg as usize],
|
||||
value,
|
||||
mask,
|
||||
);
|
||||
}
|
||||
}
|
||||
SC64_SCR_REG => {
|
||||
if !device.cart.sc64.regs_locked {
|
||||
match char::from_u32(value & mask).unwrap() {
|
||||
'V' => {
|
||||
// get version
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] = (2 << 16) | 20;
|
||||
device.cart.sc64.regs[SC64_DATA1_REG] = 2;
|
||||
}
|
||||
'c' => {
|
||||
// get config
|
||||
device.cart.sc64.regs[SC64_DATA1_REG] =
|
||||
device.cart.sc64.cfg[device.cart.sc64.regs[SC64_DATA0_REG] as usize]
|
||||
}
|
||||
'C' => {
|
||||
// set config
|
||||
if device.cart.sc64.regs[SC64_DATA0_REG] == SC64_SAVE_TYPE {
|
||||
// if save type is being written, we are probably booting a game using the flash cart menu
|
||||
// we shouldn't write saves to disk in this case (they are written to the SD card)
|
||||
device.ui.storage.saves.write_to_disk = false;
|
||||
device.ui.storage.save_type =
|
||||
match device.cart.sc64.regs[SC64_DATA1_REG] {
|
||||
0 => {
|
||||
vec![]
|
||||
}
|
||||
1 => {
|
||||
vec![ui::storage::SaveTypes::Eeprom4k]
|
||||
}
|
||||
2 => {
|
||||
vec![ui::storage::SaveTypes::Eeprom16k]
|
||||
}
|
||||
3 => {
|
||||
vec![ui::storage::SaveTypes::Sram]
|
||||
}
|
||||
4 => {
|
||||
vec![ui::storage::SaveTypes::Flash]
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"unknown sc64 save type: {}",
|
||||
device.cart.sc64.regs[SC64_DATA1_REG]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
std::mem::swap(
|
||||
&mut device.cart.sc64.cfg
|
||||
[device.cart.sc64.regs[SC64_DATA0_REG] as usize],
|
||||
&mut device.cart.sc64.regs[SC64_DATA1_REG],
|
||||
);
|
||||
}
|
||||
'i' => {
|
||||
// sd card operation
|
||||
match device.cart.sc64.regs[SC64_DATA1_REG] {
|
||||
0 => { //Init SD card
|
||||
}
|
||||
1 => { //Deinit SD card
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"unknown sc64 sd card operation: {}",
|
||||
device.cart.sc64.regs[SC64_DATA1_REG]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
'I' => {
|
||||
// set sd sector
|
||||
device.cart.sc64.sector = device.cart.sc64.regs[SC64_DATA0_REG];
|
||||
}
|
||||
's' => {
|
||||
format_sdcard(device);
|
||||
// read sd card
|
||||
let address = device.cart.sc64.regs[SC64_DATA0_REG] as u64 & 0x1FFFFFFF;
|
||||
let offset = (device.cart.sc64.sector * 512) as usize;
|
||||
let length = (device.cart.sc64.regs[SC64_DATA1_REG] * 512) as usize;
|
||||
let mut i = 0;
|
||||
|
||||
while i < length {
|
||||
let data = u32::from_be_bytes(
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.sdcard
|
||||
.data
|
||||
.get((offset + i)..(offset + i + 4))
|
||||
.unwrap_or(&[0; 4])
|
||||
.try_into()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
device::memory::data_write(
|
||||
device,
|
||||
address + i as u64,
|
||||
data,
|
||||
0xFFFFFFFF,
|
||||
false,
|
||||
);
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
'S' => {
|
||||
format_sdcard(device);
|
||||
// write sd card
|
||||
let address = device.cart.sc64.regs[SC64_DATA0_REG] as u64 & 0x1FFFFFFF;
|
||||
let offset = (device.cart.sc64.sector * 512) as usize;
|
||||
let length = (device.cart.sc64.regs[SC64_DATA1_REG] * 512) as usize;
|
||||
let mut i = 0;
|
||||
|
||||
while i < length {
|
||||
let data = device::memory::data_read(
|
||||
device,
|
||||
address + i as u64,
|
||||
device::memory::AccessSize::Word,
|
||||
false,
|
||||
)
|
||||
.to_be_bytes();
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.sdcard
|
||||
.data
|
||||
.get_mut((offset + i)..(offset + i + 4))
|
||||
.unwrap_or(&mut [0; 4])
|
||||
.copy_from_slice(&data);
|
||||
i += 4;
|
||||
}
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Sdcard);
|
||||
}
|
||||
'U' => {
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] = 0;
|
||||
}
|
||||
'u' => {
|
||||
// used to notify the game that there is data to read
|
||||
if let Some(cart_rx) = device.ui.usb.cart_rx.as_mut() {
|
||||
match cart_rx.try_recv() {
|
||||
Ok(data) => {
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] = data.data_type & 0xFF; // read_status/type
|
||||
device.cart.sc64.regs[SC64_DATA1_REG] =
|
||||
data.data_size & 0xFFFFFF; // length
|
||||
device.cart.sc64.usb_buffer = data.data; // store the data to be read
|
||||
}
|
||||
Err(_err) => {
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] = 0; // read_status/type
|
||||
device.cart.sc64.regs[SC64_DATA1_REG] = 0; // length
|
||||
}
|
||||
}
|
||||
} else {
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] = 0; // read_status/type
|
||||
device.cart.sc64.regs[SC64_DATA1_REG] = 0; // length
|
||||
}
|
||||
}
|
||||
'M' => {
|
||||
// Send data from from flashcart to USB
|
||||
let address = device.cart.sc64.regs[SC64_DATA0_REG] as u64 & 0x1FFFFFFF;
|
||||
let length = device.cart.sc64.regs[SC64_DATA1_REG] as usize & 0xFFFFFF;
|
||||
|
||||
if let Some(usb_tx) = device.ui.usb.usb_tx.as_mut() {
|
||||
let mut usb_buffer = vec![0; length];
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
if address < device.rdram.size as u64 {
|
||||
while i < length {
|
||||
*usb_buffer.get_mut(i).unwrap_or(&mut 0) = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get((address as usize + i) ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
i += 1;
|
||||
}
|
||||
} else if address >= device::memory::MM_CART_ROM as u64
|
||||
&& address < device::memory::MM_PIF_MEM as u64
|
||||
{
|
||||
while i < length {
|
||||
*usb_buffer.get_mut(i).unwrap_or(&mut 0) = *device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.romsave
|
||||
.data
|
||||
.get(
|
||||
&(((address as usize + i)
|
||||
& device::cart::rom::CART_MASK)
|
||||
as u32),
|
||||
)
|
||||
.unwrap_or(
|
||||
device
|
||||
.cart
|
||||
.rom
|
||||
.get(
|
||||
(address as usize + i)
|
||||
& device::cart::rom::CART_MASK,
|
||||
)
|
||||
.unwrap_or(&0),
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
panic!("Unknown address {address:#x} for SC64 M command");
|
||||
}
|
||||
|
||||
ui::usb::send_to_usb(
|
||||
usb_tx,
|
||||
ui::usb::UsbData {
|
||||
data: usb_buffer,
|
||||
data_type: device.cart.sc64.regs[SC64_DATA1_REG] >> 24,
|
||||
data_size: device.cart.sc64.regs[SC64_DATA1_REG] & 0xFFFFFF,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
'm' => {
|
||||
// Receive data from USB to flashcart
|
||||
let address = device.cart.sc64.regs[SC64_DATA0_REG] as u64 & 0x1FFFFFFF;
|
||||
let length = device.cart.sc64.regs[SC64_DATA1_REG] as usize;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
if address < device.rdram.size as u64 {
|
||||
while i < length {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut((address as usize + i) ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) =
|
||||
*device.cart.sc64.usb_buffer.get(i).unwrap_or(&0);
|
||||
i += 1;
|
||||
}
|
||||
} else if address >= device::memory::MM_CART_ROM as u64
|
||||
&& address < device::memory::MM_PIF_MEM as u64
|
||||
{
|
||||
while i < length {
|
||||
device.ui.storage.saves.romsave.data.insert(
|
||||
((address as usize + i) & device::cart::rom::CART_MASK) as u32,
|
||||
*device.cart.sc64.usb_buffer.get(i).unwrap_or(&0),
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
panic!("Unknown address {address:#x} for SC64 m command");
|
||||
}
|
||||
}
|
||||
'w' => {
|
||||
// SD card writeback pending
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] = 0;
|
||||
}
|
||||
'W' => {
|
||||
let writeback_sectors_address =
|
||||
device.cart.sc64.regs[SC64_DATA0_REG] as u64;
|
||||
for i in 0..256 {
|
||||
device.cart.sc64.writeback_sector[i] = device::memory::data_read(
|
||||
device,
|
||||
writeback_sectors_address + (i * 4) as u64,
|
||||
device::memory::AccessSize::Word,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"unknown sc64 command: {}",
|
||||
char::from_u32(value & mask).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => panic!(
|
||||
"unknown write reg {} address {:#x} value {}",
|
||||
reg,
|
||||
address,
|
||||
char::from_u32(value & mask).unwrap()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_mem(
|
||||
device: &mut device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
if address & 0x2000 != 0 {
|
||||
let masked_address = address as usize & SC64_EEPROM_MASK;
|
||||
u32::from_be_bytes(
|
||||
device.ui.storage.saves.eeprom.data[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
let masked_address = address as usize & SC64_BUFFER_MASK;
|
||||
u32::from_be_bytes(
|
||||
device.cart.sc64.buffer[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
if address & 0x2000 != 0 {
|
||||
let masked_address = address as usize & SC64_EEPROM_MASK;
|
||||
let mut data = u32::from_be_bytes(
|
||||
device.ui.storage.saves.eeprom.data[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
device::memory::masked_write_32(&mut data, value, mask);
|
||||
device.ui.storage.saves.eeprom.data[masked_address..masked_address + 4]
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Eeprom4k);
|
||||
} else {
|
||||
let masked_address = address as usize & SC64_BUFFER_MASK;
|
||||
let mut data = u32::from_be_bytes(
|
||||
device.cart.sc64.buffer[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
device::memory::masked_write_32(&mut data, value, mask);
|
||||
device.cart.sc64.buffer[masked_address..masked_address + 4]
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dma_read(
|
||||
device: &mut device::Device,
|
||||
mut cart_addr: u32,
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
let buffer = if cart_addr & 0x2000 != 0 {
|
||||
cart_addr &= SC64_EEPROM_MASK as u32;
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Eeprom4k);
|
||||
&mut device.ui.storage.saves.eeprom.data
|
||||
} else {
|
||||
cart_addr &= SC64_BUFFER_MASK as u32;
|
||||
&mut device.cart.sc64.buffer
|
||||
};
|
||||
let mut i = dram_addr;
|
||||
let mut j = cart_addr;
|
||||
|
||||
while i < dram_addr + length {
|
||||
*buffer.get_mut(j as usize).unwrap_or(&mut 0) = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get(i as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
}
|
||||
|
||||
pub fn dma_write(
|
||||
device: &mut device::Device,
|
||||
mut cart_addr: u32,
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
let buffer = if cart_addr & 0x2000 != 0 {
|
||||
cart_addr &= SC64_EEPROM_MASK as u32;
|
||||
&device.ui.storage.saves.eeprom.data
|
||||
} else {
|
||||
cart_addr &= SC64_BUFFER_MASK as u32;
|
||||
&device.cart.sc64.buffer
|
||||
};
|
||||
let mut i = dram_addr;
|
||||
let mut j = cart_addr;
|
||||
|
||||
while i < dram_addr + length {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(i as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = *buffer.get(j as usize).unwrap_or(&0);
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
}
|
||||
380
src/device/cart/sram.rs
Normal file
@ -0,0 +1,380 @@
|
||||
use crate::{device, ui};
|
||||
|
||||
const SRAM_MASK: usize = 0xFFFF;
|
||||
pub const SRAM_SIZE: usize = 0x8000;
|
||||
pub const FLASHRAM_SIZE: usize = 0x20000;
|
||||
pub const FLASHRAM_TYPE_ID: u32 = 0x11118001;
|
||||
pub const MX29L1100_ID: u32 = 0x00c2001e;
|
||||
const MX29L0000_ID: u32 = 0x00c20000;
|
||||
const MX29L0001_ID: u32 = 0x00c20001;
|
||||
|
||||
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum FlashramMode {
|
||||
ReadArray,
|
||||
ReadSiliconId,
|
||||
Status,
|
||||
SectorErase,
|
||||
ChipErase,
|
||||
PageProgram,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Flashram {
|
||||
pub status: u32,
|
||||
pub mode: FlashramMode,
|
||||
pub erase_page: u16,
|
||||
#[serde(with = "serde_big_array::BigArray")]
|
||||
pub page_buf: [u8; 128],
|
||||
pub silicon_id: [u32; 2],
|
||||
}
|
||||
|
||||
pub fn format_sram(device: &mut device::Device) {
|
||||
if device.ui.storage.saves.sram.data.len() < SRAM_SIZE {
|
||||
device.ui.storage.saves.sram.data.resize(SRAM_SIZE, 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_flash(device: &mut device::Device) {
|
||||
if device.ui.storage.saves.flash.data.len() < FLASHRAM_SIZE {
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.flash
|
||||
.data
|
||||
.resize(FLASHRAM_SIZE, 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_mem_sram(device: &mut device::Device, address: u64) -> u32 {
|
||||
let masked_address = address as usize & SRAM_MASK;
|
||||
|
||||
u32::from_be_bytes(
|
||||
device.ui.storage.saves.sram.data[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn read_mem_flash(device: &device::Device, address: u64) -> u32 {
|
||||
if (address & 0x1ffff) == 0x00000 && device.cart.flashram.mode == FlashramMode::Status {
|
||||
/* read Status register */
|
||||
device.cart.flashram.status
|
||||
} else if (address & 0x1ffff) == 0x0000 && device.cart.flashram.mode == FlashramMode::ReadArray
|
||||
{
|
||||
/* flashram MMIO read are not supported except for the "dummy" read @0x0000 done before DMA.
|
||||
* returns a "dummy" value. */
|
||||
0
|
||||
} else {
|
||||
/* other accesses are not implemented */
|
||||
panic!("unknown flashram read")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_mem(
|
||||
device: &mut device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
let cycles = device::pi::calculate_cycles(device, 2, 4);
|
||||
device::cop0::add_cycles(device, cycles);
|
||||
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Sram)
|
||||
{
|
||||
read_mem_sram(device, address)
|
||||
} else {
|
||||
read_mem_flash(device, address)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mem_sram(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
let masked_address = address as usize & SRAM_MASK;
|
||||
|
||||
let mut data = u32::from_be_bytes(
|
||||
device.ui.storage.saves.sram.data[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
device::memory::masked_write_32(&mut data, value, mask);
|
||||
device.ui.storage.saves.sram.data[masked_address..masked_address + 4]
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Sram);
|
||||
}
|
||||
|
||||
fn write_mem_flash(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
if (address & 0x1ffff) == 0x00000 && device.cart.flashram.mode == FlashramMode::Status {
|
||||
/* clear/set Status register */
|
||||
device.cart.flashram.status = (value & mask) & 0xff;
|
||||
} else if (address & 0x1ffff) == 0x10000 {
|
||||
/* set command */
|
||||
flashram_command(device, value & mask);
|
||||
} else {
|
||||
/* other accesses are not implemented */
|
||||
panic!("unknown flashram write")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Sram)
|
||||
{
|
||||
write_mem_sram(device, address, value, mask)
|
||||
} else {
|
||||
write_mem_flash(device, address, value, mask)
|
||||
}
|
||||
|
||||
device.pi.regs[device::pi::PI_STATUS_REG] |= device::pi::PI_STATUS_IO_BUSY;
|
||||
|
||||
let cycles = device::pi::calculate_cycles(device, 2, 4);
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles);
|
||||
}
|
||||
|
||||
fn dma_read_sram(device: &mut device::Device, mut cart_addr: u32, mut dram_addr: u32, length: u32) {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= SRAM_MASK as u32;
|
||||
let mut i = dram_addr;
|
||||
let mut j = cart_addr;
|
||||
|
||||
while i < dram_addr + length {
|
||||
*device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.sram
|
||||
.data
|
||||
.get_mut(j as usize)
|
||||
.unwrap_or(&mut 0) = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get(i as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Sram);
|
||||
}
|
||||
|
||||
fn dma_read_flash(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) {
|
||||
if (cart_addr & 0x1ffff) == 0x00000
|
||||
&& length == 128
|
||||
&& device.cart.flashram.mode == FlashramMode::PageProgram
|
||||
{
|
||||
/* load page buf using DMA */
|
||||
for i in 0..length {
|
||||
device.cart.flashram.page_buf[i as usize] = *device
|
||||
.rdram
|
||||
.mem
|
||||
.get((dram_addr + i) as usize ^ device.byte_swap)
|
||||
.unwrap_or(&0);
|
||||
}
|
||||
} else {
|
||||
/* other accesses are not implemented */
|
||||
panic!("unknown flash dma read")
|
||||
}
|
||||
}
|
||||
|
||||
// cart is big endian, rdram is native endian
|
||||
pub fn dma_read(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 {
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Sram)
|
||||
{
|
||||
dma_read_sram(device, cart_addr, dram_addr, length)
|
||||
} else {
|
||||
dma_read_flash(device, cart_addr, dram_addr, length)
|
||||
}
|
||||
device::pi::calculate_cycles(device, 2, length)
|
||||
}
|
||||
|
||||
fn dma_write_sram(
|
||||
device: &mut device::Device,
|
||||
mut cart_addr: u32,
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= SRAM_MASK as u32;
|
||||
let mut i = dram_addr;
|
||||
let mut j = cart_addr;
|
||||
|
||||
while i < dram_addr + length {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(i as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = *device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.sram
|
||||
.data
|
||||
.get(j as usize)
|
||||
.unwrap_or(&0);
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn dma_write_flash(
|
||||
device: &mut device::Device,
|
||||
mut cart_addr: u32,
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
|
||||
if (cart_addr & 0x1ffff) == 0x00000
|
||||
&& length == 8
|
||||
&& device.cart.flashram.mode == FlashramMode::ReadSiliconId
|
||||
{
|
||||
/* read Silicon ID using DMA */
|
||||
device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(dram_addr as usize..dram_addr as usize + 4)
|
||||
.unwrap_or(&mut [0; 4])
|
||||
.copy_from_slice(&device.cart.flashram.silicon_id[0].to_ne_bytes());
|
||||
dram_addr += 4;
|
||||
device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(dram_addr as usize..dram_addr as usize + 4)
|
||||
.unwrap_or(&mut [0; 4])
|
||||
.copy_from_slice(&device.cart.flashram.silicon_id[1].to_ne_bytes());
|
||||
} else if (cart_addr & 0x1ffff) < 0x10000
|
||||
&& device.cart.flashram.mode == FlashramMode::ReadArray
|
||||
{
|
||||
/* adjust flashram address before starting DMA. */
|
||||
if device.cart.flashram.silicon_id[1] == MX29L1100_ID
|
||||
|| device.cart.flashram.silicon_id[1] == MX29L0000_ID
|
||||
|| device.cart.flashram.silicon_id[1] == MX29L0001_ID
|
||||
{
|
||||
/* "old" flash needs special address adjusting */
|
||||
cart_addr = (cart_addr & 0xffff) * 2;
|
||||
} else {
|
||||
/* "new" flash doesn't require special address adjusting at DMA start. */
|
||||
cart_addr &= 0xffff;
|
||||
}
|
||||
|
||||
/* do actual DMA */
|
||||
for i in 0..length {
|
||||
*device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut((dram_addr + i) as usize ^ device.byte_swap)
|
||||
.unwrap_or(&mut 0) = device.ui.storage.saves.flash.data[(cart_addr + i) as usize];
|
||||
}
|
||||
} else {
|
||||
/* other accesses are not implemented */
|
||||
panic!("unknown flash dma write")
|
||||
}
|
||||
}
|
||||
|
||||
// cart is big endian, rdram is native endian
|
||||
pub fn dma_write(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 {
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Sram)
|
||||
{
|
||||
dma_write_sram(device, cart_addr, dram_addr, length)
|
||||
} else {
|
||||
dma_write_flash(device, cart_addr, dram_addr, length)
|
||||
}
|
||||
device::pi::calculate_cycles(device, 2, length)
|
||||
}
|
||||
|
||||
fn flashram_command(device: &mut device::Device, command: u32) {
|
||||
match command & 0xff000000 {
|
||||
0x3c000000 => {
|
||||
/* set chip erase mode */
|
||||
device.cart.flashram.mode = FlashramMode::ChipErase;
|
||||
}
|
||||
|
||||
0x4b000000 => {
|
||||
/* set sector erase mode, set erase sector */
|
||||
device.cart.flashram.mode = FlashramMode::SectorErase;
|
||||
device.cart.flashram.erase_page = command as u16;
|
||||
}
|
||||
|
||||
0x78000000 => {
|
||||
/* set erase busy flag */
|
||||
device.cart.flashram.status |= 0x02;
|
||||
|
||||
/* do chip/sector erase */
|
||||
if device.cart.flashram.mode == FlashramMode::SectorErase {
|
||||
let offset: usize = (device.cart.flashram.erase_page & 0xff80) as usize * 128;
|
||||
for i in 0..128 * 128 {
|
||||
device.ui.storage.saves.flash.data[offset + i] = 0xFF;
|
||||
}
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Flash);
|
||||
} else if device.cart.flashram.mode == FlashramMode::ChipErase {
|
||||
for i in 0..FLASHRAM_SIZE {
|
||||
device.ui.storage.saves.flash.data[i] = 0xFF;
|
||||
}
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Flash);
|
||||
} else {
|
||||
panic!("Unexpected flash erase command")
|
||||
}
|
||||
|
||||
/* clear erase busy flag, set erase success flag, transition to status mode */
|
||||
device.cart.flashram.status &= !0x02;
|
||||
device.cart.flashram.status |= 0x08;
|
||||
device.cart.flashram.mode = FlashramMode::Status;
|
||||
}
|
||||
|
||||
0xa5000000 => {
|
||||
/* set program busy flag */
|
||||
device.cart.flashram.status |= 0x01;
|
||||
|
||||
/* program selected page */
|
||||
let offset: usize = (command & 0xffff) as usize * 128;
|
||||
for i in 0..128 {
|
||||
device.ui.storage.saves.flash.data[offset + i] = device.cart.flashram.page_buf[i];
|
||||
}
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Flash);
|
||||
|
||||
/* clear program busy flag, set program success flag, transition to status mode */
|
||||
device.cart.flashram.status &= !0x01;
|
||||
device.cart.flashram.status |= 0x04;
|
||||
device.cart.flashram.mode = FlashramMode::Status;
|
||||
}
|
||||
|
||||
0xb4000000 => {
|
||||
/* set page program mode */
|
||||
device.cart.flashram.mode = FlashramMode::PageProgram;
|
||||
}
|
||||
|
||||
0xd2000000 => {
|
||||
/* set status mode */
|
||||
device.cart.flashram.mode = FlashramMode::Status;
|
||||
}
|
||||
|
||||
0xe1000000 => {
|
||||
/* set silicon_id mode */
|
||||
device.cart.flashram.mode = FlashramMode::ReadSiliconId;
|
||||
device.cart.flashram.status |= 0x01; /* Needed for Pokemon Puzzle League */
|
||||
}
|
||||
|
||||
0xf0000000 => {
|
||||
/* set read mode */
|
||||
device.cart.flashram.mode = FlashramMode::ReadArray;
|
||||
}
|
||||
|
||||
_ => {
|
||||
panic!("unknown flash command")
|
||||
}
|
||||
}
|
||||
}
|
||||
217
src/device/controller.rs
Normal file
@ -0,0 +1,217 @@
|
||||
use crate::{device, savestates, ui};
|
||||
|
||||
pub mod gbcart;
|
||||
pub mod mempak;
|
||||
pub mod rumble;
|
||||
pub mod transferpak;
|
||||
pub mod vru;
|
||||
|
||||
pub const JCMD_STATUS: u8 = 0x00;
|
||||
pub const JCMD_CONTROLLER_READ: u8 = 0x01;
|
||||
const JCMD_PAK_READ: u8 = 0x02;
|
||||
const JCMD_PAK_WRITE: u8 = 0x03;
|
||||
pub const JCMD_RESET: u8 = 0xff;
|
||||
|
||||
//const JDT_NONE: u16 = 0x0000;
|
||||
const JDT_JOY_ABS_COUNTERS: u16 = 0x0001; /* joystick with absolute coordinates */
|
||||
//const JDT_JOY_REL_COUNTERS: u16 = 0x0002; /* joystick with relative coordinates (= mouse) */
|
||||
const JDT_JOY_PORT: u16 = 0x0004; /* has port for external paks */
|
||||
const PAK_CHUNK_SIZE: usize = 0x20;
|
||||
const CONT_STATUS_PAK_PRESENT: u8 = 1;
|
||||
const CONT_STATUS_PAK_NOT_PRESENT: u8 = 2;
|
||||
const CONT_FLAVOR: u16 = JDT_JOY_ABS_COUNTERS | JDT_JOY_PORT;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PakType {
|
||||
None = 0,
|
||||
MemPak = 1,
|
||||
RumblePak = 2,
|
||||
TransferPak = 3,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PakHandler {
|
||||
#[serde(skip, default = "savestates::default_pak_handler")]
|
||||
pub read: fn(&mut device::Device, usize, u16, usize, usize),
|
||||
#[serde(skip, default = "savestates::default_pak_handler")]
|
||||
pub write: fn(&mut device::Device, usize, u16, usize, usize),
|
||||
pub pak_type: PakType,
|
||||
}
|
||||
|
||||
pub fn process(device: &mut device::Device, channel: usize) {
|
||||
let cmd = device.pif.ram[device.pif.channels[channel].tx_buf.unwrap()];
|
||||
|
||||
match cmd {
|
||||
JCMD_RESET | JCMD_STATUS => {
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = CONT_FLAVOR as u8;
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] =
|
||||
(CONT_FLAVOR >> 8) as u8;
|
||||
if device.pif.channels[channel].pak_handler.is_none() {
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] =
|
||||
CONT_STATUS_PAK_NOT_PRESENT;
|
||||
} else {
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] =
|
||||
CONT_STATUS_PAK_PRESENT;
|
||||
}
|
||||
}
|
||||
JCMD_CONTROLLER_READ => {
|
||||
let offset = device.pif.channels[channel].rx_buf.unwrap();
|
||||
let input = if let Some(netplay) = &mut device.netplay {
|
||||
netplay.inputs[channel].0
|
||||
} else {
|
||||
ui::input::get(&mut device.ui, channel)
|
||||
};
|
||||
|
||||
device.pif.ram[offset..offset + 4].copy_from_slice(&input.data.to_ne_bytes());
|
||||
if input.pak_change_pressed {
|
||||
// pak change button pressed
|
||||
if device::events::get_event(device, device::events::EVENT_TYPE_PAK).is_none() {
|
||||
device.pif.channels[channel].change_pak =
|
||||
device.pif.channels[channel].pak_handler.unwrap().pak_type;
|
||||
device.pif.channels[channel].pak_handler = None;
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_PAK,
|
||||
device.cpu.clock_rate / 2, // 500ms
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
JCMD_PAK_READ => pak_read_block(
|
||||
device,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
||||
device.pif.channels[channel].rx_buf.unwrap(),
|
||||
device.pif.channels[channel].rx_buf.unwrap() + 32,
|
||||
channel,
|
||||
),
|
||||
JCMD_PAK_WRITE => pak_write_block(
|
||||
device,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
||||
device.pif.channels[channel].tx_buf.unwrap() + 3,
|
||||
device.pif.channels[channel].rx_buf.unwrap(),
|
||||
channel,
|
||||
),
|
||||
_ => eprintln!("unknown controller command {cmd}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn pak_read_block(
|
||||
device: &mut device::Device,
|
||||
addr_acrc: usize,
|
||||
data: usize,
|
||||
dcrc: usize,
|
||||
channel: usize,
|
||||
) {
|
||||
let address =
|
||||
((device.pif.ram[addr_acrc] as u16) << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0) as u16;
|
||||
let handler = device.pif.channels[channel].pak_handler;
|
||||
|
||||
if let Some(handler) = handler {
|
||||
(handler.read)(device, channel, address, data, PAK_CHUNK_SIZE);
|
||||
device.pif.ram[dcrc] = data_crc(device, data, PAK_CHUNK_SIZE)
|
||||
} else {
|
||||
device.pif.ram[dcrc] = !data_crc(device, data, PAK_CHUNK_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
fn pak_write_block(
|
||||
device: &mut device::Device,
|
||||
addr_acrc: usize,
|
||||
data: usize,
|
||||
dcrc: usize,
|
||||
channel: usize,
|
||||
) {
|
||||
let address =
|
||||
((device.pif.ram[addr_acrc] as u16) << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0) as u16;
|
||||
let handler = device.pif.channels[channel].pak_handler;
|
||||
|
||||
if let Some(handler) = handler {
|
||||
(handler.write)(device, channel, address, data, PAK_CHUNK_SIZE);
|
||||
device.pif.ram[dcrc] = data_crc(device, data, PAK_CHUNK_SIZE)
|
||||
} else {
|
||||
device.pif.ram[dcrc] = !data_crc(device, data, PAK_CHUNK_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_crc(device: &device::Device, data_offset: usize, size: usize) -> u8 {
|
||||
let mut i = 0;
|
||||
let mut crc = 0;
|
||||
|
||||
while i <= size {
|
||||
let mut mask = 0x80;
|
||||
while mask >= 1 {
|
||||
let xor_tap = if crc & 0x80 != 0 { 0x85 } else { 0x00 };
|
||||
crc <<= 1;
|
||||
if i != size && (device.pif.ram[data_offset + i] & mask) != 0 {
|
||||
crc |= 1;
|
||||
}
|
||||
crc ^= xor_tap;
|
||||
mask >>= 1
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
crc
|
||||
}
|
||||
|
||||
pub fn pak_switch_event(device: &mut device::Device) {
|
||||
for (i, channel) in device.pif.channels.iter_mut().enumerate() {
|
||||
if channel.change_pak != PakType::None {
|
||||
//stop rumble if it is on
|
||||
if let Some(netplay) = &device.netplay {
|
||||
if netplay.player_number == i {
|
||||
device::ui::input::set_rumble(&device.ui, 0, 0);
|
||||
}
|
||||
} else {
|
||||
device::ui::input::set_rumble(&device.ui, i, 0);
|
||||
}
|
||||
|
||||
let new_pak_type = match channel.change_pak {
|
||||
PakType::MemPak => PakType::RumblePak,
|
||||
PakType::RumblePak => {
|
||||
if device.transferpaks[i].cart.rom.is_empty() {
|
||||
PakType::MemPak
|
||||
} else {
|
||||
PakType::TransferPak
|
||||
}
|
||||
}
|
||||
PakType::TransferPak => PakType::MemPak,
|
||||
_ => {
|
||||
panic!("Invalid pak type");
|
||||
}
|
||||
};
|
||||
|
||||
if new_pak_type == PakType::MemPak {
|
||||
channel.pak_handler = Some(device::controller::PakHandler {
|
||||
read: device::controller::mempak::read,
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
ui::video::onscreen_message(
|
||||
&format!("P{} MemPak selected", i + 1),
|
||||
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
||||
);
|
||||
} else if new_pak_type == PakType::RumblePak {
|
||||
channel.pak_handler = Some(device::controller::PakHandler {
|
||||
read: device::controller::rumble::read,
|
||||
write: device::controller::rumble::write,
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
ui::video::onscreen_message(
|
||||
&format!("P{} RumblePak selected", i + 1),
|
||||
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
||||
);
|
||||
} else if new_pak_type == PakType::TransferPak {
|
||||
channel.pak_handler = Some(device::controller::PakHandler {
|
||||
read: device::controller::transferpak::read,
|
||||
write: device::controller::transferpak::write,
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
ui::video::onscreen_message(
|
||||
&format!("P{} TransferPak selected", i + 1),
|
||||
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
||||
);
|
||||
}
|
||||
channel.change_pak = PakType::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
430
src/device/controller/gbcart.rs
Normal file
@ -0,0 +1,430 @@
|
||||
use crate::device;
|
||||
|
||||
use std::io::Write;
|
||||
const MBC3_RTC_SECONDS: usize = 0;
|
||||
const MBC3_RTC_MINUTES: usize = 1;
|
||||
const MBC3_RTC_HOURS: usize = 2;
|
||||
const MBC3_RTC_DAYS_L: usize = 3;
|
||||
const MBC3_RTC_DAYS_H: usize = 4;
|
||||
const MBC3_RTC_REGS_COUNT: usize = 5;
|
||||
|
||||
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct GbCart {
|
||||
pub enabled: bool,
|
||||
#[serde(skip)]
|
||||
pub rom: Vec<u8>,
|
||||
pub ram: Vec<u8>,
|
||||
pub cart_type: device::controller::gbcart::CartType,
|
||||
pub ram_enabled: bool,
|
||||
pub mbc1_mode: bool,
|
||||
pub ram_bank: u16,
|
||||
pub rom_bank: u32,
|
||||
pub rtc_regs: [u8; MBC3_RTC_REGS_COUNT],
|
||||
pub rtc_regs_latch: [u8; MBC3_RTC_REGS_COUNT],
|
||||
pub last_elapsed_time: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum CartType {
|
||||
#[default]
|
||||
None,
|
||||
MBC1RamBatt,
|
||||
MBC3RamBatt,
|
||||
MBC3RamBattRtc,
|
||||
MBC5RamBatt,
|
||||
}
|
||||
|
||||
pub fn init(gb_cart: &mut device::controller::gbcart::GbCart, rom: &[u8], ram: &[u8]) {
|
||||
gb_cart.rom = rom.to_vec();
|
||||
let offset = match ram.len() % 8192 {
|
||||
44 => {
|
||||
gb_cart.ram = ram[..ram.len() - 44].to_vec();
|
||||
ram.len() - 44
|
||||
}
|
||||
48 => {
|
||||
gb_cart.ram = ram[..ram.len() - 48].to_vec();
|
||||
ram.len() - 48
|
||||
}
|
||||
_ => {
|
||||
gb_cart.ram = ram.to_vec();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
gb_cart.rtc_regs[MBC3_RTC_SECONDS] =
|
||||
u32::from_le_bytes(ram[offset..offset + 4].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs[MBC3_RTC_MINUTES] =
|
||||
u32::from_le_bytes(ram[offset + 4..offset + 8].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs[MBC3_RTC_HOURS] =
|
||||
u32::from_le_bytes(ram[offset + 8..offset + 12].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs[MBC3_RTC_DAYS_L] =
|
||||
u32::from_le_bytes(ram[offset + 12..offset + 16].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs[MBC3_RTC_DAYS_H] =
|
||||
u32::from_le_bytes(ram[offset + 16..offset + 20].try_into().unwrap()) as u8;
|
||||
|
||||
gb_cart.rtc_regs_latch[MBC3_RTC_SECONDS] =
|
||||
u32::from_le_bytes(ram[offset + 20..offset + 24].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs_latch[MBC3_RTC_MINUTES] =
|
||||
u32::from_le_bytes(ram[offset + 24..offset + 28].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs_latch[MBC3_RTC_HOURS] =
|
||||
u32::from_le_bytes(ram[offset + 28..offset + 32].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_L] =
|
||||
u32::from_le_bytes(ram[offset + 32..offset + 36].try_into().unwrap()) as u8;
|
||||
gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_H] =
|
||||
u32::from_le_bytes(ram[offset + 36..offset + 40].try_into().unwrap()) as u8;
|
||||
}
|
||||
|
||||
pub fn save(
|
||||
gb_cart: &mut device::controller::gbcart::GbCart,
|
||||
elapsed_time: i64,
|
||||
ram_path: &std::string::String,
|
||||
) {
|
||||
if cfg!(target_os = "android") {
|
||||
return;
|
||||
}
|
||||
update_rtc_regs(gb_cart, elapsed_time);
|
||||
|
||||
if let Ok(file) = std::fs::File::create(ram_path) {
|
||||
let mut f = std::io::BufWriter::new(file);
|
||||
f.write_all(&gb_cart.ram).unwrap();
|
||||
|
||||
if gb_cart.cart_type == CartType::MBC3RamBattRtc {
|
||||
f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_SECONDS] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_MINUTES] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_HOURS] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_DAYS_L] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_DAYS_H] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
|
||||
f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_SECONDS] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_MINUTES] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_HOURS] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_L] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_H] as u32).to_le_bytes())
|
||||
.unwrap();
|
||||
|
||||
let max = ((i32::MAX as i64) << 32) | (i32::MAX as i64);
|
||||
f.write_all(&max.to_le_bytes()).unwrap();
|
||||
|
||||
f.flush().unwrap();
|
||||
}
|
||||
} else {
|
||||
eprintln!("Error saving TransferPak RAM to {ram_path}");
|
||||
}
|
||||
}
|
||||
|
||||
fn update_rtc_regs(cart: &mut device::controller::gbcart::GbCart, elapsed_time: i64) {
|
||||
let mut diff = elapsed_time - cart.last_elapsed_time;
|
||||
cart.last_elapsed_time = elapsed_time;
|
||||
|
||||
if diff > 0 {
|
||||
cart.rtc_regs[MBC3_RTC_SECONDS] += (diff % 60) as u8;
|
||||
if cart.rtc_regs[MBC3_RTC_SECONDS] >= 60 {
|
||||
cart.rtc_regs[MBC3_RTC_SECONDS] -= 60;
|
||||
cart.rtc_regs[MBC3_RTC_MINUTES] += 1;
|
||||
}
|
||||
diff /= 60;
|
||||
|
||||
cart.rtc_regs[MBC3_RTC_MINUTES] += (diff % 60) as u8;
|
||||
if cart.rtc_regs[MBC3_RTC_MINUTES] >= 60 {
|
||||
cart.rtc_regs[MBC3_RTC_MINUTES] -= 60;
|
||||
cart.rtc_regs[MBC3_RTC_HOURS] += 1;
|
||||
}
|
||||
diff /= 60;
|
||||
|
||||
cart.rtc_regs[MBC3_RTC_HOURS] += (diff % 24) as u8;
|
||||
if cart.rtc_regs[MBC3_RTC_HOURS] >= 24 {
|
||||
cart.rtc_regs[MBC3_RTC_HOURS] -= 24;
|
||||
cart.rtc_regs[MBC3_RTC_DAYS_L] += 1;
|
||||
}
|
||||
diff /= 24;
|
||||
|
||||
/* update days counter */
|
||||
let days = ((((cart.rtc_regs[MBC3_RTC_DAYS_H] & 0x01) as i64) << 8)
|
||||
| cart.rtc_regs[MBC3_RTC_DAYS_L] as i64)
|
||||
+ diff;
|
||||
|
||||
cart.rtc_regs[MBC3_RTC_DAYS_L] = (days & 0xff) as u8;
|
||||
cart.rtc_regs[MBC3_RTC_DAYS_H] &= !0x01;
|
||||
cart.rtc_regs[MBC3_RTC_DAYS_H] |= ((days >> 8) & 1) as u8;
|
||||
|
||||
/* set carry bit if days overflow */
|
||||
if days > 511 {
|
||||
cart.rtc_regs[MBC3_RTC_DAYS_H] |= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mbc1(
|
||||
pif_ram: &[u8],
|
||||
cart: &mut device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
) {
|
||||
let value = pif_ram[data + size - 1];
|
||||
if address < 0x2000 {
|
||||
cart.ram_enabled = value & 0xf == 0xa;
|
||||
} else if address < 0x4000 {
|
||||
let bank = value & 0x1f;
|
||||
cart.rom_bank = bank as u32;
|
||||
if cart.rom_bank == 0 {
|
||||
cart.rom_bank = 1;
|
||||
}
|
||||
} else if address < 0x6000 {
|
||||
cart.ram_bank = (value & 0x3) as u16;
|
||||
} else if address < 0x8000 {
|
||||
cart.mbc1_mode = (value & 0x1) != 0;
|
||||
} else if (0xa000..0xc000).contains(&address) {
|
||||
if !cart.ram_enabled {
|
||||
return;
|
||||
}
|
||||
if cart.mbc1_mode {
|
||||
let banked_address = address - 0xA000 + (cart.ram_bank * 0x2000);
|
||||
cart.ram[banked_address as usize..banked_address as usize + size]
|
||||
.copy_from_slice(&pif_ram[data..data + size]);
|
||||
} else {
|
||||
let banked_address = address - 0xA000;
|
||||
cart.ram[banked_address as usize..banked_address as usize + size]
|
||||
.copy_from_slice(&pif_ram[data..data + size]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_mbc1(
|
||||
pif_ram: &mut [u8],
|
||||
cart: &device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
) {
|
||||
if address < 0x4000 {
|
||||
let banked_address = address & 0x3FFF;
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
|
||||
} else if address < 0x8000 {
|
||||
let banked_address = address as u32 - 0x4000 + (cart.rom_bank * 0x4000);
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
|
||||
} else if (0xa000..0xc000).contains(&address) {
|
||||
if !cart.ram_enabled {
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = 0xff;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if cart.ram_bank > 3 {
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = 0;
|
||||
}
|
||||
} else {
|
||||
let banked_address = address - 0xA000 + (cart.ram_bank * 0x2000);
|
||||
pif_ram[data..data + size].copy_from_slice(
|
||||
&cart.ram[banked_address as usize..banked_address as usize + size],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mbc3(
|
||||
pif_ram: &[u8],
|
||||
cart: &mut device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
elapsed_time: i64,
|
||||
) {
|
||||
let value = pif_ram[data + size - 1];
|
||||
if address < 0x2000 {
|
||||
cart.ram_enabled = value & 0xf == 0xa;
|
||||
} else if address < 0x4000 {
|
||||
let bank = value & if cart.rom.len() > 2097152 { 0xff } else { 0x7f };
|
||||
cart.rom_bank = bank as u32;
|
||||
if cart.rom_bank == 0 {
|
||||
cart.rom_bank = 1;
|
||||
}
|
||||
} else if address < 0x6000 {
|
||||
cart.ram_bank = (value & 0xf) as u16;
|
||||
} else if address < 0x8000 {
|
||||
if cart.cart_type == CartType::MBC3RamBattRtc && (value & 0x1) != 0 {
|
||||
update_rtc_regs(cart, elapsed_time);
|
||||
cart.rtc_regs_latch = cart.rtc_regs;
|
||||
}
|
||||
} else if (0xa000..0xc000).contains(&address) {
|
||||
if !cart.ram_enabled {
|
||||
return;
|
||||
}
|
||||
if cart.ram_bank < 0x8 {
|
||||
let banked_address = address - 0xA000 + (cart.ram_bank * 0x2000);
|
||||
cart.ram[banked_address as usize..banked_address as usize + size]
|
||||
.copy_from_slice(&pif_ram[data..data + size]);
|
||||
} else if cart.cart_type == CartType::MBC3RamBattRtc {
|
||||
cart.rtc_regs[cart.ram_bank as usize - 0x8] = value;
|
||||
}
|
||||
} else {
|
||||
panic!("Unsupported write address {address:x}");
|
||||
}
|
||||
}
|
||||
|
||||
fn read_mbc3(
|
||||
pif_ram: &mut [u8],
|
||||
cart: &mut device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
) {
|
||||
if address < 0x4000 {
|
||||
let banked_address = address & 0x3FFF;
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
|
||||
} else if address < 0x8000 {
|
||||
let banked_address = address as u32 - 0x4000 + (cart.rom_bank * 0x4000);
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
|
||||
} else if (0xa000..0xc000).contains(&address) {
|
||||
if !cart.ram_enabled {
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = 0xff;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if cart.ram_bank < 0x8 {
|
||||
let banked_address = address - 0xA000 + (cart.ram_bank * 0x2000);
|
||||
pif_ram[data..data + size].copy_from_slice(
|
||||
cart.ram
|
||||
.get(banked_address as usize..banked_address as usize + size)
|
||||
.unwrap_or(&vec![0; size]),
|
||||
);
|
||||
} else if cart.cart_type == CartType::MBC3RamBattRtc {
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = cart.rtc_regs_latch[cart.ram_bank as usize - 0x8];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Unsupported read address {address:x}");
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mbc5(
|
||||
pif_ram: &[u8],
|
||||
cart: &mut device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
) {
|
||||
let value = pif_ram[data + size - 1];
|
||||
if address < 0x2000 {
|
||||
cart.ram_enabled = value & 0xf == 0xa;
|
||||
} else if address < 0x3000 {
|
||||
cart.rom_bank &= 0xff00;
|
||||
cart.rom_bank |= value as u32;
|
||||
} else if address < 0x4000 {
|
||||
cart.rom_bank &= 0x00ff;
|
||||
cart.rom_bank |= ((value & 0x1) as u32) << 8;
|
||||
} else if address < 0x6000 {
|
||||
cart.ram_bank = (value & 0xf) as u16;
|
||||
} else if address < 0xa000 {
|
||||
eprintln!("Unknown MBC5 write address {address:x}");
|
||||
} else if (0xa000..0xc000).contains(&address) {
|
||||
if !cart.ram_enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
let banked_address = address - 0xA000 + (cart.ram_bank << 13);
|
||||
cart.ram[banked_address as usize..banked_address as usize + size]
|
||||
.copy_from_slice(&pif_ram[data..data + size]);
|
||||
} else {
|
||||
panic!("Unsupported write address {address:x}");
|
||||
}
|
||||
}
|
||||
|
||||
fn read_mbc5(
|
||||
pif_ram: &mut [u8],
|
||||
cart: &device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
) {
|
||||
if address < 0x4000 {
|
||||
let banked_address = address & 0x3FFF;
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
|
||||
} else if address < 0x8000 {
|
||||
let banked_address = address as u32 - 0x4000 + (cart.rom_bank << 14);
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
|
||||
} else if (0xa000..0xc000).contains(&address) {
|
||||
if !cart.ram_enabled {
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = 0xff;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let banked_address = address - 0xA000 + (cart.ram_bank << 13);
|
||||
pif_ram[data..data + size]
|
||||
.copy_from_slice(&cart.ram[banked_address as usize..banked_address as usize + size]);
|
||||
} else {
|
||||
panic!("Unsupported read address {address:x}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
pif_ram: &mut [u8],
|
||||
cart: &mut device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
) {
|
||||
if !cart.enabled {
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = 0x00;
|
||||
}
|
||||
return;
|
||||
}
|
||||
match cart.cart_type {
|
||||
CartType::MBC1RamBatt => read_mbc1(pif_ram, cart, address, data, size),
|
||||
CartType::MBC3RamBatt => read_mbc3(pif_ram, cart, address, data, size),
|
||||
CartType::MBC3RamBattRtc => read_mbc3(pif_ram, cart, address, data, size),
|
||||
CartType::MBC5RamBatt => read_mbc5(pif_ram, cart, address, data, size),
|
||||
_ => panic!("Unsupported cart type"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
pif_ram: &[u8],
|
||||
cart: &mut device::controller::gbcart::GbCart,
|
||||
address: u16,
|
||||
data: usize,
|
||||
size: usize,
|
||||
elapsed_time: i64,
|
||||
) {
|
||||
if !cart.enabled {
|
||||
return;
|
||||
}
|
||||
match cart.cart_type {
|
||||
CartType::MBC1RamBatt => write_mbc1(pif_ram, cart, address, data, size),
|
||||
CartType::MBC3RamBatt => write_mbc3(pif_ram, cart, address, data, size, elapsed_time),
|
||||
CartType::MBC3RamBattRtc => write_mbc3(pif_ram, cart, address, data, size, elapsed_time),
|
||||
CartType::MBC5RamBatt => write_mbc5(pif_ram, cart, address, data, size),
|
||||
_ => panic!("Unsupported cart type"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cart_type(data: u8) -> CartType {
|
||||
match data {
|
||||
0x03 => CartType::MBC1RamBatt,
|
||||
0x10 => CartType::MBC3RamBattRtc,
|
||||
0x13 => CartType::MBC3RamBatt,
|
||||
0x1b => CartType::MBC5RamBatt,
|
||||
_ => panic!("Unsupported cart type {data:x}"),
|
||||
}
|
||||
}
|
||||
103
src/device/controller/mempak.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub const MEMPAK_SIZE: usize = 0x8000;
|
||||
const MPK_PAGE_SIZE: usize = 256;
|
||||
|
||||
pub fn format_mempak(device: &mut device::Device) {
|
||||
if device.ui.storage.saves.mempak.data.len() < MEMPAK_SIZE * 4 {
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.mempak
|
||||
.data
|
||||
.resize(MEMPAK_SIZE * 4, 0);
|
||||
|
||||
let page_0: [u8; MPK_PAGE_SIZE] = [
|
||||
/* Label area */
|
||||
0x81, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, /* Main ID area */
|
||||
0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff,
|
||||
0x66, 0x25, 0x99, 0xcd, /* Unused */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, /* ID area backup #1 */
|
||||
0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff,
|
||||
0x66, 0x25, 0x99, 0xcd, /* ID area backup #2 */
|
||||
0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff,
|
||||
0x66, 0x25, 0x99, 0xcd, /* Unused */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, /* ID area backup #3 */
|
||||
0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff,
|
||||
0x66, 0x25, 0x99, 0xcd, /* Unused */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
for i in 0..4 {
|
||||
let offset = i * MEMPAK_SIZE;
|
||||
if device.ui.storage.saves.mempak.data[offset..offset + MEMPAK_SIZE] != [0; MEMPAK_SIZE]
|
||||
{
|
||||
// don't overwrite existing data
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill Page 0 with pre-initialized content */
|
||||
device.ui.storage.saves.mempak.data[offset..offset + MPK_PAGE_SIZE]
|
||||
.copy_from_slice(&page_0);
|
||||
|
||||
/* Fill INODE page 1 and update it's checkum */
|
||||
let start_page = 5;
|
||||
for i in MPK_PAGE_SIZE..2 * start_page {
|
||||
device.ui.storage.saves.mempak.data[offset + i] = 0;
|
||||
}
|
||||
for i in (MPK_PAGE_SIZE + 2 * start_page..2 * MPK_PAGE_SIZE).step_by(2) {
|
||||
device.ui.storage.saves.mempak.data[offset + i] = 0x00;
|
||||
device.ui.storage.saves.mempak.data[offset + i + 1] = 0x03;
|
||||
}
|
||||
device.ui.storage.saves.mempak.data[offset + (MPK_PAGE_SIZE + 1)] = 0x71;
|
||||
|
||||
/* Page 2 is identical to page 1 */
|
||||
let page1 = offset + MPK_PAGE_SIZE;
|
||||
let page2 = offset + 2 * MPK_PAGE_SIZE;
|
||||
let page1data =
|
||||
device.ui.storage.saves.mempak.data[page1..page1 + MPK_PAGE_SIZE].to_vec();
|
||||
device.ui.storage.saves.mempak.data[page2..page2 + MPK_PAGE_SIZE]
|
||||
.copy_from_slice(&page1data);
|
||||
|
||||
/* Remaining pages DIR+DATA (3...) are initialized with 0x00 */
|
||||
for i in 3 * MPK_PAGE_SIZE..MEMPAK_SIZE - 3 * MPK_PAGE_SIZE {
|
||||
device.ui.storage.saves.mempak.data[offset + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
|
||||
if (address as usize) < MEMPAK_SIZE {
|
||||
let offset = (channel * MEMPAK_SIZE) + address as usize;
|
||||
device.pif.ram[data..data + size]
|
||||
.copy_from_slice(&device.ui.storage.saves.mempak.data[offset..offset + size])
|
||||
} else {
|
||||
for i in 0..size {
|
||||
device.pif.ram[data + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
|
||||
if (address as usize) < MEMPAK_SIZE {
|
||||
let offset = (channel * MEMPAK_SIZE) + address as usize;
|
||||
device.ui.storage.saves.mempak.data[offset..offset + size]
|
||||
.copy_from_slice(&device.pif.ram[data..data + size]);
|
||||
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Mempak);
|
||||
}
|
||||
}
|
||||
27
src/device/controller/rumble.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::device;
|
||||
|
||||
pub fn read(device: &mut device::Device, _channel: usize, address: u16, data: usize, size: usize) {
|
||||
let value: u8 = if (0x8000..0x9000).contains(&address) {
|
||||
0x80
|
||||
} else {
|
||||
0x00
|
||||
};
|
||||
|
||||
for i in 0..size {
|
||||
device.pif.ram[data + i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
|
||||
if address == 0xc000 {
|
||||
let rumble = device.pif.ram[data + size - 1];
|
||||
|
||||
if let Some(netplay) = &device.netplay {
|
||||
if netplay.player_number == channel {
|
||||
device::ui::input::set_rumble(&device.ui, 0, rumble);
|
||||
}
|
||||
} else {
|
||||
device::ui::input::set_rumble(&device.ui, channel, rumble);
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/device/controller/transferpak.rs
Normal file
@ -0,0 +1,134 @@
|
||||
use crate::device;
|
||||
|
||||
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TransferPak {
|
||||
pub enabled: bool,
|
||||
pub reset_state: u8,
|
||||
pub bank: u16,
|
||||
pub cart: device::controller::gbcart::GbCart,
|
||||
}
|
||||
|
||||
pub fn read(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
|
||||
let pak = &mut device.transferpaks[channel];
|
||||
|
||||
match address >> 12 {
|
||||
0x8 => {
|
||||
let value = if pak.enabled { 0x84 } else { 0x00 };
|
||||
for i in 0..size {
|
||||
device.pif.ram[data + i] = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
if !pak.enabled {
|
||||
for i in 0..size {
|
||||
device.pif.ram[data + i] = 0x00;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match address >> 12 {
|
||||
0xB => {
|
||||
let mut value = 0;
|
||||
if pak.cart.enabled {
|
||||
value |= 1 << 0;
|
||||
}
|
||||
value |= (pak.reset_state & 3) << 2;
|
||||
if pak.enabled {
|
||||
value |= 1 << 7;
|
||||
}
|
||||
|
||||
if pak.cart.enabled && pak.reset_state == 3 {
|
||||
pak.reset_state = 2
|
||||
} else if !pak.cart.enabled && pak.reset_state == 2 {
|
||||
pak.reset_state = 1
|
||||
} else if !pak.cart.enabled && pak.reset_state == 1 {
|
||||
pak.reset_state = 0
|
||||
}
|
||||
for i in 0..size {
|
||||
device.pif.ram[data + i] = value;
|
||||
}
|
||||
}
|
||||
0xC..=0xF => {
|
||||
device::controller::gbcart::read(
|
||||
&mut device.pif.ram,
|
||||
&mut pak.cart,
|
||||
0x4000 * pak.bank + (address & 0x7fff) - 0x4000,
|
||||
data,
|
||||
size,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
eprintln!("unknown transfer pak read {:x}", address >> 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
|
||||
let pak = &mut device.transferpaks[channel];
|
||||
|
||||
let value = device.pif.ram[data + size - 1];
|
||||
match address >> 12 {
|
||||
0x8 => {
|
||||
match value {
|
||||
0xFE => {
|
||||
pak.enabled = false;
|
||||
}
|
||||
0x84 => {
|
||||
if !pak.enabled {
|
||||
pak.bank = 3;
|
||||
pak.cart.enabled = false;
|
||||
pak.reset_state = 0;
|
||||
}
|
||||
pak.enabled = true;
|
||||
}
|
||||
_ => eprintln!("unknown transfer pak value"),
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
if !pak.enabled {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match address >> 12 {
|
||||
0xA => {
|
||||
pak.bank = value as u16;
|
||||
if pak.bank > 3 {
|
||||
pak.bank = 0;
|
||||
}
|
||||
}
|
||||
0xB => {
|
||||
if value & 1 != 0 {
|
||||
if !pak.cart.enabled {
|
||||
pak.reset_state = 3;
|
||||
pak.cart.rom_bank = 1;
|
||||
pak.cart.ram_bank = 0;
|
||||
pak.cart.ram_enabled = false;
|
||||
pak.cart.cart_type =
|
||||
device::controller::gbcart::get_cart_type(pak.cart.rom[0x147]);
|
||||
}
|
||||
pak.cart.enabled = true;
|
||||
} else {
|
||||
pak.cart.enabled = false;
|
||||
}
|
||||
}
|
||||
0xC..=0xF => {
|
||||
device::controller::gbcart::write(
|
||||
&device.pif.ram,
|
||||
&mut pak.cart,
|
||||
0x4000 * pak.bank + (address & 0x7fff) - 0x4000,
|
||||
data,
|
||||
size,
|
||||
device.vi.elapsed_time as i64,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
eprintln!("unknown transfer pak write {:x}", address >> 12);
|
||||
}
|
||||
}
|
||||
}
|
||||