start on ram hacking stuff
This commit is contained in:
parent
5842f5f430
commit
a98a6eeb5e
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'ferretro-dev-gui pokemon stadium'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=ferretro-dev-gui",
|
||||
"--package=ferretro-dev-gui"
|
||||
],
|
||||
"filter": {
|
||||
"name": "ferretro-dev-gui",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": ["--core", "/home/vivlim/.config/retroarch/cores/parallel_n64_libretro.so", "--rom", "/home/vivlim/games/roms/n64/Super Smash Bros. (U) [!].z64"],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'ferretro-dev-gui' pokeblue",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=ferretro-dev-gui",
|
||||
"--package=ferretro-dev-gui"
|
||||
],
|
||||
"filter": {
|
||||
"name": "ferretro-dev-gui",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": ["--core", "/home/vivlim/.config/retroarch/cores/gambatte_libretro.so", "--rom", "/home/vivlim/games/roms/gbc/pokeblue.gb"],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'ferretro-dev-gui'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=ferretro-dev-gui",
|
||||
"--package=ferretro-dev-gui"
|
||||
],
|
||||
"filter": {
|
||||
"name": "ferretro-dev-gui",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -672,7 +672,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ferretro"
|
||||
version = "0.1.0"
|
||||
source = "git+ssh://git@vvn.space:2222/cinnabon/rustro.git?branch=viv/ffmpeg2#3cad3b1e29cfc76116399e3475113b59ee22e312"
|
||||
source = "git+ssh://git@vvn.space:2222/cinnabon/rustro.git?branch=matriarch#d8d00386437960309d8fb21d4a107a65d42f942b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"failure",
|
||||
|
|
|
@ -10,7 +10,7 @@ cc = "^1"
|
|||
[dependencies]
|
||||
crossbeam-channel = "^0.4"
|
||||
structopt = "^0.3"
|
||||
ferretro = { git = "ssh://git@vvn.space:2222/cinnabon/rustro.git", branch = "viv/ffmpeg2"}
|
||||
ferretro = { git = "ssh://git@vvn.space:2222/cinnabon/rustro.git", branch = "matriarch" }
|
||||
failure = "^0.1"
|
||||
libloading = "^0.5"
|
||||
|
||||
|
|
48
src/main.rs
48
src/main.rs
|
@ -19,7 +19,7 @@ use mini_gl_fb::glutin::platform::run_return::EventLoopExtRunReturn;
|
|||
|
||||
use failure::{Fallible};
|
||||
use ferretro::retro;
|
||||
use ferretro::retro::ffi::{GameGeometry, SystemInfo, SystemAvInfo};
|
||||
use ferretro::retro::ffi::{GameGeometry, MemoryMap, SystemAvInfo, SystemInfo};
|
||||
use ferretro::retro::constants::{InputIndex, JoypadButton, AnalogAxis, DeviceType};
|
||||
use ferretro::retro::wrapped_types::{ControllerDescription2, InputDescriptor2, InputDeviceId, SubsystemInfo2, Variable2};
|
||||
use ferretro::retro::wrapper::LibretroWrapper;
|
||||
|
@ -40,7 +40,9 @@ use structopt::StructOpt;
|
|||
use gilrs::{Button, GamepadId, Gilrs, Axis};
|
||||
|
||||
mod video;
|
||||
mod memory;
|
||||
use crate::video::video_buffer::{VideoBuffer, PixelFormat};
|
||||
use crate::memory::libretro_memory_map::LibRetroMemoryMap;
|
||||
|
||||
struct MyEmulator {
|
||||
pub retro: retro::wrapper::LibretroWrapper,
|
||||
|
@ -60,6 +62,8 @@ struct MyEmulator {
|
|||
|
||||
video_buffer: Option<crate::video::video_buffer::VideoBuffer>,
|
||||
pixel_format: Option<crate::video::video_buffer::PixelFormat>,
|
||||
|
||||
pub memory_map: Option<LibRetroMemoryMap>
|
||||
}
|
||||
|
||||
impl MyEmulator {
|
||||
|
@ -85,6 +89,10 @@ impl MyEmulator {
|
|||
|
||||
let mut gamepads = Gilrs::new().unwrap();
|
||||
|
||||
for (_id, gamepad) in gamepads.gamepads() {
|
||||
println!("{} is {:?}", gamepad.name(), gamepad.power_info());
|
||||
}
|
||||
|
||||
let emu = MyEmulator {
|
||||
retro,
|
||||
frame: 0,
|
||||
|
@ -97,7 +105,8 @@ impl MyEmulator {
|
|||
gamepads,
|
||||
title,
|
||||
video_buffer: None,
|
||||
pixel_format: None
|
||||
pixel_format: None,
|
||||
memory_map: None,
|
||||
};
|
||||
let mut pin_emu = Box::pin(emu);
|
||||
retro::wrapper::set_handler(pin_emu.as_mut());
|
||||
|
@ -301,7 +310,8 @@ impl retro::wrapper::Handler for MyEmulator {
|
|||
}
|
||||
|
||||
fn input_poll(&mut self) {
|
||||
//self.gamepad_subsys.update();
|
||||
// process events, but don't actually need the result. they'll be cached for when input_state reads them
|
||||
while let Some(_) = self.gamepads.next_event() {}
|
||||
}
|
||||
|
||||
fn input_state(&mut self, port: u32, device: InputDeviceId, index: InputIndex) -> i16 {
|
||||
|
@ -315,9 +325,9 @@ impl retro::wrapper::Handler for MyEmulator {
|
|||
}
|
||||
}
|
||||
InputDeviceId::Analog(axis) => {
|
||||
let gilrs_axis = axis_map(index, axis);
|
||||
let (gilrs_axis, scale) = axis_map(index, axis);
|
||||
match gamepad.1.axis_data(gilrs_axis) {
|
||||
Some(data) => data.value() as i16,
|
||||
Some(data) => (data.value() * i16::max_value() as f32) as i16 * scale,
|
||||
None => 0
|
||||
}
|
||||
},
|
||||
|
@ -424,6 +434,11 @@ impl retro::wrapper::Handler for MyEmulator {
|
|||
fn log_print(&mut self, level: retro::ffi::LogLevel, msg: &str) {
|
||||
eprint!("[{:?}] {}", level, msg);
|
||||
}
|
||||
|
||||
fn set_memory_maps(&mut self, ffi_memory_map: MemoryMap) -> bool {
|
||||
self.memory_map = Some(LibRetroMemoryMap::create(&ffi_memory_map));
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() -> failure::Fallible<()> {
|
||||
|
@ -484,15 +499,20 @@ fn button_map(retro_button: &JoypadButton) -> Option<Button> {
|
|||
}
|
||||
}
|
||||
|
||||
fn axis_map(index: InputIndex, axis: AnalogAxis) -> Axis {
|
||||
fn axis_map(index: InputIndex, axis: AnalogAxis) -> (Axis, i16) {
|
||||
match (index, axis) {
|
||||
(InputIndex::Left, AnalogAxis::X) => Axis::LeftStickX,
|
||||
(InputIndex::Left, AnalogAxis::Y) => Axis::LeftStickY,
|
||||
(InputIndex::Right, AnalogAxis::X) => Axis::RightStickX,
|
||||
(InputIndex::Right, AnalogAxis::Y) => Axis::RightStickY,
|
||||
(InputIndex::Left, AnalogAxis::X) => (Axis::LeftStickX, 1),
|
||||
(InputIndex::Left, AnalogAxis::Y) => (Axis::LeftStickY, -1),
|
||||
(InputIndex::Right, AnalogAxis::X) => (Axis::RightStickX, 1),
|
||||
(InputIndex::Right, AnalogAxis::Y) => (Axis::RightStickY, -1),
|
||||
}
|
||||
}
|
||||
|
||||
enum Flip {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
fn build_savestate_filename(game_name: &PathBuf) -> PathBuf {
|
||||
let mut index = 0;
|
||||
|
||||
|
@ -774,10 +794,11 @@ impl TrackedWindow for ConrodWindow {
|
|||
}
|
||||
Event::MainEventsCleared => {
|
||||
let mut emu = self.emu.borrow_mut();
|
||||
let memory = emu.retro.get_memory(2);
|
||||
|
||||
let mut ui = self.ui.set_widgets();
|
||||
widget::Text::new(format!("frame {}", emu.frame).as_str())
|
||||
.middle_of(ui.window)
|
||||
widget::Text::new(format!("frame {}, memory size {:X} {:X}", emu.frame, memory.len(), memory.get(0xA4B3C).unwrap()).as_str())
|
||||
.top_left_of(ui.window)
|
||||
.color(conrod_core::color::WHITE)
|
||||
.font_size(32)
|
||||
.set(self.ids.text, &mut ui);
|
||||
|
@ -787,6 +808,9 @@ impl TrackedWindow for ConrodWindow {
|
|||
.set(self.ids.circle, &mut ui);
|
||||
|
||||
|
||||
//widget::TextEdit::new(format!("{:X?}", memory[]))
|
||||
|
||||
|
||||
self.display.gl_window().window().request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(id) if self.matches_id(id) => {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
use std::convert::TryFrom;
|
||||
use ferretro::retro::ffi::{MemoryMap};
|
||||
|
||||
pub struct LibRetroMemoryMap {
|
||||
regions: Vec<LibRetroMemoryRegion>,
|
||||
}
|
||||
|
||||
impl LibRetroMemoryMap {
|
||||
pub fn get_slice_from_region(&self, offset: usize, length: usize, bank_switch: usize) -> Option<&'static mut [u8]> {
|
||||
match self.find_bank(offset, length, bank_switch) {
|
||||
Some(ptr) => Some(unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, length) }),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
fn find_bank(&self, offset: usize, length: usize, bank_switch: usize) -> Option<*const std::ffi::c_void> {
|
||||
if self.regions.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
for item in self.regions.iter() {
|
||||
if (item.start <= offset) && (offset < item.end) {
|
||||
if offset + length > item.end {
|
||||
println!("({:x}, {:x}) overruns ({:x}, {:x}) memory bank.", offset, length, item.start, item.end);
|
||||
return None;
|
||||
}
|
||||
|
||||
let bank_size = item.end - item.start;
|
||||
let relative_offset = isize::try_from(offset - item.start + (bank_size * bank_switch)).unwrap();
|
||||
return unsafe { Some(item.pointer.offset(relative_offset)) }
|
||||
}
|
||||
}
|
||||
println!("({:x}, {:x}) address range not found in any memory map region.", offset, length);
|
||||
None
|
||||
}
|
||||
|
||||
pub fn create(memory_map: &MemoryMap) -> Self {
|
||||
let mut regions: Vec<LibRetroMemoryRegion> = Vec::new();
|
||||
let num_descriptors = isize::try_from(memory_map.num_descriptors).unwrap();
|
||||
for i in 0..num_descriptors {
|
||||
let region = unsafe { &*memory_map.descriptors.offset(i) };
|
||||
let mut length = region.len;
|
||||
|
||||
if region.select > 0 { // FIXME: hack for oversized SRAM eating addr space
|
||||
length = (!region.select + 1) & 0xffffffff;
|
||||
println!("truncating memory region {:x} from size {:x} to {:x} banks of size {:x}", region.start, region.len, region.len/length, length)
|
||||
}
|
||||
|
||||
regions.push(LibRetroMemoryRegion {
|
||||
start: region.start,
|
||||
end: region.start + length,
|
||||
pointer: region.ptr,
|
||||
})
|
||||
}
|
||||
|
||||
LibRetroMemoryMap {
|
||||
regions: regions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LibRetroMemoryRegion {
|
||||
start: usize,
|
||||
end: usize,
|
||||
pointer: *const std::ffi::c_void,
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
use crate::memory::libretro_memory_map::LibRetroMemoryMap;
|
||||
|
||||
pub struct MemorySliceHandle<'a> {
|
||||
pub offset: usize,
|
||||
pub length: usize,
|
||||
pub bank_switch: usize,
|
||||
pub slice: Option<&'a mut [u8]>,
|
||||
pub last_read_value: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl MemorySliceHandle<'_> {
|
||||
pub fn create(offset: usize, length: usize, bank_switch: usize, memory_map: &LibRetroMemoryMap) -> Self {
|
||||
let slice = memory_map.get_slice_from_region(offset, length, bank_switch);
|
||||
|
||||
MemorySliceHandle {
|
||||
offset: offset,
|
||||
length: length,
|
||||
bank_switch: bank_switch,
|
||||
slice,
|
||||
last_read_value: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_to_slice(&mut self, data: Vec<u8>) -> Result<(), failure::Error> {
|
||||
match &mut self.slice {
|
||||
Some(slice) => {
|
||||
if data.len() != slice.len() {
|
||||
return Err(failure::err_msg("message size and slice size differ."));
|
||||
}
|
||||
|
||||
// if last_read_value is set, update it as the same time that we write back to the emulator.
|
||||
match &mut self.last_read_value {
|
||||
None => {
|
||||
let mut index: usize = 0;
|
||||
for value in data.into_iter(){
|
||||
slice[index] = value;
|
||||
index += 1;
|
||||
}
|
||||
},
|
||||
Some(last_read_value) => {
|
||||
let mut index: usize = 0;
|
||||
for value in data.into_iter(){
|
||||
slice[index] = value;
|
||||
last_read_value[index] = value;
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
None => {
|
||||
println!("slice doesn't exist to write to.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_copy_if_changed_since_last_read(&mut self) -> Option<Vec<u8>> {
|
||||
match &mut self.slice {
|
||||
None => None,
|
||||
Some(data) => match &mut self.last_read_value {
|
||||
None => {
|
||||
// last read value isn't allocated yet
|
||||
let last = data.to_vec();
|
||||
self.last_read_value = Some(last);
|
||||
self.last_read_value.clone()
|
||||
},
|
||||
Some(last) => {
|
||||
let mut index: usize = 0;
|
||||
let mut any_changes = false;
|
||||
|
||||
for value in data.into_iter() {
|
||||
if last[index] != *value {
|
||||
last[index] = *value;
|
||||
any_changes = true;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if any_changes {
|
||||
return Some(last.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod libretro_memory_map;
|
||||
pub mod memory_slice_handle;
|
Loading…
Reference in New Issue