Replay check version from header

This commit is contained in:
Sofia 2026-08-09 22:25:52 +03:00
parent d34eb0bd33
commit d2aded9e44

View File

@ -135,6 +135,10 @@ impl StandaloneReplayManager {
thread::spawn(move || match fs::read(path) { thread::spawn(move || match fs::read(path) {
Ok(mut bytes) => { Ok(mut bytes) => {
if let Err(e) = read_header(&mut bytes) {
return send.send(ReplayReadEvent::Error(e.to_string()));
}
let cursor = Cursor::new(&mut bytes); let cursor = Cursor::new(&mut bytes);
let decoder = GzDecoder::new(cursor); let decoder = GzDecoder::new(cursor);
@ -665,6 +669,7 @@ impl ReplayRecorder {
thread::spawn(move || { thread::spawn(move || {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
bytes.extend(produce_header());
let encoder = GzEncoder::new(&mut bytes, Compression::default()); let encoder = GzEncoder::new(&mut bytes, Compression::default());
match ciborium::into_writer(&replay, encoder) { match ciborium::into_writer(&replay, encoder) {
Ok(_) => match fs::write(path, bytes) { Ok(_) => match fs::write(path, bytes) {
@ -928,3 +933,102 @@ pub struct ReplayTriggerableState {
active: Option<u16>, active: Option<u16>,
active_for: f64, active_for: f64,
} }
const GAME_NAME: &str = "skullball";
const MAJOR: u8 = match u8::from_str_radix(env!("CARGO_PKG_VERSION_MAJOR"), 10) {
Ok(res) => res,
Err(_) => 0,
};
const MINOR: u8 = match u8::from_str_radix(env!("CARGO_PKG_VERSION_MINOR"), 10) {
Ok(res) => res,
Err(_) => 0,
};
const PATCH: u8 = match u8::from_str_radix(env!("CARGO_PKG_VERSION_PATCH"), 10) {
Ok(res) => res,
Err(_) => 0,
};
const VERSION: Version = Version {
major: MAJOR,
minor: MINOR,
patch: PATCH,
};
fn produce_header() -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
bytes.extend(GAME_NAME.as_bytes());
bytes.push(MAJOR);
bytes.push(MINOR);
bytes.push(PATCH);
bytes
}
fn read_header(bytes: &mut Vec<u8>) -> Result<(), HeaderReadError> {
if bytes.len() < (GAME_NAME.len() + 3) {
return Err(HeaderReadError::ReadIncomplete);
}
let mut name_bytes = Vec::with_capacity(GAME_NAME.len());
for _ in 0..GAME_NAME.len() {
name_bytes.push(bytes.remove(0));
}
let name = String::from_utf8(name_bytes).map_err(|_| HeaderReadError::InvalidTitle)?;
if name != GAME_NAME {
return Err(HeaderReadError::InvalidTitle);
}
let major = bytes.remove(0);
let minor = bytes.remove(0);
let patch = bytes.remove(0);
let version = Version {
major,
minor,
patch,
};
if version != VERSION {
return Err(HeaderReadError::InvalidVersion(version));
}
Ok(())
}
pub enum HeaderReadError {
ReadIncomplete,
InvalidTitle,
InvalidVersion(Version),
}
impl std::fmt::Display for HeaderReadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HeaderReadError::ReadIncomplete => {
write!(f, "Header read incomplete, invalid replay file")
}
HeaderReadError::InvalidTitle => {
write!(f, "Invalid title in header, invalid replay file")
}
HeaderReadError::InvalidVersion(version) => write!(
f,
"Replay game version mismatch: {} != {}",
version, VERSION
),
}
}
}
#[derive(Debug, PartialEq, PartialOrd, Eq)]
pub struct Version {
pub major: u8,
pub minor: u8,
pub patch: u8,
}
impl std::fmt::Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.major, f)?;
write!(f, ".")?;
std::fmt::Display::fmt(&self.minor, f)?;
write!(f, ".")?;
std::fmt::Display::fmt(&self.patch, f)?;
Ok(())
}
}