From 564d894846e6972796ddfe453202b52eb24dd209 Mon Sep 17 00:00:00 2001 From: alterwain Date: Sat, 8 Feb 2025 19:10:12 +0300 Subject: [PATCH] modified: outdb modified: src/deserializer.rs modified: src/main.rs modified: src/objects.rs modified: src/serializer.rs modified: src/xobjects.rs --- outdb | Bin 15518 -> 15758 bytes src/deserializer.rs | 52 ++++++++++++------ src/main.rs | 2 +- src/objects.rs | 4 +- src/serializer.rs | 129 +++++++++++++++++++++++++------------------- src/xobjects.rs | 25 ++------- 6 files changed, 116 insertions(+), 96 deletions(-) diff --git a/outdb b/outdb index 25a6e68d4a3c6ca04bd1a463d86fc49c4bfd9575..1e67b324ccd108b465b88e4be4c066c86a3a8331 100644 GIT binary patch delta 316 zcmbPN*;mb#n~{|Ag@J*g%x)u>8yl+tF9QSfWJbm?rh=Hsh63S}c_cX)zibv1_{)); zS>OXySOLV0Knw!PKr9Qy_wx!LgZMDe^yshM4F$h>Fn&RZ+pqm{SH)rc+>HDb2_Q!Y zh*MyKAok>iYN8y)DG5Nf0Pkc2>&O9j#yZu>8?>z^FW{GB&H)CApq%yQ4-#fd0F{10 ARR910 delta 114 zcmeCHo>$40n~{|Ag@J*g&2}S~8yo93a|Q0svLXAQk`s diff --git a/src/deserializer.rs b/src/deserializer.rs index 2259d8b..e755b06 100644 --- a/src/deserializer.rs +++ b/src/deserializer.rs @@ -37,16 +37,17 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { u = usize::try_from(header.end_of_chunk).unwrap() - 12; let ds: DataSet = bincode::deserialize(&data[i..i+u]).unwrap(); info!("DataSet: {:?}", ds); + last_type = ds.data_type; xdb.children.push(XDataSet { header: header, data: ds.clone(), child: match ds.data_type { - 3 => XSomeList::Playlists(Vec::new()), // Playlist List 4 => XSomeList::AlbumList(Vec::new()), // Album List - _ => XSomeList::TrackList(Vec::new()) // Track List (1) + 1 => XSomeList::TrackList(Vec::new()), // Track List + _ => XSomeList::Playlists(Vec::new()) // Playlist List 3 }}); }, ChunkType::AlbumList => { info!("AlbumList"); u = usize::try_from(header.end_of_chunk).unwrap() - 12; - last_type = 4; + //last_type = 4; }, ChunkType::AlbumItem => { u = usize::try_from(header.end_of_chunk).unwrap() - 12; @@ -60,7 +61,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { ChunkType::TrackList => { info!("TrackList"); u = usize::try_from(header.end_of_chunk).unwrap() - 12; - last_type = 1; + //last_type = 1; }, ChunkType::TrackItem => { u = usize::try_from(header.end_of_chunk).unwrap() - 12; @@ -71,9 +72,12 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { } }, ChunkType::SongReference => { - u = usize::try_from(header.children_count).unwrap() - 12; - let item: PlaylistItem = bincode::deserialize(&data[i..i+24]).unwrap(); + u = usize::try_from(header.end_of_chunk).unwrap() - 12; + let item: PlaylistItem = bincode::deserialize(&data[i..i+76]).unwrap(); info!("PlaylistItem: {:?}", item); + if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child { // 3 + playlists.last_mut().unwrap().elems.push((item, Vec::new())); + } }, ChunkType::StringTypes => { u = usize::try_from(header.children_count).unwrap() - 12; @@ -100,7 +104,11 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { albums.last_mut().unwrap().args.push(XArgument{ arg_type: entry_type, val: g}); }, XSomeList::Playlists(playlists) => { - playlists.last_mut().unwrap().args.push(XPlArgument::String(XArgument{ arg_type: entry_type, val: g})); + if playlists.last().unwrap().elems.is_empty() { + playlists.last_mut().unwrap().args.push(XPlArgument::String(XArgument{ arg_type: entry_type, val: g})); + } else { + playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::String(XArgument{ arg_type: entry_type, val: g})); + } }, XSomeList::TrackList(tracks) => { tracks.last_mut().unwrap().args.push(XArgument{ arg_type: entry_type, val: g}); @@ -109,24 +117,28 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { }, 52 => { let entry: PlaylistIndexEntry = bincode::deserialize(&data[i..i+60]).unwrap(); - info!("valPl: {:?}", &entry); + //info!("valPl: {:?}", &entry); let mut h = i+60; let mut v = Vec::new(); while h < i+60+((4*entry.count) as usize) { v.push(u32::from_le_bytes(data[h..h+4].try_into().unwrap())); h += 4; } - info!("Indexes: {:?}", v); + //info!("Indexes: {:?}", v); match &mut xdb.find_dataset(last_type).child { XSomeList::Playlists(playlists) => { - playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v})); + if playlists.last().unwrap().elems.is_empty() { + playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v})); + } else { + playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v})); + } } _ => {} } }, 53 => { let entry: LetterJumpEntry = bincode::deserialize(&data[i..i+28]).unwrap(); - info!("valJT: {:?}", &entry); + //info!("valJT: {:?}", &entry); let mut h = i+28; let mut v: Vec = Vec::new(); while h < i+28+((12*entry.count) as usize) { @@ -136,7 +148,11 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { info!("Indexes: {:?}", v); match &mut xdb.find_dataset(last_type).child { XSomeList::Playlists(playlists) => { - playlists.last_mut().unwrap().args.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v })); + if playlists.last().unwrap().elems.is_empty() { + playlists.last_mut().unwrap().args.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v })); + } else { + playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v })); + } } _ => {} } @@ -145,7 +161,11 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { info!("Entry #100,102 fetched"); match &mut xdb.find_dataset(last_type).child { XSomeList::Playlists(playlists) => { - playlists.last_mut().unwrap().args.push(XPlArgument::RawArgument(data[i-12..i+(header.children_count as usize)-12].to_vec())); + if playlists.last().unwrap().elems.is_empty() { + playlists.last_mut().unwrap().args.push(XPlArgument::RawArgument(data[i-12..i+(header.children_count as usize)-12].to_vec())); + } else { + playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::RawArgument(data[i-12..i+(header.children_count as usize)-12].to_vec())); + } } _ => {} } @@ -156,14 +176,14 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase { ChunkType::PlaylistList => { info!("Playlists count: {}", header.children_count); u = usize::try_from(header.end_of_chunk).unwrap() - 12; - last_type = 3; + //last_type = 3; }, ChunkType::Playlist => { u = usize::try_from(header.end_of_chunk).unwrap() - 12; let playlist: Playlist = bincode::deserialize(&data[i..i+u]).unwrap(); info!("playlist: {:?}", playlist); - if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(3).child { - playlists.push(XPlaylist {header: header, data: playlist,args: Vec::new()}); + if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child { // 3 + playlists.push(XPlaylist {header: header, data: playlist,args: Vec::new(), elems: Vec::new()}); } }, _ => { u = 1; } diff --git a/src/main.rs b/src/main.rs index cbdd187..25c4504 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ fn main() { Ok(n) => { let data = &buf[..n]; let xdb = deserializer::parse_bytes(data); - info!("XDB: {:?}", xdb); + //info!("XDB: {:?}", xdb); let mut op = File::create("outdb").unwrap(); info!("Write res: {:?}", op.write(&serializer::to_bytes(xdb))); }, diff --git a/src/objects.rs b/src/objects.rs index fcd5ea5..6ea21b6 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -221,5 +221,7 @@ pub struct PlaylistItem { group_id: u32, track_id: u32, timestamp: u32, - podcast_grouping_reference: u32 + podcast_grouping_reference: u32, + unk: [u8; 30], + unk1: [u8; 10] } \ No newline at end of file diff --git a/src/serializer.rs b/src/serializer.rs index 76ea6bf..72a4af2 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -1,4 +1,6 @@ -use crate::{objects::{ChunkHeader, ChunkType, StringEntry}, xobjects::{XArgument, XDatabase, XPlArgument, XSomeList}}; +use log::info; + +use crate::{objects::{ChunkHeader, ChunkType, PlaylistItem, StringEntry}, xobjects::{XArgument, XDatabase, XLetterJump, XPlArgument, XPlaylistIndexEntry, XSomeList}}; fn string_to_ipod16(str: &String) -> Vec { @@ -45,71 +47,86 @@ fn generate_zeroes(cnt: u32) -> Vec { v } +fn serialize_string_arg(xarg: &XArgument) -> Vec { + let s = string_to_ipod16(&xarg.val); + let mut b = bincode::serialize(&StringEntry { + entry_type: xarg.arg_type, + unk1: 0, + unk2: 0, + position: 1, + length: s.len() as u32, + unknown: 0, + unk4: 0 + }).unwrap(); + let h = bincode::serialize(&ChunkHeader { + chunk_type: ChunkType::StringTypes.into(), + end_of_chunk: 0x18, + children_count: 16 + 0x18 + s.len() as u32 + }).unwrap(); + b = [h, b, s].concat(); + return b; +} + +fn serialize_index_entry(xpl: &XPlaylistIndexEntry) -> Vec { + let mut b = bincode::serialize(&xpl.data).unwrap(); + let mut v: Vec = Vec::new(); + for i in xpl.v.iter() { + v = [v, i.to_le_bytes().to_vec()].concat(); + } + let h = bincode::serialize(&ChunkHeader { + chunk_type: ChunkType::StringTypes.into(), + end_of_chunk: 24, + children_count: 12 + (v.len() + b.len()) as u32 + }).unwrap(); + b = [h, b, v].concat(); + return b; +} + +fn serialize_x_letter(xjump: &XLetterJump) -> Vec { + let mut b = bincode::serialize(&xjump.data).unwrap(); + let mut v: Vec = Vec::new(); + for i in xjump.v.iter() { + v.append(&mut bincode::serialize(i).unwrap()); + } + let h = bincode::serialize(&ChunkHeader { + chunk_type: ChunkType::StringTypes.into(), + end_of_chunk: 24, + children_count: 12 + (v.len() + b.len()) as u32 + }).unwrap(); + b = [h, b, v].concat(); + return b; +} + +fn serialize_arguments(pl: &Vec) -> Vec { + pl.iter() + .map(|arg| match arg { + XPlArgument::String(xarg) => serialize_string_arg(xarg), + XPlArgument::IndexEntry(xpl) => serialize_index_entry(xpl), + XPlArgument::LetterJumpEntry(xjump) => serialize_x_letter(xjump), + XPlArgument::RawArgument(raw_arg) => raw_arg.to_vec() + }) + .flatten() + .collect() +} + pub fn to_bytes(xdb: XDatabase) -> Vec { let mut bytes: Vec = Vec::new(); for i in 0..xdb.children.len() { let data_set = xdb.children.get(i).unwrap(); + info!("Serializer: {:?}", data_set); match &data_set.child { XSomeList::Playlists(playlists) => { let mut pl_bytes = Vec::new(); for u in 0..playlists.len() { let playlist = playlists.get(u).unwrap(); - let mut args: Vec = playlist.args.iter() - .map(|arg| match arg { - XPlArgument::String(xarg) => { - let s = string_to_ipod16(&xarg.val); - let mut b = bincode::serialize(&StringEntry { - entry_type: xarg.arg_type, - unk1: 0, - unk2: 0, - position: 1, - length: s.len() as u32, - unknown: 0, - unk4: 0 - }).unwrap(); - let h = bincode::serialize(&ChunkHeader { - chunk_type: ChunkType::StringTypes.into(), - end_of_chunk: 0x18, - children_count: 16 + 0x18 + s.len() as u32 - }).unwrap(); - b = [h, b, s].concat(); - return b; - }, - XPlArgument::IndexEntry(xpl) => { - let mut b = bincode::serialize(&xpl.data).unwrap(); - let mut v: Vec = Vec::new(); - for i in xpl.v.iter() { - v = [v, i.to_le_bytes().to_vec()].concat(); - } - let h = bincode::serialize(&ChunkHeader { - chunk_type: ChunkType::StringTypes.into(), - end_of_chunk: 24, - children_count: 12 + (v.len() + b.len()) as u32 - }).unwrap(); - b = [h, b, v].concat(); - return b; - }, - XPlArgument::LetterJumpEntry(xjump) => { - let mut b = bincode::serialize(&xjump.data).unwrap(); - let mut v: Vec = Vec::new(); - for i in xjump.v.iter() { - v.append(&mut bincode::serialize(i).unwrap()); - } - let h = bincode::serialize(&ChunkHeader { - chunk_type: ChunkType::StringTypes.into(), - end_of_chunk: 24, - children_count: 12 + (v.len() + b.len()) as u32 - }).unwrap(); - b = [h, b, v].concat(); - return b; - }, - XPlArgument::RawArgument(raw_arg) => { - raw_arg.to_vec() - } - }) - .flatten() - .collect(); + let mut args: Vec = serialize_arguments(&playlist.args); + for (playlist_item, xargs) in playlist.elems.iter() { + let mut a = serialize_arguments(xargs); + args.append(&mut generate_header(ChunkType::SongReference, 64,a.len())); + args.append(&mut bincode::serialize(playlist_item).unwrap()); + args.append(&mut a); + } pl_bytes.append(&mut generate_header(ChunkType::Playlist, 36,args.len())); pl_bytes.append(&mut bincode::serialize(&playlist.data).unwrap()); diff --git a/src/xobjects.rs b/src/xobjects.rs index a509425..976a80f 100644 --- a/src/xobjects.rs +++ b/src/xobjects.rs @@ -1,4 +1,4 @@ -use crate::objects::{JumpTable, LetterJumpEntry, PlaylistIndexEntry, ChunkHeader, Database, DataSet, TrackItem, AlbumItem, Playlist}; +use crate::objects::{AlbumItem, ChunkHeader, DataSet, Database, JumpTable, LetterJumpEntry, Playlist, PlaylistIndexEntry, PlaylistItem, TrackItem}; #[derive(Debug, serde::Serialize)] @@ -33,7 +33,8 @@ pub struct XAlbumItem { pub struct XPlaylist { pub header: ChunkHeader, pub data: Playlist, - pub args: Vec + pub args: Vec, + pub elems: Vec<(PlaylistItem, Vec)> } #[derive(Debug, serde::Serialize)] @@ -69,26 +70,6 @@ pub enum XSomeList { AlbumList(Vec) } -impl XSomeList { - fn push_album(&mut self, album: XAlbumItem ) { - if let XSomeList::AlbumList(albums) = self { - albums.push(album); - } - } - - fn push_track(&mut self, track: XTrackItem ) { - if let XSomeList::TrackList(tracks) = self { - tracks.push(track); - } - } - - fn push_playlist(&mut self, playlist: XPlaylist ) { - if let XSomeList::Playlists(playlists) = self { - playlists.push(playlist); - } - } -} - impl XDatabase { pub fn find_dataset(&mut self, data_type: u32) -> &mut XDataSet { self.children.iter_mut().find(|d| d.data.data_type == data_type).unwrap()