mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 09:35:21 +02:00
Compare commits
118 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b6f9b76a6 | |||
| 5a838464a4 | |||
| 383b568012 | |||
| 27bb66df4c | |||
| 33f74e5062 | |||
| 68f90e1cd7 | |||
| cc5f636912 | |||
| e3a9849ad7 | |||
| 7ed6867691 | |||
| 11a8e96564 | |||
| 7111cc9761 | |||
| b4832b941f | |||
| 82b1888f82 | |||
| f35869190b | |||
| 64eda1a1e3 | |||
| 8026bf13d6 | |||
| 83103130e7 | |||
| 87717c4c7e | |||
| 6355885a56 | |||
| 5d180dfb65 | |||
| fd6965ff3b | |||
| 2fcf202a5c | |||
| 93630c4156 | |||
| a66e8184e4 | |||
| e4f05af38d | |||
| 8a09cb711f | |||
| a58eed7dc9 | |||
| 09ba7e4d13 | |||
| 7316e41c7e | |||
| 1aa00de1b2 | |||
| f71ec9d54a | |||
| bf2734e59c | |||
| 7ee26961f1 | |||
| 5d692318fe | |||
| f2966230f9 | |||
| 48575f9c52 | |||
| dbab58f4a7 | |||
| f6b0b59bbc | |||
| ca61695dd1 | |||
| a182fbb1f9 | |||
| da9b2247f8 | |||
| 1bcadb3a8b | |||
| 433f08c7e0 | |||
| b07dde8768 | |||
| cba7bc7477 | |||
| ea6f203e33 | |||
| e48ad9bdff | |||
| 11b1137412 | |||
| 5528f233c0 | |||
| 9d18673257 | |||
| 2a3fa03b35 | |||
| ec2f47e5d3 | |||
| 139d66eb82 | |||
| 6e10bee216 | |||
| dff9fc5381 | |||
| 84dcc6c154 | |||
| 8aa23e1665 | |||
| f2b75a2dcc | |||
| 5bac846bda | |||
| e93c275795 | |||
| 67a8bb5e4c | |||
| 2828accee1 | |||
| 16a9754bda | |||
| fbe95767f5 | |||
| 0989be1e0f | |||
| e73816541e | |||
| f6b2342e0e | |||
| 7ab57d592b | |||
| a2cda03c8d | |||
| a07237aa64 | |||
| fdc17f4ae5 | |||
| 4802f3b71a | |||
| a4c505c72d | |||
| 30cb3f8339 | |||
| a5f5867ebb | |||
| ee8e34e17c | |||
| da1e686c1a | |||
| 5a3609d501 | |||
| 7b51ca82eb | |||
| ee77e75855 | |||
| ff093a9f7f | |||
| f7cb0db348 | |||
| eb9f37f1f5 | |||
| 37b902eb31 | |||
| 00964f06f0 | |||
| 319d7c8553 | |||
| 82b4adb4ad | |||
| 8fbc63a677 | |||
| 48ee50dea3 | |||
| 34a09925b4 | |||
| 168347b46e | |||
| 95a3d3d5ec | |||
| f6349f13da | |||
| fb2cf7fcb9 | |||
| 9980302d37 | |||
| 4692e2bcb9 | |||
| 04390301c9 | |||
| bc415821d6 | |||
| a068363787 | |||
| 97839fae40 | |||
| b6c805a27d | |||
| b7509a2425 | |||
| 6ecedde329 | |||
| 09dfd69283 | |||
| d262567084 | |||
| c363e78fbb | |||
| 792a3a6476 | |||
| 5bdb54a911 | |||
| b01c2d1885 | |||
| bb2da243b3 | |||
| 938a99caa9 | |||
| f7bfcfccba | |||
| 656e390171 | |||
| 17990dfebc | |||
| 671a961789 | |||
| aac68a4a1b | |||
| dc5a78a75e | |||
| 91a5483452 |
@@ -18,8 +18,10 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
arch: linux-x86_64
|
||||
- os: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-gnu
|
||||
arch: linux-aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -43,18 +45,22 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --release -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/release/gopher64 output/gopher64-${{ matrix.arch }}
|
||||
- name: Upload file
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gopher64-${{ matrix.target }}
|
||||
path: target/${{ matrix.target }}/release/gopher64
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: output/gopher64-${{ matrix.arch }}
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-2025
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [x86_64-pc-windows-msvc]
|
||||
include:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
arch: windows-x86_64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -66,18 +72,24 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --release -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/release/gopher64.exe output/gopher64-${{ matrix.arch }}.exe
|
||||
- name: Upload file
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gopher64-${{ matrix.target }}
|
||||
path: target/${{ matrix.target }}/release/gopher64.exe
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: output/gopher64-${{ matrix.arch }}.exe
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [x86_64-apple-darwin, aarch64-apple-darwin]
|
||||
include:
|
||||
- target: x86_64-apple-darwin
|
||||
arch: mac-x86_64
|
||||
- target: aarch64-apple-darwin
|
||||
arch: mac-aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -89,8 +101,10 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --release -v --target=${{ matrix.target }}
|
||||
mkdir output
|
||||
cp target/${{ matrix.target }}/release/gopher64 output/gopher64-${{ matrix.arch }}
|
||||
- name: Upload file
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gopher64-${{ matrix.target }}
|
||||
path: target/${{ matrix.target }}/release/gopher64
|
||||
name: gopher64-${{ matrix.arch }}
|
||||
path: output/gopher64-${{ matrix.arch }}
|
||||
|
||||
@@ -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@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: rustsec/audit-check@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
Generated
+768
-512
File diff suppressed because it is too large
Load Diff
+13
-9
@@ -1,23 +1,23 @@
|
||||
[package]
|
||||
name = "gopher64"
|
||||
version = "1.0.7"
|
||||
edition = "2021"
|
||||
rust-version = "1.82"
|
||||
version = "1.0.16"
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dirs = "6.0"
|
||||
zip = "2.1"
|
||||
governor = "0.8"
|
||||
sevenz-rust = "0.6"
|
||||
zip = "2.6"
|
||||
governor = "0.10"
|
||||
sevenz-rust2 = { version = "0.13.0", default-features = false }
|
||||
chrono = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde-big-array = "0.5"
|
||||
eframe = { version = "0.31", default-features = false, features = ["wayland", "x11", "wgpu"] }
|
||||
eframe = { version = "0.31", default-features = false, features = ["wayland", "x11", "glow"] }
|
||||
sha2 = "0.10"
|
||||
rusttype = "0.9"
|
||||
ab_glyph = "0.2"
|
||||
sdl3-sys = { version = "0.4", features = ["build-from-source-static"] }
|
||||
rfd = { version = "0.15", default-features = false, features = ["xdg-portal", "tokio"] }
|
||||
tokio = {version = "1.43", features = ["rt-multi-thread", "macros"] }
|
||||
@@ -27,15 +27,19 @@ mimalloc = "0.1"
|
||||
regex = "1.11"
|
||||
tungstenite = "0.26"
|
||||
socket2 = "0.5"
|
||||
rand_chacha = "0.9"
|
||||
fatfs = "0.3"
|
||||
semver = "1.0"
|
||||
postcard = { version = "1.1", features = ["use-std"] }
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["http2","rustls-tls","charset","json"] }
|
||||
|
||||
[build-dependencies]
|
||||
winres = "0.1"
|
||||
winresource = "0.1"
|
||||
bindgen = "0.71"
|
||||
cc = { version = "1.2", features = ["parallel"] }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
panic = "abort"
|
||||
|
||||
@@ -1,76 +1,59 @@
|
||||
# gopher64
|
||||
(gopher64 also has a GUI that can be launched by just running the executable)
|
||||
## download
|
||||
|
||||
```
|
||||
Usage: gopher64 [OPTIONS] [GAME]
|
||||
https://github.com/gopher64/gopher64/releases
|
||||
|
||||
Arguments:
|
||||
[GAME]
|
||||
## wiki
|
||||
|
||||
https://github.com/gopher64/gopher64/wiki
|
||||
|
||||
Options:
|
||||
-f, --fullscreen
|
||||
|
||||
-c, --configure-input-profile <PROFILE_NAME>
|
||||
Create a new input profile (keyboard/gamepad mappings).
|
||||
-u, --use-dinput
|
||||
Use DirectInput when configuring a new input profile.
|
||||
-b, --bind-input-profile <PROFILE_NAME>
|
||||
Must also specify --port. Used to bind a previously created profile to a port
|
||||
-l, --list-controllers
|
||||
Lists connected controllers which can be used in --assign-controller
|
||||
-a, --assign-controller <CONTROLLER_NUMBER>
|
||||
Must also specify --port. Used to assign a controller listed in --list-controllers to a port
|
||||
-p, --port <PORT>
|
||||
Valid values: 1-4. To be used alongside --bind-input-profile and --assign-controller
|
||||
-z, --clear-input-bindings
|
||||
Clear all input profile bindings and controller assignments
|
||||
-h, --help
|
||||
Print help
|
||||
-V, --version
|
||||
Print version
|
||||
```
|
||||
## discord
|
||||
|
||||
https://discord.gg/9RGXq8W8JQ
|
||||
|
||||
## controls
|
||||
|
||||
Keys are mapped according to mupen64plus defaults: https://mupen64plus.org/wiki/index.php/KeyboardSetup#2._Default_Key_Mappings_for_SDL-Input_Plugin. Xbox-style controllers also have a default mapping applied.
|
||||
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).
|
||||
|
||||
You can create you own mappings by running `./gopher64 --configure-input-profile my_profile`. You then bind that profile to a port: `./gopher64 --bind-input-profile my_profile --port 1`
|
||||
You can create your mappings by running `./gopher64 --configure-input-profile my_profile`. You then bind that profile to a port: `./gopher64 --bind-input-profile my_profile --port 1`
|
||||
|
||||
In order to use a controller (for example, an Xbox controller), run `./gopher64 --list-controllers` to get a list of attached controllers, and then assign it by doing `./gopher64 --assign-controller <controller_number> --port 1`
|
||||
To use a controller (for example, an Xbox controller), run `./gopher64 --list-controllers` to get a list of attached controllers, and then assign it by doing `./gopher64 --assign-controller <controller_number> --port 1`
|
||||
|
||||
## netplay
|
||||
|
||||
Gopher64 supports netplay (online play with others). It has a few public netplay servers. If you are interested in running a public netplay server, please let me know (open an issue or discussion, or contact me on Discord). You can also run the server (https://github.com/simple64/simple64-netplay-server) yourself on a LAN.
|
||||
Gopher64 supports netplay (online play with others). It has a few public netplay servers. If you are interested in running a public netplay server, please let me know (open an issue or discussion or contact me on Discord). You can also run the server (https://github.com/gopher64/gopher64-netplay-server) yourself on a LAN.
|
||||
|
||||
## portable mode
|
||||
|
||||
If you would like to keep all the game data in the same folder as executable, you just need to create a file called "portable.txt" in the same directory as the executable.
|
||||
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
|
||||
```
|
||||
|
||||
## goals
|
||||
|
||||
1. Performance. I want to be able to use this emulator on my laptop.
|
||||
2. Easy to use.
|
||||
3. Easy to work on. Dynamic recompilers perform well, but they are very hard to read and understand. This emulator will only have interpreters for the CPU and RSP. Additionally, it is completely written in Rust (besides Parallel-RDP), a modern programming language with a growing user base. I've tried to avoid the use of macros, which can reduce some repetitiveness in the code, but also reduce readability.
|
||||
3. Easy to work on. Dynamic recompilers perform well, but they are very hard to read and understand. This emulator will only have interpreters for the CPU and RSP. Additionally, it is completely written in Rust (besides Parallel-RDP), a modern programming language with a growing user base. I've tried to avoid the use of macros, which can reduce some repetitiveness in the code but also reduce readability.
|
||||
|
||||
## building and usage
|
||||
|
||||
1. Install rust: https://www.rust-lang.org/tools/install
|
||||
2. `git clone --recursive https://github.com/gopher64/gopher64.git`
|
||||
3. `cd gopher64`
|
||||
4. `cargo build --release`
|
||||
5. `./target/release/gopher64 /path/to/rom.z64`
|
||||
1. Linux only: install the SDL3 dependencies: https://wiki.libsdl.org/SDL3/README/linux
|
||||
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
|
||||
|
||||
I am very open to contributions! Please reach out to me via a GitHub issue, or via discord (loganmc10) before doing substantial work on a PR.
|
||||
I am very open to contributions! 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.
|
||||
|
||||
## where to download
|
||||
|
||||
https://github.com/gopher64/gopher64/releases
|
||||
|
||||
@@ -59,9 +59,16 @@ fn main() {
|
||||
|
||||
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap();
|
||||
if os == "windows" {
|
||||
if arch == "x86_64" {
|
||||
build.flag("/arch:AVX2");
|
||||
if env == "msvc" {
|
||||
build.flag("/arch:AVX2");
|
||||
} else if env == "gnu" {
|
||||
build.flag("-march=x86-64-v3");
|
||||
} else {
|
||||
panic!("unknown env")
|
||||
}
|
||||
} else if arch == "aarch64" {
|
||||
panic!("unsupported platform")
|
||||
} else {
|
||||
@@ -69,7 +76,7 @@ fn main() {
|
||||
}
|
||||
build.flag("-DVK_USE_PLATFORM_WIN32_KHR");
|
||||
|
||||
winres::WindowsResource::new()
|
||||
winresource::WindowsResource::new()
|
||||
.set_icon("data/icon.ico")
|
||||
.compile()
|
||||
.unwrap();
|
||||
@@ -101,6 +108,9 @@ fn main() {
|
||||
.allowlist_function("rdp_process_commands")
|
||||
.allowlist_function("rdp_check_callback")
|
||||
.allowlist_function("rdp_new_processor")
|
||||
.allowlist_function("rdp_state_size")
|
||||
.allowlist_function("rdp_save_state")
|
||||
.allowlist_function("rdp_load_state")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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);
|
||||
}
|
||||
@@ -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}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
Executable
+5
@@ -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
|
||||
+160
-51
@@ -3,6 +3,7 @@
|
||||
#include "rdp_device.hpp"
|
||||
#include "interface.hpp"
|
||||
#include "spirv.hpp"
|
||||
#include "spirv_crt.hpp"
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
using namespace Vulkan;
|
||||
@@ -51,18 +52,31 @@ enum vi_registers
|
||||
VI_REGS_COUNT
|
||||
};
|
||||
|
||||
static bool fullscreen;
|
||||
static bool integer_scaling;
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cmd_data[0x00040000 >> 2];
|
||||
int cmd_cur;
|
||||
int cmd_ptr;
|
||||
uint32_t region;
|
||||
} RDP_DEVICE;
|
||||
|
||||
static SDL_Window *window;
|
||||
static RDP::CommandProcessor *processor;
|
||||
static SDL_WSIPlatform *wsi_platform;
|
||||
static WSI *wsi;
|
||||
static uint32_t cmd_data[0x00040000 >> 2];
|
||||
static int cmd_cur;
|
||||
static int cmd_ptr;
|
||||
|
||||
static RDP_DEVICE rdp_device;
|
||||
static bool crop_letterbox;
|
||||
static CALL_BACK callback;
|
||||
static GFX_INFO gfx_info;
|
||||
static uint32_t region;
|
||||
static const uint32_t *fragment_spirv;
|
||||
static size_t fragment_size;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float SourceSize[4];
|
||||
float OutputSize[4];
|
||||
} Push;
|
||||
|
||||
static const unsigned cmd_len_lut[64] = {
|
||||
1,
|
||||
@@ -141,27 +155,42 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
|
||||
{
|
||||
wsi_platform->do_resize();
|
||||
}
|
||||
else if (event->type == SDL_EVENT_KEY_UP)
|
||||
else if (event->type == SDL_EVENT_KEY_DOWN && !event->key.repeat)
|
||||
{
|
||||
switch (event->key.scancode)
|
||||
{
|
||||
case SDL_SCANCODE_RETURN:
|
||||
if (event->key.mod & SDL_KMOD_ALT)
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
SDL_SetWindowFullscreen(window, fullscreen);
|
||||
gfx_info.fullscreen = !gfx_info.fullscreen;
|
||||
SDL_SetWindowFullscreen(window, gfx_info.fullscreen);
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_F:
|
||||
if (event->key.mod & SDL_KMOD_ALT)
|
||||
{
|
||||
callback.enable_speedlimiter = !callback.enable_speedlimiter;
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
if (fullscreen)
|
||||
if (gfx_info.fullscreen)
|
||||
callback.emu_running = false;
|
||||
break;
|
||||
case SDL_SCANCODE_F4:
|
||||
crop_letterbox = !crop_letterbox;
|
||||
break;
|
||||
case SDL_SCANCODE_F5:
|
||||
callback.save_state = true;
|
||||
break;
|
||||
case SDL_SCANCODE_F7:
|
||||
callback.load_state = true;
|
||||
break;
|
||||
case SDL_SCANCODE_LEFTBRACKET:
|
||||
callback.lower_volume = true;
|
||||
break;
|
||||
case SDL_SCANCODE_RIGHTBRACKET:
|
||||
callback.raise_volume = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -170,23 +199,31 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rdp_new_processor(GFX_INFO _gfx_info, bool _upscale)
|
||||
void rdp_new_processor(GFX_INFO _gfx_info)
|
||||
{
|
||||
gfx_info = _gfx_info;
|
||||
|
||||
if (processor)
|
||||
{
|
||||
delete processor;
|
||||
}
|
||||
RDP::CommandProcessorFlags flags = 0;
|
||||
if (_upscale)
|
||||
|
||||
if (gfx_info.upscale == 2)
|
||||
{
|
||||
flags |= RDP::COMMAND_PROCESSOR_FLAG_UPSCALING_2X_BIT;
|
||||
flags |= RDP::COMMAND_PROCESSOR_FLAG_SUPER_SAMPLED_DITHER_BIT;
|
||||
flags |= RDP::COMMAND_PROCESSOR_FLAG_UPSCALING_2X_BIT;
|
||||
}
|
||||
else if (gfx_info.upscale == 4)
|
||||
{
|
||||
flags |= RDP::COMMAND_PROCESSOR_FLAG_SUPER_SAMPLED_DITHER_BIT;
|
||||
flags |= RDP::COMMAND_PROCESSOR_FLAG_UPSCALING_4X_BIT;
|
||||
}
|
||||
|
||||
processor = new RDP::CommandProcessor(wsi->get_device(), gfx_info.RDRAM, 0, gfx_info.RDRAM_SIZE, gfx_info.RDRAM_SIZE / 2, flags);
|
||||
}
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_scaling, bool _fullscreen)
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info)
|
||||
{
|
||||
window = (SDL_Window *)_window;
|
||||
bool result = SDL_AddEventWatch(sdl_event_filter, nullptr);
|
||||
@@ -197,8 +234,18 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_sc
|
||||
}
|
||||
|
||||
gfx_info = _gfx_info;
|
||||
fullscreen = _fullscreen;
|
||||
integer_scaling = _integer_scaling;
|
||||
|
||||
if (gfx_info.crt)
|
||||
{
|
||||
fragment_spirv = crt_fragment_spirv;
|
||||
fragment_size = sizeof(crt_fragment_spirv);
|
||||
}
|
||||
else
|
||||
{
|
||||
fragment_spirv = plain_fragment_spirv;
|
||||
fragment_size = sizeof(plain_fragment_spirv);
|
||||
}
|
||||
|
||||
bool window_vsync = 0;
|
||||
wsi = new WSI;
|
||||
wsi_platform = new SDL_WSIPlatform;
|
||||
@@ -215,7 +262,8 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_sc
|
||||
{
|
||||
rdp_close();
|
||||
}
|
||||
rdp_new_processor(gfx_info, _upscale);
|
||||
|
||||
rdp_new_processor(gfx_info);
|
||||
|
||||
if (!processor->device_is_supported())
|
||||
{
|
||||
@@ -227,6 +275,8 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_sc
|
||||
wsi->begin_frame();
|
||||
|
||||
callback.emu_running = true;
|
||||
callback.enable_speedlimiter = true;
|
||||
crop_letterbox = false;
|
||||
}
|
||||
|
||||
void rdp_close()
|
||||
@@ -250,15 +300,14 @@ void rdp_close()
|
||||
}
|
||||
}
|
||||
|
||||
static void calculate_viewport(float *x, float *y, float *width, float *height)
|
||||
static void calculate_viewport(float *x, float *y, float *width, float *height, uint32_t display_height)
|
||||
{
|
||||
const int32_t display_width = gfx_info.PAL ? 384 : 320;
|
||||
const int32_t display_height = gfx_info.PAL ? 288 : 240;
|
||||
uint32_t display_width = gfx_info.widescreen ? display_height * 16 / 9 : display_height * 4 / 3;
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
|
||||
if (integer_scaling)
|
||||
if (gfx_info.integer_scaling)
|
||||
{
|
||||
// Integer scaling path
|
||||
int scale_x = w / display_width;
|
||||
@@ -273,6 +322,13 @@ static void calculate_viewport(float *x, float *y, float *width, float *height)
|
||||
|
||||
*width = scaled_width;
|
||||
*height = scaled_height;
|
||||
|
||||
// Center the viewport
|
||||
int integer_x = (w - *width) / 2.0f;
|
||||
int integer_y = (h - *height) / 2.0f;
|
||||
|
||||
*x = integer_x;
|
||||
*y = integer_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -283,11 +339,11 @@ static void calculate_viewport(float *x, float *y, float *width, float *height)
|
||||
|
||||
*width = display_width * scale;
|
||||
*height = display_height * scale;
|
||||
}
|
||||
|
||||
// Center the viewport
|
||||
*x = (w - *width) / 2.0f;
|
||||
*y = (h - *height) / 2.0f;
|
||||
// Center the viewport
|
||||
*x = (w - *width) / 2.0f;
|
||||
*y = (h - *height) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void render_frame(Vulkan::Device &device)
|
||||
@@ -296,17 +352,34 @@ static void render_frame(Vulkan::Device &device)
|
||||
options.persist_frame_on_invalid_input = true;
|
||||
options.blend_previous_frame = true;
|
||||
options.upscale_deinterlacing = false;
|
||||
|
||||
if (crop_letterbox && gfx_info.widescreen)
|
||||
{
|
||||
options.crop_rect.enable = true;
|
||||
if (gfx_info.PAL)
|
||||
{
|
||||
options.crop_rect.top = 36;
|
||||
options.crop_rect.bottom = 36;
|
||||
}
|
||||
else
|
||||
{
|
||||
options.crop_rect.top = 30;
|
||||
options.crop_rect.bottom = 30;
|
||||
}
|
||||
}
|
||||
|
||||
Vulkan::ImageHandle image = processor->scanout(options);
|
||||
|
||||
// Normally reflection is automated.
|
||||
Vulkan::ResourceLayout vertex_layout = {};
|
||||
Vulkan::ResourceLayout fragment_layout = {};
|
||||
fragment_layout.output_mask = 1 << 0;
|
||||
fragment_layout.sets[0].sampled_image_mask = 1 << 0;
|
||||
if (gfx_info.crt)
|
||||
fragment_layout.push_constant_size = sizeof(Push);
|
||||
|
||||
// This request is cached.
|
||||
auto *program = device.request_program(vertex_spirv, sizeof(vertex_spirv),
|
||||
fragment_spirv, sizeof(fragment_spirv),
|
||||
fragment_spirv, fragment_size,
|
||||
&vertex_layout,
|
||||
&fragment_layout);
|
||||
|
||||
@@ -316,9 +389,6 @@ static void render_frame(Vulkan::Device &device)
|
||||
auto rp = device.get_swapchain_render_pass(Vulkan::SwapchainRenderPass::ColorOnly);
|
||||
cmd->begin_render_pass(rp);
|
||||
|
||||
VkViewport vp = cmd->get_viewport();
|
||||
calculate_viewport(&vp.x, &vp.y, &vp.width, &vp.height);
|
||||
|
||||
cmd->set_program(program);
|
||||
|
||||
// Basic default render state.
|
||||
@@ -329,6 +399,19 @@ static void render_frame(Vulkan::Device &device)
|
||||
// If we don't have an image, we just get a cleared screen in the render pass.
|
||||
if (image)
|
||||
{
|
||||
VkViewport vp = cmd->get_viewport();
|
||||
calculate_viewport(&vp.x, &vp.y, &vp.width, &vp.height, image->get_height() / gfx_info.upscale);
|
||||
|
||||
if (gfx_info.crt)
|
||||
{
|
||||
// Set shader parameters
|
||||
Push push = {
|
||||
{float(image->get_width()), float(image->get_height()), 1.0f / float(image->get_width()), 1.0f / float(image->get_height())},
|
||||
{vp.width, vp.height, 1.0f / vp.width, 1.0f / vp.height},
|
||||
};
|
||||
cmd->push_constants(&push, 0, sizeof(push));
|
||||
}
|
||||
|
||||
cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::LinearClamp);
|
||||
cmd->set_viewport(vp);
|
||||
// The vertices are constants in the shader.
|
||||
@@ -359,9 +442,26 @@ CALL_BACK rdp_check_callback()
|
||||
CALL_BACK return_value = callback;
|
||||
callback.save_state = false;
|
||||
callback.load_state = false;
|
||||
callback.lower_volume = false;
|
||||
callback.raise_volume = false;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
size_t rdp_state_size()
|
||||
{
|
||||
return sizeof(RDP_DEVICE);
|
||||
}
|
||||
|
||||
void rdp_save_state(uint8_t *state)
|
||||
{
|
||||
memcpy(state, &rdp_device, sizeof(RDP_DEVICE));
|
||||
}
|
||||
|
||||
void rdp_load_state(const uint8_t *state)
|
||||
{
|
||||
memcpy(&rdp_device, state, sizeof(RDP_DEVICE));
|
||||
}
|
||||
|
||||
uint64_t rdp_process_commands()
|
||||
{
|
||||
uint64_t interrupt_timer = 0;
|
||||
@@ -373,7 +473,7 @@ uint64_t rdp_process_commands()
|
||||
return interrupt_timer;
|
||||
|
||||
length = unsigned(length) >> 3;
|
||||
if ((cmd_ptr + length) & ~(0x0003FFFF >> 3))
|
||||
if ((rdp_device.cmd_ptr + length) & ~(0x0003FFFF >> 3))
|
||||
return interrupt_timer;
|
||||
|
||||
uint32_t offset = DP_CURRENT;
|
||||
@@ -382,10 +482,10 @@ uint64_t rdp_process_commands()
|
||||
do
|
||||
{
|
||||
offset &= 0xFF8;
|
||||
cmd_data[2 * cmd_ptr + 0] = SDL_Swap32BE(*reinterpret_cast<const uint32_t *>(gfx_info.DMEM + offset));
|
||||
cmd_data[2 * cmd_ptr + 1] = SDL_Swap32BE(*reinterpret_cast<const uint32_t *>(gfx_info.DMEM + offset + 4));
|
||||
rdp_device.cmd_data[2 * rdp_device.cmd_ptr + 0] = SDL_Swap32BE(*reinterpret_cast<const uint32_t *>(gfx_info.DMEM + offset));
|
||||
rdp_device.cmd_data[2 * rdp_device.cmd_ptr + 1] = SDL_Swap32BE(*reinterpret_cast<const uint32_t *>(gfx_info.DMEM + offset + 4));
|
||||
offset += sizeof(uint64_t);
|
||||
cmd_ptr++;
|
||||
rdp_device.cmd_ptr++;
|
||||
} while (--length > 0);
|
||||
}
|
||||
else
|
||||
@@ -399,52 +499,61 @@ uint64_t rdp_process_commands()
|
||||
do
|
||||
{
|
||||
offset &= 0xFFFFF8;
|
||||
cmd_data[2 * cmd_ptr + 0] = *reinterpret_cast<const uint32_t *>(gfx_info.RDRAM + offset);
|
||||
cmd_data[2 * cmd_ptr + 1] = *reinterpret_cast<const uint32_t *>(gfx_info.RDRAM + offset + 4);
|
||||
rdp_device.cmd_data[2 * rdp_device.cmd_ptr + 0] = *reinterpret_cast<const uint32_t *>(gfx_info.RDRAM + offset);
|
||||
rdp_device.cmd_data[2 * rdp_device.cmd_ptr + 1] = *reinterpret_cast<const uint32_t *>(gfx_info.RDRAM + offset + 4);
|
||||
offset += sizeof(uint64_t);
|
||||
cmd_ptr++;
|
||||
rdp_device.cmd_ptr++;
|
||||
} while (--length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
while (cmd_cur - cmd_ptr < 0)
|
||||
while (rdp_device.cmd_cur - rdp_device.cmd_ptr < 0)
|
||||
{
|
||||
uint32_t w1 = cmd_data[2 * cmd_cur];
|
||||
uint32_t w2 = cmd_data[2 * cmd_cur + 1];
|
||||
uint32_t w1 = rdp_device.cmd_data[2 * rdp_device.cmd_cur];
|
||||
uint32_t w2 = rdp_device.cmd_data[2 * rdp_device.cmd_cur + 1];
|
||||
uint32_t command = (w1 >> 24) & 63;
|
||||
int cmd_length = cmd_len_lut[command];
|
||||
|
||||
if (cmd_ptr - cmd_cur - cmd_length < 0)
|
||||
if (rdp_device.cmd_ptr - rdp_device.cmd_cur - cmd_length < 0)
|
||||
{
|
||||
*gfx_info.DPC_START_REG = *gfx_info.DPC_CURRENT_REG = *gfx_info.DPC_END_REG;
|
||||
return interrupt_timer;
|
||||
}
|
||||
|
||||
if (command >= 8)
|
||||
processor->enqueue_command(cmd_length * 2, &cmd_data[2 * cmd_cur]);
|
||||
processor->enqueue_command(cmd_length * 2, &rdp_device.cmd_data[2 * rdp_device.cmd_cur]);
|
||||
|
||||
if (RDP::Op(command) == RDP::Op::SetScissor)
|
||||
switch (RDP::Op(command))
|
||||
{
|
||||
case RDP::Op::SetScissor:
|
||||
{
|
||||
uint32_t upper_left_x = ((w1 >> 12) & 0xFFF) >> 2;
|
||||
uint32_t upper_left_y = (w1 & 0xFFF) >> 2;
|
||||
uint32_t lower_right_x = ((w2 >> 12) & 0xFFF) >> 2;
|
||||
uint32_t lower_right_y = (w2 & 0xFFF) >> 2;
|
||||
region = (lower_right_x - upper_left_x) * (lower_right_y - upper_left_y);
|
||||
if (lower_right_x > upper_left_x && lower_right_y > upper_left_y)
|
||||
{
|
||||
rdp_device.region = (lower_right_x - upper_left_x) * (lower_right_y - upper_left_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
rdp_device.region = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (RDP::Op(command) == RDP::Op::SyncFull)
|
||||
{
|
||||
case RDP::Op::SyncFull:
|
||||
processor->wait_for_timeline(processor->signal_timeline());
|
||||
|
||||
interrupt_timer = region;
|
||||
interrupt_timer = rdp_device.region;
|
||||
if (interrupt_timer == 0)
|
||||
interrupt_timer = 5000;
|
||||
break;
|
||||
}
|
||||
|
||||
cmd_cur += cmd_length;
|
||||
rdp_device.cmd_cur += cmd_length;
|
||||
}
|
||||
|
||||
cmd_ptr = 0;
|
||||
cmd_cur = 0;
|
||||
rdp_device.cmd_ptr = 0;
|
||||
rdp_device.cmd_cur = 0;
|
||||
*gfx_info.DPC_CURRENT_REG = *gfx_info.DPC_END_REG;
|
||||
|
||||
return interrupt_timer;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -17,6 +18,11 @@ extern "C"
|
||||
uint32_t *DPC_END_REG;
|
||||
uint32_t *DPC_STATUS_REG;
|
||||
bool PAL;
|
||||
bool widescreen;
|
||||
bool fullscreen;
|
||||
bool integer_scaling;
|
||||
uint32_t upscale;
|
||||
bool crt;
|
||||
} GFX_INFO;
|
||||
|
||||
typedef struct
|
||||
@@ -24,15 +30,21 @@ extern "C"
|
||||
bool emu_running;
|
||||
bool save_state;
|
||||
bool load_state;
|
||||
bool enable_speedlimiter;
|
||||
bool lower_volume;
|
||||
bool raise_volume;
|
||||
} CALL_BACK;
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_scaling, bool _fullscreen);
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info);
|
||||
void rdp_close();
|
||||
void rdp_set_vi_register(uint32_t reg, uint32_t value);
|
||||
void rdp_update_screen();
|
||||
CALL_BACK rdp_check_callback();
|
||||
uint64_t rdp_process_commands();
|
||||
void rdp_new_processor(GFX_INFO _gfx_info, bool _upscale);
|
||||
void rdp_new_processor(GFX_INFO _gfx_info);
|
||||
size_t rdp_state_size();
|
||||
void rdp_save_state(uint8_t *state);
|
||||
void rdp_load_state(const uint8_t *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,19 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
static const uint32_t vertex_spirv[] =
|
||||
{0x07230203, 0x00010000, 0x000d000a, 0x00000034,
|
||||
{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,
|
||||
0x00050048, 0x00000014, 0x00000000, 0x0000000b,
|
||||
0x00000000, 0x00050048, 0x00000014, 0x00000001,
|
||||
0x0000000b, 0x00000001, 0x00050048, 0x00000014,
|
||||
0x00000002, 0x0000000b, 0x00000003, 0x00050048,
|
||||
0x00000014, 0x00000003, 0x0000000b, 0x00000004,
|
||||
0x00030047, 0x00000014, 0x00000002, 0x00040047,
|
||||
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,
|
||||
@@ -74,8 +74,8 @@ static const uint32_t vertex_spirv[] =
|
||||
0x00000030, 0x00000033, 0x0003003e, 0x0000002b,
|
||||
0x00000032, 0x000100fd, 0x00010038};
|
||||
|
||||
static const uint32_t fragment_spirv[] =
|
||||
{0x07230203, 0x00010000, 0x000d000a, 0x00000015,
|
||||
static const uint32_t plain_fragment_spirv[] =
|
||||
{0x07230203, 0x00010300, 0x000d000b, 0x00000015,
|
||||
0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
|
||||
0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
||||
@@ -83,8 +83,8 @@ static const uint32_t fragment_spirv[] =
|
||||
0x00000000, 0x00000009, 0x00000011, 0x00030010,
|
||||
0x00000004, 0x00000007, 0x00040047, 0x00000009,
|
||||
0x0000001e, 0x00000000, 0x00040047, 0x0000000d,
|
||||
0x00000022, 0x00000000, 0x00040047, 0x0000000d,
|
||||
0x00000021, 0x00000000, 0x00040047, 0x00000011,
|
||||
0x00000021, 0x00000000, 0x00040047, 0x0000000d,
|
||||
0x00000022, 0x00000000, 0x00040047, 0x00000011,
|
||||
0x0000001e, 0x00000000, 0x00020013, 0x00000002,
|
||||
0x00030021, 0x00000003, 0x00000002, 0x00030016,
|
||||
0x00000006, 0x00000020, 0x00040017, 0x00000007,
|
||||
|
||||
@@ -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};
|
||||
@@ -28,7 +28,7 @@ std::vector<const char *> SDL_WSIPlatform::get_instance_extensions()
|
||||
}
|
||||
|
||||
std::vector<const char *> extensionNames;
|
||||
for (int i = 0; i < extensionCount; ++i)
|
||||
for (unsigned int i = 0; i < extensionCount; ++i)
|
||||
{
|
||||
extensionNames.push_back(extensions[i]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.86.0"
|
||||
+56
-17
@@ -10,6 +10,8 @@ include!(concat!(env!("OUT_DIR"), "/simd_bindings.rs"));
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use rand_chacha::rand_core::{RngCore, SeedableRng};
|
||||
|
||||
use crate::{netplay, ui};
|
||||
use std::{collections::HashMap, fs, io::Read};
|
||||
|
||||
@@ -42,7 +44,12 @@ pub mod tlb;
|
||||
pub mod unmapped;
|
||||
pub mod vi;
|
||||
|
||||
pub fn run_game(rom_contents: Vec<u8>, device: &mut Device) {
|
||||
pub fn run_game(device: &mut Device, rom_contents: Vec<u8>, fullscreen: bool, overclock: bool) {
|
||||
device.ui.video.fullscreen = fullscreen;
|
||||
device.cpu.overclock = overclock;
|
||||
|
||||
init_rng(device);
|
||||
|
||||
cart::rom::init(device, rom_contents); // cart needs to come before rdram
|
||||
|
||||
// rdram pointer is shared with parallel-rdp
|
||||
@@ -72,14 +79,31 @@ pub fn run_game(rom_contents: Vec<u8>, device: &mut Device) {
|
||||
ui::input::close(&mut device.ui);
|
||||
ui::video::close(&device.ui);
|
||||
ui::audio::close(&mut device.ui);
|
||||
ui::storage::write_saves(&device.ui, &device.netplay);
|
||||
ui::storage::write_saves(device);
|
||||
}
|
||||
|
||||
fn swap_rom(contents: Vec<u8>) -> Vec<u8> {
|
||||
fn set_rng() -> rand_chacha::ChaCha8Rng {
|
||||
rand_chacha::ChaCha8Rng::from_os_rng()
|
||||
}
|
||||
|
||||
fn init_rng(device: &mut Device) {
|
||||
let mut rng_seed = set_rng().next_u64();
|
||||
if device.netplay.is_some() {
|
||||
let netplay = device.netplay.as_mut().unwrap();
|
||||
if netplay.player_number == 0 {
|
||||
netplay::send_rng(netplay, rng_seed);
|
||||
} else {
|
||||
rng_seed = netplay::receive_rng(netplay);
|
||||
}
|
||||
}
|
||||
device.rng = rand_chacha::ChaCha8Rng::seed_from_u64(rng_seed);
|
||||
}
|
||||
|
||||
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
|
||||
contents
|
||||
Some(contents)
|
||||
} else if test == 0x37804012 {
|
||||
// v64
|
||||
let mut data: Vec<u8> = vec![0; contents.len()];
|
||||
@@ -87,7 +111,7 @@ fn swap_rom(contents: Vec<u8>) -> Vec<u8> {
|
||||
let temp = u16::from_ne_bytes(contents[i..i + 2].try_into().unwrap());
|
||||
data[i..i + 2].copy_from_slice(&temp.to_be_bytes());
|
||||
}
|
||||
return data;
|
||||
Some(data)
|
||||
} else if test == 0x40123780 {
|
||||
// n64
|
||||
let mut data: Vec<u8> = vec![0; contents.len()];
|
||||
@@ -95,14 +119,13 @@ fn swap_rom(contents: Vec<u8>) -> Vec<u8> {
|
||||
let temp = u32::from_ne_bytes(contents[i..i + 4].try_into().unwrap());
|
||||
data[i..i + 4].copy_from_slice(&temp.to_be_bytes());
|
||||
}
|
||||
return data;
|
||||
Some(data)
|
||||
} else {
|
||||
let data: Vec<u8> = vec![];
|
||||
data
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rom_contents(file_path: &std::path::Path) -> Vec<u8> {
|
||||
pub fn get_rom_contents(file_path: &std::path::Path) -> Option<Vec<u8>> {
|
||||
let mut contents = vec![];
|
||||
if file_path.extension().unwrap().eq_ignore_ascii_case("zip") {
|
||||
let zip_file = fs::File::open(file_path).unwrap();
|
||||
@@ -123,12 +146,12 @@ pub fn get_rom_contents(file_path: &std::path::Path) -> Vec<u8> {
|
||||
}
|
||||
} else if file_path.extension().unwrap().eq_ignore_ascii_case("7z") {
|
||||
let mut archive =
|
||||
sevenz_rust::SevenZReader::open(file_path, sevenz_rust::Password::empty()).unwrap();
|
||||
sevenz_rust2::SevenZReader::open(file_path, sevenz_rust2::Password::empty()).unwrap();
|
||||
|
||||
let mut found = false;
|
||||
archive
|
||||
.for_each_entries(
|
||||
&mut |entry: &sevenz_rust::SevenZArchiveEntry, reader: &mut dyn std::io::Read| {
|
||||
&mut |entry: &sevenz_rust2::SevenZArchiveEntry, reader: &mut dyn std::io::Read| {
|
||||
let name = entry.name().to_ascii_lowercase();
|
||||
if !found
|
||||
&& (name.ends_with("z64") || name.ends_with("n64") || name.ends_with("v64"))
|
||||
@@ -149,7 +172,11 @@ pub fn get_rom_contents(file_path: &std::path::Path) -> Vec<u8> {
|
||||
contents = fs::read(file_path).expect("Should have been able to read the file");
|
||||
}
|
||||
|
||||
swap_rom(contents)
|
||||
if contents.is_empty() {
|
||||
None
|
||||
} else {
|
||||
swap_rom(contents)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
@@ -174,8 +201,11 @@ pub struct Device {
|
||||
pub ai: ai::Ai,
|
||||
pub si: si::Si,
|
||||
pub ri: ri::Ri,
|
||||
#[serde(skip, default = "set_rng")]
|
||||
pub rng: rand_chacha::ChaCha8Rng,
|
||||
pub vru: controller::vru::Vru,
|
||||
pub vru_window: controller::vru::VruWindow,
|
||||
pub transferpaks: [controller::transferpak::TransferPak; 4],
|
||||
}
|
||||
|
||||
pub fn zero_m128i() -> __m128i {
|
||||
@@ -269,6 +299,7 @@ impl Device {
|
||||
pc: 0xBFC00000,
|
||||
pc_phys: 0,
|
||||
llbit: false,
|
||||
overclock: false,
|
||||
lo: 0,
|
||||
hi: 0,
|
||||
running: false,
|
||||
@@ -297,19 +328,19 @@ impl Device {
|
||||
},
|
||||
cart: cart::Cart {
|
||||
rom: Vec::new(),
|
||||
is_viewer_buffer: [0; 0xFFFF],
|
||||
is_viewer_buffer: vec![0; 0xFFFF],
|
||||
pal: false,
|
||||
latch: 0,
|
||||
cic_seed: 0,
|
||||
cic_type: cart::CicType::CicNus6102,
|
||||
rdram_size_offset: 0,
|
||||
rtc_timestamp: 0,
|
||||
rtc: cart::AfRtc { control: 0x0200 },
|
||||
sc64: cart::sc64::Sc64 {
|
||||
regs: [0; cart::sc64::SC64_REGS_COUNT as usize],
|
||||
regs_locked: true,
|
||||
cfg: [0; cart::sc64::SC64_CFG_COUNT as usize],
|
||||
sector: 0,
|
||||
buffer: [0; 8192],
|
||||
buffer: vec![0; 8192],
|
||||
writeback_sector: vec![0; 256],
|
||||
},
|
||||
flashram: cart::sram::Flashram {
|
||||
status: 0,
|
||||
@@ -320,7 +351,7 @@ impl Device {
|
||||
},
|
||||
},
|
||||
memory: memory::Memory {
|
||||
fast_read: [unmapped::read_mem; 0x2000],
|
||||
fast_read: [unmapped::read_mem_fast; 0x2000],
|
||||
memory_map_read: [unmapped::read_mem; 0x2000],
|
||||
memory_map_write: [unmapped::write_mem; 0x2000],
|
||||
icache: [cache::ICache {
|
||||
@@ -435,6 +466,7 @@ impl Device {
|
||||
field: 0,
|
||||
delay: 0,
|
||||
count_per_scanline: 0,
|
||||
enable_speed_limiter: true,
|
||||
limiter: None,
|
||||
vi_counter: 0,
|
||||
last_origin: 0,
|
||||
@@ -459,6 +491,13 @@ impl Device {
|
||||
talking: false,
|
||||
word_mappings: HashMap::new(),
|
||||
},
|
||||
rng: set_rng(),
|
||||
transferpaks: [
|
||||
controller::transferpak::TransferPak::default(),
|
||||
controller::transferpak::TransferPak::default(),
|
||||
controller::transferpak::TransferPak::default(),
|
||||
controller::transferpak::TransferPak::default(),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ pub struct AiDma {
|
||||
pub duration: u64,
|
||||
}
|
||||
|
||||
fn get_remaining_dma_length(device: &mut device::Device) -> u64 {
|
||||
fn get_remaining_dma_length(device: &device::Device) -> u64 {
|
||||
if device.ai.fifo[0].duration == 0 {
|
||||
return 0;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ fn do_dma(device: &mut device::Device) {
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_AI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.ai.fifo[0].duration,
|
||||
device.ai.fifo[0].duration,
|
||||
);
|
||||
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI);
|
||||
}
|
||||
|
||||
+36
-160
@@ -30,54 +30,14 @@ pub fn icache_writeback(device: &mut device::Device, line_index: usize) {
|
||||
let cache_address = ((device.memory.icache[line_index].tag
|
||||
| (device.memory.icache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address,
|
||||
device.memory.icache[line_index].words[0],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x4,
|
||||
device.memory.icache[line_index].words[1],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x8,
|
||||
device.memory.icache[line_index].words[2],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0xC,
|
||||
device.memory.icache[line_index].words[3],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x10,
|
||||
device.memory.icache[line_index].words[4],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x14,
|
||||
device.memory.icache[line_index].words[5],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x18,
|
||||
device.memory.icache[line_index].words[6],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x1C,
|
||||
device.memory.icache[line_index].words[7],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
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) {
|
||||
@@ -88,71 +48,17 @@ pub fn icache_fill(device: &mut device::Device, line_index: usize, phys_address:
|
||||
let cache_address = ((device.memory.icache[line_index].tag
|
||||
| (device.memory.icache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
device.memory.icache[line_index].words[0] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[1] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x4,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[2] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x8,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[3] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0xC,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[4] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x10,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[5] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x14,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[6] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x18,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
device.memory.icache[line_index].words[7] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x1C,
|
||||
device::memory::AccessSize::Icache,
|
||||
);
|
||||
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[0] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[0]);
|
||||
device.memory.icache[line_index].instruction[1] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[1]);
|
||||
device.memory.icache[line_index].instruction[2] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[2]);
|
||||
device.memory.icache[line_index].instruction[3] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[3]);
|
||||
device.memory.icache[line_index].instruction[4] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[4]);
|
||||
device.memory.icache[line_index].instruction[5] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[5]);
|
||||
device.memory.icache[line_index].instruction[6] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[6]);
|
||||
device.memory.icache[line_index].instruction[7] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[7]);
|
||||
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) {
|
||||
@@ -180,30 +86,15 @@ pub fn dcache_writeback(device: &mut device::Device, line_index: usize) {
|
||||
let cache_address = ((device.memory.dcache[line_index].tag
|
||||
| (device.memory.dcache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address,
|
||||
device.memory.dcache[line_index].words[0],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x4,
|
||||
device.memory.dcache[line_index].words[1],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x8,
|
||||
device.memory.dcache[line_index].words[2],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
device.memory.memory_map_write[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0xC,
|
||||
device.memory.dcache[line_index].words[3],
|
||||
0xFFFFFFFF,
|
||||
);
|
||||
|
||||
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) {
|
||||
@@ -216,30 +107,15 @@ fn dcache_fill(device: &mut device::Device, line_index: usize, phys_address: u64
|
||||
let cache_address = ((device.memory.dcache[line_index].tag
|
||||
| (device.memory.dcache[line_index].index) as u32)
|
||||
& 0x1ffffffc) as u64;
|
||||
device.memory.dcache[line_index].words[0] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address,
|
||||
device::memory::AccessSize::Dcache,
|
||||
);
|
||||
device.memory.dcache[line_index].words[1] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x4,
|
||||
device::memory::AccessSize::Dcache,
|
||||
);
|
||||
device.memory.dcache[line_index].words[2] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0x8,
|
||||
device::memory::AccessSize::Dcache,
|
||||
);
|
||||
device.memory.dcache[line_index].words[3] = device.memory.memory_map_read
|
||||
[(cache_address >> 16) as usize](
|
||||
device,
|
||||
cache_address | 0xC,
|
||||
device::memory::AccessSize::Dcache,
|
||||
);
|
||||
|
||||
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 {
|
||||
|
||||
+21
-12
@@ -45,16 +45,14 @@ fn byte2bcd(mut n: u32) -> u8 {
|
||||
pub struct Cart {
|
||||
#[serde(skip)]
|
||||
pub rom: Vec<u8>,
|
||||
#[serde(with = "serde_big_array::BigArray")]
|
||||
pub is_viewer_buffer: [u8; 0xFFFF],
|
||||
pub is_viewer_buffer: Vec<u8>,
|
||||
pub pal: bool,
|
||||
pub latch: u32,
|
||||
pub cic_type: CicType,
|
||||
pub cic_seed: u8,
|
||||
pub rdram_size_offset: usize,
|
||||
pub rtc: AfRtc,
|
||||
pub sc64: sc64::Sc64,
|
||||
pub flashram: sram::Flashram,
|
||||
pub rtc_timestamp: i64,
|
||||
}
|
||||
|
||||
pub fn process(device: &mut device::Device, channel: usize) {
|
||||
@@ -65,12 +63,14 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
let eeprom_type;
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Eeprom16k)
|
||||
{
|
||||
eeprom_type = JDT_EEPROM_16K;
|
||||
} else if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Eeprom4k)
|
||||
{
|
||||
@@ -125,9 +125,15 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_eeprom(device: &mut device::Device) {
|
||||
if device.ui.saves.eeprom.0.len() < EEPROM_MAX_SIZE {
|
||||
device.ui.saves.eeprom.0.resize(EEPROM_MAX_SIZE, 0xFF)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +142,9 @@ fn eeprom_read_block(device: &mut device::Device, block: usize, offset: usize) {
|
||||
|
||||
format_eeprom(device);
|
||||
|
||||
device.pif.ram[offset..offset + EEPROM_BLOCK_SIZE]
|
||||
.copy_from_slice(&device.ui.saves.eeprom.0[address..address + 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) {
|
||||
@@ -145,16 +152,18 @@ fn eeprom_write_block(device: &mut device::Device, block: usize, offset: usize,
|
||||
|
||||
format_eeprom(device);
|
||||
|
||||
device.ui.saves.eeprom.0[address..address + 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;
|
||||
|
||||
device.ui.saves.eeprom.1 = true
|
||||
device.ui.storage.saves.eeprom.written = true
|
||||
}
|
||||
|
||||
fn time2data(device: &mut device::Device, offset: usize) {
|
||||
let now: chrono::DateTime<chrono::Local> = chrono::Local::now();
|
||||
let timestamp =
|
||||
device.cart.rtc_timestamp + (device.vi.frame_time * device.vi.vi_counter as f64) as i64;
|
||||
let now = chrono::DateTime::from_timestamp(timestamp, 0).unwrap();
|
||||
|
||||
device.pif.ram[offset] = byte2bcd(now.second());
|
||||
device.pif.ram[offset + 1] = byte2bcd(now.minute());
|
||||
|
||||
+58
-58
@@ -1,26 +1,30 @@
|
||||
use crate::device;
|
||||
use crate::netplay;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
const CART_MASK: usize = 0xFFFFFFF;
|
||||
|
||||
fn read_cart_word(device: &mut device::Device, address: usize) -> u32 {
|
||||
let mut data: [u8; 4] = device.cart.rom[address..address + 4].try_into().unwrap();
|
||||
fn read_cart_word(device: &device::Device, address: usize) -> u32 {
|
||||
let mut data: [u8; 4] = [0; 4];
|
||||
for i in 0..4 {
|
||||
if device
|
||||
if let Some(value) = device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.romsave
|
||||
.0
|
||||
.contains_key(&(address as u32 + i))
|
||||
.data
|
||||
.get(&(address as u32 + i))
|
||||
{
|
||||
data[i as usize] = device.ui.saves.romsave.0[&(address as u32 + i)];
|
||||
data[i as usize] = *value;
|
||||
} else {
|
||||
data[i as usize] = *device.cart.rom.get(address + i as usize).unwrap_or(&0);
|
||||
}
|
||||
}
|
||||
u32::from_be_bytes(data)
|
||||
}
|
||||
|
||||
pub fn read_mem_fast(
|
||||
device: &mut device::Device,
|
||||
device: &device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
@@ -46,19 +50,22 @@ pub fn read_mem(
|
||||
}
|
||||
|
||||
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 {
|
||||
if device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] != 0
|
||||
&& device.cart.sc64.cfg[device::cart::sc64::SC64_BOOTLOADER_SWITCH 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
|
||||
.0
|
||||
.data
|
||||
.insert((masked_address + i) as u32, *item);
|
||||
}
|
||||
device.ui.saves.romsave.1 = true;
|
||||
device.ui.storage.saves.romsave.written = true;
|
||||
}
|
||||
|
||||
device.cart.latch = value & mask;
|
||||
@@ -66,11 +73,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
|
||||
device.pi.regs[device::pi::PI_STATUS_REG as usize] |= 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,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles,
|
||||
);
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles);
|
||||
}
|
||||
|
||||
pub fn dma_read(
|
||||
@@ -79,20 +82,19 @@ pub fn dma_read(
|
||||
mut dram_addr: u32,
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
if device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] != 0 {
|
||||
if device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] != 0
|
||||
&& device.cart.sc64.cfg[device::cart::sc64::SC64_BOOTLOADER_SWITCH as usize] == 0
|
||||
{
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= CART_MASK as u32;
|
||||
|
||||
for i in 0..length {
|
||||
if cart_addr + i < device.cart.rom.len() as u32 {
|
||||
device.ui.saves.romsave.0.insert(
|
||||
cart_addr + i,
|
||||
device.rdram.mem[(dram_addr + i) as usize ^ device.byte_swap],
|
||||
);
|
||||
|
||||
device.ui.saves.romsave.1 = true;
|
||||
}
|
||||
device.ui.storage.saves.romsave.data.insert(
|
||||
cart_addr + i,
|
||||
device.rdram.mem[(dram_addr + i) as usize ^ device.byte_swap],
|
||||
);
|
||||
}
|
||||
device.ui.storage.saves.romsave.written = true;
|
||||
}
|
||||
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
@@ -109,26 +111,36 @@ pub fn dma_write(
|
||||
cart_addr &= CART_MASK as u32;
|
||||
let mut i = dram_addr;
|
||||
let mut j = cart_addr;
|
||||
while i < dram_addr + length && j < device.cart.rom.len() as u32 {
|
||||
if device.ui.saves.romsave.0.contains_key(&j) {
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = device.ui.saves.romsave.0[&j];
|
||||
while i < dram_addr + length {
|
||||
if let Some(value) = device.ui.storage.saves.romsave.data.get(&j) {
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = *value;
|
||||
} else {
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = device.cart.rom[j as usize];
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] =
|
||||
*device.cart.rom.get(j as usize).unwrap_or(&0);
|
||||
}
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
while i < dram_addr + length {
|
||||
// DMAs that extend past the end of the ROM return 0's for the portion that extends past the ROM length
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = 0;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
}
|
||||
|
||||
pub fn init(device: &mut device::Device, rom_file: Vec<u8>) {
|
||||
let now: chrono::DateTime<chrono::Local> = chrono::Local::now();
|
||||
device.cart.rtc_timestamp = now.naive_local().and_utc().timestamp();
|
||||
if device.netplay.is_some() {
|
||||
let netplay = device.netplay.as_mut().unwrap();
|
||||
if netplay.player_number == 0 {
|
||||
netplay::send_rtc(netplay, device.cart.rtc_timestamp);
|
||||
} else {
|
||||
device.cart.rtc_timestamp = netplay::receive_rtc(netplay);
|
||||
}
|
||||
}
|
||||
|
||||
device.cart.sc64.cfg[device::cart::sc64::SC64_BOOTLOADER_SWITCH as usize] = 1;
|
||||
|
||||
device.cart.rom = rom_file;
|
||||
set_system_region(device, device.cart.rom[0x3E]);
|
||||
device.cart.pal = is_system_pal(device.cart.rom[0x3E]);
|
||||
set_cic(device);
|
||||
|
||||
device.ui.game_hash = calculate_hash(&device.cart.rom);
|
||||
@@ -139,54 +151,42 @@ pub fn init(device: &mut device::Device, rom_file: Vec<u8>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_system_region(device: &mut device::Device, country: u8) {
|
||||
fn is_system_pal(country: u8) -> bool {
|
||||
let pal_codes: [u8; 8] = [b'D', b'F', b'I', b'P', b'S', b'U', b'X', b'Y'];
|
||||
for i in pal_codes {
|
||||
if country == i {
|
||||
device.cart.pal = true
|
||||
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_type = device::cart::CicType::CicNus6101;
|
||||
device.cart.cic_seed = 0x3F;
|
||||
device.cart.rdram_size_offset = 0x318;
|
||||
device.cart.cic_seed = 0x3F; // CicNus6101
|
||||
}
|
||||
"61E88238552C356C23D19409FE5570EE6910419586BC6FC740F638F761ADC46E" => {
|
||||
device.cart.cic_type = device::cart::CicType::CicNus6102;
|
||||
device.cart.cic_seed = 0x3F;
|
||||
device.cart.rdram_size_offset = 0x318;
|
||||
device.cart.cic_seed = 0x3F; // CicNus6102
|
||||
}
|
||||
"BF3620D30817007091EBE9BDDD1B88C23B8A0052170B3309CDE5B6B4238E45E7" => {
|
||||
device.cart.cic_type = device::cart::CicType::CicNus6103;
|
||||
device.cart.cic_seed = 0x78;
|
||||
device.cart.rdram_size_offset = 0x318;
|
||||
device.cart.cic_seed = 0x78; // CicNus6103
|
||||
}
|
||||
"04B7BC6717A9F0EB724CF927E74AD3876C381CBB280D841736FC5E55580B756B" => {
|
||||
device.cart.cic_type = device::cart::CicType::CicNus6105;
|
||||
device.cart.cic_seed = 0x91;
|
||||
device.cart.rdram_size_offset = 0x3F0;
|
||||
device.cart.cic_seed = 0x91; // CicNus6105
|
||||
}
|
||||
"36ADC40148AF56F0D78CD505EB6A90117D1FD6F11C6309E52ED36BC4C6BA340E" => {
|
||||
device.cart.cic_type = device::cart::CicType::CicNus6106;
|
||||
device.cart.cic_seed = 0x85;
|
||||
device.cart.rdram_size_offset = 0x318;
|
||||
device.cart.cic_seed = 0x85; // CicNus6106
|
||||
}
|
||||
"53C0088FB777870D0AF32F0251E964030E2E8B72E830C26042FD191169508C05" => {
|
||||
device.cart.cic_type = device::cart::CicType::CicNus5167;
|
||||
device.cart.cic_seed = 0xdd;
|
||||
device.cart.rdram_size_offset = 0x318;
|
||||
device.cart.cic_seed = 0xdd; // CicNus5167
|
||||
device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] = 1;
|
||||
device.cart.sc64.cfg[device::cart::sc64::SC64_BOOTLOADER_SWITCH as usize] = 0;
|
||||
}
|
||||
_ => {
|
||||
device.cart.cic_type = device::cart::CicType::CicNus6102;
|
||||
device.cart.cic_seed = 0x3F;
|
||||
device.cart.rdram_size_offset = 0x318;
|
||||
println!("unknown IPL3 {}", hash)
|
||||
device.cart.cic_seed = 0x3F; // CicNus6102
|
||||
//println!("unknown IPL3 {}", hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+124
-30
@@ -1,4 +1,5 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub const SDCARD_SIZE: usize = 0x4000000;
|
||||
|
||||
@@ -11,25 +12,28 @@ const SC64_KEY_REG: u32 = 4;
|
||||
//const SC64_AUX_REG: u32 = 6;
|
||||
pub const SC64_REGS_COUNT: u32 = 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: u32 = 15;
|
||||
|
||||
const SC64_BUFFER_MASK: usize = 0x1FFF;
|
||||
const SC64_EEPROM_MASK: usize = 0xFFF;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Sc64 {
|
||||
#[serde(with = "serde_big_array::BigArray")]
|
||||
pub buffer: [u8; 8192],
|
||||
pub buffer: Vec<u8>,
|
||||
pub regs: [u32; SC64_REGS_COUNT as usize],
|
||||
pub regs_locked: bool,
|
||||
pub cfg: [u32; SC64_CFG_COUNT as usize],
|
||||
pub sector: u32,
|
||||
pub writeback_sector: Vec<u32>,
|
||||
}
|
||||
|
||||
fn format_sdcard(device: &mut device::Device) {
|
||||
if device.ui.saves.sdcard.0.is_empty() {
|
||||
device.ui.saves.sdcard.0.resize(SDCARD_SIZE, 0);
|
||||
let buf = std::io::Cursor::new(&mut device.ui.saves.sdcard.0);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -74,6 +78,11 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
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 as usize] = (2 << 16) | 20;
|
||||
device.cart.sc64.regs[SC64_DATA1_REG as usize] = 2;
|
||||
}
|
||||
'c' => {
|
||||
// get config
|
||||
device.cart.sc64.regs[SC64_DATA1_REG as usize] = device.cart.sc64.cfg
|
||||
@@ -81,6 +90,35 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
}
|
||||
'C' => {
|
||||
// set config
|
||||
if device.cart.sc64.regs[SC64_DATA0_REG as usize] == 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 as usize] {
|
||||
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 as usize]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
std::mem::swap(
|
||||
&mut device.cart.sc64.cfg
|
||||
[device.cart.sc64.regs[SC64_DATA0_REG as usize] as usize],
|
||||
@@ -117,9 +155,10 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
let mut i = 0;
|
||||
|
||||
while i < length {
|
||||
if offset + i < device.ui.saves.sdcard.0.len() {
|
||||
if offset + i < device.ui.storage.saves.sdcard.data.len() {
|
||||
let data = u32::from_be_bytes(
|
||||
device.ui.saves.sdcard.0[(offset + i)..(offset + i + 4)]
|
||||
device.ui.storage.saves.sdcard.data
|
||||
[(offset + i)..(offset + i + 4)]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
@@ -148,7 +187,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
let mut i = 0;
|
||||
|
||||
while i < length {
|
||||
if offset + i < device.ui.saves.sdcard.0.len() {
|
||||
if offset + i < device.ui.storage.saves.sdcard.data.len() {
|
||||
let data = device::memory::data_read(
|
||||
device,
|
||||
address + i as u64,
|
||||
@@ -156,17 +195,33 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
false,
|
||||
)
|
||||
.to_be_bytes();
|
||||
device.ui.saves.sdcard.0[(offset + i)..(offset + i + 4)]
|
||||
device.ui.storage.saves.sdcard.data[(offset + i)..(offset + i + 4)]
|
||||
.copy_from_slice(&data);
|
||||
} else {
|
||||
panic!("sd card write out of bounds")
|
||||
}
|
||||
i += 4;
|
||||
}
|
||||
device.ui.saves.sdcard.1 = true;
|
||||
device.ui.storage.saves.sdcard.written = true;
|
||||
}
|
||||
'U' | 'u' => {} // USB status, ignored
|
||||
'M' | 'm' => {} // USB read/write, ignored
|
||||
'w' => {
|
||||
// SD card writeback pending
|
||||
device.cart.sc64.regs[SC64_DATA0_REG as usize] = 0;
|
||||
}
|
||||
'W' => {
|
||||
let writeback_sectors_address =
|
||||
device.cart.sc64.regs[SC64_DATA0_REG as usize] 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
'U' => {} // USB_WRITE_STATUS, ignored
|
||||
'M' => {} // USB_WRITE, ignored
|
||||
_ => {
|
||||
panic!(
|
||||
"unknown sc64 command: {}",
|
||||
@@ -190,24 +245,48 @@ pub fn read_mem(
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
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(),
|
||||
)
|
||||
if address & 0x2000 != 0 {
|
||||
device::cart::format_eeprom(device);
|
||||
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) {
|
||||
let masked_address = address as usize & SC64_BUFFER_MASK;
|
||||
let mut data = u32::from_be_bytes(
|
||||
if address & 0x2000 != 0 {
|
||||
device::cart::format_eeprom(device);
|
||||
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());
|
||||
device.ui.storage.saves.eeprom.written = true
|
||||
} 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]
|
||||
.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());
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dma_read(
|
||||
@@ -217,12 +296,20 @@ pub fn dma_read(
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= SC64_BUFFER_MASK as u32;
|
||||
let buffer = if cart_addr & 0x2000 != 0 {
|
||||
device::cart::format_eeprom(device);
|
||||
cart_addr &= SC64_EEPROM_MASK as u32;
|
||||
device.ui.storage.saves.eeprom.written = true;
|
||||
&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 && i < device.rdram.size {
|
||||
device.cart.sc64.buffer[j as usize] = device.rdram.mem[i as usize ^ device.byte_swap];
|
||||
buffer[j as usize] = device.rdram.mem[i as usize ^ device.byte_swap];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
@@ -237,12 +324,19 @@ pub fn dma_write(
|
||||
length: u32,
|
||||
) -> u64 {
|
||||
dram_addr &= device::rdram::RDRAM_MASK as u32;
|
||||
cart_addr &= SC64_BUFFER_MASK as u32;
|
||||
let buffer = if cart_addr & 0x2000 != 0 {
|
||||
device::cart::format_eeprom(device);
|
||||
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 && i < device.rdram.size {
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = device.cart.sc64.buffer[j as usize];
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = buffer[j as usize];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
+52
-27
@@ -29,14 +29,20 @@ pub struct Flashram {
|
||||
}
|
||||
|
||||
fn format_sram(device: &mut device::Device) {
|
||||
if device.ui.saves.sram.0.len() < SRAM_SIZE {
|
||||
device.ui.saves.sram.0.resize(SRAM_SIZE, 0xFF)
|
||||
if device.ui.storage.saves.sram.data.len() < SRAM_SIZE {
|
||||
device.ui.storage.saves.sram.data.resize(SRAM_SIZE, 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
fn format_flash(device: &mut device::Device) {
|
||||
if device.ui.saves.flash.0.len() < FLASHRAM_SIZE {
|
||||
device.ui.saves.flash.0.resize(FLASHRAM_SIZE, 0xFF)
|
||||
if device.ui.storage.saves.flash.data.len() < FLASHRAM_SIZE {
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.saves
|
||||
.flash
|
||||
.data
|
||||
.resize(FLASHRAM_SIZE, 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +52,7 @@ fn read_mem_sram(device: &mut device::Device, address: u64) -> u32 {
|
||||
format_sram(device);
|
||||
|
||||
u32::from_be_bytes(
|
||||
device.ui.saves.sram.0[masked_address..masked_address + 4]
|
||||
device.ui.storage.saves.sram.data[masked_address..masked_address + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
@@ -75,7 +81,12 @@ pub fn read_mem(
|
||||
let cycles = device::pi::calculate_cycles(device, 2, 4);
|
||||
device::cop0::add_cycles(device, cycles);
|
||||
|
||||
if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) {
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Sram)
|
||||
{
|
||||
read_mem_sram(device, address)
|
||||
} else {
|
||||
read_mem_flash(device, address)
|
||||
@@ -88,14 +99,15 @@ fn write_mem_sram(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
format_sram(device);
|
||||
|
||||
let mut data = u32::from_be_bytes(
|
||||
device.ui.saves.sram.0[masked_address..masked_address + 4]
|
||||
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.saves.sram.0[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes());
|
||||
device.ui.storage.saves.sram.data[masked_address..masked_address + 4]
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
|
||||
device.ui.saves.sram.1 = true
|
||||
device.ui.storage.saves.sram.written = true
|
||||
}
|
||||
|
||||
fn write_mem_flash(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
@@ -113,7 +125,12 @@ fn write_mem_flash(device: &mut device::Device, address: u64, value: u32, mask:
|
||||
}
|
||||
|
||||
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) {
|
||||
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)
|
||||
@@ -122,11 +139,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
|
||||
device.pi.regs[device::pi::PI_STATUS_REG as usize] |= 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,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles,
|
||||
);
|
||||
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) {
|
||||
@@ -138,12 +151,13 @@ fn dma_read_sram(device: &mut device::Device, mut cart_addr: u32, mut dram_addr:
|
||||
format_sram(device);
|
||||
|
||||
while i < dram_addr + length && i < device.rdram.size {
|
||||
device.ui.saves.sram.0[j as usize] = device.rdram.mem[i as usize ^ device.byte_swap];
|
||||
device.ui.storage.saves.sram.data[j as usize] =
|
||||
device.rdram.mem[i as usize ^ device.byte_swap];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
device.ui.saves.sram.1 = true
|
||||
device.ui.storage.saves.sram.written = true
|
||||
}
|
||||
|
||||
fn dma_read_flash(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) {
|
||||
@@ -166,7 +180,12 @@ fn dma_read_flash(device: &mut device::Device, cart_addr: u32, dram_addr: u32, l
|
||||
|
||||
// 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.save_type.contains(&ui::storage::SaveTypes::Sram) {
|
||||
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)
|
||||
@@ -188,7 +207,8 @@ fn dma_write_sram(
|
||||
format_sram(device);
|
||||
|
||||
while i < dram_addr + length && i < device.rdram.size {
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] = device.ui.saves.sram.0[j as usize];
|
||||
device.rdram.mem[i as usize ^ device.byte_swap] =
|
||||
device.ui.storage.saves.sram.data[j as usize];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
@@ -231,7 +251,7 @@ fn dma_write_flash(
|
||||
/* do actual DMA */
|
||||
for i in 0..length {
|
||||
device.rdram.mem[(dram_addr + i) as usize ^ device.byte_swap] =
|
||||
device.ui.saves.flash.0[(cart_addr + i) as usize];
|
||||
device.ui.storage.saves.flash.data[(cart_addr + i) as usize];
|
||||
}
|
||||
} else {
|
||||
/* other accesses are not implemented */
|
||||
@@ -241,7 +261,12 @@ fn dma_write_flash(
|
||||
|
||||
// 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.save_type.contains(&ui::storage::SaveTypes::Sram) {
|
||||
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)
|
||||
@@ -270,14 +295,14 @@ fn flashram_command(device: &mut device::Device, command: u32) {
|
||||
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.saves.flash.0[offset + i] = 0xFF;
|
||||
device.ui.storage.saves.flash.data[offset + i] = 0xFF;
|
||||
}
|
||||
device.ui.saves.flash.1 = true
|
||||
device.ui.storage.saves.flash.written = true
|
||||
} else if device.cart.flashram.mode == FlashramMode::ChipErase {
|
||||
for i in 0..FLASHRAM_SIZE {
|
||||
device.ui.saves.flash.0[i] = 0xFF;
|
||||
device.ui.storage.saves.flash.data[i] = 0xFF;
|
||||
}
|
||||
device.ui.saves.flash.1 = true
|
||||
device.ui.storage.saves.flash.written = true
|
||||
} else {
|
||||
panic!("Unexpected flash erase command")
|
||||
}
|
||||
@@ -295,9 +320,9 @@ fn flashram_command(device: &mut device::Device, command: u32) {
|
||||
/* program selected page */
|
||||
let offset: usize = (command & 0xffff) as usize * 128;
|
||||
for i in 0..128 {
|
||||
device.ui.saves.flash.0[offset + i] = device.cart.flashram.page_buf[i];
|
||||
device.ui.storage.saves.flash.data[offset + i] = device.cart.flashram.page_buf[i];
|
||||
}
|
||||
device.ui.saves.flash.1 = true;
|
||||
device.ui.storage.saves.flash.written = true;
|
||||
|
||||
/* clear program busy flag, set program success flag, transition to status mode */
|
||||
device.cart.flashram.status &= !0x01;
|
||||
|
||||
+17
-10
@@ -1,5 +1,6 @@
|
||||
use crate::{device, netplay, savestates, ui};
|
||||
|
||||
pub mod gbcart;
|
||||
pub mod mempak;
|
||||
pub mod rumble;
|
||||
pub mod transferpak;
|
||||
@@ -56,18 +57,18 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
JCMD_CONTROLLER_READ => {
|
||||
let offset = device.pif.channels[channel].rx_buf.unwrap();
|
||||
let input = if device.netplay.is_none() {
|
||||
ui::input::get(&mut device.ui, channel)
|
||||
ui::input::get(&device.ui, channel)
|
||||
} else {
|
||||
if device.netplay.as_ref().unwrap().player_number as usize == channel {
|
||||
let local_input = ui::input::get(&mut device.ui, 0);
|
||||
let local_input = ui::input::get(&device.ui, 0);
|
||||
netplay::send_input(device.netplay.as_ref().unwrap(), local_input);
|
||||
}
|
||||
|
||||
netplay::get_input(device, channel)
|
||||
};
|
||||
|
||||
device.pif.ram[offset..offset + 4].copy_from_slice(&input.0.to_ne_bytes());
|
||||
if input.1 {
|
||||
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 =
|
||||
@@ -76,8 +77,7 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_PAK,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]
|
||||
+ (device.cpu.clock_rate), // 1 second
|
||||
device.cpu.clock_rate, // 1 second
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -163,14 +163,21 @@ pub fn pak_switch_event(device: &mut device::Device) {
|
||||
if channel.change_pak != PakType::None {
|
||||
//stop rumble if it is on
|
||||
if device.netplay.is_none() {
|
||||
device::ui::input::set_rumble(&mut device.ui, i, 0);
|
||||
device::ui::input::set_rumble(&device.ui, i, 0);
|
||||
} else if device.netplay.as_ref().unwrap().player_number as usize == i {
|
||||
device::ui::input::set_rumble(&mut device.ui, 0, 0);
|
||||
device::ui::input::set_rumble(&device.ui, 0, 0);
|
||||
}
|
||||
|
||||
let new_pak_type = match channel.change_pak {
|
||||
PakType::MemPak => PakType::RumblePak,
|
||||
PakType::RumblePak => PakType::MemPak,
|
||||
PakType::RumblePak => {
|
||||
if device.transferpaks[i].cart.rom.is_empty() {
|
||||
PakType::MemPak
|
||||
} else {
|
||||
PakType::TransferPak
|
||||
}
|
||||
}
|
||||
PakType::TransferPak => PakType::MemPak,
|
||||
_ => {
|
||||
panic!("Invalid pak type");
|
||||
}
|
||||
@@ -195,7 +202,7 @@ pub fn pak_switch_event(device: &mut device::Device) {
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
}
|
||||
ui::audio::play_pak_switch(&mut device.ui, new_pak_type);
|
||||
ui::audio::play_pak_switch(&device.ui, new_pak_type);
|
||||
channel.change_pak = PakType::None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
use crate::device;
|
||||
|
||||
#[derive(Default, 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 set_latch: bool,
|
||||
pub latch_second: u8,
|
||||
pub latch_minute: u8,
|
||||
pub latch_hour: u8,
|
||||
pub latch_day: u16,
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum CartType {
|
||||
#[default]
|
||||
None,
|
||||
MBC1RamBatt,
|
||||
MBC3RamBatt,
|
||||
MBC3RamBattRtc,
|
||||
MBC5RamBatt,
|
||||
}
|
||||
|
||||
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,
|
||||
) {
|
||||
let value = pif_ram[data + size - 1];
|
||||
if address < 0x2000 {
|
||||
cart.ram_enabled = value & 0xf == 0xa;
|
||||
} else if address < 0x4000 {
|
||||
let bank = value & 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.set_latch && value != 0 {
|
||||
// not implemented
|
||||
}
|
||||
cart.set_latch = value != 0;
|
||||
} 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 {
|
||||
match cart.ram_bank {
|
||||
0x8 => {
|
||||
cart.latch_second = value;
|
||||
}
|
||||
0x9 => {
|
||||
cart.latch_minute = value;
|
||||
}
|
||||
0xA => {
|
||||
cart.latch_hour = value;
|
||||
}
|
||||
0xB => {
|
||||
cart.latch_day &= 0xFF00;
|
||||
cart.latch_day |= value as u16;
|
||||
}
|
||||
0xC => {
|
||||
cart.latch_day &= 0x00FF;
|
||||
cart.latch_day |= (value as u16) << 8;
|
||||
}
|
||||
_ => {
|
||||
panic!("Unsupported ram bank {:x}", cart.ram_bank);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Unsupported write address {:x}", address);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_mbc3(
|
||||
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 < 0x8 {
|
||||
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],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let latch = match cart.ram_bank {
|
||||
0x8 => cart.latch_second,
|
||||
0x9 => cart.latch_minute,
|
||||
0xA => cart.latch_hour,
|
||||
0xB => cart.latch_day as u8,
|
||||
0xC => (cart.latch_day >> 8) as u8,
|
||||
_ => {
|
||||
panic!("Unsupported ram bank {:x}", cart.ram_bank);
|
||||
}
|
||||
};
|
||||
for i in 0..size {
|
||||
pif_ram[data + i] = latch;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Unsupported read address {:x}", address);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
println!("Unknown MBC5 write address {:x}", address);
|
||||
} 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 {:x}", address);
|
||||
}
|
||||
}
|
||||
|
||||
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 {:x}", address);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
pif_ram: &mut [u8],
|
||||
cart: &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,
|
||||
) {
|
||||
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),
|
||||
CartType::MBC3RamBattRtc => write_mbc3(pif_ram, cart, address, data, size),
|
||||
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 {:x}", data),
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,14 @@ pub const MEMPAK_SIZE: usize = 0x8000;
|
||||
const MPK_PAGE_SIZE: usize = 256;
|
||||
|
||||
fn format_mempak(device: &mut device::Device) {
|
||||
if device.ui.saves.mempak.0.len() < MEMPAK_SIZE * 4 {
|
||||
device.ui.saves.mempak.0.resize(MEMPAK_SIZE * 4, 0);
|
||||
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 */
|
||||
@@ -38,28 +44,31 @@ fn format_mempak(device: &mut device::Device) {
|
||||
let offset = i * MEMPAK_SIZE;
|
||||
|
||||
/* Fill Page 0 with pre-initialized content */
|
||||
device.ui.saves.mempak.0[offset..offset + MPK_PAGE_SIZE].copy_from_slice(&page_0);
|
||||
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.saves.mempak.0[offset + i] = 0;
|
||||
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.saves.mempak.0[offset + i] = 0x00;
|
||||
device.ui.saves.mempak.0[offset + i + 1] = 0x03;
|
||||
device.ui.storage.saves.mempak.data[offset + i] = 0x00;
|
||||
device.ui.storage.saves.mempak.data[offset + i + 1] = 0x03;
|
||||
}
|
||||
device.ui.saves.mempak.0[offset + (MPK_PAGE_SIZE + 1)] = 0x71;
|
||||
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.saves.mempak.0[page1..page1 + MPK_PAGE_SIZE].to_vec();
|
||||
device.ui.saves.mempak.0[page2..page2 + MPK_PAGE_SIZE].copy_from_slice(&page1data);
|
||||
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.saves.mempak.0[offset + i] = 0;
|
||||
device.ui.storage.saves.mempak.data[offset + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +80,7 @@ pub fn read(device: &mut device::Device, channel: usize, address: u16, data: usi
|
||||
|
||||
let offset = (channel * MEMPAK_SIZE) + address as usize;
|
||||
device.pif.ram[data..data + size]
|
||||
.copy_from_slice(&device.ui.saves.mempak.0[offset..offset + 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;
|
||||
@@ -84,9 +93,9 @@ pub fn write(device: &mut device::Device, channel: usize, address: u16, data: us
|
||||
format_mempak(device);
|
||||
|
||||
let offset = (channel * MEMPAK_SIZE) + address as usize;
|
||||
device.ui.saves.mempak.0[offset..offset + size]
|
||||
device.ui.storage.saves.mempak.data[offset..offset + size]
|
||||
.copy_from_slice(&device.pif.ram[data..data + size]);
|
||||
|
||||
device.ui.saves.mempak.1 = true
|
||||
device.ui.storage.saves.mempak.written = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ pub fn write(device: &mut device::Device, channel: usize, address: u16, data: us
|
||||
if address == 0xc000 {
|
||||
let rumble = device.pif.ram[data + size - 1];
|
||||
if device.netplay.is_none() {
|
||||
device::ui::input::set_rumble(&mut device.ui, channel, rumble);
|
||||
device::ui::input::set_rumble(&device.ui, channel, rumble);
|
||||
} else if device.netplay.as_ref().unwrap().player_number as usize == channel {
|
||||
device::ui::input::set_rumble(&mut device.ui, 0, rumble);
|
||||
device::ui::input::set_rumble(&device.ui, 0, rumble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,133 @@
|
||||
use crate::device;
|
||||
|
||||
pub fn read(
|
||||
_device: &mut device::Device,
|
||||
_channel: usize,
|
||||
_address: u16,
|
||||
_data: usize,
|
||||
_size: usize,
|
||||
) {
|
||||
#[derive(Default, 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 write(
|
||||
_device: &mut device::Device,
|
||||
_channel: usize,
|
||||
_address: u16,
|
||||
_data: usize,
|
||||
_size: usize,
|
||||
) {
|
||||
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,
|
||||
&pak.cart,
|
||||
0x4000 * pak.bank + (address & 0x7fff) - 0x4000,
|
||||
data,
|
||||
size,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
panic!("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;
|
||||
}
|
||||
_ => panic!("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,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
panic!("unknown transfer pak write {:x}", address >> 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,7 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_VRU,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]
|
||||
+ (device.cpu.clock_rate * 2), // 2 seconds
|
||||
device.cpu.clock_rate * 2, // 2 seconds
|
||||
)
|
||||
} else if device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] == 0xEF {
|
||||
device.vru.talking = false;
|
||||
|
||||
+2
-2
@@ -215,7 +215,7 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64)
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_COMPARE,
|
||||
device.cpu.cop0.regs[COP0_COUNT_REG as usize] + ((compare_event_diff as u64) << 1),
|
||||
(compare_event_diff as u64) << 1,
|
||||
);
|
||||
device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_IP7;
|
||||
device.cpu.cop0.pending_compare_interrupt = false;
|
||||
@@ -237,7 +237,7 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64)
|
||||
|
||||
pub fn compare_event(device: &mut device::Device) {
|
||||
device.cpu.cop0.pending_compare_interrupt = true;
|
||||
device::events::create_event(
|
||||
device::events::create_event_at(
|
||||
device,
|
||||
device::events::EVENT_TYPE_COMPARE,
|
||||
device.cpu.next_event_count + (u32::MAX as u64),
|
||||
|
||||
+6
-1
@@ -30,6 +30,7 @@ pub struct Cpu {
|
||||
pub hi: u64,
|
||||
pub running: bool,
|
||||
pub llbit: bool,
|
||||
pub overclock: bool,
|
||||
pub clock_rate: u64,
|
||||
#[serde(skip, default = "savestates::default_instructions")]
|
||||
pub instrs: [fn(&mut device::Device, u32); 64],
|
||||
@@ -223,7 +224,11 @@ pub fn map_instructions(device: &mut device::Device) {
|
||||
}
|
||||
|
||||
pub fn init(device: &mut device::Device) {
|
||||
device.cpu.clock_rate = 93750000;
|
||||
device.cpu.clock_rate = if !device.cpu.overclock {
|
||||
93750000
|
||||
} else {
|
||||
125000000
|
||||
};
|
||||
|
||||
map_instructions(device);
|
||||
|
||||
|
||||
+10
-2
@@ -21,6 +21,14 @@ pub struct Event {
|
||||
}
|
||||
|
||||
pub fn create_event(device: &mut device::Device, name: usize, when: u64) {
|
||||
device.cpu.events[name] = Event {
|
||||
enabled: true,
|
||||
count: device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + when,
|
||||
};
|
||||
set_next_event(device);
|
||||
}
|
||||
|
||||
pub fn create_event_at(device: &mut device::Device, name: usize, when: u64) {
|
||||
device.cpu.events[name] = Event {
|
||||
enabled: true,
|
||||
count: when,
|
||||
@@ -28,9 +36,9 @@ pub fn create_event(device: &mut device::Device, name: usize, when: u64) {
|
||||
set_next_event(device);
|
||||
}
|
||||
|
||||
pub fn get_event(device: &mut device::Device, name: usize) -> Option<&mut Event> {
|
||||
pub fn get_event(device: &device::Device, name: usize) -> Option<&Event> {
|
||||
if device.cpu.events[name].enabled {
|
||||
return Some(&mut device.cpu.events[name]);
|
||||
return Some(&device.cpu.events[name]);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -33,11 +33,7 @@ pub fn check_pending_interrupts(device: &mut device::Device) {
|
||||
if device.cpu.cop0.is_event {
|
||||
interrupt_exception(device);
|
||||
} else {
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_INT,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize],
|
||||
);
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_INT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ pub enum AccessSize {
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Memory {
|
||||
#[serde(skip, default = "savestates::default_memory_read")]
|
||||
pub fast_read: [fn(&mut device::Device, u64, AccessSize) -> u32; 0x2000], // fast_read is used for lookups that try to detect idle loops
|
||||
#[serde(skip, default = "savestates::default_memory_read_fast")]
|
||||
pub fast_read: [fn(&device::Device, u64, AccessSize) -> u32; 0x2000], // fast_read is used for lookups that try to detect idle loops
|
||||
#[serde(skip, default = "savestates::default_memory_read")]
|
||||
pub memory_map_read: [fn(&mut device::Device, u64, AccessSize) -> u32; 0x2000],
|
||||
#[serde(skip, default = "savestates::default_memory_write")]
|
||||
@@ -153,13 +153,16 @@ pub fn init(device: &mut device::Device) {
|
||||
} else if (MM_DOM2_ADDR2 >> 16..=(MM_DOM2_ADDR2 + 0x1FFFF) >> 16).contains(&i) {
|
||||
device.memory.memory_map_read[i] = device::cart::sram::read_mem;
|
||||
device.memory.memory_map_write[i] = device::cart::sram::write_mem;
|
||||
} else if i >= MM_CART_ROM >> 16 && i <= (MM_CART_ROM + device.cart.rom.len() - 1) >> 16 {
|
||||
} else if device.cart.rom.len() <= MM_IS_VIEWER - MM_CART_ROM
|
||||
&& i >= MM_IS_VIEWER >> 16
|
||||
&& i <= (MM_IS_VIEWER + 0xFFFF) >> 16
|
||||
{
|
||||
device.memory.memory_map_read[i] = device::is_viewer::read_mem;
|
||||
device.memory.memory_map_write[i] = device::is_viewer::write_mem;
|
||||
} else if (MM_CART_ROM >> 16..=(MM_CART_ROM + 0x0FBFFFFF) >> 16).contains(&i) {
|
||||
device.memory.fast_read[i] = device::cart::rom::read_mem_fast;
|
||||
device.memory.memory_map_read[i] = device::cart::rom::read_mem;
|
||||
device.memory.memory_map_write[i] = device::cart::rom::write_mem;
|
||||
} else if i >= MM_IS_VIEWER >> 16 && i <= (MM_IS_VIEWER + 0xFFFF) >> 16 {
|
||||
device.memory.memory_map_read[i] = device::is_viewer::read_mem;
|
||||
device.memory.memory_map_write[i] = device::is_viewer::write_mem;
|
||||
} else if i >= MM_PIF_MEM >> 16 && i <= (MM_PIF_MEM + 0xFFFF) >> 16 {
|
||||
device.memory.memory_map_read[i] = device::pif::read_mem;
|
||||
device.memory.memory_map_write[i] = device::pif::write_mem;
|
||||
|
||||
+9
-11
@@ -1,4 +1,5 @@
|
||||
use crate::device;
|
||||
use rand_chacha::rand_core::RngCore;
|
||||
|
||||
const PI_DRAM_ADDR_REG: u32 = 0;
|
||||
const PI_CART_ADDR_REG: u32 = 1;
|
||||
@@ -65,11 +66,7 @@ fn dma_read(device: &mut device::Device) {
|
||||
|
||||
let cycles = (handler.read)(device, cart_addr, dram_addr, length);
|
||||
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_PI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles,
|
||||
);
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles);
|
||||
|
||||
/* Update PI_DRAM_ADDR_REG and PI_CART_ADDR_REG */
|
||||
device.pi.regs[PI_DRAM_ADDR_REG as usize] =
|
||||
@@ -97,11 +94,7 @@ fn dma_write(device: &mut device::Device) {
|
||||
|
||||
let cycles = (handler.write)(device, cart_addr, dram_addr, length);
|
||||
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_PI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles,
|
||||
);
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles);
|
||||
|
||||
/* Update PI_DRAM_ADDR_REG and PI_CART_ADDR_REG */
|
||||
device.pi.regs[PI_DRAM_ADDR_REG as usize] =
|
||||
@@ -172,7 +165,11 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_cycles(device: &device::Device, domain: i32, length: u32) -> u64 {
|
||||
fn randomize_interrupt_time(rng: &mut rand_chacha::ChaCha8Rng) -> u64 {
|
||||
rng.next_u64() % 0x10
|
||||
}
|
||||
|
||||
pub fn calculate_cycles(device: &mut device::Device, domain: i32, length: u32) -> u64 {
|
||||
let mut cycles: f64 = 0.0;
|
||||
let (page_size, latency, pulse_width, release, pages);
|
||||
let page_size_base: f64 = 2.0;
|
||||
@@ -195,6 +192,7 @@ pub fn calculate_cycles(device: &device::Device, domain: i32, length: u32) -> u6
|
||||
cycles += (14.0 + latency) * pages;
|
||||
cycles += (pulse_width + release) * (length as f64 / 2.0);
|
||||
cycles += 5.0 * pages;
|
||||
cycles += randomize_interrupt_time(&mut device.rng) as f64;
|
||||
(cycles * 1.5) as u64 // Converting RCP clock speed to CPU clock speed
|
||||
}
|
||||
|
||||
|
||||
+30
-12
@@ -55,7 +55,7 @@ pub fn read_mem(
|
||||
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
let mut masked_address = address as usize & PIF_MASK;
|
||||
if masked_address < PIF_RAM_OFFSET {
|
||||
panic! {"write to pif rom"}
|
||||
panic!("write to pif rom")
|
||||
}
|
||||
masked_address -= PIF_RAM_OFFSET;
|
||||
let mut data = u32::from_be_bytes(
|
||||
@@ -67,11 +67,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
|
||||
device.pif.ram[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes());
|
||||
|
||||
device.si.dma_dir = device::si::DmaDir::Write;
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_SI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + 3200,
|
||||
); //based on https://github.com/rasky/n64-systembench
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_SI, 3200); //based on https://github.com/rasky/n64-systembench
|
||||
device.si.regs[device::si::SI_STATUS_REG as usize] |=
|
||||
device::si::SI_STATUS_DMA_BUSY | device::si::SI_STATUS_IO_BUSY
|
||||
}
|
||||
@@ -236,6 +232,23 @@ pub fn connect_pif_channels(device: &mut device::Device) {
|
||||
device.pif.channels[4].process = Some(device::cart::process)
|
||||
}
|
||||
|
||||
fn get_default_handler(device: &device::Device) -> device::controller::PakHandler {
|
||||
if device.ui.game_id == "NCT" {
|
||||
// Chameleon Twist does not support the mempak
|
||||
device::controller::PakHandler {
|
||||
read: device::controller::rumble::read,
|
||||
write: device::controller::rumble::write,
|
||||
pak_type: device::controller::PakType::RumblePak,
|
||||
}
|
||||
} else {
|
||||
device::controller::PakHandler {
|
||||
read: device::controller::mempak::read,
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: device::controller::PakType::MemPak,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(device: &mut device::Device) {
|
||||
if device.cart.pal {
|
||||
device.pif.rom = rom::PAL_PIF_ROM;
|
||||
@@ -245,10 +258,11 @@ pub fn init(device: &mut device::Device) {
|
||||
device.pif.ram[0x26] = device.cart.cic_seed;
|
||||
device.pif.ram[0x27] = device.cart.cic_seed;
|
||||
|
||||
let mempak_handler = device::controller::PakHandler {
|
||||
read: device::controller::mempak::read,
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: device::controller::PakType::MemPak,
|
||||
let default_handler = get_default_handler(device);
|
||||
let tpak_handler = device::controller::PakHandler {
|
||||
read: device::controller::transferpak::read,
|
||||
write: device::controller::transferpak::write,
|
||||
pak_type: device::controller::PakType::TransferPak,
|
||||
};
|
||||
|
||||
connect_pif_channels(device);
|
||||
@@ -256,10 +270,14 @@ pub fn init(device: &mut device::Device) {
|
||||
for i in 0..4 {
|
||||
if device.netplay.is_none() {
|
||||
if device.ui.config.input.controller_enabled[i] {
|
||||
device.pif.channels[i].pak_handler = Some(mempak_handler);
|
||||
if device.transferpaks[i].cart.rom.is_empty() {
|
||||
device.pif.channels[i].pak_handler = Some(default_handler);
|
||||
} else {
|
||||
device.pif.channels[i].pak_handler = Some(tpak_handler);
|
||||
}
|
||||
}
|
||||
} else if device.netplay.as_ref().unwrap().player_data[i].reg_id != 0 {
|
||||
device.pif.channels[i].pak_handler = Some(mempak_handler);
|
||||
device.pif.channels[i].pak_handler = Some(default_handler);
|
||||
}
|
||||
}
|
||||
if device.ui.config.input.emulate_vru {
|
||||
|
||||
+1
-5
@@ -130,11 +130,7 @@ fn run_rdp(device: &mut device::Device) {
|
||||
device.load_state = false;
|
||||
savestates::load_savestate(device);
|
||||
}
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_DP,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + timer,
|
||||
)
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_DP, timer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
use crate::device;
|
||||
use std::alloc::{alloc_zeroed, Layout};
|
||||
use std::alloc::{Layout, alloc_zeroed};
|
||||
|
||||
//const RDRAM_CONFIG_REG: u32 = 0;
|
||||
//const RDRAM_DEVICE_ID_REG: u32 = 1;
|
||||
@@ -23,7 +23,7 @@ pub struct Rdram {
|
||||
}
|
||||
|
||||
pub fn read_mem_fast(
|
||||
device: &mut device::Device,
|
||||
device: &device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
@@ -103,13 +103,12 @@ pub fn init(device: &mut device::Device) {
|
||||
unsafe { Vec::from_raw_parts(ptr, device.rdram.size as usize, device.rdram.size as usize) };
|
||||
|
||||
// hack, skip RDRAM initialization
|
||||
device.rdram.mem[device.cart.rdram_size_offset..device.cart.rdram_size_offset + 4]
|
||||
.copy_from_slice(&device.rdram.size.to_ne_bytes());
|
||||
device.rdram.mem[0x318..0x318 + 4].copy_from_slice(&device.rdram.size.to_ne_bytes());
|
||||
// hack, skip RDRAM initialization
|
||||
device.rdram.mem[0x3f0..0x3f0 + 4].copy_from_slice(&device.rdram.size.to_ne_bytes());
|
||||
|
||||
device.ri.regs[device::ri::RI_MODE_REG as usize] = 0x0e;
|
||||
device.ri.regs[device::ri::RI_CONFIG_REG as usize] = 0x40;
|
||||
device.ri.regs[device::ri::RI_SELECT_REG as usize] = 0x14;
|
||||
device.ri.regs[device::ri::RI_REFRESH_REG as usize] = 0x00063634;
|
||||
}
|
||||
|
||||
pub fn rdram_calculate_cycles(length: u64) -> u64 {
|
||||
|
||||
+16
-9
@@ -22,17 +22,24 @@ pub fn read_regs(
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
device::cop0::add_cycles(device, 20);
|
||||
if ((address & 0xFFFF) >> 2) as u32 == RI_SELECT_REG && !device.ri.ram_init {
|
||||
device::cop0::add_cycles(device, device.cpu.clock_rate / 2); // hack, simulate RDRAM initialization
|
||||
device.ri.ram_init = true;
|
||||
let reg = (address & 0xFFFF) >> 2;
|
||||
match reg as u32 {
|
||||
RI_SELECT_REG => {
|
||||
if !device.ri.ram_init {
|
||||
device::cop0::add_cycles(device, device.cpu.clock_rate / 2); // hack, simulate RDRAM initialization
|
||||
device.ri.ram_init = true;
|
||||
}
|
||||
0x14 // hack, skip RDRAM initialization
|
||||
}
|
||||
RI_REFRESH_REG => 0x00063634, // hack, skip RDRAM initialization
|
||||
_ => device.ri.regs[reg as usize],
|
||||
}
|
||||
device.ri.regs[((address & 0xFFFF) >> 2) as usize]
|
||||
}
|
||||
|
||||
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
device::memory::masked_write_32(
|
||||
&mut device.ri.regs[((address & 0xFFFF) >> 2) as usize],
|
||||
value,
|
||||
mask,
|
||||
);
|
||||
let reg = (address & 0xFFFF) >> 2;
|
||||
if reg as u32 == RI_SELECT_REG {
|
||||
device.ri.ram_init = false;
|
||||
}
|
||||
device::memory::masked_write_32(&mut device.ri.regs[reg as usize], value, mask);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ pub struct Rsp {
|
||||
}
|
||||
|
||||
pub fn read_mem_fast(
|
||||
device: &mut device::Device,
|
||||
device: &device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
@@ -204,9 +204,7 @@ fn do_dma(device: &mut device::Device, dma: RspDma) {
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_SPDMA,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]
|
||||
+ device::rdram::rdram_calculate_cycles((count * length) as u64)
|
||||
+ 9,
|
||||
device::rdram::rdram_calculate_cycles((count * length) as u64) + 9,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -465,11 +463,7 @@ fn do_task(device: &mut device::Device) {
|
||||
} else {
|
||||
let timer = device::rsp_cpu::run(device);
|
||||
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_SP,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + timer,
|
||||
)
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_SP, timer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -317,10 +317,9 @@ pub fn lw(device: &mut device::Device, opcode: u32) {
|
||||
device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32);
|
||||
|
||||
let mut w = [0; 4];
|
||||
w[0] = device.rsp.mem[address as usize & 0xFFF];
|
||||
w[1] = device.rsp.mem[(address as usize + 1) & 0xFFF];
|
||||
w[2] = device.rsp.mem[(address as usize + 2) & 0xFFF];
|
||||
w[3] = device.rsp.mem[(address as usize + 3) & 0xFFF];
|
||||
for (i, item) in w.iter_mut().enumerate() {
|
||||
*item = device.rsp.mem[(address as usize + i) & 0xFFF];
|
||||
}
|
||||
|
||||
device.rsp.cpu.gpr[rt(opcode) as usize] =
|
||||
((w[0] as u32) << 24) | ((w[1] as u32) << 16) | ((w[2] as u32) << 8) | (w[3] as u32)
|
||||
@@ -349,10 +348,9 @@ pub fn lwu(device: &mut device::Device, opcode: u32) {
|
||||
device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32);
|
||||
|
||||
let mut w = [0; 4];
|
||||
w[0] = device.rsp.mem[address as usize & 0xFFF];
|
||||
w[1] = device.rsp.mem[(address as usize + 1) & 0xFFF];
|
||||
w[2] = device.rsp.mem[(address as usize + 2) & 0xFFF];
|
||||
w[3] = device.rsp.mem[(address as usize + 3) & 0xFFF];
|
||||
for (i, item) in w.iter_mut().enumerate() {
|
||||
*item = device.rsp.mem[(address as usize + i) & 0xFFF];
|
||||
}
|
||||
|
||||
device.rsp.cpu.gpr[rt(opcode) as usize] =
|
||||
((w[0] as u32) << 24) | ((w[1] as u32) << 16) | ((w[2] as u32) << 8) | (w[3] as u32)
|
||||
@@ -378,14 +376,10 @@ pub fn sw(device: &mut device::Device, opcode: u32) {
|
||||
let address =
|
||||
device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32);
|
||||
|
||||
device.rsp.mem[address as usize & 0xFFF] =
|
||||
(device.rsp.cpu.gpr[rt(opcode) as usize] >> 24) as u8;
|
||||
device.rsp.mem[(address as usize + 1) & 0xFFF] =
|
||||
(device.rsp.cpu.gpr[rt(opcode) as usize] >> 16) as u8;
|
||||
device.rsp.mem[(address as usize + 2) & 0xFFF] =
|
||||
(device.rsp.cpu.gpr[rt(opcode) as usize] >> 8) as u8;
|
||||
device.rsp.mem[(address as usize + 3) & 0xFFF] =
|
||||
(device.rsp.cpu.gpr[rt(opcode) as usize]) as u8;
|
||||
for i in 0..4 {
|
||||
device.rsp.mem[(address as usize + i) & 0xFFF] =
|
||||
(device.rsp.cpu.gpr[rt(opcode) as usize] >> ((3 - i) * 8)) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sll(device: &mut device::Device, opcode: u32) {
|
||||
@@ -606,19 +600,19 @@ pub fn mfc2(device: &mut device::Device, opcode: u32) {
|
||||
pub fn cfc2(device: &mut device::Device, opcode: u32) {
|
||||
let hi;
|
||||
let lo;
|
||||
let mut zero = unsafe { _mm_setzero_si128() };
|
||||
let zero = unsafe { _mm_setzero_si128() };
|
||||
match rd(opcode) & 3 {
|
||||
0x00 => {
|
||||
hi = &mut device.rsp.cpu.vcoh;
|
||||
lo = &mut device.rsp.cpu.vcol;
|
||||
hi = &device.rsp.cpu.vcoh;
|
||||
lo = &device.rsp.cpu.vcol;
|
||||
}
|
||||
0x01 => {
|
||||
hi = &mut device.rsp.cpu.vcch;
|
||||
lo = &mut device.rsp.cpu.vccl;
|
||||
hi = &device.rsp.cpu.vcch;
|
||||
lo = &device.rsp.cpu.vccl;
|
||||
}
|
||||
0x02 | 0x03 => {
|
||||
hi = &mut zero;
|
||||
lo = &mut device.rsp.cpu.vce;
|
||||
hi = &zero;
|
||||
lo = &device.rsp.cpu.vce;
|
||||
}
|
||||
_ => {
|
||||
panic!("unknown cfc2")
|
||||
|
||||
+12
-10
@@ -1,3 +1,5 @@
|
||||
use rand_chacha::rand_core::RngCore;
|
||||
|
||||
use crate::device;
|
||||
|
||||
const SI_DRAM_ADDR_REG: u32 = 0;
|
||||
@@ -36,6 +38,10 @@ pub fn read_regs(
|
||||
device.si.regs[((address & 0xFFFF) >> 2) as usize]
|
||||
}
|
||||
|
||||
fn randomize_interrupt_time(rng: &mut rand_chacha::ChaCha8Rng) -> u64 {
|
||||
rng.next_u64() % 0x100
|
||||
}
|
||||
|
||||
fn dma_read(device: &mut device::Device) {
|
||||
device.si.dma_dir = DmaDir::Read;
|
||||
|
||||
@@ -43,11 +49,9 @@ fn dma_read(device: &mut device::Device) {
|
||||
|
||||
device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_DMA_BUSY;
|
||||
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_SI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + duration,
|
||||
)
|
||||
let length = duration + randomize_interrupt_time(&mut device.rng);
|
||||
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_SI, length)
|
||||
}
|
||||
|
||||
fn dma_write(device: &mut device::Device) {
|
||||
@@ -57,11 +61,9 @@ fn dma_write(device: &mut device::Device) {
|
||||
|
||||
device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_DMA_BUSY;
|
||||
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_SI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + 6000, //based on https://github.com/rasky/n64-systembench
|
||||
)
|
||||
let length = 6000 + randomize_interrupt_time(&mut device.rng); //based on https://github.com/rasky/n64-systembench
|
||||
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_SI, length)
|
||||
}
|
||||
|
||||
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
|
||||
+2
-2
@@ -146,7 +146,7 @@ pub fn probe(device: &mut device::Device) {
|
||||
}
|
||||
|
||||
fn tlb_unmap(device: &mut device::Device, index: u64) {
|
||||
let e = &mut device.cpu.cop0.tlb_entries[index as usize];
|
||||
let e = &device.cpu.cop0.tlb_entries[index as usize];
|
||||
|
||||
if e.v_even != 0 {
|
||||
let mut i = e.start_even;
|
||||
@@ -184,7 +184,7 @@ fn tlb_unmap(device: &mut device::Device, index: u64) {
|
||||
}
|
||||
|
||||
fn tlb_map(device: &mut device::Device, index: u64) {
|
||||
let e = &mut device.cpu.cop0.tlb_entries[index as usize];
|
||||
let e = &device.cpu.cop0.tlb_entries[index as usize];
|
||||
|
||||
if e.v_even != 0
|
||||
&& e.start_even < e.end_even
|
||||
|
||||
+10
-2
@@ -1,7 +1,7 @@
|
||||
use crate::device;
|
||||
|
||||
pub fn read_mem(
|
||||
_device: &mut device::Device,
|
||||
pub fn read_mem_fast(
|
||||
_device: &device::Device,
|
||||
address: u64,
|
||||
_access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
@@ -9,4 +9,12 @@ pub fn read_mem(
|
||||
value | (value << 16)
|
||||
}
|
||||
|
||||
pub fn read_mem(
|
||||
device: &mut device::Device,
|
||||
address: u64,
|
||||
access_size: device::memory::AccessSize,
|
||||
) -> u32 {
|
||||
read_mem_fast(device, address, access_size)
|
||||
}
|
||||
|
||||
pub fn write_mem(_device: &mut device::Device, _address: u64, _value: u32, _mask: u32) {}
|
||||
|
||||
+7
-10
@@ -26,6 +26,7 @@ pub struct Vi {
|
||||
#[serde(skip)]
|
||||
pub limiter: Option<governor::DefaultDirectRateLimiter>,
|
||||
pub count_per_scanline: u64,
|
||||
pub enable_speed_limiter: bool,
|
||||
pub vi_counter: u64,
|
||||
pub last_origin: u32,
|
||||
pub internal_frame_counter: u64,
|
||||
@@ -64,11 +65,7 @@ fn create_limiter(device: &mut device::Device) {
|
||||
|
||||
fn set_vertical_interrupt(device: &mut device::Device) {
|
||||
if device::events::get_event(device, device::events::EVENT_TYPE_VI).is_none() {
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_VI,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.vi.delay,
|
||||
)
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_VI, device.vi.delay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +86,8 @@ fn set_current_line(device: &mut device::Device) {
|
||||
}
|
||||
/* update current field */
|
||||
device.vi.regs[VI_CURRENT_REG as usize] =
|
||||
(device.vi.regs[VI_CURRENT_REG as usize] & !1) | device.vi.field
|
||||
(device.vi.regs[VI_CURRENT_REG as usize] & !1) | device.vi.field;
|
||||
ui::video::set_register(VI_CURRENT_REG, device.vi.regs[VI_CURRENT_REG as usize])
|
||||
}
|
||||
|
||||
pub fn read_regs(
|
||||
@@ -139,14 +137,13 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
pub fn vertical_interrupt_event(device: &mut device::Device) {
|
||||
ui::video::check_callback(device);
|
||||
|
||||
let mut enable_speed_limiter = true;
|
||||
if device.netplay.is_some() {
|
||||
netplay::send_sync_check(device);
|
||||
enable_speed_limiter = !device.netplay.as_ref().unwrap().fast_forward;
|
||||
device.vi.enable_speed_limiter = !device.netplay.as_ref().unwrap().fast_forward;
|
||||
}
|
||||
|
||||
device.vi.vi_counter += 1;
|
||||
if device.vi.vi_counter % device.vi.limit_freq == 0 && enable_speed_limiter {
|
||||
if device.vi.vi_counter % device.vi.limit_freq == 0 && device.vi.enable_speed_limiter {
|
||||
speed_limiter(device);
|
||||
}
|
||||
ui::video::update_screen();
|
||||
@@ -164,7 +161,7 @@ pub fn vertical_interrupt_event(device: &mut device::Device) {
|
||||
|
||||
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_VI);
|
||||
|
||||
device::events::create_event(
|
||||
device::events::create_event_at(
|
||||
device,
|
||||
device::events::EVENT_TYPE_VI,
|
||||
device.cpu.next_event_count + device.vi.delay,
|
||||
|
||||
+14
-14
@@ -151,26 +151,26 @@ async fn main() {
|
||||
}
|
||||
} else if args.game.is_some() {
|
||||
let file_path = std::path::Path::new(args.game.as_ref().unwrap());
|
||||
let rom_contents = device::get_rom_contents(file_path);
|
||||
if rom_contents.is_empty() {
|
||||
if let Some(rom_contents) = device::get_rom_contents(file_path) {
|
||||
let handle = std::thread::Builder::new()
|
||||
.name("n64".to_string())
|
||||
.stack_size(env!("N64_STACK_SIZE").parse().unwrap())
|
||||
.spawn(move || {
|
||||
let mut device = device::Device::new();
|
||||
let overclock = device.ui.config.emulation.overclock;
|
||||
device::run_game(&mut device, rom_contents, args.fullscreen, overclock);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
handle.join().unwrap();
|
||||
} else {
|
||||
println!("Could not read rom file");
|
||||
return;
|
||||
}
|
||||
let handle = std::thread::Builder::new()
|
||||
.name("n64".to_string())
|
||||
.stack_size(env!("N64_STACK_SIZE").parse().unwrap())
|
||||
.spawn(move || {
|
||||
let mut device = device::Device::new();
|
||||
device.ui.fullscreen = args.fullscreen;
|
||||
device::run_game(rom_contents, &mut device);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
handle.join().unwrap();
|
||||
} else {
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: eframe::egui::ViewportBuilder::default()
|
||||
.with_inner_size([640.0, 480.0])
|
||||
.with_inner_size([854.0, 480.0])
|
||||
.with_icon(
|
||||
eframe::icon_data::from_png_bytes(include_bytes!("../data/gopher64.png"))
|
||||
.unwrap(),
|
||||
|
||||
+58
-30
@@ -1,5 +1,4 @@
|
||||
use crate::{device, ui};
|
||||
use eframe::egui;
|
||||
use std::io::{Read, Write};
|
||||
//UDP packet formats
|
||||
const UDP_SEND_KEY_INFO: u8 = 0;
|
||||
@@ -17,6 +16,10 @@ const TCP_REGISTER_PLAYER: u8 = 5;
|
||||
const TCP_GET_REGISTRATION: u8 = 6;
|
||||
const TCP_DISCONNECT_NOTICE: u8 = 7;
|
||||
const TCP_RECEIVE_SAVE_WITH_SIZE: u8 = 8;
|
||||
const TCP_SEND_RTC: u8 = 64;
|
||||
const TCP_RECEIVE_RTC: u8 = 128;
|
||||
const TCP_SEND_RNG: u8 = 65;
|
||||
const TCP_RECEIVE_RNG: u8 = 129;
|
||||
|
||||
const CS4: u32 = 32;
|
||||
|
||||
@@ -29,8 +32,6 @@ pub struct Netplay {
|
||||
status: u8,
|
||||
buffer_target: u8,
|
||||
pub fast_forward: bool,
|
||||
pub error_notifier: tokio::sync::mpsc::Sender<String>,
|
||||
pub gui_ctx: egui::Context,
|
||||
}
|
||||
|
||||
pub struct PlayerData {
|
||||
@@ -45,10 +46,44 @@ struct InputEvent {
|
||||
plugin: u8,
|
||||
}
|
||||
|
||||
fn send_error(netplay: &mut Netplay, error: String) {
|
||||
netplay.error_notifier.try_send(error).unwrap();
|
||||
pub const NETPLAY_ERROR_DESYNC: u32 = 0;
|
||||
pub const NETPLAY_ERROR_LOST_CONNECTION: u32 = 1;
|
||||
pub const NETPLAY_ERROR_PLAYER_DISCONNECTED: u32 = 2;
|
||||
pub const NETPLAY_ERROR_PLAYER_1_DISCONNECTED: u32 = 3;
|
||||
pub const NETPLAY_ERROR_PLAYER_2_DISCONNECTED: u32 = 4;
|
||||
pub const NETPLAY_ERROR_PLAYER_3_DISCONNECTED: u32 = 5;
|
||||
pub const NETPLAY_ERROR_PLAYER_4_DISCONNECTED: u32 = 6;
|
||||
|
||||
netplay.gui_ctx.request_repaint(); // this is so the window pops up right away
|
||||
pub fn send_rtc(netplay: &mut Netplay, rtc: i64) {
|
||||
let mut request: Vec<u8> = [TCP_SEND_RTC].to_vec();
|
||||
let size: u32 = 8;
|
||||
request.extend_from_slice(&size.to_be_bytes());
|
||||
request.extend_from_slice(&rtc.to_be_bytes());
|
||||
netplay.tcp_stream.write_all(&request).unwrap();
|
||||
}
|
||||
|
||||
pub fn receive_rtc(netplay: &mut Netplay) -> i64 {
|
||||
let request: Vec<u8> = [TCP_RECEIVE_RTC].to_vec();
|
||||
netplay.tcp_stream.write_all(&request).unwrap();
|
||||
let mut rtc: [u8; 8] = [0; 8];
|
||||
netplay.tcp_stream.read_exact(&mut rtc).unwrap();
|
||||
i64::from_be_bytes(rtc)
|
||||
}
|
||||
|
||||
pub fn send_rng(netplay: &mut Netplay, seed: u64) {
|
||||
let mut request: Vec<u8> = [TCP_SEND_RNG].to_vec();
|
||||
let size: u32 = 8;
|
||||
request.extend_from_slice(&size.to_be_bytes());
|
||||
request.extend_from_slice(&seed.to_be_bytes());
|
||||
netplay.tcp_stream.write_all(&request).unwrap();
|
||||
}
|
||||
|
||||
pub fn receive_rng(netplay: &mut Netplay) -> u64 {
|
||||
let request: Vec<u8> = [TCP_RECEIVE_RNG].to_vec();
|
||||
netplay.tcp_stream.write_all(&request).unwrap();
|
||||
let mut seed: [u8; 8] = [0; 8];
|
||||
netplay.tcp_stream.read_exact(&mut seed).unwrap();
|
||||
u64::from_be_bytes(seed)
|
||||
}
|
||||
|
||||
pub fn send_save(netplay: &mut Netplay, save_type: &str, save_data: &[u8], size: usize) {
|
||||
@@ -91,25 +126,25 @@ pub fn send_sync_check(device: &mut device::Device) {
|
||||
netplay.vi_counter = netplay.vi_counter.wrapping_add(1);
|
||||
}
|
||||
|
||||
pub fn send_input(netplay: &Netplay, input: (u32, bool)) {
|
||||
pub fn send_input(netplay: &Netplay, input: ui::input::InputData) {
|
||||
let mut request: Vec<u8> = [UDP_SEND_KEY_INFO].to_vec();
|
||||
request.push(netplay.player_number);
|
||||
request.extend_from_slice(
|
||||
&(netplay.player_data[netplay.player_number as usize].count).to_be_bytes(),
|
||||
);
|
||||
request.extend_from_slice(&(input.0).to_be_bytes());
|
||||
request.push(input.1 as u8);
|
||||
request.extend_from_slice(&(input.data).to_be_bytes());
|
||||
request.push(input.pak_change_pressed as u8);
|
||||
netplay.udp_socket.send(&request).unwrap();
|
||||
}
|
||||
|
||||
pub fn get_input(device: &mut device::Device, channel: usize) -> (u32, bool) {
|
||||
pub fn get_input(device: &mut device::Device, channel: usize) -> ui::input::InputData {
|
||||
let netplay = device.netplay.as_mut().unwrap();
|
||||
let mut input = None;
|
||||
|
||||
let timeout = std::time::Instant::now() + std::time::Duration::from_secs(10);
|
||||
let mut request_timer = std::time::Instant::now() - std::time::Duration::from_millis(5);
|
||||
while input.is_none() {
|
||||
process_incoming(netplay); // we execute process_incoming before request_input so that we send an accurate buffer count
|
||||
process_incoming(netplay, &device.ui); // we execute process_incoming before request_input so that we send an accurate buffer count
|
||||
if std::time::Instant::now() > request_timer {
|
||||
// sends a request packet every 5ms
|
||||
request_input(netplay, channel);
|
||||
@@ -120,10 +155,7 @@ pub fn get_input(device: &mut device::Device, channel: usize) -> (u32, bool) {
|
||||
.remove(&netplay.player_data[channel].count);
|
||||
|
||||
if std::time::Instant::now() > timeout {
|
||||
send_error(
|
||||
netplay,
|
||||
"Timed out waiting for input. Lost connection to server".to_string(),
|
||||
);
|
||||
ui::audio::play_netplay_audio(&device.ui, NETPLAY_ERROR_LOST_CONNECTION);
|
||||
input = Some(InputEvent {
|
||||
input: 0,
|
||||
plugin: 0,
|
||||
@@ -136,10 +168,10 @@ pub fn get_input(device: &mut device::Device, channel: usize) -> (u32, bool) {
|
||||
&& netplay.player_data[channel].input_events.len() as u8 > netplay.buffer_target;
|
||||
|
||||
netplay.player_data[channel].count = netplay.player_data[channel].count.wrapping_add(1);
|
||||
(
|
||||
input.as_ref().unwrap().input,
|
||||
input.as_ref().unwrap().plugin != 0,
|
||||
)
|
||||
ui::input::InputData {
|
||||
data: input.as_ref().unwrap().input,
|
||||
pak_change_pressed: input.as_ref().unwrap().plugin != 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn request_input(netplay: &Netplay, channel: usize) {
|
||||
@@ -154,7 +186,7 @@ fn request_input(netplay: &Netplay, channel: usize) {
|
||||
netplay.udp_socket.send(&request).unwrap();
|
||||
}
|
||||
|
||||
fn process_incoming(netplay: &mut Netplay) {
|
||||
fn process_incoming(netplay: &mut Netplay, ui: &ui::Ui) {
|
||||
let mut buf: [u8; 1024] = [0; 1024];
|
||||
while let Ok(_incoming) = netplay.udp_socket.recv(&mut buf) {
|
||||
match buf[0] {
|
||||
@@ -168,15 +200,15 @@ fn process_incoming(netplay: &mut Netplay) {
|
||||
}
|
||||
if current_status != netplay.status {
|
||||
if ((current_status & 0x1) ^ (netplay.status & 0x1)) != 0 {
|
||||
send_error(
|
||||
netplay,
|
||||
format!("Players have desynced at VI {}", netplay.vi_counter),
|
||||
);
|
||||
ui::audio::play_netplay_audio(ui, NETPLAY_ERROR_DESYNC);
|
||||
}
|
||||
for dis in 1..5 {
|
||||
if ((current_status & (0x1 << dis)) ^ (netplay.status & (0x1 << dis))) != 0
|
||||
{
|
||||
send_error(netplay, format!("Player {} has disconnected", dis));
|
||||
ui::audio::play_netplay_audio(
|
||||
ui,
|
||||
NETPLAY_ERROR_PLAYER_DISCONNECTED + dis,
|
||||
);
|
||||
}
|
||||
}
|
||||
netplay.status = current_status;
|
||||
@@ -208,7 +240,7 @@ fn process_incoming(netplay: &mut Netplay) {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic! {"unknown UDP packet"}
|
||||
panic!("unknown UDP packet")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,8 +250,6 @@ pub fn init(
|
||||
mut peer_addr: std::net::SocketAddr,
|
||||
session: ui::gui::gui_netplay::NetplayRoom,
|
||||
player_number: u8,
|
||||
error_notifier: tokio::sync::mpsc::Sender<String>,
|
||||
gui_ctx: egui::Context,
|
||||
) -> Netplay {
|
||||
peer_addr.set_port(session.port.unwrap() as u16);
|
||||
let udp_socket;
|
||||
@@ -283,8 +313,6 @@ pub fn init(
|
||||
status: 0,
|
||||
buffer_target,
|
||||
fast_forward: false,
|
||||
error_notifier,
|
||||
gui_ctx,
|
||||
player_data: [
|
||||
PlayerData {
|
||||
lag: 0,
|
||||
|
||||
+112
-85
@@ -70,107 +70,129 @@ where
|
||||
}
|
||||
|
||||
pub fn create_savestate(device: &device::Device) {
|
||||
let mut rdp_state: Vec<u8> = vec![0; ui::video::state_size()];
|
||||
ui::video::save_state(rdp_state.as_mut_ptr());
|
||||
|
||||
let data: &[(&[u8], &str)] = &[
|
||||
(&postcard::to_stdvec(device).unwrap(), "device"),
|
||||
(&postcard::to_stdvec(&device.ui.saves).unwrap(), "saves"),
|
||||
(
|
||||
&postcard::to_stdvec(&device.ui.storage.saves).unwrap(),
|
||||
"saves",
|
||||
),
|
||||
(&rdp_state, "rdp_state"),
|
||||
];
|
||||
let compressed_file = ui::storage::compress_file(data);
|
||||
std::fs::write(device.ui.paths.savestate_file_path.clone(), compressed_file).unwrap();
|
||||
std::fs::write(
|
||||
device.ui.storage.paths.savestate_file_path.clone(),
|
||||
compressed_file,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn load_savestate(device: &mut device::Device) {
|
||||
let savestate = std::fs::read(&mut device.ui.paths.savestate_file_path);
|
||||
let savestate = std::fs::read(&device.ui.storage.paths.savestate_file_path);
|
||||
if savestate.is_ok() {
|
||||
let device_bytes = ui::storage::decompress_file(savestate.as_ref().unwrap(), "device");
|
||||
let save_bytes = ui::storage::decompress_file(savestate.as_ref().unwrap(), "saves");
|
||||
let state: device::Device = postcard::from_bytes(&device_bytes).unwrap();
|
||||
let rdp_state = ui::storage::decompress_file(savestate.as_ref().unwrap(), "rdp_state");
|
||||
if let Ok(state) = postcard::from_bytes::<device::Device>(&device_bytes) {
|
||||
device.ui.storage.saves = postcard::from_bytes(&save_bytes).unwrap();
|
||||
|
||||
device.ui.saves = postcard::from_bytes(&save_bytes).unwrap();
|
||||
device.cpu = state.cpu;
|
||||
device.pif = state.pif;
|
||||
|
||||
device.cpu = state.cpu;
|
||||
device.pif = state.pif;
|
||||
let rom = device.cart.rom.clone();
|
||||
device.cart = state.cart;
|
||||
device.cart.rom = rom;
|
||||
|
||||
let rom = device.cart.rom.clone();
|
||||
device.cart = state.cart;
|
||||
device.cart.rom = rom;
|
||||
device.memory = state.memory;
|
||||
device.rsp = state.rsp;
|
||||
device.rdp = state.rdp;
|
||||
device.rdram = state.rdram;
|
||||
device.mi = state.mi;
|
||||
device.pi = state.pi;
|
||||
device.vi = state.vi;
|
||||
device.ai = state.ai;
|
||||
device.si = state.si;
|
||||
device.ri = state.ri;
|
||||
device.vru = state.vru;
|
||||
|
||||
device.memory = state.memory;
|
||||
device.rsp = state.rsp;
|
||||
device.rdp = state.rdp;
|
||||
device.rdram = state.rdram;
|
||||
device.mi = state.mi;
|
||||
device.pi = state.pi;
|
||||
device.vi = state.vi;
|
||||
device.ai = state.ai;
|
||||
device.si = state.si;
|
||||
device.ri = state.ri;
|
||||
device.vru = state.vru;
|
||||
let mut tpak_rom = [vec![], vec![], vec![], vec![]];
|
||||
for (i, item) in tpak_rom.iter_mut().enumerate() {
|
||||
*item = device.transferpaks[i].cart.rom.clone();
|
||||
}
|
||||
device.transferpaks = state.transferpaks;
|
||||
for (i, item) in tpak_rom.iter().enumerate() {
|
||||
device.transferpaks[i].cart.rom = item.clone();
|
||||
}
|
||||
|
||||
device::memory::init(device);
|
||||
device::vi::set_expected_refresh_rate(device);
|
||||
device::cpu::map_instructions(device);
|
||||
device::cop0::map_instructions(device);
|
||||
device::cop1::map_instructions(device);
|
||||
device::cop2::map_instructions(device);
|
||||
device::rsp_cpu::map_instructions(device);
|
||||
device::memory::init(device);
|
||||
device::vi::set_expected_refresh_rate(device);
|
||||
device::cpu::map_instructions(device);
|
||||
device::cop0::map_instructions(device);
|
||||
device::cop1::map_instructions(device);
|
||||
device::cop2::map_instructions(device);
|
||||
device::rsp_cpu::map_instructions(device);
|
||||
|
||||
let mut mem_addr = 0x1000;
|
||||
while mem_addr < 0x2000 {
|
||||
let data =
|
||||
u32::from_be_bytes(device.rsp.mem[mem_addr..mem_addr + 4].try_into().unwrap());
|
||||
device.rsp.cpu.instructions[(mem_addr & 0xFFF) / 4].func =
|
||||
device::rsp_cpu::decode_opcode(device, data);
|
||||
device.rsp.cpu.instructions[(mem_addr & 0xFFF) / 4].opcode = data;
|
||||
mem_addr += 4;
|
||||
}
|
||||
let mut mem_addr = 0x1000;
|
||||
while mem_addr < 0x2000 {
|
||||
let data =
|
||||
u32::from_be_bytes(device.rsp.mem[mem_addr..mem_addr + 4].try_into().unwrap());
|
||||
device.rsp.cpu.instructions[(mem_addr & 0xFFF) / 4].func =
|
||||
device::rsp_cpu::decode_opcode(device, data);
|
||||
device.rsp.cpu.instructions[(mem_addr & 0xFFF) / 4].opcode = data;
|
||||
mem_addr += 4;
|
||||
}
|
||||
|
||||
for line_index in 0..512 {
|
||||
device.memory.icache[line_index].instruction[0] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[0]);
|
||||
device.memory.icache[line_index].instruction[1] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[1]);
|
||||
device.memory.icache[line_index].instruction[2] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[2]);
|
||||
device.memory.icache[line_index].instruction[3] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[3]);
|
||||
device.memory.icache[line_index].instruction[4] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[4]);
|
||||
device.memory.icache[line_index].instruction[5] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[5]);
|
||||
device.memory.icache[line_index].instruction[6] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[6]);
|
||||
device.memory.icache[line_index].instruction[7] =
|
||||
device::cpu::decode_opcode(device, device.memory.icache[line_index].words[7]);
|
||||
}
|
||||
|
||||
device::pif::connect_pif_channels(device);
|
||||
for i in 0..4 {
|
||||
if device.pif.channels[i].pak_handler.is_some() {
|
||||
if device.pif.channels[i].pak_handler.unwrap().pak_type
|
||||
== device::controller::PakType::RumblePak
|
||||
{
|
||||
let rumblepak_handler = device::controller::PakHandler {
|
||||
read: device::controller::rumble::read,
|
||||
write: device::controller::rumble::write,
|
||||
pak_type: device::controller::PakType::RumblePak,
|
||||
};
|
||||
device.pif.channels[i].pak_handler = Some(rumblepak_handler);
|
||||
} else if device.pif.channels[i].pak_handler.unwrap().pak_type
|
||||
== device::controller::PakType::MemPak
|
||||
{
|
||||
let mempak_handler = device::controller::PakHandler {
|
||||
read: device::controller::mempak::read,
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: device::controller::PakType::MemPak,
|
||||
};
|
||||
device.pif.channels[i].pak_handler = Some(mempak_handler);
|
||||
for line_index in 0..512 {
|
||||
for i in 0..8 {
|
||||
device.memory.icache[line_index].instruction[i] = device::cpu::decode_opcode(
|
||||
device,
|
||||
device.memory.icache[line_index].words[i],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui::audio::close(&mut device.ui);
|
||||
ui::audio::init(&mut device.ui, device.ai.freq);
|
||||
ui::video::load_state(device);
|
||||
device::pif::connect_pif_channels(device);
|
||||
for i in 0..4 {
|
||||
if device.pif.channels[i].pak_handler.is_some() {
|
||||
if device.pif.channels[i].pak_handler.unwrap().pak_type
|
||||
== device::controller::PakType::RumblePak
|
||||
{
|
||||
let rumblepak_handler = device::controller::PakHandler {
|
||||
read: device::controller::rumble::read,
|
||||
write: device::controller::rumble::write,
|
||||
pak_type: device::controller::PakType::RumblePak,
|
||||
};
|
||||
device.pif.channels[i].pak_handler = Some(rumblepak_handler);
|
||||
} else if device.pif.channels[i].pak_handler.unwrap().pak_type
|
||||
== device::controller::PakType::MemPak
|
||||
{
|
||||
let mempak_handler = device::controller::PakHandler {
|
||||
read: device::controller::mempak::read,
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: device::controller::PakType::MemPak,
|
||||
};
|
||||
device.pif.channels[i].pak_handler = Some(mempak_handler);
|
||||
} else if device.pif.channels[i].pak_handler.unwrap().pak_type
|
||||
== device::controller::PakType::TransferPak
|
||||
{
|
||||
let tpak_handler = device::controller::PakHandler {
|
||||
read: device::controller::transferpak::read,
|
||||
write: device::controller::transferpak::write,
|
||||
pak_type: device::controller::PakType::TransferPak,
|
||||
};
|
||||
device.pif.channels[i].pak_handler = Some(tpak_handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui::audio::close(&mut device.ui);
|
||||
ui::audio::init(&mut device.ui, device.ai.freq);
|
||||
ui::video::load_state(device, rdp_state.as_ptr());
|
||||
} else {
|
||||
println!("Failed to load savestate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,11 +211,16 @@ where
|
||||
[device::cop0::reserved; N]
|
||||
}
|
||||
|
||||
pub fn default_memory_read(
|
||||
) -> [fn(&mut device::Device, u64, device::memory::AccessSize) -> u32; 0x2000] {
|
||||
[device::rdram::read_mem; 0x2000]
|
||||
pub fn default_memory_read_fast()
|
||||
-> [fn(&device::Device, u64, device::memory::AccessSize) -> u32; 0x2000] {
|
||||
[device::unmapped::read_mem_fast; 0x2000]
|
||||
}
|
||||
|
||||
pub fn default_memory_read()
|
||||
-> [fn(&mut device::Device, u64, device::memory::AccessSize) -> u32; 0x2000] {
|
||||
[device::unmapped::read_mem; 0x2000]
|
||||
}
|
||||
|
||||
pub fn default_memory_write() -> [fn(&mut device::Device, u64, u32, u32); 0x2000] {
|
||||
[device::rdram::write_mem; 0x2000]
|
||||
[device::unmapped::write_mem; 0x2000]
|
||||
}
|
||||
|
||||
@@ -12,32 +12,47 @@ pub struct Dirs {
|
||||
pub data_dir: std::path::PathBuf,
|
||||
}
|
||||
|
||||
pub struct Ui {
|
||||
pub dirs: Dirs,
|
||||
pub struct Audio {
|
||||
pub audio_stream: *mut sdl3_sys::audio::SDL_AudioStream,
|
||||
pub event_audio_stream: *mut sdl3_sys::audio::SDL_AudioStream,
|
||||
pub audio_device: u32,
|
||||
pub event_audio: audio::EventAudio,
|
||||
pub gain: f32,
|
||||
}
|
||||
|
||||
pub struct Input {
|
||||
pub joysticks: Vec<sdl3_sys::joystick::SDL_JoystickID>,
|
||||
pub keyboard_state: *const bool,
|
||||
pub controllers: [input::Controllers; 4],
|
||||
pub config: config::Config,
|
||||
}
|
||||
|
||||
pub struct Storage {
|
||||
pub save_type: Vec<storage::SaveTypes>,
|
||||
pub game_id: String,
|
||||
pub game_hash: String,
|
||||
pub paths: storage::Paths,
|
||||
pub saves: storage::Saves,
|
||||
pub pak_audio: audio::PakAudio,
|
||||
}
|
||||
|
||||
pub struct Video {
|
||||
pub window: *mut sdl3_sys::video::SDL_Window,
|
||||
pub audio_stream: *mut sdl3_sys::audio::SDL_AudioStream,
|
||||
pub pak_audio_stream: *mut sdl3_sys::audio::SDL_AudioStream,
|
||||
pub audio_device: u32,
|
||||
pub num_joysticks: i32,
|
||||
pub fullscreen: bool,
|
||||
pub joysticks: *mut sdl3_sys::joystick::SDL_JoystickID,
|
||||
}
|
||||
|
||||
pub struct Ui {
|
||||
pub dirs: Dirs,
|
||||
pub config: config::Config,
|
||||
pub game_id: String,
|
||||
pub game_hash: String,
|
||||
pub with_sdl: bool,
|
||||
pub audio: Audio,
|
||||
pub input: Input,
|
||||
pub storage: Storage,
|
||||
pub video: Video,
|
||||
}
|
||||
|
||||
impl Drop for Ui {
|
||||
fn drop(&mut self) {
|
||||
if self.with_sdl {
|
||||
unsafe {
|
||||
sdl3_sys::stdinc::SDL_free(self.joysticks as *mut std::ffi::c_void);
|
||||
sdl3_sys::init::SDL_Quit();
|
||||
}
|
||||
}
|
||||
@@ -81,73 +96,109 @@ pub fn get_dirs() -> Dirs {
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
fn construct_ui(num_joysticks: i32, joysticks: *mut u32, with_sdl: bool) -> Ui {
|
||||
fn construct_ui(joysticks: Vec<u32>, with_sdl: bool) -> Ui {
|
||||
let dirs = get_dirs();
|
||||
|
||||
Ui {
|
||||
controllers: [
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
input: Input {
|
||||
controllers: [
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
},
|
||||
],
|
||||
keyboard_state: std::ptr::null_mut(),
|
||||
joysticks,
|
||||
},
|
||||
storage: Storage {
|
||||
save_type: vec![],
|
||||
paths: storage::Paths {
|
||||
eep_file_path: std::path::PathBuf::new(),
|
||||
fla_file_path: std::path::PathBuf::new(),
|
||||
sra_file_path: std::path::PathBuf::new(),
|
||||
pak_file_path: std::path::PathBuf::new(),
|
||||
sdcard_file_path: std::path::PathBuf::new(),
|
||||
romsave_file_path: std::path::PathBuf::new(),
|
||||
savestate_file_path: std::path::PathBuf::new(),
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
saves: storage::Saves {
|
||||
write_to_disk: true,
|
||||
eeprom: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
},
|
||||
sram: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
},
|
||||
flash: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
},
|
||||
mempak: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
},
|
||||
sdcard: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
},
|
||||
romsave: storage::RomSave {
|
||||
data: std::collections::HashMap::new(),
|
||||
written: false,
|
||||
},
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: std::ptr::null_mut(),
|
||||
joystick: std::ptr::null_mut(),
|
||||
rumble: false,
|
||||
},
|
||||
],
|
||||
keyboard_state: std::ptr::null_mut(),
|
||||
},
|
||||
config: config::Config::new(),
|
||||
save_type: vec![],
|
||||
game_id: String::new(),
|
||||
game_hash: String::new(),
|
||||
paths: storage::Paths {
|
||||
eep_file_path: std::path::PathBuf::new(),
|
||||
fla_file_path: std::path::PathBuf::new(),
|
||||
sra_file_path: std::path::PathBuf::new(),
|
||||
pak_file_path: std::path::PathBuf::new(),
|
||||
sdcard_file_path: std::path::PathBuf::new(),
|
||||
romsave_file_path: std::path::PathBuf::new(),
|
||||
savestate_file_path: std::path::PathBuf::new(),
|
||||
audio: Audio {
|
||||
event_audio: audio::EventAudio {
|
||||
mempak: include_bytes!("../data/mempak.wav").to_vec(),
|
||||
rumblepak: include_bytes!("../data/rumblepak.wav").to_vec(),
|
||||
transferpak: include_bytes!("../data/transferpak.wav").to_vec(),
|
||||
netplay_desync: include_bytes!("../data/netplay_desync.wav").to_vec(),
|
||||
netplay_lost_connection: include_bytes!("../data/netplay_lost_connection.wav")
|
||||
.to_vec(),
|
||||
netplay_disconnected: [
|
||||
include_bytes!("../data/netplay_p1_disconnected.wav").to_vec(),
|
||||
include_bytes!("../data/netplay_p2_disconnected.wav").to_vec(),
|
||||
include_bytes!("../data/netplay_p3_disconnected.wav").to_vec(),
|
||||
include_bytes!("../data/netplay_p4_disconnected.wav").to_vec(),
|
||||
],
|
||||
},
|
||||
audio_stream: std::ptr::null_mut(),
|
||||
event_audio_stream: std::ptr::null_mut(),
|
||||
audio_device: 0,
|
||||
gain: 1.0,
|
||||
},
|
||||
saves: storage::Saves {
|
||||
eeprom: (Vec::new(), false),
|
||||
sram: (Vec::new(), false),
|
||||
flash: (Vec::new(), false),
|
||||
mempak: (Vec::new(), false),
|
||||
sdcard: (Vec::new(), false),
|
||||
romsave: (std::collections::HashMap::new(), false),
|
||||
video: Video {
|
||||
window: std::ptr::null_mut(),
|
||||
fullscreen: false,
|
||||
},
|
||||
pak_audio: audio::PakAudio {
|
||||
mempak: include_bytes!("../data/mempak.wav").to_vec(),
|
||||
rumblepak: include_bytes!("../data/rumblepak.wav").to_vec(),
|
||||
transferpak: include_bytes!("../data/transferpak.wav").to_vec(),
|
||||
},
|
||||
window: std::ptr::null_mut(),
|
||||
audio_stream: std::ptr::null_mut(),
|
||||
pak_audio_stream: std::ptr::null_mut(),
|
||||
audio_device: 0,
|
||||
fullscreen: false,
|
||||
num_joysticks,
|
||||
joysticks,
|
||||
dirs,
|
||||
with_sdl,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default() -> Ui {
|
||||
Self::construct_ui(0, std::ptr::null_mut(), false)
|
||||
Self::construct_ui(vec![], false)
|
||||
}
|
||||
|
||||
pub fn new() -> Ui {
|
||||
@@ -157,6 +208,11 @@ impl Ui {
|
||||
if joysticks.is_null() {
|
||||
panic!("Could not get joystick list");
|
||||
}
|
||||
Self::construct_ui(num_joysticks, joysticks, true)
|
||||
let mut joystick_vec = vec![];
|
||||
for i in 0..num_joysticks {
|
||||
joystick_vec.push(unsafe { *joysticks.add(i as usize) });
|
||||
}
|
||||
unsafe { sdl3_sys::stdinc::SDL_free(joysticks as *mut std::ffi::c_void) }
|
||||
Self::construct_ui(joystick_vec, true)
|
||||
}
|
||||
}
|
||||
|
||||
+110
-36
@@ -1,42 +1,59 @@
|
||||
use crate::device;
|
||||
use crate::netplay;
|
||||
use crate::ui;
|
||||
|
||||
pub struct PakAudio {
|
||||
pub struct EventAudio {
|
||||
pub mempak: Vec<u8>,
|
||||
pub rumblepak: Vec<u8>,
|
||||
pub transferpak: Vec<u8>,
|
||||
pub netplay_desync: Vec<u8>,
|
||||
pub netplay_lost_connection: Vec<u8>,
|
||||
pub netplay_disconnected: [Vec<u8>; 4],
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui, frequency: u64) {
|
||||
ui::sdl_init(sdl3_sys::init::SDL_INIT_AUDIO);
|
||||
|
||||
let audio_spec = sdl3_sys::audio::SDL_AudioSpec {
|
||||
let game_audio_spec = sdl3_sys::audio::SDL_AudioSpec {
|
||||
format: sdl3_sys::audio::SDL_AUDIO_S16LE,
|
||||
freq: frequency as i32,
|
||||
channels: 2,
|
||||
};
|
||||
ui.audio_device = unsafe {
|
||||
let device_audio_spec = sdl3_sys::audio::SDL_AudioSpec {
|
||||
format: sdl3_sys::audio::SDL_AUDIO_S16LE,
|
||||
freq: 48000,
|
||||
channels: 2,
|
||||
};
|
||||
ui.audio.audio_device = unsafe {
|
||||
sdl3_sys::audio::SDL_OpenAudioDevice(
|
||||
sdl3_sys::audio::SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
|
||||
&audio_spec,
|
||||
&device_audio_spec,
|
||||
)
|
||||
};
|
||||
if ui.audio_device == 0 {
|
||||
if ui.audio.audio_device == 0 {
|
||||
panic!("Could not open audio device");
|
||||
}
|
||||
|
||||
let mut dst = Default::default();
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_GetAudioDeviceFormat(ui.audio_device, &mut dst, std::ptr::null_mut())
|
||||
sdl3_sys::audio::SDL_GetAudioDeviceFormat(
|
||||
ui.audio.audio_device,
|
||||
&mut dst,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
panic!("Could not get audio device format");
|
||||
}
|
||||
|
||||
ui.audio_stream = unsafe { sdl3_sys::audio::SDL_CreateAudioStream(&audio_spec, &dst) };
|
||||
if ui.audio_stream.is_null() {
|
||||
ui.audio.audio_stream =
|
||||
unsafe { sdl3_sys::audio::SDL_CreateAudioStream(&game_audio_spec, &dst) };
|
||||
if ui.audio.audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
if !unsafe { sdl3_sys::audio::SDL_BindAudioStream(ui.audio_device, ui.audio_stream) } {
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.audio_stream, ui.audio.gain)
|
||||
&& sdl3_sys::audio::SDL_BindAudioStream(ui.audio.audio_device, ui.audio.audio_stream)
|
||||
} {
|
||||
panic!("Could not bind audio stream");
|
||||
}
|
||||
|
||||
@@ -46,45 +63,80 @@ pub fn init(ui: &mut ui::Ui, frequency: u64) {
|
||||
channels: 1,
|
||||
};
|
||||
|
||||
ui.pak_audio_stream = unsafe { sdl3_sys::audio::SDL_CreateAudioStream(&wav_audio_spec, &dst) };
|
||||
if !unsafe { sdl3_sys::audio::SDL_BindAudioStream(ui.audio_device, ui.pak_audio_stream) } {
|
||||
ui.audio.event_audio_stream =
|
||||
unsafe { sdl3_sys::audio::SDL_CreateAudioStream(&wav_audio_spec, &dst) };
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.event_audio_stream, ui.audio.gain)
|
||||
&& sdl3_sys::audio::SDL_BindAudioStream(
|
||||
ui.audio.audio_device,
|
||||
ui.audio.event_audio_stream,
|
||||
)
|
||||
} {
|
||||
panic!("Could not bind audio stream");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(ui: &mut ui::Ui) {
|
||||
unsafe {
|
||||
if !ui.audio_stream.is_null() {
|
||||
sdl3_sys::audio::SDL_DestroyAudioStream(ui.audio_stream);
|
||||
ui.audio_stream = std::ptr::null_mut();
|
||||
if !ui.audio.audio_stream.is_null() {
|
||||
sdl3_sys::audio::SDL_DestroyAudioStream(ui.audio.audio_stream);
|
||||
ui.audio.audio_stream = std::ptr::null_mut();
|
||||
}
|
||||
if !ui.pak_audio_stream.is_null() {
|
||||
sdl3_sys::audio::SDL_DestroyAudioStream(ui.pak_audio_stream);
|
||||
ui.pak_audio_stream = std::ptr::null_mut();
|
||||
if !ui.audio.event_audio_stream.is_null() {
|
||||
sdl3_sys::audio::SDL_DestroyAudioStream(ui.audio.event_audio_stream);
|
||||
ui.audio.event_audio_stream = std::ptr::null_mut();
|
||||
}
|
||||
sdl3_sys::audio::SDL_CloseAudioDevice(ui.audio_device);
|
||||
ui.audio_device = 0;
|
||||
sdl3_sys::audio::SDL_CloseAudioDevice(ui.audio.audio_device);
|
||||
ui.audio.audio_device = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_pak_switch(ui: &mut ui::Ui, pak: device::controller::PakType) {
|
||||
if ui.pak_audio_stream.is_null() {
|
||||
return;
|
||||
pub fn lower_audio_volume(ui: &mut ui::Ui) {
|
||||
unsafe {
|
||||
ui.audio.gain = sdl3_sys::audio::SDL_GetAudioStreamGain(ui.audio.audio_stream) - 0.05;
|
||||
if ui.audio.gain < 0.0 {
|
||||
ui.audio.gain = 0.0;
|
||||
}
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.audio_stream, ui.audio.gain);
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.event_audio_stream, ui.audio.gain);
|
||||
}
|
||||
}
|
||||
|
||||
let sound;
|
||||
if pak == device::controller::PakType::RumblePak {
|
||||
sound = &ui.pak_audio.rumblepak;
|
||||
} else if pak == device::controller::PakType::MemPak {
|
||||
sound = &ui.pak_audio.mempak;
|
||||
} else if pak == device::controller::PakType::TransferPak {
|
||||
sound = &ui.pak_audio.transferpak;
|
||||
} else {
|
||||
pub fn raise_audio_volume(ui: &mut ui::Ui) {
|
||||
unsafe {
|
||||
ui.audio.gain = sdl3_sys::audio::SDL_GetAudioStreamGain(ui.audio.audio_stream) + 0.05;
|
||||
if ui.audio.gain > 2.0 {
|
||||
ui.audio.gain = 2.0;
|
||||
}
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.audio_stream, ui.audio.gain);
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.event_audio_stream, ui.audio.gain);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_netplay_audio(ui: &ui::Ui, error: u32) {
|
||||
if ui.audio.event_audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
let sound = match error {
|
||||
netplay::NETPLAY_ERROR_DESYNC => &ui.audio.event_audio.netplay_desync,
|
||||
netplay::NETPLAY_ERROR_LOST_CONNECTION => &ui.audio.event_audio.netplay_lost_connection,
|
||||
netplay::NETPLAY_ERROR_PLAYER_1_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[0]
|
||||
}
|
||||
netplay::NETPLAY_ERROR_PLAYER_2_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[1]
|
||||
}
|
||||
netplay::NETPLAY_ERROR_PLAYER_3_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[2]
|
||||
}
|
||||
netplay::NETPLAY_ERROR_PLAYER_4_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[3]
|
||||
}
|
||||
_ => panic!("Invalid netplay error"),
|
||||
};
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
ui.pak_audio_stream,
|
||||
ui.audio.event_audio_stream,
|
||||
sound.as_ptr() as *const std::ffi::c_void,
|
||||
sound.len() as i32,
|
||||
)
|
||||
@@ -93,8 +145,30 @@ pub fn play_pak_switch(ui: &mut ui::Ui, pak: device::controller::PakType) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
|
||||
if device.ui.audio_stream.is_null() {
|
||||
pub fn play_pak_switch(ui: &ui::Ui, pak: device::controller::PakType) {
|
||||
if ui.audio.event_audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let sound = match pak {
|
||||
device::controller::PakType::RumblePak => &ui.audio.event_audio.rumblepak,
|
||||
device::controller::PakType::MemPak => &ui.audio.event_audio.mempak,
|
||||
device::controller::PakType::TransferPak => &ui.audio.event_audio.transferpak,
|
||||
_ => panic!("Invalid pak type"),
|
||||
};
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
ui.audio.event_audio_stream,
|
||||
sound.as_ptr() as *const std::ffi::c_void,
|
||||
sound.len() as i32,
|
||||
)
|
||||
} {
|
||||
panic!("Could not play audio");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_audio(device: &device::Device, dram_addr: usize, length: u64) {
|
||||
if device.ui.audio.audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,7 +186,7 @@ pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
|
||||
}
|
||||
|
||||
let audio_queued =
|
||||
unsafe { sdl3_sys::audio::SDL_GetAudioStreamQueued(device.ui.audio_stream) } as f64;
|
||||
unsafe { sdl3_sys::audio::SDL_GetAudioStreamQueued(device.ui.audio.audio_stream) } as f64;
|
||||
let acceptable_latency = (device.ai.freq as f64 * 0.2) * 4.0;
|
||||
let min_latency = (device.ai.freq as f64 * 0.02) * 4.0;
|
||||
|
||||
@@ -120,7 +194,7 @@ pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
|
||||
let silence_buffer: Vec<u8> = vec![0; min_latency as usize & !3];
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
device.ui.audio_stream,
|
||||
device.ui.audio.audio_stream,
|
||||
silence_buffer.as_ptr() as *const std::ffi::c_void,
|
||||
silence_buffer.len() as i32,
|
||||
)
|
||||
@@ -132,7 +206,7 @@ pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
|
||||
if audio_queued < acceptable_latency
|
||||
&& !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
device.ui.audio_stream,
|
||||
device.ui.audio.audio_stream,
|
||||
primary_buffer.as_ptr() as *const std::ffi::c_void,
|
||||
primary_buffer.len() as i32 * 2,
|
||||
)
|
||||
|
||||
+43
-8
@@ -1,13 +1,33 @@
|
||||
use crate::ui;
|
||||
|
||||
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct InputKeyButton {
|
||||
pub enabled: bool,
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct InputControllerAxis {
|
||||
pub enabled: bool,
|
||||
pub id: i32,
|
||||
pub axis: i16,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct InputJoystickHat {
|
||||
pub enabled: bool,
|
||||
pub id: i32,
|
||||
pub direction: u8,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct InputProfile {
|
||||
pub keys: [(bool, i32); ui::input::PROFILE_SIZE],
|
||||
pub controller_buttons: [(bool, i32); ui::input::PROFILE_SIZE],
|
||||
pub controller_axis: [(bool, i32, i16); ui::input::PROFILE_SIZE],
|
||||
pub joystick_buttons: [(bool, i32); ui::input::PROFILE_SIZE],
|
||||
pub joystick_hat: [(bool, i32, u8); ui::input::PROFILE_SIZE],
|
||||
pub joystick_axis: [(bool, i32, i16); ui::input::PROFILE_SIZE],
|
||||
pub keys: [InputKeyButton; ui::input::PROFILE_SIZE],
|
||||
pub controller_buttons: [InputKeyButton; ui::input::PROFILE_SIZE],
|
||||
pub controller_axis: [InputControllerAxis; ui::input::PROFILE_SIZE],
|
||||
pub joystick_buttons: [InputKeyButton; ui::input::PROFILE_SIZE],
|
||||
pub joystick_hat: [InputJoystickHat; ui::input::PROFILE_SIZE],
|
||||
pub joystick_axis: [InputControllerAxis; ui::input::PROFILE_SIZE],
|
||||
pub dinput: bool,
|
||||
}
|
||||
|
||||
@@ -17,18 +37,29 @@ pub struct Input {
|
||||
pub input_profile_binding: [String; 4],
|
||||
pub controller_assignment: [Option<String>; 4],
|
||||
pub controller_enabled: [bool; 4],
|
||||
pub transfer_pak: [bool; 4],
|
||||
pub emulate_vru: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Video {
|
||||
pub upscale: bool,
|
||||
pub upscale: u32,
|
||||
pub integer_scaling: bool,
|
||||
pub fullscreen: bool,
|
||||
pub widescreen: bool,
|
||||
pub crt: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Emulation {
|
||||
pub overclock: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Config {
|
||||
pub input: Input,
|
||||
pub video: Video,
|
||||
pub emulation: Emulation,
|
||||
}
|
||||
|
||||
impl Drop for Config {
|
||||
@@ -68,13 +99,17 @@ impl Config {
|
||||
controller_assignment: [None, None, None, None],
|
||||
input_profiles,
|
||||
controller_enabled: [true, false, false, false],
|
||||
transfer_pak: [false, false, false, false],
|
||||
emulate_vru: false,
|
||||
},
|
||||
video: Video {
|
||||
upscale: false,
|
||||
upscale: 1,
|
||||
integer_scaling: false,
|
||||
fullscreen: false,
|
||||
widescreen: false,
|
||||
crt: false,
|
||||
},
|
||||
emulation: Emulation { overclock: false },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+273
-124
@@ -12,28 +12,42 @@ pub struct GopherEguiApp {
|
||||
selected_profile: [String; 4],
|
||||
input_profiles: Vec<String>,
|
||||
controller_enabled: [bool; 4],
|
||||
transfer_pak: [bool; 4],
|
||||
controller_paths: Vec<String>,
|
||||
upscale: bool,
|
||||
upscale: u32,
|
||||
integer_scaling: bool,
|
||||
fullscreen: bool,
|
||||
widescreen: bool,
|
||||
crt: bool,
|
||||
overclock: bool,
|
||||
emulate_vru: bool,
|
||||
dinput: bool,
|
||||
show_vru_dialog: bool,
|
||||
vru_window_receiver: Option<tokio::sync::mpsc::Receiver<Vec<String>>>,
|
||||
netplay_error_receiver: Option<tokio::sync::mpsc::Receiver<String>>,
|
||||
vru_word_notifier: Option<tokio::sync::mpsc::Sender<String>>,
|
||||
vru_word_list: Vec<String>,
|
||||
pub netplay: gui_netplay::GuiNetplay,
|
||||
latest_version: Option<semver::Version>,
|
||||
update_receiver: Option<tokio::sync::mpsc::Receiver<GithubData>>,
|
||||
netplay: gui_netplay::GuiNetplay,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct GithubData {
|
||||
tag_name: String,
|
||||
}
|
||||
|
||||
struct SaveConfig {
|
||||
selected_controller: [i32; 4],
|
||||
selected_profile: [String; 4],
|
||||
controller_enabled: [bool; 4],
|
||||
upscale: bool,
|
||||
transfer_pak: [bool; 4],
|
||||
upscale: u32,
|
||||
integer_scaling: bool,
|
||||
fullscreen: bool,
|
||||
widescreen: bool,
|
||||
crt: bool,
|
||||
emulate_vru: bool,
|
||||
overclock: bool,
|
||||
}
|
||||
|
||||
fn get_input_profiles(config: &ui::config::Config) -> Vec<String> {
|
||||
@@ -47,11 +61,9 @@ fn get_input_profiles(config: &ui::config::Config) -> Vec<String> {
|
||||
pub fn get_controller_names(game_ui: &ui::Ui) -> Vec<String> {
|
||||
let mut controllers: Vec<String> = vec![];
|
||||
|
||||
for offset in 0..game_ui.num_joysticks as isize {
|
||||
for joystick in game_ui.input.joysticks.iter() {
|
||||
let name = unsafe {
|
||||
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickNameForID(
|
||||
*(game_ui.joysticks.offset(offset)),
|
||||
))
|
||||
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickNameForID(*joystick))
|
||||
};
|
||||
controllers.push(name.to_string_lossy().to_string());
|
||||
}
|
||||
@@ -62,13 +74,11 @@ pub fn get_controller_names(game_ui: &ui::Ui) -> Vec<String> {
|
||||
pub fn get_controller_paths(game_ui: &ui::Ui) -> Vec<String> {
|
||||
let mut controller_paths: Vec<String> = vec![];
|
||||
|
||||
for offset in 0..game_ui.num_joysticks as isize {
|
||||
for joystick in game_ui.input.joysticks.iter() {
|
||||
let path = unsafe {
|
||||
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(
|
||||
*(game_ui.joysticks.offset(offset)),
|
||||
))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(*joystick))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
};
|
||||
controller_paths.push(path);
|
||||
}
|
||||
@@ -104,16 +114,21 @@ impl GopherEguiApp {
|
||||
controller_names,
|
||||
input_profiles: get_input_profiles(&config),
|
||||
controller_enabled: config.input.controller_enabled,
|
||||
transfer_pak: config.input.transfer_pak,
|
||||
upscale: config.video.upscale,
|
||||
integer_scaling: config.video.integer_scaling,
|
||||
fullscreen: config.video.fullscreen,
|
||||
widescreen: config.video.widescreen,
|
||||
crt: config.video.crt,
|
||||
emulate_vru: config.input.emulate_vru,
|
||||
overclock: config.emulation.overclock,
|
||||
show_vru_dialog: false,
|
||||
dinput: false,
|
||||
controller_paths,
|
||||
netplay_error_receiver: None,
|
||||
vru_window_receiver: None,
|
||||
vru_word_notifier: None,
|
||||
latest_version: None,
|
||||
update_receiver: None,
|
||||
vru_word_list: Vec::new(),
|
||||
netplay: Default::default(),
|
||||
dirs: ui::get_dirs(),
|
||||
@@ -137,11 +152,16 @@ fn save_config(
|
||||
|
||||
config.input.input_profile_binding = save_config_items.selected_profile;
|
||||
config.input.controller_enabled = save_config_items.controller_enabled;
|
||||
config.input.transfer_pak = save_config_items.transfer_pak;
|
||||
|
||||
config.video.upscale = save_config_items.upscale;
|
||||
config.video.integer_scaling = save_config_items.integer_scaling;
|
||||
config.video.fullscreen = save_config_items.fullscreen;
|
||||
config.video.widescreen = save_config_items.widescreen;
|
||||
config.video.crt = save_config_items.crt;
|
||||
config.input.emulate_vru = save_config_items.emulate_vru;
|
||||
|
||||
config.emulation.overclock = save_config_items.overclock;
|
||||
}
|
||||
|
||||
impl Drop for GopherEguiApp {
|
||||
@@ -150,10 +170,14 @@ impl Drop for GopherEguiApp {
|
||||
selected_controller: self.selected_controller,
|
||||
selected_profile: self.selected_profile.clone(),
|
||||
controller_enabled: self.controller_enabled,
|
||||
transfer_pak: self.transfer_pak,
|
||||
upscale: self.upscale,
|
||||
integer_scaling: self.integer_scaling,
|
||||
fullscreen: self.fullscreen,
|
||||
widescreen: self.widescreen,
|
||||
crt: self.crt,
|
||||
emulate_vru: self.emulate_vru,
|
||||
overclock: self.overclock,
|
||||
};
|
||||
let mut config = ui::config::Config::new();
|
||||
save_config(
|
||||
@@ -165,90 +189,120 @@ impl Drop for GopherEguiApp {
|
||||
}
|
||||
|
||||
fn configure_profile(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
egui::Window::new("Configure Input Profile")
|
||||
// .open(&mut self.configure_profile)
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let name_label = ui.label("Profile Name:");
|
||||
ui.text_edit_singleline(&mut app.profile_name)
|
||||
.labelled_by(name_label.id);
|
||||
});
|
||||
ui.checkbox(&mut app.dinput, "Use DirectInput");
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Configure Profile").clicked() {
|
||||
let profile_name = app.profile_name.clone();
|
||||
let dinput = app.dinput;
|
||||
std::thread::spawn(move || {
|
||||
let mut game_ui = ui::Ui::new();
|
||||
ui::input::configure_input_profile(&mut game_ui, profile_name, dinput);
|
||||
});
|
||||
app.configure_profile = false;
|
||||
if !app.profile_name.is_empty()
|
||||
&& !app.input_profiles.contains(&app.profile_name)
|
||||
{
|
||||
app.input_profiles.push(app.profile_name.clone())
|
||||
}
|
||||
};
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui.button("Close").clicked() {
|
||||
app.configure_profile = false
|
||||
};
|
||||
})
|
||||
});
|
||||
egui::Window::new("Configure Input Profile").show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let name_label = ui.label("Profile Name:");
|
||||
ui.text_edit_singleline(&mut app.profile_name)
|
||||
.labelled_by(name_label.id);
|
||||
});
|
||||
ui.checkbox(&mut app.dinput, "Use DirectInput");
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Configure Profile").clicked() {
|
||||
let profile_name = app.profile_name.clone();
|
||||
let dinput = app.dinput;
|
||||
std::thread::spawn(move || {
|
||||
let mut game_ui = ui::Ui::new();
|
||||
ui::input::configure_input_profile(&mut game_ui, profile_name, dinput);
|
||||
});
|
||||
app.configure_profile = false;
|
||||
if !app.profile_name.is_empty() && !app.input_profiles.contains(&app.profile_name) {
|
||||
app.input_profiles.push(app.profile_name.clone())
|
||||
}
|
||||
};
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui.button("Close").clicked() {
|
||||
app.configure_profile = false
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn show_vru_dialog(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
ctx.show_viewport_immediate(
|
||||
egui::ViewportId::from_hash_of("vru_dialog"),
|
||||
egui::ViewportBuilder::default()
|
||||
.with_title("What would you like to say?")
|
||||
.with_always_on_top(),
|
||||
|ctx, class| {
|
||||
assert!(
|
||||
class == egui::ViewportClass::Immediate,
|
||||
"This egui backend doesn't support multiple viewports"
|
||||
);
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
egui::Grid::new("vru_words").show(ui, |ui| {
|
||||
for (i, v) in app.vru_word_list.iter().enumerate() {
|
||||
if i % 5 == 0 {
|
||||
ui.end_row();
|
||||
}
|
||||
if ui.button((*v).to_string()).clicked() {
|
||||
app.vru_word_notifier
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.try_send(v.clone())
|
||||
.unwrap();
|
||||
app.show_vru_dialog = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ctx.input(|i| i.viewport().close_requested()) {
|
||||
app.vru_word_notifier
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.try_send(String::from(""))
|
||||
.unwrap();
|
||||
app.show_vru_dialog = false;
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.label("What would you like to say?");
|
||||
egui::Grid::new("vru_words").show(ui, |ui| {
|
||||
for (i, v) in app.vru_word_list.iter().enumerate() {
|
||||
if i % 5 == 0 {
|
||||
ui.end_row();
|
||||
}
|
||||
if ui.button((*v).to_string()).clicked() {
|
||||
app.vru_word_notifier
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.try_send(v.clone())
|
||||
.unwrap();
|
||||
app.show_vru_dialog = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
ui.add_space(16.0);
|
||||
|
||||
if ui.button("Close without saying anything").clicked() {
|
||||
app.vru_word_notifier
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.try_send(String::from(""))
|
||||
.unwrap();
|
||||
app.show_vru_dialog = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
fn get_latest_version(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
if app.update_receiver.is_none() {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(1);
|
||||
app.update_receiver = Some(rx);
|
||||
let gui_ctx = ctx.clone();
|
||||
let client = reqwest::Client::builder()
|
||||
.user_agent(env!("CARGO_PKG_NAME"))
|
||||
.build()
|
||||
.unwrap();
|
||||
let task = client
|
||||
.get("https://api.github.com/repos/gopher64/gopher64/releases/latest")
|
||||
.send();
|
||||
tokio::spawn(async move {
|
||||
let response = task.await;
|
||||
if let Ok(response) = response {
|
||||
let data: Result<GithubData, reqwest::Error> = response.json().await;
|
||||
if data.is_ok() {
|
||||
tx.send(data.unwrap()).await.unwrap();
|
||||
} else {
|
||||
tx.send(GithubData {
|
||||
tag_name: format!("v{}", env!("CARGO_PKG_VERSION")),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
gui_ctx.request_repaint();
|
||||
}
|
||||
});
|
||||
} else if app.latest_version.is_none() {
|
||||
let result = app.update_receiver.as_mut().unwrap().try_recv();
|
||||
if result.is_ok() {
|
||||
let tag = &result.unwrap().tag_name[1..];
|
||||
app.latest_version = Some(semver::Version::parse(tag).unwrap());
|
||||
} else {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context, enable_overclock: bool) {
|
||||
let netplay;
|
||||
|
||||
let selected_controller = app.selected_controller;
|
||||
let selected_profile = app.selected_profile.clone();
|
||||
let controller_enabled = app.controller_enabled;
|
||||
let transfer_pak = app.transfer_pak;
|
||||
let upscale = app.upscale;
|
||||
let integer_scaling = app.integer_scaling;
|
||||
let fullscreen = app.fullscreen;
|
||||
let widescreen = app.widescreen;
|
||||
let crt = app.crt;
|
||||
let emulate_vru = app.emulate_vru;
|
||||
let overclock = app.overclock;
|
||||
let peer_addr;
|
||||
let session;
|
||||
let player_number;
|
||||
@@ -267,11 +321,6 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
player_number = Some(app.netplay.player_number);
|
||||
}
|
||||
|
||||
let (netplay_error_notifier, netplay_error_receiver): (
|
||||
tokio::sync::mpsc::Sender<String>,
|
||||
tokio::sync::mpsc::Receiver<String>,
|
||||
) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
let (vru_window_notifier, vru_window_receiver): (
|
||||
tokio::sync::mpsc::Sender<Vec<String>>,
|
||||
tokio::sync::mpsc::Receiver<Vec<String>>,
|
||||
@@ -282,9 +331,6 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
tokio::sync::mpsc::Receiver<String>,
|
||||
) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
if netplay {
|
||||
app.netplay_error_receiver = Some(netplay_error_receiver);
|
||||
}
|
||||
if emulate_vru && !netplay {
|
||||
app.vru_window_receiver = Some(vru_window_receiver);
|
||||
app.vru_word_notifier = Some(vru_word_notifier);
|
||||
@@ -296,12 +342,47 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
let rom_contents = app.netplay.rom_contents.clone();
|
||||
let gui_ctx = ctx.clone();
|
||||
|
||||
let mut task = None;
|
||||
let mut select_rom = None;
|
||||
let mut select_gb_rom = [None, None, None, None];
|
||||
let mut select_gb_ram = [None, None, None, None];
|
||||
|
||||
if !netplay {
|
||||
task = Some(rfd::AsyncFileDialog::new().pick_file());
|
||||
select_rom = Some(
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title("Select ROM")
|
||||
.pick_file(),
|
||||
);
|
||||
for i in 0..4 {
|
||||
if transfer_pak[i] {
|
||||
select_gb_rom[i] = Some(
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("GB ROM P{}", i + 1))
|
||||
.pick_file(),
|
||||
);
|
||||
select_gb_ram[i] = Some(
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("GB RAM P{}", i + 1))
|
||||
.pick_file(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
let file = if !netplay { task.unwrap().await } else { None };
|
||||
let file = if !netplay {
|
||||
select_rom.unwrap().await
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut gb_rom_path = [None, None, None, None];
|
||||
let mut gb_ram_path = [None, None, None, None];
|
||||
if !netplay {
|
||||
for i in 0..4 {
|
||||
if transfer_pak[i] {
|
||||
gb_rom_path[i] = select_gb_rom[i].as_mut().unwrap().await;
|
||||
gb_ram_path[i] = select_gb_ram[i].as_mut().unwrap().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("n64".to_string())
|
||||
@@ -311,10 +392,14 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
selected_controller,
|
||||
selected_profile,
|
||||
controller_enabled,
|
||||
transfer_pak,
|
||||
upscale,
|
||||
integer_scaling,
|
||||
fullscreen,
|
||||
widescreen,
|
||||
crt,
|
||||
emulate_vru,
|
||||
overclock,
|
||||
};
|
||||
|
||||
if file.is_some() || netplay {
|
||||
@@ -336,25 +421,35 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
peer_addr.unwrap(),
|
||||
session.unwrap(),
|
||||
player_number.unwrap(),
|
||||
netplay_error_notifier,
|
||||
gui_ctx,
|
||||
));
|
||||
device.ui.fullscreen = fullscreen;
|
||||
device::run_game(rom_contents, &mut device);
|
||||
device::run_game(&mut device, rom_contents, fullscreen, enable_overclock);
|
||||
netplay::close(&mut device);
|
||||
} else {
|
||||
for i in 0..4 {
|
||||
if gb_rom_path[i].is_some() && gb_ram_path[i].is_some() {
|
||||
device.transferpaks[i].cart.rom =
|
||||
std::fs::read(gb_rom_path[i].as_ref().unwrap().path()).unwrap();
|
||||
|
||||
device.transferpaks[i].cart.ram =
|
||||
std::fs::read(gb_ram_path[i].as_ref().unwrap().path()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if emulate_vru {
|
||||
device.vru_window.window_notifier = Some(vru_window_notifier);
|
||||
device.vru_window.word_receiver = Some(vru_word_receiver);
|
||||
device.vru_window.gui_ctx = Some(gui_ctx);
|
||||
}
|
||||
|
||||
let rom_contents = device::get_rom_contents(file.unwrap().path());
|
||||
if rom_contents.is_empty() {
|
||||
println!("Could not read rom file");
|
||||
if let Some(rom_contents) = device::get_rom_contents(file.unwrap().path()) {
|
||||
device::run_game(
|
||||
&mut device,
|
||||
rom_contents,
|
||||
fullscreen,
|
||||
enable_overclock,
|
||||
);
|
||||
} else {
|
||||
device.ui.fullscreen = fullscreen;
|
||||
device::run_game(rom_contents, &mut device);
|
||||
println!("Could not read rom file");
|
||||
}
|
||||
}
|
||||
let result = std::fs::remove_file(running_file);
|
||||
@@ -369,6 +464,11 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
|
||||
impl eframe::App for GopherEguiApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
if self.show_vru_dialog {
|
||||
show_vru_dialog(self, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.netplay.create {
|
||||
gui_netplay::netplay_create(self, ctx);
|
||||
}
|
||||
@@ -381,13 +481,6 @@ impl eframe::App for GopherEguiApp {
|
||||
gui_netplay::netplay_wait(self, ctx);
|
||||
}
|
||||
|
||||
if self.netplay_error_receiver.is_some() {
|
||||
let result = self.netplay_error_receiver.as_mut().unwrap().try_recv();
|
||||
if result.is_ok() {
|
||||
self.netplay.error = result.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if !self.netplay.error.is_empty() {
|
||||
gui_netplay::netplay_error(self, ctx, self.netplay.error.clone());
|
||||
}
|
||||
@@ -404,7 +497,7 @@ impl eframe::App for GopherEguiApp {
|
||||
.min_col_width(200.0)
|
||||
.show(ui, |ui| {
|
||||
if ui.button("Open ROM").clicked() {
|
||||
open_rom(self, ctx);
|
||||
open_rom(self, ctx, self.overclock);
|
||||
}
|
||||
if ui.button("Netplay: Create Session").clicked()
|
||||
&& !self.dirs.cache_dir.join("game_running").exists()
|
||||
@@ -412,6 +505,19 @@ impl eframe::App for GopherEguiApp {
|
||||
self.netplay.create = true;
|
||||
}
|
||||
|
||||
if ui.button("Open Saves Folder").clicked() {
|
||||
let command = if cfg!(target_os = "windows") {
|
||||
"explorer"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"xdg-open"
|
||||
} else {
|
||||
panic!("Unsupported platform");
|
||||
};
|
||||
let _ = std::process::Command::new(command)
|
||||
.arg(self.dirs.data_dir.join("saves"))
|
||||
.spawn();
|
||||
}
|
||||
|
||||
ui.end_row();
|
||||
|
||||
if ui.button("Configure Input Profile").clicked()
|
||||
@@ -427,17 +533,33 @@ impl eframe::App for GopherEguiApp {
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_space(32.0);
|
||||
ui.add_space(16.0);
|
||||
ui.label("Controller Config:");
|
||||
egui::Grid::new("controller_config").show(ui, |ui| {
|
||||
ui.label("Port");
|
||||
ui.label("Enabled");
|
||||
ui.label("Emulate VRU");
|
||||
ui.label("Transfer Pak");
|
||||
ui.label("Profile");
|
||||
ui.label("Controller");
|
||||
ui.end_row();
|
||||
for i in 0..4 {
|
||||
ui.label(format!("{}", i + 1));
|
||||
ui.checkbox(&mut self.controller_enabled[i], "");
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.checkbox(&mut self.controller_enabled[i], "");
|
||||
});
|
||||
let mut vru = false;
|
||||
ui.centered_and_justified(|ui| {
|
||||
if i < 3 {
|
||||
ui.add_enabled(false, egui::Checkbox::new(&mut vru, ""));
|
||||
} else {
|
||||
ui.add_enabled(true, egui::Checkbox::new(&mut self.emulate_vru, ""));
|
||||
}
|
||||
});
|
||||
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.checkbox(&mut self.transfer_pak[i], "");
|
||||
});
|
||||
|
||||
egui::ComboBox::from_id_salt(format!("profile-combo-{}", i))
|
||||
.selected_text(self.selected_profile[i].clone())
|
||||
@@ -475,19 +597,48 @@ impl eframe::App for GopherEguiApp {
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
ui.add_space(32.0);
|
||||
ui.checkbox(&mut self.upscale, "High-Res Graphics");
|
||||
ui.add_space(16.0);
|
||||
let upscale_values = [1, 2, 4];
|
||||
let mut slider_value = match self.upscale {
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
4 => 2,
|
||||
_ => 0,
|
||||
};
|
||||
let display_text = format!("{}x Resolution", upscale_values[slider_value]);
|
||||
if ui
|
||||
.add(
|
||||
egui::Slider::new(&mut slider_value, 0..=2)
|
||||
.show_value(false)
|
||||
.text(display_text),
|
||||
)
|
||||
.changed()
|
||||
{
|
||||
self.upscale = upscale_values[slider_value];
|
||||
};
|
||||
ui.checkbox(&mut self.integer_scaling, "Integer Scaling");
|
||||
ui.checkbox(&mut self.fullscreen, "Fullscreen (Esc closes game)");
|
||||
ui.add_space(32.0);
|
||||
ui.checkbox(&mut self.widescreen, "Widescreen (stretch)");
|
||||
ui.checkbox(&mut self.crt, "Apply CRT shader");
|
||||
|
||||
ui.checkbox(
|
||||
&mut self.emulate_vru,
|
||||
"Emulate VRU (connects VRU to controller port 4)",
|
||||
);
|
||||
ui.add_space(16.0);
|
||||
ui.checkbox(&mut self.overclock, "Overclock N64 CPU (may cause bugs)");
|
||||
ui.add_space(16.0);
|
||||
|
||||
ui.hyperlink_to("Wiki", "https://github.com/gopher64/gopher64/wiki");
|
||||
ui.hyperlink_to("Discord Server", "https://discord.gg/9RGXq8W8JQ");
|
||||
ui.add_space(16.0);
|
||||
|
||||
ui.add_space(32.0);
|
||||
ui.label(format!("Version: {}", env!("CARGO_PKG_VERSION")));
|
||||
if self.latest_version.is_some() {
|
||||
let current_version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
if current_version < *self.latest_version.as_ref().unwrap() {
|
||||
ui.hyperlink_to(
|
||||
"New version available!",
|
||||
"https://github.com/gopher64/gopher64/releases/latest",
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if self.emulate_vru && self.vru_window_receiver.is_some() {
|
||||
@@ -498,9 +649,7 @@ impl eframe::App for GopherEguiApp {
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_vru_dialog {
|
||||
show_vru_dialog(self, ctx);
|
||||
}
|
||||
get_latest_version(self, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+125
-59
@@ -7,7 +7,19 @@ use sha2::{Digest, Sha256};
|
||||
const NETPLAY_VERSION: i32 = 17;
|
||||
const EMU_NAME: &str = "gopher64";
|
||||
|
||||
type GameInfo = (String, String, String, Vec<u8>);
|
||||
#[derive(Default)]
|
||||
pub struct GameInfo {
|
||||
pub md5: String,
|
||||
pub game_name: String,
|
||||
pub rom_label: String,
|
||||
pub rom_contents: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Default)]
|
||||
pub struct NetplayServer {
|
||||
pub name: String,
|
||||
pub ip: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GuiNetplay {
|
||||
@@ -21,7 +33,7 @@ pub struct GuiNetplay {
|
||||
pub create_rom_label: String,
|
||||
pub join_rom_label: String,
|
||||
pub send_chat: bool,
|
||||
pub have_sessions: Option<(String, String)>,
|
||||
pub have_sessions: Option<NetplayServer>,
|
||||
pub begin_game: bool,
|
||||
pub chat_log: String,
|
||||
pub chat_message: String,
|
||||
@@ -33,10 +45,10 @@ pub struct GuiNetplay {
|
||||
pub rom_contents: Vec<u8>,
|
||||
pub player_number: u8,
|
||||
pub player_names: [String; 4],
|
||||
pub server: (String, String),
|
||||
pub server: NetplayServer,
|
||||
pub socket_waiting: bool,
|
||||
pub game_info: GameInfo,
|
||||
pub servers: std::collections::HashMap<String, String>,
|
||||
pub servers: Vec<NetplayServer>,
|
||||
pub waiting_session: Option<NetplayRoom>,
|
||||
pub socket:
|
||||
Option<tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<std::net::TcpStream>>>,
|
||||
@@ -56,6 +68,8 @@ pub struct NetplayRoom {
|
||||
md5: Option<String>,
|
||||
game_name: Option<String>,
|
||||
pub port: Option<i32>,
|
||||
features: Option<std::collections::HashMap<String, String>>,
|
||||
buffer_target: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
@@ -131,10 +145,14 @@ fn get_servers(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
serde_json::from_slice(&buffer[..amt]).unwrap();
|
||||
for server in data.iter() {
|
||||
let (server_name, server_ip) = server;
|
||||
app.netplay
|
||||
.servers
|
||||
.insert(server_name.to_string(), server_ip.to_string());
|
||||
app.netplay.server = (server.0.clone(), server.1.clone());
|
||||
app.netplay.servers.push(NetplayServer {
|
||||
name: server_name.to_string(),
|
||||
ip: server_ip.to_string(),
|
||||
});
|
||||
app.netplay.server = NetplayServer {
|
||||
name: server.0.clone(),
|
||||
ip: server.1.clone(),
|
||||
};
|
||||
}
|
||||
app.netplay.broadcast_socket = None;
|
||||
}
|
||||
@@ -143,11 +161,19 @@ fn get_servers(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
if app.netplay.server_receiver.is_some() {
|
||||
let result = app.netplay.server_receiver.as_mut().unwrap().try_recv();
|
||||
if result.is_ok() {
|
||||
app.netplay.servers.extend(result.unwrap());
|
||||
for server in result.unwrap().iter() {
|
||||
app.netplay.servers.push(NetplayServer {
|
||||
name: server.0.clone(),
|
||||
ip: server.1.clone(),
|
||||
});
|
||||
}
|
||||
app.netplay.server_receiver = None;
|
||||
if app.netplay.server.0.is_empty() {
|
||||
let first_server = app.netplay.servers.iter().next().unwrap();
|
||||
app.netplay.server = (first_server.0.clone(), first_server.1.clone());
|
||||
if app.netplay.server.name.is_empty() {
|
||||
let first_server = app.netplay.servers.first().unwrap();
|
||||
app.netplay.server = NetplayServer {
|
||||
name: first_server.name.clone(),
|
||||
ip: first_server.ip.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
ctx.request_repaint();
|
||||
@@ -183,19 +209,21 @@ pub fn netplay_create(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
app.netplay.game_info_receiver = Some(rx);
|
||||
let gui_ctx = ctx.clone();
|
||||
app.netplay.create_rom_label = "Inspecting ROM".to_string();
|
||||
let task = rfd::AsyncFileDialog::new().pick_file();
|
||||
let task = rfd::AsyncFileDialog::new()
|
||||
.set_title("Select ROM")
|
||||
.pick_file();
|
||||
tokio::spawn(async move {
|
||||
let file = task.await;
|
||||
|
||||
if let Some(file) = file {
|
||||
parse_rom_file(file, tx).await;
|
||||
} else {
|
||||
tx.send((
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"Open ROM".to_string(),
|
||||
vec![],
|
||||
))
|
||||
tx.send(GameInfo {
|
||||
md5: "".to_string(),
|
||||
game_name: "".to_string(),
|
||||
rom_label: "Open ROM".to_string(),
|
||||
rom_contents: vec![],
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -221,25 +249,28 @@ pub fn netplay_create(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
if result.is_ok() {
|
||||
app.netplay.game_info_receiver = None;
|
||||
let data = result.unwrap();
|
||||
if !data.0.is_empty() {
|
||||
if !data.md5.is_empty() {
|
||||
app.netplay.game_info = data;
|
||||
app.netplay.rom_contents = app.netplay.game_info.3.clone();
|
||||
app.netplay.create_rom_label = app.netplay.game_info.2.clone();
|
||||
app.netplay.rom_contents = app.netplay.game_info.rom_contents.clone();
|
||||
app.netplay.create_rom_label = app.netplay.game_info.rom_label.clone();
|
||||
} else {
|
||||
app.netplay.create_rom_label = data.2;
|
||||
app.netplay.create_rom_label = data.rom_label;
|
||||
}
|
||||
}
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
egui::ComboBox::from_id_salt("server-combobox")
|
||||
.selected_text(app.netplay.server.0.to_string())
|
||||
.selected_text(app.netplay.server.name.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
for server in app.netplay.servers.iter() {
|
||||
ui.selectable_value(
|
||||
&mut app.netplay.server,
|
||||
(server.0.clone(), server.1.clone()),
|
||||
server.0,
|
||||
NetplayServer {
|
||||
name: server.name.clone(),
|
||||
ip: server.ip.clone(),
|
||||
},
|
||||
server.name.clone(),
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -271,16 +302,18 @@ pub fn netplay_create(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
app.netplay.error = "Player Name cannot be empty".to_string();
|
||||
} else if app.netplay.session_name.is_empty() {
|
||||
app.netplay.error = "Session Name cannot be empty".to_string();
|
||||
} else if app.netplay.game_info.0.is_empty() {
|
||||
} else if app.netplay.game_info.md5.is_empty() {
|
||||
app.netplay.error = "ROM not loaded".to_string();
|
||||
} else {
|
||||
let now_utc = chrono::Utc::now().timestamp_millis().to_string();
|
||||
let hasher = Sha256::new().chain_update(&now_utc).chain_update(EMU_NAME);
|
||||
let mut game_name = app.netplay.game_info.1.to_string();
|
||||
let mut game_name = app.netplay.game_info.game_name.to_string();
|
||||
if game_name.is_empty() {
|
||||
// If the ROM doesn't report a name, use the filename
|
||||
game_name = app.netplay.create_rom_label.clone();
|
||||
}
|
||||
let mut features = std::collections::HashMap::new();
|
||||
features.insert("overclock".to_string(), app.overclock.to_string());
|
||||
let netplay_message = NetplayMessage {
|
||||
message_type: "request_create_room".to_string(),
|
||||
player_name: Some(app.netplay.player_name.clone()),
|
||||
@@ -297,13 +330,15 @@ pub fn netplay_create(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
room_name: Some(app.netplay.session_name.clone()),
|
||||
password: Some(app.netplay.password.clone()),
|
||||
game_name: Some(game_name),
|
||||
md5: Some(app.netplay.game_info.0.clone()),
|
||||
md5: Some(app.netplay.game_info.md5.clone()),
|
||||
protected: None,
|
||||
port: None,
|
||||
features: Some(features),
|
||||
buffer_target: None,
|
||||
}),
|
||||
};
|
||||
let (mut socket, _response) =
|
||||
tungstenite::connect(&app.netplay.server.1).expect("Can't connect");
|
||||
tungstenite::connect(&app.netplay.server.ip).expect("Can't connect");
|
||||
socket
|
||||
.send(tungstenite::Message::Binary(tungstenite::Bytes::from(
|
||||
serde_json::to_vec(&netplay_message).unwrap(),
|
||||
@@ -350,7 +385,7 @@ fn get_sessions(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
}
|
||||
if app.netplay.socket.is_none() {
|
||||
let (mut sock, _response) =
|
||||
tungstenite::connect(&app.netplay.server.1).expect("Can't connect");
|
||||
tungstenite::connect(&app.netplay.server.ip).expect("Can't connect");
|
||||
match sock.get_mut() {
|
||||
tungstenite::stream::MaybeTlsStream::Plain(stream) => {
|
||||
app.netplay.peer_addr = Some(stream.peer_addr().unwrap());
|
||||
@@ -385,28 +420,35 @@ fn get_sessions(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
serde_json::to_vec(&request_rooms).unwrap(),
|
||||
)))
|
||||
.unwrap();
|
||||
app.netplay.have_sessions = Some(app.netplay.server.clone());
|
||||
app.netplay.have_sessions = Some(NetplayServer {
|
||||
name: app.netplay.server.name.clone(),
|
||||
ip: app.netplay.server.ip.clone(),
|
||||
});
|
||||
app.netplay.socket_waiting = true;
|
||||
ctx.request_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
async fn parse_rom_file(file: rfd::FileHandle, tx: tokio::sync::mpsc::Sender<GameInfo>) {
|
||||
let rom_contents = device::get_rom_contents(file.path());
|
||||
if !rom_contents.is_empty() {
|
||||
if let Some(rom_contents) = device::get_rom_contents(file.path()) {
|
||||
let hash = device::cart::rom::calculate_hash(&rom_contents);
|
||||
let game_name = ui::storage::get_game_name(&rom_contents);
|
||||
|
||||
tx.send((hash, game_name, file.file_name(), rom_contents))
|
||||
.await
|
||||
.unwrap();
|
||||
tx.send(GameInfo {
|
||||
md5: hash,
|
||||
game_name,
|
||||
rom_label: file.file_name(),
|
||||
rom_contents,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
} else {
|
||||
tx.send((
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"Invalid ROM".to_string(),
|
||||
vec![],
|
||||
))
|
||||
tx.send(GameInfo {
|
||||
md5: "".to_string(),
|
||||
game_name: "".to_string(),
|
||||
rom_label: "Invalid ROM".to_string(),
|
||||
rom_contents: vec![],
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -442,9 +484,9 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
if result.is_ok() {
|
||||
app.netplay.game_info_receiver = None;
|
||||
let data = result.unwrap();
|
||||
if !data.0.is_empty() {
|
||||
if !data.md5.is_empty() {
|
||||
app.netplay.game_info = data;
|
||||
app.netplay.rom_contents = app.netplay.game_info.3.clone();
|
||||
app.netplay.rom_contents = app.netplay.game_info.rom_contents.clone();
|
||||
|
||||
let netplay_message = NetplayMessage {
|
||||
message_type: "request_join_room".to_string(),
|
||||
@@ -462,9 +504,11 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
room_name: None,
|
||||
password: Some(app.netplay.password.clone()),
|
||||
game_name: None,
|
||||
md5: Some(app.netplay.game_info.0.clone()),
|
||||
md5: Some(app.netplay.game_info.md5.clone()),
|
||||
protected: None,
|
||||
port: app.netplay.selected_session.as_ref().unwrap().port,
|
||||
features: None,
|
||||
buffer_target: None,
|
||||
}),
|
||||
};
|
||||
let socket = app.netplay.socket.as_mut().unwrap();
|
||||
@@ -476,7 +520,7 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
|
||||
app.netplay.socket_waiting = true;
|
||||
} else {
|
||||
app.netplay.error = data.2;
|
||||
app.netplay.error = data.rom_label;
|
||||
app.netplay.join_rom_label = "Join Session (Open ROM)".to_string();
|
||||
}
|
||||
}
|
||||
@@ -501,19 +545,22 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
egui::ComboBox::from_id_salt("server-combobox")
|
||||
.selected_text(app.netplay.server.0.to_string())
|
||||
.selected_text(app.netplay.server.name.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
for server in app.netplay.servers.iter() {
|
||||
ui.selectable_value(
|
||||
&mut app.netplay.server,
|
||||
(server.0.clone(), server.1.clone()),
|
||||
server.0,
|
||||
NetplayServer {
|
||||
name: server.name.clone(),
|
||||
ip: server.ip.clone(),
|
||||
},
|
||||
server.name.clone(),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
if !app.netplay.server.0.is_empty() {
|
||||
if !app.netplay.server.name.is_empty() {
|
||||
get_sessions(app, ctx);
|
||||
}
|
||||
ui.add_space(16.0);
|
||||
@@ -583,19 +630,21 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
app.netplay.game_info_receiver = Some(rx);
|
||||
let gui_ctx = ctx.clone();
|
||||
app.netplay.join_rom_label = "Inspecting ROM".to_string();
|
||||
let task = rfd::AsyncFileDialog::new().pick_file();
|
||||
let task = rfd::AsyncFileDialog::new()
|
||||
.set_title("Select ROM")
|
||||
.pick_file();
|
||||
tokio::spawn(async move {
|
||||
let file = task.await;
|
||||
|
||||
if let Some(file) = file {
|
||||
parse_rom_file(file, tx).await;
|
||||
} else {
|
||||
tx.send((
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"No ROM selected".to_string(),
|
||||
vec![],
|
||||
))
|
||||
tx.send(GameInfo {
|
||||
md5: "".to_string(),
|
||||
game_name: "".to_string(),
|
||||
rom_label: "No ROM selected".to_string(),
|
||||
rom_contents: vec![],
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -609,6 +658,17 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
}
|
||||
|
||||
pub fn netplay_wait(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
let overclock = app
|
||||
.netplay
|
||||
.waiting_session
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.features
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get("overclock")
|
||||
.unwrap();
|
||||
|
||||
let motd_message = NetplayMessage {
|
||||
message_type: "request_motd".to_string(),
|
||||
player_name: None,
|
||||
@@ -642,6 +702,8 @@ pub fn netplay_wait(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
md5: None,
|
||||
protected: None,
|
||||
port: app.netplay.waiting_session.as_ref().unwrap().port,
|
||||
features: None,
|
||||
buffer_target: None,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -680,6 +742,8 @@ pub fn netplay_wait(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
md5: None,
|
||||
protected: None,
|
||||
port: app.netplay.waiting_session.as_ref().unwrap().port,
|
||||
features: None,
|
||||
buffer_target: None,
|
||||
}),
|
||||
};
|
||||
let socket = app.netplay.socket.as_mut().unwrap();
|
||||
@@ -711,6 +775,8 @@ pub fn netplay_wait(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
md5: None,
|
||||
protected: None,
|
||||
port: app.netplay.waiting_session.as_ref().unwrap().port,
|
||||
features: None,
|
||||
buffer_target: None,
|
||||
}),
|
||||
};
|
||||
app.netplay.chat_message.clear();
|
||||
@@ -759,7 +825,7 @@ pub fn netplay_wait(app: &mut GopherEguiApp, ctx: &egui::Context) {
|
||||
};
|
||||
}
|
||||
|
||||
gui::open_rom(app, ctx);
|
||||
gui::open_rom(app, ctx, *overclock == "true");
|
||||
app.netplay = Default::default();
|
||||
return;
|
||||
} else {
|
||||
|
||||
+353
-210
@@ -34,6 +34,11 @@ pub struct Controllers {
|
||||
pub joystick: *mut sdl3_sys::joystick::SDL_Joystick,
|
||||
}
|
||||
|
||||
pub struct InputData {
|
||||
pub data: u32,
|
||||
pub pak_change_pressed: bool,
|
||||
}
|
||||
|
||||
fn bound_axis(x: &mut f64, y: &mut f64) {
|
||||
let radius = 95.0; // this is roughly the maxium diagonal distance of the controller
|
||||
|
||||
@@ -54,35 +59,35 @@ fn set_axis_from_joystick(
|
||||
) -> (f64, f64) {
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
if profile.joystick_axis[AXIS_LEFT].0 {
|
||||
if profile.joystick_axis[AXIS_LEFT].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_LEFT].1)
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_LEFT].id)
|
||||
};
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_LEFT].2 as isize > 0 {
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_LEFT].axis as isize > 0 {
|
||||
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
|
||||
}
|
||||
}
|
||||
if profile.joystick_axis[AXIS_RIGHT].0 {
|
||||
if profile.joystick_axis[AXIS_RIGHT].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_RIGHT].1)
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_RIGHT].id)
|
||||
};
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_RIGHT].2 as isize > 0 {
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_RIGHT].axis as isize > 0 {
|
||||
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
|
||||
}
|
||||
}
|
||||
if profile.joystick_axis[AXIS_DOWN].0 {
|
||||
if profile.joystick_axis[AXIS_DOWN].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_DOWN].1)
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_DOWN].id)
|
||||
};
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_DOWN].2 as isize > 0 {
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_DOWN].axis as isize > 0 {
|
||||
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
|
||||
}
|
||||
}
|
||||
if profile.joystick_axis[AXIS_UP].0 {
|
||||
if profile.joystick_axis[AXIS_UP].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_UP].1)
|
||||
sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_UP].id)
|
||||
};
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_UP].2 as isize > 0 {
|
||||
if axis_position as isize * profile.joystick_axis[AXIS_UP].axis as isize > 0 {
|
||||
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
|
||||
}
|
||||
}
|
||||
@@ -128,47 +133,47 @@ fn set_axis_from_controller(
|
||||
) -> (f64, f64) {
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
if profile.controller_axis[AXIS_LEFT].0 {
|
||||
if profile.controller_axis[AXIS_LEFT].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadAxis(
|
||||
controller,
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_LEFT].1),
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_LEFT].id),
|
||||
)
|
||||
};
|
||||
if axis_position as isize * profile.controller_axis[AXIS_LEFT].2 as isize > 0 {
|
||||
if axis_position as isize * profile.controller_axis[AXIS_LEFT].axis as isize > 0 {
|
||||
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
|
||||
}
|
||||
}
|
||||
if profile.controller_axis[AXIS_RIGHT].0 {
|
||||
if profile.controller_axis[AXIS_RIGHT].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadAxis(
|
||||
controller,
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_RIGHT].1),
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_RIGHT].id),
|
||||
)
|
||||
};
|
||||
if axis_position as isize * profile.controller_axis[AXIS_RIGHT].2 as isize > 0 {
|
||||
if axis_position as isize * profile.controller_axis[AXIS_RIGHT].axis as isize > 0 {
|
||||
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
|
||||
}
|
||||
}
|
||||
if profile.controller_axis[AXIS_DOWN].0 {
|
||||
if profile.controller_axis[AXIS_DOWN].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadAxis(
|
||||
controller,
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_DOWN].1),
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_DOWN].id),
|
||||
)
|
||||
};
|
||||
if axis_position as isize * profile.controller_axis[AXIS_DOWN].2 as isize > 0 {
|
||||
if axis_position as isize * profile.controller_axis[AXIS_DOWN].axis as isize > 0 {
|
||||
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
|
||||
}
|
||||
}
|
||||
if profile.controller_axis[AXIS_UP].0 {
|
||||
if profile.controller_axis[AXIS_UP].enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadAxis(
|
||||
controller,
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_UP].1),
|
||||
get_axis_from_i32(profile.controller_axis[AXIS_UP].id),
|
||||
)
|
||||
};
|
||||
if axis_position as isize * profile.controller_axis[AXIS_UP].2 as isize > 0 {
|
||||
if axis_position as isize * profile.controller_axis[AXIS_UP].axis as isize > 0 {
|
||||
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
|
||||
}
|
||||
}
|
||||
@@ -181,23 +186,23 @@ fn set_axis_from_keys(
|
||||
) -> (f64, f64) {
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
if profile.keys[AXIS_LEFT].0
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_LEFT].1 as isize) }
|
||||
if profile.keys[AXIS_LEFT].enabled
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_LEFT].id as isize) }
|
||||
{
|
||||
x = -MAX_AXIS_VALUE
|
||||
}
|
||||
if profile.keys[AXIS_RIGHT].0
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_RIGHT].1 as isize) }
|
||||
if profile.keys[AXIS_RIGHT].enabled
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_RIGHT].id as isize) }
|
||||
{
|
||||
x = MAX_AXIS_VALUE
|
||||
}
|
||||
if profile.keys[AXIS_DOWN].0
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_DOWN].1 as isize) }
|
||||
if profile.keys[AXIS_DOWN].enabled
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_DOWN].id as isize) }
|
||||
{
|
||||
y = -MAX_AXIS_VALUE
|
||||
}
|
||||
if profile.keys[AXIS_UP].0
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_UP].1 as isize) }
|
||||
if profile.keys[AXIS_UP].enabled
|
||||
&& unsafe { *keyboard_state.offset(profile.keys[AXIS_UP].id as isize) }
|
||||
{
|
||||
y = MAX_AXIS_VALUE
|
||||
}
|
||||
@@ -211,26 +216,26 @@ fn set_buttons_from_joystick(
|
||||
keys: &mut u32,
|
||||
) {
|
||||
let profile_joystick_button = profile.joystick_buttons[i];
|
||||
if profile_joystick_button.0 {
|
||||
if profile_joystick_button.enabled {
|
||||
*keys |= (unsafe {
|
||||
sdl3_sys::joystick::SDL_GetJoystickButton(joystick, profile_joystick_button.1)
|
||||
sdl3_sys::joystick::SDL_GetJoystickButton(joystick, profile_joystick_button.id)
|
||||
} as u32)
|
||||
<< i;
|
||||
}
|
||||
|
||||
let profile_joystick_hat = profile.joystick_hat[i];
|
||||
if profile_joystick_hat.0
|
||||
&& unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, profile_joystick_hat.1) }
|
||||
== profile_joystick_hat.2
|
||||
if profile_joystick_hat.enabled
|
||||
&& unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, profile_joystick_hat.id) }
|
||||
== profile_joystick_hat.direction
|
||||
{
|
||||
*keys |= 1 << i;
|
||||
}
|
||||
|
||||
let profile_joystick_axis = profile.joystick_axis[i];
|
||||
if profile_joystick_axis.0 {
|
||||
if profile_joystick_axis.enabled {
|
||||
let axis_position =
|
||||
unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile_joystick_axis.1) };
|
||||
if axis_position as isize * profile_joystick_axis.2 as isize > 0
|
||||
unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile_joystick_axis.id) };
|
||||
if axis_position as isize * profile_joystick_axis.axis as isize > 0
|
||||
&& axis_position.saturating_abs() > i16::MAX / 2
|
||||
{
|
||||
*keys |= 1 << i;
|
||||
@@ -245,25 +250,25 @@ fn set_buttons_from_controller(
|
||||
keys: &mut u32,
|
||||
) {
|
||||
let profile_controller_button = profile.controller_buttons[i];
|
||||
if profile_controller_button.0 {
|
||||
if profile_controller_button.enabled {
|
||||
*keys |= (unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadButton(
|
||||
controller,
|
||||
get_button_from_i32(profile_controller_button.1),
|
||||
get_button_from_i32(profile_controller_button.id),
|
||||
)
|
||||
} as u32)
|
||||
<< i;
|
||||
}
|
||||
|
||||
let profile_controller_axis = profile.controller_axis[i];
|
||||
if profile_controller_axis.0 {
|
||||
if profile_controller_axis.enabled {
|
||||
let axis_position = unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadAxis(
|
||||
controller,
|
||||
get_axis_from_i32(profile_controller_axis.1),
|
||||
get_axis_from_i32(profile_controller_axis.id),
|
||||
)
|
||||
};
|
||||
if axis_position as isize * profile_controller_axis.2 as isize > 0
|
||||
if axis_position as isize * profile_controller_axis.axis as isize > 0
|
||||
&& axis_position.saturating_abs() > i16::MAX / 2
|
||||
{
|
||||
*keys |= 1 << i;
|
||||
@@ -271,12 +276,12 @@ fn set_buttons_from_controller(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rumble(ui: &mut ui::Ui, channel: usize, rumble: u8) {
|
||||
if !ui.controllers[channel].rumble {
|
||||
pub fn set_rumble(ui: &ui::Ui, channel: usize, rumble: u8) {
|
||||
if !ui.input.controllers[channel].rumble {
|
||||
return;
|
||||
}
|
||||
let controller = ui.controllers[channel].game_controller;
|
||||
let joystick = ui.controllers[channel].joystick;
|
||||
let controller = ui.input.controllers[channel].game_controller;
|
||||
let joystick = ui.input.controllers[channel].joystick;
|
||||
if !controller.is_null() {
|
||||
unsafe {
|
||||
sdl3_sys::gamepad::SDL_RumbleGamepad(
|
||||
@@ -310,36 +315,39 @@ fn change_paks(
|
||||
let key = profile.keys[CHANGE_PAK];
|
||||
|
||||
let mut pressed = false;
|
||||
if controller_button.0 && !controller.is_null() {
|
||||
if controller_button.enabled && !controller.is_null() {
|
||||
pressed = unsafe {
|
||||
sdl3_sys::gamepad::SDL_GetGamepadButton(
|
||||
controller,
|
||||
get_button_from_i32(controller_button.1),
|
||||
get_button_from_i32(controller_button.id),
|
||||
)
|
||||
};
|
||||
} else if joystick_button.0 && !joystick.is_null() {
|
||||
pressed = unsafe { sdl3_sys::joystick::SDL_GetJoystickButton(joystick, joystick_button.1) };
|
||||
} else if joystick_hat.0 && !joystick.is_null() {
|
||||
pressed = unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, joystick_hat.1) }
|
||||
== joystick_hat.2;
|
||||
} else if key.0 {
|
||||
pressed = unsafe { *keyboard_state.offset(key.1 as isize) };
|
||||
} else if joystick_button.enabled && !joystick.is_null() {
|
||||
pressed =
|
||||
unsafe { sdl3_sys::joystick::SDL_GetJoystickButton(joystick, joystick_button.id) };
|
||||
} else if joystick_hat.enabled && !joystick.is_null() {
|
||||
pressed = unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, joystick_hat.id) }
|
||||
== joystick_hat.direction;
|
||||
} else if key.enabled {
|
||||
pressed = unsafe { *keyboard_state.offset(key.id as isize) };
|
||||
}
|
||||
pressed
|
||||
}
|
||||
|
||||
pub fn get(ui: &mut ui::Ui, channel: usize) -> (u32, bool) {
|
||||
pub fn get(ui: &ui::Ui, channel: usize) -> InputData {
|
||||
let profile_name = ui.config.input.input_profile_binding[channel].clone();
|
||||
let profile = ui.config.input.input_profiles.get(&profile_name).unwrap();
|
||||
let mut keys = 0;
|
||||
let controller = ui.controllers[channel].game_controller;
|
||||
let joystick = ui.controllers[channel].joystick;
|
||||
let controller = ui.input.controllers[channel].game_controller;
|
||||
let joystick = ui.input.controllers[channel].joystick;
|
||||
|
||||
let alt_pressed = unsafe {
|
||||
// ignore key presses if ALT is pressed
|
||||
*ui.keyboard_state
|
||||
*ui.input
|
||||
.keyboard_state
|
||||
.offset(i32::from(sdl3_sys::scancode::SDL_SCANCODE_LALT) as isize)
|
||||
|| *ui
|
||||
.input
|
||||
.keyboard_state
|
||||
.offset(i32::from(sdl3_sys::scancode::SDL_SCANCODE_RALT) as isize)
|
||||
};
|
||||
@@ -347,8 +355,10 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> (u32, bool) {
|
||||
for i in 0..14 {
|
||||
if profile_name != "default" || channel == 0 {
|
||||
let profile_key = profile.keys[i];
|
||||
if profile_key.0 && !alt_pressed {
|
||||
keys |= (unsafe { *ui.keyboard_state.offset(profile_key.1 as isize) } as u32) << i;
|
||||
if profile_key.enabled && !alt_pressed {
|
||||
keys |= (unsafe { *ui.input.keyboard_state.offset(profile_key.id as isize) }
|
||||
as u32)
|
||||
<< i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +373,7 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> (u32, bool) {
|
||||
let mut y: f64 = 0.0;
|
||||
|
||||
if profile_name != "default" || channel == 0 {
|
||||
(x, y) = set_axis_from_keys(profile, ui.keyboard_state);
|
||||
(x, y) = set_axis_from_keys(profile, ui.input.keyboard_state);
|
||||
}
|
||||
|
||||
if !controller.is_null() {
|
||||
@@ -376,17 +386,17 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> (u32, bool) {
|
||||
keys |= (x.round() as i8 as u8 as u32) << X_AXIS_SHIFT;
|
||||
keys |= (y.round() as i8 as u8 as u32) << Y_AXIS_SHIFT;
|
||||
|
||||
(
|
||||
keys,
|
||||
change_paks(profile, joystick, controller, ui.keyboard_state),
|
||||
)
|
||||
InputData {
|
||||
data: keys,
|
||||
pak_change_pressed: change_paks(profile, joystick, controller, ui.input.keyboard_state),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assign_controller(ui: &mut ui::Ui, controller: i32, port: usize) {
|
||||
if controller < ui.num_joysticks {
|
||||
if controller < ui.input.joysticks.len() as i32 {
|
||||
let path = unsafe {
|
||||
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(
|
||||
*(ui.joysticks.offset(controller as isize)),
|
||||
ui.input.joysticks[controller as usize],
|
||||
))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
@@ -438,16 +448,14 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
let mut open_joysticks: Vec<*mut sdl3_sys::joystick::SDL_Joystick> = Vec::new();
|
||||
let mut open_controllers: Vec<*mut sdl3_sys::gamepad::SDL_Gamepad> = Vec::new();
|
||||
|
||||
for offset in 0..ui.num_joysticks as isize {
|
||||
for joystick in ui.input.joysticks.iter() {
|
||||
if !dinput {
|
||||
let controller =
|
||||
unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*ui.joysticks.offset(offset)) };
|
||||
let controller = unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*joystick) };
|
||||
if !controller.is_null() {
|
||||
open_controllers.push(controller);
|
||||
}
|
||||
} else {
|
||||
let joystick =
|
||||
unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*ui.joysticks.offset(offset)) };
|
||||
let joystick = unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*joystick) };
|
||||
if !joystick.is_null() {
|
||||
open_joysticks.push(joystick);
|
||||
}
|
||||
@@ -472,8 +480,6 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
if !unsafe { sdl3_sys::video::SDL_ShowWindow(window) } {
|
||||
panic!("Could not show window")
|
||||
}
|
||||
let font =
|
||||
rusttype::Font::try_from_bytes(include_bytes!("../../data/Roboto-Regular.ttf")).unwrap();
|
||||
|
||||
let key_labels: [(&str, usize); PROFILE_SIZE] = [
|
||||
("A", A_BUTTON),
|
||||
@@ -497,14 +503,43 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
("Change Pak", CHANGE_PAK),
|
||||
];
|
||||
|
||||
let mut new_keys = [(false, 0); PROFILE_SIZE];
|
||||
let mut new_joystick_buttons = [(false, 0i32); PROFILE_SIZE];
|
||||
let mut new_joystick_hat = [(false, 0i32, 0); PROFILE_SIZE];
|
||||
let mut new_joystick_axis = [(false, 0i32, 0); PROFILE_SIZE];
|
||||
let mut new_controller_buttons = [(false, 0i32); PROFILE_SIZE];
|
||||
let mut new_controller_axis = [(false, 0i32, 0); PROFILE_SIZE];
|
||||
let mut new_keys = [ui::config::InputKeyButton {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut new_joystick_buttons = [ui::config::InputKeyButton {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut new_joystick_hat = [ui::config::InputJoystickHat {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
direction: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut new_joystick_axis = [ui::config::InputControllerAxis {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
axis: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut new_controller_buttons = [ui::config::InputKeyButton {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut new_controller_axis = [ui::config::InputControllerAxis {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
axis: 0,
|
||||
}; PROFILE_SIZE];
|
||||
|
||||
let mut last_joystick_axis_result = ui::config::InputControllerAxis {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
axis: 0,
|
||||
};
|
||||
|
||||
let font =
|
||||
ab_glyph::FontRef::try_from_slice(include_bytes!("../../data/Roboto-Regular.ttf")).unwrap();
|
||||
|
||||
let mut last_joystick_axis_result = (false, 0, 0);
|
||||
for (key, value) in key_labels.iter() {
|
||||
let mut event: sdl3_sys::events::SDL_Event = Default::default();
|
||||
while unsafe { sdl3_sys::events::SDL_PollEvent(&mut event) } {} // clear events
|
||||
@@ -523,25 +558,35 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
if event_type == u32::from(sdl3_sys::events::SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
|
||||
close_controllers(open_joysticks, open_controllers);
|
||||
return;
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_KEY_UP) {
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_KEY_DOWN) {
|
||||
if unsafe {
|
||||
event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_LALT
|
||||
!event.key.repeat
|
||||
&& event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_LALT
|
||||
&& event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_RALT
|
||||
} {
|
||||
new_keys[*value] = (true, i32::from(unsafe { event.key.scancode }));
|
||||
new_keys[*value] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(unsafe { event.key.scancode }),
|
||||
};
|
||||
key_set = true
|
||||
}
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
|
||||
if !open_controllers.is_empty() {
|
||||
new_controller_buttons[*value] =
|
||||
(true, i32::from(unsafe { event.gbutton.button }));
|
||||
new_controller_buttons[*value] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(unsafe { event.gbutton.button }),
|
||||
};
|
||||
key_set = true
|
||||
}
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_GAMEPAD_AXIS_MOTION) {
|
||||
let axis_value = unsafe { event.gaxis.value };
|
||||
let axis = unsafe { event.gaxis.axis };
|
||||
if !open_controllers.is_empty() && axis_value.saturating_abs() > i16::MAX / 2 {
|
||||
let result = (true, axis as i32, axis_value / axis_value.saturating_abs());
|
||||
let result = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: axis as i32,
|
||||
axis: axis_value / axis_value.saturating_abs(),
|
||||
};
|
||||
if result != last_joystick_axis_result {
|
||||
new_controller_axis[*value] = result;
|
||||
last_joystick_axis_result = result;
|
||||
@@ -551,15 +596,21 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_JOYSTICK_BUTTON_DOWN)
|
||||
{
|
||||
if !open_joysticks.is_empty() {
|
||||
new_joystick_buttons[*value] =
|
||||
(true, i32::from(unsafe { event.jbutton.button }));
|
||||
new_joystick_buttons[*value] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(unsafe { event.jbutton.button }),
|
||||
};
|
||||
key_set = true
|
||||
}
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_JOYSTICK_HAT_MOTION) {
|
||||
let state = unsafe { event.jhat.value };
|
||||
let hat = unsafe { event.jhat.hat };
|
||||
if !open_joysticks.is_empty() && state != sdl3_sys::joystick::SDL_HAT_CENTERED {
|
||||
new_joystick_hat[*value] = (true, hat as i32, state);
|
||||
new_joystick_hat[*value] = ui::config::InputJoystickHat {
|
||||
enabled: true,
|
||||
id: hat as i32,
|
||||
direction: state,
|
||||
};
|
||||
key_set = true
|
||||
}
|
||||
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_JOYSTICK_AXIS_MOTION)
|
||||
@@ -567,7 +618,11 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
let axis_value = unsafe { event.jaxis.value };
|
||||
let axis = unsafe { event.jaxis.axis };
|
||||
if !open_joysticks.is_empty() && axis_value.saturating_abs() > i16::MAX / 2 {
|
||||
let result = (true, axis as i32, axis_value / axis_value.saturating_abs());
|
||||
let result = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: axis as i32,
|
||||
axis: axis_value / axis_value.saturating_abs(),
|
||||
};
|
||||
if result != last_joystick_axis_result {
|
||||
new_joystick_axis[*value] = result;
|
||||
last_joystick_axis_result = result;
|
||||
@@ -599,142 +654,230 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
}
|
||||
|
||||
pub fn get_default_profile() -> ui::config::InputProfile {
|
||||
let mut default_controller_buttons = [(false, 0); PROFILE_SIZE];
|
||||
let mut default_controller_axis = [(false, 0, 0); PROFILE_SIZE];
|
||||
let mut default_keys = [(false, 0); PROFILE_SIZE];
|
||||
default_keys[R_DPAD] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_D));
|
||||
default_keys[L_DPAD] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_A));
|
||||
default_keys[D_DPAD] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_S));
|
||||
default_keys[U_DPAD] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_W));
|
||||
default_keys[START_BUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_RETURN));
|
||||
default_keys[Z_TRIG] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_Z));
|
||||
default_keys[B_BUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_LCTRL));
|
||||
default_keys[A_BUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_LSHIFT));
|
||||
default_keys[R_CBUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_L));
|
||||
default_keys[L_CBUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_J));
|
||||
default_keys[D_CBUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_K));
|
||||
default_keys[U_CBUTTON] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_I));
|
||||
default_keys[R_TRIG] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_C));
|
||||
default_keys[L_TRIG] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_X));
|
||||
default_keys[AXIS_LEFT] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_LEFT));
|
||||
default_keys[AXIS_RIGHT] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_RIGHT));
|
||||
default_keys[AXIS_UP] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_UP));
|
||||
default_keys[AXIS_DOWN] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_DOWN));
|
||||
default_keys[CHANGE_PAK] = (true, i32::from(sdl3_sys::scancode::SDL_SCANCODE_COMMA));
|
||||
let mut default_controller_buttons = [ui::config::InputKeyButton {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut default_controller_axis = [ui::config::InputControllerAxis {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
axis: 0,
|
||||
}; PROFILE_SIZE];
|
||||
let mut default_keys = [ui::config::InputKeyButton {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
}; PROFILE_SIZE];
|
||||
default_keys[R_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_D),
|
||||
};
|
||||
default_keys[L_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_A),
|
||||
};
|
||||
default_keys[D_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_S),
|
||||
};
|
||||
default_keys[U_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_W),
|
||||
};
|
||||
default_keys[START_BUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RETURN),
|
||||
};
|
||||
default_keys[Z_TRIG] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_Z),
|
||||
};
|
||||
default_keys[B_BUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LCTRL),
|
||||
};
|
||||
default_keys[A_BUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LSHIFT),
|
||||
};
|
||||
default_keys[R_CBUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_L),
|
||||
};
|
||||
default_keys[L_CBUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_J),
|
||||
};
|
||||
default_keys[D_CBUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_K),
|
||||
};
|
||||
default_keys[U_CBUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_I),
|
||||
};
|
||||
default_keys[R_TRIG] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_C),
|
||||
};
|
||||
default_keys[L_TRIG] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_X),
|
||||
};
|
||||
default_keys[AXIS_LEFT] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LEFT),
|
||||
};
|
||||
default_keys[AXIS_RIGHT] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RIGHT),
|
||||
};
|
||||
default_keys[AXIS_UP] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_UP),
|
||||
};
|
||||
default_keys[AXIS_DOWN] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_DOWN),
|
||||
};
|
||||
default_keys[CHANGE_PAK] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_COMMA),
|
||||
};
|
||||
|
||||
default_controller_buttons[R_DPAD] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_RIGHT),
|
||||
);
|
||||
default_controller_buttons[L_DPAD] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_LEFT),
|
||||
);
|
||||
default_controller_buttons[D_DPAD] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_DOWN),
|
||||
);
|
||||
default_controller_buttons[U_DPAD] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_UP),
|
||||
);
|
||||
default_controller_buttons[START_BUTTON] =
|
||||
(true, i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_START));
|
||||
default_controller_axis[Z_TRIG] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFT_TRIGGER),
|
||||
1,
|
||||
);
|
||||
default_controller_buttons[B_BUTTON] =
|
||||
(true, i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_WEST));
|
||||
default_controller_buttons[A_BUTTON] =
|
||||
(true, i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_SOUTH));
|
||||
default_controller_axis[R_CBUTTON] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
1,
|
||||
);
|
||||
default_controller_axis[L_CBUTTON] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
-1,
|
||||
);
|
||||
default_controller_axis[D_CBUTTON] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY),
|
||||
1,
|
||||
);
|
||||
default_controller_axis[U_CBUTTON] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY),
|
||||
-1,
|
||||
);
|
||||
default_controller_buttons[R_TRIG] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER),
|
||||
);
|
||||
default_controller_buttons[L_TRIG] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER),
|
||||
);
|
||||
default_controller_axis[AXIS_LEFT] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX),
|
||||
-1,
|
||||
);
|
||||
default_controller_axis[AXIS_RIGHT] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX),
|
||||
1,
|
||||
);
|
||||
default_controller_axis[AXIS_UP] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY),
|
||||
-1,
|
||||
);
|
||||
default_controller_axis[AXIS_DOWN] = (
|
||||
true,
|
||||
i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY),
|
||||
1,
|
||||
);
|
||||
default_controller_buttons[CHANGE_PAK] =
|
||||
(true, i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_BACK));
|
||||
default_controller_buttons[R_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_RIGHT),
|
||||
};
|
||||
default_controller_buttons[L_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_LEFT),
|
||||
};
|
||||
default_controller_buttons[D_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_DOWN),
|
||||
};
|
||||
default_controller_buttons[U_DPAD] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_UP),
|
||||
};
|
||||
default_controller_buttons[START_BUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_START),
|
||||
};
|
||||
default_controller_axis[Z_TRIG] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFT_TRIGGER),
|
||||
axis: 1,
|
||||
};
|
||||
default_controller_buttons[B_BUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_WEST),
|
||||
};
|
||||
default_controller_buttons[A_BUTTON] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_SOUTH),
|
||||
};
|
||||
default_controller_axis[R_CBUTTON] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
axis: 1,
|
||||
};
|
||||
default_controller_axis[L_CBUTTON] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
axis: -1,
|
||||
};
|
||||
default_controller_axis[D_CBUTTON] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY),
|
||||
axis: 1,
|
||||
};
|
||||
default_controller_axis[U_CBUTTON] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY),
|
||||
axis: -1,
|
||||
};
|
||||
default_controller_buttons[R_TRIG] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER),
|
||||
};
|
||||
default_controller_buttons[L_TRIG] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER),
|
||||
};
|
||||
default_controller_axis[AXIS_LEFT] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX),
|
||||
axis: -1,
|
||||
};
|
||||
default_controller_axis[AXIS_RIGHT] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX),
|
||||
axis: 1,
|
||||
};
|
||||
default_controller_axis[AXIS_UP] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY),
|
||||
axis: -1,
|
||||
};
|
||||
default_controller_axis[AXIS_DOWN] = ui::config::InputControllerAxis {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY),
|
||||
axis: 1,
|
||||
};
|
||||
default_controller_buttons[CHANGE_PAK] = ui::config::InputKeyButton {
|
||||
enabled: true,
|
||||
id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_BACK),
|
||||
};
|
||||
|
||||
ui::config::InputProfile {
|
||||
keys: default_keys,
|
||||
controller_buttons: default_controller_buttons,
|
||||
controller_axis: default_controller_axis,
|
||||
joystick_buttons: Default::default(),
|
||||
joystick_hat: Default::default(),
|
||||
joystick_axis: Default::default(),
|
||||
joystick_buttons: [ui::config::InputKeyButton {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
}; PROFILE_SIZE],
|
||||
joystick_hat: [ui::config::InputJoystickHat {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
direction: 0,
|
||||
}; PROFILE_SIZE],
|
||||
joystick_axis: [ui::config::InputControllerAxis {
|
||||
enabled: false,
|
||||
id: 0,
|
||||
axis: 0,
|
||||
}; PROFILE_SIZE],
|
||||
dinput: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui) {
|
||||
ui.keyboard_state = unsafe { sdl3_sys::keyboard::SDL_GetKeyboardState(std::ptr::null_mut()) };
|
||||
if ui.keyboard_state.is_null() {
|
||||
ui.input.keyboard_state =
|
||||
unsafe { sdl3_sys::keyboard::SDL_GetKeyboardState(std::ptr::null_mut()) };
|
||||
if ui.input.keyboard_state.is_null() {
|
||||
panic!("Could not get keyboard state");
|
||||
}
|
||||
|
||||
let mut taken = [false; 4];
|
||||
for i in 0..4 {
|
||||
let controller_assignment = &ui.config.input.controller_assignment[i];
|
||||
if controller_assignment.is_some() && ui.config.input.controller_enabled[i] {
|
||||
let mut joystick_id = 0;
|
||||
let assigned_path = controller_assignment.as_ref().unwrap();
|
||||
|
||||
for offset in 0..ui.num_joysticks as isize {
|
||||
for joystick in ui.input.joysticks.iter() {
|
||||
let path = unsafe {
|
||||
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(
|
||||
*(ui.joysticks.offset(offset)),
|
||||
*joystick,
|
||||
))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
};
|
||||
if path == *assigned_path && !taken[offset as usize] {
|
||||
joystick_id = unsafe { *(ui.joysticks.offset(offset)) };
|
||||
taken[offset as usize] = true;
|
||||
if path == *assigned_path
|
||||
&& unsafe { sdl3_sys::joystick::SDL_GetJoystickFromID(*joystick) }.is_null()
|
||||
&& unsafe { sdl3_sys::gamepad::SDL_GetGamepadFromID(*joystick) }.is_null()
|
||||
{
|
||||
joystick_id = *joystick;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -748,13 +891,13 @@ pub fn init(ui: &mut ui::Ui) {
|
||||
if gamepad.is_null() {
|
||||
println!("could not connect gamepad: {}", joystick_id)
|
||||
} else {
|
||||
ui.controllers[i].game_controller = gamepad;
|
||||
ui.input.controllers[i].game_controller = gamepad;
|
||||
let properties =
|
||||
unsafe { sdl3_sys::gamepad::SDL_GetGamepadProperties(gamepad) };
|
||||
if properties == 0 {
|
||||
println!("could not get gamepad properties");
|
||||
}
|
||||
ui.controllers[i].rumble = unsafe {
|
||||
ui.input.controllers[i].rumble = unsafe {
|
||||
sdl3_sys::properties::SDL_GetBooleanProperty(
|
||||
properties,
|
||||
sdl3_sys::gamepad::SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN,
|
||||
@@ -767,13 +910,13 @@ pub fn init(ui: &mut ui::Ui) {
|
||||
if joystick.is_null() {
|
||||
println!("could not connect joystick: {}", joystick_id)
|
||||
} else {
|
||||
ui.controllers[i].joystick = joystick;
|
||||
ui.input.controllers[i].joystick = joystick;
|
||||
let properties =
|
||||
unsafe { sdl3_sys::joystick::SDL_GetJoystickProperties(joystick) };
|
||||
if properties == 0 {
|
||||
println!("could not get joystick properties");
|
||||
}
|
||||
ui.controllers[i].rumble = unsafe {
|
||||
ui.input.controllers[i].rumble = unsafe {
|
||||
sdl3_sys::properties::SDL_GetBooleanProperty(
|
||||
properties,
|
||||
sdl3_sys::joystick::SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN,
|
||||
@@ -790,7 +933,7 @@ pub fn init(ui: &mut ui::Ui) {
|
||||
}
|
||||
|
||||
pub fn close(ui: &mut ui::Ui) {
|
||||
for controller in ui.controllers.iter_mut() {
|
||||
for controller in ui.input.controllers.iter_mut() {
|
||||
if !controller.joystick.is_null() {
|
||||
unsafe { sdl3_sys::joystick::SDL_CloseJoystick(controller.joystick) }
|
||||
controller.joystick = std::ptr::null_mut();
|
||||
|
||||
+160
-81
@@ -1,4 +1,4 @@
|
||||
use crate::{netplay, ui};
|
||||
use crate::{device, netplay, ui};
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
|
||||
@@ -25,14 +25,27 @@ pub struct Paths {
|
||||
|
||||
// the bool indicates whether the save has been written to
|
||||
// if that is the case, it will be flushed to the disk when the program closes
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Save {
|
||||
pub data: Vec<u8>,
|
||||
pub written: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct RomSave {
|
||||
pub data: std::collections::HashMap<u32, u8>,
|
||||
pub written: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Saves {
|
||||
pub eeprom: (Vec<u8>, bool),
|
||||
pub sram: (Vec<u8>, bool),
|
||||
pub flash: (Vec<u8>, bool),
|
||||
pub mempak: (Vec<u8>, bool),
|
||||
pub sdcard: (Vec<u8>, bool),
|
||||
pub romsave: (std::collections::HashMap<u32, u8>, bool),
|
||||
pub eeprom: Save,
|
||||
pub sram: Save,
|
||||
pub flash: Save,
|
||||
pub mempak: Save,
|
||||
pub sdcard: Save,
|
||||
pub romsave: RomSave,
|
||||
pub write_to_disk: bool,
|
||||
}
|
||||
|
||||
fn get_save_type(rom: &[u8], game_id: &str) -> Vec<SaveTypes> {
|
||||
@@ -121,7 +134,7 @@ pub fn get_game_name(rom: &[u8]) -> String {
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui, rom: &[u8]) {
|
||||
ui.save_type = get_save_type(rom, &ui.game_id);
|
||||
ui.storage.save_type = get_save_type(rom, &ui.game_id);
|
||||
|
||||
let saves_path = ui.dirs.data_dir.join("saves");
|
||||
|
||||
@@ -135,67 +148,78 @@ pub fn init(ui: &mut ui::Ui, rom: &[u8]) {
|
||||
&game_name
|
||||
};
|
||||
|
||||
ui.paths.eep_file_path.clone_from(&saves_path);
|
||||
ui.paths
|
||||
ui.storage.paths.eep_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.eep_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".eep");
|
||||
|
||||
ui.paths.sra_file_path.clone_from(&saves_path);
|
||||
ui.paths
|
||||
ui.storage.paths.sra_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.sra_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".sra");
|
||||
|
||||
ui.paths.fla_file_path.clone_from(&saves_path);
|
||||
ui.paths
|
||||
ui.storage.paths.fla_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.fla_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".fla");
|
||||
|
||||
ui.paths.pak_file_path.clone_from(&saves_path);
|
||||
ui.paths
|
||||
ui.storage.paths.pak_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.pak_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".mpk");
|
||||
|
||||
ui.paths.sdcard_file_path.clone_from(&saves_path);
|
||||
ui.paths
|
||||
ui.storage.paths.sdcard_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.sdcard_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".img");
|
||||
|
||||
ui.paths.romsave_file_path.clone_from(&saves_path);
|
||||
ui.paths
|
||||
ui.storage.paths.romsave_file_path.clone_from(&saves_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.romsave_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".romsave");
|
||||
|
||||
ui.paths.savestate_file_path.clone_from(&states_path);
|
||||
ui.paths
|
||||
ui.storage
|
||||
.paths
|
||||
.savestate_file_path
|
||||
.clone_from(&states_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.savestate_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".state");
|
||||
}
|
||||
|
||||
pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
|
||||
if netplay.is_none() || netplay.as_ref().unwrap().player_number == 0 {
|
||||
let eep = std::fs::read(&mut ui.paths.eep_file_path);
|
||||
let eep = std::fs::read(&ui.storage.paths.eep_file_path);
|
||||
if eep.is_ok() {
|
||||
ui.saves.eeprom.0 = eep.unwrap();
|
||||
ui.storage.saves.eeprom.data = eep.unwrap();
|
||||
}
|
||||
let sra = std::fs::read(&mut ui.paths.sra_file_path);
|
||||
let sra = std::fs::read(&ui.storage.paths.sra_file_path);
|
||||
if sra.is_ok() {
|
||||
ui.saves.sram.0 = sra.unwrap();
|
||||
ui.storage.saves.sram.data = sra.unwrap();
|
||||
}
|
||||
let fla = std::fs::read(&mut ui.paths.fla_file_path);
|
||||
let fla = std::fs::read(&ui.storage.paths.fla_file_path);
|
||||
if fla.is_ok() {
|
||||
ui.saves.flash.0 = fla.unwrap();
|
||||
ui.storage.saves.flash.data = fla.unwrap();
|
||||
}
|
||||
let mempak = std::fs::read(&mut ui.paths.pak_file_path);
|
||||
let mempak = std::fs::read(&ui.storage.paths.pak_file_path);
|
||||
if mempak.is_ok() {
|
||||
ui.saves.mempak.0 = mempak.unwrap();
|
||||
ui.storage.saves.mempak.data = mempak.unwrap();
|
||||
}
|
||||
let sdcard = std::fs::read(&mut ui.paths.sdcard_file_path);
|
||||
let sdcard = std::fs::read(&ui.storage.paths.sdcard_file_path);
|
||||
if sdcard.is_ok() {
|
||||
ui.saves.sdcard.0 = sdcard.unwrap();
|
||||
ui.storage.saves.sdcard.data = sdcard.unwrap();
|
||||
}
|
||||
let romsave = std::fs::read(&mut ui.paths.romsave_file_path);
|
||||
let romsave = std::fs::read(&ui.storage.paths.romsave_file_path);
|
||||
if romsave.is_ok() {
|
||||
ui.saves.romsave.0 = postcard::from_bytes(romsave.unwrap().as_ref()).unwrap();
|
||||
ui.storage.saves.romsave.data =
|
||||
postcard::from_bytes(romsave.unwrap().as_ref()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,31 +228,31 @@ pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
|
||||
netplay::send_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"eep",
|
||||
&ui.saves.eeprom.0,
|
||||
ui.saves.eeprom.0.len(),
|
||||
&ui.storage.saves.eeprom.data,
|
||||
ui.storage.saves.eeprom.data.len(),
|
||||
);
|
||||
netplay::send_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"sra",
|
||||
&ui.saves.sram.0,
|
||||
ui.saves.sram.0.len(),
|
||||
&ui.storage.saves.sram.data,
|
||||
ui.storage.saves.sram.data.len(),
|
||||
);
|
||||
netplay::send_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"fla",
|
||||
&ui.saves.flash.0,
|
||||
ui.saves.flash.0.len(),
|
||||
&ui.storage.saves.flash.data,
|
||||
ui.storage.saves.flash.data.len(),
|
||||
);
|
||||
netplay::send_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"mpk",
|
||||
&ui.saves.mempak.0,
|
||||
ui.saves.mempak.0.len(),
|
||||
&ui.storage.saves.mempak.data,
|
||||
ui.storage.saves.mempak.data.len(),
|
||||
);
|
||||
|
||||
let mut compressed_sd = Vec::new();
|
||||
if !ui.saves.sdcard.0.is_empty() {
|
||||
compressed_sd = compress_file(&[(&ui.saves.sdcard.0, "save")]);
|
||||
if !ui.storage.saves.sdcard.data.is_empty() {
|
||||
compressed_sd = compress_file(&[(&ui.storage.saves.sdcard.data, "save")]);
|
||||
}
|
||||
netplay::send_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
@@ -238,9 +262,11 @@ pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
|
||||
);
|
||||
|
||||
let mut compressed_romsave = Vec::new();
|
||||
if !ui.saves.romsave.0.is_empty() {
|
||||
compressed_romsave =
|
||||
compress_file(&[(&postcard::to_stdvec(&ui.saves.romsave.0).unwrap(), "save")]);
|
||||
if !ui.storage.saves.romsave.data.is_empty() {
|
||||
compressed_romsave = compress_file(&[(
|
||||
&postcard::to_stdvec(&ui.storage.saves.romsave.data).unwrap(),
|
||||
"save",
|
||||
)]);
|
||||
}
|
||||
netplay::send_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
@@ -249,22 +275,38 @@ pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
|
||||
compressed_romsave.len(),
|
||||
);
|
||||
} else {
|
||||
netplay::receive_save(netplay.as_mut().unwrap(), "eep", &mut ui.saves.eeprom.0);
|
||||
netplay::receive_save(netplay.as_mut().unwrap(), "sra", &mut ui.saves.sram.0);
|
||||
netplay::receive_save(netplay.as_mut().unwrap(), "fla", &mut ui.saves.flash.0);
|
||||
netplay::receive_save(netplay.as_mut().unwrap(), "mpk", &mut ui.saves.mempak.0);
|
||||
netplay::receive_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"eep",
|
||||
&mut ui.storage.saves.eeprom.data,
|
||||
);
|
||||
netplay::receive_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"sra",
|
||||
&mut ui.storage.saves.sram.data,
|
||||
);
|
||||
netplay::receive_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"fla",
|
||||
&mut ui.storage.saves.flash.data,
|
||||
);
|
||||
netplay::receive_save(
|
||||
netplay.as_mut().unwrap(),
|
||||
"mpk",
|
||||
&mut ui.storage.saves.mempak.data,
|
||||
);
|
||||
|
||||
let mut compressed_sd = Vec::new();
|
||||
netplay::receive_save(netplay.as_mut().unwrap(), "img", &mut compressed_sd);
|
||||
if !compressed_sd.is_empty() {
|
||||
ui.saves.sdcard.0 = decompress_file(&compressed_sd, "save");
|
||||
ui.storage.saves.sdcard.data = decompress_file(&compressed_sd, "save");
|
||||
}
|
||||
|
||||
let mut compressed_romsave = Vec::new();
|
||||
netplay::receive_save(netplay.as_mut().unwrap(), "rom", &mut compressed_romsave);
|
||||
if !compressed_romsave.is_empty() {
|
||||
let romsave_bytes = decompress_file(&compressed_romsave, "save");
|
||||
ui.saves.romsave.0 = postcard::from_bytes(&romsave_bytes).unwrap();
|
||||
ui.storage.saves.romsave.data = postcard::from_bytes(&romsave_bytes).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,29 +341,66 @@ pub fn compress_file(data: &[(&[u8], &str)]) -> Vec<u8> {
|
||||
}
|
||||
|
||||
fn write_rom_save(ui: &ui::Ui) {
|
||||
let data = postcard::to_stdvec(&ui.saves.romsave.0).unwrap();
|
||||
std::fs::write(ui.paths.romsave_file_path.clone(), data).unwrap();
|
||||
let data = postcard::to_stdvec(&ui.storage.saves.romsave.data).unwrap();
|
||||
std::fs::write(ui.storage.paths.romsave_file_path.clone(), data).unwrap();
|
||||
}
|
||||
|
||||
pub fn write_saves(ui: &ui::Ui, netplay: &Option<netplay::Netplay>) {
|
||||
if netplay.is_none() || netplay.as_ref().unwrap().player_number == 0 {
|
||||
if ui.saves.eeprom.1 {
|
||||
write_save(ui, SaveTypes::Eeprom16k)
|
||||
fn writeback_sdcard(device: &mut device::Device) {
|
||||
let length;
|
||||
let save_data: &[u8];
|
||||
if device.ui.storage.saves.eeprom.written {
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
.save_type
|
||||
.contains(&ui::storage::SaveTypes::Eeprom4k)
|
||||
{
|
||||
length = 1;
|
||||
} else {
|
||||
length = 4;
|
||||
}
|
||||
if ui.saves.sram.1 {
|
||||
write_save(ui, SaveTypes::Sram)
|
||||
save_data = device.ui.storage.saves.eeprom.data.as_ref();
|
||||
} else if device.ui.storage.saves.sram.written {
|
||||
length = device.ui.storage.saves.sram.data.len() / 512;
|
||||
save_data = device.ui.storage.saves.sram.data.as_ref();
|
||||
} else if device.ui.storage.saves.flash.written {
|
||||
length = device.ui.storage.saves.flash.data.len() / 512;
|
||||
save_data = device.ui.storage.saves.flash.data.as_ref();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
for i in 0..length {
|
||||
let offset = device.cart.sc64.writeback_sector[i] as usize * 512;
|
||||
device.ui.storage.saves.sdcard.data[offset..offset + 512]
|
||||
.copy_from_slice(&save_data[i * 512..(i + 1) * 512]);
|
||||
}
|
||||
device.ui.storage.saves.sdcard.written = true;
|
||||
}
|
||||
|
||||
pub fn write_saves(device: &mut device::Device) {
|
||||
if device.netplay.is_none() || device.netplay.as_ref().unwrap().player_number == 0 {
|
||||
if device.ui.storage.saves.write_to_disk {
|
||||
if device.ui.storage.saves.eeprom.written {
|
||||
write_save(&device.ui, SaveTypes::Eeprom16k)
|
||||
}
|
||||
if device.ui.storage.saves.sram.written {
|
||||
write_save(&device.ui, SaveTypes::Sram)
|
||||
}
|
||||
if device.ui.storage.saves.flash.written {
|
||||
write_save(&device.ui, SaveTypes::Flash)
|
||||
}
|
||||
if device.ui.storage.saves.romsave.written {
|
||||
write_save(&device.ui, SaveTypes::Romsave)
|
||||
}
|
||||
} else {
|
||||
writeback_sdcard(device)
|
||||
}
|
||||
if ui.saves.flash.1 {
|
||||
write_save(ui, SaveTypes::Flash)
|
||||
if device.ui.storage.saves.mempak.written {
|
||||
write_save(&device.ui, SaveTypes::Mempak)
|
||||
}
|
||||
if ui.saves.mempak.1 {
|
||||
write_save(ui, SaveTypes::Mempak)
|
||||
}
|
||||
if ui.saves.sdcard.1 {
|
||||
write_save(ui, SaveTypes::Sdcard)
|
||||
}
|
||||
if ui.saves.romsave.1 {
|
||||
write_save(ui, SaveTypes::Romsave)
|
||||
if device.ui.storage.saves.sdcard.written {
|
||||
write_save(&device.ui, SaveTypes::Sdcard)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,24 +410,24 @@ fn write_save(ui: &ui::Ui, save_type: SaveTypes) {
|
||||
let data: &Vec<u8>;
|
||||
match save_type {
|
||||
SaveTypes::Eeprom4k | SaveTypes::Eeprom16k => {
|
||||
path = ui.paths.eep_file_path.as_ref();
|
||||
data = ui.saves.eeprom.0.as_ref();
|
||||
path = ui.storage.paths.eep_file_path.as_ref();
|
||||
data = ui.storage.saves.eeprom.data.as_ref();
|
||||
}
|
||||
SaveTypes::Sram => {
|
||||
path = ui.paths.sra_file_path.as_ref();
|
||||
data = ui.saves.sram.0.as_ref();
|
||||
path = ui.storage.paths.sra_file_path.as_ref();
|
||||
data = ui.storage.saves.sram.data.as_ref();
|
||||
}
|
||||
SaveTypes::Flash => {
|
||||
path = ui.paths.fla_file_path.as_ref();
|
||||
data = ui.saves.flash.0.as_ref();
|
||||
path = ui.storage.paths.fla_file_path.as_ref();
|
||||
data = ui.storage.saves.flash.data.as_ref();
|
||||
}
|
||||
SaveTypes::Mempak => {
|
||||
path = ui.paths.pak_file_path.as_ref();
|
||||
data = ui.saves.mempak.0.as_ref();
|
||||
path = ui.storage.paths.pak_file_path.as_ref();
|
||||
data = ui.storage.saves.mempak.data.as_ref();
|
||||
}
|
||||
SaveTypes::Sdcard => {
|
||||
path = ui.paths.sdcard_file_path.as_ref();
|
||||
data = ui.saves.sdcard.0.as_ref();
|
||||
path = ui.storage.paths.sdcard_file_path.as_ref();
|
||||
data = ui.storage.saves.sdcard.data.as_ref();
|
||||
}
|
||||
SaveTypes::Romsave => {
|
||||
write_rom_save(ui);
|
||||
|
||||
+88
-37
@@ -1,37 +1,54 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/parallel_bindings.rs"));
|
||||
use crate::{device, ui};
|
||||
use ab_glyph::{Font, ScaleFont};
|
||||
|
||||
pub fn init(device: &mut device::Device) {
|
||||
ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO);
|
||||
|
||||
let title = std::ffi::CString::new("gopher64").unwrap();
|
||||
|
||||
let mut flags = sdl3_sys::video::SDL_WINDOW_VULKAN;
|
||||
let mut flags = sdl3_sys::video::SDL_WINDOW_VULKAN
|
||||
| sdl3_sys::video::SDL_WINDOW_RESIZABLE
|
||||
| sdl3_sys::video::SDL_WINDOW_INPUT_FOCUS;
|
||||
|
||||
if device.ui.fullscreen {
|
||||
if device.ui.video.fullscreen {
|
||||
flags |= sdl3_sys::video::SDL_WINDOW_FULLSCREEN;
|
||||
} else {
|
||||
flags |= sdl3_sys::video::SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
flags |= sdl3_sys::video::SDL_WINDOW_INPUT_FOCUS;
|
||||
|
||||
let mut window_width = 640;
|
||||
let mut window_height = 480;
|
||||
let window_width;
|
||||
let window_height;
|
||||
if device.cart.pal {
|
||||
window_width = 768;
|
||||
window_width = if device.ui.config.video.widescreen {
|
||||
1024
|
||||
} else {
|
||||
768
|
||||
};
|
||||
window_height = 576;
|
||||
} else {
|
||||
window_width = if device.ui.config.video.widescreen {
|
||||
852
|
||||
} else {
|
||||
640
|
||||
};
|
||||
window_height = 480;
|
||||
}
|
||||
device.ui.window = unsafe {
|
||||
device.ui.video.window = unsafe {
|
||||
sdl3_sys::video::SDL_CreateWindow(title.as_ptr(), window_width, window_height, flags)
|
||||
};
|
||||
if device.ui.window.is_null() {
|
||||
if device.ui.video.window.is_null() {
|
||||
panic!("Could not create window");
|
||||
}
|
||||
if !unsafe { sdl3_sys::video::SDL_ShowWindow(device.ui.window) } {
|
||||
if !unsafe { sdl3_sys::video::SDL_ShowWindow(device.ui.video.window) } {
|
||||
panic!("Could not show window");
|
||||
}
|
||||
unsafe { sdl3_sys::everything::SDL_HideCursor() };
|
||||
unsafe {
|
||||
sdl3_sys::everything::SDL_HideCursor();
|
||||
sdl3_sys::everything::SDL_SetHint(
|
||||
sdl3_sys::everything::SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
|
||||
std::ffi::CString::new("1").unwrap().as_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
let gfx_info = GFX_INFO {
|
||||
RDRAM: device.rdram.mem.as_mut_ptr(),
|
||||
@@ -42,23 +59,20 @@ pub fn init(device: &mut device::Device) {
|
||||
DPC_END_REG: &mut device.rdp.regs_dpc[device::rdp::DPC_END_REG as usize],
|
||||
DPC_STATUS_REG: &mut device.rdp.regs_dpc[device::rdp::DPC_STATUS_REG as usize],
|
||||
PAL: device.cart.pal,
|
||||
widescreen: device.ui.config.video.widescreen,
|
||||
fullscreen: device.ui.video.fullscreen,
|
||||
integer_scaling: device.ui.config.video.integer_scaling,
|
||||
upscale: device.ui.config.video.upscale,
|
||||
crt: device.ui.config.video.crt,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rdp_init(
|
||||
device.ui.window as *mut std::ffi::c_void,
|
||||
gfx_info,
|
||||
device.ui.config.video.upscale,
|
||||
device.ui.config.video.integer_scaling,
|
||||
device.ui.fullscreen,
|
||||
)
|
||||
}
|
||||
unsafe { rdp_init(device.ui.video.window as *mut std::ffi::c_void, gfx_info) }
|
||||
}
|
||||
|
||||
pub fn close(ui: &ui::Ui) {
|
||||
unsafe {
|
||||
rdp_close();
|
||||
sdl3_sys::video::SDL_DestroyWindow(ui.window);
|
||||
sdl3_sys::video::SDL_DestroyWindow(ui.video.window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +81,15 @@ pub fn update_screen() {
|
||||
unsafe { rdp_update_screen() }
|
||||
}
|
||||
|
||||
pub fn load_state(device: &mut device::Device) {
|
||||
pub fn state_size() -> usize {
|
||||
unsafe { rdp_state_size() }
|
||||
}
|
||||
|
||||
pub fn save_state(rdp_state: *mut u8) {
|
||||
unsafe { rdp_save_state(rdp_state) }
|
||||
}
|
||||
|
||||
pub fn load_state(device: &mut device::Device, rdp_state: *const u8) {
|
||||
let gfx_info = GFX_INFO {
|
||||
RDRAM: device.rdram.mem.as_mut_ptr(),
|
||||
DMEM: device.rsp.mem.as_mut_ptr(),
|
||||
@@ -77,9 +99,18 @@ pub fn load_state(device: &mut device::Device) {
|
||||
DPC_END_REG: &mut device.rdp.regs_dpc[device::rdp::DPC_END_REG as usize],
|
||||
DPC_STATUS_REG: &mut device.rdp.regs_dpc[device::rdp::DPC_STATUS_REG as usize],
|
||||
PAL: device.cart.pal,
|
||||
widescreen: device.ui.config.video.widescreen,
|
||||
fullscreen: device.ui.video.fullscreen,
|
||||
integer_scaling: device.ui.config.video.integer_scaling,
|
||||
upscale: device.ui.config.video.upscale,
|
||||
crt: device.ui.config.video.crt,
|
||||
};
|
||||
unsafe {
|
||||
rdp_new_processor(gfx_info, device.ui.config.video.upscale);
|
||||
rdp_new_processor(gfx_info);
|
||||
rdp_load_state(rdp_state);
|
||||
for reg in 0..device::vi::VI_REGS_COUNT {
|
||||
rdp_set_vi_register(reg, device.vi.regs[reg as usize])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +123,13 @@ pub fn check_callback(device: &mut device::Device) {
|
||||
} else if callback.load_state {
|
||||
device.load_state = true;
|
||||
}
|
||||
device.vi.enable_speed_limiter = callback.enable_speedlimiter;
|
||||
}
|
||||
|
||||
if callback.lower_volume {
|
||||
ui::audio::lower_audio_volume(&mut device.ui);
|
||||
} else if callback.raise_volume {
|
||||
ui::audio::raise_audio_volume(&mut device.ui);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,12 +143,11 @@ pub fn process_rdp_list() -> u64 {
|
||||
unsafe { rdp_process_commands() }
|
||||
}
|
||||
|
||||
pub fn draw_text(text: &str, renderer: *mut sdl3_sys::render::SDL_Renderer, font: &rusttype::Font) {
|
||||
let text_size = 32;
|
||||
let scale = rusttype::Scale::uniform(text_size as f32);
|
||||
let v_metrics = font.v_metrics(scale);
|
||||
let offset = rusttype::point(10.0, 10.0 + v_metrics.ascent);
|
||||
|
||||
pub fn draw_text(
|
||||
text: &str,
|
||||
renderer: *mut sdl3_sys::render::SDL_Renderer,
|
||||
font: &ab_glyph::FontRef,
|
||||
) {
|
||||
// Clear the canvas
|
||||
unsafe {
|
||||
sdl3_sys::render::SDL_SetRenderDrawColor(
|
||||
@@ -123,12 +160,20 @@ pub fn draw_text(text: &str, renderer: *mut sdl3_sys::render::SDL_Renderer, font
|
||||
sdl3_sys::render::SDL_RenderClear(renderer);
|
||||
};
|
||||
|
||||
for glyph in font.layout(text, scale, offset) {
|
||||
if let Some(bb) = glyph.pixel_bounding_box() {
|
||||
glyph.draw(|x, y, v| {
|
||||
let x = x as i32 + bb.min.x;
|
||||
let y = y as i32 + bb.min.y + (240 - text_size);
|
||||
if v > 0.5 {
|
||||
let text_size = 40.0;
|
||||
let (mut w, mut h) = (0, 0);
|
||||
unsafe { sdl3_sys::render::SDL_GetRenderOutputSize(renderer, &mut w, &mut h) };
|
||||
let x_start = 20.0;
|
||||
let y_start = (h / 2) as f32;
|
||||
|
||||
let mut x_offset = 0.0;
|
||||
for c in text.chars() {
|
||||
let q_glyph_id = font.glyph_id(c);
|
||||
let q_glyph = q_glyph_id.with_scale(text_size);
|
||||
|
||||
if let Some(q) = font.outline_glyph(q_glyph) {
|
||||
q.draw(|x, y, c| {
|
||||
if c > 0.5 {
|
||||
unsafe {
|
||||
sdl3_sys::render::SDL_SetRenderDrawColor(
|
||||
renderer,
|
||||
@@ -137,11 +182,17 @@ pub fn draw_text(text: &str, renderer: *mut sdl3_sys::render::SDL_Renderer, font
|
||||
255,
|
||||
sdl3_sys::pixels::SDL_ALPHA_OPAQUE,
|
||||
);
|
||||
sdl3_sys::render::SDL_RenderPoint(renderer, x as f32, y as f32);
|
||||
sdl3_sys::render::SDL_RenderPoint(
|
||||
renderer,
|
||||
x_start + x_offset + x as f32 - q.px_bounds().width()
|
||||
+ q.px_bounds().max.x,
|
||||
y_start + y as f32 - q.px_bounds().height() + q.px_bounds().max.y,
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
x_offset += font.as_scaled(text_size).h_advance(q_glyph_id);
|
||||
}
|
||||
|
||||
// Present the canvas
|
||||
|
||||
Reference in New Issue
Block a user