1use std::fmt;
6use tact_parser::download::DownloadManifest;
7use tact_parser::encoding::EncodingFile;
8use tact_parser::install::InstallManifest;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub struct ContentKey(pub Vec<u8>);
13
14impl ContentKey {
15 pub fn new(data: Vec<u8>) -> Self {
16 Self(data)
17 }
18
19 pub fn as_bytes(&self) -> &[u8] {
20 &self.0
21 }
22
23 pub fn to_hex(&self) -> String {
24 hex::encode(&self.0)
25 }
26}
27
28impl fmt::Display for ContentKey {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 write!(f, "CKey:{}", self.to_hex())
31 }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct EncodingKey(pub Vec<u8>);
37
38impl EncodingKey {
39 pub fn new(data: Vec<u8>) -> Self {
40 Self(data)
41 }
42
43 pub fn as_bytes(&self) -> &[u8] {
44 &self.0
45 }
46
47 pub fn to_hex(&self) -> String {
48 hex::encode(&self.0)
49 }
50}
51
52impl fmt::Display for EncodingKey {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 write!(f, "EKey:{}", self.to_hex())
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct InstallManifestEntry {
61 pub path: String,
63 pub content_key: ContentKey,
65 pub size: usize,
67}
68
69#[derive(Debug, Clone)]
71pub struct DownloadManifestEntry {
72 pub generated_path: String,
74 pub encoding_key: EncodingKey,
76 pub compressed_size: usize,
78 pub priority: i32,
80}
81
82#[derive(Debug)]
84pub enum TypedManifest {
85 Install(TypedInstallManifest),
86 Download(TypedDownloadManifest),
87}
88
89#[derive(Debug)]
91pub struct TypedInstallManifest {
92 entries: Vec<InstallManifestEntry>,
93}
94
95#[derive(Debug)]
97pub struct TypedDownloadManifest {
98 entries: Vec<DownloadManifestEntry>,
99}
100
101impl TypedInstallManifest {
102 pub fn from_raw(manifest: InstallManifest) -> Self {
103 let entries = manifest
104 .entries
105 .iter()
106 .map(|entry| InstallManifestEntry {
107 path: entry.path.clone(),
108 content_key: ContentKey::new(entry.ckey.clone()),
109 size: entry.size as usize,
110 })
111 .collect();
112
113 Self { entries }
114 }
115
116 pub fn entries(&self) -> &[InstallManifestEntry] {
117 &self.entries
118 }
119
120 pub fn entries_requiring_encoding_lookup(&self) -> Vec<&InstallManifestEntry> {
122 self.entries.iter().collect()
123 }
124}
125
126impl TypedDownloadManifest {
127 pub fn from_raw(manifest: DownloadManifest) -> Self {
128 let entries = manifest
129 .entries
130 .iter()
131 .enumerate()
132 .map(|(i, (ekey, entry))| DownloadManifestEntry {
133 generated_path: format!("data/{:08x}", i),
134 encoding_key: EncodingKey::new(ekey.clone()),
135 compressed_size: entry.compressed_size as usize,
136 priority: entry.priority as i32,
137 })
138 .collect();
139
140 Self { entries }
141 }
142
143 pub fn entries(&self) -> &[DownloadManifestEntry] {
144 &self.entries
145 }
146
147 pub fn entries_with_encoding_keys(&self) -> Vec<&DownloadManifestEntry> {
149 self.entries.iter().collect()
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct DownloadableFile {
156 pub path: String,
158 pub encoding_key: EncodingKey,
160 pub content_key: Option<ContentKey>,
162 pub size: usize,
164 pub source: ManifestSource,
166}
167
168#[derive(Debug, Clone, Copy)]
169pub enum ManifestSource {
170 InstallManifest,
171 DownloadManifest,
172}
173
174pub struct TypedKeyResolver<'a> {
176 encoding_file: &'a EncodingFile,
177}
178
179impl<'a> TypedKeyResolver<'a> {
180 pub fn new(encoding_file: &'a EncodingFile) -> Self {
181 Self { encoding_file }
182 }
183
184 pub fn resolve_install_entry(
186 &self,
187 entry: &InstallManifestEntry,
188 ) -> Result<DownloadableFile, String> {
189 let encoding_entry = self
191 .encoding_file
192 .lookup_by_ckey(entry.content_key.as_bytes())
193 .ok_or_else(|| format!("No encoding entry for {}", entry.content_key))?;
194
195 let encoding_key = encoding_entry
196 .encoding_keys
197 .first()
198 .ok_or_else(|| format!("No encoding keys for {}", entry.content_key))?;
199
200 Ok(DownloadableFile {
201 path: entry.path.clone(),
202 encoding_key: EncodingKey::new(encoding_key.clone()),
203 content_key: Some(entry.content_key.clone()),
204 size: self
205 .encoding_file
206 .get_file_size(entry.content_key.as_bytes())
207 .map(|s| s as usize)
208 .unwrap_or(entry.size),
209 source: ManifestSource::InstallManifest,
210 })
211 }
212
213 pub fn resolve_download_entry(
215 &self,
216 entry: &DownloadManifestEntry,
217 ) -> Result<DownloadableFile, String> {
218 let content_key = self
220 .encoding_file
221 .lookup_by_ekey(entry.encoding_key.as_bytes())
222 .map(|ckey| ContentKey::new(ckey.clone()));
223
224 let size = content_key
225 .as_ref()
226 .and_then(|ckey| self.encoding_file.get_file_size(ckey.as_bytes()))
227 .map(|s| s as usize)
228 .unwrap_or(entry.compressed_size);
229
230 Ok(DownloadableFile {
231 path: entry.generated_path.clone(),
232 encoding_key: entry.encoding_key.clone(),
233 content_key,
234 size,
235 source: ManifestSource::DownloadManifest,
236 })
237 }
238
239 pub fn resolve_manifest(
241 &self,
242 manifest: &TypedManifest,
243 ) -> Vec<Result<DownloadableFile, String>> {
244 match manifest {
245 TypedManifest::Install(m) => m
246 .entries()
247 .iter()
248 .map(|e| self.resolve_install_entry(e))
249 .collect(),
250 TypedManifest::Download(m) => m
251 .entries()
252 .iter()
253 .map(|e| self.resolve_download_entry(e))
254 .collect(),
255 }
256 }
257}
258
259#[derive(Debug, Clone, Copy)]
261pub enum InstallStrategy {
262 UseInstallManifest,
264 UseDownloadManifest,
266 Auto,
268}
269
270impl InstallStrategy {
271 pub fn select_manifest(self, install_type: InstallType) -> ManifestChoice {
273 match self {
274 Self::UseInstallManifest => ManifestChoice::Install,
275 Self::UseDownloadManifest => ManifestChoice::Download,
276 Self::Auto => {
277 match install_type {
278 InstallType::Minimal => ManifestChoice::Download, InstallType::Full => ManifestChoice::Install, InstallType::Custom(_) => ManifestChoice::Download, }
282 }
283 }
284 }
285}
286
287#[derive(Debug, Clone, Copy)]
288pub enum ManifestChoice {
289 Install,
290 Download,
291}
292
293#[derive(Debug, Clone, Copy)]
295pub enum InstallType {
296 Minimal,
297 Full,
298 Custom(i32), }