Advertisement
Guest User

QB Script Insert (Bulk Inserter) v3

a guest
Feb 8th, 2016
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.38 KB | None | 0 0
  1. #v3: Accomodate new EBOOT program header (+0x20 to EBOOT offsets)
  2. import struct   #So many imports...
  3. import codecs
  4. import re
  5. import zlib
  6. import copy
  7. import os
  8. import shutil
  9.  
  10. BASE_ADDR = 0x182200
  11.                
  12. s1 = " .,'!?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~-&>():;/"
  13. s2 = " .,’!?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~‐&>():;/"
  14. translate_table = str.maketrans(s1, s2)     #For converting between ASCII and fullwidth SHIFT-JIS characters
  15. namelist = {'#M': '#M', '#F': '#F', '#SC': '#SC', '#SA': '#SA', '#SB': '#SB',   #For fixing corruptions
  16.             '#TB': '#TB', '#TC': '#TC', '#GB': '#GB', '#GA': '#GA',
  17.             '#GC': '#GC', '#TA': '#TA'}
  18.  
  19. if os.path.isfile('shared.orig'):
  20.     pass
  21. elif os.path.isfile('shared.bin'):
  22.     shutil.copy('shared.bin','shared.orig')
  23. else:
  24.     print('Missing shared.bin.')
  25.     quit()
  26.  
  27. #Given the pointer location, it returns the location and the value
  28. def getpointer(pos, filedata):
  29.     return [pos, struct.unpack('<I', filedata[pos:pos+4])[0]]
  30.  
  31. #Multiple replace. Pretty cool, huh? repls is a dic w/ the replacements
  32. def replace_all(repls, str):
  33.     return re.sub('|'.join(re.escape(key) for key in repls.keys()),
  34.                   lambda k: repls[k.group(0)], str)                                    
  35.  
  36. #It's supposed to insert the script with your input.
  37. #Writes a compressed, inserted script file
  38. #Also, it updates the archive index
  39. def insert_script(file_num, input_col):
  40. #Declaring globals is cheating. But I'm not sure how to avoid it.
  41.     global archive_index
  42.     file_num_str = '{:0>3}'.format(file_num)            #Need the str for file operations
  43.  
  44.     inputdata = []
  45.     with codecs.open(file_num_str + '.tsv','rb','utf-8') as f:  #Open input file
  46.         for line in f:
  47.             line = line.rstrip('\r\n').split('\t')
  48.             text = line[input_col].translate(translate_table)   #Change to fullwidth characters
  49.             text = replace_all(namelist, text)                  #Get rid of corruptions like #M (should be #M)
  50.             while '"' in text:                          #Mess with quotations (gives open/close quotation marks)
  51.                 text = text.replace('"', '“', 1)
  52.                 text = text.replace('"', '”', 1)
  53.             if line[1] != '':                           #Start of opcode
  54.                 inputdata.append([line[0]])             #Append address
  55.                 if line[1] in names:                    #Starts w/ a name
  56.                     inputdata[-1] += [names[line[1]]]   #Lookup translated name
  57.                 elif line[2] != '':                     #Two strings on this line
  58.                     inputdata[-1] += text.split('|')
  59.                     continue
  60.                 else:                                   #One string, non-name
  61.                     inputdata[-1] += [text]
  62.                     continue
  63.             if line[input_col] == '':                   #Blank line
  64.                 continue
  65.             inputdata[-1] += [text]                     #Continuation line (additional line for same opcode)
  66.  
  67.     #Load and decompress data
  68.     offset, compressed_size = (archive_index_orig[file_num][0],
  69.                                archive_index_orig[file_num][2])
  70.     with open('shared.orig', 'rb') as f:
  71.         f.seek(BASE_ADDR + offset)
  72.         filedata = bytearray(zlib.decompress(f.read(compressed_size)))
  73.  
  74.     #Get pointers
  75.     ptr_loc = [0x4, 0x44, 0x4c, 0x54, 0x5c, 0x6c]
  76.     num_of_strings = struct.unpack('<I', filedata[0x38:0x3C])[0]
  77.     ptr_table_addr = struct.unpack('<I', filedata[0x44:0x48])[0]
  78.     str_table_addr = struct.unpack('<I', filedata[0x4c:0x50])[0]
  79.  
  80.     ptr_loc2 = [ptr_table_addr + x * 4 for x in range(num_of_strings + 1)]
  81.     ptrs = [getpointer(ptr, filedata) for ptr in ptr_loc]
  82.     ptrs2 = [getpointer(ptr, filedata) for ptr in ptr_loc2]
  83.  
  84.     #Replace each string, update pointers
  85.     offset = 0
  86.     for data in inputdata:
  87.         pos = int(data[0],16) + str_table_addr + offset
  88.         text = b'\x40'.join([x.encode('cp932') for x in data[1:]])
  89.         orig_len = len(filedata[pos:filedata.find(b'\x00',pos)])
  90.         for i, (ptr_loc, ptr_tgt) in enumerate(ptrs):
  91.             if ptr_tgt > pos:
  92.                 ptrs[i][1] += len(text) - orig_len
  93.         for i, (ptr_loc, ptr_tgt) in enumerate(ptrs2):
  94.             if ptr_tgt + str_table_addr > pos:
  95.                 ptrs2[i][1] += len(text) - orig_len
  96.         filedata[pos:pos + orig_len] = text
  97.         offset += len(text) - orig_len
  98.  
  99.     #Write-back pointers
  100.     for ptr_loc, ptr_tgt in ptrs + ptrs2:
  101.         filedata[ptr_loc:ptr_loc+4] = struct.pack('<I', ptr_tgt)
  102.  
  103.     #Update archive index compressed and decompressed sizes
  104.     archive_index[file_num][1] = len(filedata)      #Update decompressed size
  105. ##    with open(file_num_str + '.temp1','wb') as f:
  106. ##        f.write(filedata)
  107.     filedata = zlib.compress(filedata, 9)           #Compress data
  108.     archive_index[file_num][2] = len(filedata)      #Update compressed size
  109.  
  110.     #Update archive index offsets
  111.     if file_num == num_of_files - 1:                #Ignore last file
  112.         pass
  113.     else:                                           #Not the last file
  114.         avail_blocks = (archive_index[file_num + 1][0] - archive_index[file_num][0]) // 16  #1 block = 16 bytes
  115.         if len(filedata) % 16 == 0:
  116.             needed_blocks = len(filedata) // 16 + 1     #It gives an extra whole block of zeros. Doesn't really matter I don't think.
  117.         else:
  118.             needed_blocks = len(filedata) // 16 + 2    
  119.         if needed_blocks > avail_blocks:            #More blocks are needed. This is usually the case.
  120.             for x in range(file_num + 1, num_of_files):     #Move all files after this one down by # of extra blocks needed
  121.                 archive_index[x][0] += 16 * (needed_blocks - avail_blocks)
  122.  
  123.     with open(file_num_str + '.temp','wb') as f:    #Write compressed data to file (it's needed later)
  124.         f.write(filedata)
  125.  
  126. #Load names file
  127. names = {}
  128. with codecs.open('names.tsv','rb','utf-8') as f:
  129.     for line in f:
  130.         line = line.rstrip('\r\n').split('\t')
  131.         if line[0] in namelist.values():
  132.             names[line[0]] = line[0]
  133.         else:
  134.             names[line[0]] = line[1].translate(translate_table)
  135.  
  136. #Load archive index
  137. with open('shared.orig', 'rb') as f:
  138.     f.seek(BASE_ADDR + 4)
  139.     num_of_files = struct.unpack('<I', f.read(4))[0]
  140.     archive_index = []
  141.     for x in range(num_of_files):
  142.         archive_index.append(list(struct.unpack('<III', f.read(12))))   #Offset, decompressed size, compressed size
  143.     #The program needs data from shared.orig.
  144.     #It uses archive_index_orig to access that data.
  145.     #You need deepcopy! Nothing else worked!
  146. archive_index_orig = copy.deepcopy(archive_index)
  147.  
  148. scripts_to_insert_3 = [3, 4, 5, 6, 7, 8]
  149. scripts_to_insert_4 = []
  150. scripts_to_insert_5 = [0, 1, 2]
  151. scripts_to_insert = [[x] + [3] for x in scripts_to_insert_3] + \
  152.                     [[x] + [4] for x in scripts_to_insert_4] + \
  153.                     [[x] + [5] for x in scripts_to_insert_5]
  154. for file_num, col_to_insert in sorted(scripts_to_insert, key=lambda x: x[0]):
  155.     insert_script(file_num, col_to_insert)
  156.  
  157. tempfilename = 'shared.tmp'
  158. shutil.copy('shared.bin', tempfilename)
  159.  
  160. with open(tempfilename, 'rb') as f:
  161.     with open('shared.bin','wb') as g:
  162.         g.write(f.read(BASE_ADDR))                  #Everything up to where shared.bin should go.
  163.         g.write(b'Wpbb')                            #0x0 - File identifier
  164.         g.write(struct.pack('<I', num_of_files))    #0x4 - Number of files
  165.         for i in archive_index:                     #Write archive index
  166.             g.write(struct.pack('<III', *i))
  167.         for i in range(num_of_files):               #Write each sub-file in archive
  168.             if g.tell() - BASE_ADDR > archive_index[i][0]:  
  169.                 print('error')
  170.                 print(str(i), hex(g.tell() - BASE_ADDR), hex(archive_index[i][0]))
  171.                 quit()
  172.             while g.tell() - BASE_ADDR < archive_index[i][0]:   #Write zeros to get to the correct start position
  173.                 g.write(b'\x00')
  174.             if i in [x[0] for x in scripts_to_insert]:          #If it's a new custom script file...
  175.                 with open('{:0>3}'.format(i) + '.temp','rb') as h:  #Write the previously saved, compressed script
  176.                     g.write(h.read())
  177.             else:                                               #Not a new custom script file
  178.                 f.seek(BASE_ADDR + archive_index_orig[i][0])    #Write original data from shared.bin
  179.                 g.write(f.read(archive_index[i][2]))
  180.         #Finished writing archive file
  181.         size = g.tell() - BASE_ADDR + 0x200
  182.         g.write(b'\x00' * (BASE_ADDR + 0xc5600 - g.tell()))    #Write zeros until where the next file in shared.bin should go
  183.         f.seek(BASE_ADDR + 0xc5600)                            #Jump shared.bin to correct position
  184.         g.write(f.read())                                      #Write the rest of shared.bin, unchanged
  185. os.remove(tempfilename)
  186. with open('EBOOT.BIN', 'rb') as f:
  187.     filedata = bytearray(f.read())
  188. filedata[0x15DD1C:0x15DD20] = struct.pack('<I', size)
  189. with open('EBOOT.BIN', 'wb') as f:
  190.     f.write(filedata)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement