tiger_pkg/d2_prebl/
impl.rs

1use std::{
2    fs::File,
3    io::{BufReader, Seek, SeekFrom},
4    sync::Arc,
5};
6
7use binrw::{BinReaderExt, Endian, VecArgs};
8
9use crate::{
10    d2_prebl::structs::PackageHeader,
11    d2_shared::{CommonPackageData, HashTableEntry, PackageCommonD2, PackageNamedTagEntry},
12    package::{Package, PackageLanguage, PackagePlatform, ReadSeek, UEntryHeader, UHashTableEntry},
13    DestinyVersion,
14};
15
16pub struct PackageD2PreBL {
17    common: PackageCommonD2,
18    pub header: PackageHeader,
19    pub named_tags: Vec<PackageNamedTagEntry>,
20}
21
22unsafe impl Send for PackageD2PreBL {}
23unsafe impl Sync for PackageD2PreBL {}
24
25impl PackageD2PreBL {
26    pub fn open(path: &str) -> anyhow::Result<PackageD2PreBL> {
27        let _span = tracing::trace_span!("PackageD2PreBL::open", path);
28        let reader = File::open(path)?;
29
30        Self::from_reader(path, reader)
31    }
32
33    pub fn from_reader<R: ReadSeek + 'static>(
34        path: &str,
35        reader: R,
36    ) -> anyhow::Result<PackageD2PreBL> {
37        let _span = tracing::trace_span!("PackageD2PreBL::from_reader", path);
38        let mut reader = BufReader::new(reader);
39        let header: PackageHeader = reader.read_le()?;
40
41        reader.seek(SeekFrom::Start(header.entry_table_offset as u64 - 16))?;
42        let entry_table_size_bytes = reader.read_le::<u32>()? * 16;
43
44        reader.seek(SeekFrom::Start(header.entry_table_offset as _))?;
45        let entries = reader.read_le_args(VecArgs {
46            count: header.entry_table_size as _,
47            inner: (),
48        })?;
49
50        reader.seek(SeekFrom::Start(
51            (header.entry_table_offset + entry_table_size_bytes + 32) as _,
52        ))?;
53        let blocks = reader.read_le_args(VecArgs {
54            count: header.block_table_size as _,
55            inner: (),
56        })?;
57
58        let wide_hashes: Vec<HashTableEntry> = if header.misc_data_offset != 0 {
59            reader.seek(SeekFrom::Start((header.misc_data_offset + 0x30) as _))?;
60            let h64_table_size: u64 = reader.read_le()?;
61            let real_h64_table_offset: u64 = reader.read_le()?;
62            reader.seek(SeekFrom::Current(-8 + real_h64_table_offset as i64 + 16))?;
63            reader.read_le_args(VecArgs {
64                count: h64_table_size as _,
65                inner: (),
66            })?
67        } else {
68            vec![]
69        };
70
71        let named_tags: Vec<PackageNamedTagEntry> = if header.misc_data_offset != 0 {
72            reader.seek(SeekFrom::Start((header.misc_data_offset + 0x10) as _))?;
73            let named_tags_size: u64 = reader.read_le()?;
74            let real_named_tags_offset: u64 = reader.read_le()?;
75            reader.seek(SeekFrom::Current(-8 + real_named_tags_offset as i64 + 16))?;
76            reader.read_le_args(VecArgs {
77                count: named_tags_size as _,
78                inner: (),
79            })?
80        } else {
81            vec![]
82        };
83
84        Ok(PackageD2PreBL {
85            common: PackageCommonD2::new(
86                reader.into_inner(),
87                DestinyVersion::Destiny2Shadowkeep,
88                path.to_string(),
89                CommonPackageData {
90                    pkg_id: header.pkg_id,
91                    patch_id: header.patch_id,
92                    group_id: header.group_id,
93                    entries,
94                    blocks,
95                    wide_hashes,
96                    language: header.language,
97                },
98            )?,
99            header,
100            named_tags,
101        })
102    }
103}
104
105// TODO(cohae): Can we implement this on PackageCommon?
106impl Package for PackageD2PreBL {
107    fn endianness(&self) -> Endian {
108        Endian::Little // TODO(cohae): Not necessarily
109    }
110
111    fn pkg_id(&self) -> u16 {
112        self.common.pkg_id
113    }
114
115    fn patch_id(&self) -> u16 {
116        self.common.patch_id
117    }
118
119    fn language(&self) -> PackageLanguage {
120        self.common.language
121    }
122
123    fn platform(&self) -> PackagePlatform {
124        self.header.platform
125    }
126
127    fn hash64_table(&self) -> Vec<UHashTableEntry> {
128        self.common
129            .wide_hashes
130            .iter()
131            .map(|h| UHashTableEntry {
132                hash64: h.hash64,
133                hash32: h.hash32,
134                reference: h.reference,
135            })
136            .collect()
137    }
138
139    fn named_tags(&self) -> Vec<PackageNamedTagEntry> {
140        self.named_tags.clone()
141    }
142
143    fn entries(&self) -> &[UEntryHeader] {
144        &self.common.entries_unified
145    }
146
147    fn entry(&self, index: usize) -> Option<UEntryHeader> {
148        self.common.entries_unified.get(index).cloned()
149    }
150
151    fn get_block(&self, index: usize) -> anyhow::Result<Arc<Vec<u8>>> {
152        self.common.get_block(index)
153    }
154}