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"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.99"
|
||||||
|
flate2 = "1.1.2"
|
139
src/main.rs
139
src/main.rs
@ -1,3 +1,138 @@
|
|||||||
fn main() {
|
use std::{env, fs::{create_dir, File}, io::{BufReader, Cursor, Read, Write}, path::PathBuf};
|
||||||
println!("Hello, world!");
|
|
||||||
|
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