diff --git a/Cargo.lock b/Cargo.lock index 262c9cf..74dc0d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 97bda47..7a71d0a 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,9 +1,10 @@ pub mod args; use std::{ fs::{self, File}, - io::{BufReader, Read}, + io::{BufReader, Read, Write}, path::Path, process::exit, + str, }; use args::Args; @@ -13,12 +14,31 @@ use models::{ key::Key, model::Model, resources::types::{extention_to_resource_type, ResourceType}, + spell::Spell, tlk::Lookup, }; use erased_serde::Serializer; -fn write_file(path: &Path, model: std::rc::Rc) { +fn write_file(path: &Path, extension: &str, buffer: &[u8]) { + let file_name = Path::new(path.file_stem().unwrap_or_default()).with_extension(extension); + if let Ok(mut file) = File::create(file_name) { + if let Err(err) = file.write_all(buffer) { + println!("Error: {}", err); + } + } +} + +fn json_back_to_ie_type(path: &Path) { + let file_contents = read_file(path); + if let Ok(spell) = serde_json::from_slice::(&file_contents) { + write_file(path, "spl", &spell.to_bytes()) + } else { + panic!("Could not convert back to model") + } +} + +fn write_model(path: &Path, model: std::rc::Rc) { let file_name = Path::new(path.file_stem().unwrap_or_default()).with_extension("json"); if let Ok(file) = File::create(file_name) { let mut json = serde_json::Serializer::new(file); @@ -29,7 +49,7 @@ fn write_file(path: &Path, model: std::rc::Rc) { } } -fn from_file(path: &Path) -> Vec { +fn read_file(path: &Path) -> Vec { let file = File::open(path).unwrap_or_else(|_| panic!("Could not open file: {:#?}", path)); let mut reader = BufReader::new(file); let mut buffer = Vec::new(); @@ -40,7 +60,7 @@ fn from_file(path: &Path) -> Vec { } fn parse_tlk_file(path: &Path) -> Lookup { - let buffer = from_file(path); + let buffer = read_file(path); Lookup::new(&buffer) } @@ -51,14 +71,14 @@ fn parse_key_file(path: &Path, buffer: &[u8]) -> Vec { key.bif_entries .iter() .map(|bif_ref| { - let buffer = from_file(&parent.join(bif_ref.name.to_string()).with_extension("bif")); + let buffer = read_file(&parent.join(bif_ref.name.to_string()).with_extension("bif")); Biff::new(&buffer) }) .collect() } fn get_model_from_file(path: &Path, json: bool) -> Vec { - let buffer = from_file(path); + let buffer = read_file(path); let extention = path .extension() .unwrap_or_default() @@ -71,15 +91,18 @@ fn get_model_from_file(path: &Path, json: bool) -> Vec { if resource_type == ResourceType::NotFound { return match extention.as_str() { "key" => parse_key_file(path, &buffer), - "biff" => vec![Biff::new(&from_file(path))], - "json" => panic!(), + "biff" => vec![Biff::new(&read_file(path))], + "json" => { + json_back_to_ie_type(&path); + exit(0) + } _ => panic!("Unprocessable file type: {:?}", path.as_os_str()), }; } let model = from_buffer(&buffer, resource_type).expect("Could not parse file"); if json { - write_file(path, model); + write_model(path, model); } else { println!("{:?}", model); } diff --git a/gate1.spl b/gate1.spl new file mode 100644 index 0000000..e8a24cb Binary files /dev/null and b/gate1.spl differ diff --git a/models/Cargo.toml b/models/Cargo.toml index 3c65d5b..dabaf22 100644 --- a/models/Cargo.toml +++ b/models/Cargo.toml @@ -7,4 +7,9 @@ edition = "2021" [dependencies] erased-serde = "0.3" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.189", features = ["derive"] } + +[dev-dependencies] +pretty_assertions = "1.3.0" +serde_json = "1.0.94" +tempfile = "3" diff --git a/models/src/area.rs b/models/src/area.rs index 6c2abe9..a07fbc1 100644 --- a/models/src/area.rs +++ b/models/src/area.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::header::Header; use crate::item_table::ItemReferenceTable; @@ -9,7 +9,8 @@ use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; use crate::{common::fixed_char_array::FixedCharSlice, game::GlobalVarriables}; -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Area { pub header: FileHeader, pub actors: Vec, @@ -108,11 +109,15 @@ impl Model for Area { fn name(&self, _lookup: &Lookup) -> String { self.header.area_wed.to_string().replace(".WED", ".ARE") } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Header #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct FileHeader { pub header: Header<4, 4>, pub area_wed: FixedCharSlice<8>, @@ -173,12 +178,12 @@ pub struct FileHeader { // bgee and bg2:tob pub rest_movie_night: FixedCharSlice<8>, #[serde(skip_serializing)] - _unused: [u8; 56], + _unused: FixedCharSlice<56>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Actor #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] pub struct Actor { pub name: FixedCharSlice<32>, pub current_x_coordinate: u16, @@ -209,12 +214,12 @@ pub struct Actor { pub offset_to_cre_structure: u32, pub size_of_stored_cre_structure: u32, #[serde(skip_serializing)] - pub _unused_2: [u8; 128], + pub _unused_2: FixedCharSlice<128>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Info #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Region { pub name: FixedCharSlice<32>, pub region_type: u16, @@ -249,7 +254,7 @@ pub struct Region { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Spawn #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct SpawnPoint { pub name: FixedCharSlice<32>, pub x_coordinate: u16, @@ -301,24 +306,24 @@ pub struct SpawnPoint { // Offset 0x006c pub spawn_weight_of_10th_creature_slot: u8, #[serde(skip_serializing)] - pub unused: [u8; 38], + pub unused: FixedCharSlice<38>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Entrance #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Entrance { pub name: FixedCharSlice<32>, pub x_coordinate: u16, pub y_coordinate: u16, pub orientation: u16, #[serde(skip_serializing)] - pub unused: [u8; 66], + pub unused: FixedCharSlice<66>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Container #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Container { pub name: FixedCharSlice<32>, pub x_coordinate: u16, @@ -349,22 +354,22 @@ pub struct Container { pub break_difficulty: u32, pub lockpick_string: FixedCharSlice<4>, #[serde(skip_serializing)] - pub unused: [u8; 56], + pub unused: FixedCharSlice<56>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Item #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct AreaItem(pub ItemReferenceTable); // An array of points used to create the outlines of regions and containers. Elements are 16-bit words stored x0, y0, x1, y1 etc. #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Vertice(pub u16); // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Ambient #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Ambients { pub name: FixedCharSlice<32>, pub x_coordinate: u16, @@ -391,22 +396,22 @@ pub struct Ambients { pub ambient_appearence_schedule: u32, pub flags: u32, #[serde(skip_serializing)] - _unused_2: [u8; 64], + _unused_2: FixedCharSlice<64>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Variable #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct AreaVarriable(pub GlobalVarriables); // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Explored #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct ExploredBitmask(pub u8); // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Door #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Door { pub name: FixedCharSlice<32>, // Link with WED @@ -450,7 +455,7 @@ pub struct Door { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Anim #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Animation { pub name: FixedCharSlice<32>, pub x_coordinate: u16, @@ -477,7 +482,7 @@ pub struct Animation { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Automap #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct AutomapNotesBGEE { pub x_coordinate: u16, pub y_coordinate: u16, @@ -488,12 +493,12 @@ pub struct AutomapNotesBGEE { pub colour: u16, pub note_count: u32, #[serde(skip_serializing)] - pub unused: [u8; 36], + pub unused: FixedCharSlice<36>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_TiledObj #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct TiledObject { pub name: FixedCharSlice<32>, pub tile_id: FixedCharSlice<8>, @@ -503,12 +508,12 @@ pub struct TiledObject { pub count_of_closed_search_squares: u16, pub offset_to_closed_search_squares: u32, #[serde(skip_serializing)] - pub unused: [u8; 48], + pub unused: FixedCharSlice<48>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_ProjTraps #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct ProjectileTrap { pub projectile_resref: FixedCharSlice<8>, pub effect_block_offset: u32, @@ -525,7 +530,7 @@ pub struct ProjectileTrap { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Song_entries #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct SongEntry { pub day_song_reference_number: u32, pub night_song_reference_number: u32, @@ -545,7 +550,7 @@ pub struct SongEntry { pub main_night_ambient_volume_percent: u32, pub reverb_or_unused: u32, #[serde(skip_serializing)] - pub unused: [u8; 60], + pub unused: FixedCharSlice<60>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/are_v1.htm#formAREAV1_0_Rest_Interruptions @@ -566,5 +571,5 @@ pub struct RestInterruption { pub probability_day_per_hour: u16, pub probability_night_per_hour: u16, #[serde(skip_serializing)] - pub unused: [u8; 56], + pub unused: FixedCharSlice<56>, } diff --git a/models/src/biff.rs b/models/src/biff.rs index 42d5936..98917d9 100644 --- a/models/src/biff.rs +++ b/models/src/biff.rs @@ -7,6 +7,7 @@ use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::{from_buffer, model::Model, resources::types::ResourceType}; // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/bif_v1.htm +#[repr(C)] #[derive(Debug)] pub struct Biff { pub header: BiffHeader, diff --git a/models/src/bio.rs b/models/src/bio.rs index 06516f0..293d1e6 100644 --- a/models/src/bio.rs +++ b/models/src/bio.rs @@ -1,15 +1,16 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; -use crate::{common::varriable_char_array::VarriableCharArray, model::Model, tlk::Lookup}; +use crate::{common::varriable_char_array::VariableCharArray, model::Model, tlk::Lookup}; -#[derive(Debug, Serialize)] -pub struct Biography(pub VarriableCharArray); +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] +pub struct Biography(pub VariableCharArray); impl Model for Biography { fn new(buffer: &[u8]) -> Self { - Self(VarriableCharArray(buffer.into())) + Self(VariableCharArray(buffer.into())) } fn create_as_rc(buffer: &[u8]) -> Rc { @@ -19,4 +20,8 @@ impl Model for Biography { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } diff --git a/models/src/character.rs b/models/src/character.rs index e768ef4..cb22345 100644 --- a/models/src/character.rs +++ b/models/src/character.rs @@ -1,16 +1,15 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; +use crate::common::fixed_char_array::FixedCharSlice; +use crate::common::signed_fixed_char_array::SignedFixedCharSlice; use crate::resources::utils::copy_buff_to_struct; use crate::tlk::Lookup; -use crate::{ - common::{fixed_char_array::FixedCharSlice, header::Header}, - creature::Creature, - model::Model, -}; +use crate::{common::header::Header, creature::Creature, model::Model}; -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct ExpandedCharacter { pub character: BGCharacter, pub creature: Creature, @@ -38,10 +37,14 @@ impl Model for ExpandedCharacter { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct BGCharacter { pub header: Header<4, 4>, pub name: FixedCharSlice<32>, @@ -55,9 +58,9 @@ pub struct BGCharacter { pub show_quick_weapon_2: i16, pub show_quick_weapon_3: i16, pub show_quick_weapon_4: i16, - pub quick_spell_1_resource: [i8; 8], - pub quick_spell_2_resource: [i8; 8], - pub quick_spell_3_resource: [i8; 8], + pub quick_spell_1_resource: SignedFixedCharSlice<8>, + pub quick_spell_2_resource: SignedFixedCharSlice<8>, + pub quick_spell_3_resource: SignedFixedCharSlice<8>, pub index_into_slot_ids_for_quick_item_1: i16, pub index_into_slot_ids_for_quick_item_2: i16, pub index_into_slot_ids_for_quick_item_3: i16, diff --git a/models/src/common/feature_block.rs b/models/src/common/feature_block.rs index f1014af..d31c81e 100644 --- a/models/src/common/feature_block.rs +++ b/models/src/common/feature_block.rs @@ -1,9 +1,9 @@ -use serde::Serialize; +use serde::{Deserialize, Serialize}; use super::fixed_char_array::FixedCharSlice; #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct FeatureBlock { pub opcode_number: u16, pub target_type: u8, diff --git a/models/src/common/fixed_char_array.rs b/models/src/common/fixed_char_array.rs index 337476f..c13d525 100644 --- a/models/src/common/fixed_char_array.rs +++ b/models/src/common/fixed_char_array.rs @@ -1,8 +1,6 @@ use std::fmt::{Debug, Display}; -use serde::{Serialize, Serializer}; - -use super::varriable_char_array::VarriableCharArray; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; #[repr(C, packed)] #[derive(PartialEq, Eq, Copy, Clone)] @@ -52,33 +50,52 @@ impl Debug for FixedCharSlice<{ N }> { } } -impl TryFrom<&VarriableCharArray> for FixedCharSlice { - type Error = String; +impl Serialize for FixedCharSlice { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq(self.0) + } +} + +struct FixedCharSliceVisitor; + +impl<'de, const N: usize> Visitor<'de> for FixedCharSliceVisitor { + type Value = FixedCharSlice; - fn try_from(value: &VarriableCharArray) -> Result { - if value.0.len() != N { - return Err( - "Attempt to move a larger vector to a smaller destination aborting".to_string(), - ); + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "struct FixedCharSlice") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut destination = [0; N]; + let mut counter = 0; + while let Ok(Some(item)) = seq.next_element::() { + destination[counter] = item; + counter += 1; } - Ok(Self(unsafe { - std::ptr::read(value.0.as_ptr() as *const _) - })) + Ok(FixedCharSlice(destination)) } } -impl Serialize for FixedCharSlice { - fn serialize(&self, serializer: S) -> Result +impl<'de, const N: usize> Deserialize<'de> for FixedCharSlice { + fn deserialize(deserializer: D) -> Result where - S: Serializer, + D: serde::Deserializer<'de>, { - serializer.collect_str(&format!("{}", self)) + deserializer.deserialize_seq(FixedCharSliceVisitor) } } #[cfg(test)] mod tests { + use std::io::{BufReader, Read, Seek, SeekFrom, Write}; + use super::*; #[test] fn valid_from_bytes_to_fixed_char_slice() { @@ -95,7 +112,7 @@ mod tests { fn valid_longer_from_bytes_to_fixed_char_slice() { let from = "BALDUR".as_bytes(); assert_eq!( - FixedCharSlice::<7>::try_from(from) + FixedCharSlice::<6>::try_from(from) .unwrap_or_default() .to_string(), "BALDUR" @@ -112,4 +129,23 @@ mod tests { "BALDU" ) } + + #[test] + fn deserialize_serialize_deserialize() { + let expected = FixedCharSlice::<6>::from("BALDUR"); + let value = serde_json::to_string(&expected).unwrap(); + + let mut file = tempfile::tempfile().unwrap(); + file.write_all(value.as_bytes()).unwrap(); + + file.seek(SeekFrom::Start(0)).unwrap(); + let mut buffer = Vec::new(); + let mut reader = BufReader::new(file); + reader + .read_to_end(&mut buffer) + .expect("Could not read to buffer"); + + let result: FixedCharSlice<6> = serde_json::from_slice(&buffer).unwrap(); + assert_eq!(expected, result) + } } diff --git a/models/src/common/fixed_char_nd_array.rs b/models/src/common/fixed_char_nd_array.rs index fe7bcda..cd397a9 100644 --- a/models/src/common/fixed_char_nd_array.rs +++ b/models/src/common/fixed_char_nd_array.rs @@ -1,6 +1,6 @@ use std::fmt::{Debug, Display}; -use serde::{ser::SerializeSeq, Serialize, Serializer}; +use serde::{de::Visitor, ser::SerializeSeq, Deserialize, Serialize, Serializer}; use super::fixed_char_array::FixedCharSlice; @@ -54,6 +54,7 @@ impl Serialize for FixedCharNDArray { where S: Serializer, { + // TODO: Fix this crap let mut seq = serializer.serialize_seq(Some({ self.0 }.len())).unwrap(); for char_slice in self.0 { let _ = seq.serialize_element(&char_slice); @@ -62,9 +63,43 @@ impl Serialize for FixedCharNDArray { } } +struct FixedCharNDArrayVisitor; + +impl<'de, const N: usize, const M: usize> Visitor<'de> for FixedCharNDArrayVisitor { + type Value = FixedCharNDArray; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "struct FixedCharNDArray") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut destination = FixedCharNDArray::::default(); + let mut counter = 0; + while let Ok(Some(item)) = seq.next_element::>() { + destination.0[counter] = item; + counter += 1; + } + Ok(destination) + } +} + +impl<'de, const N: usize, const M: usize> Deserialize<'de> for FixedCharNDArray { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(FixedCharNDArrayVisitor) + } +} + #[cfg(test)] mod tests { + use std::io::{BufReader, Read, Seek, SeekFrom, Write}; + use super::*; #[test] fn valid_from_bytes() { @@ -110,4 +145,24 @@ mod tests { "BALDU, RBALD, URBAL, DURBA" ) } + + #[test] + fn deserialize_serialize_deserialize() { + let from = "BALDUR".as_bytes(); + let expected = FixedCharNDArray::<3, 2>::from(from); + let value = serde_json::to_string(&expected).unwrap(); + + let mut file = tempfile::tempfile().unwrap(); + file.write_all(value.as_bytes()).unwrap(); + + file.seek(SeekFrom::Start(0)).unwrap(); + let mut buffer = Vec::new(); + let mut reader = BufReader::new(file); + reader + .read_to_end(&mut buffer) + .expect("Could not read to buffer"); + + let result: FixedCharNDArray<3, 2> = serde_json::from_slice(&buffer).unwrap(); + assert_eq!(expected, result) + } } diff --git a/models/src/common/header.rs b/models/src/common/header.rs index 095764b..10f3a04 100644 --- a/models/src/common/header.rs +++ b/models/src/common/header.rs @@ -1,9 +1,9 @@ -use serde::Serialize; +use serde::{Deserialize, Serialize}; use super::fixed_char_array::FixedCharSlice; #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Header { pub signature: FixedCharSlice, pub version: FixedCharSlice, diff --git a/models/src/common/signed_fixed_char_array.rs b/models/src/common/signed_fixed_char_array.rs index 3d194c7..28bb4cf 100644 --- a/models/src/common/signed_fixed_char_array.rs +++ b/models/src/common/signed_fixed_char_array.rs @@ -3,7 +3,7 @@ use std::{ slice, }; -use serde::{Serialize, Serializer}; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; #[repr(C, packed)] #[derive(PartialEq, Eq, Copy, Clone)] @@ -29,6 +29,20 @@ impl From<&[i8]> for SignedFixedCharSlice { } } +impl From<&[u8]> for SignedFixedCharSlice { + fn from(value: &[u8]) -> Self { + let mut destination = [0; N]; + for (counter, byte) in value.iter().enumerate() { + if counter >= destination.len() { + // TODO: Throw a warning here + break; + } + destination[counter] = i8::try_from(*byte).unwrap_or(0); + } + Self(destination) + } +} + impl From<&str> for SignedFixedCharSlice { fn from(value: &str) -> Self { Self::from(unsafe { @@ -62,13 +76,47 @@ impl Serialize for SignedFixedCharSlice { where S: Serializer, { - serializer.collect_str(&format!("{}", self)) + serializer.collect_seq(self.0) + } +} + +struct SignedFixedCharSliceVisitor; + +impl<'de, const N: usize> Visitor<'de> for SignedFixedCharSliceVisitor { + type Value = SignedFixedCharSlice; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "struct SignedFixedCharSlice") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut destination = [0; N]; + let mut counter = 0; + while let Ok(Some(item)) = seq.next_element::() { + destination[counter] = item; + counter += 1; + } + Ok(SignedFixedCharSlice(destination)) + } +} + +impl<'de, const N: usize> Deserialize<'de> for SignedFixedCharSlice { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(SignedFixedCharSliceVisitor) } } #[cfg(test)] mod tests { + use std::io::{BufReader, Read, Seek, SeekFrom, Write}; + use super::*; #[test] fn valid_from_bytes_to_fixed_char_slice() { @@ -102,4 +150,23 @@ mod tests { "BALDU" ) } + + #[test] + fn deserialize_serialize_deserialize() { + let expected = SignedFixedCharSlice::<6>::from("BALDUR"); + let value = serde_json::to_string(&expected).unwrap(); + + let mut file = tempfile::tempfile().unwrap(); + file.write_all(value.as_bytes()).unwrap(); + + file.seek(SeekFrom::Start(0)).unwrap(); + let mut buffer = vec![]; + let mut reader = BufReader::new(file); + reader + .read_to_end(&mut buffer) + .expect("Could not read to buffer"); + + let result: SignedFixedCharSlice<6> = serde_json::from_slice(&buffer).unwrap(); + assert_eq!(expected, result) + } } diff --git a/models/src/common/varriable_char_array.rs b/models/src/common/varriable_char_array.rs index 2381e29..97e1df5 100644 --- a/models/src/common/varriable_char_array.rs +++ b/models/src/common/varriable_char_array.rs @@ -3,12 +3,12 @@ use std::{ rc::Rc, }; -use serde::{Serialize, Serializer}; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; #[derive(PartialEq, Eq)] -pub struct VarriableCharArray(pub Rc<[u8]>); +pub struct VariableCharArray(pub Rc<[u8]>); -impl Display for VarriableCharArray { +impl Display for VariableCharArray { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -20,43 +20,91 @@ impl Display for VarriableCharArray { } } -impl Debug for VarriableCharArray { +impl Debug for VariableCharArray { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("\"{}\"", self)) + f.write_str(&format!("{}", self)) } } -impl From<&str> for VarriableCharArray { +impl From<&str> for VariableCharArray { fn from(value: &str) -> Self { Self(value.as_bytes().into()) } } -impl Clone for VarriableCharArray { +impl Clone for VariableCharArray { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl Serialize for VarriableCharArray { +impl Serialize for VariableCharArray { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - serializer.collect_str(&format!("{}", self)) + serializer.collect_seq(self.0.iter()) + } +} + +struct VarriableCharArrayVisitor; + +impl<'de> Visitor<'de> for VarriableCharArrayVisitor { + type Value = VariableCharArray; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "struct VarriableCharArray") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut destination = Vec::with_capacity(seq.size_hint().unwrap_or(0)); + while let Ok(Some(item)) = seq.next_element::() { + destination.push(item); + } + Ok(VariableCharArray(destination.into())) + } +} + +impl<'de> Deserialize<'de> for VariableCharArray { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(VarriableCharArrayVisitor) } } #[cfg(test)] mod tests { + + use std::io::{BufReader, Read, Seek, SeekFrom, Write}; + use super::*; #[test] fn strips_nulls_and_returns() { - let from = "BALDUR\0"; + let input = "BALDUR\0"; let expected = "BALDUR"; - assert_eq!( - VarriableCharArray(from.as_bytes().into()).to_string(), - expected - ) + assert_eq!(VariableCharArray::from(input).to_string(), expected) + } + + #[test] + fn deserialize_serialize_deserialize() { + let expected = VariableCharArray::from("BALDUR"); + let value = serde_json::to_string(&expected).unwrap(); + + let mut file = tempfile::tempfile().unwrap(); + file.write_all(value.as_bytes()).unwrap(); + + file.seek(SeekFrom::Start(0)).unwrap(); + let mut buffer = Vec::new(); + let mut reader = BufReader::new(file); + reader + .read_to_end(&mut buffer) + .expect("Could not read to buffer"); + let result: VariableCharArray = serde_json::from_slice(&buffer).unwrap(); + assert_eq!(expected, result) } } diff --git a/models/src/creature.rs b/models/src/creature.rs index 5c9816d..eb1e658 100644 --- a/models/src/creature.rs +++ b/models/src/creature.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::fixed_char_nd_array::FixedCharNDArray; use crate::common::header::Header; @@ -14,7 +14,8 @@ use crate::{ spell_table::{generate_spell_memorization, KnownSpells, MemorizedSpells}, }; -#[derive(Debug, PartialEq, Eq, Serialize)] +#[repr(C)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Creature { pub header: BGEECreature, pub item_slot_table: ItemTable, @@ -66,11 +67,16 @@ impl Model for Creature { fn name(&self, _lookup: &Lookup) -> String { self.header.dialog_ref.to_string() } + + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/cre_v1.htm #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct BGEECreature { pub header: Header<4, 4>, pub long_creature_name: u32, diff --git a/models/src/dialog.rs b/models/src/dialog.rs index 7d1d641..bb7a9bc 100644 --- a/models/src/dialog.rs +++ b/models/src/dialog.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::fixed_char_array::FixedCharSlice; use crate::common::header::Header; @@ -9,7 +9,8 @@ use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Dialog { pub header: DialogHeader, pub state_tables: Vec, @@ -60,11 +61,15 @@ impl Model for Dialog { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm#formDLGV1_Header #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct DialogHeader { pub header: Header<4, 4>, pub count_of_state_tables: i32, @@ -82,7 +87,7 @@ pub struct DialogHeader { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm#formDLGV1_State #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct StateTable { pub actor_response_text: FixedCharSlice<4>, pub index_of_the_first_transition: u32, @@ -92,7 +97,7 @@ pub struct StateTable { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm#formDLGV1_Transition #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Transition { pub flags: FixedCharSlice<4>, pub player_character_text: FixedCharSlice<4>, @@ -105,7 +110,7 @@ pub struct Transition { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm#formDLGV1_StateTrigger #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct StateTrigger { pub offset_to_start_of_file: u32, pub length_in_bytes: u32, @@ -113,7 +118,7 @@ pub struct StateTrigger { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm#formDLGV1_TransTrigger #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct TransitionTrigger { pub offset_to_start_of_file: u32, pub length_in_bytes: u32, @@ -121,7 +126,7 @@ pub struct TransitionTrigger { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/dlg_v1.htm#formDLGV1_Action #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct ActionTable { pub offset_to_start_of_file: u32, pub length_in_bytes: u32, diff --git a/models/src/effect_v1.rs b/models/src/effect_v1.rs index 22a1d23..e8f3992 100644 --- a/models/src/effect_v1.rs +++ b/models/src/effect_v1.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::fixed_char_array::FixedCharSlice; use crate::model::Model; @@ -8,7 +8,8 @@ use crate::resources::utils::copy_buff_to_struct; use crate::tlk::Lookup; // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/eff_v1.htm#effv1_Header -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct EffectV1 { pub effect_type: u16, pub target_type: u8, @@ -41,4 +42,8 @@ impl Model for EffectV1 { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } diff --git a/models/src/effect_v2.rs b/models/src/effect_v2.rs index 2adade0..e30a7d2 100644 --- a/models/src/effect_v2.rs +++ b/models/src/effect_v2.rs @@ -1,13 +1,14 @@ use std::{mem::size_of, rc::Rc}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::header::Header; use crate::resources::utils::copy_buff_to_struct; use crate::tlk::Lookup; use crate::{common::fixed_char_array::FixedCharSlice, model::Model}; -#[derive(Debug, PartialEq, Eq, Serialize)] +#[repr(C)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct EffectV2 { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/eff_v2.htm pub effect_v2_header: Header<4, 4>, @@ -31,11 +32,15 @@ impl Model for EffectV2 { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/eff_v2.htm#effv2_Body #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct EffectV2Body { pub header: Header<4, 4>, pub opcode_number: u32, diff --git a/models/src/game.rs b/models/src/game.rs index 97d4698..39bdd81 100644 --- a/models/src/game.rs +++ b/models/src/game.rs @@ -1,6 +1,6 @@ use std::{mem::size_of, rc::Rc}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::header::Header; use crate::common::signed_fixed_char_array::SignedFixedCharSlice; @@ -8,7 +8,8 @@ use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; use crate::{common::fixed_char_array::FixedCharSlice, creature::Creature, model::Model}; -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Game { pub header: BGEEGameHeader, pub party_npcs: Vec, @@ -85,11 +86,15 @@ impl Model for Game { fn name(&self, _lookup: &Lookup) -> String { "BALDUR.GAM".to_string() } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_Header #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct BGEEGameHeader { pub header: Header<4, 4>, pub game_time: u32, @@ -145,7 +150,7 @@ pub struct BGEEGameHeader { pub random_encounter_script: [u8; 20], } -#[derive(Debug, PartialEq, Eq, Serialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Npc { pub game_npc: GameNPC, pub creature: Creature, @@ -169,7 +174,7 @@ fn generate_npcs(buffer: &[u8], start: usize, count: usize) -> Vec { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_NPC #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct GameNPC { pub character_selection: u16, // x0-0x5 = player_x_fill, 0x_ffff = not in party @@ -227,7 +232,7 @@ pub struct GameNPC { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_Stats #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct CharacterKillStats { pub most_powerful_vanquished_name: SignedFixedCharSlice<4>, pub most_powerful_vanquished_xp_reward: u32, @@ -253,9 +258,9 @@ pub struct CharacterKillStats { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_Variable #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct GlobalVarriables { - pub name: [i8; 32], + pub name: SignedFixedCharSlice<32>, /* bit 0: int bit 1: float @@ -264,17 +269,17 @@ pub struct GlobalVarriables { bit 4: strref bit 5: dword */ - pub varriable_type: [i8; 2], + pub varriable_type: i16, pub resource_value: i16, pub dword_value: i32, pub int_value: i32, pub double_value: i64, - pub script_name_value: [i8; 32], + pub script_name_value: SignedFixedCharSlice<32>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_Journal #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct JournalEntries { pub journal_text: FixedCharSlice<4>, // seconds @@ -295,7 +300,7 @@ pub struct JournalEntries { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_Familiar #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Familiar { pub lawful_good_familiar: FixedCharSlice<8>, pub lawful_neutral_familiar: FixedCharSlice<8>, @@ -307,21 +312,21 @@ pub struct Familiar { pub chaotic_neutral_familiar: FixedCharSlice<8>, pub chaotic_evil_familiar: FixedCharSlice<8>, pub offset_to_familiar_resources: i32, - pub lg_familiar_spell_count: [u32; 9], - pub ln_familiar_spell_count: [u32; 9], - pub cg_familiar_spell_count: [u32; 9], - pub ng_familiar_spell_count: [u32; 9], - pub tn_familiar_spell_count: [u32; 9], - pub ne_familiar_spell_count: [u32; 9], - pub le_familiar_spell_count: [u32; 9], - pub cn_familiar_spell_count: [u32; 9], - pub ce_familiar_spell_count: [u32; 9], + pub lg_familiar_spell_count: FixedCharSlice<9>, + pub ln_familiar_spell_count: FixedCharSlice<9>, + pub cg_familiar_spell_count: FixedCharSlice<9>, + pub ng_familiar_spell_count: FixedCharSlice<9>, + pub tn_familiar_spell_count: FixedCharSlice<9>, + pub ne_familiar_spell_count: FixedCharSlice<9>, + pub le_familiar_spell_count: FixedCharSlice<9>, + pub cn_familiar_spell_count: FixedCharSlice<9>, + pub ce_familiar_spell_count: FixedCharSlice<9>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_Stored // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_PocketPlane #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Location { pub area: [i8; 8], pub x_coordinate: i16, @@ -330,7 +335,7 @@ pub struct Location { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/gam_v2.0.htm#GAMEV2_0_FamiliarExtra #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct FamiliarExtra { pub data: [i8; 8], } diff --git a/models/src/ids.rs b/models/src/ids.rs index 6819b3a..cbeb147 100644 --- a/models/src/ids.rs +++ b/models/src/ids.rs @@ -1,10 +1,10 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::{ common::{ - fixed_char_array::FixedCharSlice, header::Header, varriable_char_array::VarriableCharArray, + fixed_char_array::FixedCharSlice, header::Header, varriable_char_array::VariableCharArray, }, model::Model, resources::utils::row_parser, @@ -12,31 +12,30 @@ use crate::{ }; //https://gibberlings3.github.io/iesdp/file_formats/ie_formats/ids.htm -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Ids { pub header: Header<3, 4>, pub data_entries: Vec, } -#[derive(Debug, PartialEq, Eq, Serialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DataEntry { - pub value: VarriableCharArray, - pub identifier: VarriableCharArray, + pub value: VariableCharArray, + pub identifier: VariableCharArray, } impl Model for Ids { fn new(buffer: &[u8]) -> Self { let (headers, mut end) = row_parser(buffer, 0); - let signature = if let Some(_) = headers.first() { + let signature = if headers.first().is_some() { FixedCharSlice::<3>::from(&buffer[0..3]) } else { FixedCharSlice::<3>::default() }; let version = match headers.last() { - Some(x) if FixedCharSlice::<4>::try_from(x).is_ok() => { - FixedCharSlice::<4>::try_from(x).unwrap() - } + Some(x) => FixedCharSlice::<4>::from(x.0.as_ref()), _ => FixedCharSlice::<4>::from(signature.0.as_ref()), }; let header = Header { signature, version }; @@ -61,13 +60,17 @@ impl Model for Ids { } } - fn create_as_rc(buffer: &[u8]) -> std::rc::Rc { + fn create_as_rc(buffer: &[u8]) -> Rc { Rc::new(Self::new(buffer)) } fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } #[cfg(test)] diff --git a/models/src/item.rs b/models/src/item.rs index 6fee6c7..771d740 100644 --- a/models/src/item.rs +++ b/models/src/item.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::feature_block::FeatureBlock; use crate::common::fixed_char_array::FixedCharSlice; @@ -11,7 +11,8 @@ use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; //https://gibberlings3.github.io/iesdp/file_formats/ie_formats/itm_v1.htm -#[derive(Debug, Serialize)] + +#[derive(Debug, Serialize, Deserialize)] pub struct Item { pub header: ItemHeader, pub extended_headers: Vec, @@ -65,11 +66,15 @@ impl Model for Item { .replace(' ', "_"); format!("{}.spl", name) } + + fn to_bytes(&self) -> Vec { + todo!() + } } //https://gibberlings3.github.io/iesdp/file_formats/ie_formats/itm_v1.htm#itmv1_Header #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct ItemHeader { header: Header<4, 4>, unidentified_item_name: i32, @@ -112,7 +117,7 @@ pub struct ItemHeader { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/itm_v1.htm#itmv1_Extended_Header #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct ItemExtendedHeader { attack_type: u8, // Note zero is very bad here id_required: u8, diff --git a/models/src/item_table.rs b/models/src/item_table.rs index c908a6a..f3caff2 100644 --- a/models/src/item_table.rs +++ b/models/src/item_table.rs @@ -1,12 +1,13 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; use crate::{common::fixed_char_array::FixedCharSlice, model::Model}; -#[derive(Debug, PartialEq, Eq, Serialize)] +#[repr(C)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ItemTable { pub helmet: Option, pub armor: Option, @@ -115,7 +116,7 @@ impl ItemTable { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/cre_v1.htm#CREV1_0_Item #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct ItemReferenceTable { pub resource_name: FixedCharSlice<8>, // Item expiration time - item creation hour (replace with drained item) @@ -145,11 +146,15 @@ impl Model for ItemReferenceTable { fn name(&self, _lookup: &Lookup) -> String { self.resource_name.to_string() } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/cre_v1.htm#CREV1_0_ItemSlots #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] struct ItemSlotTable { helmet: i16, armor: i16, @@ -205,6 +210,10 @@ impl Model for ItemSlotTable { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } #[cfg(test)] diff --git a/models/src/key.rs b/models/src/key.rs index 3b700bd..4039124 100644 --- a/models/src/key.rs +++ b/models/src/key.rs @@ -1,12 +1,13 @@ use std::{fmt::Debug, usize}; use crate::common::header::Header; -use crate::common::{fixed_char_array::FixedCharSlice, varriable_char_array::VarriableCharArray}; +use crate::common::{fixed_char_array::FixedCharSlice, varriable_char_array::VariableCharArray}; use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use super::resources::types::ResourceType; // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/key_v1.htm +#[repr(C)] #[derive(Debug)] pub struct Key { pub header: KeyHeader, @@ -67,7 +68,7 @@ pub struct BiffIndexHeader { #[derive(Debug)] pub struct BiffIndex { pub header: BiffIndexHeader, - pub name: VarriableCharArray, + pub name: VariableCharArray, } impl BiffIndex { @@ -76,7 +77,7 @@ impl BiffIndex { let end = start + usize::try_from(header.file_name_length).unwrap_or(0); buffer.get(start..end).map(|buff| BiffIndex { header: *header, - name: VarriableCharArray(buff.into()), + name: VariableCharArray(buff.into()), }) } } diff --git a/models/src/model.rs b/models/src/model.rs index 451556f..8a45aed 100644 --- a/models/src/model.rs +++ b/models/src/model.rs @@ -14,4 +14,5 @@ pub trait Model: Debug + Serialize { fn new(buffer: &[u8]) -> Self where Self: Sized; + fn to_bytes(&self) -> Vec; } diff --git a/models/src/resources/utils.rs b/models/src/resources/utils.rs index 113eaac..6578297 100644 --- a/models/src/resources/utils.rs +++ b/models/src/resources/utils.rs @@ -5,7 +5,12 @@ use std::{ vec, }; -use crate::common::varriable_char_array::VarriableCharArray; +use crate::common::varriable_char_array::VariableCharArray; + + +pub fn to_u8_slice(p: &T) -> &[u8] { + unsafe { return core::slice::from_raw_parts((p as *const T) as *const u8, size_of::()) } +} pub fn copy_buff_to_struct(buffer: &[u8], start: usize) -> T { let end: usize = start + size_of::(); @@ -42,27 +47,27 @@ pub fn copy_transmute_buff(buffer: &[u8], start: usize, count: usize) -> Vec< const CARRAGE_RETURN: u8 = 0xD; const NEW_LINE: u8 = 0xA; -pub fn dumb_row_parser(buffer: &[u8]) -> Vec { +pub fn dumb_row_parser(buffer: &[u8]) -> Vec { let mut acc = vec![]; let mut pos = 0; for (i, x) in buffer.iter().enumerate() { if x == &NEW_LINE || x == &CARRAGE_RETURN { if pos < i { - acc.push(VarriableCharArray(buffer.get(pos..i).unwrap().into())) + acc.push(VariableCharArray(buffer.get(pos..i).unwrap().into())) } - acc.push(VarriableCharArray(Rc::new([32]))); + acc.push(VariableCharArray(Rc::new([32]))); pos = i; } } if pos < buffer.len() { - acc.push(VarriableCharArray( + acc.push(VariableCharArray( buffer.get(pos..buffer.len()).unwrap().into(), )) } acc } -pub fn row_parser(buffer: &[u8], row_start: usize) -> (Vec, usize) { +pub fn row_parser(buffer: &[u8], row_start: usize) -> (Vec, usize) { if let Some(end) = buffer .get(row_start..) .unwrap_or_default() @@ -77,7 +82,7 @@ pub fn row_parser(buffer: &[u8], row_start: usize) -> (Vec, if buff.is_empty() { return None; } - Some(VarriableCharArray(buff.into())) + Some(VariableCharArray(buff.into())) }) .collect(); diff --git a/models/src/spell.rs b/models/src/spell.rs index 5faf3e3..03aeefa 100644 --- a/models/src/spell.rs +++ b/models/src/spell.rs @@ -1,17 +1,18 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::feature_block::FeatureBlock; use crate::common::fixed_char_array::FixedCharSlice; use crate::common::header::Header; use crate::common::signed_fixed_char_array::SignedFixedCharSlice; use crate::model::Model; -use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; +use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff, to_u8_slice}; use crate::tlk::Lookup; // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/spl_v1.htm -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Spell { pub header: SpellHeader, pub extended_headers: Vec, @@ -59,11 +60,22 @@ impl Model for Spell { }; format!("{}.spl", name) } + + fn to_bytes(&self) -> Vec { + let mut out = to_u8_slice(&self.header).to_vec(); + for extend in self.extended_headers.iter() { + out.extend(to_u8_slice(extend)) + } + for blocks in self.equipping_feature_blocks.iter() { + out.extend(to_u8_slice(blocks)) + } + out + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/spl_v1.htm#splv1_Header #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct SpellHeader { header: Header<4, 4>, unidentified_spell_name: i32, @@ -107,7 +119,7 @@ pub struct SpellHeader { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/spl_v1.htm#splv1_Extended_Header #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct SpellExtendedHeader { spell_form: u8, friendly: u8, diff --git a/models/src/spell_table.rs b/models/src/spell_table.rs index 6e383e3..2e0f261 100644 --- a/models/src/spell_table.rs +++ b/models/src/spell_table.rs @@ -1,12 +1,12 @@ use std::mem::size_of; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::fixed_char_array::FixedCharSlice; use crate::resources::utils::copy_transmute_buff; #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct KnownSpells { pub spell_name: FixedCharSlice<8>, pub spell_level: u16, @@ -14,7 +14,7 @@ pub struct KnownSpells { } #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct SpellMemorizationInfo { pub spell_level: u16, pub number_of_spells_memorizable: u16, @@ -25,13 +25,14 @@ pub struct SpellMemorizationInfo { } #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct SpellMemorizationTable { pub spell_name: FixedCharSlice<8>, pub memorised: u32, } -#[derive(Debug, PartialEq, Eq, Serialize)] +#[repr(C)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct MemorizedSpells { pub spell_memorization_info: SpellMemorizationInfo, pub spell_memorization_table: Vec, diff --git a/models/src/store.rs b/models/src/store.rs index eb5bd84..318235e 100644 --- a/models/src/store.rs +++ b/models/src/store.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; @@ -10,7 +10,8 @@ use crate::{ }; // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/sto_v1.htm -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Store { pub store_header: StoreHeader, pub items_for_sale: Vec, @@ -54,11 +55,15 @@ impl Model for Store { fn name(&self, _lookup: &Lookup) -> String { self.store_header.name.to_string() } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/sto_v1.htm#storv1_0_Header #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct StoreHeader { pub header: Header<4, 4>, // (0=Store, 1=Tavern, 2=Inn, 3=Temple, 5=Container) @@ -93,7 +98,7 @@ pub struct StoreHeader { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/sto_v1.htm#storv1_0_Sale #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct ItemsForSale { pub filename_of_item: [i8; 8], pub item_expiration_time: i16, @@ -108,7 +113,7 @@ pub struct ItemsForSale { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/sto_v1.htm#storv1_0_Drink #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct DrinksForSale { pub rumour_resource: [i8; 8], pub drink_name: FixedCharSlice<4>, @@ -118,12 +123,12 @@ pub struct DrinksForSale { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/sto_v1.htm#storv1_0_Cure #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct CuresForSale { pub filename_of_spell: [i8; 8], pub spell_price: i32, } #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct ItemsPurchasedHere(i32); diff --git a/models/src/tlk.rs b/models/src/tlk.rs index 4903582..72f32b7 100644 --- a/models/src/tlk.rs +++ b/models/src/tlk.rs @@ -1,11 +1,11 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::{ common::{ header::Header, signed_fixed_char_array::SignedFixedCharSlice, - varriable_char_array::VarriableCharArray, + varriable_char_array::VariableCharArray, }, model::Model, resources::utils::{copy_buff_to_struct, copy_transmute_buff}, @@ -14,11 +14,13 @@ use crate::{ // This is hard coded by the file format const START: usize = 18; -#[derive(Debug, Serialize)] +// https://gibberlings3.github.io/iesdp/file_formats/ie_formats/tlk_v1.htm +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct Lookup { pub header: TLKHeader, pub entries: Vec, - pub strings: Vec, + pub strings: Vec, } impl Model for Lookup { @@ -37,7 +39,7 @@ impl Model for Lookup { .unwrap_or(0); let buff_end = buff_start + usize::try_from(entry.length_of_this_string).unwrap_or(0); - VarriableCharArray(buffer.get(buff_start..buff_end).unwrap().into()) + VariableCharArray(buffer.get(buff_start..buff_end).unwrap().into()) }) .collect(); Self { @@ -54,11 +56,15 @@ impl Model for Lookup { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } //https://gibberlings3.github.io/iesdp/file_formats/ie_formats/tlk_v1.htm#tlkv1_Header #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize, PartialEq)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)] pub struct TLKHeader { pub header: Header<4, 4>, pub language_id: i16, @@ -68,7 +74,7 @@ pub struct TLKHeader { //https://gibberlings3.github.io/iesdp/file_formats/ie_formats/tlk_v1.htm#tlkv1_Entry #[repr(C, packed)] -#[derive(Debug, Copy, Clone, Serialize, PartialEq)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)] pub struct TLKEntry { /* 00 - No message data @@ -87,6 +93,27 @@ pub struct TLKEntry { pub length_of_this_string: u32, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct TLKDataEntry { + pub entry: TLKEntry, + pub strings: VariableCharArray, +} + +impl TLKDataEntry { + fn new(start: usize, entry: &TLKEntry, buffer: &[u8]) -> Self { + let buff_start = start + + usize::try_from(entry.offset_of_this_string_relative_to_the_strings_section) + .unwrap_or(0); + let buff_end = buff_start + usize::try_from(entry.length_of_this_string).unwrap_or(0); + let strings = VariableCharArray(buffer.get(buff_start..buff_end).unwrap().into()); + + TLKDataEntry { + entry: *entry, + strings, + } + } +} + #[cfg(test)] mod tests { diff --git a/models/src/twoda.rs b/models/src/twoda.rs index a866785..32901fd 100644 --- a/models/src/twoda.rs +++ b/models/src/twoda.rs @@ -2,27 +2,29 @@ use std::{rc::Rc, vec}; use std::fmt::Debug; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::fixed_char_array::FixedCharSlice; use crate::common::header::Header; -use crate::common::varriable_char_array::VarriableCharArray; +use crate::common::varriable_char_array::VariableCharArray; use crate::model::Model; use crate::resources::utils::row_parser; use crate::tlk::Lookup; //https://gibberlings3.github.io/iesdp/file_formats/ie_formats/2da.htm -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct TwoDA { pub header: Header<3, 4>, - pub default_value: VarriableCharArray, + pub default_value: VariableCharArray, pub data_entries: DataEntry, } -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct DataEntry { - pub data_entry_headers: Vec, - pub values: Vec>, + pub data_entry_headers: Vec, + pub values: Vec>, } impl Model for TwoDA { @@ -30,6 +32,17 @@ impl Model for TwoDA { // Parse Headers let (headers, end) = row_parser(buffer, 0); + let signature = if headers.first().is_some() { + FixedCharSlice::<3>::from(&buffer[0..3]) + } else { + FixedCharSlice::<3>::default() + }; + let version = match headers.last() { + Some(x) => FixedCharSlice::<4>::from(x.0.as_ref()), + _ => FixedCharSlice::<4>::from(signature.0.as_ref()), + }; + let header = Header { signature, version }; + // Parse Default Value let (default_values, end) = row_parser(buffer, end); @@ -46,20 +59,6 @@ impl Model for TwoDA { } end = row_end; } - - let signature = if let Some(_) = headers.first() { - FixedCharSlice::<3>::from(&buffer[0..3]) - } else { - FixedCharSlice::<3>::default() - }; - let version = match headers.last() { - Some(x) if FixedCharSlice::<4>::try_from(x).is_ok() => { - FixedCharSlice::<4>::try_from(x).unwrap() - } - _ => FixedCharSlice::<4>::from(signature.0.as_ref()), - }; - let header = Header { signature, version }; - Self { header, default_value: default_values.first().unwrap().clone(), @@ -70,13 +69,17 @@ impl Model for TwoDA { } } - fn create_as_rc(buffer: &[u8]) -> std::rc::Rc { + fn create_as_rc(buffer: &[u8]) -> Rc { Rc::new(Self::new(buffer)) } fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } #[cfg(test)] diff --git a/models/src/world_map.rs b/models/src/world_map.rs index c09dd71..7bb9e4c 100644 --- a/models/src/world_map.rs +++ b/models/src/world_map.rs @@ -1,13 +1,14 @@ use std::rc::Rc; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::common::header::Header; use crate::resources::utils::{copy_buff_to_struct, copy_transmute_buff}; use crate::tlk::Lookup; use crate::{common::fixed_char_array::FixedCharSlice, model::Model}; -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct WorldMap { pub world_map_header: WorldMapHeader, pub world_map_entries: Vec, @@ -55,11 +56,15 @@ impl Model for WorldMap { fn name(&self, _lookup: &Lookup) -> String { todo!() } + + fn to_bytes(&self) -> Vec { + todo!() + } } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/wmap_v1.htm#wmapv1_0_Header #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct WorldMapHeader { pub header: Header<4, 4>, pub count_of_worldmap_entries: i32, @@ -69,7 +74,8 @@ pub struct WorldMapHeader { // One to Many // One WorldMapEntry -> Many Area Entries // -> Many AreaEntries -#[derive(Debug, Serialize)] +#[repr(C)] +#[derive(Debug, Serialize, Deserialize)] pub struct WorldMapEntry { pub world_map_entry: WorldMapEntryUnlinked, pub area_entries: Vec, @@ -78,7 +84,7 @@ pub struct WorldMapEntry { // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/wmap_v1.htm#wmapv1_0_Entry #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct WorldMapEntryUnlinked { pub background_image_mos_file: FixedCharSlice<8>, pub width: u32, @@ -95,12 +101,12 @@ pub struct WorldMapEntryUnlinked { // BGEE feild only pub flags: u32, #[serde(skip_serializing)] - _unused: [u8; 128], + _unused: FixedCharSlice<128>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/wmap_v1.htm#wmapv1_0_Area #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct AreaEntry { pub area_resref: FixedCharSlice<8>, pub area_name_short: FixedCharSlice<8>, @@ -121,12 +127,12 @@ pub struct AreaEntry { pub link_index_east: u32, pub link_count_east: u32, #[serde(skip_serializing)] - _unused: [u8; 128], + _unused: FixedCharSlice<128>, } // https://gibberlings3.github.io/iesdp/file_formats/ie_formats/wmap_v1.htm#wmapv1_0_AreaLink #[repr(C, packed)] -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct AreaLink { pub index_of_destination_area: u32, pub entry_point: FixedCharSlice<32>, @@ -139,5 +145,5 @@ pub struct AreaLink { pub random_encounter_area_5: FixedCharSlice<8>, pub random_encounter_probability: u32, #[serde(skip_serializing)] - _unused: [u8; 128], + _unused: FixedCharSlice<128>, }