1
1
use clap:: { value_parser, Arg , ArgAction , ArgMatches , Command } ;
2
- use rayon :: prelude :: * ;
2
+ use rpgmad_lib :: Decrypter ;
3
3
use std:: {
4
- cell:: UnsafeCell ,
5
- fs:: { create_dir_all, read, write} ,
4
+ fs:: read,
6
5
path:: { Path , PathBuf } ,
7
- sync:: { Arc , Mutex } ,
8
6
time:: Instant ,
9
7
} ;
10
- #[ derive( PartialEq , Clone , Copy ) ]
11
- #[ allow( clippy:: upper_case_acronyms) ]
12
- enum EngineType {
13
- XPVX ,
14
- VXAce ,
15
- }
16
-
17
- impl std:: fmt:: Display for EngineType {
18
- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
19
- let variant_name: & str = match self {
20
- EngineType :: XPVX => "XP/VX" ,
21
- EngineType :: VXAce => "VXAce" ,
22
- } ;
23
-
24
- write ! ( f, "{}" , variant_name)
25
- }
26
- }
27
-
28
- #[ derive( Default ) ]
29
- struct VecWalker {
30
- data : Vec < u8 > ,
31
- pos : usize ,
32
- len : usize ,
33
- }
34
-
35
- enum SeekFrom {
36
- Start ,
37
- Current ,
38
- }
39
-
40
- impl VecWalker {
41
- pub fn new ( data : Vec < u8 > ) -> Self {
42
- let len: usize = data. len ( ) ;
43
- VecWalker { data, pos : 0 , len }
44
- }
45
-
46
- pub fn advance ( & mut self , bytes : usize ) -> & [ u8 ] {
47
- let start: usize = self . pos ;
48
- self . pos += bytes;
49
- & self . data [ start..self . pos ]
50
- }
51
-
52
- pub fn read_chunk ( & mut self ) -> [ u8 ; 4 ] {
53
- let read: & [ u8 ] = self . advance ( 4 ) ;
54
- unsafe { * ( read. as_ptr ( ) as * const [ u8 ; 4 ] ) }
55
- }
56
-
57
- pub fn read_byte ( & mut self ) -> u8 {
58
- let byte: u8 = self . data [ self . pos ] ;
59
- self . pos += 1 ;
60
- byte
61
- }
62
-
63
- pub fn seek ( & mut self , offset : usize , seek_from : SeekFrom ) {
64
- self . pos = match seek_from {
65
- SeekFrom :: Start => offset,
66
- SeekFrom :: Current => self . pos + offset,
67
- } ;
68
- }
69
- }
70
8
71
9
struct Localization < ' a > {
72
10
// Arg descriptions
@@ -79,238 +17,37 @@ struct Localization<'a> {
79
17
// Messages
80
18
input_path_missing_msg : & ' a str ,
81
19
output_path_missing_msg : & ' a str ,
82
- unknown_engine_type_msg : & ' a str ,
83
- unknown_archive_header_msg : & ' a str ,
84
- output_files_already_exists_msg : & ' a str ,
85
- }
86
-
87
- struct Archive {
88
- name : String ,
89
- size : i32 ,
90
- offset : usize ,
91
- key : u32 ,
92
- }
93
-
94
- struct Decrypter < ' a > {
95
- walker : UnsafeCell < VecWalker > ,
96
- key : u32 ,
97
- engine : EngineType ,
98
- localization : Localization < ' a > ,
99
- }
100
-
101
- impl < ' a > Decrypter < ' a > {
102
- fn new ( bytes : Vec < u8 > , localization : Localization < ' a > ) -> Self {
103
- Self {
104
- walker : UnsafeCell :: new ( VecWalker :: new ( bytes) ) ,
105
- key : 0xDEADCAFE ,
106
- engine : EngineType :: XPVX ,
107
- localization,
108
- }
109
- }
110
-
111
- fn extract ( & mut self , output_path : & Path , force : bool ) {
112
- let version: u8 = self . get_archive_version ( ) ;
113
-
114
- if version == 1 {
115
- self . engine = EngineType :: XPVX
116
- } else if version == 3 {
117
- self . engine = EngineType :: VXAce
118
- } else {
119
- panic ! ( "{}" , self . localization. unknown_engine_type_msg)
120
- }
121
-
122
- let archives: Vec < Archive > = self . read_archive ( ) ;
123
-
124
- let walker: & mut VecWalker = unsafe { & mut * self . walker . get ( ) } ;
125
- let arc: Arc < Mutex < & mut VecWalker > > = Arc :: new ( Mutex :: new ( walker) ) ;
126
-
127
- archives. into_par_iter ( ) . for_each ( |archive : Archive | {
128
- let output_path: PathBuf = output_path. join ( archive. name ) ;
129
-
130
- if output_path. exists ( ) && !force {
131
- println ! ( "{}" , self . localization. output_files_already_exists_msg) ;
132
- return ;
133
- }
134
-
135
- let mut walker = arc. lock ( ) . unwrap ( ) ;
136
-
137
- walker. seek ( archive. offset , SeekFrom :: Start ) ;
138
-
139
- let mut data: Vec < u8 > = Vec :: with_capacity ( archive. size as usize ) ;
140
- data. extend_from_slice ( walker. advance ( archive. size as usize ) ) ;
141
-
142
- drop ( walker) ;
143
-
144
- create_dir_all ( unsafe { output_path. parent ( ) . unwrap_unchecked ( ) } ) . unwrap ( ) ;
145
- write ( output_path, Self :: decrypt_archive ( & data, archive. key ) ) . unwrap ( ) ;
146
- } ) ;
147
- }
148
-
149
- fn decrypt_archive ( data : & [ u8 ] , mut key : u32 ) -> Vec < u8 > {
150
- let mut decrypted: Vec < u8 > = Vec :: with_capacity ( data. len ( ) ) ;
151
-
152
- let mut key_bytes: [ u8 ; 4 ] = key. to_le_bytes ( ) ;
153
- let mut j: usize = 0 ;
154
-
155
- for item in data {
156
- if j == 4 {
157
- j = 0 ;
158
- key = key. wrapping_mul ( 7 ) . wrapping_add ( 3 ) ;
159
- key_bytes = key. to_le_bytes ( ) ;
160
- }
161
-
162
- decrypted. push ( item ^ key_bytes[ j] ) ;
163
- j += 1 ;
164
- }
165
-
166
- decrypted
167
- }
168
-
169
- fn get_archive_version ( & mut self ) -> u8 {
170
- let walker: & mut VecWalker = unsafe { & mut * self . walker . get ( ) } ;
171
- let header: & [ u8 ] = walker. advance ( 6 ) ;
172
-
173
- if header != b"RGSSAD" {
174
- panic ! ( "{}" , self . localization. unknown_archive_header_msg) ;
175
- }
176
-
177
- walker. seek ( 1 , SeekFrom :: Current ) ;
178
- let version: u8 = walker. read_byte ( ) ;
179
-
180
- walker. seek ( 0 , SeekFrom :: Start ) ;
181
-
182
- version
183
- }
184
-
185
- fn decrypt_integer ( & mut self , value : i32 ) -> i32 {
186
- let result: i32 = value ^ self . key as i32 ;
187
-
188
- if self . engine == EngineType :: XPVX {
189
- self . key = self . key . wrapping_mul ( 7 ) . wrapping_add ( 3 ) ;
190
- }
191
-
192
- result
193
- }
194
-
195
- fn decrypt_filename ( & mut self , filename : & [ u8 ] ) -> String {
196
- let mut decrypted: Vec < u8 > = Vec :: with_capacity ( filename. len ( ) ) ;
197
-
198
- if self . engine == EngineType :: VXAce {
199
- let key_bytes: [ u8 ; 4 ] = self . key . to_le_bytes ( ) ;
200
- let mut j: usize = 0 ;
201
-
202
- for item in filename {
203
- if j == 4 {
204
- j = 0 ;
205
- }
206
-
207
- decrypted. push ( item ^ key_bytes[ j] ) ;
208
- j += 1 ;
209
- }
210
- } else {
211
- for item in filename {
212
- decrypted. push ( item ^ ( self . key & 0xff ) as u8 ) ;
213
- self . key = self . key . wrapping_mul ( 7 ) . wrapping_add ( 3 ) ;
214
- }
215
- }
216
-
217
- String :: from_utf8 ( decrypted) . unwrap ( )
218
- }
219
-
220
- fn read_archive ( & mut self ) -> Vec < Archive > {
221
- let walker: & mut VecWalker = unsafe { & mut * self . walker . get ( ) } ;
222
- walker. seek ( 8 , SeekFrom :: Start ) ;
223
-
224
- if self . engine == EngineType :: VXAce {
225
- self . key = u32:: from_le_bytes ( walker. read_chunk ( ) )
226
- . wrapping_mul ( 9 )
227
- . wrapping_add ( 3 ) ;
228
- }
229
-
230
- let mut archives: Vec < Archive > = Vec :: with_capacity ( 1024 ) ;
231
-
232
- loop {
233
- let ( name, size, offset, key) = if self . engine == EngineType :: VXAce {
234
- let offset: usize =
235
- self . decrypt_integer ( i32:: from_le_bytes ( walker. read_chunk ( ) ) ) as usize ;
236
-
237
- let size: i32 = self . decrypt_integer ( i32:: from_le_bytes ( walker. read_chunk ( ) ) ) ;
238
-
239
- let key: u32 = self . decrypt_integer ( i32:: from_le_bytes ( walker. read_chunk ( ) ) ) as u32 ;
240
-
241
- let length: i32 = self . decrypt_integer ( i32:: from_le_bytes ( walker. read_chunk ( ) ) ) ;
242
-
243
- if offset == 0 {
244
- break ;
245
- }
246
-
247
- let name: String = self . decrypt_filename ( walker. advance ( length as usize ) ) ;
248
-
249
- ( name, size, offset, key)
250
- } else {
251
- let length: i32 = self . decrypt_integer ( i32:: from_le_bytes ( walker. read_chunk ( ) ) ) ;
252
-
253
- let name: String = self . decrypt_filename ( walker. advance ( length as usize ) ) ;
254
-
255
- let size: i32 = self . decrypt_integer ( i32:: from_le_bytes ( walker. read_chunk ( ) ) ) ;
256
-
257
- let offset: usize = walker. pos ;
258
-
259
- let key: u32 = self . key ;
260
-
261
- walker. seek ( size as usize , SeekFrom :: Current ) ;
262
-
263
- if walker. pos == walker. len {
264
- break ;
265
- }
266
-
267
- ( name, size, offset, key)
268
- } ;
269
-
270
- archives. push ( Archive {
271
- name,
272
- size,
273
- offset,
274
- key,
275
- } ) ;
276
- }
277
-
278
- archives
279
- }
280
20
}
281
21
282
22
fn main ( ) {
283
23
let start_time: Instant = Instant :: now ( ) ;
284
24
285
25
const LOCALIZATION : Localization = Localization {
286
- input_path_arg_desc : "Path to the RGSSAD file." ,
26
+ input_path_arg_desc : "Path to the .rgss file." ,
287
27
output_path_arg_desc : "Path to put output files." ,
288
28
force_arg_desc : "Forcefully overwrite existing Data, Graphics and other files." ,
289
29
help_arg_desc : "Prints the help message." ,
290
30
about : "A tool to extract encrypted .rgss RPG Maker archives." ,
291
31
292
32
input_path_missing_msg : "Input file does not exist." ,
293
33
output_path_missing_msg : "Output path does not exist." ,
294
- unknown_archive_header_msg : "Unknown archive header. Expected: RGSSAD." ,
295
- unknown_engine_type_msg :
296
- "Unknown archive game engine. Maybe, file's extension is spelled wrong?" ,
297
- output_files_already_exists_msg :
298
- "Output file already exists. Use --force to forcefully overwrite it." ,
299
34
} ;
300
35
301
36
let input_path_arg: Arg = Arg :: new ( "input-path" )
302
37
. short ( 'i' )
303
38
. long ( "input-file" )
304
39
. help ( LOCALIZATION . input_path_arg_desc )
305
40
. value_parser ( value_parser ! ( PathBuf ) )
306
- . default_value ( "./" ) ;
41
+ . default_value ( "./" )
42
+ . hide_default_value ( true ) ;
307
43
308
44
let output_path_arg: Arg = Arg :: new ( "output-path" )
309
45
. short ( 'o' )
310
46
. long ( "output-dir" )
311
47
. help ( LOCALIZATION . output_path_arg_desc )
312
48
. value_parser ( value_parser ! ( PathBuf ) )
313
- . default_value ( "./" ) ;
49
+ . default_value ( "./" )
50
+ . hide_default_value ( true ) ;
314
51
315
52
let force: Arg = Arg :: new ( "force" )
316
53
. short ( 'f' )
@@ -342,12 +79,9 @@ fn main() {
342
79
}
343
80
344
81
let mut output_path: & Path = matches. get_one :: < PathBuf > ( "output-path" ) . unwrap ( ) ;
345
-
346
- output_path = if * output_path. as_os_str ( ) == * "./" {
347
- unsafe { input_path. parent ( ) . unwrap_unchecked ( ) }
348
- } else {
349
- output_path
350
- } ;
82
+ if * output_path. as_os_str ( ) == * "./" {
83
+ output_path = unsafe { input_path. parent ( ) . unwrap_unchecked ( ) }
84
+ }
351
85
352
86
if !output_path. exists ( ) {
353
87
panic ! ( "{}" , LOCALIZATION . output_path_missing_msg) ;
@@ -356,7 +90,10 @@ fn main() {
356
90
let force_flag: bool = matches. get_flag ( "force" ) ;
357
91
358
92
let bytes: Vec < u8 > = read ( input_path) . unwrap ( ) ;
359
- Decrypter :: new ( bytes, LOCALIZATION ) . extract ( output_path, force_flag) ;
93
+
94
+ Decrypter :: new ( bytes)
95
+ . extract ( output_path, force_flag)
96
+ . unwrap ( ) ;
360
97
361
98
println ! ( "Elapsed: {}" , start_time. elapsed( ) . as_secs_f64( ) )
362
99
}
0 commit comments