This commit is contained in:
Rph :3 2025-08-20 19:49:59 +02:00
parent a885648065
commit da5f9b62c2
No known key found for this signature in database
3 changed files with 196 additions and 2 deletions

57
Cargo.lock generated Normal file
View File

@ -0,0 +1,57 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "Excavator"
version = "0.1.0"
dependencies = [
"anyhow",
"flate2",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "anyhow"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]

View File

@ -4,3 +4,5 @@ version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.99"
flate2 = "1.1.2"

View File

@ -1,3 +1,138 @@
fn main() {
println!("Hello, world!");
use std::{env, fs::{create_dir, File}, io::{BufReader, Cursor, Read, Write}, path::PathBuf};
use anyhow::{anyhow, bail, Context};
use flate2::read::DeflateDecoder;
const EXPECTED_HEADER: [u8; 12] = [129, 70, 79, 83, 83, 73, 76, 73, 90, 69, 68, 66]; // \x81FOSSILIZEDB
fn main() -> anyhow::Result<()> {
let args: Vec<String> = env::args().collect();
let (input_file, output_path) = {
match args.len() {
2 => {
let base = PathBuf::from(&args[1]);
let name = base.to_str().context("Unable to convert path to string")?;
let name = PathBuf::from(format!("{name}.output"));
if name.exists() {
return Err(anyhow!("Output path {:?} already exists. Remove it.", name))
}
if base.exists() {
Ok((
base,
name
))
} else {
Err(anyhow!("Unable to find base file."))
}
},
3 => {
let base = PathBuf::from(&args[1]);
let name = PathBuf::from(&args[2]);
if name.exists() {
return Err(anyhow!("Output path {:?} already exists. Remove it.", name))
}
if base.exists() {
Ok((
base,
name
))
} else {
Err(anyhow!("Unable to find base file."))
}
},
_ => Err(anyhow!("Must provide 1 or 2 parameters. (input file) or (input file, output directory)"))
}
}?;
create_dir(&output_path)?;
let fd = File::open(input_file)?;
let mut reader = BufReader::new(fd);
let mut buffer: [u8; 12] = [0; 12];
reader.read_exact(&mut buffer)?;
// magic_number uint8_t[12] "\x81FOSSILIZEDB"
if buffer.as_slice() != EXPECTED_HEADER.as_slice() {
bail!("File header mismatch.");
}
// unused1, unused2, unused3 - uint8_t
reader.seek_relative(3)?; // Skip 3 unused bytes
let mut buffer: [u8; 1] = [0; 1];
// version uint8_t StreamArchive version: 6
reader.read_exact(&mut buffer)?;
if buffer[0] != 6 {
bail!("File version mismatch. Expected 6, got {}", buffer[0]);
}
// tag and hash, 40 bytes total. Application defined ASCII, used for file names here.
let mut tag_hash_buffer: [u8; 40] = [0; 40];
// uint32_t (LE) - Size of the payload
let mut stored_size_buffer: [u8; 4] = [0; 4];
// uint32_t (LE) - Flags [0x1 no compression | 0x2 compressio (deflate)]
let mut flags_buffer: [u8; 4] = [0; 4];
// uint32_t (LE) - CRC32 of the payload
let mut crc32_buffer: [u8; 4] = [0; 4];
// uint32_t (LE) - Payload size after decompression
let mut payload_size_buffer: [u8; 4] = [0; 4];
loop {
match reader.read_exact(&mut tag_hash_buffer) {
Ok(_) => {}
Err(_) => {
println!("Finished");
break;
}
}
reader.read_exact(&mut stored_size_buffer)?;
reader.read_exact(&mut flags_buffer)?;
reader.read_exact(&mut crc32_buffer)?;
reader.read_exact(&mut payload_size_buffer)?;
let tag_hash = str::from_utf8(&tag_hash_buffer)?;
let stored_size = u32::from_le_bytes(stored_size_buffer);
let flags = u32::from_le_bytes(flags_buffer);
let crc32 = u32::from_le_bytes(crc32_buffer);
let payload_size = u32::from_le_bytes(payload_size_buffer);
let mut data_buf: Vec<u8> = vec![0; stored_size as usize];
reader.read_exact(&mut data_buf)?;
let mutated_data = match flags {
1 => Ok(data_buf),
2 => {
let mut decoder = DeflateDecoder::new(Cursor::new(data_buf));
let mut data_buf = vec![0; payload_size as usize];
decoder.read_to_end(&mut data_buf)?;
Ok(data_buf)
},
_ => Err(anyhow!("Invalid flag: {flags}"))
}?;
let mut output_path = output_path.clone();
output_path.push(tag_hash);
let mut fd = File::create(output_path)?;
fd.write_all(&mutated_data)?;
println!("tag hash {tag_hash} stored size {stored_size} flags {flags} crc32 {crc32} payload size {payload_size}");
}
Ok(())
}