use std::{hash::Hasher, io::Read, path::Path, sync::{Arc, Mutex, Weak}};
use log::info;
use crate::{lol::{io::{self, bytes::Bytes}, utility}, lol_trace_func};
#[derive(Debug, Clone, Copy)]
pub enum EntryType {
Raw = 0,
Link = 1,
Gzip = 2,
Zstd = 3,
ZstdMulti = 4,
}
#[derive(Debug, Clone, Copy)]
pub struct EntryLoc {
pub r#type:EntryType,
pub subchunk_count: u8,
pub subchunk_index: u32,
pub offset: u64,
pub size: u64,
pub size_decompressed: u64,
pub checksum: u64,
}
impl EntryLoc {
pub fn new() ->Self{
Self { r#type: EntryType::Raw, subchunk_count: 0, subchunk_index: 0, offset: 0, size: 0, size_decompressed: 0, checksum: 0 }
}
}
#[derive(Debug, Clone)]
pub struct EntryData {
impl_: Arc<Mutex<EntryDataImpl>>,
}
#[derive(Debug, Clone,)]
pub struct EntryDataImpl {
pub type_: EntryType,
pub is_optimal: bool,
pub subchunk_count: u8,
pub subchunk_index: u16,
pub extension: Option<String>,
pub bytes: Bytes,
pub size_decompressed: usize,
pub checksum: u64,
pub compressed: Weak<Mutex<EntryDataImpl>>,
pub decompressed: Weak<Mutex<EntryDataImpl>>,
}
impl EntryData {
pub fn new() -> Self {
Self {
impl_: Arc::new(Mutex::new(EntryDataImpl {
type_: EntryType::Raw,
is_optimal: false,
subchunk_count: 0,
subchunk_index: 0,
extension: None,
bytes: Bytes::bytes(),
size_decompressed: 0,
checksum: 0,
compressed: Weak::<Mutex<EntryDataImpl>>::new(),
decompressed: Weak::<Mutex<EntryDataImpl>>::new(),
})),
}
}
pub fn checksum(&self) -> u64 {
let mut data = self.impl_.lock().unwrap();
let mut result = data.checksum;
if result == 0 {
let mut hasher = twox_hash::XxHash3_64::with_seed(0);
let bytes_data = if !data.bytes.0.impl_.vec.is_empty() {
&data.bytes.0.impl_.vec[..]
} else {
let ptr = data.bytes.0.data_;
let size = data.bytes.0.size_;
if ptr.is_null() || size == 0 {
data.checksum = 0;
return 0;
}
unsafe {
std::slice::from_raw_parts(ptr, size)
}
};
hasher.write(bytes_data);
result = hasher.finish();
data.checksum = result;
}
result
}
pub fn bytes_data(&self) -> usize {
let mut data = self.impl_.lock().unwrap();
let ptr = data.bytes.0.data() as usize;
ptr
}
pub fn bytes_size(&self) -> usize {
let data = self.impl_.lock().unwrap();
let size = data.bytes.0.size() as usize;
size
}
pub fn from_file(path: &Path) -> EntryData {
let bytes =io::bytes::Bytes::from_file(path);
let data = EntryData::from_raw(&bytes, 0);
data
}
pub fn from_raw(bytes: &Bytes, checksum: u64) -> EntryData{
let result = EntryData::new();
let mut data = result.impl_.lock().unwrap();
data.type_= EntryType::Raw;
data.size_decompressed= bytes.0.size();
data.checksum = checksum;
data.bytes = bytes.clone();
data.decompressed = Arc::downgrade(&result.impl_);
drop(data);
result
}
pub fn from_link(bytes: &Bytes, checksum: u64) -> EntryData{
let result = EntryData::new();
let mut data = result.impl_.lock().unwrap();
data.type_= EntryType::Link;
data.size_decompressed = bytes.0.size();
data.checksum = checksum;
data.bytes = bytes.clone();
data.compressed = Arc::downgrade(&result.impl_);
data.decompressed = Arc::downgrade(&result.impl_);
drop(data);
result
}
pub fn from_gzip(bytes: &Bytes, checksum: u64, decompressed: u64) -> EntryData{
let result = EntryData::new();
let mut data = result.impl_.lock().unwrap();
data.type_= EntryType::Gzip;
data.size_decompressed = decompressed as usize;
data.checksum = checksum;
data.bytes = bytes.clone();
drop(data);
result
}
pub fn from_zstd(bytes: &Bytes, checksum: u64, decompressed: u64) -> EntryData{
let result = EntryData::new();
let mut data = result.impl_.lock().unwrap();
data.type_= EntryType::Zstd;
data.size_decompressed = decompressed as usize;
data.checksum = checksum;
data.bytes = bytes.clone();
data.compressed = Arc::downgrade(&result.impl_);
drop(data);
result
}
pub fn from_zstd_multi(bytes: &Bytes, checksum: u64, decompressed: u64, subchunk_count: u8, subchunk_index: u32) -> EntryData{
let result = EntryData::new();
let mut data = result.impl_.lock().unwrap();
data.type_= EntryType::ZstdMulti;
data.size_decompressed = decompressed as usize;
data.checksum = checksum;
data.subchunk_count = subchunk_count;
data.subchunk_index = subchunk_index as u16;
data.bytes = bytes.clone();
data.compressed = Arc::downgrade(&result.impl_);
drop(data);
result
}
pub fn from_loc(src: &Bytes, loc: EntryLoc) -> EntryData {
let _size = loc.size;
let _offset = loc.offset;
let bytes = src.copy(loc.offset as usize, loc.size as usize);
lol_trace_func!(from_loc,lol_trace_var!(_size), lol_trace_var!(_offset));
let data = match loc.r#type {
EntryType::Raw => {
EntryData::from_raw(&bytes, loc.checksum)
},
EntryType::Link => {
EntryData::from_link(&bytes, loc.checksum)
},
EntryType::Gzip => {
EntryData::from_gzip(&bytes, loc.checksum, loc.size_decompressed)
},
EntryType::Zstd => {
EntryData::from_zstd(&bytes, loc.checksum, loc.size_decompressed)
},
EntryType::ZstdMulti => {
EntryData::from_zstd_multi(&bytes, loc.checksum, loc.size_decompressed, loc.subchunk_count, loc.subchunk_index)
},
};
data
}
pub fn extension(&mut self) -> String {
let mut data =self.impl_.lock().unwrap();
let ptr = data.bytes.0.data_ as usize;
let s = utility::Magic::find(ptr as _);
data.extension = Some(s.to_string());
drop(data);
s.to_string()
}
pub fn into_optimal(&mut self) -> Result<(), std::io::Error> {
let mut data = self.impl_.lock().unwrap();
if !data.is_optimal {
let ext = data.extension.clone().unwrap_or_default();
let data_size = data.bytes.0.size();
if ext == ".bnk" || ext == ".wpk" || ext == ".jpg" || ext == ".jpeg" || ext ==" "||
ext == ".png" || ext == ".webm" || ext == ".mp3" || ext == ".ogg" || ext =="" {
data.type_ = EntryType::Raw;
} else if data_size < 128 {
data.type_ = EntryType::Raw;
} else {
data.type_ = EntryType::Zstd; }
}
data.is_optimal = true;
Ok(())
}
pub fn get_ext(&self) ->Option<String> {
let data =self.impl_.lock().unwrap();
data.extension.clone()
}
pub fn get_type(&self) -> EntryType {
let data =self.impl_.lock().unwrap();
data.type_
}
pub fn into_decompressed(&mut self) -> EntryData {
let mut data = self.impl_.lock().unwrap();
match data.type_ {
EntryType::Raw => {
let count = data.bytes.0.size();
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), data.bytes.0.size())
};
let mut buf = vec![0u8; count];
buf.copy_from_slice(&src[..]);
data.bytes.0.impl_.vec = buf.clone();
},
EntryType::Link => {
let count = data.bytes.0.size();
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), data.bytes.0.size())
};
let mut buf = vec![0u8; count];
buf.copy_from_slice(&src[..]);
data.bytes.0.impl_.vec = buf.clone();
},
EntryType::Gzip => {
let _src_count = data.bytes.0.size();
let count = data.size_decompressed as usize;
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), data.bytes.0.size())
};
let mut de = flate2::read::GzDecoder::new(&src[..]);
let mut buf = vec![0u8; count];
de.read_to_end(&mut buf).unwrap();
data.bytes.0.impl_.vec.copy_from_slice(&buf[0..count]);
},
EntryType::Zstd => {
let src_count = data.bytes.0.size_;
let count = data.size_decompressed as usize;
data.bytes.0.impl_.vec.resize(count, 0);
info!("count {} src_count {} tpye: {:?}", count, src_count, data.type_);
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), data.bytes.0.size())
};
data.bytes.0.write_decompress_zstd(0, count, src, src_count).unwrap();
},
EntryType::ZstdMulti => {
let pos = 0usize;
let src_count = data.bytes.0.size_;
let count = data.size_decompressed;
info!("count {} src_count {} tpye: {:?}", count, src_count, data.type_);
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), data.bytes.0.size())
};
data.bytes.0.write_decompress_zstd_hack(pos, count, src, src_count).unwrap();
},
}
data.decompressed = Arc::downgrade(&self.impl_);
drop(data);
self.clone()
}
pub fn into_compressed(&mut self) -> EntryData {
let mut data = self.impl_.lock().unwrap();
match data.type_ {
EntryType::Raw => {
let count = data.bytes.0.size();
if !data.bytes.0.data_.is_null() && count > 0 {
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), count)
};
let mut buf = vec![0u8; count];
buf.copy_from_slice(&src[..]);
data.bytes.0.impl_.vec = buf.clone();
}
},
EntryType::Link => {
let count = data.bytes.0.size();
if !data.bytes.0.data_.is_null() && count > 0 {
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), count)
};
let mut buf = vec![0u8; count];
buf.copy_from_slice(&src[..]);
data.bytes.0.impl_.vec = buf.clone();
}
},
EntryType::Gzip => {
let count = data.bytes.0.size();
if !data.bytes.0.data_.is_null() && count > 0 {
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), count)
};
let mut buf = vec![0u8; count];
buf.copy_from_slice(&src[..]);
data.bytes.0.impl_.vec = buf.clone();
}
},
EntryType::Zstd | EntryType::ZstdMulti => {
let src_data = if !data.bytes.0.data_.is_null() && data.bytes.0.size() > 0 {
let src = unsafe {
std::slice::from_raw_parts(data.bytes.0.data(), data.bytes.0.size())
};
src.to_vec()
} else {
info!("No valid data for compression, using Raw type");
data.type_ = EntryType::Raw;
return self.clone();
};
let compressed = data.bytes.0.copy_compress_zstd(&src_data);
let compressed_size = compressed.0.size();
let original_size = src_data.len();
if compressed_size >= original_size {
info!("Compression not effective, using Raw type. Original: {}, Compressed: {}",
original_size, compressed_size);
data.type_ = EntryType::Raw;
data.bytes.0.impl_.vec = src_data;
} else {
info!("Compression successful. Original: {}, Compressed: {}",
original_size, compressed_size);
data.bytes = compressed;
}
},
}
data.compressed = Arc::downgrade(&self.impl_);
drop(data);
self.clone()
}
pub fn decompressed_bytes(&mut self) -> (Vec<u8>, usize, EntryType) {
let binding = self.into_decompressed();
let binding = binding.impl_.lock().unwrap().decompressed.upgrade().unwrap();
let data = binding.lock().unwrap();
(data.bytes.0.impl_.vec.clone(), data.bytes.0.size_, data.type_)
}
pub fn compressed_bytes(&mut self) -> (Vec<u8>, EntryType, u8, u16, usize, bool) {
let binding = self.into_compressed();
let binding = binding.impl_.lock().unwrap().decompressed.upgrade().unwrap();
let data = binding.lock().unwrap();
(data.bytes.0.impl_.vec.clone(), data.type_, data.subchunk_count, data.subchunk_index, data.size_decompressed, data.is_optimal)
}
}