Last active
February 19, 2020 13:15
-
-
Save yuriks/71c4a02526edf83273dae88c2de00a7f to your computer and use it in GitHub Desktop.
La-Mulana MSD parser using nom
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod parser; | |
// Actual range: 11 bits [0, 2048) | |
pub type TileIndex = u16; | |
#[derive(Debug)] | |
pub struct AnimatedTileFrame { | |
pub frames_wait: u8, | |
pub tile_index: TileIndex, | |
} | |
#[derive(Debug)] | |
pub struct AnimatedTile { | |
pub animate_in_boss: bool, | |
pub frames: Vec<AnimatedTileFrame>, | |
} | |
#[derive(Copy, Clone, Debug)] | |
pub struct Tile(u16); | |
#[derive(Copy, Clone, PartialEq, Eq)] | |
pub enum BlendType { | |
Empty = 0, | |
Standard = 1, | |
Add = 2, | |
Multiply = 3, | |
} | |
impl Tile { | |
pub fn tile_index(self) -> TileIndex { | |
self.0 & 0x7ff | |
} | |
pub fn blend_type(self) -> BlendType { | |
match self.0 >> 11 & 0x3 { | |
0 => BlendType::Empty, | |
1 => BlendType::Standard, | |
2 => BlendType::Add, | |
3 => BlendType::Multiply, | |
_ => unreachable!(), | |
} | |
} | |
pub fn h_flip(self) -> bool { | |
(self.0 >> 13 & 1) != 0 | |
} | |
/// Rotation in 90-degree steps | |
pub fn rotation(self) -> u32 { | |
(self.0 >> 14 & 0x3) as u32 | |
} | |
pub fn rotated_90(self) -> bool { | |
(self.0 >> 14 & 1) != 0 | |
} | |
pub fn rotated_180(self) -> bool { | |
(self.0 >> 15 & 1) != 0 | |
} | |
} | |
pub type SubLayer = Vec<Tile>; | |
#[derive(Debug)] | |
pub struct Layer { | |
// Dimensions in 20x20 graphics tiles | |
pub width: usize, | |
pub height: usize, | |
pub sublayers: Vec<SubLayer>, | |
} | |
#[derive(Debug)] | |
pub struct Room { | |
pub use_boss_graphics: bool, | |
pub gameplay_layer_index: usize, | |
// Dimensions in 10x10 collision tiles | |
pub collision_map_width: usize, | |
pub collision_map_height: usize, | |
pub collision_map: Vec<u8>, | |
pub layers: Vec<Layer>, | |
} | |
#[derive(Debug)] | |
pub struct MapMSD { | |
pub animated_tiles: Vec<AnimatedTile>, | |
pub graphics_file_id: u8, | |
pub rooms: Vec<Room>, | |
} | |
pub use parser::parse_msd; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use super::{AnimatedTile, AnimatedTileFrame, Layer, MapMSD, Room, SubLayer, Tile}; | |
use nom::bytes::complete::{tag, take}; | |
use nom::combinator::{map, map_res, verify}; | |
use nom::multi::{count, many_till}; | |
use nom::number::complete::{be_u16, be_u8}; | |
use nom::sequence::tuple; | |
use nom::IResult; | |
use nom::{bits, do_parse, named, take_bits, tuple}; | |
fn to_bool_strict(x: u8) -> Result<bool, ()> { | |
match x { | |
0 => Ok(false), | |
1 => Ok(true), | |
_ => Err(()), | |
} | |
} | |
fn animated_tile(i: &[u8]) -> IResult<&[u8], AnimatedTile> { | |
let (i, header) = be_u16(i)?; | |
let animate_in_boss = (header >> 15 & 1) != 0; | |
let frame_count = header & 0x7fff; | |
let (i, frames) = count( | |
map(be_u16, |entry| AnimatedTileFrame { | |
frames_wait: (entry >> 11 & 0x1f) as u8, | |
tile_index: entry & 0x7ff, | |
}), | |
frame_count as usize, | |
)(i)?; | |
Ok(( | |
i, | |
AnimatedTile { | |
animate_in_boss, | |
frames, | |
}, | |
)) | |
} | |
fn layer(i: &[u8]) -> IResult<&[u8], Layer> { | |
let (i, layer_width) = map(be_u16, |x| x as usize)(i)?; | |
let (i, layer_height) = map(be_u16, |x| x as usize)(i)?; | |
let (i, sublayer_count) = map(be_u8, |x| x as usize)(i)?; | |
let (i, sublayers) = count( | |
count(map(be_u16, |x| Tile(x)), layer_width * layer_height), | |
sublayer_count, | |
)(i)?; | |
Ok(( | |
i, | |
Layer { | |
width: layer_width, | |
height: layer_height, | |
sublayers, | |
}, | |
)) | |
} | |
fn room(i: &[u8]) -> IResult<&[u8], Room> { | |
let (i, use_boss_graphics) = map_res(be_u8, to_bool_strict)(i)?; | |
let (i, layer_count) = map(be_u8, |x| x as usize)(i)?; | |
let (i, gameplay_layer_index) = map(be_u8, |x| x as usize)(i)?; | |
let (i, collision_map_width) = map(be_u16, |x| x as usize)(i)?; | |
let (i, collision_map_height) = map(be_u16, |x| x as usize)(i)?; | |
let (i, collision_map) = map( | |
take(collision_map_width * collision_map_height), | |
|s: &[u8]| s.to_vec(), | |
)(i)?; | |
let (i, layers) = count(layer, layer_count)(i)?; | |
Ok(( | |
i, | |
Room { | |
use_boss_graphics, | |
gameplay_layer_index, | |
collision_map_width, | |
collision_map_height, | |
collision_map, | |
layers, | |
}, | |
)) | |
} | |
pub fn parse_msd(i: &[u8]) -> IResult<&[u8], MapMSD> { | |
let (i, (animated_tiles, _)) = many_till(animated_tile, tag([0, 0]))(i)?; | |
let (i, graphics_file_id) = be_u8(i)?; | |
let (i, room_count) = be_u16(i)?; | |
let (i, rooms) = count(room, room_count as usize)(i)?; | |
Ok(( | |
i, | |
MapMSD { | |
animated_tiles, | |
graphics_file_id, | |
rooms, | |
}, | |
)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment