mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 09:35:21 +02:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b065f807a2 | |||
| ee95816385 | |||
| d021cb5d32 | |||
| 9819fad475 | |||
| dd767047fd | |||
| f6480ea312 | |||
| e4174c7d4b | |||
| f3d8080a26 | |||
| b581bdcd40 | |||
| 842c81988f | |||
| 45d05cecf0 | |||
| eae81c4623 | |||
| b3f8688ba5 | |||
| 0a10b2c0ba | |||
| be67cb264c | |||
| c6c2187652 | |||
| b2c1d88ea4 | |||
| 80176766a7 | |||
| 7ac6f2adb1 | |||
| 643338e086 | |||
| d9a08f112d | |||
| 344f41cf47 | |||
| 9978e28cc3 |
+35
-11
@@ -13,6 +13,7 @@ jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu]
|
||||
steps:
|
||||
@@ -30,43 +31,64 @@ jobs:
|
||||
Types: deb
|
||||
URIs: http://archive.ubuntu.com/ubuntu/
|
||||
Suites: noble
|
||||
Components: main restricted universe
|
||||
Components: main multiverse universe
|
||||
Architectures: amd64
|
||||
|
||||
Types: deb
|
||||
URIs: http://security.ubuntu.com/ubuntu/
|
||||
Suites: noble-security
|
||||
Components: main restricted universe
|
||||
Components: main multiverse universe
|
||||
Architectures: amd64
|
||||
|
||||
Types: deb
|
||||
URIs: http://archive.ubuntu.com/ubuntu/
|
||||
Suites: noble-updates
|
||||
Components: main restricted universe
|
||||
Components: main multiverse universe
|
||||
Architectures: amd64
|
||||
|
||||
Types: deb
|
||||
URIs: http://azure.ports.ubuntu.com/ubuntu-ports/
|
||||
URIs: http://archive.ubuntu.com/ubuntu/
|
||||
Suites: noble-backports
|
||||
Components: main multiverse universe
|
||||
Architectures: amd64
|
||||
|
||||
Types: deb
|
||||
URIs: http://ports.ubuntu.com/
|
||||
Suites: noble
|
||||
Components: main restricted multiverse universe
|
||||
Components: main multiverse universe
|
||||
Architectures: arm64
|
||||
|
||||
Types: deb
|
||||
URIs: http://azure.ports.ubuntu.com/ubuntu-ports/
|
||||
URIs: http://ports.ubuntu.com/
|
||||
Suites: noble-security
|
||||
Components: main multiverse universe
|
||||
Architectures: arm64
|
||||
|
||||
Types: deb
|
||||
URIs: http://ports.ubuntu.com/
|
||||
Suites: noble-updates
|
||||
Components: main restricted multiverse universe
|
||||
Components: main multiverse universe
|
||||
Architectures: arm64
|
||||
|
||||
Types: deb
|
||||
URIs: http://ports.ubuntu.com/
|
||||
Suites: noble-backports
|
||||
Components: main multiverse universe
|
||||
Architectures: arm64
|
||||
EOF
|
||||
|
||||
sudo mv deb822sources /etc/apt/sources.list.d/ubuntu.sources
|
||||
sudo dpkg --add-architecture arm64
|
||||
sudo apt update
|
||||
sudo apt -y install libsdl2-dev:arm64 g++-aarch64-linux-gnu
|
||||
sudo apt-get update
|
||||
sudo apt-get -y dist-upgrade
|
||||
sudo apt-get -y install aptitude
|
||||
sudo aptitude -y install libsdl2-dev:arm64 g++-aarch64-linux-gnu
|
||||
- name: Install SDL2
|
||||
if: matrix.target == 'x86_64-unknown-linux-gnu'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt -y install libsdl2-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get -y dist-upgrade
|
||||
sudo apt-get -y install libsdl2-dev
|
||||
- name: Set aarch64 linker
|
||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||
run: echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
@@ -82,6 +104,7 @@ jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [x86_64-pc-windows-msvc]
|
||||
steps:
|
||||
@@ -104,6 +127,7 @@ jobs:
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [x86_64-apple-darwin, aarch64-apple-darwin]
|
||||
steps:
|
||||
|
||||
+3
-1
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "gopher64"
|
||||
version = "0.1.17"
|
||||
version = "0.1.19"
|
||||
edition = "2021"
|
||||
rust-version = "1.82"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -21,6 +22,7 @@ futures = "0.3"
|
||||
sdl2-sys = "0.37"
|
||||
spin_sleep = "1.3"
|
||||
encoding_rs = "0.8"
|
||||
mimalloc = "0.1"
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
|
||||
[dependencies.sdl2]
|
||||
|
||||
@@ -24,6 +24,10 @@ Options:
|
||||
-V, --version
|
||||
Print version
|
||||
```
|
||||
## discord
|
||||
|
||||
There is no dedicated server at this point, but there is a #gopher64 channel on the simple64 server: https://discord.gg/R2cz7S94vD
|
||||
|
||||
## goals
|
||||
|
||||
1. Performance. I want to be able to use this emulator on my laptop.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+36
-23
@@ -51,6 +51,7 @@ enum vi_registers
|
||||
};
|
||||
|
||||
static bool fullscreen;
|
||||
static bool integer_scaling;
|
||||
static SDL_Window *window;
|
||||
static RDP::CommandProcessor *processor;
|
||||
static SDL_WSIPlatform *wsi_platform;
|
||||
@@ -142,7 +143,10 @@ int sdl_event_filter(void *userdata, SDL_Event *event)
|
||||
emu_running = false;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
wsi_platform->do_resize();
|
||||
if (emu_running)
|
||||
{
|
||||
wsi_platform->do_resize();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -163,13 +167,14 @@ int sdl_event_filter(void *userdata, SDL_Event *event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _fullscreen, bool _upscale)
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_scaling, bool _fullscreen)
|
||||
{
|
||||
window = (SDL_Window *)_window;
|
||||
SDL_SetEventFilter(sdl_event_filter, nullptr);
|
||||
SDL_AddEventWatch(sdl_event_filter, nullptr);
|
||||
|
||||
gfx_info = _gfx_info;
|
||||
fullscreen = _fullscreen;
|
||||
integer_scaling = _integer_scaling;
|
||||
bool window_vsync = 0;
|
||||
wsi = new WSI;
|
||||
wsi_platform = new SDL_WSIPlatform;
|
||||
@@ -231,34 +236,42 @@ void rdp_close()
|
||||
|
||||
static void calculate_viewport(float *x, float *y, float *width, float *height)
|
||||
{
|
||||
bool window_widescreen = false;
|
||||
int32_t display_width = (window_widescreen ? 854 : 640);
|
||||
int32_t display_height = 480;
|
||||
const int32_t display_width = 320;
|
||||
const int32_t display_height = 240;
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
|
||||
*width = w;
|
||||
*height = h;
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
int32_t hw = display_height * *width;
|
||||
int32_t wh = display_width * *height;
|
||||
if (integer_scaling)
|
||||
{
|
||||
// Integer scaling path
|
||||
int scale_x = w / display_width;
|
||||
int scale_y = h / display_height;
|
||||
int scale = (scale_x < scale_y) ? scale_x : scale_y;
|
||||
if (scale < 1)
|
||||
scale = 1;
|
||||
|
||||
// add letterboxes or pillarboxes if the window has a different aspect ratio
|
||||
// than the current display mode
|
||||
if (hw > wh)
|
||||
{
|
||||
int32_t w_max = wh / display_height;
|
||||
*x += (*width - w_max) / 2;
|
||||
*width = w_max;
|
||||
// Calculate scaled dimensions
|
||||
int scaled_width = display_width * scale;
|
||||
int scaled_height = display_height * scale;
|
||||
|
||||
*width = scaled_width;
|
||||
*height = scaled_height;
|
||||
}
|
||||
else if (hw < wh)
|
||||
else
|
||||
{
|
||||
int32_t h_max = hw / display_width;
|
||||
*y += (*height - h_max) / 2;
|
||||
*height = h_max;
|
||||
// Regular scaling path - maintain aspect ratio
|
||||
float scale_x = w / (float)display_width;
|
||||
float scale_y = h / (float)display_height;
|
||||
float scale = (scale_x < scale_y) ? scale_x : scale_y;
|
||||
|
||||
*width = display_width * scale;
|
||||
*height = display_height * scale;
|
||||
}
|
||||
|
||||
// Center the viewport
|
||||
*x = (w - *width) / 2.0f;
|
||||
*y = (h - *height) / 2.0f;
|
||||
}
|
||||
|
||||
static void render_frame(Vulkan::Device &device)
|
||||
|
||||
@@ -23,7 +23,7 @@ extern "C"
|
||||
uint32_t *VI_WIDTH_REG;
|
||||
} GFX_INFO;
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, bool fullscreen, bool _upscale);
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_scaling, bool _fullscreen);
|
||||
void rdp_close();
|
||||
void rdp_set_vi_register(uint32_t reg, uint32_t value);
|
||||
bool rdp_update_screen();
|
||||
|
||||
+4
-5
@@ -30,7 +30,6 @@ pub mod exceptions;
|
||||
pub mod fpu_instructions;
|
||||
pub mod is_viewer;
|
||||
pub mod memory;
|
||||
pub mod mempak;
|
||||
pub mod mi;
|
||||
pub mod pi;
|
||||
pub mod pif;
|
||||
@@ -46,7 +45,6 @@ pub mod sram;
|
||||
pub mod tlb;
|
||||
pub mod unmapped;
|
||||
pub mod vi;
|
||||
pub mod vru;
|
||||
|
||||
pub fn run_game(
|
||||
file_path: &std::path::Path,
|
||||
@@ -68,7 +66,7 @@ pub fn run_game(
|
||||
mi::init(device);
|
||||
pif::init(device);
|
||||
if device.ui.config.input.emulate_vru {
|
||||
vru::init(device);
|
||||
controller::vru::init(device);
|
||||
}
|
||||
memory::init(device);
|
||||
rsp_interface::init(device);
|
||||
@@ -179,7 +177,7 @@ pub struct Device {
|
||||
si: si::Si,
|
||||
ri: ri::Ri,
|
||||
flashram: sram::Flashram,
|
||||
pub vru: vru::Vru,
|
||||
pub vru: controller::vru::Vru,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -288,6 +286,7 @@ impl Device {
|
||||
rx_buf: None,
|
||||
process: None,
|
||||
pak_handler: None,
|
||||
change_pak: controller::PakType::None,
|
||||
}; 5],
|
||||
},
|
||||
cart: cart_rom::Cart {
|
||||
@@ -419,7 +418,7 @@ impl Device {
|
||||
silicon_id: [sram::FLASHRAM_TYPE_ID, sram::MX29L1100_ID],
|
||||
mode: sram::FlashramMode::ReadArray,
|
||||
},
|
||||
vru: vru::Vru {
|
||||
vru: controller::vru::Vru {
|
||||
status: 0,
|
||||
voice_state: 0,
|
||||
load_offset: 0,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub mod mempak;
|
||||
pub mod rumble;
|
||||
pub mod vru;
|
||||
|
||||
pub const JCMD_STATUS: u8 = 0x00;
|
||||
pub const JCMD_CONTROLLER_READ: u8 = 0x01;
|
||||
const JCMD_PAK_READ: u8 = 0x02;
|
||||
@@ -12,14 +16,22 @@ const JDT_JOY_ABS_COUNTERS: u16 = 0x0001; /* joystick with absolute coordinates
|
||||
//const JDT_JOY_REL_COUNTERS: u16 = 0x0002; /* joystick with relative coordinates (= mouse) */
|
||||
const JDT_JOY_PORT: u16 = 0x0004; /* has port for external paks */
|
||||
const PAK_CHUNK_SIZE: usize = 0x20;
|
||||
const CONT_STATUS_PAK_NOT_PRESENT: u8 = 0;
|
||||
const CONT_STATUS_PAK_PRESENT: u8 = 1;
|
||||
const CONT_STATUS_PAK_NOT_PRESENT: u8 = 2;
|
||||
const CONT_FLAVOR: u16 = JDT_JOY_ABS_COUNTERS | JDT_JOY_PORT;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum PakType {
|
||||
None = 0,
|
||||
MemPak = 1,
|
||||
RumblePak = 2,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PakHandler {
|
||||
pub read: fn(&mut device::Device, usize, u16, usize, usize),
|
||||
pub write: fn(&mut device::Device, usize, u16, usize, usize),
|
||||
pub pak_type: PakType,
|
||||
}
|
||||
|
||||
pub fn process(device: &mut device::Device, channel: usize) {
|
||||
@@ -41,7 +53,23 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
JCMD_CONTROLLER_READ => {
|
||||
let offset = device.pif.channels[channel].rx_buf.unwrap();
|
||||
let input = ui::input::get(&mut device.ui, channel);
|
||||
device.pif.ram[offset..offset + 4].copy_from_slice(&input.to_ne_bytes());
|
||||
device.pif.ram[offset..offset + 4].copy_from_slice(&input.0.to_ne_bytes());
|
||||
if input.1 {
|
||||
// pak change button pressed
|
||||
if device::events::get_event(device, device::events::EventType::PakSwitch).is_none()
|
||||
{
|
||||
device.pif.channels[channel].change_pak =
|
||||
device.pif.channels[channel].pak_handler.unwrap().pak_type;
|
||||
device.pif.channels[channel].pak_handler = None;
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EventType::PakSwitch,
|
||||
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]
|
||||
+ (device.cpu.clock_rate), // 1 second
|
||||
pak_switch_event,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
JCMD_PAK_READ => pak_read_block(
|
||||
device,
|
||||
@@ -118,3 +146,29 @@ pub fn data_crc(device: &device::Device, data_offset: usize, size: usize) -> u8
|
||||
}
|
||||
crc
|
||||
}
|
||||
|
||||
pub fn pak_switch_event(device: &mut device::Device) {
|
||||
for (i, channel) in device.pif.channels.iter_mut().enumerate() {
|
||||
if channel.change_pak != PakType::None {
|
||||
if channel.change_pak == PakType::RumblePak {
|
||||
device::ui::input::set_rumble(&mut device.ui, i, 0);
|
||||
let handler = 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 {
|
||||
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);
|
||||
}
|
||||
channel.change_pak = PakType::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::device;
|
||||
|
||||
pub fn read(device: &mut device::Device, _channel: usize, address: u16, data: usize, size: usize) {
|
||||
let value: u8 = if (0x8000..0x9000).contains(&address) {
|
||||
0x80
|
||||
} else {
|
||||
0x00
|
||||
};
|
||||
|
||||
for i in 0..size {
|
||||
device.pif.ram[data + i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
|
||||
if address == 0xc000 {
|
||||
let rumble = device.pif.ram[data + size - 1];
|
||||
device::ui::input::set_rumble(&mut device.ui, channel, rumble);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ pub enum EventType {
|
||||
SPDma,
|
||||
Compare,
|
||||
Vru,
|
||||
PakSwitch,
|
||||
Count,
|
||||
}
|
||||
|
||||
|
||||
@@ -56,13 +56,10 @@ pub fn translate_address(
|
||||
address: u64,
|
||||
access_type: AccessType,
|
||||
) -> (u64, bool, bool) {
|
||||
let mut cached = false;
|
||||
if (address & 0xc0000000) != 0x80000000 {
|
||||
return device::tlb::get_physical_address(device, address, access_type);
|
||||
} else if address & 0x20000000 == 0 {
|
||||
cached = true;
|
||||
}
|
||||
(address & 0x1FFFFFFF, cached, false)
|
||||
(address & 0x1FFFFFFF, address & 0x20000000 == 0, false)
|
||||
}
|
||||
|
||||
pub fn data_read(
|
||||
|
||||
+5
-3
@@ -15,6 +15,7 @@ pub struct PifChannel {
|
||||
pub rx_buf: Option<usize>,
|
||||
pub process: Option<fn(&mut device::Device, usize)>,
|
||||
pub pak_handler: Option<device::controller::PakHandler>,
|
||||
pub change_pak: device::controller::PakType,
|
||||
}
|
||||
|
||||
pub const PIF_RAM_SIZE: usize = 64;
|
||||
@@ -225,8 +226,9 @@ pub fn init(device: &mut device::Device) {
|
||||
device.pif.ram[0x27] = device.cart.cic_seed;
|
||||
|
||||
let mempak_handler = device::controller::PakHandler {
|
||||
read: device::mempak::read,
|
||||
write: device::mempak::write,
|
||||
read: device::controller::mempak::read,
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: device::controller::PakType::MemPak,
|
||||
};
|
||||
|
||||
for i in 0..4 {
|
||||
@@ -237,7 +239,7 @@ pub fn init(device: &mut device::Device) {
|
||||
}
|
||||
if device.ui.config.input.emulate_vru {
|
||||
device.pif.channels[3].pak_handler = None;
|
||||
device.pif.channels[3].process = Some(device::vru::process);
|
||||
device.pif.channels[3].process = Some(device::controller::vru::process);
|
||||
}
|
||||
device.pif.channels[4].process = Some(device::cart::process)
|
||||
}
|
||||
|
||||
@@ -745,13 +745,14 @@ pub fn llv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element % 4 == 0 {
|
||||
let mut value = 0;
|
||||
for i in 0..4 {
|
||||
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u32) << (8 * (3 - i));
|
||||
}
|
||||
modify_vpr32(
|
||||
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
|
||||
element / 4,
|
||||
(device.rsp.mem[(address & 0xFFF) as usize] as u32) << (8 * 3)
|
||||
| (device.rsp.mem[((address + 1) & 0xFFF) as usize] as u32) << (8 * 2)
|
||||
| (device.rsp.mem[((address + 2) & 0xFFF) as usize] as u32) << 8
|
||||
| (device.rsp.mem[((address + 3) & 0xFFF) as usize] as u32),
|
||||
value,
|
||||
);
|
||||
} else {
|
||||
let end = std::cmp::min(element + 4, 16);
|
||||
@@ -774,17 +775,14 @@ pub fn ldv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element % 8 == 0 {
|
||||
let mut value = 0;
|
||||
for i in 0..8 {
|
||||
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u64) << (8 * (7 - i));
|
||||
}
|
||||
modify_vpr64(
|
||||
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
|
||||
element / 8,
|
||||
(device.rsp.mem[(address & 0xFFF) as usize] as u64) << (8 * 7)
|
||||
| (device.rsp.mem[((address + 1) & 0xFFF) as usize] as u64) << (8 * 6)
|
||||
| (device.rsp.mem[((address + 2) & 0xFFF) as usize] as u64) << (8 * 5)
|
||||
| (device.rsp.mem[((address + 3) & 0xFFF) as usize] as u64) << (8 * 4)
|
||||
| (device.rsp.mem[((address + 4) & 0xFFF) as usize] as u64) << (8 * 3)
|
||||
| (device.rsp.mem[((address + 5) & 0xFFF) as usize] as u64) << (8 * 2)
|
||||
| (device.rsp.mem[((address + 6) & 0xFFF) as usize] as u64) << 8
|
||||
| (device.rsp.mem[((address + 7) & 0xFFF) as usize] as u64),
|
||||
value,
|
||||
);
|
||||
} else {
|
||||
let end = std::cmp::min(element + 8, 16);
|
||||
@@ -807,25 +805,11 @@ pub fn lqv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element == 0 && address % 16 == 0 {
|
||||
modify_vpr128(
|
||||
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
|
||||
(device.rsp.mem[(address & 0xFFF) as usize] as u128) << (8 * 15)
|
||||
| (device.rsp.mem[((address + 1) & 0xFFF) as usize] as u128) << (8 * 14)
|
||||
| (device.rsp.mem[((address + 2) & 0xFFF) as usize] as u128) << (8 * 13)
|
||||
| (device.rsp.mem[((address + 3) & 0xFFF) as usize] as u128) << (8 * 12)
|
||||
| (device.rsp.mem[((address + 4) & 0xFFF) as usize] as u128) << (8 * 11)
|
||||
| (device.rsp.mem[((address + 5) & 0xFFF) as usize] as u128) << (8 * 10)
|
||||
| (device.rsp.mem[((address + 6) & 0xFFF) as usize] as u128) << (8 * 9)
|
||||
| (device.rsp.mem[((address + 7) & 0xFFF) as usize] as u128) << (8 * 8)
|
||||
| (device.rsp.mem[((address + 8) & 0xFFF) as usize] as u128) << (8 * 7)
|
||||
| (device.rsp.mem[((address + 9) & 0xFFF) as usize] as u128) << (8 * 6)
|
||||
| (device.rsp.mem[((address + 10) & 0xFFF) as usize] as u128) << (8 * 5)
|
||||
| (device.rsp.mem[((address + 11) & 0xFFF) as usize] as u128) << (8 * 4)
|
||||
| (device.rsp.mem[((address + 12) & 0xFFF) as usize] as u128) << (8 * 3)
|
||||
| (device.rsp.mem[((address + 13) & 0xFFF) as usize] as u128) << (8 * 2)
|
||||
| (device.rsp.mem[((address + 14) & 0xFFF) as usize] as u128) << 8
|
||||
| (device.rsp.mem[((address + 15) & 0xFFF) as usize] as u128),
|
||||
);
|
||||
let mut value = 0;
|
||||
for i in 0..16 {
|
||||
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u128) << (8 * (15 - i));
|
||||
}
|
||||
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
|
||||
} else {
|
||||
let end = std::cmp::min(16 + element - ((address & 15) as u8), 16);
|
||||
while element < end {
|
||||
@@ -847,25 +831,11 @@ pub fn lrv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = 16u8.wrapping_sub(((address & 15) as u8).wrapping_sub(velement(opcode)));
|
||||
address &= !15;
|
||||
if element == 0 {
|
||||
modify_vpr128(
|
||||
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
|
||||
(device.rsp.mem[(address & 0xFFF) as usize] as u128) << (8 * 15)
|
||||
| (device.rsp.mem[((address + 1) & 0xFFF) as usize] as u128) << (8 * 14)
|
||||
| (device.rsp.mem[((address + 2) & 0xFFF) as usize] as u128) << (8 * 13)
|
||||
| (device.rsp.mem[((address + 3) & 0xFFF) as usize] as u128) << (8 * 12)
|
||||
| (device.rsp.mem[((address + 4) & 0xFFF) as usize] as u128) << (8 * 11)
|
||||
| (device.rsp.mem[((address + 5) & 0xFFF) as usize] as u128) << (8 * 10)
|
||||
| (device.rsp.mem[((address + 6) & 0xFFF) as usize] as u128) << (8 * 9)
|
||||
| (device.rsp.mem[((address + 7) & 0xFFF) as usize] as u128) << (8 * 8)
|
||||
| (device.rsp.mem[((address + 8) & 0xFFF) as usize] as u128) << (8 * 7)
|
||||
| (device.rsp.mem[((address + 9) & 0xFFF) as usize] as u128) << (8 * 6)
|
||||
| (device.rsp.mem[((address + 10) & 0xFFF) as usize] as u128) << (8 * 5)
|
||||
| (device.rsp.mem[((address + 11) & 0xFFF) as usize] as u128) << (8 * 4)
|
||||
| (device.rsp.mem[((address + 12) & 0xFFF) as usize] as u128) << (8 * 3)
|
||||
| (device.rsp.mem[((address + 13) & 0xFFF) as usize] as u128) << (8 * 2)
|
||||
| (device.rsp.mem[((address + 14) & 0xFFF) as usize] as u128) << 8
|
||||
| (device.rsp.mem[((address + 15) & 0xFFF) as usize] as u128),
|
||||
);
|
||||
let mut value = 0;
|
||||
for i in 0..16 {
|
||||
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u128) << (8 * (15 - i));
|
||||
}
|
||||
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
|
||||
} else {
|
||||
while element < 16 {
|
||||
modify_vpr8(
|
||||
@@ -885,35 +855,15 @@ pub fn lpv(device: &mut device::Device, opcode: u32) {
|
||||
|
||||
let index = ((address & 7) as u8).wrapping_sub(velement(opcode));
|
||||
address &= !7;
|
||||
unsafe {
|
||||
device.rsp.cpu.vpr[rt(opcode) as usize] =
|
||||
_mm_set_epi16(
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(0)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(1)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(2)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(3)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(4)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(5)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(6)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(7)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 8,
|
||||
);
|
||||
let mut value = 0;
|
||||
for i in 0..8 {
|
||||
value |= (((device.rsp.mem
|
||||
[((address.wrapping_add(((index.wrapping_add(i)) & 15) as u32)) & 0xFFF) as usize]
|
||||
as u16)
|
||||
<< 8) as u128)
|
||||
<< (16 * (7 - i));
|
||||
}
|
||||
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
|
||||
}
|
||||
|
||||
pub fn luv(device: &mut device::Device, opcode: u32) {
|
||||
@@ -922,35 +872,15 @@ pub fn luv(device: &mut device::Device, opcode: u32) {
|
||||
|
||||
let index = ((address & 7) as u8).wrapping_sub(velement(opcode));
|
||||
address &= !7;
|
||||
unsafe {
|
||||
device.rsp.cpu.vpr[rt(opcode) as usize] =
|
||||
_mm_set_epi16(
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(0)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(1)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(2)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(3)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(4)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(5)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(6)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(7)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
);
|
||||
let mut value = 0;
|
||||
for i in 0..8 {
|
||||
value |= (((device.rsp.mem
|
||||
[((address.wrapping_add(((index.wrapping_add(i)) & 15) as u32)) & 0xFFF) as usize]
|
||||
as u16)
|
||||
<< 7) as u128)
|
||||
<< (16 * (7 - i));
|
||||
}
|
||||
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
|
||||
}
|
||||
|
||||
pub fn lhv(device: &mut device::Device, opcode: u32) {
|
||||
@@ -959,35 +889,15 @@ pub fn lhv(device: &mut device::Device, opcode: u32) {
|
||||
|
||||
let index = ((address & 7) as u8).wrapping_sub(velement(opcode));
|
||||
address &= !7;
|
||||
unsafe {
|
||||
device.rsp.cpu.vpr[rt(opcode) as usize] =
|
||||
_mm_set_epi16(
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(0)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(2)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(4)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(6)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(8)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(10)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(12)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(14)) & 15) as u32))
|
||||
& 0xFFF) as usize] as i16)
|
||||
<< 7,
|
||||
);
|
||||
let mut value = 0;
|
||||
for i in 0..8 {
|
||||
value |= (((device.rsp.mem
|
||||
[((address.wrapping_add(((index.wrapping_add(i * 2)) & 15) as u32)) & 0xFFF) as usize]
|
||||
as u16)
|
||||
<< 7) as u128)
|
||||
<< (16 * (7 - i));
|
||||
}
|
||||
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
|
||||
}
|
||||
|
||||
pub fn lfv(device: &mut device::Device, opcode: u32) {
|
||||
@@ -1074,10 +984,10 @@ pub fn ssv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element % 2 == 0 {
|
||||
device.rsp.mem[(address & 0xFFF) as usize..((address + 2) & 0xFFF) as usize]
|
||||
.copy_from_slice(
|
||||
&get_vpr16(device.rsp.cpu.vpr[rt(opcode) as usize], element / 2).to_be_bytes(),
|
||||
);
|
||||
let start = (address & 0xFFF) as usize;
|
||||
device.rsp.mem[start..start + 2].copy_from_slice(
|
||||
&get_vpr16(device.rsp.cpu.vpr[rt(opcode) as usize], element / 2).to_be_bytes(),
|
||||
);
|
||||
} else {
|
||||
let end = element + 2;
|
||||
while element < end {
|
||||
@@ -1096,10 +1006,10 @@ pub fn slv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element % 4 == 0 {
|
||||
device.rsp.mem[(address & 0xFFF) as usize..((address + 4) & 0xFFF) as usize]
|
||||
.copy_from_slice(
|
||||
&get_vpr32(device.rsp.cpu.vpr[rt(opcode) as usize], element / 4).to_be_bytes(),
|
||||
);
|
||||
let start = (address & 0xFFF) as usize;
|
||||
device.rsp.mem[start..start + 4].copy_from_slice(
|
||||
&get_vpr32(device.rsp.cpu.vpr[rt(opcode) as usize], element / 4).to_be_bytes(),
|
||||
);
|
||||
} else {
|
||||
let end = element + 4;
|
||||
while element < end {
|
||||
@@ -1118,10 +1028,10 @@ pub fn sdv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element % 8 == 0 {
|
||||
device.rsp.mem[(address & 0xFFF) as usize..((address + 8) & 0xFFF) as usize]
|
||||
.copy_from_slice(
|
||||
&get_vpr64(device.rsp.cpu.vpr[rt(opcode) as usize], element / 8).to_be_bytes(),
|
||||
);
|
||||
let start = (address & 0xFFF) as usize;
|
||||
device.rsp.mem[start..start + 8].copy_from_slice(
|
||||
&get_vpr64(device.rsp.cpu.vpr[rt(opcode) as usize], element / 8).to_be_bytes(),
|
||||
);
|
||||
} else {
|
||||
let end = element + 8;
|
||||
while element < end {
|
||||
@@ -1140,7 +1050,8 @@ pub fn sqv(device: &mut device::Device, opcode: u32) {
|
||||
let mut element = velement(opcode);
|
||||
|
||||
if element == 0 && address % 16 == 0 {
|
||||
device.rsp.mem[(address & 0xFFF) as usize..((address + 16) & 0xFFF) as usize]
|
||||
let start = (address & 0xFFF) as usize;
|
||||
device.rsp.mem[start..start + 16]
|
||||
.copy_from_slice(&get_vpr128(device.rsp.cpu.vpr[rt(opcode) as usize]).to_be_bytes());
|
||||
} else {
|
||||
let end = element + (16 - (address & 15)) as u8;
|
||||
@@ -1163,7 +1074,8 @@ pub fn srv(device: &mut device::Device, opcode: u32) {
|
||||
address &= !15;
|
||||
|
||||
if element == 0 && base == 0 {
|
||||
device.rsp.mem[(address & 0xFFF) as usize..((address + 16) & 0xFFF) as usize]
|
||||
let start = (address & 0xFFF) as usize;
|
||||
device.rsp.mem[start..start + 16]
|
||||
.copy_from_slice(&get_vpr128(device.rsp.cpu.vpr[rt(opcode) as usize]).to_be_bytes());
|
||||
} else {
|
||||
while element < end {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
#[global_allocator]
|
||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
||||
mod device;
|
||||
mod ui;
|
||||
@@ -18,6 +20,12 @@ struct Args {
|
||||
help = "Create a new input profile (keyboard/gamepad mappings)."
|
||||
)]
|
||||
configure_input_profile: Option<String>,
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "Use DirectInput when configuring a new input profile."
|
||||
)]
|
||||
use_dinput: bool,
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
@@ -139,6 +147,7 @@ fn main() {
|
||||
ui::input::configure_input_profile(
|
||||
&mut device.ui,
|
||||
args.configure_input_profile.unwrap(),
|
||||
args.use_dinput,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ pub struct Ui {
|
||||
pub sdl_context: Option<sdl2::Sdl>,
|
||||
pub video_subsystem: Option<sdl2::VideoSubsystem>,
|
||||
pub audio_subsystem: Option<sdl2::AudioSubsystem>,
|
||||
pub pak_audio: Option<audio::PakAudio>,
|
||||
pub joystick_subsystem: Option<sdl2::JoystickSubsystem>,
|
||||
#[allow(dead_code)]
|
||||
pub controller_subsystem: Option<sdl2::GameControllerSubsystem>,
|
||||
@@ -59,18 +60,22 @@ impl Ui {
|
||||
input::Controllers {
|
||||
game_controller: None,
|
||||
joystick: None,
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: None,
|
||||
joystick: None,
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: None,
|
||||
joystick: None,
|
||||
rumble: false,
|
||||
},
|
||||
input::Controllers {
|
||||
game_controller: None,
|
||||
joystick: None,
|
||||
rumble: false,
|
||||
},
|
||||
],
|
||||
config_file_path,
|
||||
@@ -99,6 +104,7 @@ impl Ui {
|
||||
controller_subsystem: Some(controller_subsystem),
|
||||
window: None,
|
||||
audio_device: None,
|
||||
pak_audio: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+54
-3
@@ -1,6 +1,11 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub struct PakAudio {
|
||||
mempak: Vec<u8>,
|
||||
rumblepak: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui, frequency: u64) {
|
||||
let desired_spec = sdl2::audio::AudioSpecDesired {
|
||||
freq: Some(frequency as i32),
|
||||
@@ -9,16 +14,62 @@ pub fn init(ui: &mut ui::Ui, frequency: u64) {
|
||||
};
|
||||
ui.audio_device = Some(
|
||||
ui.audio_subsystem
|
||||
.as_mut()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.open_queue::<i16, _>(None, &desired_spec)
|
||||
.unwrap(),
|
||||
);
|
||||
ui.audio_device.as_mut().unwrap().resume();
|
||||
let audio_device = ui.audio_device.as_ref().unwrap();
|
||||
audio_device.resume();
|
||||
|
||||
let mempak_audio = Box::new(sdl2::audio::AudioSpecWAV::load_wav_rw(
|
||||
&mut sdl2::rwops::RWops::from_bytes(include_bytes!("../../data/mempak.wav"))
|
||||
.expect("Could not mempak WAV file"),
|
||||
))
|
||||
.expect("Could not load mempak WAV file");
|
||||
let rumblepak_audio = Box::new(
|
||||
sdl2::audio::AudioSpecWAV::load_wav_rw(
|
||||
&mut sdl2::rwops::RWops::from_bytes(include_bytes!("../../data/rumblepak.wav"))
|
||||
.expect("Could not load rumblepak WAV file"),
|
||||
)
|
||||
.expect("Could not load rumblepak WAV file"),
|
||||
);
|
||||
|
||||
let cvt = sdl2::audio::AudioCVT::new(
|
||||
mempak_audio.format,
|
||||
mempak_audio.channels,
|
||||
mempak_audio.freq,
|
||||
audio_device.spec().format,
|
||||
audio_device.spec().channels,
|
||||
audio_device.spec().freq,
|
||||
)
|
||||
.expect("Could not create AudioCVT");
|
||||
|
||||
ui.pak_audio = Some(PakAudio {
|
||||
mempak: cvt.convert(mempak_audio.buffer().to_vec()),
|
||||
rumblepak: cvt.convert(rumblepak_audio.buffer().to_vec()),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn play_pak_switch(ui: &mut ui::Ui, pak: device::controller::PakType) {
|
||||
let sound;
|
||||
if pak == device::controller::PakType::RumblePak {
|
||||
sound = &ui.pak_audio.as_ref().unwrap().rumblepak;
|
||||
} else if pak == device::controller::PakType::MemPak {
|
||||
sound = &ui.pak_audio.as_ref().unwrap().mempak;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let audio_device = ui.audio_device.as_ref().unwrap();
|
||||
let i16_buffer: Vec<i16> = sound
|
||||
.chunks_exact(2)
|
||||
.map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]]))
|
||||
.collect();
|
||||
audio_device.queue_audio(&i16_buffer).unwrap();
|
||||
}
|
||||
|
||||
pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
|
||||
let audio_device = device.ui.audio_device.as_mut().unwrap();
|
||||
let audio_device = device.ui.audio_device.as_ref().unwrap();
|
||||
let mut primary_buffer: Vec<i16> = vec![0; length as usize / 2];
|
||||
let mut i = 0;
|
||||
while i < length as usize / 2 {
|
||||
|
||||
+14
-7
@@ -2,12 +2,13 @@ use crate::ui;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct InputProfile {
|
||||
pub keys: [(bool, i32); 18],
|
||||
pub controller_buttons: [(bool, i32); 14],
|
||||
pub controller_axis: [(bool, i32, i16); 18],
|
||||
pub joystick_buttons: [(bool, u32); 14],
|
||||
pub joystick_hat: [(bool, u32, u8); 14],
|
||||
pub joystick_axis: [(bool, u32, i16); 18],
|
||||
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, u32); ui::input::PROFILE_SIZE],
|
||||
pub joystick_hat: [(bool, u32, u8); ui::input::PROFILE_SIZE],
|
||||
pub joystick_axis: [(bool, u32, i16); ui::input::PROFILE_SIZE],
|
||||
pub dinput: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
@@ -21,6 +22,8 @@ pub struct Input {
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Video {
|
||||
pub upscale: bool,
|
||||
pub integer_scaling: bool,
|
||||
pub fullscreen: bool,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Config {
|
||||
@@ -45,7 +48,11 @@ impl Config {
|
||||
controller_enabled: [true, false, false, false],
|
||||
emulate_vru: false,
|
||||
},
|
||||
video: Video { upscale: false },
|
||||
video: Video {
|
||||
upscale: false,
|
||||
integer_scaling: false,
|
||||
fullscreen: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-27
@@ -14,13 +14,26 @@ pub struct GopherEguiApp {
|
||||
input_profiles: Vec<String>,
|
||||
controller_enabled: [bool; 4],
|
||||
upscale: bool,
|
||||
integer_scaling: bool,
|
||||
fullscreen: bool,
|
||||
emulate_vru: bool,
|
||||
dinput: bool,
|
||||
show_vru_dialog: bool,
|
||||
vru_window_receiver: Option<std::sync::mpsc::Receiver<Vec<String>>>,
|
||||
vru_word_notifier: Option<std::sync::mpsc::Sender<String>>,
|
||||
vru_word_list: Vec<String>,
|
||||
}
|
||||
|
||||
struct SaveConfig {
|
||||
selected_controller: [i32; 4],
|
||||
selected_profile: [String; 4],
|
||||
controller_enabled: [bool; 4],
|
||||
upscale: bool,
|
||||
integer_scaling: bool,
|
||||
fullscreen: bool,
|
||||
emulate_vru: bool,
|
||||
}
|
||||
|
||||
fn get_input_profiles(game_ui: &ui::Ui) -> Vec<String> {
|
||||
let mut profiles = vec![];
|
||||
for key in game_ui.config.input.input_profiles.keys() {
|
||||
@@ -84,8 +97,11 @@ impl GopherEguiApp {
|
||||
input_profiles: get_input_profiles(&game_ui),
|
||||
controller_enabled: game_ui.config.input.controller_enabled,
|
||||
upscale: game_ui.config.video.upscale,
|
||||
integer_scaling: game_ui.config.video.integer_scaling,
|
||||
fullscreen: game_ui.config.video.fullscreen,
|
||||
emulate_vru: game_ui.config.input.emulate_vru,
|
||||
show_vru_dialog: false,
|
||||
dinput: false,
|
||||
vru_window_receiver: None,
|
||||
vru_word_notifier: None,
|
||||
vru_word_list: Vec::new(),
|
||||
@@ -93,16 +109,9 @@ impl GopherEguiApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn save_config(
|
||||
game_ui: &mut ui::Ui,
|
||||
selected_controller: [i32; 4],
|
||||
selected_profile: [String; 4],
|
||||
controller_enabled: [bool; 4],
|
||||
upscale: bool,
|
||||
emulate_vru: bool,
|
||||
) {
|
||||
fn save_config(game_ui: &mut ui::Ui, save_config_items: SaveConfig) {
|
||||
let joystick_subsystem = game_ui.joystick_subsystem.as_ref().unwrap();
|
||||
for (pos, item) in selected_controller.iter().enumerate() {
|
||||
for (pos, item) in save_config_items.selected_controller.iter().enumerate() {
|
||||
if *item != -1 {
|
||||
game_ui.config.input.controller_assignment[pos] = Some(
|
||||
joystick_subsystem
|
||||
@@ -115,24 +124,28 @@ fn save_config(
|
||||
}
|
||||
}
|
||||
|
||||
game_ui.config.input.input_profile_binding = selected_profile;
|
||||
game_ui.config.input.controller_enabled = controller_enabled;
|
||||
game_ui.config.input.input_profile_binding = save_config_items.selected_profile;
|
||||
game_ui.config.input.controller_enabled = save_config_items.controller_enabled;
|
||||
|
||||
game_ui.config.video.upscale = upscale;
|
||||
game_ui.config.input.emulate_vru = emulate_vru;
|
||||
game_ui.config.video.upscale = save_config_items.upscale;
|
||||
game_ui.config.video.integer_scaling = save_config_items.integer_scaling;
|
||||
game_ui.config.video.fullscreen = save_config_items.fullscreen;
|
||||
game_ui.config.input.emulate_vru = save_config_items.emulate_vru;
|
||||
}
|
||||
|
||||
impl Drop for GopherEguiApp {
|
||||
fn drop(&mut self) {
|
||||
let mut game_ui = ui::Ui::new(self.config_dir.clone());
|
||||
save_config(
|
||||
&mut game_ui,
|
||||
self.selected_controller,
|
||||
self.selected_profile.clone(),
|
||||
self.controller_enabled,
|
||||
self.upscale,
|
||||
self.emulate_vru,
|
||||
);
|
||||
let save_config_items = SaveConfig {
|
||||
selected_controller: self.selected_controller,
|
||||
selected_profile: self.selected_profile.clone(),
|
||||
controller_enabled: self.controller_enabled,
|
||||
upscale: self.upscale,
|
||||
integer_scaling: self.integer_scaling,
|
||||
fullscreen: self.fullscreen,
|
||||
emulate_vru: self.emulate_vru,
|
||||
};
|
||||
save_config(&mut game_ui, save_config_items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +160,19 @@ impl eframe::App for GopherEguiApp {
|
||||
ui.text_edit_singleline(&mut self.profile_name)
|
||||
.labelled_by(name_label.id);
|
||||
});
|
||||
ui.checkbox(&mut self.dinput, "Use DirectInput");
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Configure Profile").clicked() {
|
||||
let profile_name = self.profile_name.clone();
|
||||
let config_dir = self.config_dir.clone();
|
||||
execute(async {
|
||||
let dinput = self.dinput;
|
||||
execute(async move {
|
||||
let mut game_ui = ui::Ui::new(config_dir);
|
||||
ui::input::configure_input_profile(&mut game_ui, profile_name);
|
||||
ui::input::configure_input_profile(
|
||||
&mut game_ui,
|
||||
profile_name,
|
||||
dinput,
|
||||
);
|
||||
});
|
||||
self.configure_profile = false;
|
||||
if !self.profile_name.is_empty()
|
||||
@@ -181,6 +200,8 @@ impl eframe::App for GopherEguiApp {
|
||||
let selected_profile = self.selected_profile.clone();
|
||||
let controller_enabled = self.controller_enabled;
|
||||
let upscale = self.upscale;
|
||||
let integer_scaling = self.integer_scaling;
|
||||
let fullscreen = self.fullscreen;
|
||||
let emulate_vru = self.emulate_vru;
|
||||
let config_dir = self.config_dir.clone();
|
||||
let cache_dir = self.cache_dir.clone();
|
||||
@@ -215,14 +236,18 @@ impl eframe::App for GopherEguiApp {
|
||||
panic!("could not create running file: {}", result.err().unwrap())
|
||||
}
|
||||
let mut device = device::Device::new(config_dir);
|
||||
save_config(
|
||||
&mut device.ui,
|
||||
|
||||
let save_config_items = SaveConfig {
|
||||
selected_controller,
|
||||
selected_profile,
|
||||
controller_enabled,
|
||||
upscale,
|
||||
integer_scaling,
|
||||
fullscreen,
|
||||
emulate_vru,
|
||||
);
|
||||
};
|
||||
save_config(&mut device.ui, save_config_items);
|
||||
|
||||
if emulate_vru {
|
||||
device.vru.window_notifier = Some(vru_window_notifier);
|
||||
device.vru.word_receiver = Some(vru_word_receiver);
|
||||
@@ -232,7 +257,7 @@ impl eframe::App for GopherEguiApp {
|
||||
std::path::Path::new(file.path()),
|
||||
data_dir,
|
||||
&mut device,
|
||||
false,
|
||||
fullscreen,
|
||||
);
|
||||
let result = std::fs::remove_file(running_file.clone());
|
||||
if result.is_err() {
|
||||
@@ -299,6 +324,9 @@ impl eframe::App for GopherEguiApp {
|
||||
});
|
||||
ui.add_space(32.0);
|
||||
ui.checkbox(&mut self.upscale, "High-Res Graphics");
|
||||
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.emulate_vru,
|
||||
"Emulate VRU (connects VRU to controller port 4)",
|
||||
|
||||
+175
-60
@@ -16,17 +16,20 @@ const D_CBUTTON: usize = 10;
|
||||
const U_CBUTTON: usize = 11;
|
||||
const R_TRIG: usize = 12;
|
||||
const L_TRIG: usize = 13;
|
||||
const X_AXIS: usize = 16;
|
||||
const Y_AXIS: usize = 24;
|
||||
|
||||
const AXIS_LEFT: usize = 14;
|
||||
const AXIS_RIGHT: usize = 15;
|
||||
const AXIS_UP: usize = 16;
|
||||
const AXIS_DOWN: usize = 17;
|
||||
const CHANGE_PAK: usize = 18;
|
||||
pub const PROFILE_SIZE: usize = 19;
|
||||
|
||||
const X_AXIS_SHIFT: usize = 16;
|
||||
const Y_AXIS_SHIFT: usize = 24;
|
||||
|
||||
const MAX_AXIS_VALUE: f64 = 85.0;
|
||||
|
||||
pub struct Controllers {
|
||||
pub rumble: bool,
|
||||
pub game_controller: Option<sdl2::controller::GameController>,
|
||||
pub joystick: Option<sdl2::joystick::Joystick>,
|
||||
}
|
||||
@@ -149,7 +152,7 @@ pub fn set_axis_from_controller(
|
||||
|
||||
pub fn set_axis_from_keys(
|
||||
profile: &ui::config::InputProfile,
|
||||
keyboard_state: sdl2::keyboard::KeyboardState,
|
||||
keyboard_state: &sdl2::keyboard::KeyboardState,
|
||||
) -> (f64, f64) {
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
@@ -236,9 +239,70 @@ pub fn set_buttons_from_controller(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(ui: &mut ui::Ui, channel: usize) -> u32 {
|
||||
let context = ui.sdl_context.as_mut().unwrap();
|
||||
let events = context.event_pump().unwrap();
|
||||
pub fn set_rumble(ui: &mut ui::Ui, channel: usize, rumble: u8) {
|
||||
if !ui.controllers[channel].rumble {
|
||||
return;
|
||||
}
|
||||
let controller = &mut ui.controllers[channel].game_controller;
|
||||
let joystick = &mut ui.controllers[channel].joystick;
|
||||
if controller.is_some() {
|
||||
controller
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_rumble(
|
||||
(rumble & 1) as u16 * u16::MAX,
|
||||
(rumble & 1) as u16 * u16::MAX,
|
||||
(rumble & 1) as u32 * 60000,
|
||||
)
|
||||
.unwrap();
|
||||
} else if joystick.is_some() {
|
||||
joystick
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_rumble(
|
||||
(rumble & 1) as u16 * u16::MAX,
|
||||
(rumble & 1) as u16 * u16::MAX,
|
||||
(rumble & 1) as u32 * 60000,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_paks(
|
||||
profile: &ui::config::InputProfile,
|
||||
joystick: &Option<sdl2::joystick::Joystick>,
|
||||
controller: &Option<sdl2::controller::GameController>,
|
||||
keyboard_state: &sdl2::keyboard::KeyboardState,
|
||||
) -> bool {
|
||||
let controller_button = profile.controller_buttons[CHANGE_PAK];
|
||||
let joystick_button = profile.joystick_buttons[CHANGE_PAK];
|
||||
let joystick_hat = profile.joystick_hat[CHANGE_PAK];
|
||||
let key = profile.keys[CHANGE_PAK];
|
||||
|
||||
let mut pressed = false;
|
||||
if controller_button.0 && controller.is_some() {
|
||||
pressed = controller
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.button(get_button_from_i32(controller_button.1));
|
||||
} else if joystick_button.0 && joystick.is_some() {
|
||||
pressed = joystick
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.button(joystick_button.1)
|
||||
.unwrap();
|
||||
} else if joystick_hat.0 && joystick.is_some() {
|
||||
pressed = joystick.as_ref().unwrap().hat(joystick_hat.1).unwrap()
|
||||
== sdl2::joystick::HatState::from_raw(joystick_hat.2);
|
||||
} else if key.0 {
|
||||
pressed =
|
||||
keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::from_i32(key.1).unwrap());
|
||||
}
|
||||
pressed
|
||||
}
|
||||
|
||||
pub fn get(ui: &mut ui::Ui, channel: usize) -> (u32, bool) {
|
||||
let events = ui.sdl_context.as_ref().unwrap().event_pump().unwrap();
|
||||
let keyboard_state = events.keyboard_state();
|
||||
|
||||
let profile_name = ui.config.input.input_profile_binding[channel].clone();
|
||||
@@ -257,10 +321,10 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
if joystick.is_some() {
|
||||
set_buttons_from_joystick(profile, i, joystick.as_ref().unwrap(), &mut keys);
|
||||
} else if controller.is_some() {
|
||||
if controller.is_some() {
|
||||
set_buttons_from_controller(profile, i, controller.as_ref().unwrap(), &mut keys);
|
||||
} else if joystick.is_some() {
|
||||
set_buttons_from_joystick(profile, i, joystick.as_ref().unwrap(), &mut keys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,19 +332,23 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> u32 {
|
||||
let mut y: f64 = 0.0;
|
||||
|
||||
if profile_name != "default" || channel == 0 {
|
||||
(x, y) = set_axis_from_keys(profile, keyboard_state);
|
||||
(x, y) = set_axis_from_keys(profile, &keyboard_state);
|
||||
}
|
||||
|
||||
if joystick.is_some() {
|
||||
(x, y) = set_axis_from_joystick(profile, joystick.as_ref().unwrap())
|
||||
} else if controller.is_some() {
|
||||
if controller.is_some() {
|
||||
(x, y) = set_axis_from_controller(profile, controller.as_ref().unwrap())
|
||||
} else if joystick.is_some() {
|
||||
(x, y) = set_axis_from_joystick(profile, joystick.as_ref().unwrap())
|
||||
}
|
||||
bound_axis(&mut x, &mut y);
|
||||
|
||||
keys |= (x.round() as i8 as u8 as u32) << X_AXIS;
|
||||
keys |= (y.round() as i8 as u8 as u32) << Y_AXIS;
|
||||
keys
|
||||
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, &keyboard_state),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn list_controllers(ui: &ui::Ui) {
|
||||
@@ -324,7 +392,7 @@ pub fn clear_bindings(ui: &mut ui::Ui) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
if profile == "default" {
|
||||
println!("Profile name cannot be default");
|
||||
return;
|
||||
@@ -333,10 +401,20 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
println!("Profile name cannot be empty");
|
||||
return;
|
||||
}
|
||||
let joystick_subsystem = ui.sdl_context.as_ref().unwrap().joystick().unwrap();
|
||||
let controller_subsystem = ui.controller_subsystem.as_ref().unwrap();
|
||||
let joystick_subsystem = ui.joystick_subsystem.as_ref().unwrap();
|
||||
let mut controllers = vec![];
|
||||
let mut joysticks = vec![];
|
||||
for i in 0..joystick_subsystem.num_joysticks().unwrap() {
|
||||
joysticks.push(ui.sdl_context.as_ref().unwrap().joystick().unwrap().open(i));
|
||||
if !dinput {
|
||||
if let Ok(controller) = controller_subsystem.open(i) {
|
||||
controllers.push(controller);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Ok(joystick) = joystick_subsystem.open(i) {
|
||||
joysticks.push(joystick);
|
||||
}
|
||||
}
|
||||
|
||||
let mut builder =
|
||||
@@ -350,7 +428,7 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
let font =
|
||||
rusttype::Font::try_from_bytes(include_bytes!("../../data/Roboto-Regular.ttf")).unwrap();
|
||||
|
||||
let key_labels = [
|
||||
let key_labels: [(&str, usize); PROFILE_SIZE] = [
|
||||
("A", A_BUTTON),
|
||||
("B", B_BUTTON),
|
||||
("Start", START_BUTTON),
|
||||
@@ -369,17 +447,21 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
("Control Stick Down", AXIS_DOWN),
|
||||
("Control Stick Left", AXIS_LEFT),
|
||||
("Control Stick Right", AXIS_RIGHT),
|
||||
("Change Pak", CHANGE_PAK),
|
||||
];
|
||||
|
||||
let mut new_keys = [(false, 0); 18];
|
||||
let mut new_joystick_buttons = [(false, 0u32); 14];
|
||||
let mut new_joystick_hat = [(false, 0u32, 0); 14];
|
||||
let mut new_joystick_axis = [(false, 0u32, 0); 18];
|
||||
let mut new_keys = [(false, 0); PROFILE_SIZE];
|
||||
let mut new_joystick_buttons = [(false, 0u32); PROFILE_SIZE];
|
||||
let mut new_joystick_hat = [(false, 0u32, 0); PROFILE_SIZE];
|
||||
let mut new_joystick_axis = [(false, 0u32, 0); PROFILE_SIZE];
|
||||
let mut new_controller_buttons = [(false, 0i32); PROFILE_SIZE];
|
||||
let mut new_controller_axis = [(false, 0i32, 0); PROFILE_SIZE];
|
||||
|
||||
let mut last_axis_result = (false, 0, 0);
|
||||
let mut event_pump = ui.sdl_context.as_ref().unwrap().event_pump().unwrap();
|
||||
let mut last_joystick_axis_result = (false, 0, 0);
|
||||
let mut last_controller_axis_result = (false, 0, 0);
|
||||
let mut events = ui.sdl_context.as_ref().unwrap().event_pump().unwrap();
|
||||
for (key, value) in key_labels.iter() {
|
||||
for _event in event_pump.poll_iter() {} // clear events
|
||||
for _event in events.poll_iter() {} // clear events
|
||||
|
||||
ui::video::draw_text(
|
||||
format!("Select binding for: {key}").as_str(),
|
||||
@@ -390,7 +472,7 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
let mut key_set = false;
|
||||
while !key_set {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
for event in event_pump.poll_iter() {
|
||||
for event in events.poll_iter() {
|
||||
match event {
|
||||
sdl2::event::Event::Window {
|
||||
win_event: sdl2::event::WindowEvent::Close,
|
||||
@@ -403,32 +485,57 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
new_keys[*value] = (true, scancode as i32);
|
||||
key_set = true
|
||||
}
|
||||
sdl2::event::Event::ControllerButtonDown { button, .. } => {
|
||||
if !controllers.is_empty() {
|
||||
new_controller_buttons[*value] = (true, button as i32);
|
||||
key_set = true
|
||||
}
|
||||
}
|
||||
sdl2::event::Event::ControllerAxisMotion {
|
||||
axis,
|
||||
value: axis_value,
|
||||
..
|
||||
} => {
|
||||
if !controllers.is_empty() && axis_value.saturating_abs() > i16::MAX / 2 {
|
||||
let result =
|
||||
(true, axis as i32, axis_value / axis_value.saturating_abs());
|
||||
if result != last_controller_axis_result {
|
||||
new_controller_axis[*value] = result;
|
||||
last_controller_axis_result = result;
|
||||
key_set = true
|
||||
}
|
||||
}
|
||||
}
|
||||
sdl2::event::Event::JoyButtonDown { button_idx, .. } => {
|
||||
new_joystick_buttons[*value] = (true, button_idx as u32);
|
||||
key_set = true
|
||||
if !joysticks.is_empty() {
|
||||
new_joystick_buttons[*value] = (true, button_idx as u32);
|
||||
key_set = true
|
||||
}
|
||||
}
|
||||
sdl2::event::Event::JoyHatMotion { hat_idx, state, .. } => {
|
||||
new_joystick_hat[*value] = (
|
||||
true,
|
||||
hat_idx as u32,
|
||||
sdl2::joystick::HatState::to_raw(state),
|
||||
);
|
||||
key_set = true
|
||||
if !joysticks.is_empty() && state != sdl2::joystick::HatState::Centered {
|
||||
new_joystick_hat[*value] = (
|
||||
true,
|
||||
hat_idx as u32,
|
||||
sdl2::joystick::HatState::to_raw(state),
|
||||
);
|
||||
key_set = true
|
||||
}
|
||||
}
|
||||
sdl2::event::Event::JoyAxisMotion {
|
||||
axis_idx,
|
||||
value: axis_value,
|
||||
..
|
||||
} => {
|
||||
if axis_value.saturating_abs() > 24576 {
|
||||
if !joysticks.is_empty() && axis_value.saturating_abs() > i16::MAX / 2 {
|
||||
let result = (
|
||||
true,
|
||||
axis_idx as u32,
|
||||
axis_value / axis_value.saturating_abs(),
|
||||
);
|
||||
if result != last_axis_result {
|
||||
if result != last_joystick_axis_result {
|
||||
new_joystick_axis[*value] = result;
|
||||
last_axis_result = result;
|
||||
last_joystick_axis_result = result;
|
||||
key_set = true
|
||||
}
|
||||
}
|
||||
@@ -441,19 +548,20 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
|
||||
|
||||
let new_profile = ui::config::InputProfile {
|
||||
keys: new_keys,
|
||||
controller_buttons: Default::default(),
|
||||
controller_axis: Default::default(),
|
||||
controller_buttons: new_controller_buttons,
|
||||
controller_axis: new_controller_axis,
|
||||
joystick_buttons: new_joystick_buttons,
|
||||
joystick_hat: new_joystick_hat,
|
||||
joystick_axis: new_joystick_axis,
|
||||
dinput,
|
||||
};
|
||||
ui.config.input.input_profiles.insert(profile, new_profile);
|
||||
}
|
||||
|
||||
pub fn get_default_profile() -> ui::config::InputProfile {
|
||||
let mut default_controller_buttons = [(false, 0); 14];
|
||||
let mut default_controller_axis = [(false, 0, 0); 18];
|
||||
let mut default_keys = [(false, 0); 18];
|
||||
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, sdl2::keyboard::Scancode::D as i32);
|
||||
default_keys[L_DPAD] = (true, sdl2::keyboard::Scancode::A as i32);
|
||||
default_keys[D_DPAD] = (true, sdl2::keyboard::Scancode::S as i32);
|
||||
@@ -472,6 +580,7 @@ pub fn get_default_profile() -> ui::config::InputProfile {
|
||||
default_keys[AXIS_RIGHT] = (true, sdl2::keyboard::Scancode::Right as i32);
|
||||
default_keys[AXIS_UP] = (true, sdl2::keyboard::Scancode::Up as i32);
|
||||
default_keys[AXIS_DOWN] = (true, sdl2::keyboard::Scancode::Down as i32);
|
||||
default_keys[CHANGE_PAK] = (true, sdl2::keyboard::Scancode::Comma as i32);
|
||||
|
||||
default_controller_buttons[R_DPAD] = (true, sdl2::controller::Button::DPadRight as i32);
|
||||
default_controller_buttons[L_DPAD] = (true, sdl2::controller::Button::DPadLeft as i32);
|
||||
@@ -491,6 +600,7 @@ pub fn get_default_profile() -> ui::config::InputProfile {
|
||||
default_controller_axis[AXIS_RIGHT] = (true, sdl2::controller::Axis::LeftX as i32, 1);
|
||||
default_controller_axis[AXIS_UP] = (true, sdl2::controller::Axis::LeftY as i32, -1);
|
||||
default_controller_axis[AXIS_DOWN] = (true, sdl2::controller::Axis::LeftY as i32, 1);
|
||||
default_controller_buttons[CHANGE_PAK] = (true, sdl2::controller::Button::Back as i32);
|
||||
|
||||
ui::config::InputProfile {
|
||||
keys: default_keys,
|
||||
@@ -499,11 +609,13 @@ pub fn get_default_profile() -> ui::config::InputProfile {
|
||||
joystick_buttons: Default::default(),
|
||||
joystick_hat: Default::default(),
|
||||
joystick_axis: Default::default(),
|
||||
dinput: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui) {
|
||||
let joystick_subsystem = ui.joystick_subsystem.as_ref().unwrap();
|
||||
let controller_subsystem = ui.controller_subsystem.as_ref().unwrap();
|
||||
let mut taken = [false; 4];
|
||||
for i in 0..4 {
|
||||
let controller_assignment = &ui.config.input.controller_assignment[i];
|
||||
@@ -520,26 +632,26 @@ pub fn init(ui: &mut ui::Ui) {
|
||||
}
|
||||
}
|
||||
if joystick_index < u32::MAX {
|
||||
if ui.config.input.input_profile_binding[i] == "default" {
|
||||
let controller_result = ui
|
||||
.sdl_context
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.game_controller()
|
||||
.unwrap()
|
||||
.open(joystick_index);
|
||||
let profile_name = ui.config.input.input_profile_binding[i].clone();
|
||||
let profile = ui.config.input.input_profiles.get(&profile_name).unwrap();
|
||||
|
||||
if !profile.dinput {
|
||||
let controller_result = controller_subsystem.open(joystick_index);
|
||||
if controller_result.is_ok() {
|
||||
ui.controllers[i].game_controller = Some(controller_result.unwrap());
|
||||
if ui.controllers[i]
|
||||
.game_controller
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.has_rumble()
|
||||
{
|
||||
ui.controllers[i].rumble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ui.controllers[i].game_controller.is_none() {
|
||||
let joystick_result = ui
|
||||
.sdl_context
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.joystick()
|
||||
.unwrap()
|
||||
.open(joystick_index);
|
||||
let joystick_result = joystick_subsystem.open(joystick_index);
|
||||
if joystick_result.is_err() {
|
||||
println!(
|
||||
"could not connect joystick: {}",
|
||||
@@ -547,6 +659,9 @@ pub fn init(ui: &mut ui::Ui) {
|
||||
)
|
||||
} else {
|
||||
ui.controllers[i].joystick = Some(joystick_result.unwrap());
|
||||
if ui.controllers[i].joystick.as_ref().unwrap().has_rumble() {
|
||||
ui.controllers[i].rumble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+3
-2
@@ -36,10 +36,11 @@ pub fn init(device: &mut device::Device, fullscreen: bool) {
|
||||
|
||||
unsafe {
|
||||
rdp_init(
|
||||
device.ui.window.as_mut().unwrap().raw() as *mut std::ffi::c_void,
|
||||
device.ui.window.as_ref().unwrap().raw() as *mut std::ffi::c_void,
|
||||
gfx_info,
|
||||
fullscreen,
|
||||
device.ui.config.video.upscale,
|
||||
device.ui.config.video.integer_scaling,
|
||||
fullscreen,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user