tiger_pkg/
tag.rs

1use std::{
2    fmt::{Debug, Display, Formatter},
3    hash::Hash,
4    str::FromStr,
5};
6
7use binrw::{BinRead, BinWrite};
8
9#[derive(
10    BinRead,
11    BinWrite,
12    Copy,
13    Clone,
14    PartialEq,
15    PartialOrd,
16    Eq,
17    Ord,
18    serde::Serialize,
19    serde::Deserialize,
20    bincode::Decode,
21    bincode::Encode,
22)]
23#[repr(transparent)]
24pub struct TagHash(pub u32);
25
26impl From<TagHash> for u32 {
27    fn from(value: TagHash) -> Self {
28        value.0
29    }
30}
31
32impl From<u32> for TagHash {
33    fn from(value: u32) -> Self {
34        Self(value)
35    }
36}
37
38impl From<(u16, u16)> for TagHash {
39    fn from((pkg_id, index): (u16, u16)) -> Self {
40        Self::new(pkg_id, index)
41    }
42}
43
44impl Default for TagHash {
45    fn default() -> Self {
46        Self::NONE
47    }
48}
49
50impl TagHash {
51    pub const NONE: TagHash = TagHash(u32::MAX);
52
53    pub fn new(pkg_id: u16, entry: u16) -> TagHash {
54        TagHash(
55            0x80800000u32
56                .wrapping_add((pkg_id as u32) << 13)
57                .wrapping_add(entry as u32 % 8192),
58        )
59    }
60
61    pub fn is_valid(&self) -> bool {
62        self.0 > 0x80800000 && self.0 <= 0x81ffffff
63    }
64
65    pub fn is_none(&self) -> bool {
66        self.0 == u32::MAX
67    }
68
69    pub fn is_some(&self) -> bool {
70        !self.is_none() && self.is_valid()
71    }
72
73    /// Does this hash look like a pkg hash?
74    pub fn is_pkg_file(&self) -> bool {
75        self.is_some() && (0x9..0xa00).contains(&self.pkg_id())
76    }
77
78    pub fn pkg_id(&self) -> u16 {
79        (self.0.wrapping_sub(0x80800000) >> 13) as u16
80    }
81
82    pub fn entry_index(&self) -> u16 {
83        ((self.0 & 0x1fff) % 8192) as u16
84    }
85}
86
87impl Debug for TagHash {
88    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89        if self.is_none() {
90            f.write_str("TagHash(NONE)")
91        } else if !self.is_valid() {
92            f.write_fmt(format_args!("TagHash(INVALID(0x{:x}))", self.0))
93        } else {
94            f.debug_struct("TagHash")
95                .field("pkg_id", &self.pkg_id())
96                .field("entry_index", &self.entry_index())
97                .finish()
98        }
99    }
100}
101
102impl FromStr for TagHash {
103    type Err = std::num::ParseIntError;
104
105    fn from_str(s: &str) -> Result<Self, Self::Err> {
106        let hash = u32::from_str_radix(s, 16)?;
107        if cfg!(feature = "flip_tag_format") {
108            Ok(TagHash(hash.swap_bytes()))
109        } else {
110            Ok(TagHash(hash))
111        }
112    }
113}
114
115impl Display for TagHash {
116    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117        if cfg!(feature = "flip_tag_format") {
118            f.write_fmt(format_args!("{:08X}", self.0.swap_bytes()))
119        } else {
120            f.write_fmt(format_args!("{:08X}", self.0))
121        }
122    }
123}
124
125impl Hash for TagHash {
126    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
127        state.write_u32(self.0)
128    }
129}
130
131#[derive(
132    BinRead,
133    BinWrite,
134    Copy,
135    Clone,
136    PartialEq,
137    PartialOrd,
138    Eq,
139    Ord,
140    serde::Serialize,
141    serde::Deserialize,
142    bincode::Decode,
143    bincode::Encode,
144)]
145#[repr(transparent)]
146pub struct TagHash64(pub u64);
147
148impl TagHash64 {
149    pub const NONE: TagHash64 = TagHash64(0);
150}
151
152impl Default for TagHash64 {
153    fn default() -> Self {
154        Self::NONE
155    }
156}
157
158impl From<TagHash64> for u64 {
159    fn from(value: TagHash64) -> Self {
160        value.0
161    }
162}
163
164impl From<u64> for TagHash64 {
165    fn from(value: u64) -> Self {
166        Self(value)
167    }
168}
169
170impl Debug for TagHash64 {
171    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
172        f.write_fmt(format_args!("TagHash(0x{:016X})", self.0))
173    }
174}
175
176impl Display for TagHash64 {
177    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
178        if cfg!(feature = "flip_tag_format") {
179            f.write_fmt(format_args!("{:016X}", self.0.swap_bytes()))
180        } else {
181            f.write_fmt(format_args!("{:016X}", self.0))
182        }
183    }
184}
185
186impl Hash for TagHash64 {
187    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
188        state.write_u64(self.0)
189    }
190}