1use std::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct LocaleFlags(pub u32);
13
14impl LocaleFlags {
15 pub const NONE: Self = Self(0);
17 pub const EN_US: Self = Self(0x2);
19 pub const KO_KR: Self = Self(0x4);
21 pub const FR_FR: Self = Self(0x10);
23 pub const DE_DE: Self = Self(0x20);
25 pub const ZH_CN: Self = Self(0x40);
27 pub const ES_ES: Self = Self(0x80);
29 pub const ZH_TW: Self = Self(0x100);
31 pub const EN_GB: Self = Self(0x200);
33 pub const EN_CN: Self = Self(0x400);
35 pub const EN_TW: Self = Self(0x800);
37 pub const ES_MX: Self = Self(0x1000);
39 pub const RU_RU: Self = Self(0x2000);
41 pub const PT_BR: Self = Self(0x4000);
43 pub const IT_IT: Self = Self(0x8000);
45 pub const PT_PT: Self = Self(0x10000);
47 pub const ALL: Self = Self(0xFFFFFFFF);
49
50 pub fn contains(self, other: Self) -> bool {
52 (self.0 & other.0) != 0
53 }
54
55 pub fn matches(self, filter: Self) -> bool {
59 filter.0 == 0 || (self.0 & filter.0) != 0
60 }
61}
62
63impl fmt::Display for LocaleFlags {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 if self.0 == 0 {
66 return write!(f, "None");
67 }
68 if self.0 == 0xFFFFFFFF {
69 return write!(f, "All");
70 }
71
72 const NAMES: &[(u32, &str)] = &[
73 (0x2, "enUS"),
74 (0x4, "koKR"),
75 (0x10, "frFR"),
76 (0x20, "deDE"),
77 (0x40, "zhCN"),
78 (0x80, "esES"),
79 (0x100, "zhTW"),
80 (0x200, "enGB"),
81 (0x400, "enCN"),
82 (0x800, "enTW"),
83 (0x1000, "esMX"),
84 (0x2000, "ruRU"),
85 (0x4000, "ptBR"),
86 (0x8000, "itIT"),
87 (0x10000, "ptPT"),
88 ];
89
90 let mut first = true;
91 for &(bit, name) in NAMES {
92 if (self.0 & bit) != 0 {
93 if !first {
94 write!(f, "|")?;
95 }
96 write!(f, "{}", name)?;
97 first = false;
98 }
99 }
100 if first {
101 write!(f, "0x{:X}", self.0)?;
102 }
103 Ok(())
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct ContentFlags(pub u32);
110
111impl ContentFlags {
112 pub const NONE: Self = Self(0);
114 pub const HIGH_RES_TEXTURE: Self = Self(0x1);
116 pub const INSTALL: Self = Self(0x4);
118 pub const LOAD_ON_WINDOWS: Self = Self(0x8);
120 pub const LOAD_ON_MACOS: Self = Self(0x10);
122 pub const X86_32: Self = Self(0x20);
124 pub const X86_64: Self = Self(0x40);
126 pub const LOW_VIOLENCE: Self = Self(0x80);
128 pub const DO_NOT_LOAD: Self = Self(0x100);
130 pub const UPDATE_PLUGIN: Self = Self(0x800);
132 pub const ARM64: Self = Self(0x8000);
134 pub const ENCRYPTED: Self = Self(0x8000000);
136 pub const NO_NAME_HASH: Self = Self(0x10000000);
138 pub const UNCOMMON_RES: Self = Self(0x20000000);
140 pub const BUNDLE: Self = Self(0x40000000);
142 pub const NO_COMPRESSION: Self = Self(0x80000000);
144
145 pub fn has(self, flag: Self) -> bool {
147 (self.0 & flag.0) != 0
148 }
149
150 pub fn has_no_name_hash(self) -> bool {
152 self.has(Self::NO_NAME_HASH)
153 }
154}
155
156impl fmt::Display for ContentFlags {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 if self.0 == 0 {
159 return write!(f, "None");
160 }
161
162 const NAMES: &[(u32, &str)] = &[
163 (0x1, "HighResTexture"),
164 (0x4, "Install"),
165 (0x8, "LoadOnWindows"),
166 (0x10, "LoadOnMacOS"),
167 (0x20, "x86_32"),
168 (0x40, "x86_64"),
169 (0x80, "LowViolence"),
170 (0x100, "DoNotLoad"),
171 (0x800, "UpdatePlugin"),
172 (0x8000, "ARM64"),
173 (0x8000000, "Encrypted"),
174 (0x10000000, "NoNameHash"),
175 (0x20000000, "UncommonRes"),
176 (0x40000000, "Bundle"),
177 (0x80000000, "NoCompression"),
178 ];
179
180 let mut first = true;
181 for &(bit, name) in NAMES {
182 if (self.0 & bit) != 0 {
183 if !first {
184 write!(f, "|")?;
185 }
186 write!(f, "{}", name)?;
187 first = false;
188 }
189 }
190 if first {
191 write!(f, "0x{:X}", self.0)?;
192 }
193 Ok(())
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn locale_enus() {
203 assert_eq!(LocaleFlags::EN_US.0, 0x2);
204 }
205
206 #[test]
207 fn locale_dede() {
208 assert_eq!(LocaleFlags::DE_DE.0, 0x20);
209 }
210
211 #[test]
212 fn locale_contains() {
213 let flags = LocaleFlags(0x2 | 0x200); assert!(flags.contains(LocaleFlags::EN_US));
215 assert!(flags.contains(LocaleFlags::EN_GB));
216 assert!(!flags.contains(LocaleFlags::DE_DE));
217 }
218
219 #[test]
220 fn locale_matches_filter() {
221 let flags = LocaleFlags(0x2); assert!(flags.matches(LocaleFlags::EN_US));
223 assert!(!flags.matches(LocaleFlags::DE_DE));
224 assert!(flags.matches(LocaleFlags::ALL)); }
226
227 #[test]
228 fn locale_matches_none_filter() {
229 let flags = LocaleFlags(0x2);
231 assert!(flags.matches(LocaleFlags::NONE));
232 }
233
234 #[test]
235 fn content_no_name_hash() {
236 let flags = ContentFlags(0x10000000);
237 assert!(flags.has_no_name_hash());
238 }
239
240 #[test]
241 fn content_flags_combined() {
242 let flags = ContentFlags(0x8 | 0x40); assert!(flags.has(ContentFlags::LOAD_ON_WINDOWS));
244 assert!(flags.has(ContentFlags::X86_64));
245 assert!(!flags.has(ContentFlags::LOAD_ON_MACOS));
246 }
247
248 #[test]
249 fn content_flags_none_has_nothing() {
250 let flags = ContentFlags::NONE;
251 assert!(!flags.has(ContentFlags::LOAD_ON_WINDOWS));
252 assert!(!flags.has_no_name_hash());
253 }
254
255 #[test]
256 fn locale_display_single() {
257 assert_eq!(format!("{}", LocaleFlags::EN_US), "enUS");
258 }
259
260 #[test]
261 fn locale_display_combined() {
262 let flags = LocaleFlags(0x2 | 0x20); assert_eq!(format!("{}", flags), "enUS|deDE");
264 }
265
266 #[test]
267 fn locale_display_none() {
268 assert_eq!(format!("{}", LocaleFlags::NONE), "None");
269 }
270
271 #[test]
272 fn locale_display_all() {
273 assert_eq!(format!("{}", LocaleFlags::ALL), "All");
274 }
275
276 #[test]
277 fn content_display_single() {
278 assert_eq!(
279 format!("{}", ContentFlags::LOAD_ON_WINDOWS),
280 "LoadOnWindows"
281 );
282 }
283
284 #[test]
285 fn content_display_combined() {
286 let flags = ContentFlags(0x8 | 0x10000000); assert_eq!(format!("{}", flags), "LoadOnWindows|NoNameHash");
288 }
289
290 #[test]
291 fn content_display_none() {
292 assert_eq!(format!("{}", ContentFlags::NONE), "None");
293 }
294}