verticopolis_engine/tdt/mod.rs
1//! The 1994 `.TDT` save format: a port of `src/storage/tdt*.ts`, held to
2//! `conformance/tdt-cases.json` by the `tdt` referee binary and the test in
3//! `referee.rs`. Bytes in, the serialized game out (`import_tdt`); the
4//! serialized game in, bytes out (`export_tdt`). The engine never depends on
5//! this module; a tower that never touches a 1994 file carries none of it.
6
7pub mod byte_reader;
8pub mod byte_writer;
9#[cfg(test)]
10mod cathedral_tests;
11pub mod encoder;
12pub mod export;
13pub mod export_gather;
14pub mod export_parking;
15pub mod export_report;
16pub mod export_tables;
17#[cfg(any(test, feature = "conformance"))]
18pub mod fixtures;
19#[cfg(test)]
20mod fixtures_tests;
21pub mod format;
22pub mod import;
23pub mod import_report;
24pub mod pacing;
25pub mod parse;
26pub mod part_merge;
27#[cfg(any(test, feature = "conformance"))]
28pub mod referee;
29pub mod resync;
30pub mod stair_scan;
31pub mod stamp;
32pub mod tables;
33pub mod tail;
34pub mod transports;
35pub mod types;
36pub mod venues;
37
38/// A `.TDT` file that cannot be read; the message is player-readable.
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct LegacyImportError(pub String);
41
42/// A tower the 1994 format cannot hold; the message is player-readable.
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct LegacyExportError(pub String);
45
46impl std::fmt::Display for LegacyImportError {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.write_str(&self.0)
49 }
50}
51
52impl std::fmt::Display for LegacyExportError {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.write_str(&self.0)
55 }
56}
57
58/// Reads a 1994 SimTower `.TDT` save into this engine's save JSON, plus
59/// warnings about anything it could not carry over. Load the JSON with
60/// [`deserialize_tdt`](crate::load::deserialize_tdt) after parsing it. The
61/// error is a sentence a player can read.
62///
63/// ```
64/// use verticopolis_engine::clock::GameMode;
65/// use verticopolis_engine::sim::Simulation;
66/// use verticopolis_engine::{export_tdt, import_tdt};
67///
68/// let sim = Simulation::new_game(1, GameMode::Classic);
69/// let bytes = export_tdt(&sim.serialize().to_string()).unwrap();
70/// let (json, _warnings) = import_tdt(&bytes, "TOWER.TDT").unwrap();
71/// let save: serde_json::Value = serde_json::from_str(&json).unwrap();
72/// let tower = verticopolis_engine::load::deserialize_tdt(&save).unwrap();
73/// assert_eq!(tower.mode, GameMode::Classic);
74/// ```
75pub fn import_tdt(bytes: &[u8], filename: &str) -> Result<(String, Vec<String>), String> {
76 let parsed = parse::parse_tdt(bytes, filename).map_err(|e| e.0)?;
77 Ok((parsed.save.to_string(), parsed.warnings))
78}
79
80/// Writes a Classic game's save JSON (the text of
81/// [`Simulation::serialize`](crate::sim::Simulation::serialize)) as a 1994
82/// SimTower `.TDT` file. The error is a sentence a player can read, or says
83/// what made the JSON unusable.
84pub fn export_tdt(save_json: &str) -> Result<Vec<u8>, String> {
85 let save: serde_json::Value =
86 crate::load::parse_save_json(save_json.as_bytes()).map_err(|e| format!("save: {e}"))?;
87 export::build_tdt(&save)
88 .map(|built| built.bytes)
89 .map_err(|e| e.0)
90}