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::{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 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                header.pkg_id,
89                header.patch_id,
90                header.group_id,
91                entries,
92                blocks,
93                hashes,
94                path.to_string(),
95                header.language,
96            )?,
97            header,
98            named_tags,
99        })
100    }
101}
102
103// TODO(cohae): Can we implement this on PackageCommon?
104impl Package for PackageD2PreBL {
105    fn endianness(&self) -> Endian {
106        Endian::Little // TODO(cohae): Not necessarily
107    }
108
109    fn pkg_id(&self) -> u16 {
110        self.common.pkg_id
111    }
112
113    fn patch_id(&self) -> u16 {
114        self.common.patch_id
115    }
116
117    fn language(&self) -> PackageLanguage {
118        self.common.language
119    }
120
121    fn platform(&self) -> PackagePlatform {
122        self.header.platform
123    }
124
125    fn hash64_table(&self) -> Vec<UHashTableEntry> {
126        self.common
127            .hashes
128            .iter()
129            .map(|h| UHashTableEntry {
130                hash64: h.hash64,
131                hash32: h.hash32,
132                reference: h.reference,
133            })
134            .collect()
135    }
136
137    fn named_tags(&self) -> Vec<PackageNamedTagEntry> {
138        self.named_tags.clone()
139    }
140
141    fn entries(&self) -> &[UEntryHeader] {
142        &self.common.entries_unified
143    }
144
145    fn entry(&self, index: usize) -> Option<UEntryHeader> {
146        self.common.entries_unified.get(index).cloned()
147    }
148
149    fn get_block(&self, index: usize) -> anyhow::Result<Arc<Vec<u8>>> {
150        self.common.get_block(index)
151    }
152}