Add support to create BGI sysgrp image

This commit is contained in:
2025-06-14 16:44:42 +08:00
parent 1e70036d47
commit d807b9fe5b
6 changed files with 235 additions and 11 deletions

View File

@@ -1,6 +1,7 @@
use crate::ext::io::*;
use crate::scripts::base::*;
use crate::types::*;
use crate::utils::img::*;
use anyhow::Result;
fn try_parse(buf: &[u8]) -> Result<u8> {
@@ -72,6 +73,19 @@ impl ScriptBuilder for BgiImageBuilder {
}
None
}
fn can_create_image_file(&self) -> bool {
true
}
fn create_image_file<'a>(
&'a self,
data: ImageData,
writer: Box<dyn WriteSeek + 'a>,
options: &ExtraConfig,
) -> Result<()> {
create_image(data, writer, options.bgi_img_scramble.unwrap_or(false))
}
}
#[derive(Debug)]
@@ -81,10 +95,73 @@ pub struct BgiImage {
height: u32,
color_type: ImageColorType,
is_scrambled: bool,
opt_is_scrambled: Option<bool>,
}
fn create_image<'a>(
mut data: ImageData,
mut writer: Box<dyn WriteSeek + 'a>,
scrambled: bool,
) -> Result<()> {
writer.write_u16(data.width as u16)?;
writer.write_u16(data.height as u16)?;
if data.depth != 8 {
return Err(anyhow::anyhow!("Unsupported image depth: {}", data.depth));
}
match data.color_type {
ImageColorType::Bgr => {}
ImageColorType::Bgra => {}
ImageColorType::Grayscale => {}
ImageColorType::Rgb => {
convert_rgb_to_bgr(&mut data)?;
}
ImageColorType::Rgba => {
convert_rgba_to_bgra(&mut data)?;
}
}
let bpp = data.color_type.bpp(8);
writer.write_u16(bpp)?;
let flag = if scrambled { 1 } else { 0 };
writer.write_u16(flag)?;
writer.write_u64(0)?; // Padding
let stride = data.width as usize * ((data.color_type.bpp(8) as usize + 7) / 8);
let buf_size = stride * data.height as usize;
if scrambled {
let bpp = data.color_type.bpp(1) as usize;
for i in 0..bpp {
let mut dst = i;
let mut incr = 0u8;
let mut h = data.height;
while h > 0 {
for _ in 0..data.width {
writer.write_u8(data.data[dst].wrapping_sub(incr))?;
incr = data.data[dst];
dst += bpp;
}
h -= 1;
if h == 0 {
break;
}
dst += stride;
let mut pos = dst;
for _ in 0..data.width {
pos -= bpp;
writer.write_u8(data.data[pos].wrapping_sub(incr))?;
incr = data.data[pos];
}
h -= 1;
}
}
} else {
// PNG sometimes return more padding data than expected
// We will write only the required size
writer.write_all(&data.data[..buf_size])?;
}
Ok(())
}
impl BgiImage {
pub fn new(buf: Vec<u8>, _config: &ExtraConfig) -> Result<Self> {
pub fn new(buf: Vec<u8>, config: &ExtraConfig) -> Result<Self> {
let mut reader = MemReader::new(buf);
let width = reader.read_u16()? as u32;
let height = reader.read_u16()? as u32;
@@ -108,6 +185,7 @@ impl BgiImage {
height,
color_type,
is_scrambled,
opt_is_scrambled: config.bgi_img_scramble,
})
}
}
@@ -126,14 +204,41 @@ impl Script for BgiImage {
}
fn export_image(&self) -> Result<ImageData> {
let stride = self.width as usize * ((self.color_type.bbp(8) as usize + 7) / 8);
let stride = self.width as usize * ((self.color_type.bpp(8) as usize + 7) / 8);
let buf_size = stride * self.height as usize;
if self.is_scrambled {
return Err(anyhow::anyhow!("Scrambled images are not supported"));
}
let mut data = Vec::with_capacity(buf_size);
data.resize(buf_size, 0);
self.data.cpeek_extract_at(0x10, &mut data)?;
if self.is_scrambled {
let mut reader = self.data.to_ref();
reader.pos = 0x10;
let bpp = self.color_type.bpp(1) as usize;
for i in 0..bpp {
let mut dst = i;
let mut incr = 0u8;
let mut h = self.height;
while h > 0 {
for _ in 0..self.width {
incr = incr.wrapping_add(reader.read_u8()?);
data[dst] = incr;
dst += bpp;
}
h -= 1;
if h == 0 {
break;
}
dst += stride;
let mut pos = dst;
for _ in 0..self.width {
pos -= bpp;
incr = incr.wrapping_add(reader.read_u8()?);
data[pos] = incr;
}
h -= 1;
}
}
} else {
self.data.cpeek_extract_at(0x10, &mut data)?;
}
Ok(ImageData {
width: self.width,
height: self.height,
@@ -142,4 +247,12 @@ impl Script for BgiImage {
data,
})
}
fn import_image<'a>(&'a self, data: ImageData, file: Box<dyn WriteSeek + 'a>) -> Result<()> {
create_image(
data,
file,
self.opt_is_scrambled.unwrap_or(self.is_scrambled),
)
}
}