Compare commits

...

85 Commits

Author SHA1 Message Date
Logan McNaughton 84dcc6c154 fix image scale when using upscaling (#317) 2025-03-10 22:53:16 +01:00
Logan McNaughton 8aa23e1665 bump to 1.0.12 (#316) 2025-03-10 17:22:51 +01:00
Logan McNaughton f2b75a2dcc update cargo packages 2025-03-10 17:14:04 +01:00
Logan McNaughton 5bac846bda update shaders (#315) 2025-03-10 17:13:45 +01:00
Logan McNaughton e93c275795 Add option for CRT shader (#314)
* add CRT shader

* more

* more

* update type

* more

* more

* more

* more
2025-03-10 16:52:25 +01:00
Logan McNaughton 67a8bb5e4c show VRU dialog in main window (#311) 2025-03-09 22:20:16 +01:00
Logan McNaughton 2828accee1 re-arrange UI struct (#310)
* re-arrange UI struct

* format
2025-03-09 10:30:43 +01:00
Logan McNaughton 16a9754bda GB MBC1 support (#309) 2025-03-08 21:48:51 +01:00
Logan McNaughton fbe95767f5 update zip 2025-03-07 18:40:11 +01:00
Logan McNaughton 0989be1e0f update cargo packages 2025-03-07 18:36:08 +01:00
Logan McNaughton e73816541e condense code (#308) 2025-03-07 18:35:48 +01:00
Logan McNaughton f6b2342e0e always make the window resizeable (#305) 2025-03-07 10:43:35 +01:00
Logan McNaughton 7ab57d592b bump to 1.0.11 (#301) 2025-03-05 15:20:42 +01:00
Logan McNaughton a2cda03c8d update cargo packages 2025-03-05 15:12:05 +01:00
Logan McNaughton a07237aa64 set netplay buffer target (#300) 2025-03-04 21:23:33 +01:00
Logan McNaughton fdc17f4ae5 make is viewer optional (#299)
* make is viewer optional

* more
2025-03-04 18:52:47 +01:00
Logan McNaughton 4802f3b71a remove pub 2025-03-04 12:03:30 +01:00
Logan McNaughton a4c505c72d remove some code duplication (#298) 2025-03-04 10:12:56 +01:00
Logan McNaughton 30cb3f8339 add some randomness (#297) 2025-03-04 09:45:25 +01:00
Logan McNaughton a5f5867ebb update cargo packages 2025-03-03 10:03:44 +01:00
Logan McNaughton ee8e34e17c Add audio file 2025-03-01 21:59:35 +01:00
Logan McNaughton da1e686c1a Add option for overclocking (#293)
* add option to overclock CPU

* netplay

* more

* more

* more

* more

* more
2025-02-28 21:34:33 +01:00
Logan McNaughton 5a3609d501 add option to crop out letterbox (#292) 2025-02-28 09:47:34 +01:00
Logan McNaughton 7b51ca82eb add widescreen stretch option (#291) 2025-02-27 22:05:19 +01:00
Logan McNaughton ee77e75855 bump to 1.0.10 (#290) 2025-02-27 17:22:19 +01:00
Logan McNaughton ff093a9f7f Update README.md 2025-02-27 15:55:07 +01:00
Logan McNaughton f7cb0db348 update cargo packages 2025-02-27 11:31:18 +01:00
Logan McNaughton eb9f37f1f5 add button to open save folder (#289) 2025-02-27 11:29:30 +01:00
Logan McNaughton 37b902eb31 use deterministic rtc time (#288)
* use deterministic rtc time

* netplay
2025-02-27 10:59:44 +01:00
Logan McNaughton 00964f06f0 use GL backend for GUI to avoid conflicts with game window (#287) 2025-02-27 10:04:53 +01:00
Logan McNaughton 319d7c8553 some mbc3 rtc code (#284) 2025-02-26 21:00:15 +01:00
Logan McNaughton 82b4adb4ad flash cart menu support (#283)
* flash cart menu support

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more
2025-02-26 10:04:44 +01:00
Logan McNaughton 8fbc63a677 add links to wiki and discord (#282) 2025-02-24 18:50:29 +01:00
Logan McNaughton 48ee50dea3 fix Stadium 2 GB games (#281) 2025-02-24 15:31:19 +01:00
Logan McNaughton 34a09925b4 fix GB tower (#280) 2025-02-24 14:43:06 +01:00
Logan McNaughton 168347b46e Transfer Pak UI (#279)
* Transfer Pak UI

* more

* more

* more

* more
2025-02-24 12:49:33 +01:00
Logan McNaughton 95a3d3d5ec fix type 2025-02-23 22:28:07 +01:00
Logan McNaughton f6349f13da tpak cleanup (#278)
* tpak cleanup

* more

* more
2025-02-23 16:10:53 +01:00
Logan McNaughton fb2cf7fcb9 move UI elements around (#277) 2025-02-23 13:25:23 +01:00
Logan McNaughton 9980302d37 more tpak code (#276)
* more tpak code

* more
2025-02-23 13:10:50 +01:00
Logan McNaughton 4692e2bcb9 more tpak code (#275)
* more tpak code

* more
2025-02-23 11:59:15 +01:00
Logan McNaughton 04390301c9 more tpak code (#274) 2025-02-22 21:41:21 +01:00
Logan McNaughton bc415821d6 check for new version (#273)
* check for new version

* more
2025-02-21 23:02:16 +01:00
Logan McNaughton a068363787 Add note about command line flatpak usage 2025-02-20 20:12:25 +01:00
Logan McNaughton 97839fae40 Bump to 1.0.9 (#272) 2025-02-20 18:44:15 +01:00
Logan McNaughton b6c805a27d update cargo packages 2025-02-20 18:21:15 +01:00
Logan McNaughton b7509a2425 use rust 2024 edition (#268) 2025-02-20 18:20:55 +01:00
Logan McNaughton 6ecedde329 add note about building on linux 2025-02-20 16:12:31 +01:00
Logan McNaughton 09dfd69283 use better data struct for netplay servers (#269)
* use better data struct for netplay servers

* more
2025-02-20 12:46:44 +01:00
Logan McNaughton d262567084 use struct in netplay code (#265)
* use struct in netplay code

* more
2025-02-19 20:57:03 +01:00
Logan McNaughton c363e78fbb add slider for resolution (#264)
* add slider for resolution

* more
2025-02-19 14:48:36 +01:00
Logan McNaughton 792a3a6476 reduce binary size (#263) 2025-02-19 11:26:55 +01:00
Logan McNaughton 5bdb54a911 Update README.md 2025-02-19 10:52:26 +01:00
Logan McNaughton b01c2d1885 use key down for input 2025-02-18 14:25:24 +01:00
Logan McNaughton bb2da243b3 fix hotkeys 2025-02-18 13:38:38 +01:00
Logan McNaughton 938a99caa9 clean up data struct (#262)
* clean up data struct

* add check

* more

* more

* more
2025-02-18 10:05:48 +01:00
dependabot[bot] f7bfcfccba Bump clap from 4.5.29 to 4.5.30 (#261)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.29 to 4.5.30.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.29...clap_complete-v4.5.30)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-18 05:42:01 +01:00
Logan McNaughton 656e390171 track RDP RDRAM writes (#259)
* track RDP RDRAM writes

* more

* more

* more

* more

* more
2025-02-17 22:13:45 +01:00
Logan McNaughton 17990dfebc toggle speed limiter (#260) 2025-02-17 13:40:25 +01:00
Logan McNaughton 671a961789 Use struct instead of tuple (#258)
* Use struct instead of tuple

* more
2025-02-17 09:19:25 +01:00
Logan McNaughton aac68a4a1b rename artifacts (#257) 2025-02-16 11:35:28 +01:00
Logan McNaughton dc5a78a75e use audio for netplay messages (#256)
* use audio for netplay messages

* more
2025-02-16 11:04:59 +01:00
Logan McNaughton 91a5483452 bump to 1.0.8 (#255) 2025-02-15 23:11:38 +01:00
Logan McNaughton baee7ffb82 update cargo packages 2025-02-15 23:10:04 +01:00
Logan McNaughton c2780c78b3 Transferpak stub (#254)
* transferpak support

* more

* more
2025-02-15 18:32:36 +01:00
Logan McNaughton ba006ddd53 add transferpak wav 2025-02-15 15:54:25 +01:00
Logan McNaughton e04a151d49 set DPC_PIPEBUSY_REG (#253)
* set DPC_PIPEBUSY_REG

* revert hidden rdram change

* more
2025-02-15 08:29:37 +01:00
Logan McNaughton 8566eee906 fix possible crash in is viewer 2025-02-14 22:20:15 +01:00
Logan McNaughton a1c2ac21be fix hidden rdram size 2025-02-14 21:32:11 +01:00
Logan McNaughton b7d194e12d alt_return to toggle fullscreen (#252)
* alt_return to toggle fullscreen

* more
2025-02-14 12:29:49 +01:00
Logan McNaughton df9ad377d3 add cycles for rsp mfc0 (#251)
* add cycles for rsp mfc0

* more
2025-02-14 10:07:58 +01:00
Logan McNaughton 8f20abe813 update_screen after speed limiter (#250) 2025-02-13 22:09:17 +01:00
Logan McNaughton 3aca3c1664 bump to 1.0.7 (#249) 2025-02-13 21:28:47 +01:00
Logan McNaughton 500b4bb505 only try to assign enabled controllers (#248) 2025-02-13 21:05:22 +01:00
Logan McNaughton b01bbaa82f update cargo packages 2025-02-13 21:04:15 +01:00
Logan McNaughton 2010b82c8e remove dead code 2025-02-13 11:57:16 +01:00
Logan McNaughton 74c65750a3 add some sdl window flags (#247) 2025-02-13 11:40:06 +01:00
Logan McNaughton 2d09425ca1 fix rsp reserved commands (#246) 2025-02-12 22:04:30 +01:00
Logan McNaughton d0965c03e1 dynamically calculate speed limit frequency (#245)
* dynamically calculate speed limit frequency

* dynamically calculate speed limit frequency

* dynamically calculate speed limit frequency

* more
2025-02-12 21:18:54 +01:00
Logan McNaughton 6ee55ce4ca add a way to show internal FPS (#244) 2025-02-12 17:53:17 +01:00
Logan McNaughton 63b2ed69e4 don't modify cart.rom (#243)
* don't modify cart.rom

* don't modify cart.rom

* don't modify cart.rom

* more

* more
2025-02-12 16:09:42 +01:00
Logan McNaughton 0faf33fcfa PAL aspect ratio adjustment (#242) 2025-02-12 10:14:06 +01:00
Logan McNaughton b67268ff62 fix PAL aspect ratio (#241) 2025-02-12 09:19:39 +01:00
dependabot[bot] bda50f8d9d Bump clap from 4.5.28 to 4.5.29 (#240)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.28 to 4.5.29.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.28...clap_complete-v4.5.29)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 06:13:45 +01:00
Logan McNaughton ea012b4587 bump to 1.0.6 (#239) 2025-02-11 15:48:46 +01:00
63 changed files with 4206 additions and 1325 deletions
+22 -8
View File
@@ -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 }}
Generated
+374 -280
View File
File diff suppressed because it is too large Load Diff
+9 -5
View File
@@ -1,14 +1,14 @@
[package]
name = "gopher64"
version = "1.0.5"
edition = "2021"
rust-version = "1.82"
version = "1.0.12"
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"
zip = "2.2"
governor = "0.8"
sevenz-rust = "0.6"
chrono = "0.4"
@@ -27,9 +27,11 @@ 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.4", features = ["derive"] }
clap = { version = "4.5", features = ["derive"] }
reqwest = { version = "0.12", default-features = false, features = ["http2","rustls-tls","charset","json"] }
[build-dependencies]
@@ -39,3 +41,5 @@ cc = { version = "1.2", features = ["parallel"] }
[profile.release]
codegen-units = 1
strip = true
panic = "abort"
+20 -37
View File
@@ -1,41 +1,19 @@
# 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`
@@ -49,6 +27,14 @@ Gopher64 supports netplay (online play with others). It has a few public netplay
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.
## 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.
@@ -57,11 +43,12 @@ If you would like to keep all the game data in the same folder as executable, yo
## 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
@@ -70,7 +57,3 @@ I am very open to contributions! Please reach out to me via a GitHub issue, or v
## 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
+2
View File
@@ -101,6 +101,8 @@ fn main() {
.allowlist_function("rdp_process_commands")
.allowlist_function("rdp_check_callback")
.allowlist_function("rdp_new_processor")
.allowlist_function("rdp_check_framebuffers")
.allowlist_function("rdp_full_sync")
.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.
+135
View File
@@ -0,0 +1,135 @@
#version 450
/*
CRT Shader by EasyMode
License: GPL
*/
layout(push_constant) uniform Push
{
vec4 SourceSize;
vec4 OutputSize;
} params;
#define SHARPNESS_IMAGE 1.0
#define SHARPNESS_EDGES 3.0
#define GLOW_WIDTH 0.5
#define GLOW_HEIGHT 0.5
#define GLOW_HALATION 0.1
#define GLOW_DIFFUSION 0.05
#define MASK_COLORS 2.0
#define MASK_STRENGTH 0.3
#define MASK_SIZE 1.0
#define SCANLINE_SIZE_MIN 0.5
#define SCANLINE_SIZE_MAX 1.5
#define SCANLINE_SHAPE 2.5
#define SCANLINE_OFFSET 1.0
#define GAMMA_INPUT 2.4
#define GAMMA_OUTPUT 2.4
#define BRIGHTNESS 1.5
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
} global;
#define FIX(c) max(abs(c), 1e-5)
#define PI 3.141592653589
#define TEX2D(c) pow(texture(tex, c).rgb, vec3(GAMMA_INPUT))
#define saturate(c) clamp(c, 0.0, 1.0)
mat3x3 get_color_matrix(sampler2D tex, vec2 co, vec2 dx)
{
return mat3x3(TEX2D(co - dx), TEX2D(co), TEX2D(co + dx));
}
vec3 blur(mat3 m, float dist, float rad)
{
vec3 x = vec3(dist - 1.0, dist, dist + 1.0) / rad;
vec3 w = exp2(x * x * -1.0);
return (m[0] * w.x + m[1] * w.y + m[2] * w.z) / (w.x + w.y + w.z);
}
vec3 filter_gaussian(sampler2D tex, vec2 co, vec2 tex_size)
{
vec2 dx = vec2(1.0 / tex_size.x, 0.0);
vec2 dy = vec2(0.0, 1.0 / tex_size.y);
vec2 pix_co = co * tex_size;
vec2 tex_co = (floor(pix_co) + 0.5) / tex_size;
vec2 dist = (fract(pix_co) - 0.5) * -1.0;
mat3x3 line0 = get_color_matrix(tex, tex_co - dy, dx);
mat3x3 line1 = get_color_matrix(tex, tex_co, dx);
mat3x3 line2 = get_color_matrix(tex, tex_co + dy, dx);
mat3x3 column = mat3x3(blur(line0, dist.x, GLOW_WIDTH),
blur(line1, dist.x, GLOW_WIDTH),
blur(line2, dist.x, GLOW_WIDTH));
return blur(column, dist.y, GLOW_HEIGHT);
}
vec3 filter_lanczos(sampler2D tex, vec2 co, vec2 tex_size, float sharp)
{
tex_size.x *= sharp;
vec2 dx = vec2(1.0 / tex_size.x, 0.0);
vec2 pix_co = co * tex_size - vec2(0.5, 0.0);
vec2 tex_co = (floor(pix_co) + vec2(0.5, 0.001)) / tex_size;
vec2 dist = fract(pix_co);
vec4 coef = PI * vec4(dist.x + 1.0, dist.x, dist.x - 1.0, dist.x - 2.0);
coef = FIX(coef);
coef = 2.0 * sin(coef) * sin(coef / 2.0) / (coef * coef);
coef /= dot(coef, vec4(1.0));
vec4 col1 = vec4(TEX2D(tex_co), 1.0);
vec4 col2 = vec4(TEX2D(tex_co + dx), 1.0);
return (mat4x4(col1, col1, col2, col2) * coef).rgb;
}
vec3 get_scanline_weight(float x, vec3 col)
{
vec3 beam = mix(vec3(SCANLINE_SIZE_MIN), vec3(SCANLINE_SIZE_MAX), pow(col, vec3(1.0 / SCANLINE_SHAPE)));
vec3 x_mul = 2.0 / beam;
vec3 x_offset = x_mul * 0.5;
return smoothstep(0.0, 1.0, 1.0 - abs(x * x_mul - x_offset)) * x_offset;
}
vec3 get_mask_weight(float x)
{
float i = mod(floor(x * params.OutputSize.x * params.SourceSize.x / (params.SourceSize.x * MASK_SIZE)), MASK_COLORS);
if (i == 0.0) return mix(vec3(1.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), MASK_COLORS - 2.0);
else if (i == 1.0) return vec3(0.0, 1.0, 0.0);
else return vec3(0.0, 0.0, 1.0);
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 0) uniform sampler2D Source;
void main()
{
float scale = floor(params.OutputSize.y * params.SourceSize.w);
float offset = 1.0 / scale * 0.5;
if (bool(mod(scale, 2.0))) offset = 0.0;
vec2 co = (vTexCoord * params.SourceSize.xy - vec2(0.0, offset * SCANLINE_OFFSET)) * params.SourceSize.zw;
vec3 col_glow = filter_gaussian(Source, co, params.SourceSize.xy);
vec3 col_soft = filter_lanczos(Source, co, params.SourceSize.xy, SHARPNESS_IMAGE);
vec3 col_sharp = filter_lanczos(Source, co, params.SourceSize.xy, SHARPNESS_EDGES);
vec3 col = sqrt(col_sharp * col_soft);
col *= get_scanline_weight(fract(co.y * params.SourceSize.y), col_soft);
col_glow = saturate(col_glow - col);
col += col_glow * col_glow * GLOW_HALATION;
col = mix(col, col * get_mask_weight(vTexCoord.x) * MASK_COLORS, MASK_STRENGTH);
col += col_glow * GLOW_DIFFUSION;
col = pow(col * BRIGHTNESS, vec3(1.0 / GAMMA_OUTPUT));
FragColor = vec4(col, 1.0);
}
+453
View File
@@ -0,0 +1,453 @@
{0x07230203,0x00010300,0x000d000b,0x000004fd,
0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,
0x00000000,0x0003000e,0x00000000,0x00000001,
0x0007000f,0x00000004,0x00000004,0x6e69616d,
0x00000000,0x000001ba,0x0000021a,0x00030010,
0x00000004,0x00000007,0x00030047,0x00000185,
0x00000002,0x00050048,0x00000185,0x00000000,
0x00000023,0x00000000,0x00050048,0x00000185,
0x00000001,0x00000023,0x00000010,0x00040047,
0x000001ba,0x0000001e,0x00000000,0x00040047,
0x000001ca,0x00000021,0x00000000,0x00040047,
0x000001ca,0x00000022,0x00000000,0x00040047,
0x0000021a,0x0000001e,0x00000000,0x00020013,
0x00000002,0x00030021,0x00000003,0x00000002,
0x00030016,0x00000006,0x00000020,0x00090019,
0x00000007,0x00000006,0x00000001,0x00000000,
0x00000000,0x00000000,0x00000001,0x00000000,
0x0003001b,0x00000008,0x00000007,0x00040020,
0x00000009,0x00000000,0x00000008,0x00040017,
0x0000000a,0x00000006,0x00000002,0x00040017,
0x0000000c,0x00000006,0x00000003,0x00040017,
0x00000037,0x00000006,0x00000004,0x0004002b,
0x00000006,0x0000003a,0x4019999a,0x0006002c,
0x0000000c,0x0000003b,0x0000003a,0x0000003a,
0x0000003a,0x0004002b,0x00000006,0x00000049,
0x3f800000,0x0004002b,0x00000006,0x0000004a,
0x00000000,0x0004002b,0x00000006,0x00000068,
0xbf800000,0x00040015,0x0000006b,0x00000020,
0x00000001,0x0004002b,0x0000006b,0x0000006c,
0x00000000,0x00040015,0x0000006f,0x00000020,
0x00000000,0x0004002b,0x0000006f,0x00000070,
0x00000000,0x0004002b,0x0000006b,0x00000074,
0x00000001,0x0004002b,0x0000006f,0x00000077,
0x00000001,0x0004002b,0x00000006,0x000000a1,
0x3f000000,0x0005002c,0x0000000a,0x000000fc,
0x000000a1,0x0000004a,0x0004002b,0x00000006,
0x00000101,0x3a83126f,0x0005002c,0x0000000a,
0x00000102,0x000000a1,0x00000101,0x0004002b,
0x00000006,0x0000010b,0x40490fdb,0x0004002b,
0x00000006,0x00000116,0x40000000,0x0004002b,
0x00000006,0x0000011c,0x3727c5ac,0x0007002c,
0x00000037,0x0000012c,0x00000049,0x00000049,
0x00000049,0x00000049,0x00040018,0x00000149,
0x00000037,0x00000004,0x0006002c,0x0000000c,
0x00000165,0x000000a1,0x000000a1,0x000000a1,
0x0004002b,0x00000006,0x00000166,0x3fc00000,
0x0006002c,0x0000000c,0x00000167,0x00000166,
0x00000166,0x00000166,0x0004002b,0x00000006,
0x00000169,0x3ecccccd,0x0006002c,0x0000000c,
0x0000016a,0x00000169,0x00000169,0x00000169,
0x0004001e,0x00000185,0x00000037,0x00000037,
0x00040020,0x00000186,0x00000009,0x00000185,
0x0004003b,0x00000186,0x00000187,0x00000009,
0x00040020,0x00000188,0x00000009,0x00000006,
0x00020014,0x00000196,0x0006002c,0x0000000c,
0x0000019a,0x00000049,0x0000004a,0x00000049,
0x0006002c,0x0000000c,0x000001a1,0x0000004a,
0x00000049,0x0000004a,0x0006002c,0x0000000c,
0x000001a4,0x0000004a,0x0000004a,0x00000049,
0x0004002b,0x0000006f,0x000001aa,0x00000003,
0x00040020,0x000001b9,0x00000001,0x0000000a,
0x0004003b,0x000001b9,0x000001ba,0x00000001,
0x00040020,0x000001bc,0x00000009,0x00000037,
0x0004003b,0x00000009,0x000001ca,0x00000000,
0x0004002b,0x00000006,0x000001dc,0x40400000,
0x0004002b,0x00000006,0x000001ff,0x3dcccccd,
0x00040020,0x00000206,0x00000001,0x00000006,
0x0004002b,0x00000006,0x0000020c,0x3e99999a,
0x0004002b,0x00000006,0x00000210,0x3d4ccccd,
0x0004002b,0x00000006,0x00000216,0x3ed55555,
0x0006002c,0x0000000c,0x00000217,0x00000216,
0x00000216,0x00000216,0x00040020,0x00000219,
0x00000003,0x00000037,0x0004003b,0x00000219,
0x0000021a,0x00000003,0x0005002c,0x0000000a,
0x000004f3,0x000000a1,0x000000a1,0x0007002c,
0x00000037,0x000004f4,0x0000011c,0x0000011c,
0x0000011c,0x0000011c,0x0006002c,0x0000000c,
0x000004f6,0x00000116,0x00000116,0x00000116,
0x0006002c,0x0000000c,0x000004f7,0x00000049,
0x00000049,0x00000049,0x0006002c,0x0000000c,
0x000004f8,0x0000004a,0x0000004a,0x0000004a,
0x0006002c,0x0000000c,0x000004f9,0x0000020c,
0x0000020c,0x0000020c,0x0007002c,0x00000037,
0x000004fa,0x000000a1,0x000000a1,0x000000a1,
0x000000a1,0x0004002b,0x00000006,0x000004fb,
0x3eaaaaab,0x00050036,0x00000002,0x00000004,
0x00000000,0x00000003,0x000200f8,0x00000005,
0x00060041,0x00000188,0x000001a8,0x00000187,
0x00000074,0x00000077,0x0004003d,0x00000006,
0x000001a9,0x000001a8,0x00060041,0x00000188,
0x000001ab,0x00000187,0x0000006c,0x000001aa,
0x0004003d,0x00000006,0x000001ac,0x000001ab,
0x00050085,0x00000006,0x000001ad,0x000001a9,
0x000001ac,0x0006000c,0x00000006,0x000001ae,
0x00000001,0x00000008,0x000001ad,0x00050088,
0x00000006,0x000001b2,0x000000a1,0x000001ae,
0x0005008d,0x00000006,0x000001b4,0x000001ae,
0x00000116,0x000500b7,0x00000196,0x000001b5,
0x000001b4,0x0000004a,0x000600a9,0x00000006,
0x000004fc,0x000001b5,0x0000004a,0x000001b2,
0x0004003d,0x0000000a,0x000001bb,0x000001ba,
0x00050041,0x000001bc,0x000001bd,0x00000187,
0x0000006c,0x0004003d,0x00000037,0x000001be,
0x000001bd,0x0007004f,0x0000000a,0x000001bf,
0x000001be,0x000001be,0x00000000,0x00000001,
0x00050085,0x0000000a,0x000001c0,0x000001bb,
0x000001bf,0x00050050,0x0000000a,0x000001c3,
0x0000004a,0x000004fc,0x00050083,0x0000000a,
0x000001c4,0x000001c0,0x000001c3,0x0007004f,
0x0000000a,0x000001c7,0x000001be,0x000001be,
0x00000002,0x00000003,0x00050085,0x0000000a,
0x000001c8,0x000001c4,0x000001c7,0x00050051,
0x00000006,0x00000249,0x000001be,0x00000000,
0x00050088,0x00000006,0x0000024a,0x00000049,
0x00000249,0x00050050,0x0000000a,0x0000024b,
0x0000024a,0x0000004a,0x00050051,0x00000006,
0x0000024d,0x000001be,0x00000001,0x00050088,
0x00000006,0x0000024e,0x00000049,0x0000024d,
0x00050050,0x0000000a,0x0000024f,0x0000004a,
0x0000024e,0x00050085,0x0000000a,0x00000252,
0x000001c8,0x000001bf,0x0006000c,0x0000000a,
0x00000254,0x00000001,0x00000008,0x00000252,
0x00050081,0x0000000a,0x00000256,0x00000254,
0x000004f3,0x00050088,0x0000000a,0x00000258,
0x00000256,0x000001bf,0x0006000c,0x0000000a,
0x0000025a,0x00000001,0x0000000a,0x00000252,
0x00050083,0x0000000a,0x0000025c,0x0000025a,
0x000004f3,0x0005008e,0x0000000a,0x0000025d,
0x0000025c,0x00000068,0x00050083,0x0000000a,
0x00000260,0x00000258,0x0000024f,0x0004003d,
0x00000008,0x0000028a,0x000001ca,0x00050083,
0x0000000a,0x0000028d,0x00000260,0x0000024b,
0x00050057,0x00000037,0x0000028e,0x0000028a,
0x0000028d,0x0008004f,0x0000000c,0x0000028f,
0x0000028e,0x0000028e,0x00000000,0x00000001,
0x00000002,0x0007000c,0x0000000c,0x00000290,
0x00000001,0x0000001a,0x0000028f,0x0000003b,
0x00050057,0x00000037,0x00000293,0x0000028a,
0x00000260,0x0008004f,0x0000000c,0x00000294,
0x00000293,0x00000293,0x00000000,0x00000001,
0x00000002,0x0007000c,0x0000000c,0x00000295,
0x00000001,0x0000001a,0x00000294,0x0000003b,
0x00050081,0x0000000a,0x00000299,0x00000260,
0x0000024b,0x00050057,0x00000037,0x0000029a,
0x0000028a,0x00000299,0x0008004f,0x0000000c,
0x0000029b,0x0000029a,0x0000029a,0x00000000,
0x00000001,0x00000002,0x0007000c,0x0000000c,
0x0000029c,0x00000001,0x0000001a,0x0000029b,
0x0000003b,0x00050083,0x0000000a,0x000002af,
0x00000258,0x0000024b,0x00050057,0x00000037,
0x000002b0,0x0000028a,0x000002af,0x0008004f,
0x0000000c,0x000002b1,0x000002b0,0x000002b0,
0x00000000,0x00000001,0x00000002,0x0007000c,
0x0000000c,0x000002b2,0x00000001,0x0000001a,
0x000002b1,0x0000003b,0x00050057,0x00000037,
0x000002b5,0x0000028a,0x00000258,0x0008004f,
0x0000000c,0x000002b6,0x000002b5,0x000002b5,
0x00000000,0x00000001,0x00000002,0x0007000c,
0x0000000c,0x000002b7,0x00000001,0x0000001a,
0x000002b6,0x0000003b,0x00050081,0x0000000a,
0x000002bb,0x00000258,0x0000024b,0x00050057,
0x00000037,0x000002bc,0x0000028a,0x000002bb,
0x0008004f,0x0000000c,0x000002bd,0x000002bc,
0x000002bc,0x00000000,0x00000001,0x00000002,
0x0007000c,0x0000000c,0x000002be,0x00000001,
0x0000001a,0x000002bd,0x0000003b,0x00050081,
0x0000000a,0x00000268,0x00000258,0x0000024f,
0x00050083,0x0000000a,0x000002d1,0x00000268,
0x0000024b,0x00050057,0x00000037,0x000002d2,
0x0000028a,0x000002d1,0x0008004f,0x0000000c,
0x000002d3,0x000002d2,0x000002d2,0x00000000,
0x00000001,0x00000002,0x0007000c,0x0000000c,
0x000002d4,0x00000001,0x0000001a,0x000002d3,
0x0000003b,0x00050057,0x00000037,0x000002d7,
0x0000028a,0x00000268,0x0008004f,0x0000000c,
0x000002d8,0x000002d7,0x000002d7,0x00000000,
0x00000001,0x00000002,0x0007000c,0x0000000c,
0x000002d9,0x00000001,0x0000001a,0x000002d8,
0x0000003b,0x00050081,0x0000000a,0x000002dd,
0x00000268,0x0000024b,0x00050057,0x00000037,
0x000002de,0x0000028a,0x000002dd,0x0008004f,
0x0000000c,0x000002df,0x000002de,0x000002de,
0x00000000,0x00000001,0x00000002,0x0007000c,
0x0000000c,0x000002e0,0x00000001,0x0000001a,
0x000002df,0x0000003b,0x00050051,0x00000006,
0x0000026d,0x0000025d,0x00000000,0x00050083,
0x00000006,0x000002f3,0x0000026d,0x00000049,
0x00050081,0x00000006,0x000002f6,0x0000026d,
0x00000049,0x00060050,0x0000000c,0x000002f7,
0x000002f3,0x0000026d,0x000002f6,0x00050085,
0x0000000c,0x000002fa,0x000002f7,0x000004f6,
0x00050085,0x0000000c,0x000002fd,0x000002fa,
0x000002fa,0x0005008e,0x0000000c,0x000002fe,
0x000002fd,0x00000068,0x0006000c,0x0000000c,
0x000002ff,0x00000001,0x0000001d,0x000002fe,
0x00050051,0x00000006,0x00000303,0x000002ff,
0x00000000,0x0005008e,0x0000000c,0x00000304,
0x00000290,0x00000303,0x00050051,0x00000006,
0x00000308,0x000002ff,0x00000001,0x0005008e,
0x0000000c,0x00000309,0x00000295,0x00000308,
0x00050081,0x0000000c,0x0000030a,0x00000304,
0x00000309,0x00050051,0x00000006,0x0000030e,
0x000002ff,0x00000002,0x0005008e,0x0000000c,
0x0000030f,0x0000029c,0x0000030e,0x00050081,
0x0000000c,0x00000310,0x0000030a,0x0000030f,
0x00050081,0x00000006,0x00000315,0x00000303,
0x00000308,0x00050081,0x00000006,0x00000318,
0x00000315,0x0000030e,0x00060050,0x0000000c,
0x00000319,0x00000318,0x00000318,0x00000318,
0x00050088,0x0000000c,0x0000031a,0x00000310,
0x00000319,0x0005008e,0x0000000c,0x00000331,
0x000002b2,0x00000303,0x0005008e,0x0000000c,
0x00000336,0x000002b7,0x00000308,0x00050081,
0x0000000c,0x00000337,0x00000331,0x00000336,
0x0005008e,0x0000000c,0x0000033c,0x000002be,
0x0000030e,0x00050081,0x0000000c,0x0000033d,
0x00000337,0x0000033c,0x00050088,0x0000000c,
0x00000347,0x0000033d,0x00000319,0x0005008e,
0x0000000c,0x0000035e,0x000002d4,0x00000303,
0x0005008e,0x0000000c,0x00000363,0x000002d9,
0x00000308,0x00050081,0x0000000c,0x00000364,
0x0000035e,0x00000363,0x0005008e,0x0000000c,
0x00000369,0x000002e0,0x0000030e,0x00050081,
0x0000000c,0x0000036a,0x00000364,0x00000369,
0x00050088,0x0000000c,0x00000374,0x0000036a,
0x00000319,0x00050051,0x00000006,0x00000286,
0x0000025d,0x00000001,0x00050083,0x00000006,
0x0000037a,0x00000286,0x00000049,0x00050081,
0x00000006,0x0000037d,0x00000286,0x00000049,
0x00060050,0x0000000c,0x0000037e,0x0000037a,
0x00000286,0x0000037d,0x00050085,0x0000000c,
0x00000381,0x0000037e,0x000004f6,0x00050085,
0x0000000c,0x00000384,0x00000381,0x00000381,
0x0005008e,0x0000000c,0x00000385,0x00000384,
0x00000068,0x0006000c,0x0000000c,0x00000386,
0x00000001,0x0000001d,0x00000385,0x00050051,
0x00000006,0x0000038a,0x00000386,0x00000000,
0x0005008e,0x0000000c,0x0000038b,0x0000031a,
0x0000038a,0x00050051,0x00000006,0x0000038f,
0x00000386,0x00000001,0x0005008e,0x0000000c,
0x00000390,0x00000347,0x0000038f,0x00050081,
0x0000000c,0x00000391,0x0000038b,0x00000390,
0x00050051,0x00000006,0x00000395,0x00000386,
0x00000002,0x0005008e,0x0000000c,0x00000396,
0x00000374,0x00000395,0x00050081,0x0000000c,
0x00000397,0x00000391,0x00000396,0x00050081,
0x00000006,0x0000039c,0x0000038a,0x0000038f,
0x00050081,0x00000006,0x0000039f,0x0000039c,
0x00000395,0x00060050,0x0000000c,0x000003a0,
0x0000039f,0x0000039f,0x0000039f,0x00050088,
0x0000000c,0x000003a1,0x00000397,0x000003a0,
0x00060052,0x0000000a,0x000004e1,0x00000249,
0x000001bf,0x00000000,0x00050085,0x0000000a,
0x000003b6,0x000001c8,0x000004e1,0x00050083,
0x0000000a,0x000003b7,0x000003b6,0x000000fc,
0x0006000c,0x0000000a,0x000003b9,0x00000001,
0x00000008,0x000003b7,0x00050081,0x0000000a,
0x000003ba,0x000003b9,0x00000102,0x00050088,
0x0000000a,0x000003bc,0x000003ba,0x000004e1,
0x0006000c,0x0000000a,0x000003be,0x00000001,
0x0000000a,0x000003b7,0x00050051,0x00000006,
0x000003c0,0x000003be,0x00000000,0x00050081,
0x00000006,0x000003c1,0x000003c0,0x00000049,
0x00050083,0x00000006,0x000003c6,0x000003c0,
0x00000049,0x00050083,0x00000006,0x000003c9,
0x000003c0,0x00000116,0x00070050,0x00000037,
0x000003ca,0x000003c1,0x000003c0,0x000003c6,
0x000003c9,0x0005008e,0x00000037,0x000003cb,
0x000003ca,0x0000010b,0x0006000c,0x00000037,
0x000003cd,0x00000001,0x00000004,0x000003cb,
0x0007000c,0x00000037,0x000003cf,0x00000001,
0x00000028,0x000003cd,0x000004f4,0x0006000c,
0x00000037,0x000003d1,0x00000001,0x0000000d,
0x000003cf,0x0005008e,0x00000037,0x000003d2,
0x000003d1,0x00000116,0x00050085,0x00000037,
0x000003d5,0x000003cf,0x000004fa,0x0006000c,
0x00000037,0x000003d6,0x00000001,0x0000000d,
0x000003d5,0x00050085,0x00000037,0x000003d7,
0x000003d2,0x000003d6,0x00050085,0x00000037,
0x000003da,0x000003cf,0x000003cf,0x00050088,
0x00000037,0x000003db,0x000003d7,0x000003da,
0x00050094,0x00000006,0x000003dd,0x000003db,
0x0000012c,0x00070050,0x00000037,0x000003df,
0x000003dd,0x000003dd,0x000003dd,0x000003dd,
0x00050088,0x00000037,0x000003e0,0x000003db,
0x000003df,0x00050057,0x00000037,0x000003e3,
0x0000028a,0x000003bc,0x0008004f,0x0000000c,
0x000003e4,0x000003e3,0x000003e3,0x00000000,
0x00000001,0x00000002,0x0007000c,0x0000000c,
0x000003e5,0x00000001,0x0000001a,0x000003e4,
0x0000003b,0x00050051,0x00000006,0x000003e6,
0x000003e5,0x00000000,0x00050051,0x00000006,
0x000003e7,0x000003e5,0x00000001,0x00050051,
0x00000006,0x000003e8,0x000003e5,0x00000002,
0x00050081,0x0000000a,0x000003ed,0x000003bc,
0x0000024b,0x00050057,0x00000037,0x000003ee,
0x0000028a,0x000003ed,0x0008004f,0x0000000c,
0x000003ef,0x000003ee,0x000003ee,0x00000000,
0x00000001,0x00000002,0x0007000c,0x0000000c,
0x000003f0,0x00000001,0x0000001a,0x000003ef,
0x0000003b,0x00050051,0x00000006,0x000003f1,
0x000003f0,0x00000000,0x00050051,0x00000006,
0x000003f2,0x000003f0,0x00000001,0x00050051,
0x00000006,0x000003f3,0x000003f0,0x00000002,
0x00070050,0x00000037,0x00000407,0x000003e6,
0x000003e7,0x000003e8,0x00000049,0x00070050,
0x00000037,0x00000409,0x000003f1,0x000003f2,
0x000003f3,0x00000049,0x00070050,0x00000149,
0x0000040b,0x00000407,0x00000407,0x00000409,
0x00000409,0x00050091,0x00000037,0x0000040d,
0x0000040b,0x000003e0,0x0008004f,0x0000000c,
0x0000040e,0x0000040d,0x0000040d,0x00000000,
0x00000001,0x00000002,0x00050085,0x00000006,
0x0000041b,0x00000249,0x000001dc,0x00060052,
0x0000000a,0x000004e9,0x0000041b,0x000001bf,
0x00000000,0x00050088,0x00000006,0x0000041f,
0x000004fb,0x00000249,0x00050050,0x0000000a,
0x00000420,0x0000041f,0x0000004a,0x00050085,
0x0000000a,0x00000423,0x000001c8,0x000004e9,
0x00050083,0x0000000a,0x00000424,0x00000423,
0x000000fc,0x0006000c,0x0000000a,0x00000426,
0x00000001,0x00000008,0x00000424,0x00050081,
0x0000000a,0x00000427,0x00000426,0x00000102,
0x00050088,0x0000000a,0x00000429,0x00000427,
0x000004e9,0x0006000c,0x0000000a,0x0000042b,
0x00000001,0x0000000a,0x00000424,0x00050051,
0x00000006,0x0000042d,0x0000042b,0x00000000,
0x00050081,0x00000006,0x0000042e,0x0000042d,
0x00000049,0x00050083,0x00000006,0x00000433,
0x0000042d,0x00000049,0x00050083,0x00000006,
0x00000436,0x0000042d,0x00000116,0x00070050,
0x00000037,0x00000437,0x0000042e,0x0000042d,
0x00000433,0x00000436,0x0005008e,0x00000037,
0x00000438,0x00000437,0x0000010b,0x0006000c,
0x00000037,0x0000043a,0x00000001,0x00000004,
0x00000438,0x0007000c,0x00000037,0x0000043c,
0x00000001,0x00000028,0x0000043a,0x000004f4,
0x0006000c,0x00000037,0x0000043e,0x00000001,
0x0000000d,0x0000043c,0x0005008e,0x00000037,
0x0000043f,0x0000043e,0x00000116,0x00050085,
0x00000037,0x00000442,0x0000043c,0x000004fa,
0x0006000c,0x00000037,0x00000443,0x00000001,
0x0000000d,0x00000442,0x00050085,0x00000037,
0x00000444,0x0000043f,0x00000443,0x00050085,
0x00000037,0x00000447,0x0000043c,0x0000043c,
0x00050088,0x00000037,0x00000448,0x00000444,
0x00000447,0x00050094,0x00000006,0x0000044a,
0x00000448,0x0000012c,0x00070050,0x00000037,
0x0000044c,0x0000044a,0x0000044a,0x0000044a,
0x0000044a,0x00050088,0x00000037,0x0000044d,
0x00000448,0x0000044c,0x00050057,0x00000037,
0x00000450,0x0000028a,0x00000429,0x0008004f,
0x0000000c,0x00000451,0x00000450,0x00000450,
0x00000000,0x00000001,0x00000002,0x0007000c,
0x0000000c,0x00000452,0x00000001,0x0000001a,
0x00000451,0x0000003b,0x00050051,0x00000006,
0x00000453,0x00000452,0x00000000,0x00050051,
0x00000006,0x00000454,0x00000452,0x00000001,
0x00050051,0x00000006,0x00000455,0x00000452,
0x00000002,0x00050081,0x0000000a,0x0000045a,
0x00000429,0x00000420,0x00050057,0x00000037,
0x0000045b,0x0000028a,0x0000045a,0x0008004f,
0x0000000c,0x0000045c,0x0000045b,0x0000045b,
0x00000000,0x00000001,0x00000002,0x0007000c,
0x0000000c,0x0000045d,0x00000001,0x0000001a,
0x0000045c,0x0000003b,0x00050051,0x00000006,
0x0000045e,0x0000045d,0x00000000,0x00050051,
0x00000006,0x0000045f,0x0000045d,0x00000001,
0x00050051,0x00000006,0x00000460,0x0000045d,
0x00000002,0x00070050,0x00000037,0x00000474,
0x00000453,0x00000454,0x00000455,0x00000049,
0x00070050,0x00000037,0x00000476,0x0000045e,
0x0000045f,0x00000460,0x00000049,0x00070050,
0x00000149,0x00000478,0x00000474,0x00000474,
0x00000476,0x00000476,0x00050091,0x00000037,
0x0000047a,0x00000478,0x0000044d,0x0008004f,
0x0000000c,0x0000047b,0x0000047a,0x0000047a,
0x00000000,0x00000001,0x00000002,0x00050085,
0x0000000c,0x000001e8,0x0000047b,0x0000040e,
0x0006000c,0x0000000c,0x000001e9,0x00000001,
0x0000001f,0x000001e8,0x00050051,0x00000006,
0x000001eb,0x000001c8,0x00000001,0x00060041,
0x00000188,0x000001ec,0x00000187,0x0000006c,
0x00000077,0x0004003d,0x00000006,0x000001ed,
0x000001ec,0x00050085,0x00000006,0x000001ee,
0x000001eb,0x000001ed,0x0006000c,0x00000006,
0x000001ef,0x00000001,0x0000000a,0x000001ee,
0x0007000c,0x0000000c,0x00000482,0x00000001,
0x0000001a,0x0000040e,0x0000016a,0x0008000c,
0x0000000c,0x00000483,0x00000001,0x0000002e,
0x00000165,0x00000167,0x00000482,0x00050088,
0x0000000c,0x00000486,0x000004f6,0x00000483,
0x0005008e,0x0000000c,0x00000488,0x00000486,
0x000000a1,0x0005008e,0x0000000c,0x0000048b,
0x00000486,0x000001ef,0x00050083,0x0000000c,
0x0000048d,0x0000048b,0x00000488,0x0006000c,
0x0000000c,0x0000048e,0x00000001,0x00000004,
0x0000048d,0x00050083,0x0000000c,0x00000490,
0x000004f7,0x0000048e,0x0008000c,0x0000000c,
0x00000493,0x00000001,0x00000031,0x000004f8,
0x000004f7,0x00000490,0x00050085,0x0000000c,
0x00000495,0x00000493,0x00000488,0x00050085,
0x0000000c,0x000001f5,0x000001e9,0x00000495,
0x00050083,0x0000000c,0x000001f8,0x000003a1,
0x000001f5,0x0008000c,0x0000000c,0x000001fb,
0x00000001,0x0000002b,0x000001f8,0x000004f8,
0x000004f7,0x00050085,0x0000000c,0x000001fe,
0x000001fb,0x000001fb,0x0005008e,0x0000000c,
0x00000200,0x000001fe,0x000001ff,0x00050081,
0x0000000c,0x00000202,0x000001f5,0x00000200,
0x00050041,0x00000206,0x00000207,0x000001ba,
0x00000070,0x0004003d,0x00000006,0x00000208,
0x00000207,0x000300f7,0x000004b3,0x00000000,
0x000300fb,0x00000070,0x0000049b,0x000200f8,
0x0000049b,0x00060041,0x00000188,0x0000049d,
0x00000187,0x00000074,0x00000070,0x0004003d,
0x00000006,0x0000049e,0x0000049d,0x00050085,
0x00000006,0x0000049f,0x00000208,0x0000049e,
0x0006000c,0x00000006,0x000004a7,0x00000001,
0x00000008,0x0000049f,0x0005008d,0x00000006,
0x000004a8,0x000004a7,0x00000116,0x000500b4,
0x00000196,0x000004aa,0x000004a8,0x0000004a,
0x000300f7,0x000004b2,0x00000000,0x000400fa,
0x000004aa,0x000004ab,0x000004ac,0x000200f8,
0x000004ac,0x000500b4,0x00000196,0x000004ae,
0x000004a8,0x00000049,0x000300f7,0x000004b1,
0x00000000,0x000400fa,0x000004ae,0x000004af,
0x000004b0,0x000200f8,0x000004b0,0x000200f9,
0x000004b3,0x000200f8,0x000004af,0x000200f9,
0x000004b3,0x000200f8,0x000004b1,0x000100ff,
0x000200f8,0x000004ab,0x000200f9,0x000004b3,
0x000200f8,0x000004b2,0x000100ff,0x000200f8,
0x000004b3,0x000900f5,0x0000000c,0x000004f1,
0x0000019a,0x000004ab,0x000001a1,0x000004af,
0x000001a4,0x000004b0,0x00050085,0x0000000c,
0x0000020a,0x00000202,0x000004f1,0x0005008e,
0x0000000c,0x0000020b,0x0000020a,0x00000116,
0x0008000c,0x0000000c,0x0000020e,0x00000001,
0x0000002e,0x00000202,0x0000020b,0x000004f9,
0x0005008e,0x0000000c,0x00000211,0x000001fb,
0x00000210,0x00050081,0x0000000c,0x00000213,
0x0000020e,0x00000211,0x0005008e,0x0000000c,
0x00000215,0x00000213,0x00000166,0x0007000c,
0x0000000c,0x00000218,0x00000001,0x0000001a,
0x00000215,0x00000217,0x00050051,0x00000006,
0x0000021c,0x00000218,0x00000000,0x00050051,
0x00000006,0x0000021d,0x00000218,0x00000001,
0x00050051,0x00000006,0x0000021e,0x00000218,
0x00000002,0x00070050,0x00000037,0x0000021f,
0x0000021c,0x0000021d,0x0000021e,0x00000049,
0x0003003e,0x0000021a,0x0000021f,0x000100fd,
0x00010038}
+10
View File
@@ -0,0 +1,10 @@
#version 450
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 0) uniform sampler2D uTexture;
layout(location = 0) in vec2 vUV;
void main()
{
FragColor = textureLod(uTexture, vUV, 0.0);
}
+21
View File
@@ -0,0 +1,21 @@
#version 450
layout(location = 0) out vec2 vUV;
void main()
{
if (gl_VertexIndex == 0)
{
gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);
}
else if (gl_VertexIndex == 1)
{
gl_Position = vec4(-1.0, +3.0, 0.0, 1.0);
}
else
{
gl_Position = vec4(+3.0, -1.0, 0.0, 1.0);
}
vUV = gl_Position.xy * 0.5 + 0.5;
}
+32
View File
@@ -0,0 +1,32 @@
{0x07230203,0x00010300,0x000d000b,0x00000015,
0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,
0x00000000,0x0003000e,0x00000000,0x00000001,
0x0007000f,0x00000004,0x00000004,0x6e69616d,
0x00000000,0x00000009,0x00000011,0x00030010,
0x00000004,0x00000007,0x00040047,0x00000009,
0x0000001e,0x00000000,0x00040047,0x0000000d,
0x00000021,0x00000000,0x00040047,0x0000000d,
0x00000022,0x00000000,0x00040047,0x00000011,
0x0000001e,0x00000000,0x00020013,0x00000002,
0x00030021,0x00000003,0x00000002,0x00030016,
0x00000006,0x00000020,0x00040017,0x00000007,
0x00000006,0x00000004,0x00040020,0x00000008,
0x00000003,0x00000007,0x0004003b,0x00000008,
0x00000009,0x00000003,0x00090019,0x0000000a,
0x00000006,0x00000001,0x00000000,0x00000000,
0x00000000,0x00000001,0x00000000,0x0003001b,
0x0000000b,0x0000000a,0x00040020,0x0000000c,
0x00000000,0x0000000b,0x0004003b,0x0000000c,
0x0000000d,0x00000000,0x00040017,0x0000000f,
0x00000006,0x00000002,0x00040020,0x00000010,
0x00000001,0x0000000f,0x0004003b,0x00000010,
0x00000011,0x00000001,0x0004002b,0x00000006,
0x00000013,0x00000000,0x00050036,0x00000002,
0x00000004,0x00000000,0x00000003,0x000200f8,
0x00000005,0x0004003d,0x0000000b,0x0000000e,
0x0000000d,0x0004003d,0x0000000f,0x00000012,
0x00000011,0x00070058,0x00000007,0x00000014,
0x0000000e,0x00000012,0x00000002,0x00000013,
0x0003003e,0x00000009,0x00000014,0x000100fd,
0x00010038}
+72
View File
@@ -0,0 +1,72 @@
{0x07230203,0x00010300,0x000d000b,0x00000034,
0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,
0x00000000,0x0003000e,0x00000000,0x00000001,
0x0008000f,0x00000000,0x00000004,0x6e69616d,
0x00000000,0x00000008,0x00000016,0x0000002b,
0x00040047,0x00000008,0x0000000b,0x0000002a,
0x00030047,0x00000014,0x00000002,0x00050048,
0x00000014,0x00000000,0x0000000b,0x00000000,
0x00050048,0x00000014,0x00000001,0x0000000b,
0x00000001,0x00050048,0x00000014,0x00000002,
0x0000000b,0x00000003,0x00050048,0x00000014,
0x00000003,0x0000000b,0x00000004,0x00040047,
0x0000002b,0x0000001e,0x00000000,0x00020013,
0x00000002,0x00030021,0x00000003,0x00000002,
0x00040015,0x00000006,0x00000020,0x00000001,
0x00040020,0x00000007,0x00000001,0x00000006,
0x0004003b,0x00000007,0x00000008,0x00000001,
0x0004002b,0x00000006,0x0000000a,0x00000000,
0x00020014,0x0000000b,0x00030016,0x0000000f,
0x00000020,0x00040017,0x00000010,0x0000000f,
0x00000004,0x00040015,0x00000011,0x00000020,
0x00000000,0x0004002b,0x00000011,0x00000012,
0x00000001,0x0004001c,0x00000013,0x0000000f,
0x00000012,0x0006001e,0x00000014,0x00000010,
0x0000000f,0x00000013,0x00000013,0x00040020,
0x00000015,0x00000003,0x00000014,0x0004003b,
0x00000015,0x00000016,0x00000003,0x0004002b,
0x0000000f,0x00000017,0xbf800000,0x0004002b,
0x0000000f,0x00000018,0x00000000,0x0004002b,
0x0000000f,0x00000019,0x3f800000,0x0007002c,
0x00000010,0x0000001a,0x00000017,0x00000017,
0x00000018,0x00000019,0x00040020,0x0000001b,
0x00000003,0x00000010,0x0004002b,0x00000006,
0x0000001f,0x00000001,0x0004002b,0x0000000f,
0x00000023,0x40400000,0x0007002c,0x00000010,
0x00000024,0x00000017,0x00000023,0x00000018,
0x00000019,0x0007002c,0x00000010,0x00000027,
0x00000023,0x00000017,0x00000018,0x00000019,
0x00040017,0x00000029,0x0000000f,0x00000002,
0x00040020,0x0000002a,0x00000003,0x00000029,
0x0004003b,0x0000002a,0x0000002b,0x00000003,
0x0004002b,0x0000000f,0x0000002f,0x3f000000,
0x0005002c,0x00000029,0x00000033,0x0000002f,
0x0000002f,0x00050036,0x00000002,0x00000004,
0x00000000,0x00000003,0x000200f8,0x00000005,
0x0004003d,0x00000006,0x00000009,0x00000008,
0x000500aa,0x0000000b,0x0000000c,0x00000009,
0x0000000a,0x000300f7,0x0000000e,0x00000000,
0x000400fa,0x0000000c,0x0000000d,0x0000001d,
0x000200f8,0x0000000d,0x00050041,0x0000001b,
0x0000001c,0x00000016,0x0000000a,0x0003003e,
0x0000001c,0x0000001a,0x000200f9,0x0000000e,
0x000200f8,0x0000001d,0x000500aa,0x0000000b,
0x00000020,0x00000009,0x0000001f,0x000300f7,
0x00000022,0x00000000,0x000400fa,0x00000020,
0x00000021,0x00000026,0x000200f8,0x00000021,
0x00050041,0x0000001b,0x00000025,0x00000016,
0x0000000a,0x0003003e,0x00000025,0x00000024,
0x000200f9,0x00000022,0x000200f8,0x00000026,
0x00050041,0x0000001b,0x00000028,0x00000016,
0x0000000a,0x0003003e,0x00000028,0x00000027,
0x000200f9,0x00000022,0x000200f8,0x00000022,
0x000200f9,0x0000000e,0x000200f8,0x0000000e,
0x00050041,0x0000001b,0x0000002c,0x00000016,
0x0000000a,0x0004003d,0x00000010,0x0000002d,
0x0000002c,0x0007004f,0x00000029,0x0000002e,
0x0000002d,0x0000002d,0x00000000,0x00000001,
0x0005008e,0x00000029,0x00000030,0x0000002e,
0x0000002f,0x00050081,0x00000029,0x00000032,
0x00000030,0x00000033,0x0003003e,0x0000002b,
0x00000032,0x000100fd,0x00010038}
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
glslc -O crt-aperture.frag -o crt_frag.spv --target-env=vulkan1.1 -mfmt=c
glslc -O main.frag -o main_frag.spv --target-env=vulkan1.1 -mfmt=c
glslc -O main.vert -o main_vert.spv --target-env=vulkan1.1 -mfmt=c
Binary file not shown.
+204 -28
View File
@@ -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,8 +52,6 @@ enum vi_registers
VI_REGS_COUNT
};
static bool fullscreen;
static bool integer_scaling;
static SDL_Window *window;
static RDP::CommandProcessor *processor;
static SDL_WSIPlatform *wsi_platform;
@@ -63,6 +62,31 @@ static int cmd_ptr;
static CALL_BACK callback;
static GFX_INFO gfx_info;
static uint32_t region;
static bool crop_letterbox;
static const uint32_t *fragment_spirv;
static size_t fragment_size;
typedef struct
{
uint32_t depthbuffer_address;
uint32_t framebuffer_address;
uint32_t framebuffer_pixel_size;
uint32_t framebuffer_width;
uint32_t framebuffer_height;
uint32_t framebuffer_size;
uint32_t depthbuffer_size;
uint8_t depthbuffer_enabled;
} FrameBufferInfo;
typedef struct
{
float SourceSize[4];
float OutputSize[4];
} Push;
static uint8_t *rdram_dirty;
static uint64_t sync_signal;
static FrameBufferInfo frame_buffer_info;
static const unsigned cmd_len_lut[64] = {
1,
@@ -141,14 +165,30 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
{
wsi_platform->do_resize();
}
else if (event->type == SDL_EVENT_KEY_DOWN)
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)
{
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;
@@ -163,23 +203,34 @@ 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)
{
memset(&frame_buffer_info, 0, sizeof(FrameBufferInfo));
sync_signal = 0;
memset(rdram_dirty, 0, gfx_info.RDRAM_SIZE / 8);
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);
@@ -190,8 +241,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;
@@ -208,7 +269,9 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_sc
{
rdp_close();
}
rdp_new_processor(gfx_info, _upscale);
rdram_dirty = (uint8_t *)malloc(gfx_info.RDRAM_SIZE / 8);
rdp_new_processor(gfx_info);
if (!processor->device_is_supported())
{
@@ -220,10 +283,18 @@ 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()
{
if (rdram_dirty)
{
free(rdram_dirty);
rdram_dirty = nullptr;
}
wsi->end_frame();
if (processor)
@@ -243,15 +314,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 = 320;
const int32_t display_height = 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;
@@ -266,6 +336,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
{
@@ -276,11 +353,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)
@@ -289,17 +366,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);
@@ -309,9 +403,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.
@@ -322,6 +413,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.
@@ -355,6 +459,41 @@ CALL_BACK rdp_check_callback()
return return_value;
}
void rdp_check_framebuffers(uint32_t address)
{
if (sync_signal && rdram_dirty[address >> 3])
{
processor->wait_for_timeline(sync_signal);
memset(rdram_dirty, 0, gfx_info.RDRAM_SIZE / 8);
sync_signal = 0;
}
}
void rdp_full_sync()
{
processor->wait_for_timeline(processor->signal_timeline());
}
void calculate_buffer_size()
{
switch (frame_buffer_info.framebuffer_pixel_size)
{
case 0:
frame_buffer_info.framebuffer_size = (frame_buffer_info.framebuffer_width * frame_buffer_info.framebuffer_height / 2) >> 3;
break;
case 1:
frame_buffer_info.framebuffer_size = (frame_buffer_info.framebuffer_width * frame_buffer_info.framebuffer_height) >> 3;
break;
case 2:
frame_buffer_info.framebuffer_size = (frame_buffer_info.framebuffer_width * frame_buffer_info.framebuffer_height * 2) >> 3;
break;
case 3:
frame_buffer_info.framebuffer_size = (frame_buffer_info.framebuffer_width * frame_buffer_info.framebuffer_height * 4) >> 3;
break;
}
frame_buffer_info.depthbuffer_size = (frame_buffer_info.framebuffer_width * frame_buffer_info.framebuffer_height * 2) >> 3;
}
uint64_t rdp_process_commands()
{
uint64_t interrupt_timer = 0;
@@ -416,18 +555,55 @@ uint64_t rdp_process_commands()
if (command >= 8)
processor->enqueue_command(cmd_length * 2, &cmd_data[2 * cmd_cur]);
if (RDP::Op(command) == RDP::Op::SetScissor)
if ((RDP::Op(command) >= RDP::Op::FillTriangle && RDP::Op(command) <= RDP::Op::ShadeTextureZBufferTriangle) ||
RDP::Op(command) == RDP::Op::TextureRectangle ||
RDP::Op(command) == RDP::Op::TextureRectangleFlip ||
RDP::Op(command) == RDP::Op::FillRectangle)
{
if (!rdram_dirty[frame_buffer_info.framebuffer_address])
{
for (uint32_t i = frame_buffer_info.framebuffer_address; i < frame_buffer_info.framebuffer_address + frame_buffer_info.framebuffer_size; ++i)
{
rdram_dirty[i] = 1;
}
}
if (frame_buffer_info.depthbuffer_enabled && !rdram_dirty[frame_buffer_info.depthbuffer_address])
{
for (uint32_t i = frame_buffer_info.depthbuffer_address; i < frame_buffer_info.depthbuffer_address + frame_buffer_info.depthbuffer_size; ++i)
{
rdram_dirty[i] = 1;
}
}
}
else if (RDP::Op(command) == RDP::Op::SetOtherModes)
{
frame_buffer_info.depthbuffer_enabled = (w2 >> 5) & 1;
}
else if (RDP::Op(command) == RDP::Op::SetColorImage)
{
frame_buffer_info.framebuffer_address = (w2 & 0x00FFFFFF) >> 3;
frame_buffer_info.framebuffer_pixel_size = (w1 >> 19) & 0x3;
frame_buffer_info.framebuffer_width = (w1 & 0x3FF) + 1;
calculate_buffer_size();
}
else if (RDP::Op(command) == RDP::Op::SetMaskImage)
{
frame_buffer_info.depthbuffer_address = (w2 & 0x00FFFFFF) >> 3;
}
else if (RDP::Op(command) == 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);
frame_buffer_info.framebuffer_height = lower_right_y;
calculate_buffer_size();
}
if (RDP::Op(command) == RDP::Op::SyncFull)
else if (RDP::Op(command) == RDP::Op::SyncFull)
{
processor->wait_for_timeline(processor->signal_timeline());
sync_signal = processor->signal_timeline();
interrupt_timer = region;
if (interrupt_timer == 0)
interrupt_timer = 5000;
+11 -2
View File
@@ -16,6 +16,12 @@ extern "C"
uint32_t *DPC_START_REG;
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
@@ -23,15 +29,18 @@ extern "C"
bool emu_running;
bool save_state;
bool load_state;
bool enable_speedlimiter;
} 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);
void rdp_check_framebuffers(uint32_t address);
void rdp_full_sync();
#ifdef __cplusplus
}
+11 -11
View File
@@ -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,
+456
View File
@@ -0,0 +1,456 @@
#pragma once
static const uint32_t crt_fragment_spirv[] =
{0x07230203, 0x00010300, 0x000d000b, 0x000004fd,
0x00000000, 0x00020011, 0x00000001, 0x0006000b,
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
0x00000000, 0x0003000e, 0x00000000, 0x00000001,
0x0007000f, 0x00000004, 0x00000004, 0x6e69616d,
0x00000000, 0x000001ba, 0x0000021a, 0x00030010,
0x00000004, 0x00000007, 0x00030047, 0x00000185,
0x00000002, 0x00050048, 0x00000185, 0x00000000,
0x00000023, 0x00000000, 0x00050048, 0x00000185,
0x00000001, 0x00000023, 0x00000010, 0x00040047,
0x000001ba, 0x0000001e, 0x00000000, 0x00040047,
0x000001ca, 0x00000021, 0x00000000, 0x00040047,
0x000001ca, 0x00000022, 0x00000000, 0x00040047,
0x0000021a, 0x0000001e, 0x00000000, 0x00020013,
0x00000002, 0x00030021, 0x00000003, 0x00000002,
0x00030016, 0x00000006, 0x00000020, 0x00090019,
0x00000007, 0x00000006, 0x00000001, 0x00000000,
0x00000000, 0x00000000, 0x00000001, 0x00000000,
0x0003001b, 0x00000008, 0x00000007, 0x00040020,
0x00000009, 0x00000000, 0x00000008, 0x00040017,
0x0000000a, 0x00000006, 0x00000002, 0x00040017,
0x0000000c, 0x00000006, 0x00000003, 0x00040017,
0x00000037, 0x00000006, 0x00000004, 0x0004002b,
0x00000006, 0x0000003a, 0x4019999a, 0x0006002c,
0x0000000c, 0x0000003b, 0x0000003a, 0x0000003a,
0x0000003a, 0x0004002b, 0x00000006, 0x00000049,
0x3f800000, 0x0004002b, 0x00000006, 0x0000004a,
0x00000000, 0x0004002b, 0x00000006, 0x00000068,
0xbf800000, 0x00040015, 0x0000006b, 0x00000020,
0x00000001, 0x0004002b, 0x0000006b, 0x0000006c,
0x00000000, 0x00040015, 0x0000006f, 0x00000020,
0x00000000, 0x0004002b, 0x0000006f, 0x00000070,
0x00000000, 0x0004002b, 0x0000006b, 0x00000074,
0x00000001, 0x0004002b, 0x0000006f, 0x00000077,
0x00000001, 0x0004002b, 0x00000006, 0x000000a1,
0x3f000000, 0x0005002c, 0x0000000a, 0x000000fc,
0x000000a1, 0x0000004a, 0x0004002b, 0x00000006,
0x00000101, 0x3a83126f, 0x0005002c, 0x0000000a,
0x00000102, 0x000000a1, 0x00000101, 0x0004002b,
0x00000006, 0x0000010b, 0x40490fdb, 0x0004002b,
0x00000006, 0x00000116, 0x40000000, 0x0004002b,
0x00000006, 0x0000011c, 0x3727c5ac, 0x0007002c,
0x00000037, 0x0000012c, 0x00000049, 0x00000049,
0x00000049, 0x00000049, 0x00040018, 0x00000149,
0x00000037, 0x00000004, 0x0006002c, 0x0000000c,
0x00000165, 0x000000a1, 0x000000a1, 0x000000a1,
0x0004002b, 0x00000006, 0x00000166, 0x3fc00000,
0x0006002c, 0x0000000c, 0x00000167, 0x00000166,
0x00000166, 0x00000166, 0x0004002b, 0x00000006,
0x00000169, 0x3ecccccd, 0x0006002c, 0x0000000c,
0x0000016a, 0x00000169, 0x00000169, 0x00000169,
0x0004001e, 0x00000185, 0x00000037, 0x00000037,
0x00040020, 0x00000186, 0x00000009, 0x00000185,
0x0004003b, 0x00000186, 0x00000187, 0x00000009,
0x00040020, 0x00000188, 0x00000009, 0x00000006,
0x00020014, 0x00000196, 0x0006002c, 0x0000000c,
0x0000019a, 0x00000049, 0x0000004a, 0x00000049,
0x0006002c, 0x0000000c, 0x000001a1, 0x0000004a,
0x00000049, 0x0000004a, 0x0006002c, 0x0000000c,
0x000001a4, 0x0000004a, 0x0000004a, 0x00000049,
0x0004002b, 0x0000006f, 0x000001aa, 0x00000003,
0x00040020, 0x000001b9, 0x00000001, 0x0000000a,
0x0004003b, 0x000001b9, 0x000001ba, 0x00000001,
0x00040020, 0x000001bc, 0x00000009, 0x00000037,
0x0004003b, 0x00000009, 0x000001ca, 0x00000000,
0x0004002b, 0x00000006, 0x000001dc, 0x40400000,
0x0004002b, 0x00000006, 0x000001ff, 0x3dcccccd,
0x00040020, 0x00000206, 0x00000001, 0x00000006,
0x0004002b, 0x00000006, 0x0000020c, 0x3e99999a,
0x0004002b, 0x00000006, 0x00000210, 0x3d4ccccd,
0x0004002b, 0x00000006, 0x00000216, 0x3ed55555,
0x0006002c, 0x0000000c, 0x00000217, 0x00000216,
0x00000216, 0x00000216, 0x00040020, 0x00000219,
0x00000003, 0x00000037, 0x0004003b, 0x00000219,
0x0000021a, 0x00000003, 0x0005002c, 0x0000000a,
0x000004f3, 0x000000a1, 0x000000a1, 0x0007002c,
0x00000037, 0x000004f4, 0x0000011c, 0x0000011c,
0x0000011c, 0x0000011c, 0x0006002c, 0x0000000c,
0x000004f6, 0x00000116, 0x00000116, 0x00000116,
0x0006002c, 0x0000000c, 0x000004f7, 0x00000049,
0x00000049, 0x00000049, 0x0006002c, 0x0000000c,
0x000004f8, 0x0000004a, 0x0000004a, 0x0000004a,
0x0006002c, 0x0000000c, 0x000004f9, 0x0000020c,
0x0000020c, 0x0000020c, 0x0007002c, 0x00000037,
0x000004fa, 0x000000a1, 0x000000a1, 0x000000a1,
0x000000a1, 0x0004002b, 0x00000006, 0x000004fb,
0x3eaaaaab, 0x00050036, 0x00000002, 0x00000004,
0x00000000, 0x00000003, 0x000200f8, 0x00000005,
0x00060041, 0x00000188, 0x000001a8, 0x00000187,
0x00000074, 0x00000077, 0x0004003d, 0x00000006,
0x000001a9, 0x000001a8, 0x00060041, 0x00000188,
0x000001ab, 0x00000187, 0x0000006c, 0x000001aa,
0x0004003d, 0x00000006, 0x000001ac, 0x000001ab,
0x00050085, 0x00000006, 0x000001ad, 0x000001a9,
0x000001ac, 0x0006000c, 0x00000006, 0x000001ae,
0x00000001, 0x00000008, 0x000001ad, 0x00050088,
0x00000006, 0x000001b2, 0x000000a1, 0x000001ae,
0x0005008d, 0x00000006, 0x000001b4, 0x000001ae,
0x00000116, 0x000500b7, 0x00000196, 0x000001b5,
0x000001b4, 0x0000004a, 0x000600a9, 0x00000006,
0x000004fc, 0x000001b5, 0x0000004a, 0x000001b2,
0x0004003d, 0x0000000a, 0x000001bb, 0x000001ba,
0x00050041, 0x000001bc, 0x000001bd, 0x00000187,
0x0000006c, 0x0004003d, 0x00000037, 0x000001be,
0x000001bd, 0x0007004f, 0x0000000a, 0x000001bf,
0x000001be, 0x000001be, 0x00000000, 0x00000001,
0x00050085, 0x0000000a, 0x000001c0, 0x000001bb,
0x000001bf, 0x00050050, 0x0000000a, 0x000001c3,
0x0000004a, 0x000004fc, 0x00050083, 0x0000000a,
0x000001c4, 0x000001c0, 0x000001c3, 0x0007004f,
0x0000000a, 0x000001c7, 0x000001be, 0x000001be,
0x00000002, 0x00000003, 0x00050085, 0x0000000a,
0x000001c8, 0x000001c4, 0x000001c7, 0x00050051,
0x00000006, 0x00000249, 0x000001be, 0x00000000,
0x00050088, 0x00000006, 0x0000024a, 0x00000049,
0x00000249, 0x00050050, 0x0000000a, 0x0000024b,
0x0000024a, 0x0000004a, 0x00050051, 0x00000006,
0x0000024d, 0x000001be, 0x00000001, 0x00050088,
0x00000006, 0x0000024e, 0x00000049, 0x0000024d,
0x00050050, 0x0000000a, 0x0000024f, 0x0000004a,
0x0000024e, 0x00050085, 0x0000000a, 0x00000252,
0x000001c8, 0x000001bf, 0x0006000c, 0x0000000a,
0x00000254, 0x00000001, 0x00000008, 0x00000252,
0x00050081, 0x0000000a, 0x00000256, 0x00000254,
0x000004f3, 0x00050088, 0x0000000a, 0x00000258,
0x00000256, 0x000001bf, 0x0006000c, 0x0000000a,
0x0000025a, 0x00000001, 0x0000000a, 0x00000252,
0x00050083, 0x0000000a, 0x0000025c, 0x0000025a,
0x000004f3, 0x0005008e, 0x0000000a, 0x0000025d,
0x0000025c, 0x00000068, 0x00050083, 0x0000000a,
0x00000260, 0x00000258, 0x0000024f, 0x0004003d,
0x00000008, 0x0000028a, 0x000001ca, 0x00050083,
0x0000000a, 0x0000028d, 0x00000260, 0x0000024b,
0x00050057, 0x00000037, 0x0000028e, 0x0000028a,
0x0000028d, 0x0008004f, 0x0000000c, 0x0000028f,
0x0000028e, 0x0000028e, 0x00000000, 0x00000001,
0x00000002, 0x0007000c, 0x0000000c, 0x00000290,
0x00000001, 0x0000001a, 0x0000028f, 0x0000003b,
0x00050057, 0x00000037, 0x00000293, 0x0000028a,
0x00000260, 0x0008004f, 0x0000000c, 0x00000294,
0x00000293, 0x00000293, 0x00000000, 0x00000001,
0x00000002, 0x0007000c, 0x0000000c, 0x00000295,
0x00000001, 0x0000001a, 0x00000294, 0x0000003b,
0x00050081, 0x0000000a, 0x00000299, 0x00000260,
0x0000024b, 0x00050057, 0x00000037, 0x0000029a,
0x0000028a, 0x00000299, 0x0008004f, 0x0000000c,
0x0000029b, 0x0000029a, 0x0000029a, 0x00000000,
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
0x0000029c, 0x00000001, 0x0000001a, 0x0000029b,
0x0000003b, 0x00050083, 0x0000000a, 0x000002af,
0x00000258, 0x0000024b, 0x00050057, 0x00000037,
0x000002b0, 0x0000028a, 0x000002af, 0x0008004f,
0x0000000c, 0x000002b1, 0x000002b0, 0x000002b0,
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
0x0000000c, 0x000002b2, 0x00000001, 0x0000001a,
0x000002b1, 0x0000003b, 0x00050057, 0x00000037,
0x000002b5, 0x0000028a, 0x00000258, 0x0008004f,
0x0000000c, 0x000002b6, 0x000002b5, 0x000002b5,
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
0x0000000c, 0x000002b7, 0x00000001, 0x0000001a,
0x000002b6, 0x0000003b, 0x00050081, 0x0000000a,
0x000002bb, 0x00000258, 0x0000024b, 0x00050057,
0x00000037, 0x000002bc, 0x0000028a, 0x000002bb,
0x0008004f, 0x0000000c, 0x000002bd, 0x000002bc,
0x000002bc, 0x00000000, 0x00000001, 0x00000002,
0x0007000c, 0x0000000c, 0x000002be, 0x00000001,
0x0000001a, 0x000002bd, 0x0000003b, 0x00050081,
0x0000000a, 0x00000268, 0x00000258, 0x0000024f,
0x00050083, 0x0000000a, 0x000002d1, 0x00000268,
0x0000024b, 0x00050057, 0x00000037, 0x000002d2,
0x0000028a, 0x000002d1, 0x0008004f, 0x0000000c,
0x000002d3, 0x000002d2, 0x000002d2, 0x00000000,
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
0x000002d4, 0x00000001, 0x0000001a, 0x000002d3,
0x0000003b, 0x00050057, 0x00000037, 0x000002d7,
0x0000028a, 0x00000268, 0x0008004f, 0x0000000c,
0x000002d8, 0x000002d7, 0x000002d7, 0x00000000,
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
0x000002d9, 0x00000001, 0x0000001a, 0x000002d8,
0x0000003b, 0x00050081, 0x0000000a, 0x000002dd,
0x00000268, 0x0000024b, 0x00050057, 0x00000037,
0x000002de, 0x0000028a, 0x000002dd, 0x0008004f,
0x0000000c, 0x000002df, 0x000002de, 0x000002de,
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
0x0000000c, 0x000002e0, 0x00000001, 0x0000001a,
0x000002df, 0x0000003b, 0x00050051, 0x00000006,
0x0000026d, 0x0000025d, 0x00000000, 0x00050083,
0x00000006, 0x000002f3, 0x0000026d, 0x00000049,
0x00050081, 0x00000006, 0x000002f6, 0x0000026d,
0x00000049, 0x00060050, 0x0000000c, 0x000002f7,
0x000002f3, 0x0000026d, 0x000002f6, 0x00050085,
0x0000000c, 0x000002fa, 0x000002f7, 0x000004f6,
0x00050085, 0x0000000c, 0x000002fd, 0x000002fa,
0x000002fa, 0x0005008e, 0x0000000c, 0x000002fe,
0x000002fd, 0x00000068, 0x0006000c, 0x0000000c,
0x000002ff, 0x00000001, 0x0000001d, 0x000002fe,
0x00050051, 0x00000006, 0x00000303, 0x000002ff,
0x00000000, 0x0005008e, 0x0000000c, 0x00000304,
0x00000290, 0x00000303, 0x00050051, 0x00000006,
0x00000308, 0x000002ff, 0x00000001, 0x0005008e,
0x0000000c, 0x00000309, 0x00000295, 0x00000308,
0x00050081, 0x0000000c, 0x0000030a, 0x00000304,
0x00000309, 0x00050051, 0x00000006, 0x0000030e,
0x000002ff, 0x00000002, 0x0005008e, 0x0000000c,
0x0000030f, 0x0000029c, 0x0000030e, 0x00050081,
0x0000000c, 0x00000310, 0x0000030a, 0x0000030f,
0x00050081, 0x00000006, 0x00000315, 0x00000303,
0x00000308, 0x00050081, 0x00000006, 0x00000318,
0x00000315, 0x0000030e, 0x00060050, 0x0000000c,
0x00000319, 0x00000318, 0x00000318, 0x00000318,
0x00050088, 0x0000000c, 0x0000031a, 0x00000310,
0x00000319, 0x0005008e, 0x0000000c, 0x00000331,
0x000002b2, 0x00000303, 0x0005008e, 0x0000000c,
0x00000336, 0x000002b7, 0x00000308, 0x00050081,
0x0000000c, 0x00000337, 0x00000331, 0x00000336,
0x0005008e, 0x0000000c, 0x0000033c, 0x000002be,
0x0000030e, 0x00050081, 0x0000000c, 0x0000033d,
0x00000337, 0x0000033c, 0x00050088, 0x0000000c,
0x00000347, 0x0000033d, 0x00000319, 0x0005008e,
0x0000000c, 0x0000035e, 0x000002d4, 0x00000303,
0x0005008e, 0x0000000c, 0x00000363, 0x000002d9,
0x00000308, 0x00050081, 0x0000000c, 0x00000364,
0x0000035e, 0x00000363, 0x0005008e, 0x0000000c,
0x00000369, 0x000002e0, 0x0000030e, 0x00050081,
0x0000000c, 0x0000036a, 0x00000364, 0x00000369,
0x00050088, 0x0000000c, 0x00000374, 0x0000036a,
0x00000319, 0x00050051, 0x00000006, 0x00000286,
0x0000025d, 0x00000001, 0x00050083, 0x00000006,
0x0000037a, 0x00000286, 0x00000049, 0x00050081,
0x00000006, 0x0000037d, 0x00000286, 0x00000049,
0x00060050, 0x0000000c, 0x0000037e, 0x0000037a,
0x00000286, 0x0000037d, 0x00050085, 0x0000000c,
0x00000381, 0x0000037e, 0x000004f6, 0x00050085,
0x0000000c, 0x00000384, 0x00000381, 0x00000381,
0x0005008e, 0x0000000c, 0x00000385, 0x00000384,
0x00000068, 0x0006000c, 0x0000000c, 0x00000386,
0x00000001, 0x0000001d, 0x00000385, 0x00050051,
0x00000006, 0x0000038a, 0x00000386, 0x00000000,
0x0005008e, 0x0000000c, 0x0000038b, 0x0000031a,
0x0000038a, 0x00050051, 0x00000006, 0x0000038f,
0x00000386, 0x00000001, 0x0005008e, 0x0000000c,
0x00000390, 0x00000347, 0x0000038f, 0x00050081,
0x0000000c, 0x00000391, 0x0000038b, 0x00000390,
0x00050051, 0x00000006, 0x00000395, 0x00000386,
0x00000002, 0x0005008e, 0x0000000c, 0x00000396,
0x00000374, 0x00000395, 0x00050081, 0x0000000c,
0x00000397, 0x00000391, 0x00000396, 0x00050081,
0x00000006, 0x0000039c, 0x0000038a, 0x0000038f,
0x00050081, 0x00000006, 0x0000039f, 0x0000039c,
0x00000395, 0x00060050, 0x0000000c, 0x000003a0,
0x0000039f, 0x0000039f, 0x0000039f, 0x00050088,
0x0000000c, 0x000003a1, 0x00000397, 0x000003a0,
0x00060052, 0x0000000a, 0x000004e1, 0x00000249,
0x000001bf, 0x00000000, 0x00050085, 0x0000000a,
0x000003b6, 0x000001c8, 0x000004e1, 0x00050083,
0x0000000a, 0x000003b7, 0x000003b6, 0x000000fc,
0x0006000c, 0x0000000a, 0x000003b9, 0x00000001,
0x00000008, 0x000003b7, 0x00050081, 0x0000000a,
0x000003ba, 0x000003b9, 0x00000102, 0x00050088,
0x0000000a, 0x000003bc, 0x000003ba, 0x000004e1,
0x0006000c, 0x0000000a, 0x000003be, 0x00000001,
0x0000000a, 0x000003b7, 0x00050051, 0x00000006,
0x000003c0, 0x000003be, 0x00000000, 0x00050081,
0x00000006, 0x000003c1, 0x000003c0, 0x00000049,
0x00050083, 0x00000006, 0x000003c6, 0x000003c0,
0x00000049, 0x00050083, 0x00000006, 0x000003c9,
0x000003c0, 0x00000116, 0x00070050, 0x00000037,
0x000003ca, 0x000003c1, 0x000003c0, 0x000003c6,
0x000003c9, 0x0005008e, 0x00000037, 0x000003cb,
0x000003ca, 0x0000010b, 0x0006000c, 0x00000037,
0x000003cd, 0x00000001, 0x00000004, 0x000003cb,
0x0007000c, 0x00000037, 0x000003cf, 0x00000001,
0x00000028, 0x000003cd, 0x000004f4, 0x0006000c,
0x00000037, 0x000003d1, 0x00000001, 0x0000000d,
0x000003cf, 0x0005008e, 0x00000037, 0x000003d2,
0x000003d1, 0x00000116, 0x00050085, 0x00000037,
0x000003d5, 0x000003cf, 0x000004fa, 0x0006000c,
0x00000037, 0x000003d6, 0x00000001, 0x0000000d,
0x000003d5, 0x00050085, 0x00000037, 0x000003d7,
0x000003d2, 0x000003d6, 0x00050085, 0x00000037,
0x000003da, 0x000003cf, 0x000003cf, 0x00050088,
0x00000037, 0x000003db, 0x000003d7, 0x000003da,
0x00050094, 0x00000006, 0x000003dd, 0x000003db,
0x0000012c, 0x00070050, 0x00000037, 0x000003df,
0x000003dd, 0x000003dd, 0x000003dd, 0x000003dd,
0x00050088, 0x00000037, 0x000003e0, 0x000003db,
0x000003df, 0x00050057, 0x00000037, 0x000003e3,
0x0000028a, 0x000003bc, 0x0008004f, 0x0000000c,
0x000003e4, 0x000003e3, 0x000003e3, 0x00000000,
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
0x000003e5, 0x00000001, 0x0000001a, 0x000003e4,
0x0000003b, 0x00050051, 0x00000006, 0x000003e6,
0x000003e5, 0x00000000, 0x00050051, 0x00000006,
0x000003e7, 0x000003e5, 0x00000001, 0x00050051,
0x00000006, 0x000003e8, 0x000003e5, 0x00000002,
0x00050081, 0x0000000a, 0x000003ed, 0x000003bc,
0x0000024b, 0x00050057, 0x00000037, 0x000003ee,
0x0000028a, 0x000003ed, 0x0008004f, 0x0000000c,
0x000003ef, 0x000003ee, 0x000003ee, 0x00000000,
0x00000001, 0x00000002, 0x0007000c, 0x0000000c,
0x000003f0, 0x00000001, 0x0000001a, 0x000003ef,
0x0000003b, 0x00050051, 0x00000006, 0x000003f1,
0x000003f0, 0x00000000, 0x00050051, 0x00000006,
0x000003f2, 0x000003f0, 0x00000001, 0x00050051,
0x00000006, 0x000003f3, 0x000003f0, 0x00000002,
0x00070050, 0x00000037, 0x00000407, 0x000003e6,
0x000003e7, 0x000003e8, 0x00000049, 0x00070050,
0x00000037, 0x00000409, 0x000003f1, 0x000003f2,
0x000003f3, 0x00000049, 0x00070050, 0x00000149,
0x0000040b, 0x00000407, 0x00000407, 0x00000409,
0x00000409, 0x00050091, 0x00000037, 0x0000040d,
0x0000040b, 0x000003e0, 0x0008004f, 0x0000000c,
0x0000040e, 0x0000040d, 0x0000040d, 0x00000000,
0x00000001, 0x00000002, 0x00050085, 0x00000006,
0x0000041b, 0x00000249, 0x000001dc, 0x00060052,
0x0000000a, 0x000004e9, 0x0000041b, 0x000001bf,
0x00000000, 0x00050088, 0x00000006, 0x0000041f,
0x000004fb, 0x00000249, 0x00050050, 0x0000000a,
0x00000420, 0x0000041f, 0x0000004a, 0x00050085,
0x0000000a, 0x00000423, 0x000001c8, 0x000004e9,
0x00050083, 0x0000000a, 0x00000424, 0x00000423,
0x000000fc, 0x0006000c, 0x0000000a, 0x00000426,
0x00000001, 0x00000008, 0x00000424, 0x00050081,
0x0000000a, 0x00000427, 0x00000426, 0x00000102,
0x00050088, 0x0000000a, 0x00000429, 0x00000427,
0x000004e9, 0x0006000c, 0x0000000a, 0x0000042b,
0x00000001, 0x0000000a, 0x00000424, 0x00050051,
0x00000006, 0x0000042d, 0x0000042b, 0x00000000,
0x00050081, 0x00000006, 0x0000042e, 0x0000042d,
0x00000049, 0x00050083, 0x00000006, 0x00000433,
0x0000042d, 0x00000049, 0x00050083, 0x00000006,
0x00000436, 0x0000042d, 0x00000116, 0x00070050,
0x00000037, 0x00000437, 0x0000042e, 0x0000042d,
0x00000433, 0x00000436, 0x0005008e, 0x00000037,
0x00000438, 0x00000437, 0x0000010b, 0x0006000c,
0x00000037, 0x0000043a, 0x00000001, 0x00000004,
0x00000438, 0x0007000c, 0x00000037, 0x0000043c,
0x00000001, 0x00000028, 0x0000043a, 0x000004f4,
0x0006000c, 0x00000037, 0x0000043e, 0x00000001,
0x0000000d, 0x0000043c, 0x0005008e, 0x00000037,
0x0000043f, 0x0000043e, 0x00000116, 0x00050085,
0x00000037, 0x00000442, 0x0000043c, 0x000004fa,
0x0006000c, 0x00000037, 0x00000443, 0x00000001,
0x0000000d, 0x00000442, 0x00050085, 0x00000037,
0x00000444, 0x0000043f, 0x00000443, 0x00050085,
0x00000037, 0x00000447, 0x0000043c, 0x0000043c,
0x00050088, 0x00000037, 0x00000448, 0x00000444,
0x00000447, 0x00050094, 0x00000006, 0x0000044a,
0x00000448, 0x0000012c, 0x00070050, 0x00000037,
0x0000044c, 0x0000044a, 0x0000044a, 0x0000044a,
0x0000044a, 0x00050088, 0x00000037, 0x0000044d,
0x00000448, 0x0000044c, 0x00050057, 0x00000037,
0x00000450, 0x0000028a, 0x00000429, 0x0008004f,
0x0000000c, 0x00000451, 0x00000450, 0x00000450,
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
0x0000000c, 0x00000452, 0x00000001, 0x0000001a,
0x00000451, 0x0000003b, 0x00050051, 0x00000006,
0x00000453, 0x00000452, 0x00000000, 0x00050051,
0x00000006, 0x00000454, 0x00000452, 0x00000001,
0x00050051, 0x00000006, 0x00000455, 0x00000452,
0x00000002, 0x00050081, 0x0000000a, 0x0000045a,
0x00000429, 0x00000420, 0x00050057, 0x00000037,
0x0000045b, 0x0000028a, 0x0000045a, 0x0008004f,
0x0000000c, 0x0000045c, 0x0000045b, 0x0000045b,
0x00000000, 0x00000001, 0x00000002, 0x0007000c,
0x0000000c, 0x0000045d, 0x00000001, 0x0000001a,
0x0000045c, 0x0000003b, 0x00050051, 0x00000006,
0x0000045e, 0x0000045d, 0x00000000, 0x00050051,
0x00000006, 0x0000045f, 0x0000045d, 0x00000001,
0x00050051, 0x00000006, 0x00000460, 0x0000045d,
0x00000002, 0x00070050, 0x00000037, 0x00000474,
0x00000453, 0x00000454, 0x00000455, 0x00000049,
0x00070050, 0x00000037, 0x00000476, 0x0000045e,
0x0000045f, 0x00000460, 0x00000049, 0x00070050,
0x00000149, 0x00000478, 0x00000474, 0x00000474,
0x00000476, 0x00000476, 0x00050091, 0x00000037,
0x0000047a, 0x00000478, 0x0000044d, 0x0008004f,
0x0000000c, 0x0000047b, 0x0000047a, 0x0000047a,
0x00000000, 0x00000001, 0x00000002, 0x00050085,
0x0000000c, 0x000001e8, 0x0000047b, 0x0000040e,
0x0006000c, 0x0000000c, 0x000001e9, 0x00000001,
0x0000001f, 0x000001e8, 0x00050051, 0x00000006,
0x000001eb, 0x000001c8, 0x00000001, 0x00060041,
0x00000188, 0x000001ec, 0x00000187, 0x0000006c,
0x00000077, 0x0004003d, 0x00000006, 0x000001ed,
0x000001ec, 0x00050085, 0x00000006, 0x000001ee,
0x000001eb, 0x000001ed, 0x0006000c, 0x00000006,
0x000001ef, 0x00000001, 0x0000000a, 0x000001ee,
0x0007000c, 0x0000000c, 0x00000482, 0x00000001,
0x0000001a, 0x0000040e, 0x0000016a, 0x0008000c,
0x0000000c, 0x00000483, 0x00000001, 0x0000002e,
0x00000165, 0x00000167, 0x00000482, 0x00050088,
0x0000000c, 0x00000486, 0x000004f6, 0x00000483,
0x0005008e, 0x0000000c, 0x00000488, 0x00000486,
0x000000a1, 0x0005008e, 0x0000000c, 0x0000048b,
0x00000486, 0x000001ef, 0x00050083, 0x0000000c,
0x0000048d, 0x0000048b, 0x00000488, 0x0006000c,
0x0000000c, 0x0000048e, 0x00000001, 0x00000004,
0x0000048d, 0x00050083, 0x0000000c, 0x00000490,
0x000004f7, 0x0000048e, 0x0008000c, 0x0000000c,
0x00000493, 0x00000001, 0x00000031, 0x000004f8,
0x000004f7, 0x00000490, 0x00050085, 0x0000000c,
0x00000495, 0x00000493, 0x00000488, 0x00050085,
0x0000000c, 0x000001f5, 0x000001e9, 0x00000495,
0x00050083, 0x0000000c, 0x000001f8, 0x000003a1,
0x000001f5, 0x0008000c, 0x0000000c, 0x000001fb,
0x00000001, 0x0000002b, 0x000001f8, 0x000004f8,
0x000004f7, 0x00050085, 0x0000000c, 0x000001fe,
0x000001fb, 0x000001fb, 0x0005008e, 0x0000000c,
0x00000200, 0x000001fe, 0x000001ff, 0x00050081,
0x0000000c, 0x00000202, 0x000001f5, 0x00000200,
0x00050041, 0x00000206, 0x00000207, 0x000001ba,
0x00000070, 0x0004003d, 0x00000006, 0x00000208,
0x00000207, 0x000300f7, 0x000004b3, 0x00000000,
0x000300fb, 0x00000070, 0x0000049b, 0x000200f8,
0x0000049b, 0x00060041, 0x00000188, 0x0000049d,
0x00000187, 0x00000074, 0x00000070, 0x0004003d,
0x00000006, 0x0000049e, 0x0000049d, 0x00050085,
0x00000006, 0x0000049f, 0x00000208, 0x0000049e,
0x0006000c, 0x00000006, 0x000004a7, 0x00000001,
0x00000008, 0x0000049f, 0x0005008d, 0x00000006,
0x000004a8, 0x000004a7, 0x00000116, 0x000500b4,
0x00000196, 0x000004aa, 0x000004a8, 0x0000004a,
0x000300f7, 0x000004b2, 0x00000000, 0x000400fa,
0x000004aa, 0x000004ab, 0x000004ac, 0x000200f8,
0x000004ac, 0x000500b4, 0x00000196, 0x000004ae,
0x000004a8, 0x00000049, 0x000300f7, 0x000004b1,
0x00000000, 0x000400fa, 0x000004ae, 0x000004af,
0x000004b0, 0x000200f8, 0x000004b0, 0x000200f9,
0x000004b3, 0x000200f8, 0x000004af, 0x000200f9,
0x000004b3, 0x000200f8, 0x000004b1, 0x000100ff,
0x000200f8, 0x000004ab, 0x000200f9, 0x000004b3,
0x000200f8, 0x000004b2, 0x000100ff, 0x000200f8,
0x000004b3, 0x000900f5, 0x0000000c, 0x000004f1,
0x0000019a, 0x000004ab, 0x000001a1, 0x000004af,
0x000001a4, 0x000004b0, 0x00050085, 0x0000000c,
0x0000020a, 0x00000202, 0x000004f1, 0x0005008e,
0x0000000c, 0x0000020b, 0x0000020a, 0x00000116,
0x0008000c, 0x0000000c, 0x0000020e, 0x00000001,
0x0000002e, 0x00000202, 0x0000020b, 0x000004f9,
0x0005008e, 0x0000000c, 0x00000211, 0x000001fb,
0x00000210, 0x00050081, 0x0000000c, 0x00000213,
0x0000020e, 0x00000211, 0x0005008e, 0x0000000c,
0x00000215, 0x00000213, 0x00000166, 0x0007000c,
0x0000000c, 0x00000218, 0x00000001, 0x0000001a,
0x00000215, 0x00000217, 0x00050051, 0x00000006,
0x0000021c, 0x00000218, 0x00000000, 0x00050051,
0x00000006, 0x0000021d, 0x00000218, 0x00000001,
0x00050051, 0x00000006, 0x0000021e, 0x00000218,
0x00000002, 0x00070050, 0x00000037, 0x0000021f,
0x0000021c, 0x0000021d, 0x0000021e, 0x00000049,
0x0003003e, 0x0000021a, 0x0000021f, 0x000100fd,
0x00010038};
-1
View File
@@ -61,7 +61,6 @@ void SDL_WSIPlatform::poll_input()
void SDL_WSIPlatform::poll_input_async(Granite::InputTrackerHandler *handler)
{
SDL_PumpEvents();
}
void SDL_WSIPlatform::set_window(SDL_Window *_window)
+49 -7
View File
@@ -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
@@ -66,14 +73,30 @@ pub fn run_game(rom_contents: Vec<u8>, device: &mut Device) {
ui::storage::init(&mut device.ui, &device.cart.rom);
ui::storage::load_saves(&mut device.ui, &mut device.netplay);
cart::rom::load_rom_save(device);
cpu::run(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 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>) -> Vec<u8> {
@@ -175,8 +198,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 {
@@ -270,6 +296,7 @@ impl Device {
pc: 0xBFC00000,
pc_phys: 0,
llbit: false,
overclock: false,
lo: 0,
hi: 0,
running: false,
@@ -298,19 +325,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,
@@ -358,6 +385,7 @@ impl Device {
pc: 0,
},
broken: false,
running: false,
halted: false,
sync_point: false,
cycle_counter: 0,
@@ -435,8 +463,15 @@ impl Device {
field: 0,
delay: 0,
count_per_scanline: 0,
enable_speed_limiter: true,
limiter: None,
vi_counter: 0,
last_origin: 0,
internal_frame_counter: 0,
min_wait_time: std::time::Duration::from_secs(1),
frame_time: 0.0,
limit_freq: 2,
limit_freq_check: std::time::Instant::now(),
},
vru_window: controller::vru::VruWindow {
window_notifier: None,
@@ -453,6 +488,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(),
],
}
}
}
+1 -1
View File
@@ -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);
}
+21 -12
View File
@@ -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());
+77 -82
View File
@@ -1,19 +1,35 @@
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] = [0; 4];
for i in 0..4 {
if let Some(value) = device
.ui
.storage
.saves
.romsave
.data
.get(&(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,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
let masked_address = address as usize & CART_MASK;
u32::from_be_bytes(
device.cart.rom[masked_address..masked_address + 4]
.try_into()
.unwrap(),
)
read_cart_word(device, masked_address)
}
pub fn read_mem(
@@ -29,31 +45,27 @@ pub fn read_mem(
device.cart.latch
} else {
let masked_address = address as usize & CART_MASK;
u32::from_be_bytes(
device.cart.rom[masked_address..masked_address + 4]
.try_into()
.unwrap(),
)
read_cart_word(device, masked_address)
}
}
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
if device.cart.sc64.cfg[device::cart::sc64::SC64_ROM_WRITE_ENABLE as usize] != 0 {
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 = u32::from_be_bytes(
device.cart.rom[masked_address..masked_address + 4]
.try_into()
.unwrap(),
);
let mut data = read_cart_word(device, masked_address);
device::memory::masked_write_32(&mut data, value, mask);
device.cart.rom[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes());
for i in 0..4 {
device.ui.saves.romsave.0.insert(
(masked_address + i) as u32,
device.cart.rom[masked_address + i],
);
for (i, item) in data.to_be_bytes().iter().enumerate() {
device
.ui
.storage
.saves
.romsave
.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;
@@ -61,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(
@@ -74,25 +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.cart.rom[(cart_addr + i) as usize] =
device.rdram.mem[(dram_addr + i) as usize ^ device.byte_swap];
device
.ui
.saves
.romsave
.0
.insert(cart_addr + i, device.cart.rom[(cart_addr + i) as usize]);
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,22 +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 {
device.rdram.mem[i as usize ^ device.byte_swap] = device.cart.rom[j as usize];
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.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);
@@ -135,62 +151,41 @@ pub fn init(device: &mut device::Device, rom_file: Vec<u8>) {
}
}
pub fn load_rom_save(device: &mut device::Device) {
if device.ui.saves.romsave.0.is_empty() {
return;
}
for (key, value) in device.ui.saves.romsave.0.iter() {
device.cart.rom[*key as usize] = *value
}
}
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;
device.cart.cic_seed = 0x3F; // CicNus6102
println!("unknown IPL3 {}", hash)
}
}
+124 -30
View File
@@ -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
View File
@@ -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;
+42 -21
View File
@@ -1,7 +1,9 @@
use crate::{device, netplay, savestates, ui};
pub mod gbcart;
pub mod mempak;
pub mod rumble;
pub mod transferpak;
pub mod vru;
pub const JCMD_STATUS: u8 = 0x00;
@@ -24,6 +26,7 @@ pub enum PakType {
None = 0,
MemPak = 1,
RumblePak = 2,
TransferPak = 3,
}
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
@@ -64,8 +67,8 @@ pub fn process(device: &mut device::Device, channel: usize) {
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 =
@@ -74,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
)
}
}
@@ -159,29 +161,48 @@ fn data_crc(device: &device::Device, data_offset: usize, size: usize) -> u8 {
pub fn pak_switch_event(device: &mut device::Device) {
for (i, channel) in device.pif.channels.iter_mut().enumerate() {
if channel.change_pak != PakType::None {
if channel.change_pak == PakType::RumblePak {
if device.netplay.is_none() {
device::ui::input::set_rumble(&mut 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);
}
//stop rumble if it is on
if device.netplay.is_none() {
device::ui::input::set_rumble(&mut 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);
}
let handler = device::controller::PakHandler {
let new_pak_type = match channel.change_pak {
PakType::MemPak => PakType::RumblePak,
PakType::RumblePak => {
if device.transferpaks[i].cart.rom.is_empty() {
PakType::MemPak
} else {
PakType::TransferPak
}
}
PakType::TransferPak => PakType::MemPak,
_ => {
panic!("Invalid pak type");
}
};
if new_pak_type == PakType::MemPak {
channel.pak_handler = Some(device::controller::PakHandler {
read: device::controller::mempak::read,
write: device::controller::mempak::write,
pak_type: device::controller::PakType::MemPak,
};
channel.pak_handler = Some(handler);
ui::audio::play_pak_switch(&mut device.ui, PakType::MemPak);
} else if channel.change_pak == PakType::MemPak {
let handler = device::controller::PakHandler {
pak_type: new_pak_type,
});
} else if new_pak_type == PakType::RumblePak {
channel.pak_handler = Some(device::controller::PakHandler {
read: device::controller::rumble::read,
write: device::controller::rumble::write,
pak_type: device::controller::PakType::RumblePak,
};
channel.pak_handler = Some(handler);
ui::audio::play_pak_switch(&mut device.ui, PakType::RumblePak);
pak_type: new_pak_type,
});
} else if new_pak_type == PakType::TransferPak {
channel.pak_handler = Some(device::controller::PakHandler {
read: device::controller::transferpak::read,
write: device::controller::transferpak::write,
pak_type: new_pak_type,
});
}
ui::audio::play_pak_switch(&mut device.ui, new_pak_type);
channel.change_pak = PakType::None;
}
}
+328
View File
@@ -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: &mut [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: &mut device::controller::gbcart::GbCart,
address: u16,
data: usize,
size: usize,
) {
if address < 0x4000 {
let banked_address = address & 0x3FFF;
pif_ram[data..data + size]
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
} else if address < 0x8000 {
let banked_address = address as u32 - 0x4000 + (cart.rom_bank * 0x4000);
pif_ram[data..data + size]
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
} else if (0xa000..0xc000).contains(&address) {
if !cart.ram_enabled {
for i in 0..size {
pif_ram[data + i] = 0xff;
}
return;
}
if cart.ram_bank > 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: &mut [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: &mut device::controller::gbcart::GbCart,
address: u16,
data: usize,
size: usize,
) {
if address < 0x4000 {
let banked_address = address & 0x3FFF;
pif_ram[data..data + size]
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
} else if address < 0x8000 {
let banked_address = address as u32 - 0x4000 + (cart.rom_bank * 0x4000);
pif_ram[data..data + size]
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
} else if (0xa000..0xc000).contains(&address) {
if !cart.ram_enabled {
for i in 0..size {
pif_ram[data + i] = 0xff;
}
return;
}
if cart.ram_bank < 0x8 {
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: &mut [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: &mut device::controller::gbcart::GbCart,
address: u16,
data: usize,
size: usize,
) {
if address < 0x4000 {
let banked_address = address & 0x3FFF;
pif_ram[data..data + size]
.copy_from_slice(&cart.rom[banked_address as usize..banked_address as usize + size]);
} else if address < 0x8000 {
let banked_address = address as u32 - 0x4000 + (cart.rom_bank << 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: &mut device::controller::gbcart::GbCart,
address: u16,
data: usize,
size: usize,
) {
if !cart.enabled {
for i in 0..size {
pif_ram[data + i] = 0x00;
}
return;
}
match cart.cart_type {
CartType::MBC1RamBatt => read_mbc1(pif_ram, cart, address, data, size),
CartType::MBC3RamBatt => read_mbc3(pif_ram, cart, address, data, size),
CartType::MBC3RamBattRtc => read_mbc3(pif_ram, cart, address, data, size),
CartType::MBC5RamBatt => read_mbc5(pif_ram, cart, address, data, size),
_ => panic!("Unsupported cart type"),
}
}
pub fn write(
pif_ram: &mut [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),
}
}
+22 -13
View File
@@ -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
}
}
+133
View File
@@ -0,0 +1,133 @@
use crate::device;
#[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 read(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
let pak = &mut device.transferpaks[channel];
match address >> 12 {
0x8 => {
let value = if pak.enabled { 0x84 } else { 0x00 };
for i in 0..size {
device.pif.ram[data + i] = value;
}
return;
}
_ => {
if !pak.enabled {
for i in 0..size {
device.pif.ram[data + i] = 0x00;
}
return;
}
}
}
match address >> 12 {
0xB => {
let mut value = 0;
if pak.cart.enabled {
value |= 1 << 0;
}
value |= (pak.reset_state & 3) << 2;
if pak.enabled {
value |= 1 << 7;
}
if pak.cart.enabled && pak.reset_state == 3 {
pak.reset_state = 2
} else if !pak.cart.enabled && pak.reset_state == 2 {
pak.reset_state = 1
} else if !pak.cart.enabled && pak.reset_state == 1 {
pak.reset_state = 0
}
for i in 0..size {
device.pif.ram[data + i] = value;
}
}
0xC..=0xF => {
device::controller::gbcart::read(
&mut device.pif.ram,
&mut pak.cart,
0x4000 * pak.bank + (address & 0x7fff) - 0x4000,
data,
size,
);
}
_ => {
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(
&mut device.pif.ram,
&mut pak.cart,
0x4000 * pak.bank + (address & 0x7fff) - 0x4000,
data,
size,
);
}
_ => {
panic!("unknown transfer pak write {:x}", address >> 12);
}
}
}
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+8
View File
@@ -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,
+1 -5
View File
@@ -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);
}
}
+4 -3
View File
@@ -21,9 +21,10 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
let length = (value & mask) as u64;
let data = std::str::from_utf8(
&device.cart.is_viewer_buffer[0x20_usize..(0x20 + length) as usize],
)
.unwrap();
print!("{}", data);
);
if data.is_ok() {
print!("{}", data.unwrap());
}
} else {
let mut data = u32::from_be_bytes(
device.cart.is_viewer_buffer[masked_address..masked_address + 4]
+7 -4
View File
@@ -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;
+11 -12
View File
@@ -1,4 +1,5 @@
use crate::device;
use crate::{device, ui};
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] =
@@ -86,6 +83,7 @@ fn dma_write(device: &mut device::Device) {
let cart_addr = device.pi.regs[PI_CART_ADDR_REG as usize] & !1;
let dram_addr = device.pi.regs[PI_DRAM_ADDR_REG as usize] & 0xFFFFFE;
let mut length = (device.pi.regs[PI_WR_LEN_REG as usize] & 0xFFFFFF) + 1;
ui::video::check_framebuffers(dram_addr);
/* PI seems to treat the first 128 bytes differently, see https://n64brew.dev/wiki/Peripheral_Interface#Unaligned_DMA_transfer */
if length >= 0x7f && (length & 1) != 0 {
@@ -97,11 +95,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 +166,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 +193,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
}
+12 -7
View File
@@ -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
}
@@ -250,13 +246,22 @@ pub fn init(device: &mut device::Device) {
write: device::controller::mempak::write,
pak_type: device::controller::PakType::MemPak,
};
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);
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(mempak_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);
+6 -6
View File
@@ -53,7 +53,9 @@ pub fn read_regs_dpc(
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
device::cop0::add_cycles(device, 20);
if !device.rsp.cpu.running {
device::cop0::add_cycles(device, 20);
}
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
DPC_STATUS_REG => {
@@ -116,6 +118,7 @@ fn run_rdp(device: &mut device::Device) {
let timer = ui::video::process_rdp_list();
device.rdp.regs_dpc[DPC_STATUS_REG as usize] |=
DPC_STATUS_START_GCLK | DPC_STATUS_PIPE_BUSY | DPC_STATUS_CMD_BUSY;
device.rdp.regs_dpc[DPC_PIPEBUSY_REG as usize] = 0xFFFFFF;
if timer != 0 {
if device.save_state {
@@ -127,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)
}
}
@@ -203,6 +202,7 @@ fn update_dpc_status(device: &mut device::Device, w: u32) {
pub fn init(device: &mut device::Device) {
device.rdp.regs_dpc[DPC_STATUS_REG as usize] |=
DPC_STATUS_START_GCLK | DPC_STATUS_PIPE_BUSY | DPC_STATUS_CBUF_READY;
device.rdp.regs_dpc[DPC_PIPEBUSY_REG as usize] = 0xFFFFFF;
}
pub fn rdp_interrupt_event(device: &mut device::Device) {
+7 -5
View File
@@ -1,5 +1,6 @@
use crate::device;
use std::alloc::{alloc_zeroed, Layout};
use crate::ui;
use std::alloc::{Layout, alloc_zeroed};
//const RDRAM_CONFIG_REG: u32 = 0;
//const RDRAM_DEVICE_ID_REG: u32 = 1;
@@ -46,6 +47,7 @@ pub fn read_mem(
);
let masked_address = address as usize & RDRAM_MASK;
if address < device.rdram.size as u64 {
ui::video::check_framebuffers(masked_address as u32);
u32::from_ne_bytes(
device.rdram.mem[masked_address..masked_address + 4]
.try_into()
@@ -58,6 +60,7 @@ pub fn read_mem(
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
if address < device.rdram.size as u64 {
ui::video::check_framebuffers(address as u32);
let mut data = u32::from_ne_bytes(
device.rdram.mem[address as usize..(address + 4) as usize]
.try_into()
@@ -103,13 +106,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
View File
@@ -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);
}
+67 -64
View File
@@ -34,6 +34,7 @@ pub struct Cpu {
pub pipeline_full: bool,
pub branch_state: BranchState,
pub broken: bool,
pub running: bool,
pub halted: bool,
pub sync_point: bool,
pub cycle_counter: u64,
@@ -125,6 +126,7 @@ pub fn in_delay_slot_taken(device: &device::Device) -> bool {
pub fn run(device: &mut device::Device) -> u64 {
device.rsp.cpu.broken = false;
device.rsp.cpu.cycle_counter = 0;
device.rsp.cpu.running = true;
while !device.rsp.cpu.sync_point {
device.rsp.cpu.instruction_type = InstructionType::Su;
device.rsp.cpu.gpr[0] = 0; // gpr 0 is read only
@@ -186,6 +188,7 @@ pub fn run(device: &mut device::Device) -> u64 {
}
}
}
device.rsp.cpu.running = false;
(device.rsp.cpu.cycle_counter as f64 * 1.5) as u64 // converting RCP clock to CPU clock
}
@@ -288,70 +291,70 @@ pub fn map_instructions(device: &mut device::Device) {
];
device.rsp.cpu.special_instrs = [
device::rsp_su_instructions::sll, // 0
device::rsp_su_instructions::reserved, // 1
device::rsp_su_instructions::srl, // 2
device::rsp_su_instructions::sra, // 3
device::rsp_su_instructions::sllv, // 4
device::rsp_su_instructions::reserved, // 5
device::rsp_su_instructions::srlv, // 6
device::rsp_su_instructions::srav, // 7
device::rsp_su_instructions::jr, // 8
device::rsp_su_instructions::jalr, // 9
device::rsp_su_instructions::reserved, // 10
device::rsp_su_instructions::reserved, // 11
device::rsp_su_instructions::reserved, // 12
device::rsp_su_instructions::break_, // 13
device::rsp_su_instructions::reserved, // 14
device::rsp_su_instructions::reserved, // 15
device::rsp_su_instructions::reserved, // 16
device::rsp_su_instructions::reserved, // 17
device::rsp_su_instructions::reserved, // 18
device::rsp_su_instructions::reserved, // 19
device::rsp_su_instructions::reserved, // 20
device::rsp_su_instructions::reserved, // 21
device::rsp_su_instructions::reserved, // 22
device::rsp_su_instructions::reserved, // 23
device::rsp_su_instructions::reserved, // 24
device::rsp_su_instructions::reserved, // 25
device::rsp_su_instructions::reserved, // 26
device::rsp_su_instructions::reserved, // 27
device::rsp_su_instructions::reserved, // 28
device::rsp_su_instructions::reserved, // 29
device::rsp_su_instructions::reserved, // 30
device::rsp_su_instructions::reserved, // 31
device::rsp_su_instructions::add, // 32
device::rsp_su_instructions::addu, // 33
device::rsp_su_instructions::sub, // 34
device::rsp_su_instructions::subu, // 35
device::rsp_su_instructions::and, // 36
device::rsp_su_instructions::or, // 37
device::rsp_su_instructions::xor, // 38
device::rsp_su_instructions::nor, // 39
device::rsp_su_instructions::reserved, // 40
device::rsp_su_instructions::reserved, // 41
device::rsp_su_instructions::slt, // 42
device::rsp_su_instructions::sltu, // 43
device::rsp_su_instructions::reserved, // 44
device::rsp_su_instructions::reserved, // 45
device::rsp_su_instructions::reserved, // 46
device::rsp_su_instructions::reserved, // 47
device::rsp_su_instructions::reserved, // 48
device::rsp_su_instructions::reserved, // 49
device::rsp_su_instructions::reserved, // 50
device::rsp_su_instructions::reserved, // 51
device::rsp_su_instructions::reserved, // 52
device::rsp_su_instructions::reserved, // 53
device::rsp_su_instructions::reserved, // 54
device::rsp_su_instructions::reserved, // 55
device::rsp_su_instructions::reserved, // 56
device::rsp_su_instructions::reserved, // 57
device::rsp_su_instructions::reserved, // 58
device::rsp_su_instructions::reserved, // 59
device::rsp_su_instructions::reserved, // 60
device::rsp_su_instructions::reserved, // 61
device::rsp_su_instructions::reserved, // 62
device::rsp_su_instructions::reserved, // 63
device::rsp_su_instructions::sll, // 0
device::rsp_su_instructions::special_reserved, // 1
device::rsp_su_instructions::srl, // 2
device::rsp_su_instructions::sra, // 3
device::rsp_su_instructions::sllv, // 4
device::rsp_su_instructions::special_reserved, // 5
device::rsp_su_instructions::srlv, // 6
device::rsp_su_instructions::srav, // 7
device::rsp_su_instructions::jr, // 8
device::rsp_su_instructions::jalr, // 9
device::rsp_su_instructions::special_reserved, // 10
device::rsp_su_instructions::special_reserved, // 11
device::rsp_su_instructions::special_reserved, // 12
device::rsp_su_instructions::break_, // 13
device::rsp_su_instructions::special_reserved, // 14
device::rsp_su_instructions::special_reserved, // 15
device::rsp_su_instructions::special_reserved, // 16
device::rsp_su_instructions::special_reserved, // 17
device::rsp_su_instructions::special_reserved, // 18
device::rsp_su_instructions::special_reserved, // 19
device::rsp_su_instructions::special_reserved, // 20
device::rsp_su_instructions::special_reserved, // 21
device::rsp_su_instructions::special_reserved, // 22
device::rsp_su_instructions::special_reserved, // 23
device::rsp_su_instructions::special_reserved, // 24
device::rsp_su_instructions::special_reserved, // 25
device::rsp_su_instructions::special_reserved, // 26
device::rsp_su_instructions::special_reserved, // 27
device::rsp_su_instructions::special_reserved, // 28
device::rsp_su_instructions::special_reserved, // 29
device::rsp_su_instructions::special_reserved, // 30
device::rsp_su_instructions::special_reserved, // 31
device::rsp_su_instructions::add, // 32
device::rsp_su_instructions::addu, // 33
device::rsp_su_instructions::sub, // 34
device::rsp_su_instructions::subu, // 35
device::rsp_su_instructions::and, // 36
device::rsp_su_instructions::or, // 37
device::rsp_su_instructions::xor, // 38
device::rsp_su_instructions::nor, // 39
device::rsp_su_instructions::special_reserved, // 40
device::rsp_su_instructions::special_reserved, // 41
device::rsp_su_instructions::slt, // 42
device::rsp_su_instructions::sltu, // 43
device::rsp_su_instructions::special_reserved, // 44
device::rsp_su_instructions::special_reserved, // 45
device::rsp_su_instructions::special_reserved, // 46
device::rsp_su_instructions::special_reserved, // 47
device::rsp_su_instructions::special_reserved, // 48
device::rsp_su_instructions::special_reserved, // 49
device::rsp_su_instructions::special_reserved, // 50
device::rsp_su_instructions::special_reserved, // 51
device::rsp_su_instructions::special_reserved, // 52
device::rsp_su_instructions::special_reserved, // 53
device::rsp_su_instructions::special_reserved, // 54
device::rsp_su_instructions::special_reserved, // 55
device::rsp_su_instructions::special_reserved, // 56
device::rsp_su_instructions::special_reserved, // 57
device::rsp_su_instructions::special_reserved, // 58
device::rsp_su_instructions::special_reserved, // 59
device::rsp_su_instructions::special_reserved, // 60
device::rsp_su_instructions::special_reserved, // 61
device::rsp_su_instructions::special_reserved, // 62
device::rsp_su_instructions::special_reserved, // 63
];
device.rsp.cpu.regimm_instrs = [
+7 -10
View File
@@ -1,4 +1,5 @@
use crate::device;
use crate::ui;
const SP_MEM_ADDR_REG: u32 = 0;
const SP_DRAM_ADDR_REG: u32 = 1;
@@ -146,6 +147,7 @@ fn do_dma(device: &mut device::Device, dma: RspDma) {
let mut dram_addr = dma.dramaddr & 0xfffff8;
let offset = dma.memaddr & 0x1000;
ui::video::check_framebuffers(dram_addr);
if dma.dir == DmaDir::Read {
let mut j = 0;
while j < count {
@@ -204,9 +206,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,
);
}
@@ -267,7 +267,9 @@ pub fn read_regs(
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
device::cop0::add_cycles(device, 20);
if !device.rsp.cpu.running {
device::cop0::add_cycles(device, 20);
}
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
SP_DMA_BUSY_REG | SP_DMA_FULL_REG => {
@@ -292,7 +294,6 @@ pub fn read_regs(
let value = device.rsp.regs[reg as usize];
if value == 1 {
device.rsp.cpu.sync_point = true;
device.rsp.cpu.cycle_counter += 4; // needed for DK64
}
device.rsp.regs[reg as usize] = 1;
value
@@ -464,11 +465,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)
}
}
+6 -2
View File
@@ -552,6 +552,7 @@ pub fn bgezal(device: &mut device::Device, opcode: u32) {
}
pub fn mfc0(device: &mut device::Device, opcode: u32) {
device.rsp.cpu.cycle_counter += 2;
if rd(opcode) < device::rsp_interface::SP_REGS_COUNT {
device.rsp.cpu.gpr[rt(opcode) as usize] = device::rsp_interface::read_regs(
device,
@@ -1204,6 +1205,9 @@ pub fn stv(device: &mut device::Device, opcode: u32) {
}
}
pub fn reserved(_device: &mut device::Device, _opcode: u32) {
panic!("rsp su reserved")
pub fn special_reserved(device: &mut device::Device, opcode: u32) {
device.rsp.cpu.gpr[rd(opcode) as usize] =
device.rsp.cpu.gpr[rs(opcode) as usize] >> (device.rsp.cpu.gpr[rs(opcode) as usize] & 31)
}
pub fn reserved(_device: &mut device::Device, _opcode: u32) {}
+12 -10
View File
@@ -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) {
+66 -35
View File
@@ -2,7 +2,7 @@ use crate::{device, netplay, ui};
use governor::clock::Clock;
const VI_STATUS_REG: u32 = 0;
//const VI_ORIGIN_REG: u32 = 1;
const VI_ORIGIN_REG: u32 = 1;
//const VI_WIDTH_REG: u32 = 2;
//const VI_V_INTR_REG: u32 = 3;
const VI_CURRENT_REG: u32 = 4;
@@ -26,36 +26,46 @@ 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,
pub min_wait_time: std::time::Duration,
pub frame_time: f64,
pub limit_freq: u64,
#[serde(skip)]
#[serde(default = "std::time::Instant::now")]
pub limit_freq_check: std::time::Instant,
}
//static mut FRAME_COUNTER: u64 = 0;
const LIMIT_BUFFER: u64 = 3;
const MAX_LIMIT_FREQ: u64 = 3;
pub fn set_expected_refresh_rate(device: &mut device::Device) {
let expected_refresh_rate = device.vi.clock as f64
/ (device.vi.regs[VI_V_SYNC_REG as usize] + 1) as f64
/ ((device.vi.regs[VI_H_SYNC_REG as usize] & 0xFFF) + 1) as f64
* 2.0;
device.vi.frame_time = 1.0 / expected_refresh_rate;
device.vi.delay = (device.cpu.clock_rate as f64 / expected_refresh_rate) as u64;
device.vi.count_per_scanline =
device.vi.delay / (device.vi.regs[VI_V_SYNC_REG as usize] + 1) as u64;
create_limiter(device);
}
fn create_limiter(device: &mut device::Device) {
let quota = governor::Quota::with_period(std::time::Duration::from_secs_f64(
(1.0 / expected_refresh_rate) * LIMIT_BUFFER as f64,
device.vi.frame_time * device.vi.limit_freq as f64,
))
.unwrap();
device.vi.limiter = Some(governor::RateLimiter::direct(quota))
device.vi.limiter = Some(governor::RateLimiter::direct(quota));
let _ = device.vi.limiter.as_ref().unwrap().check();
//println!("new limit freq: {}", device.vi.limit_freq);
}
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)
}
}
@@ -109,6 +119,13 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
set_expected_refresh_rate(device);
}
}
VI_ORIGIN_REG => {
device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask);
if device.vi.regs[reg as usize] != device.vi.last_origin {
device.vi.last_origin = device.vi.regs[reg as usize];
device.vi.internal_frame_counter += 1;
}
}
_ => {
device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask);
}
@@ -117,32 +134,33 @@ 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::update_screen();
ui::video::check_callback(device);
/*
unsafe {
FRAME_COUNTER += 1;
}
*/
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 % LIMIT_BUFFER == 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();
/*
let vis = if device.cart.pal { 50 } else { 60 };
if device.vi.vi_counter % vis == 0 {
println!("FPS: {}", device.vi.internal_frame_counter);
device.vi.internal_frame_counter = 0;
}
*/
/* toggle vi field if in interlaced mode */
device.vi.field ^= (device.vi.regs[VI_STATUS_REG as usize] >> 6) & 0x1;
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,
@@ -155,27 +173,40 @@ pub fn init(device: &mut device::Device) {
} else {
device.vi.clock = 48681812
}
/*
std::thread::spawn(move || {
let mut last_count = 0;
loop {
unsafe {
println!("{:?}", FRAME_COUNTER - last_count);
last_count = FRAME_COUNTER;
}
std::thread::sleep(std::time::Duration::from_secs(1));
}
});
*/
}
fn speed_limiter(device: &device::Device) {
fn speed_limiter(device: &mut device::Device) {
let result = device.vi.limiter.as_ref().unwrap().check();
if result.is_err() {
let outcome = result.unwrap_err();
let dur = outcome.wait_time_from(governor::clock::DefaultClock::default().now());
spin_sleep::sleep(dur);
if dur < device.vi.min_wait_time {
device.vi.min_wait_time = dur;
}
let _ = device.vi.limiter.as_ref().unwrap().check();
} else {
device.vi.min_wait_time = std::time::Duration::from_secs(0);
}
if std::time::Instant::now()
.duration_since(device.vi.limit_freq_check)
.as_secs_f64()
> 1.0
{
if device.vi.min_wait_time.as_secs_f64() == 0.0 && device.vi.limit_freq < MAX_LIMIT_FREQ {
device.vi.limit_freq += 1;
create_limiter(device);
} else if device.vi.min_wait_time.as_secs_f64() > device.vi.frame_time
&& device.vi.limit_freq > 1
{
device.vi.limit_freq -= 1;
create_limiter(device);
}
//println!("limit freq: {}", device.vi.limit_freq);
device.vi.min_wait_time = std::time::Duration::from_secs(1);
device.vi.limit_freq_check = std::time::Instant::now();
}
}
+15 -3
View File
@@ -161,16 +161,28 @@ async fn main() {
.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);
let overclock = device.ui.config.emulation.overclock;
device::run_game(&mut device, rom_contents, args.fullscreen, overclock);
})
.unwrap();
handle.join().unwrap();
} else {
let options = eframe::NativeOptions {
wgpu_options: eframe::egui_wgpu::WgpuConfiguration {
wgpu_setup: eframe::egui_wgpu::WgpuSetup::CreateNew(
eframe::egui_wgpu::WgpuSetupCreateNew {
instance_descriptor: eframe::egui_wgpu::wgpu::InstanceDescriptor {
backends: eframe::wgpu::Backends::GL,
..Default::default()
},
..Default::default()
},
),
..Default::default()
},
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
View File
@@ -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, &mut 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(&mut 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: &mut 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,
+33 -6
View File
@@ -70,22 +70,31 @@ where
}
pub fn create_savestate(device: &device::Device) {
ui::video::full_sync();
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",
),
];
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(&mut 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();
device.ui.saves = postcard::from_bytes(&save_bytes).unwrap();
device.ui.storage.saves = postcard::from_bytes(&save_bytes).unwrap();
device.cpu = state.cpu;
device.pif = state.pif;
@@ -106,6 +115,15 @@ pub fn load_savestate(device: &mut device::Device) {
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);
@@ -164,6 +182,15 @@ pub fn load_savestate(device: &mut device::Device) {
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);
}
}
}
@@ -189,8 +216,8 @@ where
[device::cop0::reserved; N]
}
pub fn default_memory_read(
) -> [fn(&mut device::Device, u64, device::memory::AccessSize) -> u32; 0x2000] {
pub fn default_memory_read()
-> [fn(&mut device::Device, u64, device::memory::AccessSize) -> u32; 0x2000] {
[device::rdram::read_mem; 0x2000]
}
+113 -60
View File
@@ -12,32 +12,48 @@ 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 struct Input {
pub num_joysticks: i32,
pub joysticks: *mut 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::stdinc::SDL_free(self.input.joysticks as *mut std::ffi::c_void);
sdl3_sys::init::SDL_Quit();
}
}
@@ -85,61 +101,98 @@ impl 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(),
num_joysticks,
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,
},
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(),
},
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,
}
+83 -33
View File
@@ -1,41 +1,58 @@
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_BindAudioStream(ui.audio.audio_device, ui.audio.audio_stream)
} {
panic!("Could not bind audio stream");
}
@@ -45,43 +62,76 @@ 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_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_netplay_audio(ui: &mut 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.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_pak_switch(ui: &mut ui::Ui, pak: device::controller::PakType) {
if ui.pak_audio_stream.is_null() {
if ui.audio.event_audio_stream.is_null() {
return;
}
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 {
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.pak_audio_stream,
ui.audio.event_audio_stream,
sound.as_ptr() as *const std::ffi::c_void,
sound.len() as i32,
)
@@ -91,7 +141,7 @@ 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() {
if device.ui.audio.audio_stream.is_null() {
return;
}
@@ -109,7 +159,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;
@@ -117,7 +167,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,
)
@@ -129,7 +179,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
View File
@@ -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 },
}
}
}
+243 -85
View File
@@ -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,10 +61,10 @@ 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 offset in 0..game_ui.input.num_joysticks as isize {
let name = unsafe {
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickNameForID(
*(game_ui.joysticks.offset(offset)),
*(game_ui.input.joysticks.offset(offset)),
))
};
controllers.push(name.to_string_lossy().to_string());
@@ -62,10 +76,10 @@ 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 offset in 0..game_ui.input.num_joysticks as isize {
let path = unsafe {
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(
*(game_ui.joysticks.offset(offset)),
*(game_ui.input.joysticks.offset(offset)),
))
.to_string_lossy()
.to_string()
@@ -104,16 +118,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 +156,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 +174,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(
@@ -199,56 +227,90 @@ fn configure_profile(app: &mut GopherEguiApp, ctx: &egui::Context) {
}
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 +329,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 +339,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 +350,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 +400,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,13 +429,20 @@ 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);
@@ -353,8 +453,12 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) {
if rom_contents.is_empty() {
println!("Could not read rom file");
} else {
device.ui.fullscreen = fullscreen;
device::run_game(rom_contents, &mut device);
device::run_game(
&mut device,
rom_contents,
fullscreen,
enable_overclock,
);
}
}
let result = std::fs::remove_file(running_file);
@@ -369,6 +473,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 +490,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 +506,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 +514,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 +542,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 +606,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 +658,7 @@ impl eframe::App for GopherEguiApp {
}
}
if self.show_vru_dialog {
show_vru_dialog(self, ctx);
}
get_latest_version(self, ctx);
}
}
+124 -57
View File
@@ -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: Some(2),
}),
};
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,7 +420,10 @@ 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();
}
@@ -397,16 +435,21 @@ async fn parse_rom_file(file: rfd::FileHandle, tx: tokio::sync::mpsc::Sender<Gam
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 +485,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 +505,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 +521,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 +546,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 +631,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 +659,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 +703,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 +743,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 +776,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 +826,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 {
+358 -201
View File
@@ -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;
@@ -272,11 +277,11 @@ fn set_buttons_from_controller(
}
pub fn set_rumble(ui: &mut ui::Ui, channel: usize, rumble: u8) {
if !ui.controllers[channel].rumble {
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,35 +315,50 @@ 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: &mut 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.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)
};
for i in 0..14 {
if profile_name != "default" || channel == 0 {
let profile_key = profile.keys[i];
if profile_key.0 {
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;
}
}
@@ -353,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() {
@@ -366,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.num_joysticks {
let path = unsafe {
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(
*(ui.joysticks.offset(controller as isize)),
*(ui.input.joysticks.offset(controller as isize)),
))
.to_string_lossy()
.to_string()
@@ -428,16 +448,16 @@ 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 offset in 0..ui.input.num_joysticks as isize {
if !dinput {
let controller =
unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*ui.joysticks.offset(offset)) };
unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*ui.input.joysticks.offset(offset)) };
if !controller.is_null() {
open_controllers.push(controller);
}
} else {
let joystick =
unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*ui.joysticks.offset(offset)) };
unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*ui.input.joysticks.offset(offset)) };
if !joystick.is_null() {
open_joysticks.push(joystick);
}
@@ -487,14 +507,39 @@ 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 = (false, 0, 0);
let mut last_joystick_axis_result = ui::config::InputControllerAxis {
enabled: false,
id: 0,
axis: 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
@@ -514,19 +559,34 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
close_controllers(open_joysticks, open_controllers);
return;
} else if event_type == u32::from(sdl3_sys::events::SDL_EVENT_KEY_DOWN) {
new_keys[*value] = (true, i32::from(unsafe { event.key.scancode }));
key_set = true
if unsafe {
!event.key.repeat
&& event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_LALT
&& event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_RALT
} {
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;
@@ -536,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)
@@ -552,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;
@@ -584,141 +654,228 @@ 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() {
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 offset in 0..ui.input.num_joysticks as isize {
let path = unsafe {
std::ffi::CStr::from_ptr(sdl3_sys::joystick::SDL_GetJoystickPathForID(
*(ui.joysticks.offset(offset)),
*(ui.input.joysticks.offset(offset)),
))
.to_string_lossy()
.to_string()
};
if path == *assigned_path && !taken[offset as usize] {
joystick_id = unsafe { *(ui.joysticks.offset(offset)) };
joystick_id = unsafe { *(ui.input.joysticks.offset(offset)) };
taken[offset as usize] = true;
break;
}
@@ -733,13 +890,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,
@@ -752,13 +909,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,
@@ -775,7 +932,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
View File
@@ -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(&mut 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(&mut 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(&mut 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(&mut 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(&mut 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(&mut 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);
+51 -19
View File
@@ -7,22 +7,41 @@ pub fn init(device: &mut device::Device) {
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;
}
device.ui.window =
unsafe { sdl3_sys::video::SDL_CreateWindow(title.as_ptr(), 640, 480, flags) };
if device.ui.window.is_null() {
let window_width;
let window_height;
if device.cart.pal {
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.video.window = unsafe {
sdl3_sys::video::SDL_CreateWindow(title.as_ptr(), window_width, window_height, flags)
};
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() };
let gfx_info = GFX_INFO {
RDRAM: device.rdram.mem.as_mut_ptr(),
@@ -32,23 +51,21 @@ pub fn init(device: &mut device::Device) {
DPC_START_REG: &mut device.rdp.regs_dpc[device::rdp::DPC_START_REG as usize],
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);
}
}
@@ -57,6 +74,14 @@ pub fn update_screen() {
unsafe { rdp_update_screen() }
}
pub fn check_framebuffers(address: u32) {
unsafe { rdp_check_framebuffers(address) }
}
pub fn full_sync() {
unsafe { rdp_full_sync() }
}
pub fn load_state(device: &mut device::Device) {
let gfx_info = GFX_INFO {
RDRAM: device.rdram.mem.as_mut_ptr(),
@@ -66,9 +91,15 @@ pub fn load_state(device: &mut device::Device) {
DPC_START_REG: &mut device.rdp.regs_dpc[device::rdp::DPC_START_REG as usize],
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);
}
}
@@ -81,6 +112,7 @@ 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;
}
}