mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-07-08 01:31:53 +08:00
Use custom json serde method to import/export psb
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -542,6 +542,12 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "json"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -598,6 +604,7 @@ dependencies = [
|
|||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"flate2",
|
"flate2",
|
||||||
"int-enum",
|
"int-enum",
|
||||||
|
"json",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libtlg-rs",
|
"libtlg-rs",
|
||||||
"msg_tool_macro",
|
"msg_tool_macro",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ encoding_rs = "0.8"
|
|||||||
fancy-regex = { version = "0.14", optional = true }
|
fancy-regex = { version = "0.14", optional = true }
|
||||||
flate2 = { version = "1.1", optional = true }
|
flate2 = { version = "1.1", optional = true }
|
||||||
int-enum = { version = "1.2", optional = true }
|
int-enum = { version = "1.2", optional = true }
|
||||||
|
json = { version = "0.12", optional = true }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
libtlg-rs = { version = "0.1", optional = true }
|
libtlg-rs = { version = "0.1", optional = true }
|
||||||
msg_tool_macro = { path = "./msg_tool_macro" }
|
msg_tool_macro = { path = "./msg_tool_macro" }
|
||||||
@@ -36,7 +37,7 @@ cat-system-img = ["cat-system", "flate2", "image", "utils-bit-stream"]
|
|||||||
circus = []
|
circus = []
|
||||||
escude = ["int-enum"]
|
escude = ["int-enum"]
|
||||||
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
||||||
kirikiri = ["emote-psb", "fancy-regex", "flate2", "utils-escape"]
|
kirikiri = ["emote-psb", "fancy-regex", "flate2", "json", "utils-escape"]
|
||||||
kirikiri-img = ["kirikiri", "emote-psb", "image", "libtlg-rs", "url"]
|
kirikiri-img = ["kirikiri", "emote-psb", "image", "libtlg-rs", "url"]
|
||||||
yaneurao = []
|
yaneurao = []
|
||||||
yaneurao-itufuru = ["yaneurao"]
|
yaneurao-itufuru = ["yaneurao"]
|
||||||
|
|||||||
134
src/ext/psb.rs
134
src/ext/psb.rs
@@ -5,6 +5,8 @@ use emote_psb::types::number::*;
|
|||||||
use emote_psb::types::reference::*;
|
use emote_psb::types::reference::*;
|
||||||
use emote_psb::types::string::*;
|
use emote_psb::types::string::*;
|
||||||
use emote_psb::types::*;
|
use emote_psb::types::*;
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
use json::JsonValue;
|
||||||
use std::cmp::PartialEq;
|
use std::cmp::PartialEq;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
@@ -163,6 +165,89 @@ impl PsbValueFixed {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn to_json(&self) -> Option<JsonValue> {
|
||||||
|
match self {
|
||||||
|
PsbValueFixed::Null => Some(JsonValue::Null),
|
||||||
|
PsbValueFixed::Bool(b) => Some(JsonValue::Boolean(*b)),
|
||||||
|
PsbValueFixed::Number(n) => match n {
|
||||||
|
PsbNumber::Integer(i) => Some(JsonValue::Number((*i).into())),
|
||||||
|
PsbNumber::Float(f) => Some(JsonValue::Number((*f).into())),
|
||||||
|
PsbNumber::Double(d) => Some(JsonValue::Number((*d).into())),
|
||||||
|
},
|
||||||
|
PsbValueFixed::String(s) => Some(JsonValue::String(s.string().to_owned())),
|
||||||
|
PsbValueFixed::Resource(s) => {
|
||||||
|
Some(JsonValue::String(format!("resource#{}", s.resource_ref)))
|
||||||
|
}
|
||||||
|
PsbValueFixed::ExtraResource(s) => Some(JsonValue::String(format!(
|
||||||
|
"extra_resource#{}",
|
||||||
|
s.extra_resource_ref
|
||||||
|
))),
|
||||||
|
PsbValueFixed::IntArray(arr) => Some(JsonValue::Array(
|
||||||
|
arr.iter().map(|n| JsonValue::Number((*n).into())).collect(),
|
||||||
|
)),
|
||||||
|
PsbValueFixed::List(l) => Some(l.to_json()),
|
||||||
|
PsbValueFixed::Object(o) => Some(o.to_json()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn from_json(obj: &JsonValue) -> Self {
|
||||||
|
match obj {
|
||||||
|
JsonValue::Null => PsbValueFixed::Null,
|
||||||
|
JsonValue::Boolean(b) => PsbValueFixed::Bool(*b),
|
||||||
|
JsonValue::Number(n) => {
|
||||||
|
let data: f64 = (*n).into();
|
||||||
|
if data.fract() == 0.0 {
|
||||||
|
PsbValueFixed::Number(PsbNumber::Integer(data as i64))
|
||||||
|
} else {
|
||||||
|
PsbValueFixed::Number(PsbNumber::Float(data as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonValue::String(s) => {
|
||||||
|
if s.starts_with("resource#") {
|
||||||
|
if let Ok(id) = s[9..].parse::<u64>() {
|
||||||
|
return PsbValueFixed::Resource(PsbResourceRef { resource_ref: id });
|
||||||
|
}
|
||||||
|
} else if s.starts_with("extra_resource#") {
|
||||||
|
if let Ok(id) = s[16..].parse::<u64>() {
|
||||||
|
return PsbValueFixed::ExtraResource(PsbExtraRef {
|
||||||
|
extra_resource_ref: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PsbValueFixed::String(PsbString::from(s.clone()))
|
||||||
|
}
|
||||||
|
JsonValue::Array(arr) => {
|
||||||
|
let values: Vec<PsbValueFixed> = arr.iter().map(PsbValueFixed::from_json).collect();
|
||||||
|
PsbValueFixed::List(PsbListFixed { values })
|
||||||
|
}
|
||||||
|
JsonValue::Object(obj) => {
|
||||||
|
let mut values = HashMap::new();
|
||||||
|
for (key, value) in obj.iter() {
|
||||||
|
values.insert(key.to_owned(), PsbValueFixed::from_json(value));
|
||||||
|
}
|
||||||
|
PsbValueFixed::Object(PsbObjectFixed { values })
|
||||||
|
}
|
||||||
|
JsonValue::Short(n) => {
|
||||||
|
let s = n.as_str();
|
||||||
|
if s.starts_with("resource#") {
|
||||||
|
if let Ok(id) = s[9..].parse::<u64>() {
|
||||||
|
return PsbValueFixed::Resource(PsbResourceRef { resource_ref: id });
|
||||||
|
}
|
||||||
|
} else if s.starts_with("extra_resource#") {
|
||||||
|
if let Ok(id) = s[16..].parse::<u64>() {
|
||||||
|
return PsbValueFixed::ExtraResource(PsbExtraRef {
|
||||||
|
extra_resource_ref: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PsbValueFixed::String(PsbString::from(s.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for PsbValueFixed {
|
impl Index<usize> for PsbValueFixed {
|
||||||
@@ -351,6 +436,12 @@ impl PsbListFixed {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.values.len()
|
self.values.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn to_json(&self) -> JsonValue {
|
||||||
|
let data: Vec<_> = self.values.iter().filter_map(|v| v.to_json()).collect();
|
||||||
|
JsonValue::Array(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for PsbListFixed {
|
impl Index<usize> for PsbListFixed {
|
||||||
@@ -484,6 +575,26 @@ impl PsbObjectFixed {
|
|||||||
inner: self.values.iter_mut(),
|
inner: self.values.iter_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn to_json(&self) -> JsonValue {
|
||||||
|
let mut obj = json::object::Object::new();
|
||||||
|
for (key, value) in &self.values {
|
||||||
|
if let Some(json_value) = value.to_json() {
|
||||||
|
obj.insert(key, json_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonValue::Object(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn from_json(obj: &JsonValue) -> Self {
|
||||||
|
let mut values = HashMap::new();
|
||||||
|
for (key, value) in obj.entries() {
|
||||||
|
values.insert(key.to_owned(), PsbValueFixed::from_json(value));
|
||||||
|
}
|
||||||
|
PsbObjectFixed { values }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Index<&'a str> for PsbObjectFixed {
|
impl<'a> Index<&'a str> for PsbObjectFixed {
|
||||||
@@ -658,6 +769,29 @@ impl VirtualPsbFixed {
|
|||||||
let (header, resources, extra, root) = self.unwrap();
|
let (header, resources, extra, root) = self.unwrap();
|
||||||
VirtualPsb::new(header, resources, extra, root.to_psb())
|
VirtualPsb::new(header, resources, extra, root.to_psb())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn from_json(&mut self, obj: &JsonValue) -> Result<(), anyhow::Error> {
|
||||||
|
let version = obj["version"]
|
||||||
|
.as_u16()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Invalid PSB version"))?;
|
||||||
|
let encryption = obj["encryption"]
|
||||||
|
.as_u16()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Invalid PSB encryption"))?;
|
||||||
|
self.header.version = version;
|
||||||
|
self.header.encryption = encryption;
|
||||||
|
self.root = PsbObjectFixed::from_json(&obj["data"]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub fn to_json(&self) -> JsonValue {
|
||||||
|
json::object! {
|
||||||
|
"version": self.header.version,
|
||||||
|
"encryption": self.header.encryption,
|
||||||
|
"data": self.root.to_json(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VirtualPsbExt {
|
pub trait VirtualPsbExt {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use crate::ext::io::*;
|
|||||||
use crate::ext::psb::*;
|
use crate::ext::psb::*;
|
||||||
use crate::scripts::base::*;
|
use crate::scripts::base::*;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
use crate::utils::encoding::*;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use emote_psb::{PsbReader, PsbWriter};
|
use emote_psb::{PsbReader, PsbWriter};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@@ -131,6 +132,10 @@ impl Script for ScnScript {
|
|||||||
FormatOptions::None
|
FormatOptions::None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_output_supported(&self, _: OutputScriptType) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_messages(&self) -> Result<Vec<Message>> {
|
fn extract_messages(&self) -> Result<Vec<Message>> {
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
let root = self.psb.root();
|
let root = self.psb.root();
|
||||||
@@ -566,6 +571,34 @@ impl Script for ScnScript {
|
|||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn custom_export(&self, filename: &Path, encoding: Encoding) -> Result<()> {
|
||||||
|
let s = json::stringify_pretty(self.psb.to_json(), 2);
|
||||||
|
let mut f = crate::utils::files::write_file(filename)?;
|
||||||
|
let b = encode_string(encoding, &s, false)?;
|
||||||
|
f.write_all(&b)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_import<'a>(
|
||||||
|
&'a self,
|
||||||
|
custom_filename: &'a str,
|
||||||
|
file: Box<dyn WriteSeek + 'a>,
|
||||||
|
_encoding: Encoding,
|
||||||
|
output_encoding: Encoding,
|
||||||
|
) -> Result<()> {
|
||||||
|
let data = crate::utils::files::read_file(custom_filename)?;
|
||||||
|
let s = decode_to_string(output_encoding, &data)?;
|
||||||
|
let json = json::parse(&s)?;
|
||||||
|
let mut psb = self.psb.clone();
|
||||||
|
psb.from_json(&json)?;
|
||||||
|
let psb = psb.to_psb();
|
||||||
|
let writer = PsbWriter::new(psb, file);
|
||||||
|
writer.finish().map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to write PSB to file {}: {:?}", self.filename, e)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
Reference in New Issue
Block a user