1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::{
    fmt::{Debug, Display, Formatter},
    hash::Hash,
};

use binrw::{BinRead, BinWrite};

#[derive(
    BinRead, BinWrite, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, serde::Serialize, serde::Deserialize,
)]
pub struct TagHash(pub u32);

impl From<TagHash> for u32 {
    fn from(value: TagHash) -> Self {
        value.0
    }
}

impl From<u32> for TagHash {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

impl From<(u16, u16)> for TagHash {
    fn from((pkg_id, index): (u16, u16)) -> Self {
        Self::new(pkg_id, index)
    }
}

impl Default for TagHash {
    fn default() -> Self {
        Self::NONE
    }
}

impl TagHash {
    pub const NONE: TagHash = TagHash(u32::MAX);

    pub fn new(pkg_id: u16, entry: u16) -> TagHash {
        TagHash(
            0x80800000u32
                .wrapping_add((pkg_id as u32) << 13)
                .wrapping_add(entry as u32 % 8192),
        )
    }

    pub fn is_valid(&self) -> bool {
        self.0 > 0x80800000 && self.0 <= 0x81ffffff
    }

    pub fn is_none(&self) -> bool {
        self.0 == u32::MAX
    }

    pub fn is_some(&self) -> bool {
        !self.is_none() && self.is_valid()
    }

    /// Does this hash look like a pkg hash?
    pub fn is_pkg_file(&self) -> bool {
        self.is_some() && (0x9..0xa00).contains(&self.pkg_id())
    }

    pub fn pkg_id(&self) -> u16 {
        ((self.0 - 0x80800000) >> 13) as u16
    }

    pub fn entry_index(&self) -> u16 {
        ((self.0 & 0x1fff) % 8192) as u16
    }
}

impl Debug for TagHash {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        if self.is_none() {
            f.write_str("TagHash(NONE)")
        } else if !self.is_valid() {
            f.write_fmt(format_args!("TagHash(INVALID(0x{:x}))", self.0))
        } else {
            f.write_fmt(format_args!(
                "TagHash(pkg={:04x}, entry={})",
                self.pkg_id(),
                self.entry_index()
            ))
        }
    }
}

impl Display for TagHash {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{:08X}", self.0.to_be()))
    }
}

impl Hash for TagHash {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        state.write_u32(self.0)
    }
}

#[derive(
    BinRead, BinWrite, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, serde::Serialize, serde::Deserialize,
)]
pub struct TagHash64(pub u64);

impl TagHash64 {
    pub const NONE: TagHash64 = TagHash64(0);
}

impl Default for TagHash64 {
    fn default() -> Self {
        Self::NONE
    }
}

impl From<TagHash64> for u64 {
    fn from(value: TagHash64) -> Self {
        value.0
    }
}

impl From<u64> for TagHash64 {
    fn from(value: u64) -> Self {
        Self(value)
    }
}

impl Debug for TagHash64 {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("TagHash(0x{:016X})", self.0))
    }
}

impl Display for TagHash64 {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{:016X}", self.0.to_be()))
    }
}

impl Hash for TagHash64 {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        state.write_u64(self.0)
    }
}