use crate::{Ra2Error, CncGame, checksum::ra2_crc, constants::*, crypto::{decrypt_blowfish_key, decrypt_mix_header, get_decryption_block_sizing}, MixDatabase};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::{
collections::HashMap,
fs::File,
io::{Seek, SeekFrom, Write},
path::Path,
};
pub mod reader;
pub mod writer;
#[derive(Default, Debug)]
pub struct MixPackage {
pub game: CncGame,
pub files: HashMap<String, Vec<u8>>,
}
#[derive(Copy, Debug, Clone)]
struct MixHeader {
pub flags: Option<u32>,
pub file_count: u16,
pub data_size: u32,
}
#[derive(Debug, Clone, Copy)]
struct FileEntry {
pub id: u32,
pub offset: i32,
pub size: i32,
}
#[derive(Debug, Clone)]
struct FileInfo {
file_id: u32,
data: Vec<u8>,
}
impl MixPackage {
pub fn add_any(&mut self, name: String, data: Vec<u8>) {
self.files.insert(name, data);
}
pub fn add_file(&mut self, data: &Path) -> Result<usize, Ra2Error> {
if !data.is_file() {
return Err(Ra2Error::FileNotFound("must file".to_string()));
}
let name = data.file_name().and_then(|s| s.to_str()).ok_or(Ra2Error::FileNotFound("".to_string()))?;
let data = std::fs::read(data)?;
let size = data.len();
self.files.insert(name.to_string(), data);
Ok(size)
}
}
pub fn extract(input: &Path, output: &Path) -> Result<(), Ra2Error> {
let xcc = MixPackage::load(input, &MixDatabase::default())?;
let file_map = xcc.files;
std::fs::create_dir_all(output)?;
for (filename, file_data) in file_map {
let file_path = output.join(filename);
let mut file = File::create(file_path)?;
file.write_all(&file_data)?;
}
Ok(())
}
pub fn patch(input: &Path, output: &Path) -> Result<(), Ra2Error> {
let mut xcc = MixPackage::load(input, &MixDatabase::default())?;
for entry in std::fs::read_dir(input)? {
let entry = entry?;
xcc.add_file(&entry.path())?;
}
xcc.save(output)?;
Ok(())
}