modified: Cargo.lock

modified:   Cargo.toml
	modified:   src/deserializer.rs
	modified:   src/lib.rs
	modified:   src/objects.rs
	modified:   src/serializer.rs
	modified:   src/xobjects.rs
This commit is contained in:
Michael Wain 2025-02-12 06:03:43 +03:00
parent 5a6ca7a9f5
commit 2db99df934
7 changed files with 63 additions and 78 deletions

2
Cargo.lock generated
View File

@ -90,7 +90,7 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]] [[package]]
name = "itunesdb" name = "itunesdb"
version = "0.1.1" version = "0.1.2"
dependencies = [ dependencies = [
"bincode", "bincode",
"env_logger", "env_logger",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "itunesdb" name = "itunesdb"
version = "0.1.1" version = "0.1.2"
edition = "2021" edition = "2021"
authors = ["alterwain"] authors = ["alterwain"]

View File

@ -38,7 +38,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
let ds: DataSet = bincode::deserialize(&data[i..i+u]).unwrap(); let ds: DataSet = bincode::deserialize(&data[i..i+u]).unwrap();
info!("DataSet: {:?}", ds); info!("DataSet: {:?}", ds);
last_type = ds.data_type; last_type = ds.data_type;
xdb.children.push(XDataSet { header: header, data: ds.clone(), child: match ds.data_type { xdb.children.push(XDataSet { header, data: ds, child: match ds.data_type {
4 => XSomeList::AlbumList(Vec::new()), // Album List 4 => XSomeList::AlbumList(Vec::new()), // Album List
1 => XSomeList::TrackList(Vec::new()), // Track List 1 => XSomeList::TrackList(Vec::new()), // Track List
_ => XSomeList::Playlists(Vec::new()) // Playlist List 3 _ => XSomeList::Playlists(Vec::new()) // Playlist List 3
@ -55,7 +55,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
info!("val: {:?}", ai); info!("val: {:?}", ai);
info!("AlbumItem: {}", u); info!("AlbumItem: {}", u);
if let XSomeList::AlbumList(albums) = &mut xdb.find_dataset(4).child { if let XSomeList::AlbumList(albums) = &mut xdb.find_dataset(4).child {
albums.push(XAlbumItem {header: header, data: ai,args: Vec::new()}); albums.push(XAlbumItem {header, data: ai,args: Vec::new()});
} }
}, },
ChunkType::TrackList => { ChunkType::TrackList => {
@ -68,7 +68,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
let ti: TrackItem = bincode::deserialize(&data[i..i+u]).unwrap(); let ti: TrackItem = bincode::deserialize(&data[i..i+u]).unwrap();
info!("val: {:?}", ti); info!("val: {:?}", ti);
if let XSomeList::TrackList(tracks) = &mut xdb.find_dataset(1).child { if let XSomeList::TrackList(tracks) = &mut xdb.find_dataset(1).child {
tracks.push(XTrackItem {header: header, data: ti,args: Vec::new()}); tracks.push(XTrackItem {header, data: ti,args: Vec::new()});
} }
}, },
ChunkType::SongReference => { ChunkType::SongReference => {
@ -123,16 +123,13 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
h += 4; h += 4;
} }
//info!("Indexes: {:?}", v); //info!("Indexes: {:?}", v);
match &mut xdb.find_dataset(last_type).child { if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child {
XSomeList::Playlists(playlists) => {
if playlists.last().unwrap().elems.is_empty() { if playlists.last().unwrap().elems.is_empty() {
playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v})); playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
} else { } else {
playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v})); playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
} }
} }
_ => {}
}
}, },
53 => { 53 => {
let entry: LetterJumpEntry = bincode::deserialize(&data[i..i+28]).unwrap(); let entry: LetterJumpEntry = bincode::deserialize(&data[i..i+28]).unwrap();
@ -144,29 +141,23 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
h += 12; h += 12;
} }
info!("Indexes: {:?}", v); info!("Indexes: {:?}", v);
match &mut xdb.find_dataset(last_type).child { if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child {
XSomeList::Playlists(playlists) => {
if playlists.last().unwrap().elems.is_empty() { if playlists.last().unwrap().elems.is_empty() {
playlists.last_mut().unwrap().args.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v })); playlists.last_mut().unwrap().args.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v }));
} else { } else {
playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v })); playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v }));
} }
} }
_ => {}
}
}, },
100 | 102 | 50 | 51 => { 100 | 102 | 50 | 51 => {
info!("Entry #100,102 fetched"); info!("Entry #100,102 fetched");
match &mut xdb.find_dataset(last_type).child { if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child {
XSomeList::Playlists(playlists) => {
if playlists.last().unwrap().elems.is_empty() { 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())); playlists.last_mut().unwrap().args.push(XPlArgument::RawArgument(data[i-12..i+(header.children_count as usize)-12].to_vec()));
} else { } 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())); playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::RawArgument(data[i-12..i+(header.children_count as usize)-12].to_vec()));
} }
} }
_ => {}
}
}, },
_ => warn!("Unknown entry: {}", entry_type) _ => warn!("Unknown entry: {}", entry_type)
} }
@ -181,7 +172,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
let playlist: Playlist = bincode::deserialize(&data[i..i+u]).unwrap(); let playlist: Playlist = bincode::deserialize(&data[i..i+u]).unwrap();
info!("playlist: {:?}", playlist); info!("playlist: {:?}", playlist);
if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child { // 3 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()}); playlists.push(XPlaylist {header, data: playlist,args: Vec::new(), elems: Vec::new()});
} }
}, },
_ => { u = 1; info!("Unknown stuff happened"); } _ => { u = 1; info!("Unknown stuff happened"); }

