def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: string_id = STRING_ID_US overlay_size = OVERLAY13_INITAL_SIZE_US if config.game_region == GAME_REGION_EU: string_id = STRING_ID_EU overlay_size = OVERLAY13_INITAL_SIZE_EU if config.game_region == GAME_REGION_JP: string_id = STRING_ID_JP overlay_size = OVERLAY13_INITAL_SIZE_JP # Change dialogue for lang in config.string_index_data.languages: filename = 'MESSAGE/' + lang.filename bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) strings.strings[string_id - 1] = get_locales().translate( MESSAGE, lang.locale.replace('-', '_')) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) table = loadOverlayTable(rom.arm9OverlayTable, lambda x, y: bytes()) ov = table[13] ov.ramSize = overlay_size + OVERLAY13_ADD_SIZE rom.arm9OverlayTable = saveOverlayTable(table) ov13 = rom.files[ov.fileID] rom.files[ov.fileID] = ov13[:overlay_size] try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: bar_list = BAR_LIST_US if config.game_region == GAME_REGION_EU: bar_list = BAR_LIST_EU if config.game_region == GAME_REGION_JP: bar_list = BAR_LIST_JP data = rom.loadArm9Overlays([19])[19].data header = bytearray([0xFF] * (4 + 2 * NB_ITEMS)) write_u32(header, u32(4 + 2 * NB_ITEMS), 0) list_data: List[bytes] = [] for x in range(bar_list, bar_list + BAR_LIST_SIZE, BAR_LIST_ENTRY_SIZE): item_id = read_u16(data, x) cdata = bytes(data[x + 2:x + BAR_LIST_ENTRY_SIZE]) if cdata in list_data: index = list_data.index(cdata) else: index = len(list_data) list_data.append(cdata) write_u16(header, u16_checked(index), 4 + 2 * item_id) file_data = header + b''.join(list_data) if ITEM_LIST_PATH not in rom.filenames: create_file_in_rom(rom, ITEM_LIST_PATH, file_data) else: rom.setFileByName(ITEM_LIST_PATH, file_data) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data): if not self.is_applied(rom, config): path_len = len(LIST_PATH%0)+1 if path_len%4!=0: path_len += 4-(path_len%4) if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: table = ITEM_LISTS_TABLE_US if config.game_region == GAME_REGION_EU: table = ITEM_LISTS_TABLE_EU ranges = [] for i in range(ITEM_LISTS_NB): start = read_uintle(rom.arm9, table+i*4, 4)-ARM9_START end = start size = 0 while size<ITEM_LISTS_SIZE: val = read_uintle(rom.arm9, end, 2) if val>=30000: size += (val-30000)*2 else: size += 2 end += 2 data = rom.arm9[start:end] if end%4!=0: end += 4-(end%4) path = LIST_PATH%i if path not in rom.filenames: create_file_in_rom(rom, path, data) else: rom.setFileByName(path, data) rom.arm9 = rom.arm9[:start]+bytes([0xCC]*(end-start))+rom.arm9[end:] ranges.append([start, end]) ranges.sort() i = 0 while i<len(ranges)-1: if ranges[i][1]==ranges[i+1][0]: ranges[i][1] = ranges[i+1][1] del ranges[i+1] i-=1 i+=1 buffer = bytearray(4*ITEM_LISTS_NB) for i in range(ITEM_LISTS_NB): path = LIST_PATH%i while ranges[0][1]-ranges[0][0]<path_len: del ranges[0] if len(ranges)==0: raise RuntimeError("Don't have enough space to put filenames! ") rom.arm9 = rom.arm9[:ranges[0][0]]+path.encode(encoding="ascii")+bytes(path_len-len(path))+rom.arm9[ranges[0][0]+path_len:] write_uintle(buffer, ARM9_START+ranges[0][0], i*4, 4) ranges[0][0] += path_len rom.arm9 = rom.arm9[:table]+buffer+rom.arm9[table+len(buffer):] try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: START_ACCURACY = self.get_parameter("StartGraphicPos") START_POWER = START_ACCURACY + 12 MAX_POWER = f"[M:B{START_POWER}]" + (f"[M:B{START_POWER + 10}]") * 9 + f"[M:B{START_POWER + 9}]" MAX_ACCU = f"[M:B{START_POWER}]" + (f"[M:B{START_ACCURACY + 11}]") * 10 + f"[M:B{START_POWER}]" DESC_CHANGES = {8: MAX_POWER, 60: MAX_POWER, 75: MAX_POWER, 100: MAX_POWER, 153: MAX_POWER, 156: MAX_POWER, 205: MAX_POWER, 206: MAX_POWER, 348: MAX_POWER, 477: MAX_POWER, 61: MAX_ACCU, 340: MAX_ACCU, 535: MAX_ACCU} bin_before = rom.getFileByName("FONT/markfont.dat") model = GraphicFontHandler.deserialize(bin_before) entries = [] for x in range(model.get_nb_entries()): entries.append(model.get_entry(x)) while len(entries) < max(START_ACCURACY + 12, START_POWER + 11): entries.append(None) for x in range(START_ACCURACY, START_ACCURACY + 12): img = Image.open(os.path.join(SRC_DIR, "accu_%02d.png" % (x - START_ACCURACY)), 'r') entries[x] = img for x in range(START_POWER, START_POWER + 11): img = Image.open(os.path.join(SRC_DIR, "pow_%02d.png" % (x - START_POWER)), 'r') entries[x] = img model.set_entries(entries) bin_after = GraphicFontHandler.serialize(model) rom.setFileByName("FONT/markfont.dat", bin_after) # Change some move descriptions for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Move Descriptions'] for k, v in DESC_CHANGES.items(): strings.strings[block.begin + k] = strings.strings[block.begin + k].replace(OLD_STAT, v) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: type_table = TYPE_TABLE_US gummi_iq_table = GUMMI_IQ_TABLE_US gummi_belly_table = GUMMI_BELLY_TABLE_US if config.game_region == GAME_REGION_EU: type_table = TYPE_TABLE_EU gummi_iq_table = GUMMI_IQ_TABLE_EU gummi_belly_table = GUMMI_BELLY_TABLE_EU if config.game_region == GAME_REGION_JP: type_table = TYPE_TABLE_JP gummi_iq_table = GUMMI_IQ_TABLE_JP gummi_belly_table = GUMMI_BELLY_TABLE_JP bincfg = config.binaries['overlay/overlay_0010.bin'] data = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) data[type_table:type_table + TABLE_LEN] = bytearray(NEW_TYPES) set_binary_in_rom_ppmdu(rom, bincfg, bytes(data)) # Change Fairy's type name for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Type Names'] strings.strings[block.begin + 18] = TYPE_LIST[filename] bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) bincfg = config.binaries['arm9.bin'] data = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) data[gummi_iq_table:gummi_iq_table + TABLE_LEN] = bytearray(NEW_IQ_GUMMI) data[gummi_belly_table:gummi_belly_table + TABLE_LEN] = bytearray(NEW_BELLY_GUMMI) set_binary_in_rom_ppmdu(rom, bincfg, bytes(data)) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: string_id = STRING_ID_US if config.game_region == GAME_REGION_EU: string_id = STRING_ID_EU if config.game_region == GAME_REGION_JP: string_id = STRING_ID_JP # Change dialogue for lang in config.string_index_data.languages: filename = 'MESSAGE/' + lang.filename bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) strings.strings[string_id - 1] = get_locales().translate(MESSAGE, lang.locale.replace('-', '_')) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data): if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: rank_table = FLOOR_RANKS_TABLE_US item_table = ITEM_AVAILABLE_TABLE_US forbid_table = FLOOR_FORBID_TABLE_US if config.game_region == GAME_REGION_EU: rank_table = FLOOR_RANKS_TABLE_EU item_table = ITEM_AVAILABLE_TABLE_EU forbid_table = FLOOR_FORBID_TABLE_EU header = bytearray(NB_ITEMS_TABLE*4) rank_data = bytearray(0) forbid_data = bytearray(0) current_ptr = len(header) for i in range(NB_ITEMS_TABLE): start = read_uintle(rom.arm9, rank_table+i*4, 4)-ARM9_START end = start+1+FLOORS_NB[i] if end%4!=0: end += 4-(end%4) rdata = rom.arm9[start:end] fdata = bytearray(len(rdata)) x = forbid_table while rom.arm9[x]!=0x64: if rom.arm9[x]==i: fdata[rom.arm9[x+1]] = 1 x += 2 rom.arm9 = rom.arm9[:start]+bytes([0xCC]*(end-start))+rom.arm9[end:] write_uintle(header, current_ptr, i*4, 4) rank_data += bytearray(rdata) forbid_data += bytearray(fdata) current_ptr += end-start file_data = header + rank_data if FLOOR_RANKS_PATH not in rom.filenames: create_file_in_rom(rom, FLOOR_RANKS_PATH, file_data) else: rom.setFileByName(FLOOR_RANKS_PATH, file_data) file_data = header + forbid_data if FLOOR_FORBID_PATH not in rom.filenames: create_file_in_rom(rom, FLOOR_FORBID_PATH, file_data) else: rom.setFileByName(FLOOR_FORBID_PATH, file_data) dungeon_list = HardcodedDungeons.get_dungeon_list(rom.arm9, config) groups = [d.mappa_index for d in dungeon_list] print(hex(len(groups))) list_available = [] for x in range(AVAILABLE_ITEMS_NB): list_available.append(bytearray(0x100//8)) for i, g in enumerate(groups): off = item_table + g * (AVAILABLE_ITEMS_NB//8) + (x//8) if rom.arm9[off]&(1<<(x%8)): list_available[-1][i//8] |= 1<<(i%8) file_data = bytearray().join(list_available) if AVAILABLE_ITEMS_PATH not in rom.filenames: create_file_in_rom(rom, AVAILABLE_ITEMS_PATH, file_data) else: rom.setFileByName(AVAILABLE_ITEMS_PATH, file_data) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: start_ov29 = START_OV29_US start_table = START_TABLE_US start_m_functions = START_M_FUNC_US end_m_functions = END_M_FUNC_US data_seg = DATA_SEG_US if config.game_region == GAME_REGION_EU: start_ov29 = START_OV29_EU start_table = START_TABLE_EU start_m_functions = START_M_FUNC_EU end_m_functions = END_M_FUNC_EU data_seg = DATA_SEG_EU main_func = dict() data = rom.loadArm9Overlays([29])[29].data switch = AsmFunction( data[start_table - start_ov29:start_m_functions - start_ov29], start_table) ext_data = switch.process()[1] lst_data = {} data_processed = set() for offset in ext_data: code = 0 for x in range(4): code += data[offset - start_ov29 + x] * (256**x) lst_data[offset] = code data_processed.add(offset) switch.provide_data(lst_data) main_calls = switch.process_switch(0, (0, 1400), {}) unique_main_calls = set(main_calls) unique_main_calls.add(data_seg) unique_main_calls = list(sorted(unique_main_calls)) unique_main_calls.append(end_m_functions) for i in range(len(unique_main_calls) - 1): if unique_main_calls[i] != data_seg: start = unique_main_calls[i] end = unique_main_calls[i + 1] func_data = data[start - start_ov29:end - start_ov29] main_func[start] = AsmFunction(func_data, start) ext_data = main_func[start].process()[1] if i >= len(unique_main_calls) - 3: new_baddr = (end_m_functions - len(func_data) - start_m_functions - 0x8) // 0x4 if new_baddr < 0: new_baddr += 2 * 0x800000 main_func[start].add_instructions( bytes([ new_baddr % 256, (new_baddr // 256) % 256, (new_baddr // 65536) % 256, 0xEA ])) lst_data = {} for offset in ext_data: code = 0 for x in range(4): code += data[offset - start_ov29 + x] * (256**x) lst_data[offset] = code data_processed.add(offset) main_func[start].provide_data(lst_data) nb_items = len(main_calls) header = bytearray(4 + 2 * nb_items + len(main_func) * 8) write_u32(header, u32_checked(4 + 2 * nb_items), 0) code_data = bytearray(0) current_ptr = len(header) id_codes = dict() for i, t in enumerate(main_func.items()): k = t[0] x = t[1] id_codes[k] = i fdata = bytearray(x.compile(start_m_functions)) write_u32(header, u32_checked(current_ptr), 4 + 2 * nb_items + i * 8) write_u32(header, u32_checked(len(fdata)), 4 + 2 * nb_items + i * 8 + 4) code_data += fdata current_ptr += len(fdata) for i, x in enumerate(main_calls): write_u16(header, u16(id_codes[x]), 4 + 2 * i) file_data = header + code_data if ITEM_CODE_PATH not in rom.filenames: create_file_in_rom(rom, ITEM_CODE_PATH, file_data) else: rom.setFileByName(ITEM_CODE_PATH, file_data) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: param = self.get_parameters() if self.is_applied_ms(rom, config): param["MoveShortcuts"] = 1 else: param["MoveShortcuts"] = 0 if self.is_applied_dv(rom, config): param["DisplayVal"] = 1 else: param["DisplayVal"] = 0 START_EXT = param["StartGraphicPos"] START_LVL = START_EXT + 11 START_SUB = START_LVL + 9 bin_before = rom.getFileByName("FONT/markfont.dat") model = GraphicFontHandler.deserialize(bin_before) entries = [] for x in range(model.get_nb_entries()): entries.append(model.get_entry(x)) while len(entries) < max(START_LVL + 9, START_SUB + 4, START_EXT + 11): entries.append(None) for x in range(START_EXT, START_EXT + 11): img = Image.open( os.path.join(SRC_DIR, "ext%d.png" % (x - START_EXT)), 'r') entries[x] = img for x in range(START_LVL, START_LVL + 9): img = Image.open( os.path.join(SRC_DIR, "lvl%d.png" % (x - START_LVL)), 'r') entries[x] = img for x in range(START_SUB, START_SUB + 4): img = Image.open( os.path.join(SRC_DIR, "sub%d.png" % (x - START_SUB)), 'r') entries[x] = img model.set_entries(entries) bin_after = GraphicFontHandler.serialize(model) rom.setFileByName("FONT/markfont.dat", bin_after) if MGROW_PATH not in rom.filenames: mgrow_data_stat = bytearray(0x96) mgrow_data_dama = bytearray(0x96) for x in range(25): exp_req = MGROW_TABLE[x * 4] pwr = MGROW_TABLE[x * 4 + 1] pp = MGROW_TABLE[x * 4 + 2] acc = MGROW_TABLE[x * 4 + 3] write_u16(mgrow_data_dama, u16(exp_req), x * 6) write_u16(mgrow_data_dama, u16(pwr), x * 6 + 2) write_u8(mgrow_data_dama, u8(pp), x * 6 + 4) write_u8(mgrow_data_dama, u8(acc), x * 6 + 5) bin_before = rom.getFileByName("BALANCE/waza_p.bin") model = WazaPHandler.deserialize(bin_before) total = bytearray(0) for m in model.moves: if m.category != WazaMoveCategory.STATUS and m.max_upgrade_level == 99: total += mgrow_data_dama else: total += mgrow_data_stat create_file_in_rom(rom, MGROW_PATH, bytes(total)) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: item_effect = ITEM_EFFECT_US if config.game_region == GAME_REGION_EU: item_effect = ITEM_EFFECT_EU if config.game_region == GAME_REGION_JP: item_effect = ITEM_EFFECT_JP # Apply Gummi item properties item_p_bin = rom.getFileByName('BALANCE/item_p.bin') item_p_model = ItemPHandler.deserialize(item_p_bin) gummi = item_p_model.item_list[GUMMI_ITEM_ID] gummi.buy_price = 800 gummi.sell_price = 50 gummi.category = 3 gummi.sprite = 17 gummi.item_id = GUMMI_ITEM_ID gummi.move_id = 0 gummi.range_min = 0 gummi.range_max = 0 gummi.palette = 14 gummi.action_name = 4 gummi.is_valid = True gummi.is_in_td = False gummi.ai_flag_1 = False gummi.ai_flag_2 = True gummi.ai_flag_3 = False rom.setFileByName('BALANCE/item_p.bin', ItemPHandler.serialize(item_p_model)) # Apply Gummi item dungeon effect item_cd_bin = rom.getFileByName('BALANCE/item_cd.bin') item_cd_model = DataCDHandler.deserialize(item_cd_bin) effect_id = item_cd_model.nb_effects() item_cd_model.add_effect_code(item_effect) item_cd_model.set_item_effect_id(GUMMI_ITEM_ID, effect_id) rom.setFileByName('BALANCE/item_cd.bin', DataCDHandler.serialize(item_cd_model)) # Change item's text attributes for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Item Names'] strings.strings[block.begin + GUMMI_ITEM_ID] = NAME_LIST[filename] block = config.string_index_data.string_blocks[ 'Item Short Descriptions'] strings.strings[block.begin + GUMMI_ITEM_ID] = SDES_LIST[filename] block = config.string_index_data.string_blocks[ 'Item Long Descriptions'] strings.strings[block.begin + GUMMI_ITEM_ID] = LDES_LIST[filename] bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) bar_bin = rom.getFileByName('BALANCE/itembar.bin') bar_model = DataSTHandler.deserialize(bar_bin) gummi_id = bar_model.get_item_struct_id(OTHER_GUMMI_ID) bar_model.set_item_struct_id(GUMMI_ITEM_ID, gummi_id) rom.setFileByName('BALANCE/itembar.bin', DataSTHandler.serialize(bar_model)) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: evo_hp_bonus = EVO_HP_BONUS_US evo_ph_bonus = EVO_PHYS_BONUS_US evo_sp_bonus = EVO_SPEC_BONUS_US if config.game_region == GAME_REGION_EU: evo_hp_bonus = EVO_HP_BONUS_EU evo_ph_bonus = EVO_PHYS_BONUS_EU evo_sp_bonus = EVO_SPEC_BONUS_EU if config.game_region == GAME_REGION_JP: evo_hp_bonus = EVO_HP_BONUS_JP evo_ph_bonus = EVO_PHYS_BONUS_JP evo_sp_bonus = EVO_SPEC_BONUS_JP # Create Evo Table md_bin = rom.getFileByName('BALANCE/monster.md') md_model = MdHandler.deserialize(md_bin) mevo_data = bytearray(len(md_model) * MEVO_ENTRY_LENGTH + 4) write_u32(mevo_data, u32_checked(len(mevo_data)), 0) for i in range(len(md_model)): if i in SPECIAL_EVOS: next_stage = SPECIAL_EVOS[i] else: next_stage = [] for j, x in enumerate(md_model): if (i < 600 and 1 <= j < 555) or (i >= 600 and 601 <= j < 1155): if x.pre_evo_index == i and x.evo_method != EvolutionMethod.NONE and ( x.evo_param2 != AdditionalRequirement.MALE or i < 600 ) and (x.evo_param2 != AdditionalRequirement.FEMALE or i >= 600): next_stage.append(j) write_u16(mevo_data, u16_checked(len(next_stage)), i * MEVO_ENTRY_LENGTH + 4) for j, x in enumerate(next_stage): write_u16(mevo_data, x, i * MEVO_ENTRY_LENGTH + j * 2 + 6) if i in SPECIAL_EGGS: next_stage = SPECIAL_EGGS[i] else: next_stage = [] pre_evo = i tries = 0 while md_model[pre_evo].pre_evo_index != 0: current = md_model[pre_evo] pre_evo = current.pre_evo_index if current.evo_param2 == AdditionalRequirement.MALE and md_model[ pre_evo % 600].gender == Gender.MALE and md_model[ pre_evo % 600 + 600].gender == Gender.FEMALE: pre_evo = pre_evo % 600 elif current.evo_param2 == AdditionalRequirement.FEMALE and md_model[ pre_evo % 600].gender == Gender.MALE and md_model[ pre_evo % 600 + 600].gender == Gender.FEMALE: pre_evo = pre_evo % 600 + 600 tries += 1 if tries >= MAX_TRY: raise Exception( _("Infinite recursion detected in pre evolutions for md entry {i}. " )) next_stage.append(pre_evo) if next_stage != [i]: write_u16(mevo_data, u16_checked(len(next_stage)), i * MEVO_ENTRY_LENGTH + 0x16) for j, x in enumerate(next_stage): write_u16(mevo_data, x, i * MEVO_ENTRY_LENGTH + j * 2 + 0x18) hp_bonus = read_u32(rom.arm9, evo_hp_bonus) atk_bonus = read_u16(rom.arm9, evo_ph_bonus) def_bonus = read_u16(rom.arm9, evo_ph_bonus + 2) spatk_bonus = read_u16(rom.arm9, evo_sp_bonus) spdef_bonus = read_u16(rom.arm9, evo_sp_bonus + 2) evo_add_stats = bytearray(MEVO_STATS_LENGTH) write_u16(evo_add_stats, u16_checked(hp_bonus), 0) write_u16(evo_add_stats, atk_bonus, 2) write_u16(evo_add_stats, spatk_bonus, 4) write_u16(evo_add_stats, def_bonus, 6) write_u16(evo_add_stats, spdef_bonus, 8) mevo_data += evo_add_stats * len(md_model) if MEVO_PATH not in rom.filenames: create_file_in_rom(rom, MEVO_PATH, mevo_data) else: rom.setFileByName(MEVO_PATH, mevo_data) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: new_pkmn_str_region = US_NEW_PKMN_STR_REGION new_cat_str_region = US_NEW_CAT_STR_REGION file_assoc = US_FILE_ASSOC table_sf = US_TABLE_SF table_mf = US_TABLE_MF table_sp = US_TABLE_SP if config.game_region == GAME_REGION_EU: new_pkmn_str_region = EU_NEW_PKMN_STR_REGION new_cat_str_region = EU_NEW_CAT_STR_REGION file_assoc = EU_FILE_ASSOC table_sf = EU_TABLE_SF table_mf = EU_TABLE_MF table_sp = EU_TABLE_SP if not self.is_applied(rom, config): bincfg = config.binaries['arm9.bin'] binary = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) # Apply the patch for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Pokemon Names'] monsters = strings.strings[block.begin:block.end] strings.strings[block.begin:block.end] = [""] * (block.end - block.begin) block = config.string_index_data.string_blocks[ 'Pokemon Categories'] cats = strings.strings[block.begin:block.end] strings.strings[block.begin:block.end] = [""] * (block.end - block.begin) for x in range(NUM_NEW_ENTRIES): if x < NUM_PREVIOUS_ENTRIES * 2: str_pkmn = monsters[x % NUM_PREVIOUS_ENTRIES] else: str_pkmn = "DmyPk%04d" % x if len(strings.strings) <= new_pkmn_str_region + x - 1: strings.strings.append(str_pkmn) else: strings.strings[new_pkmn_str_region + x - 1] = str_pkmn for x in range(NUM_NEW_ENTRIES): if x < NUM_PREVIOUS_ENTRIES * 2: str_cat = cats[x % NUM_PREVIOUS_ENTRIES] else: str_cat = "DmyCa%04d" % x if len(strings.strings) <= new_cat_str_region + x - 1: strings.strings.append(str_cat) else: strings.strings[new_cat_str_region + x - 1] = str_cat bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) sorted_list = list( enumerate(strings.strings[new_pkmn_str_region - 1:new_pkmn_str_region - 1 + NUM_NEW_ENTRIES])) sorted_list.sort(key=lambda x: normalize_string(x[1])) sorted_list = [x[0] for x in sorted_list] inv_sorted_list = [ sorted_list.index(i) for i in range(NUM_NEW_ENTRIES) ] m2n_model = ValListHandler.deserialize( rom.getFileByName(file_assoc[filename][0])) m2n_model.set_list(inv_sorted_list) rom.setFileByName(file_assoc[filename][0], ValListHandler.serialize(m2n_model)) n2m_model = ValListHandler.deserialize( rom.getFileByName(file_assoc[filename][1])) n2m_model.set_list(sorted_list) rom.setFileByName(file_assoc[filename][1], ValListHandler.serialize(n2m_model)) # Expand kao file kao_bin = rom.getFileByName('FONT/kaomado.kao') kao_model = KaoHandler.deserialize(kao_bin) kao_model.expand(NUM_NEW_ENTRIES - 1) for i in range(NUM_PREVIOUS_ENTRIES - 1): for j in range(SUBENTRIES): a = kao_model.get(i, j) b = kao_model.get(i + NUM_PREVIOUS_ENTRIES, j) if b == None and a != None: kao_model.set(i + NUM_PREVIOUS_ENTRIES, j, a) rom.setFileByName('FONT/kaomado.kao', KaoHandler.serialize(kao_model)) # Expand tbl_talk tlk_bin = rom.getFileByName('MESSAGE/tbl_talk.tlk') tlk_model = TblTalkHandler.deserialize(tlk_bin) while tlk_model.get_nb_monsters() < NUM_NEW_ENTRIES: tlk_model.add_monster_personality(DUMMY_PERSONALITY) rom.setFileByName('MESSAGE/tbl_talk.tlk', TblTalkHandler.serialize(tlk_model)) # Add monsters md_bin = rom.getFileByName('BALANCE/monster.md') md_model = MdHandler.deserialize(md_bin) while len(md_model.entries) < NUM_NEW_ENTRIES: md_model.entries.append( MdEntry.new_empty(u16_checked(len(md_model.entries)))) for i in range(NUM_PREVIOUS_ENTRIES): md_model.entries[i].entid = i if md_model.entries[NUM_PREVIOUS_ENTRIES + i].gender == Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + i].entid = NUM_PREVIOUS_ENTRIES + i else: md_model.entries[NUM_PREVIOUS_ENTRIES + i].entid = i block = bincfg.symbols['MonsterSpriteData'] data = binary[block.begin:block.end] + binary[block.begin:block. end] data += b'\x00\x00' * (NUM_NEW_ENTRIES - (len(data) // 2)) for i in range(0, len(data), 2): md_model.entries[i // 2].unk17 = data[i] md_model.entries[i // 2].unk18 = data[i + 1] md_model.entries[i // 2].bitfield1_0 = False md_model.entries[i // 2].bitfield1_1 = False md_model.entries[i // 2].bitfield1_2 = False md_model.entries[i // 2].bitfield1_3 = False x = table_sf while read_u16(rom.arm9, x) != 0: pkmn_id = read_u16(rom.arm9, x) md_model.entries[pkmn_id].bitfield1_3 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_3 = True x += 2 x = table_mf while read_u16(rom.arm9, x) != 0: pkmn_id = read_u16(rom.arm9, x) md_model.entries[pkmn_id].bitfield1_2 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_2 = True x += 2 ov19 = rom.loadArm9Overlays([19])[19].data for x in range(table_sp, table_sp + TABLE_SP_SIZE, 2): pkmn_id = read_u16(ov19, x) md_model.entries[pkmn_id].bitfield1_1 = True # pylint: disable=invalid-sequence-index md_model.entries[pkmn_id].bitfield1_0 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_1 = True md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_0 = True rom.setFileByName('BALANCE/monster.md', MdHandler.serialize(md_model)) # Edit Mappa bin mappa_bin = rom.getFileByName('BALANCE/mappa_s.bin') mappa_model = MappaBinHandler.deserialize(mappa_bin) dl = HardcodedDungeons.get_dungeon_list(bytes(rom.arm9), config) # Handle Dojos start_floor = 0 for x in range(DOJO_DUNGEONS_FIRST, DOJO_DUNGEONS_LAST - 2): dl.append( DungeonDefinition(u8(5), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 5 dl.append( DungeonDefinition(u8(1), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 1 dl.append( DungeonDefinition(u8(0x30), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 0x30 for dungeon in dl: for f in range(dungeon.start_after + 1, dungeon.start_after + dungeon.number_floors, 2): try: for entry in mappa_model.floor_lists[ dungeon.mappa_index][f].monsters: if entry.md_index != DUMMY_PKMN and entry.md_index < NUM_PREVIOUS_ENTRIES and \ entry.md_index + NUM_PREVIOUS_ENTRIES < len(md_model.entries) and \ md_model.entries[entry.md_index + NUM_PREVIOUS_ENTRIES].gender != Gender.INVALID: entry.md_index += NUM_PREVIOUS_ENTRIES except: print(f"{dungeon.mappa_index}, {f} is not valid.") rom.setFileByName('BALANCE/mappa_s.bin', MappaBinHandler.serialize(mappa_model)) # Add moves waza_p_bin = rom.getFileByName('BALANCE/waza_p.bin') waza_p_model = WazaPHandler.deserialize(waza_p_bin) while len(waza_p_model.learnsets) < NUM_PREVIOUS_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset waza_p_model.learnsets = waza_p_model.learnsets + waza_p_model.learnsets while len(waza_p_model.learnsets) < NUM_NEW_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset rom.setFileByName('BALANCE/waza_p.bin', WazaPHandler.serialize(waza_p_model)) # Add moves 2 waza_p_bin = rom.getFileByName('BALANCE/waza_p2.bin') waza_p_model = WazaPHandler.deserialize(waza_p_bin) while len(waza_p_model.learnsets) < NUM_PREVIOUS_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset waza_p_model.learnsets = waza_p_model.learnsets + waza_p_model.learnsets while len(waza_p_model.learnsets) < NUM_NEW_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset rom.setFileByName('BALANCE/waza_p2.bin', WazaPHandler.serialize(waza_p_model)) # Add levels level_bin = rom.getFileByName('BALANCE/m_level.bin') level_model = BinPackHandler.deserialize(level_bin) while len(level_model.get_files_bytes()) < NUM_PREVIOUS_ENTRIES: new_bytes_unpacked = bytes(LEVEL_BIN_ENTRY_LEVEL_LEN * 100) new_bytes_pkdpx = PkdpxHandler.serialize( PkdpxHandler.compress(new_bytes_unpacked)) new_bytes = Sir0Handler.serialize( Sir0Handler.wrap(new_bytes_pkdpx, [])) level_model.append(new_bytes) # Empty Levelup data for i in range(NUM_PREVIOUS_ENTRIES): level_model.append(level_model[i]) while len(level_model.get_files_bytes()) < NUM_NEW_ENTRIES: new_bytes_unpacked = bytes(LEVEL_BIN_ENTRY_LEVEL_LEN * 100) new_bytes_pkdpx = PkdpxHandler.serialize( PkdpxHandler.compress(new_bytes_unpacked)) new_bytes = Sir0Handler.serialize( Sir0Handler.wrap(new_bytes_pkdpx, [])) level_model.append(new_bytes) # Empty Levelup data rom.setFileByName('BALANCE/m_level.bin', BinPackHandler.serialize(level_model)) # Add evolutions evo_bin = rom.getFileByName('BALANCE/md_evo.bin') evo_model = MdEvoHandler.deserialize(evo_bin) while len(evo_model.evo_entries) < NUM_NEW_ENTRIES: evo_model.evo_entries.append( MdEvoEntry(bytearray(MEVO_ENTRY_LENGTH))) while len(evo_model.evo_stats) < NUM_NEW_ENTRIES: evo_model.evo_stats.append( MdEvoStats(bytearray(MEVO_STATS_LENGTH))) rom.setFileByName('BALANCE/md_evo.bin', MdEvoHandler.serialize(evo_model)) # Fixed floors ov29 = config.binaries['overlay/overlay_0029.bin'] ov29bin = bytearray(get_binary_from_rom_ppmdu(rom, ov29)) monster_list = HardcodedFixedFloorTables.get_monster_spawn_list( ov29bin, config) for m in monster_list: if m.md_idx >= NUM_PREVIOUS_MD_MAX: m.md_idx += NUM_NEW_ENTRIES - NUM_PREVIOUS_MD_MAX HardcodedFixedFloorTables.set_monster_spawn_list( ov29bin, monster_list, config) set_binary_in_rom_ppmdu(rom, ov29, bytes(ov29bin)) try: apply() except RuntimeError as ex: raise ex