code
This commit is contained in:
139
src/main.rs
139
src/main.rs
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user