mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 21:08:48 +08:00
Fix wrong serde serialize for psb file
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -216,7 +216,6 @@ dependencies = [
|
||||
"encoding",
|
||||
"flate2",
|
||||
"itertools",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -381,12 +380,6 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -427,7 +420,6 @@ dependencies = [
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"int-enum",
|
||||
"json",
|
||||
"lazy_static",
|
||||
"msg_tool_macro",
|
||||
"overf",
|
||||
|
||||
@@ -8,11 +8,10 @@ anyhow = "1"
|
||||
blowfish = { version = "0.9", optional = true }
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
csv = "1.3"
|
||||
emote-psb = { version = "0.5", features = ["serde"], optional = true }
|
||||
emote-psb = { version = "0.5", optional = true }
|
||||
encoding_rs = "0.8"
|
||||
flate2 = { version = "1.1", optional = true }
|
||||
int-enum = { version = "1.2", optional = true }
|
||||
json = { version = "0.12", optional = true }
|
||||
lazy_static = "1.5.0"
|
||||
msg_tool_macro = { path = "./msg_tool_macro" }
|
||||
overf = "0.1"
|
||||
@@ -33,7 +32,7 @@ cat-system-img = ["cat-system", "flate2", "image", "utils-bit-stream"]
|
||||
circus = []
|
||||
escude = ["int-enum"]
|
||||
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
||||
kirikiri = ["emote-psb", "json"]
|
||||
kirikiri = ["emote-psb"]
|
||||
yaneurao = []
|
||||
yaneurao-itufuru = ["yaneurao"]
|
||||
# basic feature
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod atomic;
|
||||
pub mod io;
|
||||
#[cfg(feature = "emote-psb")]
|
||||
pub mod psb;
|
||||
pub mod vec;
|
||||
|
||||
606
src/ext/psb.rs
Normal file
606
src/ext/psb.rs
Normal file
@@ -0,0 +1,606 @@
|
||||
use emote_psb::VirtualPsb;
|
||||
use emote_psb::header::PsbHeader;
|
||||
use emote_psb::types::collection::*;
|
||||
use emote_psb::types::number::*;
|
||||
use emote_psb::types::reference::*;
|
||||
use emote_psb::types::string::*;
|
||||
use emote_psb::types::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
const NONE: PsbValueFixed = PsbValueFixed::None;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PsbValueFixed {
|
||||
None,
|
||||
Null,
|
||||
Bool(bool),
|
||||
Number(PsbNumber),
|
||||
IntArray(PsbUintArray),
|
||||
String(PsbString),
|
||||
List(PsbListFixed),
|
||||
Object(PsbObjectFixed),
|
||||
Resource(PsbResourceRef),
|
||||
ExtraResource(PsbExtraRef),
|
||||
CompilerNumber,
|
||||
CompilerString,
|
||||
CompilerResource,
|
||||
CompilerDecimal,
|
||||
CompilerArray,
|
||||
CompilerBool,
|
||||
CompilerBinaryTree,
|
||||
}
|
||||
|
||||
impl PsbValueFixed {
|
||||
pub fn to_psb(self) -> PsbValue {
|
||||
match self {
|
||||
PsbValueFixed::None => PsbValue::None,
|
||||
PsbValueFixed::Null => PsbValue::Null,
|
||||
PsbValueFixed::Bool(b) => PsbValue::Bool(b),
|
||||
PsbValueFixed::Number(n) => PsbValue::Number(n),
|
||||
PsbValueFixed::IntArray(arr) => PsbValue::IntArray(arr),
|
||||
PsbValueFixed::String(s) => PsbValue::String(s),
|
||||
PsbValueFixed::List(l) => PsbValue::List(l.to_psb()),
|
||||
PsbValueFixed::Object(o) => PsbValue::Object(o.to_psb()),
|
||||
PsbValueFixed::Resource(r) => PsbValue::Resource(r),
|
||||
PsbValueFixed::ExtraResource(er) => PsbValue::ExtraResource(er),
|
||||
PsbValueFixed::CompilerNumber => PsbValue::CompilerNumber,
|
||||
PsbValueFixed::CompilerString => PsbValue::CompilerString,
|
||||
PsbValueFixed::CompilerResource => PsbValue::CompilerResource,
|
||||
PsbValueFixed::CompilerDecimal => PsbValue::CompilerDecimal,
|
||||
PsbValueFixed::CompilerArray => PsbValue::CompilerArray,
|
||||
PsbValueFixed::CompilerBool => PsbValue::CompilerBool,
|
||||
PsbValueFixed::CompilerBinaryTree => PsbValue::CompilerBinaryTree,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_list(&self) -> bool {
|
||||
matches!(self, PsbValueFixed::List(_))
|
||||
}
|
||||
|
||||
pub fn is_object(&self) -> bool {
|
||||
matches!(self, PsbValueFixed::Object(_))
|
||||
}
|
||||
|
||||
pub fn is_string_or_null(&self) -> bool {
|
||||
self.is_string() || self.is_null()
|
||||
}
|
||||
|
||||
pub fn is_string(&self) -> bool {
|
||||
matches!(self, PsbValueFixed::String(_))
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, PsbValueFixed::None)
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
matches!(self, PsbValueFixed::Null)
|
||||
}
|
||||
|
||||
pub fn set_str(&mut self, value: &str) {
|
||||
match self {
|
||||
PsbValueFixed::String(s) => {
|
||||
let s = s.string_mut();
|
||||
s.clear();
|
||||
s.push_str(value);
|
||||
}
|
||||
_ => {
|
||||
*self = PsbValueFixed::String(PsbString::from(value.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_string(&mut self, value: String) {
|
||||
self.set_str(&value);
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
PsbValueFixed::String(s) => Some(s.string()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
PsbValueFixed::List(l) => l.len(),
|
||||
PsbValueFixed::Object(o) => o.values.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entries(&self) -> ObjectIter<'_> {
|
||||
match self {
|
||||
PsbValueFixed::Object(o) => o.iter(),
|
||||
_ => ObjectIter::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entries_mut(&mut self) -> ObjectIterMut<'_> {
|
||||
match self {
|
||||
PsbValueFixed::Object(o) => o.iter_mut(),
|
||||
_ => ObjectIterMut::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn members(&self) -> ListIter<'_> {
|
||||
match self {
|
||||
PsbValueFixed::List(l) => l.iter(),
|
||||
_ => ListIter::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn members_mut(&mut self) -> ListIterMut<'_> {
|
||||
match self {
|
||||
PsbValueFixed::List(l) => l.iter_mut(),
|
||||
_ => ListIterMut::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for PsbValueFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
match self {
|
||||
PsbValueFixed::List(l) => &l.values[index],
|
||||
_ => &NONE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for PsbValueFixed {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
match self {
|
||||
PsbValueFixed::List(l) => {
|
||||
if index < l.values.len() {
|
||||
&mut l.values[index]
|
||||
} else {
|
||||
l.values.push(NONE);
|
||||
&mut l.values[index]
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
*self = PsbValueFixed::List(PsbListFixed { values: vec![NONE] });
|
||||
self.index_mut(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<&'a str> for PsbValueFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: &'a str) -> &Self::Output {
|
||||
match self {
|
||||
PsbValueFixed::Object(o) => &o[index],
|
||||
_ => &NONE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<&'a String> for PsbValueFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: &'a String) -> &Self::Output {
|
||||
self.index(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<String> for PsbValueFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: String) -> &Self::Output {
|
||||
self.index(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<&str> for PsbValueFixed {
|
||||
fn index_mut(&mut self, index: &str) -> &mut Self::Output {
|
||||
match self {
|
||||
PsbValueFixed::Object(o) => o.index_mut(index),
|
||||
_ => {
|
||||
*self = PsbValueFixed::Object(PsbObjectFixed {
|
||||
values: HashMap::new(),
|
||||
});
|
||||
self.index_mut(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<&String> for PsbValueFixed {
|
||||
fn index_mut(&mut self, index: &String) -> &mut Self::Output {
|
||||
self.index_mut(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<String> for PsbValueFixed {
|
||||
fn index_mut(&mut self, index: String) -> &mut Self::Output {
|
||||
self.index_mut(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for PsbValueFixed {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
PsbValueFixed::None => PsbValueFixed::None,
|
||||
PsbValueFixed::Null => PsbValueFixed::Null,
|
||||
PsbValueFixed::Bool(b) => PsbValueFixed::Bool(*b),
|
||||
PsbValueFixed::Number(n) => PsbValueFixed::Number(n.clone()),
|
||||
PsbValueFixed::IntArray(arr) => PsbValueFixed::IntArray(arr.clone()),
|
||||
PsbValueFixed::String(s) => PsbValueFixed::String(PsbString::from(s.string().clone())),
|
||||
PsbValueFixed::List(l) => PsbValueFixed::List(l.clone()),
|
||||
PsbValueFixed::Object(o) => PsbValueFixed::Object(o.clone()),
|
||||
PsbValueFixed::Resource(r) => PsbValueFixed::Resource(r.clone()),
|
||||
PsbValueFixed::ExtraResource(er) => PsbValueFixed::ExtraResource(er.clone()),
|
||||
PsbValueFixed::CompilerNumber => PsbValueFixed::CompilerNumber,
|
||||
PsbValueFixed::CompilerString => PsbValueFixed::CompilerString,
|
||||
PsbValueFixed::CompilerResource => PsbValueFixed::CompilerResource,
|
||||
PsbValueFixed::CompilerDecimal => PsbValueFixed::CompilerDecimal,
|
||||
PsbValueFixed::CompilerArray => PsbValueFixed::CompilerArray,
|
||||
PsbValueFixed::CompilerBool => PsbValueFixed::CompilerBool,
|
||||
PsbValueFixed::CompilerBinaryTree => PsbValueFixed::CompilerBinaryTree,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PsbValueExt {
|
||||
fn to_psb_fixed(self) -> PsbValueFixed;
|
||||
}
|
||||
|
||||
impl PsbValueExt for PsbValue {
|
||||
fn to_psb_fixed(self) -> PsbValueFixed {
|
||||
match self {
|
||||
PsbValue::None => PsbValueFixed::None,
|
||||
PsbValue::Null => PsbValueFixed::Null,
|
||||
PsbValue::Bool(b) => PsbValueFixed::Bool(b),
|
||||
PsbValue::Number(n) => PsbValueFixed::Number(n),
|
||||
PsbValue::IntArray(arr) => PsbValueFixed::IntArray(arr),
|
||||
PsbValue::String(s) => PsbValueFixed::String(s),
|
||||
PsbValue::List(l) => PsbValueFixed::List(PsbList::to_psb_fixed(l)),
|
||||
PsbValue::Object(o) => PsbValueFixed::Object(PsbObject::to_psb_fixed(o)),
|
||||
PsbValue::Resource(r) => PsbValueFixed::Resource(r),
|
||||
PsbValue::ExtraResource(er) => PsbValueFixed::ExtraResource(er),
|
||||
PsbValue::CompilerNumber => PsbValueFixed::CompilerNumber,
|
||||
PsbValue::CompilerString => PsbValueFixed::CompilerString,
|
||||
PsbValue::CompilerResource => PsbValueFixed::CompilerResource,
|
||||
PsbValue::CompilerDecimal => PsbValueFixed::CompilerDecimal,
|
||||
PsbValue::CompilerArray => PsbValueFixed::CompilerArray,
|
||||
PsbValue::CompilerBool => PsbValueFixed::CompilerBool,
|
||||
PsbValue::CompilerBinaryTree => PsbValueFixed::CompilerBinaryTree,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PsbListFixed {
|
||||
pub values: Vec<PsbValueFixed>,
|
||||
}
|
||||
|
||||
impl PsbListFixed {
|
||||
pub fn to_psb(self) -> PsbList {
|
||||
let v: Vec<_> = self.values.into_iter().map(|v| v.to_psb()).collect();
|
||||
PsbList::from(v)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ListIter<'_> {
|
||||
ListIter {
|
||||
inner: self.values.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> ListIterMut<'_> {
|
||||
ListIterMut {
|
||||
inner: self.values.iter_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn values(&self) -> &Vec<PsbValueFixed> {
|
||||
&self.values
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListIter<'a> {
|
||||
inner: std::slice::Iter<'a, PsbValueFixed>,
|
||||
}
|
||||
|
||||
impl<'a> ListIter<'a> {
|
||||
pub fn empty() -> Self {
|
||||
ListIter {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ListIter<'a> {
|
||||
type Item = &'a PsbValueFixed;
|
||||
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for ListIter<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for ListIter<'a> {
|
||||
#[inline(always)]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListIterMut<'a> {
|
||||
inner: std::slice::IterMut<'a, PsbValueFixed>,
|
||||
}
|
||||
|
||||
impl<'a> ListIterMut<'a> {
|
||||
pub fn empty() -> Self {
|
||||
ListIterMut {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ListIterMut<'a> {
|
||||
type Item = &'a mut PsbValueFixed;
|
||||
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for ListIterMut<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for ListIterMut<'a> {
|
||||
#[inline(always)]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PsbListExt {
|
||||
fn to_psb_fixed(self) -> PsbListFixed;
|
||||
}
|
||||
|
||||
impl PsbListExt for PsbList {
|
||||
fn to_psb_fixed(self) -> PsbListFixed {
|
||||
let values: Vec<_> = self
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(PsbValue::to_psb_fixed)
|
||||
.collect();
|
||||
PsbListFixed { values }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PsbObjectFixed {
|
||||
pub values: HashMap<String, PsbValueFixed>,
|
||||
}
|
||||
|
||||
impl PsbObjectFixed {
|
||||
pub fn to_psb(self) -> PsbObject {
|
||||
let mut hash_map = HashMap::new();
|
||||
for (key, value) in self.values {
|
||||
hash_map.insert(key, value.to_psb());
|
||||
}
|
||||
PsbObject::from(hash_map)
|
||||
}
|
||||
|
||||
pub fn get_value(&self, key: &str) -> Option<&PsbValueFixed> {
|
||||
self.values.get(key)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ObjectIter<'_> {
|
||||
ObjectIter {
|
||||
inner: self.values.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> ObjectIterMut<'_> {
|
||||
ObjectIterMut {
|
||||
inner: self.values.iter_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<&'a str> for PsbObjectFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: &'a str) -> &Self::Output {
|
||||
self.values.get(index).unwrap_or(&NONE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<&'a String> for PsbObjectFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: &'a String) -> &Self::Output {
|
||||
self.index(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<String> for PsbObjectFixed {
|
||||
type Output = PsbValueFixed;
|
||||
|
||||
fn index(&self, index: String) -> &Self::Output {
|
||||
self.index(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<&'a str> for PsbObjectFixed {
|
||||
fn index_mut(&mut self, index: &'a str) -> &mut Self::Output {
|
||||
self.values.entry(index.to_string()).or_insert(NONE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<&'a String> for PsbObjectFixed {
|
||||
fn index_mut(&mut self, index: &'a String) -> &mut Self::Output {
|
||||
self.index_mut(index.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<String> for PsbObjectFixed {
|
||||
fn index_mut(&mut self, index: String) -> &mut Self::Output {
|
||||
self.values.entry(index).or_insert(NONE)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PsbObjectExt {
|
||||
fn to_psb_fixed(self) -> PsbObjectFixed;
|
||||
}
|
||||
|
||||
impl PsbObjectExt for PsbObject {
|
||||
fn to_psb_fixed(self) -> PsbObjectFixed {
|
||||
let mut hash_map = HashMap::new();
|
||||
for (key, value) in self.unwrap() {
|
||||
hash_map.insert(key, PsbValue::to_psb_fixed(value));
|
||||
}
|
||||
PsbObjectFixed { values: hash_map }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ObjectIter<'a> {
|
||||
inner: std::collections::hash_map::Iter<'a, String, PsbValueFixed>,
|
||||
}
|
||||
|
||||
impl<'a> ObjectIter<'a> {
|
||||
pub fn empty() -> Self {
|
||||
ObjectIter {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ObjectIter<'a> {
|
||||
type Item = (&'a String, &'a PsbValueFixed);
|
||||
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
impl<'a> ExactSizeIterator for ObjectIter<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ObjectIterMut<'a> {
|
||||
inner: std::collections::hash_map::IterMut<'a, String, PsbValueFixed>,
|
||||
}
|
||||
|
||||
impl<'a> ObjectIterMut<'a> {
|
||||
pub fn empty() -> Self {
|
||||
ObjectIterMut {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ObjectIterMut<'a> {
|
||||
type Item = (&'a String, &'a mut PsbValueFixed);
|
||||
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for ObjectIterMut<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VirtualPsbFixed {
|
||||
header: PsbHeader,
|
||||
resources: Vec<Vec<u8>>,
|
||||
extra: Vec<Vec<u8>>,
|
||||
root: PsbObjectFixed,
|
||||
}
|
||||
|
||||
impl VirtualPsbFixed {
|
||||
pub fn new(
|
||||
header: PsbHeader,
|
||||
resources: Vec<Vec<u8>>,
|
||||
extra: Vec<Vec<u8>>,
|
||||
root: PsbObjectFixed,
|
||||
) -> Self {
|
||||
Self {
|
||||
header,
|
||||
resources,
|
||||
extra,
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn header(&self) -> PsbHeader {
|
||||
self.header
|
||||
}
|
||||
|
||||
pub fn resources(&self) -> &Vec<Vec<u8>> {
|
||||
&self.resources
|
||||
}
|
||||
|
||||
pub fn resources_mut(&mut self) -> &mut Vec<Vec<u8>> {
|
||||
&mut self.resources
|
||||
}
|
||||
|
||||
pub fn extra(&self) -> &Vec<Vec<u8>> {
|
||||
&self.extra
|
||||
}
|
||||
|
||||
pub fn extra_mut(&mut self) -> &mut Vec<Vec<u8>> {
|
||||
&mut self.extra
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &PsbObjectFixed {
|
||||
&self.root
|
||||
}
|
||||
|
||||
pub fn root_mut(&mut self) -> &mut PsbObjectFixed {
|
||||
&mut self.root
|
||||
}
|
||||
|
||||
pub fn set_root(&mut self, root: PsbObjectFixed) {
|
||||
self.root = root;
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (PsbHeader, Vec<Vec<u8>>, Vec<Vec<u8>>, PsbObjectFixed) {
|
||||
(self.header, self.resources, self.extra, self.root)
|
||||
}
|
||||
|
||||
pub fn to_psb(self) -> VirtualPsb {
|
||||
let (header, resources, extra, root) = self.unwrap();
|
||||
VirtualPsb::new(header, resources, extra, root.to_psb())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VirtualPsbExt {
|
||||
fn to_psb_fixed(self) -> VirtualPsbFixed;
|
||||
}
|
||||
|
||||
impl VirtualPsbExt for VirtualPsb {
|
||||
fn to_psb_fixed(self) -> VirtualPsbFixed {
|
||||
let (header, resources, extra, root) = self.unwrap();
|
||||
VirtualPsbFixed::new(header, resources, extra, root.to_psb_fixed())
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,13 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::ext::psb::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::{decode_to_string, encode_string};
|
||||
use anyhow::Result;
|
||||
use emote_psb::types::PsbValue;
|
||||
use emote_psb::types::collection::PsbObject;
|
||||
use emote_psb::{PsbReader, PsbWriter, VirtualPsb};
|
||||
use json::JsonValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use emote_psb::{PsbReader, PsbWriter};
|
||||
use std::collections::HashSet;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::{Read, Seek};
|
||||
use std::path::Path;
|
||||
|
||||
trait JsonExt {
|
||||
fn is_valid_str(&self) -> bool;
|
||||
}
|
||||
|
||||
impl JsonExt for JsonValue {
|
||||
fn is_valid_str(&self) -> bool {
|
||||
self.is_string() || self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScnScriptBuilder {}
|
||||
|
||||
@@ -111,7 +97,7 @@ impl ScriptBuilder for ScnScriptBuilder {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScnScript {
|
||||
psb: VirtualPsb,
|
||||
psb: VirtualPsbFixed,
|
||||
language_index: usize,
|
||||
export_comumode: bool,
|
||||
filename: String,
|
||||
@@ -125,7 +111,7 @@ impl ScnScript {
|
||||
.load()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to load PSB from {}: {:?}", filename, e))?;
|
||||
Ok(Self {
|
||||
psb,
|
||||
psb: psb.to_psb_fixed(),
|
||||
language_index: config.kirikiri_language_index.unwrap_or(0),
|
||||
export_comumode: config.kirikiri_export_comumode,
|
||||
filename: filename.to_string(),
|
||||
@@ -133,40 +119,6 @@ impl ScnScript {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct PsbDataRef<'a> {
|
||||
pub version: u16,
|
||||
pub encryption: u16,
|
||||
pub root: &'a emote_psb::types::collection::PsbObject,
|
||||
}
|
||||
|
||||
impl<'a> PsbDataRef<'a> {
|
||||
pub fn new(psb: &'a VirtualPsb) -> Self {
|
||||
let header = psb.header();
|
||||
Self {
|
||||
version: header.version,
|
||||
encryption: header.encryption,
|
||||
root: psb.root(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PsbData {
|
||||
pub version: u16,
|
||||
pub encryption: u16,
|
||||
pub root: emote_psb::types::collection::PsbObject,
|
||||
}
|
||||
|
||||
impl PsbData {
|
||||
pub fn header(&self) -> emote_psb::header::PsbHeader {
|
||||
emote_psb::header::PsbHeader {
|
||||
version: self.version,
|
||||
encryption: self.encryption,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for ScnScript {
|
||||
fn default_output_script_type(&self) -> OutputScriptType {
|
||||
OutputScriptType::Json
|
||||
@@ -184,10 +136,10 @@ impl Script for ScnScript {
|
||||
let mut messages = Vec::new();
|
||||
let root = self.psb.root();
|
||||
let scenes = root
|
||||
.get_value("scenes".into())
|
||||
.get_value("scenes")
|
||||
.ok_or(anyhow::anyhow!("scenes not found"))?;
|
||||
let scenes = match scenes {
|
||||
PsbValue::List(list) => list,
|
||||
PsbValueFixed::List(list) => list,
|
||||
_ => return Err(anyhow::anyhow!("scenes is not a list")),
|
||||
};
|
||||
let mut comu = if self.export_comumode {
|
||||
@@ -197,26 +149,25 @@ impl Script for ScnScript {
|
||||
};
|
||||
for (i, oscene) in scenes.iter().enumerate() {
|
||||
let scene = match oscene {
|
||||
PsbValue::Object(obj) => obj,
|
||||
PsbValueFixed::Object(obj) => obj,
|
||||
_ => return Err(anyhow::anyhow!("scene at index {} is not an object", i)),
|
||||
};
|
||||
if let Some(PsbValue::List(texts)) = scene.get_value("texts".into()) {
|
||||
if let Some(PsbValueFixed::List(texts)) = scene.get_value("texts") {
|
||||
for (j, text) in texts.iter().enumerate() {
|
||||
if let PsbValue::List(text) = text {
|
||||
if let PsbValueFixed::List(text) = text {
|
||||
let values = text.values();
|
||||
if values.len() <= 1 {
|
||||
continue; // Skip if there are not enough values
|
||||
}
|
||||
let name = &values[0];
|
||||
let name = match name {
|
||||
PsbValue::String(s) => Some(s),
|
||||
PsbValue::Null => None,
|
||||
PsbValue::None => None,
|
||||
PsbValueFixed::String(s) => Some(s),
|
||||
PsbValueFixed::Null => None,
|
||||
_ => return Err(anyhow::anyhow!("name is not a string or null")),
|
||||
};
|
||||
let mut display_name;
|
||||
let mut message;
|
||||
if matches!(values[1], PsbValue::List(_)) {
|
||||
if matches!(values[1], PsbValueFixed::List(_)) {
|
||||
display_name = None;
|
||||
message = &values[1];
|
||||
} else {
|
||||
@@ -224,9 +175,8 @@ impl Script for ScnScript {
|
||||
continue; // Skip if there is no message
|
||||
}
|
||||
display_name = match &values[1] {
|
||||
PsbValue::String(s) => Some(s),
|
||||
PsbValue::Null => None,
|
||||
PsbValue::None => None,
|
||||
PsbValueFixed::String(s) => Some(s),
|
||||
PsbValueFixed::Null => None,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"display name is not a string or null at {i},{j}"
|
||||
@@ -235,19 +185,18 @@ impl Script for ScnScript {
|
||||
};
|
||||
message = &values[2];
|
||||
}
|
||||
if matches!(message, PsbValue::List(_)) {
|
||||
if matches!(message, PsbValueFixed::List(_)) {
|
||||
let tmp = message;
|
||||
if let PsbValue::List(list) = tmp {
|
||||
if let PsbValueFixed::List(list) = tmp {
|
||||
if list.len() > self.language_index {
|
||||
if let PsbValue::List(data) =
|
||||
if let PsbValueFixed::List(data) =
|
||||
&list.values()[self.language_index]
|
||||
{
|
||||
if data.len() >= 2 {
|
||||
let data = data.values();
|
||||
display_name = match &data[0] {
|
||||
PsbValue::String(s) => Some(s),
|
||||
PsbValue::Null => None,
|
||||
PsbValue::None => None,
|
||||
PsbValueFixed::String(s) => Some(s),
|
||||
PsbValueFixed::Null => None,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"display name is not a string or null at {i},{j}"
|
||||
@@ -260,7 +209,7 @@ impl Script for ScnScript {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let PsbValue::String(message) = message {
|
||||
if let PsbValueFixed::String(message) = message {
|
||||
match name {
|
||||
Some(name) => {
|
||||
let name = match display_name {
|
||||
@@ -285,19 +234,17 @@ impl Script for ScnScript {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(PsbValue::List(selects)) = scene.get_value("selects".into()) {
|
||||
if let Some(PsbValueFixed::List(selects)) = scene.get_value("selects") {
|
||||
for select in selects.iter() {
|
||||
if let PsbValue::Object(select) = select {
|
||||
if let PsbValueFixed::Object(select) = select {
|
||||
let mut text = None;
|
||||
if let Some(PsbValue::List(language)) = select.get_value("language".into())
|
||||
{
|
||||
if let Some(PsbValueFixed::List(language)) = select.get_value("language") {
|
||||
if language.len() > self.language_index {
|
||||
let v = &language.values()[self.language_index];
|
||||
if let PsbValue::Object(v) = v {
|
||||
text = match v.get_value("text".into()) {
|
||||
Some(PsbValue::String(s)) => Some(s),
|
||||
Some(PsbValue::Null) => None,
|
||||
Some(PsbValue::None) => None,
|
||||
if let PsbValueFixed::Object(v) = v {
|
||||
text = match v.get_value("text") {
|
||||
Some(PsbValueFixed::String(s)) => Some(s),
|
||||
Some(PsbValueFixed::Null) => None,
|
||||
None => None,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
@@ -309,10 +256,9 @@ impl Script for ScnScript {
|
||||
}
|
||||
}
|
||||
if text.is_none() {
|
||||
text = match select.get_value("text".into()) {
|
||||
Some(PsbValue::String(s)) => Some(s),
|
||||
Some(PsbValue::Null) => None,
|
||||
Some(PsbValue::None) => None,
|
||||
text = match select.get_value("text") {
|
||||
Some(PsbValueFixed::String(s)) => Some(s),
|
||||
Some(PsbValueFixed::Null) => None,
|
||||
None => None,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
@@ -372,11 +318,10 @@ impl Script for ScnScript {
|
||||
) -> Result<()> {
|
||||
let mut mes = messages.iter();
|
||||
let mut cur_mes = mes.next();
|
||||
// We use json library to process the PSB data, because emote-psb does not support update data.
|
||||
let t = serde_json::to_string(&self.psb.root())?;
|
||||
let mut root = json::parse(&t)?;
|
||||
let mut psb = self.psb.clone();
|
||||
let root = psb.root_mut();
|
||||
let scenes = &mut root["scenes"];
|
||||
if !scenes.is_array() {
|
||||
if !scenes.is_list() {
|
||||
return Err(anyhow::anyhow!("scenes is not an array"));
|
||||
}
|
||||
for (i, scene) in scenes.members_mut().enumerate() {
|
||||
@@ -384,19 +329,19 @@ impl Script for ScnScript {
|
||||
return Err(anyhow::anyhow!("scene at {} is not an object", i));
|
||||
}
|
||||
for text in scene["texts"].members_mut() {
|
||||
if text.is_array() {
|
||||
if text.is_list() {
|
||||
if text.len() <= 1 {
|
||||
continue; // Skip if there are not enough values
|
||||
}
|
||||
if cur_mes.is_none() {
|
||||
cur_mes = mes.next();
|
||||
}
|
||||
if !text[0].is_valid_str() {
|
||||
if !text[0].is_string_or_null() {
|
||||
return Err(anyhow::anyhow!("name is not a string or null"));
|
||||
}
|
||||
let has_name = text[0].is_string();
|
||||
let mut has_display_name;
|
||||
if text[1].is_array() {
|
||||
if text[1].is_list() {
|
||||
if text[1].is_string() {
|
||||
let m = match cur_mes.take() {
|
||||
Some(m) => m,
|
||||
@@ -412,7 +357,7 @@ impl Script for ScnScript {
|
||||
name = name.replace(key, value);
|
||||
}
|
||||
}
|
||||
text[0] = json::JsonValue::String(name);
|
||||
text[0].set_string(name);
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Name is missing for message."));
|
||||
}
|
||||
@@ -423,13 +368,13 @@ impl Script for ScnScript {
|
||||
message = message.replace(key, value);
|
||||
}
|
||||
}
|
||||
text[1] = json::JsonValue::String(message.replace("\n", "\\n"));
|
||||
} else if text[1].is_array() {
|
||||
text[1].set_string(message.replace("\n", "\\n"));
|
||||
} else if text[1].is_list() {
|
||||
if text[1].len() > self.language_index
|
||||
&& text[1][self.language_index].is_array()
|
||||
&& text[1][self.language_index].is_list()
|
||||
&& text[1][self.language_index].len() >= 2
|
||||
{
|
||||
if !text[1][self.language_index][0].is_valid_str() {
|
||||
if !text[1][self.language_index][0].is_string_or_null() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"display name is not a string or null"
|
||||
));
|
||||
@@ -451,10 +396,9 @@ impl Script for ScnScript {
|
||||
}
|
||||
}
|
||||
if has_display_name {
|
||||
text[1][self.language_index][0] =
|
||||
json::JsonValue::String(name);
|
||||
text[1][self.language_index][0].set_string(name);
|
||||
} else {
|
||||
text[0] = json::JsonValue::String(name);
|
||||
text[0].set_string(name);
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
@@ -468,8 +412,8 @@ impl Script for ScnScript {
|
||||
message = message.replace(key, value);
|
||||
}
|
||||
}
|
||||
text[1][self.language_index][1] =
|
||||
json::JsonValue::String(message.replace("\n", "\\n"));
|
||||
text[1][self.language_index][1]
|
||||
.set_string(message.replace("\n", "\\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,7 +421,7 @@ impl Script for ScnScript {
|
||||
if text.len() <= 2 {
|
||||
continue; // Skip if there is no message
|
||||
}
|
||||
if !text[1].is_valid_str() {
|
||||
if !text[1].is_string_or_null() {
|
||||
return Err(anyhow::anyhow!("display name is not a string or null"));
|
||||
}
|
||||
has_display_name = text[1].is_string();
|
||||
@@ -497,9 +441,9 @@ impl Script for ScnScript {
|
||||
}
|
||||
}
|
||||
if has_display_name {
|
||||
text[1] = json::JsonValue::String(name);
|
||||
text[1].set_string(name);
|
||||
} else {
|
||||
text[0] = json::JsonValue::String(name);
|
||||
text[0].set_string(name);
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Name is missing for message."));
|
||||
@@ -511,13 +455,13 @@ impl Script for ScnScript {
|
||||
message = message.replace(key, value);
|
||||
}
|
||||
}
|
||||
text[2] = json::JsonValue::String(message.replace("\n", "\\n"));
|
||||
} else if text[2].is_array() {
|
||||
text[2].set_string(message.replace("\n", "\\n"));
|
||||
} else if text[2].is_list() {
|
||||
if text[2].len() > self.language_index
|
||||
&& text[2][self.language_index].is_array()
|
||||
&& text[2][self.language_index].is_list()
|
||||
&& text[2][self.language_index].len() >= 2
|
||||
{
|
||||
if !text[2][self.language_index][0].is_valid_str() {
|
||||
if !text[2][self.language_index][0].is_string_or_null() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"display name is not a string or null"
|
||||
));
|
||||
@@ -539,10 +483,9 @@ impl Script for ScnScript {
|
||||
}
|
||||
}
|
||||
if has_display_name {
|
||||
text[2][self.language_index][0] =
|
||||
json::JsonValue::String(name);
|
||||
text[2][self.language_index][0].set_string(name);
|
||||
} else {
|
||||
text[0] = json::JsonValue::String(name);
|
||||
text[0].set_string(name);
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
@@ -556,8 +499,8 @@ impl Script for ScnScript {
|
||||
message = message.replace(key, value);
|
||||
}
|
||||
}
|
||||
text[2][self.language_index][1] =
|
||||
json::JsonValue::String(message.replace("\n", "\\n"));
|
||||
text[2][self.language_index][1]
|
||||
.set_string(message.replace("\n", "\\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,61 +512,11 @@ impl Script for ScnScript {
|
||||
if cur_mes.is_some() || mes.next().is_some() {
|
||||
return Err(anyhow::anyhow!("Some messages were not processed."));
|
||||
}
|
||||
let s = json::stringify(root);
|
||||
let obj = serde_json::from_str::<PsbObject>(&s)?;
|
||||
let oheader = self.psb.header();
|
||||
let header = emote_psb::header::PsbHeader {
|
||||
version: oheader.version,
|
||||
encryption: oheader.encryption,
|
||||
};
|
||||
let psb = VirtualPsb::new(header, Vec::new(), Vec::new(), obj);
|
||||
let psb = psb.to_psb();
|
||||
let writer = PsbWriter::new(psb, file);
|
||||
writer
|
||||
.finish()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to write PSB: {:?}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn custom_output_extension(&self) -> &'static str {
|
||||
"json"
|
||||
}
|
||||
|
||||
fn custom_export(&self, filename: &Path, encoding: Encoding) -> Result<()> {
|
||||
if !self.psb.resources().is_empty() {
|
||||
eprintln!(
|
||||
"Warning: The PSB contains resources, which may not be fully represented in the JSON output."
|
||||
);
|
||||
crate::COUNTER.inc_warning();
|
||||
}
|
||||
if !self.psb.extra().is_empty() {
|
||||
eprintln!(
|
||||
"Warning: The PSB contains extra data, which may not be fully represented in the JSON output."
|
||||
);
|
||||
crate::COUNTER.inc_warning();
|
||||
}
|
||||
let psb_data = PsbDataRef::new(&self.psb);
|
||||
let str = serde_json::to_string_pretty(&psb_data)?;
|
||||
let s = encode_string(encoding, &str, false)?;
|
||||
let mut f = std::fs::File::create(filename)?;
|
||||
f.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<()> {
|
||||
let data = crate::utils::files::read_file(custom_filename)?;
|
||||
let s = decode_to_string(encoding, &data)?;
|
||||
let psb_data: PsbData = serde_json::from_str(&s)?;
|
||||
let psb = VirtualPsb::new(psb_data.header(), Vec::new(), Vec::new(), psb_data.root);
|
||||
let writer = PsbWriter::new(psb, file);
|
||||
writer
|
||||
.finish()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to write PSB: {:?}", e))?;
|
||||
writer.finish().map_err(|e| {
|
||||
anyhow::anyhow!("Failed to write PSB to file {}: {:?}", self.filename, e)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -640,16 +533,15 @@ impl ExportComuMes {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export(&mut self, value: &PsbValue) {
|
||||
pub fn export(&mut self, value: &PsbValueFixed) {
|
||||
match value {
|
||||
PsbValue::Object(obj) => {
|
||||
PsbValueFixed::Object(obj) => {
|
||||
for (k, v) in obj.iter() {
|
||||
if k == "comumode" {
|
||||
if let PsbValue::List(list) = v {
|
||||
if let PsbValueFixed::List(list) = v {
|
||||
for item in list.iter() {
|
||||
if let PsbValue::Object(obj) = item {
|
||||
if let Some(PsbValue::String(s)) = obj.get_value("text".into())
|
||||
{
|
||||
if let PsbValueFixed::Object(obj) = item {
|
||||
if let Some(PsbValueFixed::String(s)) = obj.get_value("text") {
|
||||
self.messages.insert(s.string().replace("\\n", "\n"));
|
||||
}
|
||||
}
|
||||
@@ -660,15 +552,15 @@ impl ExportComuMes {
|
||||
}
|
||||
}
|
||||
}
|
||||
PsbValue::List(list) => {
|
||||
PsbValueFixed::List(list) => {
|
||||
let list = list.values();
|
||||
if list.len() > 1 {
|
||||
if let PsbValue::String(s) = &list[0] {
|
||||
if let PsbValueFixed::String(s) = &list[0] {
|
||||
if s.string() == "comumode" {
|
||||
for i in 1..list.len() {
|
||||
if let PsbValue::String(s) = &list[i - 1] {
|
||||
if let PsbValueFixed::String(s) = &list[i - 1] {
|
||||
if s.string() == "text" {
|
||||
if let PsbValue::String(text) = &list[i] {
|
||||
if let PsbValueFixed::String(text) = &list[i] {
|
||||
self.messages
|
||||
.insert(text.string().replace("\\n", "\n"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user