1use std::fs::File;
7use std::io::{BufWriter, Read, Write};
8use std::{fmt, str};
9
10use byteorder::{LittleEndian, ReadBytesExt};
11use deku::prelude::*;
12use gif::{Encoder as GifEncoder, Frame as GifFrame, Repeat};
13use png::Encoder;
14
15mod errors;
16pub use crate::errors::MCError;
17
18const BLOCK: usize = 0x2000;
19const FRAME: usize = 0x80;
20
21#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
22#[deku(endian = "little")]
23pub struct Header {
24 id: [u8; 2],
25 pad: [u8; 125],
26 checksum: u8,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30#[repr(u32)]
31pub enum BAState {
32 AllocFirst = 0x51,
33 AllocMid = 0x52,
34 AllocLast = 0x53,
35 Free = 0xa0,
36 FreeFirst = 0xa1,
37 FreeMid = 0xa2,
38 FreeLast = 0xa3,
39 UNKNOWN,
40}
41
42#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
43#[deku(endian = "little")]
44pub struct DirectoryFrame {
45 pub state: u32,
46 pub filesize: u32,
47 pub next_block: u16,
48 pub filename: [u8; 21],
49 pub pad: [u8; 96],
50 pub checksum: u8,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub enum Region {
55 Japan,
56 America,
57 Europe,
58 UNKNOWN,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62pub enum License {
63 Sony,
64 Licensed,
65 UNKNOWN,
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct RegionInfo {
70 pub region: Region,
71 pub license: License,
72 pub name: String,
73}
74
75impl DirectoryFrame {
76 fn load(input: &[u8], n: usize) -> Result<Vec<Self>, MCError> {
77 let mut frame = Vec::<Self>::new();
78 validate_checksum(input)?;
79 let (mut next, mut df) = Self::from_bytes((input, 0))?;
80 frame.push(df);
81 loop {
82 if frame.len() == n {
83 break;
84 }
85 let (input, _) = next;
86 validate_checksum(input)?;
87 (next, df) = Self::from_bytes(next)?;
88 frame.push(df);
89 }
90 Ok(frame)
91 }
92
93 fn get_alloc_state(&self) -> BAState {
94 match self.state {
95 0x51 => BAState::AllocFirst,
96 0x52 => BAState::AllocMid,
97 0x53 => BAState::AllocLast,
98 0xa0 => BAState::Free,
99 0xa1 => BAState::FreeFirst,
100 0xa2 => BAState::FreeMid,
101 0xa3 => BAState::FreeLast,
102 _ => BAState::UNKNOWN,
103 }
104 }
105
106 fn get_region_info(&self) -> Result<RegionInfo, MCError> {
107 let region = match self.filename[1] {
108 b'I' => Region::Japan,
109 b'A' => Region::America,
110 b'E' => Region::Europe,
111 _ => Region::UNKNOWN,
112 };
113
114 let license = match self.filename[3] {
115 b'C' => License::Sony,
116 b'L' => License::Licensed,
117 _ => License::UNKNOWN,
118 };
119
120 let name = str::from_utf8(&self.filename[12..])?.to_string();
121
122 Ok(RegionInfo {
123 region,
124 license,
125 name,
126 })
127 }
128}
129
130impl fmt::Display for DirectoryFrame {
131 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132 write!(
133 f,
134 "\n State: {:?}\n Filesize: {}\n Next block: {}\n Region Info: {:?}\n Checksum: {}",
135 self.get_alloc_state(),
136 self.filesize,
137 self.next_block,
138 self.get_region_info(),
139 self.checksum
140 )
141 }
142}
143
144#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
145#[deku(endian = "little")]
146pub struct BrokenFrame {
147 broken_frame: u32,
148 pad: [u8; 123],
149 checksum: u8,
150}
151
152impl BrokenFrame {
153 fn load(input: &[u8], n: usize) -> Result<Vec<Self>, MCError> {
154 let mut frame = Vec::<Self>::new();
155 validate_checksum(input)?;
156 let (mut next, mut df) = Self::from_bytes((input, 0))?;
157 frame.push(df);
158 loop {
159 if frame.len() == n {
160 break;
161 }
162 let (input, _) = next;
163 validate_checksum(input)?;
164 (next, df) = Self::from_bytes(next)?;
165 frame.push(df);
166 }
167 Ok(frame)
168 }
169}
170
171#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
176#[deku(endian = "little")]
177pub struct Frame {
178 pub data: [u8; FRAME],
180}
181
182impl Frame {
183 pub fn load(input: &[u8], n: usize) -> Result<Vec<Self>, MCError> {
186 let mut frame = Vec::<Self>::new();
187 validate_checksum(input)?;
188 let (mut next, mut df) = Self::from_bytes((input, 0))?;
189 frame.push(df);
190 loop {
191 if frame.len() == n {
192 break;
193 }
194 let (input, _) = next;
195 validate_checksum(input)?;
196 (next, df) = Self::from_bytes(next)?;
197 frame.push(df);
198 }
199 Ok(frame)
200 }
201
202 pub fn print_strings(f: &Frame) {
203 let mut s = String::new();
204
205 for i in f.data {
206 if i.is_ascii_graphic() || i == b' ' {
207 s.push(i as char);
208 continue;
209 } else if i == 0 && s.len() > 1 {
210 println!("{}", s.trim_start());
211 }
212 s.clear();
213 }
214 if !s.is_empty() {
215 println!("{}", s.trim_start());
216 }
217 }
218
219 pub fn print_values(f: &Frame) {
220 let mut buf: &[u8] = &f.data;
221 let mut count = 0;
222 while let Ok(v) = buf.read_u16::<LittleEndian>() {
223 if v != 0 {
224 println!("{:02}) Hex: {:04x} Dec: {}", count, v, v);
225 }
226 count += 1;
227 };
228 }
229
230 pub fn print_hex(f: &Frame) {
231 let mut s = String::new();
232 for (e, i) in f.data.iter().enumerate() {
233 if e > 0 {
234 if e % 16 == 0 {
235 println!(" {}", s);
236 s.clear();
237 } else if e % 2 == 0 {
238 print!(" |");
239 }
240 }
241 print!(" {:02x}", i);
242 if i.is_ascii_graphic() {
243 s.push(*i as char);
244 } else {
245 s.push('.');
246 }
247 }
248 println!(" {}", s);
249 }
250
251 pub fn set_u32_at(f: &mut Frame, v: u32, ofs: usize) -> Result<(), MCError> {
252 let mut idx = &mut f.data[ofs..];
253 idx.write_all(v.to_le_bytes().as_ref())?;
254
255 Ok(())
256 }
257
258 pub fn set_u16_at(f: &mut Frame, v: u16, ofs: usize) -> Result<(), MCError> {
259 let mut idx = &mut f.data[ofs..];
260 idx.write_all(v.to_le_bytes().as_ref())?;
261
262 Ok(())
263 }
264}
265
266#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
270#[deku(endian = "little")]
271pub struct Block {
272 pub data: [u8; BLOCK],
274}
275
276#[derive(Clone, Debug, PartialEq, Eq)]
280pub struct DataBlock {
281 pub title_frame: TitleFrame,
283 pub icon_frames: Vec<Frame>,
287
288 pub data_frames: Vec<Frame>,
290}
291
292impl DataBlock {
293 pub fn load_data_block(b: Block) -> Result<Self, MCError> {
295 let (_, title_frame) = TitleFrame::from_bytes((&b.data, 0))?;
297
298 let num_frames = title_frame.display as usize & 0x03;
300 let icon_frames = DataBlock::read_n_frames(&b.data[FRAME..], num_frames)?;
301
302 let next = FRAME + (FRAME * icon_frames.len());
305 let num_frames = b.data[next..].len() / FRAME;
306 let data_frames = DataBlock::read_n_frames(&b.data[next..], num_frames)?;
307
308 Ok(DataBlock {
309 title_frame,
310 icon_frames,
311 data_frames,
312 })
313 }
314
315 pub fn load_all_data_blocks(v: &[Block]) -> Result<Vec<Self>, MCError> {
317 let mut out = Vec::<Self>::new();
318 for i in v {
319 out.push(Self::load_data_block(*i)?);
320 }
321
322 Ok(out)
323 }
324
325 fn read_n_frames(input: &[u8], num_frames: usize) -> Result<Vec<Frame>, MCError> {
326 let mut frame = Vec::<Frame>::new();
327 let (mut next, mut f) = Frame::from_bytes((input, 0))?;
328 frame.push(f);
329 loop {
330 if frame.len() == num_frames {
331 break;
332 }
333 (next, f) = Frame::from_bytes(next)?;
334 frame.push(f);
335 }
336 Ok(frame)
337 }
338
339 pub fn write<T: std::io::Write>(&self, out: &mut T) -> Result<(), MCError> {
341 let t = self.title_frame.to_bytes()?;
342 out.write_all(&t)?;
343
344 for ic in &self.icon_frames {
345 let i = ic.to_bytes()?;
346 out.write_all(&i)?;
347 }
348
349 for df in &self.data_frames {
350 let d = df.to_bytes()?;
351 out.write_all(&d)?;
352 }
353
354 Ok(())
355 }
356
357 pub fn export_all_images(&self) -> Result<(), MCError> {
360 for (n, i) in self.icon_frames.iter().enumerate() {
362 let filename = format!("{}_frame{}.png", self.title_frame.decode_title()?, n);
363 let file = File::create(filename)?;
364 let mut w = BufWriter::new(file);
365 let mut enc = Encoder::new(&mut w, 16, 16);
366 enc.set_color(png::ColorType::Rgba);
367 enc.set_depth(png::BitDepth::Eight);
368
369 let mut writer = enc.write_header()?;
370
371 let pixel_data = self.translate_bmp_to_rgba(i)?;
372
373 writer.write_image_data(&pixel_data)?;
374 }
375
376 if self.icon_frames.len() > 1 {
378 self.export_gif()?;
379 }
380
381 Ok(())
382 }
383
384 fn export_gif(&self) -> Result<(), MCError> {
385 let w = 16;
386 let h = 16;
387 let filename = format!("{}.gif", self.title_frame.decode_title()?);
388 let mut file = File::create(filename)?;
389 let mut enc = GifEncoder::new(&mut file, w, h, &[])?;
390 enc.set_repeat(Repeat::Infinite)?;
391 for i in self.icon_frames.iter() {
392 let mut pixels = self.translate_bmp_to_rgba(i)?;
393 let gifframe = GifFrame::from_rgba(w, h, &mut *pixels);
394 enc.write_frame(&gifframe)?;
395 }
396
397 Ok(())
398 }
399
400 fn translate_bmp_to_rgba(&self, f: &Frame) -> Result<Vec<u8>, MCError> {
401 let mut rgba = Vec::<u8>::new();
402
403 for v in f.data {
405 for s in 0..2 {
406 let index = (v >> (4 * s as u8)) & 0x0f;
407 let pixel: u16 = self.title_frame.icon_palette[index as usize];
408 rgba.push(((pixel & 0x001f) as u16) as u8 * 8);
412 rgba.push(((pixel & (0x001f << 5)) as u16 >> 5) as u8 * 8);
414 rgba.push(((pixel & (0x001f << 10)) as u16 >> 10) as u8 * 8);
416 rgba.push(255);
418 }
419 }
420
421 Ok(rgba)
422 }
423}
424
425#[derive(Clone, Copy, Debug, PartialEq, Eq)]
426pub enum IconDisplay {
427 OneFrame,
428 TwoFrames,
429 ThreeFrames,
430 UNKNOWNFrames,
431}
432
433#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
438#[deku(endian = "little")]
439pub struct TitleFrame {
440 pub id: [u8; 2],
441 pub display: u8,
442 pub block_num: u8,
443 pub title: [u8; 64],
444 pub reserved: [u8; 28],
445 pub icon_palette: [u16; 16],
446}
447
448impl TitleFrame {
449 pub fn decode_title(self) -> Result<String, MCError> {
451 let mut s = String::new();
453
454 let mut p = 0;
455 loop {
456 match self.title[p] {
457 0x81 => {
459 if self.title[p + 1] == 0x40 {
460 s.push(' ');
461 }
462 }
463 0x82 => {
464 if (self.title[p + 1] >= 0x4f && self.title[p + 1] <= 0x58)
465 || (self.title[p + 1] >= 0x60 && self.title[p + 1] <= 0x79)
466 {
467 s.push((self.title[p + 1] - 0x1f) as char);
469 } else if self.title[p + 1] >= 0x81 && self.title[p + 1] <= 0x9a {
470 s.push((self.title[p + 1] - 0x20) as char);
472 }
473 }
474 0x00 => break,
475 _ => (),
476 }
477 p += 2;
478 if p >= self.title.len() {
479 break;
480 }
481 }
482
483 Ok(s)
484 }
485
486 fn get_icon_display(&self) -> IconDisplay {
487 match self.display {
488 0x11 => IconDisplay::OneFrame,
489 0x12 => IconDisplay::TwoFrames,
490 0x13 => IconDisplay::ThreeFrames,
491 _ => IconDisplay::UNKNOWNFrames,
492 }
493 }
494}
495
496impl fmt::Display for TitleFrame {
497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498 let name = match self.decode_title() {
499 Ok(s) => s,
500 Err(_) => "Unknown".to_string(),
501 };
502 write!(
503 f,
504 "\n Filename: {}\n Icon: {:?}\n Block Number: {}",
505 name,
506 self.get_icon_display(),
507 self.block_num
508 )
509 }
510}
511
512#[derive(Clone, Debug, PartialEq, Eq)]
517pub struct InfoBlock {
518 pub header: Header,
520
521 pub dir_frames: Vec<DirectoryFrame>,
524
525 pub broken_frames: Vec<BrokenFrame>,
527
528 unused_frames: Vec<Frame>,
529 wr_test_frame: Header,
530}
531
532impl InfoBlock {
533 pub fn open(b: Block) -> Result<Self, MCError> {
535 validate_checksum(&b.data)?;
537 let (_, header) = Header::from_bytes((&b.data, 0))?;
538
539 let dir_frames = DirectoryFrame::load(&b.data[FRAME..], 15)?;
541
542 let mut offset = (dir_frames.len() * FRAME) + FRAME;
544 let broken_frames = BrokenFrame::load(&b.data[offset..], 20)?;
545
546 offset += broken_frames.len() * FRAME;
547 let unused_frames = Frame::load(&b.data[offset..], 27)?;
548
549 offset += unused_frames.len() * FRAME;
550 validate_checksum(&b.data[offset..])?;
551 let (_, wr_test_frame) = Header::from_bytes((&b.data[offset..], 0))?;
552
553 Ok(InfoBlock {
554 header,
555 dir_frames,
556 broken_frames,
557 unused_frames,
558 wr_test_frame,
559 })
560 }
561
562 pub fn write<T: std::io::Write>(&self, out: &mut T) -> Result<(), MCError> {
564 let mut h = self.header.to_bytes()?;
565 out.write_all(update_checksum(&mut h)?)?;
566
567 for df in &self.dir_frames {
568 let mut d = df.to_bytes()?;
569 out.write_all(update_checksum(&mut d)?)?;
570 }
571
572 for bf in &self.broken_frames {
573 let mut b = bf.to_bytes()?;
574 out.write_all(update_checksum(&mut b)?)?;
575 }
576
577 for uf in &self.unused_frames {
578 let mut f = uf.to_bytes()?;
579 out.write_all(update_checksum(&mut f)?)?;
580 }
581
582 let mut wrt = self.wr_test_frame.to_bytes()?;
583 out.write_all(update_checksum(&mut wrt)?)?;
584
585 Ok(())
586 }
587}
588
589#[derive(Clone, Debug, PartialEq, Eq)]
594pub struct MemCard {
595 pub info: InfoBlock,
597
598 pub data: Vec<DataBlock>,
600}
601
602impl MemCard {
603 pub fn open(filename: &str) -> Result<Self, MCError> {
606 let mut file = File::open(&filename)?;
607
608 let mut block0 = Block { data: [0u8; BLOCK] };
610 file.read_exact(&mut block0.data)?;
611 let info = InfoBlock::open(block0)?;
612
613 let mut blocks = Vec::<Block>::new();
615 loop {
616 let mut block = Block { data: [0u8; BLOCK] };
617 file.read_exact(&mut block.data)?;
618 blocks.push(block);
619 if blocks.len() == 15 {
620 break;
621 }
622 }
623
624 let data = DataBlock::load_all_data_blocks(&blocks)?;
626
627 Ok(MemCard { info, data })
628 }
629
630 pub fn write(&self, filename: &str) -> Result<(), MCError> {
632 let mut file = File::create(&filename)?;
633
634 self.info.write(&mut file)?;
635 for d in &self.data {
636 d.write(&mut file)?;
637 }
638
639 Ok(())
640 }
641
642 pub fn find_game(&self, search: &str) -> Result<Vec<DataBlock>, MCError> {
645 let mut found = Vec::<DataBlock>::new();
646 let mut needle = String::from(search);
647 needle.make_ascii_lowercase();
648
649 for info in &self.data {
651 let mut haystack = info.title_frame.decode_title()?;
652 haystack.make_ascii_lowercase();
653
654 if haystack.contains(&needle) {
655 found.push(info.clone());
656 }
657 }
658
659 Ok(found)
660 }
661}
662
663pub fn calc_checksum(d: &[u8]) -> u8 {
665 let mut c = 0;
666 for i in d.iter().take(FRAME - 1) {
667 c ^= *i;
668 }
669 c
670}
671
672pub fn validate_checksum(d: &[u8]) -> Result<(), MCError> {
674 let c = calc_checksum(d);
675 if c != d[FRAME - 1] {
676 return Err(MCError::BadChecksum);
677 }
678
679 Ok(())
680}
681
682pub fn update_checksum(d: &mut [u8]) -> Result<&[u8], MCError> {
684 let c = calc_checksum(d);
685 d[FRAME - 1] = c;
686
687 validate_checksum(d)?;
688
689 Ok(d)
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695
696 #[test]
697 fn memcard_open() {
698 let _ = MemCard::open("epsxe000.mcr").unwrap();
699
700 }
707
708 #[test]
709 fn memcard_write() {
710 let m = MemCard::open("epsxe000.mcr").unwrap();
711
712 let w = m.find_game("WILD").unwrap();
713 for i in w {
714 println!("{}", i.title_frame);
715 }
716
717 m.write("test.mcr").unwrap();
718 }
719
720 #[test]
721 fn memcard_modify() {
722 let mut a = MemCard::open("epsxe000.mcr").unwrap();
723 a.info.header.id = [0x11, 0x22];
724 a.write("test.mcr").unwrap();
725
726 let mut b = MemCard::open("test.mcr").unwrap();
727 b.info.dir_frames[0].filesize = 4000000;
728 b.write("test.mcr").unwrap();
729
730 let mut c = MemCard::open("test.mcr").unwrap();
731 c.info.broken_frames[0].broken_frame = 12345;
732 c.write("test.mcr").unwrap();
733 }
734}