Skip to content

Commit

Permalink
Added dolphin style txt compatibility, improved tmp folder logic
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaMKW committed Jul 22, 2020
1 parent 5e9e2e3 commit dfb7115
Showing 1 changed file with 63 additions and 31 deletions.
94 changes: 63 additions & 31 deletions GeckoLoader.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import time
import re
import shutil
import glob
import dolreader
import random

from io import BytesIO, RawIOBase

Expand Down Expand Up @@ -118,28 +118,41 @@ def geckoParser(self, geckoText, parseAll=False):
encodeType = result['encoding']

with open(r'{}'.format(geckoText), 'r', encoding=encodeType) as gecko:
data = gecko.readlines()
geckoCodes = ''
state = None

for line in data:
if parseAll.lower() == 'all':
geckoLine = re.findall(r'[A-F0-9]{8}\s[A-F0-9]{8}', line, re.IGNORECASE)
elif parseAll.lower() == 'active':
geckoLine = re.findall(r'\*\s[A-F0-9]{8}\s[A-F0-9]{8}', line, re.IGNORECASE)
else:
geckoLine = re.findall(r'\*\s[A-F0-9]{8}\s[A-F0-9]{8}', line, re.IGNORECASE)
for line in gecko.readlines():
if line in ('', '\n'):
continue

if state is None:
if line.startswith('$'):
state = 'Dolphin'
else:
state = 'OcarinaM'

try:
if state == 'OcarinaM':
if parseAll.lower() == 'all':
geckoLine = re.findall(r'[A-F0-9]{8}[\t\f ][A-F0-9]{8}', line, re.IGNORECASE)[0]
elif parseAll.lower() == 'active':
geckoLine = re.findall(r'(?:\*\s*)([A-F0-9]{8}[\t\f ][A-F0-9]{8})', line, re.IGNORECASE)[0]
else:
geckoLine = re.findall(r'(?:\*\s*)([A-F0-9]{8}[\t\f ][A-F0-9]{8})', line, re.IGNORECASE)[0]
else:
geckoLine = re.findall(r'(?<![$\*])[A-F0-9]{8}[\t\f ][A-F0-9]{8}', line, re.IGNORECASE)[0]
except IndexError:
continue

geckoLine = ''.join(geckoLine)
geckoLine = re.sub(r'\s+', '', geckoLine)
geckoCodes = geckoCodes + geckoLine.replace('*', '')
geckoCodes += geckoLine.replace(' ', '').strip()

return geckoCodes

def build(gctFile, dolFile, codehandlerFile, allocation: int, codehook: int):
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as code, open(r'{}'.format(dolFile), 'rb') as dol, open(resource_path(os.path.join('bin', r'{}'.format(codehandlerFile))), 'rb') as handler, open(os.path.join('tmp', 'tmp.bin'), 'wb+') as tmp, open(os.path.join('BUILD', os.path.basename(dolFile)), 'wb+') as final:
def build(gctFile, dolFile, codehandlerFile, tmpdir, allocation: int, codehook: int):
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as code, open(r'{}'.format(dolFile), 'rb') as dol, open(resource_path(os.path.join('bin', r'{}'.format(codehandlerFile))), 'rb') as handler, open(os.path.join(tmpdir, 'tmp.bin'), 'wb+') as tmp, open(os.path.join('BUILD', os.path.basename(dolFile)), 'wb+') as final:

if get_size(dol) < 0x100:
shutil.rmtree('tmp')
shutil.rmtree(tmpdir)
parser.error('DOL header is corrupted. Please provide a clean file')

dol.seek(0)
Expand All @@ -159,7 +172,7 @@ def build(gctFile, dolFile, codehandlerFile, allocation: int, codehook: int):

if '.' in gctFile:
if os.path.splitext(gctFile)[1].lower() == '.txt':
with open(os.path.join('tmp', 'gct.bin'), 'wb+') as temp:
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
temp.write(bytes.fromhex('00D0C0DE'*2 + codehandler.geckoParser(gctFile, args.txtcodes) + 'F000000000000000'))
temp.seek(0)
codehandler.geckocodes = GCT(temp)
Expand All @@ -169,8 +182,9 @@ def build(gctFile, dolFile, codehandlerFile, allocation: int, codehook: int):
else:
parser.error('No valid gecko code file found')
else:
with open(os.path.join('tmp', 'gct.bin'), 'wb+') as temp:
with open(os.path.join(tmpdir, 'gct.bin'), 'wb+') as temp:
temp.write(bytes.fromhex('00D0C0DE'*2))

for file in os.listdir(gctFile):
if os.path.isfile(os.path.join(gctFile, file)):
if os.path.splitext(file)[1].lower() == '.txt':
Expand All @@ -180,6 +194,7 @@ def build(gctFile, dolFile, codehandlerFile, allocation: int, codehook: int):
temp.write(gct.read()[8:-8])
else:
print(TYELLOW + ' :: WARNING: {} is not a .txt or .gct file'.format(file) + TRESET)

temp.write(bytes.fromhex('F000000000000000'))
temp.seek(0)
print(temp.read())
Expand Down Expand Up @@ -213,20 +228,24 @@ def build(gctFile, dolFile, codehandlerFile, allocation: int, codehook: int):

