WIP - New metatile converter

stinkhead7ds-wip1
Ashley N. 2023-11-02 01:00:01 -04:00
parent 15143e08cb
commit e6d56304e4
4 changed files with 43 additions and 99 deletions

View File

@ -1,9 +1,10 @@
use std::{error::Error, fs::read_to_string, path::Path, borrow::Cow, collections::HashSet}; use std::{error::Error, fs::read_to_string, path::Path, borrow::Cow, collections::HashSet, convert::TryInto};
use euclid::default::Rect; use euclid::default::Rect;
use image::{DynamicImage, GenericImageView}; use image::{DynamicImage, GenericImageView};
use linked_hash_map::LinkedHashMap; use linked_hash_map::LinkedHashMap;
use roxmltree::Node; use roxmltree::Node;
use crate::reskit::{utility::print_warning, cli::settings::TileOrder}; use crate::reskit::{utility::print_warning, cli::settings::TileOrder};
use super::dmapack;
#[derive(Debug)] #[derive(Debug)]
pub struct TiledTilemap { pub struct TiledTilemap {
@ -22,6 +23,7 @@ pub struct Metatile {
pub source: String, pub source: String,
pub width: u16, pub width: u16,
pub height: u16, pub height: u16,
pub pack: dmapack::PackSettings,
pub tiles: Vec<u32> pub tiles: Vec<u32>
} }
@ -268,6 +270,25 @@ pub fn get_metatile( id: u32, tileset: Node, working_directory: &str ) -> Result
if let Some( extension ) = formatted_path.extension() { if let Some( extension ) = formatted_path.extension() {
let extension = extension.to_string_lossy(); let extension = extension.to_string_lossy();
if extension == "tmx" { if extension == "tmx" {
// Before we discard tileset, obtain dmapack bucket and slot
let bucket: u16 = tileset.descendants()
.find( | node | node.tag_name() == "property".into() && node.attribute( "name" ) == Some( "reskit-dmapack-bucket" ) )
.ok_or( "invalid file: no dmapack bucket specified for metatile" )?
.attribute( "value" )
.ok_or( "invalid file: reskit-dmapack-bucket property has no value" )?
.parse()?;
let slot: Vec<u16> = tileset.descendants()
.find( | node | node.tag_name() == "property".into() && node.attribute( "name" ) == Some( "reskit-dmapack" ) )
.ok_or( "invalid file: no dmapack slot specified for metatile" )?
.attribute( "value" )
.ok_or( "invalid file: reskit-dmapack property has no value" )?
.split( "," )
.map( | each | each.trim().parse::<u16>().expect( "fatal: non-u16 value specified for reskit-dmapack" ) )
.collect();
let slot: [u16; 2] = slot[0..2].try_into()?;
let pack = dmapack::PackSettings { bucket, slot };
// Case where source is a nested tileset. Open the .tmx file // Case where source is a nested tileset. Open the .tmx file
let file = read_to_string( full_path.clone() )?; let file = read_to_string( full_path.clone() )?;
let tmx_document = roxmltree::Document::parse( &file )?; let tmx_document = roxmltree::Document::parse( &file )?;
@ -301,7 +322,7 @@ pub fn get_metatile( id: u32, tileset: Node, working_directory: &str ) -> Result
val - 1 val - 1
} }
} ).collect(); } ).collect();
return Ok( Some( Metatile { id, source, width, height, tiles } ) ) return Ok( Some( Metatile { id, source, width, height, pack, tiles } ) )
} }
} }

View File

@ -0,0 +1,19 @@
use std::{error::Error, collections::{HashSet, HashMap}};
use super::converter::Metatile;
#[derive(Debug, PartialEq)]
pub struct PackSettings {
pub bucket: u16,
pub slot: [u16; 2]
}
pub type Bucket = Vec<u16>;
pub fn get_buckets( metatiles: &Vec<Metatile> ) -> Result<Vec<Bucket>, Box<dyn Error>> {
let mut buckets: HashMap<u16, Bucket> = HashMap::new();
// Prepare buckets. Each bucket is exactly 64 tiles wide, and as tall as the largest
// height in the bucket.
todo!()
}

View File

@ -1,3 +1,4 @@
pub mod converter; pub mod converter;
pub mod dmapack;
pub mod ecs; pub mod ecs;
pub mod system; pub mod system;

View File

