//! Qlie Abmp10/11/12 image (.b) use crate::ext::io::*; use crate::scripts::base::*; use crate::types::*; use crate::utils::encoding::*; use crate::utils::files::*; use anyhow::Result; use serde::{Deserialize, Serialize}; use std::io::{Read, Seek, Write}; #[derive(Debug)] /// Qlie Abmp10/11/12 image builder pub struct Abmp10ImageBuilder {} impl Abmp10ImageBuilder { pub fn new() -> Self { Self {} } } impl ScriptBuilder for Abmp10ImageBuilder { fn default_encoding(&self) -> Encoding { Encoding::Cp932 } fn build_script( &self, buf: Vec, _filename: &str, encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, _archive: Option<&Box>, ) -> Result> { Ok(Box::new(Abmp10Image::new( MemReader::new(buf), encoding, config, )?)) } fn extensions(&self) -> &'static [&'static str] { &["b"] } fn script_type(&self) -> &'static ScriptType { &ScriptType::QlieAbmp10 } fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option { if buf_len >= 6 && buf.starts_with(b"abmp1") { let v = buf[5]; if v >= b'0' && v <= b'2' { return Some(25); } } None } fn can_create_file(&self) -> bool { true } fn create_file<'a>( &'a self, filename: &'a str, writer: Box, encoding: Encoding, file_encoding: Encoding, config: &ExtraConfig, ) -> Result<()> { create_file(filename, writer, encoding, file_encoding, config) } } trait AbmpRes { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized; fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()>; } #[derive(Clone, Debug, Serialize, Deserialize)] struct ResourceRef { index: usize, } #[derive(Clone, Debug, Serialize, Deserialize)] struct AbData { /// abdataxx xx = version tag: String, data: ResourceRef, } impl AbmpRes for AbData { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if !tag.starts_with("abdata") { anyhow::bail!("Invalid AbData tag: {}", tag); } let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; img.resource_filenames.push(format!("{tag}_{index}")); Ok(AbData { tag, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring(&self.tag, 0x10, encoding, 0, false)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: abimage10 struct AbImage10 { datas: Vec, } impl AbmpRes for AbImage10 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "abimage10" { anyhow::bail!("Invalid AbImage10 tag: {}", tag); } let mut datas = Vec::new(); let count = data.read_u8()?; for _ in 0..count { let data = AbmpResource::read_from(data, encoding, img)?; datas.push(data); } Ok(AbImage10 { datas }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("abimage10", 0x10, encoding, 0, false)?; data.write_u8(self.datas.len() as u8)?; for res in &self.datas { res.write_to(data, encoding, img)?; } Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: absound10 struct AbSound10 { datas: Vec, } impl AbmpRes for AbSound10 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "absound10" { anyhow::bail!("Invalid AbSound10 tag: {}", tag); } let mut datas = Vec::new(); let count = data.read_u8()?; for _ in 0..count { let data = AbmpResource::read_from(data, encoding, img)?; datas.push(data); } Ok(AbSound10 { datas }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("absound10", 0x10, encoding, 0, false)?; data.write_u8(self.datas.len() as u8)?; for res in &self.datas { res.write_to(data, encoding, img)?; } Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: abimgdat15 struct AbImgData15 { version: u32, name: String, internal_name: String, typ: u8, param: Vec, data: ResourceRef, } impl AbmpRes for AbImgData15 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "abimgdat15" { anyhow::bail!("Invalid AbImgData15 tag: {}", tag); } let version = data.read_u32()?; let name_length = data.read_u16()? as usize * 2; let name = data.read_fstring(name_length, Encoding::Utf16LE, false)?; let internal_name_length = data.read_u16()? as usize; let internal_name = data.read_fstring(internal_name_length, encoding, false)?; let typ = data.read_u8()?; let param_size = if version == 2 { 0x1d } else { 0x11 }; let param = data.read_exact_vec(param_size)?; let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; let mut nname = if !name.is_empty() { name.clone() } else if !internal_name.is_empty() { internal_name.clone() } else { format!("abimage15_{index}") }; match typ { 0 => nname.push_str(".bmp"), 1 => nname.push_str(".jpg"), 2 | 3 => nname.push_str(".png"), 4 => nname.push_str(".m"), 5 => nname.push_str(".argb"), 6 => nname.push_str(".b"), 7 => nname.push_str(".ogv"), 8 => nname.push_str(".mdl"), _ => {} } img.resource_filenames.push(nname); Ok(AbImgData15 { version, name, internal_name, typ, param, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("abimgdat15", 0x10, encoding, 0, false)?; data.write_u32(self.version)?; let name_length = self.name.encode_utf16().count() as u16; let name = encode_string(Encoding::Utf16LE, &self.name, true)?; if name.len() != (name_length as usize) * 2 { anyhow::bail!("Name length mismatch when writing AbImgData15"); } data.write_u16(name_length)?; data.write_all(&name)?; let internal_name = encode_string(encoding, &self.internal_name, true)?; data.write_u16(internal_name.len() as u16)?; data.write_all(&internal_name)?; data.write_u8(self.typ)?; let param_size = if self.version == 2 { 0x1d } else { 0x11 }; if self.param.len() != param_size { anyhow::bail!("Param size mismatch when writing AbImgData15"); } data.write_all(&self.param)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: abimgdat14 struct AbImgData14 { name: String, internal_name: String, typ: u8, param: Vec, data: ResourceRef, } impl AbmpRes for AbImgData14 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "abimgdat14" { anyhow::bail!("Invalid AbImgData14 tag: {}", tag); } let name_length = data.read_u16()? as usize; let name = data.read_fstring(name_length, encoding, false)?; let internal_name_length = data.read_u16()? as usize; let internal_name = data.read_fstring(internal_name_length, encoding, false)?; let typ = data.read_u8()?; let param = data.read_exact_vec(0x4C)?; let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; let mut nname = if !name.is_empty() { name.clone() } else if !internal_name.is_empty() { internal_name.clone() } else { format!("abimage14_{index}") }; match typ { 0 => nname.push_str(".bmp"), 1 => nname.push_str(".jpg"), 2 | 3 => nname.push_str(".png"), 4 => nname.push_str(".m"), 5 => nname.push_str(".argb"), 6 => nname.push_str(".b"), 7 => nname.push_str(".ogv"), 8 => nname.push_str(".mdl"), _ => {} } img.resource_filenames.push(nname); Ok(AbImgData14 { name, internal_name, typ, param, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("abimgdat14", 0x10, encoding, 0, false)?; let name = encode_string(encoding, &self.name, true)?; data.write_u16(name.len() as u16)?; data.write_all(&name)?; let internal_name = encode_string(encoding, &self.internal_name, true)?; data.write_u16(internal_name.len() as u16)?; data.write_all(&internal_name)?; data.write_u8(self.typ)?; if self.param.len() != 0x4C { anyhow::bail!("Param size mismatch when writing AbImgData14"); } data.write_all(&self.param)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: abimgdat13 struct AbImgData13 { name: String, internal_name: String, typ: u8, param: Vec, data: ResourceRef, } impl AbmpRes for AbImgData13 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "abimgdat13" { anyhow::bail!("Invalid AbImgData13 tag: {}", tag); } let name_length = data.read_u16()? as usize; let name = data.read_fstring(name_length, encoding, false)?; let internal_name_length = data.read_u16()? as usize; let internal_name = data.read_fstring(internal_name_length, encoding, false)?; let typ = data.read_u8()?; let param = data.read_exact_vec(0xC)?; let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; let mut nname = if !name.is_empty() { name.clone() } else if !internal_name.is_empty() { internal_name.clone() } else { format!("abimage13_{index}") }; match typ { 0 => nname.push_str(".bmp"), 1 => nname.push_str(".jpg"), 2 | 3 => nname.push_str(".png"), 4 => nname.push_str(".m"), 5 => nname.push_str(".argb"), 6 => nname.push_str(".b"), 7 => nname.push_str(".ogv"), 8 => nname.push_str(".mdl"), _ => {} } img.resource_filenames.push(nname); Ok(AbImgData13 { name, internal_name, typ, param, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("abimgdat13", 0x10, encoding, 0, false)?; let name = encode_string(encoding, &self.name, true)?; data.write_u16(name.len() as u16)?; data.write_all(&name)?; let internal_name = encode_string(encoding, &self.internal_name, true)?; data.write_u16(internal_name.len() as u16)?; data.write_all(&internal_name)?; data.write_u8(self.typ)?; if self.param.len() != 0xC { anyhow::bail!("Param size mismatch when writing AbImgData13"); } data.write_all(&self.param)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: abimgdat11 struct AbImgData11 { name: String, internal_name: String, typ: u8, data: ResourceRef, } impl AbmpRes for AbImgData11 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "abimgdat11" { anyhow::bail!("Invalid AbImgData11 tag: {}", tag); } let name_length = data.read_u16()? as usize; let name = data.read_fstring(name_length, encoding, false)?; let internal_name_length = data.read_u16()? as usize; let internal_name = data.read_fstring(internal_name_length, encoding, false)?; let typ = data.read_u8()?; let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; let mut nname = if !name.is_empty() { name.clone() } else if !internal_name.is_empty() { internal_name.clone() } else { format!("abimage11_{index}") }; match typ { 0 => nname.push_str(".bmp"), 1 => nname.push_str(".jpg"), 2 | 3 => nname.push_str(".png"), 4 => nname.push_str(".m"), 5 => nname.push_str(".argb"), 6 => nname.push_str(".b"), 7 => nname.push_str(".ogv"), 8 => nname.push_str(".mdl"), _ => {} } img.resource_filenames.push(nname); Ok(AbImgData11 { name, internal_name, typ, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("abimgdat11", 0x10, encoding, 0, false)?; let name = encode_string(encoding, &self.name, true)?; data.write_u16(name.len() as u16)?; data.write_all(&name)?; let internal_name = encode_string(encoding, &self.internal_name, true)?; data.write_u16(internal_name.len() as u16)?; data.write_all(&internal_name)?; data.write_u8(self.typ)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: absnddat12 struct AbSndData12 { version: u32, name: String, internal_name: String, typ: u8, data: ResourceRef, } impl AbmpRes for AbSndData12 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "absnddat12" { anyhow::bail!("Invalid AbSndData12 tag: {}", tag); } let version = data.read_u32()?; let name_length = data.read_u16()? as usize * 2; let name = data.read_fstring(name_length, Encoding::Utf16LE, false)?; let internal_name_length = data.read_u16()? as usize; let internal_name = data.read_fstring(internal_name_length, encoding, false)?; let typ = data.read_u8()?; let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; let mut nname = if !name.is_empty() { name.clone() } else if !internal_name.is_empty() { internal_name.clone() } else { format!("absnddat12_{index}") }; match typ { 1 => nname.push_str(".ogg"), _ => {} } img.resource_filenames.push(nname); Ok(AbSndData12 { version, name, internal_name, typ, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("absnddat12", 0x10, encoding, 0, false)?; data.write_u32(self.version)?; let name_length = self.name.encode_utf16().count() as u16; let name = encode_string(Encoding::Utf16LE, &self.name, true)?; if name.len() != (name_length as usize) * 2 { anyhow::bail!("Name length mismatch when writing AbSndData12"); } data.write_u16(name_length)?; data.write_all(&name)?; let internal_name = encode_string(encoding, &self.internal_name, true)?; data.write_u16(internal_name.len() as u16)?; data.write_all(&internal_name)?; data.write_u8(self.typ)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] /// tag: absnddat11 struct AbSndData11 { name: String, internal_name: String, typ: u8, data: ResourceRef, } impl AbmpRes for AbSndData11 { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result where Self: Sized, { let tag = data.read_fstring(0x10, encoding, true)?; if tag != "absnddat11" { anyhow::bail!("Invalid AbSndData11 tag: {}", tag); } let name_length = data.read_u16()? as usize; let name = data.read_fstring(name_length, encoding, false)?; let internal_name_length = data.read_u16()? as usize; let internal_name = data.read_fstring(internal_name_length, encoding, false)?; let typ = data.read_u8()?; let size = data.read_u32()?; let resource = data.read_exact_vec(size as usize)?; img.resources.push(resource); let index = img.resources.len() - 1; let mut nname = if !name.is_empty() { name.clone() } else if !internal_name.is_empty() { internal_name.clone() } else { format!("absnddat11_{index}") }; match typ { 1 => nname.push_str(".ogg"), _ => {} } img.resource_filenames.push(nname); Ok(AbSndData11 { name, internal_name, typ, data: ResourceRef { index }, }) } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { data.write_fstring("absnddat11", 0x10, encoding, 0, false)?; let name = encode_string(encoding, &self.name, true)?; data.write_u16(name.len() as u16)?; data.write_all(&name)?; let internal_name = encode_string(encoding, &self.internal_name, true)?; data.write_u16(internal_name.len() as u16)?; data.write_all(&internal_name)?; data.write_u8(self.typ)?; let res = img .resources .get(self.data.index) .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?; data.write_u32(res.len() as u32)?; data.write_all(res)?; Ok(()) } } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "@type")] enum AbmpResource { Data(AbData), Image10(AbImage10), ImgData15(AbImgData15), ImgData14(AbImgData14), ImgData13(AbImgData13), ImgData11(AbImgData11), Sound10(AbSound10), SndData12(AbSndData12), SndData11(AbSndData11), } impl AbmpRes for AbmpResource { fn read_from( data: &mut T, encoding: Encoding, img: &mut AbmpImage, ) -> Result { let tag = data.peek_fstring(0x10, encoding, true)?; if tag.starts_with("abdata") { return Ok(AbmpResource::Data(AbData::read_from(data, encoding, img)?)); } match tag.as_str() { "abimage10" => Ok(AbmpResource::Image10(AbImage10::read_from( data, encoding, img, )?)), "abimgdat15" => Ok(AbmpResource::ImgData15(AbImgData15::read_from( data, encoding, img, )?)), "abimgdat14" => Ok(AbmpResource::ImgData14(AbImgData14::read_from( data, encoding, img, )?)), "abimgdat13" => Ok(AbmpResource::ImgData13(AbImgData13::read_from( data, encoding, img, )?)), "abimgdat11" => Ok(AbmpResource::ImgData11(AbImgData11::read_from( data, encoding, img, )?)), "absound10" => Ok(AbmpResource::Sound10(AbSound10::read_from( data, encoding, img, )?)), "absnddat11" => Ok(AbmpResource::SndData11(AbSndData11::read_from( data, encoding, img, )?)), "absnddat12" => Ok(AbmpResource::SndData12(AbSndData12::read_from( data, encoding, img, )?)), _ => { anyhow::bail!("Unknown Abmp resource tag: {}", tag); } } } fn write_to( &self, data: &mut T, encoding: Encoding, img: &AbmpImage, ) -> Result<()> { match self { AbmpResource::Data(res) => res.write_to(data, encoding, img), AbmpResource::Image10(res) => res.write_to(data, encoding, img), AbmpResource::ImgData15(res) => res.write_to(data, encoding, img), AbmpResource::ImgData14(res) => res.write_to(data, encoding, img), AbmpResource::ImgData13(res) => res.write_to(data, encoding, img), AbmpResource::ImgData11(res) => res.write_to(data, encoding, img), AbmpResource::Sound10(res) => res.write_to(data, encoding, img), AbmpResource::SndData12(res) => res.write_to(data, encoding, img), AbmpResource::SndData11(res) => res.write_to(data, encoding, img), } } } /// Qlie Abmp10/11/12 image #[derive(Clone, Debug, Serialize, Deserialize)] struct AbmpImage { /// Valid version: 10, 11, 12 version: u8, datas: Vec, extra: Vec, #[serde(skip)] resources: Vec>, /// Just used for dump #[serde(skip)] resource_filenames: Vec, } fn is_false(b: &bool) -> bool { !*b } #[derive(Clone, Debug, Serialize, Deserialize)] struct Resource { path: String, #[serde(skip_serializing_if = "is_false", default)] ambp10: bool, } #[derive(Clone, Debug, Serialize, Deserialize)] struct AbmpImage2 { version: u8, datas: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] extra: Vec, resources: Vec, } impl AbmpImage { pub fn new_from(reader: &mut T, encoding: Encoding) -> Result { let magic = reader.read_fstring(16, encoding, true)?; if !magic.starts_with("abmp1") { anyhow::bail!("Not a valid Abmp image"); } let version = magic.as_bytes()[5] - b'0' + 10; let mut img = AbmpImage { version, datas: Vec::new(), resources: Vec::new(), resource_filenames: Vec::new(), extra: Vec::new(), }; let len = reader.stream_length()?; let mut pos = reader.stream_position()?; while pos < len - 16 { let data = AbmpResource::read_from(reader, encoding, &mut img)?; img.datas.push(data); pos = reader.stream_position()?; } if pos < len { img.extra = reader.read_exact_vec((len - pos) as usize)?; } Ok(img) } pub fn dump_to(&self, mut writer: T, encoding: Encoding) -> Result<()> { writer.write_fstring( &format!("abmp1{}", (self.version - 10 + b'0') as char), 16, encoding, 0, false, )?; for data in &self.datas { data.write_to(&mut writer, encoding, self)?; } writer.write_all(&self.extra)?; Ok(()) } fn to_image2(&self) -> AbmpImage2 { AbmpImage2 { version: self.version, datas: self.datas.clone(), resources: Vec::new(), extra: self.extra.clone(), } } fn from_image2(img: &AbmpImage2) -> Self { AbmpImage { version: img.version, datas: img.datas.clone(), resources: Vec::new(), resource_filenames: Vec::new(), extra: img.extra.clone(), } } } #[derive(Debug)] pub struct Abmp10Image { img: AbmpImage, encoding: Encoding, config: ExtraConfig, } impl Abmp10Image { pub fn new( mut data: T, encoding: Encoding, config: &ExtraConfig, ) -> Result { let img = AbmpImage::new_from(&mut data, encoding)?; Ok(Abmp10Image { img, encoding, config: config.clone(), }) } fn output_resource( &self, folder_path: &std::path::PathBuf, path: String, data: &[u8], encoding: Encoding, ) -> Result { let mut res = Resource { path, ambp10: false, }; if self.config.qlie_abmp10_process_abmp10 && data.len() > 6 && data.starts_with(b"abmp1") && data[5] >= b'0' && data[5] <= b'2' { res.ambp10 = true; let another = Abmp10Image::new(MemReaderRef::new(data), self.encoding, &self.config)?; let mut np = std::path::PathBuf::from(&res.path); np.set_extension(another.custom_output_extension()); res.path = np.to_string_lossy().to_string(); let path = folder_path.join(&res.path); make_sure_dir_exists(&path)?; another.custom_export(&path, encoding)?; } else { let path = folder_path.join(&res.path); make_sure_dir_exists(&path)?; std::fs::write(&path, data)?; } Ok(res) } } impl Script for Abmp10Image { fn default_output_script_type(&self) -> OutputScriptType { OutputScriptType::Custom } fn is_output_supported(&self, output: OutputScriptType) -> bool { matches!(output, OutputScriptType::Custom) } fn default_format_type(&self) -> FormatOptions { FormatOptions::None } fn custom_output_extension<'a>(&'a self) -> &'a str { if self.config.custom_yaml { "yaml" } else { "json" } } fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> { let file = std::fs::File::create(filename)?; let mut file = std::io::BufWriter::new(file); let mut img = self.img.to_image2(); let mut base_path = filename.to_path_buf(); base_path.set_extension(""); for (res, res_name) in self .img .resources .iter() .zip(self.img.resource_filenames.iter()) { let res_name = sanitize_path(res_name); let res = self.output_resource(&base_path, res_name, res, encoding)?; img.resources.push(res); } let s = if self.config.custom_yaml { serde_yaml_ng::to_string(&img)? } else { serde_json::to_string_pretty(&img)? }; let s = encode_string(encoding, &s, false)?; file.write_all(&s)?; Ok(()) } fn custom_import<'a>( &'a self, custom_filename: &'a str, file: Box, encoding: Encoding, output_encoding: Encoding, ) -> Result<()> { create_file( custom_filename, file, encoding, output_encoding, &self.config, ) } } fn create_file<'a>( filename: &str, mut writer: Box, encoding: Encoding, file_encoding: Encoding, config: &ExtraConfig, ) -> Result<()> { let data = crate::utils::files::read_file(filename)?; let s = decode_to_string(file_encoding, &data, true)?; let img2: AbmpImage2 = if config.custom_yaml { serde_yaml_ng::from_str(&s)? } else { serde_json::from_str(&s)? }; let mut img = AbmpImage::from_image2(&img2); let mut base_path = std::path::PathBuf::from(filename); base_path.set_extension(""); for res in &img2.resources { let path = base_path.join(&res.path); let buf = if res.ambp10 { let mut mem = MemWriter::new(); create_file( &path.to_string_lossy(), Box::new(&mut mem), encoding, file_encoding, config, )?; mem.into_inner() } else { crate::utils::files::read_file(&path)? }; img.resources.push(buf); } img.dump_to(&mut writer, encoding)?; Ok(()) }