Add docs on treatment of strings, World fields
This commit is contained in:
+20
@@ -7,6 +7,26 @@
|
|||||||
//! zztff currently only supports the format used by ZZT 3.2, the last official release.
|
//! zztff currently only supports the format used by ZZT 3.2, the last official release.
|
||||||
//! Super ZZT (the sequel) is not currently supported.
|
//! Super ZZT (the sequel) is not currently supported.
|
||||||
//!
|
//!
|
||||||
|
//! ## String encoding
|
||||||
|
//!
|
||||||
|
//! zztff decodes all text data from extended ASCII into Unicode `String`s, because those are usually more convenient
|
||||||
|
//! for examining text and performing string manipuation. "Extended ASCII" is ambiguous as an encoding, so zztff assumes
|
||||||
|
//! [CP437](https://en.wikipedia.org/wiki/Code_page_437) for all conversions.
|
||||||
|
//! This is the de facto standard for most ZZT worlds and is what you want most of the time.
|
||||||
|
//!
|
||||||
|
//! If you have text that is not CP437, zztff will still read and write it just fine, and it is guaranteed to round-trip
|
||||||
|
//! back to disk unchanged. However, depending on the author's original encoding, foreign characters and dingbats
|
||||||
|
//! may display incorrectly.
|
||||||
|
//! (This is a difficult problem to solve in general, because ZZT formats do not specify their encodings, and if the
|
||||||
|
//! ZZT world was using a custom font, it can have characters without any Unicode equivalent.)
|
||||||
|
//!
|
||||||
|
//! Serialization will fail with [`EncodeError::EncodingError`] if you add a character to a String that doesn't have a
|
||||||
|
//! CP437 equivalent.
|
||||||
|
//! Serialization can also fail with [`EncodeError::StringTooLong`] if a String would exceed the maximum length of the
|
||||||
|
//! given text field in the file format.
|
||||||
|
//! (You can check the encoded length of a String by counting its codepoints, e.g., `s.chars().count()`;
|
||||||
|
//! zztff guarantees a 1:1 correspondence between codepoints in the String and bytes in the file format.)
|
||||||
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! ### Reading an existing .ZZT file
|
//! ### Reading an existing .ZZT file
|
||||||
|
|||||||
@@ -76,6 +76,15 @@ mod tests {
|
|||||||
assert_eq!(bytes, serialize_title(&decode_oneline(&bytes)))
|
assert_eq!(bytes, serialize_title(&decode_oneline(&bytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_codepoint_per_byte() {
|
||||||
|
// This check guarantees that length checks on Unicode Strings will tell you something useful
|
||||||
|
// about how much headroom your text will have once serialized into a field in the file format.
|
||||||
|
let bytes: Vec<u8> = (0..=255).collect();
|
||||||
|
assert_eq!(256, decode_oneline(&bytes).chars().count());
|
||||||
|
assert_eq!(256, decode_multiline(&bytes).chars().count());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn newlines_to_cr() {
|
fn newlines_to_cr() {
|
||||||
assert_debug_snapshot!(serialize_code("ABC\nDEF"), @r###"
|
assert_debug_snapshot!(serialize_code("ABC\nDEF"), @r###"
|
||||||
|
|||||||
+57
-1
@@ -11,23 +11,79 @@ use super::elements::Element;
|
|||||||
use super::errors::{DecodeError, EncodeError};
|
use super::errors::{DecodeError, EncodeError};
|
||||||
use super::text::{decode_multiline, decode_oneline, encode_multiline, encode_oneline};
|
use super::text::{decode_multiline, decode_oneline, encode_multiline, encode_oneline};
|
||||||
|
|
||||||
/// A ZZT world file.
|
/// A data structure representing a ZZT world.
|
||||||
|
///
|
||||||
|
/// ZZT uses the same format for both worlds proper (.ZZT files) as well as their saved games (.SAV); saved games are
|
||||||
|
/// just snapshots of the world state after it's been played in. This `World` struct is for working with either type of
|
||||||
|
/// file.
|
||||||
|
///
|
||||||
|
/// Some of the fields are more esoteric than others due to this need of being able to represent the entire world state,
|
||||||
|
/// not just the starting conditions. If you just want to create a new world, it's safe to call `::default()` and leave
|
||||||
|
/// most of the fields alone. The only one that needs a non-default value is [`World::name`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
|
/// Value of the `ammo` counter.
|
||||||
pub ammo: i16,
|
pub ammo: i16,
|
||||||
|
/// Value of the `gems` counter.
|
||||||
pub gems: i16,
|
pub gems: i16,
|
||||||
|
/// Which keys the player is carrying.
|
||||||
pub keys: Keys,
|
pub keys: Keys,
|
||||||
|
/// Value of the `health` counter.
|
||||||
pub health: i16,
|
pub health: i16,
|
||||||
|
/// Index of the board the player starts on.
|
||||||
|
///
|
||||||
|
/// If this is a saved game, this field holds the index of the board that the player is _currently_ on, which is
|
||||||
|
/// where they will start play upon restoring their save.
|
||||||
pub starting_board: i16,
|
pub starting_board: i16,
|
||||||
|
/// Value of the `torches` counter.
|
||||||
pub torches: i16,
|
pub torches: i16,
|
||||||
|
/// Remaining cycles of torch light (0 = torch not active).
|
||||||
pub torch_cycles: i16,
|
pub torch_cycles: i16,
|
||||||
|
/// Remaining cycles of energizer effect (0 = not active).
|
||||||
pub energizer_cycles: i16,
|
pub energizer_cycles: i16,
|
||||||
|
/// Value of the `score` counter.
|
||||||
pub score: i16,
|
pub score: i16,
|
||||||
|
/// Stem of the world's filename, e.g., "TOWN" for a world named TOWN.ZZT (max 20 characters).
|
||||||
|
///
|
||||||
|
/// This field should typically match the filename of the .ZZT file you're writing to disk. If it differs, ZZT may
|
||||||
|
/// have trouble loading your file.
|
||||||
|
///
|
||||||
|
/// For saved games (.SAV files), this field should match the filename of the .ZZT file that was being played.
|
||||||
|
/// played. ZZT uses this field to associate the saved game with the world file it came from.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Named flags set by ZZT-OOP `#set` commands (max 20 characters each).
|
||||||
|
///
|
||||||
|
/// Empty strings represent unused slots. The slot order is mostly invisible to ZZT-OOP, and ZZT will fill up the
|
||||||
|
/// slots from lowest to highest. However, order does matter once the array is full: trying to `#set` an eleventh
|
||||||
|
/// flag overwrites the tenth slot.
|
||||||
|
///
|
||||||
|
/// The file format supports arbitrary names, but ZZT-OOP only recognizes uppercase flags. Additionally, ZZT-OOP's
|
||||||
|
/// quirky parsing rules mean that many special characters are not recognized; see [this wiki
|
||||||
|
/// article](https://wiki.zzt.org/wiki/Set) for details.
|
||||||
pub flags: [String; 10],
|
pub flags: [String; 10],
|
||||||
|
/// Value of the `time` counter.
|
||||||
pub time: i16,
|
pub time: i16,
|
||||||
|
/// Sub-second state for the `time` countdown.
|
||||||
|
///
|
||||||
|
/// When on a board with a time limit, ZZT decrements the `time` counter approximately every second. This field
|
||||||
|
/// tracks partial seconds that have elapsed, so that ZZT can decide between decrementing `time` during this tick
|
||||||
|
/// versus waiting another tick.
|
||||||
pub time_ticks: i16,
|
pub time_ticks: i16,
|
||||||
|
/// Whether this file is a saved game (.SAV) rather than a world file (.ZZT).
|
||||||
|
///
|
||||||
|
/// ZZT 3.2 uses this as an anti-cheat mechanism: the built-in editor refuses to open any files that have it set to
|
||||||
|
/// true. Otherwise, people could temporarily rename their save to have a .ZZT extension, then edit their way out of
|
||||||
|
/// a sticky situation.
|
||||||
|
///
|
||||||
|
/// (This was never very secure, and is even less of a deterrent nowadays. But it's still part of the file format.)
|
||||||
pub saved_game: bool,
|
pub saved_game: bool,
|
||||||
|
/// The boards in this world.
|
||||||
|
///
|
||||||
|
/// A valid world must have at least 1 board (the title screen). Trying to serialize an empty world will result in
|
||||||
|
/// an encoding error.
|
||||||
|
///
|
||||||
|
/// ZZT 3.2 supports a maximum of 101 boards per world. The file format (and zztff) can handle larger worlds, but
|
||||||
|
/// trying to load them in ZZT may cause it to crash.
|
||||||
pub boards: Vec<Board>,
|
pub boards: Vec<Board>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user