1use std::{error::Error, fmt::Debug, io::Cursor, path::Path, u8, slice::Iter};
2
3use binread::prelude::*;
4use binwrite::*;
5use modular_bitfield::prelude::*;
6
7#[derive(BitfieldSpecifier, Debug, Clone, Copy, PartialEq, Eq)]
8#[bits = 5]
9pub enum ChunkType {
10 Dummy = 0,
11 Tiles = 1,
12 Sprites = 2,
13
14 Map = 4,
15 Code = 5,
16 Flags = 6,
17 Samples = 9,
18 Waveform = 10,
19 Palette = 12,
20 Music = 14,
21 Patterns = 15,
22 Default = 17,
23 Screen = 18,
24 Binary = 19,
25
26 #[deprecated]
27 CoverDep = 3,
28 #[deprecated]
29 CodeZip = 16,
30 #[deprecated]
31 PatternsDep = 13,
32}
33
34#[bitfield]
35#[derive(BinRead, BinWrite, Debug, Clone)]
36#[br(map = Self::from_bytes)]
37pub struct ChunkInfo {
38 pub chunk_type: ChunkType,
39 pub bank: B3, }
41
42
43#[derive(BinRead, BinWrite, Debug, Clone)]
44pub struct Chunk {
45 pub info: ChunkInfo,
46
47 #[br(little)]
48 pub size: u16,
49
50 pub reserved: u8,
51
52 #[br(count=size)]
53 pub data: Vec<u8>,
54}
55
56#[derive(Clone)]
57pub struct Cartridge {
58 pub title: String,
59 pub chunks: Vec<Chunk>,
60}
61
62impl TryFrom<&[u8]> for Cartridge {
63 type Error = Box<dyn Error>;
64
65 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
66 let mut cursor = Cursor::new(bytes);
67 let mut cart = Cartridge {
68 chunks: vec![],
69 title: "cart.tic".to_string(),
70 };
71
72 while (cursor.position() as usize) < bytes.len() {
73 let chunk: Chunk = cursor.read_ne()?;
74 cart.chunks.push(chunk);
75 }
76 Ok(cart)
77 }
78}
79
80impl TryInto<Vec<u8>> for Cartridge {
81 type Error = Box<dyn Error>;
82
83 fn try_into(self) -> Result<Vec<u8>, Self::Error> {
84 let mut out = Vec::new();
85 for chunk in self.chunks {
86 chunk.write(&mut out)?;
87 }
88 Ok(out)
89 }
90}
91
92impl Cartridge {
93 pub fn load(path: impl AsRef<Path>) -> Result<Cartridge, Box<dyn Error>> {
94 let bytes = std::fs::read(path)?;
95 Ok(Cartridge::try_from(bytes.as_slice())?)
96 }
97}
98
99impl Debug for Cartridge {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 writeln!(f, "=== CARTRIDGE {} ===", self.title)?;
102 for chunk in &self.chunks {
103 writeln!(
104 f,
105 "BANK: {} SIZE: {} TYPE: {:?}",
106 chunk.info.bank(),
107 chunk.size,
108 chunk.info.chunk_type()
109 )?;
110 }
111 Ok(())
112 }
113}