daily pastebin goal
68%
SHARE
TWEET

Disgaea PC save unpacking/repacking

HenryEx Jul 29th, 2017 (edited) 116 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # Disgaea PC / Disgaea 2 PC save game decompression
  2. # De-XORs and decompresses save files from these games.
  3. # Leaves headers intact, so actual save data starts at offset 0x44.
  4. #
  5. # Saves decompressed with this script can be repacked as well with
  6. # pseudo-compression to allow loading modified save files.
  7. #
  8. # Written by HenryEx
  9. # version 2
  10. #
  11. # script for QuickBMS http://quickbms.aluigi.org
  12.  
  13. ################################################
  14. # set up virtual memory file for save data
  15.   print "Preparing..."
  16.   get FILENAME filename 0
  17.   get FILESIZE asize 0
  18.   if FILESIZE < 0x44
  19.     print "[!] Error: File too small! Exiting..."
  20.     CleanExit
  21.   endif
  22.   log MEMORY_FILE 0 0  # MEMORY_FILE is the working copy for de-/encryption
  23.   log MEMORY_FILE2 0 0  # MEMORY_FILE2 will be the target for de-/recompression
  24.   putvarchr MEMORY_FILE 0x100000 0
  25.   putvarchr MEMORY_FILE2 0x100000 0
  26.   log MEMORY_FILE 0 0  # reset MF1
  27.   log MEMORY_FILE2 0 0  # reset MF2
  28.  
  29.   getDString HEADSTART 0x20
  30.   get XORKEY long
  31.   get XORUNK1 short
  32.   get XORUNK2 short
  33.   get XORCHUNKS long  # num of XOR'd integers in file
  34.   get FSIZE_COMP_A long  # savedata size with YKCMP header minus padding (for XOR ints)
  35.   SavePos FSTART 0
  36.   getDString MAGIC 8 0   # check for YKCMP_V1 string if already de-crypted / -compressed
  37.  
  38. if MAGIC = "YKCMP_V1"
  39. ################################################
  40. # Pseudo re-compress save
  41.   print "File seems to be a decompressed save, will attempt to pseudo re-compress and encrypt it."
  42.  
  43.   get ARCHIVE_VERSION long 0
  44.   if ARCHIVE_VERSION != 4
  45.     print "[!] Unexpected archive version: %ARCHIVE_VERSION%! Exiting..."
  46.     CleanExit
  47.   endif
  48.   get FSIZE_COMP_B long 0  # comp. filesize without 0x30 decryption header, minus padding
  49.   get FSIZE_TARGET long 0  # target filesize without all headers after decomp.
  50.   SavePos FSTART 0
  51.  
  52. # set up compression stuff and prepare MF2
  53.   print "Setting up file in memory..."
  54.   putct "YKCMP_V1" string -1 MEMORY_FILE2
  55.   put ARCHIVE_VERSION long MEMORY_FILE2
  56.   put FSIZE_COMP_B long MEMORY_FILE2  # place holder, needs to be updated after recompression
  57.   math FSIZE_TARGET = FILESIZE
  58.   math FSIZE_TARGET - 0x44
  59.   put FSIZE_TARGET long MEMORY_FILE2
  60.   set FBYTES long FSIZE_TARGET
  61.   math POS = FSTART  # starting position to read bytes from, should be 0x44
  62.  
  63. # start pseudo compressing into MF2
  64.   print "Pseudo re-compressing save data..."
  65.   append  # append mode ON
  66.   for FBYTES = FBYTES != 0  # loop while num of bytes to process is not 0
  67.     if FBYTES > 0x7F
  68.       set READLEN byte 0x7F
  69.       math FBYTES - 0x7F
  70.     else
  71.       set READLEN byte FBYTES
  72.       math FBYTES = 0
  73.     endif
  74.  
  75.     put READLEN byte MEMORY_FILE2   # put byte length to straight copy
  76.     log MEMORY_FILE2 POS READLEN 0  # append [READLEN] bytes to MF2
  77.     math POS + READLEN              # increment read offset
  78.   next
  79.   append  # append mode OFF
  80.  
  81.   get FSIZE_COMP_B asize MEMORY_FILE2
  82.   math FSIZE_COMP_A = FSIZE_COMP_B
  83.   putvarchr MEMORY_FILE2 0xC FSIZE_COMP_B long  # update header value
  84.  
  85. ################################################
  86. # Encrypt save file in MF1
  87.  
  88.   print "Setting up encryption..."
  89.   xmath PAD "4 - ( FSIZE_COMP_A % 4 )"  # num of bytes for padding
  90.   if PAD > 0
  91.     for i = 0 < PAD
  92.       put 0 byte MEMORY_FILE2  # pad file with 0 for 32-bit alignment
  93.     next i
  94.   endif
  95.   xmath FSIZE "FSIZE_COMP_A + PAD"
  96.   xmath XORCHUNKS "FSIZE / 4"
  97. # print "Padding needed: %PAD%. Padded filesize is %FSIZE%. Xor chunks: %XORCHUNKS%."
  98.  
  99. # Set up MF1
  100.   putDString HEADSTART 0x20 MEMORY_FILE
  101.   put XORKEY long MEMORY_FILE
  102.   put XORUNK1 short MEMORY_FILE
  103.   put XORUNK2 short MEMORY_FILE
  104.   put XORCHUNKS long MEMORY_FILE
  105.   put FSIZE_COMP_A long MEMORY_FILE
  106.   SavePos FSTART MEMORY_FILE
  107.   append  # append mode ON
  108.   log MEMORY_FILE 0 FSIZE MEMORY_FILE2  # put recomp. save after encryption header
  109.   append  # append mode OFF
  110.  
  111.   print "Encrypting File..."
  112. # XOR save file with key
  113.   for i = 0 < XORCHUNKS
  114.     xmath POS "(i * 4) + FSTART"  # get current file position
  115.  
  116.     getvarchr DATA MEMORY_FILE POS long
  117.     math DATA u^ XORKEY
  118.     putvarchr MEMORY_FILE POS DATA long
  119.   next i
  120.  
  121.   get ENDSIZE asize MEMORY_FILE
  122.  
  123. ################################################
  124. # Save file to disk
  125.  
  126.   string FILENAME $ "save"  # last occurrence + searched string
  127.   print "Exporting re-compressed save to %FILENAME%"
  128.   log FILENAME 0 ENDSIZE MEMORY_FILE
  129.  
  130.   CleanExit
  131.  
  132. else
  133. ################################################
  134. # Decrypt save
  135.  
  136.   print "Decrypting File..."
  137. # XOR save file with key
  138.   log MEMORY_FILE 0 FILESIZE 0
  139.   for i = 0 < XORCHUNKS
  140.     xmath POS "(i * 4) + FSTART"  # get current file position
  141.  
  142.     getvarchr DATA MEMORY_FILE POS long
  143.     math DATA u^ XORKEY
  144.     putvarchr MEMORY_FILE POS DATA long
  145.   next i
  146.  
  147.   goto FSTART MEMORY_FILE
  148.   getDString MAGIC 8 MEMORY_FILE
  149.   if MAGIC != "YKCMP_V1"
  150.     print "[!] Unexpected magic string: %MAGIC%! Decryption might have failed. Exiting..."
  151.     CleanExit
  152.   endif
  153.  
  154.  
  155. ################################################
  156. # Decompress save
  157.  
  158.   print "Begin Decompression..."
  159.   get ARCHIVE_VERSION long MEMORY_FILE
  160.   if ARCHIVE_VERSION != 4
  161.     print "[!] Unexpected archive version: %ARCHIVE_VERSION%! Exiting..."
  162.     CleanExit
  163.   endif
  164.   get FSIZE_COMP_B long MEMORY_FILE  # comp. filesize without 0x30 decryption header, minus padding
  165.   get FSIZE_TARGET long MEMORY_FILE  # target filesize without all headers after decomp.
  166.   SavePos FSTART 0
  167.  
  168. # set up decompression and prepare MF2
  169.   print "Setting up file in memory..."
  170.   putDString HEADSTART 0x20 MEMORY_FILE2
  171.   put XORKEY long MEMORY_FILE2
  172.   put XORUNK1 short MEMORY_FILE2
  173.   put XORUNK2 short MEMORY_FILE2
  174.   put XORCHUNKS long MEMORY_FILE2
  175.   put FSIZE_COMP_A long MEMORY_FILE2
  176.   putct "YKCMP_V1" string -1 MEMORY_FILE2
  177.   put ARCHIVE_VERSION long MEMORY_FILE2
  178.   put FSIZE_COMP_B long MEMORY_FILE2
  179.   put FSIZE_TARGET long MEMORY_FILE2
  180.   set FBYTES long FSIZE_COMP_B
  181.   math FBYTES + 0x30  # num. of compressed bytes + XOR & YKCMP header
  182.  
  183.   math POS = FSTART  # offset after the YKCMP_V1 header (save data start) for MF2
  184.  
  185. # start decompressing into MF2
  186.   print "Decompressing save data..."
  187.   for i = 68 < FBYTES  # i works as byte offset, start at offset 0x44
  188.     goto i MEMORY_FILE
  189.     get A_BYTE byte MEMORY_FILE
  190.    
  191.     if A_BYTE >= 0xE0    # read data like XX XY YY
  192.       get B_BYTE byte MEMORY_FILE
  193.       get C_BYTE byte MEMORY_FILE
  194.       set READLEN long A_BYTE
  195.       math READLEN & 0x1F  # remove 0xE0 from X
  196.       math READLEN < 4
  197.       xmath READLEN "READLEN + (B_BYTE > 4)"
  198.       math READLEN + 3
  199.       set SEEKBACK long B_BYTE
  200.       math SEEKBACK & 0x0F
  201.       math SEEKBACK < 8
  202.       math SEEKBACK + C_BYTE
  203.       math SEEKBACK + 1
  204.     # print "Offset %i|h4%: byte %A_BYTE|2h% is >= 0xE0! Next bytes %B_BYTE|2h% %C_BYTE|2h%. Look back by %SEEKBACK% and copy %READLEN% bytes to %POS|6h%!"
  205.       math i + 2  # advance counter of processed bytes
  206.     elif A_BYTE >= 0xC0  # read data like XX YY
  207.       get B_BYTE byte MEMORY_FILE
  208.       math READLEN = A_BYTE
  209.       math READLEN & 0x3F  # remove 0xC0 from X
  210.       math READLEN + 2
  211.       math SEEKBACK = B_BYTE
  212.       math SEEKBACK + 1
  213.     # print "Offset %i|h4%: byte %A_BYTE|2h% is >= 0xC0! Next byte %B_BYTE|2h%. Look back by %SEEKBACK% and copy %READLEN% bytes to %POS|6h%!"
  214.       math i + 1  # advance counter of processed bytes
  215.     elif A_BYTE >= 0x80  # read data like XY
  216.       math READLEN = A_BYTE
  217.       math READLEN > 4
  218.       math READLEN & 3  # remove 0x80 from X
  219.       math READLEN + 1
  220.       math SEEKBACK = A_BYTE
  221.       math SEEKBACK & 0x0F
  222.       math SEEKBACK + 1
  223.     # print "Offset %i|h4%: byte %A_BYTE|2h% is >= 0x80! Look back by %SEEKBACK% and copy %READLEN% bytes to %POS|6h%!"
  224.     else                 # byte is < 0x80, straight copy next bytes MF1 -> MF2
  225.     # print "Offset %i|h4%: byte %A_BYTE|2h% is < 0x80! Straight copy %A_BYTE% bytes to %POS|6h%!"
  226.       for j = 0 < A_BYTE
  227.         math i + 1
  228.         getvarchr DATA MEMORY_FILE1 i byte
  229.         put DATA byte MEMORY_FILE2
  230.       next j
  231.     endif
  232.  
  233.     if A_BYTE >= 0x80  # Copy bytes within MF2 via lookback
  234.  
  235.       math POS - SEEKBACK
  236.  
  237.       for j = 0 < READLEN
  238.         getvarchr DATA MEMORY_FILE2 POS byte
  239.         put DATA byte MEMORY_FILE2
  240.         math POS + 1
  241.       next j
  242.     endif
  243.  
  244.     get POS asize MEMORY_FILE2
  245.   next i
  246.  
  247. # check if filesize matches?
  248.   math ENDSIZE = POS
  249.   math POS - 68
  250.   if FSIZE_TARGET != POS
  251.     print "WARNING! Target filesize doesn't match real filesize!"
  252.   endif
  253.  
  254. ################################################
  255. # Save file to disk
  256.  
  257.   string FILENAME P= "dec_%FILENAME%"
  258.   print "Exporting decompressed save to %FILENAME%"
  259.   log FILENAME 0 ENDSIZE MEMORY_FILE2
  260.  
  261.   CleanExit
  262.  
  263. endif
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top