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};
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 mut resource_metadata = vec![];
91 for _ in 0..file_count {
92 resource_metadata.push(ResourceHeader::read_options(reader, endian, ())?);
93 }
94
95 let resources = zip(resource_entries, resource_metadata)
96 .map(|(entry, header)| ResourceInfo { entry, header })
97 .collect::<Vec<ResourceInfo>>();
98
99 for resource in resources {
100 map.insert(resource.entry.runtime_resource_id, resource);
101 }
102
103 Ok(map)
104}
105
106impl ResourcePackage {
107 pub fn from_file<P: AsRef<Path> + Copy>(package_path: P) -> Result<Self, ResourcePackageError> {
112 let file = File::open(package_path).map_err(ResourcePackageError::IoError)?;
113 let mmap = unsafe { Mmap::map(&file).map_err(ResourcePackageError::IoError)? };
114 let mut reader = Cursor::new(&mmap[..]);
115
116 let package_path = package_path.as_ref();
117
118 let is_patch = package_path
119 .file_name()
120 .and_then(|f| f.to_str())
121 .map(|s| s.contains("patch"))
122 .unwrap_or(false);
123
124 let mut package = reader
125 .read_ne_args::<ResourcePackage>((is_patch,))
126 .map_err(ResourcePackageError::ParsingError)?;
127
128 package.source = Some(ResourcePackageSource::File(package_path.to_path_buf()));
129
130 Ok(package)
131 }
132
133 pub fn from_memory(data: Vec<u8>, is_patch: bool) -> Result<Self, ResourcePackageError> {
139 let mut reader = Cursor::new(&data);
140 let mut package = reader
141 .read_ne_args::<ResourcePackage>((is_patch,))
142 .map_err(ResourcePackageError::ParsingError)?;
143
144 package.source = Some(ResourcePackageSource::Memory(data));
145
146 Ok(package)
147 }
148
149 pub fn version(&self) -> PackageVersion {
151 match &self.magic {
152 b"GKPR" => PackageVersion::RPKGv1,
153 b"2KPR" => PackageVersion::RPKGv2,
154 _ => panic!("Unknown package version"),
155 }
156 }
157
158 pub fn source(&self) -> Option<&ResourcePackageSource> {
160 self.source.as_ref()
161 }
162
163 pub fn resources(&self) -> &IndexMap<RuntimeResourceID, ResourceInfo> {
165 &self.resources
166 }
167
168 pub fn has_legacy_references(&self) -> bool {
170 self.resources.iter().any(|(_, resource)| {
171 resource.references().iter().any(|(_, flags)| match flags {
172 ResourceReferenceFlags::Legacy(_) => true,
173 ResourceReferenceFlags::Standard(_) => false,
174 })
175 })
176 }
177
178 pub fn has_unneeded_resource(&self, rrid: &RuntimeResourceID) -> bool {
183 if let Some(unneeded_resources) = &self.unneeded_resources {
184 unneeded_resources.contains(rrid)
185 } else {
186 false
187 }
188 }
189
190 pub fn unneeded_resource_ids(&self) -> Vec<&RuntimeResourceID> {
192 match &self.unneeded_resources {
193 None => {
194 vec![]
195 }
196 Some(val) => val.iter().collect(),
197 }
198 }
199
200 pub fn read_resource(&self, rrid: &RuntimeResourceID) -> Result<Vec<u8>, ResourcePackageError> {
205 let resource = self
206 .resources
207 .get(rrid)
208 .ok_or(ResourcePackageError::ResourceNotFound)?;
209
210 let final_size = resource
211 .compressed_size()
212 .unwrap_or(resource.header.data_size);
213
214 let is_lz4ed = resource.is_compressed();
215 let is_scrambled = resource.is_scrambled();
216
217 let mut buffer = match &self.source {
219 Some(ResourcePackageSource::File(package_path)) => {
220 let mut file = File::open(package_path).map_err(ResourcePackageError::IoError)?;
221 file.seek(io::SeekFrom::Start(resource.entry.data_offset))
222 .map_err(ResourcePackageError::IoError)?;
223
224 let mut buffer = vec![0; final_size as usize];
225 file.read_exact(&mut buffer)
226 .map_err(ResourcePackageError::IoError)?;
227 buffer
228 }
229
230 Some(ResourcePackageSource::Memory(data)) => {
231 let start_offset = resource.entry.data_offset as usize;
232 let end_offset = start_offset + final_size as usize;
233 data[start_offset..end_offset].to_vec()
234 }
235
236 None => return Err(ResourcePackageError::NoSource),
237 };
238
239 if is_scrambled {
240 let str_xor = [0xdc, 0x45, 0xa6, 0x9c, 0xd3, 0x72, 0x4c, 0xab];
241 buffer.iter_mut().enumerate().for_each(|(index, byte)| {
242 *byte ^= str_xor[index % str_xor.len()];
243 });
244 }
245
246 if is_lz4ed {
247 let mut decompressed_buffer = vec![0; resource.header.data_size as usize];
248 lz4::decompress(&buffer, &mut decompressed_buffer)?;
249 return Ok(decompressed_buffer);
250 }
251
252 Ok(buffer)
253 }
254}
255
256#[binrw]
257#[brw(repr(u8))]
258#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
259pub enum ChunkType {
260 #[default]
261 Standard,
262 Addon,
263}
264
265#[allow(dead_code)]
266#[binrw]
267pub struct PackageMetadata {
268 pub unknown: u32,
269 pub chunk_id: u8,
270 pub chunk_type: ChunkType,
271 pub patch_id: u8,
272 pub language_tag: [u8; 2], }
274
275#[allow(dead_code)]
276#[binrw]
277pub struct PackageHeader {
278 pub file_count: u32,
279 pub offset_table_size: u32,
280 pub metadata_table_size: u32,
281}
282
283#[bitfield(u32)]
284#[binrw]
285#[derive(Eq, PartialEq)]
286pub struct PackageOffsetFlags {
287 #[bits(31)]
288 pub compressed_size: u32,
289 pub is_scrambled: bool,
290}
291
292#[allow(dead_code)]
293#[derive(Copy, Clone)]
294#[binrw]
295#[brw(little)]
296pub struct PackageOffsetInfo {
297 pub(crate) runtime_resource_id: RuntimeResourceID,
298 pub(crate) data_offset: u64,
299 pub(crate) flags: PackageOffsetFlags,
300}
301
302impl PackageOffsetInfo {
303 pub fn is_scrambled(&self) -> bool {
304 self.flags.is_scrambled()
305 }
306
307 pub fn compressed_size(&self) -> Option<u32> {
308 match self.flags.compressed_size() {
309 0 => None,
310 n => Some(n),
311 }
312 }
313}
314
315#[allow(dead_code)]
316#[derive(Clone, PartialEq, Eq)]
317#[binrw]
318#[brw(little)]
319pub struct ResourceHeader {
320 pub(crate) resource_type: [u8; 4],
321 pub(crate) references_chunk_size: u32,
322 pub(crate) states_chunk_size: u32,
323 pub(crate) data_size: u32,
324 pub(crate) system_memory_requirement: u32,
325 pub(crate) video_memory_requirement: u32,
326
327 #[br(if (references_chunk_size > 0), parse_with = read_references)]
328 #[bw(write_with = empty_writer)]
329 pub references: Vec<(RuntimeResourceID, ResourceReferenceFlags)>,
330}
331
332#[bitfield(u8)]
333#[binrw]
334#[bw(map = |&x| Self::into_bits(x))]
335#[derive(Eq, PartialEq)]
336pub struct ResourceReferenceFlagsLegacy {
337 pub __: bool,
338 pub runtime_acquired: bool,
339 pub weak_reference: bool,
340 pub __: bool,
341 pub type_of_streaming_entity: bool,
342 pub state_streamed: bool,
343 pub media_streamed: bool,
344 pub install_dependency: bool,
345}
346
347#[bitfield(u8)]
348#[binrw]
349#[derive(Eq, PartialEq)]
350#[bw(map = |&x: &Self| x.into_bits())]
351pub struct ResourceReferenceFlagsStandard {
352 #[bits(5, default = 0x1F)]
353 pub language_code: u8,
354 pub runtime_acquired: bool,
355 #[bits(2)]
356 pub reference_type: ReferenceType,
357}
358
359#[derive(Debug, Copy, Clone, Eq, PartialEq)]
360pub enum ReferenceType {
361 INSTALL = 0,
362 NORMAL = 1,
363 WEAK = 2,
364}
365
366impl ReferenceType {
367 const fn into_bits(self) -> u16 {
368 self as _
369 }
370 const fn from_bits(value: u8) -> Self {
371 match value {
372 0 => INSTALL,
373 1 => NORMAL,
374 2 => WEAK,
375 _ => NORMAL,
376 }
377 }
378}
379
380#[derive(Copy, Clone, Eq, PartialEq, Debug)]
382pub enum ResourceReferenceFlags {
383 Legacy(ResourceReferenceFlagsLegacy),
384 Standard(ResourceReferenceFlagsStandard),
385}
386
387impl From<ResourceReferenceFlags> for ResourceReferenceFlagsLegacy {
388 fn from(value: ResourceReferenceFlags) -> Self {
389 match value {
390 ResourceReferenceFlags::Legacy(b) => b,
391 ResourceReferenceFlags::Standard(b) => ResourceReferenceFlagsLegacy::new()
392 .with_runtime_acquired(b.runtime_acquired())
393 .with_weak_reference(b.reference_type() == WEAK)
394 .with_install_dependency(b.reference_type() == INSTALL),
395 }
396 }
397}
398
399impl From<ResourceReferenceFlags> for ResourceReferenceFlagsStandard {
400 fn from(value: ResourceReferenceFlags) -> Self {
401 match value {
402 ResourceReferenceFlags::Standard(b) => b,
403 ResourceReferenceFlags::Legacy(b) => ResourceReferenceFlagsStandard::new()
404 .with_language_code(0x1F)
405 .with_runtime_acquired(b.runtime_acquired())
406 .with_reference_type(value.reference_type()),
407 }
408 }
409}
410
411impl ResourceReferenceFlags {
412 pub fn to_legacy(&self) -> ResourceReferenceFlagsLegacy {
422 (*self).into()
423 }
424
425 pub fn to_standard(&self) -> ResourceReferenceFlagsStandard {
435 (*self).into()
436 }
437
438 pub fn as_byte(&self) -> u8 {
439 match self {
440 ResourceReferenceFlags::Legacy(x) => x.into_bits(),
441 ResourceReferenceFlags::Standard(x) => x.into_bits(),
442 }
443 }
444}
445
446impl ResourceReferenceFlags {
447 pub fn language_code(&self) -> u8 {
448 match self {
449 ResourceReferenceFlags::Legacy(_) => 0x1F,
450 ResourceReferenceFlags::Standard(b) => b.language_code(),
451 }
452 }
453
454 pub fn is_acquired(&self) -> bool {
455 match self {
456 ResourceReferenceFlags::Legacy(b) => b.runtime_acquired(),
457 ResourceReferenceFlags::Standard(b) => b.runtime_acquired(),
458 }
459 }
460
461 pub fn reference_type(&self) -> ReferenceType {
462 match self {
463 ResourceReferenceFlags::Legacy(b) => match b.install_dependency() {
464 true => INSTALL,
465 false if b.weak_reference() => WEAK,
466 false => NORMAL,
467 },
468 ResourceReferenceFlags::Standard(b) => b.reference_type(),
469 }
470 }
471}
472
473#[bitfield(u32)]
474#[binrw]
475#[derive(Eq, PartialEq)]
476#[bw(map = |&x: &Self| x.into_bits())]
477pub struct ResourceReferenceCountAndFlags {
478 #[bits(30)]
479 pub reference_count: u32,
480 pub is_new_format: bool,
481 pub always_true: bool,
482}
483
484#[parser(reader)]
485fn read_references() -> BinResult<Vec<(RuntimeResourceID, ResourceReferenceFlags)>> {
486 let reference_count_and_flag = reader.read_le::<ResourceReferenceCountAndFlags>()?;
487 let reference_count = reference_count_and_flag.reference_count();
488 let is_new_format = reference_count_and_flag.is_new_format();
489
490 let arrays = if is_new_format {
491 let flags: Vec<ResourceReferenceFlags> = (0..reference_count)
492 .map(|_| reader.read_le::<ResourceReferenceFlagsStandard>())
493 .map_ok(ResourceReferenceFlags::Standard)
494 .collect::<BinResult<Vec<_>>>()?;
495 let rrids: Vec<RuntimeResourceID> = (0..reference_count)
496 .map(|_| u64::read_le(reader))
497 .map_ok(RuntimeResourceID::from)
498 .collect::<BinResult<Vec<_>>>()?;
499 (rrids, flags)
500 } else {
501 let rrids: Vec<RuntimeResourceID> = (0..reference_count)
502 .map(|_| u64::read_le(reader))
503 .map_ok(RuntimeResourceID::from)
504 .collect::<BinResult<Vec<_>>>()?;
505 let flags: Vec<ResourceReferenceFlags> = (0..reference_count)
506 .map(|_| reader.read_le::<ResourceReferenceFlagsLegacy>())
507 .map_ok(ResourceReferenceFlags::Legacy)
508 .collect::<BinResult<Vec<_>>>()?;
509 (rrids, flags)
510 };
511
512 Ok(arrays.0.into_iter().zip(arrays.1).collect::<Vec<(_, _)>>())
513}
514
515impl fmt::Display for PackageOffsetInfo {
516 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
517 write!(
518 f,
519 "resource {} is {} bytes at {}",
520 self.runtime_resource_id.to_hex_string(),
521 self.flags.compressed_size(),
522 self.data_offset
523 )
524 }
525}
526
527impl fmt::Display for ResourceHeader {
528 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
529 let mut res_type = self.resource_type;
530 res_type.reverse();
531 write!(
532 f,
533 "type: {}, reference_num: {}, size: {}, num_reqs: ({} {})",
534 std::str::from_utf8(&res_type).unwrap(),
535 self.references_chunk_size,
536 self.data_size,
537 self.system_memory_requirement,
538 self.video_memory_requirement
539 )
540 }
541}
542
543#[binrw::writer]
544fn empty_writer<T>(_: &T) -> BinResult<()> {
545 Ok(())
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552 #[test]
553 fn test_flag_conversion_801f() {
554 let flag_v1 = ResourceReferenceFlagsLegacy::from_bits(0x80);
555 let flag_v2 = ResourceReferenceFlagsStandard::from_bits(0x1F);
556
557 assert_eq!(
558 flag_v1,
559 ResourceReferenceFlags::Standard(flag_v2).to_legacy()
560 );
561 assert_eq!(
562 flag_v2,
563 ResourceReferenceFlags::Legacy(flag_v1).to_standard()
564 );
565 }
566
567 #[test]
568 fn test_flag_conversion_005f() {
569 let flag_v1 = ResourceReferenceFlagsLegacy::from_bits(0x00);
570 let flag_v2 = ResourceReferenceFlagsStandard::from_bits(0x5F);
571
572 assert_eq!(
573 flag_v1,
574 ResourceReferenceFlags::Standard(flag_v2).to_legacy()
575 );
576 assert_eq!(
577 flag_v2,
578 ResourceReferenceFlags::Legacy(flag_v1).to_standard()
579 );
580 }
581}