· 8 years ago · Aug 04, 2018, 12:16 PM
1#!/usr/bin/env python3
2
3#To Edit
4BWRepel = True
5#next feature
6WildPokeBattleMusic = False
7TrainerMusicVar = 0x0 #a var of your choice, if it's 0 music plays normally, other value will make that music play
8#next feature
9EVTrainers = False
10#next feature
11MoreMoney = False
12#next feature
13NewEvolutionMethods = True
14EvosPerPoke = 5 #Number of evolutions per pokemon in your rom hack
15EeveeTable = True #will create an additional table for eevee evolution, so that you dont have to expand evolutions to 8 slots
16#next feature
17BallsExpansion = True
18FirstNewBallID = 226 #item ID of your first new ball, you need 15 free consecutive item slots
19FishingFlag = 0x20 #a flag of your choice, works as a way to make game know that you're fighting against a fished pokemon, only for lure ball
20#next feature
21NatureStatColor = True
22#next feature
23MoreLevels = True
24MaxLevel = 200 #Maximal level available
25#next feature
26NewSpecials = False #new special commands
27Tutors_Number = 32 #number of move tutors in your game
28TmsHms_Number = 58 #number of tms + hms in your game
29TutorMovesTable = 0x08616040 #pointer to move tutor table
30TmsMovesTable = 0x0832937C #pointer to tms moves table
31ExpandedLearnsetTable = True #set to true if you're using battle engine's upgrade
32LearnsetsTable = 0x08F4A6A8 #pointer to learnsets table in your game
33#next feature
34MenuOptionsControl = False #enables a menu option only if a flag is set, if flag is 0, the option will be always shown
35FlagPokedex = 0x861 #default one game uses
36FlagPokemon = 0x860 #default one game uses
37FlagBag = 0x0
38FlagPokenav = 0x862 #default one game uses
39FlagSave = 0x0
40FlagExit = 0x0
41FlagTrainerCard = 0x0
42FlagOptions = 0x0
43#next feature
44FireRedFishing = True #no biting, you either encounter on the first try or not
45ChainFishing = True #XY feature, the more tries you fish on the first try, the more likely you'll encounter a shiny
46FishingFlag = 0x20 #a flag of your choice, works as a way to make game know that you're fighting against a fished pokemon, make sure it's the same as the one you set in the balls expansion(if you expand balls)
47#next feature
48ShinyCharm = 0x0 #set to item ID if you want to have that item in your hack
49#next feature
50HallofFameFix = True #set to true if you want to be able to have expanded pokemon properly displayed in the hall of fame
51#next feature
52FlagsVarsExpansion = False #set to true if you want to expand flags
53FlagsVarsOffset = 0x0203CF64 #set to the location of your expanded saveblock
54NoOfNewFlags = 0x0 #number of your new flags
55NoOfNewVars = 0x0 #number of your new vars
56ReuseOldDexFlags = True #set to true only after dex expansion, allows you to have additional 1664 flags that were used as the original seen/caught flags
57#next feature
58NewEscapeChars = True #currently only allows you to set a specific colour in text
59#next feature
60DNSystem = True #is being worked on, use at your own risk!
61
62insert_offset = 0xFF0000 #Offset as to where insert data
63rom_name = "BPEE0.gba" #Name of your rom
64copy_name = "test.gba" #Name of the created rom
65#To Edit ends
66
67from glob import glob
68import os
69import itertools
70import hashlib
71import subprocess
72import sys
73import time
74import shutil
75import binascii
76import textwrap
77
78PathVar = os.environ.get('Path')
79Paths = PathVar.split(';')
80PATH = ""
81for candidatePath in Paths:
82 if "devkitARM" in candidatePath:
83 PATH = candidatePath
84 break
85if PATH == "":
86 print('DevKit does not exist in your Path variable.\nChecking default location.')
87 PATH = 'C://devkitPro//devkitARM//bin'
88 if os.path.isdir(PATH) == False:
89 print("...\nDevkit not found.")
90 sys.exit(1)
91 else:
92 print("Devkit found.")
93
94PREFIX = '/arm-none-eabi-'
95PREFIX2 = 'arm-none-eabi-'
96NM = os.path.join(PATH, PREFIX2 + 'nm')
97AS = (PATH + PREFIX + 'as')
98CC = (PATH + PREFIX + 'gcc')
99CXX = os.path.join(PATH, PREFIX + 'g++')
100LD = (PATH + PREFIX + 'ld')
101OBJCOPY = (PATH + PREFIX + 'objcopy')
102OBJDUMP = os.path.join(PATH, PREFIX2 + 'objdump')
103SRC = './src'
104BUILD = './build'
105ASFLAGS = ['-mthumb', '-I', SRC]
106LDFLAGS = ['BPEE.ld', '-T', 'linker.ld']
107CFLAGS = ['-mthumb', '-mno-thumb-interwork', '-mcpu=arm7tdmi', '-mtune=arm7tdmi', '-mlong-calls', '-march=armv4t', '-Wall', '-O2']
108
109def run_command(cmd):
110 try:
111 subprocess.check_output(cmd)
112 except subprocess.CalledProcessError as e:
113 print(e.output.decode(), file = sys.stderr)
114 sys.exit(1)
115
116def make_output_file(filename):
117 '''Return hash of filename to use as object filename'''
118 m = hashlib.md5()
119 m.update(filename.encode())
120 return os.path.join(BUILD, m.hexdigest() + '.o')
121
122def process_assembly(in_file):
123 '''Assemble'''
124 out_file = make_output_file(in_file)
125 print ('Assembling %s' % in_file)
126 cmd = [AS] + ASFLAGS + ['-c', in_file, '-o', out_file]
127 run_command(cmd)
128 return out_file
129
130def process_c(in_file):
131 '''Compile C'''
132 out_file = make_output_file(in_file)
133 print ('Compiling %s' % in_file)
134 cmd = [CC] + CFLAGS + ['-c', in_file, '-o', out_file]
135 run_command(cmd)
136 return out_file
137
138def link(objects):
139 '''Link objects into one binary'''
140 linked = 'build/linked.o'
141 cmd = [LD] + LDFLAGS + ['-o', linked] + list(objects)
142 run_command(cmd)
143 return linked
144
145def objcopy(binary):
146 cmd = [OBJCOPY, '-O', 'binary', binary, 'build/output.bin']
147 run_command(cmd)
148
149def run_glob(globstr, fn):
150 '''Glob recursively and run the processor function on each file in result'''
151 files = glob(os.path.join(SRC, globstr), recursive = True)
152 return map(fn, files)
153
154def bool_to_string(s):
155 return "true" if s == True else "false"
156
157def edit_defines():
158 defines = open(SRC + "/defines.h", 'r+')
159 copy = defines.read()
160 defines.seek(0x0)
161 for line in defines:
162 try:
163 directive, name, val = line.split()
164 except:
165 continue
166 newval = ""
167 if name == "MAX_LEVEL" and MoreLevels == True:
168 newval = str(MaxLevel)
169 elif name == "MoreLevels":
170 newval = bool_to_string(MoreLevels)
171 elif NewEvolutionMethods == True and name == "EVO_PER_POKE":
172 newval = str(EvosPerPoke)
173 elif name == "COLORED_STATS":
174 newval = bool_to_string(NatureStatColor)
175 elif BallsExpansion == True and name == "FIRST_NEW_BALL_ID":
176 newval = str(FirstNewBallID)
177 elif NewSpecials == True and name == "NO_OF_TUTORS":
178 newval = str(Tutors_Number)
179 elif NewSpecials == True and name == "NO_OF_TMS_HMS":
180 newval = str(TmsHms_Number)
181 elif NewSpecials == True and name == "EXPANDED_LEARNSETS":
182 newval = bool_to_string(ExpandedLearnsetTable)
183 elif name == "BW_REPEL":
184 newval = bool_to_string(BWRepel)
185 elif name == "FIRERED_FISHING":
186 newval = bool_to_string(FireRedFishing)
187 elif name == "CHAIN_FISHING":
188 newval = bool_to_string(ChainFishing)
189 elif name == "FISHING_FLAG":
190 newval = hex(FishingFlag)
191 elif name == "SHINY_CHARM":
192 newval = hex(ShinyCharm)
193 elif name == "EEVEE_TABLE":
194 newval = bool_to_string(EeveeTable)
195 elif name == "TRAINER_BATTLE_MUSIC_VAR":
196 newval = hex(TrainerMusicVar)
197 elif name == "CUSTOM_WILD_POKE_MUSIC":
198 newval = hex(WildPokeBattleMusic)
199 elif name == "NEWFLAGS":
200 newval = hex(NoOfNewFlags)
201 elif name == "NEWVARS":
202 newval == hex(NoOfNewVars)
203 elif name == "USEOLD_DEX_FLAGS":
204 newval == bool_to_string(ReuseOldDexFlags)
205 elif MenuOptionsControl == True:
206 if name == "DEX_MENU_FLAG":
207 newval = hex(FlagPokedex)
208 elif name == "POKE_MENU_FLAG":
209 newval = hex(FlagPokemon)
210 elif name == "POKENAV_MENU_FLAG":
211 newval = hex(FlagPokenav)
212 elif name == "BAG_MENU_FLAG":
213 newval = hex(FlagBag)
214 elif name == "SAVE_MENU_FLAG":
215 newval = hex(FlagSave)
216 elif name == "EXIT_MENU_FLAG":
217 newval = hex(FlagExit)
218 elif name == "TRAINERCARD_MENU_FLAG":
219 newval = hex(FlagTrainerCard)
220 elif name == "OPTIONS_MENU_FLAG":
221 newval = hex(FlagOptions)
222 if newval != "":
223 copy = copy.replace(line, directive + ' ' + name + '\t\t' + newval + '\n')
224 defines.seek(0x0)
225 defines.write(copy)
226 defines.close()
227
228def edit_linker():
229 linker = open("linker.ld", 'r+')
230 copy = linker.read()
231 linker.seek(0x0)
232 line_no = 1
233 for line in linker:
234 if line_no == 4:
235 copy = copy.replace(line, "\t\trom : ORIGIN = (0x08000000 + " + hex(insert_offset) + "), LENGTH = 32M\n")
236 break
237 line_no += 1
238 linker.seek(0x0)
239 linker.write(copy)
240 linker.close()
241
242def edit_bpee():
243 bpee = open("BPEE.ld", 'r+')
244 copy = bpee.read()
245 bpee.seek(0x0)
246 for line in bpee:
247 try:
248 label, equal_sign, address = line.split()
249 except:
250 continue
251 new_address = ""
252 if label == "new_saveblock":
253 new_address = hex(FlagsVarsOffset)
254 if new_address != "":
255 copy = copy.replace(line, label + ' ' + equal_sign + ' ' + new_address + ';\n')
256 bpee.seek(0x0)
257 bpee.write(copy)
258 bpee.close()
259
260def build_script():
261 #erase build folder if it exists
262 if os.path.isdir(BUILD):
263 shutil.rmtree(BUILD)
264 #get which files to compiler and assemble
265 globs = {}
266 if MoreMoney == True:
267 globs['MoreMoney.c'] = process_c
268 if EVTrainers == True:
269 globs['EVTrainers.c'] = process_c
270 if WildPokeBattleMusic == True or TrainerMusicVar != 0x0:
271 globs['BattleMusic.c'] = process_c
272 if BWRepel == True:
273 globs['Repel_Code.c'] = process_c
274 globs['Repel_Script.s'] = process_assembly
275 if MoreLevels == True or NatureStatColor == True:
276 globs['MoreLevels.c'] = process_c
277 if MoreLevels == True:
278 globs['MoreLevels.s'] = process_assembly
279 if NewEvolutionMethods == True:
280 globs['EvolutionMethods.c'] = process_c
281 if BallsExpansion == True:
282 globs['BallsExpansion.c'] = process_c
283 globs['BallsExpansion.s'] = process_assembly
284 if BallsExpansion == True or MoreLevels == True:
285 globs['OriginsStruct.s'] = process_assembly
286 if NewSpecials == True:
287 globs['NewSpecials.c'] = process_c
288 if MenuOptionsControl == True:
289 globs['MenuControl.c'] = process_c
290 if FireRedFishing == True or ChainFishing == True or FishingFlag != 0 or ShinyCharm != 0:
291 globs['WildPokes.c'] = process_c
292 if ChainFishing == True:
293 globs["ChainFishMoving.s"] = process_assembly
294 if HallofFameFix == True:
295 globs["HallOfFame.c"] = process_c
296 globs["HallOfFame.s"] = process_assembly
297 if FlagsVarsExpansion == True:
298 globs["FlagsVarsExpansion.c"] = process_c
299 if NewEscapeChars == True:
300 globs["SpecialChars.s"] = process_assembly
301 if DNSystem == True:
302 globs["DNS.c"] = process_c
303 globs["DNS.s"] = process_assembly
304 #check if at least one file is being built
305 if not globs:
306 print("No feature chosen.")
307 sys.exit(1)
308 #edit defines in defines.h
309 edit_defines()
310 #change offset in linker file
311 edit_linker()
312 #edit bpee linker
313 edit_bpee()
314 # Create output directory
315 try:
316 os.makedirs(BUILD)
317 except FileExistsError:
318 pass
319
320 # Gather source files and process them
321 objects = itertools.starmap(run_glob, globs.items())
322
323 # Link and extract raw binary
324 linked = link(itertools.chain.from_iterable(objects))
325 objcopy(linked)
326
327def get_text_section():
328 # Dump sections
329 out = subprocess.check_output([OBJDUMP, '-t', 'build/linked.o'])
330 lines = out.decode().split('\n')
331
332 # Find text section
333 text = filter(lambda x: x.strip().endswith('.text'), lines)
334 section = (list(text))[0]
335
336 # Get the offset
337 offset = int(section.split(' ')[0], 16)
338
339 return offset
340
341def symbols(subtract=0):
342 out = subprocess.check_output([NM, 'build/linked.o'])
343 lines = out.decode().split('\n')
344
345 name = ''
346
347 ret = {}
348 for line in lines:
349 parts = line.strip().split()
350
351 if (len(parts) < 3):
352 continue
353
354 if (parts[1].lower() not in {'t','d'}):
355 continue
356
357 offset = int(parts[0], 16)
358 ret[parts[2]] = offset - subtract
359
360 return ret
361
362def hook(rom, space, hook_at, register=0):
363 # Align 2
364 if hook_at & 1:
365 hook_at -= 1
366
367 rom.seek(hook_at)
368
369 register &= 7
370
371 if hook_at % 4:
372 data = bytes([0x01, 0x48 | register, 0x00 | (register << 3), 0x47, 0x0, 0x0])
373 else:
374 data = bytes([0x00, 0x48 | register, 0x00 | (register << 3), 0x47])
375
376 space += 0x08000001
377 data += (space.to_bytes(4, 'little'))
378 rom.write(bytes(data))
379
380def funcwrap(rom, space, hook_at, nparams, isreturning):
381 # Align 2
382 if hook_at & 1:
383 hook_at -= 1
384
385 rom.seek(hook_at)
386 nparams=nparams-1
387
388 if nparams<4:
389 data = bytes([0x10, 0xB5, 0x3, 0x4C, 0x0, 0xF0, 0x3, 0xF8, 0x10, 0xBC , (isreturning+1), 0xBC , (isreturning<<3), 0x47, 0x20, 0x47])
390 else:
391 k=nparams-3
392 data = bytes([0x10, 0xB5, 0x82, 0xB0])
393 for i in range(k+2):
394 data += bytes([ i+2, 0x9C , i, 0x94])
395 data += bytes([0x0, 0x9C , (nparams-1), 0x94, 0x1, 0x9C , nparams, 0x94, 0x2, 0xB0 , (k+8), 0x4C,
396 0x0, 0xF0 , ((k<<1)+13), 0xF8, 0x82, 0xB0 , nparams, 0x9C, 0x1, 0x94 , (nparams-1), 0x9C , 0x0, 0x94])
397 for i in reversed(range(k+2)):
398 data += bytes([ i, 0x9C , i+2, 0x94])
399 data += bytes([0x2, 0xB0 , 0x10, 0xBC, (isreturning+1), 0xBC , (isreturning<<3), 0x47, 0x20, 0x47])
400
401 space += 0x08000001
402 data += (space.to_bytes(4, 'little'))
403 rom.write(bytes(data))
404
405def repoint(rom, space, repoint_at):
406 rom.seek(repoint_at)
407 space += (0x08000000)
408 data = (space.to_bytes(4, 'little'))
409 rom.write(bytes(data))
410
411def repoint_list(rom, space, list, len):
412 for i in range (0, len):
413 repoint(rom, space, list[i])
414
415def routine_repoint(rom, space, repoint_at):
416 repoint(rom, space | 1, repoint_at)
417
418def bytereplace(rom, offset, value, bytecount):
419 rom.seek(offset)
420 rom.write(value.to_bytes(bytecount, byteorder = 'little'))
421
422def insert_ball_items(rom, table):
423 print("Inserting item balls data.")
424 balls = open("./scripts/new_balls.txt", 'r')
425 rom.seek(0x1C8)
426 rom.seek(int.from_bytes(rom.read(4), byteorder = 'little') - 0x08000000 + (FirstNewBallID * 0x2C)) #get offset of first new item
427 counter = 0x0
428 #write item table data
429 for line in balls:
430 name, price, descr = line.split()
431 #write item name
432 name = int(name, 16)
433 rom.write(name.to_bytes(14, 'big'))
434 #write index
435 rom.write((FirstNewBallID + counter).to_bytes(2, 'little'))
436 #write price
437 price = int(price, 10)
438 rom.write(price.to_bytes(2, 'little'))
439 #write battle function and quality
440 rom.write((0).to_bytes(2, 'little'))
441 #write description
442 rom.write((table[descr] + 0x08000000).to_bytes(4, 'little'))
443 #write everything else
444 bytes = 0x00000201000000000200000095E30F0800000000
445 rom.write(bytes.to_bytes(20, 'big'))
446 counter += 1
447 balls.close()
448 #write bag images and palettes
449 ball_names = ["level", "lure", "moon", "friend", "love", "heavy", "fast", "sport", "dusk", "quick", "heal", "cherish", "park", "dream", "beast"]
450 rom.seek(0x1B0034)
451 rom.seek(int.from_bytes(rom.read(4), byteorder = 'little') - 0x08000000 + (FirstNewBallID * 0x8)) #get offset of first new item
452 for i in range(0, len(ball_names)):
453 try:
454 offset_img = table["bag_" + ball_names[i] + "_img"] + 0x08000000
455 except:
456 offset_img = 0x8DAB45C
457 try:
458 offset_pal = table["bag_" + ball_names[i] + "_pal"] + 0x08000000
459 except:
460 offset_pal = 0x8DAB50C
461 rom.write(offset_img.to_bytes(4, 'little'))
462 rom.write(offset_pal.to_bytes(4, 'little'))
463
464def insert_script(rom):
465 print("Inserting code.")
466 table = symbols(get_text_section())
467 rom.seek(insert_offset)
468 with open('build/output.bin', 'rb') as binary:
469 data = binary.read()
470 binary.close()
471 rom.write(data)
472
473 # Adjust symbol table
474 for entry in table:
475 table[entry] += insert_offset
476
477 # Insert hooks
478 if MoreMoney == True:
479 hook(rom, table["prepare_money_box"], 0x0E51F4, 3)
480 if EVTrainers == True:
481 hook(rom, table["create_trainer_pokemon"], 0x0385E8, 3)
482 if WildPokeBattleMusic == True or TrainerMusicVar != 0x0:
483 hook(rom, table["choose_song_for_battle"], 0x06E42C, 0)
484 if MoreLevels == True:
485 hook(rom, table["display_exp_instatssummary"], 0x1C38C0, 0)
486 hook(rom, table["LEVELUP_MORE_DIGITS"], 0x1D37FA, 2)
487 hook(rom, table["pokemenu_print_currHP"], 0x1B2D3C, 2)
488 hook(rom, table["pokemenu_print_maxHP"], 0x1B2DDC, 2)
489 if MoreLevels == True or NatureStatColor == True:
490 hook(rom, table["display_stats_on_left"], 0x1C3710, 0)
491 hook(rom, table["display_stats_on_right"], 0x1C3808, 0)
492 if NewEvolutionMethods == True:
493 hook(rom, table["try_evolving_poke"], 0x06D098, 3)
494 if BallsExpansion == True:
495 hook(rom, table["item_to_ball"], 0x170D84, 1)
496 hook(rom, table["FT1_use_new_balls"], 0x03E952, 0)
497 hook(rom, table["third_frame_ball_hook"], 0x076AA0, 0)
498 if MenuOptionsControl == True:
499 hook(rom, table["add_suitable_menu_options"], 0x09F4CC, 0)
500 if FireRedFishing == True or ChainFishing == True or FishingFlag != 0 or ShinyCharm != 0x0:
501 hook(rom, table["fishing_start"], 0x08C88C, 1)
502 hook(rom, table["wild_fishing_battle"], 0x0B5734, 1)
503 hook(rom, table["create_wild_poke_new"], 0x0B4E68, 2)
504 if ChainFishing == True:
505 hook(rom, table["chain_fishing_move_check"], 0x09CBE8, 2)
506 if HallofFameFix == True:
507 hook(rom, table["fame_hall_print_poke_data"], 0x174A88, 1)
508 hook(rom, table["fame_hall_registering_poke_display"], 0x173AA8, 1)
509 hook(rom, table["set_fame_hall_pokes_from_party"], 0x17371C, 1)
510 hook(rom, table["prepare_fame_hall_registering1"], 0x173694, 0)
511 hook(rom, table["fame_hall_read_pokes_from_sav"], 0x174324, 1)
512 hook(rom, table["fame_hall_pc_poke_data_display"], 0x1745FC, 1)
513 hook(rom, table["fame_hall_pc_display_poke_sprites"], 0x1743EC, 1)
514 if FlagsVarsExpansion == True:
515 hook(rom, table["get_flag_address_new"], 0x09D6EC, 1)
516 hook(rom, table["get_var_address_new"], 0x09D648, 1)
517 if NewEscapeChars == True:
518 hook(rom, table["FC_switch_hook"], 0x0058E0, 0)
519 if DNSystem == True:
520 hook(rom, table["blockset_load_pal"], 0x088CC4, 3)
521 hook(rom, table["npc_pal_patch"], 0x08E91C, 2)
522 hook(rom, table["oec_alloc_apply_pal"], 0x0B5C94, 2)
523 hook(rom, table["oec_pal_alloc2"], 0x0B5C6C, 2)
524
525 # Insert repoints
526 if BWRepel == True:
527 repoint(rom, table["REPEL_SCRIPT"], 0x0B58C0)
528 if MoreLevels == True:
529 exptable = table["exp_table"]
530 to_repoint = [0x0690B8, 0x069124, 0x067DC0, 0x0592C4, 0x074A10, 0x0593F0, 0x059500, 0x1C3940, 0x1C2338, 0x06C2E4, 0x06DFF0]
531 for i in range(0, len(to_repoint)):
532 repoint(rom, exptable, to_repoint[i])
533 if BallsExpansion == True:
534 to_repoint_tiles = [0x0001D0, 0x076510, 0x0767C0, 0x076AD4, 0x076B0C]
535 repoint_list(rom, table["ball_tiles"], to_repoint_tiles, len(to_repoint_tiles))
536
537 to_repoint_pals = [0x0001D4, 0x076514, 0x0767C4, 0x076ADC, 0x076B10]
538 repoint_list(rom, table["ball_pals"], to_repoint_pals, len(to_repoint_pals))
539
540 to_repoint_templates = [0x075538, 0x076518, 0x0767C8, 0x170ED0, 0x171010, 0x1C4A74]
541 repoint_list(rom, table["ball_templates"], to_repoint_templates, len(to_repoint_templates))
542
543 stars_tiles = [0x171D8C, 0x1729D4]
544 repoint_list(rom, table["ball_stars_tiles"], stars_tiles, len(stars_tiles))
545
546 stars_pals = [0x171D94, 0x1729D8]
547 repoint_list(rom, table["ball_stars_pals"], stars_pals, len(stars_pals))
548
549 stars_IDs = [0x171AA8, 0x171F38, 0x1720B4, 0x1721B8, 0x1722B4, 0x1723C0, 0x1724EC, 0x172644, 0x17277C, 0x1728C8]
550 repoint_list(rom, table["Ball_Stars"], stars_IDs, len(stars_IDs))
551
552 ball_tasks_IDs = [0x171E18, 0x172990]
553 repoint_list(rom, table["ball_anim_tasks"], ball_tasks_IDs, len(ball_tasks_IDs))
554
555 stars_templates = [0x171F30, 0x1720B0, 0x1721B4, 0x1722B8, 0x1723BC, 0x1724F0, 0x172648, 0x172774, 0x1728CC]
556 repoint_list(rom, table["ball_stars_templates"], stars_templates, len(stars_templates))
557
558 ball_fade = [0x172A54, 0x172AA0, 0x172B04, 0x172BE0]
559 repoint_list(rom, table["ball_fadepoke_table"], ball_fade, len(ball_fade))
560 if BallsExpansion == True or MoreLevels == True:
561 repoint(rom, table["getattributes_origingame"], 0x06A79C)
562 repoint(rom, table["setattributes_origingame"], 0x06AEC8)
563 repoint(rom, table["getattributes_lvl"], 0x06A798)
564 repoint(rom, table["setattributes_lvl"], 0x06AEC4)
565 repoint(rom, table["getattributes_ball"], 0x06A7A0)
566 repoint(rom, table["setattributes_ball"], 0x06AECC)
567 #Insert routine pointers
568 if NewSpecials == True:
569 routine_repoint(rom, table["s25_special"], 0x1DB710)
570 routine_repoint(rom, table["s26_special2"], 0x1DB714)
571 if FireRedFishing == True or ChainFishing == True or FishingFlag != 0 or ShinyCharm != 0x0:
572 routine_repoint(rom, table["run_fishing_functions"], 0x08CD90)
573 if DNSystem == True:
574 routine_repoint(rom, table["c2_overworld"], 0x085E20)
575 routine_repoint(rom, table["c2_overworld"], 0x085F54)
576 routine_repoint(rom, table["c2_overworld"], 0x085FC8)
577 routine_repoint(rom, table["c2_overworld"], 0x086020)
578 routine_repoint(rom, table["c2_overworld"], 0x086070)
579 routine_repoint(rom, table["c2_overworld"], 0x0860C4)
580 routine_repoint(rom, table["c2_overworld"], 0x086114)
581 routine_repoint(rom, table["c2_overworld"], 0x08613C)
582 # Insert byte changes
583 if MoreMoney == True:
584 bytereplace(rom, 0x0E5188, 9999999, 4)
585 bytereplace(rom, 0x0C36EE, 7, 1)
586 bytereplace(rom, 0x0E5238, 7, 1)
587 if MoreLevels == True:
588 bytereplace(rom, 0x06D91C, 0, 4) #large stat display
589 bytereplace(rom, 0x06FE68, 3, 1) #Daycare int three-digits
590 #Max Level
591 to_bytereplace = [0x1C3908, 0x1C22E2, 0x05C54A, 0x06DFD8, 0x06FD20, 0x06C17C, 0x1B743A, 0x04A88E, 0x04A5BE, 0x069096, 0x069102, 0x1B889C]
592 for i in range(0, len(to_bytereplace)):
593 if i > 2:
594 bytereplace(rom, to_bytereplace[i], MaxLevel, 1)
595 else:
596 bytereplace(rom, to_bytereplace[i], MaxLevel - 1, 1)
597 #Sizeof changing
598 to_change = [0x069082, 0x0690EE, 0x067CF0, 0x1C3922, 0x059240, 0x07499C, 0x0593A0, 0x0594A8, 0x1C22FC, 0x06C1A8, 0x06DFB2]
599 for i in range(0, len(to_change)):
600 curr_offset = to_change[i]
601 bytereplace(rom, curr_offset, 0x20, 1)
602 bytereplace(rom, curr_offset + 3, 1, 1)
603 if BallsExpansion == True:
604 #Don't call item_to_ball function as argument is already ballID
605 to_bytereplace = [0x0754CE, 0x07567C, 0x075D3A, 0x170C14, 0x1C4A14]
606 for i in range(0, len(to_bytereplace)):
607 bytereplace(rom, to_bytereplace[i], 0, 4)
608 #correct luxury ball ID in happiness boost
609 bytereplace(rom, 0x06DA64, 0xA, 1)
610 #limiter
611 bytereplace(rom, 0x1711A4, 31, 1)
612 bytereplace(rom, 0x171B08, 31, 1)
613 if FireRedFishing == True or ChainFishing == True or FishingFlag != 0 or ShinyCharm != 0x0:
614 #modify the wild poke get nature function, so it returns 30 if wants a random nature
615 bytereplace(rom, 0x0B4E4C, 0xE006201E, 4)
616 #if HallofFameFix == True:
617
618 width = max(map(len, table.keys())) + 1
619 offset_file = open("offsets.ini", 'r+')
620 offset_file.truncate()
621 for key in sorted(table.keys()):
622 fstr = ('{:' + str(width) + '} {:08X}')
623 offset_file.write(fstr.format(key + ':', table[key] + 0x08000000) + '\n')
624 offset_file.close()
625 if BallsExpansion == True:
626 insert_ball_items(rom, table)
627
628def main():
629 build_script()
630 shutil.copyfile(rom_name, copy_name) #copy rom
631 rom = open(copy_name, 'rb+')
632 insert_script(rom)
633 rom.close()
634
635if __name__ == '__main__':
636 main()