From 0b73a45f21dcde4ef876bd36fd9c4f99273a300c Mon Sep 17 00:00:00 2001
From: Learath <learath2@gmail.com>
Date: Fri, 5 Jun 2020 16:05:10 +0300
Subject: [PATCH] Add ddnet SHA256 extension

---
 demo/src/file.rs                   | 16 ++++++++--------
 demo/src/format.rs                 | 12 ++++++++++--
 demo/src/writer.rs                 |  7 +++++--
 teehistorian/Cargo.toml            |  1 +
 teehistorian/src/format/mod.rs     |  6 ++++++
 teehistorian/src/lib.rs            |  1 +
 tools/src/bin/teehistorian2demo.rs |  1 +
 7 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/demo/src/file.rs b/demo/src/file.rs
index 75340da..091bcc3 100644
--- a/demo/src/file.rs
+++ b/demo/src/file.rs
@@ -137,24 +137,24 @@ pub struct Writer {
 }
 
 impl Writer {
-    fn new_impl(file: File, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8]) -> io::Result<Writer> {
+    fn new_impl(file: File, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8], map_sha256: &[u8]) -> io::Result<Writer> {
         let mut callback_data = WriteCallbackData {
             file: BufWriter::new(file),
         };
-        let raw = writer::Writer::new(&mut callback_data, net_version, map_name, map_crc, type_, timestamp)?;
+        let raw = writer::Writer::new(&mut callback_data, net_version, map_name, map_crc, type_, timestamp, map_sha256)?;
         Ok(Writer {
             callback_data: callback_data,
             raw: raw,
         })
     }
-    pub fn new(file: File, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8]) -> io::Result<Writer> {
-        Self::new_impl(file, net_version, map_name, map_crc, type_, timestamp)
+    pub fn new(file: File, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8], map_sha256: &[u8]) -> io::Result<Writer> {
+        Self::new_impl(file, net_version, map_name, map_crc, type_, timestamp, map_sha256)
     }
-    pub fn create<P: AsRef<Path>>(path: P, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8]) -> io::Result<Writer> {
-        fn inner(path: &Path, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8]) -> io::Result<Writer> {
-            Writer::new_impl(File::create(path)?, net_version, map_name, map_crc, type_, timestamp)
+    pub fn create<P: AsRef<Path>>(path: P, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8], map_sha256: &[u8]) -> io::Result<Writer> {
+        fn inner(path: &Path, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8], map_sha256: &[u8]) -> io::Result<Writer> {
+            Writer::new_impl(File::create(path)?, net_version, map_name, map_crc, type_, timestamp, map_sha256)
         }
-        inner(path.as_ref(), net_version, map_name, map_crc, type_, timestamp)
+        inner(path.as_ref(), net_version, map_name, map_crc, type_, timestamp, map_sha256)
     }
     pub fn write_chunk(&mut self, chunk: format::Chunk) -> io::Result<()> {
         self.raw.write_chunk(&mut self.callback_data, chunk)
diff --git a/demo/src/format.rs b/demo/src/format.rs
index cba2922..dea2992 100644
--- a/demo/src/format.rs
+++ b/demo/src/format.rs
@@ -21,6 +21,11 @@ use writer;
 
 pub const MAGIC: &'static [u8; 7] = b"TWDEMO\0";
 pub const MAX_SNAPSHOT_SIZE: usize = 65536;
+pub const UUID_DEMO_EXTENSION: [u8; 16] = [
+	// "6be6da4a-cebd-380c-9b5b-1289c842d780"
+	0x6b, 0xe6, 0xda, 0x4a, 0xce, 0xbd, 0x38, 0x0c,
+	0x9b, 0x5b, 0x12, 0x89, 0xc8, 0x42, 0xd7, 0x80
+];
 
 pub const TYPE_CLIENT: &'static [u8] = b"client";
 pub const TYPE_SERVER: &'static [u8] = b"server";
@@ -33,6 +38,7 @@ pub enum Version {
     V3,
     V4,
     V5,
+    V6,
 }
 
 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -77,6 +83,7 @@ impl Version {
             3 => Version::V3,
             4 => Version::V4,
             5 => Version::V5,
+            6 => Version::V6,
             _ => return Err(Error::UnknownVersion(v)),
         })
     }
@@ -85,12 +92,13 @@ impl Version {
             Version::V3 => 3,
             Version::V4 => 4,
             Version::V5 => 5,
+            Version::V6 => 6,
         }
     }
     fn max_tick_delta(self) -> u8 {
         match self {
             Version::V3 | Version::V4 => CHUNKTICKMASK_TICK_V3,
-            Version::V5 => CHUNKTICKMASK_TICK_V5,
+            Version::V5 | Version::V6 => CHUNKTICKMASK_TICK_V5,
         }
     }
 }
