daily pastebin goal
31%
SHARE
TWEET

Disgaea PC save unpacking/repacking

HenryEx Jul 29th, 2017 (edited) 56 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
Top