示例#1
0
def init_free_airship(rom: Rom) -> Rom:
    # Move the airship's start location to right outside of Coneria Castle.
    airship_start = OutputStream()
    airship_start.put_u32(0x918)
    airship_start.put_u32(0x998)

    return rom.apply_patch(0x65280, airship_start.get_buffer())
示例#2
0
def update_xp_requirements(rom: Rom, value) -> Rom:
    level_data = rom.open_bytestream(0x1BE3B4, 396)
    new_table = OutputStream()
    next_value = level_data.get_u32()
    while next_value is not None:
        new_table.put_u32(int(next_value * value))
        next_value = level_data.get_u32()
    rom = rom.apply_patch(0x1BE3B4, new_table.get_buffer())
    return rom
示例#3
0
 def test_overlap_patch(self):
     patch = OutputStream()
     patch.put_u32(0x12345678)
     patch.put_u32(0x12345678)
     with self.assertRaises(RuntimeError):
         patched = self.rom.apply_patches({
             0x0: patch.get_buffer(),
             0x4: patch.get_buffer()
         })
         self.assertNotEqual(patched, patched)
示例#4
0
    def test_patches(self):
        patch = OutputStream()
        patch.put_u32(0x12345678)
        patched = self.rom.apply_patches({
            0x0: patch.get_buffer(),
            0x10: patch.get_buffer()
        })
        confirm = patched.open_bytestream(0x0, 0x4)
        self.assertEqual(confirm.get_u32(), 0x12345678)

        confirm = patched.open_bytestream(0x10, 0x4)
        self.assertEqual(confirm.get_u32(), 0x12345678)
示例#5
0
    def pack(self, rom: Rom) -> Rom:
        text_block = OutputStream()
        text_lut = OutputStream()

        next_addr = self.lut[0]
        text_block_offset = Rom.pointer_to_offset(next_addr)

        for index, data in enumerate(self.strings):
            if data is not None:
                text_lut.put_u32(next_addr)
                text_block.put_bytes(data)
                next_addr += len(data)
            else:
                text_lut.put_u32(self.lut[0])

        patches = {
            self.lut_offset: text_lut.get_buffer(),
            text_block_offset: text_block.get_buffer()
        }
        return rom.apply_patches(patches)
示例#6
0
def add_credits(rom: Rom) -> Rom:
    credits_lut = rom.get_lut(0x1D871C, 128)
    base_addr = credits_lut[0]

    new_lut = OutputStream()
    data_stream = OutputStream()

    for index, line in enumerate(CREDITS_TEXT.splitlines()[1:]):
        line = line.strip()
        if len(line) > 0:
            encoded = TextBlock.encode_text(line)

            new_lut.put_u32(base_addr + data_stream.size())
            data_stream.put_bytes(encoded)
        else:
            new_lut.put_u32(0x0)

    # And EOF marker
    new_lut.put_u32(0xffffffff)

    # Change the duration so it doesn't take so long to scroll
    duration = OutputStream()
    duration.put_u16(60 * 60)

    return rom.apply_patches({
        0x016848:
        duration.get_buffer(),
        0x1D871C:
        new_lut.get_buffer(),
        Rom.pointer_to_offset(base_addr):
        data_stream.get_buffer()
    })
示例#7
0
 def write(self, stream: OutputStream):
     stream.put_u8(self.contents)
     for data in self.unused:
         stream.put_u8(data)
     stream.put_u32(self.pointer)
示例#8
0
def randomize_rom(rom: Rom, flags: Flags, rom_seed: str) -> Rom:
    rng = random.Random()
    rng.seed(rom_seed)

    print(f"Randomize ROM: {flags.text()}, seed='{rom_seed}'")
    patches_to_load = BASE_PATCHES
    if flags.encounters is not None:
        patches_to_load.append("data/FF1EncounterToggle.ips")
    if flags.default_party is not None:
        patches_to_load.append("data/RandomDefault.ips")

    patched_rom_data = rom.rom_data

    for patch_path in patches_to_load:
        patch = Patch.load(patch_path)
        patched_rom_data = patch.apply(patched_rom_data)
    rom = Rom(data=bytearray(patched_rom_data))

    rom = init_free_airship(rom)
    rom = add_credits(rom)

    event_text_block = EventTextBlock(rom)
    event_text_block.shrink()
    rom = event_text_block.pack(rom)

    rom = update_xp_requirements(rom, flags.exp_mult)

    if flags.key_item_shuffle is not None:
        placement = KeyItemPlacement(rom, rng.randint(0, 0xffffffff))
    else:
        placement = KeyItemPlacement(rom)
    rom = placement.rom

    if flags.magic is not None:
        shuffle_magic = SpellShuffle(rom, rng)
        rom = shuffle_magic.write(rom)

    if flags.treasures is not None:
        if flags.treasures == "shuffle":
            rom = treasure_shuffle(rom, rng)
        else:
            rom = random_bucketed_treasures(rom, rng, flags.wealth)

    if flags.debug is not None:
        class_stats_stream = rom.open_bytestream(0x1E1354, 96)
        class_stats = []
        while not class_stats_stream.is_eos():
            class_stats.append(JobClass(class_stats_stream))

        class_out_stream = OutputStream()
        for job_class in class_stats:
            # Set the starting weapon and armor for all classes to something
            # very fair and balanced: Masamune + Diamond Armlet. :)
            job_class.weapon_id = 0x28
            job_class.armor_id = 0x0e

            # Write the (very balanced) new data out
            job_class.write(class_out_stream)

        rom = rom.apply_patch(0x1E1354, class_out_stream.get_buffer())

    if flags.shuffle_formations:
        formation = FormationRandomization(rom, rng)
        rom = rom.apply_patches(formation.patches())

    if True:
        enemy_data_stream = rom.open_bytestream(0x1DE044, 0x1860)
        enemies = []
        while not enemy_data_stream.is_eos():
            enemies.append(EnemyStats(enemy_data_stream))

        # Rebalance (Revisited) Fiend HP
        enemies[0x78].max_hp = enemies[0x77].max_hp * 2
        enemies[0x7a].max_hp = enemies[0x79].max_hp * 2
        enemies[0x7c].max_hp = enemies[0x7b].max_hp * 2
        enemies[0x7e].max_hp = enemies[0x7d].max_hp * 2

        # And Chaos
        enemies[0x7f].max_hp = enemies[0x7e].max_hp * 2

        # Finally, Piscodemons can suck it
        enemies[0x67].atk = int(enemies[0x67].atk / 2)

        # We'll also lower everyone's INT just to see how that works
        for index in range(0x80):
            enemies[index].intel = int(.666 * enemies[index].intel)
            # print(f"{hex(index)} HP: {enemies[index].max_hp}, INT: {enemies[index].intel}")

        out = OutputStream()
        for enemy in enemies:
            enemy.write(out)
        rom = rom.apply_patch(0x1DE044, out.get_buffer())

    # Add the seed + flags to the party creation screen.
    seed_str = TextBlock.encode_text(f"Seed:\n{rom_seed}\nFlags:\n{flags}\x00")
    pointer = OutputStream()
    pointer.put_u32(0x8227054)
    rom = rom.apply_patches({
        0x227054: seed_str,
        0x4d8d4: pointer.get_buffer()
    })

    return rom
