mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-07 05:18:44 +08:00
Add dref img support
This commit is contained in:
283
Cargo.lock
generated
283
Cargo.lock
generated
@@ -223,6 +223,17 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@@ -345,6 +356,15 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -373,6 +393,113 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"potential_utf",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
@@ -436,6 +563,12 @@ dependencies = [
|
||||
"overf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@@ -474,6 +607,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
"utf16string",
|
||||
"windows-sys",
|
||||
]
|
||||
@@ -503,6 +637,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
@@ -516,6 +656,15 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -651,6 +800,18 @@ version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -668,6 +829,27 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
@@ -686,6 +868,17 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16string"
|
||||
version = "0.2.0"
|
||||
@@ -695,6 +888,12 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
@@ -798,6 +997,36 @@ dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.25"
|
||||
@@ -817,3 +1046,57 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@@ -22,6 +22,7 @@ rand = { version = "0.9", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
unicode-segmentation = "1.12"
|
||||
url = { version = "2.5", optional = true }
|
||||
utf16string = "0.2"
|
||||
|
||||
[features]
|
||||
@@ -36,7 +37,7 @@ circus = []
|
||||
escude = ["int-enum"]
|
||||
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
||||
kirikiri = ["emote-psb", "fancy-regex", "flate2", "utils-escape"]
|
||||
kirikiri-img = ["kirikiri", "emote-psb", "image", "libtlg-rs"]
|
||||
kirikiri-img = ["kirikiri", "emote-psb", "image", "libtlg-rs", "url"]
|
||||
yaneurao = []
|
||||
yaneurao-itufuru = ["yaneurao"]
|
||||
# basic feature
|
||||
|
||||
238
src/scripts/kirikiri/image/dref.rs
Normal file
238
src/scripts/kirikiri/image/dref.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::ext::psb::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::*;
|
||||
use crate::utils::img::*;
|
||||
use anyhow::Result;
|
||||
use emote_psb::PsbReader;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrefBuilder {}
|
||||
|
||||
impl DrefBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptBuilder for DrefBuilder {
|
||||
fn default_encoding(&self) -> Encoding {
|
||||
Encoding::Cp932
|
||||
}
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
buf: Vec<u8>,
|
||||
filename: &str,
|
||||
encoding: Encoding,
|
||||
_archive_encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(Dref::new(buf, encoding, filename, config)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
&["dref"]
|
||||
}
|
||||
|
||||
fn script_type(&self) -> &'static ScriptType {
|
||||
&ScriptType::KirikiriDref
|
||||
}
|
||||
|
||||
fn is_image(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct Dpak {
|
||||
psb: VirtualPsbFixed,
|
||||
}
|
||||
|
||||
struct OffsetData {
|
||||
left: u32,
|
||||
top: u32,
|
||||
}
|
||||
|
||||
impl Dpak {
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||
let f = std::fs::File::open(path)?;
|
||||
let mut f = std::io::BufReader::new(f);
|
||||
let mut psb = PsbReader::open_psb(&mut f)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to read PSB from DPAK: {:?}", e))?;
|
||||
let psb = psb
|
||||
.load()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to load PSB from DPAK: {:?}", e))?;
|
||||
let psb = psb.to_psb_fixed();
|
||||
Ok(Self { psb })
|
||||
}
|
||||
|
||||
pub fn load_image(&self, name: &str) -> Result<(ImageData, Option<OffsetData>)> {
|
||||
let root = self.psb.root();
|
||||
let rid = root[name]
|
||||
.resource_id()
|
||||
.ok_or_else(|| anyhow::anyhow!("Resource ID for image '{}' not found in DPAK", name))?
|
||||
as usize;
|
||||
if rid >= self.psb.resources().len() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Resource ID {} out of bounds for DPAK with {} resources",
|
||||
rid,
|
||||
self.psb.resources().len()
|
||||
));
|
||||
}
|
||||
let resource = &self.psb.resources()[rid];
|
||||
Self::load_png(&resource)
|
||||
}
|
||||
|
||||
fn load_png(data: &[u8]) -> Result<(ImageData, Option<OffsetData>)> {
|
||||
let mut img = load_png(MemReaderRef::new(&data))?;
|
||||
match img.color_type {
|
||||
ImageColorType::Rgb => {
|
||||
convert_rgb_to_rgba(&mut img)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok((
|
||||
img,
|
||||
Self::try_read_offset_from_png(MemReaderRef::new(&data))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn try_read_offset_from_png(mut data: MemReaderRef) -> Result<Option<OffsetData>> {
|
||||
data.pos = 8; // Skip PNG signature
|
||||
data.pos += 8; // Skip chunk size, type
|
||||
data.pos += 17; // Skip IHDR chunk (length + type + width + height + bit depth + color type + compression method + filter method + interlace method)
|
||||
loop {
|
||||
let chunk_size = data.read_u32_be()?;
|
||||
let mut chunk_type = [0u8; 4];
|
||||
data.read_exact(&mut chunk_type)?;
|
||||
if &chunk_type == b"IDAT" || &chunk_type == b"IEND" {
|
||||
break;
|
||||
}
|
||||
if &chunk_type == b"oFFs" {
|
||||
let x = data.read_u32_be()?;
|
||||
let y = data.read_u32_be()?;
|
||||
if data.read_u8()? == 0 {
|
||||
return Ok(Some(OffsetData { left: x, top: y }));
|
||||
}
|
||||
}
|
||||
data.pos += chunk_size as usize + 4; // Skip chunk data and CRC
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DpakLoader {
|
||||
map: HashMap<String, Dpak>,
|
||||
}
|
||||
|
||||
impl DpakLoader {
|
||||
pub fn load_image(
|
||||
&mut self,
|
||||
dir: &Path,
|
||||
dpak: &str,
|
||||
filename: &str,
|
||||
) -> Result<(ImageData, Option<OffsetData>)> {
|
||||
let dpak = match self.map.get(dpak) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
let path = dir.join(dpak);
|
||||
let ndpak = Dpak::new(&path)?;
|
||||
self.map.insert(dpak.to_string(), ndpak);
|
||||
self.map.get(dpak).unwrap()
|
||||
}
|
||||
};
|
||||
dpak.load_image(filename)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Dref {
|
||||
urls: Vec<Url>,
|
||||
dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Dref {
|
||||
pub fn new(
|
||||
buf: Vec<u8>,
|
||||
encoding: Encoding,
|
||||
filename: &str,
|
||||
_config: &ExtraConfig,
|
||||
) -> Result<Self> {
|
||||
let text = decode_with_bom_detect(encoding, &buf)?.0;
|
||||
let mut urls = Vec::new();
|
||||
for text in text.lines() {
|
||||
let text = text.trim();
|
||||
if text.is_empty() {
|
||||
continue;
|
||||
}
|
||||
urls.push(Url::parse(text)?);
|
||||
}
|
||||
let path = Path::new(filename);
|
||||
let dir = if let Some(parent) = path.parent() {
|
||||
parent.to_path_buf()
|
||||
} else {
|
||||
PathBuf::from(".")
|
||||
};
|
||||
if urls.is_empty() {
|
||||
return Err(anyhow::anyhow!("No URLs found in DREF file: {}", filename));
|
||||
}
|
||||
for u in urls.iter() {
|
||||
if u.scheme() != "psb" {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Invalid URL scheme in DREF file: {} (expected 'psb')",
|
||||
u
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(Self { urls, dir })
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for Dref {
|
||||
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<ImageData> {
|
||||
let mut loader = DpakLoader::default();
|
||||
let base_url = &self.urls[0];
|
||||
let dpak = base_url.domain().ok_or(anyhow::anyhow!(
|
||||
"Invalid URL in DREF file: {} (missing domain)",
|
||||
base_url
|
||||
))?;
|
||||
let (mut base_img, base_offset) =
|
||||
loader.load_image(&self.dir, dpak, base_url.path().trim_start_matches("/"))?;
|
||||
if let Some(o) = base_offset {
|
||||
eprintln!("WARN: Base image offset: left={}, top={}", o.left, o.top);
|
||||
crate::COUNTER.inc_warning();
|
||||
}
|
||||
for url in &self.urls[1..] {
|
||||
let dpak = url.domain().ok_or(anyhow::anyhow!(
|
||||
"Invalid URL in DREF file: {} (missing domain)",
|
||||
url
|
||||
))?;
|
||||
let (img, img_offset) =
|
||||
loader.load_image(&self.dir, dpak, url.path().trim_start_matches("/"))?;
|
||||
let (top, left) = match img_offset {
|
||||
Some(o) => (o.top, o.left),
|
||||
None => (0, 0),
|
||||
};
|
||||
draw_on_img_with_opacity(&mut base_img, &img, left, top, 0xff)?;
|
||||
}
|
||||
Ok(base_img)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod dref;
|
||||
pub mod pimg;
|
||||
pub mod tlg;
|
||||
|
||||
@@ -58,6 +58,8 @@ lazy_static::lazy_static! {
|
||||
Box::new(kirikiri::image::tlg::TlgImageBuilder::new()),
|
||||
#[cfg(feature = "kirikiri-img")]
|
||||
Box::new(kirikiri::image::pimg::PImgBuilder::new()),
|
||||
#[cfg(feature = "kirikiri-img")]
|
||||
Box::new(kirikiri::image::dref::DrefBuilder::new()),
|
||||
];
|
||||
pub static ref ALL_EXTS: Vec<String> =
|
||||
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();
|
||||
|
||||
@@ -294,6 +294,10 @@ pub enum ScriptType {
|
||||
#[value(alias("kr-pimg"))]
|
||||
/// Kirikiri PIMG image
|
||||
KirikiriPimg,
|
||||
#[cfg(feature = "kirikiri-img")]
|
||||
#[value(alias("kr-dref"))]
|
||||
/// Kirikiri DREF(DPAK-referenced) image
|
||||
KirikiriDref,
|
||||
#[cfg(feature = "yaneurao-itufuru")]
|
||||
#[value(alias("itufuru"))]
|
||||
/// Yaneurao Itufuru script
|
||||
|
||||
@@ -52,6 +52,27 @@ pub fn convert_bgra_to_rgba(data: &mut ImageData) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn convert_rgb_to_rgba(data: &mut ImageData) -> Result<()> {
|
||||
if data.color_type != ImageColorType::Rgb {
|
||||
return Err(anyhow::anyhow!("Image is not RGB"));
|
||||
}
|
||||
if data.depth != 8 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"RGB to RGBA conversion only supports 8-bit depth"
|
||||
));
|
||||
}
|
||||
let mut new_data = Vec::with_capacity(data.data.len() / 3 * 4);
|
||||
for chunk in data.data.chunks_exact(3) {
|
||||
new_data.push(chunk[0]); // R
|
||||
new_data.push(chunk[1]); // G
|
||||
new_data.push(chunk[2]); // B
|
||||
new_data.push(255); // A
|
||||
}
|
||||
data.data = new_data;
|
||||
data.color_type = ImageColorType::Rgba;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn convert_rgb_to_bgr(data: &mut ImageData) -> Result<()> {
|
||||
if data.color_type != ImageColorType::Rgb {
|
||||
return Err(anyhow::anyhow!("Image is not RGB"));
|
||||
@@ -124,39 +145,44 @@ pub fn encode_img(mut data: ImageData, typ: ImageOutputType, filename: &str) ->
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_png<R: std::io::Read>(data: R) -> Result<ImageData> {
|
||||
let decoder = png::Decoder::new(data);
|
||||
let mut reader = decoder.read_info()?;
|
||||
let bit_depth = match reader.info().bit_depth {
|
||||
png::BitDepth::One => 1,
|
||||
png::BitDepth::Two => 2,
|
||||
png::BitDepth::Four => 4,
|
||||
png::BitDepth::Eight => 8,
|
||||
png::BitDepth::Sixteen => 16,
|
||||
};
|
||||
let color_type = match reader.info().color_type {
|
||||
png::ColorType::Grayscale => ImageColorType::Grayscale,
|
||||
png::ColorType::Rgb => ImageColorType::Rgb,
|
||||
png::ColorType::Rgba => ImageColorType::Rgba,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unsupported color type: {:?}",
|
||||
reader.info().color_type
|
||||
));
|
||||
}
|
||||
};
|
||||
let stride = reader.info().width as usize * color_type.bpp(bit_depth) as usize / 8;
|
||||
let mut data = vec![0; stride * reader.info().height as usize];
|
||||
reader.next_frame(&mut data)?;
|
||||
Ok(ImageData {
|
||||
width: reader.info().width,
|
||||
height: reader.info().height,
|
||||
depth: bit_depth,
|
||||
color_type,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decode_img(typ: ImageOutputType, filename: &str) -> Result<ImageData> {
|
||||
match typ {
|
||||
ImageOutputType::Png => {
|
||||
let file = crate::utils::files::read_file(filename)?;
|
||||
let decoder = png::Decoder::new(&file[..]);
|
||||
let mut reader = decoder.read_info()?;
|
||||
let bit_depth = match reader.info().bit_depth {
|
||||
png::BitDepth::One => 1,
|
||||
png::BitDepth::Two => 2,
|
||||
png::BitDepth::Four => 4,
|
||||
png::BitDepth::Eight => 8,
|
||||
png::BitDepth::Sixteen => 16,
|
||||
};
|
||||
let color_type = match reader.info().color_type {
|
||||
png::ColorType::Grayscale => ImageColorType::Grayscale,
|
||||
png::ColorType::Rgb => ImageColorType::Rgb,
|
||||
png::ColorType::Rgba => ImageColorType::Rgba,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unsupported color type: {:?}",
|
||||
reader.info().color_type
|
||||
));
|
||||
}
|
||||
};
|
||||
let mut data = vec![0; reader.info().raw_bytes()];
|
||||
reader.next_frame(&mut data)?;
|
||||
Ok(ImageData {
|
||||
width: reader.info().width,
|
||||
height: reader.info().height,
|
||||
depth: bit_depth,
|
||||
color_type,
|
||||
data,
|
||||
})
|
||||
load_png(&file[..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user