add import support for emote psb files

fix create file bug in main
This commit is contained in:
2025-09-15 23:14:19 +08:00
parent 28288ecf1b
commit ec137bc5cd
5 changed files with 293 additions and 24 deletions

View File

@@ -4,6 +4,7 @@ use crate::ext::psb::*;
use crate::scripts::base::*;
use crate::types::*;
use crate::utils::encoding::*;
use crate::utils::files::*;
use crate::utils::img::*;
use anyhow::Result;
use base64::Engine;
@@ -78,6 +79,21 @@ impl ScriptBuilder for PsbBuilder {
}
None
}
fn can_create_file(&self) -> bool {
true
}
fn create_file<'a>(
&'a self,
filename: &'a str,
writer: Box<dyn WriteSeek + 'a>,
encoding: Encoding,
file_encoding: Encoding,
_config: &ExtraConfig,
) -> Result<()> {
create_file(filename, writer, encoding, file_encoding)
}
}
#[derive(Debug)]
@@ -123,6 +139,7 @@ impl Psb {
pb.to_string_lossy().to_string()
};
let path = folder_path.join(&res.path);
make_sure_dir_exists(&path)?;
let img = ImageData {
width: tlg.width as u32,
height: tlg.height as u32,
@@ -137,6 +154,7 @@ impl Psb {
encode_img(img, outtype, &path.to_string_lossy(), &self.config)?;
} else {
let path = folder_path.join(&res.path);
make_sure_dir_exists(&path)?;
std::fs::write(&path, data)?;
}
Ok(res)
@@ -172,11 +190,30 @@ impl TlgInfo {
}
Self { metadata }
}
fn to_tlg_tags(&self, encoding: Encoding) -> Result<HashMap<Vec<u8>, Vec<u8>>> {
let mut tags = HashMap::new();
for (k, v) in &self.metadata {
let k = if k.starts_with("base64:") {
base64::engine::general_purpose::STANDARD.decode(&k[7..])?
} else {
encode_string(encoding, k, false)?
};
let v = if v.starts_with("base64:") {
base64::engine::general_purpose::STANDARD.decode(&v[7..])?
} else {
encode_string(encoding, v, false)?
};
tags.insert(k, v);
}
Ok(tags)
}
}
#[derive(Debug, Deserialize, Serialize)]
struct Resource {
path: String,
#[serde(skip_serializing_if = "Option::is_none")]
tlg: Option<TlgInfo>,
}
@@ -206,17 +243,15 @@ impl Script for Psb {
pb.set_extension("");
pb
};
if self.psb.resources().len() > 0 || self.psb.extra().len() > 0 {
std::fs::create_dir_all(&folder_path)?;
}
for (i, data) in self.psb.resources().iter().enumerate() {
let i = i as u64;
let res_name = self
.psb
.root()
.find_resource_key(i)
.map(|s| s.to_string())
.find_resource_key(i, vec![])
.map(|s| s.join("/"))
.unwrap_or(format!("res_{}", i));
let res_name = sanitize_path(&res_name);
let res = self.output_resource(&folder_path, res_name, data)?;
resources.push(res);
}
@@ -225,9 +260,10 @@ impl Script for Psb {
let res_name = self
.psb
.root()
.find_resource_key(i)
.map(|s| format!("extra_{}", s))
.find_resource_key(i, vec![])
.map(|s| format!("extra_{}", s.join("/")))
.unwrap_or(format!("extra_res_{}", i));
let res_name = sanitize_path(&res_name);
let res = self.output_resource(&folder_path, res_name, data)?;
extra_resources.push(res);
}
@@ -239,4 +275,91 @@ impl Script for Psb {
file.write_all(&s)?;
Ok(())
}
fn custom_import<'a>(
&'a self,
custom_filename: &'a str,
file: Box<dyn WriteSeek + 'a>,
encoding: Encoding,
output_encoding: Encoding,
) -> Result<()> {
create_file(custom_filename, file, encoding, output_encoding)
}
}
fn read_resource(
folder_path: &std::path::PathBuf,
res: &Resource,
encoding: Encoding,
) -> Result<Vec<u8>> {
if let Some(tlg) = &res.tlg {
let path = folder_path.join(&res.path);
let imgfmt = ImageOutputType::try_from(path.as_path())?;
let mut img = decode_img(imgfmt, &path.to_string_lossy())?;
if img.depth != 8 {
return Err(anyhow::anyhow!(
"Only 8-bit images are supported for TLG conversion"
));
}
let color_type = match img.color_type {
ImageColorType::Bgr => TlgColorType::Bgr24,
ImageColorType::Bgra => TlgColorType::Bgra32,
ImageColorType::Grayscale => TlgColorType::Grayscale8,
ImageColorType::Rgb => {
convert_rgb_to_bgr(&mut img)?;
TlgColorType::Bgr24
}
ImageColorType::Rgba => {
convert_rgba_to_bgra(&mut img)?;
TlgColorType::Bgra32
}
};
let tlg = Tlg {
width: img.width,
height: img.height,
version: 5,
color: color_type,
data: img.data,
tags: tlg.to_tlg_tags(encoding)?,
};
let mut writer = MemWriter::new();
save_tlg(&tlg, &mut writer)?;
Ok(writer.into_inner())
} else {
let path = folder_path.join(&res.path);
Ok(std::fs::read(&path)?)
}
}
fn create_file<'a>(
custom_filename: &'a str,
mut writer: Box<dyn WriteSeek + 'a>,
encoding: Encoding,
output_encoding: Encoding,
) -> Result<()> {
let input = read_file(custom_filename)?;
let s = decode_to_string(output_encoding, &input, true)?;
let data = json::parse(&s)?;
let resources: Vec<Resource> = serde_json::from_str(&data["resources"].dump())?;
let extra_resources: Vec<Resource> = serde_json::from_str(&data["extra_resources"].dump())?;
let mut psb = VirtualPsbFixed::with_json(&data)?;
let folder_path = {
let mut pb = std::path::PathBuf::from(custom_filename);
pb.set_extension("");
pb
};
for res in resources {
let res = read_resource(&folder_path, &res, encoding)?;
psb.resources_mut().push(res);
}
for res in extra_resources {
let res = read_resource(&folder_path, &res, encoding)?;
psb.extra_mut().push(res);
}
let psb = psb.to_psb(false);
let psb_writer = PsbWriter::new(psb, &mut writer);
psb_writer
.finish()
.map_err(|e| anyhow::anyhow!("Failed to write psb: {:?}", e))?;
Ok(())
}