示例#9
0
 def get_lut(self) -> bytearray:
     stream = OutputStream()
     for addr in self._lut:
         stream.put_u32(addr)
     return stream.get_buffer()
示例#10
0
def parse(source: str, base_addr: int, debug=False) -> bytearray:
    symbol_table = {}
    icode = []
    current_addr = base_addr

    for line_number, line in enumerate(source.splitlines()):
        tokens = TokenStream(line_number, line)

        token = tokens.next()
        if token is None:
            # Empty or comment only line
            continue

        if isinstance(token, RawCommandToken):
            parameters = []
            token = tokens.expect(GRAMMAR["$$value$$"])
            while token is not None:
                if isinstance(token, SymbolToken):
                    if token in symbol_table:
                        parameters.append(symbol_table[token])
                    else:
                        raise SymbolNotDefinedError(token, line, line_number)
                else:
                    parameters.append(token)
                token = tokens.expect(GRAMMAR["$$value$$"])

            icode.append(parameters)
            current_addr += parameters[1]
        else:
            # Save the op name
            op_name = token

            if type(token) not in GRAMMAR:
                raise ParserSyntaxError(token, line, line_number)
            match = GRAMMAR[type(token)]
            if isinstance(match, dict):
                rule_set = match["rule"]
            else:
                rule_set = match

            parameters = []

            if rule_set is not None:
                for rule in rule_set:
                    if isinstance(rule, str) and rule.startswith("$$"):
                        rule = GRAMMAR[rule]
                    token = tokens.expect(rule)

                    if token is None:
                        raise ParserSyntaxError(token, line, line_number)

                    if isinstance(op_name, SymbolToken):
                        parameters.append(token)
                    else:
                        if isinstance(token, SymbolToken):
                            if token in symbol_table:
                                parameters.append(symbol_table[token])
                            else:
                                raise SymbolNotDefinedError(
                                    token, line, line_number)
                        else:
                            parameters.append(token)

                verify_end = tokens.expect(CommentToken())
            else:
                verify_end = tokens.expect(CommentToken())

            if op_name == "def_symbol" or op_name == "def_label":
                name = parameters[0]
                value = parameters[1]
                if name in symbol_table:
                    raise DuplicateSymbolError(name, line, line_number)

                if isinstance(value, ColonToken):
                    value = current_addr
                symbol_table[name] = value
            else:
                if isinstance(op_name, list):
                    output = simple_gen(op_name, parameters)
                else:
                    method = getattr(codegen, op_name)
                    output = method(parameters)

                if output is not None:
                    icode.append(output)
                    current_addr += output[1]

    # At this point, all of the intermediate code is built and the only thing left is to resolve
    # the left over symbols, which will all bel labels.
    bytecode = OutputStream()
    for code in icode:
        txt = ""
        for bd in code:
            if isinstance(bd, LabelToken):
                label = bd
                if label not in symbol_table:
                    raise UndefinedLabel(label)
                bytecode.put_u32(symbol_table[label])
                txt += f"{label} ({hex(symbol_table[label])}) "
            else:
                txt += f"{hex(bd)} "
                bytecode.put_u8(bd)
        if debug:
            print(txt)

    # Done!
    return bytecode.get_buffer()
示例#11
0
 def write(self, stream: OutputStream):
     chest_data = (self.id << 24) | self.qty
     stream.put_u32(chest_data)
示例#12
0
 def write(self, stream: OutputStream):
     chest_data = (self.id << 24) | (self.item_id << 8) | (self.item_type
                                                           & 0xff)
     stream.put_u32(chest_data)