Add a option to dump debug info for xp3 archive

handle time prop in xp3 file entry
Remove already processed hash file name
This commit is contained in:
2026-04-07 13:33:43 +08:00
parent 73f635cdac
commit a6dda9397b
10 changed files with 72 additions and 5 deletions

View File

@@ -1,6 +1,7 @@
use super::consts::*;
use super::crypt::Crypt;
use crate::scripts::base::ReadSeek;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
/// Represents a single data segment for a file.
@@ -35,6 +36,7 @@ pub struct Xp3Entry {
pub file_hash: u32,
pub original_size: u64,
pub archived_size: u64,
pub timestamp: Option<u64>,
pub segments: Vec<Segment>,
pub extras: Vec<ExtraProp>,
}
@@ -47,13 +49,50 @@ impl Xp3Entry {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExtraProp {
pub tag: [u8; 4],
pub tag: PropTag,
pub data: Vec<u8>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PropTag {
tag: [u8; 4],
}
impl std::fmt::Debug for PropTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", bytes::Bytes::copy_from_slice(&self.tag))
}
}
impl Deref for PropTag {
type Target = [u8; 4];
fn deref(&self) -> &Self::Target {
&self.tag
}
}
impl DerefMut for PropTag {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.tag
}
}
impl From<[u8; 4]> for PropTag {
fn from(value: [u8; 4]) -> Self {
PropTag { tag: value }
}
}
impl PartialEq<&[u8; 4]> for PropTag {
fn eq(&self, other: &&[u8; 4]) -> bool {
&self.tag == *other
}
}
impl ExtraProp {
pub fn is_filename_hash(&self) -> bool {
&self.tag == CHUNK_HNFN
self.tag == CHUNK_HNFN
}
}

View File

@@ -7,6 +7,7 @@ pub const CHUNK_INFO: &[u8; 4] = b"info";
pub const CHUNK_SEGM: &[u8; 4] = b"segm";
pub const CHUNK_ADLR: &[u8; 4] = b"adlr";
pub const CHUNK_HNFN: &[u8; 4] = b"hnfn";
pub const CHUNK_TIME: &[u8; 4] = b"time";
// Index entry flags
pub const TVP_XP3_INDEX_ENCODE_METHOD_MASK: u8 = 0x07;

View File

@@ -26,6 +26,7 @@ pub fn default_init_crypt(archive: &mut Xp3Archive) -> Result<()> {
filename_map.insert(hash, name);
}
}
archive.extras.retain(|extra| !extra.is_filename_hash());
for entry in &mut archive.entries {
if let Some(name) = filename_map.get(&entry.file_hash) {
entry.name = name.clone();

View File

@@ -171,6 +171,9 @@ impl Xp3Archive {
filename: &str,
) -> Result<Self> {
let mut archive = archive::Xp3Archive::new(stream, config, filename)?;
if config.xp3_debug_archive {
println!("Debug info for {}:\n{:#?}", filename, archive);
}
archive.entries.retain(|entry| {
let i = &entry.name;
!(i.find("$$$ This is a protected archive. $$$").is_some()

View File

@@ -66,6 +66,7 @@ impl Xp3Archive {
let mut file_hash = None;
let mut original_size = None;
let mut archived_size = None;
let mut timestamp = None;
let mut segments = Vec::new();
let mut seg_offset = 0;
let mut entry_extras = Vec::new();
@@ -116,11 +117,16 @@ impl Xp3Archive {
});
seg_offset += original_size;
}
} else if &chunk_sig == CHUNK_TIME {
if chunk_size == 8 {
timestamp = Some(index_stream.read_u64()?);
chunk_size -= 8;
}
} else {
let data = index_stream.read_exact_vec(chunk_size as usize)?;
chunk_size = 0;
entry_extras.push(ExtraProp {
tag: chunk_sig,
tag: chunk_sig.into(),
data,
});
}
@@ -140,12 +146,16 @@ impl Xp3Archive {
archived_size: archived_size.ok_or_else(|| {
anyhow::anyhow!("Missing archived size chunk in file entry")
})?,
timestamp,
segments,
extras: entry_extras,
});
} else {
let data = index_stream.read_exact_vec(size as usize)?;
extras.push(ExtraProp { tag: sig, data });
extras.push(ExtraProp {
tag: sig.into(),
data,
});
}
}
}