Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import struct #So many imports...
- import codecs
- import re
- import zlib
- import copy
- import os
- import shutil
- BASE_ADDR = 0x182200
- s1 = " .,'!?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~-&>():;/"
- s2 = " .,’!?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~‐&>():;/"
- translate_table = str.maketrans(s1, s2) #For converting between ASCII and fullwidth SHIFT-JIS characters
- namelist = {'#M': '#M', '#F': '#F', '#SC': '#SC', '#SA': '#SA', '#SB': '#SB', #For fixing corruptions
- '#TB': '#TB', '#TC': '#TC', '#GB': '#GB', '#GA': '#GA',
- '#GC': '#GC', '#TA': '#TA'}
- if os.path.isfile('shared.orig'):
- pass
- elif os.path.isfile('shared.bin'):
- shutil.copy('shared.bin','shared.orig')
- else:
- print('Missing shared.bin.')
- quit()
- #Given the pointer location, it returns the location and the value
- def getpointer(pos, filedata):
- return [pos, struct.unpack('<I', filedata[pos:pos+4])[0]]
- #Multiple replace. Pretty cool, huh? repls is a dic w/ the replacements
- def replace_all(repls, str):
- return re.sub('|'.join(re.escape(key) for key in repls.keys()),
- lambda k: repls[k.group(0)], str)
- #It's supposed to insert the script with your input.
- #Writes a compressed, inserted script file
- #Also, it updates the archive index
- def insert_script(file_num, input_col):
- #Declaring globals is cheating. But I'm not sure how to avoid it.
- global archive_index
- file_num_str = '{:0>3}'.format(file_num) #Need the str for file operations
- inputdata = []
- with codecs.open(file_num_str + '.tsv','rb','utf-8') as f: #Open input file
- for line in f:
- line = line.rstrip('\r\n').split('\t')
- text = line[input_col].translate(translate_table) #Change to fullwidth characters
- text = replace_all(namelist, text) #Get rid of corruptions like #M (should be #M)
- while '"' in text: #Mess with quotations (gives open/close quotation marks)
- text = text.replace('"', '“', 1)
- text = text.replace('"', '”', 1)
- if line[1] != '': #Start of opcode
- inputdata.append([line[0]]) #Append address
- if line[1] in names: #Starts w/ a name
- inputdata[-1] += [names[line[1]]] #Lookup translated name
- elif line[2] != '': #Two strings on this line
- inputdata[-1] += text.split('|')
- continue
- else: #One string, non-name
- inputdata[-1] += [text]
- continue
- if line[input_col] == '': #Blank line
- continue
- inputdata[-1] += [text] #Continuation line (additional line for same opcode)
- #Load and decompress data
- offset, compressed_size = (archive_index_orig[file_num][0],
- archive_index_orig[file_num][2])
- with open('shared.orig', 'rb') as f:
- f.seek(BASE_ADDR + offset)
- filedata = bytearray(zlib.decompress(f.read(compressed_size)))
- #Get pointers
- ptr_loc = [0x4, 0x44, 0x4c, 0x54, 0x5c, 0x6c]
- num_of_strings = struct.unpack('<I', filedata[0x38:0x3C])[0]
- ptr_table_addr = struct.unpack('<I', filedata[0x44:0x48])[0]
- str_table_addr = struct.unpack('<I', filedata[0x4c:0x50])[0]
- ptr_loc2 = [ptr_table_addr + x * 4 for x in range(num_of_strings + 1)]
- ptrs = [getpointer(ptr, filedata) for ptr in ptr_loc]
- ptrs2 = [getpointer(ptr, filedata) for ptr in ptr_loc2]
- #Replace each string, update pointers
- offset = 0
- for data in inputdata:
- pos = int(data[0],16) + str_table_addr + offset
- text = b'\x40'.join([x.encode('cp932') for x in data[1:]])
- orig_len = len(filedata[pos:filedata.find(b'\x00',pos)])
- for i, (ptr_loc, ptr_tgt) in enumerate(ptrs):
- if ptr_tgt > pos:
- ptrs[i][1] += len(text) - orig_len
- for i, (ptr_loc, ptr_tgt) in enumerate(ptrs2):
- if ptr_tgt + str_table_addr > pos:
- ptrs2[i][1] += len(text) - orig_len
- filedata[pos:pos + orig_len] = text
- offset += len(text) - orig_len
- #Write-back pointers
- for ptr_loc, ptr_tgt in ptrs + ptrs2:
- filedata[ptr_loc:ptr_loc+4] = struct.pack('<I', ptr_tgt)
- #Update archive index compressed and decompressed sizes
- archive_index[file_num][1] = len(filedata) #Update decompressed size
- ## with open(file_num_str + '.temp1','wb') as f:
- ## f.write(filedata)
- filedata = zlib.compress(filedata, 9) #Compress data
- archive_index[file_num][2] = len(filedata) #Update compressed size
- #Update archive index offsets
- if file_num == num_of_files - 1: #Ignore last file
- pass
- else: #Not the last file
- avail_blocks = (archive_index[file_num + 1][0] - archive_index[file_num][0]) // 16 #1 block = 16 bytes
- if len(filedata) % 16 == 0:
- needed_blocks = len(filedata) // 16 + 1 #It gives an extra whole block of zeros. Doesn't really matter I don't think.
- else:
- needed_blocks = len(filedata) // 16 + 2
- if needed_blocks > avail_blocks: #More blocks are needed. This is usually the case.
- for x in range(file_num + 1, num_of_files): #Move all files after this one down by # of extra blocks needed
- archive_index[x][0] += 16 * (needed_blocks - avail_blocks)
- with open(file_num_str + '.temp','wb') as f: #Write compressed data to file (it's needed later)
- f.write(filedata)
- #Load names file
- names = {}
- with codecs.open('names.tsv','rb','utf-8') as f:
- for line in f:
- line = line.rstrip('\r\n').split('\t')
- if line[0] in namelist.values():
- names[line[0]] = line[0]
- else:
- names[line[0]] = line[1].translate(translate_table)
- #Load archive index
- with open('shared.orig', 'rb') as f:
- f.seek(BASE_ADDR + 4)
- num_of_files = struct.unpack('<I', f.read(4))[0]
- archive_index = []
- for x in range(num_of_files):
- archive_index.append(list(struct.unpack('<III', f.read(12)))) #Offset, decompressed size, compressed size
- #The program needs data from shared.orig.
- #It uses archive_index_orig to access that data.
- #You need deepcopy! Nothing else worked!
- archive_index_orig = copy.deepcopy(archive_index)
- scripts_to_insert_3 = [3, 4, 5, 6, 7, 8]
- scripts_to_insert_4 = []
- scripts_to_insert_5 = [0, 1, 2]
- scripts_to_insert = [[x] + [3] for x in scripts_to_insert_3] + \
- [[x] + [4] for x in scripts_to_insert_4] + \
- [[x] + [5] for x in scripts_to_insert_5]
- for file_num, col_to_insert in sorted(scripts_to_insert, key=lambda x: x[0]):
- insert_script(file_num, col_to_insert)
- tempfilename = 'shared.tmp'
- shutil.copy('shared.bin', tempfilename)
- with open(tempfilename, 'rb') as f:
- with open('shared.bin','wb') as g:
- g.write(f.read(BASE_ADDR)) #Everything up to where shared.bin should go.
- g.write(b'Wpbb') #0x0 - File identifier
- g.write(struct.pack('<I', num_of_files)) #0x4 - Number of files
- for i in archive_index: #Write archive index
- g.write(struct.pack('<III', *i))
- for i in range(num_of_files): #Write each sub-file in archive
- if g.tell() - BASE_ADDR > archive_index[i][0]:
- print('error')
- print(str(i), hex(g.tell() - BASE_ADDR), hex(archive_index[i][0]))
- quit()
- while g.tell() - BASE_ADDR < archive_index[i][0]: #Write zeros to get to the correct start position
- g.write(b'\x00')
- if i in [x[0] for x in scripts_to_insert]: #If it's a new custom script file...
- with open('{:0>3}'.format(i) + '.temp','rb') as h: #Write the previously saved, compressed script
- g.write(h.read())
- else: #Not a new custom script file
- f.seek(BASE_ADDR + archive_index_orig[i][0]) #Write original data from shared.bin
- g.write(f.read(archive_index[i][2]))
- #Finished writing archive file
- size = g.tell() - BASE_ADDR + 0x200
- g.write(b'\x00' * (BASE_ADDR + 0xc5600 - g.tell())) #Write zeros until where the next file in shared.bin should go
- f.seek(BASE_ADDR + 0xc5600) #Jump shared.bin to correct position
- g.write(f.read()) #Write the rest of shared.bin, unchanged
- os.remove(tempfilename)
- with open('EBOOT.BIN', 'rb') as f:
- filedata = bytearray(f.read())
- filedata[0x15DCFC:0x15DD00] = struct.pack('<I', size)
- with open('EBOOT.BIN', 'wb') as f:
- f.write(filedata)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement