mirror of
https://github.com/Skeli789/Dynamic-Pokemon-Expansion.git
synced 2026-04-23 16:57:23 -05:00
Fix Merge Conflicts
This commit is contained in:
parent
993b3ce2bd
commit
745de0d9fa
|
|
@ -2,381 +2,541 @@
|
|||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import binascii
|
||||
import textwrap
|
||||
import sys
|
||||
|
||||
OFFSET_TO_PUT = 0x133bed0
|
||||
SOURCE_ROM = "Pokemon Unbound.gba"
|
||||
|
||||
from datetime import datetime
|
||||
import _io
|
||||
|
||||
PathVar = os.environ.get('Path')
|
||||
Paths = PathVar.split(';')
|
||||
PATH = ""
|
||||
for candidatePath in Paths:
|
||||
if "devkitARM" in candidatePath:
|
||||
PATH = candidatePath
|
||||
break
|
||||
if PATH == "":
|
||||
PATH = 'C://devkitPro//devkitARM//bin'
|
||||
if os.path.isdir(PATH) == False:
|
||||
print('Devkit not found.')
|
||||
sys.exit(1)
|
||||
|
||||
OFFSET_TO_PUT = 0x1800000
|
||||
SOURCE_ROM = "BPRE0.gba"
|
||||
ROM_NAME = "test.gba"
|
||||
|
||||
PREFIX = 'arm-none-eabi-'
|
||||
OBJCOPY = os.path.join(PATH, PREFIX + 'objcopy')
|
||||
OBJDUMP = os.path.join(PATH, PREFIX + 'objdump')
|
||||
NM = os.path.join(PATH, PREFIX + 'nm')
|
||||
AS = os.path.join(PATH, PREFIX + 'as')
|
||||
CC = os.path.join(PATH, PREFIX + 'gcc')
|
||||
CXX = os.path.join(PATH, PREFIX + 'g++')
|
||||
if sys.platform.startswith('win'):
|
||||
PathVar = os.environ.get('Path')
|
||||
Paths = PathVar.split(';')
|
||||
PATH = ''
|
||||
for candidatePath in Paths:
|
||||
if 'devkitARM' in candidatePath:
|
||||
PATH = candidatePath
|
||||
break
|
||||
if PATH == '':
|
||||
PATH = 'C://devkitPro//devkitARM//bin'
|
||||
if os.path.isdir(PATH) is False:
|
||||
print('Devkit not found.')
|
||||
sys.exit(1)
|
||||
|
||||
def ExtractPointer(listy):
|
||||
pointer = 0
|
||||
for a in range(len(listy)):
|
||||
pointer += (int(listy[a])) << (8 * a)
|
||||
return pointer
|
||||
PREFIX = 'arm-none-eabi-'
|
||||
OBJDUMP = os.path.join(PATH, PREFIX + 'objdump')
|
||||
NM = os.path.join(PATH, PREFIX + 'nm')
|
||||
AS = os.path.join(PATH, PREFIX + 'as')
|
||||
|
||||
def get_text_section():
|
||||
try:
|
||||
# Dump sections
|
||||
out = subprocess.check_output([OBJDUMP, '-t', 'build/linked.o'])
|
||||
lines = out.decode().split('\n')
|
||||
|
||||
# Find text section
|
||||
text = filter(lambda x: x.strip().endswith('.text'), lines)
|
||||
section = (list(text))[0]
|
||||
|
||||
# Get the offset
|
||||
offset = int(section.split(' ')[0], 16)
|
||||
|
||||
return offset
|
||||
|
||||
except:
|
||||
print("Error: The insertion process could not be completed.\n" +
|
||||
"The linker symbol file was not found.")
|
||||
sys.exit(1)
|
||||
else: # Linux, OSX, etc.
|
||||
PREFIX = 'arm-none-eabi-'
|
||||
OBJDUMP = (PREFIX + 'objdump')
|
||||
NM = (PREFIX + 'nm')
|
||||
AS = (PREFIX + 'as')
|
||||
|
||||
def symbols(subtract=0):
|
||||
out = subprocess.check_output([NM, 'build/linked.o'])
|
||||
lines = out.decode().split('\n')
|
||||
|
||||
name = ''
|
||||
|
||||
ret = {}
|
||||
for line in lines:
|
||||
parts = line.strip().split()
|
||||
|
||||
if (len(parts) < 3):
|
||||
continue
|
||||
|
||||
if (parts[1].lower() not in {'t','d'}):
|
||||
continue
|
||||
|
||||
offset = int(parts[0], 16)
|
||||
ret[parts[2]] = offset - subtract
|
||||
|
||||
return ret
|
||||
|
||||
def hook(rom, space, hook_at, register=0):
|
||||
# Align 2
|
||||
if hook_at & 1:
|
||||
hook_at -= 1
|
||||
|
||||
rom.seek(hook_at)
|
||||
|
||||
register &= 7
|
||||
|
||||
if hook_at % 4:
|
||||
data = bytes([0x01, 0x48 | register, 0x00 | (register << 3), 0x47, 0x0, 0x0])
|
||||
else:
|
||||
data = bytes([0x00, 0x48 | register, 0x00 | (register << 3), 0x47])
|
||||
|
||||
space += 0x08000001
|
||||
data += (space.to_bytes(4, 'little'))
|
||||
rom.write(bytes(data))
|
||||
OUTPUT = 'build/output.bin'
|
||||
BYTE_REPLACEMENT = 'bytereplacement'
|
||||
HOOKS = 'hooks'
|
||||
REPOINTS = 'repoints'
|
||||
GENERATED_REPOINTS = 'generatedrepoints'
|
||||
REPOINT_ALL = 'repointall'
|
||||
ROUTINE_POINTERS = 'routinepointers'
|
||||
FUNCTION_REWRITES = 'functionrewrites'
|
||||
SPECIAL_INSERTS = 'special_inserts.asm'
|
||||
SPECIAL_INSERTS_OUT = 'build/special_inserts.bin'
|
||||
|
||||
def funcwrap(rom, space, hook_at, nparams, isreturning):
|
||||
# Align 2
|
||||
if hook_at & 1:
|
||||
hook_at -= 1
|
||||
|
||||
rom.seek(hook_at)
|
||||
nparams=nparams-1
|
||||
|
||||
if nparams<4:
|
||||
data = bytes([0x10, 0xB5, 0x3, 0x4C, 0x0, 0xF0, 0x3, 0xF8, 0x10, 0xBC , (isreturning+1), 0xBC , (isreturning<<3), 0x47, 0x20, 0x47])
|
||||
else:
|
||||
k=nparams-3
|
||||
data = bytes([0x10, 0xB5, 0x82, 0xB0])
|
||||
for i in range(k+2):
|
||||
data += bytes([ i+2, 0x9C , i, 0x94])
|
||||
data += bytes([0x0, 0x9C , (nparams-1), 0x94, 0x1, 0x9C , nparams, 0x94, 0x2, 0xB0 , (k+8), 0x4C,
|
||||
0x0, 0xF0 , ((k<<1)+13), 0xF8, 0x82, 0xB0 , nparams, 0x9C, 0x1, 0x94 , (nparams-1), 0x9C , 0x0, 0x94])
|
||||
for i in reversed(range(k+2)):
|
||||
data += bytes([ i, 0x9C , i+2, 0x94])
|
||||
data += bytes([0x2, 0xB0 , 0x10, 0xBC, (isreturning+1), 0xBC , (isreturning<<3), 0x47, 0x20, 0x47])
|
||||
|
||||
space += 0x08000001
|
||||
data += (space.to_bytes(4, 'little'))
|
||||
rom.write(bytes(data))
|
||||
def ExtractPointer(byteList: [bytes]):
|
||||
pointer = 0
|
||||
for a in range(len(byteList)):
|
||||
pointer += (int(byteList[a])) << (8 * a)
|
||||
|
||||
def repoint(rom, space, repoint_at, slidefactor=0):
|
||||
rom.seek(repoint_at)
|
||||
|
||||
space += (0x08000000+slidefactor)
|
||||
data = (space.to_bytes(4, 'little'))
|
||||
rom.write(bytes(data))
|
||||
return pointer
|
||||
|
||||
ignored_offsets = [0x3986C0, 0x3986EC, 0xDABDF0] #These offsets contain the word 0x8900000 - the attack data from
|
||||
#Mr. DS's rombase. In order to maintain as much compatability as
|
||||
#possible, the data at these offsets is never modified.
|
||||
|
||||
def real_repoint(rom, offset_tuples):
|
||||
pointer_list = []
|
||||
pointer_dict = {}
|
||||
for tuple in offset_tuples: #Format is (Double Pointer, New Pointer, Symbol)
|
||||
offset = tuple[0]
|
||||
rom.seek(offset)
|
||||
pointer = ExtractPointer(rom.read(4))
|
||||
pointer_list.append(pointer)
|
||||
pointer_dict[pointer] = (tuple[1] + 0x08000000, tuple[2])
|
||||
|
||||
offset = 0
|
||||
offset_list = []
|
||||
|
||||
while (offset < 0xFFFFFD):
|
||||
if offset in ignored_offsets:
|
||||
offset += 4
|
||||
continue
|
||||
|
||||
rom.seek(offset)
|
||||
word = ExtractPointer(rom.read(4))
|
||||
rom.seek(offset)
|
||||
|
||||
for pointer in pointer_list:
|
||||
if word == pointer:
|
||||
offset_list.append((offset, pointer_dict[pointer][1]))
|
||||
rom.write(bytes(pointer_dict[pointer][0].to_bytes(4, 'little')))
|
||||
break
|
||||
|
||||
offset += 4
|
||||
|
||||
return offset_list
|
||||
def GetTextSection() -> int:
|
||||
try:
|
||||
# Dump sections
|
||||
out = subprocess.check_output([OBJDUMP, '-t', 'build/linked.o'])
|
||||
lines = out.decode().split('\n')
|
||||
|
||||
def bytereplace(rom, offset, data):
|
||||
# Find text section
|
||||
text = filter(lambda x: x.strip().endswith('.text'), lines)
|
||||
section = (list(text))[0]
|
||||
|
||||
ar=offset
|
||||
words=data.split()
|
||||
for i in range(0,len(words)):
|
||||
rom.seek(ar)
|
||||
intbyte=int(words[i],16)
|
||||
rom.write(bytes(intbyte.to_bytes(1, 'big')))
|
||||
ar += 1
|
||||
# Get the offset
|
||||
offset = int(section.split(' ')[0], 16)
|
||||
return offset
|
||||
|
||||
starttime = datetime.now()
|
||||
except:
|
||||
print("Error: The insertion process could not be completed.\n"
|
||||
+ "The linker symbol file was not found.")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
shutil.copyfile(SOURCE_ROM, ROM_NAME)
|
||||
except FileNotFoundError:
|
||||
print('Error: Insertion could not be completed.\nCould not find source rom: "' + SOURCE_ROM + '".\nPlease make sure a rom with this name exists in the root.')
|
||||
sys.exit(0)
|
||||
except PermissionError:
|
||||
print('Error: Insertion could not be completed.\n"' + ROM_NAME + '" is currently in use by another application.\nPlease free it up before trying again.')
|
||||
sys.exit(0)
|
||||
|
||||
with open(ROM_NAME, 'rb+') as rom:
|
||||
print("Inserting code.")
|
||||
table = symbols(get_text_section())
|
||||
rom.seek(OFFSET_TO_PUT)
|
||||
with open('build/output.bin', 'rb') as binary:
|
||||
rom.write(binary.read())
|
||||
binary.close()
|
||||
|
||||
# Adjust symbol table
|
||||
for entry in table:
|
||||
table[entry] += OFFSET_TO_PUT
|
||||
|
||||
# Insert byte changes
|
||||
with open('bytereplacement', 'r') as replacelist:
|
||||
for line in replacelist:
|
||||
if line.strip().startswith('#') or line.strip() == '' : continue
|
||||
offset = int(line[:8],16) - 0x08000000
|
||||
bytereplace(rom, offset, line[9:].strip())
|
||||
|
||||
# Do Special Inserts
|
||||
with open('special_inserts.asm', 'r') as file:
|
||||
loadOffsets = False
|
||||
offsetList = []
|
||||
for line in file:
|
||||
if line.strip().startswith('.org '):
|
||||
offsetList.append(int(line.split('.org ')[1].split(',')[0], 16))
|
||||
|
||||
offsetList.sort()
|
||||
|
||||
try:
|
||||
with open('build/special_inserts.bin', 'rb') as binFile:
|
||||
for offset in offsetList:
|
||||
originalOffset = offset
|
||||
dataList = ""
|
||||
|
||||
if offsetList.index(offset) == len(offsetList) - 1:
|
||||
while True:
|
||||
try:
|
||||
binFile.seek(offset)
|
||||
dataList += hex(binFile.read(1)[0]) + " "
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
offset += 1
|
||||
else:
|
||||
binFile.seek(offset)
|
||||
word = ExtractPointer(binFile.read(4))
|
||||
|
||||
while (word != 0xFFFFFFFF):
|
||||
binFile.seek(offset)
|
||||
dataList += hex(binFile.read(1)[0]) + " "
|
||||
offset += 1
|
||||
|
||||
if offset in offsetList: #Overlapping data
|
||||
break
|
||||
|
||||
word = ExtractPointer(binFile.read(4))
|
||||
|
||||
bytereplace(rom, originalOffset, dataList.strip())
|
||||
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Read hooks from a file
|
||||
with open('hooks', 'r') as hooklist:
|
||||
for line in hooklist:
|
||||
if line.strip().startswith('#') or line.strip() == '': continue
|
||||
|
||||
symbol, address, register = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
def GetSymbols(subtract=0) -> {str: int}:
|
||||
out = subprocess.check_output([NM, 'build/linked.o'])
|
||||
lines = out.decode().split('\n')
|
||||
|
||||
hook(rom, code, offset, int(register))
|
||||
|
||||
# Read repoints from a file
|
||||
with open('repoints', 'r') as repointlist:
|
||||
for line in repointlist:
|
||||
if line.strip().startswith('#') or line.strip() == '': continue
|
||||
if len(line.split()) is 2:
|
||||
symbol, address = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
repoint(rom, code, offset)
|
||||
|
||||
if len(line.split()) is 3:
|
||||
symbol, address, slide = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
repoint(rom, code, offset, int(slide))
|
||||
ret = {}
|
||||
for line in lines:
|
||||
parts = line.strip().split()
|
||||
|
||||
symbols_repointed = set()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
|
||||
try:
|
||||
with open('generatedrepoints', 'r') as repointlist:
|
||||
for line in repointlist:
|
||||
if line.strip().startswith('#') or line.strip() == '': continue
|
||||
|
||||
symbol, address = line.split()
|
||||
offset = int(address)
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
symbols_repointed.add(symbol)
|
||||
repoint(rom, code, offset)
|
||||
|
||||
except FileNotFoundError:
|
||||
with open('generatedrepoints', 'w') as repointlist:
|
||||
repointlist.write('##This is a generated file at runtime. Do not modify it!\n')
|
||||
|
||||
offsets_to_repoint_together = []
|
||||
|
||||
with open('repointall', 'r') as repointlist:
|
||||
for line in repointlist:
|
||||
if line.strip().startswith('#') or line.strip() == '': continue
|
||||
|
||||
symbol, address = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
|
||||
if symbol in symbols_repointed: continue
|
||||
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
offsets_to_repoint_together.append((offset, code, symbol))
|
||||
|
||||
if offsets_to_repoint_together != []:
|
||||
offsets = real_repoint(rom, offsets_to_repoint_together) #Format is [(offset, symbol), ...]
|
||||
|
||||
output = open('generatedrepoints', 'a')
|
||||
for tuple in offsets:
|
||||
output.write(tuple[1] + ' ' + str(tuple[0]) + '\n')
|
||||
#output.close() #Purposely left open so the user can't modify it
|
||||
if parts[1].lower() not in {'t', 'd'}:
|
||||
continue
|
||||
|
||||
# Read routine repoints from a file
|
||||
with open('routinepointers', 'r') as pointerlist:
|
||||
for line in pointerlist:
|
||||
if line.strip().startswith('#') or line.strip() == '': continue
|
||||
|
||||
symbol, address = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
offset = int(parts[0], 16)
|
||||
ret[parts[2]] = offset - subtract
|
||||
|
||||
repoint(rom, code, offset, 1)
|
||||
return ret
|
||||
|
||||
# Read routine rewrite wrapper from a file
|
||||
with open('functionrewrites', 'r') as frwlist:
|
||||
for line in frwlist:
|
||||
if line.strip().startswith('#') or line.strip() == '': continue
|
||||
|
||||
symbol, address, nparam, isreturning = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
funcwrap(rom, code, offset, int(nparam), int(isreturning))
|
||||
|
||||
width = max(map(len, table.keys())) + 1
|
||||
try:
|
||||
offset_file = open("offsets.ini", 'r+')
|
||||
except FileNotFoundError:
|
||||
offset_file = open("offsets.ini", 'w')
|
||||
offset_file.truncate()
|
||||
for key in sorted(table.keys()):
|
||||
fstr = ('{:' + str(width) + '} {:08X}')
|
||||
offset_file.write(fstr.format(key + ':', table[key] + 0x08000000) + '\n')
|
||||
offset_file.close()
|
||||
print('Inserted in ' + str(datetime.now() - starttime) + '.')
|
||||
def Hook(rom: _io.BufferedReader, space: int, hookAt: int, register=0):
|
||||
# Align 2
|
||||
if hookAt & 1:
|
||||
hookAt -= 1
|
||||
|
||||
rom.seek(hookAt)
|
||||
|
||||
register &= 7
|
||||
|
||||
if hookAt % 4:
|
||||
data = bytes([0x01, 0x48 | register, 0x00 | (register << 3), 0x47, 0x0, 0x0])
|
||||
else:
|
||||
data = bytes([0x00, 0x48 | register, 0x00 | (register << 3), 0x47])
|
||||
|
||||
space += 0x08000001
|
||||
data += (space.to_bytes(4, 'little'))
|
||||
rom.write(bytes(data))
|
||||
|
||||
|
||||
def FunctionWrap(rom: _io.BufferedReader, space: int, hookAt: int, numParams: int, isReturning: int):
|
||||
# Align 2
|
||||
if hookAt & 1:
|
||||
hookAt -= 1
|
||||
|
||||
rom.seek(hookAt)
|
||||
numParams = numParams - 1
|
||||
|
||||
if numParams < 4:
|
||||
data = bytes([0x10, 0xB5, 0x3, 0x4C, 0x0, 0xF0, 0x3, 0xF8, 0x10, 0xBC,
|
||||
(isReturning + 1), 0xBC, (isReturning << 3), 0x47, 0x20, 0x47])
|
||||
else:
|
||||
k = numParams - 3
|
||||
data = bytes([0x10, 0xB5, 0x82, 0xB0])
|
||||
for i in range(k + 2):
|
||||
data += bytes([i + 2, 0x9C, i, 0x94])
|
||||
data += bytes([0x0, 0x9C, numParams - 1, 0x94, 0x1, 0x9C, numParams, 0x94, 0x2, 0xB0, k + 8, 0x4C,
|
||||
0x0, 0xF0, (k << 1) + 13, 0xF8, 0x82, 0xB0, numParams, 0x9C, 0x1, 0x94, numParams - 1,
|
||||
0x9C, 0x0, 0x94])
|
||||
|
||||
for i in reversed(range(k + 2)):
|
||||
data += bytes([i, 0x9C, i+2, 0x94])
|
||||
data += bytes([0x2, 0xB0, 0x10, 0xBC, isReturning + 1, 0xBC, isReturning << 3, 0x47, 0x20, 0x47])
|
||||
|
||||
space += 0x08000001
|
||||
data += (space.to_bytes(4, 'little'))
|
||||
rom.write(bytes(data))
|
||||
|
||||
|
||||
def Repoint(rom: _io.BufferedReader, space: int, repointAt: int, slideFactor=0):
|
||||
rom.seek(repointAt)
|
||||
|
||||
space += (0x08000000 + slideFactor)
|
||||
data = (space.to_bytes(4, 'little'))
|
||||
rom.write(bytes(data))
|
||||
|
||||
|
||||
# These offsets contain the word 0x8900000 - the attack data from
|
||||
# Mr. DS's rombase. In order to maintain as much compatibility as
|
||||
# possible, the data at these offsets is never modified.
|
||||
IGNORED_OFFSETS = [0x3986C0, 0x3986EC, 0xDABDF0]
|
||||
|
||||
|
||||
def RealRepoint(rom: _io.BufferedReader, offsetTuples: [(int, int, str)]):
|
||||
pointerList = []
|
||||
pointerDict = {}
|
||||
for tup in offsetTuples: # Format is (Double Pointer, New Pointer, Symbol)
|
||||
offset = tup[0]
|
||||
rom.seek(offset)
|
||||
pointer = ExtractPointer(rom.read(4))
|
||||
pointerList.append(pointer)
|
||||
pointerDict[pointer] = (tup[1] + 0x08000000, tup[2])
|
||||
|
||||
offset = 0
|
||||
offsetList = []
|
||||
|
||||
while offset < 0xFFFFFD:
|
||||
if offset in IGNORED_OFFSETS:
|
||||
offset += 4
|
||||
continue
|
||||
|
||||
rom.seek(offset)
|
||||
word = ExtractPointer(rom.read(4))
|
||||
rom.seek(offset)
|
||||
|
||||
for pointer in pointerList:
|
||||
if word == pointer:
|
||||
offsetList.append((offset, pointerDict[pointer][1]))
|
||||
rom.write(bytes(pointerDict[pointer][0].to_bytes(4, 'little')))
|
||||
break
|
||||
|
||||
offset += 4
|
||||
|
||||
return offsetList
|
||||
|
||||
|
||||
def ReplaceBytes(rom: _io.BufferedReader, offset: int, data: str):
|
||||
ar = offset
|
||||
words = data.split()
|
||||
for i in range(0, len(words)):
|
||||
rom.seek(ar)
|
||||
intByte = int(words[i], 16)
|
||||
rom.write(bytes(intByte.to_bytes(1, 'big')))
|
||||
ar += 1
|
||||
|
||||
|
||||
def TryProcessFileInclusion(line: str, definesDict: dict) -> bool:
|
||||
if line.startswith('#include "'):
|
||||
try:
|
||||
path = line.split('"')[1].strip()
|
||||
with open(path, 'r') as file:
|
||||
for line in file:
|
||||
if line.startswith('#define '):
|
||||
try:
|
||||
lineList = line.strip().split()
|
||||
title = lineList[1]
|
||||
|
||||
if len(lineList) == 2 or lineList[2].startswith('//') or lineList[2].startswith('/*'):
|
||||
define = True
|
||||
else:
|
||||
define = lineList[2]
|
||||
|
||||
definesDict[title] = define
|
||||
except IndexError:
|
||||
print('Error reading define on line"' + line.strip() + '" in file "' + path + '".')
|
||||
|
||||
except Exception as e:
|
||||
print('Error including file on line "' + line.strip() + '".')
|
||||
print(e)
|
||||
|
||||
return True # Inclusion line; don't read otherwise
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def TryProcessConditionalCompilation(line: str, definesDict: dict, conditionals: [(str, bool)]) -> bool:
|
||||
line = line.strip()
|
||||
upperLine = line.upper()
|
||||
numWordsOnLine = len(line.split())
|
||||
|
||||
if upperLine.startswith('#IFDEF ') and numWordsOnLine > 1:
|
||||
condition = line.strip().split()[1]
|
||||
conditionals.insert(0, (condition, True)) # Insert at front
|
||||
return True
|
||||
elif upperLine.startswith('#INFDEF ') and numWordsOnLine > 1:
|
||||
condition = line.strip().split()[1]
|
||||
conditionals.insert(0, (condition, False)) # Insert at front
|
||||
return True
|
||||
elif upperLine == '#ELSE':
|
||||
if len(conditionals) >= 1: # At least one statement was pushed before
|
||||
condition = conditionals.pop(0)
|
||||
if condition[1] is True:
|
||||
conditionals.insert(0, (condition[0], False)) # Invert old statement
|
||||
else:
|
||||
conditionals.insert(0, (condition[0], True)) # Invert old statement
|
||||
return True
|
||||
elif upperLine == '#ENDIF':
|
||||
conditionals.pop(0) # Remove first element (last pushed)
|
||||
return True
|
||||
else:
|
||||
for condition in conditionals:
|
||||
definedType = condition[1]
|
||||
condition = condition[0]
|
||||
|
||||
if definedType is True: # From #ifdef
|
||||
if condition not in definesDict:
|
||||
return True # If something isn't defined then skip the line
|
||||
else: # From #ifndef
|
||||
if condition in definesDict:
|
||||
return True # If something is defined then skip the line
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
startTime = datetime.now()
|
||||
|
||||
try:
|
||||
shutil.copyfile(SOURCE_ROM, ROM_NAME)
|
||||
except FileNotFoundError:
|
||||
print('Error: Insertion could not be completed.\n'
|
||||
+ 'Could not find source rom: "' + SOURCE_ROM + '".\n'
|
||||
+ 'Please make sure a rom with this name exists in the root.')
|
||||
sys.exit(1)
|
||||
except PermissionError:
|
||||
print('Error: Insertion could not be completed.\n'
|
||||
+ '"' + ROM_NAME + '" is currently in use by another application.'
|
||||
+ '\nPlease free it up before trying again.')
|
||||
sys.exit(1)
|
||||
|
||||
with open(ROM_NAME, 'rb+') as rom:
|
||||
print("Inserting code.")
|
||||
table = GetSymbols(GetTextSection())
|
||||
rom.seek(OFFSET_TO_PUT)
|
||||
with open(OUTPUT, 'rb') as binary:
|
||||
rom.write(binary.read())
|
||||
binary.close()
|
||||
|
||||
# Adjust symbol table
|
||||
for entry in table:
|
||||
table[entry] += OFFSET_TO_PUT
|
||||
|
||||
# Insert byte changes
|
||||
if os.path.isfile(BYTE_REPLACEMENT):
|
||||
with open(BYTE_REPLACEMENT, 'r') as replacelist:
|
||||
definesDict = {}
|
||||
conditionals = []
|
||||
for line in replacelist:
|
||||
if TryProcessFileInclusion(line, definesDict):
|
||||
continue
|
||||
if TryProcessConditionalCompilation(line, definesDict, conditionals):
|
||||
continue
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
offset = int(line[:8], 16) - 0x08000000
|
||||
try:
|
||||
ReplaceBytes(rom, offset, line[9:].strip())
|
||||
except ValueError: #Try loading from the defines dict if unrecognizable character
|
||||
newNumber = definesDict[line[9:].strip()]
|
||||
try:
|
||||
newNumber = int(newNumber)
|
||||
except ValueError:
|
||||
newNumber = int(newNumber, 16)
|
||||
|
||||
newNumber = str(hex(newNumber)).split('0x')[1]
|
||||
ReplaceBytes(rom, offset, newNumber)
|
||||
|
||||
# Do Special Inserts
|
||||
if os.path.isfile(SPECIAL_INSERTS) and os.path.isfile(SPECIAL_INSERTS_OUT):
|
||||
with open(SPECIAL_INSERTS, 'r') as file:
|
||||
offsetList = []
|
||||
for line in file:
|
||||
if line.strip().startswith('.org '):
|
||||
offsetList.append(int(line.split('.org ')[1].split(',')[0], 16))
|
||||
|
||||
offsetList.sort()
|
||||
|
||||
with open(SPECIAL_INSERTS_OUT, 'rb') as binFile:
|
||||
for offset in offsetList:
|
||||
originalOffset = offset
|
||||
dataList = ""
|
||||
|
||||
if offsetList.index(offset) == len(offsetList) - 1:
|
||||
while True:
|
||||
try:
|
||||
binFile.seek(offset)
|
||||
dataList += hex(binFile.read(1)[0]) + ' '
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
offset += 1
|
||||
else:
|
||||
binFile.seek(offset)
|
||||
word = ExtractPointer(binFile.read(4))
|
||||
|
||||
while word != 0xFFFFFFFF:
|
||||
binFile.seek(offset)
|
||||
dataList += hex(binFile.read(1)[0]) + ' '
|
||||
offset += 1
|
||||
|
||||
if offset in offsetList: # Overlapping data
|
||||
break
|
||||
|
||||
word = ExtractPointer(binFile.read(4))
|
||||
|
||||
ReplaceBytes(rom, originalOffset, dataList.strip())
|
||||
|
||||
# Read hooks from a file
|
||||
if os.path.isfile(HOOKS):
|
||||
with open(HOOKS, 'r') as hookList:
|
||||
definesDict = {}
|
||||
conditionals = []
|
||||
for line in hookList:
|
||||
if TryProcessFileInclusion(line, definesDict):
|
||||
continue
|
||||
if TryProcessConditionalCompilation(line, definesDict, conditionals):
|
||||
continue
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
symbol, address, register = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
Hook(rom, code, offset, int(register))
|
||||
|
||||
# Read repoints from a file
|
||||
if os.path.isfile(REPOINTS):
|
||||
with open(REPOINTS, 'r') as repointList:
|
||||
definesDict = {}
|
||||
conditionals = []
|
||||
for line in repointList:
|
||||
if TryProcessFileInclusion(line, definesDict):
|
||||
continue
|
||||
if TryProcessConditionalCompilation(line, definesDict, conditionals):
|
||||
continue
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
if len(line.split()) == 2:
|
||||
symbol, address = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
Repoint(rom, code, offset)
|
||||
|
||||
if len(line.split()) == 3:
|
||||
symbol, address, slide = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
Repoint(rom, code, offset, int(slide))
|
||||
|
||||
symbolsRepointed = set()
|
||||
if os.path.isfile(GENERATED_REPOINTS):
|
||||
with open(GENERATED_REPOINTS, 'r') as repointList:
|
||||
for line in repointList:
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
symbol, address = line.split()
|
||||
offset = int(address)
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
symbolsRepointed.add(symbol)
|
||||
Repoint(rom, code, offset)
|
||||
|
||||
else:
|
||||
with open(GENERATED_REPOINTS, 'w') as repointList:
|
||||
repointList.write('##This is a generated file at runtime. Do not modify it!\n')
|
||||
|
||||
if os.path.isfile(REPOINT_ALL):
|
||||
offsetsToRepointTogether = []
|
||||
with open(REPOINT_ALL, 'r') as repointList:
|
||||
definesDict = {}
|
||||
conditionals = []
|
||||
for line in repointList:
|
||||
if TryProcessFileInclusion(line, definesDict):
|
||||
continue
|
||||
if TryProcessConditionalCompilation(line, definesDict, conditionals):
|
||||
continue
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
symbol, address = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
|
||||
if symbol in symbolsRepointed:
|
||||
continue
|
||||
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
offsetsToRepointTogether.append((offset, code, symbol))
|
||||
|
||||
if offsetsToRepointTogether != []:
|
||||
offsets = RealRepoint(rom, offsetsToRepointTogether) # Format is [(offset, symbol), ...]
|
||||
|
||||
output = open(GENERATED_REPOINTS, 'a')
|
||||
for tup in offsets:
|
||||
output.write(tup[1] + ' ' + str(tup[0]) + '\n')
|
||||
output.close()
|
||||
|
||||
# Read routine repoints from a file
|
||||
if os.path.isfile(ROUTINE_POINTERS):
|
||||
with open(ROUTINE_POINTERS, 'r') as pointerlist:
|
||||
definesDict = {}
|
||||
conditionals = []
|
||||
for line in pointerlist:
|
||||
if TryProcessFileInclusion(line, definesDict):
|
||||
continue
|
||||
if TryProcessConditionalCompilation(line, definesDict, conditionals):
|
||||
continue
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
symbol, address = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
Repoint(rom, code, offset, 1)
|
||||
|
||||
# Read routine rewrite wrapper from a file
|
||||
if os.path.isfile(FUNCTION_REWRITES):
|
||||
with open(FUNCTION_REWRITES, 'r') as frwlist:
|
||||
definesDict = {}
|
||||
conditionals = []
|
||||
for line in frwlist:
|
||||
if TryProcessFileInclusion(line, definesDict):
|
||||
continue
|
||||
if TryProcessConditionalCompilation(line, definesDict, conditionals):
|
||||
continue
|
||||
if line.strip().startswith('#') or line.strip() == '':
|
||||
continue
|
||||
|
||||
symbol, address, numParams, isReturning = line.split()
|
||||
offset = int(address, 16) - 0x08000000
|
||||
try:
|
||||
code = table[symbol]
|
||||
except KeyError:
|
||||
print('Symbol missing:', symbol)
|
||||
continue
|
||||
|
||||
FunctionWrap(rom, code, offset, int(numParams), int(isReturning))
|
||||
|
||||
width = max(map(len, table.keys())) + 1
|
||||
if os.path.isfile('offsets.ini'):
|
||||
offsetIni = open('offsets.ini', 'r+')
|
||||
else:
|
||||
offsetIni = open('offsets.ini', 'w')
|
||||
|
||||
offsetIni.truncate()
|
||||
for key in sorted(table.keys()):
|
||||
fstr = ('{:' + str(width) + '} {:08X}')
|
||||
offsetIni.write(fstr.format(key + ':', table[key] + 0x08000000) + '\n')
|
||||
offsetIni.close()
|
||||
|
||||
print('Inserted in ' + str(datetime.now() - startTime) + '.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
171
scripts/make.py
171
scripts/make.py
|
|
@ -1,94 +1,119 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import shutil
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
|
||||
############
|
||||
#Options go here.
|
||||
############
|
||||
|
||||
ROM_NAME = "Pokemon Unbound.gba" #The name of your rom
|
||||
OFFSET_TO_PUT = 0x133bed0
|
||||
SEARCH_FREE_SPACE = False #Set to True if you want the script to search for free space;Set to False if you don't want to search for free space as you for example update the engine
|
||||
ROM_NAME = "BPRE0.gba" #The name of your rom
|
||||
OFFSET_TO_PUT = 0x1800000
|
||||
SEARCH_FREE_SPACE = False # Set to True if you want the script to search for free space
|
||||
# Set to False if you don't want to search for free space as you for example update the engine
|
||||
|
||||
#############
|
||||
#Options end here.
|
||||
# Options end here.
|
||||
#############
|
||||
|
||||
###############
|
||||
#Functions start here.
|
||||
# Functions start here.
|
||||
###############
|
||||
|
||||
def align_x100(offset):
|
||||
mod_x100 = offset % 0x100
|
||||
if mod_x100 != 0x0: #not aligned properly
|
||||
offset += (0x100 - mod_x100)
|
||||
return offset
|
||||
|
||||
def find_offset_to_put(rom, needed_bytes, start_loc):
|
||||
offset = start_loc
|
||||
rom.seek(0, 2)
|
||||
max_pos = rom.tell()
|
||||
found_bytes = 0
|
||||
while found_bytes < needed_bytes:
|
||||
if offset + found_bytes >= max_pos:
|
||||
print("End of file reached. Not enough free space.")
|
||||
return 0
|
||||
found_bytes += 1
|
||||
rom.seek(offset + found_bytes)
|
||||
if rom.read(1) != b'\xFF':
|
||||
offset = align_x100(offset + found_bytes)
|
||||
found_bytes = 0
|
||||
return offset
|
||||
|
||||
def file_change_line(file_path, line_to_change, replacement):
|
||||
file = open(file_path, 'r')
|
||||
copy = file.read()
|
||||
file.seek(0x0)
|
||||
line_no = 1
|
||||
for line in file:
|
||||
if (line_no == line_to_change):
|
||||
copy = copy.replace(line, replacement)
|
||||
break
|
||||
line_no += 1
|
||||
file.close()
|
||||
|
||||
file = open(file_path, 'w')
|
||||
file.write(copy)
|
||||
file.close()
|
||||
def MakeOffset0x100Aligned(offset: int) -> int:
|
||||
while offset % 16 != 0:
|
||||
offset += 1
|
||||
|
||||
def edit_linker(offset):
|
||||
file_change_line("linker.ld", 4, "\t\trom : ORIGIN = (0x08000000 + " + hex(offset) + "), LENGTH = 32M\n")
|
||||
|
||||
def edit_insert(offset):
|
||||
file_change_line("./scripts/insert.py", 11, "OFFSET_TO_PUT = " + hex(offset) + '\n')
|
||||
file_change_line("./scripts/insert.py", 12, 'SOURCE_ROM = "' + ROM_NAME + '"\n')
|
||||
|
||||
def build_code():
|
||||
os.system("python scripts/build.py")
|
||||
|
||||
def insert_code():
|
||||
os.system("python scripts/insert.py")
|
||||
|
||||
def clear_from_to(rom, from_, to_):
|
||||
rom.seek(from_)
|
||||
for i in range(0, to_ - from_):
|
||||
rom.write(b'\xFF')
|
||||
return offset
|
||||
|
||||
|
||||
def FindOffsetToPut(rom, neededBytes: int, startOffset: int) -> int:
|
||||
offset = startOffset
|
||||
rom.seek(0, 2)
|
||||
maxPosition = rom.tell()
|
||||
numFoundBytes = 0
|
||||
|
||||
while numFoundBytes < neededBytes:
|
||||
if offset + numFoundBytes >= maxPosition:
|
||||
print("End of file reached. Not enough free space.")
|
||||
return 0
|
||||
|
||||
numFoundBytes += 1
|
||||
rom.seek(offset + numFoundBytes)
|
||||
if rom.read(1) != b'\xFF':
|
||||
offset = MakeOffset0x100Aligned(offset + numFoundBytes)
|
||||
numFoundBytes = 0
|
||||
|
||||
return offset
|
||||
|
||||
|
||||
def ChangeFileLine(filePath: str, lineToChange: int, replacement: str):
|
||||
with open(filePath, 'r') as file:
|
||||
copy = file.read()
|
||||
file.seek(0x0)
|
||||
lineNum = 1
|
||||
for line in file:
|
||||
if lineNum == lineToChange:
|
||||
copy = copy.replace(line, replacement)
|
||||
break
|
||||
lineNum += 1
|
||||
|
||||
with open(filePath, 'w') as file:
|
||||
file.write(copy)
|
||||
|
||||
|
||||
def EditLinker(offset: int):
|
||||
ChangeFileLine("linker.ld", 4, "\t\trom : ORIGIN = (0x08000000 + " + hex(offset) + "), LENGTH = 32M\n")
|
||||
|
||||
|
||||
def EditInsert(offset: int):
|
||||
ChangeFileLine("./scripts/insert.py", 10, "OFFSET_TO_PUT = " + hex(offset) + '\n')
|
||||
ChangeFileLine("./scripts/insert.py", 11, 'SOURCE_ROM = "' + ROM_NAME + '"\n')
|
||||
|
||||
|
||||
def BuildCode():
|
||||
if shutil.which('python3') is not None:
|
||||
os.system("python3 scripts/build.py")
|
||||
else:
|
||||
os.system("python scripts/build.py")
|
||||
|
||||
|
||||
def InsertCode():
|
||||
if shutil.which('python3') is not None:
|
||||
os.system("python3 scripts/insert.py")
|
||||
else:
|
||||
os.system("python scripts/insert.py")
|
||||
|
||||
|
||||
def ClearFromTo(rom, from_: int, to_: int):
|
||||
rom.seek(from_)
|
||||
for i in range(0, to_ - from_):
|
||||
rom.write(b'\xFF')
|
||||
|
||||
##############
|
||||
#Functions end here.
|
||||
# Functions end here.
|
||||
##############
|
||||
|
||||
try:
|
||||
with open(ROM_NAME, 'rb+') as rom:
|
||||
offset = OFFSET_TO_PUT
|
||||
if SEARCH_FREE_SPACE == True:
|
||||
offset = find_offset_to_put(rom, 0x50000, align_x100(offset))
|
||||
edit_linker(offset)
|
||||
edit_insert(offset)
|
||||
build_code()
|
||||
insert_code()
|
||||
rom.close()
|
||||
except:
|
||||
print('Error: Could not find source rom: "' + ROM_NAME + '".\nPlease make sure a rom with this name exists in the root.')
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(ROM_NAME, 'rb+') as rom:
|
||||
offset = OFFSET_TO_PUT
|
||||
if SEARCH_FREE_SPACE is True:
|
||||
offset = FindOffsetToPut(rom, 0x50000, MakeOffset0x100Aligned(offset))
|
||||
|
||||
EditLinker(offset)
|
||||
EditInsert(offset)
|
||||
BuildCode()
|
||||
InsertCode()
|
||||
rom.close()
|
||||
|
||||
except:
|
||||
print('Error: Could not find source rom: "' + ROM_NAME + '".\n'
|
||||
+ 'Please make sure a rom with this name exists in the root.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user