msg_tool\ext/
psb.rs

1//!Extensions for emote_psb crate.
2use crate::ext::io::*;
3use adler::Adler32;
4use anyhow::Result;
5use emote_psb::PsbError;
6use emote_psb::PsbFile;
7use emote_psb::PsbReader;
8use emote_psb::PsbRefs;
9use emote_psb::PsbWriter;
10use emote_psb::VirtualPsb;
11use emote_psb::header::PsbHeader;
12use emote_psb::offsets::{PsbOffsets, PsbResourcesOffset, PsbStringOffset};
13use emote_psb::reader::MdfReader;
14use emote_psb::types::collection::*;
15use emote_psb::types::number::*;
16use emote_psb::types::reference::*;
17use emote_psb::types::string::*;
18use emote_psb::types::*;
19#[cfg(feature = "json")]
20use json::JsonValue;
21#[cfg(feature = "json")]
22use json::number::Number;
23use serde::ser::SerializeStruct;
24use serde::{Deserialize, Serialize};
25use std::cmp::PartialEq;
26use std::collections::{BTreeMap, HashMap};
27use std::io::{Read, Seek, SeekFrom, Write};
28use std::ops::{Index, IndexMut};
29
30#[cfg(feature = "json")]
31fn f32_to_number(x: f32) -> Number {
32    if !x.is_finite() {
33        return Number::from_parts(true, 0, 0);
34    }
35
36    let s = format!("{}", x);
37
38    if s.contains('e') || s.contains('E') {
39        let value = x as f64;
40        return if value >= 0.0 {
41            Number::from(value)
42        } else {
43            Number::from(value)
44        };
45    }
46
47    let positive = !s.starts_with('-');
48    let s = s.trim_start_matches('-');
49
50    let parts: Vec<&str> = s.split('.').collect();
51
52    if parts.len() == 1 {
53        let mantissa: u64 = parts[0].parse().unwrap_or(0);
54        Number::from_parts(positive, mantissa, 0)
55    } else {
56        let int_part = parts[0];
57        let frac_part = parts[1].trim_end_matches('0'); // 去除尾部的0
58
59        if frac_part.is_empty() {
60            // 没有实际小数部分
61            let mantissa: u64 = int_part.parse().unwrap_or(0);
62            Number::from_parts(positive, mantissa, 0)
63        } else {
64            let combined = format!("{}{}", int_part, frac_part);
65            let mantissa: u64 = combined.parse().unwrap_or(0);
66            let exponent: i16 = -(frac_part.len() as i16);
67
68            Number::from_parts(positive, mantissa, exponent)
69        }
70    }
71}
72
73const NONE: PsbValueFixed = PsbValueFixed::None;
74
75#[derive(Debug, Serialize, Deserialize)]
76/// Represents of a PSB value.
77pub enum PsbValueFixed {
78    /// No value.
79    None,
80    /// Represents a null value.
81    Null,
82    /// Represents a boolean value.
83    Bool(bool),
84    /// Represents a number value.
85    Number(PsbNumber),
86    /// Represents an array of integers.
87    IntArray(PsbUintArray),
88    /// Represents a string value.
89    String(PsbString),
90    /// Represents a list of PSB values.
91    List(PsbListFixed),
92    /// Represents an object with key-value pairs.
93    Object(PsbObjectFixed),
94    /// Represents a resource reference.
95    Resource(PsbResourceRef),
96    /// Represents an extra resource reference.
97    ExtraResource(PsbExtraRef),
98    /// Represents a compiler number.
99    CompilerNumber,
100    /// Represents a compiler string.
101    CompilerString,
102    /// Represents a compiler resource.
103    CompilerResource,
104    /// Represents a compiler decimal.
105    CompilerDecimal,
106    /// Represents a compiler array.
107    CompilerArray,
108    /// Represents a compiler boolean.
109    CompilerBool,
110    /// Represents a compiler binary tree.
111    CompilerBinaryTree,
112}
113
114impl From<String> for PsbValueFixed {
115    fn from(value: String) -> Self {
116        PsbValueFixed::String(PsbString::from(value))
117    }
118}
119
120impl From<bool> for PsbValueFixed {
121    fn from(value: bool) -> Self {
122        PsbValueFixed::Bool(value)
123    }
124}
125
126impl From<i64> for PsbValueFixed {
127    fn from(value: i64) -> Self {
128        PsbValueFixed::Number(PsbNumber::Integer(value))
129    }
130}
131
132impl From<f64> for PsbValueFixed {
133    fn from(value: f64) -> Self {
134        PsbValueFixed::Number(PsbNumber::Double(value))
135    }
136}
137
138impl From<f32> for PsbValueFixed {
139    fn from(value: f32) -> Self {
140        PsbValueFixed::Number(PsbNumber::Float(value))
141    }
142}
143
144impl From<PsbObjectFixed> for PsbValueFixed {
145    fn from(value: PsbObjectFixed) -> Self {
146        PsbValueFixed::Object(value)
147    }
148}
149
150impl From<PsbListFixed> for PsbValueFixed {
151    fn from(value: PsbListFixed) -> Self {
152        PsbValueFixed::List(value)
153    }
154}
155
156impl From<&[PsbValueFixed]> for PsbValueFixed {
157    fn from(value: &[PsbValueFixed]) -> Self {
158        PsbValueFixed::List(PsbListFixed {
159            values: value.to_vec(),
160        })
161    }
162}
163
164impl PsbValueFixed {
165    /// Converts this value to original PSB value type.
166    pub fn to_psb(self, warn_on_none: bool) -> PsbValue {
167        match self {
168            PsbValueFixed::None => {
169                if warn_on_none {
170                    eprintln!("Warning: PSB value is None, output script may broken.");
171                    crate::COUNTER.inc_warning();
172                }
173                PsbValue::None
174            }
175            PsbValueFixed::Null => PsbValue::Null,
176            PsbValueFixed::Bool(b) => PsbValue::Bool(b),
177            PsbValueFixed::Number(n) => PsbValue::Number(n),
178            PsbValueFixed::IntArray(arr) => PsbValue::IntArray(arr),
179            PsbValueFixed::String(s) => PsbValue::String(s),
180            PsbValueFixed::List(l) => PsbValue::List(l.to_psb(warn_on_none)),
181            PsbValueFixed::Object(o) => PsbValue::Object(o.to_psb(warn_on_none)),
182            PsbValueFixed::Resource(r) => PsbValue::Resource(r),
183            PsbValueFixed::ExtraResource(er) => PsbValue::ExtraResource(er),
184            PsbValueFixed::CompilerNumber => PsbValue::CompilerNumber,
185            PsbValueFixed::CompilerString => PsbValue::CompilerString,
186            PsbValueFixed::CompilerResource => PsbValue::CompilerResource,
187            PsbValueFixed::CompilerDecimal => PsbValue::CompilerDecimal,
188            PsbValueFixed::CompilerArray => PsbValue::CompilerArray,
189            PsbValueFixed::CompilerBool => PsbValue::CompilerBool,
190            PsbValueFixed::CompilerBinaryTree => PsbValue::CompilerBinaryTree,
191        }
192    }
193
194    /// Returns true if this value is a list.
195    pub fn is_list(&self) -> bool {
196        matches!(self, PsbValueFixed::List(_))
197    }
198
199    /// Returns true if this value is an object.
200    pub fn is_object(&self) -> bool {
201        matches!(self, PsbValueFixed::Object(_))
202    }
203
204    /// Returns true if this value is a string or null.
205    pub fn is_string_or_null(&self) -> bool {
206        self.is_string() || self.is_null()
207    }
208
209    /// Returns true if this value is a string.
210    pub fn is_string(&self) -> bool {
211        matches!(self, PsbValueFixed::String(_))
212    }
213
214    /// Returns true if this value is none.
215    pub fn is_none(&self) -> bool {
216        matches!(self, PsbValueFixed::None)
217    }
218
219    /// Returns true if this value is null.
220    pub fn is_null(&self) -> bool {
221        matches!(self, PsbValueFixed::Null)
222    }
223
224    /// Find the resource's key in object
225    pub fn find_resource_key<'a>(
226        &'a self,
227        resource_id: u64,
228        now: Vec<&'a str>,
229    ) -> Option<Vec<&'a str>> {
230        match self {
231            PsbValueFixed::List(l) => l.find_resource_key(resource_id, now),
232            PsbValueFixed::Object(o) => o.find_resource_key(resource_id, now),
233            _ => None,
234        }
235    }
236
237    /// Find the extra resource's key in object
238    pub fn find_extra_resource_key<'a>(
239        &'a self,
240        extra_resource_id: u64,
241        now: Vec<&'a str>,
242    ) -> Option<Vec<&'a str>> {
243        match self {
244            PsbValueFixed::List(l) => l.find_extra_resource_key(extra_resource_id, now),
245            PsbValueFixed::Object(o) => o.find_extra_resource_key(extra_resource_id, now),
246            _ => None,
247        }
248    }
249
250    /// Sets the value of this PSB value to a new integer.
251    pub fn set_i64(&mut self, value: i64) {
252        *self = PsbValueFixed::Number(PsbNumber::Integer(value));
253    }
254
255    /// Sets the value of this PSB value to a new object
256    pub fn set_obj(&mut self, value: PsbObjectFixed) {
257        *self = PsbValueFixed::Object(value);
258    }
259
260    /// Sets the value of this PSB value to a new string.
261    pub fn set_str(&mut self, value: &str) {
262        match self {
263            PsbValueFixed::String(s) => {
264                let s = s.string_mut();
265                s.clear();
266                s.push_str(value);
267            }
268            _ => {
269                *self = PsbValueFixed::String(PsbString::from(value.to_owned()));
270            }
271        }
272    }
273
274    /// Sets the value of this PSB value to a new string.
275    pub fn set_string(&mut self, value: String) {
276        self.set_str(&value);
277    }
278
279    /// Returns the value as a boolean, if it is a boolean.
280    pub fn as_u8(&self) -> Option<u8> {
281        self.as_i64().map(|n| n.try_into().ok()).flatten()
282    }
283
284    /// Returns the value as a [u32], if it is a number.
285    pub fn as_u32(&self) -> Option<u32> {
286        self.as_i64().map(|n| n as u32)
287    }
288
289    /// Returns the value as a [i64], if it is a number.
290    pub fn as_i64(&self) -> Option<i64> {
291        match self {
292            PsbValueFixed::Number(n) => match n {
293                PsbNumber::Integer(n) => Some(*n),
294                PsbNumber::Double(n) if n.fract() == 0.0 => Some(*n as i64),
295                PsbNumber::Float(n) if n.fract() == 0.0 => Some(*n as i64),
296                _ => None,
297            },
298            _ => None,
299        }
300    }
301
302    /// Returns the value as a string, if it is a string.
303    pub fn as_str(&self) -> Option<&str> {
304        match self {
305            PsbValueFixed::String(s) => Some(s.string()),
306            _ => None,
307        }
308    }
309
310    /// Returns the lengtho of a list or object.
311    pub fn len(&self) -> usize {
312        match self {
313            PsbValueFixed::List(l) => l.len(),
314            PsbValueFixed::Object(o) => o.values.len(),
315            _ => 0,
316        }
317    }
318
319    /// Returns a iterator over the entries of an object.
320    pub fn entries(&self) -> ObjectIter<'_> {
321        match self {
322            PsbValueFixed::Object(o) => o.iter(),
323            _ => ObjectIter::empty(),
324        }
325    }
326
327    /// Returns a mutable iterator over the entries of an object.
328    pub fn entries_mut(&mut self) -> ObjectIterMut<'_> {
329        match self {
330            PsbValueFixed::Object(o) => o.iter_mut(),
331            _ => ObjectIterMut::empty(),
332        }
333    }
334
335    /// Returns a iterator over the members of a list.
336    pub fn members(&self) -> ListIter<'_> {
337        match self {
338            PsbValueFixed::List(l) => l.iter(),
339            _ => ListIter::empty(),
340        }
341    }
342
343    /// Returns a mutable iterator over the members of a list.
344    pub fn members_mut(&mut self) -> ListIterMut<'_> {
345        match self {
346            PsbValueFixed::List(l) => l.iter_mut(),
347            _ => ListIterMut::empty(),
348        }
349    }
350
351    /// Pushes a new member to a list. If this value is not a list, it will be converted to a list.
352    pub fn push_member<T: Into<PsbValueFixed>>(&mut self, value: T) {
353        match self {
354            PsbValueFixed::List(l) => {
355                l.values.push(value.into());
356            }
357            _ => {
358                *self = PsbValueFixed::List(PsbListFixed {
359                    values: vec![value.into()],
360                });
361            }
362        }
363    }
364
365    /// Clears all members in a list. If this value is not a list, it will be converted to an empty list.
366    pub fn clear_members(&mut self) {
367        match self {
368            PsbValueFixed::List(l) => {
369                l.clear();
370            }
371            _ => {
372                *self = PsbValueFixed::List(PsbListFixed { values: vec![] });
373            }
374        }
375    }
376
377    /// Inserts a new member at the specified index in a list. If this value is not a list, it will be converted to a list.
378    /// If the index is out of bounds, the value will be appended to the end of the list.
379    pub fn insert_member<T: Into<PsbValueFixed>>(&mut self, index: usize, value: T) {
380        match self {
381            PsbValueFixed::List(l) => {
382                l.insert(index, value);
383            }
384            _ => {
385                *self = PsbValueFixed::List(PsbListFixed {
386                    values: vec![value.into()],
387                });
388            }
389        }
390    }
391
392    /// Returns the resource ID if this value is a resource reference.
393    pub fn resource_id(&self) -> Option<u64> {
394        match self {
395            PsbValueFixed::Resource(r) => Some(r.resource_ref),
396            _ => None,
397        }
398    }
399
400    /// Returns the extra resource ID if this value is an extra resource reference.
401    pub fn extra_resource_id(&self) -> Option<u64> {
402        match self {
403            PsbValueFixed::ExtraResource(er) => Some(er.extra_resource_ref),
404            _ => None,
405        }
406    }
407
408    /// Converts this value to a JSON value, if possible.
409    #[cfg(feature = "json")]
410    pub fn to_json(&self) -> Option<JsonValue> {
411        match self {
412            PsbValueFixed::Null => Some(JsonValue::Null),
413            PsbValueFixed::Bool(b) => Some(JsonValue::Boolean(*b)),
414            PsbValueFixed::Number(n) => match n {
415                PsbNumber::Integer(i) => Some(JsonValue::Number((*i).into())),
416                PsbNumber::Float(f) => Some(JsonValue::Number(f32_to_number(*f))),
417                PsbNumber::Double(d) => Some(JsonValue::Number((*d).into())),
418            },
419            PsbValueFixed::String(s) => Some(JsonValue::String(s.string().to_owned())),
420            PsbValueFixed::Resource(s) => {
421                Some(JsonValue::String(format!("#resource#{}", s.resource_ref)))
422            }
423            PsbValueFixed::ExtraResource(s) => Some(JsonValue::String(format!(
424                "#resource@{}",
425                s.extra_resource_ref
426            ))),
427            PsbValueFixed::IntArray(arr) => Some(JsonValue::Array(
428                arr.iter().map(|n| JsonValue::Number((*n).into())).collect(),
429            )),
430            PsbValueFixed::List(l) => Some(l.to_json()),
431            PsbValueFixed::Object(o) => Some(o.to_json()),
432            _ => None,
433        }
434    }
435
436    /// Converts a JSON value to a PSB value.
437    #[cfg(feature = "json")]
438    pub fn from_json(obj: &JsonValue) -> Self {
439        match obj {
440            JsonValue::Null => PsbValueFixed::Null,
441            JsonValue::Boolean(b) => PsbValueFixed::Bool(*b),
442            JsonValue::Number(n) => {
443                let data: f64 = (*n).into();
444                if data.fract() == 0.0 {
445                    PsbValueFixed::Number(PsbNumber::Integer(data as i64))
446                } else {
447                    PsbValueFixed::Number(PsbNumber::Float(data as f32))
448                }
449            }
450            JsonValue::String(s) => {
451                if s.starts_with("#resource#") {
452                    if let Ok(id) = s[10..].parse::<u64>() {
453                        return PsbValueFixed::Resource(PsbResourceRef { resource_ref: id });
454                    }
455                } else if s.starts_with("#resource@") {
456                    if let Ok(id) = s[10..].parse::<u64>() {
457                        return PsbValueFixed::ExtraResource(PsbExtraRef {
458                            extra_resource_ref: id,
459                        });
460                    }
461                }
462                PsbValueFixed::String(PsbString::from(s.clone()))
463            }
464            JsonValue::Array(arr) => {
465                let values: Vec<PsbValueFixed> = arr.iter().map(PsbValueFixed::from_json).collect();
466                PsbValueFixed::List(PsbListFixed { values })
467            }
468            JsonValue::Object(obj) => {
469                let mut values = BTreeMap::new();
470                for (key, value) in obj.iter() {
471                    values.insert(key.to_owned(), PsbValueFixed::from_json(value));
472                }
473                PsbValueFixed::Object(PsbObjectFixed { values })
474            }
475            JsonValue::Short(n) => {
476                let s = n.as_str();
477                if s.starts_with("#resource#") {
478                    if let Ok(id) = s[10..].parse::<u64>() {
479                        return PsbValueFixed::Resource(PsbResourceRef { resource_ref: id });
480                    }
481                } else if s.starts_with("#resource@") {
482                    if let Ok(id) = s[10..].parse::<u64>() {
483                        return PsbValueFixed::ExtraResource(PsbExtraRef {
484                            extra_resource_ref: id,
485                        });
486                    }
487                }
488                PsbValueFixed::String(PsbString::from(s.to_owned()))
489            }
490        }
491    }
492}
493
494impl Index<usize> for PsbValueFixed {
495    type Output = PsbValueFixed;
496
497    fn index(&self, index: usize) -> &Self::Output {
498        match self {
499            PsbValueFixed::List(l) => &l[index],
500            _ => &NONE,
501        }
502    }
503}
504
505impl IndexMut<usize> for PsbValueFixed {
506    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
507        match self {
508            PsbValueFixed::List(l) => {
509                if index < l.values.len() {
510                    &mut l.values[index]
511                } else {
512                    l.values.push(NONE);
513                    l.values.last_mut().unwrap()
514                }
515            }
516            _ => {
517                *self = PsbValueFixed::List(PsbListFixed { values: vec![NONE] });
518                self.index_mut(0)
519            }
520        }
521    }
522}
523
524impl<'a> Index<&'a str> for PsbValueFixed {
525    type Output = PsbValueFixed;
526
527    fn index(&self, index: &'a str) -> &Self::Output {
528        match self {
529            PsbValueFixed::Object(o) => &o[index],
530            _ => &NONE,
531        }
532    }
533}
534
535impl<'a> Index<&'a String> for PsbValueFixed {
536    type Output = PsbValueFixed;
537
538    fn index(&self, index: &'a String) -> &Self::Output {
539        self.index(index.as_str())
540    }
541}
542
543impl Index<String> for PsbValueFixed {
544    type Output = PsbValueFixed;
545
546    fn index(&self, index: String) -> &Self::Output {
547        self.index(index.as_str())
548    }
549}
550
551impl IndexMut<&str> for PsbValueFixed {
552    fn index_mut(&mut self, index: &str) -> &mut Self::Output {
553        match self {
554            PsbValueFixed::Object(o) => o.index_mut(index),
555            _ => {
556                *self = PsbValueFixed::Object(PsbObjectFixed {
557                    values: BTreeMap::new(),
558                });
559                self.index_mut(index)
560            }
561        }
562    }
563}
564
565impl IndexMut<&String> for PsbValueFixed {
566    fn index_mut(&mut self, index: &String) -> &mut Self::Output {
567        self.index_mut(index.as_str())
568    }
569}
570
571impl IndexMut<String> for PsbValueFixed {
572    fn index_mut(&mut self, index: String) -> &mut Self::Output {
573        self.index_mut(index.as_str())
574    }
575}
576
577impl Clone for PsbValueFixed {
578    fn clone(&self) -> Self {
579        match self {
580            PsbValueFixed::None => PsbValueFixed::None,
581            PsbValueFixed::Null => PsbValueFixed::Null,
582            PsbValueFixed::Bool(b) => PsbValueFixed::Bool(*b),
583            PsbValueFixed::Number(n) => PsbValueFixed::Number(n.clone()),
584            PsbValueFixed::IntArray(arr) => PsbValueFixed::IntArray(arr.clone()),
585            PsbValueFixed::String(s) => PsbValueFixed::String(PsbString::from(s.string().clone())),
586            PsbValueFixed::List(l) => PsbValueFixed::List(l.clone()),
587            PsbValueFixed::Object(o) => PsbValueFixed::Object(o.clone()),
588            PsbValueFixed::Resource(r) => PsbValueFixed::Resource(r.clone()),
589            PsbValueFixed::ExtraResource(er) => PsbValueFixed::ExtraResource(er.clone()),
590            PsbValueFixed::CompilerNumber => PsbValueFixed::CompilerNumber,
591            PsbValueFixed::CompilerString => PsbValueFixed::CompilerString,
592            PsbValueFixed::CompilerResource => PsbValueFixed::CompilerResource,
593            PsbValueFixed::CompilerDecimal => PsbValueFixed::CompilerDecimal,
594            PsbValueFixed::CompilerArray => PsbValueFixed::CompilerArray,
595            PsbValueFixed::CompilerBool => PsbValueFixed::CompilerBool,
596            PsbValueFixed::CompilerBinaryTree => PsbValueFixed::CompilerBinaryTree,
597        }
598    }
599}
600
601impl PartialEq<String> for PsbValueFixed {
602    fn eq(&self, other: &String) -> bool {
603        self == other.as_str()
604    }
605}
606
607impl PartialEq<str> for PsbValueFixed {
608    fn eq(&self, other: &str) -> bool {
609        match self {
610            PsbValueFixed::String(s) => s.string() == other,
611            _ => false,
612        }
613    }
614}
615
616impl<'a> PartialEq<&'a str> for PsbValueFixed {
617    fn eq(&self, other: &&'a str) -> bool {
618        self == *other
619    }
620}
621
622/// Trait to convert a PSB value to a fixed PSB value.
623pub trait PsbValueExt {
624    /// Converts this PSB value to a fixed PSB value.
625    fn to_psb_fixed(self) -> PsbValueFixed;
626}
627
628impl PsbValueExt for PsbValue {
629    fn to_psb_fixed(self) -> PsbValueFixed {
630        match self {
631            PsbValue::None => PsbValueFixed::None,
632            PsbValue::Null => PsbValueFixed::Null,
633            PsbValue::Bool(b) => PsbValueFixed::Bool(b),
634            PsbValue::Number(n) => PsbValueFixed::Number(n),
635            PsbValue::IntArray(arr) => PsbValueFixed::IntArray(arr),
636            PsbValue::String(s) => PsbValueFixed::String(s),
637            PsbValue::List(l) => PsbValueFixed::List(PsbList::to_psb_fixed(l)),
638            PsbValue::Object(o) => PsbValueFixed::Object(PsbObject::to_psb_fixed(o)),
639            PsbValue::Resource(r) => PsbValueFixed::Resource(r),
640            PsbValue::ExtraResource(er) => PsbValueFixed::ExtraResource(er),
641            PsbValue::CompilerNumber => PsbValueFixed::CompilerNumber,
642            PsbValue::CompilerString => PsbValueFixed::CompilerString,
643            PsbValue::CompilerResource => PsbValueFixed::CompilerResource,
644            PsbValue::CompilerDecimal => PsbValueFixed::CompilerDecimal,
645            PsbValue::CompilerArray => PsbValueFixed::CompilerArray,
646            PsbValue::CompilerBool => PsbValueFixed::CompilerBool,
647            PsbValue::CompilerBinaryTree => PsbValueFixed::CompilerBinaryTree,
648        }
649    }
650}
651
652#[derive(Clone, Debug, Serialize, Deserialize)]
653#[serde(transparent)]
654/// Represents a PSB list of PSB values.
655pub struct PsbListFixed {
656    /// The values in the list.
657    pub values: Vec<PsbValueFixed>,
658}
659
660impl PsbListFixed {
661    pub fn new() -> Self {
662        PsbListFixed { values: vec![] }
663    }
664
665    /// Converts this PSB list to a original PSB list.
666    pub fn to_psb(self, warn_on_none: bool) -> PsbList {
667        let v: Vec<_> = self
668            .values
669            .into_iter()
670            .map(|v| v.to_psb(warn_on_none))
671            .collect();
672        PsbList::from(v)
673    }
674
675    /// Find the resource's key in object
676    pub fn find_resource_key<'a>(
677        &'a self,
678        resource_id: u64,
679        now: Vec<&'a str>,
680    ) -> Option<Vec<&'a str>> {
681        for value in &self.values {
682            if let Some(key) = value.find_resource_key(resource_id, now.clone()) {
683                return Some(key);
684            }
685        }
686        None
687    }
688
689    /// Find the extra resource's key in object
690    pub fn find_extra_resource_key<'a>(
691        &'a self,
692        extra_resource_id: u64,
693        now: Vec<&'a str>,
694    ) -> Option<Vec<&'a str>> {
695        for value in &self.values {
696            if let Some(key) = value.find_extra_resource_key(extra_resource_id, now.clone()) {
697                return Some(key);
698            }
699        }
700        None
701    }
702
703    /// Returns a iterator over the values in the list.
704    pub fn iter(&self) -> ListIter<'_> {
705        ListIter {
706            inner: self.values.iter(),
707        }
708    }
709
710    /// Returns a mutable iterator over the values in the list.
711    pub fn iter_mut(&mut self) -> ListIterMut<'_> {
712        ListIterMut {
713            inner: self.values.iter_mut(),
714        }
715    }
716
717    /// Returns a reference to the values in the list.
718    pub fn values(&self) -> &Vec<PsbValueFixed> {
719        &self.values
720    }
721
722    /// Returns the length of the list.
723    pub fn len(&self) -> usize {
724        self.values.len()
725    }
726
727    /// Clears all values in the list.
728    pub fn clear(&mut self) {
729        self.values.clear();
730    }
731
732    /// Inserts a new value at the specified index in the list.
733    /// If the index is out of bounds, the value will be appended to the end of the list.
734    pub fn insert<V: Into<PsbValueFixed>>(&mut self, index: usize, value: V) {
735        if index <= self.values.len() {
736            self.values.insert(index, value.into());
737        } else {
738            self.values.push(value.into());
739        }
740    }
741
742    /// Converts this PSB list to a JSON value.
743    #[cfg(feature = "json")]
744    pub fn to_json(&self) -> JsonValue {
745        let data: Vec<_> = self.values.iter().filter_map(|v| v.to_json()).collect();
746        JsonValue::Array(data)
747    }
748}
749
750impl Index<usize> for PsbListFixed {
751    type Output = PsbValueFixed;
752
753    fn index(&self, index: usize) -> &Self::Output {
754        self.values.get(index).unwrap_or(&NONE)
755    }
756}
757
758impl IndexMut<usize> for PsbListFixed {
759    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
760        if index < self.values.len() {
761            &mut self.values[index]
762        } else {
763            self.values.push(NONE);
764            self.values.last_mut().unwrap()
765        }
766    }
767}
768
769/// Iterator for a slice of PSB values in a list.
770pub struct ListIter<'a> {
771    inner: std::slice::Iter<'a, PsbValueFixed>,
772}
773
774impl<'a> ListIter<'a> {
775    /// Creates an empty iterator.
776    pub fn empty() -> Self {
777        ListIter {
778            inner: Default::default(),
779        }
780    }
781}
782
783impl<'a> Iterator for ListIter<'a> {
784    type Item = &'a PsbValueFixed;
785
786    #[inline(always)]
787    fn next(&mut self) -> Option<Self::Item> {
788        self.inner.next()
789    }
790}
791
792impl<'a> ExactSizeIterator for ListIter<'a> {
793    fn len(&self) -> usize {
794        self.inner.len()
795    }
796}
797
798impl<'a> DoubleEndedIterator for ListIter<'a> {
799    #[inline(always)]
800    fn next_back(&mut self) -> Option<Self::Item> {
801        self.inner.next_back()
802    }
803}
804
805/// Mutable iterator for a slice of PSB values in a list.
806pub struct ListIterMut<'a> {
807    inner: std::slice::IterMut<'a, PsbValueFixed>,
808}
809
810impl<'a> ListIterMut<'a> {
811    /// Creates an empty mutable iterator.
812    pub fn empty() -> Self {
813        ListIterMut {
814            inner: Default::default(),
815        }
816    }
817}
818
819impl<'a> Iterator for ListIterMut<'a> {
820    type Item = &'a mut PsbValueFixed;
821
822    #[inline(always)]
823    fn next(&mut self) -> Option<Self::Item> {
824        self.inner.next()
825    }
826}
827
828impl<'a> ExactSizeIterator for ListIterMut<'a> {
829    fn len(&self) -> usize {
830        self.inner.len()
831    }
832}
833
834impl<'a> DoubleEndedIterator for ListIterMut<'a> {
835    #[inline(always)]
836    fn next_back(&mut self) -> Option<Self::Item> {
837        self.inner.next_back()
838    }
839}
840
841/// Trait to convert a PSB list to a fixed PSB list.
842pub trait PsbListExt {
843    /// Converts this PSB list to a fixed PSB list.
844    fn to_psb_fixed(self) -> PsbListFixed;
845}
846
847impl PsbListExt for PsbList {
848    fn to_psb_fixed(self) -> PsbListFixed {
849        let values: Vec<_> = self
850            .unwrap()
851            .into_iter()
852            .map(PsbValue::to_psb_fixed)
853            .collect();
854        PsbListFixed { values }
855    }
856}
857
858#[derive(Clone, Debug, Serialize, Deserialize)]
859#[serde(transparent)]
860/// Represents a PSB object with key-value pairs.
861pub struct PsbObjectFixed {
862    /// The key-value pairs in the object.
863    pub values: BTreeMap<String, PsbValueFixed>,
864}
865
866impl PsbObjectFixed {
867    pub fn new() -> Self {
868        Self {
869            values: BTreeMap::new(),
870        }
871    }
872
873    /// Creates a new empty PSB object.
874    pub fn to_psb(self, warn_on_none: bool) -> PsbObject {
875        let mut hash_map = HashMap::new();
876        for (key, value) in self.values {
877            hash_map.insert(key, value.to_psb(warn_on_none));
878        }
879        PsbObject::from(hash_map)
880    }
881
882    /// Gets a reference of value in the object by key.
883    pub fn get_value(&self, key: &str) -> Option<&PsbValueFixed> {
884        self.values.get(key)
885    }
886
887    /// Find the resource's key in object
888    pub fn find_resource_key<'a>(
889        &'a self,
890        resource_id: u64,
891        now: Vec<&'a str>,
892    ) -> Option<Vec<&'a str>> {
893        for (key, value) in &self.values {
894            let mut now = now.clone();
895            now.push(key);
896            if let Some(id) = value.resource_id() {
897                if id == resource_id {
898                    return Some(now);
899                }
900            }
901            if let Some(key) = value.find_resource_key(resource_id, now) {
902                return Some(key);
903            }
904        }
905        None
906    }
907
908    /// Find the extra resource's key in object
909    pub fn find_extra_resource_key<'a>(
910        &'a self,
911        extra_resource_id: u64,
912        now: Vec<&'a str>,
913    ) -> Option<Vec<&'a str>> {
914        for (key, value) in &self.values {
915            let mut now = now.clone();
916            now.push(key);
917            if let Some(id) = value.extra_resource_id() {
918                if id == extra_resource_id {
919                    return Some(now);
920                }
921            }
922            if let Some(key) = value.find_extra_resource_key(extra_resource_id, now) {
923                return Some(key);
924            }
925        }
926        None
927    }
928
929    /// Returns a iterator over the entries of the object.
930    pub fn iter(&self) -> ObjectIter<'_> {
931        ObjectIter {
932            inner: self.values.iter(),
933        }
934    }
935
936    /// Returns a mutable iterator over the entries of the object.
937    pub fn iter_mut(&mut self) -> ObjectIterMut<'_> {
938        ObjectIterMut {
939            inner: self.values.iter_mut(),
940        }
941    }
942
943    /// Converts this PSB object to a JSON value.
944    #[cfg(feature = "json")]
945    pub fn to_json(&self) -> JsonValue {
946        let mut obj = json::object::Object::new();
947        for (key, value) in &self.values {
948            if let Some(json_value) = value.to_json() {
949                obj.insert(key, json_value);
950            }
951        }
952        JsonValue::Object(obj)
953    }
954
955    /// Converts a JSON object to a PSB object.
956    #[cfg(feature = "json")]
957    pub fn from_json(obj: &JsonValue) -> Self {
958        let mut values = BTreeMap::new();
959        for (key, value) in obj.entries() {
960            values.insert(key.to_owned(), PsbValueFixed::from_json(value));
961        }
962        PsbObjectFixed { values }
963    }
964}
965
966impl<'a> Index<&'a str> for PsbObjectFixed {
967    type Output = PsbValueFixed;
968
969    fn index(&self, index: &'a str) -> &Self::Output {
970        self.values.get(index).unwrap_or(&NONE)
971    }
972}
973
974impl<'a> Index<&'a String> for PsbObjectFixed {
975    type Output = PsbValueFixed;
976
977    fn index(&self, index: &'a String) -> &Self::Output {
978        self.index(index.as_str())
979    }
980}
981
982impl Index<String> for PsbObjectFixed {
983    type Output = PsbValueFixed;
984
985    fn index(&self, index: String) -> &Self::Output {
986        self.index(index.as_str())
987    }
988}
989
990impl<'a> IndexMut<&'a str> for PsbObjectFixed {
991    fn index_mut(&mut self, index: &'a str) -> &mut Self::Output {
992        self.values.entry(index.to_string()).or_insert(NONE)
993    }
994}
995
996impl<'a> IndexMut<&'a String> for PsbObjectFixed {
997    fn index_mut(&mut self, index: &'a String) -> &mut Self::Output {
998        self.index_mut(index.as_str())
999    }
1000}
1001
1002impl IndexMut<String> for PsbObjectFixed {
1003    fn index_mut(&mut self, index: String) -> &mut Self::Output {
1004        self.values.entry(index).or_insert(NONE)
1005    }
1006}
1007
1008/// Trait to convert a PSB object to a fixed PSB object.
1009pub trait PsbObjectExt {
1010    /// Converts this PSB object to a fixed PSB object.
1011    fn to_psb_fixed(self) -> PsbObjectFixed;
1012}
1013
1014impl PsbObjectExt for PsbObject {
1015    fn to_psb_fixed(self) -> PsbObjectFixed {
1016        let mut hash_map = BTreeMap::new();
1017        for (key, value) in self.unwrap() {
1018            hash_map.insert(key, PsbValue::to_psb_fixed(value));
1019        }
1020        PsbObjectFixed { values: hash_map }
1021    }
1022}
1023
1024/// Iterator for a slice of PSB values in an object.
1025pub struct ObjectIter<'a> {
1026    inner: std::collections::btree_map::Iter<'a, String, PsbValueFixed>,
1027}
1028
1029impl<'a> ObjectIter<'a> {
1030    /// Creates an empty iterator.
1031    pub fn empty() -> Self {
1032        ObjectIter {
1033            inner: Default::default(),
1034        }
1035    }
1036}
1037
1038impl<'a> Iterator for ObjectIter<'a> {
1039    type Item = (&'a String, &'a PsbValueFixed);
1040
1041    #[inline(always)]
1042    fn next(&mut self) -> Option<Self::Item> {
1043        self.inner.next()
1044    }
1045}
1046
1047impl<'a> ExactSizeIterator for ObjectIter<'a> {
1048    fn len(&self) -> usize {
1049        self.inner.len()
1050    }
1051}
1052
1053impl<'a> DoubleEndedIterator for ObjectIter<'a> {
1054    #[inline(always)]
1055    fn next_back(&mut self) -> Option<Self::Item> {
1056        self.inner.next_back()
1057    }
1058}
1059
1060/// Mutable iterator for a slice of PSB values in an object.
1061pub struct ObjectIterMut<'a> {
1062    inner: std::collections::btree_map::IterMut<'a, String, PsbValueFixed>,
1063}
1064
1065impl<'a> ObjectIterMut<'a> {
1066    /// Creates an empty mutable iterator.
1067    pub fn empty() -> Self {
1068        ObjectIterMut {
1069            inner: Default::default(),
1070        }
1071    }
1072}
1073
1074impl<'a> Iterator for ObjectIterMut<'a> {
1075    type Item = (&'a String, &'a mut PsbValueFixed);
1076
1077    #[inline(always)]
1078    fn next(&mut self) -> Option<Self::Item> {
1079        self.inner.next()
1080    }
1081}
1082
1083impl<'a> ExactSizeIterator for ObjectIterMut<'a> {
1084    fn len(&self) -> usize {
1085        self.inner.len()
1086    }
1087}
1088
1089impl<'a> DoubleEndedIterator for ObjectIterMut<'a> {
1090    #[inline(always)]
1091    fn next_back(&mut self) -> Option<Self::Item> {
1092        self.inner.next_back()
1093    }
1094}
1095
1096/// Represents a fixed version of a virtual PSB.
1097#[derive(Clone, Debug)]
1098pub struct VirtualPsbFixed {
1099    header: PsbHeader,
1100    resources: Vec<Vec<u8>>,
1101    extra: Vec<Vec<u8>>,
1102    root: PsbObjectFixed,
1103}
1104
1105impl Serialize for VirtualPsbFixed {
1106    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1107    where
1108        S: serde::Serializer,
1109    {
1110        let mut state = serializer.serialize_struct("VirtualPsbFixed", 3)?;
1111        state.serialize_field("version", &self.header.version)?;
1112        state.serialize_field("encryption", &self.header.encryption)?;
1113        state.serialize_field("data", &self.root)?;
1114        state.end()
1115    }
1116}
1117
1118#[derive(Deserialize)]
1119pub struct VirtualPsbFixedData {
1120    version: u16,
1121    encryption: u16,
1122    data: PsbObjectFixed,
1123}
1124
1125impl VirtualPsbFixed {
1126    /// Creates a new fixed virtual PSB.
1127    pub fn new(
1128        header: PsbHeader,
1129        resources: Vec<Vec<u8>>,
1130        extra: Vec<Vec<u8>>,
1131        root: PsbObjectFixed,
1132    ) -> Self {
1133        Self {
1134            header,
1135            resources,
1136            extra,
1137            root,
1138        }
1139    }
1140
1141    /// Returns the header of the PSB.
1142    pub fn header(&self) -> PsbHeader {
1143        self.header
1144    }
1145
1146    /// Return a mutable reference to the header of the PSB.
1147    pub fn header_mut(&mut self) -> &mut PsbHeader {
1148        &mut self.header
1149    }
1150
1151    /// Returns a reference to the resources of the PSB.
1152    pub fn resources(&self) -> &Vec<Vec<u8>> {
1153        &self.resources
1154    }
1155
1156    /// Returns a mutable reference to the resources of the PSB.
1157    pub fn resources_mut(&mut self) -> &mut Vec<Vec<u8>> {
1158        &mut self.resources
1159    }
1160
1161    /// Returns a reference to the extra resources of the PSB.
1162    pub fn extra(&self) -> &Vec<Vec<u8>> {
1163        &self.extra
1164    }
1165
1166    /// Returns a mutable reference to the extra resources of the PSB.
1167    pub fn extra_mut(&mut self) -> &mut Vec<Vec<u8>> {
1168        &mut self.extra
1169    }
1170
1171    /// Returns a reference to the root object of the PSB.
1172    pub fn root(&self) -> &PsbObjectFixed {
1173        &self.root
1174    }
1175
1176    /// Returns a mutable reference to the root object of the PSB.
1177    pub fn root_mut(&mut self) -> &mut PsbObjectFixed {
1178        &mut self.root
1179    }
1180
1181    /// Sets the root of the PSB.
1182    pub fn set_root(&mut self, root: PsbObjectFixed) {
1183        self.root = root;
1184    }
1185
1186    /// Unwraps the PSB into its components.
1187    pub fn unwrap(self) -> (PsbHeader, Vec<Vec<u8>>, Vec<Vec<u8>>, PsbObjectFixed) {
1188        (self.header, self.resources, self.extra, self.root)
1189    }
1190
1191    /// Converts this fixed PSB to a virtual PSB.
1192    pub fn to_psb(self, warn_on_none: bool) -> VirtualPsb {
1193        let (header, resources, extra, root) = self.unwrap();
1194        VirtualPsb::new(header, resources, extra, root.to_psb(warn_on_none))
1195    }
1196
1197    /// Converts json object to a fixed PSB.
1198    #[cfg(feature = "json")]
1199    pub fn from_json(&mut self, obj: &JsonValue) -> Result<(), anyhow::Error> {
1200        let version = obj["version"]
1201            .as_u16()
1202            .ok_or_else(|| anyhow::anyhow!("Invalid PSB version"))?;
1203        let encryption = obj["encryption"]
1204            .as_u16()
1205            .ok_or_else(|| anyhow::anyhow!("Invalid PSB encryption"))?;
1206        self.header.version = version;
1207        self.header.encryption = encryption;
1208        self.root = PsbObjectFixed::from_json(&obj["data"]);
1209        Ok(())
1210    }
1211
1212    #[cfg(feature = "json")]
1213    /// Creates a fixed PSB from a JSON object.
1214    pub fn with_json(obj: &JsonValue) -> Result<Self, anyhow::Error> {
1215        let version = obj["version"]
1216            .as_u16()
1217            .ok_or_else(|| anyhow::anyhow!("Invalid PSB version"))?;
1218        let encryption = obj["encryption"]
1219            .as_u16()
1220            .ok_or_else(|| anyhow::anyhow!("Invalid PSB encryption"))?;
1221        let root = PsbObjectFixed::from_json(&obj["data"]);
1222        Ok(Self {
1223            header: PsbHeader {
1224                version,
1225                encryption,
1226            },
1227            resources: Vec::new(),
1228            extra: Vec::new(),
1229            root,
1230        })
1231    }
1232
1233    pub fn set_data(&mut self, data: VirtualPsbFixedData) {
1234        self.header.version = data.version;
1235        self.header.encryption = data.encryption;
1236        self.root = data.data;
1237    }
1238
1239    /// Converts this fixed PSB to a JSON object.
1240    #[cfg(feature = "json")]
1241    pub fn to_json(&self) -> JsonValue {
1242        json::object! {
1243            "version": self.header.version,
1244            "encryption": self.header.encryption,
1245            "data": self.root.to_json(),
1246        }
1247    }
1248}
1249
1250/// Trait to convert a virtual PSB to a fixed PSB.
1251pub trait VirtualPsbExt {
1252    /// Converts this virtual PSB to a fixed PSB.
1253    fn to_psb_fixed(self) -> VirtualPsbFixed;
1254}
1255
1256impl VirtualPsbExt for VirtualPsb {
1257    fn to_psb_fixed(self) -> VirtualPsbFixed {
1258        let (header, resources, extra, root) = self.unwrap();
1259        VirtualPsbFixed::new(header, resources, extra, root.to_psb_fixed())
1260    }
1261}
1262
1263/// Trait to extend PSB writer behavior.
1264pub trait PsbWriterExt {
1265    /// Writes PSB with a v4-compatible resource layout.
1266    fn finish_v4<T: Write + Seek>(self, stream: T) -> std::result::Result<u64, PsbError>;
1267}
1268
1269impl PsbWriterExt for VirtualPsb {
1270    fn finish_v4<T: Write + Seek>(self, mut stream: T) -> std::result::Result<u64, PsbError> {
1271        let file_start = stream.seek(SeekFrom::Current(0))?;
1272
1273        let (header, resources, extra, root) = self.unwrap();
1274
1275        stream.write_u32(PSB_SIGNATURE)?;
1276        header.write_bytes(&mut stream)?;
1277
1278        let offsets_end_pos = stream.seek(SeekFrom::Current(0))? - file_start;
1279        stream.write_u32(0)?;
1280
1281        let offset_start_pos = stream.seek(SeekFrom::Current(0))? - file_start;
1282        let mut offsets = PsbOffsets::default();
1283
1284        offsets.write_bytes(header.version, &mut stream)?;
1285        let offsets_end = stream.seek(SeekFrom::Current(0))? - file_start;
1286
1287        let refs = {
1288            let mut names = Vec::new();
1289            let mut strings = Vec::new();
1290
1291            root.collect_names(&mut names);
1292            root.collect_strings(&mut strings);
1293
1294            names.sort();
1295            strings.sort();
1296
1297            PsbRefs::new(names, strings)
1298        };
1299
1300        offsets.name_offset = (stream.seek(SeekFrom::Current(0))? - file_start) as u32;
1301        PsbWriter::write_names(refs.names(), &mut stream)?;
1302
1303        offsets.entry_point = (stream.seek(SeekFrom::Current(0))? - file_start) as u32;
1304        PsbValue::Object(root).write_bytes_refs(&mut stream, &refs)?;
1305
1306        let (_, string_offsets) = PsbWriter::write_strings(refs.strings(), &mut stream)?;
1307        offsets.strings = string_offsets;
1308
1309        // For v4, write extra resources before normal resources to keep compatibility
1310        // with tools expecting the FreeMote layout.
1311        if header.version > 3 {
1312            let (_, extra_offsets) = PsbWriter::write_resources(&extra, &mut stream)?;
1313            offsets.extra = Some(extra_offsets);
1314        }
1315
1316        let (_, res_offsets) = PsbWriter::write_resources(&resources, &mut stream)?;
1317        offsets.resources = res_offsets;
1318
1319        let file_end = stream.seek(SeekFrom::Current(0))?;
1320
1321        stream.seek(SeekFrom::Start(offsets_end_pos))?;
1322        stream.write_u32(offsets_end as u32)?;
1323
1324        if header.version > 2 {
1325            let mut adler = Adler32::new();
1326
1327            adler.write_slice(&(offset_start_pos as u32).to_le_bytes());
1328            adler.write_slice(&offsets.name_offset.to_le_bytes());
1329            adler.write_slice(&offsets.strings.offset_pos.to_le_bytes());
1330            adler.write_slice(&offsets.strings.data_pos.to_le_bytes());
1331            adler.write_slice(&offsets.resources.offset_pos.to_le_bytes());
1332            adler.write_slice(&offsets.resources.lengths_pos.to_le_bytes());
1333            adler.write_slice(&offsets.resources.data_pos.to_le_bytes());
1334            adler.write_slice(&offsets.entry_point.to_le_bytes());
1335
1336            offsets.checksum = Some(adler.checksum());
1337        }
1338
1339        stream.seek(SeekFrom::Start(offset_start_pos))?;
1340        offsets.write_bytes(header.version, &mut stream)?;
1341        stream.seek(SeekFrom::Start(file_end))?;
1342
1343        Ok(file_end - file_start)
1344    }
1345}
1346
1347/// Trait to extend PSB reader functionality.
1348pub trait PsbReaderExt {
1349    /// Opens a PSB v2 file from a stream, handling other formats like LZ4 compression.
1350    fn open_psb_v2<T: Read + Seek>(stream: T) -> Result<VirtualPsb>;
1351}
1352
1353const LZ4_SIGNATURE: u32 = 0x184D2204;
1354const PSB_SIGNATURE: u32 = emote_psb::PSB_SIGNATURE;
1355const MDF_SIGNATURE: u32 = emote_psb::PSB_MDF_SIGNATURE;
1356const PSB_TYPE_INTEGER_ARRAY_N: u8 = 0x0C;
1357const PSB_TYPE_STRING_N: u8 = 0x14;
1358const PSB_TYPE_RESOURCE_N: u8 = 0x18;
1359const PSB_TYPE_LIST: u8 = 0x20;
1360const PSB_TYPE_OBJECT: u8 = 0x21;
1361const PSB_TYPE_EXTRA_N: u8 = 0x21;
1362
1363fn is_psb_array_type(value: u8) -> bool {
1364    (PSB_TYPE_INTEGER_ARRAY_N + 1..=PSB_TYPE_INTEGER_ARRAY_N + 8).contains(&value)
1365}
1366
1367fn read_int_array<R: Read + Seek>(stream: &mut R) -> Result<Vec<u64>> {
1368    let (_, value) = PsbValue::from_bytes(stream)
1369        .map_err(|e| anyhow::anyhow!("Failed to read PSB array: {:?}", e))?;
1370    match value {
1371        PsbValue::IntArray(arr) => Ok(arr.unwrap()),
1372        _ => Err(anyhow::anyhow!("Expected PSB int array")),
1373    }
1374}
1375
1376fn skip_psb_value<R: Read + Seek>(stream: &mut R) -> Result<u64> {
1377    let start = stream.seek(SeekFrom::Current(0))?;
1378    let value_type = stream.read_u8()?;
1379    match value_type {
1380        PSB_TYPE_LIST => {
1381            let ref_offsets = read_int_array(stream)?;
1382            if ref_offsets.is_empty() {
1383                return Ok(stream.seek(SeekFrom::Current(0))? - start);
1384            }
1385            let mut max_end = 0_u64;
1386            let data_start = stream.seek(SeekFrom::Current(0))?;
1387            for offset in ref_offsets {
1388                stream.seek(SeekFrom::Start(data_start + offset))?;
1389                let read = skip_psb_value(stream)?;
1390                max_end = max_end.max(offset + read);
1391            }
1392            stream.seek(SeekFrom::Start(data_start + max_end))?;
1393            Ok(stream.seek(SeekFrom::Current(0))? - start)
1394        }
1395        PSB_TYPE_OBJECT => {
1396            let _ = read_int_array(stream)?;
1397            let ref_offsets = read_int_array(stream)?;
1398            if ref_offsets.is_empty() {
1399                return Ok(stream.seek(SeekFrom::Current(0))? - start);
1400            }
1401            let mut max_end = 0_u64;
1402            let data_start = stream.seek(SeekFrom::Current(0))?;
1403            for offset in ref_offsets {
1404                stream.seek(SeekFrom::Start(data_start + offset))?;
1405                let read = skip_psb_value(stream)?;
1406                max_end = max_end.max(offset + read);
1407            }
1408            stream.seek(SeekFrom::Start(data_start + max_end))?;
1409            Ok(stream.seek(SeekFrom::Current(0))? - start)
1410        }
1411        // String/resource/extra references are encoded as fixed-width indexes.
1412        _ if (PSB_TYPE_STRING_N + 1..=PSB_TYPE_STRING_N + 4).contains(&value_type)
1413            || (PSB_TYPE_RESOURCE_N + 1..=PSB_TYPE_RESOURCE_N + 4).contains(&value_type)
1414            || (PSB_TYPE_EXTRA_N + 1..=PSB_TYPE_EXTRA_N + 4).contains(&value_type) =>
1415        {
1416            let n = if value_type <= PSB_TYPE_STRING_N + 4 {
1417                value_type - PSB_TYPE_STRING_N
1418            } else if value_type <= PSB_TYPE_RESOURCE_N + 4 {
1419                value_type - PSB_TYPE_RESOURCE_N
1420            } else {
1421                value_type - PSB_TYPE_EXTRA_N
1422            };
1423            stream.seek(SeekFrom::Current(n as i64))?;
1424            Ok(stream.seek(SeekFrom::Current(0))? - start)
1425        }
1426        _ => {
1427            stream.seek(SeekFrom::Start(start))?;
1428            let (read, _) = PsbValue::from_bytes(stream)
1429                .map_err(|e| anyhow::anyhow!("Failed to skip PSB value: {:?}", e))?;
1430            Ok(read)
1431        }
1432    }
1433}
1434
1435fn detect_name_pos(data: &[u8]) -> Option<usize> {
1436    if data.len() < 8 {
1437        return None;
1438    }
1439    let mut name_pos = None;
1440    for i in 0..(data.len() - 7) {
1441        if data[i] == 0x0D || data[i] == 0x0E {
1442            let offset1 = (data[i] - 0x0B) as usize;
1443            if i + offset1 < data.len() {
1444                if data[i + offset1] == 0x0E
1445                    && i + 7 < data.len()
1446                    && data[i + 4..i + 8] == [1, 0, 0, 0]
1447                {
1448                    name_pos = Some(i);
1449                    break;
1450                }
1451                if data[i + offset1] == 0x0D
1452                    && i + offset1 + 2 < data.len()
1453                    && data[i + offset1 + 1..i + offset1 + 3] == [1, 0]
1454                {
1455                    name_pos = Some(i);
1456                    break;
1457                }
1458            }
1459        }
1460    }
1461    name_pos
1462}
1463
1464fn load_psb_dullahan<R: Read + Seek>(mut stream: R) -> Result<VirtualPsb> {
1465    let mut probe = vec![0_u8; 1024];
1466    stream.seek(SeekFrom::Start(0))?;
1467    let probe_len = stream.read(&mut probe)?;
1468    probe.truncate(probe_len);
1469
1470    let name_pos = detect_name_pos(&probe)
1471        .ok_or_else(|| anyhow::anyhow!("Dullahan fallback: cannot find names segment"))?
1472        as u64;
1473
1474    let version = stream.peek_u16_at(4)?;
1475    let encryption = stream.peek_u16_at(6)?;
1476    let mut header = PsbHeader {
1477        version,
1478        encryption,
1479    };
1480
1481    stream.seek(SeekFrom::Start(name_pos))?;
1482    let (_, names) = PsbReader::read_names(&mut stream)
1483        .map_err(|e| anyhow::anyhow!("Dullahan fallback: failed to read names: {:?}", e))?;
1484
1485    let file_len = stream.stream_length()?;
1486    let mut entry_point = None;
1487    while stream.seek(SeekFrom::Current(0))? < file_len {
1488        let b = stream.read_u8()?;
1489        if b == PSB_TYPE_LIST || b == PSB_TYPE_OBJECT {
1490            entry_point = Some(stream.seek(SeekFrom::Current(-1))? as u32);
1491            break;
1492        }
1493    }
1494    let entry_point = entry_point
1495        .ok_or_else(|| anyhow::anyhow!("Dullahan fallback: cannot find root object/list"))?;
1496
1497    stream.seek(SeekFrom::Start(entry_point as u64))?;
1498    let _ = skip_psb_value(&mut stream)?;
1499
1500    let mut strings_offset_pos = None;
1501    while stream.seek(SeekFrom::Current(0))? < file_len {
1502        let b = stream.read_u8()?;
1503        if is_psb_array_type(b) {
1504            strings_offset_pos = Some(stream.seek(SeekFrom::Current(-1))? as u32);
1505            break;
1506        }
1507    }
1508    let strings_offset_pos = strings_offset_pos
1509        .ok_or_else(|| anyhow::anyhow!("Dullahan fallback: cannot find strings offset table"))?;
1510
1511    stream.seek(SeekFrom::Start(strings_offset_pos as u64))?;
1512    let string_offsets = read_int_array(&mut stream)?;
1513    let strings_data_pos = stream.seek(SeekFrom::Current(0))? as u32;
1514
1515    stream.seek(SeekFrom::Start(strings_offset_pos as u64))?;
1516    let (_, strings) = PsbReader::read_strings(strings_data_pos, &mut stream)
1517        .map_err(|e| anyhow::anyhow!("Dullahan fallback: failed to read strings: {:?}", e))?;
1518
1519    let mut strings_data_end = strings_data_pos as u64;
1520    for (idx, offset) in string_offsets.iter().enumerate() {
1521        if let Some(s) = strings.get(idx) {
1522            strings_data_end = strings_data_end
1523                .max(strings_data_pos as u64 + *offset + s.as_bytes().len() as u64 + 1);
1524        }
1525    }
1526
1527    stream.seek(SeekFrom::Start(strings_data_end.min(file_len)))?;
1528    let mut resource_array_start = None;
1529    while stream.seek(SeekFrom::Current(0))? < file_len {
1530        let b = stream.read_u8()?;
1531        if is_psb_array_type(b) {
1532            resource_array_start = Some(stream.seek(SeekFrom::Current(-1))? as u32);
1533            break;
1534        }
1535    }
1536
1537    let mut resources = PsbResourcesOffset::default();
1538    let mut extra: Option<PsbResourcesOffset> = None;
1539
1540    if let Some(pos1) = resource_array_start {
1541        stream.seek(SeekFrom::Start(pos1 as u64))?;
1542        let array1 = read_int_array(&mut stream)?;
1543        let pos2 = stream.seek(SeekFrom::Current(0))? as u32;
1544        let array2 = read_int_array(&mut stream)?;
1545
1546        let after_array2 = stream.seek(SeekFrom::Current(0))?;
1547        let mut probe_next = 0_u8;
1548        let mut has_next = false;
1549        if after_array2 < file_len {
1550            probe_next = stream.read_u8()?;
1551            stream.seek(SeekFrom::Current(-1))?;
1552            has_next = true;
1553        }
1554
1555        if has_next && (version >= 4 || is_psb_array_type(probe_next)) {
1556            header.version = 4;
1557            let mut extra_table = PsbResourcesOffset {
1558                offset_pos: pos1,
1559                lengths_pos: pos2,
1560                data_pos: after_array2 as u32,
1561            };
1562
1563            if !array1.is_empty() && !array2.is_empty() {
1564                let max_idx = array1
1565                    .iter()
1566                    .enumerate()
1567                    .max_by_key(|(_, offset)| *offset)
1568                    .map(|(idx, _)| idx)
1569                    .unwrap_or(0);
1570                let should_be_len = array1[max_idx] + array2[max_idx];
1571                let detect_start = after_array2.saturating_add(should_be_len);
1572                let detect_end = (detect_start + 1024).min(file_len);
1573
1574                let mut found = false;
1575                let mut cursor = detect_start;
1576                while cursor < detect_end {
1577                    stream.seek(SeekFrom::Start(cursor))?;
1578                    let b = stream.read_u8()?;
1579                    if !is_psb_array_type(b) {
1580                        cursor += 1;
1581                        continue;
1582                    }
1583
1584                    stream.seek(SeekFrom::Start(cursor))?;
1585                    if read_int_array(&mut stream).is_ok() {
1586                        let off_pos = cursor as u32;
1587                        let len_pos = stream.seek(SeekFrom::Current(0))? as u32;
1588                        if read_int_array(&mut stream).is_ok() {
1589                            extra_table.data_pos = off_pos.saturating_sub(should_be_len as u32);
1590                            resources.offset_pos = off_pos;
1591                            resources.lengths_pos = len_pos;
1592                            resources.data_pos = stream.seek(SeekFrom::Current(0))? as u32;
1593                            found = true;
1594                            break;
1595                        }
1596                    }
1597
1598                    cursor += 1;
1599                }
1600
1601                if !found {
1602                    return Err(anyhow::anyhow!(
1603                        "Dullahan fallback: cannot find resource offset/length tables"
1604                    ));
1605                }
1606            } else {
1607                stream.seek(SeekFrom::Start(after_array2))?;
1608                while stream.seek(SeekFrom::Current(0))? < file_len {
1609                    let b = stream.read_u8()?;
1610                    if is_psb_array_type(b) {
1611                        stream.seek(SeekFrom::Current(-1))?;
1612                        resources.offset_pos = stream.seek(SeekFrom::Current(0))? as u32;
1613                        let _ = read_int_array(&mut stream)?;
1614                        resources.lengths_pos = stream.seek(SeekFrom::Current(0))? as u32;
1615                        let _ = read_int_array(&mut stream)?;
1616                        resources.data_pos = stream.seek(SeekFrom::Current(0))? as u32;
1617                        break;
1618                    }
1619                }
1620            }
1621
1622            extra = Some(extra_table);
1623        } else {
1624            resources.offset_pos = pos1;
1625            resources.lengths_pos = pos2;
1626            resources.data_pos = after_array2 as u32;
1627        }
1628
1629        stream.seek(SeekFrom::Start(resources.offset_pos as u64))?;
1630        let chunk_offsets = read_int_array(&mut stream).unwrap_or_default();
1631        stream.seek(SeekFrom::Start(resources.lengths_pos as u64))?;
1632        let chunk_lengths = read_int_array(&mut stream).unwrap_or_default();
1633        if !chunk_offsets.is_empty() && !chunk_lengths.is_empty() {
1634            let current_pos = resources.data_pos as u64;
1635            if current_pos <= file_len {
1636                let remain_length = file_len - current_pos;
1637                let max_idx = chunk_offsets
1638                    .iter()
1639                    .enumerate()
1640                    .max_by_key(|(_, offset)| *offset)
1641                    .map(|(idx, _)| idx)
1642                    .unwrap_or(0);
1643                let should_be_len = chunk_offsets[max_idx] + chunk_lengths[max_idx];
1644                let padding = remain_length.saturating_sub(should_be_len);
1645                resources.data_pos = current_pos.saturating_add(padding) as u32;
1646            }
1647        }
1648    }
1649
1650    let refs = PsbRefs::new(names, strings);
1651    let offsets = PsbOffsets {
1652        name_offset: name_pos as u32,
1653        strings: PsbStringOffset {
1654            offset_pos: strings_offset_pos,
1655            data_pos: strings_data_pos,
1656        },
1657        resources,
1658        entry_point,
1659        checksum: Some(0),
1660        extra,
1661    };
1662
1663    stream.seek(SeekFrom::Start(0))?;
1664    let mut file = PsbFile::new(header, refs, offsets, stream);
1665    file.load()
1666        .map_err(|e| anyhow::anyhow!("Dullahan fallback: failed to load PSB: {:?}", e))
1667}
1668
1669impl PsbReaderExt for PsbReader {
1670    fn open_psb_v2<T: Read + Seek>(mut stream: T) -> Result<VirtualPsb> {
1671        let signature = stream.peek_u32_at(0)?;
1672        if signature == LZ4_SIGNATURE {
1673            let mut decoder = lz4::Decoder::new(stream)?;
1674            let mut mem_stream = MemWriter::new();
1675            std::io::copy(&mut decoder, &mut mem_stream)?;
1676            return Self::open_psb_v2(MemReader::new(mem_stream.into_inner()));
1677        }
1678        if signature == MDF_SIGNATURE {
1679            let mut file = MdfReader::open_mdf(stream)
1680                .map_err(|e| anyhow::anyhow!("Failed to open MDF/PSB: {:?}", e))?;
1681            return file
1682                .load()
1683                .map_err(|e| anyhow::anyhow!("Failed to load MDF/PSB: {:?}", e));
1684        }
1685        if signature != PSB_SIGNATURE {
1686            return Err(anyhow::anyhow!("Failed to open PSB: invalid signature"));
1687        }
1688        let normal_file = PsbReader::open_psb(&mut stream);
1689        match normal_file {
1690            Ok(mut file) => file
1691                .load()
1692                .map_err(|e| anyhow::anyhow!("Failed to load PSB: {:?}", e)),
1693            Err(err) => {
1694                stream.seek(SeekFrom::Start(0))?;
1695                let encryption = stream.peek_u16_at(6).unwrap_or(0);
1696                if encryption != 0 {
1697                    load_psb_dullahan(&mut stream).map_err(|fallback_err| {
1698                        anyhow::anyhow!(
1699                            "Failed to open PSB: {:?}; fallback failed: {}",
1700                            err,
1701                            fallback_err
1702                        )
1703                    })
1704                } else {
1705                    Err(anyhow::anyhow!("Failed to open PSB: {:?}", err))
1706                }
1707            }
1708        }
1709    }
1710}
1711
1712#[cfg(feature = "json")]
1713#[test]
1714fn test_f32_to_json() {
1715    let num = PsbValueFixed::Number(PsbNumber::Float(3.03));
1716    let json_value = num.to_json().unwrap();
1717    assert_eq!(json_value.to_string(), "3.03");
1718    let num = PsbValueFixed::Number(PsbNumber::Double(3.03));
1719    let json_value = num.to_json().unwrap();
1720    assert_eq!(json_value.to_string(), "3.03");
1721}