use crate::map::*;
use crate::map_parse::MapVersion;
use crate::map_checks::bezier_in_use;
use crate::datafile_parse::Item;
use crate::datafile_save::{SaveError, save as save_datafile};
use crate::convert::TryTo;
use itertools::Itertools;
use structview::View;
use std::borrow::Cow;
use std::collections::HashMap;
use std::convert::TryInto;
use std::fs;
use std::io::Write;
use std::iter;
use std::mem;
use std::path::Path;
use std::slice;
impl From<SaveError> for Error {
fn from(err: SaveError) -> Self {
match err {
SaveError::Io(err) => Error::Io(err),
SaveError::Datafile(err) => Error::DatafileSaving(err),
}
}
}
impl ItemType {
fn identifier(&self) -> Identifier {
match self {
ItemType::Version => Identifier::TypeId(0),
ItemType::Info => Identifier::TypeId(1),
ItemType::Image => Identifier::TypeId(2),
ItemType::Envelope => Identifier::TypeId(3),
ItemType::Group => Identifier::TypeId(4),
ItemType::Layer => Identifier::TypeId(5),
ItemType::EnvPoints => Identifier::TypeId(6),
ItemType::Sound => Identifier::TypeId(7),
ItemType::AutoMapper => Identifier::Uuid(AutoMapper::uuid()),
ItemType::Unknown(id) => panic!("type id not available for MapItem of id {}", id),
}
}
}
impl From<u16> for ItemType {
fn from(id: u16) -> Self {
use ItemType::*;
match id {
0 => Version,
1 => Info,
2 => Image,
3 => Envelope,
4 => Group,
5 => Layer,
6 => EnvPoints,
7 => Sound,
_ => Unknown(id),
}
}
}
#[derive(Debug)]
enum Identifier {
TypeId(u16),
Uuid([u8; 16]),
}
#[derive(Debug)]
struct ItemTypeInsert<'a> {
id: Identifier,
items: Vec<Item>,
data_items: Vec<Cow<'a, [u8]>>,
}
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)
}
fn add_uuid_type(items: &mut HashMap<u16, Vec<Item>>,uuid: [u8; 16]) -> u16 {
let ex_item_id = 0xffff;
match items.get(&ex_item_id) {
None => { items.insert(ex_item_id, Vec::new()); },
Some(ex_items) => {
for item in ex_items {
if uuid_equal(&uuid, item.item_data[..4].try_into().unwrap()) {
panic!("Tried to insert ex_items of already inserted uuid: {:?}.", uuid);
}
}
}
}
let ex_items = items.get_mut(&ex_item_id).unwrap();
let type_id = ex_item_id - ex_items.len().try_to::<u16>() - 1;
let item = Item {
id: type_id,
item_data: uuid.chunks(4)
.map(|bytes| i32::from_be_bytes(bytes[0..4].try_into().unwrap()))
.collect(),
};
ex_items.push(item);
type_id
}
fn option_insert<'a>(items: &mut HashMap<u16, Vec<Item>>, data_items: &mut Vec<Cow<'a, [u8]>>, option_to_insert: Option<ItemTypeInsert<'a>>) {
match option_to_insert {
None => (),
Some(to_insert) => insert(items, data_items, to_insert)
}
}
fn insert<'a>(mut items: &mut HashMap<u16, Vec<Item>>, data_items: &mut Vec<Cow<'a, [u8]>>, mut to_insert: ItemTypeInsert<'a>) {
assert_ne!(to_insert.items.len(), 0);
let type_id = match to_insert.id {
Identifier::TypeId(id) => id,
Identifier::Uuid(uuid) => add_uuid_type(&mut items, uuid),
};
let replaced = items.insert(type_id, to_insert.items);
if replaced.is_some() {
panic!("Tried to insert items of already existing type id: {}", type_id);
}
data_items.append(&mut to_insert.data_items);
}
impl TwMap {
pub fn save_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
let mut file = fs::File::create(path)?;
self.save(&mut file)
}
pub fn save(&mut self, output: &mut dyn Write) -> Result<(), Error> {
self.load()?;
self.check()?;
self.process_tile_flag_opaque();
let (items, data_items) = self.save_to_datafile_unwrap();
save_datafile(output, &items, &data_items)?;
Ok(())
}
pub(crate) fn save_to_datafile_unwrap(&self) -> (HashMap<u16, Vec<Item>>, Vec<Cow<[u8]>>){
let mut items = HashMap::new();
let mut data_items = Vec::new();
let raw_map_version = MapVersion::save();
insert(&mut items, &mut data_items, raw_map_version);
let raw_map_info = Info::save(&self.info, self.version, data_items.len().try_to());
insert(&mut items, &mut data_items, raw_map_info);
let raw_map_images = Image::save(&self.images, self.version, data_items.len().try_to());
option_insert(&mut items, &mut data_items, raw_map_images);
let (raw_map_envelopes, raw_env_points) = Envelope::save(&self.envelopes);
option_insert(&mut items, &mut data_items, raw_map_envelopes);
let raw_map_groups = Group::save(&self.groups);
insert(&mut items, &mut data_items, raw_map_groups);
let raw_map_layers = Layer::save(&self.groups, self.version, data_items.len().try_to());
insert(&mut items, &mut data_items, raw_map_layers);
insert(&mut items, &mut data_items, raw_env_points);
let raw_map_sounds = Sound::save(&self.sounds, data_items.len().try_to());
option_insert(&mut items, &mut data_items, raw_map_sounds);
if self.version == Version::DDNet06 {
let raw_auto_mappers = AutoMapper::save(&self.groups);
option_insert(&mut items, &mut data_items, raw_auto_mappers);
}
(items, data_items)
}
}
fn c_string(string: &str) -> Vec<u8> {
let mut bytes = string.as_bytes().to_vec();
bytes.push(0); bytes
}
impl MapVersion {
fn save<'a>() -> ItemTypeInsert<'a> {
let item = Item {
id: 0,
item_data: vec![1],
};
ItemTypeInsert {
id: ItemType::Version.identifier(),
items: vec![item],
data_items: vec![],
}
}
}
impl Info {
fn save<'a>(&self, version: Version, data_len: i32) -> ItemTypeInsert<'a> {
let (item, data_items) = self.itemize(data_len, version);
ItemTypeInsert {
id: ItemType::Info.identifier(),
items: vec![item],
data_items,
}
}
fn itemize<'a>(&self, mut data_len: i32, version: Version) -> (Item, Vec<Cow<'a, [u8]>>) {
let mut item_data = vec![-1; 5];
item_data[0] = 1; let mut data = Vec::new();
let fields = [&self.author, &self.version, &self.credits, &self.license];
for (i, string) in fields.iter().enumerate() {
if !string.is_empty() {
item_data[i + 1] = data_len;
data_len += 1;
data.push(Cow::from(c_string(string)))
}
}
if version == Version::DDNet06 {
if !self.settings.is_empty() {
item_data.push(data_len);
let mut string_data = Vec::new();
for string in &self.settings {
string_data.extend(c_string(string))
}
data.push(Cow::from(string_data))
}
else {
item_data.push(-1);
}
}
let item = Item {
id: 0,
item_data,
};
(item, data)
}
}
impl Image {
fn save(map_images: &[Image], version: Version, data_len: i32) -> Option<ItemTypeInsert> {
if map_images.len() == 0 {
return None;
}
let mut items = Vec::new();
let mut data_items = Vec::new();
for (i, image) in map_images.iter().enumerate() {
let id = i.try_to();
let (new_item, new_data_items) = image.itemize(id, version, data_len + data_items.len().try_to::<i32>());
items.push(new_item);
data_items.extend(new_data_items);
}
Some(ItemTypeInsert {
id: ItemType::Image.identifier(),
items,
data_items,
})
}
fn itemize(&self, id: u16, version: Version, data_len: i32) -> (Item, Vec<Cow<[u8]>>) {
let mut item_data = vec![0; 6];
let mut data_items = Vec::new();
item_data[0] = match version {
Version::DDNet06 => 1,
Version::Teeworlds07 => 2,
}; item_data[1] = self.width();
item_data[2] = self.height();
item_data[4] = data_len; data_items.push(Cow::from(c_string(self.name())));
match self.image() {
None => {
item_data[3] = true.into();
item_data[5] = -1;
},
Some(CompressedData::Compressed(_, _, _)) => unreachable!(),
Some(CompressedData::Loaded(image)) => {
item_data[3] = false.into();
item_data[5] = data_len + 1;
data_items.push(Cow::from(image.as_ref()))
}
}
if version == Version::Teeworlds07 {
item_data.push(1); }
let item = Item {
id,
item_data
};
(item, data_items)
}
}
fn string_to_i32s(str: &String, i32_count: usize) -> Vec<i32> {
assert!(str.len() + 1 <= i32_count * 4);
let mut bytes = str.clone().into_bytes()
.into_iter()
.pad_using(i32_count * 4, |_| 0)
.map(|x| x.wrapping_sub(128)) .collect::<Vec<u8>>();
bytes[i32_count * 4 - 1] = 0; bytes.chunks(4)
.into_iter()
.map(|i32_bytes| i32::from_be_bytes(i32_bytes.try_into().unwrap()))
.collect::<Vec<i32>>()
}
impl<T: EnvPointContentWriting + Copy> Env<T> {
fn itemize(&self, id: u16, total_points_len: i32, bezier_in_use: bool) -> (Item, Vec<i32>, i32) {
let mut item_data = [0; 13];
item_data[0] = match bezier_in_use {
false => 2,
true => 3,
}; item_data[1] = T::channels(); item_data[2] = total_points_len; item_data[3] = self.points.len().try_to::<i32>(); item_data[12] = self.synchronized.into(); let i32_string= string_to_i32s(&self.name, 8);
item_data[4..12].copy_from_slice(&i32_string);
let point_data = match bezier_in_use {
false => EnvPoint::<T>::vec_to_raw_v1(&self.points),
true => EnvPoint::<T>::vec_to_raw_v2(&self.points),
};
let item = Item {
id,
item_data: item_data.to_vec(),
};
(item, point_data, self.points.len().try_to::<i32>())
}
}
impl<T> CurveKind<T> {
fn to_id(self) -> i32 {
use CurveKind::*;
match self {
Step => 0,
Linear => 1,
Slow => 2,
Fast => 3,
Smooth => 4,
Bezier(_) => 5,
Unknown(n) => panic!("Tried to access i32 representation of unknown curve type {}", n),
}
}
}
pub trait EnvPointContentWriting {
fn to_raw(&self) -> [i32; 4];
fn channels() -> i32;
}
impl EnvPointContentWriting for i32 {
fn to_raw(&self) -> [i32; 4] {
[*self, 0, 0, 0]
}
fn channels() -> i32 {
1
}
}
impl EnvPointContentWriting for Position {
fn to_raw(&self) -> [i32; 4] {
[self.x, self.y, self.rotation, 0]
}
fn channels() -> i32 {
3
}
}
impl EnvPointContentWriting for I32Color {
fn to_raw(&self) -> [i32; 4] {
[self.r, self.g, self.b, self.a]
}
fn channels() -> i32 {
4
}
}
impl<T: EnvPointContentWriting> From<CurveKind<T>> for CurveKind<[i32; 4]> {
fn from(curve_type: CurveKind<T>) -> 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: b.in_tangent_dx.to_raw(),
in_tangent_dy: b.in_tangent_dy.to_raw(),
out_tangent_dx: b.out_tangent_dx.to_raw(),
out_tangent_dy: b.out_tangent_dy.to_raw(),
}),
}
}
}
impl<T: EnvPointContentWriting + Copy> EnvPoint<T> {
fn to_raw_v1(&self) -> [i32; 6] {
let mut data = [0; 6];
data[0] = self.time;
data[1] = self.curve.to_id();
data[2..6].copy_from_slice(&self.content.to_raw());
data
}
fn to_raw_v2(&self) -> [i32; 22] {
let mut data = [0; 22];
data[0..6].copy_from_slice(&self.to_raw_v1());
match CurveKind::<[i32; 4]>::from(self.curve) {
CurveKind::Bezier(b) => {
data[6..10].copy_from_slice(&b.in_tangent_dx);
data[10..14].copy_from_slice(&b.in_tangent_dy);
data[14..18].copy_from_slice(&b.out_tangent_dx);
data[18..22].copy_from_slice(&b.out_tangent_dy);
}
_ => {},
}
data
}
fn vec_to_raw_v1(points: &[Self]) -> Vec<i32> {
let mut data = Vec::new();
for point in points {
data.extend(&point.to_raw_v1());
}
data
}
fn vec_to_raw_v2(points: &[Self]) -> Vec<i32> {
let mut data = Vec::new();
for point in points {
data.extend(&point.to_raw_v2());
}
data
}
}
impl Envelope {
fn save(map_envelopes: &[Envelope]) -> (Option<ItemTypeInsert>, ItemTypeInsert) {
let env_points_item = Item {
id: 0,
item_data: vec![],
};
let mut env_points_save = ItemTypeInsert {
id: ItemType::EnvPoints.identifier(),
items: vec![env_points_item],
data_items: vec![],
};
if map_envelopes.len() == 0 {
return (None, env_points_save);
}
let mut envelope_items = Vec::new();
let mut env_point_data = Vec::new();
let mut total_points_len = 0;
let bezier_in_use = bezier_in_use(map_envelopes);
for (i, envelope) in map_envelopes.iter().enumerate() {
let (new_item, new_env_point_data, new_env_point_count) = envelope.itemize(i.try_to(), total_points_len, bezier_in_use);
envelope_items.push(new_item);
env_point_data.extend(new_env_point_data);
total_points_len += new_env_point_count;
}
let envelope_save = ItemTypeInsert {
id: ItemType::Envelope.identifier(),
items: envelope_items,
data_items: vec![],
};
env_points_save.items[0].item_data = env_point_data;
(Some(envelope_save), env_points_save)
}
fn itemize(&self, id: u16, total_points_len: i32, bezier_in_use: bool) -> (Item, Vec<i32>, i32) {
use Envelope::*;
match self {
Position(position_env) => position_env.itemize(id, total_points_len, bezier_in_use),
Color(color_env) => color_env.itemize(id, total_points_len, bezier_in_use),
Sound(sound_env) => sound_env.itemize(id, total_points_len, bezier_in_use),
}
}
}
impl Group {
fn save(map_groups: &[Group]) -> ItemTypeInsert {
let mut items = Vec::new();
let mut layer_count = 0;
for (i, group) in map_groups.iter().enumerate() {
let (new_group, added_layer_count) = group.itemize(layer_count, i.try_to());
items.push(new_group);
layer_count += added_layer_count;
}
ItemTypeInsert {
id: ItemType::Group.identifier(),
items,
data_items: vec![],
}
}
fn itemize(&self, first_layer_index: i32, id: u16) -> (Item, i32) {
let mut item_data = [0;15];
item_data[0] = 3; item_data[1] = self.offset_x;
item_data[2] = self.offset_y;
item_data[3] = self.parallax_x;
item_data[4] = self.parallax_y;
item_data[5] = first_layer_index;
item_data[6] = self.layers.len().try_to();
item_data[7] = self.clipping.into();
item_data[8] = self.clip_x;
item_data[9] = self.clip_y;
item_data[10] = self.clip_width;
item_data[11] = self.clip_height;
item_data[12..15].copy_from_slice(&string_to_i32s(&self.name, 3));
let item_data = item_data.to_vec();
(Item {
id,
item_data,
}, self.layers.len().try_to())
}
}
trait ViewAsBytes: View {
fn into_boxed_bytes(boxed_slice: Box<[Self]>) -> Box<[u8]> {
let len = boxed_slice.len() * mem::size_of::<Self>();
let ptr = Box::into_raw(boxed_slice);
unsafe {
let byte_slice = slice::from_raw_parts_mut(ptr as *mut u8, len);
Box::from_raw(byte_slice)
}
}
}
impl <T: View> ViewAsBytes for T {}
trait TileCompression: AnyTile { fn compress_tiles(_: Vec<Self>) -> Vec<Self> {
panic!();
}
}
impl TileCompression for Tile {
fn compress_tiles(tiles: Vec<Tile>) -> Vec<Tile> {
let mut compressed_tiles = Vec::new();
compressed_tiles.push(tiles[0]);
for &tile in &tiles[1..] {
let current_tile = compressed_tiles.last_mut().unwrap();
if current_tile.skip == u8::max_value() { compressed_tiles.push(tile);
} else {
if current_tile.id == tile.id && current_tile.flags == tile.flags {
current_tile.skip += 1;
} else {
compressed_tiles.push(tile);
}
}
}
compressed_tiles
}
}
impl TileCompression for GameTile {
fn compress_tiles(tiles: Vec<GameTile>) -> Vec<GameTile> {
let mut compressed_tiles = Vec::new();
compressed_tiles.push(tiles[0]);
for &tile in &tiles[1..] {
let current_tile = compressed_tiles.last_mut().unwrap();
if current_tile.skip == u8::max_value() { compressed_tiles.push(tile);
} else {
if current_tile.id == tile.id && current_tile.flags == tile.flags {
current_tile.skip += 1;
} else {
compressed_tiles.push(tile);
}
}
}
compressed_tiles
}
}
impl TileCompression for Tele {}
impl TileCompression for Switch {}
impl TileCompression for Tune {}
impl TileCompression for Speedup {}
trait PhysicsLayerSaving: TileMapLayer
where Self::TileType: TileCompression {
fn tiles_layer_flag() -> i32;
fn itemize(&self, version: Version, data_len: i32, id: u16) -> (Item, Vec<Cow<[u8]>>) {
let mut item_data = vec![-1; 18];
let mut data_items = Vec::new();
item_data[0] = 0; item_data[1] = 2; item_data[2] = 0; item_data[3] = match version { Version::DDNet06 => 3,
Version::Teeworlds07 => 4,
};
let width = self.tiles().unwrap_ref().ncols();
item_data[4] = width.try_to();
let height = self.tiles().unwrap_ref().nrows();
item_data[5] = height.try_to();
item_data[6] = Self::tiles_layer_flag(); item_data[7..11].copy_from_slice(&[255;4]); item_data[11] = -1; item_data[12] = 0; item_data[13] = -1; item_data[14] = data_len;
item_data[15..18].copy_from_slice(&string_to_i32s(&String::from(Self::kind().static_name()), 3));
if version == Version::DDNet06 {
item_data.extend(&[-1; 5]);
if Self::kind() != LayerKind::Game {
item_data[Self::kind().data_index()] = data_len + 1;
data_items.push(Cow::from(iter::repeat(0).take(width * height * mem::size_of:: < Tile>()).collect:: < Vec<_ > > ()));
}
}
let mut tiles = self.tiles().unwrap_ref().clone()
.iter()
.map(|&tile| tile)
.collect::<Vec<Self::TileType>>();
if version == Version::Teeworlds07 && Self::kind() == LayerKind::Game {
tiles = Self::TileType::compress_tiles(tiles);
}
data_items.push(Cow::from(Self::TileType::into_boxed_bytes(tiles
.into_boxed_slice())
.into_vec()));
(Item {
id,
item_data: item_data.to_vec(),
}, data_items)
}
}
impl PhysicsLayerSaving for GameLayer {
fn tiles_layer_flag() -> i32 { 1 }
}
impl PhysicsLayerSaving for FrontLayer {
fn tiles_layer_flag() -> i32 { 8 }
}
impl PhysicsLayerSaving for TeleLayer {
fn tiles_layer_flag() -> i32 { 2 }
}
impl PhysicsLayerSaving for SpeedupLayer {
fn tiles_layer_flag() -> i32 { 4 }
}
impl PhysicsLayerSaving for SwitchLayer {
fn tiles_layer_flag() -> i32 { 16 }
}
impl PhysicsLayerSaving for TuneLayer {
fn tiles_layer_flag() -> i32 { 32 }
}
fn from_u16_option_index(index: Option<u16>) -> i32 {
match index {
None => -1,
Some(n) => n.into()
}
}
impl TilesLayer {
fn itemize(&self, version: Version, data_len: i32, id: u16) -> (Item, Vec<Cow<[u8]>>) {
let mut item_data = vec![-1; 18];
let mut data_items = Vec::new();
let tiles = self.tiles.unwrap_ref();
item_data[0] = 0; item_data[1] = 2; item_data[2] = self.detail.into(); item_data[3] = match version {
Version::DDNet06 => 3,
Version::Teeworlds07 => 4,
};
let width = tiles.ncols();
item_data[4] = width.try_to();
let height = tiles.nrows();
item_data[5] = height.try_to();
item_data[6] = 0; item_data[7] = self.color.r.into();
item_data[8] = self.color.g.into();
item_data[9] = self.color.b.into();
item_data[10] = self.color.a.into();
item_data[11] = from_u16_option_index(self.color_env); item_data[12] = self.color_env_offset; item_data[13] = from_u16_option_index(self.image); item_data[14] = data_len;
item_data[15..18].copy_from_slice(&string_to_i32s(&self.name, 3));
let mut tiles = tiles.iter()
.map(|&tile| tile)
.collect::<Vec<Tile>>();
if version == Version::Teeworlds07 {
tiles = Tile::compress_tiles(tiles);
}
data_items.push(Cow::from(Tile::into_boxed_bytes(tiles
.into_boxed_slice())
.into_vec()));
if version == Version::DDNet06 {
item_data.extend(&[-1; 5]);
}
(Item {
id,
item_data: item_data.to_vec(),
}, data_items)
}
}
impl Point {
fn to_i32(&self) -> Vec<i32> {
vec![self.x, self.y]
}
}
impl Color {
fn to_i32(&self) -> Vec<i32> {
vec![
self.r.into(),
self.g.into(),
self.b.into(),
self.a.into(),
]
}
}
impl Quad {
fn to_bytes(&self) -> Vec<u8> {
let mut i32_data = Vec::new();
for i in 0..4 {
i32_data.extend(self.corners[i].to_i32());
}
i32_data.extend(self.position.to_i32());
for i in 0..4 {
i32_data.extend(self.colors[i].to_i32());
}
for i in 0..4 {
i32_data.extend(self.texture_coords[i].to_i32());
}
i32_data.push(from_u16_option_index(self.position_env));
i32_data.push(self.position_env_offset);
i32_data.push(from_u16_option_index(self.color_env));
i32_data.push(self.color_env_offset);
let mut u8_data = Vec::new();
for n in i32_data {
u8_data.extend(&i32::to_le_bytes(n));
}
u8_data
}
}
impl QuadsLayer {
fn create_data_item(&self) -> Cow<[u8]> {
let mut data_item = Vec::new();
for quad in &self.quads {
data_item.extend(quad.to_bytes());
}
Cow::from(data_item)
}
fn itemize(&self, data_len: i32, id: u16) -> (Item, Vec<Cow<[u8]>>) {
let mut item_data = [-1; 10];
item_data[0] = 0; item_data[1] = 3; item_data[2] = self.detail.into(); item_data[3] = 2; item_data[4] = self.quads.len().try_to();
item_data[5] = data_len; item_data[6] = from_u16_option_index(self.image); item_data[7..10].copy_from_slice(&string_to_i32s(&self.name, 3));
let data_items = vec![self.create_data_item()];
(Item {
id,
item_data: item_data.to_vec()
}, data_items)
}
}
impl SoundSource {
fn to_bytes(&self) -> Vec<u8> {
use SoundShape::*;
let mut i32_data = Vec::new();
i32_data.extend(self.position.to_i32());
i32_data.push(self.looping.into());
i32_data.push(self.panning.into());
i32_data.push(self.delay);
i32_data.push(self.falloff.into());
i32_data.push(from_u16_option_index(self.position_env));
i32_data.push(self.position_env_offset);
i32_data.push(from_u16_option_index(self.sound_env));
i32_data.push(self.sound_env_offset);
match self.shape {
Rectangle { width, height } => {
i32_data.push(0); i32_data.push(width);
i32_data.push(height);
}
Circle { radius } => {
i32_data.push(1); i32_data.push(radius);
i32_data.push(-1); }
}
let mut u8_data = Vec::new();
for n in i32_data {
u8_data.extend(&i32::to_le_bytes(n));
}
u8_data
}
}
impl SoundsLayer {
fn create_data_item(&self) -> Cow<[u8]> {
let mut data_item = Vec::new();
for source in &self.sources {
data_item.extend(source.to_bytes());
}
Cow::from(data_item)
}
fn itemize(&self, data_len: i32, id: u16) -> (Item, Vec<Cow<[u8]>>) {
let mut item_data = [-1; 10];
item_data[0] = 1; item_data[1] = 10; item_data[2] = self.detail.into(); item_data[3] = 2; item_data[4] = self.sources.len().try_to();
item_data[5] = data_len; item_data[6] = from_u16_option_index(self.sound);
item_data[7..10].copy_from_slice(&string_to_i32s(&self.name, 3));
let data_items = vec![self.create_data_item()];
(Item {
id,
item_data: item_data.to_vec(),
}, data_items)
}
}
impl Layer {
fn save(groups: &[Group], version: Version, data_len: i32) -> ItemTypeInsert {
let mut items = Vec::new();
let mut data_items = Vec::new();
for group in groups {
for layer in &group.layers {
let (new_item, new_data_items) = layer.itemize(items.len().try_to(), version, data_len + data_items.len().try_to::<i32>());
items.push(new_item);
data_items.extend(new_data_items);
}
}
ItemTypeInsert {
id: ItemType::Layer.identifier(),
items,
data_items,
}
}
fn itemize(&self, id: u16, version: Version, data_len: i32) -> (Item, Vec<Cow<[u8]>>) {
use Layer::*;
let (layer_item, data_items) = match self {
Game(l) => l.itemize(version, data_len, id),
Front(l) => l.itemize(version, data_len, id),
Tele(l) => l.itemize(version, data_len, id),
Speedup(l) => l.itemize(version, data_len, id),
Switch(l) => l.itemize(version, data_len, id),
Tune(l) => l.itemize(version, data_len, id),
Tiles(l) => l.itemize(version, data_len, id),
Quads(l) => l.itemize(data_len, id),
Sounds(l) => l.itemize(data_len, id),
Invalid(_) => panic!(),
};
(layer_item, data_items)
}
}
impl Sound {
fn save(map_sounds: &[Sound], data_len: i32) -> Option<ItemTypeInsert> {
if map_sounds.len() == 0 {
return None;
}
let mut items = Vec::new();
let mut data_items = Vec::new();
for (i, sound) in map_sounds.iter().enumerate() {
let (new_item, new_data_items) = sound.itemize(i.try_to(), data_len + data_items.len().try_to::<i32>());
items.push(new_item);
data_items.extend(new_data_items);
}
Some(ItemTypeInsert {
id: ItemType::Sound.identifier(),
items,
data_items,
})
}
fn itemize(&self, id: u16, data_len: i32) -> (Item, Vec<Cow<[u8]>>) {
let mut item_data = vec![0; 5];
let mut data_items = Vec::new();
item_data[0] = 1; item_data[2] = data_len; data_items.push(Cow::from(c_string(&self.name)));
match &self.data {
CompressedData::Compressed(_, _, _) => unreachable!(),
CompressedData::Loaded(data) => {
item_data[1] = false.into();
item_data[3] = data_len + 1;
item_data[4] = data.len().try_to();
data_items.push(Cow::from(data))
}
}
let item = Item {
id,
item_data
};
(item, data_items)
}
}
impl AutoMapper {
fn save(groups: &[Group]) -> Option<ItemTypeInsert> {
let mut items = Vec::new();
for (group_id, group) in groups.iter().enumerate() {
for (layer_id, layer) in group.layers.iter().enumerate() {
if let Layer::Tiles(layer) = layer {
items.push(layer.auto_mapper.itemize(items.len().try_to(), group_id.try_to(), layer_id.try_to()));
}
}
}
match items.len() {
0 => None,
_ => Some(ItemTypeInsert {
id: ItemType::AutoMapper.identifier(),
items,
data_items: vec![]
})
}
}
fn itemize(&self, id: u16, group_id: u16, layer_id: u16) -> Item {
let mut item_data = [-1; 6];
item_data[0] = 1; item_data[1] = group_id.into();
item_data[2] = layer_id.into();
item_data[3] = from_u16_option_index(self.config);
item_data[4] = self.seed;
item_data[5] = self.automatic.into();
Item {
id,
item_data: item_data.to_vec(),
}
}
}