1use std::io::{Read, Seek, SeekFrom, Write};
5
6use binrw::{BinRead, BinWrite, binrw};
7use data::{BlockHeader, CompressionMode};
8
9use crate::common::{Platform, Region};
10use crate::compression::no_header_decompress;
11
12mod data;
13pub use data::SqPackData;
14
15mod db;
16pub use db::SqPackDatabase;
17
18mod index;
19pub use index::{Hash, IndexEntry, SqPackIndex};
20
21#[binrw]
23#[brw(repr = u8)]
24#[derive(Debug)]
25pub(crate) enum SqPackFileType {
26 SQDB = 0x0,
28 Data = 0x1,
30 Index = 0x2,
32}
33
34#[binrw]
35#[brw(magic = b"SqPack\0\0")]
36#[derive(Debug)]
37pub(crate) struct SqPackHeader {
38 #[brw(pad_size_to = 4)]
39 platform_id: Platform,
40 pub size: u32,
41 version: u32,
43 #[brw(pad_size_to = 4)]
44 file_type: SqPackFileType,
45
46 unk1: u32,
49 unk2: u32,
50
51 #[br(pad_size_to = 4)]
52 region: Region,
53
54 #[brw(pad_before = 924)]
55 #[brw(pad_after = 44)]
56 sha1_hash: [u8; 20],
58}
59
60pub(crate) fn read_data_block<T: Read + Seek>(
61 mut buf: T,
62 starting_position: u64,
63) -> Option<Vec<u8>> {
64 buf.seek(SeekFrom::Start(starting_position)).ok()?;
65
66 let block_header = BlockHeader::read(&mut buf).unwrap();
67
68 match block_header.compression {
69 CompressionMode::Compressed {
70 compressed_length,
71 decompressed_length,
72 } => {
73 let mut compressed_data: Vec<u8> = vec![0; compressed_length as usize];
74 buf.read_exact(&mut compressed_data).ok()?;
75
76 let mut decompressed_data: Vec<u8> = vec![0; decompressed_length as usize];
77 if !no_header_decompress(&mut compressed_data, &mut decompressed_data) {
78 return None;
79 }
80
81 Some(decompressed_data)
82 }
83 CompressionMode::Uncompressed { file_size } => {
84 let mut local_data: Vec<u8> = vec![0; file_size as usize];
85 buf.read_exact(&mut local_data).ok()?;
86
87 Some(local_data)
88 }
89 }
90}
91
92pub(crate) fn read_data_block_patch<T: Read + Seek>(mut buf: T) -> Option<Vec<u8>> {
94 let block_header = BlockHeader::read(&mut buf).unwrap();
95
96 match block_header.compression {
97 CompressionMode::Compressed {
98 compressed_length,
99 decompressed_length,
100 } => {
101 let compressed_length: usize =
102 ((compressed_length as usize + 143) & 0xFFFFFF80) - (block_header.size as usize);
103
104 let mut compressed_data: Vec<u8> = vec![0; compressed_length];
105 buf.read_exact(&mut compressed_data).ok()?;
106
107 let mut decompressed_data: Vec<u8> = vec![0; decompressed_length as usize];
108 if !no_header_decompress(&mut compressed_data, &mut decompressed_data) {
109 return None;
110 }
111
112 Some(decompressed_data)
113 }
114 CompressionMode::Uncompressed { file_size } => {
115 let new_file_size: usize = (file_size as usize + 143) & 0xFFFFFF80;
116
117 let mut local_data: Vec<u8> = vec![0; file_size as usize];
118 buf.read_exact(&mut local_data).ok()?;
119
120 buf.seek(SeekFrom::Current(
121 (new_file_size - block_header.size as usize - file_size as usize) as i64,
122 ))
123 .ok()?;
124
125 Some(local_data)
126 }
127 }
128}
129
130pub(crate) fn write_data_block_patch<T: Write + Seek>(mut writer: T, data: Vec<u8>) {
131 let new_file_size: usize = (data.len() + 143) & 0xFFFFFF80;
132
133 let block_header = BlockHeader {
136 size: (new_file_size - data.len()) as u32, compression: CompressionMode::Uncompressed {
138 file_size: data.len() as i32,
139 },
140 };
141 block_header.write(&mut writer).unwrap();
142
143 data.write(&mut writer).unwrap();
144}