1use crate::memory;
2
3const BACKUP_RAM_LEN: usize = memory::BACKUP_RAM_LEN;
4const RAM_CARTRIDGE_LEN: usize = memory::RAM_CARTRIDGE_LEN;
5
6const BACKUP_RAM_FOOTER_LEN: usize = 64;
7
8#[rustfmt::skip]
9const BACKUP_RAM_FOOTER: [u8; BACKUP_RAM_FOOTER_LEN] = [
10    // $1FC0-$1FCF
11    0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x40,
12    // $1FD0-$1FDF
13    0x00, 0x7D, 0x00, 0x7D, 0x00, 0x7D, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14    // $1FE0-$1FEF
15    0x53, 0x45, 0x47, 0x41, 0x5F, 0x43, 0x44, 0x5F, 0x52, 0x4F, 0x4D, 0x00, 0x01, 0x00, 0x00, 0x00,
16    // $1FF0-$1FFF
17    0x52, 0x41, 0x4D, 0x5F, 0x43, 0x41, 0x52, 0x54, 0x52, 0x49, 0x44, 0x47, 0x45, 0x5F, 0x5F, 0x5F,
18];
19
20#[rustfmt::skip]
21const RAM_CARTRIDGE_FOOTER: [u8; BACKUP_RAM_FOOTER_LEN] = [
22    // $1FFC0-$1FFCF
23    0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x40,
24    // $1FFD0-$1FFDF
25    0x07, 0xFD, 0x07, 0xFD, 0x07, 0xFD, 0x07, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26    // $1FFE0-$1FFEF
27    0x53, 0x45, 0x47, 0x41, 0x5F, 0x43, 0x44, 0x5F, 0x52, 0x4F, 0x4D, 0x00, 0x01, 0x00, 0x00, 0x00,
28    // $1FFF0-$1FFFF
29    0x52, 0x41, 0x4D, 0x5F, 0x43, 0x41, 0x52, 0x54, 0x52, 0x49, 0x44, 0x47, 0x45, 0x5F, 0x5F, 0x5F,
30];

Boxing is desired because these boxed arrays will be large (8KB / 128KB)

33#[allow(clippy::unnecessary_box_returns)]
34fn new_formatted_backup_ram<const LEN: usize>(
35    footer: &[u8; BACKUP_RAM_FOOTER_LEN],
36) -> Box<[u8; LEN]> {
37    // If no backup RAM was provided during construction, initialize it pre-formatted.
38    // Some games (Popful Mail) blow up horribly if backup RAM is not formatted, so it's more
39    // convenient to initialize it already formatted.
40    let mut backup_ram: Box<[u8; LEN]> = vec![0; LEN].into_boxed_slice().try_into().unwrap();
41
42    // Most of a freshly-formatted backup RAM is filled with 0s, but the last 64 bytes need to be
43    // filled in
44    backup_ram[LEN - BACKUP_RAM_FOOTER_LEN..].copy_from_slice(footer);
45
46    backup_ram
47}
49pub fn load_initial_backup_ram(
50    initial_backup_ram: Option<&Vec<u8>>,
51    initial_ram_cartridge: Option<&Vec<u8>>,
52) -> (Box<[u8; BACKUP_RAM_LEN]>, Box<[u8; RAM_CARTRIDGE_LEN]>) {
53    let backup_ram: Box<[u8; BACKUP_RAM_LEN]> = match initial_backup_ram {
54        Some(backup_ram) if backup_ram.len() == BACKUP_RAM_LEN => {
55            backup_ram.clone().into_boxed_slice().try_into().unwrap()
56        }
57        Some(combined_ram) if combined_ram.len() == BACKUP_RAM_LEN + RAM_CARTRIDGE_LEN => {
58            Vec::from(&combined_ram[..BACKUP_RAM_LEN]).into_boxed_slice().try_into().unwrap()
59        }
60        _ => new_formatted_backup_ram(&BACKUP_RAM_FOOTER),
61    };
62
63    // Prefer to load RAM cartridge from the initial RAM cartridge data, and fall back to looking for
64    // it at the end of the initial backup RAM data
65    let ram_cartridge: Box<[u8; RAM_CARTRIDGE_LEN]> =
66        match (initial_backup_ram, initial_ram_cartridge) {
67            (_, Some(ram_cartridge)) if ram_cartridge.len() == RAM_CARTRIDGE_LEN => {
68                ram_cartridge.clone().into_boxed_slice().try_into().unwrap()
69            }
70            (Some(combined_ram), _) if combined_ram.len() == BACKUP_RAM_LEN + RAM_CARTRIDGE_LEN => {
71                Vec::from(&combined_ram[BACKUP_RAM_LEN..]).into_boxed_slice().try_into().unwrap()
72            }
73            _ => new_formatted_backup_ram(&RAM_CARTRIDGE_FOOTER),
74        };
75
76    (backup_ram, ram_cartridge)
77}