This commit is contained in:
Rph :3
2025-08-20 19:49:59 +02:00
parent a885648065
commit da5f9b62c2
3 changed files with 196 additions and 2 deletions

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(())
}