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 [0u8; 16]
24 }
25
26 fn aes_key_1(&self) -> [u8; 16] {
27 [0u8; 16]
28 }
29
30 fn aes_nonce_base(&self) -> [u8; 12] {
31 [0u8; 12]
32 }
33}
34
35#[derive(serde::Serialize, serde::Deserialize, PartialEq, PartialOrd, Debug, Clone, Copy)]
36pub enum GameVersion {
37 Destiny(DestinyVersion),
38 Marathon(MarathonVersion),
39}
40
41impl Version for GameVersion {
42 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
43 match self {
44 Self::Destiny(v) => v.open(path),
45 Self::Marathon(v) => v.open(path),
46 }
47 }
48
49 fn endian(&self) -> Endian {
50 match self {
51 Self::Destiny(v) => v.endian(),
52 Self::Marathon(v) => v.endian(),
53 }
54 }
55
56 fn name(&self) -> &'static str {
57 match self {
58 Self::Destiny(v) => v.name(),
59 Self::Marathon(v) => v.name(),
60 }
61 }
62
63 fn aes_key_0(&self) -> [u8; 16] {
64 match self {
65 Self::Destiny(v) => v.aes_key_0(),
66 Self::Marathon(v) => v.aes_key_0(),
67 }
68 }
69
70 fn aes_key_1(&self) -> [u8; 16] {
71 match self {
72 Self::Destiny(v) => v.aes_key_1(),
73 Self::Marathon(v) => v.aes_key_1(),
74 }
75 }
76
77 fn aes_nonce_base(&self) -> [u8; 12] {
78 match self {
79 Self::Destiny(v) => v.aes_nonce_base(),
80 Self::Marathon(v) => v.aes_nonce_base(),
81 }
82 }
83}
84
85impl clap::ValueEnum for GameVersion {
86 fn value_variants<'a>() -> &'a [Self] {
87 &[
88 Self::Destiny(DestinyVersion::DestinyInternalAlpha),
89 Self::Destiny(DestinyVersion::DestinyFirstLookAlpha),
90 Self::Destiny(DestinyVersion::DestinyTheTakenKing),
91 Self::Destiny(DestinyVersion::DestinyRiseOfIron),
92 Self::Destiny(DestinyVersion::Destiny2Beta),
93 Self::Destiny(DestinyVersion::Destiny2Forsaken),
94 Self::Destiny(DestinyVersion::Destiny2Shadowkeep),
95 Self::Destiny(DestinyVersion::Destiny2BeyondLight),
96 Self::Destiny(DestinyVersion::Destiny2WitchQueen),
97 Self::Destiny(DestinyVersion::Destiny2Lightfall),
98 Self::Destiny(DestinyVersion::Destiny2TheFinalShape),
99 Self::Marathon(MarathonVersion::MarathonAlpha),
100 ]
101 }
102
103 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
104 match self {
105 Self::Destiny(v) => v.to_possible_value(),
106 Self::Marathon(v) => v.to_possible_value(),
107 }
108 }
109}
110
111#[derive(
112 serde::Serialize, serde::Deserialize, clap::ValueEnum, PartialEq, PartialOrd, Debug, Clone, Copy,
113)]
114pub enum MarathonVersion {
115 #[value(name = "ma_alpha")]
117 MarathonAlpha = 500,
118}
119
120impl Version for MarathonVersion {
121 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
122 unimplemented!()
123 }
124
125 fn endian(&self) -> Endian {
126 Endian::Little
127 }
128
129 fn name(&self) -> &'static str {
130 match self {
131 MarathonVersion::MarathonAlpha => "Marathon Closed Alpha",
132 }
133 }
134}
135
136#[derive(
137 serde::Serialize, serde::Deserialize, clap::ValueEnum, PartialEq, PartialOrd, Debug, Clone, Copy,
138)]
139pub enum DestinyVersion {
140 #[value(name = "d1_devalpha")]
142 DestinyInternalAlpha = 1_0500,
143
144 #[value(name = "d1_fla")]
146 DestinyFirstLookAlpha = 1_0800,
147
148 #[value(name = "d1_ttk")]
150 DestinyTheTakenKing = 1_2000,
151
152 #[value(name = "d1_roi")]
154 DestinyRiseOfIron = 1_2400,
155
156 #[value(name = "d2_beta")]
158 Destiny2Beta = 2_1000,
159
160 #[value(name = "d2_fs")]
162 Destiny2Forsaken = 2_2000,
163
164 #[value(name = "d2_sk")]
166 Destiny2Shadowkeep = 2_2600,
167
168 #[value(name = "d2_bl")]
170 Destiny2BeyondLight = 2_3000,
171
172 #[value(name = "d2_wq")]
174 Destiny2WitchQueen = 2_4000,
175
176 #[value(name = "d2_lf")]
178 Destiny2Lightfall = 2_7000,
179
180 #[value(name = "d2_tfs")]
182 Destiny2TheFinalShape = 2_8000,
183}
184
185impl DestinyVersion {
186 pub fn is_d1(&self) -> bool {
187 *self <= DestinyVersion::DestinyRiseOfIron
188 }
189
190 pub fn is_d2(&self) -> bool {
191 *self >= DestinyVersion::Destiny2Beta
192 }
193
194 pub fn is_prebl(&self) -> bool {
195 DestinyVersion::Destiny2Beta <= *self && *self <= DestinyVersion::Destiny2Shadowkeep
196 }
197}
198
199impl Version for DestinyVersion {
200 fn open(&self, path: &str) -> anyhow::Result<Arc<dyn Package>> {
201 Ok(match self {
202 DestinyVersion::DestinyInternalAlpha => Arc::new(PackageD1InternalAlpha::open(path)?),
203 DestinyVersion::DestinyFirstLookAlpha => Arc::new(PackageD1RiseOfIron::open(path)?),
204 DestinyVersion::DestinyTheTakenKing => Arc::new(PackageD1Legacy::open(path)?),
205 DestinyVersion::DestinyRiseOfIron => Arc::new(PackageD1RiseOfIron::open(path)?),
206 DestinyVersion::Destiny2Beta => Arc::new(PackageD2Beta::open(path)?),
207
208 DestinyVersion::Destiny2Forsaken | DestinyVersion::Destiny2Shadowkeep => {
209 Arc::new(PackageD2PreBL::open(path)?)
210 }
211
212 DestinyVersion::Destiny2BeyondLight
213 | DestinyVersion::Destiny2WitchQueen
214 | DestinyVersion::Destiny2Lightfall
215 | DestinyVersion::Destiny2TheFinalShape => {
216 Arc::new(PackageD2BeyondLight::open(path, *self)?)
217 }
218 })
219 }
220
221 fn endian(&self) -> Endian {
222 match self {
223 DestinyVersion::DestinyInternalAlpha | DestinyVersion::DestinyTheTakenKing => {
224 Endian::Big
225 }
226 _ => Endian::Little,
227 }
228 }
229
230 fn name(&self) -> &'static str {
231 match self {
232 DestinyVersion::DestinyInternalAlpha => "Destiny X360 Internal Alpha",
233 DestinyVersion::DestinyFirstLookAlpha => "Destiny First Look Alpha",
234 DestinyVersion::DestinyTheTakenKing => "Destiny: The Taken King",
235 DestinyVersion::DestinyRiseOfIron => "Destiny: Rise of Iron",
236 DestinyVersion::Destiny2Beta => "Destiny 2: Beta",
237 DestinyVersion::Destiny2Forsaken => "Destiny 2: Forsaken",
238 DestinyVersion::Destiny2Shadowkeep => "Destiny 2: Shadowkeep",
239 DestinyVersion::Destiny2BeyondLight => "Destiny 2: Beyond Light",
240 DestinyVersion::Destiny2WitchQueen => "Destiny 2: Witch Queen",
241 DestinyVersion::Destiny2Lightfall => "Destiny 2: Lightfall",
242 DestinyVersion::Destiny2TheFinalShape => "Destiny 2: The Final Shape",
243 }
244 }
245
246 fn aes_key_0(&self) -> [u8; 16] {
247 [
248 0xD6, 0x2A, 0xB2, 0xC1, 0x0C, 0xC0, 0x1B, 0xC5, 0x35, 0xDB, 0x7B, 0x86, 0x55, 0xC7,
249 0xDC, 0x3B,
250 ]
251 }
252
253 fn aes_key_1(&self) -> [u8; 16] {
254 [
255 0x3A, 0x4A, 0x5D, 0x36, 0x73, 0xA6, 0x60, 0x58, 0x7E, 0x63, 0xE6, 0x76, 0xE4, 0x08,
256 0x92, 0xB5,
257 ]
258 }
259
260 fn aes_nonce_base(&self) -> [u8; 12] {
261 [
262 0x84, 0xDF, 0x11, 0xC0, 0xAC, 0xAB, 0xFA, 0x20, 0x33, 0x11, 0x26, 0x99,
263 ]
264 }
265}