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 Version for GameVersion {
50 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
51 match self {
52 Self::Destiny(v) => v.open(path),
53 Self::Marathon(v) => v.open(path),
54 }
55 }
56
57 fn endian(&self) -> Endian {
58 match self {
59 Self::Destiny(v) => v.endian(),
60 Self::Marathon(v) => v.endian(),
61 }
62 }
63
64 fn name(&self) -> &'static str {
65 match self {
66 Self::Destiny(v) => v.name(),
67 Self::Marathon(v) => v.name(),
68 }
69 }
70
71 fn aes_key_0(&self) -> [u8; 16] {
72 match self {
73 Self::Destiny(v) => v.aes_key_0(),
74 Self::Marathon(v) => v.aes_key_0(),
75 }
76 }
77
78 fn aes_key_1(&self) -> [u8; 16] {
79 match self {
80 Self::Destiny(v) => v.aes_key_1(),
81 Self::Marathon(v) => v.aes_key_1(),
82 }
83 }
84
85 fn aes_nonce_base(&self) -> [u8; 12] {
86 match self {
87 Self::Destiny(v) => v.aes_nonce_base(),
88 Self::Marathon(v) => v.aes_nonce_base(),
89 }
90 }
91}
92
93impl clap::ValueEnum for GameVersion {
94 fn value_variants<'a>() -> &'a [Self] {
95 &[
96 Self::Destiny(DestinyVersion::DestinyInternalAlpha),
97 Self::Destiny(DestinyVersion::DestinyFirstLookAlpha),
98 Self::Destiny(DestinyVersion::DestinyTheTakenKing),
99 Self::Destiny(DestinyVersion::DestinyRiseOfIron),
100 Self::Destiny(DestinyVersion::Destiny2Beta),
101 Self::Destiny(DestinyVersion::Destiny2Forsaken),
102 Self::Destiny(DestinyVersion::Destiny2Shadowkeep),
103 Self::Destiny(DestinyVersion::Destiny2BeyondLight),
104 Self::Destiny(DestinyVersion::Destiny2WitchQueen),
105 Self::Destiny(DestinyVersion::Destiny2Lightfall),
106 Self::Destiny(DestinyVersion::Destiny2TheFinalShape),
107 Self::Destiny(DestinyVersion::Destiny2TheEdgeOfFate),
108 Self::Destiny(DestinyVersion::Destiny2Renegades),
109 Self::Marathon(MarathonVersion::MarathonAlpha),
110 Self::Marathon(MarathonVersion::Marathon),
111 ]
112 }
113
114 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
115 match self {
116 Self::Destiny(v) => v.to_possible_value(),
117 Self::Marathon(v) => v.to_possible_value(),
118 }
119 }
120}
121
122#[derive(
123 serde::Serialize, serde::Deserialize, clap::ValueEnum, PartialEq, PartialOrd, Debug, Clone, Copy,
124)]
125pub enum MarathonVersion {
127 #[value(name = "goliath_alpha")]
129 MarathonAlpha = 500,
130
131 #[value(name = "goliath")]
133 Marathon = 1000,
134}
135
136impl Version for MarathonVersion {
137 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
138 Ok(Arc::new(PackageD2BeyondLight::open(
140 path,
141 DestinyVersion::Destiny2TheFinalShape,
142 )?))
143 }
144
145 fn endian(&self) -> Endian {
146 Endian::Little
147 }
148
149 fn name(&self) -> &'static str {
150 match self {
151 MarathonVersion::MarathonAlpha => "Marathon Closed Alpha",
152 MarathonVersion::Marathon => "Marathon",
153 }
154 }
155}
156
157#[derive(
158 serde::Serialize, serde::Deserialize, clap::ValueEnum, PartialEq, PartialOrd, Debug, Clone, Copy,
159)]
160pub enum DestinyVersion {
161 #[value(name = "d1_devalpha")]
163 DestinyInternalAlpha = 1_0500,
164
165 #[value(name = "d1_fla")]
167 DestinyFirstLookAlpha = 1_0800,
168
169 #[value(name = "d1_ttk")]
171 DestinyTheTakenKing = 1_2000,
172
173 #[value(name = "d1_roi")]
175 DestinyRiseOfIron = 1_2400,
176
177 #[value(name = "d2_beta")]
179 Destiny2Beta = 2_1000,
180
181 #[value(name = "d2_fs")]
183 Destiny2Forsaken = 2_2000,
184
185 #[value(name = "d2_sk")]
187 Destiny2Shadowkeep = 2_2600,
188
189 #[value(name = "d2_bl")]
191 Destiny2BeyondLight = 2_3000,
192
193 #[value(name = "d2_wq")]
195 Destiny2WitchQueen = 2_4000,
196
197 #[value(name = "d2_lf")]
199 Destiny2Lightfall = 2_7000,
200
201 #[value(name = "d2_tfs")]
203 Destiny2TheFinalShape = 2_8000,
204
205 #[value(name = "d2_eof")]
207 Destiny2TheEdgeOfFate = 2_9000,
208
209 #[value(name = "d2_ren")]
211 Destiny2Renegades = 2_9500,
212}
213
214impl DestinyVersion {
215 pub fn is_d1(&self) -> bool {
216 *self <= DestinyVersion::DestinyRiseOfIron
217 }
218
219 pub fn is_d2(&self) -> bool {
220 *self >= DestinyVersion::Destiny2Beta
221 }
222
223 pub fn is_prebl(&self) -> bool {
224 DestinyVersion::Destiny2Beta <= *self && *self <= DestinyVersion::Destiny2Shadowkeep
225 }
226}
227
228impl Version for DestinyVersion {
229 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
230 Ok(match self {
231 DestinyVersion::DestinyInternalAlpha => Arc::new(PackageD1InternalAlpha::open(path)?),
232 DestinyVersion::DestinyFirstLookAlpha => Arc::new(PackageD1RiseOfIron::open(path)?),
233 DestinyVersion::DestinyTheTakenKing => Arc::new(PackageD1Legacy::open(path)?),
234 DestinyVersion::DestinyRiseOfIron => Arc::new(PackageD1RiseOfIron::open(path)?),
235 DestinyVersion::Destiny2Beta => Arc::new(PackageD2Beta::open(path)?),
236
237 DestinyVersion::Destiny2Forsaken | DestinyVersion::Destiny2Shadowkeep => {
238 Arc::new(PackageD2PreBL::open(path)?)
239 }
240
241 DestinyVersion::Destiny2BeyondLight
242 | DestinyVersion::Destiny2WitchQueen
243 | DestinyVersion::Destiny2Lightfall
244 | DestinyVersion::Destiny2TheFinalShape
245 | DestinyVersion::Destiny2TheEdgeOfFate
246 | DestinyVersion::Destiny2Renegades => {
247 Arc::new(PackageD2BeyondLight::open(path, *self)?)
248 }
249 })
250 }
251
252 fn endian(&self) -> Endian {
253 match self {
254 DestinyVersion::DestinyInternalAlpha | DestinyVersion::DestinyTheTakenKing => {
255 Endian::Big
256 }
257 _ => Endian::Little,
258 }
259 }
260
261 fn name(&self) -> &'static str {
262 match self {
263 DestinyVersion::DestinyInternalAlpha => "Destiny X360 Internal Alpha",
264 DestinyVersion::DestinyFirstLookAlpha => "Destiny First Look Alpha",
265 DestinyVersion::DestinyTheTakenKing => "Destiny: The Taken King",
266 DestinyVersion::DestinyRiseOfIron => "Destiny: Rise of Iron",
267 DestinyVersion::Destiny2Beta => "Destiny 2: Beta",
268 DestinyVersion::Destiny2Forsaken => "Destiny 2: Forsaken",
269 DestinyVersion::Destiny2Shadowkeep => "Destiny 2: Shadowkeep",
270 DestinyVersion::Destiny2BeyondLight => "Destiny 2: Beyond Light",
271 DestinyVersion::Destiny2WitchQueen => "Destiny 2: Witch Queen",
272 DestinyVersion::Destiny2Lightfall => "Destiny 2: Lightfall",
273 DestinyVersion::Destiny2TheFinalShape => "Destiny 2: The Final Shape",
274 DestinyVersion::Destiny2TheEdgeOfFate => "Destiny 2: The Edge of Fate",
275 DestinyVersion::Destiny2Renegades => "Destiny 2: Renegades",
276 }
277 }
278}