diff --git a/.gitignore b/.gitignore index ea8c4bf..0d20b7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +output.json \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bc58c83..aedf2a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,34 +23,14 @@ dependencies = [ ] [[package]] -name = "bytecheck" -version = "0.8.0" +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "bytecheck_derive", - "ptr_meta", - "rancor", - "simdutf8", + "serde", ] -[[package]] -name = "bytecheck_derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bytes" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" - [[package]] name = "env_logger" version = "0.9.3" @@ -64,18 +44,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -92,22 +60,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] -name = "indexmap" -version = "2.7.1" +name = "itoa" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" -dependencies = [ - "equivalent", - "hashbrown", -] +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "itunesdb" version = "0.1.0" dependencies = [ + "bincode", "env_logger", "log", - "rkyv", + "serde", + "serde_json", ] [[package]] @@ -128,26 +94,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "munge" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" -dependencies = [ - "munge_macro", -] - -[[package]] -name = "munge_macro" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.93" @@ -157,26 +103,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "ptr_meta" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "quote" version = "1.0.38" @@ -186,15 +112,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rancor" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" -dependencies = [ - "ptr_meta", -] - [[package]] name = "regex" version = "1.11.1" @@ -225,38 +142,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rend" -version = "0.5.2" +name = "ryu" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ - "bytecheck", + "serde_derive", ] [[package]] -name = "rkyv" -version = "0.8.10" +name = "serde_derive" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65" -dependencies = [ - "bytecheck", - "bytes", - "hashbrown", - "indexmap", - "munge", - "ptr_meta", - "rancor", - "rend", - "rkyv_derive", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -264,10 +168,16 @@ dependencies = [ ] [[package]] -name = "simdutf8" -version = "0.1.5" +name = "serde_json" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] [[package]] name = "syn" @@ -289,33 +199,12 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "tinyvec" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" -[[package]] -name = "uuid" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 8ce067f..c50e23d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,14 @@ name = "itunesdb" version = "0.1.0" edition = "2021" +authors = ["alterwain"] + +[lib] +crate-type = ["staticlib", "cdylib", "lib"] [dependencies] env_logger = "0.9" log = "0.4.20" -rkyv = { version = "0.8.10", features = ["unaligned", "little_endian"]} +bincode = "1.3.3" +serde = { version = "1.0.217", features = ["derive"] } +serde_json = "1.0.138" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b76dedf..636f298 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use std::{fs::File, io::Read}; +use std::{fs::File, io::{Read, Write}}; use env_logger::Builder; use log::{error, info, LevelFilter}; -use rkyv::{deserialize, rancor::Error, Archive, Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; +use xml::{XAlbumItem, XArgument, XDataSet, XDatabase, XPlaylist, XSomeList, XTrackItem}; mod xml; @@ -35,16 +36,14 @@ impl From<[u8; 4]> for ChunkType { } } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] +#[derive(Serialize, Deserialize, PartialEq, Debug)] struct ChunkHeader { chunk_type: [u8; 4], end_of_chunk: u32, children_count: u32 } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] +#[derive(Serialize, Deserialize, PartialEq, Debug)] struct Database { unknown: u32, version: u32, @@ -56,14 +55,12 @@ struct Database { hash: [u8; 20] } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] struct DataSet { data_type: u32 } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] +#[derive(Serialize, Deserialize, PartialEq, Debug)] struct AlbumItem { number_of_strings: u32, unknown: u16, @@ -72,8 +69,7 @@ struct AlbumItem { unknown1: u32 } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] +#[derive(Serialize, Deserialize, PartialEq, Debug)] struct TrackItem { number_of_strings: u32, // number of mhod's count unique_id: u32, @@ -147,9 +143,8 @@ struct TrackItem { mhii_link: u32 } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] -struct Entry { // mhod +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct StringEntry { // mhod entry_type: u32, unk1: u32, unk2: u32, @@ -159,8 +154,38 @@ struct Entry { // mhod unk4: u32 } -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[rkyv(compare(PartialEq), derive(Debug))] +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct PlaylistIndexEntry { // mhod + entry_type: u32, + unk1: u32, + unk2: u32, + index_type: u32, + count: u32, + null_padding: u64, + null_padding1: u64, + null_padding2: u64, + null_padding3: u64, + null_padding4: u64 +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct LetterJumpEntry { + entry_type: u32, + unk1: u32, + unk2: u32, + index_type: u32, + count: u32, + null_padding: u64 +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct JumpTable { + letter: u32, // UTF-16 LE Uppercase with two padding null bytes + entry_num: u32, // the number of the first entry in the corresponding MHOD52 index starting with this letter. Zero-based and incremented by one for each entry, not 4. + count: u32 // the count of entries starting with this letter in the corresponding MHOD52. +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] struct Playlist { data_object_child_count: u32, playlist_item_count: u32, @@ -179,14 +204,17 @@ enum ChunkState { Data } -fn db(data: &[u8]) { +pub fn parse_bytes(data: &[u8]) -> XDatabase { + let mut xdb = XDatabase{data: None, children: Vec::new()}; let mut state = ChunkState::Header; - let mut chunk_header = None; + 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 => { - chunk_header = Some(rkyv::access::(&data[i..i+12]).unwrap()); + if i + 12 >= data.len() { break; } + chunk_header = Some(bincode::deserialize(&data[i..i+12]).unwrap()); i += 12; ChunkState::Data }, @@ -197,56 +225,121 @@ fn db(data: &[u8]) { ChunkType::Database => { info!("Db header: {:?}", header); u = usize::try_from(header.end_of_chunk).unwrap() - 12; - info!("val: {:?}", rkyv::access::(&data[i..i+u]).unwrap()); + let db: Database = bincode::deserialize(&data[i..i+u]).unwrap(); + info!("val: {:?}", db); + xdb.data = Some(db); }, ChunkType::DataSet => { u = usize::try_from(header.end_of_chunk).unwrap() - 12; - info!("val: {:?}", rkyv::access::(&data[i..i+u]).unwrap()); + let ds: DataSet = bincode::deserialize(&data[i..i+u]).unwrap(); + info!("val: {:?}", ds); + xdb.children.push(XDataSet { 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()) // 1 Track List + }}); }, 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; - info!("val: {:?}", rkyv::access::(&data[i..i+u]).unwrap()); + let ai: AlbumItem = bincode::deserialize(&data[i..i+u]).unwrap(); + info!("val: {:?}", ai); + if let XSomeList::AlbumList(albums) = &mut xdb.find_dataset(4).child { + albums.push(XAlbumItem {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; - info!("val: {:?}", rkyv::access::(&data[i..i+u]).unwrap()); + 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 {data: ti,args: Vec::new()}); + } }, ChunkType::StringTypes => { u = usize::try_from(header.children_count).unwrap() - 12; let header_offset: usize = (header.end_of_chunk + 4) as usize; - let str_end: usize = (header.children_count - 12) as usize; - let entry = rkyv::access::(&data[i..i+28]).unwrap(); - info!("val: {:?}", &entry); - if entry.entry_type <= 15 { - let mut bytes = Vec::new(); - - let mut h = i+header_offset; - while h < i+str_end { - if data[h] != 0 { - bytes.push(data[h]); + 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; } - h+=1; - } - let g = String::from_utf8(bytes).unwrap(); - info!("str: {}", g); + 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(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); + }, + 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); + }, + 100 => { + + }, + 102 => { + + }, + _ => {} } }, 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 = rkyv::access::(&data[i..i+u]).unwrap(); + 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 {data: playlist,args: Vec::new()}); + } }, _ => { u = 1; } } @@ -256,9 +349,13 @@ fn db(data: &[u8]) { } } } + //let mut f = File::create("output.json").unwrap(); + //let r = f.write(serde_json::to_string::(&xdb).unwrap().as_bytes()); + //info!("Result: {:?}", r); + xdb } -fn main() { +/*fn main() { // Initialize the logger with 'info' as the default level Builder::new() @@ -270,10 +367,10 @@ fn main() { match f.read_to_end(&mut buf) { Ok(n) => { let data = &buf[..n]; - db(data); + parse_bytes(data); }, Err(e) => { error!("Error: {}",e); } } -} +}*/ diff --git a/src/xml.rs b/src/xml.rs index f3809eb..82a5942 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -1,7 +1,69 @@ -struct Database { - + +#[derive(Debug, serde::Serialize)] +pub struct XDatabase { + pub data: Option, + pub children: Vec } -trait Child { +#[derive(Debug, serde::Serialize)] +pub struct XDataSet { + pub data: crate::DataSet, + pub child: XSomeList +} +#[derive(Debug, serde::Serialize)] +pub struct XTrackItem { + pub data: crate::TrackItem, + pub args: Vec +} + +#[derive(Debug, serde::Serialize)] +pub struct XAlbumItem { + pub data: crate::AlbumItem, + pub args: Vec +} + +#[derive(Debug, serde::Serialize)] +pub struct XPlaylist { + pub data: crate::Playlist, + pub args: Vec +} + +#[derive(Debug, serde::Serialize)] +pub struct XArgument { + pub arg_type: u32, + pub val: String +} + +#[derive(Debug, serde::Serialize)] +pub enum XSomeList { + Playlists(Vec), + TrackList(Vec), + 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() + } } \ No newline at end of file