modified: src/deserializer.rs modified: src/objects.rs modified: src/serializer.rs
181 lines
9.9 KiB
Rust
181 lines
9.9 KiB
Rust
use log::{info, warn};
|
|
|
|
use crate::{objects::{AlbumItem, ChunkHeader, ChunkType, DataSet, Database, JumpTable, LetterJumpEntry, Playlist, PlaylistIndexEntry, PlaylistItem, StringEntry, TrackItem}, xobjects::{XAlbumItem, XArgument, XDataSet, XDatabase, XLetterJump, XPlArgument, XPlaylist, XPlaylistIndexEntry, XSomeList, XTrackItem}};
|
|
|
|
enum ChunkState {
|
|
Header,
|
|
Data
|
|
}
|
|
|
|
pub fn parse_bytes(data: &[u8]) -> XDatabase {
|
|
let mut xdb = XDatabase{data: None, header: None, children: Vec::new()};
|
|
let mut state = ChunkState::Header;
|
|
let mut chunk_header: Option<ChunkHeader> = None;
|
|
let mut last_type: u32 = 0;
|
|
let mut i = 0;
|
|
while i < data.len() {
|
|
state = match state {
|
|
ChunkState::Header => {
|
|
if i + 12 >= data.len() { break; }
|
|
chunk_header = Some(bincode::deserialize(&data[i..i+12]).unwrap());
|
|
i += 12;
|
|
ChunkState::Data
|
|
},
|
|
ChunkState::Data => {
|
|
let mut u = 0;
|
|
let header = chunk_header.unwrap();
|
|
match ChunkType::from(header.chunk_type) {
|
|
ChunkType::Database => {
|
|
info!("Db header: {:?}", header);
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
let db: Database = bincode::deserialize(&data[i..i+u]).unwrap();
|
|
info!("val: {:?}", db);
|
|
xdb.data = Some(db);
|
|
xdb.header = Some(header);
|
|
},
|
|
ChunkType::DataSet => {
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
let ds: DataSet = bincode::deserialize(&data[i..i+u]).unwrap();
|
|
info!("DataSet: {:?}", ds);
|
|
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)
|
|
}});
|
|
},
|
|
ChunkType::AlbumList => {
|
|
info!("AlbumList");
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
last_type = 4;
|
|
},
|
|
ChunkType::AlbumItem => {
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
let ai: AlbumItem = bincode::deserialize(&data[i..i+u]).unwrap();
|
|
info!("val: {:?}", ai);
|
|
info!("AlbumItem: {}", u);
|
|
if let XSomeList::AlbumList(albums) = &mut xdb.find_dataset(4).child {
|
|
albums.push(XAlbumItem {header: header, data: ai,args: Vec::new()});
|
|
}
|
|
},
|
|
ChunkType::TrackList => {
|
|
info!("TrackList");
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
last_type = 1;
|
|
},
|
|
ChunkType::TrackItem => {
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
let ti: TrackItem = bincode::deserialize(&data[i..i+u]).unwrap();
|
|
info!("val: {:?}", ti);
|
|
if let XSomeList::TrackList(tracks) = &mut xdb.find_dataset(1).child {
|
|
tracks.push(XTrackItem {header: header, data: ti,args: Vec::new()});
|
|
}
|
|
},
|
|
ChunkType::SongReference => {
|
|
u = usize::try_from(header.children_count).unwrap() - 12;
|
|
let item: PlaylistItem = bincode::deserialize(&data[i..i+24]).unwrap();
|
|
info!("PlaylistItem: {:?}", item);
|
|
},
|
|
ChunkType::StringTypes => {
|
|
u = usize::try_from(header.children_count).unwrap() - 12;
|
|
let header_offset: usize = (header.end_of_chunk + 4) as usize;
|
|
let entry_type = u32::from_le_bytes(data[i..i+4].try_into().unwrap());
|
|
match entry_type {
|
|
0..=15 => {
|
|
let str_end: usize = (header.children_count - 12) as usize;
|
|
let entry: StringEntry = bincode::deserialize(&data[i..i+28]).unwrap();
|
|
info!("val: {:?}", &entry);
|
|
let mut bytes = Vec::new();
|
|
|
|
let mut h = i+header_offset;
|
|
while h < i+str_end {
|
|
if data[h] != 0 {
|
|
bytes.push(data[h]);
|
|
}
|
|
h+=1;
|
|
}
|
|
let g = String::from_utf8(bytes).unwrap();
|
|
info!("str: {}", g);
|
|
match &mut xdb.find_dataset(last_type).child {
|
|
XSomeList::AlbumList(albums) => {
|
|
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}));
|
|
},
|
|
XSomeList::TrackList(tracks) => {
|
|
tracks.last_mut().unwrap().args.push(XArgument{ arg_type: entry_type, val: g});
|
|
}
|
|
}
|
|
},
|
|
52 => {
|
|
let entry: PlaylistIndexEntry = bincode::deserialize(&data[i..i+60]).unwrap();
|
|
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);
|
|
match &mut xdb.find_dataset(last_type).child {
|
|
XSomeList::Playlists(playlists) => {
|
|
playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
|
|
}
|
|
_ => {}
|
|
}
|
|
},
|
|
53 => {
|
|
let entry: LetterJumpEntry = bincode::deserialize(&data[i..i+28]).unwrap();
|
|
info!("valJT: {:?}", &entry);
|
|
let mut h = i+28;
|
|
let mut v: Vec<JumpTable> = Vec::new();
|
|
while h < i+28+((12*entry.count) as usize) {
|
|
v.push(bincode::deserialize(&data[h..h+12]).unwrap());
|
|
h += 12;
|
|
}
|
|
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 }));
|
|
}
|
|
_ => {}
|
|
}
|
|
},
|
|
100 | 102 | 50 | 51 => {
|
|
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()));
|
|
}
|
|
_ => {}
|
|
}
|
|
},
|
|
_ => warn!("Unknown entry: {}", entry_type)
|
|
}
|
|
},
|
|
ChunkType::PlaylistList => {
|
|
info!("Playlists count: {}", header.children_count);
|
|
u = usize::try_from(header.end_of_chunk).unwrap() - 12;
|
|
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()});
|
|
}
|
|
},
|
|
_ => { u = 1; }
|
|
}
|
|
i += u;
|
|
chunk_header = None;
|
|
ChunkState::Header
|
|
}
|
|
}
|
|
}
|
|
//let mut f = File::create("output.json").unwrap();
|
|
//let r = f.write(serde_json::to_string::<XDatabase>(&xdb).unwrap().as_bytes());
|
|
//info!("Result: {:?}", r);
|
|
xdb
|
|
} |