diff --git a/Cargo.toml b/Cargo.toml index c305a09..7ac68a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ log = "0.3" arrayvec = "0.4" rustc-serialize = "0.3" lazy_static = { version = "0.2", optional = true } +serde = "1.0" +serde_derive = "1.0" [lib] name = "rustation" diff --git a/cdimage b/cdimage index 3140ff5..88d72ae 160000 --- a/cdimage +++ b/cdimage @@ -1 +1 @@ -Subproject commit 3140ff5a081139200a39c5b1a57e29522bfb2a75 +Subproject commit 88d72aefdd709ad8edbf244d192da7ccaf0e50c1 diff --git a/src/assembler.rs b/src/assembler.rs index e54f56e..5362496 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -672,7 +672,7 @@ impl Assembler { .cop_r(cop_r)) } - /// Alignment padding + // Alignment padding Align(o) => for _ in 0..pad_to_order(self.location(), o) { self.emit_byte(0); diff --git a/src/bigarray.rs b/src/bigarray.rs new file mode 100644 index 0000000..62cdc75 --- /dev/null +++ b/src/bigarray.rs @@ -0,0 +1,136 @@ +//! Workaround for big arrays (len > 32), see https://github.com/serde-rs/serde/issues/631 + +use std::fmt; +use std::marker::PhantomData; +use serde::ser::{Serialize, Serializer, SerializeTuple}; +use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess, Error}; + +pub trait BigArray<'de>: Sized { + fn serialize(&self, serializer: S) -> Result + where S: Serializer; + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de>; +} + +macro_rules! big_array { + ($($len:expr,)+) => { + $( + impl<'de, T> BigArray<'de> for [T; $len] + where T: Default + Copy + Serialize + Deserialize<'de> + { + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + let mut seq = serializer.serialize_tuple(self.len())?; + for elem in &self[..] { + seq.serialize_element(elem)?; + } + seq.end() + } + + fn deserialize(deserializer: D) -> Result<[T; $len], D::Error> + where D: Deserializer<'de> + { + struct ArrayVisitor { + element: PhantomData, + } + + impl<'de, T> Visitor<'de> for ArrayVisitor + where T: Default + Copy + Deserialize<'de> + { + type Value = [T; $len]; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("an array of length ", $len)) + } + + fn visit_seq(self, mut seq: A) -> Result<[T; $len], A::Error> + where A: SeqAccess<'de> + { + let mut arr = [T::default(); $len]; + for i in 0..$len { + arr[i] = seq.next_element()? + .ok_or_else(|| Error::invalid_length(i, &self))?; + } + Ok(arr) + } + } + + let visitor = ArrayVisitor { element: PhantomData }; + deserializer.deserialize_tuple($len, visitor) + } + } + )+ + } +} + +big_array! { + 1024, + 2097152, +} + + + +pub trait BigArrayBox<'de>: Sized { + fn serialize(&self, serializer: S) -> Result + where S: Serializer; + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de>; +} + +macro_rules! big_array_box { + ($($len:expr,)+) => { + $( + impl<'de, T> BigArrayBox<'de> for Box<[T; $len]> + where T: Default + Copy + Serialize + Deserialize<'de> + { + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + let mut seq = serializer.serialize_tuple(self.len())?; + for elem in &self[..] { + seq.serialize_element(elem)?; + } + seq.end() + } + + fn deserialize(deserializer: D) -> Result, D::Error> + where D: Deserializer<'de> + { + struct ArrayVisitor { + element: PhantomData, + } + + impl<'de, T> Visitor<'de> for ArrayVisitor + where T: Default + Copy + Deserialize<'de> + { + type Value = Box<[T; $len]>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("an array of length ", $len)) + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where A: SeqAccess<'de> + { + let mut arr = Box::new([T::default(); $len]); + for i in 0..$len { + arr[i] = seq.next_element()? + .ok_or_else(|| Error::invalid_length(i, &self))?; + } + Ok(arr) + } + } + + let visitor = ArrayVisitor { element: PhantomData }; + deserializer.deserialize_tuple($len, visitor) + } + } + )+ + } +} + +big_array_box! { + 2097152, // RAM_SIZE + 524288, // VRAM_SIZE_PIXELS +} diff --git a/src/bios/mod.rs b/src/bios/mod.rs index 31ead2b..4a0442b 100644 --- a/src/bios/mod.rs +++ b/src/bios/mod.rs @@ -1,4 +1,7 @@ -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; +use std::fmt; + +use serde::ser::{Serializer, Serialize, SerializeSeq}; +use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess, Error}; use memory::Addressable; use cdrom::disc::Region; @@ -110,43 +113,55 @@ impl Bios { } } -impl Encodable for Bios { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - // We don't store the full BIOS image in the savestate, mainly - // because I want to be able to share and distribute - // savestates without having to worry about legal - // implications. Let's just serialize the checksum to make - // sure we use the correct BIOS when loading the savestate. - let sha256 = &self.metadata.sha256; +impl Serialize for Bios { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let sha256 = &self.metadata.sha256; - s.emit_seq(sha256.len(), |s| { - for (i, b) in sha256.iter().enumerate() { - try!(s.emit_seq_elt(i, |s| b.encode(s))); + let mut seq = serializer.serialize_seq(Some(sha256.len()))?; + for e in sha256 { + seq.serialize_element(e)?; } - Ok(()) - }) - } + seq.end() + } } -impl Decodable for Bios { - fn decode(d: &mut D) -> Result { - d.read_seq(|d, len| { - let mut sha256 = [0; 32]; - - if len != sha256.len() { - return Err(d.error("wrong BIOM checksum length")); +impl<'de> Deserialize<'de> for Bios { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> + { + struct Sha256Visitor; + + impl<'de> Visitor<'de> for Sha256Visitor + { + type Value = [u8; 32]; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an array of length 32") + } + + fn visit_seq(self, mut seq: A) -> Result<[u8; 32], A::Error> + where + A: SeqAccess<'de>, + { + let mut arr = [0u8; 32]; + for i in 0..32 { + arr[i] = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?; + } + Ok(arr) + } } - for (i, b) in sha256.iter_mut().enumerate() { - *b = try!(d.read_seq_elt(i, |d| Decodable::decode(d))) - } + let sha256: [u8; 32] = deserializer.deserialize_seq(Sha256Visitor)?; - let meta = - match db::lookup_sha256(&sha256) { - Some(m) => m, - None => return Err(d.error("unknown BIOS checksum")), - }; + // try to lookup the sha256 + let meta = db::lookup_sha256(&sha256) + .ok_or_else(|| Error::custom("unknown BIOS checksum"))?; // Create an "empty" BIOS instance, only referencing the // metadata. It's up to the caller to fill the blanks. @@ -155,10 +170,10 @@ impl Decodable for Bios { bios.metadata = meta; Ok(bios) - }) - } + } } + /// Dummy metadata used as a placeholder for dummy BIOS instances static DUMMY_METADATA: Metadata = Metadata { diff --git a/src/cdrom/disc.rs b/src/cdrom/disc.rs index 60442b9..586822c 100644 --- a/src/cdrom/disc.rs +++ b/src/cdrom/disc.rs @@ -5,21 +5,27 @@ use cdimage::msf::Msf; use cdimage::bcd::Bcd; use cdimage::sector::Sector; -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; - use super::iso9660; /// PlayStation disc. /// /// XXX: add support for CD-DA? Not really useful but shouldn't /// be very hard either. We need to support audio tracks anyway... +#[derive(Serialize, Deserialize)] pub struct Disc { /// Image file + #[serde(skip)] + #[serde(default = "default_image")] image: Box, /// Disc serial number serial: SerialNumber, } + +pub fn default_image() -> Box { + Box::new(MissingImage) +} + impl Disc { /// Reify a disc using `image` as a backend. pub fn new(mut image: Box) -> Result { @@ -58,25 +64,6 @@ impl Disc { } } -impl Encodable for Disc { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - // Only encode the serial number - self.serial.encode(s) - } -} - -impl Decodable for Disc { - fn decode(d: &mut D) -> Result { - let serial = try!(SerialNumber::decode(d)); - - // Placeholder disc image - Ok(Disc { - image: Box::new(MissingImage), - serial: serial, - }) - } -} - /// Dummy Image implemementation used when deserializing a Disc. Since /// we don't want to store the entire disc in the image it will be /// missing after a load, it's up to the frontend to make sure to @@ -98,7 +85,7 @@ impl Image for MissingImage { } /// Disc region -#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Region { /// Japan (NTSC): SCEI Japan, @@ -109,7 +96,7 @@ pub enum Region { } /// Disc serial number -#[derive(Copy, Clone, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SerialNumber([u8; 10]); impl SerialNumber { diff --git a/src/cdrom/mod.rs b/src/cdrom/mod.rs index 7ff0966..e389ecd 100644 --- a/src/cdrom/mod.rs +++ b/src/cdrom/mod.rs @@ -15,6 +15,11 @@ //! based on No$'s specs, mednafen's source code and some educated //! guesses. +use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; +use serde::ser::{Serialize, SerializeSeq, SerializeTuple, Serializer}; +use std::fmt; +use std::marker::PhantomData; + use memory::Addressable; use timekeeper::{Peripheral, Cycles}; use interrupt::Interrupt; @@ -32,7 +37,7 @@ pub mod iso9660; mod simple_rand; /// CDROM drive, controller and decoder. -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct CdRom { /// The CD-ROM interface has four memory-mapped registers. The /// first one contains an index which defines the meaning of the @@ -1201,7 +1206,7 @@ impl CdRom { } /// 16byte FIFO used to store command arguments and responses -#[derive(Copy, Clone, Debug, RustcDecodable, RustcEncodable)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] struct Fifo { /// Data buffer buffer: [u8; 16], @@ -1277,7 +1282,7 @@ impl Fifo { buffer!(struct RxBuffer([u8; 2352])); /// CDROM disc state -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] enum ReadState { Idle, /// We're expecting a sector @@ -1294,7 +1299,7 @@ impl ReadState { } /// Description of the sub-CPU processing sequence -#[derive(PartialEq, Eq, Debug, Copy, Clone, RustcDecodable, RustcEncodable)] +#[derive(PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize)] enum SubCpuSequence { /// Sub-CPU waits for commands and async events Idle, @@ -1318,7 +1323,7 @@ enum SubCpuSequence { } /// Sub-CPU state. This is an 8bit microcontroller in charge -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] struct SubCpu { /// Current sub-CPU command state sequence: SubCpuSequence, @@ -1412,7 +1417,7 @@ callback!(struct AsyncResponse(fn (&mut CdRom) -> u32) { }); /// Various IRQ codes used by the sub-CPU -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] enum IrqCode { /// A CD sector has been read and is ready to be processed. SectorReady = 1, @@ -1427,7 +1432,7 @@ enum IrqCode { /// CD-DA Audio playback mixer. The CDROM's audio stereo output can be /// mixed arbitrarily before reaching the SPU stereo input. -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] struct Mixer { cd_left_to_spu_left: u8, cd_left_to_spu_right: u8, diff --git a/src/cdrom/simple_rand.rs b/src/cdrom/simple_rand.rs index 80dc30e..a9c55a5 100644 --- a/src/cdrom/simple_rand.rs +++ b/src/cdrom/simple_rand.rs @@ -9,7 +9,7 @@ /// One of the pitfalls of this algorithm is that if the output is /// used as a raw 32bit random number (without modulo) it'll never /// return 0. -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct SimpleRand { state: u32, } diff --git a/src/cpu/cop0.rs b/src/cpu/cop0.rs index e66837b..d7a2a08 100644 --- a/src/cpu/cop0.rs +++ b/src/cpu/cop0.rs @@ -1,7 +1,7 @@ use interrupt::InterruptState; /// Coprocessor 0: System control -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct Cop0 { /// Cop0 register 12: Status register sr: u32, diff --git a/src/cpu/gte/mod.rs b/src/cpu/gte/mod.rs index b9c1f31..1433fe9 100644 --- a/src/cpu/gte/mod.rs +++ b/src/cpu/gte/mod.rs @@ -7,7 +7,7 @@ mod divider; #[cfg(test)] mod tests; -#[derive(Debug, RustcDecodable, RustcEncodable)] +#[derive(Debug, Serialize, Deserialize)] pub struct Gte { // Control registers /// Screen offset X: signed 16.16 diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index 40d0614..00cb288 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -4,9 +4,14 @@ mod gte; #[cfg(test)] mod tests; -use std::fmt::{Display, Formatter, Error}; +use std::fmt::{Display, Formatter, Error, self}; use std::default::Default; +use std::marker::PhantomData; +use serde::de::{Visitor, SeqAccess}; +use serde::ser::SerializeSeq; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use memory::{Interconnect, Addressable, Byte, HalfWord, Word}; use shared::SharedState; use gpu::renderer::Renderer; @@ -19,7 +24,7 @@ use self::gte::Gte; /// This struct contains the CPU state, including the `Interconnect` /// instance which owns most of the peripherals. -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct Cpu { /// The program counter register: points to the next instruction pc: u32, @@ -1760,7 +1765,7 @@ impl Cpu { } /// Simple wrapper around an instruction word to provide type-safety. -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] struct Instruction(u32); impl Instruction { @@ -1852,11 +1857,11 @@ impl Display for Instruction { } } -#[derive(Clone, Copy, RustcDecodable, RustcEncodable, PartialEq, Eq)] +#[derive(Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] struct RegisterIndex(u32); /// Instruction cache line -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] struct ICacheLine { /// Tag: high 22bits of the address associated with this cacheline /// Valid bits: 3 bit index of the first valid word in line. diff --git a/src/debug_uart.rs b/src/debug_uart.rs index 7386d4c..b1709ab 100644 --- a/src/debug_uart.rs +++ b/src/debug_uart.rs @@ -7,7 +7,7 @@ use memory::Addressable; use shared::SharedState; -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct DebugUart { /// We don't want to display the TX data one character at a time /// so we attempt to line buffer it. diff --git a/src/gpu/mod.rs b/src/gpu/mod.rs index b47e4e8..2b5bc8d 100644 --- a/src/gpu/mod.rs +++ b/src/gpu/mod.rs @@ -1,4 +1,4 @@ -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; +use bigarray::BigArrayBox; use memory::Addressable; use memory::timers::Timers; @@ -11,7 +11,7 @@ use self::renderer::{BlendMode, SemiTransparencyMode, TextureDepth}; pub mod renderer; -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct Gpu { /// Draw mode for rectangles, dithering enable and a few other /// things @@ -1459,7 +1459,7 @@ callback!(struct Gp0Handler(fn (&mut Gpu, &mut Renderer, u32)) { }); /// Interlaced output splits each frame in two fields -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] enum Field { /// Top field (odd lines). Top = 1, @@ -1468,7 +1468,7 @@ enum Field { } /// Video output horizontal resolution -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] struct HorizontalRes(u8); impl HorizontalRes { @@ -1544,7 +1544,7 @@ impl HorizontalRes { } /// Video output vertical resolution -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] enum VerticalRes { /// 240 lines Y240Lines = 0, @@ -1562,7 +1562,7 @@ impl VerticalRes { } /// Video Modes -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] enum VMode { /// NTSC: 480i60H Ntsc = 0, @@ -1571,7 +1571,7 @@ enum VMode { } /// Display area color depth -#[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] enum DisplayDepth { /// 15 bits per pixel D15Bits = 0, @@ -1580,7 +1580,7 @@ enum DisplayDepth { } /// Requested DMA direction. -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] enum DmaDirection { Off = 0, Fifo = 1, @@ -1589,7 +1589,7 @@ enum DmaDirection { } /// Buffer holding multi-word fixed-length GP0 command parameters -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] struct CommandBuffer { /// Command buffer: the longuest possible command is GP0(0x3E) /// which takes 12 parameters @@ -1634,7 +1634,7 @@ impl ::std::ops::Index for CommandBuffer { /// Attributes of the various GP0 commands. Some attributes don't make /// sense for certain commands but those will just be ignored by the /// `parser_callback` -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] struct Gp0Attributes { /// Method called when all the parameters have been received. callback: Callback, @@ -1784,6 +1784,7 @@ fn is_polyline_end_marker(val: u32) -> bool { } /// Buffer holding a portion of the VRAM while it's being transfered +#[derive(Serialize, Deserialize)] struct ImageBuffer { /// Coordinates of the top-left corner in VRAM top_left: (u16, u16), @@ -1792,6 +1793,7 @@ struct ImageBuffer { /// Current write position in the buffer index: u32, /// Pixel buffer. The maximum size is the entire VRAM resolution. + #[serde(with = "BigArrayBox")] buffer: Box<[u16; VRAM_SIZE_PIXELS]>, } @@ -1846,78 +1848,6 @@ impl ImageBuffer { } } -impl Encodable for ImageBuffer { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - - s.emit_struct("ImageBuffer", 3, |s| { - try!(s.emit_struct_field("top_left", 0, - |s| self.top_left.encode(s))); - try!(s.emit_struct_field("resolution", 1, - |s| self.resolution.encode(s))); - - // Lastly we only store the part of `buffer` that's - // actually used, i.e. up to `index`. That also means we - // don't have to store `index` itself. - let len = self.index as usize; - - try!(s.emit_struct_field( - "buffer", 2, - |s| s.emit_seq( - len, - |s| { - for i in 0..len { - try!(s.emit_seq_elt( - i, - |s| self.buffer[i].encode(s))) - } - - Ok(()) - }))); - - Ok(()) - }) - } -} - -impl Decodable for ImageBuffer { - fn decode(d: &mut D) -> Result { - d.read_struct("ImageBuffer", 3, |d| { - let mut ib = ImageBuffer::new(); - - ib.top_left = - try!(d.read_struct_field("top_left", 0, - Decodable::decode)); - - ib.resolution = - try!(d.read_struct_field("resolution", 1, - Decodable::decode)); - let index = - try!(d.read_struct_field( - "buffer", - 2, - |d| { - d.read_seq(|d, len| { - if len >= VRAM_SIZE_PIXELS { - return Err( - d.error("wrong image buffer length")); - } - - for i in 0..len { - ib.buffer[i] = - try!(d.read_seq_elt(i, Decodable::decode)); - } - - Ok(len) - }) - })); - - ib.index = index as u32; - - Ok(ib) - }) - } -} - // Width of the VRAM in 16bit pixels pub const VRAM_WIDTH_PIXELS: u16 = 1024; // Height of the VRAM in lines @@ -1929,7 +1859,7 @@ pub const VRAM_SIZE_PIXELS: usize = /// The are a few hardware differences between PAL and NTSC consoles, /// in particular the pixelclock runs slightly slower on PAL consoles. -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub enum VideoClock { Ntsc, Pal, diff --git a/src/gpu/renderer.rs b/src/gpu/renderer.rs index 179a5f5..e2fc54e 100644 --- a/src/gpu/renderer.rs +++ b/src/gpu/renderer.rs @@ -49,7 +49,7 @@ impl Vertex { } } -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct PrimitiveAttributes { /// If true then the equation defined by `semi_transparency_mode` /// is applied to semi-transparent pixels. @@ -79,7 +79,7 @@ pub struct PrimitiveAttributes { } /// Primitive texturing methods -#[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BlendMode { /// No texture None, @@ -90,7 +90,7 @@ pub enum BlendMode { } /// Semi-transparency modes supported by the PlayStation GPU -#[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum SemiTransparencyMode { /// Source / 2 + destination / 2 Average = 0, @@ -103,7 +103,7 @@ pub enum SemiTransparencyMode { } /// Depth of the pixel values in a texture page -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub enum TextureDepth { /// 4 bits per pixel, paletted T4Bpp = 0, diff --git a/src/interrupt.rs b/src/interrupt.rs index 74adfa0..33ebfda 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -1,6 +1,6 @@ /// The PlayStation supports 10 interrupts #[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub enum Interrupt { /// Display in vertical blanking VBlank = 0, @@ -18,7 +18,7 @@ pub enum Interrupt { PadMemCard = 7, } -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct InterruptState { /// Interrupt status status: u16, diff --git a/src/lib.rs b/src/lib.rs index 9437203..cfd6a55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ extern crate shaman; extern crate cdimage; extern crate arrayvec; extern crate rustc_serialize; +extern crate serde; +#[macro_use] extern crate serde_derive; #[cfg(feature = "trace")] #[macro_use] @@ -32,6 +34,7 @@ mod interrupt; mod timekeeper; mod spu; mod mdec; +mod bigarray; mod version { // VERSION and VERSION_CSTR are generated by build.rs diff --git a/src/mdec/mod.rs b/src/mdec/mod.rs index af77083..5e777bb 100644 --- a/src/mdec/mod.rs +++ b/src/mdec/mod.rs @@ -1,9 +1,15 @@ +use std::fmt; +use std::marker::PhantomData; +use serde::de::{Visitor, SeqAccess}; +use serde::ser::SerializeSeq; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use memory::Addressable; use shared::SharedState; use tracer::module_tracer; /// Motion Decoder (sometimes called macroblock or movie decoder). -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct MDec { dma_in_enable: bool, dma_out_enable: bool, @@ -233,7 +239,7 @@ buffer!(struct QuantMatrix([u8; 64])); buffer!(struct IdctMatrix([i16; 64])); /// Pixel color depths supported by the MDEC -#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcDecodable, RustcEncodable)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] enum OutputDepth { D4Bpp = 0, D8Bpp = 1, @@ -242,7 +248,7 @@ enum OutputDepth { } #[allow(dead_code)] -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] enum BlockType { Y1 = 0, Y2 = 1, diff --git a/src/memory/dma.rs b/src/memory/dma.rs index 0b84381..183e810 100644 --- a/src/memory/dma.rs +++ b/src/memory/dma.rs @@ -4,7 +4,7 @@ use interrupt::Interrupt; use tracer::SizedValue; /// Direct Memory Access -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct Dma { /// DMA control register control: u32, @@ -128,7 +128,7 @@ impl Dma { } /// Per-channel data -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct Channel { /// If true the channel is enabled and the copy can take place /// depending on the condition mandated by the `sync` mode. @@ -300,21 +300,21 @@ impl Channel { } /// DMA transfer direction -#[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Direction { ToRam = 0, FromRam = 1, } /// DMA transfer step -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub enum Step { Increment = 0, Decrement = 1, } /// DMA transfer synchronization mode -#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub enum Sync { /// Transfer starts when the CPU writes to the Trigger bit and /// transfers everything at once @@ -332,7 +332,7 @@ impl From for SizedValue { } /// The 7 DMA ports -#[derive(Clone, Copy, PartialEq, Eq, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)] pub enum Port { /// Macroblock decoder input MDecIn = 0, diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 7c9a0c1..4545fe3 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -21,7 +21,7 @@ use debug_uart::DebugUart; use tracer::module_tracer; /// Global interconnect -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct Interconnect { /// Basic Input/Output memory bios: Bios, @@ -650,7 +650,7 @@ impl Interconnect { } } -#[derive(Clone,Copy, RustcDecodable, RustcEncodable)] +#[derive(Clone,Copy, Serialize, Deserialize)] pub struct CacheControl(u32); impl CacheControl { diff --git a/src/memory/ram.rs b/src/memory/ram.rs index 0734d94..ae337bb 100644 --- a/src/memory/ram.rs +++ b/src/memory/ram.rs @@ -1,12 +1,14 @@ -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; +use bigarray::{BigArrayBox, BigArray}; use super::Addressable; /// RAM +#[derive(Serialize,Deserialize)] pub struct Ram { /// RAM buffer. Boxed in order not to overflow the stack at the /// construction site. Might change once "placement new" is /// available. + #[serde(with = "BigArrayBox")] data: Box<[u8; RAM_SIZE]> } @@ -45,37 +47,11 @@ impl Ram { } } -impl Encodable for Ram { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.data.len(), |s| { - for (i, b) in self.data.iter().enumerate() { - try!(s.emit_seq_elt(i, |s| b.encode(s))); - } - Ok(()) - }) - } -} - -impl Decodable for Ram { - fn decode(d: &mut D) -> Result { - d.read_seq(|d, len| { - if len != RAM_SIZE { - return Err(d.error("wrong RAM length")); - } - - let mut ram = Ram::new(); - - for (i, b) in ram.data.iter_mut().enumerate() { - *b = try!(d.read_seq_elt(i, Decodable::decode)) - } - - Ok(ram) - }) - } -} /// ScratchPad memory +#[derive(Serialize,Deserialize)] pub struct ScratchPad { + #[serde(with = "BigArray")] data: [u8; SCRATCH_PAD_SIZE] } @@ -109,35 +85,6 @@ impl ScratchPad { } } -impl Encodable for ScratchPad { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(SCRATCH_PAD_SIZE, |s| { - for i in 0..SCRATCH_PAD_SIZE { - try!(s.emit_seq_elt(i, |s| self.data[i].encode(s))); - } - Ok(()) - }) - } -} - -impl Decodable for ScratchPad { - fn decode(d: &mut D) -> Result { - d.read_seq(|d, len| { - if len != SCRATCH_PAD_SIZE { - return Err(d.error("wrong SCRATCH_PAD length")); - } - - let mut ram = ScratchPad::new(); - - for (i, b) in ram.data.iter_mut().enumerate() { - *b = try!(d.read_seq_elt(i, Decodable::decode)) - } - - Ok(ram) - }) - } -} - /// Main PlayStation RAM: 2Megabytes const RAM_SIZE: usize = 2 * 1024 * 1024; diff --git a/src/memory/timers.rs b/src/memory/timers.rs index 8781833..c6d1d83 100644 --- a/src/memory/timers.rs +++ b/src/memory/timers.rs @@ -4,7 +4,7 @@ use super::Addressable; use interrupt::Interrupt; use shared::SharedState; -#[derive(Debug, RustcDecodable, RustcEncodable)] +#[derive(Debug, Serialize, Deserialize)] pub struct Timers { /// The three timers. They're mostly identical except that they /// can each select a unique clock source besides the regular @@ -113,7 +113,7 @@ impl Timers { } } -#[derive(Debug, RustcDecodable, RustcEncodable)] +#[derive(Debug, Serialize, Deserialize)] struct Timer { /// Timer instance (Timer0, 1 or 2) instance: Peripheral, @@ -414,7 +414,7 @@ impl Timer { /// Various synchronization modes when the timer is not in /// free-run. -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] enum Sync { /// For timer 1/2: Pause during H/VBlank. For timer 3: Stop counter Pause = 0, @@ -442,7 +442,7 @@ impl Sync { /// Clock source is stored on two bits. The meaning of those bits /// depends on the timer instance. -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] struct ClockSource(u8); impl ClockSource { @@ -486,7 +486,7 @@ impl ClockSource { /// The four possible clock sources for the timers -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] enum Clock { /// The CPU clock at ~33.87MHz SysClock, diff --git a/src/padmemcard/gamepad.rs b/src/padmemcard/gamepad.rs index 85d4126..b7c6ded 100644 --- a/src/padmemcard/gamepad.rs +++ b/src/padmemcard/gamepad.rs @@ -1,7 +1,9 @@ -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; +#[derive(Serialize, Deserialize)] pub struct GamePad { /// Gamepad profile. *Not* stored in the savestate. + #[serde(skip)] + #[serde(default = "default_profile")] profile: Box, /// Counter keeping track of the current position in the reply /// sequence @@ -58,40 +60,6 @@ impl GamePad { } } -impl Encodable for GamePad { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - - // We don't store the Profile in the serialized data, we'll - // let the frontend reset it - s.emit_struct("GamePad", 2, |s| { - - try!(s.emit_struct_field("seq", 0, - |s| self.seq.encode(s))); - try!(s.emit_struct_field("active", 1, - |s| self.active.encode(s))); - - Ok(()) - }) - } -} - -impl Decodable for GamePad { - fn decode(d: &mut D) -> Result { - - d.read_struct("GamePad", 2, |d| { - let mut pad = GamePad::disconnected(); - - pad.seq = - try!(d.read_struct_field("seq", 0, Decodable::decode)); - - pad.active = - try!(d.read_struct_field("active", 1, Decodable::decode)); - - Ok(pad) - }) - } -} - /// Digital buttons on a PlayStation controller. The value assigned to /// each button is the bit position in the 16bit word returned in the /// serial protocol @@ -140,6 +108,10 @@ pub trait Profile { /// Dummy profile emulating an empty pad slot pub struct DisconnectedProfile; +fn default_profile() -> Box { + Box::new(DisconnectedProfile) +} + impl Profile for DisconnectedProfile { fn handle_command(&mut self, _: u8, _: u8) -> (u8, bool) { // The bus is open, no response diff --git a/src/padmemcard/mod.rs b/src/padmemcard/mod.rs index 9d3c80e..a5486a4 100644 --- a/src/padmemcard/mod.rs +++ b/src/padmemcard/mod.rs @@ -10,7 +10,7 @@ use self::gamepad::GamePad; pub mod gamepad; -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct PadMemCard { /// Serial clock divider. The LSB is read/write but is not used, /// This way the hardware divide the CPU clock by half of @@ -393,7 +393,7 @@ impl PadMemCard { /// Identifies the target of the serial communication, either the /// gamepad/memory card port 0 or 1. -#[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] enum Target { PadMemCard1 = 0, PadMemCard2 = 1, @@ -409,7 +409,7 @@ impl Target { } /// Controller transaction state machine -#[derive(Debug, RustcDecodable, RustcEncodable)] +#[derive(Debug, Serialize, Deserialize)] enum BusState { /// Bus is idle Idle, diff --git a/src/parallel_io/mod.rs b/src/parallel_io/mod.rs index f82c3f6..c156cf0 100644 --- a/src/parallel_io/mod.rs +++ b/src/parallel_io/mod.rs @@ -1,7 +1,8 @@ //! Emulation of the parallel interface (the Parallel I/O connector on //! the back of older PlayStation models) -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; +use serde::de::{Deserialize,Deserializer}; +use serde::ser::{Serialize,Serializer}; use memory::Addressable; use shared::SharedState; @@ -39,22 +40,20 @@ impl ParallelIo { } } -impl Encodable for ParallelIo { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - // Since the parallel interface is supposed to support a whole - // bunch of modules (some of which potentially defined outside - // of this crate) it's not trivial to serialize them. For not - // let's ignore it and not include it in the savestate. - s.emit_nil() - } +impl Serialize for ParallelIo { + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + serializer.serialize_unit() + } } -impl Decodable for ParallelIo { - fn decode(d: &mut D) -> Result { - try!(d.read_nil()); - - Ok(ParallelIo::disconnected()) - } +impl<'de> Deserialize<'de> for ParallelIo { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> + { + Ok(ParallelIo::disconnected()) + } } /// Since there can be all sorts of hardware connected to the Parallel diff --git a/src/serializer.rs b/src/serializer.rs index 3929358..84ac05f 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -149,39 +149,58 @@ macro_rules! buffer { } } - impl ::rustc_serialize::Encodable for $st { - fn encode(&self, s: &mut S) -> Result<(), S::Error> - where S: ::rustc_serialize::Encoder { - - s.emit_seq($len, |s| { - for (i, b) in self.0.iter().enumerate() { - try!(s.emit_seq_elt(i, |s| b.encode(s))); + impl Serialize for $st { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.0.len()))?; + for e in self.0.iter() { + seq.serialize_element(e)?; } - Ok(()) - }) - } + seq.end() + } } - impl ::rustc_serialize::Decodable for $st { - fn decode(d: &mut D) -> Result<$st, D::Error> - where D: ::rustc_serialize::Decoder { - - use ::rustc_serialize::Decodable; - - d.read_seq(|d, len| { - if len != $len { - return Err(d.error("wrong buffer length")); - } - - let mut b = $st::new(); - - for (i, b) in b.0.iter_mut().enumerate() { - *b = try!(d.read_seq_elt(i, Decodable::decode)) + impl<'de> Deserialize<'de> for $st { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CustomVisitor { + element: PhantomData, } - Ok(b) - }) - } + impl<'de, T> Visitor<'de> for CustomVisitor + where + T: Default + Copy + Deserialize<'de>, + { + type Value = $st; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("an array of length ", $len)) + } + + fn visit_seq(self, mut seq: A) -> Result<$st, A::Error> + where + A: SeqAccess<'de>, + { + let mut arr = $st([Default::default(); $len]); + for i in 0..$len { + arr.0[i] = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?; + } + Ok(arr) + } + } + + let visitor: CustomVisitor<$elem> = CustomVisitor { + element: PhantomData, + }; + deserializer.deserialize_seq(visitor) + } } + ); } diff --git a/src/shared.rs b/src/shared.rs index 2b837fc..5ffb3c6 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -2,7 +2,7 @@ use timekeeper::TimeKeeper; use interrupt::InterruptState; /// State shared between various modules -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct SharedState { tk: TimeKeeper, irq_state: InterruptState, @@ -40,7 +40,7 @@ impl SharedState { } /// Struct holding various counters for debugging and profiling -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct Counters { /// Increments at each frame drawn to the display. It will wrap in /// a little more than 2 years at 60Hz. @@ -65,7 +65,7 @@ impl Counters { } /// Simple wrapper around a `u32` to implement a counter interface -#[derive(Copy, Clone, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Deserialize, Serialize)] pub struct Counter(u32); impl Counter { diff --git a/src/spu/mod.rs b/src/spu/mod.rs index 125dcb2..bf824a9 100644 --- a/src/spu/mod.rs +++ b/src/spu/mod.rs @@ -1,21 +1,32 @@ -use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; - use memory::Addressable; /// Sound Processing Unit +#[derive(Serialize, Deserialize)] pub struct Spu { /// Most of the SPU registers are not updated by the hardware, /// their value is just moved to the internal registers when /// needed. Therefore we can emulate those registers like a RAM of /// sorts. + #[serde(skip)] + #[serde(default = "default_shadow_register")] shadow_registers: [u16; 0x100], /// SPU RAM: 256k 16bit samples + #[serde(skip)] + #[serde(default = "default_ram")] ram: Box<[u16; 256 * 1024]>, /// Write pointer in the SPU RAM ram_index: u32, } +fn default_shadow_register() -> [u16; 0x100] { + [0u16; 0x100] +} + +fn default_ram() -> Box<[u16; 256 * 1024]> { + Box::new([0u16; 256 * 1024]) +} + impl Spu { pub fn new() -> Spu { Spu { @@ -222,97 +233,6 @@ impl Spu { } } -impl Encodable for Spu { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Spu", 3, |s| { - try!(s.emit_struct_field( - "shadow_registers", 0, - |s| s.emit_seq( - 0x100, - |s| { - for i in 0..0x100 { - try!(s.emit_seq_elt( - i, - |s| self.shadow_registers[i].encode(s))); - } - - Ok(()) - }))); - - try!(s.emit_struct_field( - "ram", 1, - |s| s.emit_seq( - 256 * 1024, - |s| { - for i in 0..(256 * 1024) { - try!(s.emit_seq_elt( - i, - |s| self.ram[i].encode(s))); - } - - Ok(()) - }))); - - try!(s.emit_struct_field("ram_index", 2, - |s| self.ram_index.encode(s))); - - Ok(()) - }) - } -} - -impl Decodable for Spu { - fn decode(d: &mut D) -> Result { - d.read_struct("Spu", 3, |d| { - let mut spu = Spu::new(); - - try!(d.read_struct_field( - "shadow_registers", 0, - |d| { - d.read_seq(|d, len| { - if len != 0x100 { - return Err( - d.error("wrong SPU shadow registers length")); - } - - for i in 0..len { - spu.shadow_registers[i] = - try!(d.read_seq_elt(i, Decodable::decode)); - } - - Ok(()) - }) - })); - - - try!(d.read_struct_field( - "ram", 1, - |d| { - d.read_seq(|d, len| { - if len != 256 * 1024 { - return Err( - d.error("wrong SPU RAM length")); - } - - for i in 0..len { - spu.ram[i] = - try!(d.read_seq_elt(i, Decodable::decode)); - } - - Ok(()) - }) - })); - - spu.ram_index = - try!(d.read_struct_field("ram_index", - 2, - Decodable::decode)); - - Ok(spu) - }) - } -} - mod regmap { //! SPU register map: offset from the base in number of //! *halfwords* diff --git a/src/timekeeper.rs b/src/timekeeper.rs index e418435..bca5b3a 100644 --- a/src/timekeeper.rs +++ b/src/timekeeper.rs @@ -7,7 +7,7 @@ use std::{fmt}; /// List of all peripherals requiring a TimeSheet. The value of the /// enum is used as the index in the timesheet table -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum Peripheral { /// Graphics Processing Unit Gpu, @@ -23,7 +23,7 @@ pub enum Peripheral { /// Struct keeping track of the various peripheral's emulation advancement. -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Serialize, Deserialize)] pub struct TimeKeeper { /// Counter keeping track of the current date. Unit is a period of /// the CPU clock at 33.8685MHz (~29.5ns) @@ -128,7 +128,7 @@ impl fmt::Display for TimeKeeper { } -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] /// Struct used to keep track of individual peripherals struct TimeSheet { /// Date of the last synchronization @@ -178,7 +178,7 @@ pub type Cycles = u64; /// Fixed point representation of a cycle counter used to store /// non-integer cycle counts. Required because the CPU and GPU clocks /// have a non-integer ratio. -#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct FracCycles(Cycles); impl FracCycles {