code
This commit is contained in:
parent
a885648065
commit
da5f9b62c2
57
Cargo.lock
generated
Normal file
57
Cargo.lock
generated
Normal 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",
|
||||
]
|
@ -4,3 +4,5 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.99"
|
||||
flate2 = "1.1.2"
|
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(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user