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]]
name = "itunesdb"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"bincode",
"env_logger",

View File

@ -1,6 +1,6 @@
[package]
name = "itunesdb"
version = "0.1.1"
version = "0.1.2"
edition = "2021"
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();
info!("DataSet: {:?}", ds);
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
1 => XSomeList::TrackList(Vec::new()), // Track List
_ => XSomeList::Playlists(Vec::new()) // Playlist List 3
@ -55,7 +55,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
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()});
albums.push(XAlbumItem {header, data: ai,args: Vec::new()});
}
},
ChunkType::TrackList => {
@ -68,7 +68,7 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
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()});
tracks.push(XTrackItem {header, data: ti,args: Vec::new()});
}
},
ChunkType::SongReference => {
@ -123,15 +123,12 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
h += 4;
}
//info!("Indexes: {:?}", v);
match &mut xdb.find_dataset(last_type).child {
XSomeList::Playlists(playlists) => {
if playlists.last().unwrap().elems.is_empty() {
playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
} else {
playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
}
if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child {
if playlists.last().unwrap().elems.is_empty() {
playlists.last_mut().unwrap().args.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
} else {
playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::IndexEntry(XPlaylistIndexEntry{data: entry, v}));
}
_ => {}
}
},
53 => {
@ -144,28 +141,22 @@ pub fn parse_bytes(data: &[u8]) -> XDatabase {
h += 12;
}
info!("Indexes: {:?}", v);
match &mut xdb.find_dataset(last_type).child {
XSomeList::Playlists(playlists) => {
if playlists.last().unwrap().elems.is_empty() {
playlists.last_mut().unwrap().args.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v }));
} else {
playlists.last_mut().unwrap().elems.last_mut().unwrap().1.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v }));
}
if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child {
if playlists.last().unwrap().elems.is_empty() {
playlists.last_mut().unwrap().args.push(XPlArgument::LetterJumpEntry(XLetterJump{ data: entry, v }));
} else {
playlists.last_mut().unwrap().elems.last_mut().unwrap().1.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) => {
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()));
} 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()));
}
if let XSomeList::Playlists(playlists) = &mut xdb.find_dataset(last_type).child {
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()));
} 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()));
}
_ => {}
}
},
_ => 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();
info!("playlist: {:?}", playlist);
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"); }

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 xobjects;

View File

@ -11,7 +11,7 @@ pub enum ChunkType {
PlaylistList,
Playlist,
SongReference,
Unknown
Unknown,
}
impl From<[u8; 4]> for ChunkType {
@ -27,7 +27,7 @@ impl From<[u8; 4]> for ChunkType {
[0x6D, 0x68, 0x69, 0x70] => ChunkType::SongReference,
[0x6D, 0x68, 0x6C, 0x70] => ChunkType::PlaylistList,
[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::PlaylistList => [0x6D, 0x68, 0x6C, 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 chunk_type: [u8; 4],
pub end_of_chunk: u32,
pub children_count: u32
pub children_count: u32,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -69,12 +69,12 @@ pub struct Database {
hash: [u8; 20],
unk: [u8; 30],
unk1: [u8; 32],
unk2: [u8; 20]
unk2: [u8; 20],
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
pub struct DataSet {
pub data_type: u32
pub data_type: u32,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -83,32 +83,32 @@ pub struct AlbumItem {
unknown: u16,
album_id_for_track: u16,
timestamp: u64,
unknown1: u32
unknown1: u32,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
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,
visible: u32,
filetype: u32,
pub filetype: u32,
type1: u8,
type2: u8,
compilation_flag: u8,
stars: u8,
last_modified_time: u32,
size: u32,
length: u32,
pub stars: u8,
pub last_modified_time: u32,
pub size: u32,
pub length: u32,
track_number: u32,
total_tracks: u32,
year: u32,
bitrate: u32,
sample_rate: u32,
pub year: u32,
pub bitrate: u32,
pub sample_rate: u32,
volume: u32,
start_time: u32,
stop_time: u32,
soundcheck: u32,
play_count: u32,
pub play_count: u32,
play_count2: u32,
last_played_time: u32,
disc_number: u32,
@ -119,7 +119,7 @@ pub struct TrackItem {
pub dbid: u64,
checked: u8,
application_rating: u8,
bpm: u16,
pub bpm: u16,
artwork_count: u16,
unk9: u16,
artwork_size: u32,
@ -129,9 +129,9 @@ pub struct TrackItem {
unk14: u32,
unk15: u32,
unk16: u32,
skip_count: u32,
pub skip_count: u32,
last_skipped: u32,
has_artwork: u8,
pub has_artwork: u8,
skip_when_shuffling: u8,
remember_playback_position: u8,
flag4: u8,
@ -146,7 +146,7 @@ pub struct TrackItem {
unk25: u32,
postgap: u32,
unk27: u32,
media_type: u32,
pub media_type: u32,
season_number: u32,
episode_number: u32,
unk31: [u8; 28],
@ -164,22 +164,24 @@ pub struct TrackItem {
unk3: [u8; 32],
unk4: [u8; 32],
unk5: [u8; 32],
unk6: [u8; 32]
unk6: [u8; 32],
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct StringEntry { // mhod
pub struct StringEntry {
// mhod
pub entry_type: u32,
pub unk1: u32,
pub unk2: u32,
pub position: u32,
pub length: u32,
pub unknown: u32,
pub unk4: u32
pub unk4: u32,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct PlaylistIndexEntry { // mhod
pub struct PlaylistIndexEntry {
// mhod
entry_type: u32,
unk1: u32,
unk2: u32,
@ -189,7 +191,7 @@ pub struct PlaylistIndexEntry { // mhod
null_padding1: u64,
null_padding2: u64,
null_padding3: u64,
null_padding4: u64
null_padding4: u64,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@ -199,14 +201,14 @@ pub struct LetterJumpEntry {
unk2: u32,
index_type: u32,
pub count: u32,
null_padding: u64
null_padding: u64,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub 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.
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)]
@ -235,5 +237,5 @@ pub struct PlaylistItem {
timestamp: u32,
podcast_grouping_reference: u32,
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> {
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> {
args.iter()
.filter(|arg| arg.arg_type <= 15)
.map(serialize_string_arg)
.flatten()
.flat_map(serialize_string_arg)
.collect()
}
@ -46,7 +44,7 @@ fn serialize_string_arg(xarg: &XArgument) -> Vec<u8> {
children_count: 16 + 0x18 + s.len() as u32
}).unwrap();
b = [h, b, s].concat();
return b;
b
}
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
}).unwrap();
b = [h, b, v].concat();
return b;
b
}
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
}).unwrap();
b = [h, b, v].concat();
return b;
b
}
fn serialize_arguments(pl: &Vec<XPlArgument>) -> Vec<u8> {
pl.iter()
.map(|arg| match arg {
.flat_map(|arg| match arg {
XPlArgument::String(xarg) => serialize_string_arg(xarg),
XPlArgument::IndexEntry(xpl) => serialize_index_entry(xpl),
XPlArgument::LetterJumpEntry(xjump) => serialize_x_letter(xjump),
XPlArgument::RawArgument(raw_arg) => raw_arg.to_vec()
})
.flatten()
.collect()
}

View File

@ -107,7 +107,7 @@ impl XDatabase {
fn add_track_to_playlists(&mut self, n: u32, track: &XTrackItem) {
if let XSomeList::Playlists(playlists) = &mut self.find_dataset(n).child {
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 mut pl_item = elem.0.clone();