use crate::map::*;
use crate::datafile_parse::{Item, Datafile, LoadError};
use crate::compression::{compress, decompress, DecompressionError};
use log::{warn, info};
use ndarray::Array2;
use structview::View;
use std::borrow::BorrowMut;
use std::convert::TryInto;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::fs;
use std::iter;
use std::mem;
use std::path::Path;
impl From<LoadError> for Error {
fn from(err: LoadError) -> Self {
Error::DatafileLoading(err)
}
}
#[derive(Debug)]
pub struct DatafileParseError {
pub cause: ErrorCause,
pub index: Option<u16>,
pub kind: DatafileParseErrorKind,
pub description: String,
}
impl From<DatafileParseError> for Error {
fn from(err: DatafileParseError) -> Self {
Error::DatafileParsing(err)
}
}
impl fmt::Display for DatafileParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl std::error::Error for DatafileParseError {}
impl DatafileParseError {
pub(crate) fn new(kind: DatafileParseErrorKind, description: String) -> DatafileParseError {
DatafileParseError {
cause: ErrorCause::Other,
index: None,
kind,
description
}
}
}
#[derive(Debug)]
pub enum DatafileParseErrorKind {
ItemDataOOB,
DataItemsOOB,
DataMismatch,
DataLength,
External,
Conversion,
UnknownType,
RangeOOB,
MissingItemType,
MapLogic,
Decompression,
MissingItem,
InvalidValue,
}
pub trait ErrorEdit: Sized {
fn set_error_index(self, index: u16) -> Self;
fn set_error_cause(self, err_cause: ErrorCause) -> Self;
}
impl<T> ErrorEdit for Result<T, DatafileParseError> {
fn set_error_index(self, index: u16) -> Self {
self.map_err(|mut err| {
err.index = Some(index);
err
})
}
fn set_error_cause(self, err_cause: ErrorCause) -> Self {
self.map_err(|mut err| {
if err.cause == ErrorCause::Other {
err.cause = err_cause;
}
err
})
}
}
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
pub enum ErrorCause {
Other,
Version,
Info,
Image,
Envelope,
Group,
Layer,
Sound,
AutoMapper,
}
impl From<DecompressionError> for DatafileParseError {
fn from(err: DecompressionError) -> Self {
DatafileParseError::new(
DatafileParseErrorKind::Decompression,
err.to_string(),
)
}
}
impl Item {
fn layer_variant(&self) -> LayerKind {
use LayerKind::*;
if self.item_data.len() < 2 {
return NoType;
}
match self.item_data[1] {
2 => {
if self.item_data.len() < 7 {
return NoTypeTilemap;
}
match self.item_data[6] {
0 => Tiles,
1 => Game,
2 => Tele,
4 => Speedup,
8 => Front,
16 => Switch,
32 => Tune,
id => UnknownTilemap(id),
} },
3 => Quads,
9 => SoundsDeprecated ,
10 => Sounds,
id => Unknown(id),
}
}
}
impl Item {
fn map_item_type(&self) -> ItemType {
use ItemType::*;
match self.type_id {
0 => Version,
1 => Info,
2 => Image,
3 => Envelope,
4 => Group,
5 => Layer,
6 => EnvPoints,
7 => Sound,
id => Unknown(id),
}
}
}
impl fmt::Debug for Item {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let item_type = self.map_item_type();
match item_type == ItemType::Layer {
true => write!(f, "ItemType: {:?}, LayerType: {:?}, id: {}, item_data: {:?}", item_type, self.layer_variant(), self.id, self.item_data),
false => write!(f, "ItemType: {:?}, id: {}, item_data: {:?}", item_type, self.id, self.item_data),
}
}
}
impl Item {
fn check_item_data_length(&self, expected: usize) -> Result<(), DatafileParseError> {
if self.item_data.len() < expected {
return Err(DatafileParseError::new(
DatafileParseErrorKind::ItemDataOOB,
format!("Expected {} elements, got {}. Dump: {:?}", expected, self.item_data.len(), self),
));
}
Ok(())
}
fn check_max_size(&self, max: usize, ident: &str) {
if self.item_data.len() > max {
info!("{} item expected length: {}, actual length: {}. Item dump: {:?}", ident, max, self.item_data.len(), self);
}
}
fn check_data_mismatch<T: PartialEq>(&self, some: T, other: T, ident: &str) -> Result<(), DatafileParseError> {
if some != other {
Err(DatafileParseError::new(
DatafileParseErrorKind::DataMismatch,
format!("Contradiction on '{}'. Item dump: {:?}.", ident, self),
))
}
else {
Ok(())
}
}
}
fn missing_item_type(keys: Vec<u16>) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::MissingItemType,
format!("UUID item defined a item type by its matching uuid, however the matching item type could not be found in the datafile. Available keys: {:?}.", keys)
)
}
fn data_items_oob(expected: usize, length: i32) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::DataItemsOOB,
format!("Tried to access the {}th data item, there are only {}", expected, length),
)
}
fn data_length(content: String) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::DataLength,
content,
)
}
fn external(content: String) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::External,
format!("An external library failed: {}.", content),
)
}
fn conversion(content: String) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::Conversion,
content,
)
}
fn bool(val: i32, ident: &str) -> Result<bool, DatafileParseError> {
match val {
0 => Ok(false),
1 => Ok(true),
_ => Err(conversion(format!("'{}' should be a boolean, invalid value: {}.", ident, val)))
}
}
fn usize_option_index(val: i32, ident: &str) -> Result<Option<usize>, DatafileParseError> {
match val {
-1 => Ok(None),
x if x >= 0 => Ok(Some(x as usize)),
_ => Err(conversion(format!("'{}' is a optional i32 index, so either -1 or a positive number, invalid value: {}.", ident, val)))
}
}
fn u16_option_index(val: i32, ident: &str) -> Result<Option<u16>, DatafileParseError> {
match val {
-1 => Ok(None),
_ => match val.try_into() {
Err(_) => Err(conversion(format!("'{}' is a optional u16 index, so either -1 or some u16, invalid value: {}.", ident, val))),
Ok(x) => Ok(Some(x))
}
}
}
fn i32_to_u16(val: i32, ident: &str) -> Result<u16, DatafileParseError> {
match val.try_into() {
Err(_) => Err(conversion(format!("'{}' should fit into an u16, invalid value: {}.", ident, val))),
Ok(x) => Ok(x)
}
}
fn positive(val: i32, ident: &str) -> Result<i32, DatafileParseError> {
match val {
x if x >= 0 => Ok(x),
_ => Err(conversion(format!("'{}' should be positive, invalid value: {}.", ident, val)))
}
}
fn u8(val: i32, ident: &str) -> Result<u8, DatafileParseError> {
match val.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(conversion(format!("'{}' should fit into a u8, invalid value: {}.", ident, val))),
}
}
fn unknown_type(content: String) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::UnknownType,
content,
)
}
fn min_n(var: i32, min: i32, ident: &str) -> Result<i32, DatafileParseError> {
if var < min {
return Err(conversion(format!("{} must be at least {}, invalid value: {}.", ident, min, var)));
}
Ok(var)
}
fn map_logic(content: String) -> DatafileParseError {
DatafileParseError::new(
DatafileParseErrorKind::MapLogic,
content,
)
}
fn should_be<T: PartialEq + Debug>(val: T, expected: T, ident: &str) -> T {
if val != expected {
warn!("{} should be {:?}, invalid value: {:?}, defaulting to its expected value.", ident, expected, val);
}
expected
}
impl TwMap {
pub fn parse_file_unchecked<P: AsRef<Path>>(path: P) -> Result<TwMap, Error> {
let data = fs::read(path)?;
TwMap::parse_unchecked(&data)
}
pub fn parse_file<P: AsRef<Path>>(path: P, version: Option<Version>) -> Result<TwMap, Error> {
let map = TwMap::parse_file_unchecked(path)?;
map.check(version)?;
Ok(map)
}
pub fn parse(data: &[u8], version: Option<Version>) -> Result<TwMap, Error> {
let map = TwMap::parse_unchecked(data)?;
map.check(version)?;
Ok(map)
}
pub fn parse_unchecked(data: &[u8]) -> Result<TwMap, Error> {
let (remaining, datafile) = Datafile::parse(data)?;
if remaining.len() > 0 {
warn!("{} trailing bytes.", remaining.len());
}
TwMap::parse_datafile_unchecked(datafile)
}
pub fn parse_datafile_unchecked(datafile: Datafile) -> Result<TwMap, Error> {
use ErrorCause::*;
let (groups, layer_ranges) = datafile.map_groups()
.set_error_cause(ErrorCause::Group)?;
let mut map = TwMap {
info: datafile.map_info()
.set_error_cause(Info)?,
images: datafile.map_images()
.set_error_cause(Image)?,
envelopes: datafile.map_envelopes()
.set_error_cause(Envelope)?,
groups,
sounds: datafile.map_sounds()
.set_error_cause(Sound)?,
};
map.init_layers(&datafile, layer_ranges)
.set_error_cause(ErrorCause::Layer)?;
map.init_auto_mappers(&datafile)
.set_error_cause(ErrorCause::AutoMapper)?;
let removed = map.remove_duplicate_physics_layers();
if removed > 0 {
warn!("Removed {} duplicate physics layers.", removed);
}
Ok(map)
}
fn init_layers(&mut self, df: &Datafile, layer_ranges: Vec<(usize, usize)>) -> Result<(), DatafileParseError> {
let mut layers = df.map_layers()?;
for (i, layer_range) in layer_ranges.into_iter().enumerate() {
for _ in 0..layer_range.1 - layer_range.0 {
self.groups[i].layers.push(layers.remove(0))
}
}
assert_eq!(layers.len(), 0);
Ok(())
}
fn init_auto_mappers(&mut self, df: &Datafile) -> Result<(), DatafileParseError> {
let auto_mappers = df.map_auto_mappers()?;
let mut auto_mapper_structure = auto_mapper_structure(&self.groups);
for (i, (auto_mapper, (group_index, layer))) in auto_mappers.into_iter().enumerate() {
check_auto_mapper_position(group_index, layer, &mut auto_mapper_structure)
.set_error_index(i as u16)?;
let group = &mut self.groups[group_index as usize];
match group.layers[layer as usize].borrow_mut() {
Layer::Tiles(layer) => {
layer.auto_mapper = auto_mapper;
}
_ => {},
}
}
Ok(())
}
}
impl Datafile<'_> {
fn data_item_get(&self, index: usize) -> Result<Vec<u8>, DatafileParseError> {
match self.data_item(index) {
Ok(data) => {
match data {
Some(data) => Ok(data),
None => return Err(data_items_oob(index, self.header.num_data)),
}
},
Err(error) => Err(external(format!("Data item retrieval: {:?}", error))),
}
}
fn compressed_data_item_get(&self, index: usize) -> Result<(Vec<u8>, i32), DatafileParseError> {
match self.compressed_data_item(index) {
Some((data, size)) => Ok((data, size)),
None => Err(data_items_oob(index, self.header.num_data)),
}
}
}
pub(crate) fn uuid_equal(u1: &[u8; 16], u2: &[i32; 4]) -> bool {
u1.chunks(4)
.map(|bytes| i32::from_be_bytes(bytes[0..4].try_into().unwrap()))
.zip(u2.iter())
.all(|(x, &y)| x == y)
}
impl Datafile<'_> {
fn type_id_by_uuid(&self, uuid: &[u8; 16]) -> Result<Option<u16>, DatafileParseError> {
let ex_type_id = 0xffff;
let ex_items = match self.items.get(&ex_type_id) {
Some(ex_items) => ex_items,
None => return Ok(None),
};
for item in ex_items.iter() {
item.check_item_data_length(4)?;
let item_uuid = item.item_data[..4].try_into().unwrap();
if uuid_equal(uuid, item_uuid) {
return Ok(Some(item.id));
}
}
Ok(None)
}
fn items_by_uuid(&self, uuid: &[u8; 16]) -> Result<Option<&[Item]>, DatafileParseError> {
let type_id = match self.type_id_by_uuid(uuid)? {
Some(type_id) => type_id,
None => return Ok(None),
};
match self.items.get(&type_id) {
None => Err(missing_item_type(self.items.keys().map(|&x| x).collect())),
Some(items) => Ok(Some(items)),
}
}
}
#[derive(Debug)]
pub struct MapVersion {
pub version: i32,
}
impl Datafile<'_> {
pub fn map_version(&self) -> Result<i32, DatafileParseError> {
let version_items = match self.items.get(&0) {
Some(info_items) => info_items,
None => {
return Err(map_logic(String::from("Map version item missing.")))
},
};
if version_items.len() > 1 {
return Err(map_logic(format!("Multiple ({}) map version items instead of one.", version_items.len())));
}
let item = &version_items[0];
item.check_item_data_length(1)?;
let version = item.item_data[0];
if version != 1 {
return Err(map_logic(format!("Map version must be 1, invalid value: {}", version)));
}
Ok(version)
}
}
fn parse_string(data: &[u8]) -> String {
match String::from_utf8(data.to_vec()) {
Ok(str) => str,
Err(_) => {
info!("String contained invalid utf8, replaced with empty string");
String::new()
}
}
}
impl Datafile<'_> {
fn get_option_string(&self, index: Option<usize>) -> Result<String, DatafileParseError> {
match index {
None => Ok(String::new()),
Some(index) => {
self.get_string(index)
},
}
}
fn get_string(&self, index: usize) -> Result<String, DatafileParseError> {
let bytes = self.data_item_get(index as usize)?;
Ok(parse_string(&bytes[..bytes.len() - 1]))
}
fn get_multiple_strings(&self, index: Option<usize>) -> Result<Vec<String>, DatafileParseError> {
match index {
None => Ok(Vec::new()),
Some(index) => {
let data = &self.data_item_get(index as usize)?[..];
Ok(data[..data.len() - 1]
.split(|&x| x == 0)
.map(|x| parse_string(x))
.collect())
},
}
}
}
impl Datafile<'_> {
fn map_info(&self) -> Result<Info, DatafileParseError> {
let info_items = match self.items.get(&1) {
Some(info_items) => info_items,
None => return Ok(Info::default()),
};
if info_items.len() > 1 {
return Err(map_logic(format!("Multiple ({}) map info items instead of one.", info_items.len())));
}
let item = &info_items[0];
item.check_item_data_length(5)?;
let _version = min_n(item.item_data[0], 1, "Info version")?;
let author_index = usize_option_index(item.item_data[1], "Info author index")?;
let map_version_index = usize_option_index(item.item_data[2], "Info map version index")?;
let credits_index = usize_option_index(item.item_data[3], "Info credits index")?;
let license_index = usize_option_index(item.item_data[4], "Info license index")?;
let mut settings_index = None;
if item.item_data.len() >= 6 {
settings_index = usize_option_index(item.item_data[5], "Info settings index")?;
}
item.check_max_size(6, "Info");
Ok(Info {
author: self.get_option_string(author_index)?,
version: self.get_option_string(map_version_index)?,
credits: self.get_option_string(credits_index)?,
license: self.get_option_string(license_index)?,
settings: self.get_multiple_strings(settings_index)?,
})
}
}
impl Datafile<'_> {
fn parse_map_image(&self, item: &Item) -> Result<Image, DatafileParseError> {
item.check_item_data_length(6)?;
let version = min_n(item.item_data[0], 1, "Image version")?;
let width = item.item_data[1];
let height = item.item_data[2];
let name_index = positive(item.item_data[4], "Image name index")? as usize;
let name = self.get_string(name_index)?;
if version >= 2 {
item.check_item_data_length(7)?;
item.check_max_size(7, "Image");
match item.item_data[6] {
0 => Err(map_logic(String::from("Rgb image support is deprecated")))?,
1 => {},
n => Err(map_logic(format!("Invalid value in sixth field of item_data, expected: 1, value: {}", n)))?,
};
}
else {
item.check_max_size(6, "Image");
}
let is_external = bool(item.item_data[3], "is_external")?;
let data_index = usize_option_index(item.item_data[5], "data_index of image")?;
item.check_data_mismatch(data_index.is_none(), is_external, "if image is external or embedded")?;
let data = match data_index {
None => None,
Some(index) => {
let (compressed_data, expected_size) = self.compressed_data_item_get(index as usize)?;
Some(CompressedData::Compressed(compressed_data, expected_size, ()))
}
};
Ok(Image {
width,
height,
name,
data,
})
}
fn map_images(&self) -> Result<Vec<Image>, DatafileParseError> {
let image_items = match self.items.get(&2) {
Some(image_items) => image_items,
None => return Ok(Vec::new()),
};
let mut images = Vec::new();
for (i, image_item) in image_items.iter().enumerate() {
let new_image = self.parse_map_image(image_item)
.set_error_index(i as u16)?;
images.push(new_image)
}
Ok(images)
}
}
impl LayerKind {
pub fn is_physics_layer(&self) -> bool {
use LayerKind::*;
match self {
Game | Front | Tele | Speedup | Switch | Tune => true,
_ => false,
}
}
}
impl Group {
fn physics_check_defaults(&mut self) -> Result<(), DatafileParseError> {
self.offset_x = should_be(self.offset_x, 0, "Physics layer x_offset");
self.offset_y = should_be(self.offset_y, 0, "Physics layer y_offset");
self.parallax_x = should_be(self.parallax_x, 100, "Physics layer x_parallax");
self.parallax_y = should_be(self.parallax_y, 100, "Physics layer y_parallax");
self.clipping = should_be(self.clipping, false, "Physics layer clipping");
self.clip_x = should_be(self.clip_x, 0, "Physics layer x_clip");
self.clip_y = should_be(self.clip_y, 0, "Physics layer y_clip");
self.clip_width = should_be(self.clip_width, 0, "Physics layer w_clip");
self.clip_height = should_be(self.clip_height, 0, "Physics layer h_clip");
self.name = should_be(self.name.clone(), String::from("Game"), "Physics layer name");
Ok(())
}
}
impl Datafile<'_> {
fn parse_map_group(&self, item: &Item) -> Result<(Group, (usize, usize)), DatafileParseError> {
item.check_item_data_length(7)?;
let version = item.item_data[0];
let first = positive(item.item_data[5], "Group first layer index")? as usize;
let amount = positive(item.item_data[6], "Group layer count")? as usize;
let layer_range = (first, first + amount);
let mut clipping = false;
let mut x_clip = 0;
let mut y_clip = 0;
let mut w_clip = 0;
let mut h_clip = 0;
let mut name = String::new();
if version >= 2 {
item.check_item_data_length(12)?;
clipping = bool(item.item_data[7], "Group clipping")?;
x_clip = item.item_data[8];
y_clip = item.item_data[9];
w_clip = item.item_data[10];
h_clip = item.item_data[11];
}
if version >= 3 {
item.check_item_data_length(15)?;
name = string_from_i32( &item.item_data[12..15]);
item.check_max_size(15, "Group");
}
else {
item.check_max_size(12, "Group");
}
Ok((Group {
offset_x: item.item_data[1],
offset_y: item.item_data[2],
parallax_x: item.item_data[3],
parallax_y: item.item_data[4],
layers: vec![],
clipping,
clip_x: x_clip,
clip_y: y_clip,
clip_width: w_clip,
clip_height: h_clip,
name,
}, layer_range))
}
fn check_groups(&self, groups: &mut Vec<Group>, layer_ranges: &mut Vec<(usize, usize)>) -> Result<(), DatafileParseError> {
let layer_items = match self.items.get(&5) {
None => return Err(map_logic(format!("No layers found."))),
Some(layer_items) => layer_items,
};
let layer_count = layer_items.len();
let mut end = 0;
if layer_ranges.len() > 1 {
if layer_ranges[0].1 == 1 && layer_ranges[1].0 == 0 && groups[0].name == "Game" {
warn!("Grouping is messed up (presumable by the teeuniverse editor), data was changed slightly.");
layer_ranges[0].1 = 0;
let groups_len = groups.len();
let mut last_layer_range = &mut layer_ranges[groups_len - 1];
let diff = last_layer_range.1 - layer_count;
let last_group_layer_count = last_layer_range.1 - last_layer_range.0;
if diff > 0 && diff <= 2 && last_group_layer_count == 11 {
last_layer_range.1 = layer_count;
}
}
}
for layer_range in layer_ranges.iter() {
if layer_range.0 < end {
return Err(map_logic(format!("Overlapping groups, last group ended at index {}, new one has the range {:?}.", end, layer_range)));
}
else if layer_range.0 > end {
return Err(map_logic(format!("Orphaned layer, last group ended at index {}, new one has the range {:?}.", end, layer_range)));
}
end = layer_range.1;
}
if end != layer_count {
return Err(map_logic(format!("Total layer count doesn't match up with the last layer of the last group. total layers: {}, range of last group: {:?}.",
layer_count, layer_ranges[layer_ranges.len() - 1])));
}
let mut physics_group = None;
let mut physics_group_count = 0;
for (i, layer_range) in layer_ranges.iter().enumerate() {
let group_layers = &layer_items[layer_range.0 as usize..layer_range.1 as usize];
if group_layers.iter()
.any(|item| item.layer_variant().is_physics_layer()) {
physics_group = Some(&mut groups[i]);
physics_group_count += 1;
}
}
if physics_group_count == 1 {
physics_group.unwrap().physics_check_defaults()?;
}
Ok(())
}
fn map_groups(&self) -> Result<(Vec<Group>, Vec<(usize, usize)>), DatafileParseError> {
let group_items = match self.items.get(&4) {
Some(group_items) => group_items,
None => return Ok((Vec::new(), Vec::new())),
};
let mut groups = Vec::new();
let mut layer_ranges = Vec::new();
for (i, group_item) in group_items.iter().enumerate() {
let (new_group, new_layer_range) = self.parse_map_group(group_item)
.set_error_index(i as u16)?;
groups.push(new_group);
layer_ranges.push(new_layer_range);
}
self.check_groups(&mut groups, &mut layer_ranges)?;
Ok((groups, layer_ranges))
}
}
impl Datafile<'_> {
fn parse_map_sound(&self, item: &Item) -> Result<Sound, DatafileParseError>{
item.check_item_data_length(5)?;
let _version = min_n(item.item_data[0], 1, "Sound version")?;
let is_external = bool(item.item_data[1], "Sound external")?;
let name_index = positive(item.item_data[2], "Sound name index")? as usize;
let name = self.get_string(name_index)?;
let data_index = usize_option_index(item.item_data[3], "Sound data index")?;
let expected_data_size = positive(item.item_data[4], "Sound data_size")?;
item.check_data_mismatch(data_index.is_none(), is_external, "whether sound is embedded or external")?;
let data = match data_index {
None => return Err(map_logic(String::from("Sound data is not optional"))),
Some(index) => {
let (data, data_size) = self.compressed_data_item_get(index as usize)?;
if data_size != expected_data_size {
Err(data_length(format!("Sound data of length {} expected, got data of length {}", expected_data_size, data_size)))?;
}
CompressedData::Compressed(data, data_size, ())
},
};
item.check_max_size(5, "Sound");
Ok(Sound {
name,
data,
})
}
fn map_sounds(&self) -> Result<Vec<Sound>, DatafileParseError> {
let sound_items = match self.items.get(&7) {
Some(sound_item_type) => sound_item_type,
None => return Ok(Vec::new()),
};
let mut sounds = Vec::new();
for (i, sound_item) in sound_items.iter().enumerate() {
let new_sound = self.parse_map_sound(sound_item)
.set_error_index(i as u16)?;
sounds.push(new_sound);
}
Ok(sounds)
}
}
impl Default for AutoMapper {
fn default() -> Self {
AutoMapper {
config: None,
seed: 0,
automatic: false
}
}
}
fn parse_map_auto_mapper(item: &Item) -> Result<(AutoMapper, (u16, u16)), DatafileParseError> {
item.check_item_data_length(6)?;
item.check_max_size(6, "Automapper");
let group = i32_to_u16(item.item_data[1], "Auto mapper group")?;
let layer = i32_to_u16(item.item_data[2], "Auto mapper layer")?;
let auto_mapper = AutoMapper {
config: u16_option_index(item.item_data[3], "Auto mapper config")?,
seed: item.item_data[4],
automatic: bool(item.item_data[5], "Auto mapper automatic")?,
};
Ok((auto_mapper, (group, layer)))
}
impl AutoMapper {
pub(crate) fn uuid() -> [u8; 16] {
[0x3e, 0x1b, 0x27, 0x16, 0x17, 0x8c, 0x39, 0x78, 0x9b, 0xd9, 0xb1, 0x1a, 0xe0, 0x41, 0xd, 0xd8]
}
}
impl Datafile<'_> {
fn map_auto_mappers(&self) -> Result<Vec<(AutoMapper, (u16, u16))>, DatafileParseError> {
let uuid = AutoMapper::uuid();
let auto_mapper_items = match self.items_by_uuid(&uuid)? {
Some(auto_mapper_items) => auto_mapper_items,
None => return Ok(Vec::new()),
};
let mut auto_mappers = Vec::new();
for (i, auto_mapper_item) in auto_mapper_items.iter().enumerate() {
let new_auto_mapper = parse_map_auto_mapper(auto_mapper_item)
.set_error_index(i as u16)?;
auto_mappers.push(new_auto_mapper);
}
Ok(auto_mappers)
}
}
impl Layer {
fn is_tiles_layer(&self) -> bool {
match self {
Layer::Game(_) => true,
Layer::Tiles(_) => true,
Layer::Quads(_) => false,
Layer::Front(_) => true,
Layer::Tele(_) => true,
Layer::Speedup(_) => true,
Layer::Switch(_) => true,
Layer::Tune(_) => true,
Layer::Sounds(_) => false,
Layer::Invalid(_) => false,
}
}
}
fn auto_mapper_structure(groups: &[Group]) -> Vec<Vec<Option<bool>>> {
let mut structure = Vec::new();
for group in groups {
let mut sub_structure = Vec::new();
for layer in &group.layers {
sub_structure.push(match layer.is_tiles_layer() {
true => Some(false),
false => None,
})
}
structure.push(sub_structure);
}
structure
}
fn check_auto_mapper_position(group: u16, layer: u16, structure: &mut Vec<Vec<Option<bool>>>) -> Result<(), DatafileParseError> {
match structure.get(group as usize) {
None => return Err(DatafileParseError::new(
DatafileParseErrorKind::MissingItem,
format!("AutoMapper references layer (with index {}) in group with index {}, there are only {} groups",
layer, group, structure.len())
)),
Some(layers) => {
match layers.get(layer as usize) {
None => return Err(DatafileParseError::new(
DatafileParseErrorKind::MissingItem,
format!("AutoMapper references layer with index {} (in group with index {}), there are only {} layers in that group",
layer, group, layers.len())
)),
Some(auto_mapper_state) => match auto_mapper_state {
None => return Err(DatafileParseError::new(
DatafileParseErrorKind::MissingItem,
String::from("AutoMapper references layer that can't hold an automapper")
)),
Some(used) => {
match used {
true => return Err(DatafileParseError::new(
DatafileParseErrorKind::InvalidValue,
String::from("AutoMapper references layer that already holds an automapper")
)),
false => structure[group as usize][layer as usize] = Some(true),
}
}
}
}
}
}
Ok(())
}
pub trait EnvPointContentParsing {
fn from_raw(raw: [i32; 4]) -> Self;
}
impl EnvPointContentParsing for i32 {
fn from_raw(raw: [i32; 4]) -> Self {
raw[0]
}
}
impl EnvPointContentParsing for Position {
fn from_raw(raw: [i32; 4]) -> Self {
Position {
x: raw[0],
y: raw[1],
rotation: raw[2],
}
}
}
impl EnvPointContentParsing for I32Color {
fn from_raw(raw: [i32; 4]) -> Self {
I32Color {
r: raw[0],
g: raw[1],
b: raw[2],
a: raw[3],
}
}
}
fn curve_type<T>(id: i32, bezier: Option<[i32; 16]>) -> CurveKind<[i32; 4]> {
match id {
0 => CurveKind::Step,
1 => CurveKind::Linear,
2 => CurveKind::Slow,
3 => CurveKind::Fast,
4 => CurveKind::Smooth,
5 => {
match bezier {
None => CurveKind::Unknown(id),
Some(values) => CurveKind::Bezier(BezierCurve {
in_tangent_dx: values[0..4].try_into().unwrap(),
in_tangent_dy: values[4..8].try_into().unwrap(),
out_tangent_dx: values[8..12].try_into().unwrap(),
out_tangent_dy: values[12..16].try_into().unwrap()
}),
}
}
unknown => CurveKind::Unknown(unknown),
}
}
impl<T: EnvPointContentParsing> From<CurveKind<[i32; 4]>> for CurveKind<T> {
fn from(curve_type: CurveKind<[i32; 4]>) -> Self {
use CurveKind::*;
match curve_type {
Step => Step,
Linear => Linear,
Slow => Slow,
Fast => Fast,
Smooth => Smooth,
Unknown(n) => Unknown(n),
Bezier(b) => Bezier(BezierCurve {
in_tangent_dx: T::from_raw(b.in_tangent_dx),
in_tangent_dy: T::from_raw(b.in_tangent_dy),
out_tangent_dx: T::from_raw(b.out_tangent_dx),
out_tangent_dy: T::from_raw(b.out_tangent_dy),
}),
}
}
}
impl<T: EnvPointContentParsing> EnvPoint<T> {
fn parse_v1(data: [i32; 6]) -> Self {
let time = data[0];
let curve = curve_type::<T>(data[1], None).into();
let content = T::from_raw(data[2..6].try_into().unwrap());
Self {
time,
content,
curve,
}
}
fn parse_v2(data: [i32; 22]) -> Self {
let mut v1 = Self::parse_v1(data[..6].try_into().unwrap());
let bezier_data = data[6..].try_into().unwrap();
let curve = curve_type::<T>(data[1], Some(bezier_data)).into();
v1.curve = curve;
v1
}
fn parse_range(data: &[i32], num: usize, version: i32) -> Vec<Self> {
let mut points = Vec::new();
match version {
1 | 2 => for i in 0..num {
points.push(Self::parse_v1(data[i * 6..(i + 1) * 6].try_into().unwrap()));
}
_ => for i in 0..num {
points.push(Self::parse_v2(data[i * 22..(i + 1) * 22].try_into().unwrap()))
}
}
points
}
}
impl Datafile<'_> {
fn raw_env_points(&self) -> Result<&[i32], DatafileParseError> {
let env_point_items = match self.items.get(&6) {
Some(env_point_items) => env_point_items,
None => return Ok(&[]),
};
Ok(&env_point_items[0].item_data[..])
}
}
impl<T: EnvPointContentParsing> Env<T> {
fn parse(item: &Item, raw_env_points: &[i32], expected_version: i32, last: &mut i32) -> Result<Self, DatafileParseError> {
item.check_item_data_length(5)?;
let version = min_n(item.item_data[0], 1, "Envelope version")?;
if version != expected_version {
return Err(map_logic(format!("Envelopes don't have a uniform version. First envelope: {}, {}th envelope: {}",
expected_version, item.id + 1, version
)))
}
let size = match version {
1 | 2 => 6,
_ => 22,
};
let first = positive(item.item_data[2], "Envelope first")?;
if first > *last {
return Err(map_logic(format!("Overlapping Envelopes (ids {}, {})", item.id, item.id - 1)));
}
else if first < *last {
return Err(map_logic(format!("Orphaned envelope point(s) (between envelopes with ids {}, {})", item.id, item.id - 1)));
}
let num = positive(item.item_data[3], "Envelope num")?;
*last = first + num;
let start_index = (first * size) as usize;
let end_index = ((first + num) * size) as usize;
let points = match raw_env_points.get(start_index..end_index) {
None => return Err(data_length(format!("Envelope: didn't get enough envelope point data."))),
Some(raw_env_points) => {
EnvPoint::<T>::parse_range(raw_env_points, num as usize, version)
},
};
let mut name = String::new();
let mut synchronized = false;
if item.item_data.len() > 5 {
item.check_item_data_length(12)?;
name = string_from_i32(&item.item_data[4..12]);
if version >= 2 {
item.check_item_data_length(13)?;
item.check_max_size(13, "Envelope");
synchronized = bool(item.item_data[12], "Envelope synchronized")?;
}
else {
item.check_max_size(12, "Envelope");
}
}
else {
item.check_max_size(5, "Envelope");
}
Ok(Self {
name,
synchronized,
points,
})
}
}
fn parse_envelope(item: &Item, raw_env_points: &[i32], expected_version: i32, last: &mut i32) -> Result<Envelope, DatafileParseError> {
item.check_item_data_length(2)?;
let envelope_type = item.item_data[1];
match envelope_type {
1 => Ok(Envelope::Sound(Env::<i32>::parse(item, raw_env_points, expected_version, last)?)),
3 => Ok(Envelope::Position(Env::<Position>::parse(item, raw_env_points, expected_version, last)?)),
4 => Ok(Envelope::Color(Env::<I32Color>::parse(item, raw_env_points, expected_version, last)?)),
unknown => Err(unknown_type(format!("Unknown envelope type: {}", unknown)))
}
}
impl Item {
fn envelope_version(&self) -> Result<i32, DatafileParseError> {
if self.map_item_type() != ItemType::Envelope {
panic!("envelope_version used on non-envelope item");
}
self.check_item_data_length(1)?;
Ok(self.item_data[0])
}
}
impl Datafile<'_> {
fn map_envelopes(&self) -> Result<Vec<Envelope>, DatafileParseError> {
let raw_env_points = self.raw_env_points()?;
let envelope_items = match self.items.get(&3) {
Some(envelope_items) => envelope_items,
None => return Ok(Vec::new()),
};
let envelope_version = envelope_items[0].envelope_version()?;
let size = match envelope_version {
1 | 2 => 6,
_ => 22,
};
if raw_env_points.len() % size != 0 {
return Err(data_length(format!("Data length given for envelope points: {}. Doesn't divide by the envelope point size ({}).",
raw_env_points.len(), size
)))
}
let mut last = 0;
let envelopes = envelope_items
.iter()
.enumerate()
.map(|(i, item)| parse_envelope(item, raw_env_points, envelope_version, &mut last)
.set_error_index(i as u16))
.collect();
if last as usize != raw_env_points.len() / size {
return Err(map_logic(format!("Orphaned envelope point(s) at end. Used are {} out of {}.",
last, raw_env_points.len() / size
)));
}
envelopes
}
}
pub trait PhysicsLayer: TileMapLayer + Sized {
fn from_parts(tiles: CompressedData<Array2<Self::TileType>, TilesLoadInfo>) -> Self;
fn parse_layer(item: &Item, datafile: &Datafile) -> Result<Self, DatafileParseError> {
item.check_max_size(23, "Physics layer");
let version = min_n(item.item_data[3], 1, "Physics layer version")?;
let variant = item.layer_variant();
let mut data_index = Self::data_index();
if version < 3 {
if data_index > 14 {
data_index -= 3;
}
} else {
item.check_item_data_length(18)?;
let name = string_from_i32(&item.item_data[15..18]);
if name != Self::name() {
warn!("Physics layer ({}) didn't match by name, invalid name: {}", Self::name(), name);
}
}
item.check_item_data_length(data_index + 1)?;
let data_item_index = positive(item.item_data[data_index], "Physics layer data item index")? as usize;
let width = positive(item.item_data[4], "Physics layer width")?;
let height = positive(item.item_data[5], "Physics layer height")?;
let compression = match variant {
LayerKind::Game => version >= 4,
_ => false,
};
if Self::kind() != LayerKind::Game {
let fake_data_index = positive(item.item_data[GameLayer::data_index()], "Physics layer fake data index")? as usize;
let expected_fake_data_size = width as usize * height as usize * mem::size_of::<Tile>();
let (fake_tiles_data, size) = datafile.compressed_data_item_get(fake_data_index)?;
let decompressed_fake_tiles_data = decompress(&fake_tiles_data, size as usize)?;
if decompressed_fake_tiles_data.len() != expected_fake_data_size {
Err(data_length(String::from("Fake tiles data has an incorrect size")))?;
}
if decompressed_fake_tiles_data.iter()
.any(|byte| *byte != 0) {
Err(map_logic(String::from("Fake tiles data is not all zeroes")))?;
}
}
let (tiles_data, data_size) = datafile.compressed_data_item_get(data_item_index as usize)?;
let (tiles_data, size) = Self::TileType::try_correct_data(tiles_data, data_size, width, height)?;
let tiles = CompressedData::Compressed(tiles_data, size, TilesLoadInfo { width, height, compression });
Ok(Self::from_parts(tiles))
}
fn data_index() -> usize;
fn name() -> &'static str;
}
pub trait TileParsing: Sized + Clone + View {
fn parse_tiles(data: Vec<u8>, width: usize, height: usize, _compression: bool) -> Result<Array2<Self>, DecompressionError> {
Ok(Self::parse_tiles_raw(data, width, height))
}
fn parse_tiles_raw(data: Vec<u8>, width: usize, height: usize) -> Array2<Self> {
let tiles = match Self::view_boxed_slice(data.into_boxed_slice()) {
Ok(tiles) => tiles.into_vec(),
Err(_) => panic!("(parse_tiles): Viewing of data vector failed."),
};
match Array2::from_shape_vec((height, width), tiles) {
Ok(tiles) => tiles,
Err(_) => panic!("Error while converting vector to 2d-array."),
}
}
fn try_correct_data(data: Vec<u8>, data_size: i32, width: i32, height: i32) -> Result<(Vec<u8>, i32), DecompressionError> {
if width >= 0 && height >= 0 {
let outdated_versions = Self::outdated_versions();
for version in outdated_versions {
let alt_size = width as usize * height as usize * version.bytes_per_tile;
if data_size as usize == alt_size {
info!("Outdated tiles format for tile of type {} that uses {} bytes", std::any::type_name::<Self>(), version.bytes_per_tile);
let decompressed_data = decompress(&data, data_size as usize)?;
return Ok((compress(&convert_to_new(&decompressed_data, version)), width * height * mem::size_of::<Self>() as i32))
}
}
}
Ok((data, data_size))
}
fn outdated_versions() -> &'static [OutdatedTileVersion] {
&[]
}
}
fn convert_to_new(data: &[u8], tile_version: &OutdatedTileVersion) -> Vec<u8> {
let tiles = data.chunks(tile_version.bytes_per_tile)
.map(|tile_data| (tile_version.convert_fnc)(tile_data));
let mut altered_data = Vec::new();
for mut tile in tiles {
altered_data.append(&mut tile);
}
altered_data
}
impl TileParsing for Tile {
fn parse_tiles(mut data: Vec<u8> , width: usize, height: usize, compression: bool) -> Result<Array2<Self>, DecompressionError> {
if compression {
data = data.chunks(4)
.flat_map(|tile | {
iter::repeat(tile).take(tile[2] as usize + 1)
.flat_map(|tile| [tile[0], tile[1], 0, tile[3]].to_vec())
})
.collect()
}
let expected_size = width * height * mem::size_of::<Tile>();
if data.len() > expected_size {
Err(DecompressionError::TooBig)
}
else if data.len() < expected_size {
Err(DecompressionError::TooSmall)
}
else {
Ok(Tile::parse_tiles_raw(data, width, height))
}
}
}
impl TileParsing for Tele {}
fn convert_old_speedup(data: &[u8]) -> Vec<u8> {
let mut speedup = [0; 6];
speedup[0] = data[0];
speedup[2] = 28;
speedup[4] = data[2];
speedup[5] = data[3];
speedup.to_vec()
}
static SPEEDUP_VERSIONS: [OutdatedTileVersion; 1] = [
OutdatedTileVersion {
bytes_per_tile: 4,
convert_fnc: convert_old_speedup,
}];
impl TileParsing for Speedup {
fn outdated_versions() -> &'static [OutdatedTileVersion] {
&SPEEDUP_VERSIONS[..]
}
}
fn convert_tele_to_switch(data: &[u8]) -> Vec<u8> {
let mut switch = [0; 4];
switch[0] = data[0];
switch[1] = data[1];
switch.to_vec()
}
fn convert_old_switch(data: &[u8]) -> Vec<u8> {
let mut switch = [0; 4];
switch[0] = data[0];
switch[1] = data[1];
switch[2] = data[2];
switch.to_vec()
}
static SWITCH_VERSIONS: [OutdatedTileVersion; 2] = [
OutdatedTileVersion {
bytes_per_tile: 2,
convert_fnc: convert_tele_to_switch,
},
OutdatedTileVersion{
bytes_per_tile: 3,
convert_fnc: convert_old_switch,
}];
impl TileParsing for Switch {
fn outdated_versions() -> &'static [OutdatedTileVersion] {
&SWITCH_VERSIONS[..]
}
}
impl TileParsing for Tune {}
impl PhysicsLayer for GameLayer {
fn from_parts(tiles: CompressedData<Array2<Tile>, TilesLoadInfo>) -> GameLayer {
GameLayer { tiles }
}
fn data_index() -> usize { 14 }
fn name() -> &'static str { "Game" }
}
impl PhysicsLayer for FrontLayer {
fn from_parts(tiles: CompressedData<Array2<Tile>, TilesLoadInfo>) -> FrontLayer {
FrontLayer { tiles }
}
fn data_index() -> usize { 20 }
fn name() -> &'static str { "Front" }
}
impl PhysicsLayer for TeleLayer {
fn from_parts(tiles: CompressedData<Array2<Tele>, TilesLoadInfo>) -> TeleLayer {
TeleLayer { tiles }
}
fn data_index() -> usize { 18 }
fn name() -> &'static str { "Tele" }
}
impl PhysicsLayer for SpeedupLayer {
fn from_parts(tiles: CompressedData<Array2<Speedup>, TilesLoadInfo>) -> SpeedupLayer {
SpeedupLayer { tiles }
}
fn data_index() -> usize { 19 }
fn name() -> &'static str { "Speedup" }
}
impl PhysicsLayer for SwitchLayer {
fn from_parts(tiles: CompressedData<Array2<Switch>, TilesLoadInfo>) -> SwitchLayer {
SwitchLayer { tiles }
}
fn data_index() -> usize { 21 }
fn name() -> &'static str { "Switch" }
}
impl PhysicsLayer for TuneLayer {
fn from_parts(tiles: CompressedData<Array2<Tune>, TilesLoadInfo>) -> TuneLayer {
TuneLayer { tiles }
}
fn data_index() -> usize { 22 }
fn name() -> &'static str { "Tune" }
}
fn color_from_i32_array(i32_values: [i32; 4]) -> Result<Color, DatafileParseError> {
let mut values = [0_u8; 4];
for i in 0..4 {
values[i] = match i32_values[i].try_into() {
Ok(x) => x,
Err(_) => return Err(conversion(format!("{} is too high for a color (u8) value. all values:{:?}", i32_values[i], i32_values)))
};
}
Ok(Color {
r: values[0],
g: values[1],
b: values[2],
a: values[3],
})
}
impl TilesLayer {
fn parse_layer(item: &Item, datafile: &Datafile) -> Result<TilesLayer, DatafileParseError> {
item.check_item_data_length(15)?;
let version = min_n(item.item_data[3], 1, "Tile layer version")?;
let width = positive(item.item_data[4], "Tile layer width")?;
let height = positive(item.item_data[5], "Tile layer height")?;
let compression = version >= 4;
let data_item_index = positive(item.item_data[14], "Tile layer data item index")? as usize;
let (compressed_data, data_size) = datafile.compressed_data_item_get(data_item_index as usize)?;
let tiles = CompressedData::Compressed(compressed_data, data_size, TilesLoadInfo { width, height, compression });
let mut name = String::from("Tiles");
if version >= 3 {
item.check_item_data_length(18)?;
item.check_max_size(23, "TileLayer");
name = string_from_i32(&item.item_data[15..18]);
}
else {
item.check_max_size(19, "TileLayer");
}
Ok(TilesLayer {
detail: item.item_data[2] & 0b1 == 1,
color: Color {
r: u8(item.item_data[7], "Tile layer color red")?,
g: u8(item.item_data[8],"Tile layer color green")?,
b: u8(item.item_data[9],"Tile layer color blue")?,
a: u8(item.item_data[10],"Tile layer color alpha")?,
},
color_env: u16_option_index(item.item_data[11], "Tile layer color envelope")?,
color_env_offset: item.item_data[12],
image: u16_option_index(item.item_data[13], "Tile layer image")?,
tiles,
name,
auto_mapper: AutoMapper::default(),
})
}
}
fn string_from_i32(numbers: &[i32]) -> String {
let mut name = Vec::new();
for i in 0..numbers.len() {
name.extend_from_slice(&numbers[i].to_be_bytes());
}
parse_string(&name[..name.len() - 1]
.into_iter()
.map(|&x| x.wrapping_sub(128))
.take_while(|&x| x != 0)
.collect::<Vec<u8>>()[..])
}
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "({}.{}, {}.{})", self.x / 1000, self.x % 1000, self.y / 1000, self.y % 1000)
}
}
fn point_from_bytes(data: &[u8]) -> (&[u8], Point) {
let x = i32::from_le_bytes(data[0..4].try_into().unwrap());
let y = i32::from_le_bytes(data[4..8].try_into().unwrap());
(&data[8..], Point { x, y })
}
fn color_from_i32_bytes(data: &[u8]) -> Result<(&[u8], Color), DatafileParseError> {
let mut values = [0; 4];
for i in 0..4 {
values[i] = i32::from_le_bytes(data[i * 4..i * 4 + 4].try_into().unwrap())
}
Ok((&data[16..], color_from_i32_array(values)?))
}
fn i32_from_bytes(data: &[u8]) -> (&[u8], i32) {
(&data[4..], i32::from_le_bytes(data[0..4].try_into().unwrap()))
}
fn quad_from_bytes(data: &[u8]) -> Result<Quad, DatafileParseError> {
let (mut data, position) = point_from_bytes(data);
let mut coords = [Point::default(); 4];
for i in 0..4 {
let (data_tmp, point) = point_from_bytes(data);
coords[i] = point;
data = data_tmp;
}
let mut colors = [Color::default(); 4];
for i in 0..4 {
let (data_tmp, point) = color_from_i32_bytes(data)?;
colors[i] = point;
data = data_tmp;
}
let mut texture_coords = [Point::default(); 4];
for i in 0..4 {
let (data_tmp, point) = point_from_bytes(data);
texture_coords[i] = point;
data = data_tmp;
}
let (data, position_envelope) = i32_from_bytes(data);
let position_envelope = u16_option_index(position_envelope, "Quad position envelope")?;
let (data, position_envelope_offset) = i32_from_bytes(data);
let (data, color_envelope) = i32_from_bytes(data);
let color_envelope = u16_option_index(color_envelope, "Quad color envelope")?;
let (data, color_envelope_offset) = i32_from_bytes(data);
assert_eq!(data.len(), 0);
Ok(Quad {
position,
corners: coords,
colors,
texture_coords,
position_env: position_envelope,
position_env_offset: position_envelope_offset,
color_env: color_envelope,
color_env_offset: color_envelope_offset,
})
}
impl QuadsLayer {
fn parse_layer(item: &Item, datafile: &Datafile) -> Result<QuadsLayer, DatafileParseError> {
item.check_item_data_length(7)?;
let version = min_n(item.item_data[3], 1, "Quad layer version")?;
let detail = item.item_data[2] & 0b1 == 1;
let quad_count = positive(item.item_data[4], "Quad layer quad_count")?;
let data_items_index = item.item_data[5] as usize;
let data = &datafile.data_item_get(data_items_index)?[..];
if data.len() != quad_count as usize * 152 {
return Err(data_length(format!("not enough bytes for parsing the quads.
expected: {},
got: {},
quad item: {:?}", quad_count as usize * 152, data.len(), item)))
}
let mut quads = Vec::new();
for i in 0..quad_count as usize {
let quad = quad_from_bytes(&data[i*152..(i + 1) * 152])?;
quads.push(quad);
}
let image = u16_option_index(item.item_data[6], "Quad layer image")?;
let mut name = String::new();
if version >= 2 {
item.check_item_data_length(10)?;
name = string_from_i32(&item.item_data[7..10]);
item.check_max_size(10, "QuadLayer");
}
else {
item.check_max_size(7, "QuadLayer");
}
Ok(QuadsLayer {
detail,
quads,
image,
name,
})
}
}
fn sound_shape_from_bytes(data: &[u8]) -> Result<(&[u8], SoundShape), DatafileParseError> {
let (data, id) = i32_from_bytes(data);
match id {
0 => {
let (data, width) = i32_from_bytes(data);
let (data, height) = i32_from_bytes(data);
Ok((data, SoundShape::Rectangle { width, height })) },
1 => {
let (data, radius) = i32_from_bytes(data);
let (data, _) = i32_from_bytes(data);
Ok((data, SoundShape::Circle { radius })) },
unknown => Err(unknown_type(format!("Sound shape with type {} not known. Known are 0, 1.", unknown))),
}
}
fn sound_source_from_bytes(data: &[u8]) -> Result<SoundSource, DatafileParseError>{
let (data, position) = point_from_bytes(data);
let (data, looping) = i32_from_bytes(data);
let looping = match bool(looping, "Sound source looping") {
Ok(boolean) => boolean,
Err(_) => true,
};
let (data, panning) = i32_from_bytes(data);
let panning = bool(panning, "Sound source panning")?;
let (data, delay) = i32_from_bytes(data);
let (data, falloff) = i32_from_bytes(data);
let falloff = match falloff.try_into() {
Ok(x) => x,
Err(_) => return Err(conversion(format!("Sound source falloff is a u8, was given {}", falloff))),
};
let (data, position_envelope) = i32_from_bytes(data);
let position_envelope = u16_option_index(position_envelope, "Sound source position envelope")?;
let (data, position_envelope_offset) = i32_from_bytes(data);
let (data, sound_envelope) = i32_from_bytes(data);
let sound_envelope = u16_option_index(sound_envelope, "Sound source sound envelope")?;
let (data, sound_envelope_offset) = i32_from_bytes(data);
let (data, shape) = sound_shape_from_bytes(data)?;
assert_eq!(data.len(), 0);
Ok(SoundSource {
position,
looping,
panning,
delay,
falloff,
position_env: position_envelope,
position_env_offset: position_envelope_offset,
sound_env: sound_envelope,
sound_env_offset: sound_envelope_offset,
shape,
})
}
fn sound_sources(data: &[u8], source_count: usize) -> Result<Vec<SoundSource>, DatafileParseError> {
if data.len() != source_count as usize * 52 {
return Err(data_length(format!("(deprecated) Sound source insufficient bytes. required: {}, given: {}",
source_count as usize * 52, data.len())));
}
let mut sources = Vec::new();
for i in 0..source_count {
let quad = sound_source_from_bytes(&data[i * 52..(i + 1) * 52])?;
sources.push(quad);
}
Ok(sources)
}
fn deprecated_sound_source_from_bytes(data: &[u8]) -> Result<SoundSource, DatafileParseError> {
let (data, position) = point_from_bytes(data);
let (data, looping) = i32_from_bytes(data);
let looping = match bool(looping, "(deprecated) Sound source looping") {
Ok(boolean) => boolean,
Err(_) => true,
};
let (data, delay) = i32_from_bytes(data);
let (data, falloff_distance) = i32_from_bytes(data);
let (data, position_envelope) = i32_from_bytes(data);
let position_envelope = u16_option_index(position_envelope, "(deprecated) Sound source position envelope")?;
let (data, position_envelope_offset) = i32_from_bytes(data);
let (data, sound_envelope) = i32_from_bytes(data);
let sound_envelope = u16_option_index(sound_envelope, "(deprecated) Sound source sound envelope")?;
let (data, sound_envelope_offset) = i32_from_bytes(data);
assert_eq!(data.len(), 0);
Ok(SoundSource {
position,
looping,
panning: true,
delay,
falloff: 0,
position_env: position_envelope,
position_env_offset: position_envelope_offset,
sound_env: sound_envelope,
sound_env_offset: sound_envelope_offset,
shape: SoundShape::Circle {
radius: falloff_distance,
}
})
}
fn deprecated_sound_sources(data: &[u8], source_count: usize) -> Result<Vec<SoundSource>, DatafileParseError> {
if data.len() != source_count as usize * 36 {
return Err(data_length(format!("(deprecated) Sound layer invalid bytes amount. required: {}, given: {}",
source_count as usize * 40, data.len())));
}
let mut sources = Vec::new();
for i in 0..source_count {
let quad = deprecated_sound_source_from_bytes(&data[i * 36..(i+1) * 36])?;
sources.push(quad);
}
Ok(sources)
}
impl SoundsLayer {
fn parse_layer(item: &Item, datafile: &Datafile) -> Result<SoundsLayer, DatafileParseError> {
use LayerKind::*;
item.check_item_data_length(10)?;
item.check_max_size(10, "SoundLayer");
let variant = item.layer_variant();
let _version = min_n(item.item_data[3], 1, "Sound layer version")?;
let detail = item.item_data[2] & 0b1 == 1;
let source_count = positive(item.item_data[4], "Sound layer source count")? as usize;
let data_item_index = positive(item.item_data[5], "Sound layer data item index")? as usize;
let data = &datafile.data_item_get(data_item_index)?[..];
let sources = match variant {
Sounds => sound_sources(data, source_count)?,
SoundsDeprecated => deprecated_sound_sources(data, source_count)?,
_ => unreachable!(),
};
let sound = u16_option_index(item.item_data[6], "Sound layer sound")?;
let name = string_from_i32(&item.item_data[7..10]);
Ok(SoundsLayer {
detail,
sources,
sound,
name
})
}
}
impl Datafile<'_> {
fn parse_layer(&self, item: &Item) -> Result<Layer, DatafileParseError> {
use LayerKind::*;
let layer_variant = item.layer_variant();
Ok(match layer_variant {
Tiles => Layer::Tiles(TilesLayer::parse_layer(item, &self)?),
Game => Layer::Game(GameLayer::parse_layer(item, &self)?),
Tele => Layer::Tele(TeleLayer::parse_layer(item, &self)?),
Speedup => Layer::Speedup(SpeedupLayer::parse_layer(item, &self)?),
Front => Layer::Front(FrontLayer::parse_layer(item, &self)?),
Switch => Layer::Switch(SwitchLayer::parse_layer(item, &self)?),
Tune => Layer::Tune(TuneLayer::parse_layer(item, &self)?),
Quads => Layer::Quads(QuadsLayer::parse_layer(item, &self)?),
Sounds | SoundsDeprecated => Layer::Sounds(SoundsLayer::parse_layer(item, &self)?),
_ => {
warn!("Layertype {:?} not parsable. Dump: {:?}", layer_variant, item);
Layer::Invalid(item.layer_variant())
},
})
}
fn map_layers(&self) -> Result<Vec<Layer>, DatafileParseError> {
let layer_items = match self.items.get(&5) {
Some(layer_items) => layer_items,
None => return Ok(Vec::new()),
};
let mut layers = Vec::new();
for (i, layer_item) in layer_items.iter().enumerate() {
let new_layer = self.parse_layer(layer_item)
.set_error_index(i as u16)?;
layers.push(new_layer);
}
Ok(layers)
}
}