tac_cart/
lib.rs

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, // specifies in which bank the chunk lives
40}
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}