1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::file_utils::{read_block, read_multiple_blocks, read_string};
use crate::GameFileError::InvalidHeader;
use crate::{GameFile, GameFileError, GameFileHeader, FILE_FORMAT_VER, ID_HEADER};
use maikor_language::mem::sizes;
use std::fmt::{Debug, Formatter};
use std::io::{BufRead, BufWriter, Write};
use std::{fmt, io};
impl GameFile {
pub fn from_reader<R: BufRead>(mut reader: R) -> Result<GameFile, GameFileError> {
let header = GameFileHeader::from_reader(&mut reader)?;
if let Err(text) = header.validate() {
return Err(InvalidHeader(text));
}
let name = read_string(&mut reader, header.name_length as usize)?;
let author = read_string(&mut reader, header.author_length as usize)?;
let version = read_string(&mut reader, header.version_length as usize)?;
let atlas_lengths: Vec<u16> =
read_multiple_blocks(&mut reader, 2, header.atlas_bank_count as usize)?
.iter()
.map(|arr| u16::from_be_bytes([arr[0], arr[1]]))
.collect();
let main_code = read_block(&mut reader, sizes::CODE_BANK as usize)?;
let code_banks = read_multiple_blocks(
&mut reader,
sizes::CODE_BANK as usize,
header.code_bank_count as usize,
)?
.iter()
.map(|list| list.as_slice().try_into().unwrap())
.collect();
let mut atlas_banks = vec![];
for len in atlas_lengths {
atlas_banks.push(read_block(&mut reader, len as usize)?);
}
Ok(GameFile {
id: header.id(),
build: header.build(),
compiled_for_maikor_version: header.compile_version(),
min_maikor_version: header.min_version(),
version,
name,
author,
ram_bank_count: header.ram_bank_count as usize,
main_code,
code_banks,
atlas_banks,
})
}
pub fn as_bytes(&self) -> Result<Vec<u8>, io::Error> {
let mut bytes = vec![];
let mut writer = BufWriter::new(&mut bytes);
writer.write_all(&ID_HEADER)?;
writer.write_all(&[FILE_FORMAT_VER])?;
writer.write_all(&self.min_maikor_version.to_be_bytes())?;
writer.write_all(&self.compiled_for_maikor_version.to_be_bytes())?;
writer.write_all(&self.id.to_be_bytes())?;
writer.write_all(&self.build.to_be_bytes())?;
writer.write_all(&[
self.version.len() as u8,
self.name.len() as u8,
self.author.len() as u8,
self.code_banks.len() as u8,
self.ram_bank_count as u8,
self.atlas_banks.len() as u8,
])?;
writer.write_all(self.name.as_bytes())?;
writer.write_all(self.author.as_bytes())?;
writer.write_all(self.version.as_bytes())?;
writer.write_all(
&self
.atlas_banks
.iter()
.flat_map(|arr| (arr.len() as u16).to_be_bytes())
.collect::<Vec<u8>>(),
)?;
writer.write_all(&self.main_code)?;
for bank in &self.code_banks {
writer.write_all(bank)?;
}
for bank in &self.atlas_banks {
writer.write_all(bank)?;
}
drop(writer);
Ok(bytes)
}
}
impl Debug for GameFile {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{: >10} - {} by {}, {} (#{})",
self.id, self.name, self.author, self.version, self.build
)
}
}
impl PartialEq for GameFile {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.build == other.build
}
}