mirror of
https://github.com/Skeli789/Dynamic-Pokemon-Expansion.git
synced 2026-09-08 01:55:38 -05:00
Update Build Scripts
This commit is contained in:
643
scripts/build.py
643
scripts/build.py
@@ -46,144 +46,279 @@ LDFLAGS = ['BPRE.ld', '-T', 'linker.ld']
|
||||
CFLAGS = ['-mthumb', '-mno-thumb-interwork', '-mcpu=arm7tdmi', '-mtune=arm7tdmi',
|
||||
'-mno-long-calls', '-march=armv4t', '-Wall', '-Wextra','-Os', '-fira-loop-pressure', '-fipa-pta']
|
||||
|
||||
PrintedCompilingAudio = False #Used to tell the script whether or not the strings "Compiling Cries" has been printed
|
||||
|
||||
def run_command(cmd):
|
||||
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 == '':
|
||||
print('DevKit does not exist in your Path variable.\nChecking default location.')
|
||||
PATH = 'C://devkitPro//devkitARM//bin'
|
||||
if os.path.isdir(PATH) is False:
|
||||
print('...\nDevkit not found.')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('Devkit found.')
|
||||
|
||||
PREFIX = '/arm-none-eabi-'
|
||||
AS = PATH + PREFIX + 'as'
|
||||
CC = PATH + PREFIX + 'gcc'
|
||||
LD = PATH + PREFIX + 'ld'
|
||||
GR = 'deps/grit.exe'
|
||||
WAV2AGB = 'deps/wav2agb.exe'
|
||||
MID2AGB = 'deps/mid2agb.exe'
|
||||
OBJCOPY = PATH + PREFIX + 'objcopy'
|
||||
|
||||
else: # Linux, OSX, etc.
|
||||
PREFIX = 'arm-none-eabi-'
|
||||
AS = PREFIX + 'as'
|
||||
CC = PREFIX + 'gcc'
|
||||
LD = PREFIX + 'ld'
|
||||
GR = "grit"
|
||||
WAV2AGB = 'wav2agb'
|
||||
MID2AGB = 'mid2agb'
|
||||
OBJCOPY = PREFIX + 'objcopy'
|
||||
|
||||
SRC = './src'
|
||||
GRAPHICS = './graphics'
|
||||
ASSEMBLY = './assembly'
|
||||
STRINGS = './strings'
|
||||
AUDIO = './audio'
|
||||
BUILD = './build'
|
||||
IMAGES = './Images'
|
||||
ASFLAGS = ['-mthumb', '-I', ASSEMBLY]
|
||||
LDFLAGS = ['BPRE.ld', '-T', 'linker.ld']
|
||||
CFLAGS = ['-mthumb', '-mno-thumb-interwork', '-mcpu=arm7tdmi', '-mtune=arm7tdmi',
|
||||
'-mno-long-calls', '-march=armv4t', '-Wall', '-Wextra', '-Os', '-fira-loop-pressure', '-fipa-pta']
|
||||
|
||||
|
||||
class Master:
|
||||
@staticmethod
|
||||
def init():
|
||||
Master.printedCompilingImages = False
|
||||
Master.printedCompilingAudio = False
|
||||
Master.printedCompilingMusic = False
|
||||
|
||||
@staticmethod
|
||||
def printCompilingImages():
|
||||
if not Master.printedCompilingImages:
|
||||
# Used to tell the script whether or not the string 'Compiling Images' has been printed
|
||||
Master.printedCompilingImages = True
|
||||
print('Compiling Images')
|
||||
|
||||
@staticmethod
|
||||
def printCompilingAudio():
|
||||
if not Master.printedCompilingAudio:
|
||||
# Used to tell the script whether or not the string 'Compiling Audio' has been printed
|
||||
Master.printedCompilingAudio = True
|
||||
print('Compiling Audio')
|
||||
|
||||
@staticmethod
|
||||
def printCompilingMusic():
|
||||
if not Master.printedCompilingMusic:
|
||||
# Used to tell the script whether or not the string 'Compiling Music' has been printed
|
||||
Master.printedCompilingMusic = True
|
||||
print('Compiling Music')
|
||||
|
||||
|
||||
def RunCommand(cmd: [str]):
|
||||
"""Runs the command line command."""
|
||||
try:
|
||||
subprocess.check_output(cmd)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e.output.decode(), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def CreateOutputFile(fileName: str, newFileName: str) -> [str, bool]:
|
||||
"""Helper function to produce object file output."""
|
||||
if not os.path.isfile(fileName):
|
||||
return [newFileName, False]
|
||||
|
||||
fileExists = os.path.isfile(newFileName)
|
||||
|
||||
# If the object file was created after the file was last modified
|
||||
if fileExists and os.path.getmtime(newFileName) > os.path.getmtime(fileName):
|
||||
return [newFileName, False]
|
||||
|
||||
return [newFileName, True]
|
||||
|
||||
|
||||
def MakeGeneralOutputFile(fileName: str) -> [str, bool]:
|
||||
"""Return hash of filename to use as object filename."""
|
||||
m = hashlib.md5()
|
||||
m.update(fileName.encode())
|
||||
newFileName = os.path.join(BUILD, m.hexdigest() + '.o')
|
||||
|
||||
return CreateOutputFile(fileName, newFileName)
|
||||
|
||||
|
||||
def MakeOutputImageFile(assemblyFile: str) -> [str, bool]:
|
||||
"""Return 'IMG_' + hash of filename to use as object filename."""
|
||||
m = hashlib.md5()
|
||||
m.update(assemblyFile.encode())
|
||||
objectFile = os.path.join(BUILD, 'IMG_' + m.hexdigest() + '.o')
|
||||
|
||||
return CreateOutputFile(assemblyFile, objectFile)
|
||||
|
||||
|
||||
def MakeOutputAudioFile(assemblyFile: str) -> [str, bool]:
|
||||
"""Return "SND_" + hash of filename to use as object filename."""
|
||||
objectFile = os.path.join(BUILD, 'SND_' + assemblyFile.split("gCry")[1].split(".s")[0] + '.o')
|
||||
return CreateOutputFile(assemblyFile, objectFile)
|
||||
|
||||
|
||||
def MakeOutputMusicFile(assemblyFile: str) -> [str, bool]:
|
||||
"""Return "MUS_" + hash of filename to use as object filename."""
|
||||
if sys.platform.startswith('win'): # Windows
|
||||
objectFile = os.path.join(BUILD, 'MUS_'
|
||||
+ assemblyFile.split('\\')[len(assemblyFile.split('\\')) - 1].split(".s")[0] + '.o')
|
||||
else: # Linux, OSX, etc.
|
||||
objectFile = os.path.join(BUILD, 'MUS_'
|
||||
+ assemblyFile.split('/')[len(assemblyFile.split('/')) - 1].split(".s")[0] + '.o')
|
||||
|
||||
return CreateOutputFile(assemblyFile, objectFile)
|
||||
|
||||
|
||||
def DoMiddleManAssembly(originalFile: str, assemblyFile: str, flagFile: str, flags: [str],
|
||||
cmd: [str], func, printingFunc, isMusic: bool) -> str:
|
||||
"""Process assembly files generated by things like grit, wav2agb, or mid2agb."""
|
||||
objectFile = func(assemblyFile)[0]
|
||||
fileExists = os.path.isfile(objectFile)
|
||||
flagFileExists = os.path.isfile(flagFile)
|
||||
|
||||
if fileExists \
|
||||
and os.path.getmtime(objectFile) > os.path.getmtime(originalFile) \
|
||||
and (not flagFileExists or os.path.getmtime(objectFile) > os.path.getmtime(flagFile)):
|
||||
# If the .o file was created after the original and flag file were last modified
|
||||
return objectFile
|
||||
else: # The original file or the flag file were modified recently
|
||||
printingFunc()
|
||||
RunCommand(cmd)
|
||||
|
||||
if isMusic: # Try to update the voicegroup manually
|
||||
counter = 0
|
||||
lineToChange = ''
|
||||
with open(assemblyFile, 'r') as file:
|
||||
for line in file:
|
||||
counter += 1
|
||||
if '_grp,' in line:
|
||||
lineToChange = line.split('voicegroup')[0]
|
||||
break
|
||||
|
||||
if flags != [] and lineToChange != '' and '-G' in flags:
|
||||
ChangeFileLine(assemblyFile, counter, lineToChange + flags[flags.index('-G') + 1] + '\n')
|
||||
|
||||
regenerateObjectFile = func(assemblyFile)[1]
|
||||
if regenerateObjectFile is False:
|
||||
os.remove(assemblyFile)
|
||||
return objectFile # No point in recompiling file
|
||||
|
||||
cmd = [AS] + ASFLAGS + ['-c', assemblyFile, '-o', objectFile]
|
||||
RunCommand(cmd)
|
||||
os.remove(assemblyFile)
|
||||
return objectFile
|
||||
|
||||
|
||||
def ProcessAssembly(assemblyFile: str) -> str:
|
||||
"""Assemble."""
|
||||
objectFile, regenerateObjectFile = MakeGeneralOutputFile(assemblyFile)
|
||||
if regenerateObjectFile is False:
|
||||
return objectFile # No point in recompiling file
|
||||
|
||||
try:
|
||||
print('Assembling %s' % assemblyFile)
|
||||
cmd = [AS] + ASFLAGS + ['-c', assemblyFile, '-o', objectFile]
|
||||
RunCommand(cmd)
|
||||
|
||||
except FileNotFoundError:
|
||||
print('Error! The assembler could not be located.\n'
|
||||
+ 'Are you sure you set up your path to devkitPro/devkitARM/bin correctly?')
|
||||
sys.exit(1)
|
||||
|
||||
return objectFile
|
||||
|
||||
|
||||
def ProcessC(cFile: str) -> str:
|
||||
"""Compile C."""
|
||||
objectFile, regenerateObjectFile = MakeGeneralOutputFile(cFile)
|
||||
if regenerateObjectFile is False:
|
||||
return objectFile # No point in recompiling file
|
||||
|
||||
try:
|
||||
print('Compiling %s' % cFile)
|
||||
cmd = [CC] + CFLAGS + ['-c', cFile, '-o', objectFile]
|
||||
RunCommand(cmd)
|
||||
|
||||
except FileNotFoundError:
|
||||
print('Error! The C compiler could not be located.\n'
|
||||
+ 'Are you sure you set up your path to devkitPro/devkitARM/bin correctly?')
|
||||
sys.exit(1)
|
||||
|
||||
return objectFile
|
||||
|
||||
|
||||
def ProcessString(stringFile: str) -> str:
|
||||
"""Build and assemble strings."""
|
||||
assemblyFile = stringFile.split('.string')[0] + '.s'
|
||||
objectFile = MakeGeneralOutputFile(assemblyFile)[0]
|
||||
fileExists = os.path.isfile(objectFile)
|
||||
|
||||
if fileExists and os.path.getmtime(objectFile) > os.path.getmtime(stringFile):
|
||||
# If the .o file was created after the string file was last modified
|
||||
return objectFile
|
||||
|
||||
print('Building Strings %s' % stringFile)
|
||||
StringFileConverter(stringFile)
|
||||
|
||||
cmd = [AS] + ASFLAGS + ['-c', assemblyFile, '-o', objectFile]
|
||||
RunCommand(cmd)
|
||||
os.remove(assemblyFile)
|
||||
return objectFile
|
||||
|
||||
|
||||
def GetFlagsFromFlagFile(filePath: str) -> [str]:
|
||||
try:
|
||||
subprocess.check_output(cmd)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e.output.decode(), file = sys.stderr)
|
||||
with open(filePath, "r") as file:
|
||||
line = file.readline() # Only needs the first line
|
||||
flags = line.split()
|
||||
except FileNotFoundError:
|
||||
print('"{}" could not be found.'.format(filePath))
|
||||
sys.exit(1)
|
||||
|
||||
def make_output_file(filename):
|
||||
'''Return hash of filename to use as object filename'''
|
||||
m = hashlib.md5()
|
||||
m.update(filename.encode())
|
||||
newfilename = os.path.join(BUILD, m.hexdigest() + '.o')
|
||||
|
||||
if not os.path.isfile(filename):
|
||||
return [newfilename, False]
|
||||
|
||||
fileExists = os.path.isfile(newfilename)
|
||||
|
||||
if fileExists and os.path.getmtime(newfilename) > os.path.getmtime(filename): #If the object file was created after the file was last modified
|
||||
return [newfilename, False]
|
||||
|
||||
return [newfilename, True]
|
||||
|
||||
def make_output_img_file(filename):
|
||||
'''Return "IMG" + hash of filename to use as object filename'''
|
||||
if "frontspr" in filename:
|
||||
newfilename = os.path.join(BUILD, 'IMG_Front' + filename.split("Sprite")[1].split(".s")[0] + '.o')
|
||||
elif "backspr" in filename:
|
||||
newfilename = os.path.join(BUILD, 'IMG_Back' + filename.split("Sprite")[1].split(".s")[0] + '.o')
|
||||
else:
|
||||
m = hashlib.md5()
|
||||
m.update(filename.encode())
|
||||
newfilename = os.path.join(BUILD, 'IMG_' + m.hexdigest() + '.o')
|
||||
|
||||
if not os.path.isfile(filename):
|
||||
return [newfilename, False]
|
||||
|
||||
fileExists = os.path.isfile(newfilename)
|
||||
|
||||
if fileExists and os.path.getmtime(newfilename) > os.path.getmtime(filename): #If the object file was created after the file was last modified
|
||||
return [newfilename, False]
|
||||
|
||||
return [newfilename, True]
|
||||
|
||||
def make_output_audio_file(filename):
|
||||
'''Return "AUDIO" + hash of filename to use as object filename'''
|
||||
newfilename = os.path.join(BUILD, 'SND_' + filename.split("gCry")[1].split(".s")[0] + '.o')
|
||||
|
||||
if not os.path.isfile(filename):
|
||||
return [newfilename, False]
|
||||
|
||||
fileExists = os.path.isfile(newfilename)
|
||||
|
||||
if fileExists and os.path.getmtime(newfilename) > os.path.getmtime(filename): #If the object file was created after the file was last modified
|
||||
return [newfilename, False]
|
||||
|
||||
return [newfilename, True]
|
||||
|
||||
def process_assembly(in_file):
|
||||
'''Assemble'''
|
||||
out_file_list = make_output_file(in_file)
|
||||
out_file = out_file_list[0]
|
||||
if out_file_list[1] is False:
|
||||
return out_file #No point in recompiling file
|
||||
|
||||
try:
|
||||
print ('Assembling %s' % in_file)
|
||||
cmd = [AS] + ASFLAGS + ['-c', in_file, '-o', out_file]
|
||||
run_command(cmd)
|
||||
|
||||
except FileNotFoundError:
|
||||
print('Error! The assembler could not be located.\nAre you sure you set up your path to devkitPro/devkitARM/bin correctly?')
|
||||
sys.exit()
|
||||
|
||||
return out_file
|
||||
|
||||
def process_c(in_file):
|
||||
'''Compile C'''
|
||||
out_file_list = make_output_file(in_file)
|
||||
out_file = out_file_list[0]
|
||||
if out_file_list[1] is False:
|
||||
return out_file #No point in recompiling file
|
||||
|
||||
try:
|
||||
print ('Compiling %s' % in_file)
|
||||
cmd = [CC] + CFLAGS + ['-c', in_file, '-o', out_file]
|
||||
run_command(cmd)
|
||||
return flags
|
||||
|
||||
except FileNotFoundError:
|
||||
print('Error! The C compiler could not be located.\nAre you sure you set up your path to devkitPro/devkitARM/bin correctly?')
|
||||
sys.exit()
|
||||
|
||||
return out_file
|
||||
|
||||
def process_string(filename):
|
||||
'''Build Strings'''
|
||||
out_file = filename.split(".string")[0] + '.s'
|
||||
object_file = make_output_file(out_file)[0]
|
||||
def ProcessSpriteSet(fileListing: [str], flags: [str], outputFile: str, title: str):
|
||||
assembledFile = os.path.join(ASSEMBLY, 'generated', outputFile)
|
||||
if (not os.path.isfile(assembledFile)
|
||||
or max(os.path.getmtime(file) for file in fileListing) > os.path.getmtime(assembledFile)): # If a front sprite has been modified
|
||||
print("Processing {}.".format(title))
|
||||
combinedFile = open(assembledFile, 'w')
|
||||
combinedFile.write('@THIS IS A GENERATED FILE! DO NOT MODIFY IT!\n')
|
||||
for sprite in fileListing:
|
||||
assembled = sprite.split('.png')[0] + '.s'
|
||||
|
||||
fileExists = os.path.isfile(object_file)
|
||||
if (not os.path.isfile(assembled)
|
||||
or os.path.getmtime(sprite) > os.path.getmtime(assembled)):
|
||||
RunCommand([GR, sprite] + flags + ['-o', assembled])
|
||||
|
||||
if fileExists and os.path.getmtime(object_file) > os.path.getmtime(filename): #If the .o file was created after the image was last modified
|
||||
return make_output_file(out_file)[0]
|
||||
with open(assembled, 'r') as tempFile:
|
||||
combinedFile.write(tempFile.read())
|
||||
combinedFile.close()
|
||||
|
||||
print ('Building Strings %s' % filename)
|
||||
StringFileConverter(filename)
|
||||
|
||||
out_file_list = make_output_file(out_file)
|
||||
new_out_file = out_file_list[0]
|
||||
if out_file_list[1] == False:
|
||||
os.remove(out_file)
|
||||
return new_out_file #No point in recompiling file
|
||||
|
||||
cmd = [AS] + ASFLAGS + ['-c', out_file, '-o', new_out_file]
|
||||
run_command(cmd)
|
||||
os.remove(out_file)
|
||||
return new_out_file
|
||||
|
||||
def ProcessSpriteGraphics():
|
||||
with open(GRAPHICS + "/backspriteflags.grit", "r") as file:
|
||||
for line in file:
|
||||
backflags = line.split()
|
||||
break
|
||||
|
||||
with open(GRAPHICS + "/frontspriteflags.grit", "r") as file:
|
||||
for line in file:
|
||||
frontflags = line.split()
|
||||
break
|
||||
|
||||
with open(GRAPHICS + "/iconspriteflags.grit", "r") as file:
|
||||
for line in file:
|
||||
iconflags = line.split()
|
||||
break
|
||||
frontFlags = GetFlagsFromFlagFile(GRAPHICS + "/frontspriteflags.grit")
|
||||
backFlags = GetFlagsFromFlagFile(GRAPHICS + "/backspriteflags.grit")
|
||||
iconFlags = GetFlagsFromFlagFile(GRAPHICS + "/iconspriteflags.grit")
|
||||
|
||||
try:
|
||||
os.makedirs(SRC + "/generated")
|
||||
os.makedirs(ASSEMBLY + "/generated")
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
@@ -191,170 +326,132 @@ def ProcessSpriteGraphics():
|
||||
frontsprites = [file for file in glob(GRAPHICS + "/frontspr" + "**/*.png", recursive=True)]
|
||||
iconsprites = [file for file in glob(GRAPHICS + "/pokeicon" + "**/*.png", recursive=True)]
|
||||
|
||||
assembledFrontSprites = os.path.join('SRC', 'generated', 'frontsprites.s')
|
||||
if (not os.path.isfile(assembledFrontSprites)
|
||||
or max(os.path.getmtime(file) for file in frontsprites) > os.path.getmtime(assembledFrontSprites)): #If a front sprite has been modified
|
||||
print("Processing Front Sprites")
|
||||
combinedFile = open(assembledFrontSprites, 'w')
|
||||
combinedFile.write('@THIS IS A GENERATED FILE! DO NOT MODIFY IT!\n')
|
||||
for sprite in frontsprites:
|
||||
assembled = sprite.split('.png')[0] + '.s'
|
||||
ProcessSpriteSet(frontsprites, frontFlags, 'frontsprites.s', "Front Sprites")
|
||||
ProcessSpriteSet(backsprites, backFlags, 'backsprites.s', "Back Sprites")
|
||||
ProcessSpriteSet(iconsprites, iconFlags, 'iconsprites.s', "Icon Sprites")
|
||||
|
||||
if (not os.path.isfile(assembled)
|
||||
or os.path.getmtime(sprite) > os.path.getmtime(assembled)):
|
||||
run_command([GR, sprite] + frontflags + ['-o', assembled])
|
||||
|
||||
with open(assembled, 'r') as tempFile:
|
||||
combinedFile.write(tempFile.read())
|
||||
combinedFile.close()
|
||||
def ProcessAudio(audioFile: str) -> str:
|
||||
"""Compile audio."""
|
||||
assemblyFile = audioFile.split('.wav')[0] + '.s'
|
||||
|
||||
assembledBackSprites = os.path.join('SRC', 'generated', 'backsprites.s')
|
||||
if (not os.path.isfile(assembledBackSprites)
|
||||
or max(os.path.getmtime(file) for file in backsprites) > os.path.getmtime(assembledBackSprites)): #If a back sprite has been modified
|
||||
print("Processing Back Sprites")
|
||||
combinedFile = open(assembledBackSprites, 'w')
|
||||
combinedFile.write('@THIS IS A GENERATED FILE! DO NOT MODIFY IT!\n')
|
||||
for sprite in backsprites:
|
||||
assembled = sprite.split('.png')[0] + '.s'
|
||||
flags = []
|
||||
flagFile = audioFile.split('.wav')[0] + '_flags.txt'
|
||||
|
||||
if (not os.path.isfile(assembled)
|
||||
or os.path.getmtime(sprite) > os.path.getmtime(assembled)):
|
||||
run_command([GR, sprite] + backflags + ['-o', assembled])
|
||||
try:
|
||||
with open(flagFile, 'r') as file:
|
||||
line = file.readline() # Only needs the first line
|
||||
flags = line.strip().split()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
with open(assembled, 'r') as tempFile:
|
||||
combinedFile.write(tempFile.read())
|
||||
combinedFile.close()
|
||||
cmd = [WAV2AGB, audioFile, assemblyFile] + flags
|
||||
|
||||
assembledIconSprites = os.path.join('SRC', 'generated', 'iconsprites.s')
|
||||
if (not os.path.isfile(assembledIconSprites)
|
||||
or max(os.path.getmtime(file) for file in iconsprites) > os.path.getmtime(assembledIconSprites)): #If a icon sprite has been modified
|
||||
print("Processing Icon Sprites")
|
||||
combinedFile = open(assembledIconSprites, 'w')
|
||||
combinedFile.write('@THIS IS A GENERATED FILE! DO NOT MODIFY IT!\n')
|
||||
for sprite in iconsprites:
|
||||
assembled = sprite.split('.png')[0] + '.s'
|
||||
return DoMiddleManAssembly(audioFile, assemblyFile, flagFile, flags, cmd,
|
||||
MakeOutputAudioFile, Master.printCompilingAudio, False)
|
||||
|
||||
if (not os.path.isfile(assembled)
|
||||
or os.path.getmtime(sprite) > os.path.getmtime(assembled)):
|
||||
run_command([GR, sprite] + iconflags + ['-o', assembled])
|
||||
|
||||
with open(assembled, 'r') as tempFile:
|
||||
combinedFile.write(tempFile.read())
|
||||
combinedFile.close()
|
||||
def ProcessMusic(midiFile: str) -> str:
|
||||
"""Compile audio."""
|
||||
assemblyFile = midiFile.split('.mid')[0] + '.s'
|
||||
|
||||
def process_audio(in_file):
|
||||
'''Compile Audio'''
|
||||
out_file = in_file.split('.wav')[0] + '.s'
|
||||
flags = []
|
||||
flagFile = midiFile.split('.mid')[0] + '_flags.txt'
|
||||
|
||||
cmd = [WAV2AGB, in_file] + [out_file, '-c']
|
||||
|
||||
out_file_list = make_output_audio_file(out_file)
|
||||
new_out_file = out_file_list[0]
|
||||
try:
|
||||
if os.path.getmtime(new_out_file) > os.path.getmtime(in_file): #If the .o file was created after the image was last modified
|
||||
return new_out_file
|
||||
else:
|
||||
run_command(cmd)
|
||||
|
||||
except FileNotFoundError:
|
||||
run_command(cmd) #No .o file has been created
|
||||
try:
|
||||
with open(flagFile, 'r') as file:
|
||||
line = file.readline() # Only needs the first line
|
||||
flags = line.strip().split()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
global PrintedCompilingAudio
|
||||
if (PrintedCompilingAudio is False):
|
||||
print ('Compiling Cries')
|
||||
PrintedCompilingAudio = True
|
||||
|
||||
out_file_list = make_output_audio_file(out_file)
|
||||
new_out_file = out_file_list[0]
|
||||
if out_file_list[1] == False:
|
||||
os.remove(out_file)
|
||||
return new_out_file #No point in recompiling file
|
||||
cmd = [MID2AGB, midiFile, assemblyFile] + flags
|
||||
|
||||
cmd = [AS] + ASFLAGS + ['-c', out_file, '-o', new_out_file]
|
||||
run_command(cmd)
|
||||
os.remove(out_file)
|
||||
return new_out_file
|
||||
return DoMiddleManAssembly(midiFile, assemblyFile, flagFile, flags, cmd,
|
||||
MakeOutputMusicFile, Master.printCompilingMusic, True)
|
||||
|
||||
def link(objects):
|
||||
'''Link objects into one binary'''
|
||||
linked = 'build/linked.o'
|
||||
cmd = [LD] + LDFLAGS + ['-o', linked] + list(objects)
|
||||
run_command(cmd)
|
||||
return linked
|
||||
|
||||
def objcopy(binary):
|
||||
cmd = [OBJCOPY, '-O', 'binary', binary, 'build/output.bin']
|
||||
run_command(cmd)
|
||||
|
||||
def run_glob(globstr, fn):
|
||||
'''Glob recursively and run the processor function on each file in result'''
|
||||
if globstr == '**/*.png' or globstr == '**/*.bmp': #Search the graphics location
|
||||
return run_glob_graphics(globstr, fn)
|
||||
elif globstr == '**/*.wav':
|
||||
return run_glob_audio(globstr, fn)
|
||||
|
||||
if sys.version_info > (3, 4):
|
||||
files = glob(os.path.join(SRC, globstr), recursive = True)
|
||||
return map(fn, files)
|
||||
else:
|
||||
files = Path(SRC).glob(globstr)
|
||||
return map(fn, map(str, files))
|
||||
|
||||
def run_glob_graphics(globstr, fn):
|
||||
'''Glob recursively and run the processor function on each file in result'''
|
||||
if sys.version_info > (3, 4):
|
||||
files = glob(os.path.join(GRAPHICS, globstr), recursive = True)
|
||||
return map(fn, files)
|
||||
else:
|
||||
files = Path(GRAPHICS).glob(globstr)
|
||||
return map(fn, map(str, files))
|
||||
def LinkObjects(objects: itertools.chain) -> str:
|
||||
"""Link objects into one binary."""
|
||||
linked = 'build/linked.o'
|
||||
cmd = [LD] + LDFLAGS + ['-o', linked] + list(objects)
|
||||
RunCommand(cmd)
|
||||
return linked
|
||||
|
||||
|
||||
def Objcopy(binary: str):
|
||||
"""Run the objcopy."""
|
||||
cmd = [OBJCOPY, '-O', 'binary', binary, 'build/output.bin']
|
||||
RunCommand(cmd)
|
||||
|
||||
|
||||
def RunGlob(globString: str, fn) -> map:
|
||||
"""Glob recursively and run the processor function on each file in result."""
|
||||
if globString == '**/*.png' or globString == '**/*.bmp': # Search the GRAPHICS location
|
||||
directory = GRAPHICS
|
||||
elif globString == '**/*.s':
|
||||
directory = ASSEMBLY
|
||||
elif globString == '**/*.string':
|
||||
directory = STRINGS
|
||||
elif globString == '**/*.wav' or globString == '**/*.mid':
|
||||
directory = AUDIO
|
||||
else:
|
||||
directory = SRC
|
||||
|
||||
if sys.version_info > (3, 4):
|
||||
try:
|
||||
files = glob(os.path.join(directory, globString), recursive=True)
|
||||
return map(fn, files)
|
||||
|
||||
except TypeError:
|
||||
print('Error compiling. Please make sure Python has been updated to the latest version.')
|
||||
sys.exit(1)
|
||||
else:
|
||||
files = Path(directory).glob(globString)
|
||||
return map(fn, map(str, files))
|
||||
|
||||
def run_glob_audio(globstr, fn):
|
||||
'''Glob recursively and run the processor function on each file in result'''
|
||||
if sys.version_info > (3, 4):
|
||||
files = glob(os.path.join(AUDIO, globstr), recursive = True)
|
||||
return map(fn, files)
|
||||
else:
|
||||
files = Path(AUDIO).glob(globstr)
|
||||
return map(fn, map(str, files))
|
||||
|
||||
def main():
|
||||
starttime = datetime.now()
|
||||
globs = {
|
||||
'**/*.s': process_assembly,
|
||||
'**/*.c': process_c,
|
||||
'**/*.string': process_string,
|
||||
#'**/*.png': process_image,
|
||||
#'**/*.bmp': process_image,
|
||||
'**/*.wav': process_audio,
|
||||
}
|
||||
|
||||
# Create output directory
|
||||
try:
|
||||
os.makedirs(BUILD)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
ProcessSpriteGraphics()
|
||||
Master.init()
|
||||
startTime = datetime.now()
|
||||
globs = {
|
||||
'**/*.s': ProcessAssembly,
|
||||
'**/*.c': ProcessC,
|
||||
'**/*.string': ProcessString,
|
||||
# '**/*.png': ProcessImage,
|
||||
# '**/*.bmp': ProcessImage,
|
||||
'**/*.wav': ProcessAudio,
|
||||
'**/*.mid': ProcessMusic,
|
||||
}
|
||||
|
||||
# Create output directory
|
||||
try:
|
||||
os.makedirs(BUILD)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
ProcessSpriteGraphics()
|
||||
|
||||
# Gather source files and process them
|
||||
objects = itertools.starmap(RunGlob, globs.items())
|
||||
|
||||
# Link and extract raw binary
|
||||
linked = LinkObjects(itertools.chain.from_iterable(objects))
|
||||
Objcopy(linked)
|
||||
|
||||
# Build special_inserts.asm
|
||||
if os.path.isfile('special_inserts.asm'):
|
||||
if not os.path.isfile('build/special_inserts.bin') \
|
||||
or os.path.getmtime('build/special_inserts.bin') < os.path.getmtime('special_inserts.asm'):
|
||||
print('Assembling special_inserts.asm')
|
||||
cmd = [AS] + ASFLAGS + ['-c', 'special_inserts.asm', '-o', 'build/special_inserts.o']
|
||||
RunCommand(cmd)
|
||||
|
||||
cmd = [OBJCOPY, '-O', 'binary', 'build/special_inserts.o', 'build/special_inserts.bin']
|
||||
RunCommand(cmd)
|
||||
|
||||
print('Built in ' + str(datetime.now() - startTime) + '.')
|
||||
|
||||
|
||||
# Gather source files and process them
|
||||
objects = itertools.starmap(run_glob, globs.items())
|
||||
|
||||
# Link and extract raw binary
|
||||
linked = link(itertools.chain.from_iterable(objects))
|
||||
objcopy(linked)
|
||||
|
||||
#Build special_inserts.asm
|
||||
if not os.path.isfile('build/special_inserts.bin') or os.path.getmtime('build/special_inserts.bin') < os.path.getmtime('special_inserts.asm'): #If the binary file was created after the file was last modified):
|
||||
cmd = cmd = [AS] + ASFLAGS + ['-c', 'special_inserts.asm', '-o', 'build/special_inserts.o']
|
||||
run_command(cmd)
|
||||
|
||||
cmd = [OBJCOPY, '-O', 'binary', 'build/special_inserts.o', 'build/special_inserts.bin']
|
||||
run_command(cmd)
|
||||
|
||||
print ('Assembling special_inserts.asm')
|
||||
|
||||
print('Built in ' + str(datetime.now() - starttime) + '.')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -12,7 +12,7 @@ GRAPHICS = './graphics'
|
||||
ASSEMBLY = './assembly'
|
||||
STRINGS = './strings'
|
||||
BUILD = './build'
|
||||
GENERATED = SRC + '/generated'
|
||||
GENERATED = ASSEMBLY + '/generated'
|
||||
|
||||
def PutFileNameInRightFormat(filename):
|
||||
filename = filename.split('/')
|
||||
|
||||
@@ -2,381 +2,541 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import binascii
|
||||
import textwrap
|
||||
import sys
|
||||
from datetime import datetime
|
||||
import _io
|
||||
|
||||
OFFSET_TO_PUT = 0x1800000
|
||||
SOURCE_ROM = "BPRE0.gba"
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
167
scripts/make.py
167
scripts/make.py
@@ -1,8 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import shutil
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
|
||||
############
|
||||
#Options go here.
|
||||
@@ -10,85 +9,111 @@ import os
|
||||
|
||||
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
|
||||
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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user