diff --git a/README.md b/README.md index 818e7e8..1f255d4 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ nuccbin supports a number of in game nuccChunkBinary param / bin formats. All fo | --- | --- | --- | --- | | [accessoriesParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/accessories_param.rs) | ✔️ | ✔️ | `json` | | [accessoryParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/accessory_param.rs) | ✔️ | ✔️ | `json` | +| [accessoryExceptionParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/accessory_exception_param.rs) | ✔️ | ✔️ | `json` | | [animeSongBgmParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/anime_song_bgm_param.rs) | ✔️ | ✔️ | `json` | | [anmofs](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/anm_offset.rs) | ✔️ | ✔️ | `json` | +| [bod1acc](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/bodacc.rs) | ✔️ | ✔️ | `json` | | [characode](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/characode.rs) | ✔️ | ✔️ | `json` | | [CharaPoseParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/chara_pose_param.rs) | ✔️ | ✔️ | `json` | | [characterSelectParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/character_select_param.rs) | ✔️ | ✔️ | `json` | @@ -23,7 +25,7 @@ nuccbin supports a number of in game nuccChunkBinary param / bin formats. All fo | [costumeBreakParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/costume_break_param.rs) | ✔️ | ✔️ | `json` | | [costumeParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/costume_param.rs) | ✔️ | ✔️ | `json` | | [dds](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/dds.rs) | ✔️ | ✔️ | `dds` | -| [DictionaryCharacterParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/dictionary_character_param.rs) | ✔️ | ✔️ | `json` | +| [DictionaryCharacterParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/dictionary_character_param.rs) | ✔️ | ✔️ | `dds` | | [DlcInfoParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/dlc_info_param.rs) | ✔️ | ✔️ | `json` | | [effectprm](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/effectprm.rs) | ✔️ | ✔️ | `json` | | [ev](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/ev.rs) | ✔️ | ✔️ | `json` | @@ -40,6 +42,7 @@ nuccbin supports a number of in game nuccChunkBinary param / bin formats. All fo | [staffRollTextParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/staff_roll_text_param.rs) | ✔️ | ❌ | `json` | | [supportActionParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/support_action_param.rs) | ✔️ | ✔️ | `json` | | [supportSkillRecoverySpeedParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/support_skill_recovery_speed_param.rs) | ✔️ | ✔️ | `json` | +| [updateInfoParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/update_info_param.rs) | ✔️ | ✔️ | `json` | | [xml](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/xml.rs) | ✔️ | ✔️ | `xml` | @@ -53,5 +56,5 @@ Special thanks goes to several members including: * [PortableProductions](https://www.youtube.com/@PortableProductions) for reversing some formats. * [Kuroha Saenoki](https://www.youtube.com/@KurohaSaenoki) for reversing some formats. * [Valant96](https://www.youtube.com/@valant96) for information on some formats. -* [Xact](https://www.youtube.com/channel/UCluz3KlVGPDYNnJhNvOW_AA) for reversing some formats. +* [Xact](https://www.youtube.com/@valant96) for reversing some formats. * and [SutandoTsukai181](https://github.com/SutandoTsukai181) for his initial work on the tool. diff --git a/src/lib.rs b/src/lib.rs index 54d07c8..79d6f66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,17 @@ use strum_macros::{EnumString, EnumIter, Display}; use regex::Regex; +pub mod args; +pub mod nucc_binary; -#[derive(Debug, Copy, Clone, EnumString, EnumIter, Display, PartialEq)] +#[derive(Debug, Copy, Clone, EnumString, EnumIter, Display, PartialEq, Hash, Eq)] pub enum NuccBinaryType { AccessoriesParam, AccessoryExceptionParam, AccessoryParam, AnimeSongBgmParam, Anmofs, + BodAcc, #[strum(ascii_case_insensitive)] Characode, CharaPoseParam, @@ -30,6 +33,7 @@ pub enum NuccBinaryType { PlayerSettingParam, PlayerIcon, Png, + PrmBas, PrmLoad, ProhibitedSubstringParam, SkillIndexSettingParam, @@ -37,6 +41,7 @@ pub enum NuccBinaryType { StaffRollTextParam, SupportActionParam, SupportSkillRecoverySpeedParam, + UpdateInfoParam, Xml, } @@ -49,6 +54,7 @@ impl NuccBinaryType { NuccBinaryType::AccessoryParam => { Regex::new(r"(accessoryParam\.bin)$").unwrap() }, NuccBinaryType::AnimeSongBgmParam => { Regex::new(r"(animeSongBgmParam\.bin)$").unwrap() }, NuccBinaryType::Anmofs => { Regex::new(r"(anm_offset)").unwrap() }, + NuccBinaryType::BodAcc => { Regex::new(r"(bod1acc\.bin)$").unwrap() }, NuccBinaryType::Characode => { Regex::new(r"(characode\.bin)$").unwrap() }, NuccBinaryType::CharaPoseParam => { Regex::new(r"(CharaPoseParam\.bin)$").unwrap() }, NuccBinaryType::CharacterSelectParam => { Regex::new(r"(characterSelectParam\.bin)$").unwrap() }, @@ -69,6 +75,7 @@ impl NuccBinaryType { NuccBinaryType::PlayerSettingParam => { Regex::new(r"(playerSettingParam\.bin)$").unwrap() }, NuccBinaryType::PlayerIcon => { Regex::new(r"(player_icon\.bin)$").unwrap() }, NuccBinaryType::Png => { Regex::new(r"(\.png)$").unwrap() }, + NuccBinaryType::PrmBas => { Regex::new(r"(prm_bas)").unwrap() }, NuccBinaryType::PrmLoad => { Regex::new(r"(prm_load\.bin)$").unwrap() }, NuccBinaryType::ProhibitedSubstringParam => { Regex::new(r"(prohibitedSubstringParam\.bin)$").unwrap() }, NuccBinaryType::SkillIndexSettingParam => { Regex::new(r"(skillIndexSettingParam\.bin)$").unwrap() }, @@ -76,6 +83,7 @@ impl NuccBinaryType { NuccBinaryType::StaffRollTextParam => { Regex::new(r"(staffRollTextParam\.bin)$").unwrap() }, NuccBinaryType::SupportActionParam => { Regex::new(r"(supportActionParam\.bin)$").unwrap() }, NuccBinaryType::SupportSkillRecoverySpeedParam => { Regex::new(r"(supportSkillRecoverySpeedParam\.bin)$").unwrap() }, + NuccBinaryType::UpdateInfoParam => { Regex::new(r"(updateInfoParam\.bin)$").unwrap() } NuccBinaryType::Xml => { Regex::new(r"(\.xml)$").unwrap() } } } diff --git a/src/nucc_binary/accessories_param.rs b/src/nucc_binary/accessories_param.rs index f80ba13..67b612b 100644 --- a/src/nucc_binary/accessories_param.rs +++ b/src/nucc_binary/accessories_param.rs @@ -64,10 +64,7 @@ pub struct AccessoriesParam { #[serde(skip)] pub version: u32, - pub entry_count: u16, - - #[serde(skip)] - pub unk0: u16, + pub entry_count: u32, #[serde(skip)] pub entry_ptr: u64, @@ -104,9 +101,7 @@ impl From<&[u8]> for AccessoriesParam { let size = reader.read_be::().unwrap(); let version = reader.read_le::().unwrap(); - let entry_count = reader.read_le::().unwrap(); - let unk0 = reader.read_le::().unwrap(); - + let entry_count = reader.read_le::().unwrap(); let entry_ptr = reader.read_le::().unwrap(); let mut entries = Vec::new(); @@ -143,7 +138,6 @@ impl From<&[u8]> for AccessoriesParam { size, version, entry_count, - unk0, entry_ptr, entries } @@ -155,13 +149,13 @@ impl From for Vec { fn from(mut accessories_param: AccessoriesParam) -> Self { let mut writer = Cursor::new(Vec::new()); - accessories_param.entry_count = accessories_param.entries.len() as u16; // Update entry count + accessories_param.entry_count = accessories_param.entries.len() as u32; // Update entry count writer.write_be(&accessories_param.size).unwrap(); writer.write_le(&1000u32).unwrap(); // Write the version writer.write_le(&accessories_param.entry_count).unwrap(); - writer.write_le(&accessories_param.unk0).unwrap(); + writer.write_le(&8u64).unwrap(); // Write the ptr to the entries diff --git a/src/nucc_binary/anm_offset.rs b/src/nucc_binary/anm_offset.rs index bbca4a0..122da28 100644 --- a/src/nucc_binary/anm_offset.rs +++ b/src/nucc_binary/anm_offset.rs @@ -1,5 +1,4 @@ -use binrw::{binrw, BinReaderExt, BinWriterExt, NullString}; -use binrw::io::{Cursor, Seek, SeekFrom}; +use binrw::binrw; use serde::{Serialize, Deserialize}; diff --git a/src/nucc_binary/bodacc.rs b/src/nucc_binary/bodacc.rs new file mode 100644 index 0000000..1d349c4 --- /dev/null +++ b/src/nucc_binary/bodacc.rs @@ -0,0 +1,197 @@ +use binrw::{binrw, BinReaderExt, BinWriterExt, NullString}; +use binrw::io::{Cursor, Seek, SeekFrom}; +use serde::{Serialize, Deserialize}; + + +use super::{NuccBinaryParsed, NuccBinaryType}; + +const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers + + +// Format reversed by Zinogre344 +#[allow(non_snake_case)] +#[binrw] +#[derive(Serialize, Deserialize, Debug)] +pub struct Entry { + #[serde(skip)] + pub accessory_ptr: u64, + + #[serde(skip)] + #[brw(pad_after = 8)] + pub bone_name_ptr: u64, + + #[serde(skip)] + #[brw(pad_after = 12)] + pub accessory_location_ptr: u64, + + pub location: [f32; 3], + + #[brw(pad_after = 12)] + pub rotation: [f32; 3], + + #[brw(pad_after = 4)] + pub scale: [f32; 3], + + + #[brw(ignore)] + #[bw(map = |x| x.parse::().unwrap())] + pub accessory: String, + + #[brw(ignore)] + #[bw(map = |x| x.parse::().unwrap())] + pub bone_name: String, + + #[brw(ignore)] + #[bw(map = |x| x.parse::().unwrap())] + pub accessory_location: String, +} + +#[binrw] +#[derive(Serialize, Deserialize, Debug)] +pub struct BodAcc { + #[serde(skip)] + pub size: u32, + + #[serde(skip)] + pub version: u32, + + pub entry_count: u32, + + #[serde(skip)] + pub entry_ptr: u64, + + #[br(count = entry_count)] + pub entries: Vec +} + + +impl NuccBinaryParsed for BodAcc { + fn binary_type(&self) -> NuccBinaryType { + NuccBinaryType::BodAcc + } + + fn extension(&self) -> String { + String::from(".json") + } + + fn serialize(&self) -> Vec { + serde_json::to_string_pretty(self).unwrap().into() + } + + fn deserialize(data: &[u8]) -> Self + where + Self: Sized, + { + serde_json::from_slice(data).unwrap() + } +} + +impl From<&[u8]> for BodAcc { + fn from(data: &[u8]) -> Self { + let mut reader = Cursor::new(data); + + let size = reader.read_be::().unwrap(); + let version = reader.read_le::().unwrap(); + + let entry_count = reader.read_le::().unwrap(); + let entry_ptr = reader.read_le::().unwrap(); + + let mut entries = Vec::new(); + entries.reserve_exact(entry_count as usize); // Make sure we have enough space to avoid reallocations + + for _ in 0..entry_count as usize { + let entry = reader.read_le::().unwrap(); + entries.push(entry); + } + + + fn read_string_from_ptr(reader: &mut Cursor<&[u8]>, ptr: u64, curent_offset: u64) -> String { + if ptr != 0 { + reader.seek(SeekFrom::Start(curent_offset as u64)).unwrap(); + reader.seek(SeekFrom::Current(ptr as i64)).unwrap(); + reader.read_be::().unwrap().to_string() + } else { + String::from("") + } + } + + for (current_offset, entry) in entries + .iter_mut() + .enumerate() + .map(|(i, e)| (((0x60 * i + HEADER_SIZE) as u64, e))) + { + entry.accessory = read_string_from_ptr(&mut reader, entry.accessory_ptr, current_offset); + entry.bone_name = read_string_from_ptr(&mut reader, entry.bone_name_ptr, current_offset + 0x8); + entry.accessory_location = read_string_from_ptr(&mut reader, entry.accessory_location_ptr, current_offset + 0x18); + } + + BodAcc { + size, + version, + entry_count, + entry_ptr, + entries + } + + } + +} + +impl From for Vec { + fn from(mut bodacc: BodAcc) -> Self { + let mut writer = Cursor::new(Vec::new()); + + bodacc.entry_count = bodacc.entries.len() as u32; // Update entry count + + writer.write_be(&bodacc.size).unwrap(); + writer.write_le(&1000u32).unwrap(); // Write the version + + writer.write_le(&bodacc.entry_count).unwrap(); + + + writer.write_le(&8u64).unwrap(); // Write the ptr to the entries + + + writer.write_le(&bodacc.entries).unwrap(); + + fn write_ptr_to_string( + writer: &mut Cursor>, + string: &String, + current_offset: u64, + adjustment: u64, + ) { + if !string.is_empty() { + writer.seek(SeekFrom::End(0)).unwrap(); + let string_pos = writer.seek(SeekFrom::End(0)).unwrap(); + writer.write_be::(&NullString::from(string.clone())).unwrap(); + + // Align to 8 bytes + let pos = writer.seek(SeekFrom::Current(0)).unwrap() - string_pos; + if 8 - (pos % 8) != 8 { + writer.write_le::>(&vec![0; 8 - (pos % 8) as usize]).unwrap(); + } + + writer.seek(SeekFrom::Start((current_offset + adjustment) as u64)).unwrap(); + writer.write_le::(&(string_pos - current_offset - &adjustment)).unwrap(); + + } + } + for (current_offset, entry) in bodacc.entries + .iter_mut() + .enumerate() + .map(|(i, e)| (((0x60 * i + HEADER_SIZE) as u64, e))) + { + + write_ptr_to_string(&mut writer, &entry.accessory, current_offset as u64, 0x0); + write_ptr_to_string(&mut writer, &entry.bone_name, current_offset as u64, 0x8); + write_ptr_to_string(&mut writer, &entry.accessory_location, current_offset as u64, 0x18); + } + + // Go to the start of buffer and write the size + writer.set_position(0); + writer.write_be::(&((writer.get_ref().len() - 4) as u32)).unwrap(); + + writer.into_inner() + + } +} \ No newline at end of file diff --git a/src/nucc_binary/character_select_param.rs b/src/nucc_binary/character_select_param.rs index c2b220d..38e5364 100644 --- a/src/nucc_binary/character_select_param.rs +++ b/src/nucc_binary/character_select_param.rs @@ -8,7 +8,7 @@ use super::{NuccBinaryParsed, NuccBinaryType}; const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[allow(non_snake_case)] pub struct Entry { #[serde(skip)] @@ -37,9 +37,13 @@ pub struct Entry { pub render_settings: RenderSettings, - #[serde(skip)] + #[serde(skip)] pub dictionary_link_ptr: u64, + #[brw(pad_after = 4)] + pub index: i32, // not sure maybe an index? + + #[brw(ignore)] #[bw(map = |x| x.parse::().unwrap())] pub searchcode: String, @@ -67,7 +71,7 @@ pub struct Entry { #[allow(non_snake_case)] #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct RenderSettings { pub ofsX1P: f32, pub ofsY1P: f32, @@ -161,10 +165,7 @@ pub struct CharacterSelectParam { #[serde(skip)] pub version: u32, - pub entry_count: u16, - - #[serde(skip)] - pub unk0: u16, + pub entry_count: u32, #[serde(skip)] pub entry_ptr: u64, @@ -202,8 +203,7 @@ impl From<&[u8]> for CharacterSelectParam { let size = reader.read_be::().unwrap(); let version = reader.read_le::().unwrap(); - let entry_count = reader.read_le::().unwrap(); - let unk0 = reader.read_le::().unwrap(); + let entry_count = reader.read_le::().unwrap(); let entry_ptr = reader.read_le::().unwrap(); @@ -215,6 +215,7 @@ impl From<&[u8]> for CharacterSelectParam { entries.push(entry); } + fn read_string_from_ptr(reader: &mut Cursor<&[u8]>, ptr: u64, curent_offset: u64) -> String { if ptr != 0 { reader.seek(SeekFrom::Start(curent_offset as u64)).unwrap(); @@ -228,7 +229,7 @@ impl From<&[u8]> for CharacterSelectParam { for (current_offset, entry) in entries .iter_mut() .enumerate() - .map(|(i, e)| (((0x138 * i + HEADER_SIZE) as u64, e))) + .map(|(i, e)| (((0x140 * i + HEADER_SIZE) as u64, e))) { entry.searchcode = read_string_from_ptr(&mut reader, entry.searchcode_ptr, current_offset); entry.char_name = read_string_from_ptr(&mut reader, entry.char_name_ptr, current_offset + 0x18); @@ -242,7 +243,6 @@ impl From<&[u8]> for CharacterSelectParam { size, version, entry_count, - unk0, entry_ptr, entries, } @@ -254,13 +254,12 @@ impl From for Vec { fn from(mut character_select_param: CharacterSelectParam) -> Self { let mut writer = Cursor::new(Vec::new()); - character_select_param.entry_count = character_select_param.entries.len() as u16; // Update entry count + character_select_param.entry_count = character_select_param.entries.len() as u32; // Update entry count writer.write_be(&character_select_param.size).unwrap(); writer.write_le(&1001u32).unwrap(); // Write the version writer.write_le(&character_select_param.entry_count).unwrap(); - writer.write_le(&character_select_param.unk0).unwrap(); writer.write_le(&8u64).unwrap(); // Write the ptr to the entries @@ -291,7 +290,7 @@ impl From for Vec { for (current_offset, entry) in character_select_param.entries .iter_mut() .enumerate() - .map(|(i, e)| (((0x138 * i + HEADER_SIZE) as u64, e))) + .map(|(i, e)| (((0x140 * i + HEADER_SIZE) as u64, e))) { write_ptr_to_string(&mut writer, &entry.searchcode, current_offset, 0x0); write_ptr_to_string(&mut writer, &entry.char_name, current_offset, 0x18); diff --git a/src/nucc_binary/command_list_param.rs b/src/nucc_binary/command_list_param.rs index 4f14ad0..d8e530d 100644 --- a/src/nucc_binary/command_list_param.rs +++ b/src/nucc_binary/command_list_param.rs @@ -35,8 +35,8 @@ pub struct Entry { pub unk2: u32, pub unk3: u32, - pub command_type: u32, - pub support_flag: u32, // (0 = always appear, 1 = only appears if no team, 2 = only appear if team) + pub command_type: u32, // Usually 2 for a playable character's command list + pub unk5: u32, // 0 pub unk6: i32, pub unk7: i32, diff --git a/src/nucc_binary/costume_break_param.rs b/src/nucc_binary/costume_break_param.rs index 666d833..24f0c2f 100644 --- a/src/nucc_binary/costume_break_param.rs +++ b/src/nucc_binary/costume_break_param.rs @@ -9,7 +9,7 @@ const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Entry { pub characode_index: u32, pub costume_index: u32, diff --git a/src/nucc_binary/costume_param.rs b/src/nucc_binary/costume_param.rs index 737d5e7..a019f00 100644 --- a/src/nucc_binary/costume_param.rs +++ b/src/nucc_binary/costume_param.rs @@ -8,7 +8,7 @@ use super::{NuccBinaryParsed, NuccBinaryType}; const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Entry { #[serde(skip)] pub costume_link_ptr: u64, @@ -34,7 +34,6 @@ pub struct Entry { #[bw(map = |x| x.parse::().unwrap())] pub costume_name: String, - } #[binrw] diff --git a/src/nucc_binary/dictionary_character_param.rs b/src/nucc_binary/dictionary_character_param.rs index 8ee14be..a6c1d68 100644 --- a/src/nucc_binary/dictionary_character_param.rs +++ b/src/nucc_binary/dictionary_character_param.rs @@ -51,10 +51,10 @@ pub struct Entry { #[serde(skip)] pub dictionary_desc_ptr: u64, - pub justu1: i32, - pub jutsu2: i32, + pub unk1: i32, + pub unk2: i32, #[brw(pad_after = 0x94)] - pub jutsu3: i32, + pub unk3: i32, #[serde(skip)] pub additional_link1_ptr: u64, @@ -309,4 +309,4 @@ impl From for Vec { - + \ No newline at end of file diff --git a/src/nucc_binary/message_info.rs b/src/nucc_binary/message_info.rs index 9ba4b18..20bc74f 100644 --- a/src/nucc_binary/message_info.rs +++ b/src/nucc_binary/message_info.rs @@ -9,7 +9,7 @@ const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers // Format was reversed by TheLeonX (https://github.com/TheLeonX) #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Entry { #[serde(with = "hex::serde")] pub crc32: [u8; 4], diff --git a/src/nucc_binary/mod.rs b/src/nucc_binary/mod.rs index ca88818..db73a36 100644 --- a/src/nucc_binary/mod.rs +++ b/src/nucc_binary/mod.rs @@ -3,6 +3,7 @@ mod accessory_exception_param; mod accessory_param; mod anime_song_bgm_param; mod anm_offset; +mod bodacc; mod characode; mod chara_pose_param; mod character_select_param; @@ -17,12 +18,13 @@ mod effectprm; mod ev; mod final_sp_skill_cutin; mod lua; -mod message_info; +pub mod message_info; mod ougi_finish_param; mod player_double_effect_param; mod player_setting_param; mod player_icon; mod png; +mod prm_bas; mod prm_load; mod prohibited_substring_param; mod skill_index_setting_param; @@ -30,13 +32,14 @@ mod snd; mod staff_roll_text_param; mod support_action_param; mod support_skill_recovery_speed_param; +mod update_info_param; mod xml; use binrw::{BinReaderExt, BinWriterExt}; use binrw::io::Cursor; use downcast_rs::{impl_downcast, Downcast}; -use nuccbin::NuccBinaryType; +use super::NuccBinaryType; //--------------------// pub use accessories_param::AccessoriesParam; @@ -44,6 +47,7 @@ pub use accessory_exception_param::AccessoryExceptionParam; pub use accessory_param::AccessoryParam; pub use anime_song_bgm_param::AnimeSongBgmParam; pub use anm_offset::Anmofs; +pub use bodacc::BodAcc; pub use characode::Characode; pub use chara_pose_param::CharaPoseParam; pub use character_select_param::CharacterSelectParam; @@ -64,6 +68,7 @@ pub use player_double_effect_param::PlayerDoubleEffectParam; pub use player_setting_param::PlayerSettingParam; pub use player_icon::PlayerIcon; pub use png::Png; +pub use prm_bas::PrmBas; pub use prm_load::PrmLoad; pub use prohibited_substring_param::ProhibitedSubstringParam; pub use skill_index_setting_param::SkillIndexSettingParam; @@ -71,10 +76,12 @@ pub use snd::Snd; pub use staff_roll_text_param::StaffRollTextParam; pub use support_action_param::SupportActionParam; pub use support_skill_recovery_speed_param::SupportSkillRecoverySpeedParam; +pub use update_info_param::UpdateInfoParam; pub use xml::Xml; //--------------------// + pub trait NuccBinaryParsed: Downcast { fn binary_type(&self) -> NuccBinaryType; fn extension(&self) -> String; @@ -86,6 +93,7 @@ pub trait NuccBinaryParsed: Downcast { impl_downcast!(NuccBinaryParsed); + pub struct NuccBinaryParsedReader<'a> (pub NuccBinaryType, pub &'a [u8]); impl From> for Box { @@ -101,6 +109,8 @@ impl From> for Box { let mut anm_offset = Cursor::new(data); Box::new(anm_offset.read_le::().unwrap()) } + + NuccBinaryType::BodAcc => Box::new(BodAcc::from(&data[..])), NuccBinaryType::Characode => { let mut characode = Cursor::new(data); @@ -142,6 +152,11 @@ impl From> for Box { NuccBinaryType::PlayerIcon => Box::new(PlayerIcon::from(&data[..])), NuccBinaryType::Png => Box::new(Png::from(&data[..])), + NuccBinaryType::PrmBas => { + let mut prm_bas = Cursor::new(data); + Box::new(prm_bas.read_le::().unwrap()) + } + NuccBinaryType::PrmLoad => { let mut prm_load = Cursor::new(data); Box::new(prm_load.read_le::().unwrap()) @@ -171,6 +186,8 @@ impl From> for Box { Box::new(support_skill_recovery_speed_param.read_le::().unwrap()) } + NuccBinaryType::UpdateInfoParam => Box::new(UpdateInfoParam::from(&data[..])), + NuccBinaryType::Xml => Box::new(Xml::from(&data[..])), } } @@ -196,6 +213,8 @@ impl From for Vec { anm_offset.write_be::(&((anm_offset.get_ref().len() - 4) as u32)).unwrap(); anm_offset.into_inner() }, + + NuccBinaryType::BodAcc => { (*boxed.downcast::().ok().unwrap()).into() }, NuccBinaryType::Characode => { let mut characode = Cursor::new(Vec::new()); characode.write_le(&*boxed.downcast::().ok().unwrap()).unwrap(); @@ -248,6 +267,16 @@ impl From for Vec { NuccBinaryType::PlayerSettingParam => { (*boxed.downcast::().ok().unwrap()).into() }, NuccBinaryType::PlayerIcon => { (*boxed.downcast::().ok().unwrap()).into() }, NuccBinaryType::Png => { (*boxed.downcast::().ok().unwrap()).into() }, + + NuccBinaryType::PrmBas => { + let mut prm_bas = Cursor::new(Vec::new()); + prm_bas.write_le(&*boxed.downcast::().ok().unwrap()).unwrap(); + + prm_bas.set_position(0); + prm_bas.write_be::(&((prm_bas.get_ref().len() - 4) as u32)).unwrap(); + prm_bas.into_inner() + }, + NuccBinaryType::PrmLoad => { let mut prm_load = Cursor::new(Vec::new()); prm_load.write_le(&*boxed.downcast::().ok().unwrap()).unwrap(); @@ -295,6 +324,8 @@ impl From for Vec { support_skill_recovery_speed_param.into_inner() }, + NuccBinaryType::UpdateInfoParam => { (*boxed.downcast::().ok().unwrap()).into() }, + NuccBinaryType::Xml => { (*boxed.downcast::().ok().unwrap()).into() } } } @@ -322,6 +353,7 @@ impl From for Box { NuccBinaryType::AccessoryParam => Box::new(AccessoryParam::deserialize(&data)), NuccBinaryType::AnimeSongBgmParam => Box::new(AnimeSongBgmParam::deserialize(&data)), NuccBinaryType::Anmofs => Box::new(Anmofs::deserialize(&data)), + NuccBinaryType::BodAcc => Box::new(BodAcc::deserialize(&data)), NuccBinaryType::Characode => Box::new(Characode::deserialize(&data)), NuccBinaryType::CharaPoseParam => Box::new(CharaPoseParam::deserialize(&data)), NuccBinaryType::CharacterSelectParam => Box::new(CharacterSelectParam::deserialize(&data)), @@ -342,6 +374,7 @@ impl From for Box { NuccBinaryType::PlayerSettingParam => Box::new(PlayerSettingParam::deserialize(&data)), NuccBinaryType::PlayerIcon => Box::new(PlayerIcon::deserialize(&data)), NuccBinaryType::Png => Box::new(Png::deserialize(&data)), + NuccBinaryType::PrmBas => Box::new(PrmBas::deserialize(&data)), NuccBinaryType::PrmLoad => Box::new(PrmLoad::deserialize(&data)), NuccBinaryType::ProhibitedSubstringParam => Box::new(ProhibitedSubstringParam::deserialize(&data)), NuccBinaryType::SkillIndexSettingParam => Box::new(SkillIndexSettingParam::deserialize(&data)), @@ -349,6 +382,7 @@ impl From for Box { NuccBinaryType::StaffRollTextParam => Box::new(StaffRollTextParam::deserialize(&data)), NuccBinaryType::SupportActionParam => Box::new(SupportActionParam::deserialize(&data)), NuccBinaryType::SupportSkillRecoverySpeedParam => Box::new(SupportSkillRecoverySpeedParam::deserialize(&data)), + NuccBinaryType::UpdateInfoParam => Box::new(UpdateInfoParam::deserialize(&data)), NuccBinaryType::Xml => Box::new(Xml::deserialize(&data)) } } diff --git a/src/nucc_binary/ougi_finish_param.rs b/src/nucc_binary/ougi_finish_param.rs index 5260da0..3830b16 100644 --- a/src/nucc_binary/ougi_finish_param.rs +++ b/src/nucc_binary/ougi_finish_param.rs @@ -17,14 +17,16 @@ pub struct Entry { #[serde(skip)] pub ougi_fin_link_ptr: u64, - pub index: u32, - pub spl_fin_index: u32, + #[serde(skip)] + pub unk_ptr: u64, #[serde(skip)] - pub spl_fin_ptr: u64, + pub spl_fin_ptr: u32, + pub index: u32, #[serde(skip)] - pub spl_fin_path_ptr: u64, + pub spl_fin_path_ptr: u32, + pub unk4: u32, #[serde(skip)] pub spl_fin_small_ptr: u64, @@ -33,13 +35,17 @@ pub struct Entry { pub spl_fin_big_ptr: u64, pub price: u32, - pub beginning_id: u32, + pub unlock_condition: u32, #[serde(skip)] - pub search_code_ptr: u64, + pub search_code_ptr: u32, + + pub unk1: u32, #[serde(skip)] - pub section_id_ptr: u64, + pub section_id_ptr: u32, + + pub unk2: u32, #[brw(ignore)] #[bw(map = |x| x.parse::().unwrap())] @@ -49,6 +55,10 @@ pub struct Entry { #[bw(map = |x| x.parse::().unwrap())] pub ougi_fin_link: String, + #[brw(ignore)] + #[bw(map = |x| x.parse::().unwrap())] + pub unk: String, + #[brw(ignore)] #[bw(map = |x| x.parse::().unwrap())] pub spl_fin: String, @@ -132,7 +142,10 @@ impl From<&[u8]> for OugiFinishParam { } fn read_string_from_ptr(reader: &mut Cursor<&[u8]>, ptr: u64, curent_offset: u64) -> String { - if ptr != 0 { + // If the pointer is not 0 or -1, read the string from the pointer + + + if ptr != 0 && ptr < 100 && ptr != 0xffffffff as u64 { reader.seek(SeekFrom::Start(curent_offset as u64)).unwrap(); reader.seek(SeekFrom::Current(ptr as i64)).unwrap(); reader.read_be::().unwrap().to_string() @@ -148,12 +161,13 @@ impl From<&[u8]> for OugiFinishParam { { entry.char_name = read_string_from_ptr(&mut reader, entry.char_name_ptr, current_offset as u64); entry.ougi_fin_link = read_string_from_ptr(&mut reader, entry.ougi_fin_link_ptr, current_offset + 0x8); - entry.spl_fin = read_string_from_ptr(&mut reader, entry.spl_fin_ptr, current_offset + 0x18); - entry.spl_fin_path = read_string_from_ptr(&mut reader, entry.spl_fin_path_ptr, current_offset + 0x20); + entry.unk = read_string_from_ptr(&mut reader, entry.unk_ptr, current_offset + 0x10); + entry.spl_fin = read_string_from_ptr(&mut reader, entry.spl_fin_ptr as u64, current_offset + 0x18); + entry.spl_fin_path = read_string_from_ptr(&mut reader, entry.spl_fin_path_ptr as u64, current_offset + 0x20); entry.spl_fin_small = read_string_from_ptr(&mut reader, entry.spl_fin_small_ptr, current_offset + 0x28); entry.spl_fin_big = read_string_from_ptr(&mut reader, entry.spl_fin_big_ptr, current_offset + 0x30); - entry.search_code = read_string_from_ptr(&mut reader, entry.search_code_ptr, current_offset + 0x40); - entry.section_id = read_string_from_ptr(&mut reader, entry.section_id_ptr, current_offset + 0x48); + entry.search_code = read_string_from_ptr(&mut reader, entry.search_code_ptr as u64, current_offset + 0x40); + entry.section_id = read_string_from_ptr(&mut reader, entry.section_id_ptr as u64, current_offset + 0x48); } Self { diff --git a/src/nucc_binary/player_icon.rs b/src/nucc_binary/player_icon.rs index d20fe92..e325537 100644 --- a/src/nucc_binary/player_icon.rs +++ b/src/nucc_binary/player_icon.rs @@ -8,7 +8,7 @@ const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers // Format was reversed by TheLeonX (https://github.com/TheLeonX) #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Entry { pub characode_index: u32, pub duel_player_param_costume_index: i32, diff --git a/src/nucc_binary/player_setting_param.rs b/src/nucc_binary/player_setting_param.rs index 4dfe66e..21014ad 100644 --- a/src/nucc_binary/player_setting_param.rs +++ b/src/nucc_binary/player_setting_param.rs @@ -8,7 +8,7 @@ use super::{NuccBinaryParsed, NuccBinaryType}; const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Entry { pub player_setting_id: u32, pub characode_index: u32, @@ -47,7 +47,7 @@ pub struct Entry { } #[binrw] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct PlayerSettingParam { #[serde(skip)] pub size: u32, diff --git a/src/nucc_binary/prm_bas.rs b/src/nucc_binary/prm_bas.rs new file mode 100644 index 0000000..cd9fdca --- /dev/null +++ b/src/nucc_binary/prm_bas.rs @@ -0,0 +1,65 @@ +use binrw::binrw; +use serde::{Serialize, Deserialize}; + +use super::{NuccBinaryParsed, NuccBinaryType}; + +const STR_LEN: usize = 0x8; + +#[binrw] +#[derive(Serialize, Deserialize, Debug)] +pub struct Entry { + #[br(map = |x: Vec| String::from_utf8_lossy(&x).trim_end_matches('\u{0}').to_string(), count = STR_LEN)] // Need to trim the null bytes + #[bw(map = |x: &String| (x.clone() + String::from('\u{0}').repeat(STR_LEN - x.len()).as_str()).into_bytes())] + pub characode: String, + + pub modelcodes: [VecString; 0x10], + + #[brw(pad_before = 0x18)] + pub awamodelcodes: [VecString; 0x10], + + #[br(count = 0x1C0)] + pub extra: Vec + +} + +#[binrw] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct VecString { + #[br(map = |x: Vec| String::from_utf8_lossy(&x).trim_end_matches('\u{0}').to_string(), count = STR_LEN)] + #[bw(map = |x: &String| (x.clone() + String::from('\u{0}').repeat(STR_LEN - x.len()).as_str()).into_bytes())] + pub code: String +} + + +#[binrw] +#[derive(Serialize, Deserialize, Debug)] +pub struct PrmBas { + #[serde(skip)] + pub size: u32, + + + pub entry: Entry +} + +impl NuccBinaryParsed for PrmBas { + fn binary_type(&self) -> NuccBinaryType { + NuccBinaryType::PrmBas + } + + fn extension(&self) -> String { + String::from(".json") + } + + fn serialize(&self) -> Vec { + serde_json::to_string_pretty(self).unwrap().into() + } + + fn deserialize(data: &[u8]) -> Self + where + Self: Sized, + + { + serde_json::from_slice(data).unwrap() + } +} + diff --git a/src/nucc_binary/update_info_param.rs b/src/nucc_binary/update_info_param.rs new file mode 100644 index 0000000..3abd96b --- /dev/null +++ b/src/nucc_binary/update_info_param.rs @@ -0,0 +1,165 @@ +use binrw::{binrw, BinReaderExt, BinWriterExt, NullString}; +use binrw::io::{Cursor, Seek, SeekFrom}; +use serde::{Serialize, Deserialize}; + +use super::{NuccBinaryParsed, NuccBinaryType}; + +const HEADER_SIZE: usize = 0x14; // Size of NUCC Binary headers + + +#[binrw] +#[derive(Serialize, Deserialize, Debug)] +pub struct Entry { + #[brw(pad_after = 0x4)] + pub unk1: u32, + + #[serde(skip)] + pub text_ptr: u64, + + #[brw(pad_after = 0x4)] + pub unk2: u32, + + + #[brw(ignore)] + #[bw(map = |x| x.parse::().unwrap())] + pub text: String +} + +#[binrw] +#[derive(Serialize, Deserialize, Debug)] +pub struct UpdateInfoParam { + #[serde(skip)] + pub size: u32, + + #[serde(skip)] + pub version: u32, + + pub entry_count: u32, + + #[serde(skip)] + pub entry_ptr: u64, + + #[br(count = entry_count)] + pub entries: Vec +} + +impl NuccBinaryParsed for UpdateInfoParam { + fn binary_type(&self) -> NuccBinaryType { + NuccBinaryType::UpdateInfoParam + } + + fn extension(&self) -> String { + String::from(".json") + } + + fn serialize(&self) -> Vec { + serde_json::to_string_pretty(self).unwrap().into() + } + + fn deserialize(data: &[u8]) -> Self + where + Self: Sized, + { + serde_json::from_slice(data).unwrap() + } +} + +impl From<&[u8]> for UpdateInfoParam { + fn from(data: &[u8]) -> Self { + let mut reader = Cursor::new(data); + + let size = reader.read_be::().unwrap(); + let version = reader.read_le::().unwrap(); + + let entry_count = reader.read_le::().unwrap(); + let entry_ptr = reader.read_le::().unwrap(); + + let mut entries = Vec::new(); + entries.reserve_exact(entry_count as usize); // Make sure we reserve enough space to avoid reallocations + + for _ in 0..entry_count as usize { + let entry = reader.read_le::().unwrap(); + entries.push(entry); + } + + fn read_string_from_ptr(reader: &mut Cursor<&[u8]>, ptr: u64, curent_offset: u64) -> String { + if ptr != 0 { + reader.seek(SeekFrom::Start(curent_offset as u64)).unwrap(); + reader.seek(SeekFrom::Current(ptr as i64)).unwrap(); + reader.read_be::().unwrap().to_string() + } else { + String::from("") + } + } + + for (current_offset, entry) in entries + .iter_mut() + .enumerate() + .map(|(i, e)| (((0x18 * i + HEADER_SIZE) as u64, e))) + { + entry.text = read_string_from_ptr(&mut reader, entry.text_ptr, current_offset + 0x8); + } + + Self { + size, + version, + entry_count, + entry_ptr, + entries + } + } +} + +impl From for Vec { + fn from(mut update_info_param: UpdateInfoParam) -> Self { + // Consumes the deserialized version and returns the bytes + let mut writer = Cursor::new(Vec::new()); + + update_info_param.entry_count = update_info_param.entries.len() as u32; // Update entry count + + writer.write_be(&update_info_param.size).unwrap(); + writer.write_le(&1000u32).unwrap(); // Write the version + + writer.write_le(&update_info_param.entry_count).unwrap(); + + writer.write_le(&8u64).unwrap(); // Write the ptr to the entries + + writer.write_le(&update_info_param.entries).unwrap(); + + fn write_ptr_to_string( + writer: &mut Cursor>, + string: &String, + current_offset: u64, + adjustment: u64, + ) { + if !string.is_empty() { + writer.seek(SeekFrom::End(0)).unwrap(); + let string_pos = writer.seek(SeekFrom::End(0)).unwrap(); + writer.write_be::(&NullString::from(string.clone())).unwrap(); + + // Align to 8 bytes + let pos = writer.seek(SeekFrom::Current(0)).unwrap() - string_pos; + if 8 - (pos % 8) != 8 { + writer.write_le::>(&vec![0; 8 - (pos % 8) as usize]).unwrap(); + } + + writer.seek(SeekFrom::Start((current_offset + adjustment) as u64)).unwrap(); + writer.write_le::(&(string_pos - current_offset - &adjustment)).unwrap(); + + } + } + for (current_offset, entry) in update_info_param.entries + .iter_mut() + .enumerate() + .map(|(i, e)| (((0x18 * i + HEADER_SIZE) as u64, e))) + { + write_ptr_to_string(&mut writer, &entry.text, current_offset as u64, 0x8); + } + + writer.set_position(0); + writer.write_be::(&((writer.get_ref().len() - 4) as u32)).unwrap(); + + writer.into_inner() + } +} +