lotus_utils_texture/
utils.rs1use std::fs::File;
2use std::io::{Seek, SeekFrom};
3
4use anyhow::{Error, Result};
5use bytebuffer::ByteBuffer;
6use log::debug;
7use lotus_lib::cache_pair::{CachePair, CachePairReader};
8use lotus_lib::compression::{decompress_post_ensmallening, get_block_lengths};
9use lotus_lib::package::{Package, PackageType};
10use lotus_lib::toc::{FileNode, Node};
11
12use crate::header::TextureHeader;
13use crate::raw_header::RawTextureHeader;
14use crate::kind::TextureKind;
15
16pub trait Texture {
17 fn is_texture(&self, node: &Node) -> Result<bool>;
23
24 fn decompress_texture(&self, node: &Node) -> Result<(Vec<u8>, String)>;
30}
31
32impl Texture for Package<CachePairReader> {
33 fn is_texture(&self, node: &Node) -> Result<bool> {
34 if !node.name().ends_with(".png") {
35 return Ok(false);
36 }
37
38 let h_cache = self
39 .borrow(PackageType::H)
40 .ok_or(Error::msg("No header file found"))?;
41
42 let header_file_data = h_cache.decompress_data(node.clone())?;
43 let header = match RawTextureHeader::try_from(header_file_data.as_slice()) {
44 Ok(header) => header,
45 Err(_) => return Ok(false),
46 };
47
48 match TextureKind::try_from(header.file_type) {
49 Ok(_) => Ok(true),
50 Err(_) => Ok(false),
51 }
52 }
53
54 fn decompress_texture(&self, node: &Node) -> Result<(Vec<u8>, String)> {
55 let h_cache = self.borrow(PackageType::H);
56 let f_cache = self.borrow(PackageType::F);
57 let b_cache = self.borrow(PackageType::B);
58
59 let h_cache = match h_cache {
61 Some(h_cache) => h_cache,
62 None => return Err(Error::msg("No header file found")),
63 };
64
65 let header_file_data = h_cache.decompress_data(node.clone())?;
67
68 let header = TextureHeader::try_from(header_file_data.as_slice())?;
70 debug!("Header: {:?}", header);
71
72 let mut buffer = ByteBuffer::new();
73 buffer.write_bytes(b"DDS ");
74 header.header.write(&mut buffer)?;
75 if let Some(header10) = &header.header10 {
76 header10.write(&mut buffer)?;
77 }
78
79 if header.f_cache_image_count > 0 {
80 let f_cache = match f_cache {
81 Some(f_cache) => f_cache,
82 None => return Err(Error::msg("No F cache found")),
83 };
84 let file_node = f_cache.get_file_node(node.path()).unwrap();
85
86 debug!("Cache offset: {}", file_node.cache_offset() as u64);
87 debug!("Cache image size: {}", file_node.comp_len() as u64);
88 debug!("Real image size: {}", header.size() as u64);
89 debug!("Decompressed image size: {}", file_node.len() as u64);
90
91 let f_cache_offsets = &header.f_cache_image_offsets;
92
93 if f_cache_offsets.len() != 0 {
94 let cache_image_sub_offset = f_cache_offsets.last().unwrap_or(&0).to_owned();
95
96 let mut f_cache_reader = File::open(f_cache.cache_path()).unwrap();
97 let real_cache_image_sub_offset = get_real_cache_image_offset(
98 &mut f_cache_reader,
99 file_node.cache_offset() as usize,
100 cache_image_sub_offset as usize,
101 )?;
102
103 debug!("Cache image offset: {}", cache_image_sub_offset);
104 debug!("Real cache image offset: {}", real_cache_image_sub_offset);
105
106 f_cache_reader.seek(SeekFrom::Current(real_cache_image_sub_offset as i64))?;
107
108 let file_data = decompress_post_ensmallening(
109 file_node.comp_len() as usize,
110 header.size() as usize,
111 &mut f_cache_reader,
112 )?;
113 buffer.write_bytes(&file_data);
114 } else {
115 let file_data = f_cache.decompress_data(file_node)?;
117 buffer.write_bytes(&file_data[file_data.len() - header.size()..]);
118 }
119 } else {
120 let b_cache = match b_cache {
121 Some(b_cache) => b_cache,
122 None => return Err(Error::msg("No B cache found")),
123 };
124 let file_node = b_cache.get_file_node(node.path()).unwrap();
125
126 debug!("Cache offset: {}", file_node.cache_offset() as u64);
127 debug!("Cache image size: {}", file_node.comp_len() as u64);
128 debug!("Real image size: {}", header.size() as u64);
129 debug!("Decompressed image size: {}", file_node.len() as u64);
130
131 let file_data = b_cache.decompress_data(file_node)?;
132 buffer.write_bytes(&file_data[file_data.len() - header.size()..]);
133 }
134
135 Ok((buffer.into_vec(), get_texture_file_name(node)))
136 }
137}
138
139fn get_real_cache_image_offset(
140 cache_reader: &mut File,
141 cache_image_offset: usize,
142 cache_image_sub_offset: usize,
143) -> Result<usize> {
144 cache_reader.seek(SeekFrom::Start(cache_image_offset as u64))?;
145
146 const BLOCK_HEADER_LEN: usize = 8;
147
148 let mut cache_offset_top: usize = 0;
149 let mut cache_offset_bottom: usize = 0;
150
151 loop {
152 let (block_compressed_len, _) = get_block_lengths(cache_reader)?.unwrap_or((0, 0));
153 cache_offset_top += block_compressed_len as usize + BLOCK_HEADER_LEN;
154
155 if cache_offset_top >= cache_image_sub_offset {
156 break;
157 }
158
159 cache_offset_bottom = cache_offset_top;
160 cache_reader.seek(SeekFrom::Current(block_compressed_len as i64))?;
161 }
162
163 cache_reader.seek(SeekFrom::Start(cache_image_offset as u64))?;
165
166 let diff_top = cache_offset_top - cache_image_sub_offset;
167 let diff_bottom = cache_image_sub_offset - cache_offset_bottom;
168
169 if diff_top > diff_bottom {
170 return Ok(cache_offset_bottom);
171 } else {
172 return Ok(cache_offset_top);
173 }
174}
175
176fn get_texture_file_name(node: &Node) -> String {
177 let mut file_name = node.name();
178 if file_name.ends_with(".png") {
179 file_name.truncate(file_name.len() - 4);
180 }
181 file_name.push_str(".dds");
182 file_name
183}