@ -132,105 +132,8 @@ pub fn get_tiles( tilemap: &TiledTilemap ) -> Result<(Vec<u8>, Vec<u8>), Box<dyn
* 2 bytes: Y coordinate (world, divide by 8 to get tile coordinate). * 2 bytes: Y coordinate (world, divide by 8 to get tile coordinate).
*/ */
pub fn get_metatile_maps( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> { pub fn get_metatile_maps( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
// Assemble header of offsets (as we go)
let header_offset = ( tilemap.metatiles.len() * 2 ) + 2;
let mut header: Vec<u8> = vec![];
// Assemble definitions
let mut definitions: Vec<u8> = vec![];
for metatile in &tilemap.metatiles {
let mut subdefinitions: Vec<u8> = vec![];
subdefinitions.extend( metatile.width.to_be_bytes() );
subdefinitions.extend( metatile.height.to_be_bytes() );
let ( tilemap_index, palettes ) = get_tilemap_prefix_palette( &metatile.source, &tilemap.tileset )?;
for tile in &metatile.tiles {
let palette = {
if *tile == 0 {
0
} else {
let palette = palettes.get( *tile as usize ).ok_or( "internal error: no correlation between tile index and palette index" )?;
if let Ok( palette ) = palette.ok_or( "" ) {
palette
} else {
println!( "{:?}", palettes );
return Err( format!( "invalid file: tile \"{}\" in metatile tilemap does not name reskit-palette attribute", tile ) )?;
}
}
};
let nametable_entry: u16 = ( ( palette as u16 ) << 13 ) | ( ( tile + tilemap_index as u32 ) as u16 );
subdefinitions.extend( nametable_entry.to_be_bytes() );
}
let this_offset = header_offset + definitions.len();
header.extend( ( this_offset as u16 ).to_be_bytes() );
definitions.extend( subdefinitions );
}
// Assemble instances
let mut instances: Vec<u8> = vec![];
let layer_b: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Metatile { system_plane: SystemPlane::MdPlaneB, tiles: _ } ) );
if let Some( layer_b ) = layer_b {
let tiles = match layer_b { Layer::Metatile { system_plane: _, tiles } => tiles };
let mut subdefinitions: Vec<u8> = vec![];
for tile_y in 0..tilemap.height {
for tile_x in 0..tilemap.width {
let item_at = tiles.get( ( tile_y * tilemap.width ) + tile_x ).ok_or( "internal error: tilemap does not correlate to width/height" )?;
if *item_at != 0 {
// What index-ID was that?
let index_id = tilemap.metatiles.iter().position( | metatile | metatile.id == *item_at ).ok_or( "invalid file: metatile id not found" )?;
// Write the data to subdefinitions
subdefinitions.extend( ( index_id as u16 ).to_be_bytes() );
subdefinitions.extend( ( ( tile_x * 8 ) as u16 ).to_be_bytes() );
subdefinitions.extend( ( ( tile_y * 8 ) as u16 ).to_be_bytes() );
}
}
}
instances.extend( ( ( subdefinitions.len() / 6 ) as u16 ).to_be_bytes() );
instances.extend( subdefinitions );
} else {
instances.extend( ( 0 as u16 ).to_be_bytes() );
}
let layer_a: Option<&Layer> = tilemap.layers.iter().find( | layer | matches!( layer, Layer::Metatile { system_plane: SystemPlane::MdPlaneA, tiles: _ } ) );
if let Some( layer_a ) = layer_a {
let tiles = match layer_a { Layer::Metatile { system_plane: _, tiles } => tiles };
let mut subdefinitions: Vec<u8> = vec![];
for tile_y in 0..tilemap.height {
for tile_x in 0..tilemap.width {
let item_at = tiles.get( ( tile_y * tilemap.width ) + tile_x ).ok_or( "internal error: tilemap does not correlate to width/height" )?;
if *item_at != 0 {
// What index-ID was that?
let index_id = tilemap.metatiles.iter().position( | metatile | metatile.id == *item_at ).ok_or( "invalid file: metatile id not found" )?;
// Write the data to subdefinitions
subdefinitions.extend( ( index_id as u16 ).to_be_bytes() );
subdefinitions.extend( ( ( tile_x * 8 ) as u16 ).to_be_bytes() );
subdefinitions.extend( ( ( tile_y * 8 ) as u16 ).to_be_bytes() );
}
}
}
instances.extend( ( ( subdefinitions.len() / 6 ) as u16 ).to_be_bytes() );
instances.extend( subdefinitions );
} else {
instances.extend( ( 0 as u16 ).to_be_bytes() );
}
// Write the final result // Write the final result
let mut result: Vec<u8> = vec![]; let mut result: Vec<u8> = vec![];
let offset_to_instances = header_offset + definitions.len();
result.extend( ( offset_to_instances as u16 ).to_be_bytes() );
result.extend( header );
result.extend( definitions );
result.extend( instances );
Ok( result ) Ok( result )
} }