use std::cell::{LazyCell, RefCell};
use std::rc::Rc;
use battle_background::BattleBackground;
use block::Block;
use crate::rom::background_graphics::BackgroundGraphics;
use crate::rom::background_palette::BackgroundPalette;
pub mod background_graphics;
pub mod background_layer;
pub mod background_palette;
pub mod battle_background;
pub mod block;
pub mod distorter;
pub mod distortion_effect;
pub mod palette_cycle;
pub mod rom_graphics;
const MINIMUM_INDEX: usize = 0;
const MAXIMUM_INDEX: usize = 326;
const UNCOMPRESSED_BLOCK: u8 = 0;
const RUN_LENGTH_ENCODED_BYTE: u8 = 1;
const RUN_LENGTH_ENCODED_SHORT: u8 = 2;
const INCREMENTAL_SEQUENCE: u8 = 3;
const REPEAT_PREVIOUS_DATA: u8 = 4;
const REVERSE_BITS: u8 = 5;
const UNKNOWN_1: u8 = 6;
const UNKNOWN_2: u8 = 7;
fn generate_reversed_bytes() -> [i16; 256] {
let mut reversed_bytes = [0i16; 256];
for i in 0..reversed_bytes.len() {
let binary = format!("{:08b}", i);
let reversed = binary.chars().rev().collect::<String>();
let value = i16::from_str_radix(&reversed, 2).unwrap();
reversed_bytes[i] = value;
}
reversed_bytes
}
const REVERSED_BYTES: LazyCell<[i16; 256]> = LazyCell::new(generate_reversed_bytes);
pub fn snes_to_hex(address: usize, header: bool) -> usize {
let mut new_address = address;
if new_address >= 0x400000 && new_address < 0x600000 {
new_address -= 0x0;
} else if new_address >= 0xC00000 && new_address < 0x1000000 {
new_address -= 0xC00000;
} else {
panic!("SNES address out of range: {}", new_address);
}
if header {
new_address += 0x200;
}
new_address - 0xA0200
}
pub fn read_block(location: usize) -> Block {
Block::new(location)
}
pub fn decompress(
start: usize,
data: &'static [u8],
mut output: Vec<i16>,
read: &mut usize,
) -> Option<Vec<i16>> {
let max_length = output.len() as i16;
let mut pos = start;
let mut bpos = 0i16;
let mut bpos2 = 0i16;
let new_read = read;
while data[pos] != 0xFF {
if pos >= data.len() {
*new_read = pos - start + 1;
return None;
}
let mut command_type = data[pos] >> 5;
let mut len = i16::from((data[pos] & 0x1F) + 1);
if command_type == 7 {
command_type = (data[pos] & 0x1C) >> 2;
len = ((i16::from(data[pos]) & 3) << 8) + i16::from(data[pos + 1]) + 1;
pos += 1;
}
if bpos + len > max_length || bpos + len < 0 {
*new_read = pos - start + 1;
return None;
}
pos += 1;
if command_type >= 4 {
bpos2 = (i16::from(data[pos]) << 8) + i16::from(data[pos + 1]);
if bpos2 >= max_length || bpos2 < 0 {
*new_read = pos - start + 1;
return None;
}
pos += 2;
}
match command_type {
UNCOMPRESSED_BLOCK => {
while {
let old_len = len;
len -= 1;
old_len
} != 0
{
output[bpos as usize] = data[pos].into();
bpos += 1;
pos += 1;
}
}
RUN_LENGTH_ENCODED_BYTE => {
while {
let old_len = len;
len -= 1;
old_len
} != 0
{
output[bpos as usize] = data[pos].into();
bpos += 1;
}
pos += 1;
}
RUN_LENGTH_ENCODED_SHORT => {
if bpos + 2 * len > max_length || bpos < 0 {
*new_read = pos - start + 1;
return None;
}
while {
let old_len = len;
len -= 1;
old_len
} != 0
{
output[bpos as usize] = data[pos].into();
bpos += 1;
output[bpos as usize] = data[pos + 1].into();
bpos += 1;
}
pos += 2;
}
INCREMENTAL_SEQUENCE => {
let mut tmp = data[pos];
pos += 1;
while {
let old_len = len;
len -= 1;
old_len
} != 0
{
output[bpos as usize] = tmp as i16;
bpos += 1;
tmp += 1;
}
}
REPEAT_PREVIOUS_DATA => {
if bpos + len > max_length || bpos2 < 0 {
*new_read = pos - start + 1;
return None;
}
for i in 0..len {
output[bpos as usize] = output[(bpos2 + i) as usize];
bpos += 1;
}
}
REVERSE_BITS => {
if bpos2 + len > max_length || bpos2 < 0 {
*new_read = pos - start + 1;
return None;
}
while {
let old_len = len;
len -= 1;
old_len
} != 0
{
output[bpos as usize] =
REVERSED_BYTES[(output[bpos2 as usize] & 0xFF) as usize];
bpos2 += 1;
bpos += 1;
}
}
UNKNOWN_1 => {
if bpos2 - len + 1 < 0 {
*new_read = pos - start + 1;
return None;
}
while {
let old_len = len;
len -= 1;
old_len
} != 0
{
output[bpos as usize] = output[bpos2 as usize];
bpos += 1;
bpos2 -= 1;
}
}
UNKNOWN_2 | _ => {
*new_read = pos - start + 1;
return None;
}
}
}
*new_read = pos - start + 1;
Some(output)
}
pub fn get_compressed_size(start: usize, data: &'static [u8]) -> isize {
let mut bpos = 0;
let mut pos = start;
let mut bpos2 = 0;
while data[pos] != 0xFF {
if pos >= data.len() {
return -8;
}
let mut command_type = data[pos] >> 5;
let mut length = usize::from((data[pos] & 0x1F) + 1);
if command_type == 7 {
command_type = (data[pos] & 0x1C) >> 2;
length = ((usize::from(data[pos]) & 3) << 8) + usize::from(data[pos + 1]) + 1;
pos += 1;
}
if bpos + length < 0 {
return -1;
}
pos += 1;
if command_type >= 4 {
bpos2 = (usize::from(data[pos]) << 8) + (usize::from(data[pos + 1]));
if bpos2 < 0 {
return -2;
}
pos += 2;
}
match command_type {
UNCOMPRESSED_BLOCK => {
bpos += length;
pos += length;
}
RUN_LENGTH_ENCODED_BYTE => {
bpos += length;
pos += 1;
}
RUN_LENGTH_ENCODED_SHORT => {
if bpos < 0 {
return -3;
}
bpos += 2 * length;
pos += 2;
}
INCREMENTAL_SEQUENCE => {
bpos += length;
pos += 1;
}
REPEAT_PREVIOUS_DATA => {
if bpos2 < 0 {
return -4;
}
bpos += length;
}
REVERSE_BITS => {
if bpos2 < 0 {
return -5;
}
bpos += length;
}
UNKNOWN_1 => {
if bpos - length + 1 < 0 {
return -6;
}
bpos += length;
}
UNKNOWN_2 | _ => {
return -7;
}
}
}
bpos as isize
}
pub static DATA: &[u8] = include_bytes!("../data/truncated_backgrounds.dat");
#[derive(Debug)]
pub struct Rom {
battle_background_objects: Vec<Rc<RefCell<BattleBackground>>>,
background_palette_objects: Vec<BackgroundPalette>,
background_graphics_objects: Vec<Rc<RefCell<BackgroundGraphics>>>,
}
#[allow(clippy::new_without_default)]
impl Rom {
pub fn new() -> Self {
let mut palette_bits = vec![0i32; 114];
let mut graphics_bits = vec![0i32; 103];
let mut battle_background_objects = Vec::with_capacity(MAXIMUM_INDEX);
for i in MINIMUM_INDEX..=MAXIMUM_INDEX {
let background = Rc::new(RefCell::new(BattleBackground::new(i)));
battle_background_objects.push(background.clone());
let palette = background.borrow().palette_index();
let bits_per_pixel = i32::from(background.borrow().bits_per_pixel());
if palette_bits[palette] != 0 {
assert_eq!(
palette_bits[palette], bits_per_pixel,
"BattleBackground palette Error: Inconsistent bit depth"
);
}
palette_bits[palette] = bits_per_pixel;
graphics_bits[background.borrow().graphics_index()] = bits_per_pixel;
}
let mut background_palette_objects = Vec::with_capacity(114);
for i in 0..114 {
background_palette_objects.push(BackgroundPalette::new(i, palette_bits[i] as u8));
}
let mut background_graphics_objects = Vec::with_capacity(103);
for i in 0..103 {
background_graphics_objects.push(Rc::new(RefCell::new(BackgroundGraphics::new(
i,
graphics_bits[i] as u8,
))));
}
Rom {
battle_background_objects,
background_palette_objects,
background_graphics_objects,
}
}
pub fn get_battle_background(&self, index: usize) -> Rc<RefCell<BattleBackground>> {
self.battle_background_objects[index].clone()
}
pub fn get_background_graphics(&self, index: usize) -> Rc<RefCell<BackgroundGraphics>> {
self.background_graphics_objects[index].clone()
}
pub fn get_background_palette(&self, index: usize) -> &BackgroundPalette {
&self.background_palette_objects[index]
}
}