From f804c327def26d4bde8523966c708eced1888b92 Mon Sep 17 00:00:00 2001 From: Nathan Rashleigh Date: Sat, 27 Sep 2025 09:35:26 +1000 Subject: [PATCH] mut rgs --- maple-rs/src/db.rs | 283 ------------------------------------------ maple-rs/src/main.rs | 12 +- maple.sublime-project | 5 +- 3 files changed, 11 insertions(+), 289 deletions(-) delete mode 100644 maple-rs/src/db.rs diff --git a/maple-rs/src/db.rs b/maple-rs/src/db.rs deleted file mode 100644 index f3cef6f..0000000 --- a/maple-rs/src/db.rs +++ /dev/null @@ -1,283 +0,0 @@ -use rekordcrate::pdb::Page; -use rekordcrate::util::ColorIndex; -use rekordcrate::pdb::Color; -use rekordcrate::util::RekordcrateError; -use rekordcrate::pdb::Artwork; -use rekordcrate::pdb::ArtworkId; -use rekordcrate::pdb::PageType; -use rekordcrate::pdb::PageIndex; -use std::io::Write; -use std::io::{Seek, SeekFrom}; -use binrw::BinWrite; -use rekordcrate::pdb::LabelId; -use rekordcrate::pdb::KeyId; -use rekordcrate::pdb::GenreId; -use rekordcrate::pdb::ArtistId; -use rekordcrate::pdb::AlbumId; -use rekordcrate::pdb::Album; -use rekordcrate::pdb::Artist; -use rekordcrate::pdb::Genre; -use rekordcrate::pdb::Key; -use rekordcrate::pdb::Label; -use rekordcrate::pdb::Table; -use std::fs::File; -use std::path::Path; -use binrw::BinRead; -use std::collections::BTreeMap; - -use binrw::Endian; -use rekordcrate::pdb::{ - Header, Row, - Track, TrackId, - PlaylistTreeNode, PlaylistTreeNodeId, - PlaylistEntry, -}; - -#[derive(Debug)] -pub enum DbError { - UnknownPlaylist, - UnknownTrack, - PositionOutOfBounds, -} - -#[derive(Debug)] -pub struct Database { - pub header: Header, - pub tracks: BTreeMap, - pub genres: BTreeMap, - pub artists: BTreeMap, - pub albums: BTreeMap, - pub labels: BTreeMap, - pub keys: BTreeMap, - pub colors: BTreeMap, - pub playlist_tree: BTreeMap, - // Keep playlist entries ordered by entry_index - pub playlist_entries: BTreeMap>, - pub artwork: BTreeMap, -} - -const PAGE_SIZE: u16 = 4096; - -impl Database { - // pub fn export(self, path: &Path) -> Result<(), RekordcrateError> { - // let table = Table::from_rows(PageType::Tracks, 1, PAGE_SIZE, self.tracks.values().cloned().map(Row::Track))?; - // println!("{:?}", table); - // Ok(()) - // } - - /// Iterator over (PageType, rows) in file order. - fn table_rows(&self) -> Vec<(PageType, Vec)> { - let mut playlist_rows: Vec = Vec::new(); - for (_pid, mut es) in &self.playlist_entries { - let mut v = es.clone(); - v.sort_by_key(|e| e.entry_index); - playlist_rows.extend(v.into_iter().map(Row::PlaylistEntry)); - } - - vec![ - // (PageType::Tracks, self.tracks.values().cloned().map(Row::Track).collect()), - // (PageType::Genres, self.genres.values().cloned().map(Row::Genre).collect()), - // (PageType::Artists, self.artists.values().cloned().map(Row::Artist).collect()), - // (PageType::Albums, self.albums.values().cloned().map(Row::Album).collect()), - // (PageType::Labels, self.labels.values().cloned().map(Row::Label).collect()), - // (PageType::Keys, self.keys.values().cloned().map(Row::Key).collect()), - // (PageType::Colors, self.colors.values().cloned().map(Row::Color).collect()), - // (PageType::PlaylistTree, self.playlist_tree.values().cloned().map(Row::PlaylistTreeNode).collect()), - // (PageType::PlaylistEntries, playlist_rows), - // (PageType::HistoryPlaylists, Vec::new()), - // (PageType::HistoryEntries, Vec::new()), - // (PageType::Artwork, self.artwork.values().cloned().map(Row::Artwork).collect()), - // (PageType::Columns, Vec::new()), - // (PageType::History, Vec::new()), - (PageType::Tracks, vec![]), - (PageType::Genres, vec![]), - (PageType::Artists, vec![]), - (PageType::Albums, vec![]), - (PageType::Labels, vec![]), - (PageType::Keys, vec![]), - (PageType::Colors, vec![]), - (PageType::PlaylistTree, vec![]), - (PageType::PlaylistEntries, vec![]), - (PageType::HistoryPlaylists, Vec::new()), - (PageType::HistoryEntries, Vec::new()), - (PageType::Artwork, vec![]), - (PageType::Columns, Vec::new()), - (PageType::History, Vec::new()), - ] - } - - /// Write all tables, sequential pages, return header.tables and next_unused_page. - fn get_tables_and_pages( - &self, - start_index: u32, - page_size: u16, - ) -> Result<(Vec, Vec, u32), RekordcrateError> { - let mut next = start_index; - let mut tables = Vec::new(); - let mut pages = Vec::new(); - - for (pt, rows) in self.table_rows().into_iter() { - let (table, mut row_pages, np) = Table::from_rows(pt, next, page_size, rows)?; - pages.append(&mut row_pages); - tables.push(table); - next = np; - } - Ok((tables, pages, next)) - } - - pub fn export(self, path: &Path) -> Result<(), RekordcrateError> { - let mut f = File::create(path)?; - // reserve the file header space - // tables begin at 28 - f.write_all(&vec![0u8; 28 as usize])?; - - let (tables, pages, next_page) = self.get_tables_and_pages(1, PAGE_SIZE)?; - - for table in tables.clone() { - println!("Writing table={:?} at {:?}",table, f.stream_position()); - table.write_le(&mut f)?; - } - - f.seek(SeekFrom::Start(4096)).unwrap(); - - for page in pages { - println!("Writing page={:?} at {:?}", page, f.stream_position()); - page.write_le_args(&mut f, (PAGE_SIZE.into(),)).unwrap(); - } - - let header = Header { - sequence: 0, - next_unused_page: PageIndex(next_page), - tables, - ..Default::default() - }; - - f.seek(SeekFrom::Start(0))?; - println!("Writing header at {:?}", f.stream_position()); - header.write_le(&mut f).unwrap(); - - Ok(()) - } - - // fn write_table( - // f: &mut File, - // page_type: PageType, - // start_index: u32, - // rows: I, - // ) -> std::io::Result<(u32, u32)> - // where - // I: IntoIterator, - // { - // let mut page_index = start_index; - // let mut first = 0; - // let mut last = 0; - - // // Pack rows into pages. Use your crate’s Page builder to handle row groups. - // let mut packer = PagePacker::new(page_type, PAGE_SIZE); - - // for row in rows { - // if !packer.try_push(&row) { - // // flush full page - // let mut page = packer.finish(page_index, /*next_page=*/0); - // // Temporarily set next_page=0; we will fix it after writing the next page. - // Self::write_page(f, &mut page)?; - // if first == 0 { first = page_index; } - // last = page_index; - // page_index += 1; - // // start new page with the row that didn’t fit - // packer = PagePacker::new(page_type, PAGE_SIZE); - // assert!(packer.try_push(&row)); - // } - // } - - // // flush final page if it has content or if this is an empty table create one empty page - // if packer.is_empty() && first == 0 { - // let mut page = Page::empty(page_type, page_index, /*next_page=*/0, PAGE_SIZE); - // Self::write_page(f, &mut page)?; - // first = page_index; - // last = page_index; - // } else { - // let mut page = packer.finish(page_index, /*next_page=*/0); - // Self::write_page(f, &mut page)?; - // if first == 0 { first = page_index; } - // last = page_index; - // } - - // // Fix `next_page` pointers by rewriting page headers if your Page writer requires it. - // // Simpler: during emission, remember the previous page file offset and seek back - // // to patch its `next_page` to `page_index`. - - // Ok((first, last)) - // } - - - // fn write_page(f: &mut File, page: &mut Page) -> std::io::Result<()> { - // page.write(f); - // Ok(()) - // } - - pub fn load_from_pdb(path: impl AsRef) -> rekordcrate::Result { - let mut f = std::fs::File::open(path)?; - let header = Header::read(&mut f)?; // uses the type’s endianness - - let mut tracks: BTreeMap = BTreeMap::new(); - let mut albums: BTreeMap = BTreeMap::new(); - let mut artists: BTreeMap = BTreeMap::new(); - let mut artwork: BTreeMap = BTreeMap::new(); - let mut colors: BTreeMap = BTreeMap::new(); - let mut genres: BTreeMap = BTreeMap::new(); - let mut keys: BTreeMap = BTreeMap::new(); - let mut labels: BTreeMap = BTreeMap::new(); - - let mut playlist_tree: BTreeMap = BTreeMap::new(); - let mut playlist_entries: BTreeMap> = BTreeMap::new(); - - for table in &header.tables { - let pages = header.read_pages(&mut f, Endian::Little, (&table.first_page, &table.last_page))?; - - for page in pages { - for rg in page.row_groups { - for row in rg.present_rows() { - match row { - Row::Track(t) => { - tracks.insert(t.id, t.clone()); - } - Row::Artist(a) => { - artists.insert(a.id, a.clone()); - } - Row::Album(a) => { - albums.insert(a.id, a.clone()); - } - Row::Color(c) => { - colors.insert(c.color.clone(), c.clone()); - } - Row::Genre(g) => { - genres.insert(g.id, g.clone()); - } - Row::Key(k) => { - keys.insert(k.id, k.clone()); - } - Row::Label(l) => { - labels.insert(l.id, l.clone()); - } - Row::PlaylistTreeNode(n) => { - playlist_tree.insert(n.id, n.clone()); - } - Row::PlaylistEntry(e) => { - playlist_entries.entry(e.playlist_id).or_default().push(e.clone()); - } - _ => {} - } - } - } - } - } - - // Ensure deterministic ordering within each playlist - for v in playlist_entries.values_mut() { - v.sort_by_key(|e| e.entry_index); // needs `pub entry_index` - } - - Ok(Self { header, tracks, albums, artists, artwork, genres, keys, labels, playlist_tree, playlist_entries, colors }) - } -} diff --git a/maple-rs/src/main.rs b/maple-rs/src/main.rs index 0001137..c28beb5 100644 --- a/maple-rs/src/main.rs +++ b/maple-rs/src/main.rs @@ -1,10 +1,11 @@ -mod db; - extern crate walkdir; use rekordcrate::util::RekordcrateError; +use rekordcrate::pdb::Database; +use std::fs::File; use std::path::Path; -use db::Database; + +const PAGE_SIZE: u32 = 4096; // Example fn main() -> Result<(), RekordcrateError> { @@ -13,7 +14,8 @@ fn main() -> Result<(), RekordcrateError> { println!("{:?}", db.colors); let output = Path::new("./data/output.pdb"); - db.export(&output).unwrap(); + let mut f = File::create(output)?; + db.export(PAGE_SIZE, &mut f)?; // let db = Database::load_from_pdb("./data/output.pdb").unwrap(); // println!("{:?}", db); @@ -144,4 +146,4 @@ mod tests { let _last = Track { unknown1: 36, index_shift: 128, bitmask: 788224, sample_rate: 44100, composer_id: ArtistId(0), file_size: 12384711, unknown2: 197860019, unknown3: 42343, unknown4: 54995, artwork_id: ArtworkId(681), key_id: KeyId(20), orig_artist_id: ArtistId(0), label_id: LabelId(0), remixer_id: ArtistId(0), bitrate: 0, track_number: 6, tempo: 13600, genre_id: GenreId(0), album_id: AlbumId(333), artist_id: ArtistId(398), id: TrackId(847), disc_number: 0, play_count: 0, year: 2018, sample_depth: 16, duration: 367, unknown5: 41, color: ColorIndex::None, rating: 0, unknown6: 1, unknown7: 3, isrc: DeviceSQLString::empty(), unknown_string1: DeviceSQLString::empty(), unknown_string2: "1".parse().unwrap(), unknown_string3: "2".parse().unwrap(), unknown_string4: DeviceSQLString::empty(), message: DeviceSQLString::empty(), kuvo_public: "ON".parse().unwrap(), autoload_hotcues: "ON".parse().unwrap(), unknown_string5: DeviceSQLString::empty(), unknown_string6: DeviceSQLString::empty(), date_added: "2021-12-09".parse().unwrap(), release_date: DeviceSQLString::empty(), mix_name: DeviceSQLString::empty(), unknown_string7: DeviceSQLString::empty(), analyze_path: "/PIONEER/USBANLZ/P00E/000209C4/ANLZ0000.DAT".parse().unwrap(), analyze_date: "2023-06-02".parse().unwrap(), comment: "Visit https://endofperception.bandcamp.com".parse().unwrap(), title: "Ness - Orbital Debris".parse().unwrap(), unknown_string8: DeviceSQLString::empty(), filename: "End Of Perception - ЛаBа VA 002 - 06 Ness - .mp3".parse().unwrap(), file_path: "/Contents/End Of Perception/ЛаBа VA 002/End Of Perception - ЛаBа VA 002 - 06 Ness - .mp3".parse().unwrap(), }; } -} \ No newline at end of file +} diff --git a/maple.sublime-project b/maple.sublime-project index 7373008..04f2e6b 100755 --- a/maple.sublime-project +++ b/maple.sublime-project @@ -5,7 +5,10 @@ "path": ".", }, { - "path": "/home/ntr/code/rekordcrate" + "path": "/home/ntr/code/rekordcrate", + }, + { + "path": "/Users/nathanrashleigh/code/code/rekordcrate" }, ], "settings":