Move string encoding/decoding into world parser
This commit is contained in:
+37
-3
@@ -30,16 +30,34 @@ pub fn decode_multiline(input: &[u8]) -> String {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn encode_oneline(input: &str) -> Result<Vec<u8>> {
|
||||
input
|
||||
.chars()
|
||||
.map(|c| {
|
||||
CP437_WINGDINGS
|
||||
.encode(c)
|
||||
.ok_or(anyhow!("Couldn't encode char: {}", c))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn decode_oneline(input: &[u8]) -> String {
|
||||
input.iter().map(|&x| CP437_WINGDINGS.decode(x)).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use insta::assert_debug_snapshot;
|
||||
|
||||
use crate::encoding::{decode_multiline, encode_multiline};
|
||||
|
||||
fn serialize_code(input: &str) -> Vec<u8> {
|
||||
encode_multiline(input).expect("Error in test")
|
||||
}
|
||||
|
||||
fn serialize_title(input: &str) -> Vec<u8> {
|
||||
encode_oneline(input).expect("Error in test")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_multiline() {
|
||||
let bytes: Vec<u8> = (0..=255).collect();
|
||||
@@ -47,7 +65,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn newlines() {
|
||||
fn roundtrip_oneline() {
|
||||
let bytes: Vec<u8> = (0..=255).collect();
|
||||
assert_eq!(bytes, serialize_title(&decode_oneline(&bytes)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn newlines_to_cr() {
|
||||
assert_debug_snapshot!(serialize_code("ABC\nDEF"), @r###"
|
||||
[
|
||||
65,
|
||||
@@ -61,6 +85,16 @@ mod tests {
|
||||
"###)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_13_to_wingding() {
|
||||
// Code is allowed to have newlines
|
||||
let bytes = serialize_code("ABC\nDEF");
|
||||
assert!(bytes.contains(&13));
|
||||
|
||||
// But in a board title, that same byte is a wingding
|
||||
assert_debug_snapshot!(decode_oneline(&bytes), @r#""ABC♪DEF""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wingdings() {
|
||||
assert_debug_snapshot!(decode_multiline(&(0..32).collect::<Vec<_>>()), @r###""\0☺☻♥♦♣♠•◘○◙♂♀\n♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼""###);
|
||||
|
||||
+4
-16
@@ -4,15 +4,10 @@ mod preprocess;
|
||||
mod world;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use encoding::{decode_multiline, encode_multiline};
|
||||
use preprocess::eval::Context;
|
||||
use std::{env, error::Error, fs, path::PathBuf, process::exit};
|
||||
use world::World;
|
||||
|
||||
fn to_latin1(bytes: &[u8]) -> String {
|
||||
bytes.iter().map(|&x| x as char).collect()
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
@@ -33,7 +28,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
println!("num boards: {}", &world.boards.len());
|
||||
for board in &mut world.boards {
|
||||
println!("board: {}", to_latin1(&board.name));
|
||||
println!("board: {}", board.name);
|
||||
for stat in &mut board.stats {
|
||||
// Print some of the data parsed.
|
||||
let (x, y) = (stat.x as usize, stat.y as usize);
|
||||
@@ -43,19 +38,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
let terrain_index = (x - 1) + (y - 1) * 60;
|
||||
Some(board.terrain[terrain_index])
|
||||
};
|
||||
println!(
|
||||
" stat at ({}, {}): {:?}, {:?}",
|
||||
x,
|
||||
y,
|
||||
terrain,
|
||||
to_latin1(&stat.code)
|
||||
);
|
||||
println!(" stat at ({}, {}): {:?}, {:?}", x, y, terrain, stat.code);
|
||||
|
||||
// Evaluate macros
|
||||
let old_code = decode_multiline(&stat.code);
|
||||
let new_code = eval_context.eval_program(&old_code)?;
|
||||
stat.code = encode_multiline(&new_code)?;
|
||||
stat.code = eval_context.eval_program(&stat.code)?;
|
||||
}
|
||||
board.name.push_str(" (♪)");
|
||||
}
|
||||
|
||||
// Try to write a modified world file
|
||||
|
||||
+13
-7
@@ -10,6 +10,8 @@ use nom::{
|
||||
sequence::tuple,
|
||||
};
|
||||
|
||||
use crate::encoding::{decode_multiline, decode_oneline, encode_multiline, encode_oneline};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoadError {
|
||||
message: String,
|
||||
@@ -87,7 +89,7 @@ pub struct World {
|
||||
}
|
||||
|
||||
pub struct Board {
|
||||
pub name: Vec<u8>,
|
||||
pub name: String,
|
||||
pub terrain: Vec<[u8; 2]>,
|
||||
pub max_shots: u8,
|
||||
pub is_dark: bool,
|
||||
@@ -118,7 +120,7 @@ pub struct Stat {
|
||||
pub under_color: u8,
|
||||
pub instruction_pointer: i16,
|
||||
pub bind_index: i16,
|
||||
pub code: Vec<u8>,
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
impl World {
|
||||
@@ -201,7 +203,8 @@ impl Board {
|
||||
let (input, _) = le_u16.parse(bytes)?;
|
||||
|
||||
// Read board name
|
||||
let (input, name) = pstring(50)(input)?;
|
||||
let (input, name_bytes) = pstring(50)(input)?;
|
||||
let name = decode_oneline(&name_bytes);
|
||||
|
||||
// Read terrain
|
||||
const NUM_TILES: usize = 60 * 25;
|
||||
@@ -256,7 +259,8 @@ impl Board {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, &'static str> {
|
||||
let mut result = vec![];
|
||||
result.push_padding(2); // reserve space for board size
|
||||
result.push_string(50, &self.name)?;
|
||||
let name_bytes = encode_oneline(&self.name).unwrap();
|
||||
result.push_string(50, &name_bytes)?;
|
||||
|
||||
// Encode terrain
|
||||
if self.terrain.len() != 1500 {
|
||||
@@ -315,7 +319,8 @@ impl Stat {
|
||||
let (input, _) = take(4usize)(input)?;
|
||||
let (input, (instruction_pointer, length)) = tuple((le_i16, le_i16))(input)?;
|
||||
let (input, _) = take(8usize)(input)?;
|
||||
let (input, code) = take(0.max(length) as usize)(input)?;
|
||||
let (input, code_bytes) = take(0.max(length) as usize)(input)?;
|
||||
let code = decode_multiline(&code_bytes);
|
||||
Ok((
|
||||
input,
|
||||
Stat {
|
||||
@@ -333,7 +338,7 @@ impl Stat {
|
||||
under_color,
|
||||
instruction_pointer,
|
||||
bind_index: 0.min(length),
|
||||
code: Vec::from(code),
|
||||
code,
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -363,7 +368,8 @@ impl Stat {
|
||||
result.push_padding(8);
|
||||
if self.bind_index >= 0 {
|
||||
// TODO: more safety around bind-index XOR code
|
||||
result.extend_from_slice(&self.code);
|
||||
let code_bytes = encode_multiline(&self.code).unwrap();
|
||||
result.extend_from_slice(&code_bytes);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user