Compare commits
11 Commits
1de59414da
...
419e97af23
Author | SHA1 | Date |
---|---|---|
Vivian Lim | 419e97af23 | |
Vivian Lim | 9ba078111c | |
Vivian Lim | 84d87e21ff | |
Vivian Lim | 54aeb2c0f3 | |
Vivian Lim | 26898d4ec2 | |
Vivian Lim | 295956631a | |
Vivian Lim | d83bbb606a | |
Vivian Lim | 6d6ad9ffee | |
Vivian Lim | b257dc5071 | |
Vivian Lim | cd97095b03 | |
Vivian Lim | 285eb6052e |
|
@ -220,6 +220,7 @@ dependencies = [
|
|||
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ferretro 0.1.0",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -24,4 +24,5 @@ websocket = "0.24.0"
|
|||
ascii = "1.0.0"
|
||||
byte-slice-cast = "0.3.5"
|
||||
|
||||
[dev-dependencies]
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
|
@ -2,16 +2,17 @@ use std::convert::TryFrom;
|
|||
use ferretro::retro::ffi::{MemoryMap};
|
||||
use ferretro::retro::loading::LibretroApi;
|
||||
use crate::emulator::emulator::MyEmulator;
|
||||
use failure::{Error, format_err};
|
||||
|
||||
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]> {
|
||||
pub fn get_slice_from_region(&self, offset: usize, length: usize, bank_switch: usize) -> Result<&'static mut [u8], Error> {
|
||||
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
|
||||
Some(ptr) => Ok(unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, length) }),
|
||||
None => Err(format_err!("Couldn't find memory bank for offset:{}, length:{}, bank_switch:{}", offset, length, bank_switch))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ fn attempt_to_start_sync(emu: &mut MyEmulator, comms_settings: CommunicationSett
|
|||
match &emu.game_info {
|
||||
Some(game_info) => match read_game_name(&game_info.game_name) {
|
||||
Some(which_game) => {
|
||||
let synced_game = create_game(which_game, comms_settings, &emu.memory_map);
|
||||
let synced_game = create_game(which_game, comms_settings, &emu.memory_map)?;
|
||||
emu.begin_sync(synced_game);
|
||||
Ok(())
|
||||
},
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<T> Communication<T> where T: std::marker::Send, T: Serialize, T: Deserializ
|
|||
let mut ws_sender = ws_sender;
|
||||
let mut flush = || -> Result<(), failure::Error> {
|
||||
let msg = from_main.recv()?;
|
||||
println!("sending a message {:?}", &msg);
|
||||
//println!("sending a message {:?}", &msg);
|
||||
ws_sender.send_message(&msg)?;
|
||||
Ok(())
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ impl<T> Communication<T> where T: std::marker::Send, T: Serialize, T: Deserializ
|
|||
match msg {
|
||||
OwnedMessage::Text(text) => match serde_json::from_str(&text) {
|
||||
Ok(m) => {
|
||||
println!("receiving a message {}", &text);
|
||||
//println!("receiving a message {}", &text);
|
||||
to_main.send(m); // todo print an error
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::sync::pokemon_rb::SyncedPokemonRedBlue;
|
|||
use crate::sync::sonic2::SyncedSonic2;
|
||||
use crate::sync::comms::CommunicationSettings;
|
||||
use crate::emulator::libretro_memory_map::LibRetroMemoryMap;
|
||||
use failure::{Error, format_err};
|
||||
|
||||
pub trait SyncableGame {
|
||||
// Update internal state based on tracked memory and the emulator
|
||||
|
@ -29,10 +30,10 @@ pub fn read_game_name(name: &str) -> Option<KnownGames> {
|
|||
}
|
||||
|
||||
|
||||
pub fn create_game(which_game: KnownGames, comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Box<dyn SyncableGame> {
|
||||
match which_game {
|
||||
KnownGames::PokemonRedBlue => Box::from(SyncedPokemonRedBlue::create(comms_settings, memory_map)),
|
||||
KnownGames::Sonic2 => Box::from(SyncedSonic2::create(comms_settings, memory_map)),
|
||||
}
|
||||
pub fn create_game(which_game: KnownGames, comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Result<Box<dyn SyncableGame>, Error> {
|
||||
Ok(match which_game {
|
||||
KnownGames::PokemonRedBlue => Box::from(SyncedPokemonRedBlue::create(comms_settings, memory_map)?),
|
||||
KnownGames::Sonic2 => Box::from(SyncedSonic2::create(comms_settings, memory_map)?),
|
||||
})
|
||||
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
use std::convert::{TryFrom, TryInto};
|
||||
use crate::emulator::libretro_memory_map::LibRetroMemoryMap;
|
||||
use failure::format_err;
|
||||
use failure::{format_err, Error};
|
||||
use byte_slice_cast::*;
|
||||
use std::result::Result;
|
||||
use std::ops::{Index, IndexMut, Range};
|
||||
|
||||
/*
|
||||
pub struct MemoryBackedNumbers<'a, T: 'a + FromByteSlice> {
|
||||
handle: MemorySliceHandle<'a>,
|
||||
handle: MemorySliceHandle,
|
||||
value_type: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
|
@ -18,11 +20,11 @@ impl<'a, T: 'a + FromByteSlice> MemoryBackedNumbers<'a, T> {
|
|||
}
|
||||
|
||||
pub fn get(&mut self) -> Result<&mut [T], failure::Error> {
|
||||
let byte_slices = self.handle.slice.as_mut().unwrap();
|
||||
let byte_slices = self.handle.slice.as_mut();
|
||||
let slices = byte_slices.as_mut_slice_of::<T>()?;
|
||||
Ok(slices)
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
pub fn set(&self, value: T) -> Result<(), failure::Error> {
|
||||
let new_bytes = value.try_into::<&[u8]>()?;
|
||||
|
@ -34,100 +36,305 @@ impl<'a, T: 'a + FromByteSlice> MemoryBackedNumbers<'a, T> {
|
|||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
//}
|
||||
|
||||
pub struct MemorySliceHandle<'a> {
|
||||
pub struct MemorySliceHandle {
|
||||
pub offset: usize,
|
||||
pub length: usize,
|
||||
pub bank_switch: usize,
|
||||
pub slice: Option<&'a mut [u8]>,
|
||||
pub slice: &'static 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);
|
||||
#[derive(Debug)]
|
||||
pub enum MemorySpan {
|
||||
ByAddress(Range<usize>),
|
||||
ByIndex(Range<usize>),
|
||||
}
|
||||
|
||||
MemorySliceHandle {
|
||||
impl Index<MemorySpan> for MemorySliceHandle {
|
||||
type Output = [u8];
|
||||
|
||||
fn index(&self, range: MemorySpan) -> &'static Self::Output {
|
||||
self.subslice(range).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<MemorySpan> for MemorySliceHandle {
|
||||
fn index_mut(&mut self, range: MemorySpan) -> &'static mut Self::Output {
|
||||
self.subslice_mut(range).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for MemorySpan {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
MemorySpan::ByAddress(span) => MemorySpan::ByAddress(span.start..span.end),
|
||||
MemorySpan::ByIndex(span) => MemorySpan::ByIndex(span.start..span.end),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MemorySliceHandle {
|
||||
pub fn create(offset: usize, length: usize, bank_switch: usize, memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
let slice = memory_map.get_slice_from_region(offset, length, bank_switch)?;
|
||||
|
||||
Ok(MemorySliceHandle {
|
||||
offset: offset,
|
||||
length: length,
|
||||
bank_switch: bank_switch,
|
||||
slice,
|
||||
last_read_value: None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_at_offset/*<T: AsMutSliceOf + FromByteSlice>*/(&mut self, offset: usize) -> &u16 {
|
||||
let byte_slices = self.slice.as_mut().unwrap();
|
||||
let slices = byte_slices.as_mut_slice_of::<u16>().unwrap();
|
||||
&slices[0]
|
||||
}
|
||||
|
||||
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 subslice(&self, range: MemorySpan) -> Result<&'static [u8], failure::Error> {
|
||||
match self.map_offsets(range)? {
|
||||
MemorySpan::ByAddress(_) => Err(format_err!("Mapping offsets failed, cannot subslice without indices.")),
|
||||
MemorySpan::ByIndex(indices) => {
|
||||
let len= indices.end - indices.start;
|
||||
Ok(unsafe { std::slice::from_raw_parts(self.slice.as_ptr().offset(indices.start as isize), len) })
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subslice_mut(&mut self, range: MemorySpan) -> Result<&'static mut [u8], failure::Error> {
|
||||
match self.map_offsets(range)? {
|
||||
MemorySpan::ByAddress(_) => Err(format_err!("Mapping offsets failed, cannot subslice without indices.")),
|
||||
MemorySpan::ByIndex(indices) => {
|
||||
let len= indices.end - indices.start;
|
||||
Ok(unsafe { std::slice::from_raw_parts_mut(self.slice.as_mut_ptr().offset(indices.start as isize), len) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_offsets(&self, range: MemorySpan) -> Result<MemorySpan, Error> {
|
||||
match range {
|
||||
MemorySpan::ByAddress(offsets) => {
|
||||
if offsets.start < self.offset // inclusive start bound is before first index
|
||||
|| offsets.end <= self.offset // exclusive end bound is before second index
|
||||
|| offsets.start >= self.offset + self.length // inclusive start bound is beyond final index
|
||||
|| offsets.end > self.offset + self.length { // exclusive end bound is beyond final index + 1
|
||||
return Err(format_err!("Cannot map offsets [{}, {}), they are outside of memory handle at offset {} with length {}.", offsets.start, offsets.end, self.offset, self.length));
|
||||
}
|
||||
|
||||
let start_index = offsets.start - self.offset;
|
||||
let end_index = offsets.end - self.offset;
|
||||
|
||||
Ok(MemorySpan::ByIndex(start_index..end_index))
|
||||
},
|
||||
MemorySpan::ByIndex(_) => Ok(range) // no-op
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_to_slice(&mut self, data: Vec<u8>) -> Result<(), Error> {
|
||||
self.write_to_slice_with_filter(data, |_| true)
|
||||
}
|
||||
|
||||
pub fn write_to_slice_with_filter(&mut self, data: Vec<u8>, index_filter: fn(usize) -> bool) -> Result<(), failure::Error> {
|
||||
if data.len() != self.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(){
|
||||
if index_filter(index){
|
||||
self.slice[index] = value;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
},
|
||||
Some(last_read_value) => {
|
||||
let mut index: usize = 0;
|
||||
for value in data.into_iter(){
|
||||
if index_filter(index) {
|
||||
self.slice[index] = value;
|
||||
last_read_value[index] = value;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_copy_if_changed(&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;
|
||||
match &mut self.last_read_value {
|
||||
None => {
|
||||
// last read value isn't allocated yet
|
||||
let last = self.slice.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;
|
||||
for value in self.slice.into_iter() {
|
||||
if last[index] != *value {
|
||||
last[index] = *value;
|
||||
any_changes = true;
|
||||
}
|
||||
|
||||
if any_changes {
|
||||
return Some(last.clone());
|
||||
}
|
||||
None
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if any_changes {
|
||||
return Some(last.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Mutex;
|
||||
|
||||
fn create_memory_handle(start_address: usize, length: usize) -> MemorySliceHandle {
|
||||
lazy_static! {
|
||||
static ref MEMORY: Mutex<[u8; 0x10000]> = {
|
||||
let mut mem = [0; 0x10000];
|
||||
for i in 0..mem.len() {
|
||||
mem[i] = expected_value_at_index(i);
|
||||
}
|
||||
Mutex::new(mem)
|
||||
};
|
||||
}
|
||||
|
||||
// use transmute to assert that this memory can be borrowed for static lifetime
|
||||
let memory = unsafe { std::mem::transmute::<&mut [u8], &'static mut [u8]>(&mut *MEMORY.lock().unwrap()) };
|
||||
|
||||
let handle = MemorySliceHandle {
|
||||
offset: start_address,
|
||||
length: length,
|
||||
bank_switch: 0,
|
||||
slice: memory,
|
||||
last_read_value: None,
|
||||
};
|
||||
handle
|
||||
}
|
||||
|
||||
fn expected_value_at_index(index: usize) -> u8 {
|
||||
(index % 256) as u8
|
||||
}
|
||||
|
||||
fn expect_failure<T>(result: Result<T, Error>, error_if_succeeded: Error) -> Result<(), Error> {
|
||||
match result{
|
||||
Err(e) => {
|
||||
//println!("Expected failure: {:?}", e);
|
||||
Ok(())
|
||||
}
|
||||
Ok(_) => Err(error_if_succeeded)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_in_entire_memory() -> Result<(), failure::Error>{
|
||||
let mut handle = create_memory_handle(0x0000, 0x10000);
|
||||
|
||||
// look up individual bytes
|
||||
let byte_addresses_to_look_up = vec![0x0000, 0xffff, 0xb008, 0xb03a, 0xb03b, 0xb03c, 0xf704, 0xf705];
|
||||
for address in byte_addresses_to_look_up {
|
||||
let expected_value = expected_value_at_index(address);
|
||||
let span_by_addr = MemorySpan::ByAddress(address..address + 1);
|
||||
|
||||
let slice = handle.subslice(span_by_addr.clone())?;
|
||||
assert_eq!(1, slice.len(), "slice length {} is not one byte", slice.len());
|
||||
assert_eq!(expected_value, slice[0], "looking up address {} in slice, expecting {}, actual {}, length {}", address, expected_value, slice[0], slice.len());
|
||||
|
||||
let mut_slice = handle.subslice_mut(span_by_addr)?;
|
||||
assert_eq!(1, mut_slice.len(), "mut_slice length {} is not one byte", mut_slice.len());
|
||||
assert_eq!(expected_value, mut_slice[0], "looking up address {} in mut_slice, expecting {}, actual {}, length {}", address, expected_value, mut_slice[0], mut_slice.len());
|
||||
}
|
||||
|
||||
// get ranges at the start and end
|
||||
let range_addresses_to_look_up = vec![
|
||||
(0x0000, vec![0,1,2,3,4]),
|
||||
(0xfffc, vec![252, 253, 254, 255])];
|
||||
|
||||
for (range_start_address, expected_values) in range_addresses_to_look_up {
|
||||
let range = MemorySpan::ByAddress(range_start_address..range_start_address + expected_values.len());
|
||||
let slice = handle.subslice(range.clone())?;
|
||||
assert_eq!(expected_values, slice);
|
||||
let mut_slice = handle.subslice_mut(range)?;
|
||||
assert_eq!(expected_values, mut_slice);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_oob_span_in_entire_memory() -> Result<(), Error> {
|
||||
let handle = create_memory_handle(0x0000, 0x10000);
|
||||
|
||||
expect_failure(handle.subslice(MemorySpan::ByAddress(0xfffd..0x10001)),
|
||||
format_err!("Trying to get a subslice that extends past the end of memory should fail"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_in_partial_memory() -> Result<(), Error> {
|
||||
// create a handle which only covers a small amount of memory
|
||||
let mut handle = create_memory_handle(0xb000, 0xb040);
|
||||
|
||||
// look up individual bytes
|
||||
let valid_byte_addresses_to_look_up = vec![0xb000, 0xb040, 0xb008, 0xb009];
|
||||
for address in valid_byte_addresses_to_look_up {
|
||||
let expected_value = expected_value_at_index(address);
|
||||
let span_by_addr = MemorySpan::ByAddress(address..address + 1);
|
||||
|
||||
let slice = handle.subslice(span_by_addr.clone())?;
|
||||
assert_eq!(1, slice.len(), "slice length {} is not one byte", slice.len());
|
||||
assert_eq!(expected_value, slice[0], "looking up address {} in slice, expecting {}, actual {}, length {}", address, expected_value, slice[0], slice.len());
|
||||
|
||||
let mut_slice = handle.subslice_mut(span_by_addr)?;
|
||||
assert_eq!(1, mut_slice.len(), "mut_slice length {} is not one byte", mut_slice.len());
|
||||
assert_eq!(expected_value, mut_slice[0], "looking up address {} in mut_slice, expecting {}, actual {}, length {}", address, expected_value, mut_slice[0], mut_slice.len());
|
||||
}
|
||||
|
||||
// get ranges at the start and end
|
||||
let range_addresses_to_look_up = vec![
|
||||
(0xb000, vec![0,1,2,3,4]),
|
||||
(0xb024, vec![expected_value_at_index(0xb024), expected_value_at_index(0xb025), expected_value_at_index(0xb026)]),
|
||||
(0xb03e, vec![expected_value_at_index(0xb03e), expected_value_at_index(0xb03f), expected_value_at_index(0xb040)])];
|
||||
|
||||
for (range_start_address, expected_values) in range_addresses_to_look_up {
|
||||
let range = MemorySpan::ByAddress(range_start_address..range_start_address + expected_values.len());
|
||||
let slice = handle.subslice(range.clone())?;
|
||||
assert_eq!(expected_values, slice);
|
||||
let mut_slice = handle.subslice_mut(range)?;
|
||||
assert_eq!(expected_values, mut_slice);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_oob_in_partial_memory() -> Result<(), Error> {
|
||||
// create a handle which only covers a small amount of memory
|
||||
let mut handle = create_memory_handle(0xb000, 0x40);
|
||||
|
||||
expect_failure(handle.subslice(MemorySpan::ByAddress(0xfffd..0x10001)),
|
||||
format_err!("Trying to get a subslice that extends past the end of memory should fail"))?;
|
||||
|
||||
expect_failure(handle.subslice(MemorySpan::ByAddress(0xb03c..0xb041)),
|
||||
format_err!("Trying to get a subslice that extends past the end of the slice should fail"))?;
|
||||
|
||||
expect_failure(handle.subslice(MemorySpan::ByAddress(0xafff..0xb001)),
|
||||
format_err!("Trying to get a subslice that extends past the beginning of the slice should fail"))?;
|
||||
|
||||
expect_failure(handle.subslice(MemorySpan::ByAddress(0xaffb..0xafff)),
|
||||
format_err!("Trying to get a subslice that is completely before the slice should fail"))?;
|
||||
|
||||
expect_failure(handle.subslice(MemorySpan::ByAddress(0xb041..0xb045)),
|
||||
format_err!("Trying to get a subslice that is completely after the slice should fail"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
pub trait CopyFromMemory<T> {
|
||||
fn copy_from_memory(&self) -> T;
|
||||
fn write_back(&mut self, source: T);
|
||||
}
|
||||
|
||||
impl CopyFromMemory<[u8; 2]> for &'static mut [u8] {
|
||||
fn copy_from_memory(&self) -> [u8; 2] {
|
||||
let mut result = [0, 0];
|
||||
for i in 0..result.len() {
|
||||
result[i] = self[i];
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn write_back(&mut self, source: [u8; 2]) {
|
||||
for i in 0..source.len() {
|
||||
self[i] = source[i];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,4 +2,7 @@ pub mod pokemon_rb;
|
|||
pub mod sonic2;
|
||||
pub mod comms;
|
||||
pub mod memory_slice_handle;
|
||||
pub mod game;
|
||||
pub mod game;
|
||||
pub mod sparse_vector;
|
||||
pub mod tracked_memory_slice;
|
||||
pub mod memory_traits;
|
|
@ -8,9 +8,10 @@ use crate::sync::comms::Communication;
|
|||
use crate::sync::comms::CommunicationSettings;
|
||||
use crate::sync::memory_slice_handle::MemorySliceHandle;
|
||||
use crate::sync::game::SyncableGame;
|
||||
use failure::{Error, format_err};
|
||||
|
||||
pub struct SyncedPokemonRedBlue {
|
||||
memory_handles: PokemonRedBlueMemoryHandles<'static>,
|
||||
memory_handles: PokemonRedBlueMemoryHandles,
|
||||
battle_context: BattleContext,
|
||||
comms: Communication<PokemonRedBlueMessage>
|
||||
}
|
||||
|
@ -20,7 +21,7 @@ pub struct PokemonRedBlueMessage {
|
|||
active_pkmn: Option<Vec<u8>>
|
||||
}
|
||||
|
||||
impl From<&mut PokemonRedBlueMemoryHandles<'_>> for PokemonRedBlueMessage {
|
||||
impl From<&mut PokemonRedBlueMemoryHandles> for PokemonRedBlueMessage {
|
||||
fn from(src: &mut PokemonRedBlueMemoryHandles) -> Self {
|
||||
PokemonRedBlueMessage {
|
||||
active_pkmn: src.active_pokemon.get_copy_if_changed()
|
||||
|
@ -28,10 +29,10 @@ impl From<&mut PokemonRedBlueMemoryHandles<'_>> for PokemonRedBlueMessage {
|
|||
}
|
||||
}
|
||||
|
||||
struct PokemonRedBlueMemoryHandles<'a> {
|
||||
active_pokemon: MemorySliceHandle<'a>,
|
||||
player_and_party: MemorySliceHandle<'a>,
|
||||
pokemon_out: MemorySliceHandle<'a>,
|
||||
struct PokemonRedBlueMemoryHandles {
|
||||
active_pokemon: MemorySliceHandle,
|
||||
player_and_party: MemorySliceHandle,
|
||||
pokemon_out: MemorySliceHandle,
|
||||
}
|
||||
|
||||
struct BattleContext {
|
||||
|
@ -41,10 +42,7 @@ struct BattleContext {
|
|||
impl SyncableGame for SyncedPokemonRedBlue {
|
||||
fn update_state(&mut self) {
|
||||
let battle_context = BattleContext {
|
||||
active_pokemon: match &self.memory_handles.active_pokemon.slice {
|
||||
Some(d) => num::FromPrimitive::from_u8(d[0xB]).unwrap_or(Pokemon::Unknown),
|
||||
None => Pokemon::Unknown
|
||||
}
|
||||
active_pokemon: num::FromPrimitive::from_u8(self.memory_handles.active_pokemon.slice[0xB]).unwrap_or(Pokemon::Unknown),
|
||||
};
|
||||
|
||||
self.battle_context = battle_context;
|
||||
|
@ -68,13 +66,13 @@ impl SyncableGame for SyncedPokemonRedBlue {
|
|||
}
|
||||
}
|
||||
|
||||
impl PokemonRedBlueMemoryHandles<'_> {
|
||||
pub fn create(memory_map: &LibRetroMemoryMap) -> Self {
|
||||
PokemonRedBlueMemoryHandles {
|
||||
active_pokemon: MemorySliceHandle::create(0xd009, 0x27, 0, memory_map),
|
||||
player_and_party: MemorySliceHandle::create(0xd158, 0x19e, 0, memory_map),
|
||||
pokemon_out: MemorySliceHandle::create(0xcc2f, 0x1, 0, memory_map),
|
||||
}
|
||||
impl PokemonRedBlueMemoryHandles {
|
||||
pub fn create(memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
Ok(PokemonRedBlueMemoryHandles {
|
||||
active_pokemon: MemorySliceHandle::create(0xd009, 0x27, 0, memory_map)?,
|
||||
player_and_party: MemorySliceHandle::create(0xd158, 0x19e, 0, memory_map)?,
|
||||
pokemon_out: MemorySliceHandle::create(0xcc2f, 0x1, 0, memory_map)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,15 +91,15 @@ impl Display for SyncedPokemonRedBlue {
|
|||
}
|
||||
|
||||
impl SyncedPokemonRedBlue {
|
||||
pub fn create(comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Self {
|
||||
SyncedPokemonRedBlue {
|
||||
memory_handles: PokemonRedBlueMemoryHandles::create(memory_map),
|
||||
pub fn create(comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
Ok(SyncedPokemonRedBlue {
|
||||
memory_handles: PokemonRedBlueMemoryHandles::create(memory_map)?,
|
||||
battle_context: BattleContext::default(),
|
||||
comms: Communication::new(comms_settings),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_message(&mut self, msg: PokemonRedBlueMessage) -> Result<(), failure::Error> {
|
||||
fn handle_message(&mut self, msg: PokemonRedBlueMessage) -> Result<(), Error> {
|
||||
match msg.active_pkmn {
|
||||
Some(src) => {
|
||||
println!("message contains a new active pokemon. data: {:?}", &src);
|
||||
|
@ -275,13 +273,11 @@ mod tests {
|
|||
|
||||
fn build_message(em: &MemorySliceHandle) -> PokemonRedBlueMessage {
|
||||
PokemonRedBlueMessage {
|
||||
active_pkmn: match &em.slice {
|
||||
Some(m) => Some(m.to_vec()),
|
||||
None => None
|
||||
}
|
||||
active_pkmn: Some(em.slice.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn serde_mem() -> Result<(), String> {
|
||||
let mut data: [u8; 5] = [1, 2, 3, 4, 5];
|
||||
|
@ -290,7 +286,7 @@ mod tests {
|
|||
offset: 0,
|
||||
length: 0,
|
||||
bank_switch: 0,
|
||||
slice: Some(b),
|
||||
slice: b,
|
||||
last_read_value: None
|
||||
};
|
||||
|
||||
|
@ -305,5 +301,6 @@ mod tests {
|
|||
assert_eq!(deserialized.active_pkmn.unwrap()[0], data[0]);
|
||||
Ok(())
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
use failure::{format_err, Error};
|
||||
use std::default::Default;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt::Display;
|
||||
use crate::emulator::libretro_memory_map::LibRetroMemoryMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
@ -6,65 +8,155 @@ use ferretro::retro::wrapper::LibretroWrapper; // want LibretroWrapper.api : Lib
|
|||
use crate::num::ToPrimitive;
|
||||
use crate::sync::comms::Communication;
|
||||
use crate::sync::comms::CommunicationSettings;
|
||||
use crate::sync::memory_slice_handle::{MemoryBackedNumbers, MemorySliceHandle};
|
||||
use crate::sync::memory_slice_handle::{MemorySliceHandle, MemorySpan};
|
||||
use crate::sync::tracked_memory_slice::TrackedMemorySlice;
|
||||
use crate::sync::game::SyncableGame;
|
||||
use crate::sync::sparse_vector::SparseVector;
|
||||
use crate::sync::memory_traits::CopyFromMemory;
|
||||
|
||||
use byte_slice_cast::*;
|
||||
use std::result::Result;
|
||||
|
||||
pub struct SyncedSonic2 {
|
||||
memory_handles: Sonic2MemoryHandles<'static>,
|
||||
memory_handles: Sonic2MemoryHandles,
|
||||
comms: Communication<Sonic2Message>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Sonic2Message {
|
||||
sonic_pos: Option<Vec<u8>>
|
||||
sonic_pos: SparseVector<u8>,
|
||||
ctrl1_held_press: [u8; 2],
|
||||
ctrl1_held_press_logical: [u8; 2],
|
||||
}
|
||||
|
||||
impl From<&mut Sonic2MemoryHandles<'_>> for Sonic2Message {
|
||||
fn from(src: &mut Sonic2MemoryHandles) -> Self {
|
||||
Sonic2Message {
|
||||
sonic_pos: src.sonic_pos.get_copy_if_changed()
|
||||
}
|
||||
impl TryFrom<&mut Sonic2MemoryHandles> for Sonic2Message {
|
||||
type Error = failure::Error;
|
||||
|
||||
fn try_from(src: &mut Sonic2MemoryHandles) -> Result<Self, Error> {
|
||||
let global_game_state = src.global_game_state.get()?;
|
||||
|
||||
Ok(Sonic2Message {
|
||||
sonic_pos: src.sonic_data.get_changes().expect("error getting changes"),
|
||||
ctrl1_held_press: global_game_state.ctrl1_held_press.copy_from_memory(),
|
||||
ctrl1_held_press_logical: global_game_state.ctrl1_held_press_logical.copy_from_memory(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Sonic2MemoryHandles<'a> {
|
||||
sonic_pos: MemorySliceHandle<'a>,
|
||||
tails_pos: MemorySliceHandle<'a>,
|
||||
sonic_pos2: MemoryBackedNumbers<'a, u16>,
|
||||
|
||||
struct Sonic2MemoryHandles {
|
||||
sonic_data: TrackedMemorySlice,
|
||||
tails_data: MemorySliceHandle,
|
||||
sonic_obj: PlayerCharacterHandle,
|
||||
global_game_state: GlobalGameHandle,
|
||||
}
|
||||
|
||||
struct Object<'a> {
|
||||
handle: MemorySliceHandle<'a>,
|
||||
//x_pos: u16,
|
||||
x_pos: MemoryBackedNumbers<'a, u16>,
|
||||
//y_pos: MemoryBackedNumbers<'a, u16>,
|
||||
struct GlobalGameHandle {
|
||||
handle: MemorySliceHandle,
|
||||
}
|
||||
|
||||
impl<'a> Object<'_> {
|
||||
pub fn create(start_offset: usize, memory_map: &LibRetroMemoryMap) -> Self {
|
||||
/* Object {
|
||||
x_pos: MemoryBackedNumbers::create(start_offset + 0x08, 2, 0, memory_map),
|
||||
y_pos: MemoryBackedNumbers::create(start_offset + 0x0a, 2, 0, memory_map),
|
||||
}
|
||||
*/
|
||||
let handle = MemorySliceHandle::create(start_offset, 10, 0, memory_map);
|
||||
let x_pos = MemoryBackedNumbers::create(start_offset + 0x08, 2, 0, memory_map);
|
||||
//let x_pos = handle.get_at_offset(0x08);
|
||||
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
||||
enum GameMode {
|
||||
SegaScreen = 0x00,
|
||||
TitleScreen = 0x04,
|
||||
Demo = 0x08,
|
||||
Level = 0x0C,
|
||||
SpecialStage = 0x10,
|
||||
ContinueScreen = 0x14,
|
||||
TwoPlayerResults = 0x18,
|
||||
TwoPlayerLevelSelect = 0x1C,
|
||||
EndingSequence = 0x20,
|
||||
OptionsMenu = 0x24,
|
||||
LevelSelect = 0x28,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
|
||||
Object {
|
||||
#[derive(Debug)]
|
||||
struct GlobalGameState {
|
||||
pub game_mode: GameMode,
|
||||
pub ctrl1_held_press: &'static mut [u8],
|
||||
pub ctrl1_held_press_logical: &'static mut [u8],
|
||||
pub ctrl2_held_press: &'static mut [u8],
|
||||
pub ctrl2_held_press_logical: &'static mut [u8],
|
||||
pub tails_control_counter: &'static mut [u8],
|
||||
pub tails_respawn_counter: &'static mut [u8],
|
||||
}
|
||||
|
||||
impl<'a> GlobalGameHandle {
|
||||
pub fn create(memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
let mut handle = MemorySliceHandle::create(0xf600, 0x200, 0, memory_map)?;
|
||||
|
||||
Ok(GlobalGameHandle {
|
||||
handle,
|
||||
x_pos,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&mut self) -> Result<GlobalGameState, failure::Error> {
|
||||
// for some reason, my offsets are off by +1 here.
|
||||
Ok(GlobalGameState {
|
||||
game_mode: num::FromPrimitive::from_u8(self.handle[MemorySpan::ByAddress(0xf601..0xf602)][0]).unwrap_or(GameMode::Unknown),
|
||||
ctrl1_held_press: self.handle.subslice_mut(MemorySpan::ByAddress(0xf604..0xf606))?,
|
||||
ctrl1_held_press_logical: self.handle.subslice_mut(MemorySpan::ByAddress(0xf602..0xf604))?,
|
||||
ctrl2_held_press: self.handle.subslice_mut(MemorySpan::ByAddress(0xf606..0xf608))?,
|
||||
ctrl2_held_press_logical: self.handle.subslice_mut(MemorySpan::ByAddress(0xf66a..0xf66c))?,
|
||||
tails_control_counter: self.handle.subslice_mut(MemorySpan::ByAddress(0xf702..0xf704))?,
|
||||
tails_respawn_counter: self.handle.subslice_mut(MemorySpan::ByAddress(0xf704..0xf706))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct PlayerCharacterHandle {
|
||||
pos_data: &'static mut [u16],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PlayerCharacterState {
|
||||
pub playfield_x: u16,
|
||||
pub playfield_x_subpixel: u16,
|
||||
pub playfield_y: u16,
|
||||
pub playfield_y_subpixel: u16,
|
||||
pub x_vel: u16,
|
||||
pub y_vel: u16,
|
||||
pub inertia: u16,
|
||||
}
|
||||
|
||||
impl PlayerCharacterHandle {
|
||||
pub fn create(start_offset: usize, memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
let mut player_data_handle = MemorySliceHandle::create(start_offset, 0x40, 0, memory_map)?;
|
||||
//let pos_data = player_data_handle.slice[0x08..0x16].as_mut_slice_of::<u16>().unwrap();
|
||||
|
||||
let pos_data = player_data_handle.subslice_mut(MemorySpan::ByIndex(0x08..0x16))?.as_mut_slice_of::<u16>()?;
|
||||
|
||||
Ok(PlayerCharacterHandle {
|
||||
pos_data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&mut self) -> PlayerCharacterState {
|
||||
PlayerCharacterState {
|
||||
playfield_x: self.pos_data[0],
|
||||
playfield_x_subpixel: self.pos_data[1],
|
||||
playfield_y: self.pos_data[2],
|
||||
playfield_y_subpixel: self.pos_data[3],
|
||||
x_vel: self.pos_data[4],
|
||||
y_vel: self.pos_data[5],
|
||||
inertia: self.pos_data[6],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl SyncableGame for SyncedSonic2 {
|
||||
fn update_state(&mut self) {
|
||||
let mut pos = self.memory_handles.sonic_pos2.get().unwrap();
|
||||
println!("sonic pos: {:?}", pos);
|
||||
//let game_state = self.memory_handles.global_game_state.get();
|
||||
//let sonic_state = self.memory_handles.sonic_obj.get();
|
||||
//let mut pos = self.memory_handles.sonic_pos2.get().unwrap();
|
||||
//println!("game: {:?}, sonic: {:?}", game_state, sonic_state);
|
||||
|
||||
println!("sonic data: {:?}", self.memory_handles.sonic_data.handle.slice);
|
||||
|
||||
let mut global_game_state = self.memory_handles.global_game_state.get().unwrap();
|
||||
global_game_state.tails_control_counter.write_back([0x77, 0x77]); // force CPU tails to never take over
|
||||
global_game_state.tails_respawn_counter.write_back([0x00, 0x00]); // force tails to never helicopter-respawn
|
||||
|
||||
/*
|
||||
if let Some(d) = &self.memory_handles.sonic_pos.slice {
|
||||
|
@ -73,12 +165,17 @@ impl SyncableGame for SyncedSonic2 {
|
|||
}
|
||||
|
||||
fn send_state_messages(&mut self) -> Result<(), failure::Error>{
|
||||
let message: Sonic2Message = (&mut self.memory_handles).into();
|
||||
if !message.sonic_pos.is_none() {
|
||||
self.comms.send(message)
|
||||
}
|
||||
else {
|
||||
Ok(())
|
||||
match self.memory_handles.global_game_state.get()?.game_mode {
|
||||
GameMode::Level => {
|
||||
let message: Sonic2Message = (&mut self.memory_handles).try_into()?;
|
||||
if message.sonic_pos.num_chunks > 0{
|
||||
self.comms.send(message)
|
||||
}
|
||||
else {
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
_ => Ok(()) // Don't send messages if we aren't in a level
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,33 +187,54 @@ impl SyncableGame for SyncedSonic2 {
|
|||
}
|
||||
}
|
||||
|
||||
impl Sonic2MemoryHandles<'_> {
|
||||
pub fn create(memory_map: &LibRetroMemoryMap) -> Self {
|
||||
let sonic_pos2 = MemoryBackedNumbers::create(0xb000, 0x10, 0, memory_map);
|
||||
impl Sonic2MemoryHandles {
|
||||
pub fn create(memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
|
||||
Sonic2MemoryHandles {
|
||||
sonic_pos: MemorySliceHandle::create(0xb008, 0x10, 0, memory_map),
|
||||
tails_pos: MemorySliceHandle::create(0xb048, 0x10, 0, memory_map),
|
||||
sonic_pos2,
|
||||
}
|
||||
Ok(Sonic2MemoryHandles {
|
||||
sonic_data: TrackedMemorySlice::create(
|
||||
MemorySliceHandle::create(0xb000, 0x40, 0, memory_map)?,
|
||||
vec![MemorySpan::ByAddress(0xb008..0xb01F),
|
||||
//MemorySpan::ByAddress(0xb022..0xb024),
|
||||
MemorySpan::ByAddress(0xb026..0xb03A),
|
||||
MemorySpan::ByAddress(0xb03c..0xb03d)])?,
|
||||
tails_data: MemorySliceHandle::create(0xb040, 0x40, 0, memory_map)?,
|
||||
sonic_obj: PlayerCharacterHandle::create(0xb000, memory_map)?,
|
||||
global_game_state: GlobalGameHandle::create(memory_map)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncedSonic2 {
|
||||
pub fn create(comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Self {
|
||||
SyncedSonic2 {
|
||||
memory_handles: Sonic2MemoryHandles::create(memory_map),
|
||||
pub fn create(comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Result<Self, Error> {
|
||||
Ok(SyncedSonic2 {
|
||||
memory_handles: Sonic2MemoryHandles::create(memory_map)?,
|
||||
comms: Communication::new(comms_settings),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_message(&mut self, msg: Sonic2Message) -> Result<(), failure::Error> {
|
||||
match msg.sonic_pos {
|
||||
Some(src) => {
|
||||
println!("message contains a new sonic pos. writing to tails. {:?}", &src);
|
||||
self.memory_handles.tails_pos.write_to_slice(src)
|
||||
let mut global_game_state =self.memory_handles.global_game_state.get()?;
|
||||
match global_game_state.game_mode {
|
||||
GameMode::Level => {
|
||||
/*
|
||||
const SKIP_INDICES: [usize; 5]= [0x01, 0x20, 0x21, 0x3e, 0x3f];
|
||||
match msg.sonic_pos {
|
||||
Some(src) => {
|
||||
println!("message contains a new sonic pos. writing to tails. {:?}", &src);
|
||||
self.memory_handles.tails_data.write_to_slice_with_filter(src, |x| SKIP_INDICES.contains(&x))
|
||||
}
|
||||
None => Ok(())
|
||||
}
|
||||
*/
|
||||
msg.sonic_pos.write_into(self.memory_handles.tails_data.slice);
|
||||
|
||||
global_game_state.ctrl2_held_press.write_back(msg.ctrl1_held_press);
|
||||
global_game_state.ctrl2_held_press_logical.write_back(msg.ctrl1_held_press_logical);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
None => Ok(())
|
||||
_ => Ok(()) // drop inbound messages if we aren't in a level.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SparseVector<T: Clone> {
|
||||
contents: Vec<(Range<usize>, Vec<T>)>,
|
||||
pub length: usize,
|
||||
pub num_chunks: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone> SparseVector<T> {
|
||||
pub fn create_from(source: &[T], indices_to_take: Vec<Range<usize>>) -> SparseVector<T> {
|
||||
let mut contents = vec![];
|
||||
for range in indices_to_take {
|
||||
if range.end > source.len() {
|
||||
panic!("range {:?} is outside the bounds of source, which has length {}", range, source.len());
|
||||
}
|
||||
|
||||
contents.push((range.clone(), source[range].into()));
|
||||
}
|
||||
|
||||
let num_chunks = contents.len();
|
||||
|
||||
SparseVector {
|
||||
contents,
|
||||
length: source.len(),
|
||||
num_chunks,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_into(&self, target: &mut [T]) {
|
||||
if target.len() < self.length {
|
||||
panic!("writing into a target (length {}) which is smaller than the source (length {}).", target.len(), self.length);
|
||||
}
|
||||
for (range, values) in &self.contents {
|
||||
if range.end > target.len() {
|
||||
panic!("range {:?} is outside the bounds of target, which has length {}", range, target.len())
|
||||
}
|
||||
let mut index = range.start;
|
||||
for value in values {
|
||||
target[index] = value.clone();
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: Clone + PartialEq> SparseVector<T> {
|
||||
pub fn create_from_diff(source: &[T], diffed: &[T]) -> Result<SparseVector<T>, failure::Error> {
|
||||
SparseVector::create_from_diff_in_spans(source, diffed, vec![(0..source.len())])
|
||||
}
|
||||
|
||||
pub fn create_from_diff_in_spans(source: &[T], diffed: &[T], indices_to_inspect: Vec<Range<usize>>) -> Result<SparseVector<T>, failure::Error> {
|
||||
let mut ranges = Vec::default();
|
||||
for index_range in indices_to_inspect {
|
||||
if index_range.start < 0 || index_range.end > source.len() || index_range.end > diffed.len() {
|
||||
return Err(failure::format_err!("Index range to inspect {:?} extends beyond data (lengths: {}, {})", index_range, source.len(), diffed.len()));
|
||||
|
||||
}
|
||||
let last_index_in_range = index_range.end;
|
||||
|
||||
let mut span_first_index = None;
|
||||
for index in index_range {
|
||||
if source[index] != diffed[index] { // if they differ, start a span or continue an existing one
|
||||
match span_first_index {
|
||||
Some(first) => (), // no action. the current span continues.
|
||||
None => { // begin a new span
|
||||
span_first_index = Some(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // if they're the same, close a span if one is open
|
||||
match span_first_index {
|
||||
Some(first) => { // a diff span has ended
|
||||
ranges.push(first..index);
|
||||
span_first_index = None;
|
||||
},
|
||||
None => () // no action
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(first) = span_first_index {
|
||||
// a span was still open, so close it
|
||||
ranges.push(first..last_index_in_range)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SparseVector::create_from(&source, ranges))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> From<SparseVector<T>> for Vec<T> {
|
||||
fn from(sparse_vector: SparseVector<T>) -> Self {
|
||||
let mut vec = Vec::with_capacity(sparse_vector.length);
|
||||
sparse_vector.write_into(&mut vec);
|
||||
vec
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create_from_vec() {
|
||||
let source = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
let sparse = SparseVector::create_from(&source, vec![(0..1), (3..5)]);
|
||||
|
||||
assert_eq!((0..1, vec![1]), sparse.contents[0]);
|
||||
assert_eq!((3..5, vec![4, 5]), sparse.contents[1]);
|
||||
|
||||
println!("{:?}", sparse);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_into() {
|
||||
let source = vec![1, 2, 3, 4, 5, 6, 7, 8];
|
||||
let mut destination = vec![0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
let sparse = SparseVector::create_from(&source, vec![(0..1), (3..5), (7..8)]);
|
||||
|
||||
sparse.write_into(destination.as_mut());
|
||||
|
||||
let expected = vec![1, 0, 0, 4, 5, 0, 0, 8];
|
||||
assert_eq!(expected, destination);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_diff() {
|
||||
let a = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
let b = vec![1, 0, 0, 0, 5, 0, 0];
|
||||
|
||||
let sparse = SparseVector::create_from_diff(&a, &b).expect("error during diffing");
|
||||
|
||||
println!("{:?}", sparse);
|
||||
assert_eq!((1..4, vec![2, 3, 4]), sparse.contents[0]);
|
||||
assert_eq!((5..7, vec![6, 7]), sparse.contents[1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_diff() {
|
||||
let a = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
let b = vec![1, 0, 0, 0, 5, 0, 0];
|
||||
|
||||
let sparse = SparseVector::create_from_diff_in_spans(&a, &b, vec![(2..4), (6..7)]).expect("error during diffing");
|
||||
|
||||
println!("{:?}", sparse);
|
||||
assert_eq!((2..4, vec![3, 4]), sparse.contents[0]);
|
||||
assert_eq!((6..7, vec![7]), sparse.contents[1]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use std::ops::Range;
|
||||
use crate::sync::memory_slice_handle::{MemorySliceHandle, MemorySpan};
|
||||
use crate::sync::sparse_vector::SparseVector;
|
||||
|
||||
pub struct TrackedMemorySlice {
|
||||
pub handle: MemorySliceHandle,
|
||||
tracked_spans: Vec<Range<usize>>,
|
||||
remembered_state: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TrackedMemorySlice {
|
||||
pub fn create(handle: MemorySliceHandle, tracked_spans: Vec<MemorySpan>) -> Result<TrackedMemorySlice, failure::Error> {
|
||||
let mut index_spans = Vec::new();
|
||||
for span in tracked_spans {
|
||||
match handle.map_offsets(span)? {
|
||||
MemorySpan::ByIndex(range) => index_spans.push(range),
|
||||
MemorySpan::ByAddress(_) => {
|
||||
return Err(failure::format_err!("Failed to map offset"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let remembered_state: Vec<u8> = handle.slice.into();
|
||||
|
||||
Ok(TrackedMemorySlice {
|
||||
handle,
|
||||
tracked_spans: index_spans,
|
||||
remembered_state: remembered_state,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_changes(&mut self) -> Result<SparseVector<u8>, failure::Error>{
|
||||
let changes = SparseVector::create_from_diff_in_spans(self.handle.slice, &self.remembered_state, self.tracked_spans.clone());
|
||||
self.remembered_state = self.handle.slice.into();
|
||||
changes
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue