modified: .gitignore

modified:   Cargo.lock
	modified:   Cargo.toml
	modified:   src/main.rs
	modified:   src/xml.rs
This commit is contained in:
Michael Wain 2025-02-04 05:47:00 +03:00
parent 35d3c1127d
commit d52e1ebf51
5 changed files with 244 additions and 189 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
output.json

175
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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<ChunkHeader> = 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::<ArchivedChunkHeader, Error>(&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::<ArchivedDatabase, Error>(&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::<ArchivedDataSet, Error>(&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::<ArchivedAlbumItem, Error>(&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::<ArchivedTrackItem, Error>(&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::<ArchivedEntry, Error>(&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<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);
},
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::<ArchivedPlaylist, Error>(&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::<XDatabase>(&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);
}
}
}
}*/

View File

@ -1,7 +1,69 @@
struct Database {
#[derive(Debug, serde::Serialize)]
pub struct XDatabase {
pub data: Option<crate::Database>,
pub children: Vec<XDataSet>
}
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<XArgument>
}
#[derive(Debug, serde::Serialize)]
pub struct XAlbumItem {
pub data: crate::AlbumItem,
pub args: Vec<XArgument>
}
#[derive(Debug, serde::Serialize)]
pub struct XPlaylist {
pub data: crate::Playlist,
pub args: Vec<XArgument>
}
#[derive(Debug, serde::Serialize)]
pub struct XArgument {
pub arg_type: u32,
pub val: String
}
#[derive(Debug, serde::Serialize)]
pub enum XSomeList {
Playlists(Vec<XPlaylist>),
TrackList(Vec<XTrackItem>),
AlbumList(Vec<XAlbumItem>)
}
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()
}
}