From f8fec0c80255cfc0ccd78abdbfadfbf401411374 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Fri, 22 Apr 2022 11:07:35 +0200 Subject: [PATCH 1/3] feat(pdb): Implement support for serialization of table pages --- src/pdb/mod.rs | 822 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 797 insertions(+), 25 deletions(-) diff --git a/src/pdb/mod.rs b/src/pdb/mod.rs index aa5f44c..b29ab8e 100644 --- a/src/pdb/mod.rs +++ b/src/pdb/mod.rs @@ -25,7 +25,7 @@ use crate::util::ColorIndex; use binrw::{ binread, binrw, io::{Read, Seek, SeekFrom, Write}, - BinRead, BinResult, BinWrite, Endian, FilePtr16, FilePtr8, ReadOptions, WriteOptions, + BinRead, BinResult, BinWrite, Endian, FilePtr16, FilePtr8, ReadOptions, VecArgs, WriteOptions, }; /// Do not read anything, but the return the current stream position of `reader`. @@ -200,9 +200,6 @@ impl PageFlags { /// Each page consists of a header that contains information about the type, number of rows, etc., /// followed by the data section that holds the row data. Each row needs to be located using an /// offset found in the page footer at the end of the page. -/// -/// **Note: The `Page` struct is currently not writable, because row offsets are not taken into -/// account and rows are not serialized correctly yet.** #[binread] #[derive(Debug, PartialEq)] #[br(little, magic = 0u32)] @@ -287,12 +284,6 @@ pub struct Page { // (currently nightly-only, see https://github.com/rust-lang/rust/issues/88581). #[br(calc = if num_rows > 0 { (num_rows - 1) / RowGroup::MAX_ROW_COUNT + 1 } else { 0 })] num_row_groups: u16, - /// The offset at which row groups for this page are located. - /// - /// **Note:** This is a virtual field and not actually read from the file. - #[br(temp)] - #[br(calc = SeekFrom::Current(i64::from(page_size) - i64::from(Self::HEADER_SIZE) - i64::from(num_rows) * 2 - i64::from(num_row_groups) * 4))] - row_groups_offset: SeekFrom, /// The offset at which the row data for this page are located. /// /// **Note:** This is a virtual field and not actually read from the file. @@ -300,15 +291,99 @@ pub struct Page { #[br(calc = page_index.offset(page_size) + u64::from(Self::HEADER_SIZE))] page_heap_offset: u64, /// Row groups belonging to this page. - #[br(seek_before(row_groups_offset), restore_position)] + #[br(seek_before(SeekFrom::Current(Page::row_groups_offset( + page_size, + num_rows_small, + num_rows_large + ))))] #[br(parse_with = Self::parse_row_groups, args(page_type, page_heap_offset, num_rows, num_row_groups, page_flags))] pub row_groups: Vec, } +// #[bw(little)] on #[binread] types does +// not seem to work so we manually define the endianness here. +impl binrw::meta::WriteEndian for Page { + const ENDIAN: binrw::meta::EndianKind = binrw::meta::EndianKind::Endian(Endian::Little); +} + +impl BinWrite for Page { + type Args = (u32,); + + fn write_options( + &self, + writer: &mut W, + options: &WriteOptions, + args: Self::Args, + ) -> BinResult<()> { + let options = options.with_endian(Endian::Little); + + let page_offset = writer.stream_position().map_err(binrw::Error::Io)?; + + // Header + 0u32.write_options(writer, &options, ())?; + self.page_index.write_options(writer, &options, ())?; + self.page_type.write_options(writer, &options, ())?; + self.next_page.write_options(writer, &options, ())?; + self.unknown1.write_options(writer, &options, ())?; + self.unknown2.write_options(writer, &options, ())?; + self.num_rows_small.write_options(writer, &options, ())?; + self.unknown3.write_options(writer, &options, ())?; + self.unknown4.write_options(writer, &options, ())?; + self.page_flags.write_options(writer, &options, ())?; + self.free_size.write_options(writer, &options, ())?; + self.used_size.write_options(writer, &options, ())?; + self.unknown5.write_options(writer, &options, ())?; + self.num_rows_large.write_options(writer, &options, ())?; + self.unknown6.write_options(writer, &options, ())?; + self.unknown7.write_options(writer, &options, ())?; + + let (page_size,) = args; + + // Padding + let page_heap_size: usize = + Self::row_groups_offset(page_size, self.num_rows_small, self.num_rows_large) + .try_into() + .map_err(|e| binrw::Error::Custom { + pos: (page_offset + u64::from(Self::HEADER_SIZE)), + err: Box::new(e), + })?; + vec![0u8; page_heap_size].write_options(writer, &options, ())?; + + // Row Groups + let mut relative_row_offset: u64 = Self::HEADER_SIZE.into(); + for row_group in &self.row_groups { + relative_row_offset = row_group.write_options_and_get_row_offset( + writer, + &options, + (page_offset, relative_row_offset), + )?; + } + Ok(()) + } +} + impl Page { /// Size of the page header in bytes. pub const HEADER_SIZE: u32 = 0x28; + fn row_groups_offset(page_size: u32, num_rows_small: u8, num_rows_large: u16) -> i64 { + let num_rows = if num_rows_large > num_rows_small.into() && num_rows_large != 0x1fff { + num_rows_large + } else { + num_rows_small.into() + }; + let num_row_groups = if num_rows > 0 { + (num_rows - 1) / RowGroup::MAX_ROW_COUNT + 1 + } else { + 0 + }; + + let row_groups_size = num_rows * 2 + num_row_groups * 4; + dbg!(row_groups_size); + + i64::from(page_size) - i64::from(Self::HEADER_SIZE) - i64::from(row_groups_size) + } + /// Parse the row groups at the end of the page. fn parse_row_groups( reader: &mut R, @@ -395,8 +470,8 @@ pub struct RowGroup { /// An offset which points to a row in the table, whose actual presence is controlled by one of the /// bits in `row_present_flags`. This instance allows the row itself to be lazily loaded, unless it /// is not present, in which case there is no content to be loaded. - #[br(offset = page_heap_offset, args { count: num_rows.into(), inner: (page_type,) })] - rows: Vec>, + #[br(offset = page_heap_offset, parse_with = Self::parse_rows, args { count: num_rows.into(), inner: (page_type,) })] + rows: Vec, row_presence_flags: u16, /// Unknown field, probably padding. /// @@ -409,17 +484,83 @@ impl RowGroup { /// Return the ordered list of row offsets that are actually present. pub fn present_rows(&self) -> impl Iterator { - self.rows - .iter() - .rev() - .enumerate() - .filter_map(|(i, row_offset)| { - if (self.row_presence_flags & (1 << i)) != 0 { - row_offset.value.as_ref() - } else { - None - } - }) + self.rows.iter().rev().enumerate().filter_map(|(i, row)| { + if (self.row_presence_flags & (1 << i)) != 0 { + Some(row) + } else { + None + } + }) + } + + fn parse_rows( + reader: &mut R, + options: &ReadOptions, + args: VecArgs<(PageType,)>, + ) -> BinResult> { + let mut rows = Vec::::with_capacity(args.count); + for _ in 0..args.count { + let row = FilePtr16::::parse(reader, options, args.inner)?; + rows.push(row); + } + + Ok(rows) + } + + fn write_options_and_get_row_offset( + &self, + writer: &mut W, + options: &WriteOptions, + args: (u64, u64), + ) -> binrw::BinResult { + let options = options.with_endian(Endian::Little); + + let (page_offset, relative_row_offset) = args; + + // Write rows + let mut offset = page_offset + relative_row_offset; + for row in &self.rows { + // Write row offset + let row_offset: u16 = offset + .checked_sub(page_offset) + .and_then(|v| u16::try_from(v).ok()) + .ok_or_else(|| binrw::Error::AssertFail { + pos: 0, + message: "Wraparound while calculating row offset".to_string(), + })?; + row_offset.write_options(writer, &options, ())?; + let restore_position = writer.stream_position()?; + + // Write actual row content + let offset_before_write = writer.seek(SeekFrom::Start(offset))?; + row.write_options(writer, &options, ())?; + let offset_after_write = writer.stream_position()?; + offset += offset_after_write - offset_before_write; + + // Seek back to row offset + writer.seek(SeekFrom::Start(restore_position))?; + } + let new_relative_row_offset = offset - page_offset; + + self.row_presence_flags + .write_options(writer, &options, ())?; + self.unknown.write_options(writer, &options, ())?; + + Ok(new_relative_row_offset) + } +} + +impl BinWrite for RowGroup { + type Args = (u64, u64); + + fn write_options( + &self, + writer: &mut W, + options: &WriteOptions, + args: Self::Args, + ) -> binrw::BinResult<()> { + self.write_options_and_get_row_offset(writer, options, args)?; + Ok(()) } } @@ -989,7 +1130,7 @@ pub enum Row { #[cfg(test)] mod test { use super::*; - use crate::util::testing::test_roundtrip; + use crate::util::testing::{test_roundtrip, test_roundtrip_with_args}; #[test] fn empty_header() { @@ -1301,6 +1442,7 @@ mod test { }; test_roundtrip(&[0, 0, 0, 0, 1, 1, 0, 0, 11, 80, 105, 110, 107], row); } + #[test] fn column_entry() { let row = ColumnEntry { @@ -1314,4 +1456,634 @@ mod test { ]; test_roundtrip(bin, row); } + + #[test] + fn track_page() { + let page = Page { + page_index: PageIndex(2), + page_type: PageType::Tracks, + next_page: PageIndex(47), + unknown1: 32, + unknown2: 0, + num_rows_small: 7, + unknown3: 64, + unknown4: 0, + page_flags: PageFlags(52), + free_size: 1530, + used_size: 2508, + unknown5: 1, + num_rows_large: 6, + unknown6: 0, + unknown7: 0, + row_groups: vec![RowGroup { + rows: vec![ + Row::Track(Track { + unknown1: 36, + index_shift: 192, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 5124342, + unknown2: 263879995, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(5), + orig_artist_id: ArtistId(0), + label_id: LabelId(1), + remixer_id: ArtistId(0), + bitrate: 320, + track_number: 0, + tempo: 12000, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(1), + id: TrackId(2), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 16, + duration: 128, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 1, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P053/0001D21F/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) + .unwrap(), + title: DeviceSQLString::new("Demo Track 2".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("Demo Track 2.mp3".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/Loopmasters/UnknownAlbum/Demo Track 2.mp3".to_string(), + ) + .unwrap(), + }), + Row::Track(Track { + unknown1: 36, + index_shift: 160, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 6899624, + unknown2: 214020570, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(5), + orig_artist_id: ArtistId(0), + label_id: LabelId(1), + remixer_id: ArtistId(0), + bitrate: 320, + track_number: 0, + tempo: 12800, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(1), + id: TrackId(1), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 16, + duration: 172, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 1, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P016/0000875E/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) + .unwrap(), + title: DeviceSQLString::new("Demo Track 1".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("Demo Track 1.mp3".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/Loopmasters/UnknownAlbum/Demo Track 1.mp3".to_string(), + ) + .unwrap(), + }), + Row::Track(Track { + unknown1: 36, + index_shift: 128, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 6899624, + unknown2: 214020570, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(5), + orig_artist_id: ArtistId(0), + label_id: LabelId(1), + remixer_id: ArtistId(0), + bitrate: 320, + track_number: 0, + tempo: 12800, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(1), + id: TrackId(5), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 16, + duration: 172, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 1, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P016/0000875E/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) + .unwrap(), + title: DeviceSQLString::new("Demo Track 1".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("Demo Track 1.mp3".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/Loopmasters/UnknownAlbum/Demo Track 1.mp3".to_string(), + ) + .unwrap(), + }), + Row::Track(Track { + unknown1: 36, + index_shift: 96, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 2010816, + unknown2: 227782126, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(4), + orig_artist_id: ArtistId(0), + label_id: LabelId(0), + remixer_id: ArtistId(0), + bitrate: 2116, + track_number: 0, + tempo: 0, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(0), + id: TrackId(4), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 24, + duration: 7, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 11, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P021/00006D2B/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("HORN".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("HORN.wav".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/UnknownArtist/UnknownAlbum/HORN.wav".to_string(), + ) + .unwrap(), + }), + Row::Track(Track { + unknown1: 36, + index_shift: 64, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 1941204, + unknown2: 243638374, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(3), + orig_artist_id: ArtistId(0), + label_id: LabelId(0), + remixer_id: ArtistId(0), + bitrate: 2116, + track_number: 0, + tempo: 0, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(0), + id: TrackId(3), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 24, + duration: 7, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 11, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P017/00009B77/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("SIREN".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("SIREN.wav".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/UnknownArtist/UnknownAlbum/SIREN.wav".to_string(), + ) + .unwrap(), + }), + Row::Track(Track { + unknown1: 36, + index_shift: 32, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 1515258, + unknown2: 34882935, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(2), + orig_artist_id: ArtistId(0), + label_id: LabelId(0), + remixer_id: ArtistId(0), + bitrate: 2116, + track_number: 0, + tempo: 0, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(0), + id: TrackId(2), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 24, + duration: 5, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 11, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P043/00011517/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("SINEWAVE".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("SINEWAVE.wav".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/UnknownArtist/UnknownAlbum/SINEWAVE.wav".to_string(), + ) + .unwrap(), + }), + Row::Track(Track { + unknown1: 36, + index_shift: 0, + bitmask: 788224, + sample_rate: 44100, + composer_id: ArtistId(0), + file_size: 1382226, + unknown2: 191204207, + unknown3: 64128, + unknown4: 1511, + artwork_id: ArtworkId(0), + key_id: KeyId(1), + orig_artist_id: ArtistId(0), + label_id: LabelId(0), + remixer_id: ArtistId(0), + bitrate: 2116, + track_number: 0, + tempo: 0, + genre_id: GenreId(0), + album_id: AlbumId(0), + artist_id: ArtistId(0), + id: TrackId(1), + disc_number: 0, + play_count: 0, + year: 0, + sample_depth: 24, + duration: 5, + unknown5: 41, + color: ColorIndex::None, + rating: 0, + unknown6: 11, + unknown7: 3, + isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), + unknown_string1: DeviceSQLString::empty(), + unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string4: DeviceSQLString::empty(), + message: DeviceSQLString::empty(), + kuvo_public: DeviceSQLString::empty(), + autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), + unknown_string5: DeviceSQLString::empty(), + unknown_string6: DeviceSQLString::empty(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + release_date: DeviceSQLString::empty(), + mix_name: DeviceSQLString::empty(), + unknown_string7: DeviceSQLString::empty(), + analyze_path: DeviceSQLString::new( + "/PIONEER/USBANLZ/P019/00020AA9/ANLZ0000.DAT".to_string(), + ) + .unwrap(), + analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("NOISE".to_string()).unwrap(), + unknown_string8: DeviceSQLString::empty(), + filename: DeviceSQLString::new("NOISE.wav".to_string()).unwrap(), + file_path: DeviceSQLString::new( + "/Contents/UnknownArtist/UnknownAlbum/NOISE.wav".to_string(), + ) + .unwrap(), + }), + ], + row_presence_flags: 96, + unknown: 64, + }], + }; + + let page_size: u32 = 4096; + test_roundtrip_with_args( + &[ + 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 7, 64, 0, + 52, 250, 5, 204, 9, 1, 0, 6, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 7, 12, 0, 68, 172, 0, + 0, 0, 0, 0, 0, 82, 23, 21, 0, 111, 139, 101, 11, 128, 250, 231, 5, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 5, 0, 41, + 0, 0, 0, 11, 0, 3, 0, 136, 0, 137, 0, 138, 0, 140, 0, 142, 0, 143, 0, 144, 0, 145, + 0, 148, 0, 149, 0, 150, 0, 161, 0, 162, 0, 163, 0, 164, 0, 208, 0, 219, 0, 220, 0, + 226, 0, 227, 0, 237, 0, 3, 3, 5, 51, 5, 50, 3, 3, 3, 7, 79, 78, 3, 3, 23, 50, 48, + 49, 53, 45, 48, 57, 45, 48, 55, 3, 3, 3, 89, 47, 80, 73, 79, 78, 69, 69, 82, 47, + 85, 83, 66, 65, 78, 76, 90, 47, 80, 48, 49, 57, 47, 48, 48, 48, 50, 48, 65, 65, 57, + 47, 65, 78, 76, 90, 48, 48, 48, 48, 46, 68, 65, 84, 23, 50, 48, 50, 50, 45, 48, 50, + 45, 48, 50, 3, 13, 78, 79, 73, 83, 69, 3, 21, 78, 79, 73, 83, 69, 46, 119, 97, 118, + 95, 47, 67, 111, 110, 116, 101, 110, 116, 115, 47, 85, 110, 107, 110, 111, 119, + 110, 65, 114, 116, 105, 115, 116, 47, 85, 110, 107, 110, 111, 119, 110, 65, 108, + 98, 117, 109, 47, 78, 79, 73, 83, 69, 46, 119, 97, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 32, 0, 0, 7, 12, 0, 68, 172, 0, + 0, 0, 0, 0, 0, 250, 30, 23, 0, 119, 69, 20, 2, 128, 250, 231, 5, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 5, 0, 41, 0, + 0, 0, 11, 0, 3, 0, 136, 0, 137, 0, 138, 0, 140, 0, 142, 0, 143, 0, 144, 0, 145, 0, + 148, 0, 149, 0, 150, 0, 161, 0, 162, 0, 163, 0, 164, 0, 208, 0, 219, 0, 220, 0, + 229, 0, 230, 0, 243, 0, 3, 3, 5, 51, 5, 50, 3, 3, 3, 7, 79, 78, 3, 3, 23, 50, 48, + 49, 53, 45, 48, 57, 45, 48, 55, 3, 3, 3, 89, 47, 80, 73, 79, 78, 69, 69, 82, 47, + 85, 83, 66, 65, 78, 76, 90, 47, 80, 48, 52, 51, 47, 48, 48, 48, 49, 49, 53, 49, 55, + 47, 65, 78, 76, 90, 48, 48, 48, 48, 46, 68, 65, 84, 23, 50, 48, 50, 50, 45, 48, 50, + 45, 48, 50, 3, 19, 83, 73, 78, 69, 87, 65, 86, 69, 3, 27, 83, 73, 78, 69, 87, 65, + 86, 69, 46, 119, 97, 118, 101, 47, 67, 111, 110, 116, 101, 110, 116, 115, 47, 85, + 110, 107, 110, 111, 119, 110, 65, 114, 116, 105, 115, 116, 47, 85, 110, 107, 110, + 111, 119, 110, 65, 108, 98, 117, 109, 47, 83, 73, 78, 69, 87, 65, 86, 69, 46, 119, + 97, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 64, 0, 0, 7, 12, 0, 68, 172, 0, 0, 0, 0, 0, 0, 212, 158, 29, 0, 102, + 160, 133, 14, 128, 250, 231, 5, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 68, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 7, 0, 41, 0, 0, 0, 11, 0, 3, 0, 136, 0, 137, + 0, 138, 0, 140, 0, 142, 0, 143, 0, 144, 0, 145, 0, 148, 0, 149, 0, 150, 0, 161, 0, + 162, 0, 163, 0, 164, 0, 208, 0, 219, 0, 220, 0, 226, 0, 227, 0, 237, 0, 3, 3, 5, + 51, 5, 50, 3, 3, 3, 7, 79, 78, 3, 3, 23, 50, 48, 49, 53, 45, 48, 57, 45, 48, 55, 3, + 3, 3, 89, 47, 80, 73, 79, 78, 69, 69, 82, 47, 85, 83, 66, 65, 78, 76, 90, 47, 80, + 48, 49, 55, 47, 48, 48, 48, 48, 57, 66, 55, 55, 47, 65, 78, 76, 90, 48, 48, 48, 48, + 46, 68, 65, 84, 23, 50, 48, 50, 50, 45, 48, 50, 45, 48, 50, 3, 13, 83, 73, 82, 69, + 78, 3, 21, 83, 73, 82, 69, 78, 46, 119, 97, 118, 95, 47, 67, 111, 110, 116, 101, + 110, 116, 115, 47, 85, 110, 107, 110, 111, 119, 110, 65, 114, 116, 105, 115, 116, + 47, 85, 110, 107, 110, 111, 119, 110, 65, 108, 98, 117, 109, 47, 83, 73, 82, 69, + 78, 46, 119, 97, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 96, 0, 0, 7, 12, 0, 68, 172, 0, 0, 0, 0, 0, 0, 192, 174, 30, 0, + 238, 173, 147, 13, 128, 250, 231, 5, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 68, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 7, 0, 41, 0, 0, 0, 11, 0, 3, 0, 136, 0, + 137, 0, 138, 0, 140, 0, 142, 0, 143, 0, 144, 0, 145, 0, 148, 0, 149, 0, 150, 0, + 161, 0, 162, 0, 163, 0, 164, 0, 208, 0, 219, 0, 220, 0, 225, 0, 226, 0, 235, 0, 3, + 3, 5, 51, 5, 50, 3, 3, 3, 7, 79, 78, 3, 3, 23, 50, 48, 49, 53, 45, 48, 57, 45, 48, + 55, 3, 3, 3, 89, 47, 80, 73, 79, 78, 69, 69, 82, 47, 85, 83, 66, 65, 78, 76, 90, + 47, 80, 48, 50, 49, 47, 48, 48, 48, 48, 54, 68, 50, 66, 47, 65, 78, 76, 90, 48, 48, + 48, 48, 46, 68, 65, 84, 23, 50, 48, 50, 50, 45, 48, 50, 45, 48, 50, 3, 11, 72, 79, + 82, 78, 3, 19, 72, 79, 82, 78, 46, 119, 97, 118, 93, 47, 67, 111, 110, 116, 101, + 110, 116, 115, 47, 85, 110, 107, 110, 111, 119, 110, 65, 114, 116, 105, 115, 116, + 47, 85, 110, 107, 110, 111, 119, 110, 65, 108, 98, 117, 109, 47, 72, 79, 82, 78, + 46, 119, 97, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 128, 0, 0, 7, 12, 0, 68, 172, 0, 0, 0, 0, 0, 0, 168, 71, 105, + 0, 218, 177, 193, 12, 128, 250, 231, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 172, 0, 41, 0, 0, 0, 1, 0, 3, 0, 136, + 0, 137, 0, 138, 0, 140, 0, 142, 0, 143, 0, 144, 0, 145, 0, 148, 0, 149, 0, 150, 0, + 161, 0, 162, 0, 163, 0, 164, 0, 208, 0, 219, 0, 249, 0, 6, 1, 7, 1, 24, 1, 3, 3, 5, + 51, 5, 51, 3, 3, 3, 7, 79, 78, 3, 3, 23, 50, 48, 49, 56, 45, 48, 53, 45, 50, 53, 3, + 3, 3, 89, 47, 80, 73, 79, 78, 69, 69, 82, 47, 85, 83, 66, 65, 78, 76, 90, 47, 80, + 48, 49, 54, 47, 48, 48, 48, 48, 56, 55, 53, 69, 47, 65, 78, 76, 90, 48, 48, 48, 48, + 46, 68, 65, 84, 23, 50, 48, 50, 50, 45, 48, 50, 45, 48, 50, 61, 84, 114, 97, 99, + 107, 115, 32, 98, 121, 32, 119, 119, 119, 46, 108, 111, 111, 112, 109, 97, 115, + 116, 101, 114, 115, 46, 99, 111, 109, 27, 68, 101, 109, 111, 32, 84, 114, 97, 99, + 107, 32, 49, 3, 35, 68, 101, 109, 111, 32, 84, 114, 97, 99, 107, 32, 49, 46, 109, + 112, 51, 105, 47, 67, 111, 110, 116, 101, 110, 116, 115, 47, 76, 111, 111, 112, + 109, 97, 115, 116, 101, 114, 115, 47, 85, 110, 107, 110, 111, 119, 110, 65, 108, + 98, 117, 109, 47, 68, 101, 109, 111, 32, 84, 114, 97, 99, 107, 32, 49, 46, 109, + 112, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 160, 0, 0, 7, 12, 0, 68, 172, 0, 0, 0, 0, 0, 0, 168, 71, 105, 0, 218, 177, + 193, 12, 128, 250, 231, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 64, 1, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 172, 0, 41, 0, 0, 0, 1, 0, 3, 0, 136, 0, 137, 0, + 138, 0, 140, 0, 142, 0, 143, 0, 144, 0, 145, 0, 148, 0, 149, 0, 150, 0, 161, 0, + 162, 0, 163, 0, 164, 0, 208, 0, 219, 0, 249, 0, 6, 1, 7, 1, 24, 1, 3, 3, 5, 51, 5, + 51, 3, 3, 3, 7, 79, 78, 3, 3, 23, 50, 48, 49, 56, 45, 48, 53, 45, 50, 53, 3, 3, 3, + 89, 47, 80, 73, 79, 78, 69, 69, 82, 47, 85, 83, 66, 65, 78, 76, 90, 47, 80, 48, 49, + 54, 47, 48, 48, 48, 48, 56, 55, 53, 69, 47, 65, 78, 76, 90, 48, 48, 48, 48, 46, 68, + 65, 84, 23, 50, 48, 50, 50, 45, 48, 50, 45, 48, 50, 61, 84, 114, 97, 99, 107, 115, + 32, 98, 121, 32, 119, 119, 119, 46, 108, 111, 111, 112, 109, 97, 115, 116, 101, + 114, 115, 46, 99, 111, 109, 27, 68, 101, 109, 111, 32, 84, 114, 97, 99, 107, 32, + 49, 3, 35, 68, 101, 109, 111, 32, 84, 114, 97, 99, 107, 32, 49, 46, 109, 112, 51, + 105, 47, 67, 111, 110, 116, 101, 110, 116, 115, 47, 76, 111, 111, 112, 109, 97, + 115, 116, 101, 114, 115, 47, 85, 110, 107, 110, 111, 119, 110, 65, 108, 98, 117, + 109, 47, 68, 101, 109, 111, 32, 84, 114, 97, 99, 107, 32, 49, 46, 109, 112, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 192, 0, + 0, 7, 12, 0, 68, 172, 0, 0, 0, 0, 0, 0, 246, 48, 78, 0, 59, 125, 186, 15, 128, 250, + 231, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, + 0, 0, 0, 224, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 128, 0, 41, 0, 0, 0, 1, 0, 3, 0, 136, 0, 137, 0, 138, 0, 140, 0, 142, + 0, 143, 0, 144, 0, 145, 0, 148, 0, 149, 0, 150, 0, 161, 0, 162, 0, 163, 0, 164, 0, + 208, 0, 219, 0, 249, 0, 6, 1, 7, 1, 24, 1, 3, 3, 5, 51, 5, 51, 3, 3, 3, 7, 79, 78, + 3, 3, 23, 50, 48, 49, 56, 45, 48, 53, 45, 50, 53, 3, 3, 3, 89, 47, 80, 73, 79, 78, + 69, 69, 82, 47, 85, 83, 66, 65, 78, 76, 90, 47, 80, 48, 53, 51, 47, 48, 48, 48, 49, + 68, 50, 49, 70, 47, 65, 78, 76, 90, 48, 48, 48, 48, 46, 68, 65, 84, 23, 50, 48, 50, + 50, 45, 48, 50, 45, 48, 50, 61, 84, 114, 97, 99, 107, 115, 32, 98, 121, 32, 119, + 119, 119, 46, 108, 111, 111, 112, 109, 97, 115, 116, 101, 114, 115, 46, 99, 111, + 109, 27, 68, 101, 109, 111, 32, 84, 114, 97, 99, 107, 32, 50, 3, 35, 68, 101, 109, + 111, 32, 84, 114, 97, 99, 107, 32, 50, 46, 109, 112, 51, 105, 47, 67, 111, 110, + 116, 101, 110, 116, 115, 47, 76, 111, 111, 112, 109, 97, 115, 116, 101, 114, 115, + 47, 85, 110, 107, 110, 111, 119, 110, 65, 108, 98, 117, 109, 47, 68, 101, 109, 111, + 32, 84, 114, 97, 99, 107, 32, 50, 46, 109, 112, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 76, 8, 204, 6, 76, 5, 252, 3, 172, 2, 80, 1, 0, 0, 96, 0, 64, 0, + ], + page, + (page_size,), + (page_size,), + ); + } } From 4717a0d7cd3ff68373f1776b98b1b9442df420c7 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Fri, 30 Sep 2022 00:06:38 +0200 Subject: [PATCH 2/3] fix(pdb): adhere to rows alignment to type alignment when writing --- src/pdb/mod.rs | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/util.rs | 18 ++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/pdb/mod.rs b/src/pdb/mod.rs index b29ab8e..4c9d0a2 100644 --- a/src/pdb/mod.rs +++ b/src/pdb/mod.rs @@ -20,8 +20,10 @@ pub mod string; +use std::convert::TryInto; + use crate::pdb::string::DeviceSQLString; -use crate::util::ColorIndex; +use crate::util::{align_by, ColorIndex}; use binrw::{ binread, binrw, io::{Read, Seek, SeekFrom, Write}, @@ -520,12 +522,20 @@ impl RowGroup { // Write rows let mut offset = page_offset + relative_row_offset; for row in &self.rows { + let alignment = row + .get_alignment() + .ok_or_else(|| binrw::Error::AssertFail { + pos: offset, + message: "Unexpected Row Alignment".to_string(), + })? + .get(); // Write row offset let row_offset: u16 = offset .checked_sub(page_offset) + .map(|offset| align_by(alignment, offset)) .and_then(|v| u16::try_from(v).ok()) .ok_or_else(|| binrw::Error::AssertFail { - pos: 0, + pos: offset, message: "Wraparound while calculating row offset".to_string(), })?; row_offset.write_options(writer, &options, ())?; @@ -1127,6 +1137,36 @@ pub enum Row { Unknown, } +impl Row { + #[must_use] + const fn get_alignment(&self) -> Option { + use crate::pdb::Row::*; + use std::mem::align_of; + use std::num::NonZeroU64; + // unfortunately I couldn't find any less copy-pastey way of doing this + // without unnecessarily complex macros. + const fn type_to_opt_align(_: &T) -> Option { + NonZeroU64::new(align_of::() as u64) + } + match &self { + Album(r) => type_to_opt_align(r), + Artist(r) => type_to_opt_align(r), + Artwork(r) => type_to_opt_align(r), + Color(r) => type_to_opt_align(r), + ColumnEntry(r) => type_to_opt_align(r), + Genre(r) => type_to_opt_align(r), + HistoryPlaylist(r) => type_to_opt_align(r), + HistoryEntry(r) => type_to_opt_align(r), + Key(r) => type_to_opt_align(r), + Label(r) => type_to_opt_align(r), + PlaylistTreeNode(r) => type_to_opt_align(r), + PlaylistEntry(r) => type_to_opt_align(r), + Track(r) => type_to_opt_align(r), + Unknown => None, + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/util.rs b/src/util.rs index e07fc0d..a33fff9 100644 --- a/src/util.rs +++ b/src/util.rs @@ -65,6 +65,24 @@ pub enum ColorIndex { Purple, } +/// align given value to the alignment requirements by the given type. +#[must_use] +pub const fn align_by(alignment: u64, mut offset: u64) -> u64 { + // This is technically dependent on the compile time ABI + // but for x86 (which this is likely compiled on), we should be able + // to assume that + // the alignment of a type is just the size of its largest + // member. That likely matches the assumptions made for the 32-bit + // MCU (Renesas R8A77240D500BG) built into the different CDJ-2000 variants. + // In either way, its better to overshoot the alignment + // than to undershoot it. For CDJ-3000s, this assumption + // is likely also correct since they use a 64-bit ARM CPU (Renesas R8A774C0HA01BG) + if offset % alignment != 0 { + offset += alignment - (offset % alignment); + } + offset +} + #[cfg(test)] pub(crate) mod testing { use binrw::{ From 8f8fe482f11ac3cedf476356d5b1ccdeb0ea8221 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 7 Sep 2022 02:18:15 +0200 Subject: [PATCH 3/3] WIP document some debugging/RE-ing results related to pages --- src/pdb/mod.rs | 253 ++++++++++++++++++++++++++----------------------- 1 file changed, 136 insertions(+), 117 deletions(-) diff --git a/src/pdb/mod.rs b/src/pdb/mod.rs index 4c9d0a2..6717b5f 100644 --- a/src/pdb/mod.rs +++ b/src/pdb/mod.rs @@ -519,6 +519,13 @@ impl RowGroup { let (page_offset, relative_row_offset) = args; + // TODO(Swiftb0y): DeviceSQL seems to write RowGroups so that the Rows + // with the lowest offset have their offset written at the end of the + // page. So If the Rows appeared in order Row1,Row2,Row3 in the heap/page + // their offsets would be stored in reverse order &Row3,&Row2,&Row1. + // It probably doesn't change the correctness of the (de-)serialization, + // but it makes sense to strive to be as close as possible to DeviceSQL + // Write rows let mut offset = page_offset + relative_row_offset; for row in &self.rows { @@ -541,6 +548,18 @@ impl RowGroup { row_offset.write_options(writer, &options, ())?; let restore_position = writer.stream_position()?; + // TODO(Swiftb0y): Write with proper alignment. + // Rows don't seem to be directly adjacent to each other + // but instead have gaps in between. They probably adhere to their + // member variable alignment. + // I have seen gaps of 52 to 55 bytes (ending after the last char + // of the previous row and the first byte of the next row). + // I have 0 idea why these gaps are this big or how to accurately + // guess their size. + // Rows also don't have a fixed size. Their sizes seem to fluctuate + // between 0 and 48 bytes in size (though the fluctuations always + // were multiple of 12) + // Write actual row content let offset_before_write = writer.seek(SeekFrom::Start(offset))?; row.write_options(writer, &options, ())?; @@ -1519,184 +1538,181 @@ mod test { rows: vec![ Row::Track(Track { unknown1: 36, - index_shift: 192, + index_shift: 0, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), - file_size: 5124342, - unknown2: 263879995, + file_size: 1382226, + unknown2: 191204207, unknown3: 64128, unknown4: 1511, artwork_id: ArtworkId(0), - key_id: KeyId(5), + key_id: KeyId(1), orig_artist_id: ArtistId(0), - label_id: LabelId(1), + label_id: LabelId(0), remixer_id: ArtistId(0), - bitrate: 320, + bitrate: 2116, track_number: 0, - tempo: 12000, + tempo: 0, genre_id: GenreId(0), album_id: AlbumId(0), - artist_id: ArtistId(1), - id: TrackId(2), + artist_id: ArtistId(0), + id: TrackId(1), disc_number: 0, play_count: 0, year: 0, - sample_depth: 16, - duration: 128, + sample_depth: 24, + duration: 5, unknown5: 41, color: ColorIndex::None, rating: 0, - unknown6: 1, + unknown6: 11, unknown7: 3, isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), unknown_string1: DeviceSQLString::empty(), unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), - unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: DeviceSQLString::empty(), autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), - date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: DeviceSQLString::new( - "/PIONEER/USBANLZ/P053/0001D21F/ANLZ0000.DAT".to_string(), + "/PIONEER/USBANLZ/P019/00020AA9/ANLZ0000.DAT".to_string(), ) .unwrap(), analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), - comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) - .unwrap(), - title: DeviceSQLString::new("Demo Track 2".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("NOISE".to_string()).unwrap(), unknown_string8: DeviceSQLString::empty(), - filename: DeviceSQLString::new("Demo Track 2.mp3".to_string()).unwrap(), + filename: DeviceSQLString::new("NOISE.wav".to_string()).unwrap(), file_path: DeviceSQLString::new( - "/Contents/Loopmasters/UnknownAlbum/Demo Track 2.mp3".to_string(), + "/Contents/UnknownArtist/UnknownAlbum/NOISE.wav".to_string(), ) .unwrap(), }), Row::Track(Track { unknown1: 36, - index_shift: 160, + index_shift: 32, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), - file_size: 6899624, - unknown2: 214020570, + file_size: 1515258, + unknown2: 34882935, unknown3: 64128, unknown4: 1511, artwork_id: ArtworkId(0), - key_id: KeyId(5), + key_id: KeyId(2), orig_artist_id: ArtistId(0), - label_id: LabelId(1), + label_id: LabelId(0), remixer_id: ArtistId(0), - bitrate: 320, + bitrate: 2116, track_number: 0, - tempo: 12800, + tempo: 0, genre_id: GenreId(0), album_id: AlbumId(0), - artist_id: ArtistId(1), - id: TrackId(1), + artist_id: ArtistId(0), + id: TrackId(2), disc_number: 0, play_count: 0, year: 0, - sample_depth: 16, - duration: 172, + sample_depth: 24, + duration: 5, unknown5: 41, color: ColorIndex::None, rating: 0, - unknown6: 1, + unknown6: 11, unknown7: 3, isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), unknown_string1: DeviceSQLString::empty(), unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), - unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: DeviceSQLString::empty(), autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), - date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: DeviceSQLString::new( - "/PIONEER/USBANLZ/P016/0000875E/ANLZ0000.DAT".to_string(), + "/PIONEER/USBANLZ/P043/00011517/ANLZ0000.DAT".to_string(), ) .unwrap(), analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), - comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) - .unwrap(), - title: DeviceSQLString::new("Demo Track 1".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("SINEWAVE".to_string()).unwrap(), unknown_string8: DeviceSQLString::empty(), - filename: DeviceSQLString::new("Demo Track 1.mp3".to_string()).unwrap(), + filename: DeviceSQLString::new("SINEWAVE.wav".to_string()).unwrap(), file_path: DeviceSQLString::new( - "/Contents/Loopmasters/UnknownAlbum/Demo Track 1.mp3".to_string(), + "/Contents/UnknownArtist/UnknownAlbum/SINEWAVE.wav".to_string(), ) .unwrap(), }), Row::Track(Track { unknown1: 36, - index_shift: 128, + index_shift: 64, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), - file_size: 6899624, - unknown2: 214020570, + file_size: 1941204, + unknown2: 243638374, unknown3: 64128, unknown4: 1511, artwork_id: ArtworkId(0), - key_id: KeyId(5), + key_id: KeyId(3), orig_artist_id: ArtistId(0), - label_id: LabelId(1), + label_id: LabelId(0), remixer_id: ArtistId(0), - bitrate: 320, + bitrate: 2116, track_number: 0, - tempo: 12800, + tempo: 0, genre_id: GenreId(0), album_id: AlbumId(0), - artist_id: ArtistId(1), - id: TrackId(5), + artist_id: ArtistId(0), + id: TrackId(3), disc_number: 0, play_count: 0, year: 0, - sample_depth: 16, - duration: 172, + sample_depth: 24, + duration: 7, unknown5: 41, color: ColorIndex::None, rating: 0, - unknown6: 1, + unknown6: 11, unknown7: 3, isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), unknown_string1: DeviceSQLString::empty(), unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), - unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: DeviceSQLString::empty(), autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), - date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), + date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: DeviceSQLString::new( - "/PIONEER/USBANLZ/P016/0000875E/ANLZ0000.DAT".to_string(), + "/PIONEER/USBANLZ/P017/00009B77/ANLZ0000.DAT".to_string(), ) .unwrap(), analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), - comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) - .unwrap(), - title: DeviceSQLString::new("Demo Track 1".to_string()).unwrap(), + comment: DeviceSQLString::empty(), + title: DeviceSQLString::new("SIREN".to_string()).unwrap(), unknown_string8: DeviceSQLString::empty(), - filename: DeviceSQLString::new("Demo Track 1.mp3".to_string()).unwrap(), + filename: DeviceSQLString::new("SIREN.wav".to_string()).unwrap(), file_path: DeviceSQLString::new( - "/Contents/Loopmasters/UnknownAlbum/Demo Track 1.mp3".to_string(), + "/Contents/UnknownArtist/UnknownAlbum/SIREN.wav".to_string(), ) .unwrap(), }), @@ -1762,181 +1778,184 @@ mod test { }), Row::Track(Track { unknown1: 36, - index_shift: 64, + index_shift: 128, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), - file_size: 1941204, - unknown2: 243638374, + file_size: 6899624, + unknown2: 214020570, unknown3: 64128, unknown4: 1511, artwork_id: ArtworkId(0), - key_id: KeyId(3), + key_id: KeyId(5), orig_artist_id: ArtistId(0), - label_id: LabelId(0), + label_id: LabelId(1), remixer_id: ArtistId(0), - bitrate: 2116, + bitrate: 320, track_number: 0, - tempo: 0, + tempo: 12800, genre_id: GenreId(0), album_id: AlbumId(0), - artist_id: ArtistId(0), - id: TrackId(3), + artist_id: ArtistId(1), + id: TrackId(5), disc_number: 0, play_count: 0, year: 0, - sample_depth: 24, - duration: 7, + sample_depth: 16, + duration: 172, unknown5: 41, color: ColorIndex::None, rating: 0, - unknown6: 11, + unknown6: 1, unknown7: 3, isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), unknown_string1: DeviceSQLString::empty(), unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), - unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: DeviceSQLString::empty(), autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), - date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: DeviceSQLString::new( - "/PIONEER/USBANLZ/P017/00009B77/ANLZ0000.DAT".to_string(), + "/PIONEER/USBANLZ/P016/0000875E/ANLZ0000.DAT".to_string(), ) .unwrap(), analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), - comment: DeviceSQLString::empty(), - title: DeviceSQLString::new("SIREN".to_string()).unwrap(), + comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) + .unwrap(), + title: DeviceSQLString::new("Demo Track 1".to_string()).unwrap(), unknown_string8: DeviceSQLString::empty(), - filename: DeviceSQLString::new("SIREN.wav".to_string()).unwrap(), + filename: DeviceSQLString::new("Demo Track 1.mp3".to_string()).unwrap(), file_path: DeviceSQLString::new( - "/Contents/UnknownArtist/UnknownAlbum/SIREN.wav".to_string(), + "/Contents/Loopmasters/UnknownAlbum/Demo Track 1.mp3".to_string(), ) .unwrap(), }), Row::Track(Track { unknown1: 36, - index_shift: 32, + index_shift: 160, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), - file_size: 1515258, - unknown2: 34882935, + file_size: 6899624, + unknown2: 214020570, unknown3: 64128, unknown4: 1511, artwork_id: ArtworkId(0), - key_id: KeyId(2), + key_id: KeyId(5), orig_artist_id: ArtistId(0), - label_id: LabelId(0), + label_id: LabelId(1), remixer_id: ArtistId(0), - bitrate: 2116, + bitrate: 320, track_number: 0, - tempo: 0, + tempo: 12800, genre_id: GenreId(0), album_id: AlbumId(0), - artist_id: ArtistId(0), - id: TrackId(2), + artist_id: ArtistId(1), + id: TrackId(1), disc_number: 0, play_count: 0, year: 0, - sample_depth: 24, - duration: 5, + sample_depth: 16, + duration: 172, unknown5: 41, color: ColorIndex::None, rating: 0, - unknown6: 11, + unknown6: 1, unknown7: 3, isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), unknown_string1: DeviceSQLString::empty(), unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), - unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: DeviceSQLString::empty(), autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), - date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: DeviceSQLString::new( - "/PIONEER/USBANLZ/P043/00011517/ANLZ0000.DAT".to_string(), + "/PIONEER/USBANLZ/P016/0000875E/ANLZ0000.DAT".to_string(), ) .unwrap(), analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), - comment: DeviceSQLString::empty(), - title: DeviceSQLString::new("SINEWAVE".to_string()).unwrap(), + comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) + .unwrap(), + title: DeviceSQLString::new("Demo Track 1".to_string()).unwrap(), unknown_string8: DeviceSQLString::empty(), - filename: DeviceSQLString::new("SINEWAVE.wav".to_string()).unwrap(), + filename: DeviceSQLString::new("Demo Track 1.mp3".to_string()).unwrap(), file_path: DeviceSQLString::new( - "/Contents/UnknownArtist/UnknownAlbum/SINEWAVE.wav".to_string(), + "/Contents/Loopmasters/UnknownAlbum/Demo Track 1.mp3".to_string(), ) .unwrap(), }), Row::Track(Track { unknown1: 36, - index_shift: 0, + index_shift: 192, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), - file_size: 1382226, - unknown2: 191204207, + file_size: 5124342, + unknown2: 263879995, unknown3: 64128, unknown4: 1511, artwork_id: ArtworkId(0), - key_id: KeyId(1), + key_id: KeyId(5), orig_artist_id: ArtistId(0), - label_id: LabelId(0), + label_id: LabelId(1), remixer_id: ArtistId(0), - bitrate: 2116, + bitrate: 320, track_number: 0, - tempo: 0, + tempo: 12000, genre_id: GenreId(0), album_id: AlbumId(0), - artist_id: ArtistId(0), - id: TrackId(1), + artist_id: ArtistId(1), + id: TrackId(2), disc_number: 0, play_count: 0, year: 0, - sample_depth: 24, - duration: 5, + sample_depth: 16, + duration: 128, unknown5: 41, color: ColorIndex::None, rating: 0, - unknown6: 11, + unknown6: 1, unknown7: 3, isrc: DeviceSQLString::new_isrc("".to_string()).unwrap(), unknown_string1: DeviceSQLString::empty(), unknown_string2: DeviceSQLString::new("3".to_string()).unwrap(), - unknown_string3: DeviceSQLString::new("2".to_string()).unwrap(), + unknown_string3: DeviceSQLString::new("3".to_string()).unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: DeviceSQLString::empty(), autoload_hotcues: DeviceSQLString::new("ON".to_string()).unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), - date_added: DeviceSQLString::new("2015-09-07".to_string()).unwrap(), + date_added: DeviceSQLString::new("2018-05-25".to_string()).unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: DeviceSQLString::new( - "/PIONEER/USBANLZ/P019/00020AA9/ANLZ0000.DAT".to_string(), + "/PIONEER/USBANLZ/P053/0001D21F/ANLZ0000.DAT".to_string(), ) .unwrap(), analyze_date: DeviceSQLString::new("2022-02-02".to_string()).unwrap(), - comment: DeviceSQLString::empty(), - title: DeviceSQLString::new("NOISE".to_string()).unwrap(), + comment: DeviceSQLString::new("Tracks by www.loopmasters.com".to_string()) + .unwrap(), + title: DeviceSQLString::new("Demo Track 2".to_string()).unwrap(), unknown_string8: DeviceSQLString::empty(), - filename: DeviceSQLString::new("NOISE.wav".to_string()).unwrap(), + filename: DeviceSQLString::new("Demo Track 2.mp3".to_string()).unwrap(), file_path: DeviceSQLString::new( - "/Contents/UnknownArtist/UnknownAlbum/NOISE.wav".to_string(), + "/Contents/Loopmasters/UnknownAlbum/Demo Track 2.mp3".to_string(), ) .unwrap(), }),