WIP - New metatile converter
parent
e6d56304e4
commit
3e5338670a
|
@ -1,5 +1,6 @@
|
|||
use std::{error::Error, collections::{HashSet, HashMap}};
|
||||
use super::converter::Metatile;
|
||||
use std::{error::Error, collections::HashMap, cmp::max};
|
||||
use crate::reskit::level::system::get_tilemap_prefix_palette;
|
||||
use super::converter::{Metatile, TiledTilemap, TiledTileset};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PackSettings {
|
||||
|
@ -7,13 +8,92 @@ pub struct PackSettings {
|
|||
pub slot: [u16; 2]
|
||||
}
|
||||
|
||||
pub type Bucket = Vec<u16>;
|
||||
pub type Bucket<'a> = Vec<Option<u16>>;
|
||||
|
||||
pub fn get_buckets( metatiles: &Vec<Metatile> ) -> Result<Vec<Bucket>, Box<dyn Error>> {
|
||||
let mut buckets: HashMap<u16, Bucket> = HashMap::new();
|
||||
pub fn get_buckets( tilemap: &TiledTilemap ) -> Result<Vec<(u16, Bucket)>, Box<dyn Error>> {
|
||||
let mut buckets: HashMap<u16, Bucket> = tilemap.metatiles.iter().map( | metatile | ( metatile.pack.bucket, Bucket::new() ) ).collect();
|
||||
|
||||
// Prepare buckets. Each bucket is exactly 64 tiles wide, and as tall as the largest
|
||||
// height in the bucket.
|
||||
|
||||
todo!()
|
||||
for ( bucket_id, bucket ) in &mut buckets {
|
||||
let height = get_bucket_height( *bucket_id, &tilemap.metatiles );
|
||||
bucket.resize_with( 64 * height, || None );
|
||||
}
|
||||
|
||||
// Build definitions and stamp them into their respective buckets and bucket slots
|
||||
for metatile in &tilemap.metatiles {
|
||||
// Get definition (in Vec<u16> form) so we can stamp it into the bucket
|
||||
let definition = get_metatile_definition( metatile, &tilemap.tileset )?;
|
||||
|
||||
// Get bucket to stamp definition into
|
||||
let bucket: &mut Bucket = buckets.get_mut( &metatile.pack.bucket ).ok_or( "internal error: no dmapack bucket" )?;
|
||||
|
||||
// Stamp the definition into the appropriate slot of the bucket.
|
||||
for y in 0..metatile.height {
|
||||
for x in 0..metatile.width {
|
||||
let metatile_tile = definition.get( ( ( y * metatile.width ) + x ) as usize ).ok_or( "internal error: metatile tile expected" )?;
|
||||
|
||||
let bucket_target_x = x + metatile.pack.slot[ 0 ];
|
||||
let bucket_target_y = y + metatile.pack.slot[ 1 ];
|
||||
|
||||
let bucket_target_tile = bucket.get_mut( ( ( bucket_target_y * 64 ) + bucket_target_x ) as usize ).ok_or( "internal error: bucket not correct size" )?;
|
||||
|
||||
// At any point if we encounter an overlapping bucket segment, throw an error.
|
||||
if let Some( _ ) = bucket_target_tile {
|
||||
return Err( format!( "invalid file: overlapping bucket segment at {},{}", metatile.pack.slot[ 0 ], metatile.pack.slot[ 1 ] ) )?;
|
||||
}
|
||||
|
||||
// Stamp the segment
|
||||
bucket_target_tile.replace( *metatile_tile );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut sorted_buckets: Vec<(u16, Bucket)> = buckets.into_iter().map( | pair | pair ).collect();
|
||||
sorted_buckets.sort_by_key( | ( bucket_id, _ ) | *bucket_id );
|
||||
|
||||
Ok( sorted_buckets )
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metatile definition (the block of u16s in sega genesis vdp nametable format)
|
||||
*/
|
||||
fn get_metatile_definition( metatile: &Metatile, tileset: &Vec<TiledTileset> ) -> Result<Vec<u16>, Box<dyn Error>> {
|
||||
let mut result: Vec<u16> = Vec::new();
|
||||
|
||||
let ( tile_id_prefix, palettes ) = get_tilemap_prefix_palette( &metatile.source, &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 tile_id: u16 = if *tile == 0 { 0 } else { ( tile + tile_id_prefix as u32 ) as u16 };
|
||||
result.push( ( ( palette as u16 ) << 13 ) | tile_id );
|
||||
}
|
||||
|
||||
Ok( result )
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of a given dmapack bucket (the max of all heights of metatiles in the bucket)
|
||||
*/
|
||||
fn get_bucket_height( bucket_id: u16, metatiles: &Vec<Metatile> ) -> usize {
|
||||
let filtered: Vec<&Metatile> = metatiles.iter().filter( | metatile | metatile.pack.bucket == bucket_id ).collect();
|
||||
let mut max_height = 0;
|
||||
|
||||
for metatile in filtered {
|
||||
max_height = max( max_height, metatile.height );
|
||||
}
|
||||
|
||||
max_height as usize
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use std::{error::Error, collections::HashMap};
|
||||
use image::GenericImageView;
|
||||
use crate::reskit::{tileset::image_to_tiles, utility::symbol_to_pascal, cli::settings::TileOrder};
|
||||
use super::converter::{TiledTilemap, Layer, SystemPlane, TiledTileset};
|
||||
use super::{converter::{TiledTilemap, Layer, SystemPlane, TiledTileset}, dmapack::get_buckets};
|
||||
|
||||
/**
|
||||
* Output the .bin and .pal file (using `tileset` tool to build it) containing each of the tiles
|
||||
|
@ -118,10 +118,9 @@ pub fn get_tiles( tilemap: &TiledTilemap ) -> Result<(Vec<u8>, Vec<u8>), Box<dyn
|
|||
* pointing to the metatile definition. Index into this table = the metatile ID.
|
||||
*
|
||||
* <<< DEFINITIONS >>>
|
||||
* For each metatile definition:
|
||||
* 2 bytes: Width of the metatile, in 16-bit words.
|
||||
* 2 bytes: Height of the metatile, in 16-bit words.
|
||||
* (n) bytes: The nametable definition for the metatile. Stamp this into vram on each layer.
|
||||
* For each `dmapack` bucket:
|
||||
* Contents of the `dmapack` bucket. Use offsets in headers to access metatiles in `dmapack`
|
||||
* buckets located in this block.
|
||||
*
|
||||
* <<< INSTANCES >>>
|
||||
* For B, then A layers:
|
||||
|
@ -132,13 +131,109 @@ 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).
|
||||
*/
|
||||
pub fn get_metatile_maps( tilemap: &TiledTilemap ) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
// Assemble header of offsets
|
||||
let header_offset = ( tilemap.metatiles.len() * 2 ) + 2;
|
||||
let mut header: Vec<u8> = vec![];
|
||||
|
||||
// Get offsets for each bucket
|
||||
let buckets = get_buckets( tilemap )?;
|
||||
let mut bucket_offsets: HashMap<u16, usize> = HashMap::new();
|
||||
let mut total_offset = header_offset;
|
||||
for ( bucket_id, bucket ) in &buckets {
|
||||
bucket_offsets.insert( *bucket_id, total_offset );
|
||||
total_offset += bucket.len();
|
||||
}
|
||||
|
||||
// Get the offset for each metatile within the buckets
|
||||
for metatile in &tilemap.metatiles {
|
||||
let bucket_offset = bucket_offsets.get( &metatile.pack.bucket ).ok_or( "internal error: no offset for dmapack bucket" )?;
|
||||
let cell_x = metatile.pack.slot[ 0 ];
|
||||
let cell_y = metatile.pack.slot[ 1 ];
|
||||
|
||||
let metatile_byte_offset: u16 = *bucket_offset as u16 + ( ( ( cell_y * 64 ) + cell_x ) * 2 );
|
||||
header.extend( metatile_byte_offset.to_be_bytes() );
|
||||
}
|
||||
|
||||
// Assemble definitions
|
||||
let mut definitions: Vec<u8> = vec![];
|
||||
|
||||
for ( _, bucket ) in &buckets {
|
||||
for cell in bucket {
|
||||
if let Some( nametable_entry ) = cell {
|
||||
definitions.extend( nametable_entry.to_be_bytes() );
|
||||
} else {
|
||||
definitions.extend( ( 0 as u16 ).to_be_bytes() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
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 )
|
||||
}
|
||||
|
||||
fn get_tilemap_prefix_palette( target_source: &str, tilesets: &Vec<TiledTileset> ) -> Result<(usize, Vec<Option<u8>>), Box<dyn Error>> {
|
||||
pub fn get_tilemap_prefix_palette( target_source: &str, tilesets: &Vec<TiledTileset> ) -> Result<(usize, Vec<Option<u8>>), Box<dyn Error>> {
|
||||
// Everything starts at 1 for the blank buffer tile
|
||||
let mut index: usize = 1;
|
||||
|
||||
|
|
Loading…
Reference in New Issue