1use crate::resource::resource_info::ResourceInfo;
2use crate::resource::resource_package::ReferenceType::{INSTALL, NORMAL, WEAK};
3use binrw::{binrw, parser, BinRead, BinReaderExt, BinResult};
4use bitfield_struct::bitfield;
5use indexmap::IndexMap;
6use itertools::Itertools;
7use lzzzz::lz4;
8use memmap2::Mmap;
9use std::fs::File;
10use std::io::{Cursor, Read, Seek, SeekFrom};
11use std::iter::zip;
12use std::path::{Path, PathBuf};
13use std::{fmt, io};
14use thiserror::Error;
15
16use crate::resource::runtime_resource_id::RuntimeResourceID;
17
18#[derive(Debug, Error)]
19pub enum ResourcePackageError {
20 #[error("Error opening the file: {0}")]
21 IoError(#[from] io::Error),
22
23 #[error("Couldn't find the requested resource inside of the given resource package")]
24 ResourceNotFound,
25
26 #[error("Parsing error: {0}")]
27 ParsingError(#[from] binrw::Error),
28
29 #[error("Resource package has no source")]
30 NoSource,
31
32 #[error("LZ4 decompression error: {0}")]
33 Lz4DecompressionError(#[from] lzzzz::Error),
34}
35
36pub enum ResourcePackageSource {
37 File(PathBuf),
38 Memory(Vec<u8>),
39}
40
41pub enum PackageVersion {
46 RPKGv1,
47 RPKGv2,
48}
49
50#[allow(dead_code)]
51#[binrw]
52#[brw(little, import(is_patch: bool))]
53pub struct ResourcePackage {
54 #[brw(ignore)]
55 pub(crate) source: Option<ResourcePackageSource>,
56
57 pub(crate) magic: [u8; 4],
58
59 #[br(if (magic == *b"2KPR"))]
60 #[bw(if (magic == b"2KPR"))]
61 pub(crate) metadata: Option<PackageMetadata>,
62
63 pub(crate) header: PackageHeader,
64
65 #[brw(if(is_patch))]
66 pub(crate) unneeded_resource_count: u32,
67
68 #[brw(if(is_patch))]
69 #[br(count = unneeded_resource_count, map = |ids: Vec<u64>| {
70 match unneeded_resource_count{
71 0 => None,
72 _ => Some(ids.into_iter().map(RuntimeResourceID::from).collect::<Vec<_>>()),
73 }
74 })]
75 pub(crate) unneeded_resources: Option<Vec<RuntimeResourceID>>,
76
77 #[br(parse_with = resource_parser, args(header.file_count))]
78 #[bw(write_with = empty_writer)]
79 pub(crate) resources: IndexMap<RuntimeResourceID, ResourceInfo>,
80}
81
82#[parser(reader: reader, endian)]
83fn resource_parser(file_count: u32) -> BinResult<IndexMap<RuntimeResourceID, ResourceInfo>> {
84 let mut map = IndexMap::new();
85 let mut resource_entries = vec![];
86 for _ in 0..file_count {
87 resource_entries.push(PackageOffsetInfo::read_options(reader, endian, ())?);
88 }
89
90 let position = reader.stream_position()?;
91 let has_states_size = !(0..file_count).any(|_|{
92 if let Ok(probe) = ResourceHeaderProbe::read_options(reader, endian, ()){
93 probe.states_chunk_size != 0 || (!probe.resource_type.is_ascii())
94 } else { true }
95 });
96
97 reader.seek(SeekFrom::Start(position))?;
98 let mut resource_metadata = vec![];
99 for _ in 0..file_count {
100 resource_metadata.push(ResourceHeader::read_options(reader, endian, (has_states_size,))?);
101 }
102
103 let resources = zip(resource_entries, resource_metadata)
104 .map(|(entry, header)| ResourceInfo { entry, header })
105 .collect::<Vec<ResourceInfo>>();
106
107 for resource in resources {
108 map.insert(resource.entry.runtime_resource_id, resource);
109 }
110
111 Ok(map)
112}
113
114impl ResourcePackage {
115 pub fn from_file<P: AsRef<Path> + Copy>(package_path: P) -> Result<Self, ResourcePackageError> {
120 let file = File::open(package_path).map_err(ResourcePackageError::IoError)?;
121 let mmap = unsafe { Mmap::map(&file).map_err(ResourcePackageError::IoError)? };
122 let mut reader = Cursor::new(&mmap[..]);
123
124 let package_path = package_path.as_ref();
125
126 let is_patch = package_path
127 .file_name()
128 .and_then(|f| f.to_str())
129 .map(|s| s.contains("patch"))
130 .unwrap_or(false);
131
132 let mut package = reader
133 .read_ne_args::<ResourcePackage>((is_patch,))
134 .map_err(ResourcePackageError::ParsingError)?;
135
136 package.source = Some(ResourcePackageSource::File(package_path.to_path_buf()));
137
138 Ok(package)
139 }
140
141 pub fn from_memory(data: Vec<u8>, is_patch: bool) -> Result<Self, ResourcePackageError> {
147 let mut reader = Cursor::new(&data);
148 let mut package = reader
149 .read_ne_args::<ResourcePackage>((is_patch,))
150 .map_err(ResourcePackageError::ParsingError)?;
151
152 package.source = Some(ResourcePackageSource::Memory(data));
153
154 Ok(package)
155 }
156
157 pub fn version(&self) -> PackageVersion {
159 match &self.magic {
160 b"GKPR" => PackageVersion::RPKGv1,
161 b"2KPR" => PackageVersion::RPKGv2,
162 _ => panic!("Unknown package version"),
163 }
164 }
165
166 pub fn source(&self) -> Option<&ResourcePackageSource> {
168 self.source.as_ref()
169 }
170
171 pub fn resources(&self) -> &IndexMap<RuntimeResourceID, ResourceInfo> {
173 &self.resources
174 }
175
176 pub fn has_legacy_references(&self) -> bool {
178 self.resources.iter().any(|(_, resource)| {
179 resource.references().iter().any(|(_, flags)| match flags {
180 ResourceReferenceFlags::Legacy(_) => true,
181 ResourceReferenceFlags::Standard(_) => false,
182 })
183 })
184 }
185
186 pub fn has_unneeded_resource(&self, rrid: &RuntimeResourceID) -> bool {
191 if let Some(unneeded_resources) = &self.unneeded_resources {
192 unneeded_resources.contains(rrid)
193 } else {
194 false
195 }
196 }
197
198 pub fn unneeded_resource_ids(&self) -> Vec<&RuntimeResourceID> {
200 match &self.unneeded_resources {
201 None => {
202 vec![]
203 }
204 Some(val) => val.iter().collect(),
205 }
206 }
207
208 pub fn read_resource(&self, rrid: &RuntimeResourceID) -> Result<Vec<u8>, ResourcePackageError> {
213 let resource = self
214 .resources
215 .get(rrid)
216 .ok_or(ResourcePackageError::ResourceNotFound)?;
217
218 let final_size = resource
219 .compressed_size()
220 .unwrap_or(resource.header.data_size);
221
222 let is_lz4ed = resource.is_compressed();
223 let is_scrambled = resource.is_scrambled();
224
225 let mut buffer = match &self.source {
227 Some(ResourcePackageSource::File(package_path)) => {
228 let mut file = File::open(package_path).map_err(ResourcePackageError::IoError)?;
229 file.seek(io::SeekFrom::Start(resource.entry.data_offset))
230 .map_err(ResourcePackageError::IoError)?;
231
232 let mut buffer = vec![0; final_size as usize];
233 file.read_exact(&mut buffer)
234 .map_err(ResourcePackageError::IoError)?;
235 buffer
236 }
237
238 Some(ResourcePackageSource::Memory(data)) => {
239 let start_offset = resource.entry.data_offset as usize;
240 let end_offset = start_offset + final_size as usize;
241 data[start_offset..end_offset].to_vec()
242 }
243
244 None => return Err(ResourcePackageError::NoSource),
245 };
246
247 if is_scrambled {
248 let str_xor = [0xdc, 0x45, 0xa6, 0x9c, 0xd3, 0x72, 0x4c, 0xab];
249 buffer.iter_mut().enumerate().for_each(|(index, byte)| {
250 *byte ^= str_xor[index % str_xor.len()];
251 });
252 }
253
254 if is_lz4ed {
255 let mut decompressed_buffer = vec![0; resource.header.data_size as usize];
256 lz4::decompress(&buffer, &mut decompressed_buffer)?;
257 return Ok(decompressed_buffer);
258 }
259
260 Ok(buffer)
261 }
262}
263
264#[binrw]
265#[brw(repr(u8))]
266#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
267pub enum ChunkType {
268 #[default]
269 Standard,
270 Addon,
271}
272
273#[allow(dead_code)]
274#[binrw]
275pub struct PackageMetadata {
276 pub unknown: u32,
277 pub chunk_id: u8,
278 pub chunk_type: ChunkType,
279 pub patch_id: u8,
280 pub language_tag: [u8; 2], }
282
283#[allow(dead_code)]
284#[binrw]
285pub struct PackageHeader {
286 pub file_count: u32,
287 pub offset_table_size: u32,
288 pub metadata_table_size: u32,
289}
290
291#[bitfield(u32)]
292#[binrw]
293#[derive(Eq, PartialEq)]
294pub struct PackageOffsetFlags {
295 #[bits(31)]
296 pub compressed_size: u32,
297 pub is_scrambled: bool,
298}
299
300#[allow(dead_code)]
301#[derive(Copy, Clone)]
302#[binrw]
303#[brw(little)]
304pub struct PackageOffsetInfo {
305 pub(crate) runtime_resource_id: RuntimeResourceID,
306 pub(crate) data_offset: u64,
307 pub(crate) flags: PackageOffsetFlags,
308}
309
310impl PackageOffsetInfo {
311 pub fn is_scrambled(&self) -> bool {
312 self.flags.is_scrambled()
313 }
314
315 pub fn compressed_size(&self) -> Option<u32> {
316 match self.flags.compressed_size() {
317 0 => None,
318 n => Some(n),
319 }
320 }
321}
322
323#[allow(dead_code)]
324#[derive(Clone, PartialEq, Eq)]
325#[binrw]
326#[brw(little)]
327#[br(import(has_states_size: bool))]
328pub struct ResourceHeader {
329 pub(crate) resource_type: [u8; 4],
330 pub(crate) references_chunk_size: u32,
331 #[br(calc=0)]
332 pub(crate) states_chunk_size: u32,
333
334 #[br(pad_before(if has_states_size {4} else {0}))]
335 pub(crate) data_size: u32,
336 pub(crate) system_memory_requirement: u32,
337 pub(crate) video_memory_requirement: u32,
338
339 #[br(if (references_chunk_size > 0), parse_with = read_references)]
340 #[bw(write_with = empty_writer)]
341 pub references: Vec<(RuntimeResourceID, ResourceReferenceFlags)>,
342}
343
344#[allow(dead_code)]
345#[derive(Default, Clone, PartialEq, Eq, BinRead)]
346struct ResourceHeaderProbe {
347 pub(crate) resource_type: [u8; 4],
348 references_chunk_size: u32,
349 #[br(pad_after(references_chunk_size + 12))]
350 pub(crate) states_chunk_size: u32,
351}
352
353#[bitfield(u8)]
354#[binrw]
355#[bw(map = |&x| Self::into_bits(x))]
356#[derive(Eq, PartialEq)]
357pub struct ResourceReferenceFlagsLegacy {
358 pub __: bool,
359 pub runtime_acquired: bool,
360 pub weak_reference: bool,
361 pub __: bool,
362 pub type_of_streaming_entity: bool,
363 pub state_streamed: bool,
364 pub media_streamed: bool,
365 pub install_dependency: bool,
366}
367
368#[bitfield(u8)]
369#[binrw]
370#[derive(Eq, PartialEq)]
371#[bw(map = |&x: &Self| x.into_bits())]
372pub struct ResourceReferenceFlagsStandard {
373 #[bits(5, default = 0x1F)]
374 pub language_code: u8,
375 pub runtime_acquired: bool,
376 #[bits(2)]
377 pub reference_type: ReferenceType,
378}
379
380#[derive(Debug, Copy, Clone, Eq, PartialEq)]
381pub enum ReferenceType {
382 INSTALL = 0,
383 NORMAL = 1,
384 WEAK = 2,
385}
386
387impl ReferenceType {
388 const fn into_bits(self) -> u16 {
389 self as _
390 }
391 const fn from_bits(value: u8) -> Self {
392 match value {
393 0 => INSTALL,
394 1 => NORMAL,
395 2 => WEAK,
396 _ => NORMAL,
397 }
398 }
399}
400
401#[derive(Copy, Clone, Eq, PartialEq, Debug)]
403pub enum ResourceReferenceFlags {
404 Legacy(ResourceReferenceFlagsLegacy),
405 Standard(ResourceReferenceFlagsStandard),
406}
407
408impl From<ResourceReferenceFlags> for ResourceReferenceFlagsLegacy {
409 fn from(value: ResourceReferenceFlags) -> Self {
410 match value {
411 ResourceReferenceFlags::Legacy(b) => b,
412 ResourceReferenceFlags::Standard(b) => ResourceReferenceFlagsLegacy::new()
413 .with_runtime_acquired(b.runtime_acquired())
414 .with_weak_reference(b.reference_type() == WEAK)
415 .with_install_dependency(b.reference_type() == INSTALL),
416 }
417 }
418}
419
420impl From<ResourceReferenceFlags> for ResourceReferenceFlagsStandard {
421 fn from(value: ResourceReferenceFlags) -> Self {
422 match value {
423 ResourceReferenceFlags::Standard(b) => b,
424 ResourceReferenceFlags::Legacy(b) => ResourceReferenceFlagsStandard::new()
425 .with_language_code(0x1F)
426 .with_runtime_acquired(b.runtime_acquired())
427 .with_reference_type(value.reference_type()),
428 }
429 }
430}
431
432impl ResourceReferenceFlags {
433 pub fn to_legacy(&self) -> ResourceReferenceFlagsLegacy {
443 (*self).into()
444 }
445
446 pub fn to_standard(&self) -> ResourceReferenceFlagsStandard {
456 (*self).into()
457 }
458
459 pub fn as_byte(&self) -> u8 {
460 match self {
461 ResourceReferenceFlags::Legacy(x) => x.into_bits(),
462 ResourceReferenceFlags::Standard(x) => x.into_bits(),
463 }
464 }
465}
466
467impl ResourceReferenceFlags {
468 pub fn language_code(&self) -> u8 {
469 match self {
470 ResourceReferenceFlags::Legacy(_) => 0x1F,
471 ResourceReferenceFlags::Standard(b) => b.language_code(),
472 }
473 }
474
475 pub fn is_acquired(&self) -> bool {
476 match self {
477 ResourceReferenceFlags::Legacy(b) => b.runtime_acquired(),
478 ResourceReferenceFlags::Standard(b) => b.runtime_acquired(),
479 }
480 }
481
482 pub fn reference_type(&self) -> ReferenceType {
483 match self {
484 ResourceReferenceFlags::Legacy(b) => match b.install_dependency() {
485 true => INSTALL,
486 false if b.weak_reference() => WEAK,
487 false => NORMAL,
488 },
489 ResourceReferenceFlags::Standard(b) => b.reference_type(),
490 }
491 }
492}
493
494#[bitfield(u32)]
495#[binrw]
496#[derive(Eq, PartialEq)]
497#[bw(map = |&x: &Self| x.into_bits())]
498pub struct ResourceReferenceCountAndFlags {
499 #[bits(30)]
500 pub reference_count: u32,
501 pub is_new_format: bool,
502 pub always_true: bool,
503}
504
505#[parser(reader)]
506fn read_references() -> BinResult<Vec<(RuntimeResourceID, ResourceReferenceFlags)>> {
507 let reference_count_and_flag = reader.read_le::<ResourceReferenceCountAndFlags>()?;
508 let reference_count = reference_count_and_flag.reference_count();
509 let is_new_format = reference_count_and_flag.is_new_format();
510
511 let arrays = if is_new_format {
512 let flags: Vec<ResourceReferenceFlags> = (0..reference_count)
513 .map(|_| reader.read_le::<ResourceReferenceFlagsStandard>())
514 .map_ok(ResourceReferenceFlags::Standard)
515 .collect::<BinResult<Vec<_>>>()?;
516 let rrids: Vec<RuntimeResourceID> = (0..reference_count)
517 .map(|_| u64::read_le(reader))
518 .map_ok(RuntimeResourceID::from)
519 .collect::<BinResult<Vec<_>>>()?;
520 (rrids, flags)
521 } else {
522 let rrids: Vec<RuntimeResourceID> = (0..reference_count)
523 .map(|_| u64::read_le(reader))
524 .map_ok(RuntimeResourceID::from)
525 .collect::<BinResult<Vec<_>>>()?;
526 let flags: Vec<ResourceReferenceFlags> = (0..reference_count)
527 .map(|_| reader.read_le::<ResourceReferenceFlagsLegacy>())
528 .map_ok(ResourceReferenceFlags::Legacy)
529 .collect::<BinResult<Vec<_>>>()?;
530 (rrids, flags)
531 };
532
533 Ok(arrays.0.into_iter().zip(arrays.1).collect::<Vec<(_, _)>>())
534}
535
536impl fmt::Display for PackageOffsetInfo {
537 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
538 write!(
539 f,
540 "resource {} is {} bytes at {}",
541 self.runtime_resource_id.to_hex_string(),
542 self.flags.compressed_size(),
543 self.data_offset
544 )
545 }
546}
547
548impl fmt::Display for ResourceHeader {
549 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
550 let mut res_type = self.resource_type;
551 res_type.reverse();
552 write!(
553 f,
554 "type: {}, reference_num: {}, size: {}, num_reqs: ({} {})",
555 std::str::from_utf8(&res_type).unwrap(),
556 self.references_chunk_size,
557 self.data_size,
558 self.system_memory_requirement,
559 self.video_memory_requirement
560 )
561 }
562}
563
564#[binrw::writer]
565fn empty_writer<T>(_: &T) -> BinResult<()> {
566 Ok(())
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573 #[test]
574 fn test_flag_conversion_801f() {
575 let flag_v1 = ResourceReferenceFlagsLegacy::from_bits(0x80);
576 let flag_v2 = ResourceReferenceFlagsStandard::from_bits(0x1F);
577
578 assert_eq!(
579 flag_v1,
580 ResourceReferenceFlags::Standard(flag_v2).to_legacy()
581 );
582 assert_eq!(
583 flag_v2,
584 ResourceReferenceFlags::Legacy(flag_v1).to_standard()
585 );
586 }
587
588 #[test]
589 fn test_flag_conversion_005f() {
590 let flag_v1 = ResourceReferenceFlagsLegacy::from_bits(0x00);
591 let flag_v2 = ResourceReferenceFlagsStandard::from_bits(0x5F);
592
593 assert_eq!(
594 flag_v1,
595 ResourceReferenceFlags::Standard(flag_v2).to_legacy()
596 );
597 assert_eq!(
598 flag_v2,
599 ResourceReferenceFlags::Legacy(flag_v1).to_standard()
600 );
601 }
602}