load CON files
This commit is contained in:
47
src/arena.rs
Normal file
47
src/arena.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use world;
|
||||||
|
use tile::Tile;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
// offset len notes
|
||||||
|
// 0x0 16 start_x for monsters 0-15
|
||||||
|
// 0x10 16 start_y for monsters 0-15
|
||||||
|
// 0x20 8 start_x for party members 0-7
|
||||||
|
// 0x28 8 start_y for party members 0-7
|
||||||
|
// 0x30 16 ???
|
||||||
|
// 0x40 121 11x11 map matrix
|
||||||
|
// 0xB9 7 ???
|
||||||
|
pub struct Arena {
|
||||||
|
monster_x: [u8; 16],
|
||||||
|
monster_y: [u8; 16],
|
||||||
|
party_x: [u8; 8],
|
||||||
|
party_y: [u8; 8],
|
||||||
|
_unknown1: [u8; 16],
|
||||||
|
map: Field,
|
||||||
|
_unknown2: [u8; 7],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Shrine {
|
||||||
|
map: Field,
|
||||||
|
_unknown1: [[u8; 8]; 8], // Arbitrary grouping for Clone impl
|
||||||
|
_unknown2: [u8; 7],
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field = [[Tile; 11]; 11];
|
||||||
|
impl world::Map for Field {
|
||||||
|
fn rows<'a>(&'a self) -> world::BoxedMapIterator {
|
||||||
|
Box::new(self.iter().map(|row| Box::new(row.into_iter()) as world::RowIterator<'a>))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl world::HasMap for Arena {
|
||||||
|
fn map(&self) -> &world::Map {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl world::HasMap for Shrine {
|
||||||
|
fn map(&self) -> &world::Map {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main.rs
25
src/main.rs
@@ -7,39 +7,44 @@ use std::env;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
|
|
||||||
mod world;
|
mod arena;
|
||||||
|
mod tile;
|
||||||
|
mod town;
|
||||||
mod transpose;
|
mod transpose;
|
||||||
|
mod world;
|
||||||
|
|
||||||
use world::Map;
|
fn mmap_to_rows<'a, M: world::HasMap>(mmap: &memmap::Mmap) -> &'a world::HasMap
|
||||||
|
|
||||||
fn mmap_to_rows<'a, M: world::Map>(mmap: &memmap::Mmap) -> &'a world::Map
|
|
||||||
where M: Copy + 'a
|
where M: Copy + 'a
|
||||||
{
|
{
|
||||||
assert!(std::mem::size_of::<M>() <= mmap.len());
|
assert_eq!(std::mem::size_of::<M>(), mmap.len());
|
||||||
unsafe { transmute::<*const u8, &M>(mmap.ptr()) }
|
unsafe { transmute::<*const u8, &M>(mmap.ptr()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let filename;
|
let filename;
|
||||||
let conv;
|
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
filename = &args[1] as &str;
|
filename = &args[1] as &str;
|
||||||
conv = mmap_to_rows::<world::Chunk>;
|
|
||||||
} else {
|
} else {
|
||||||
filename = "data/WORLD.MAP";
|
filename = "data/WORLD.MAP";
|
||||||
conv = mmap_to_rows::<world::World>;
|
|
||||||
}
|
}
|
||||||
|
let conv = match filename {
|
||||||
|
s if s.ends_with("SHRINE.CON") => mmap_to_rows::<arena::Shrine>,
|
||||||
|
s if s.ends_with(".CON") => mmap_to_rows::<arena::Arena>,
|
||||||
|
s if s.ends_with(".ULT") => mmap_to_rows::<town::Town>,
|
||||||
|
s if s.ends_with("WORLD.MAP") => mmap_to_rows::<world::World>,
|
||||||
|
_ => panic!("unrecognized map file"),
|
||||||
|
};
|
||||||
|
|
||||||
let file_mmap = Mmap::open_path(filename, Protection::Read).unwrap();
|
let file_mmap = Mmap::open_path(filename, Protection::Read).unwrap();
|
||||||
let world = conv(&file_mmap);
|
let world = conv(&file_mmap);
|
||||||
|
|
||||||
print_rows(world.rows());
|
print_rows(world.map().rows());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_rows<'a, T: ?Sized, U: ?Sized>(rows: Box<T>)
|
fn print_rows<'a, T: ?Sized, U: ?Sized>(rows: Box<T>)
|
||||||
where T: Iterator<Item = Box<U>> + 'a,
|
where T: Iterator<Item = Box<U>> + 'a,
|
||||||
U: Iterator<Item = &'a world::Tile> + 'a
|
U: Iterator<Item = &'a tile::Tile> + 'a
|
||||||
{
|
{
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let s1: String = row.into_iter().map(|tile| tile.as_char()).collect();
|
let s1: String = row.into_iter().map(|tile| tile.as_char()).collect();
|
||||||
|
|||||||
67
src/tile.rs
Normal file
67
src/tile.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Tile {
|
||||||
|
val: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tile {
|
||||||
|
pub fn as_char(&self) -> char {
|
||||||
|
match self.val {
|
||||||
|
0 => '~', // deep water '🌊'
|
||||||
|
1 => '≈', // medium water
|
||||||
|
2 => '≋', // shallow water
|
||||||
|
3 => ',', // swamp
|
||||||
|
4 => '⢊', // plain '░'
|
||||||
|
5 => '🌿', // scrub 'წ'
|
||||||
|
6 => '🌳', // forest
|
||||||
|
7 => '⌓', // hill '∩'
|
||||||
|
8 => '⨇', // mountain '△'
|
||||||
|
9 => '☗', // dungeon
|
||||||
|
10 => '⍟', // city
|
||||||
|
11 | 13...15 => '⛫', // castle
|
||||||
|
12 => '❖', // village
|
||||||
|
22 => '⎔', // tile floor
|
||||||
|
23 => '⟗', // bridge
|
||||||
|
24 => '⧬', // balloon
|
||||||
|
25 => '≃', // bridge top
|
||||||
|
26 => '≂', // bridge bottom
|
||||||
|
27 => '⍐', // ladder up
|
||||||
|
28 => '⍗', // ladder down
|
||||||
|
29 => 'v', // ruin
|
||||||
|
30 => '◌', // shrine
|
||||||
|
31 => '😇', // avatar
|
||||||
|
48 => '◯', // column
|
||||||
|
49 => '◣', // SW
|
||||||
|
50 => '◢', // SE
|
||||||
|
51 => '◤', // NW
|
||||||
|
52 => '◥', // NE
|
||||||
|
53 => '◉', // Mast
|
||||||
|
54 => '⎈', // ship's wheel
|
||||||
|
55 => 'ფ', // rocks '❍'
|
||||||
|
56 => '/', // Lyin down
|
||||||
|
57 => '⬛', // stone wall
|
||||||
|
58 => '⧯', // '🔒', // locked door
|
||||||
|
59 => '⧮', // '🔓', // unlocked door
|
||||||
|
60 => '💰', // chest
|
||||||
|
61 => '☥', // ankh
|
||||||
|
62 => '⨳', // brick floor '⌗'
|
||||||
|
63 => '▤', // wood planks '⧻'
|
||||||
|
68 => '🌫', // poison field
|
||||||
|
69 => '⚡', // energy field
|
||||||
|
70 => '🔥', // fire field
|
||||||
|
71 => '💤', // sleep field
|
||||||
|
72 => '▣', // solid barrier
|
||||||
|
73 => '▒', // hidden passage
|
||||||
|
75 => '🍖', // spit (rotisserie) '🍳'
|
||||||
|
76 => '⌘', // lava
|
||||||
|
96...121 => ::std::char::from_u32((self.val - 31) as u32).unwrap(),
|
||||||
|
122 => '=', // space
|
||||||
|
123 => '⊐', // right ''
|
||||||
|
124 => '⊏', // left '⊨'
|
||||||
|
125 => '▢', // window
|
||||||
|
126 => '✨', // space
|
||||||
|
127 => '▓', // brick wall
|
||||||
|
189 => '⚔', // phantom 2
|
||||||
|
_ => panic!("{0}", self.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/town.rs
Normal file
41
src/town.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use world::Chunk;
|
||||||
|
use world::HasMap;
|
||||||
|
use world::Map;
|
||||||
|
use tile::Tile;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum Behavior {
|
||||||
|
FIXED = 0x00,
|
||||||
|
WANDER = 0x01,
|
||||||
|
FOLLOW = 0x80,
|
||||||
|
ATTACK = 0xFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Town {
|
||||||
|
map: Chunk,
|
||||||
|
npc_tile1: [Tile; 32],
|
||||||
|
npc_x1: [u8; 32],
|
||||||
|
npc_y1: [u8; 32],
|
||||||
|
npc_tile2: [Tile; 32],
|
||||||
|
npc_x2: [u8; 32],
|
||||||
|
npc_y2: [u8; 32],
|
||||||
|
npc_behavior: [Behavior; 32],
|
||||||
|
npc_talk_idx: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasMap for Town {
|
||||||
|
fn map(&self) -> &Map {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x400 32 tile for NPCs 0-31
|
||||||
|
// 0x420 32 start_x for NPCs 0-31
|
||||||
|
// 0x440 32 start_y for NPCs 0-31
|
||||||
|
// 0x460 32 repitition of 0x400-0x41F
|
||||||
|
// 0x480 32 repitition of 0x420-0x43F
|
||||||
|
// 0x4A0 32 repitition of 0x440-0x45F
|
||||||
|
// 0x4C0 32 movement_behavior for NPCs 0-31 (0x0-fixed, 0x1-wander, 0x80-follow, 0xFF-attack)
|
||||||
|
// 0x4E0 32 conversion index (tlk file) for NPCs 0-31
|
||||||
89
src/world.rs
89
src/world.rs
@@ -3,85 +3,24 @@ extern crate itertools;
|
|||||||
|
|
||||||
use self::itertools::Itertools;
|
use self::itertools::Itertools;
|
||||||
|
|
||||||
|
use tile::Tile;
|
||||||
use transpose::TransposableIterator;
|
use transpose::TransposableIterator;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
pub type RowIterator<'a> = Box<Iterator<Item = &'a Tile> + 'a>;
|
||||||
pub struct Tile {
|
pub type BoxedMapIterator<'a> = Box<Iterator<Item = RowIterator<'a>> + 'a>;
|
||||||
val: u8,
|
pub type ExactBoxedMapIterator<'a> = Box<ExactSizeIterator<Item = RowIterator<'a>> + 'a>;
|
||||||
}
|
|
||||||
|
|
||||||
impl Tile {
|
|
||||||
pub fn as_char(&self) -> char {
|
|
||||||
match self.val {
|
|
||||||
0 => '~', // deep water '🌊'
|
|
||||||
1 => '≈', // medium water
|
|
||||||
2 => '≋', // shallow water
|
|
||||||
3 => ',', // swamp
|
|
||||||
4 => '⢊', // plain '░'
|
|
||||||
5 => '🌿', // scrub 'წ'
|
|
||||||
6 => '🌳', // forest
|
|
||||||
7 => '⌓', // hill '∩'
|
|
||||||
8 => '⨇', // mountain '△'
|
|
||||||
9 => '☗', // dungeon
|
|
||||||
10 => '⍟', // city
|
|
||||||
11 | 13...15 => '⛫', // castle
|
|
||||||
12 => '❖', // village
|
|
||||||
22 => '⎔', // tile floor
|
|
||||||
23 => '⟗', // bridge
|
|
||||||
24 => '⧬', // balloon
|
|
||||||
25 => '≃', // bridge top
|
|
||||||
26 => '≂', // bridge bottom
|
|
||||||
27 => '⍐', // ladder up
|
|
||||||
28 => '⍗', // ladder down
|
|
||||||
29 => 'v', // ruin
|
|
||||||
30 => '◌', // shrine
|
|
||||||
48 => '◯', // column
|
|
||||||
49 => '◣', // SW
|
|
||||||
50 => '◢', // SE
|
|
||||||
51 => '◤', // NW
|
|
||||||
52 => '◥', // NE
|
|
||||||
54 => '⎈', // ship's wheel
|
|
||||||
55 => 'ფ', // rocks '❍'
|
|
||||||
56 => '/', // Lyin down
|
|
||||||
57 => '⬛', // stone wall
|
|
||||||
58 => '⧯', // '🔒', // locked door
|
|
||||||
59 => '⧮', // '🔓', // unlocked door
|
|
||||||
60 => '💰', // chest
|
|
||||||
61 => '☥', // ankh
|
|
||||||
62 => '⨳', // brick floor '⌗'
|
|
||||||
63 => '▤', // wood planks '⧻'
|
|
||||||
68 => '🌫', // poison field
|
|
||||||
69 => '⚡', // energy field
|
|
||||||
70 => '🔥', // fire field
|
|
||||||
71 => '💤', // sleep field
|
|
||||||
72 => '▣', // solid barrier
|
|
||||||
73 => '▒', // hidden passage
|
|
||||||
75 => '🍖', // spit (rotisserie) '🍳'
|
|
||||||
76 => '⌘', // lava
|
|
||||||
96...121 => ::std::char::from_u32((self.val - 31) as u32).unwrap(),
|
|
||||||
122 => '=', // space
|
|
||||||
123 => '⊐', // right ''
|
|
||||||
124 => '⊏', // left '⊨'
|
|
||||||
125 => '▢', // window
|
|
||||||
126 => '✨', // space
|
|
||||||
127 => '▓', // brick wall
|
|
||||||
189 => '⚔', // phantom 2
|
|
||||||
_ => '?', // panic!("{0}", self.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Map {
|
pub trait Map {
|
||||||
fn rows<'a>(&'a self) -> BoxedMapIterator;
|
fn rows<'a>(&'a self) -> BoxedMapIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BoxedMapIterator<'a> = Box<Iterator<Item = ChunkRowIterator<'a>> + 'a>;
|
pub trait HasMap {
|
||||||
pub type ExactBoxedMapIterator<'a> = Box<ExactSizeIterator<Item = ChunkRowIterator<'a>> + 'a>;
|
fn map(&self) -> ⤅
|
||||||
|
}
|
||||||
|
|
||||||
const CHUNKDIM: usize = 32;
|
const CHUNKDIM: usize = 32;
|
||||||
pub type ChunkRow = [Tile; CHUNKDIM];
|
pub type ChunkRow = [Tile; CHUNKDIM];
|
||||||
pub type ChunkRect = [ChunkRow; CHUNKDIM];
|
pub type ChunkRect = [ChunkRow; CHUNKDIM];
|
||||||
pub type ChunkRowIterator<'a> = Box<Iterator<Item = &'a Tile> + 'a>;
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pub rect: ChunkRect,
|
pub rect: ChunkRect,
|
||||||
@@ -91,7 +30,7 @@ impl Chunk {
|
|||||||
fn rows_exact<'a>(&'a self) -> ExactBoxedMapIterator {
|
fn rows_exact<'a>(&'a self) -> ExactBoxedMapIterator {
|
||||||
Box::new(self.rect
|
Box::new(self.rect
|
||||||
.iter()
|
.iter()
|
||||||
.map(|row| Box::new(row.into_iter()) as ChunkRowIterator<'a>))
|
.map(|row| Box::new(row.into_iter()) as RowIterator<'a>))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,12 +38,12 @@ impl Map for Chunk {
|
|||||||
fn rows<'a>(&'a self) -> BoxedMapIterator {
|
fn rows<'a>(&'a self) -> BoxedMapIterator {
|
||||||
Box::new(self.rect
|
Box::new(self.rect
|
||||||
.iter()
|
.iter()
|
||||||
.map(|row| Box::new(row.into_iter()) as ChunkRowIterator<'a>))
|
.map(|row| Box::new(row.into_iter()) as RowIterator<'a>))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a Chunk {
|
impl<'a> IntoIterator for &'a Chunk {
|
||||||
type Item=ChunkRowIterator<'a>;
|
type Item=RowIterator<'a>;
|
||||||
type IntoIter=ExactBoxedMapIterator<'a>;
|
type IntoIter=ExactBoxedMapIterator<'a>;
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.rows_exact()
|
self.rows_exact()
|
||||||
@@ -123,6 +62,12 @@ impl Map for World {
|
|||||||
fn rows<'a>(&'a self) -> BoxedMapIterator {
|
fn rows<'a>(&'a self) -> BoxedMapIterator {
|
||||||
let flipped = self.rect.iter().map(|cr| cr.into_iter().transpose());
|
let flipped = self.rect.iter().map(|cr| cr.into_iter().transpose());
|
||||||
Box::new(flipped.flatten()
|
Box::new(flipped.flatten()
|
||||||
.map(|r| Box::new(r.flatten()) as ChunkRowIterator<'a>))
|
.map(|r| Box::new(r.flatten()) as RowIterator<'a>))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasMap for World {
|
||||||
|
fn map(&self) -> &Map {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user