Guest User

Znk Dumper v2

a guest
Jun 5th, 2014
343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.39 KB | None | 0 0
  1. #OK guys, I "cheated" and modified the program to get the opcode positions from the .pre files
  2. #Not sure if that was a good idea or not but...
  3. import os
  4. import sys
  5. import struct
  6.  
  7. facedict = {}
  8. facedict['00'] = 'Lloyd'
  9. facedict['01'] = 'Elie'
  10. facedict['02'] = 'Tio'
  11. facedict['03'] = 'Randy'
  12. facedict['04'] = 'Lazy'
  13. facedict['05'] = 'Noel'
  14. facedict['06'] = 'Dudley'
  15. facedict['07'] = 'Yin'
  16. facedict['08'] = 'Estelle'
  17. facedict['09'] = 'Joshua'
  18. facedict['10'] = 'Sergei'
  19. facedict['11'] = 'KeA'
  20. facedict['12'] = 'Zeit'
  21. facedict['13'] = 'Cecil'
  22. facedict['14'] = 'Arios'
  23. facedict['15'] = 'Sizuku'
  24. facedict['16'] = 'Wald'
  25. facedict['17'] = 'Ilya'
  26. facedict['18'] = 'Rixia'
  27. facedict['19'] = 'Fran'
  28. facedict['20'] = 'Sonya'
  29. facedict['21'] = 'Grace'
  30. facedict['22'] = 'Ian'
  31. facedict['23'] = 'Jona'
  32. facedict['24'] = 'Joachim'
  33. facedict['25'] = 'McDowell'
  34. facedict['26'] = 'Earnest'
  35. facedict['27'] = 'Hartman'
  36. facedict['28'] = 'Dieter'
  37. facedict['29'] = 'Mariabell'
  38. facedict['30'] = 'Marconi'
  39. facedict['31'] = 'Garcia'
  40. facedict['32'] = 'Cao'
  41. facedict['33'] = 'Renne'
  42. facedict['34'] = 'Kirika'
  43. facedict['35'] = 'Lector'
  44. facedict['36'] = 'Harold'
  45. facedict['37'] = 'Sophia'
  46. facedict['38'] = 'Colin'
  47. facedict['39'] = 'Jorg'
  48. facedict['50'] = 'Lloyd (Fancy)'
  49. facedict['51'] = 'Lloyd (Fancy Glasses)'
  50. facedict['52'] = 'Lloyd (Casual)'
  51. facedict['53'] = 'Elie (Fancy)'
  52. facedict['54'] = 'Special'
  53. facedict['55'] = 'Tio (Casual)'
  54. facedict['56'] = 'Randy (Fancy)'
  55. facedict['57'] = 'Lazy (Fancy)'
  56. facedict['58'] = 'KeA (Fancy)'
  57. facedict['59'] = 'Cecil (Fancy)'
  58. facedict['60'] = 'Sizuku (Fancy)'
  59. facedict['61'] = 'Ilya (Dancer)'
  60. facedict['62'] = 'Rixia (Priestess)'
  61. facedict['63'] = 'Noel (Casual)'
  62. facedict['64'] = 'Fran (Casual)'
  63. facedict['65'] = 'McDowell (PJs)'
  64. facedict['66'] = 'Earnest (Suit)'
  65. facedict['67'] = 'Joachim (Blue Hair)'
  66. facedict['68'] = 'Joachim (White Hair)'
  67.  
  68. #Grabs data. It's called by getpointers and myprogram
  69. #myprogram (couldn't think of better name) is the top level function
  70. def get_data(filename):
  71.     totalbytes = os.path.getsize(filename)
  72.     infile = open(filename, 'rb')
  73.     totalfiledata = infile.read(totalbytes)
  74.     return totalfiledata
  75.  
  76. #Takes the input string and makes a nicely formatted output string for the translators
  77. def calculateoutputstring(opcodeaddress,inputstring):
  78.     speaker = ""
  79.     outputstring = '\n' + opcodeaddress + " " #1st field is address - write to output
  80.     opcode = inputstring[0].encode('hex') #What opcode is it?
  81.     outputstring += opcode + ' ' #2nd field is opcode - write to output
  82.     n = -4
  83.     while ord(inputstring[n]) < 128: #looking for last shift-JIS character in opcode
  84.         n -= 2
  85.     if opcode == '55': userlength = len(inputstring)-5+n+1 #length varies per opcode
  86.     elif opcode == '5c': userlength = len(inputstring)-3+n+1
  87.     elif opcode == '5d': userlength = len(inputstring)-3+n+1
  88.     else:
  89.         print 'Unknown opcode %s at address %s.' % (opcode,opcodeaddress)
  90.         exit
  91.        
  92.     if opcode == '55': strpos = 5 #start position varies per opcode
  93.     elif opcode == '5c': strpos = 3
  94.     elif opcode == '5d': strpos = 3
  95.     lastuserchar = len(inputstring)+n+1 #tells program when to stop
  96.  
  97.     startflag = True #tells program whether the first line in the opcode has been output yet or not (True = not yet)
  98.     startofline = strpos #tells program where the start of the current line is
  99.     JIScharpos = 0 #misleading name. Really the position of the last ascii character. lastasciichar was too long a name...
  100.  
  101. #Go byte by byte
  102.     while strpos <= lastuserchar + 2: #Until end of the string...
  103.         output = False #reset flag
  104. #Our byte is a SHIFT-JIS value
  105.         if int(inputstring[strpos].encode('hex'),16) > 127:
  106.             strpos += 2 #move pointer forward and check again
  107. #Our byte is an ASCII value (which is fine too)
  108.         elif int(inputstring[strpos].encode('hex'),16) > 19:
  109.             strpos += 1 #move pointer forward and check again
  110.             if inputstring[strpos] in ('P','K','F','N'): #If the character is the end of a text code then:
  111.                 JIScharpos = strpos #Set (or reset) the position of the last ascii character
  112. #Our byte at this point must be some weird Falcom text code
  113. #The 0x00 code is used in 0x5D opcodes to separate the name and what the name should say
  114.         elif inputstring[strpos].encode('hex') == '00':
  115.             if opcode == '5d': #For 5D opcodes, the speaker name is in the opcode itself
  116.                 speaker = inputstring[startofline:strpos] + " "
  117.             strpos, startofline = strpos + 1, strpos + 1
  118.             JIScharpos = strpos
  119. #The 0x01 opcode is a line break
  120.         elif inputstring[strpos].encode('hex') == '01':
  121.             breaktype = "linebreak"
  122.             output = True #Tells the program to do the "output" routine on this pass through the loop
  123. #We output a line every time there is either linebreak, newdialogbox or terminalcode
  124.  
  125. #There's two codes starting with 0x02 that we know of:
  126. #0x0200 ends the opcode
  127. #0x0203 starts a new dialog box within the same opcode
  128.         elif strpos == lastuserchar + 1: #The pointer (within this program) is at the end of the opcode
  129. #I call it the "pointer" because we are looking at the actual opcode byte by byte, moving the pointer each time
  130.             output = True
  131.             breaktype = "terminalcode"
  132.            
  133.         elif inputstring[strpos].encode('hex') == '02':
  134.             output = True
  135.             if inputstring[strpos+1].encode('hex') == '03':
  136.                 breaktype = "newdialogbox"
  137.             else:
  138.                 print "Unknown opcode format at address %s." % opcodeaddress
  139.                 exit
  140.         else:
  141.             print "Unknown opcode format at address %s." % opcodeaddress
  142.             exit
  143.  
  144.         if output: #output routine
  145.  
  146.             thisline = inputstring[startofline:strpos] #Gives the string. Further processed below.
  147.             JIScharpos += 1 #That's because the value computed above is really the character _before_ the first JIS character
  148.            
  149.             if len(inputstring[startofline:JIScharpos]) > 1: #There are ASCII characters in thisline
  150.                 codes = inputstring[startofline:JIScharpos] + " " #Grabs the codes
  151.                 speech = inputstring[JIScharpos:strpos] + " " #Grabs the non-codes part of the line
  152.                 if codes.find('F') > -1 and opcode != '5d': #If there is an "F" code in the codes, we need the faces routine
  153.                     facecodepos = codes.find('F') #Gets face code position within the codes
  154.                     facecode1 = thisline[facecodepos-4:facecodepos-2] #First two numbers of face code (as string)
  155.                     facecode2 = thisline[facecodepos-2:facecodepos] #Last two numbers of face code (as string)
  156.                     speaker = facedict[facecode1] + " " #You know that dictionary at the top? Go get the name based on the 1st two numbers.
  157.                     if speaker == 'Special': #What to do if the face code starts with "54"
  158.                         if int(facecode2) < 12: speaker = "Tio (Fancy) "
  159.                         else: speaker = "Zeit "
  160.                 elif opcode != '5d': #For 5D opcodes, the speaker has already been set; we don't want to mess that up.
  161.                     speaker = " " #For non 5D opcodes, there's no speaker on this line, so we make a blank
  162.             else: #No text codes on this line - make some blanks
  163.                 codes = " "
  164.                 speaker = " "
  165.                 speech = thisline + " "
  166.  
  167.             if startflag == True: #What to do on the first pass
  168.                 outputstring += speaker + codes + speech + breaktype + " " + str(userlength)
  169.                 startflag = False
  170.             else: #Second and later passes have a newline and don't have address or opcode, so two blanks are needed
  171.                 outputstring += "\n  " + speaker + codes + speech + breaktype
  172.  
  173.             if breaktype == "linebreak": #Update state variables at the end, move the pointer, etc...
  174.                 strpos, startofline = strpos + 1, strpos + 1
  175.             else:
  176.                 strpos, startofline = strpos + 2, strpos + 2
  177.  
  178.             JIScharpos = strpos
  179.            
  180.     return outputstring
  181.  
  182. #Loads the *.pre file and returns the pointers from it
  183. def getpointers(filename):
  184.     filedata = get_data(filename)
  185.     opcodepos = filedata.find('\xcc\xcc')-2 #I can't decode .pre headers so this'll have to do.
  186.     if opcodepos == 0: exit #if there's no dialog here then exit program entirely
  187.     opcodes = []
  188.     pointers = []
  189.     while opcodepos < len(filedata)-12: #Weird magic number here ("12")
  190. # Could use some help getting rid of this magic number
  191. # I need the program to stop before the end of the file to avoid string index out of range error
  192. # The last opcode usually doesn't point to dialog so I think this could be fine for now
  193.         opcode = []
  194.         for n in range(11):
  195.             opcode.append(filedata[opcodepos+n].encode('hex'))
  196.         opcodepos += 12 #This magic number is fine; opcodes/pointer bytes/whatever in .pre are 12 bytes long
  197.         opcodes.append(opcode)
  198.     del opcodes[-1]
  199.     for opcode in opcodes:
  200.         if [opcode[2],opcode[3]] == ['cc','cc']:
  201. # It converts the little endian value (which makes no sense) to big endian (which does make sense)
  202.             thisval = hex(struct.unpack('<H',(opcode[4] + opcode[5]).decode('hex'))[0])
  203.             if not thisval in pointers:
  204.                 pointers.append(thisval)
  205.     return pointers
  206.  
  207. def myprogram(filename,filename2):
  208.     pointers = getpointers(filename2) #First grab the pointers. We'll need these later.
  209.     filedata = get_data(filename)
  210.     firstpass = True
  211.     outfiledata = filename
  212.  
  213.     for pointer in pointers:
  214. # The 5C and 5D opcodes we're looking for in this program are variable length.
  215. # We have to search for their ends.
  216.         strend = filedata.find('\x02\x00',int(pointer,16)) #Find the end of the opcode
  217. # Pass the pointer address and entire opcode to a function for formatting the dump
  218.         outputstring = calculateoutputstring(pointer,filedata[int(pointer,16):strend+2])
  219.         outfiledata += outputstring #Append formatted string to program output
  220.  
  221.     outfile = open(os.path.splitext(filename)[0] + '.data','wb')
  222.     outfile.write(outfiledata) #write the output
  223.     outfile.close
  224.        
  225. if __name__ == '__main__':
  226. #    sys.argv=[sys.argv[0],'m3000.bin','m3000.pre'] I use this line for testing in IDLE
  227.     myprogram(sys.argv[1],sys.argv[2])
Add Comment
Please, Sign In to add comment