View File

@ -1,8 +1,3 @@
use std::{fs::File, io::{Read, Write}};
use env_logger::Builder;
use log::{error, info, LevelFilter};
use rand::Rng;
use xobjects::{XArgument, XPlArgument, XSomeList};
pub mod objects; pub mod objects;
pub mod xobjects; pub mod xobjects;

View File

@ -11,7 +11,7 @@ pub enum ChunkType {
PlaylistList, PlaylistList,
Playlist, Playlist,
SongReference, SongReference,
Unknown Unknown,
} }
impl From<[u8; 4]> for ChunkType { impl From<[u8; 4]> for ChunkType {
@ -27,7 +27,7 @@ impl From<[u8; 4]> for ChunkType {
[0x6D, 0x68, 0x69, 0x70] => ChunkType::SongReference, [0x6D, 0x68, 0x69, 0x70] => ChunkType::SongReference,
[0x6D, 0x68, 0x6C, 0x70] => ChunkType::PlaylistList, [0x6D, 0x68, 0x6C, 0x70] => ChunkType::PlaylistList,
[0x6D, 0x68, 0x79, 0x70] => ChunkType::Playlist, [0x6D, 0x68, 0x79, 0x70] => ChunkType::Playlist,
_ => ChunkType::Unknown _ => ChunkType::Unknown,
} }
} }
} }
@ -45,7 +45,7 @@ impl From<ChunkType> for [u8; 4] {
ChunkType::StringTypes => [0x6D, 0x68, 0x6F, 0x64], ChunkType::StringTypes => [0x6D, 0x68, 0x6F, 0x64],
ChunkType::PlaylistList => [0x6D, 0x68, 0x6C, 0x70], ChunkType::PlaylistList => [0x6D, 0x68, 0x6C, 0x70],
ChunkType::Playlist => [0x6D, 0x68, 0x79, 0x70], ChunkType::Playlist => [0x6D, 0x68, 0x79, 0x70],
ChunkType::Unknown => [0x00, 0x00, 0x00, 0x00] ChunkType::Unknown => [0x00, 0x00, 0x00, 0x00],
} }
} }
} }
@ -54,7 +54,7 @@ impl From<ChunkType> for [u8; 4] {
pub struct ChunkHeader { pub struct ChunkHeader {
pub chunk_type: [u8; 4], pub chunk_type: [u8; 4],
pub end_of_chunk: u32, pub end_of_chunk: u32,
pub children_count: u32 pub children_count: u32,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -69,12 +69,12 @@ pub struct Database {
hash: [u8; 20], hash: [u8; 20],
unk: [u8; 30], unk: [u8; 30],
unk1: [u8; 32], unk1: [u8; 32],
unk2: [u8; 20] unk2: [u8; 20],
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
pub struct DataSet { pub struct DataSet {
pub data_type: u32 pub data_type: u32,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -83,7 +83,7 @@ pub struct AlbumItem {
unknown: u16, unknown: u16,
album_id_for_track: u16, album_id_for_track: u16,
timestamp: u64, timestamp: u64,
unknown1: u32 unknown1: u32,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
@ -91,24 +91,24 @@ pub struct TrackItem {
pub number_of_strings: u32, // number of mhod's count pub number_of_strings: u32, // number of mhod's count
pub unique_id: u32, pub unique_id: u32,
visible: u32, visible: u32,
filetype: u32, pub filetype: u32,
type1: u8, type1: u8,
type2: u8, type2: u8,
compilation_flag: u8, compilation_flag: u8,
stars: u8, pub stars: u8,
last_modified_time: u32, pub last_modified_time: u32,
size: u32, pub size: u32,
length: u32, pub length: u32,
track_number: u32, track_number: u32,
total_tracks: u32, total_tracks: u32,
year: u32, pub year: u32,
bitrate: u32, pub bitrate: u32,
sample_rate: u32, pub sample_rate: u32,
volume: u32, volume: u32,
start_time: u32, start_time: u32,
stop_time: u32, stop_time: u32,
soundcheck: u32, soundcheck: u32,
play_count: u32, pub play_count: u32,
play_count2: u32, play_count2: u32,
last_played_time: u32, last_played_time: u32,
disc_number: u32, disc_number: u32,
@ -119,7 +119,7 @@ pub struct TrackItem {
pub dbid: u64, pub dbid: u64,
checked: u8, checked: u8,
application_rating: u8, application_rating: u8,
bpm: u16, pub bpm: u16,
artwork_count: u16, artwork_count: u16,
unk9: u16, unk9: u16,
artwork_size: u32, artwork_size: u32,
@ -129,9 +129,9 @@ pub struct TrackItem {
unk14: u32, unk14: u32,
unk15: u32, unk15: u32,
unk16: u32, unk16: u32,
skip_count: u32, pub skip_count: u32,
last_skipped: u32, last_skipped: u32,
has_artwork: u8, pub has_artwork: u8,
skip_when_shuffling: u8, skip_when_shuffling: u8,
remember_playback_position: u8, remember_playback_position: u8,
flag4: u8, flag4: u8,
@ -146,7 +146,7 @@ pub struct TrackItem {
unk25: u32, unk25: u32,
postgap: u32, postgap: u32,
unk27: u32, unk27: u32,
media_type: u32, pub media_type: u32,
season_number: u32, season_number: u32,
episode_number: u32, episode_number: u32,
unk31: [u8; 28], unk31: [u8; 28],
@ -164,22 +164,24 @@ pub struct TrackItem {
unk3: [u8; 32], unk3: [u8; 32],
unk4: [u8; 32], unk4: [u8; 32],
unk5: [u8; 32], unk5: [u8; 32],
unk6: [u8; 32] unk6: [u8; 32],
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct StringEntry { // mhod pub struct StringEntry {
// mhod
pub entry_type: u32, pub entry_type: u32,
pub unk1: u32, pub unk1: u32,
pub unk2: u32, pub unk2: u32,
pub position: u32, pub position: u32,
pub length: u32, pub length: u32,
pub unknown: u32, pub unknown: u32,
pub unk4: u32 pub unk4: u32,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct PlaylistIndexEntry { // mhod pub struct PlaylistIndexEntry {
// mhod
entry_type: u32, entry_type: u32,
unk1: u32, unk1: u32,
unk2: u32, unk2: u32,
@ -189,7 +191,7 @@ pub struct PlaylistIndexEntry { // mhod
null_padding1: u64, null_padding1: u64,
null_padding2: u64, null_padding2: u64,
null_padding3: u64, null_padding3: u64,
null_padding4: u64 null_padding4: u64,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@ -199,14 +201,14 @@ pub struct LetterJumpEntry {
unk2: u32, unk2: u32,
index_type: u32, index_type: u32,
pub count: u32, pub count: u32,
null_padding: u64 null_padding: u64,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct JumpTable { pub struct JumpTable {
letter: u32, // UTF-16 LE Uppercase with two padding null bytes 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. 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. count: u32, // the count of entries starting with this letter in the corresponding MHOD52.
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -235,5 +237,5 @@ pub struct PlaylistItem {
timestamp: u32, timestamp: u32,
podcast_grouping_reference: u32, podcast_grouping_reference: u32,
unk: [u8; 30], unk: [u8; 30],
unk1: [u8; 10] unk1: [u8; 10],
} }

View File

@ -1,17 +1,15 @@
use log::info;
use crate::{objects::{ChunkHeader, ChunkType, PlaylistItem, StringEntry}, xobjects::{XArgument, XDatabase, XLetterJump, XPlArgument, XPlaylistIndexEntry, XSomeList}}; use crate::{objects::{ChunkHeader, ChunkType, StringEntry}, xobjects::{XArgument, XDatabase, XLetterJump, XPlArgument, XPlaylistIndexEntry, XSomeList}};
fn string_to_ipod16(str: &str) -> Vec<u8> { fn string_to_ipod16(str: &str) -> Vec<u8> {
str.encode_utf16().map(|f| [f as u8, (f >> 8) as u8]).flatten().collect() str.encode_utf16().flat_map(|f| [f as u8, (f >> 8) as u8]).collect()
} }
fn x_args_to_bytes(args: &Vec<XArgument>) -> Vec<u8> { fn x_args_to_bytes(args: &Vec<XArgument>) -> Vec<u8> {
args.iter() args.iter()
.filter(|arg| arg.arg_type <= 15) .filter(|arg| arg.arg_type <= 15)
.map(serialize_string_arg) .flat_map(serialize_string_arg)
.flatten()
.collect() .collect()
} }
@ -46,7 +44,7 @@ fn serialize_string_arg(xarg: &XArgument) -> Vec<u8> {
children_count: 16 + 0x18 + s.len() as u32 children_count: 16 + 0x18 + s.len() as u32
}).unwrap(); }).unwrap();
b = [h, b, s].concat(); b = [h, b, s].concat();
return b; b
} }
fn serialize_index_entry(xpl: &XPlaylistIndexEntry) -> Vec<u8> { fn serialize_index_entry(xpl: &XPlaylistIndexEntry) -> Vec<u8> {
@ -61,7 +59,7 @@ fn serialize_index_entry(xpl: &XPlaylistIndexEntry) -> Vec<u8> {
children_count: 12 + (v.len() + b.len()) as u32 children_count: 12 + (v.len() + b.len()) as u32
}).unwrap(); }).unwrap();
b = [h, b, v].concat(); b = [h, b, v].concat();
return b; b
} }
fn serialize_x_letter(xjump: &XLetterJump) -> Vec<u8> { fn serialize_x_letter(xjump: &XLetterJump) -> Vec<u8> {
@ -76,18 +74,17 @@ fn serialize_x_letter(xjump: &XLetterJump) -> Vec<u8> {
children_count: 12 + (v.len() + b.len()) as u32 children_count: 12 + (v.len() + b.len()) as u32
}).unwrap(); }).unwrap();
b = [h, b, v].concat(); b = [h, b, v].concat();
return b; b
} }
fn serialize_arguments(pl: &Vec<XPlArgument>) -> Vec<u8> { fn serialize_arguments(pl: &Vec<XPlArgument>) -> Vec<u8> {
pl.iter() pl.iter()
.map(|arg| match arg { .flat_map(|arg| match arg {
XPlArgument::String(xarg) => serialize_string_arg(xarg), XPlArgument::String(xarg) => serialize_string_arg(xarg),
XPlArgument::IndexEntry(xpl) => serialize_index_entry(xpl), XPlArgument::IndexEntry(xpl) => serialize_index_entry(xpl),
XPlArgument::LetterJumpEntry(xjump) => serialize_x_letter(xjump), XPlArgument::LetterJumpEntry(xjump) => serialize_x_letter(xjump),
XPlArgument::RawArgument(raw_arg) => raw_arg.to_vec() XPlArgument::RawArgument(raw_arg) => raw_arg.to_vec()
}) })
.flatten()
.collect() .collect()
} }

View File

@ -107,7 +107,7 @@ impl XDatabase {
fn add_track_to_playlists(&mut self, n: u32, track: &XTrackItem) { fn add_track_to_playlists(&mut self, n: u32, track: &XTrackItem) {
if let XSomeList::Playlists(playlists) = &mut self.find_dataset(n).child { if let XSomeList::Playlists(playlists) = &mut self.find_dataset(n).child {
let playlist = playlists.last_mut().unwrap(); let playlist = playlists.last_mut().unwrap();
playlist.data.playlist_item_count = playlist.data.playlist_item_count + 1; playlist.data.playlist_item_count += 1;
let elem = playlist.elems.last().unwrap(); let elem = playlist.elems.last().unwrap();
let mut pl_item = elem.0.clone(); let mut pl_item = elem.0.clone();