if args.movecodes == 'LEGACY':
codehandler.allocation = 0x80003000 - (codehandler.initaddress + codehandler.handlerlength)
patchLegacyHandler(codehandler, tmp, dolfile)
status = patchLegacyHandler(codehandler, tmp, dolfile)
legacy = True
elif args.movecodes == 'ARENA':
patchGeckoLoader(code, codehandler, tmp, dolfile, dump_address)
status = patchGeckoLoader(code, codehandler, tmp, dolfile, dump_address)
legacy = False
else: #Auto decide area
if codehandler.initaddress + codehandler.handlerlength + codehandler.geckocodes.size > 0x80002FFF:
patchGeckoLoader(code, codehandler, tmp, dolfile, dump_address)
status = patchGeckoLoader(code, codehandler, tmp, dolfile, dump_address)
legacy = False
else:
codehandler.allocation = 0x80003000 - (codehandler.initaddress + codehandler.handlerlength)
patchLegacyHandler(codehandler, tmp, dolfile)
status = patchLegacyHandler(codehandler, tmp, dolfile)
legacy = True

if status is False:
shutil.rmtree(tmpdir)
parser.error(TREDLIT + 'Not enough text sections to patch the DOL file! Potentially due to previous mods?\n' + TRESET)

dolfile.save(final)

if codehandler.allocation < codehandler.geckocodes.size:
Expand Down Expand Up @@ -274,7 +293,6 @@ def build(gctFile, dolFile, codehandlerFile, allocation: int, codehook: int):

for bit in info:
print(bit)
return

def determineCodeLength(codetype, info):
if codetype.startswith(b'\x06'):
Expand Down Expand Up @@ -429,11 +447,16 @@ def patchGeckoLoader(fLoader, codehandler: CodeHandler, tmp, dolfile: dolreader.
dolfile._rawdata.write(tmp.read())
dolfile.align(256)

assertTextSections(dolfile, 6, [[int(entrypoint, 16), geckoloader_offset]])
status = assertTextSections(dolfile, 6, [[int(entrypoint, 16), geckoloader_offset]])

if status is False:
return False

'''Write game entry in DOL file header'''
dolfile.setInitPoint(int(entrypoint, 16))

return True

def patchLegacyHandler(codehandler: CodeHandler, tmp, dolfile: dolreader.DolFile):
handler_offset = dolfile.getsize()

Expand All @@ -444,9 +467,15 @@ def patchLegacyHandler(codehandler: CodeHandler, tmp, dolfile: dolreader.DolFile
dolfile._rawdata.write(codehandler.codehandler.read() + codehandler.geckocodes.codelist.read())
dolfile.align(256)

assertTextSections(dolfile, 6, [[codehandler.initaddress, handler_offset]])
status = assertTextSections(dolfile, 6, [[codehandler.initaddress, handler_offset]])

if status is False:
return False

determineCodeHook(dolfile, codehandler)

return True

def assertTextSections(dolfile: dolreader.DolFile, textsections: int, sections_list: list):
offset = len(dolfile._text) << 2
if len(sections_list) + len(dolfile._text) <= 7:
Expand All @@ -473,9 +502,10 @@ def assertTextSections(dolfile: dolreader.DolFile, textsections: int, sections_l
dolfile._rawdata.seek(0x90 + offset)
for size in size_list:
dolfile._rawdata.write(bytes.fromhex('{:08X}'.format(size)))

return True
else:
shutil.rmtree('tmp')
parser.error(TREDLIT + 'Not enough text sections to patch the DOL file! Potentially due to previous mods?\n' + TRESET)
return False

def figureLoaderData(tmp, fLoader, codehandler: CodeHandler, dolfile: dolreader.DolFile, entrypoint: str, initpoint: list):
upperAddr, lowerAddr = entrypoint[:int(len(entrypoint)/2)], entrypoint[int(len(entrypoint)/2):]
Expand Down Expand Up @@ -612,9 +642,6 @@ def sortArgFiles(fileA, fileB):
return dolFile, gctFile

if __name__ == "__main__":
if not os.path.isdir('tmp'):
os.mkdir('tmp')

parser = argparse.ArgumentParser(prog='GeckoLoader',
description='Process files and allocations for GeckoLoader',
allow_abbrev=False)
Expand Down Expand Up @@ -707,10 +734,15 @@ def sortArgFiles(fileA, fileB):
parser.error('File/folder "' + gctFile + '" does not exist')

time1 = time.time()

tmpdir = ''.join(random.choice('1234567890-_abcdefghijklomnpqrstuvwxyz') for i in range(6)) + '-GeckoLoader'

if not os.path.isdir(tmpdir):
os.mkdir(tmpdir)

build(gctFile, dolFile, codehandlerFile, _allocation, _codehook)
build(gctFile, dolFile, codehandlerFile, tmpdir, _allocation, _codehook)

shutil.rmtree('tmp')
shutil.rmtree(tmpdir)
if not args.quiet:
print(TGREENLIT + '\n :: Compiled in {:0.4f} seconds!\n'.format(time.time() - time1) + TRESET)

Expand Down

0 comments on commit dfb7115

Please sign in to comment.