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 = 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 = 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::(&xdb).unwrap().as_bytes()); //info!("Result: {:?}", r); xdb }