1pub mod bitfields;
22pub mod ext;
23pub mod io;
24pub mod offset_array;
25pub mod string;
26
27use bitfields::{PackedRowCounts, PageFlags};
28use offset_array::{OffsetArrayContainer, OffsetArrayItems};
29
30#[cfg(test)]
31mod test_roundtrip;
32
33#[cfg(test)]
34mod test_modification;
35
36use std::collections::BTreeMap;
37use std::fmt;
38
39use crate::pdb::ext::{ExtPageType, ExtRow};
40use crate::pdb::offset_array::OffsetSize;
41use crate::pdb::string::DeviceSQLString;
42use crate::util::{parse_at_offsets, write_at_offsets, ColorIndex, FileType, TableIndex};
43use binrw::{binrw, BinRead, BinResult, BinWrite, Endian};
44use std::io::{Read, Seek, SeekFrom, Write};
45use thiserror::Error;
46
47#[derive(Debug, Error)]
49pub enum PdbError {
50 #[error("Invalid page index value: {0:#X}")]
52 InvalidPageIndex(u32),
53 #[error("Invalid index flags (expected max 3 bits): {0:#b}")]
55 InvalidIndexFlags(u8),
56}
57
58#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
61pub enum DatabaseType {
62 #[default] Plain,
65 Ext,
67}
68
69#[binrw]
71#[derive(Debug, PartialEq, Eq, Clone, Copy)]
72#[brw(little)]
73#[br(import(db_type: DatabaseType))]
74pub enum PageType {
75 #[br(pre_assert(db_type == DatabaseType::Plain))]
76 Plain(PlainPageType),
78 #[br(pre_assert(db_type == DatabaseType::Ext))]
79 Ext(ExtPageType),
81 Unknown(u32),
83}
84
85#[binrw]
87#[derive(Debug, PartialEq, Eq, Clone, Copy)]
88#[brw(little)]
89pub enum PlainPageType {
90 #[brw(magic = 0u32)]
92 Tracks,
93 #[brw(magic = 1u32)]
95 Genres,
96 #[brw(magic = 2u32)]
98 Artists,
99 #[brw(magic = 3u32)]
101 Albums,
102 #[brw(magic = 4u32)]
104 Labels,
105 #[brw(magic = 5u32)]
107 Keys,
108 #[brw(magic = 6u32)]
110 Colors,
111 #[brw(magic = 7u32)]
114 PlaylistTree,
115 #[brw(magic = 8u32)]
117 PlaylistEntries,
118 #[brw(magic = 11u32)]
121 HistoryPlaylists,
122 #[brw(magic = 12u32)]
124 HistoryEntries,
125 #[brw(magic = 13u32)]
127 Artwork,
128 #[brw(magic = 16u32)]
130 Columns,
131 #[brw(magic = 17u32)]
133 Menu,
134 #[brw(magic = 19u32)]
136 History,
137}
138
139pub trait RowVariant {
141 const PAGE_TYPE: PageType;
143
144 fn from_row(row: &Row) -> Option<&Self>;
146 fn from_row_mut(row: &mut Row) -> Option<&mut Self>;
148}
149
150#[binrw]
153#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash)]
154#[brw(little)]
155pub struct PageIndex(pub(crate) u32);
156
157impl TryFrom<u32> for PageIndex {
158 type Error = PdbError;
159
160 fn try_from(value: u32) -> Result<Self, Self::Error> {
161 if value < 0x03FF_FFFF {
162 Ok(Self(value))
163 } else {
164 Err(PdbError::InvalidPageIndex(value))
165 }
166 }
167}
168
169impl PageIndex {
170 #[must_use]
172 pub fn offset(&self, page_size: u32) -> u64 {
173 u64::from(self.0) * u64::from(page_size)
174 }
175}
176
177#[binrw]
180#[derive(Debug, PartialEq, Eq, Clone)]
181#[brw(little)]
182#[brw(import(db_type: DatabaseType))]
183pub struct Table {
184 #[br(args(db_type))]
186 pub page_type: PageType,
187 #[allow(dead_code)]
190 empty_candidate: u32,
191 pub first_page: PageIndex,
196 pub last_page: PageIndex,
198}
199
200#[binrw]
202#[derive(Debug, PartialEq, Eq, Clone)]
203#[brw(little)]
204#[brw(import(db_type: DatabaseType))]
205pub struct Header {
206 #[brw(magic = 0u32)]
208 pub page_size: u32,
212 pub num_tables: u32,
214 pub next_unused_page: PageIndex,
216 pub unknown: u32,
218 pub sequence: u32,
220 #[brw(magic = 0u32)]
222 #[br(count = num_tables, args {inner: (db_type,)})]
224 #[bw(args(db_type))]
225 pub tables: Vec<Table>,
226}
227
228impl Header {
229 #[must_use]
231 pub fn find_table(&self, page_type: PageType) -> Option<(TableIndex, &Table)> {
232 self.tables
233 .iter()
234 .enumerate()
235 .find(|(_, table)| table.page_type == page_type)
236 .map(|(i, table)| (TableIndex::from(i), table))
237 }
238
239 #[must_use]
241 pub fn find_table_mut(&mut self, page_type: PageType) -> Option<(TableIndex, &mut Table)> {
242 self.tables
243 .iter_mut()
244 .enumerate()
245 .find(|(_, table)| table.page_type == page_type)
246 .map(|(i, table)| (TableIndex::from(i), table))
247 }
248}
249
250#[binrw]
252#[derive(PartialEq, Eq, Clone, Copy)]
253#[brw(little)]
254pub struct IndexEntry(u32);
255
256impl IndexEntry {
257 pub const BINARY_SIZE: u32 = 4;
259}
260
261impl TryFrom<(PageIndex, u8)> for IndexEntry {
262 type Error = PdbError;
263
264 fn try_from(value: (PageIndex, u8)) -> Result<Self, Self::Error> {
265 let (page_index, index_flags) = value;
266 if index_flags & 0b111 != index_flags {
267 return Err(PdbError::InvalidIndexFlags(index_flags));
268 }
269 Ok(Self((page_index.0 << 3) | (index_flags & 0b111) as u32))
270 }
271}
272
273impl IndexEntry {
274 pub fn page_index(&self) -> Result<PageIndex, PdbError> {
277 PageIndex::try_from(self.0 >> 3)
278 }
279
280 #[must_use]
283 pub fn index_flags(&self) -> u8 {
284 (self.0 & 0b111) as u8
285 }
286
287 #[must_use]
289 pub fn is_empty(&self) -> bool {
290 self.0 == 0x1FFF_FFF8
291 }
292
293 #[must_use]
295 pub const fn empty() -> Self {
296 Self(0x1FFF_FFF8)
297 }
298}
299
300impl fmt::Debug for IndexEntry {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 if self.is_empty() {
303 f.debug_struct("IndexEntry")
304 .field("is_empty", &self.is_empty())
305 .finish()
306 } else {
307 f.debug_struct("IndexEntry")
308 .field("is_empty", &self.is_empty())
309 .field("page_index", &self.page_index().unwrap())
310 .field("index_flags", &self.index_flags())
311 .finish()
312 }
313 }
314}
315
316#[binrw]
318#[derive(Debug, PartialEq, Eq, Clone)]
319pub struct IndexPageHeader {
320 pub unknown_a: u16,
322 pub unknown_b: u16,
324 #[brw(magic = 0x03ecu16)]
326 pub next_offset: u16,
331 pub page_index: PageIndex,
333 pub next_page: PageIndex,
335 #[brw(magic = 0x0000_0000_03ff_ffffu64)]
337 pub num_entries: u16,
339 pub first_empty: u16,
347}
348
349impl IndexPageHeader {
350 pub const BINARY_SIZE: u32 = 28;
352}
353
354#[binrw]
356#[derive(Debug, PartialEq, Eq, Clone)]
357#[br(little)]
358#[bw(little, import { page_size: u32 })]
359pub struct IndexPageContent {
360 pub header: IndexPageHeader,
362
363 #[br(count = header.num_entries)]
365 pub entries: Vec<IndexEntry>,
366
367 #[br(temp)]
370 #[bw(calc = EmptyIndexEntries(
371 Self::total_entries(page_size) - usize::from(header.num_entries)
372 ))]
373 #[bw(pad_after = 20)]
374 _empty_entries: EmptyIndexEntries,
375}
376
377impl IndexPageContent {
378 fn total_entries(page_size: u32) -> usize {
379 let entries_space = page_size - PageHeader::BINARY_SIZE - IndexPageHeader::BINARY_SIZE - 20;
381 (entries_space / IndexEntry::BINARY_SIZE)
382 .try_into()
383 .unwrap()
384 }
385}
386
387struct EmptyIndexEntries(usize);
389
390impl BinRead for EmptyIndexEntries {
391 type Args<'a> = ();
392
393 fn read_options<Reader>(_: &mut Reader, _: Endian, (): Self::Args<'_>) -> BinResult<Self>
394 where
395 Reader: Read + Seek,
396 {
397 Ok(Self(0))
398 }
399}
400
401impl BinWrite for EmptyIndexEntries {
402 type Args<'a> = ();
403
404 fn write_options<Writer>(
405 &self,
406 writer: &mut Writer,
407 endian: Endian,
408 (): Self::Args<'_>,
409 ) -> BinResult<()>
410 where
411 Writer: Write + Seek,
412 {
413 const EMPTY: IndexEntry = IndexEntry::empty();
414 for _ in 0..self.0 {
415 EMPTY.write_options(writer, endian, ())?;
416 }
417 Ok(())
418 }
419}
420
421#[binrw]
425#[derive(Debug, PartialEq, Clone)]
426#[br(little, import { page_size: u32, header: &PageHeader })]
427#[bw(little, import { page_size: u32 })]
428pub enum PageContent {
429 #[br(pre_assert(!header.page_flags.is_index_page()))]
431 Data(
432 #[br(args { page_size, page_header: header })]
433 #[bw(args { page_size })]
434 DataPageContent,
435 ),
436 #[br(pre_assert(header.page_flags.is_index_page()))]
438 Index(#[bw(args { page_size })] IndexPageContent),
439}
440
441impl PageContent {
442 #[must_use]
444 pub fn into_data(self) -> Option<DataPageContent> {
445 match self {
446 PageContent::Data(data) => Some(data),
447 _ => None,
448 }
449 }
450
451 #[must_use]
453 pub fn as_data(&self) -> Option<&DataPageContent> {
454 match self {
455 PageContent::Data(data) => Some(data),
456 _ => None,
457 }
458 }
459
460 #[must_use]
462 pub fn as_data_mut(&mut self) -> Option<&mut DataPageContent> {
463 match self {
464 PageContent::Data(data) => Some(data),
465 _ => None,
466 }
467 }
468
469 #[must_use]
471 pub fn into_index(self) -> Option<IndexPageContent> {
472 match self {
473 PageContent::Index(index) => Some(index),
474 _ => None,
475 }
476 }
477
478 #[must_use]
480 pub fn as_index(&self) -> Option<&IndexPageContent> {
481 match self {
482 PageContent::Index(index) => Some(index),
483 _ => None,
484 }
485 }
486
487 #[must_use]
489 pub fn as_index_mut(&mut self) -> Option<&mut IndexPageContent> {
490 match self {
491 PageContent::Index(index) => Some(index),
492 _ => None,
493 }
494 }
495}
496
497#[binrw]
499#[derive(Debug, PartialEq, Eq, Clone)]
500#[brw(little)]
501#[br(import(db_type: DatabaseType))]
502pub struct PageHeader {
503 #[brw(magic = 0u32)]
505 pub page_index: PageIndex,
509 #[br(args(db_type))]
513 pub page_type: PageType,
514 pub next_page: PageIndex,
519 pub unknown1: u32,
522 pub unknown2: u32,
525 pub packed_row_counts: PackedRowCounts,
529 pub page_flags: PageFlags,
534 pub free_size: u16,
536 pub used_size: u16,
538}
539
540impl PageHeader {
541 pub const BINARY_SIZE: u32 = 0x20;
543}
544
545#[binrw]
551#[derive(Debug, PartialEq, Clone)]
552#[brw(little)]
553#[br(import(page_size: u32, db_type: DatabaseType))]
554#[bw(import(page_size: u32))]
555pub struct Page {
556 #[br(args(db_type))]
558 pub header: PageHeader,
559
560 #[br(args { page_size, header: &header })]
562 #[bw(args { page_size })]
563 pub content: PageContent,
564}
565
566impl Page {
567 pub fn allocate_row<'a>(&'a mut self, bytes: u16) -> Option<impl FnOnce(Row) + 'a> {
574 match self.content {
575 PageContent::Index(_) => None,
576 PageContent::Data(ref mut dpc) => {
577 let bytes = bytes.next_multiple_of(4);
579
580 let required_bytes = bytes + RowGroup::HEADER_SIZE + RowGroup::OFFSET_SIZE;
582 if self.header.free_size < required_bytes {
583 return None;
584 }
585
586 let offset = self.header.used_size;
587 self.header.used_size += bytes;
588 self.header.free_size -= bytes;
589 let row_counts = &mut self.header.packed_row_counts;
590 row_counts.increment_num_rows();
591 let (row_group_index, row_subindex) = row_counts.last_row_index().unwrap();
592
593 if dpc.row_groups.get(row_group_index as usize).is_none() {
594 dpc.row_groups.push(RowGroup::empty());
595 self.header.free_size -= RowGroup::HEADER_SIZE;
596 }
597
598 let row_group = dpc.row_groups.get_mut(row_group_index as usize).unwrap();
599 row_group.insert_offset(row_subindex, offset);
600 self.header.free_size -= RowGroup::OFFSET_SIZE;
601
602 let rows = &mut dpc.rows;
603
604 Some(move |row: Row| {
605 let prev_entry = rows.insert(offset, row);
606 if let Some(prev_entry) = prev_entry {
607 panic!(
608 "Offset {} was already occupied by row {:?}",
609 offset, prev_entry
610 );
611 }
612 row_group.mark_offset_present(row_subindex);
613 row_counts.increment_num_rows_valid();
614 })
615 }
616 }
617 }
618}
619
620#[binrw]
622#[derive(Debug, PartialEq, Eq, Clone)]
623pub struct DataPageHeader {
624 pub unknown5: u16,
630 pub unknown_not_num_rows_large: u16,
633 pub unknown6: u16,
635 pub unknown7: u16,
641}
642
643impl DataPageHeader {
644 pub const BINARY_SIZE: u32 = 0x8;
646}
647
648#[binrw]
650#[derive(Debug, PartialEq, Eq, Clone)]
651#[br(little, import { page_size: u32, page_header: &PageHeader })]
652#[bw(little, import { page_size: u32 })]
653pub struct DataPageContent {
654 pub header: DataPageHeader,
656
657 #[brw(seek_before(SeekFrom::Current(Self::page_heap_size(page_size) as i64)), restore_position)]
661 #[br(args {count: page_header.packed_row_counts.num_row_groups().into()})]
662 pub row_groups: Vec<RowGroup>,
663
664 #[br(args(page_header.page_type))]
668 #[br(parse_with = parse_at_offsets(row_groups.iter().flat_map(RowGroup::present_rows_offsets)))]
669 #[bw(write_with = write_at_offsets)]
671 #[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()))]
672 pub rows: BTreeMap<u16, Row>,
673
674 #[br(temp)]
680 #[bw(calc = ())]
681 #[brw(seek_before = SeekFrom::Current(Self::page_heap_size(page_size) as i64))]
682 _dummy: (),
683}
684
685impl DataPageContent {
686 fn page_heap_size(page_size: u32) -> u32 {
687 page_size - PageHeader::BINARY_SIZE - DataPageHeader::BINARY_SIZE
688 }
689}
690
691#[allow(dead_code)]
693trait PageHeapObject {
694 type Args<'a>;
695
696 fn heap_bytes_required(&self, args: Self::Args<'_>) -> u16;
698}
699
700impl PageHeapObject for u8 {
701 type Args<'a> = ();
702 fn heap_bytes_required(&self, _: ()) -> u16 {
703 std::mem::size_of::<u8>() as u16
704 }
705}
706
707impl PageHeapObject for u16 {
708 type Args<'a> = ();
709 fn heap_bytes_required(&self, _: ()) -> u16 {
710 std::mem::size_of::<u16>() as u16
711 }
712}
713
714impl PageHeapObject for u32 {
715 type Args<'a> = ();
716 fn heap_bytes_required(&self, _: ()) -> u16 {
717 std::mem::size_of::<u32>() as u16
718 }
719}
720
721impl PageHeapObject for ColorIndex {
722 type Args<'a> = ();
723 fn heap_bytes_required(&self, _: ()) -> u16 {
724 (0u8).heap_bytes_required(())
725 }
726}
727
728impl PageHeapObject for FileType {
729 type Args<'a> = ();
730 fn heap_bytes_required(&self, _: ()) -> u16 {
731 (0u16).heap_bytes_required(())
732 }
733}
734
735#[binrw]
739#[derive(Debug, Clone, Eq)]
740pub struct RowGroup {
741 #[brw(seek_before = SeekFrom::Current(-i64::from(Self::BINARY_SIZE)))]
751 #[bw(write_with = Self::write_row_offsets, args(*row_presence_flags))]
752 pub row_offsets: [u16; Self::MAX_ROW_COUNT],
753 pub row_presence_flags: u16,
755 pub unknown: u16,
763
764 #[br(temp)]
766 #[bw(calc = ())]
767 #[brw(seek_before = SeekFrom::Current(-i64::from(Self::BINARY_SIZE)))]
768 _dummy: (),
769}
770
771impl RowGroup {
772 pub const MAX_ROW_COUNT: usize = 16;
774 const HEADER_SIZE: u16 = 4; const OFFSET_SIZE: u16 = 2;
776 const BINARY_SIZE: u16 = (Self::MAX_ROW_COUNT as u16) * Self::OFFSET_SIZE + Self::HEADER_SIZE;
777
778 fn empty() -> Self {
779 Self {
780 row_offsets: [0; Self::MAX_ROW_COUNT],
781 row_presence_flags: 0,
782 unknown: 0,
783 }
784 }
785
786 fn present_rows_offsets(&self) -> impl Iterator<Item = u16> + '_ {
787 self.row_offsets
788 .iter()
789 .rev()
790 .enumerate()
791 .filter_map(move |(i, row_offset)| {
792 (self.row_presence_flags & (1 << i) != 0).then_some(*row_offset)
793 })
794 }
795
796 fn write_row_offsets<Writer>(
797 row_offsets: &[u16; 16],
798 writer: &mut Writer,
799 endian: Endian,
800 (row_presence_flags,): (u16,),
801 ) -> BinResult<()>
802 where
803 Writer: Write + Seek,
804 {
805 const U16_SIZE: u32 = std::mem::size_of::<u16>() as u32;
806 let skip = row_presence_flags.leading_zeros();
807 writer.seek(SeekFrom::Current((skip * U16_SIZE).into()))?;
808 for offset in row_offsets.iter().skip(skip.try_into().unwrap()) {
809 offset.write_options(writer, endian, ())?;
810 }
811 Ok(())
812 }
813
814 fn insert_offset(&mut self, subindex: u16, offset: u16) {
817 self.row_offsets[(Self::MAX_ROW_COUNT as u16 - 1 - subindex) as usize] = offset;
818 }
819
820 fn mark_offset_present(&mut self, subindex: u16) {
822 self.row_presence_flags |= 1 << subindex;
823 }
824}
825
826impl Default for RowGroup {
827 fn default() -> Self {
828 Self::empty()
829 }
830}
831
832impl PartialEq for RowGroup {
833 fn eq(&self, other: &Self) -> bool {
834 self.unknown == other.unknown
835 && self.present_rows_offsets().eq(other.present_rows_offsets())
836 }
837}
838
839#[binrw]
841#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
842#[brw(little)]
843pub struct Subtype(pub u16);
844
845impl PageHeapObject for Subtype {
846 type Args<'a> = ();
847 fn heap_bytes_required(&self, _: ()) -> u16 {
848 self.0.heap_bytes_required(())
849 }
850}
851
852impl Subtype {
853 #[must_use]
858 pub fn get_offset_size(&self) -> OffsetSize {
859 if self.0 & 0x04 == 0 {
860 OffsetSize::U8
861 } else {
862 OffsetSize::U16
863 }
864 }
865}
866
867#[binrw]
869#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
870#[brw(little)]
871pub struct TrackId(pub u32);
872
873impl PageHeapObject for TrackId {
874 type Args<'a> = ();
875 fn heap_bytes_required(&self, _: ()) -> u16 {
876 self.0.heap_bytes_required(())
877 }
878}
879
880#[binrw]
882#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
883#[brw(little)]
884pub struct ArtworkId(pub u32);
885
886impl PageHeapObject for ArtworkId {
887 type Args<'a> = ();
888 fn heap_bytes_required(&self, _: ()) -> u16 {
889 self.0.heap_bytes_required(())
890 }
891}
892
893#[binrw]
895#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
896#[brw(little)]
897pub struct AlbumId(pub u32);
898
899impl PageHeapObject for AlbumId {
900 type Args<'a> = ();
901 fn heap_bytes_required(&self, _: ()) -> u16 {
902 self.0.heap_bytes_required(())
903 }
904}
905
906#[binrw]
908#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
909#[brw(little)]
910pub struct ArtistId(pub u32);
911
912impl PageHeapObject for ArtistId {
913 type Args<'a> = ();
914 fn heap_bytes_required(&self, _: ()) -> u16 {
915 self.0.heap_bytes_required(())
916 }
917}
918
919#[binrw]
921#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
922#[brw(little)]
923pub struct GenreId(pub u32);
924
925impl PageHeapObject for GenreId {
926 type Args<'a> = ();
927 fn heap_bytes_required(&self, _: ()) -> u16 {
928 self.0.heap_bytes_required(())
929 }
930}
931
932#[binrw]
934#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
935#[brw(little)]
936pub struct KeyId(pub u32);
937
938impl PageHeapObject for KeyId {
939 type Args<'a> = ();
940 fn heap_bytes_required(&self, _: ()) -> u16 {
941 self.0.heap_bytes_required(())
942 }
943}
944
945#[binrw]
947#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
948#[brw(little)]
949pub struct LabelId(pub u32);
950
951impl PageHeapObject for LabelId {
952 type Args<'a> = ();
953 fn heap_bytes_required(&self, _: ()) -> u16 {
954 self.0.heap_bytes_required(())
955 }
956}
957
958#[binrw]
960#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
961#[brw(little)]
962pub struct PlaylistTreeNodeId(pub u32);
963
964impl PageHeapObject for PlaylistTreeNodeId {
965 type Args<'a> = ();
966 fn heap_bytes_required(&self, _: ()) -> u16 {
967 self.0.heap_bytes_required(())
968 }
969}
970
971#[binrw]
973#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
974#[brw(little)]
975pub struct HistoryPlaylistId(pub u32);
976
977impl PageHeapObject for HistoryPlaylistId {
978 type Args<'a> = ();
979 fn heap_bytes_required(&self, _: ()) -> u16 {
980 self.0.heap_bytes_required(())
981 }
982}
983
984#[derive(Debug, PartialEq, Clone, Eq)]
985pub struct TrailingName {
987 pub name: DeviceSQLString,
989}
990
991impl OffsetArrayItems<1> for TrailingName {
992 type Item = DeviceSQLString;
993
994 fn as_items(&self) -> [&Self::Item; 1] {
995 [&self.name]
996 }
997
998 fn from_items(items: [Self::Item; 1]) -> Self {
999 let [name] = items;
1000 Self { name }
1001 }
1002}
1003
1004#[binrw]
1006#[derive(Debug, PartialEq, Eq, Clone)]
1007#[brw(little)]
1008pub struct Album {
1009 subtype: Subtype,
1011 index_shift: u16,
1014 unknown2: u32,
1016 artist_id: ArtistId,
1018 id: AlbumId,
1020 unknown3: u32,
1022 #[brw(args(20, subtype.get_offset_size(), ()))]
1024 offsets: OffsetArrayContainer<TrailingName, 1>,
1025}
1026
1027impl RowVariant for Album {
1028 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Albums);
1029
1030 fn from_row(row: &Row) -> Option<&Self> {
1031 match row {
1032 Row::Plain(PlainRow::Album(row)) => Some(row),
1033 _ => None,
1034 }
1035 }
1036 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1037 match row {
1038 Row::Plain(PlainRow::Album(row)) => Some(row),
1039 _ => None,
1040 }
1041 }
1042}
1043
1044impl PageHeapObject for Album {
1045 type Args<'a> = ();
1046 fn heap_bytes_required(&self, _: ()) -> u16 {
1047 [
1048 self.subtype.heap_bytes_required(()),
1049 self.index_shift.heap_bytes_required(()),
1050 self.unknown2.heap_bytes_required(()),
1051 self.artist_id.heap_bytes_required(()),
1052 self.id.heap_bytes_required(()),
1053 self.unknown3.heap_bytes_required(()),
1054 self.offsets
1055 .heap_bytes_required(self.subtype.get_offset_size()),
1056 ]
1057 .iter()
1058 .sum()
1059 }
1060}
1061
1062#[binrw]
1064#[derive(Debug, PartialEq, Eq, Clone)]
1065#[brw(little)]
1066pub struct Artist {
1067 subtype: Subtype,
1069 index_shift: u16,
1072 pub id: ArtistId,
1074 #[brw(args(8, subtype.get_offset_size(), ()))]
1076 pub offsets: OffsetArrayContainer<TrailingName, 1>,
1077}
1078
1079impl RowVariant for Artist {
1080 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Artists);
1081
1082 fn from_row(row: &Row) -> Option<&Self> {
1083 match row {
1084 Row::Plain(PlainRow::Artist(row)) => Some(row),
1085 _ => None,
1086 }
1087 }
1088 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1089 match row {
1090 Row::Plain(PlainRow::Artist(row)) => Some(row),
1091 _ => None,
1092 }
1093 }
1094}
1095
1096impl PageHeapObject for Artist {
1097 type Args<'a> = ();
1098 fn heap_bytes_required(&self, _: ()) -> u16 {
1099 [
1100 self.subtype.heap_bytes_required(()),
1101 self.index_shift.heap_bytes_required(()),
1102 self.id.heap_bytes_required(()),
1103 self.offsets
1104 .heap_bytes_required(self.subtype.get_offset_size()),
1105 ]
1106 .iter()
1107 .sum()
1108 }
1109}
1110
1111#[binrw]
1113#[derive(Debug, PartialEq, Eq, Clone)]
1114#[brw(little)]
1115pub struct Artwork {
1116 id: ArtworkId,
1118 path: DeviceSQLString,
1120}
1121
1122impl RowVariant for Artwork {
1123 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Artwork);
1124
1125 fn from_row(row: &Row) -> Option<&Self> {
1126 match row {
1127 Row::Plain(PlainRow::Artwork(row)) => Some(row),
1128 _ => None,
1129 }
1130 }
1131 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1132 match row {
1133 Row::Plain(PlainRow::Artwork(row)) => Some(row),
1134 _ => None,
1135 }
1136 }
1137}
1138
1139impl PageHeapObject for Artwork {
1140 type Args<'a> = ();
1141 fn heap_bytes_required(&self, _: ()) -> u16 {
1142 [
1143 self.id.heap_bytes_required(()),
1144 self.path.heap_bytes_required(()),
1145 ]
1146 .iter()
1147 .sum()
1148 }
1149}
1150
1151#[binrw]
1153#[derive(Debug, PartialEq, Eq, Clone)]
1154#[brw(little)]
1155pub struct Color {
1156 unknown1: u32,
1158 unknown2: u8,
1160 color: ColorIndex,
1162 unknown3: u16,
1164 name: DeviceSQLString,
1166}
1167
1168impl RowVariant for Color {
1169 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Colors);
1170
1171 fn from_row(row: &Row) -> Option<&Self> {
1172 match row {
1173 Row::Plain(PlainRow::Color(row)) => Some(row),
1174 _ => None,
1175 }
1176 }
1177 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1178 match row {
1179 Row::Plain(PlainRow::Color(row)) => Some(row),
1180 _ => None,
1181 }
1182 }
1183}
1184
1185impl PageHeapObject for Color {
1186 type Args<'a> = ();
1187 fn heap_bytes_required(&self, _: ()) -> u16 {
1188 [
1189 self.unknown1.heap_bytes_required(()),
1190 self.unknown2.heap_bytes_required(()),
1191 self.color.heap_bytes_required(()),
1192 self.unknown3.heap_bytes_required(()),
1193 self.name.heap_bytes_required(()),
1194 ]
1195 .iter()
1196 .sum()
1197 }
1198}
1199
1200#[binrw]
1202#[derive(Debug, PartialEq, Eq, Clone)]
1203#[brw(little)]
1204pub struct Genre {
1205 id: GenreId,
1207 name: DeviceSQLString,
1209}
1210
1211impl RowVariant for Genre {
1212 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Genres);
1213
1214 fn from_row(row: &Row) -> Option<&Self> {
1215 match row {
1216 Row::Plain(PlainRow::Genre(row)) => Some(row),
1217 _ => None,
1218 }
1219 }
1220 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1221 match row {
1222 Row::Plain(PlainRow::Genre(row)) => Some(row),
1223 _ => None,
1224 }
1225 }
1226}
1227
1228impl PageHeapObject for Genre {
1229 type Args<'a> = ();
1230 fn heap_bytes_required(&self, _: ()) -> u16 {
1231 [
1232 self.id.heap_bytes_required(()),
1233 self.name.heap_bytes_required(()),
1234 ]
1235 .iter()
1236 .sum()
1237 }
1238}
1239
1240#[binrw]
1242#[derive(Debug, PartialEq, Eq, Clone)]
1243#[brw(little)]
1244pub struct HistoryPlaylist {
1245 id: HistoryPlaylistId,
1247 name: DeviceSQLString,
1249}
1250
1251impl RowVariant for HistoryPlaylist {
1252 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::HistoryPlaylists);
1253
1254 fn from_row(row: &Row) -> Option<&Self> {
1255 match row {
1256 Row::Plain(PlainRow::HistoryPlaylist(row)) => Some(row),
1257 _ => None,
1258 }
1259 }
1260 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1261 match row {
1262 Row::Plain(PlainRow::HistoryPlaylist(row)) => Some(row),
1263 _ => None,
1264 }
1265 }
1266}
1267
1268impl PageHeapObject for HistoryPlaylist {
1269 type Args<'a> = ();
1270 fn heap_bytes_required(&self, _: ()) -> u16 {
1271 [
1272 self.id.heap_bytes_required(()),
1273 self.name.heap_bytes_required(()),
1274 ]
1275 .iter()
1276 .sum()
1277 }
1278}
1279
1280#[binrw]
1282#[derive(Debug, PartialEq, Eq, Clone)]
1283#[brw(little)]
1284pub struct HistoryEntry {
1285 track_id: TrackId,
1287 playlist_id: HistoryPlaylistId,
1289 entry_index: u32,
1291}
1292
1293impl RowVariant for HistoryEntry {
1294 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::HistoryEntries);
1295
1296 fn from_row(row: &Row) -> Option<&Self> {
1297 match row {
1298 Row::Plain(PlainRow::HistoryEntry(row)) => Some(row),
1299 _ => None,
1300 }
1301 }
1302 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1303 match row {
1304 Row::Plain(PlainRow::HistoryEntry(row)) => Some(row),
1305 _ => None,
1306 }
1307 }
1308}
1309
1310impl PageHeapObject for HistoryEntry {
1311 type Args<'a> = ();
1312 fn heap_bytes_required(&self, _: ()) -> u16 {
1313 [
1314 self.track_id.heap_bytes_required(()),
1315 self.playlist_id.heap_bytes_required(()),
1316 self.entry_index.heap_bytes_required(()),
1317 ]
1318 .iter()
1319 .sum()
1320 }
1321}
1322
1323#[binrw]
1329#[derive(Debug, PartialEq, Eq, Clone)]
1330#[brw(little)]
1331pub struct History {
1332 subtype: Subtype,
1334 index_shift: u16,
1336 num_tracks: u32,
1338 #[brw(magic = 0u32)]
1340 date: DeviceSQLString,
1342 #[brw(magic = 0x1E19u16)]
1344 version: DeviceSQLString,
1347 label: DeviceSQLString,
1349}
1350
1351impl PageHeapObject for History {
1352 type Args<'a> = ();
1353 fn heap_bytes_required(&self, _: ()) -> u16 {
1354 [
1355 self.subtype.heap_bytes_required(()),
1356 self.index_shift.heap_bytes_required(()),
1357 self.num_tracks.heap_bytes_required(()),
1358 (0u32).heap_bytes_required(()),
1359 self.date.heap_bytes_required(()),
1360 (0u16).heap_bytes_required(()),
1361 self.version.heap_bytes_required(()),
1362 self.label.heap_bytes_required(()),
1363 ]
1364 .iter()
1365 .sum()
1366 }
1367}
1368
1369impl RowVariant for History {
1370 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::History);
1371
1372 fn from_row(row: &Row) -> Option<&Self> {
1373 match row {
1374 Row::Plain(PlainRow::History(row)) => Some(row),
1375 _ => None,
1376 }
1377 }
1378 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1379 match row {
1380 Row::Plain(PlainRow::History(row)) => Some(row),
1381 _ => None,
1382 }
1383 }
1384}
1385
1386#[binrw]
1388#[derive(Debug, PartialEq, Eq, Clone)]
1389#[brw(little)]
1390pub struct Key {
1391 id: KeyId,
1393 id2: u32,
1395 name: DeviceSQLString,
1397}
1398
1399impl RowVariant for Key {
1400 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Keys);
1401
1402 fn from_row(row: &Row) -> Option<&Self> {
1403 match row {
1404 Row::Plain(PlainRow::Key(row)) => Some(row),
1405 _ => None,
1406 }
1407 }
1408 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1409 match row {
1410 Row::Plain(PlainRow::Key(row)) => Some(row),
1411 _ => None,
1412 }
1413 }
1414}
1415
1416impl PageHeapObject for Key {
1417 type Args<'a> = ();
1418 fn heap_bytes_required(&self, _: ()) -> u16 {
1419 [
1420 self.id.heap_bytes_required(()),
1421 self.id2.heap_bytes_required(()),
1422 self.name.heap_bytes_required(()),
1423 ]
1424 .iter()
1425 .sum()
1426 }
1427}
1428
1429#[binrw]
1431#[derive(Debug, PartialEq, Eq, Clone)]
1432#[brw(little)]
1433pub struct Label {
1434 id: LabelId,
1436 name: DeviceSQLString,
1438}
1439
1440impl RowVariant for Label {
1441 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Labels);
1442
1443 fn from_row(row: &Row) -> Option<&Self> {
1444 match row {
1445 Row::Plain(PlainRow::Label(row)) => Some(row),
1446 _ => None,
1447 }
1448 }
1449 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1450 match row {
1451 Row::Plain(PlainRow::Label(row)) => Some(row),
1452 _ => None,
1453 }
1454 }
1455}
1456
1457impl PageHeapObject for Label {
1458 type Args<'a> = ();
1459 fn heap_bytes_required(&self, _: ()) -> u16 {
1460 [
1461 self.id.heap_bytes_required(()),
1462 self.name.heap_bytes_required(()),
1463 ]
1464 .iter()
1465 .sum()
1466 }
1467}
1468
1469#[binrw]
1471#[derive(Debug, PartialEq, Eq, Clone)]
1472#[brw(little)]
1473pub struct PlaylistTreeNode {
1474 pub parent_id: PlaylistTreeNodeId,
1476 unknown: u32,
1478 sort_order: u32,
1480 pub id: PlaylistTreeNodeId,
1482 node_is_folder: u32,
1484 pub name: DeviceSQLString,
1486}
1487
1488impl RowVariant for PlaylistTreeNode {
1489 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::PlaylistTree);
1490
1491 fn from_row(row: &Row) -> Option<&Self> {
1492 match row {
1493 Row::Plain(PlainRow::PlaylistTreeNode(row)) => Some(row),
1494 _ => None,
1495 }
1496 }
1497 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1498 match row {
1499 Row::Plain(PlainRow::PlaylistTreeNode(row)) => Some(row),
1500 _ => None,
1501 }
1502 }
1503}
1504
1505impl PlaylistTreeNode {
1506 #[must_use]
1508 pub fn is_folder(&self) -> bool {
1509 self.node_is_folder > 0
1510 }
1511}
1512
1513impl PageHeapObject for PlaylistTreeNode {
1514 type Args<'a> = ();
1515 fn heap_bytes_required(&self, _: ()) -> u16 {
1516 [
1517 self.parent_id.heap_bytes_required(()),
1518 self.unknown.heap_bytes_required(()),
1519 self.sort_order.heap_bytes_required(()),
1520 self.id.heap_bytes_required(()),
1521 self.node_is_folder.heap_bytes_required(()),
1522 self.name.heap_bytes_required(()),
1523 ]
1524 .iter()
1525 .sum()
1526 }
1527}
1528
1529#[binrw]
1531#[derive(Debug, PartialEq, Eq, Clone)]
1532#[brw(little)]
1533pub struct PlaylistEntry {
1534 pub entry_index: u32,
1536 pub track_id: TrackId,
1538 pub playlist_id: PlaylistTreeNodeId,
1540}
1541
1542impl RowVariant for PlaylistEntry {
1543 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::PlaylistEntries);
1544
1545 fn from_row(row: &Row) -> Option<&Self> {
1546 match row {
1547 Row::Plain(PlainRow::PlaylistEntry(row)) => Some(row),
1548 _ => None,
1549 }
1550 }
1551 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1552 match row {
1553 Row::Plain(PlainRow::PlaylistEntry(row)) => Some(row),
1554 _ => None,
1555 }
1556 }
1557}
1558
1559impl PageHeapObject for PlaylistEntry {
1560 type Args<'a> = ();
1561 fn heap_bytes_required(&self, _: ()) -> u16 {
1562 [
1563 self.entry_index.heap_bytes_required(()),
1564 self.track_id.heap_bytes_required(()),
1565 self.playlist_id.heap_bytes_required(()),
1566 ]
1567 .iter()
1568 .sum()
1569 }
1570}
1571
1572#[binrw]
1575#[derive(Debug, PartialEq, Eq, Clone)]
1576#[brw(little)]
1577pub struct ColumnEntry {
1578 id: u16,
1583 unknown0: u16,
1586 pub column_name: DeviceSQLString,
1593}
1594
1595impl RowVariant for ColumnEntry {
1596 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Columns);
1597
1598 fn from_row(row: &Row) -> Option<&Self> {
1599 match row {
1600 Row::Plain(PlainRow::ColumnEntry(row)) => Some(row),
1601 _ => None,
1602 }
1603 }
1604 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1605 match row {
1606 Row::Plain(PlainRow::ColumnEntry(row)) => Some(row),
1607 _ => None,
1608 }
1609 }
1610}
1611
1612impl PageHeapObject for ColumnEntry {
1613 type Args<'a> = ();
1614 fn heap_bytes_required(&self, _: ()) -> u16 {
1615 [
1616 self.id.heap_bytes_required(()),
1617 self.unknown0.heap_bytes_required(()),
1618 self.column_name.heap_bytes_required(()),
1619 ]
1620 .iter()
1621 .sum()
1622 }
1623}
1624
1625#[derive(Debug, PartialEq, Clone, Eq)]
1626pub struct TrackStrings {
1628 isrc: DeviceSQLString,
1630 lyricist: DeviceSQLString,
1632 unknown_string2: DeviceSQLString,
1635 unknown_string3: DeviceSQLString,
1637 unknown_string4: DeviceSQLString,
1639 message: DeviceSQLString,
1641 publish_track_information: DeviceSQLString,
1644 autoload_hotcues: DeviceSQLString,
1646 unknown_string5: DeviceSQLString,
1648 unknown_string6: DeviceSQLString,
1650 date_added: DeviceSQLString,
1652 release_date: DeviceSQLString,
1654 mix_name: DeviceSQLString,
1656 unknown_string7: DeviceSQLString,
1658 analyze_path: DeviceSQLString,
1660 analyze_date: DeviceSQLString,
1662 comment: DeviceSQLString,
1664 pub title: DeviceSQLString,
1666 unknown_string8: DeviceSQLString,
1668 filename: DeviceSQLString,
1670 pub file_path: DeviceSQLString,
1672}
1673
1674impl OffsetArrayItems<21> for TrackStrings {
1675 type Item = DeviceSQLString;
1676
1677 fn as_items(&self) -> [&Self::Item; 21] {
1678 [
1679 &self.isrc,
1680 &self.lyricist,
1681 &self.unknown_string2,
1682 &self.unknown_string3,
1683 &self.unknown_string4,
1684 &self.message,
1685 &self.publish_track_information,
1686 &self.autoload_hotcues,
1687 &self.unknown_string5,
1688 &self.unknown_string6,
1689 &self.date_added,
1690 &self.release_date,
1691 &self.mix_name,
1692 &self.unknown_string7,
1693 &self.analyze_path,
1694 &self.analyze_date,
1695 &self.comment,
1696 &self.title,
1697 &self.unknown_string8,
1698 &self.filename,
1699 &self.file_path,
1700 ]
1701 }
1702
1703 fn from_items(items: [Self::Item; 21]) -> Self {
1704 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] =
1705 items;
1706 Self {
1707 isrc,
1708 lyricist,
1709 unknown_string2,
1710 unknown_string3,
1711 unknown_string4,
1712 message,
1713 publish_track_information,
1714 autoload_hotcues,
1715 unknown_string5,
1716 unknown_string6,
1717 date_added,
1718 release_date,
1719 mix_name,
1720 unknown_string7,
1721 analyze_path,
1722 analyze_date,
1723 comment,
1724 title,
1725 unknown_string8,
1726 filename,
1727 file_path,
1728 }
1729 }
1730}
1731
1732#[binrw]
1734#[derive(Debug, PartialEq, Eq, Clone)]
1735#[brw(little)]
1736pub struct Track {
1737 subtype: Subtype,
1739 index_shift: u16,
1742 bitmask: u32,
1745 sample_rate: u32,
1747 composer_id: ArtistId,
1749 file_size: u32,
1751 unknown2: u32,
1753 unknown3: u16,
1756 unknown4: u16,
1759 artwork_id: ArtworkId,
1761 key_id: KeyId,
1763 orig_artist_id: ArtistId,
1765 label_id: LabelId,
1767 remixer_id: ArtistId,
1769 bitrate: u32,
1771 track_number: u32,
1773 tempo: u32,
1775 genre_id: GenreId,
1777 album_id: AlbumId,
1779 pub artist_id: ArtistId,
1781 pub id: TrackId,
1783 disc_number: u16,
1785 play_count: u16,
1787 year: u16,
1789 sample_depth: u16,
1791 duration: u16,
1793 unknown5: u16,
1795 color: ColorIndex,
1797 pub rating: u8,
1799 file_type: FileType,
1801 #[brw(args(0x5C, subtype.get_offset_size(), ()))]
1803 pub offsets: OffsetArrayContainer<TrackStrings, 21>,
1804}
1805
1806impl RowVariant for Track {
1807 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Tracks);
1808
1809 fn from_row(row: &Row) -> Option<&Self> {
1810 match row {
1811 Row::Plain(PlainRow::Track(track)) => Some(track),
1812 _ => None,
1813 }
1814 }
1815 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1816 match row {
1817 Row::Plain(PlainRow::Track(track)) => Some(track),
1818 _ => None,
1819 }
1820 }
1821}
1822
1823impl PageHeapObject for Track {
1824 type Args<'a> = ();
1825 fn heap_bytes_required(&self, _: ()) -> u16 {
1826 [
1827 self.subtype.heap_bytes_required(()),
1828 self.index_shift.heap_bytes_required(()),
1829 self.bitmask.heap_bytes_required(()),
1830 self.sample_rate.heap_bytes_required(()),
1831 self.composer_id.heap_bytes_required(()),
1832 self.file_size.heap_bytes_required(()),
1833 self.unknown2.heap_bytes_required(()),
1834 self.unknown3.heap_bytes_required(()),
1835 self.unknown4.heap_bytes_required(()),
1836 self.artwork_id.heap_bytes_required(()),
1837 self.key_id.heap_bytes_required(()),
1838 self.orig_artist_id.heap_bytes_required(()),
1839 self.label_id.heap_bytes_required(()),
1840 self.remixer_id.heap_bytes_required(()),
1841 self.bitrate.heap_bytes_required(()),
1842 self.track_number.heap_bytes_required(()),
1843 self.tempo.heap_bytes_required(()),
1844 self.genre_id.heap_bytes_required(()),
1845 self.album_id.heap_bytes_required(()),
1846 self.artist_id.heap_bytes_required(()),
1847 self.id.heap_bytes_required(()),
1848 self.disc_number.heap_bytes_required(()),
1849 self.play_count.heap_bytes_required(()),
1850 self.year.heap_bytes_required(()),
1851 self.sample_depth.heap_bytes_required(()),
1852 self.duration.heap_bytes_required(()),
1853 self.unknown5.heap_bytes_required(()),
1854 self.color.heap_bytes_required(()),
1855 self.rating.heap_bytes_required(()),
1856 self.file_type.heap_bytes_required(()),
1857 self.offsets
1858 .heap_bytes_required(self.subtype.get_offset_size()),
1859 ]
1860 .iter()
1861 .sum()
1862 }
1863}
1864
1865#[binrw]
1867#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1868#[brw(little)]
1869pub enum MenuVisibility {
1870 #[brw(magic = 0x00u8)]
1872 Visible,
1873 #[brw(magic = 0x01u8)]
1875 Hidden,
1876 Unknown(u8),
1878}
1879
1880impl PageHeapObject for MenuVisibility {
1881 type Args<'a> = ();
1882 fn heap_bytes_required(&self, _: ()) -> u16 {
1883 (0u8).heap_bytes_required(())
1884 }
1885}
1886
1887#[binrw]
1889#[derive(Debug, PartialEq, Eq, Clone)]
1890#[brw(little)]
1891pub struct Menu {
1892 pub category_id: u16,
1895
1896 pub content_pointer: u16,
1898 pub unknown: u8,
1907
1908 pub visibility: MenuVisibility,
1914
1915 pub sort_order: u16,
1918}
1919
1920impl PageHeapObject for Menu {
1921 type Args<'a> = ();
1922 fn heap_bytes_required(&self, _: ()) -> u16 {
1923 [
1924 self.category_id.heap_bytes_required(()),
1925 self.content_pointer.heap_bytes_required(()),
1926 self.unknown.heap_bytes_required(()),
1927 self.visibility.heap_bytes_required(()),
1928 self.sort_order.heap_bytes_required(()),
1929 ]
1930 .iter()
1931 .sum()
1932 }
1933}
1934
1935impl RowVariant for Menu {
1936 const PAGE_TYPE: PageType = PageType::Plain(PlainPageType::Menu);
1937
1938 fn from_row(row: &Row) -> Option<&Self> {
1939 match row {
1940 Row::Plain(PlainRow::Menu(row)) => Some(row),
1941 _ => None,
1942 }
1943 }
1944 fn from_row_mut(row: &mut Row) -> Option<&mut Self> {
1945 match row {
1946 Row::Plain(PlainRow::Menu(row)) => Some(row),
1947 _ => None,
1948 }
1949 }
1950}
1951
1952#[binrw]
1954#[derive(Debug, PartialEq, Eq, Clone)]
1955#[brw(little)]
1956#[br(import(page_type: PlainPageType))]
1957#[allow(clippy::large_enum_variant)]
1962pub enum PlainRow {
1963 #[br(pre_assert(page_type == PlainPageType::Albums))]
1967 Album(Album),
1968 #[br(pre_assert(page_type == PlainPageType::Artists))]
1972 Artist(Artist),
1973 #[br(pre_assert(page_type == PlainPageType::Artwork))]
1975 Artwork(Artwork),
1976 #[br(pre_assert(page_type == PlainPageType::Colors))]
1978 Color(Color),
1979 #[br(pre_assert(page_type == PlainPageType::Genres))]
1981 Genre(Genre),
1982 #[br(pre_assert(page_type == PlainPageType::HistoryPlaylists))]
1984 HistoryPlaylist(HistoryPlaylist),
1985 #[br(pre_assert(page_type == PlainPageType::HistoryEntries))]
1987 HistoryEntry(HistoryEntry),
1988 #[br(pre_assert(page_type == PlainPageType::History))]
1990 History(History),
1991 #[br(pre_assert(page_type == PlainPageType::Keys))]
1993 Key(Key),
1994 #[br(pre_assert(page_type == PlainPageType::Labels))]
1996 Label(Label),
1997 #[br(pre_assert(page_type == PlainPageType::PlaylistTree))]
1999 PlaylistTreeNode(PlaylistTreeNode),
2000 #[br(pre_assert(page_type == PlainPageType::PlaylistEntries))]
2002 PlaylistEntry(PlaylistEntry),
2003 #[br(pre_assert(page_type == PlainPageType::Columns))]
2005 ColumnEntry(ColumnEntry),
2006 #[br(pre_assert(page_type == PlainPageType::Menu))]
2008 Menu(Menu),
2009 #[br(pre_assert(page_type == PlainPageType::Tracks))]
2013 Track(Track),
2014}
2015
2016impl PageHeapObject for PlainRow {
2017 type Args<'a> = ();
2018 fn heap_bytes_required(&self, _: ()) -> u16 {
2019 match self {
2020 PlainRow::Album(album) => album.heap_bytes_required(()),
2021 PlainRow::Artist(artist) => artist.heap_bytes_required(()),
2022 PlainRow::Artwork(artwork) => artwork.heap_bytes_required(()),
2023 PlainRow::Color(color) => color.heap_bytes_required(()),
2024 PlainRow::Genre(genre) => genre.heap_bytes_required(()),
2025 PlainRow::HistoryPlaylist(history_playlist) => history_playlist.heap_bytes_required(()),
2026 PlainRow::HistoryEntry(history_entry) => history_entry.heap_bytes_required(()),
2027 PlainRow::History(history) => history.heap_bytes_required(()),
2028 PlainRow::Key(key) => key.heap_bytes_required(()),
2029 PlainRow::Label(label) => label.heap_bytes_required(()),
2030 PlainRow::PlaylistTreeNode(playlist_tree_node) => {
2031 playlist_tree_node.heap_bytes_required(())
2032 }
2033 PlainRow::PlaylistEntry(playlist_entry) => playlist_entry.heap_bytes_required(()),
2034 PlainRow::ColumnEntry(column_entry) => column_entry.heap_bytes_required(()),
2035 PlainRow::Menu(menu) => menu.heap_bytes_required(()),
2036 PlainRow::Track(track) => track.heap_bytes_required(()),
2037 }
2038 }
2039}
2040
2041#[binrw]
2043#[derive(Debug, PartialEq, Eq, Clone)]
2044#[brw(little)]
2045#[br(import(page_type: PageType))]
2046#[allow(clippy::large_enum_variant)]
2055pub enum Row {
2056 #[br(pre_assert(matches!(page_type, PageType::Plain(_))))]
2058 Plain(
2060 #[br(args(match page_type {
2061 PageType::Plain(v) => v,
2062 _ => unreachable!("by above pre_assert")
2063 }))]
2064 PlainRow,
2065 ),
2066 #[br(pre_assert(matches!(page_type, PageType::Ext(_))))]
2067 Ext(
2069 #[br(args(match page_type {
2070 PageType::Ext(v) => v,
2071 _ => unreachable!("by above pre_assert")
2072 }))]
2073 ExtRow,
2074 ),
2075 #[br(pre_assert(matches!(page_type, PageType::Unknown(_))))]
2077 Unknown,
2078}
2079
2080impl Row {
2081 #[must_use]
2083 pub fn as_variant<T: RowVariant>(&self) -> Option<&T> {
2084 T::from_row(self)
2085 }
2086 #[must_use]
2088 pub fn as_variant_mut<T: RowVariant>(&mut self) -> Option<&mut T> {
2089 T::from_row_mut(self)
2090 }
2091}
2092
2093impl PageHeapObject for Row {
2094 type Args<'a> = ();
2095 fn heap_bytes_required(&self, _: ()) -> u16 {
2096 match self {
2097 Row::Plain(plain_row) => plain_row.heap_bytes_required(()),
2098 Row::Ext(ext_row) => ext_row.heap_bytes_required(()),
2099 Row::Unknown => panic!("Unable to determine required bytes for unknown row type"),
2100 }
2101 }
2102}