1pub mod bitfields;
22pub mod ext;
23pub mod offset_array;
24pub mod string;
25
26use bitfields::{PackedRowCounts, PageFlags};
27use offset_array::{OffsetArrayContainer, OffsetArrayItems};
28
29#[cfg(test)]
30mod test_roundtrip;
31
32#[cfg(test)]
33mod test_modification;
34
35use std::collections::BTreeMap;
36use std::fmt;
37
38use crate::pdb::ext::{ExtPageType, ExtRow};
39use crate::pdb::offset_array::OffsetSize;
40use crate::pdb::string::DeviceSQLString;
41use crate::util::{parse_at_offsets, write_at_offsets, ColorIndex, FileType};
42use binrw::{
43 binrw,
44 io::{Read, Seek, SeekFrom, Write},
45 BinRead, BinResult, BinWrite, Endian,
46};
47use thiserror::Error;
48
49#[derive(Debug, Error)]
51pub enum PdbError {
52 #[error("Invalid page index value: {0:#X}")]
54 InvalidPageIndex(u32),
55 #[error("Invalid index flags (expected max 3 bits): {0:#b}")]
57 InvalidIndexFlags(u8),
58 #[error("Cannot add row to a full row group (max 16 rows)")]
60 RowGroupFull,
61}
62
63#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
66pub enum DatabaseType {
67 #[default] Plain,
70 Ext,
72}
73
74#[binrw]
76#[derive(Debug, PartialEq, Eq, Clone, Copy)]
77#[brw(little)]
78#[br(import(db_type: DatabaseType))]
79pub enum PageType {
80 #[br(pre_assert(db_type == DatabaseType::Plain))]
81 Plain(PlainPageType),
83 #[br(pre_assert(db_type == DatabaseType::Ext))]
84 Ext(ExtPageType),
86 Unknown(u32),
88}
89
90#[binrw]
92#[derive(Debug, PartialEq, Eq, Clone, Copy)]
93#[brw(little)]
94pub enum PlainPageType {
95 #[brw(magic = 0u32)]
97 Tracks,
98 #[brw(magic = 1u32)]
100 Genres,
101 #[brw(magic = 2u32)]
103 Artists,
104 #[brw(magic = 3u32)]
106 Albums,
107 #[brw(magic = 4u32)]
109 Labels,
110 #[brw(magic = 5u32)]
112 Keys,
113 #[brw(magic = 6u32)]
115 Colors,
116 #[brw(magic = 7u32)]
119 PlaylistTree,
120 #[brw(magic = 8u32)]
122 PlaylistEntries,
123 #[brw(magic = 11u32)]
126 HistoryPlaylists,
127 #[brw(magic = 12u32)]
129 HistoryEntries,
130 #[brw(magic = 13u32)]
132 Artwork,
133 #[brw(magic = 16u32)]
135 Columns,
136 #[brw(magic = 17u32)]
138 Menu,
139 #[brw(magic = 19u32)]
141 History,
142}
143
144#[binrw]
147#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
148#[brw(little)]
149pub struct PageIndex(u32);
150
151impl TryFrom<u32> for PageIndex {
152 type Error = PdbError;
153
154 fn try_from(value: u32) -> Result<Self, Self::Error> {
155 if value < 0x03FF_FFFF {
156 Ok(Self(value))
157 } else {
158 Err(PdbError::InvalidPageIndex(value))
159 }
160 }
161}
162
163impl PageIndex {
164 #[must_use]
166 pub fn offset(&self, page_size: u32) -> u64 {
167 u64::from(self.0) * u64::from(page_size)
168 }
169}
170
171#[binrw]
174#[derive(Debug, PartialEq, Eq, Clone)]
175#[brw(little)]
176#[brw(import(db_type: DatabaseType))]
177pub struct Table {
178 #[br(args(db_type))]
180 pub page_type: PageType,
181 #[allow(dead_code)]
184 empty_candidate: u32,
185 pub first_page: PageIndex,
190 pub last_page: PageIndex,
192}
193
194#[binrw]
196#[derive(Debug, PartialEq, Eq, Clone)]
197#[brw(little)]
198#[brw(import(db_type: DatabaseType))]
199pub struct Header {
200 #[brw(magic = 0u32)]
202 pub page_size: u32,
206 pub num_tables: u32,
208 pub next_unused_page: PageIndex,
210 pub unknown: u32,
212 pub sequence: u32,
214 #[brw(magic = 0u32)]
216 #[br(count = num_tables, args {inner: (db_type,)})]
218 #[bw(args(db_type))]
219 pub tables: Vec<Table>,
220}
221
222impl Header {
223 pub fn read_pages<R: Read + Seek>(
225 &self,
226 reader: &mut R,
227 _: Endian,
228 args: (&PageIndex, &PageIndex, DatabaseType),
229 ) -> BinResult<Vec<Page>> {
230 let endian = Endian::Little;
231 let (first_page, last_page, db_type) = args;
232
233 let mut pages = vec![];
234 let mut page_index = first_page.clone();
235 loop {
236 let page_offset = SeekFrom::Start(page_index.offset(self.page_size));
237 reader.seek(page_offset).map_err(binrw::Error::Io)?;
238 let page = Page::read_options(reader, endian, (self.page_size, db_type))?;
239 let is_last_page = &page.header.page_index == last_page;
240 page_index = page.header.next_page.clone();
241 pages.push(page);
242
243 if is_last_page {
244 break;
245 }
246 }
247 Ok(pages)
248 }
249}
250
251#[binrw]
253#[derive(PartialEq, Eq, Clone, Copy)]
254#[brw(little)]
255pub struct IndexEntry(u32);
256
257impl IndexEntry {
258 pub const BINARY_SIZE: u32 = 4;
260}
261
262impl TryFrom<(PageIndex, u8)> for IndexEntry {
263 type Error = PdbError;
264
265 fn try_from(value: (PageIndex, u8)) -> Result<Self, Self::Error> {
266 let (page_index, index_flags) = value;
267 if index_flags & 0b111 != index_flags {
268 return Err(PdbError::InvalidIndexFlags(index_flags));
269 }
270 Ok(Self((page_index.0 << 3) | (index_flags & 0b111) as u32))
271 }
272}
273
274impl IndexEntry {
275 pub fn page_index(&self) -> Result<PageIndex, PdbError> {
278 PageIndex::try_from(self.0 >> 3)
279 }
280
281 #[must_use]
284 pub fn index_flags(&self) -> u8 {
285 (self.0 & 0b111) as u8
286 }
287
288 #[must_use]
290 pub fn is_empty(&self) -> bool {
291 self.0 == 0x1FFF_FFF8
292 }
293
294 #[must_use]
296 pub const fn empty() -> Self {
297 Self(0x1FFF_FFF8)
298 }
299}
300
301impl fmt::Debug for IndexEntry {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 if self.is_empty() {
304 f.debug_struct("IndexEntry")
305 .field("is_empty", &self.is_empty())
306 .finish()
307 } else {
308 f.debug_struct("IndexEntry")
309 .field("is_empty", &self.is_empty())
310 .field("page_index", &self.page_index().unwrap())
311 .field("index_flags", &self.index_flags())
312 .finish()
313 }
314 }
315}
316
317#[binrw]
319#[derive(Debug, PartialEq, Eq, Clone)]
320pub struct IndexPageHeader {
321 pub unknown_a: u16,
323 pub unknown_b: u16,
325 #[brw(magic = 0x03ecu16)]
327 pub next_offset: u16,
332 pub page_index: PageIndex,
334 pub next_page: PageIndex,
336 #[brw(magic = 0x0000_0000_03ff_ffffu64)]
338 pub num_entries: u16,
340 pub first_empty: u16,
348}
349
350impl IndexPageHeader {
351 pub const BINARY_SIZE: u32 = 28;
353}
354
355#[binrw]
357#[derive(Debug, PartialEq, Eq, Clone)]
358#[br(little)]
359#[bw(little, import { page_size: u32 })]
360pub struct IndexPageContent {
361 pub header: IndexPageHeader,
363
364 #[br(count = header.num_entries)]
366 pub entries: Vec<IndexEntry>,
367
368 #[br(temp)]
371 #[bw(calc = EmptyIndexEntries(
372 Self::total_entries(page_size) - usize::from(header.num_entries)
373 ))]
374 #[bw(pad_after = 20)]
375 _empty_entries: EmptyIndexEntries,
376}
377
378impl IndexPageContent {
379 fn total_entries(page_size: u32) -> usize {
380 let entries_space = page_size - PageHeader::BINARY_SIZE - IndexPageHeader::BINARY_SIZE - 20;
382 (entries_space / IndexEntry::BINARY_SIZE)
383 .try_into()
384 .unwrap()
385 }
386}
387
388struct EmptyIndexEntries(usize);
390
391impl BinRead for EmptyIndexEntries {
392 type Args<'a> = ();
393
394 fn read_options<Reader>(_: &mut Reader, _: Endian, (): Self::Args<'_>) -> BinResult<Self>
395 where
396 Reader: Read + Seek,
397 {
398 Ok(Self(0))
399 }
400}
401
402impl BinWrite for EmptyIndexEntries {
403 type Args<'a> = ();
404
405 fn write_options<Writer>(
406 &self,
407 writer: &mut Writer,
408 endian: Endian,
409 (): Self::Args<'_>,
410 ) -> BinResult<()>
411 where
412 Writer: Write + Seek,
413 {
414 const EMPTY: IndexEntry = IndexEntry::empty();
415 for _ in 0..self.0 {
416 EMPTY.write_options(writer, endian, ())?;
417 }
418 Ok(())
419 }
420}
421
422#[binrw]
426#[derive(Debug, PartialEq, Clone)]
427#[br(little, import { page_size: u32, header: &PageHeader })]
428#[bw(little, import { page_size: u32 })]
429pub enum PageContent {
430 #[br(pre_assert(!header.page_flags.is_index_page()))]
432 Data(
433 #[br(args { page_size, page_header: header })]
434 #[bw(args { page_size })]
435 DataPageContent,
436 ),
437 #[br(pre_assert(header.page_flags.is_index_page()))]
439 Index(#[bw(args { page_size })] IndexPageContent),
440 Unknown,
442}
443
444impl PageContent {
445 #[must_use]
447 pub fn into_data(self) -> Option<DataPageContent> {
448 match self {
449 PageContent::Data(data) => Some(data),
450 _ => None,
451 }
452 }
453
454 #[must_use]
456 pub fn into_index(self) -> Option<IndexPageContent> {
457 match self {
458 PageContent::Index(index) => Some(index),
459 _ => None,
460 }
461 }
462}
463
464#[binrw]
466#[derive(Debug, PartialEq, Eq, Clone)]
467#[brw(little)]
468#[br(import(db_type: DatabaseType))]
469pub struct PageHeader {
470 #[brw(magic = 0u32)]
472 pub page_index: PageIndex,
476 #[br(args(db_type))]
480 pub page_type: PageType,
481 pub next_page: PageIndex,
486 pub unknown1: u32,
489 pub unknown2: u32,
492 pub packed_row_counts: PackedRowCounts,
496 pub page_flags: PageFlags,
501 pub free_size: u16,
503 pub used_size: u16,
505}
506
507impl PageHeader {
508 pub const BINARY_SIZE: u32 = 0x20;
510}
511
512#[binrw]
518#[derive(Debug, PartialEq)]
519#[brw(little)]
520#[br(import(page_size: u32, db_type: DatabaseType))]
521#[bw(import(page_size: u32))]
522pub struct Page {
523 #[br(args(db_type))]
525 pub header: PageHeader,
526
527 #[br(args { page_size, header: &header })]
529 #[bw(args { page_size })]
530 pub content: PageContent,
531}
532
533impl Page {
534 pub fn allocate_row<'a>(&'a mut self, bytes: u16) -> Option<impl FnOnce(Row) + 'a> {
541 match self.content {
542 PageContent::Index(_) | PageContent::Unknown => None,
543 PageContent::Data(ref mut dpc) => {
544 let bytes = bytes.next_multiple_of(4);
546
547 let required_bytes = bytes + RowGroup::HEADER_SIZE + RowGroup::OFFSET_SIZE;
549 if self.header.free_size < required_bytes {
550 return None;
551 }
552
553 let offset = self.header.used_size;
554 self.header.used_size += bytes;
555 self.header.free_size -= bytes;
556 let row_counts = &mut self.header.packed_row_counts;
557 row_counts.increment_num_rows();
558 let (row_group_index, row_subindex) = row_counts.last_row_index().unwrap();
559
560 if dpc.row_groups.get(row_group_index as usize).is_none() {
561 dpc.row_groups.push(RowGroup::empty());
562 self.header.free_size -= RowGroup::HEADER_SIZE;
563 }
564
565 let row_group = dpc.row_groups.get_mut(row_group_index as usize).unwrap();
566 row_group.insert_offset(row_subindex, offset);
567 self.header.free_size -= RowGroup::OFFSET_SIZE;
568
569 let rows = &mut dpc.rows;
570
571 Some(move |row: Row| {
572 let prev_entry = rows.insert(offset, row);
573 if prev_entry.is_some() {
574 panic!(
575 "Offset {} was already occupied by row {:?}",
576 offset,
577 prev_entry.unwrap()
578 );
579 }
580 row_group.mark_offset_present(row_subindex);
581 row_counts.increment_num_rows_valid();
582 })
583 }
584 }
585 }
586}
587
588#[binrw]
590#[derive(Debug, PartialEq, Eq, Clone)]
591pub struct DataPageHeader {
592 pub unknown5: u16,
598 pub unknown_not_num_rows_large: u16,
601 pub unknown6: u16,
603 pub unknown7: u16,
609}
610
611impl DataPageHeader {
612 pub const BINARY_SIZE: u32 = 0x8;
614}
615
616#[binrw]
618#[derive(Debug, PartialEq, Eq, Clone)]
619#[br(little, import { page_size: u32, page_header: &PageHeader })]
620#[bw(little, import { page_size: u32 })]
621pub struct DataPageContent {
622 pub header: DataPageHeader,
624
625 #[brw(seek_before(SeekFrom::Current(Self::page_heap_size(page_size).into())), restore_position)]
630 #[br(args {count: page_header.packed_row_counts.num_row_groups().into()})]
631 pub row_groups: Vec<RowGroup>,
632
633 #[br(args(page_header.page_type))]
637 #[br(parse_with = parse_at_offsets(row_groups.iter().flat_map(RowGroup::present_rows_offsets)))]
638 #[bw(write_with = write_at_offsets)]
639 #[br(assert(rows.len() == page_header.packed_row_counts.num_rows_valid().into(), "parsing page {:?}: num_rows_valid {} does not match parsed row count {}", page_header.page_index, page_header.packed_row_counts.num_rows_valid(), rows.len()))]
640 pub rows: BTreeMap<u16, Row>,
641}
642
643impl DataPageContent {
644 fn page_heap_size(page_size: u32) -> u32 {
645 page_size - PageHeader::BINARY_SIZE - DataPageHeader::BINARY_SIZE
646 }
647}
648
649#[allow(dead_code)]
651trait PageHeapObject {
652 type Args<'a>;
653
654 fn heap_bytes_required(&self, args: Self::Args<'_>) -> u16;
656}
657
658impl PageHeapObject for u8 {
659 type Args<'a> = ();
660 fn heap_bytes_required(&self, _: ()) -> u16 {
661 std::mem::size_of::<u8>() as u16
662 }
663}
664
665impl PageHeapObject for u16 {
666 type Args<'a> = ();
667 fn heap_bytes_required(&self, _: ()) -> u16 {
668 std::mem::size_of::<u16>() as u16
669 }
670}
671
672impl PageHeapObject for u32 {
673 type Args<'a> = ();
674 fn heap_bytes_required(&self, _: ()) -> u16 {
675 std::mem::size_of::<u32>() as u16
676 }
677}
678
679impl PageHeapObject for ColorIndex {
680 type Args<'a> = ();
681 fn heap_bytes_required(&self, _: ()) -> u16 {
682 (0u8).heap_bytes_required(())
683 }
684}
685
686impl PageHeapObject for FileType {
687 type Args<'a> = ();
688 fn heap_bytes_required(&self, _: ()) -> u16 {
689 (0u16).heap_bytes_required(())
690 }
691}
692
693#[binrw]
697#[derive(Debug, Clone, Eq)]
698pub struct RowGroup {
699 #[brw(seek_before = SeekFrom::Current(-i64::from(Self::BINARY_SIZE)))]
709 #[bw(write_with = Self::write_row_offsets, args(*row_presence_flags))]
710 pub row_offsets: [u16; Self::MAX_ROW_COUNT],
711 pub row_presence_flags: u16,
713 pub unknown: u16,
721
722 #[br(temp)]
724 #[bw(calc = ())]
725 #[brw(seek_before = SeekFrom::Current(-i64::from(Self::BINARY_SIZE)))]
726 _dummy: (),
727}
728
729impl RowGroup {
730 const MAX_ROW_COUNT: usize = 16;
731 const HEADER_SIZE: u16 = 4; const OFFSET_SIZE: u16 = 2;
733 const BINARY_SIZE: u16 = (Self::MAX_ROW_COUNT as u16) * Self::OFFSET_SIZE + Self::HEADER_SIZE;
734
735 fn empty() -> Self {
736 Self {
737 row_offsets: [0; Self::MAX_ROW_COUNT],
738 row_presence_flags: 0,
739 unknown: 0,
740 }
741 }
742
743 fn present_rows_offsets(&self) -> impl Iterator<Item = u16> + '_ {
744 self.row_offsets
745 .iter()
746 .rev()
747 .enumerate()
748 .filter_map(move |(i, row_offset)| {
749 (self.row_presence_flags & (1 << i) != 0).then_some(*row_offset)
750 })
751 }
752
753 fn write_row_offsets<Writer>(
754 row_offsets: &[u16; 16],
755 writer: &mut Writer,
756 endian: Endian,
757 (row_presence_flags,): (u16,),
758 ) -> BinResult<()>
759 where
760 Writer: Write + Seek,
761 {
762 const U16_SIZE: u32 = std::mem::size_of::<u16>() as u32;
763 let skip = row_presence_flags.leading_zeros();
764 writer.seek(SeekFrom::Current((skip * U16_SIZE).into()))?;
765 for offset in row_offsets.iter().skip(skip.try_into().unwrap()) {
766 offset.write_options(writer, endian, ())?;
767 }
768 Ok(())
769 }
770
771 fn insert_offset(&mut self, subindex: u16, offset: u16) {
774 self.row_offsets[(Self::MAX_ROW_COUNT as u16 - 1 - subindex) as usize] = offset;
775 }
776
777 fn mark_offset_present(&mut self, subindex: u16) {
779 self.row_presence_flags |= 1 << subindex;
780 }
781}
782
783impl Default for RowGroup {
784 fn default() -> Self {
785 Self::empty()
786 }
787}
788
789impl PartialEq for RowGroup {
790 fn eq(&self, other: &Self) -> bool {
791 self.unknown == other.unknown
792 && self.present_rows_offsets().eq(other.present_rows_offsets())
793 }
794}
795
796#[binrw]
798#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
799#[brw(little)]
800pub struct Subtype(pub u16);
801
802impl PageHeapObject for Subtype {
803 type Args<'a> = ();
804 fn heap_bytes_required(&self, _: ()) -> u16 {
805 self.0.heap_bytes_required(())
806 }
807}
808
809impl Subtype {
810 #[must_use]
815 pub fn get_offset_size(&self) -> OffsetSize {
816 if self.0 & 0x04 == 0 {
817 OffsetSize::U8
818 } else {
819 OffsetSize::U16
820 }
821 }
822}
823
824#[binrw]
826#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
827#[brw(little)]
828pub struct TrackId(pub u32);
829
830impl PageHeapObject for TrackId {
831 type Args<'a> = ();
832 fn heap_bytes_required(&self, _: ()) -> u16 {
833 self.0.heap_bytes_required(())
834 }
835}
836
837#[binrw]
839#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
840#[brw(little)]
841pub struct ArtworkId(pub u32);
842
843impl PageHeapObject for ArtworkId {
844 type Args<'a> = ();
845 fn heap_bytes_required(&self, _: ()) -> u16 {
846 self.0.heap_bytes_required(())
847 }
848}
849
850#[binrw]
852#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
853#[brw(little)]
854pub struct AlbumId(pub u32);
855
856impl PageHeapObject for AlbumId {
857 type Args<'a> = ();
858 fn heap_bytes_required(&self, _: ()) -> u16 {
859 self.0.heap_bytes_required(())
860 }
861}
862
863#[binrw]
865#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
866#[brw(little)]
867pub struct ArtistId(pub u32);
868
869impl PageHeapObject for ArtistId {
870 type Args<'a> = ();
871 fn heap_bytes_required(&self, _: ()) -> u16 {
872 self.0.heap_bytes_required(())
873 }
874}
875
876#[binrw]
878#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
879#[brw(little)]
880pub struct GenreId(pub u32);
881
882impl PageHeapObject for GenreId {
883 type Args<'a> = ();
884 fn heap_bytes_required(&self, _: ()) -> u16 {
885 self.0.heap_bytes_required(())
886 }
887}
888
889#[binrw]
891#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
892#[brw(little)]
893pub struct KeyId(pub u32);
894
895impl PageHeapObject for KeyId {
896 type Args<'a> = ();
897 fn heap_bytes_required(&self, _: ()) -> u16 {
898 self.0.heap_bytes_required(())
899 }
900}
901
902#[binrw]
904#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
905#[brw(little)]
906pub struct LabelId(pub u32);
907
908impl PageHeapObject for LabelId {
909 type Args<'a> = ();
910 fn heap_bytes_required(&self, _: ()) -> u16 {
911 self.0.heap_bytes_required(())
912 }
913}
914
915#[binrw]
917#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
918#[brw(little)]
919pub struct PlaylistTreeNodeId(pub u32);
920
921impl PageHeapObject for PlaylistTreeNodeId {
922 type Args<'a> = ();
923 fn heap_bytes_required(&self, _: ()) -> u16 {
924 self.0.heap_bytes_required(())
925 }
926}
927
928#[binrw]
930#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
931#[brw(little)]
932pub struct HistoryPlaylistId(pub u32);
933
934impl PageHeapObject for HistoryPlaylistId {
935 type Args<'a> = ();
936 fn heap_bytes_required(&self, _: ()) -> u16 {
937 self.0.heap_bytes_required(())
938 }
939}
940
941#[derive(Debug, PartialEq, Clone, Eq)]
942pub struct TrailingName {
944 pub name: DeviceSQLString,
946}
947
948impl OffsetArrayItems<1> for TrailingName {
949 type Item = DeviceSQLString;
950
951 fn as_items(&self) -> [&Self::Item; 1] {
952 [&self.name]
953 }
954
955 fn from_items(items: [Self::Item; 1]) -> Self {
956 let [name] = items;
957 Self { name }
958 }
959}
960
961#[binrw]
963#[derive(Debug, PartialEq, Eq, Clone)]
964#[brw(little)]
965pub struct Album {
966 subtype: Subtype,
968 index_shift: u16,
971 unknown2: u32,
973 artist_id: ArtistId,
975 id: AlbumId,
977 unknown3: u32,
979 #[brw(args(20, subtype.get_offset_size(), ()))]
981 offsets: OffsetArrayContainer<TrailingName, 1>,
982}
983
984impl PageHeapObject for Album {
985 type Args<'a> = ();
986 fn heap_bytes_required(&self, _: ()) -> u16 {
987 [
988 self.subtype.heap_bytes_required(()),
989 self.index_shift.heap_bytes_required(()),
990 self.unknown2.heap_bytes_required(()),
991 self.artist_id.heap_bytes_required(()),
992 self.id.heap_bytes_required(()),
993 self.unknown3.heap_bytes_required(()),
994 self.offsets
995 .heap_bytes_required(self.subtype.get_offset_size()),
996 ]
997 .iter()
998 .sum()
999 }
1000}
1001
1002#[binrw]
1004#[derive(Debug, PartialEq, Eq, Clone)]
1005#[brw(little)]
1006pub struct Artist {
1007 subtype: Subtype,
1009 index_shift: u16,
1012 pub id: ArtistId,
1014 #[brw(args(8, subtype.get_offset_size(), ()))]
1016 pub offsets: OffsetArrayContainer<TrailingName, 1>,
1017}
1018
1019impl PageHeapObject for Artist {
1020 type Args<'a> = ();
1021 fn heap_bytes_required(&self, _: ()) -> u16 {
1022 [
1023 self.subtype.heap_bytes_required(()),
1024 self.index_shift.heap_bytes_required(()),
1025 self.id.heap_bytes_required(()),
1026 self.offsets
1027 .heap_bytes_required(self.subtype.get_offset_size()),
1028 ]
1029 .iter()
1030 .sum()
1031 }
1032}
1033
1034#[binrw]
1036#[derive(Debug, PartialEq, Eq, Clone)]
1037#[brw(little)]
1038pub struct Artwork {
1039 id: ArtworkId,
1041 path: DeviceSQLString,
1043}
1044
1045impl PageHeapObject for Artwork {
1046 type Args<'a> = ();
1047 fn heap_bytes_required(&self, _: ()) -> u16 {
1048 [
1049 self.id.heap_bytes_required(()),
1050 self.path.heap_bytes_required(()),
1051 ]
1052 .iter()
1053 .sum()
1054 }
1055}
1056
1057#[binrw]
1059#[derive(Debug, PartialEq, Eq, Clone)]
1060#[brw(little)]
1061pub struct Color {
1062 unknown1: u32,
1064 unknown2: u8,
1066 color: ColorIndex,
1068 unknown3: u16,
1070 name: DeviceSQLString,
1072}
1073
1074impl PageHeapObject for Color {
1075 type Args<'a> = ();
1076 fn heap_bytes_required(&self, _: ()) -> u16 {
1077 [
1078 self.unknown1.heap_bytes_required(()),
1079 self.unknown2.heap_bytes_required(()),
1080 self.color.heap_bytes_required(()),
1081 self.unknown3.heap_bytes_required(()),
1082 self.name.heap_bytes_required(()),
1083 ]
1084 .iter()
1085 .sum()
1086 }
1087}
1088
1089#[binrw]
1091#[derive(Debug, PartialEq, Eq, Clone)]
1092#[brw(little)]
1093pub struct Genre {
1094 id: GenreId,
1096 name: DeviceSQLString,
1098}
1099
1100impl PageHeapObject for Genre {
1101 type Args<'a> = ();
1102 fn heap_bytes_required(&self, _: ()) -> u16 {
1103 [
1104 self.id.heap_bytes_required(()),
1105 self.name.heap_bytes_required(()),
1106 ]
1107 .iter()
1108 .sum()
1109 }
1110}
1111
1112#[binrw]
1114#[derive(Debug, PartialEq, Eq, Clone)]
1115#[brw(little)]
1116pub struct HistoryPlaylist {
1117 id: HistoryPlaylistId,
1119 name: DeviceSQLString,
1121}
1122
1123impl PageHeapObject for HistoryPlaylist {
1124 type Args<'a> = ();
1125 fn heap_bytes_required(&self, _: ()) -> u16 {
1126 [
1127 self.id.heap_bytes_required(()),
1128 self.name.heap_bytes_required(()),
1129 ]
1130 .iter()
1131 .sum()
1132 }
1133}
1134
1135#[binrw]
1137#[derive(Debug, PartialEq, Eq, Clone)]
1138#[brw(little)]
1139pub struct HistoryEntry {
1140 track_id: TrackId,
1142 playlist_id: HistoryPlaylistId,
1144 entry_index: u32,
1146}
1147
1148impl PageHeapObject for HistoryEntry {
1149 type Args<'a> = ();
1150 fn heap_bytes_required(&self, _: ()) -> u16 {
1151 [
1152 self.track_id.heap_bytes_required(()),
1153 self.playlist_id.heap_bytes_required(()),
1154 self.entry_index.heap_bytes_required(()),
1155 ]
1156 .iter()
1157 .sum()
1158 }
1159}
1160
1161#[binrw]
1163#[derive(Debug, PartialEq, Eq, Clone)]
1164#[brw(little)]
1165pub struct Key {
1166 id: KeyId,
1168 id2: u32,
1170 name: DeviceSQLString,
1172}
1173
1174impl PageHeapObject for Key {
1175 type Args<'a> = ();
1176 fn heap_bytes_required(&self, _: ()) -> u16 {
1177 [
1178 self.id.heap_bytes_required(()),
1179 self.id2.heap_bytes_required(()),
1180 self.name.heap_bytes_required(()),
1181 ]
1182 .iter()
1183 .sum()
1184 }
1185}
1186
1187#[binrw]
1189#[derive(Debug, PartialEq, Eq, Clone)]
1190#[brw(little)]
1191pub struct Label {
1192 id: LabelId,
1194 name: DeviceSQLString,
1196}
1197
1198impl PageHeapObject for Label {
1199 type Args<'a> = ();
1200 fn heap_bytes_required(&self, _: ()) -> u16 {
1201 [
1202 self.id.heap_bytes_required(()),
1203 self.name.heap_bytes_required(()),
1204 ]
1205 .iter()
1206 .sum()
1207 }
1208}
1209
1210#[binrw]
1212#[derive(Debug, PartialEq, Eq, Clone)]
1213#[brw(little)]
1214pub struct PlaylistTreeNode {
1215 pub parent_id: PlaylistTreeNodeId,
1217 unknown: u32,
1219 sort_order: u32,
1221 pub id: PlaylistTreeNodeId,
1223 node_is_folder: u32,
1225 pub name: DeviceSQLString,
1227}
1228
1229impl PlaylistTreeNode {
1230 #[must_use]
1232 pub fn is_folder(&self) -> bool {
1233 self.node_is_folder > 0
1234 }
1235}
1236
1237impl PageHeapObject for PlaylistTreeNode {
1238 type Args<'a> = ();
1239 fn heap_bytes_required(&self, _: ()) -> u16 {
1240 [
1241 self.parent_id.heap_bytes_required(()),
1242 self.unknown.heap_bytes_required(()),
1243 self.sort_order.heap_bytes_required(()),
1244 self.id.heap_bytes_required(()),
1245 self.node_is_folder.heap_bytes_required(()),
1246 self.name.heap_bytes_required(()),
1247 ]
1248 .iter()
1249 .sum()
1250 }
1251}
1252
1253#[binrw]
1255#[derive(Debug, PartialEq, Eq, Clone)]
1256#[brw(little)]
1257pub struct PlaylistEntry {
1258 pub entry_index: u32,
1260 pub track_id: TrackId,
1262 pub playlist_id: PlaylistTreeNodeId,
1264}
1265
1266impl PageHeapObject for PlaylistEntry {
1267 type Args<'a> = ();
1268 fn heap_bytes_required(&self, _: ()) -> u16 {
1269 [
1270 self.entry_index.heap_bytes_required(()),
1271 self.track_id.heap_bytes_required(()),
1272 self.playlist_id.heap_bytes_required(()),
1273 ]
1274 .iter()
1275 .sum()
1276 }
1277}
1278
1279#[binrw]
1282#[derive(Debug, PartialEq, Eq, Clone)]
1283#[brw(little)]
1284pub struct ColumnEntry {
1285 id: u16,
1290 unknown0: u16,
1293 pub column_name: DeviceSQLString,
1300}
1301
1302impl PageHeapObject for ColumnEntry {
1303 type Args<'a> = ();
1304 fn heap_bytes_required(&self, _: ()) -> u16 {
1305 [
1306 self.id.heap_bytes_required(()),
1307 self.unknown0.heap_bytes_required(()),
1308 self.column_name.heap_bytes_required(()),
1309 ]
1310 .iter()
1311 .sum()
1312 }
1313}
1314
1315#[derive(Debug, PartialEq, Clone, Eq)]
1316pub struct TrackStrings {
1318 isrc: DeviceSQLString,
1320 lyricist: DeviceSQLString,
1322 unknown_string2: DeviceSQLString,
1325 unknown_string3: DeviceSQLString,
1327 unknown_string4: DeviceSQLString,
1329 message: DeviceSQLString,
1331 publish_track_information: DeviceSQLString,
1334 autoload_hotcues: DeviceSQLString,
1336 unknown_string5: DeviceSQLString,
1338 unknown_string6: DeviceSQLString,
1340 date_added: DeviceSQLString,
1342 release_date: DeviceSQLString,
1344 mix_name: DeviceSQLString,
1346 unknown_string7: DeviceSQLString,
1348 analyze_path: DeviceSQLString,
1350 analyze_date: DeviceSQLString,
1352 comment: DeviceSQLString,
1354 pub title: DeviceSQLString,
1356 unknown_string8: DeviceSQLString,
1358 filename: DeviceSQLString,
1360 pub file_path: DeviceSQLString,
1362}
1363
1364impl OffsetArrayItems<21> for TrackStrings {
1365 type Item = DeviceSQLString;
1366
1367 fn as_items(&self) -> [&Self::Item; 21] {
1368 [
1369 &self.isrc,
1370 &self.lyricist,
1371 &self.unknown_string2,
1372 &self.unknown_string3,
1373 &self.unknown_string4,
1374 &self.message,
1375 &self.publish_track_information,
1376 &self.autoload_hotcues,
1377 &self.unknown_string5,
1378 &self.unknown_string6,
1379 &self.date_added,
1380 &self.release_date,
1381 &self.mix_name,
1382 &self.unknown_string7,
1383 &self.analyze_path,
1384 &self.analyze_date,
1385 &self.comment,
1386 &self.title,
1387 &self.unknown_string8,
1388 &self.filename,
1389 &self.file_path,
1390 ]
1391 }
1392
1393 fn from_items(items: [Self::Item; 21]) -> Self {
1394 let [isrc, lyricist, unknown_string2, unknown_string3, unknown_string4, message, publish_track_information, autoload_hotcues, unknown_string5, unknown_string6, date_added, release_date, mix_name, unknown_string7, analyze_path, analyze_date, comment, title, unknown_string8, filename, file_path] =
1395 items;
1396 Self {
1397 isrc,
1398 lyricist,
1399 unknown_string2,
1400 unknown_string3,
1401 unknown_string4,
1402 message,
1403 publish_track_information,
1404 autoload_hotcues,
1405 unknown_string5,
1406 unknown_string6,
1407 date_added,
1408 release_date,
1409 mix_name,
1410 unknown_string7,
1411 analyze_path,
1412 analyze_date,
1413 comment,
1414 title,
1415 unknown_string8,
1416 filename,
1417 file_path,
1418 }
1419 }
1420}
1421
1422#[binrw]
1424#[derive(Debug, PartialEq, Eq, Clone)]
1425#[brw(little)]
1426pub struct Track {
1427 subtype: Subtype,
1429 index_shift: u16,
1432 bitmask: u32,
1435 sample_rate: u32,
1437 composer_id: ArtistId,
1439 file_size: u32,
1441 unknown2: u32,
1443 unknown3: u16,
1446 unknown4: u16,
1449 artwork_id: ArtworkId,
1451 key_id: KeyId,
1453 orig_artist_id: ArtistId,
1455 label_id: LabelId,
1457 remixer_id: ArtistId,
1459 bitrate: u32,
1461 track_number: u32,
1463 tempo: u32,
1465 genre_id: GenreId,
1467 album_id: AlbumId,
1469 pub artist_id: ArtistId,
1471 pub id: TrackId,
1473 disc_number: u16,
1475 play_count: u16,
1477 year: u16,
1479 sample_depth: u16,
1481 duration: u16,
1483 unknown5: u16,
1485 color: ColorIndex,
1487 rating: u8,
1489 file_type: FileType,
1491 #[brw(args(0x5C, subtype.get_offset_size(), ()))]
1493 pub offsets: OffsetArrayContainer<TrackStrings, 21>,
1494}
1495
1496impl PageHeapObject for Track {
1497 type Args<'a> = ();
1498 fn heap_bytes_required(&self, _: ()) -> u16 {
1499 [
1500 self.subtype.heap_bytes_required(()),
1501 self.index_shift.heap_bytes_required(()),
1502 self.bitmask.heap_bytes_required(()),
1503 self.sample_rate.heap_bytes_required(()),
1504 self.composer_id.heap_bytes_required(()),
1505 self.file_size.heap_bytes_required(()),
1506 self.unknown2.heap_bytes_required(()),
1507 self.unknown3.heap_bytes_required(()),
1508 self.unknown4.heap_bytes_required(()),
1509 self.artwork_id.heap_bytes_required(()),
1510 self.key_id.heap_bytes_required(()),
1511 self.orig_artist_id.heap_bytes_required(()),
1512 self.label_id.heap_bytes_required(()),
1513 self.remixer_id.heap_bytes_required(()),
1514 self.bitrate.heap_bytes_required(()),
1515 self.track_number.heap_bytes_required(()),
1516 self.tempo.heap_bytes_required(()),
1517 self.genre_id.heap_bytes_required(()),
1518 self.album_id.heap_bytes_required(()),
1519 self.artist_id.heap_bytes_required(()),
1520 self.id.heap_bytes_required(()),
1521 self.disc_number.heap_bytes_required(()),
1522 self.play_count.heap_bytes_required(()),
1523 self.year.heap_bytes_required(()),
1524 self.sample_depth.heap_bytes_required(()),
1525 self.duration.heap_bytes_required(()),
1526 self.unknown5.heap_bytes_required(()),
1527 self.color.heap_bytes_required(()),
1528 self.rating.heap_bytes_required(()),
1529 self.file_type.heap_bytes_required(()),
1530 self.offsets
1531 .heap_bytes_required(self.subtype.get_offset_size()),
1532 ]
1533 .iter()
1534 .sum()
1535 }
1536}
1537
1538#[binrw]
1540#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1541#[brw(little)]
1542pub enum MenuVisibility {
1543 #[brw(magic = 0x00u8)]
1545 Visible,
1546 #[brw(magic = 0x01u8)]
1548 Hidden,
1549 Unknown(u8),
1551}
1552
1553impl PageHeapObject for MenuVisibility {
1554 type Args<'a> = ();
1555 fn heap_bytes_required(&self, _: ()) -> u16 {
1556 (0u8).heap_bytes_required(())
1557 }
1558}
1559
1560#[binrw]
1562#[derive(Debug, PartialEq, Eq, Clone)]
1563#[brw(little)]
1564pub struct Menu {
1565 pub category_id: u16,
1568
1569 pub content_pointer: u16,
1571 pub unknown: u8,
1580
1581 pub visibility: MenuVisibility,
1587
1588 pub sort_order: u16,
1591}
1592
1593impl PageHeapObject for Menu {
1594 type Args<'a> = ();
1595 fn heap_bytes_required(&self, _: ()) -> u16 {
1596 [
1597 self.category_id.heap_bytes_required(()),
1598 self.content_pointer.heap_bytes_required(()),
1599 self.unknown.heap_bytes_required(()),
1600 self.visibility.heap_bytes_required(()),
1601 self.sort_order.heap_bytes_required(()),
1602 ]
1603 .iter()
1604 .sum()
1605 }
1606}
1607
1608#[binrw]
1610#[derive(Debug, PartialEq, Eq, Clone)]
1611#[brw(little)]
1612#[br(import(page_type: PlainPageType))]
1613#[allow(clippy::large_enum_variant)]
1618pub enum PlainRow {
1619 #[br(pre_assert(page_type == PlainPageType::Albums))]
1624 Album(#[bw(align_after = 4)] Album),
1625 #[br(pre_assert(page_type == PlainPageType::Artists))]
1630 Artist(#[bw(align_after = 4)] Artist),
1631 #[br(pre_assert(page_type == PlainPageType::Artwork))]
1633 Artwork(#[bw(align_after = 4)] Artwork),
1634 #[br(pre_assert(page_type == PlainPageType::Colors))]
1636 Color(#[bw(align_after = 4)] Color),
1637 #[br(pre_assert(page_type == PlainPageType::Genres))]
1639 Genre(#[bw(align_after = 4)] Genre),
1640 #[br(pre_assert(page_type == PlainPageType::HistoryPlaylists))]
1642 HistoryPlaylist(#[bw(align_after = 4)] HistoryPlaylist),
1643 #[br(pre_assert(page_type == PlainPageType::HistoryEntries))]
1645 HistoryEntry(#[bw(align_after = 4)] HistoryEntry),
1646 #[br(pre_assert(page_type == PlainPageType::Keys))]
1648 Key(#[bw(align_after = 4)] Key),
1649 #[br(pre_assert(page_type == PlainPageType::Labels))]
1651 Label(#[bw(align_after = 4)] Label),
1652 #[br(pre_assert(page_type == PlainPageType::PlaylistTree))]
1654 PlaylistTreeNode(#[bw(align_after = 4)] PlaylistTreeNode),
1655 #[br(pre_assert(page_type == PlainPageType::PlaylistEntries))]
1657 PlaylistEntry(#[bw(align_after = 4)] PlaylistEntry),
1658 #[br(pre_assert(page_type == PlainPageType::Columns))]
1660 ColumnEntry(#[bw(align_after = 4)] ColumnEntry),
1661 #[br(pre_assert(page_type == PlainPageType::Menu))]
1663 Menu(#[bw(align_after = 4)] Menu),
1664 #[br(pre_assert(page_type == PlainPageType::Tracks))]
1669 Track(#[bw(align_after = 4)] Track),
1670}
1671
1672impl PageHeapObject for PlainRow {
1673 type Args<'a> = ();
1674 fn heap_bytes_required(&self, _: ()) -> u16 {
1675 match self {
1676 PlainRow::Album(album) => album.heap_bytes_required(()),
1677 PlainRow::Artist(artist) => artist.heap_bytes_required(()),
1678 PlainRow::Artwork(artwork) => artwork.heap_bytes_required(()),
1679 PlainRow::Color(color) => color.heap_bytes_required(()),
1680 PlainRow::Genre(genre) => genre.heap_bytes_required(()),
1681 PlainRow::HistoryPlaylist(history_playlist) => history_playlist.heap_bytes_required(()),
1682 PlainRow::HistoryEntry(history_entry) => history_entry.heap_bytes_required(()),
1683 PlainRow::Key(key) => key.heap_bytes_required(()),
1684 PlainRow::Label(label) => label.heap_bytes_required(()),
1685 PlainRow::PlaylistTreeNode(playlist_tree_node) => {
1686 playlist_tree_node.heap_bytes_required(())
1687 }
1688 PlainRow::PlaylistEntry(playlist_entry) => playlist_entry.heap_bytes_required(()),
1689 PlainRow::ColumnEntry(column_entry) => column_entry.heap_bytes_required(()),
1690 PlainRow::Menu(menu) => menu.heap_bytes_required(()),
1691 PlainRow::Track(track) => track.heap_bytes_required(()),
1692 }
1693 }
1694}
1695
1696#[binrw]
1698#[derive(Debug, PartialEq, Eq, Clone)]
1699#[brw(little)]
1700#[br(import(page_type: PageType))]
1701#[allow(clippy::large_enum_variant)]
1706pub enum Row {
1707 #[br(pre_assert(matches!(page_type, PageType::Plain(_))))]
1709 Plain(
1711 #[br(args(match page_type {
1712 PageType::Plain(v) => v,
1713 _ => unreachable!("by above pre_assert")
1714 }))]
1715 PlainRow,
1716 ),
1717 #[br(pre_assert(matches!(page_type, PageType::Ext(_))))]
1718 Ext(
1720 #[br(args(match page_type {
1721 PageType::Ext(v) => v,
1722 _ => unreachable!("by above pre_assert")
1723 }))]
1724 ExtRow,
1725 ),
1726 #[br(pre_assert(matches!(page_type, PageType::Plain(PlainPageType::History) | PageType::Unknown(_))))]
1728 Unknown,
1729}
1730
1731impl PageHeapObject for Row {
1732 type Args<'a> = ();
1733 fn heap_bytes_required(&self, _: ()) -> u16 {
1734 match self {
1735 Row::Plain(plain_row) => plain_row.heap_bytes_required(()),
1736 Row::Ext(ext_row) => ext_row.heap_bytes_required(()),
1737 Row::Unknown => panic!("Unable to determine required bytes for unknown row type"),
1738 }
1739 }
1740}