use std::io::{Read, Seek, SeekFrom, Write};
use binrw::{BinRead, BinWrite, Endian, binrw};
use data::{BlockHeader, CompressionMode};
use crate::common::Platform;
use crate::compression::no_header_decompress;
mod data;
pub use data::SqPackData;
mod db;
pub use db::SqPackDatabase;
mod index;
pub use index::{Hash, IndexEntry, IndexType, SqPackIndex};
#[binrw]
#[brw(repr = u32)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum SqPackFileType {
Sqdb = 0x0,
Data = 0x1,
Index = 0x2,
}
#[binrw]
#[brw(magic = b"SqPack\0\0")]
#[derive(Debug, Clone)]
pub(crate) struct SqPackHeader {
#[brw(pad_size_to = 4)]
platform: Platform,
pub size: u32,
version: u32,
file_type: SqPackFileType,
unk1: u32,
unk2: u32,
region: u8,
#[brw(pad_before = 924)]
#[brw(pad_after = 44)]
sha1_hash: [u8; 20],
}
pub(crate) fn read_data_block<T: Read + Seek>(
buf: &mut T,
endianness: Endian,
starting_position: u64,
) -> Option<Vec<u8>> {
buf.seek(SeekFrom::Start(starting_position)).ok()?;
let block_header = BlockHeader::read_options(buf, endianness, ()).unwrap();
match block_header.compression {
CompressionMode::Compressed {
compressed_length,
decompressed_length,
} => {
let mut compressed_data: Vec<u8> = vec![0; compressed_length as usize];
buf.read_exact(&mut compressed_data).ok()?;
let mut decompressed_data: Vec<u8> = vec![0; decompressed_length as usize];
if !no_header_decompress(&mut compressed_data, &mut decompressed_data) {
return None;
}
Some(decompressed_data)
}
CompressionMode::Uncompressed { file_size } => {
let mut local_data: Vec<u8> = vec![0; file_size as usize];
buf.read_exact(&mut local_data).ok()?;
Some(local_data)
}
}
}
pub(crate) fn read_data_block_patch<T: Read + Seek>(
buf: &mut T,
endianness: Endian,
) -> Option<Vec<u8>> {
let block_header = BlockHeader::read_options(buf, endianness, ()).unwrap();
match block_header.compression {
CompressionMode::Compressed {
compressed_length,
decompressed_length,
} => {
let compressed_length: usize =
((compressed_length as usize + 143) & 0xFFFFFF80) - (block_header.size as usize);
let mut compressed_data: Vec<u8> = vec![0; compressed_length];
buf.read_exact(&mut compressed_data).ok()?;
let mut decompressed_data: Vec<u8> = vec![0; decompressed_length as usize];
if !no_header_decompress(&mut compressed_data, &mut decompressed_data) {
return None;
}
Some(decompressed_data)
}
CompressionMode::Uncompressed { file_size } => {
let new_file_size: usize = (file_size as usize + 143) & 0xFFFFFF80;
let mut local_data: Vec<u8> = vec![0; file_size as usize];
buf.read_exact(&mut local_data).ok()?;
buf.seek(SeekFrom::Current(
(new_file_size - block_header.size as usize - file_size as usize) as i64,
))
.ok()?;
Some(local_data)
}
}
}
pub(crate) fn write_data_block_patch<T: Write + Seek>(
writer: &mut T,
endianness: Endian,
data: Vec<u8>,
) {
let new_file_size: usize = (data.len() + 143) & 0xFFFFFF80;
let block_header = BlockHeader {
size: (new_file_size - data.len()) as u32, compression: CompressionMode::Uncompressed {
file_size: data.len() as i32,
},
};
block_header.write_options(writer, endianness, ()).unwrap();
data.write(writer).unwrap();
}