1use std::sync::Arc;
2
3use binrw::Endian;
4
5use crate::{
6 d1_internal_alpha::PackageD1InternalAlpha, d1_legacy::PackageD1Legacy,
7 d1_roi::PackageD1RiseOfIron, d2_beta::PackageD2Beta, d2_beyondlight::PackageD2BeyondLight,
8 Package, PackageD2PreBL,
9};
10
11pub trait Version: clap::ValueEnum {
12 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>>;
13 fn endian(&self) -> Endian;
14 fn name(&self) -> &'static str;
15 fn id(&self) -> String {
16 self.to_possible_value()
17 .expect("Package version is missing an id/commandline value")
18 .get_name()
19 .to_string()
20 }
21
22 fn aes_key_0(&self) -> [u8; 16] {
23 [
24 0xD6, 0x2A, 0xB2, 0xC1, 0x0C, 0xC0, 0x1B, 0xC5, 0x35, 0xDB, 0x7B, 0x86, 0x55, 0xC7,
25 0xDC, 0x3B,
26 ]
27 }
28
29 fn aes_key_1(&self) -> [u8; 16] {
30 [
31 0x3A, 0x4A, 0x5D, 0x36, 0x73, 0xA6, 0x60, 0x58, 0x7E, 0x63, 0xE6, 0x76, 0xE4, 0x08,
32 0x92, 0xB5,
33 ]
34 }
35
36 fn aes_nonce_base(&self) -> [u8; 12] {
37 [
38 0x84, 0xDF, 0x11, 0xC0, 0xAC, 0xAB, 0xFA, 0x20, 0x33, 0x11, 0x26, 0x99,
39 ]
40 }
41}
42
43#[derive(serde::Serialize, serde::Deserialize, PartialEq, PartialOrd, Debug, Clone, Copy)]
44pub enum GameVersion {
45 Destiny(DestinyVersion),
46 Marathon(MarathonVersion),
47}
48
49impl GameVersion {
50 pub fn engine_version(&self) -> EngineVersion {
51 match self {
52 Self::Destiny(v) => v.engine_version(),
53 Self::Marathon(v) => v.engine_version(),
54 }
55 }
56}
57
58impl Version for GameVersion {
59 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
60 match self {
61 Self::Destiny(v) => v.open(path),
62 Self::Marathon(v) => v.open(path),
63 }
64 }
65
66 fn endian(&self) -> Endian {
67 match self {
68 Self::Destiny(v) => v.endian(),
69 Self::Marathon(v) => v.endian(),
70 }
71 }
72
73 fn name(&self) -> &'static str {
74 match self {
75 Self::Destiny(v) => v.name(),
76 Self::Marathon(v) => v.name(),
77 }
78 }
79
80 fn aes_key_0(&self) -> [u8; 16] {
81 match self {
82 Self::Destiny(v) => v.aes_key_0(),
83 Self::Marathon(v) => v.aes_key_0(),
84 }
85 }
86
87 fn aes_key_1(&self) -> [u8; 16] {
88 match self {
89 Self::Destiny(v) => v.aes_key_1(),
90 Self::Marathon(v) => v.aes_key_1(),
91 }
92 }
93
94 fn aes_nonce_base(&self) -> [u8; 12] {
95 match self {
96 Self::Destiny(v) => v.aes_nonce_base(),
97 Self::Marathon(v) => v.aes_nonce_base(),
98 }
99 }
100}
101
102impl clap::ValueEnum for GameVersion {
103 fn value_variants<'a>() -> &'a [Self] {
104 &[
105 Self::Destiny(DestinyVersion::DestinyInternalAlpha),
106 Self::Destiny(DestinyVersion::DestinyFirstLookAlpha),
107 Self::Destiny(DestinyVersion::DestinyTheTakenKing),
108 Self::Destiny(DestinyVersion::DestinyRiseOfIron),
109 Self::Destiny(DestinyVersion::Destiny2Beta),
110 Self::Destiny(DestinyVersion::Destiny2Forsaken),
111 Self::Destiny(DestinyVersion::Destiny2Shadowkeep),
112 Self::Destiny(DestinyVersion::Destiny2BeyondLight),
113 Self::Destiny(DestinyVersion::Destiny2WitchQueen),
114 Self::Destiny(DestinyVersion::Destiny2Lightfall),
115 Self::Destiny(DestinyVersion::Destiny2TheFinalShape),
116 Self::Destiny(DestinyVersion::Destiny2TheEdgeOfFate),
117 Self::Destiny(DestinyVersion::Destiny2Renegades),
118 Self::Marathon(MarathonVersion::MarathonAlpha),
119 Self::Marathon(MarathonVersion::Marathon),
120 ]
121 }
122
123 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
124 match self {
125 Self::Destiny(v) => v.to_possible_value(),
126 Self::Marathon(v) => v.to_possible_value(),
127 }
128 }
129}
130
131#[derive(
132 serde::Serialize, serde::Deserialize, clap::ValueEnum, PartialEq, PartialOrd, Debug, Clone, Copy,
133)]
134pub enum MarathonVersion {
136 #[value(name = "goliath_alpha")]
138 MarathonAlpha = 500,
139
140 #[value(name = "goliath")]
142 Marathon = 1000,
143}
144
145impl MarathonVersion {
146 pub fn engine_version(&self) -> EngineVersion {
147 match self {
148 Self::MarathonAlpha | Self::Marathon => EngineVersion::TigerGoliath,
149 }
150 }
151}
152
153impl Version for MarathonVersion {
154 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
155 Ok(Arc::new(PackageD2BeyondLight::open(
157 path,
158 DestinyVersion::Destiny2TheFinalShape,
159 )?))
160 }
161
162 fn endian(&self) -> Endian {
163 Endian::Little
164 }
165
166 fn name(&self) -> &'static str {
167 match self {
168 MarathonVersion::MarathonAlpha => "Marathon Closed Alpha",
169 MarathonVersion::Marathon => "Marathon",
170 }
171 }
172}
173
174#[derive(
175 serde::Serialize, serde::Deserialize, clap::ValueEnum, PartialEq, PartialOrd, Debug, Clone, Copy,
176)]
177pub enum DestinyVersion {
178 #[value(name = "d1_devalpha")]
180 DestinyInternalAlpha = 1_0500,
181
182 #[value(name = "d1_fla")]
184 DestinyFirstLookAlpha = 1_0800,
185
186 #[value(name = "d1_ttk")]
188 DestinyTheTakenKing = 1_2000,
189
190 #[value(name = "d1_roi")]
192 DestinyRiseOfIron = 1_2400,
193
194 #[value(name = "d2_beta")]
196 Destiny2Beta = 2_1000,
197
198 #[value(name = "d2_fs")]
200 Destiny2Forsaken = 2_2000,
201
202 #[value(name = "d2_sk")]
204 Destiny2Shadowkeep = 2_2600,
205
206 #[value(name = "d2_bl")]
208 Destiny2BeyondLight = 2_3000,
209
210 #[value(name = "d2_wq")]
212 Destiny2WitchQueen = 2_4000,
213
214 #[value(name = "d2_lf")]
216 Destiny2Lightfall = 2_7000,
217
218 #[value(name = "d2_tfs")]
220 Destiny2TheFinalShape = 2_8000,
221
222 #[value(name = "d2_eof")]
224 Destiny2TheEdgeOfFate = 2_9000,
225
226 #[value(name = "d2_ren")]
228 Destiny2Renegades = 2_9500,
229}
230
231impl DestinyVersion {
232 pub fn is_d1(&self) -> bool {
233 *self <= DestinyVersion::DestinyRiseOfIron
234 }
235
236 pub fn is_d2(&self) -> bool {
237 *self >= DestinyVersion::Destiny2Beta
238 }
239
240 pub fn is_prebl(&self) -> bool {
241 DestinyVersion::Destiny2Beta <= *self && *self <= DestinyVersion::Destiny2Shadowkeep
242 }
243
244 pub fn engine_version(&self) -> EngineVersion {
245 match self {
246 DestinyVersion::DestinyInternalAlpha => EngineVersion::TigerD1Indev,
247 DestinyVersion::DestinyFirstLookAlpha => EngineVersion::TigerD1Alpha,
248 DestinyVersion::DestinyTheTakenKing => EngineVersion::TigerD1v1,
249 DestinyVersion::DestinyRiseOfIron => EngineVersion::TigerD1v2,
250 DestinyVersion::Destiny2Beta
251 | DestinyVersion::Destiny2Forsaken
252 | DestinyVersion::Destiny2Shadowkeep => EngineVersion::TigerD2v1,
253 DestinyVersion::Destiny2BeyondLight
254 | DestinyVersion::Destiny2WitchQueen
255 | DestinyVersion::Destiny2Lightfall
256 | DestinyVersion::Destiny2TheFinalShape
257 | DestinyVersion::Destiny2TheEdgeOfFate
258 | DestinyVersion::Destiny2Renegades => EngineVersion::TigerD2v2,
259 }
260 }
261}
262
263impl Version for DestinyVersion {
264 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
265 Ok(match self {
266 DestinyVersion::DestinyInternalAlpha => Arc::new(PackageD1InternalAlpha::open(path)?),
267 DestinyVersion::DestinyFirstLookAlpha => Arc::new(PackageD1RiseOfIron::open(path)?),
268 DestinyVersion::DestinyTheTakenKing => Arc::new(PackageD1Legacy::open(path)?),
269 DestinyVersion::DestinyRiseOfIron => Arc::new(PackageD1RiseOfIron::open(path)?),
270 DestinyVersion::Destiny2Beta => Arc::new(PackageD2Beta::open(path)?),
271
272 DestinyVersion::Destiny2Forsaken | DestinyVersion::Destiny2Shadowkeep => {
273 Arc::new(PackageD2PreBL::open(path)?)
274 }
275
276 DestinyVersion::Destiny2BeyondLight
277 | DestinyVersion::Destiny2WitchQueen
278 | DestinyVersion::Destiny2Lightfall
279 | DestinyVersion::Destiny2TheFinalShape
280 | DestinyVersion::Destiny2TheEdgeOfFate
281 | DestinyVersion::Destiny2Renegades => {
282 Arc::new(PackageD2BeyondLight::open(path, *self)?)
283 }
284 })
285 }
286
287 fn endian(&self) -> Endian {
288 match self {
289 DestinyVersion::DestinyInternalAlpha | DestinyVersion::DestinyTheTakenKing => {
290 Endian::Big
291 }
292 _ => Endian::Little,
293 }
294 }
295
296 fn name(&self) -> &'static str {
297 match self {
298 DestinyVersion::DestinyInternalAlpha => "Destiny X360 Internal Alpha",
299 DestinyVersion::DestinyFirstLookAlpha => "Destiny First Look Alpha",
300 DestinyVersion::DestinyTheTakenKing => "Destiny: The Taken King",
301 DestinyVersion::DestinyRiseOfIron => "Destiny: Rise of Iron",
302 DestinyVersion::Destiny2Beta => "Destiny 2: Beta",
303 DestinyVersion::Destiny2Forsaken => "Destiny 2: Forsaken",
304 DestinyVersion::Destiny2Shadowkeep => "Destiny 2: Shadowkeep",
305 DestinyVersion::Destiny2BeyondLight => "Destiny 2: Beyond Light",
306 DestinyVersion::Destiny2WitchQueen => "Destiny 2: Witch Queen",
307 DestinyVersion::Destiny2Lightfall => "Destiny 2: Lightfall",
308 DestinyVersion::Destiny2TheFinalShape => "Destiny 2: The Final Shape",
309 DestinyVersion::Destiny2TheEdgeOfFate => "Destiny 2: The Edge of Fate",
310 DestinyVersion::Destiny2Renegades => "Destiny 2: Renegades",
311 }
312 }
313}
314
315#[derive(serde::Serialize, serde::Deserialize, PartialEq, PartialOrd, Debug, Clone, Copy)]
317pub enum EngineVersion {
318 TigerD1Indev = 1,
319 TigerD1Alpha = 5,
320 TigerD1v1 = 100,
322 TigerD1v2 = 101,
324
325 TigerD2v1 = 200,
327 TigerD2v2 = 201,
329
330 TigerGoliath = 300,
332}