@@ -465,7 +473,7 @@ impl ChunkHeader {
     pub fn write<CB>(&self, cb: &mut CB, version: Version) -> Result<(), CB::Error>
         where CB: writer::Callback,
     {
-        assert!(version == Version::V5, "only v5 writing is implemented");
+        assert!(version == Version::V5 || version == Version::V6, "only v5/v6 writing is implemented");
         match *self {
             ChunkHeader::Tickmarker(keyframe, Tickmarker::Delta(dt)) => {
                 assert!(dt <= version.max_tick_delta());
diff --git a/demo/src/writer.rs b/demo/src/writer.rs
index 954097b..2ba397c 100644
--- a/demo/src/writer.rs
+++ b/demo/src/writer.rs
@@ -17,6 +17,7 @@ use format::MAX_SNAPSHOT_SIZE;
 use format::Tick;
 use format::Tickmarker;
 use format::TimelineMarkers;
+use format::UUID_DEMO_EXTENSION;
 use format::Version;
 
 pub trait Callback {
@@ -39,11 +40,11 @@ fn nullterminated_arrayvec_from_slice<A: Array>(data: &[A::Item]) -> ArrayVec<A>
     data.iter().cloned().collect()
 }
 
-const WRITER_VERSION: Version = Version::V5;
+const WRITER_VERSION: Version = Version::V6;
 
 
 impl Writer {
-    pub fn new<CB: Callback>(cb: &mut CB, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8])
+    pub fn new<CB: Callback>(cb: &mut CB, net_version: &[u8], map_name: &[u8], map_crc: u32, type_: &[u8], timestamp: &[u8], map_sha256: &[u8])
         -> Result<Writer, CB::Error>
     {
         use self::nullterminated_arrayvec_from_slice as nafs;
@@ -64,6 +65,8 @@ impl Writer {
         };
         writer.write_header(cb)?;
         cb.write_raw(&TimelineMarkers { timeline_markers: ArrayVec::new() }.pack())?;
+        cb.write(&UUID_DEMO_EXTENSION)?;
+        cb.write(map_sha256)?;
         Ok(writer)
     }
     fn write_header<CB: Callback>(&mut self, cb: &mut CB) -> Result<(), CB::Error> {
diff --git a/teehistorian/Cargo.toml b/teehistorian/Cargo.toml
index 28f92cf..11bb9b2 100644
--- a/teehistorian/Cargo.toml
+++ b/teehistorian/Cargo.toml
@@ -9,6 +9,7 @@ arrayvec = { version = "0.4.6", features = ["serde-1"] }
 buffer = "0.1.7"
 common = { path = "../common/" }
 chrono = "0.4.0"
+hex = "0.4.2"
 itertools = "0.7.4"
 serde = "1.0.23"
 serde_derive = "1.0.7"
diff --git a/teehistorian/src/format/mod.rs b/teehistorian/src/format/mod.rs
index d138a7c..51145f7 100644
--- a/teehistorian/src/format/mod.rs
+++ b/teehistorian/src/format/mod.rs
@@ -38,6 +38,7 @@ pub struct Header<'a> {
     pub map_name: Cow<'a, str>,
     pub map_size: u32,
     pub map_crc: u32,
+    pub map_sha256: [u8; 32],
 }
 
 #[derive(Debug)]
@@ -63,6 +64,7 @@ pub enum HeaderError {
     MalformedServerPort,
     MalformedMapSize,
     MalformedMapCrc,
+    MalformedMapSha256,
 }
 
 impl From<WrongMagic> for HeaderError {
@@ -103,6 +105,7 @@ struct JsonHeader<'a> {
     map_name: Cow<'a, str>,
     map_size: Cow<'a, str>,
     map_crc: Cow<'a, str>,
+    map_sha256: Cow<'a, str>,
 }
 
 pub fn read_header<'a>(p: &mut Unpacker<'a>)
@@ -113,6 +116,8 @@ pub fn read_header<'a>(p: &mut Unpacker<'a>)
     let json_header: JsonHeader = serde_json::from_slice(header_data)
         .map_err(|e| if e.is_data() { MalformedHeader } else { MalformedJson })?;
     let version = json_header.version.parse().map_err(|_| MalformedVersion)?;
+    let mut sha256 = [0; 32];
+    sha256.copy_from_slice(&hex::decode(json_header.map_sha256.as_ref()).map_err(|_| MalformedMapSha256)?);
     let header = Header {
         version: version,
         game_uuid: json_header.game_uuid.parse().map_err(|_| MalformedGameUuid)?,
@@ -125,6 +130,7 @@ pub fn read_header<'a>(p: &mut Unpacker<'a>)
         map_name: json_header.map_name,
         map_size: json_header.map_size.parse().map_err(|_| MalformedMapSize)?,
         map_crc: u32::from_str_radix(&json_header.map_crc, 16).map_err(|_| MalformedMapCrc)?,
+        map_sha256: sha256,
     };
     Ok(header)
 }
diff --git a/teehistorian/src/lib.rs b/teehistorian/src/lib.rs
index c8e90f5..cc40cce 100644
--- a/teehistorian/src/lib.rs
+++ b/teehistorian/src/lib.rs
@@ -12,6 +12,7 @@ extern crate serde_json;
 extern crate uuid;
 extern crate vec_map;
 extern crate warn;
+extern crate hex;
 
 mod bitmagic;
 mod file;
diff --git a/tools/src/bin/teehistorian2demo.rs b/tools/src/bin/teehistorian2demo.rs
index 27f259c..1aee5cc 100644
--- a/tools/src/bin/teehistorian2demo.rs
+++ b/tools/src/bin/teehistorian2demo.rs
@@ -97,6 +97,7 @@ fn process(in_: &Path, out: &Path) -> Result<(), Error> {
             header.map_crc,
             demo::format::TYPE_SERVER,
             b"", // Timestamp
+            &header.map_sha256,
         )?;
     }
     let mut delta = snap::Delta::new();
-- 
2.26.2

