Skip to content

Commit

Permalink
Added support for accessoriesParam & commandListParam
Browse files Browse the repository at this point in the history
  • Loading branch information
maxcabd authored Dec 15, 2023
1 parent 7fdef81 commit 655f4ea
Show file tree
Hide file tree
Showing 23 changed files with 1,003 additions and 410 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ A tool to serialize/deserialize several nuccChunkBinary .xfbin's from Ultimate N
nuccbin supports a number of in game nuccChunkBinary param / bin formats. All formats support serializing. While some may not support deserializing.
| File | Serialize | Deserialize | Extension |
| --- | --- | --- | --- |
| [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` |
| [animeSongBgmParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/anime_song_bgm_param.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` |
| [comboPrm](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/combo_prm.rs) | ✔️ | ✔️ | `json` |
| [commmandListParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/command_list_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` |
| [DlcInfoParam](https://github.com/maxcabd/nuccbin/blob/main/src/nucc_binary/dlc_info_param.rs) | ✔️ | ✔️ | `json` |
Expand All @@ -42,9 +44,9 @@ This project is based on the [initial work](https://github.com/SutandoTsukai181/
Special thanks goes to several members including:
* [EliteAce170](https://www.youtube.com/@EliteAce) for reversing some formats.
* [HydraBladeZ](https://github.com/Al-Hydra) for reversing some formats.
* [TheLeonX](https://github.com/TheLeonX) for work on the Character Manager which was used as inspiration and several formats.
* [PortableProductions](https://www.youtube.com/@PortableProductions) for help on several formats and information.
* [Kuroha Saenoki](https://www.youtube.com/@KurohaSaenoki) for information on some formats.
* [TheLeonX](https://github.com/TheLeonX) for work on the Character Manager which was used as inspiration and some formats.
* [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/@valant96) for reversing some formats.
* and [SutandoTsukai181](https://github.com/SutandoTsukai181) for his initial work on the tool.
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ use regex::Regex;

#[derive(Debug, Copy, Clone, EnumString, EnumIter, Display, PartialEq)]
pub enum NuccBinaryType {
AccessoriesParam,
AccessoryExceptionParam,
AccessoryParam,
AnimeSongBgmParam,
#[strum(ascii_case_insensitive)]
Characode,
CharaPoseParam,
CharacterSelectParam,
ComboPrm,
CommandListParam,
CostumeParam,
Dds,
DlcInfoParam,
Expand All @@ -35,12 +38,15 @@ impl NuccBinaryType {
pub fn patterns(&self) -> Regex {

match self {
NuccBinaryType::AccessoriesParam => { Regex::new(r"(accessoriesParam\.bin)$").unwrap() },
NuccBinaryType::AccessoryExceptionParam=> { Regex::new(r"(accessoryExceptionParam\.bin)$").unwrap() },
NuccBinaryType::AccessoryParam => { Regex::new(r"(accessoryParam\.bin)$").unwrap() },
NuccBinaryType::AnimeSongBgmParam => { Regex::new(r"(animeSongBgmParam\.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() },
NuccBinaryType::ComboPrm => { Regex::new(r"(comboPrm\.bin)$").unwrap() },
NuccBinaryType::CommandListParam => { Regex::new(r"(commandListParam\.bin)$").unwrap() },
NuccBinaryType::CostumeParam => { Regex::new(r"(costumeParam\.bin)$").unwrap() },
NuccBinaryType::Dds => { Regex::new(r"(\.dds)$").unwrap() },
NuccBinaryType::DlcInfoParam => { Regex::new(r"(DlcInfoParam\.bin)$").unwrap() },
Expand Down
216 changes: 216 additions & 0 deletions src/nucc_binary/accessories_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
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 Kuroha Saenoki (https://www.youtube.com/@KurohaSaenoki)

#[allow(non_snake_case)]
#[binrw]
#[derive(Serialize, Deserialize, Debug)]
pub struct Entry {
#[serde(skip)]
pub accessory_name_id_ptr: u64,

#[serde(skip)]
pub accessory_link_ptr: u64,

pub index: u32,
pub price: u32,

#[serde(skip)]
pub icon_ptr: u64,
#[serde(skip)]
pub accessory_ptr: u64,

pub ofsX: i32, // Left/Right
pub ofsZ: i32, // Towards/Away from camera
pub ofsY: i32, // Up/Down

pub rotY: i32, // Up/Down
pub rotZ: i32, // Left/Right
pub rotX: i32, // Left/Right

#[brw(pad_after = 4)]
pub unlock_condition: u32,


#[brw(ignore)]
#[bw(map = |x| x.parse::<u8>().unwrap())]
pub accessory_name_id: String,

#[brw(ignore)]
#[bw(map = |x| x.parse::<u8>().unwrap())]
pub accessory_link: String,

#[brw(ignore)]
#[bw(map = |x| x.parse::<u8>().unwrap())]
pub icon: String,

#[brw(ignore)]
#[bw(map = |x| x.parse::<u8>().unwrap())]
pub accessory: String,
}

#[binrw]
#[derive(Serialize, Deserialize, Debug)]
pub struct AccessoriesParam {
#[serde(skip)]
pub size: u32,

#[serde(skip)]
pub version: u32,

pub entry_count: u16,

#[serde(skip)]
pub unk0: u16,

#[serde(skip)]
pub entry_ptr: u64,

#[br(count = entry_count)]
pub entries: Vec<Entry>
}

impl NuccBinaryParsed for AccessoriesParam {
fn binary_type(&self) -> NuccBinaryType {
NuccBinaryType::AccessoriesParam
}

fn extension(&self) -> String {
String::from(".json")
}

fn serialize(&self) -> Vec<u8> {
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 AccessoriesParam {
fn from(data: &[u8]) -> Self {
let mut reader = Cursor::new(data);

let size = reader.read_be::<u32>().unwrap();
let version = reader.read_le::<u32>().unwrap();

let entry_count = reader.read_le::<u16>().unwrap();
let unk0 = reader.read_le::<u16>().unwrap();

let entry_ptr = reader.read_le::<u64>().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::<Entry>().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::<NullString>().unwrap().to_string()
} else {
String::from("")
}
}

for (current_offset, entry) in entries
.iter_mut()
.enumerate()
.map(|(i, e)| (((0x48 * i + HEADER_SIZE) as u64, e)))
{
entry.accessory_name_id = read_string_from_ptr(&mut reader, entry.accessory_name_id_ptr, current_offset as u64);
entry.accessory_link = read_string_from_ptr(&mut reader, entry.accessory_link_ptr, current_offset + 0x8);
entry.icon = read_string_from_ptr(&mut reader, entry.icon_ptr, current_offset + 0x18);
entry.accessory = read_string_from_ptr(&mut reader, entry.accessory_ptr, current_offset + 0x20);
}

Self {
size,
version,
entry_count,
unk0,
entry_ptr,
entries
}

}
}

impl From<AccessoriesParam> for Vec<u8> {
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

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


writer.write_le(&accessories_param.entries).unwrap();

fn write_ptr_to_string(
writer: &mut Cursor<Vec<u8>>,
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>(&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<u8>>(&vec![0; 8 - (pos % 8) as usize]).unwrap();
}

writer.seek(SeekFrom::Start((current_offset + adjustment) as u64)).unwrap();
writer.write_le::<u64>(&(string_pos - current_offset - &adjustment)).unwrap();

}
}
for (current_offset, entry) in accessories_param.entries
.iter_mut()
.enumerate()
.map(|(i, e)| (((0x48 * i + HEADER_SIZE) as u64, e)))
{
write_ptr_to_string(&mut writer, &entry.accessory_name_id, current_offset as u64, 0x0);
write_ptr_to_string(&mut writer, &entry.accessory_link, current_offset as u64, 0x8);
write_ptr_to_string(&mut writer, &entry.icon, current_offset as u64, 0x18);
write_ptr_to_string(&mut writer, &entry.accessory, current_offset as u64, 0x20);
}

// Update the indices in case they were changed
for (i, entry) in accessories_param.entries.iter_mut().enumerate() {
entry.index = i as u32;
}

// Go to the start of buffer and write the size
writer.set_position(0);
writer.write_be::<u32>(&((writer.get_ref().len() - 4) as u32)).unwrap();

writer.into_inner()
}
}
Loading

0 comments on commit 655f4ea

Please sign in to comment.