1use std::{
31 fs::{create_dir_all, write},
32 path::{Path, PathBuf},
33};
34use thiserror::Error;
35
36const ARCHIVE_HEADER: &[u8; 6] = b"RGSSAD";
37const OLDER_DEFAULT_KEY: u32 = 0xDEADCAFE;
38
39#[derive(Error, Debug)]
40pub enum ExtractError {
41 #[error("Invalid archive file header: {found:?}. Expected: RGSSAD ([82, 71, 83, 83, 65, 68])")]
42 InvalidHeader { found: [u8; 6] },
43 #[error("Invalid game engine byte: {found}. Expected `1` for XP/VX or `3` for VX Ace.")]
44 InvalidEngine { found: u8 },
45}
46
47pub enum ExtractOutcome {
48 Extracted,
49 FilesExist,
50}
51
52#[derive(PartialEq)]
53enum EngineType {
54 Older,
55 VXAce,
56}
57
58enum SeekFrom {
59 Start,
60 Current,
61}
62
63impl std::fmt::Display for EngineType {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 EngineType::Older => write!(f, "XP/VX"),
67 EngineType::VXAce => write!(f, "VXAce"),
68 }
69 }
70}
71
72#[derive(Default)]
73struct ArchiveEntry {
74 filename_bytes: Vec<u8>,
75 size: i32,
76 offset: usize,
77 key: u32,
78}
79
80pub struct Decrypter<'a> {
82 data: &'a [u8],
83 pos: usize,
84 len: usize,
85 force: bool,
86 engine_type: EngineType,
87 key: u32,
88 key_bytes: [u8; 4],
89}
90
91impl<'a> Decrypter<'a> {
92 pub fn new() -> Self {
94 Self {
95 data: &[],
96 pos: 0,
97 len: 0,
98
99 force: false,
100
101 engine_type: EngineType::Older,
102 key: OLDER_DEFAULT_KEY,
103 key_bytes: OLDER_DEFAULT_KEY.to_le_bytes(),
104 }
105 }
106
107 #[inline]
113 pub fn force(mut self, enabled: bool) -> Self {
114 self.force = enabled;
115 self
116 }
117
118 #[inline]
122 pub fn set_force(&mut self, enabled: bool) {
123 self.force = enabled;
124 }
125
126 #[inline]
127 fn update_key(&mut self, new_key: u32) {
128 self.key = new_key;
129 self.key_bytes = new_key.to_le_bytes();
130 }
131
132 #[inline]
133 fn read_bytes(&mut self, bytes: usize) -> &[u8] {
134 self.pos += bytes;
135 &self.data[self.pos - bytes..self.pos]
136 }
137
138 #[inline]
139 fn read_int(&mut self) -> i32 {
140 let chunk: &[u8] = self.read_bytes(4);
141 i32::from_le_bytes(unsafe { *(chunk.as_ptr() as *const [u8; 4]) })
142 }
143
144 #[inline]
145 fn read_byte(&mut self) -> u8 {
146 self.pos += 1;
147 self.data[self.pos - 1]
148 }
149
150 #[inline]
151 fn seek_byte(&mut self, offset: usize, seek_from: SeekFrom) {
152 self.pos = match seek_from {
153 SeekFrom::Start => offset,
154 SeekFrom::Current => self.pos + offset,
155 };
156 }
157
158 #[inline]
159 fn decrypt_entry(&mut self, entry: &ArchiveEntry) -> Vec<u8> {
160 let mut key = entry.key;
161 let mut key_bytes: [u8; 4] = key.to_le_bytes();
162 let mut key_byte_pos: usize = 0;
163
164 self.seek_byte(entry.offset, SeekFrom::Start);
165
166 let content = self.read_bytes(entry.size as usize);
167 let mut decrypted: Vec<u8> = Vec::with_capacity(content.len());
168
169 for item in content {
170 if key_byte_pos == 4 {
171 key_byte_pos = 0;
172 key = key.wrapping_mul(7).wrapping_add(3);
173 key_bytes = key.to_le_bytes();
174 }
175
176 decrypted.push(item ^ key_bytes[key_byte_pos]);
177 key_byte_pos += 1;
178 }
179
180 decrypted
181 }
182
183 #[inline]
184 fn decrypt_int(&mut self) -> i32 {
185 let int: i32 = self.read_int();
186 let result: i32 = int ^ self.key as i32;
187
188 if self.engine_type == EngineType::Older {
189 self.update_key(self.key.wrapping_mul(7).wrapping_add(3));
190 }
191
192 result
193 }
194
195 #[inline]
196 fn decrypt_filename(&mut self, entry: &mut ArchiveEntry) {
197 let filename_bytes =
198 unsafe { &*(self.read_bytes(entry.filename_bytes.capacity()) as *const [u8]) };
199
200 if self.engine_type == EngineType::VXAce {
201 let mut key_byte_pos: usize = 0;
202
203 for byte in filename_bytes {
204 if key_byte_pos == 4 {
205 key_byte_pos = 0;
206 }
207
208 entry
209 .filename_bytes
210 .push(byte ^ self.key_bytes[key_byte_pos]);
211 key_byte_pos += 1;
212 }
213 } else {
214 for byte in filename_bytes {
215 entry.filename_bytes.push(byte ^ self.key as u8);
216 self.update_key(self.key.wrapping_mul(7).wrapping_add(3));
217 }
218 }
219 }
220
221 #[inline]
222 fn parse_header(&mut self) -> Result<(), ExtractError> {
223 let header: &[u8] = self.read_bytes(6);
224
225 if header != ARCHIVE_HEADER {
226 return Err(ExtractError::InvalidHeader {
227 found: unsafe { *(header.as_ptr() as *const [u8; 6]) },
228 });
229 }
230
231 self.seek_byte(1, SeekFrom::Current);
232 let engine_type: u8 = self.read_byte();
233
234 self.engine_type = match engine_type {
235 1 => EngineType::Older,
236 3 => EngineType::VXAce,
237 _ => return Err(ExtractError::InvalidEngine { found: engine_type }),
238 };
239
240 Ok(())
241 }
242
243 #[inline]
244 fn extract_entries(&mut self) -> Vec<ArchiveEntry> {
245 if self.engine_type == EngineType::VXAce {
246 let key = self.read_int() as u32;
248 self.update_key(key.wrapping_mul(9).wrapping_add(3));
249 }
250
251 let mut entries = Vec::with_capacity(16384);
252
253 loop {
254 let mut entry: ArchiveEntry = ArchiveEntry::default();
255
256 match self.engine_type {
257 EngineType::VXAce => {
258 entry.offset = self.decrypt_int() as usize;
259
260 if entry.offset == 0 {
261 break;
262 }
263
264 entry.size = self.decrypt_int();
265 entry.key = self.decrypt_int() as u32;
266
267 entry
268 .filename_bytes
269 .reserve_exact(self.decrypt_int() as usize);
270 self.decrypt_filename(&mut entry);
271 }
272 EngineType::Older => {
273 entry
274 .filename_bytes
275 .reserve_exact(self.decrypt_int() as usize);
276 self.decrypt_filename(&mut entry);
277
278 entry.size = self.decrypt_int();
279 entry.offset = self.pos;
280 entry.key = self.key;
281
282 self.seek_byte(entry.size as usize, SeekFrom::Current);
283
284 if self.pos == self.len {
285 break;
286 }
287 }
288 }
289
290 entries.push(entry);
291 }
292
293 entries
294 }
295
296 fn reset(&mut self, data: &[u8]) {
297 self.data = unsafe { &*(data as *const [u8]) };
298 self.len = data.len();
299 self.pos = 0;
300 self.engine_type = EngineType::Older;
301 self.key = OLDER_DEFAULT_KEY;
302 self.key_bytes = OLDER_DEFAULT_KEY.to_le_bytes();
303 }
304
305 #[inline]
325 pub fn extract<P: AsRef<Path>>(
326 &mut self,
327 data: &[u8],
328 output_path: P,
329 ) -> Result<ExtractOutcome, ExtractError> {
330 self.reset(data);
331 self.parse_header()?;
332
333 let entries: Vec<ArchiveEntry> = self.extract_entries();
334
335 for entry in entries {
336 let filename = String::from_utf8_lossy(&entry.filename_bytes);
337 let file_output_path: PathBuf = output_path.as_ref().join(&*filename);
338
339 if file_output_path.exists() && !self.force {
340 return Ok(ExtractOutcome::FilesExist);
341 }
342
343 if let Some(dir) = file_output_path.parent() {
344 create_dir_all(dir).unwrap();
345 }
346
347 let decrypted = self.decrypt_entry(&entry);
348 write(file_output_path, decrypted).unwrap();
349 }
350
351 Ok(ExtractOutcome::Extracted)
352 }
353}
354
355impl<'a> Default for Decrypter<'a> {
356 fn default() -> Self {
360 Self::new()
361 }
362}
363
364pub fn extract_archive<P: AsRef<Path>>(
387 data: &[u8],
388 output_path: P,
389 force: bool,
390) -> Result<ExtractOutcome, ExtractError> {
391 Decrypter::new().force(force).extract(data, output_path)
392}