Created
October 13, 2021 14:42
-
-
Save InvoxiPlayGames/45e1dd092a9af07b7b7159b41acd3cee to your computer and use it in GitHub Desktop.
string-gecko.py - generate Wii gecko/ocarina codes to replace ASCII strings in game executables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# string-gecko.py by github.com/InvoxiPlayGames | |
# command usage: python string-gecko.py [dol|dump] [filename] [oldstring] [newstring] | |
# code licensing: don't be a dick with it, credit for general usage would be nice but not required | |
# but if you use parts of this python script in your own project, credit is required | |
import struct | |
import sys | |
memorybase = 0x80000000 | |
memorysize = 0x01800000 # size of Wii MEM1, i think | |
memory = [0] * memorysize | |
if sys.argv[1] == "dol": | |
dolfilename = sys.argv[2] | |
print("loading DOL file '%s'..." % (dolfilename)) | |
f = open(dolfilename, "rb") | |
# read header | |
f.seek(0) | |
textoffsets = struct.unpack(">IIIIIII", f.read(7 * 4)) | |
dataoffsets = struct.unpack(">IIIIIIIIIII", f.read(11 * 4)) | |
textloadaddr = struct.unpack(">IIIIIII", f.read(7 * 4)) | |
dataloadaddr = struct.unpack(">IIIIIIIIIII", f.read(11 * 4)) | |
textsizes = struct.unpack(">IIIIIII", f.read(7 * 4)) | |
datasizes = struct.unpack(">IIIIIIIIIII", f.read(11 * 4)) | |
[bssaddr] = struct.unpack(">I", f.read(4)) | |
[bsssize] = struct.unpack(">I", f.read(4)) | |
[entrypoint] = struct.unpack(">I", f.read(4)) | |
# read data | |
f.seek(0) | |
doldata = f.read() | |
# we don't need the file anymore | |
f.close() | |
print() | |
if entrypoint >= memorybase and entrypoint < memorybase + memorysize: | |
print("entrypoint:") | |
print(" address: 0x%08x" % (entrypoint)) | |
else: | |
print("entrypoint unused or invalid") | |
print() | |
if bssaddr >= memorybase and bssaddr < memorybase + memorysize: | |
print("bss:") | |
print(" address: 0x%08x" % (bssaddr)) | |
print(" length: 0x%x" % (bsssize)) | |
else: | |
print("bss unused or invalid") | |
print() | |
for x in range(7): | |
if textloadaddr[x] >= memorybase and textloadaddr[x] < memorybase + memorysize - textsizes[x]: | |
print("text%i:" % (x+1)) | |
print(" address: 0x%08x" % (textloadaddr[x])) | |
print(" length: 0x%x" % (textsizes[x])) | |
print(" offset: 0x%x" % (textoffsets[x])) | |
memory[textloadaddr[x]-memorybase:textloadaddr[x]+textsizes[x]-memorybase] = doldata[textoffsets[x]:textoffsets[x]+textsizes[x]] | |
else: | |
print("text%i unused or invalid" % (x+1)) | |
print() | |
for x in range(11): | |
if dataloadaddr[x] >= memorybase and dataloadaddr[x] < memorybase + memorysize - datasizes[x]: | |
print("data%i:" % (x+1)) | |
print(" address: 0x%08x" % (dataloadaddr[x])) | |
print(" length: 0x%x" % (datasizes[x])) | |
print(" offset: 0x%x" % (dataoffsets[x])) | |
memory[dataloadaddr[x]-memorybase:dataloadaddr[x]+datasizes[x]-memorybase] = doldata[dataoffsets[x]:dataoffsets[x]+datasizes[x]] | |
else: | |
print("data%i unused or invalid" % (x+1)) | |
print("loaded '%s' successfully" % (dolfilename)) | |
elif sys.argv[1] == "dump": | |
dumpfilename = sys.argv[2] | |
print("loading memory dump file '%s'..." % (dumpfilename)) | |
f = open(dumpfilename, "rb") | |
memory = f.read() | |
f.close() | |
print("loaded '%s' successfully" % (dumpfilename)) | |
memory = bytearray(memory) | |
oldstring = sys.argv[3] | |
newstring = sys.argv[4] | |
codelength = len(oldstring) | |
if len(newstring) > len(oldstring): | |
print("new string is larger than old string, you will overwrite memory!!") | |
codelength = len(newstring) | |
elif len(oldstring) > len(newstring): | |
# might add automatically replacing substrings later, but not right now | |
print("new string is shorter than old string, will fill in the blank with null bytes") | |
if len(newstring) % 8 != 0: | |
newstring = newstring + ('\0' * 0x8) # lazy padding to avoid problems | |
noinstance = False | |
lastinstance = 0 | |
instances = [] | |
print("searching for instances of %s" % (oldstring)) | |
while not noinstance: | |
lastinstance = memory.find(bytearray(oldstring, "utf8"), lastinstance + len(oldstring), memorysize) | |
if lastinstance > 0: | |
print("found string at %08x" % (lastinstance)) | |
instances.append(lastinstance) | |
else: | |
noinstance = True | |
print() | |
print("code:") | |
for instance in instances: | |
print("%08x %08x" % (instance | 0x06000000, codelength)) | |
x = 0 | |
while x < (codelength - 1): | |
print("%02x%02x%02x%02x %02x%02x%02x%02x" % ( ord(newstring[x]), ord(newstring[x+1]), ord(newstring[x+2]), ord(newstring[x+3]), ord(newstring[x+4]), ord(newstring[x+5]), ord(newstring[x+6]), ord(newstring[x+7]) )) | |
x += 8 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment