use crate::newtypes::{ChunkTag, PatchIndex, SchemaVersion};
use std::path::PathBuf;
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("I/O error reading patch stream: {source}")]
Io {
#[source]
source: std::io::Error,
},
#[error("invalid magic bytes")]
InvalidMagic,
#[error("unknown chunk tag: {0}")]
UnknownChunkTag(ChunkTag),
#[error("unknown SQPK command: {0:#x}")]
UnknownSqpkCommand(u8),
#[error("CRC32 mismatch on chunk {tag}: expected {expected:#010x}, got {actual:#010x}")]
ChecksumMismatch {
tag: ChunkTag,
expected: u32,
actual: u32,
},
#[error("invalid field: {context}")]
InvalidField {
context: &'static str,
},
#[error("chunk size {0} exceeds maximum")]
OversizedChunk(usize),
#[error("unknown SqpkFile operation: {0:#02x}")]
UnknownFileOperation(u8),
#[error("UTF-8 decode error: {0}")]
Utf8Error(#[from] std::string::FromUtf8Error),
#[error("decompression error: {source}")]
Decompress {
#[source]
source: std::io::Error,
},
#[error("decode error in {context}: {message}")]
Decode {
context: &'static str,
message: String,
},
#[error("negative file_offset in SqpkFile: {0}")]
NegativeFileOffset(i64),
#[error("patch file ended without EOF_ chunk (truncated download?)")]
TruncatedPatch,
}
impl From<std::io::Error> for ParseError {
fn from(source: std::io::Error) -> Self {
ParseError::Io { source }
}
}
impl From<binrw::Error> for ParseError {
fn from(err: binrw::Error) -> Self {
ParseError::Decode {
context: "binrw",
message: err.to_string(),
}
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum ApplyError {
#[error(transparent)]
Parse(#[from] ParseError),
#[error("I/O error{}: {source}", path.as_ref().map(|p| format!(" at {}", p.display())).unwrap_or_default())]
Io {
path: Option<PathBuf>,
#[source]
source: std::io::Error,
},
#[error("unsupported platform id {0}: cannot resolve SqPack path")]
UnsupportedPlatform(u16),
#[error("apply cancelled by observer")]
Cancelled,
#[error("{kind} schema version mismatch: persisted {found}, this build expects {expected}")]
SchemaVersionMismatch {
kind: &'static str,
found: SchemaVersion,
expected: SchemaVersion,
},
}
impl From<std::io::Error> for ApplyError {
fn from(source: std::io::Error) -> Self {
ApplyError::Io { path: None, source }
}
}
impl ApplyError {
#[must_use]
pub fn io_at(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
ApplyError::Io {
path: Some(path.into()),
source,
}
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum IndexError {
#[error(transparent)]
Parse(#[from] ParseError),
#[error("I/O error{}: {source}", path.as_ref().map(|p| format!(" at {}", p.display())).unwrap_or_default())]
Io {
path: Option<PathBuf>,
#[source]
source: std::io::Error,
},
#[error("unsupported platform id {0}: cannot resolve SqPack path")]
UnsupportedPlatform(u16),
#[error("apply cancelled")]
Cancelled,
#[error(
"indexed apply: source bytes unavailable for region at target_offset {target_offset} length {length}"
)]
SourceUnavailable {
target_offset: u64,
length: u32,
},
#[error("patch source too short: offset {offset} requested {requested} bytes")]
PatchSourceTooShort {
offset: u64,
requested: usize,
},
#[error("patch index {patch} out of range: source carries {count} patches")]
PatchIndexOutOfRange {
patch: PatchIndex,
count: usize,
},
#[error("unsafe target path rejected by index builder: {0:?}")]
UnsafeTargetPath(String),
#[error("{kind} schema version mismatch: persisted {found}, this build expects {expected}")]
SchemaVersionMismatch {
kind: &'static str,
found: SchemaVersion,
expected: SchemaVersion,
},
#[error("duplicate patch in chain: {name:?}")]
DuplicatePatch {
name: String,
},
#[error("invalid field: {context}")]
InvalidField {
context: &'static str,
},
}
impl From<std::io::Error> for IndexError {
fn from(source: std::io::Error) -> Self {
IndexError::Io { path: None, source }
}
}
impl From<ApplyError> for IndexError {
fn from(err: ApplyError) -> Self {
match err {
ApplyError::Parse(p) => IndexError::Parse(p),
ApplyError::Io { path, source } => IndexError::Io { path, source },
ApplyError::UnsupportedPlatform(id) => IndexError::UnsupportedPlatform(id),
ApplyError::Cancelled => IndexError::Cancelled,
ApplyError::SchemaVersionMismatch {
kind,
found,
expected,
} => IndexError::SchemaVersionMismatch {
kind,
found,
expected,
},
}
}
}
impl From<ApplyError> for VerifyError {
fn from(err: ApplyError) -> Self {
match err {
ApplyError::Io { path, source } => VerifyError::Io { path, source },
ApplyError::UnsupportedPlatform(id) => VerifyError::UnsupportedPlatform(id),
other => VerifyError::Io {
path: None,
source: std::io::Error::other(other.to_string()),
},
}
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum VerifyError {
#[error("I/O error{}: {source}", path.as_ref().map(|p| format!(" at {}", p.display())).unwrap_or_default())]
Io {
path: Option<PathBuf>,
#[source]
source: std::io::Error,
},
#[error("unsupported platform id {0}: cannot resolve SqPack path")]
UnsupportedPlatform(u16),
#[error("invalid field: {context}")]
InvalidField {
context: &'static str,
},
#[error(
"CRC32 mismatch at target_offset {target_offset}: expected {expected:#010x}, got {actual:#010x}"
)]
Crc32Mismatch {
target_offset: u64,
expected: u32,
actual: u32,
},
}
impl From<std::io::Error> for VerifyError {
fn from(source: std::io::Error) -> Self {
VerifyError::Io { path: None, source }
}
}
pub type ParseResult<T> = std::result::Result<T, ParseError>;
pub type ApplyResult<T> = std::result::Result<T, ApplyError>;
pub type IndexResult<T> = std::result::Result<T, IndexError>;
pub type VerifyResult<T> = std::result::Result<T, VerifyError>;
pub type Result<T> = std::result::Result<T, Error>;
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Parse(#[from] ParseError),
#[error(transparent)]
Apply(#[from] ApplyError),
#[error(transparent)]
Index(#[from] IndexError),
#[error(transparent)]
Verify(#[from] VerifyError),
}