//! Buriko General Interpreter/Ethornell Compressed Image File use crate::ext::atomic::*; use crate::ext::io::*; use crate::scripts::base::*; use crate::types::*; use crate::utils::bit_stream::*; use crate::utils::img::*; use crate::utils::struct_pack::*; use crate::utils::threadpool::*; use anyhow::Result; use msg_tool_macro::*; use std::io::{Read, Seek, Write}; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; #[derive(Debug)] /// Builder for BGI Compressed Image scripts. pub struct BgiCBGBuilder {} impl BgiCBGBuilder { /// Creates a new instance of `BgiCBGBuilder`. pub const fn new() -> Self { BgiCBGBuilder {} } } impl ScriptBuilder for BgiCBGBuilder { fn default_encoding(&self) -> Encoding { Encoding::Cp932 } fn build_script( &self, data: Vec, _filename: &str, _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiCBG::new(data, config)?)) } fn extensions(&self) -> &'static [&'static str] { &[] } fn script_type(&self) -> &'static ScriptType { &ScriptType::BGICbg } fn is_image(&self) -> bool { true } fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option { if buf_len >= 0x10 && buf.starts_with(b"CompressedBG___") { return Some(255); } None } fn can_create_image_file(&self) -> bool { true } fn create_image_file<'a>( &'a self, data: ImageData, mut writer: Box, _options: &ExtraConfig, ) -> Result<()> { let encoder = CbgEncoder::new(data)?; let data = encoder.encode()?; writer.write_all(&data)?; Ok(()) } } #[derive(Debug, StructPack, StructUnpack)] struct BgiCBGHeader { width: u16, height: u16, bpp: u32, _unk: u64, intermediate_length: u32, key: u32, enc_length: u32, check_sum: u8, check_xor: u8, version: u16, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum CbgColorType { Bgra32, Bgr24, Grayscale, Bgr565, } fn convert_bgr565_to_bgr24(input: Vec, width: u16, height: u16) -> ImageData { let pixel_count = width as usize * height as usize; let mut output = Vec::with_capacity(pixel_count * 3); for chunk in input.chunks_exact(2) { let pixel = u16::from_le_bytes([chunk[0], chunk[1]]); let blue_5bit = (pixel & 0x1) as u8; let green_6bit = ((pixel >> 5) & 0x3) as u8; let red_5bit = ((pixel >> 11) & 0x1) as u8; let blue = ((blue_5bit as u16 * 255) / 31) as u8; let green = ((green_6bit as u16 * 255) / 63) as u8; let red = ((red_5bit as u16 * 255) / 31) as u8; output.push(blue); output.push(green); output.push(red); } ImageData { width: width as u32, height: height as u32, color_type: ImageColorType::Bgr, depth: 8, data: output, } } #[derive(Debug)] /// BGI Compressed Image script. pub struct BgiCBG { header: BgiCBGHeader, data: MemReader, color_type: CbgColorType, decode_workers: usize, } impl BgiCBG { /// Creates a new instance of `BgiCBG` from a buffer. /// /// * `data` - The buffer containing the script data. /// * `config` - Extra configuration options. pub fn new(data: Vec, config: &ExtraConfig) -> Result { let mut reader = MemReader::new(data); let mut magic = [0u8; 16]; reader.read_exact(&mut magic)?; if !magic.starts_with(b"CompressedBG___") { return Err(anyhow::anyhow!("Invalid magic: {:?}", magic)); } let header = BgiCBGHeader::unpack(&mut reader, false, Encoding::Cp932)?; if header.version > 2 { return Err(anyhow::anyhow!("Unsupported version: {}", header.version)); } let color_type = match header.bpp { 32 => CbgColorType::Bgra32, 24 => CbgColorType::Bgr24, 8 => CbgColorType::Grayscale, 16 => { if header.version == 2 { return Err(anyhow::anyhow!("Unsupported BPP 16 in version 2")); } CbgColorType::Bgr565 } _ => return Err(anyhow::anyhow!("Unsupported BPP: {}", header.bpp)), }; Ok(BgiCBG { header, data: reader, color_type, decode_workers: config.bgi_img_workers.max(1), }) } } impl Script for BgiCBG { fn default_output_script_type(&self) -> OutputScriptType { OutputScriptType::Json } fn default_format_type(&self) -> FormatOptions { FormatOptions::None } fn is_image(&self) -> bool { true } fn export_image(&self) -> Result { let decoder = CbgDecoder::new( self.data.to_ref(), &self.header, self.color_type, self.decode_workers, )?; Ok(decoder.unpack()?) } fn import_image<'a>( &'a self, data: ImageData, mut file: Box, ) -> Result<()> { let encoder = CbgEncoder::new(data)?; let encoded_data = encoder.encode()?; file.write_all(&encoded_data)?; Ok(()) } } struct CbgDecoder<'a> { stream: MsbBitStream>, info: &'a BgiCBGHeader, color_type: CbgColorType, key: u32, magic: u32, pixel_size: u8, stride: usize, workers: usize, } impl<'a> CbgDecoder<'a> { fn new( reader: MemReaderRef<'a>, info: &'a BgiCBGHeader, color_type: CbgColorType, workers: usize, ) -> Result { let magic = 0; let key = info.key; let stream = MsbBitStream::new(reader); let pixel_size = info.bpp as u8 / 8; let stride = info.width as usize * (info.bpp as usize / 8); Ok(CbgDecoder { stream, info, key, magic, color_type, pixel_size, stride, workers, }) } fn unpack(mut self) -> Result { self.stream.m_input.pos = 0x30; if self.info.version < 2 { return self.unpack_v1(); } else if self.info.version == 2 { if self.info.enc_length < 0x80 { return Err(anyhow::anyhow!( "Invalid encoded length: {}", self.info.enc_length )); } return self.unpack_v2(); } Err(anyhow::anyhow!("Unknown version: {}", self.info.version)) } fn unpack_v1(&mut self) -> Result { let leaf_nodes_weight = { let stream = MemReader::new(self.read_encoded()?); let mut stream_ref = stream.to_ref(); Self::read_weight_table(&mut stream_ref, 0x100)? }; let tree = HuffmanTree::new(&leaf_nodes_weight, false); let mut packed = Vec::with_capacity(self.info.intermediate_length as usize); packed.resize(self.info.intermediate_length as usize, 0); self.huffman_decompress(&tree, &mut packed)?; let buf_size = self.stride * self.info.height as usize; let mut output = Vec::with_capacity(buf_size); output.resize(buf_size, 0); Self::unpack_zeros(&packed, &mut output); self.reverse_average_sampling(&mut output); let color_type = match self.color_type { CbgColorType::Bgra32 => ImageColorType::Bgra, CbgColorType::Bgr24 => ImageColorType::Bgr, CbgColorType::Grayscale => ImageColorType::Grayscale, CbgColorType::Bgr565 => { return Ok(convert_bgr565_to_bgr24( output, self.info.width, self.info.height, )); } }; Ok(ImageData { width: self.info.width as u32, height: self.info.height as u32, color_type, depth: 8, data: output, }) } fn unpack_v2(&mut self) -> Result { let dct_data = self.read_encoded()?; let mut dct = [[0.0f32; 64]; 2]; for i in 0..0x80 { dct[i >> 6][i & 0x3f] = dct_data[i] as f32 * DCT_TABLE[i & 0x3f]; } let base_offset = self.stream.m_input.pos; let tree1 = HuffmanTree::new( &Self::read_weight_table(&mut self.stream.m_input, 0x10)?, true, ); let tree2 = HuffmanTree::new( &Self::read_weight_table(&mut self.stream.m_input, 0xB0)?, true, ); let width = ((self.info.width as i32 + 7) & -8) as i32; let height = ((self.info.height as i32 + 7) & -8) as i32; let y_blocks = height / 8; let mut offsets = Vec::with_capacity((y_blocks + 1) as usize); let input_base = (self.stream.m_input.pos + ((y_blocks + 1) as usize * 4) - base_offset) as i32; for _ in 0..=y_blocks { let offset = self.stream.m_input.read_i32()?; offsets.push(offset - input_base); } let input = self.stream.m_input.data[self.stream.m_input.pos..].to_vec(); let pad_skip = ((width >> 3) + 7) >> 3; let output_size = (width * height * 4) as usize; let output = vec![0u8; output_size]; let output_mutex = Mutex::new(output); let decoder = Arc::new(ParallelCbgDecoder { input, output: output_mutex, bpp: self.info.bpp as i32, width, height, tree1, tree2, dct, has_alpha: AtomicBool::new(false), }); let thread_pool = ThreadPool::new(self.workers, Some("cbg-decoder-worker-"), false)?; let mut dst = 0i32; for i in 0..y_blocks { let block_offset = offsets[i as usize] + pad_skip; let next_offset = if i + 1 == y_blocks { decoder.input.len() as i32 } else { offsets[(i + 1) as usize] }; let closure_dst = dst; let decoder_ref = Arc::clone(&decoder); thread_pool.execute( move |_| { decoder_ref.unpack_block(block_offset, next_offset - block_offset, closure_dst) }, true, )?; dst += width * 32; } if self.info.bpp == 32 { let decoder_ref = Arc::clone(&decoder); thread_pool.execute( move |_| decoder_ref.unpack_alpha(offsets[y_blocks as usize]), true, )?; } let tasks = thread_pool.into_results(); for task in tasks { task?; } let has_alpha = decoder.has_alpha.qload(); let mut output = decoder .output .lock() .map_err(|e| anyhow::anyhow!("Failed to lock output: {}", e))? .clone(); if !has_alpha { let mut src_idx = 0; let mut dst_idx = 0; for _ in 0..self.info.height { for _ in 0..self.info.width { output[dst_idx] = output[src_idx]; output[dst_idx + 1] = output[src_idx + 1]; output[dst_idx + 2] = output[src_idx + 2]; src_idx += 4; dst_idx += 3; } } output.truncate(dst_idx); } let color_type = if has_alpha { ImageColorType::Bgra } else { ImageColorType::Bgr }; let img = ImageData { width: decoder.width as u32, height: decoder.height as u32, color_type, depth: 8, data: output, }; if decoder.width != self.info.width as i32 || decoder.height != self.info.height as i32 { return Ok(draw_on_canvas( img, self.info.width as u32, self.info.height as u32, 0, 0, )?); } Ok(img) } fn read_encoded(&mut self) -> Result> { let mut output = Vec::with_capacity(self.info.enc_length as usize); output.resize(self.info.enc_length as usize, 0); self.stream.m_input.read_exact(&mut output)?; let mut sum = 0u8; let mut xor = 0u8; for i in 0..output.len() { output[i] = output[i].wrapping_sub(self.update_key()); sum = sum.wrapping_add(output[i]); xor ^= output[i]; } if sum != self.info.check_sum || xor != self.info.check_xor { return Err(anyhow::anyhow!( "Checksum mismatch: sum={}, xor={}", sum, xor )); } Ok(output) } fn read_int(input: &mut MemReaderRef<'_>) -> Result { let mut v = 0; let mut code_length = 0; loop { let code = input.read_i8()?; if code_length >= 32 { return Err(anyhow::anyhow!( "Failed to raed int: code={}, code_length={}", code, code_length )); } v |= ((code & 0x7f) as i32) << code_length; code_length += 7; if code & -128 == 0 { break; } } Ok(v) } fn read_weight_table(input: &mut MemReaderRef<'_>, length: usize) -> Result> { let mut weights = Vec::with_capacity(length); for _ in 0..length { let weight = Self::read_int(input)? as u32; weights.push(weight); } Ok(weights) } fn huffman_decompress(&mut self, tree: &HuffmanTree, output: &mut [u8]) -> Result<()> { for dst in 0..output.len() { output[dst] = tree.decode_token(&mut self.stream)? as u8; } Ok(()) } fn unpack_zeros(input: &[u8], output: &mut [u8]) { let mut dst = 0; let mut dec_zero = 0; let mut src = 0; while dst < output.len() { let mut code_length = 0; let mut count = 0; let mut code; loop { if src >= input.len() { return; } code = input[src]; src += 1; count |= ((code & 0x7f) as usize) << code_length; code_length += 7; if code & 0x80 == 0 { break; } } if dst + count > output.len() { break; } if dec_zero == 0 { if src + count > input.len() { break; } output[dst..dst + count].copy_from_slice(&input[src..src + count]); src += count; } else { for i in 0..count { output[dst + i] = 0; } } dec_zero ^= 1; dst += count; } } fn reverse_average_sampling(&self, output: &mut [u8]) { for y in 0..self.info.height { let line = y as usize * self.stride; for x in 0..self.info.width { let pixel = line + x as usize * self.pixel_size as usize; for p in 0..self.pixel_size { let mut avg = 0u32; if x > 0 { avg = avg.wrapping_add( output[pixel + p as usize - self.pixel_size as usize] as u32, ); } if y > 0 { avg = avg.wrapping_add(output[pixel + p as usize - self.stride] as u32); } if x > 0 && y > 0 { avg /= 2; } if avg != 0 { output[pixel + p as usize] = output[pixel + p as usize].wrapping_add(avg as u8); } } } } } fn update_key(&mut self) -> u8 { let v0 = 20021 * (self.key & 0xffff); let mut v1 = self.magic | (self.key >> 16); v1 = v1 .overflowing_mul(20021) .0 .overflowing_add(self.key.overflowing_mul(346).0) .0; v1 = (v1 + (v0 >> 16)) & 0xffff; self.key = (v1 << 16) + (v0 & 0xffff) + 1; v1 as u8 } } #[derive(Debug)] struct HuffmanNode { valid: bool, is_parent: bool, weight: u32, left_index: usize, right_index: usize, } #[derive(Debug)] struct HuffmanTree { nodes: Vec, } impl HuffmanTree { fn new(weights: &[u32], v2: bool) -> Self { let mut nodes = Vec::with_capacity(weights.len() * 2); let mut root_node_weight = 0u32; for weight in weights { let node = HuffmanNode { valid: *weight != 0, is_parent: false, weight: *weight, left_index: 0, right_index: 0, }; nodes.push(node); root_node_weight = root_node_weight.wrapping_add(*weight); } let mut child_node_index = [0usize; 2]; loop { let mut weight = 0u32; for i in 0usize..2usize { let mut min_weight = u32::MAX; child_node_index[i] = usize::MAX; let mut n = 0; if v2 { while n < nodes.len() { if nodes[n].valid { min_weight = nodes[n].weight; child_node_index[i] = n; n += 1; break; } n += 1; } n = n.max(i + 1); } while n < nodes.len() { if nodes[n].valid && nodes[n].weight < min_weight { min_weight = nodes[n].weight; child_node_index[i] = n; } n += 1; } if child_node_index[i] == usize::MAX { continue; } nodes[child_node_index[i]].valid = false; weight = weight.wrapping_add(nodes[child_node_index[i]].weight); } let parent_node = HuffmanNode { valid: true, is_parent: true, left_index: child_node_index[0], right_index: child_node_index[1], weight, }; nodes.push(parent_node); if weight >= root_node_weight { break; } } Self { nodes } } fn decode_token(&self, stream: &mut MsbBitStream>) -> Result { let mut node_index = self.nodes.len() - 1; loop { let bit = stream.get_next_bit()?; if !bit { node_index = self.nodes[node_index].left_index; } else { node_index = self.nodes[node_index].right_index; } if !self.nodes[node_index].is_parent { return Ok(node_index); } } } fn encode_token(&self, stream: &mut MsbBitWriter, token: usize) -> Result<()> { let mut path = Vec::new(); if !self.find_path(self.nodes.len() - 1, token, &mut path) { return Err(anyhow::anyhow!("Token not found in Huffman tree")); } for &bit in path.iter().rev() { stream.put_bit(bit)?; } Ok(()) } fn find_path(&self, node_index: usize, token: usize, path: &mut Vec) -> bool { if node_index == usize::MAX { return false; } let node = &self.nodes[node_index]; if !node.is_parent { return node_index == token; } if self.find_path(node.left_index, token, path) { path.push(false); return true; } if self.find_path(node.right_index, token, path) { path.push(true); return true; } false } } const DCT_TABLE: [f32; 64] = [ 1.00000000, 1.38703990, 1.30656302, 1.17587554, 1.00000000, 0.78569496, 0.54119611, 0.27589938, 1.38703990, 1.92387950, 1.81225491, 1.63098633, 1.38703990, 1.08979023, 0.75066054, 0.38268343, 1.30656302, 1.81225491, 1.70710683, 1.53635550, 1.30656302, 1.02655995, 0.70710677, 0.36047992, 1.17587554, 1.63098633, 1.53635550, 1.38268340, 1.17587554, 0.92387950, 0.63637930, 0.32442334, 1.00000000, 1.38703990, 1.30656302, 1.17587554, 1.00000000, 0.78569496, 0.54119611, 0.27589938, 0.78569496, 1.08979023, 1.02655995, 0.92387950, 0.78569496, 0.61731654, 0.42521504, 0.21677275, 0.54119611, 0.75066054, 0.70710677, 0.63637930, 0.54119611, 0.42521504, 0.29289323, 0.14931567, 0.27589938, 0.38268343, 0.36047992, 0.32442334, 0.27589938, 0.21677275, 0.14931567, 0.07612047, ]; const BLOCK_FILL_ORDER: [u8; 64] = [ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, ]; struct ParallelCbgDecoder { input: Vec, output: Mutex>, bpp: i32, width: i32, height: i32, tree1: HuffmanTree, tree2: HuffmanTree, dct: [[f32; 64]; 2], has_alpha: AtomicBool, } impl ParallelCbgDecoder { fn unpack_block(&self, offset: i32, length: i32, dst: i32) -> Result<()> { let input = MemReaderRef::new(&self.input[offset as usize..(offset + length) as usize]); let mut reader = MsbBitStream::new(input); let block_size = CbgDecoder::read_int(&mut reader.m_input)?; if block_size == -1 { return Ok(()); } let mut color_data = vec![0i16; block_size as usize]; let mut acc = 0i32; let mut i = 0i32; while i < block_size && reader.m_input.pos < reader.m_input.data.len() { let count = self.tree1.decode_token(&mut reader)?; if count != 0 { let mut v = reader.get_bits(count as u32)? as i32; if (v >> (count - 1)) == 0 { v = (-1 << count | v) + 1; } acc += v; } color_data[i as usize] = acc as i16; i += 64; } if (reader.m_cached_bits & 7) != 0 { reader.get_bits(reader.m_cached_bits & 7)?; } i = 0; while i < block_size && reader.m_input.pos < reader.m_input.data.len() { let mut index = 1usize; while index < 64 && reader.m_input.pos < reader.m_input.data.len() { let code = self.tree2.decode_token(&mut reader)?; if code == 0 { break; } if code == 0xf { index += 0x10; continue; } index += code & 0xf; if index >= BLOCK_FILL_ORDER.len() { break; } let bits = code >> 4; let mut v = reader.get_bits(bits as u32)? as i32; if bits != 0 && (v >> (bits - 1)) == 0 { v = (-1 << bits | v) + 1; } color_data[i as usize + BLOCK_FILL_ORDER[index] as usize] = v as i16; index += 1; } i += 64; } if self.bpp == 8 { self.decode_grayscale(&color_data, dst)?; } else { self.decode_rgb(&color_data, dst)?; } Ok(()) } fn decode_rgb(&self, data: &[i16], dst: i32) -> Result<()> { let block_count = self.width / 8; let mut dst = dst as usize; for i in 0..block_count { let mut src = (i * 64) as usize; let mut ycbcr_block = [[0i16; 3]; 64]; for channel in 0..3 { self.decode_dct(channel, data, src, &mut ycbcr_block)?; src += (self.width * 8) as usize; } let mut output = self .output .lock() .map_err(|e| anyhow::anyhow!("Failed to lock output: {}", e))?; for j in 0..64 { let cy = ycbcr_block[j][0] as f32; let cb = ycbcr_block[j][1] as f32; let cr = ycbcr_block[j][2] as f32; // Full-range YCbCr->RGB conversion let r = cy + 1.402f32 * cr - 178.956f32; let g = cy - 0.34414f32 * cb - 0.71414f32 * cr + 135.95984f32; let b = cy + 1.772f32 * cb - 226.316f32; let y = j >> 3; let x = j & 7; let p = (y * self.width as usize + x) * 4 + dst; output[p] = Self::float_to_byte(b); output[p + 1] = Self::float_to_byte(g); output[p + 2] = Self::float_to_byte(r); } dst += 32; } Ok(()) } fn decode_grayscale(&self, data: &[i16], dst: i32) -> Result<()> { let mut src = 0; let block_count = self.width / 8; let mut dst = dst as usize; for _ in 0..block_count { let mut ycbcr_block = [[0i16; 3]; 64]; self.decode_dct(0, data, src, &mut ycbcr_block)?; src += 64; let mut output = self .output .lock() .map_err(|e| anyhow::anyhow!("Failed to lock output: {}", e))?; for j in 0..64 { let y = j >> 3; let x = j & 7; let p = (y * self.width as usize + x) * 4 + dst; let value = ycbcr_block[j][0] as u8; output[p] = value; output[p + 1] = value; output[p + 2] = value; } dst += 32; } Ok(()) } fn unpack_alpha(&self, offset: i32) -> Result<()> { let mut input = MemReaderRef::new(&self.input[offset as usize..]); if input.read_i32()? != 1 { return Ok(()); } let mut dst = 3; let mut ctl = 1i32 << 1; let mut output = self .output .lock() .map_err(|e| anyhow::anyhow!("Failed to lock output: {}", e))?; while dst < output.len() { ctl >>= 1; if ctl == 1 { ctl = (input.read_u8()? as i32) | 0x100; } if (ctl & 1) != 0 { let v = input.read_u16()? as i32; let mut x = v & 0x3f; if x > 0x1f { x |= -0x40; } let mut y = (v >> 6) & 7; if y != 0 { y |= -8; } let count = ((v >> 9) & 0x7f) + 3; let src = dst as isize + (x as isize + y as isize * self.width as isize) * 4; if src < 0 || src >= dst as isize { return Ok(()); } let mut src = src as usize; for _ in 0..count { output[dst] = output[src]; src += 4; dst += 4; } } else { output[dst] = input.read_u8()?; dst += 4; } } self.has_alpha.qsave(true); Ok(()) } fn decode_dct( &self, channel: usize, data: &[i16], src: usize, output: &mut [[i16; 3]; 64], ) -> Result<()> { let d = if channel > 0 { 1 } else { 0 }; let mut tmp = [[0f32; 8]; 8]; for i in 0..8 { // Check if all AC coefficients are zero if data[src + 8 + i] == 0 && data[src + 16 + i] == 0 && data[src + 24 + i] == 0 && data[src + 32 + i] == 0 && data[src + 40 + i] == 0 && data[src + 48 + i] == 0 && data[src + 56 + i] == 0 { let t = data[src + i] as f32 * self.dct[d][i]; for row in 0..8 { tmp[row][i] = t; } continue; } let v1 = data[src + i] as f32 * self.dct[d][i]; let v2 = data[src + 8 + i] as f32 * self.dct[d][8 + i]; let v3 = data[src + 16 + i] as f32 * self.dct[d][16 + i]; let v4 = data[src + 24 + i] as f32 * self.dct[d][24 + i]; let v5 = data[src + 32 + i] as f32 * self.dct[d][32 + i]; let v6 = data[src + 40 + i] as f32 * self.dct[d][40 + i]; let v7 = data[src + 48 + i] as f32 * self.dct[d][48 + i]; let v8 = data[src + 56 + i] as f32 * self.dct[d][56 + i]; let v10 = v1 + v5; let v11 = v1 - v5; let v12 = v3 + v7; let v13 = (v3 - v7) * 1.414213562f32 - v12; let v1 = v10 + v12; let v7 = v10 - v12; let v3 = v11 + v13; let v5 = v11 - v13; let v14 = v2 + v8; let v15 = v2 - v8; let v16 = v6 + v4; let v17 = v6 - v4; let v8 = v14 + v16; let v11 = (v14 - v16) * 1.414213562f32; let v9 = (v17 + v15) * 1.847759065f32; let v10 = 1.082392200f32 * v15 - v9; let v13 = -2.613125930f32 * v17 + v9; let v6 = v13 - v8; let v4 = v11 - v6; let v2 = v10 + v4; tmp[0][i] = v1 + v8; tmp[1][i] = v3 + v6; tmp[2][i] = v5 + v4; tmp[3][i] = v7 - v2; tmp[4][i] = v7 + v2; tmp[5][i] = v5 - v4; tmp[6][i] = v3 - v6; tmp[7][i] = v1 - v8; } let mut dst = 0; for i in 0..8 { let v10 = tmp[i][0] + tmp[i][4]; let v11 = tmp[i][0] - tmp[i][4]; let v12 = tmp[i][2] + tmp[i][6]; let v13 = tmp[i][2] - tmp[i][6]; let v14 = tmp[i][1] + tmp[i][7]; let v15 = tmp[i][1] - tmp[i][7]; let v16 = tmp[i][5] + tmp[i][3]; let v17 = tmp[i][5] - tmp[i][3]; let v13 = 1.414213562f32 * v13 - v12; let v1 = v10 + v12; let v7 = v10 - v12; let v3 = v11 + v13; let v5 = v11 - v13; let v8 = v14 + v16; let v11 = (v14 - v16) * 1.414213562f32; let v9 = (v17 + v15) * 1.847759065f32; let v10 = v9 - v15 * 1.082392200f32; let v13 = v9 - v17 * 2.613125930f32; let v6 = v13 - v8; let v4 = v11 - v6; let v2 = v10 - v4; output[dst][channel] = Self::float_to_short(v1 + v8); output[dst + 1][channel] = Self::float_to_short(v3 + v6); output[dst + 2][channel] = Self::float_to_short(v5 + v4); output[dst + 3][channel] = Self::float_to_short(v7 + v2); output[dst + 4][channel] = Self::float_to_short(v7 - v2); output[dst + 5][channel] = Self::float_to_short(v5 - v4); output[dst + 6][channel] = Self::float_to_short(v3 - v6); output[dst + 7][channel] = Self::float_to_short(v1 - v8); dst += 8; } Ok(()) } fn float_to_short(f: f32) -> i16 { let a = 0x80 + ((f as i32) >> 3); if a <= 0 { 0 } else if a <= 0xff { a as i16 } else if a < 0x180 { 0xff } else { 0 } } fn float_to_byte(f: f32) -> u8 { if f >= 255.0 { 0xff } else if f <= 0.0 { 0 } else { f as u8 } } } struct CbgEncoder { header: BgiCBGHeader, stream: MemWriter, img: ImageData, key: u32, magic: u32, } impl CbgEncoder { pub fn new(mut img: ImageData) -> Result { if img.depth != 8 { return Err(anyhow::anyhow!("Unsupported image depth: {}", img.depth)); } let bpp = match img.color_type { ImageColorType::Bgr => 24, ImageColorType::Bgra => 32, ImageColorType::Grayscale => 8, ImageColorType::Rgb => { convert_rgb_to_bgr(&mut img)?; 24 } ImageColorType::Rgba => { convert_rgba_to_bgra(&mut img)?; 32 } }; let key = rand::random(); let header = BgiCBGHeader { width: img.width as u16, height: img.height as u16, bpp, _unk: 0, intermediate_length: 0, key, enc_length: 0, check_sum: 0, check_xor: 0, version: 1, }; Ok(CbgEncoder { header, stream: MemWriter::new(), img, key, magic: 0, }) } pub fn encode(mut self) -> Result> { self.stream.write_all(b"CompressedBG___\0")?; let header_pos = self.stream.pos; self.stream.seek(std::io::SeekFrom::Current(0x20))?; let pixel_size = (self.header.bpp / 8) as usize; let stride = self.header.width as usize * pixel_size; let mut sampled_data = self.img.data.clone(); self.average_sampling(&mut sampled_data, stride, pixel_size); let packed_data = Self::pack_zeros(&sampled_data); self.header.intermediate_length = packed_data.len() as u32; let mut frequencies = vec![0u32; 256]; for &byte in &packed_data { frequencies[byte as usize] += 1; } if frequencies.iter().all(|&f| f == 0) { frequencies[0] = 1; } let tree = HuffmanTree::new(&frequencies, false); let mut weight_writer = MemWriter::new(); for &weight in &frequencies { Self::write_int(&mut weight_writer, weight as i32)?; } let weight_data = weight_writer.into_inner(); self.write_encoded(&weight_data)?; let mut bit_writer = MsbBitWriter::new(&mut self.stream); for &byte in &packed_data { tree.encode_token(&mut bit_writer, byte as usize)?; } bit_writer.flush()?; let final_pos = self.stream.pos; self.stream.pos = header_pos; self.header.pack(&mut self.stream, false, Encoding::Cp932)?; self.stream.pos = final_pos; Ok(self.stream.into_inner()) } fn average_sampling(&self, data: &mut [u8], stride: usize, pixel_size: usize) { for y in (0..self.header.height as usize).rev() { let line = y * stride; for x in (0..self.header.width as usize).rev() { let pixel = line + x * pixel_size; for p in 0..pixel_size { let mut avg = 0u32; let mut count = 0; if x > 0 { avg = avg.wrapping_add(data[pixel + p - pixel_size] as u32); count += 1; } if y > 0 { avg = avg.wrapping_add(data[pixel + p - stride] as u32); count += 1; } if count > 0 { avg /= count; } if avg != 0 { data[pixel + p] = data[pixel + p].wrapping_sub(avg as u8); } } } } } fn pack_zeros(input: &[u8]) -> Vec { let mut output = Vec::new(); let mut i = 0; let mut is_zero_run = false; while i < input.len() { let mut count = 0; if is_zero_run { while i + count < input.len() && input[i + count] == 0 { count += 1; } } else { while i + count < input.len() && input[i + count] != 0 { count += 1; } } let mut count_buf = Vec::new(); let mut n = count; loop { let mut byte = (n & 0x7f) as u8; n >>= 7; if n > 0 { byte |= 0x80; } count_buf.push(byte); if n == 0 { break; } } output.extend_from_slice(&count_buf); if !is_zero_run { output.extend_from_slice(&input[i..i + count]); } i += count; is_zero_run = !is_zero_run; } output } fn write_int(writer: &mut W, mut value: i32) -> Result<()> { loop { let mut b = (value as u8) & 0x7f; value >>= 7; if value != 0 { b |= 0x80; } writer.write_u8(b)?; if value == 0 { break; } } Ok(()) } fn write_encoded(&mut self, data: &[u8]) -> Result<()> { self.header.enc_length = data.len() as u32; let mut sum = 0u8; let mut xor = 0u8; let mut encoded_data = Vec::with_capacity(data.len()); for &byte in data { let encrypted_byte = byte.wrapping_add(self.update_key()); sum = sum.wrapping_add(byte); xor ^= byte; encoded_data.push(encrypted_byte); } self.header.check_sum = sum; self.header.check_xor = xor; self.stream.write_all(&encoded_data)?; Ok(()) } fn update_key(&mut self) -> u8 { let v0 = 20021 * (self.key & 0xffff); let mut v1 = self.magic | (self.key >> 16); v1 = v1 .overflowing_mul(20021) .0 .overflowing_add(self.key.overflowing_mul(346).0) .0; v1 = (v1 + (v0 >> 16)) & 0xffff; self.key = (v1 << 16) + (v0 & 0xffff) + 1; v1 as u8 } }