home *** CD-ROM | disk | FTP | other *** search
/ Xentax forum attachments archive / xentax.7z / 4601 / RustyHearts.7z / RustyHearts.py
Encoding:
Python Source  |  2011-08-09  |  5.0 KB  |  153 lines

  1. from ZUnpacker import CBinaryReader
  2. from ZUnpacker import SuperStruct
  3. from Crypto.Cipher import AES
  4. import os
  5. import zlib
  6.  
  7. FILE_PREFIX = 'RustyHearts' #This is the prefix placed before all files output to the hard drive
  8.  
  9. #These are options for the table outputs
  10. DIVIDER = '|' #What kind of separation of values the tables use
  11. SHOW_TYPES = 1 #1 = Show the types at the top of the table. 0 = Do not show types
  12. NEW_LINES = '\r\n' #The new lines that tables use
  13.  
  14.  
  15. def main():
  16.     fileTree = GetFileTree()
  17.     for i, (fileName, pckFileName, fileLength, fileChecksum, fileOffset) in enumerate(fileTree, 1):
  18.         pckFile = CBinaryReader.BinaryReader(pckFileName, 'rb')
  19.         pckFile.seek(fileOffset)
  20.         fileText = pckFile.read(fileLength)
  21.         pckFile.close()
  22.  
  23.         fileName = os.path.join(FILE_PREFIX, fileName)
  24.         print('%03d:%d - %s' % (i, len(fileTree), fileName))         
  25.  
  26.         if fileName.endswith('.rh'):
  27.             fileText = TableHandler(fileText)
  28.  
  29.         WriteFile(fileName, fileText)
  30.  
  31. def GetFileTree():
  32.     decryptedFileTree = DecryptMIPFile('f00X.dat')
  33.     f = CBinaryReader.BinaryReader(decryptedFileTree, 'string')
  34.  
  35.     dirTree = []
  36.     while not f.ReachedEOF():
  37.         fileName = GetString(f)
  38.         pckFileName = '%s.pck' % (str(f.rU8()).zfill(3))
  39.         fileLength = f.rU32()
  40.         fileChecksum = f.rU32()
  41.         fileOffset = f.rU32()
  42.         nulls = f.rU32()
  43.         dirTree.append([fileName, pckFileName, fileLength, fileChecksum, fileOffset])
  44.     return dirTree
  45.  
  46. def DecryptMIPFile(fileName):
  47.     keys = SuperStruct.Unpack(GetFile('Zbin/RustyHearts.keys'))
  48.     mipText = SuperStruct.Unpack(GetFile(fileName))
  49.     mipText = list(mipText)
  50.     for i, encryptedChar in enumerate(mipText):
  51.         mipText[i] = encryptedChar ^ keys[i & 0xFF]
  52.     mipText = SuperStruct.Pack(mipText)
  53.     mipText = zlib.decompress(mipText)
  54.     return mipText
  55.  
  56. def GenerateAESCipher():
  57.     aesKey = 'gkw3iurpamv;kj20984;asdkfjat1af\0'
  58.     aesIV = SuperStruct.Pack([0xDB, 0x0F, 0x49, 0x40] * 4)
  59.     mode = AES.MODE_ECB
  60.     Cipher = AES.new(aesKey, mode, aesIV)
  61.     return Cipher
  62.  
  63.  
  64. #The tables are in encrypted, binary form
  65. #This function decrypts the files and turns them into separated values
  66. def TableHandler(fileText):
  67.     Cipher = GenerateAESCipher()
  68.     fileText = Cipher.decrypt(fileText)
  69.  
  70.     f = CBinaryReader.BinaryReader(fileText, 'string')
  71.     numRows = f.rS32()
  72.     numColumns = f.rS32()
  73.  
  74.     columnNames = []
  75.     for i in range(numColumns):
  76.         resultString = GetString(f)
  77.         columnNames.append(resultString)
  78.  
  79.     columnTypes = []
  80.     for i in range(numColumns):
  81.         columnType = f.rS32()
  82.         if columnType == 0:
  83.             columnTypes.append('int32')
  84.         elif columnType == 1:
  85.             columnTypes.append('float32')
  86.         elif columnType == 3:
  87.             columnTypes.append('string')
  88.         elif columnType == 4:
  89.             columnTypes.append('int64')
  90.         else:
  91.             raise Exception('Unknown column type %d at offset %d' % (columnType, f.tell()))
  92.     
  93.     rows = []
  94.     for i in range(numRows):
  95.         thisRow = []
  96.         for j in range(numColumns):
  97.             if columnTypes[j] == 'int32':
  98.                 thisRow.append(str(f.rS32()))
  99.             elif columnTypes[j] == 'float32':
  100.                 thisRow.append(str(f.rF32()))
  101.             elif columnTypes[j] == 'string':
  102.                 thisRow.append(GetString(f))
  103.             elif columnTypes[j] == 'int64':
  104.                 thisRow.append(str(f.rS64()))
  105.             else:
  106.                 raise Exception("Unknown column type %s?" % (columnTypes[j]))
  107.         rows.append(thisRow)
  108.     columnTypesStr = DIVIDER.join(columnTypes)
  109.     columnNamesStr = DIVIDER.join(columnNames)
  110.     rows = NEW_LINES.join([DIVIDER.join(eachRow) for eachRow in rows])
  111.  
  112.     resultTable = '%s%s%s%s' % (columnNamesStr, NEW_LINES, rows, NEW_LINES)
  113.     if SHOW_TYPES:
  114.         resultTable = '%s%s' % (columnTypesStr, NEW_LINES) + resultTable
  115.     return resultTable
  116.     
  117.  
  118. def GetString(f):
  119.     length = f.rS16()
  120.     resultString = []
  121.     for j in range(length):
  122.         char = f.rU16()
  123.         if char < 255:
  124.             char = chr(char)
  125.         else:
  126.             #Windows doesn't play nicely with actual UTF-16 characters
  127.             #However, using "&#%s;" works on Windows
  128.             #This is HTML formatting. If there's another way to get
  129.             #UTF-16 characters to work in Windows, let me know!
  130.             char = '&#%s;' % (char)
  131.         resultString.append(char)
  132.     resultString = ''.join(resultString)
  133.     return resultString
  134.  
  135. def GetFile(fileName):
  136.     f = open(fileName, 'rb')
  137.     text = f.read()
  138.     f.close()
  139.     return text
  140.  
  141. def WriteFile(fileName, fileText):
  142.     if '\\' in fileName:
  143.         fileName = fileName.replace('\\', '/')
  144.     fileDir = '/'.join(fileName.split('/')[:-1])
  145.     if not os.access(fileDir, os.F_OK):
  146.         os.makedirs(fileDir)
  147.     f = open(fileName, 'wb')
  148.     f.write(fileText)
  149.     f.close()
  150.  
  151. if __name__ == '__main__':
  152.     main()
  153.