11using System . IO . Compression ;
2- using System . IO . MemoryMappedFiles ;
32using System . Text ;
43using Maple2 . File . IO . Crypto . Common ;
54using Maple2 . File . IO . Crypto . Keys ;
@@ -32,12 +31,24 @@ public static byte[] DecryptFileTable(IPackStream stream, System.IO.Stream buffe
3231 throw new Exception ( "ERROR decrypting file table: the size of the table is invalid." ) ;
3332 }
3433
35- public static byte [ ] DecryptData ( IPackFileHeader pHeader , MemoryMappedFile data ) {
34+ public static byte [ ] DecryptData ( IPackFileHeader pHeader , FileStream data , object readLock ) {
3635 if ( pHeader . CompressedFileSize > 0 && pHeader . EncodedFileSize > 0 && pHeader . FileSize > 0 ) {
37- using MemoryMappedViewStream buffer = data . CreateViewStream ( ( long ) pHeader . Offset , pHeader . EncodedFileSize ) ;
3836 byte [ ] src = new byte [ pHeader . EncodedFileSize ] ;
37+ int bytesRead ;
38+ lock ( readLock ) {
39+ data . Seek ( ( long ) pHeader . Offset , SeekOrigin . Begin ) ;
40+ int totalRead = 0 ;
41+ int remaining = ( int ) pHeader . EncodedFileSize ;
42+ while ( remaining > 0 ) {
43+ int read = data . Read ( src , totalRead , remaining ) ;
44+ if ( read == 0 ) break ;
45+ totalRead += read ;
46+ remaining -= read ;
47+ }
48+ bytesRead = totalRead ;
49+ }
3950
40- if ( buffer . Read ( src , 0 , ( int ) pHeader . EncodedFileSize ) == pHeader . EncodedFileSize ) {
51+ if ( bytesRead == pHeader . EncodedFileSize ) {
4152 return Decrypt ( pHeader . Version , pHeader . EncodedFileSize , ( uint ) pHeader . CompressedFileSize , pHeader . BufferFlag , src ) ;
4253 }
4354 }
@@ -47,19 +58,35 @@ public static byte[] DecryptData(IPackFileHeader pHeader, MemoryMappedFile data)
4758
4859 // Decryption Routine: Base64 -> AES -> Zlib
4960 private static byte [ ] Decrypt ( PackVersion version , uint size , uint sizeCompressed , Encryption flag , byte [ ] src ) {
50- if ( flag . HasFlag ( Encryption . Aes ) ) {
61+ if ( flag . HasFlag ( Encryption . Xor ) ) {
62+ // Decrypt the XOR encrypted block (check Xor before Aes because 0xFF is a superset of 0xEE in bits)
63+ src = EncryptXor ( version , src , size , sizeCompressed ) ;
64+ } else if ( flag . HasFlag ( Encryption . Aes ) ) {
5165 // Get the AES Key/IV for transformation
5266 CipherKeys . GetKeyAndIV ( version , sizeCompressed , out byte [ ] key , out byte [ ] iv ) ;
5367
54- // Decode the base64 encoded string
55- src = Convert . FromBase64String ( Encoding . UTF8 . GetString ( src ) ) ;
68+ // Decode the base64 encoded string (handle both standard and URL-safe Base64)
69+ string b64 = Encoding . UTF8 . GetString ( src ) . Replace ( '-' , '+' ) . Replace ( '_' , '/' ) ;
70+ try {
71+ src = Convert . FromBase64String ( b64 ) ;
72+ } catch ( FormatException ) {
73+ const string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= \t \r \n " ;
74+ for ( int i = 0 ; i < b64 . Length ; i ++ ) {
75+ if ( valid . IndexOf ( b64 [ i ] ) < 0 ) {
76+ Console . Error . WriteLine ( $ "[Base64 FAIL] Invalid char at { i } /{ b64 . Length } : 0x{ ( int ) b64 [ i ] : X4} '{ b64 [ i ] } '") ;
77+ Console . Error . WriteLine ( $ "[Base64 FAIL] Context: ...{ b64 [ Math . Max ( 0 , i - 8 ) ..Math . Min ( b64 . Length , i + 8 ) ] } ...") ;
78+ break ;
79+ }
80+ }
81+ if ( b64 . Length % 4 != 0 )
82+ Console . Error . WriteLine ( $ "[Base64 FAIL] Length { b64 . Length } not multiple of 4 (remainder { b64 . Length % 4 } )") ;
83+ Console . Error . WriteLine ( $ "[Base64 FAIL] flag=0x{ ( uint ) flag : X8} size={ size } sizeCompressed={ sizeCompressed } srcLen={ b64 . Length } ") ;
84+ throw ;
85+ }
5686
5787 // Decrypt the AES encrypted block
5888 var pCipher = new AESCipher ( key , iv ) ;
5989 pCipher . TransformBlock ( src , 0 , size , src , 0 ) ;
60- } else if ( flag . HasFlag ( Encryption . Xor ) ) {
61- // Decrypt the XOR encrypted block
62- src = EncryptXor ( version , src , size , sizeCompressed ) ;
6390 }
6491
6592 return flag . HasFlag ( Encryption . Zlib ) ? UncompressBuffer ( src ) : src ;
0 commit comments