use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Write};
use std::string::String;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
#[derive(Debug, Default)]
struct Entry {
filename: String,
crc: u32,
size: u32,
pos: u32,
data: Vec<u8>,
}
impl core::fmt::Display for Entry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
f,
"{} [{:#010x}] {:#10} bytes at {:#010x}",
self.filename, self.crc, self.size, self.pos
)
}
}
impl Entry {
fn create(basepath: &String, filename: &String) -> Entry {
let fullfilename = format!("{}/{}", basepath, filename);
let size = match fs::metadata(fullfilename) {
Ok(metadata) => metadata.len() as u32,
Err(_) => 0,
};
let downcase_name = filename.to_lowercase();
let clean_name: String = downcase_name
.chars()
.map(|c| match c {
'0'..='9' => c,
'a'..='z' => c,
'!'..='@' => c,
'['..='`' => c,
'{'..='~' => c,
_ => ' ',
})
.collect();
const CRC32: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
let crc = CRC32.checksum(clean_name.as_bytes());
println!(
"CRC: {:?} -> {:?} crc: {:?} {:#10X}\n",
filename, clean_name, crc, crc
);
Entry {
filename: filename.to_string(),
crc: crc,
size: size,
..Default::default()
}
}
fn create_from_archive(crc: u32, pos: u32, size: u32) -> Entry {
Entry {
filename: String::new(),
crc: crc,
size: size,
pos: pos,
..Default::default()
}
}
fn load_from_archive(&mut self, data: &Vec<u8>) -> bool {
let start = self.pos as usize;
let end = start + self.size as usize;
self.data.resize(self.size as usize, 0);
self.data.clone_from_slice(&data[start..end]);
true
}
#[allow(dead_code)]
fn display(&self) {
println!("Displaying Entry for filename {:?}", self.filename);
print!("{:?}\n", self);
}
}
#[derive(Debug)]
struct Archive {
basepath: String,
entries: Vec<Entry>,
}
impl Archive {
fn create(basepath: &String) -> Archive {
Archive {
basepath: basepath.clone(),
entries: Vec::new(),
}
}
fn add_entry(&mut self, filename: &String) -> bool {
let entry = Entry::create(&self.basepath, &filename);
self.entries.push(entry);
true
}
fn add_entry_from_archive(&mut self, crc: u32, pos: u32, size: u32) -> bool {
let entry = Entry::create_from_archive(crc, pos, size);
self.entries.push(entry);
true
}
fn save(&self, output: &String) -> Result<u32, &'static str> {
let output_file = File::create(output);
let mut output_file = match output_file {
Ok(p) => p,
Err(_e) => return Err("Error writing file"),
};
let write_names = false;
let mut flags: u8 = 0;
if write_names {
flags |= 1
}
let number_of_files: u32 = self.entries.len() as u32;
output_file
.write_all(&[
0x4f, 0x4d, 0x41, 0x52, 2, flags, 0, 0, ])
.unwrap();
output_file
.write_u32::<LittleEndian>(number_of_files)
.unwrap();
let mut pos = 0;
for entry in &self.entries {
output_file.write_u32::<LittleEndian>(entry.crc).unwrap();
output_file.write_u32::<LittleEndian>(pos).unwrap();
output_file.write_u32::<LittleEndian>(entry.size).unwrap();
pos += entry.size;
}
for entry in &self.entries {
let filename = format!("{}/{}", self.basepath, entry.filename);
let data_file = File::open(filename);
let mut data_file = match data_file {
Ok(p) => p,
Err(_e) => return Err("Error reading data file"),
};
let mut buffer = Vec::<u8>::new();
data_file.read_to_end(&mut buffer).unwrap();
output_file.write_all(&buffer).unwrap();
}
Ok(number_of_files)
}
fn load(&mut self, filename: &String) -> Result<u32, &'static str> {
let file = File::open(filename);
let file = match file {
Ok(p) => p,
Err(_e) => return Err("Error reading file"),
};
let mut bufreader = BufReader::new(file);
let magic = [0x4fu8, 0x4d, 0x41, 0x52];
for m in &magic {
let b = bufreader.read_u8().unwrap_or(0);
if b != *m {
return Err("Broken magic");
}
}
let v = bufreader.read_u8().unwrap_or(0);
if v != 2 {
return Err("Wrong version");
}
let flags = bufreader.read_u8().unwrap_or(0);
if flags != 0 {
return Err(":TODO: Flags not implemented");
}
for _reserved in 0..2 {
let r = bufreader.read_u8().unwrap_or(0);
if r != 0 {
return Err(":TODO: Reserved field not zero");
}
}
let number_of_files = bufreader.read_u32::<LittleEndian>().unwrap_or(0);
println!("Reading {:?} files from archive", number_of_files);
for _e in 0..number_of_files {
let crc = bufreader.read_u32::<LittleEndian>().unwrap_or(0);
let pos = bufreader.read_u32::<LittleEndian>().unwrap_or(0);
let size = bufreader.read_u32::<LittleEndian>().unwrap_or(0);
self.add_entry_from_archive(crc, pos, size);
}
let mut data = Vec::new();
bufreader.read_to_end(&mut data).unwrap();
for entry in &mut self.entries {
(*entry).load_from_archive(&data);
}
Ok(0)
}
fn unpack(&self, targetpath: &String) -> Result<u32, &'static str> {
for entry in &self.entries {
let filename = format!("{}/{:#10X}", targetpath, entry.crc);
println!("{:?}", filename);
let output_file = File::create(filename);
let mut output_file = match output_file {
Ok(p) => p,
Err(_e) => return Err("Error writing file"),
};
output_file.write_all(&entry.data).unwrap();
}
Ok(0)
}
pub fn entries(&self) -> std::slice::Iter<'_, Entry> {
self.entries.iter()
}
}
impl Iterator for Archive {
type Item = Entry;
fn next(&mut self) -> std::option::Option<<Self as Iterator>::Item> {
todo!()
}
}
struct Helper {}
impl Helper {
fn filenames_in_file(filename: &String) -> Result<Vec<String>, &'static str> {
let file = File::open(filename);
let file = match file {
Ok(p) => p,
Err(_e) => return Err("Error reading file"),
};
let bufreader = BufReader::new(file);
let mut files: Vec<String> = Vec::new();
for line in bufreader.lines() {
let filename = line.unwrap();
files.push(filename);
}
Ok(files)
}
}
pub struct Packer {}
impl Packer {
pub fn pack(basepath: &String, paklist: &String, output: &String) -> Result<u32, &'static str> {
let mut archive = Archive::create(basepath);
for filename in Helper::filenames_in_file(paklist).unwrap_or(Vec::new()) {
println!("{:?}", filename);
archive.add_entry(&filename);
}
archive.save(output)
}
pub fn unpack(input: &String, targetpath: &String) -> Result<u32, &'static str> {
let metadata = match fs::metadata(targetpath) {
Err(_err) => return Err("Targetpath not found"), Ok(md) => md,
};
if !metadata.is_dir() {
return Err("Targetpath is not a directory");
}
let metadata = match fs::metadata(input) {
Err(_err) => return Err("Input not found"),
Ok(md) => md,
};
if !metadata.is_file() {
return Err("Input is not a file");
}
let mut archive = Archive::create(&String::new());
match archive.load(input) {
Err(e) => {
println!("Error in load");
return Err(e);
},
Ok(_) => {},
};
archive.unpack(targetpath).unwrap();
Ok(0)
}
pub fn list(input: &String) -> Result<u32, &'static str> {
let metadata = match fs::metadata(input) {
Err(_err) => return Err("Input not found"),
Ok(md) => md,
};
if !metadata.is_file() {
return Err("Input is not a file");
}
let mut archive = Archive::create(&String::new());
match archive.load(input) {
Err(e) => {
println!("Error in load");
return Err(e);
},
Ok(_) => {},
};
for e in archive.entries() {
println!("{}", e);
}
Ok(archive.entries().len() as u32)
}
}