示例#1
0
    def read_data(self, filename):
        f = open(filename, 'r+b')
        f.seek(self.pointer)
        start = read_multi(f, length=2)
        end = read_multi(f, length=2)
        f.close()
        n = (end - start) // 6
        assert end == start + (6 * n)
        self.entrances = []
        for i in range(n):
            e = Entrance(0x1fbb00 + start + (i * 6))
            e.set_id(i)
            self.entrances.append(e)
        for e in self.entrances:
            e.read_data(filename)
            e.set_location(self.location)

        f = open(filename, 'r+b')
        f.seek(self.longpointer)
        start = read_multi(f, length=2)
        end = read_multi(f, length=2)
        f.close()
        n = (end - start) // 7
        assert end == start + (7 * n)
        self.longentrances = []
        for i in range(n):
            e = LongEntrance(0x2DF480 + start + (i * 7))
            e.set_id(i)
            self.longentrances.append(e)
        for e in self.longentrances:
            e.read_data(filename)
            e.set_location(self.location)

        self.location.uniqify_entrances()
示例#2
0
    def read_data(self, filename=None, pointer=None):
        if pointer is None:
            pointer = self.pointer
        if filename is None:
            filename = self.filename
        if pointer is None or filename is None:
            return

        if self.variable_size is not None:
            specsattrs = self.get_variable_specsattrs()
        else:
            specsattrs = self.specsattrs

        f = open(filename, 'r+b')
        f.seek(pointer)
        for name, size, other in specsattrs:
            if other in [None, "int"]:
                value = read_multi(f, length=size)
            elif other == "str":
                value = f.read(size)
            elif other == "list":
                if not isinstance(size, int):
                    number, numbytes = size.split('x')
                    number, numbytes = int(number), int(numbytes)
                else:
                    number, numbytes = size, 1
                value = []
                for i in xrange(number):
                    value.append(read_multi(f, numbytes))
            setattr(self, name, value)
        f.close()
    def read_data(self, filename):
        f = open(filename, 'r+b')
        f.seek(self.pointer)
        start = read_multi(f, length=2)
        end = read_multi(f, length=2)
        f.close()
        n = (end - start) / 6
        assert end == start + (6*n)
        self.entrances = []
        for i in xrange(n):
            e = Entrance(0x1fbb00 + start + (i*6))
            e.set_id(i)
            self.entrances.append(e)
        for e in self.entrances:
            e.read_data(filename)
            e.set_location(self.location)

        f = open(filename, 'r+b')
        f.seek(self.longpointer)
        start = read_multi(f, length=2)
        end = read_multi(f, length=2)
        f.close()
        n = (end - start) / 7
        assert end == start + (7*n)
        self.longentrances = []
        for i in xrange(n):
            e = LongEntrance(0x2DF480 + start + (i*7))
            e.set_id(i)
            self.longentrances.append(e)
        for e in self.longentrances:
            e.read_data(filename)
            e.set_location(self.location)

        self.location.uniqify_entrances()
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.itemtype = read_multi(f, length=2)
     if self.itemtype in objname:
         self.name = objname[self.itemtype]
     self.x = read_multi(f, length=2)
     self.y = read_multi(f, length=2)
     f.close()
示例#5
0
    def read_stats(self, filename):
        global all_spells

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        self.itemtype = ord(f.read(1))

        itemtype = self.itemtype & 0x0f
        (self.is_tool, self.is_weapon, self.is_armor, self.is_relic,
            self.is_consumable) = (False, False, False, False, False)
        self.is_shield, self.is_helm, self.is_body_armor = False, False, False
        if itemtype == 0x00:
            self.is_tool = True
        elif itemtype == 0x01:
            self.is_weapon = True
        elif itemtype in [2, 3, 4]:
            self.is_armor = True
            if itemtype == 3:
                self.is_shield = True
            elif itemtype == 4:
                self.is_helm = True
            elif itemtype == 2:
                self.is_body_armor = True
        elif itemtype == 0x05:
            self.is_relic = True
        elif itemtype == 0x06:
            self.is_consumable = True

        #throwable = self.itemtype & 0x10
        #usable_battle = self.itemtype & 0x20
        #usable_field = self.itemtype & 0x40

        self.equippable = read_multi(f, length=2)
        self.heavy = bool(self.equippable & 0x8000)

        stats = map(ord, f.read(len(ITEM_STATS)))
        self.features = dict(zip(ITEM_STATS, stats))

        self.price = read_multi(f, length=2)

        if all_spells is None:
            all_spells = sorted([SpellBlock(i, filename) for i in xrange(0xFF)],
                                key=lambda s: s.rank())
            all_spells = filter(lambda s: s.valid, all_spells)

        f.seek(0x2CE408 + (8*self.itemid))
        self.weapon_animation = map(ord, f.read(8))

        f.seek(0x12B300 + (13*self.itemid))
        self.dataname = map(ord, f.read(13))

        f.close()
示例#6
0
    def read_stats(self, filename):
        global all_spells

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        self.itemtype = ord(f.read(1))

        itemtype = self.itemtype & 0x0f
        (self.is_tool, self.is_weapon, self.is_armor, self.is_relic,
            self.is_consumable) = (False, False, False, False, False)
        self.is_shield, self.is_helm, self.is_body_armor = False, False, False
        if itemtype == 0x00:
            self.is_tool = True
        elif itemtype == 0x01:
            self.is_weapon = True
        elif itemtype in [2, 3, 4]:
            self.is_armor = True
            if itemtype == 3:
                self.is_shield = True
            elif itemtype == 4:
                self.is_helm = True
            elif itemtype == 2:
                self.is_body_armor = True
        elif itemtype == 0x05:
            self.is_relic = True
        elif itemtype == 0x06:
            self.is_consumable = True

        #throwable = self.itemtype & 0x10
        #usable_battle = self.itemtype & 0x20
        #usable_field = self.itemtype & 0x40

        self.equippable = read_multi(f, length=2)
        self.heavy = bool(self.equippable & 0x8000)

        stats = map(ord, f.read(len(ITEM_STATS)))
        self.features = dict(zip(ITEM_STATS, stats))

        self.price = read_multi(f, length=2)

        if all_spells is None:
            all_spells = sorted([SpellBlock(i, filename) for i in xrange(0xFF)],
                                key=lambda s: s.rank())
            all_spells = filter(lambda s: s.valid, all_spells)

        f.seek(0x2CE408 + (8*self.itemid))
        self.weapon_animation = map(ord, f.read(8))

        f.seek(0x12B300 + (13*self.itemid))
        self.dataname = map(ord, f.read(13))

        f.close()
示例#7
0
 def read_chests(self, filename):
     from chestrandomizer import ChestBlock
     f = open(filename, 'r+b')
     f.seek(self.chestpointer)
     begin = read_multi(f, length=2)
     end = read_multi(f, length=2)
     numchests = (end - begin) // 5
     self.chests = []
     for i in range(numchests):
         pointer = begin + (i * 5) + 0x2d8634
         c = ChestBlock(pointer, self.locid)
         c.read_data(filename)
         c.set_id(i)
         self.chests.append(c)
 def read_chests(self, filename):
     from chestrandomizer import ChestBlock
     f = open(filename, 'r+b')
     f.seek(self.chestpointer)
     begin = read_multi(f, length=2)
     end = read_multi(f, length=2)
     numchests = (end - begin) / 5
     self.chests = []
     for i in xrange(numchests):
         pointer = begin + (i*5) + 0x2d8634
         c = ChestBlock(pointer, self.locid)
         c.read_data(filename)
         c.set_id(i)
         self.chests.append(c)
示例#9
0
 def read_events(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.eventpointer)
     begin = read_multi(f, length=2)
     end = read_multi(f, length=2)
     numevents = (end - begin) / 5.0
     assert numevents == round(numevents)
     numevents = int(numevents)
     self.events = []
     for i in range(numevents):
         pointer = begin + (i * 5) + 0x40000
         e = EventBlock(pointer, self.locid)
         e.read_data(filename)
         e.set_id(i)
         self.events.append(e)
示例#10
0
 def read_events(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.eventpointer)
     begin = read_multi(f, length=2)
     end = read_multi(f, length=2)
     numevents = (end - begin) / 5.0
     assert numevents == round(numevents)
     numevents = int(numevents)
     self.events = []
     for i in xrange(numevents):
         pointer = begin + (i*5) + 0x40000
         e = EventBlock(pointer, self.locid)
         e.read_data(filename)
         e.set_id(i)
         self.events.append(e)
示例#11
0
 def read_npcs(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.npcpointer)
     begin = read_multi(f, length=2)
     end = read_multi(f, length=2)
     numnpcs = (end - begin) / 9.0
     assert numnpcs == round(numnpcs)
     numnpcs = int(numnpcs)
     self.npcs = []
     for i in xrange(numnpcs):
         pointer = begin + (i*9) + 0x41a10
         e = NPCBlock(pointer, self.locid)
         e.read_data(filename)
         e.set_id(i)
         self.npcs.append(e)
示例#12
0
 def read_npcs(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.npcpointer)
     begin = read_multi(f, length=2)
     end = read_multi(f, length=2)
     numnpcs = (end - begin) / 9.0
     assert numnpcs == round(numnpcs)
     numnpcs = int(numnpcs)
     self.npcs = []
     for i in range(numnpcs):
         pointer = begin + (i * 9) + 0x41a10
         e = NPCBlock(pointer, self.locid)
         e.read_data(filename)
         e.set_id(i)
         self.npcs.append(e)
示例#13
0
def ptrfind(filename, ptrsize, minimum, reverse=True):
    ptrsize, minimum = int(ptrsize), int(minimum)
    reverse = False if reverse == "False" else bool(reverse)
    f = open(filename, 'r+b')
    f.seek(0, 2)
    filesize = f.tell()
    location = 0
    discoveries = []
    while True:
        f.seek(location)
        prev = -1
        values = []
        while True:
            if f.tell() + ptrsize >= filesize:
                break
            value = read_multi(f, ptrsize, reverse=reverse)
            if value < prev:
                break
            if value == prev and value == 0 and len(values) == 1:
                break
            values.append(value)
            prev = value
        if len(values) >= minimum and len(set(values)) >= minimum/8:
            discoveries.append((location, values))
            location += len(values) * ptrsize
        else:
            location += 1
        if location + ptrsize >= filesize:
            break
    f.close()
    return discoveries
示例#14
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.x = ord(f.read(1))
     self.y = ord(f.read(1))
     self.event_addr = read_multi(f, length=3)
     f.close()
示例#15
0
    def read_data(self, filename):
        f = open(filename, 'r+b')
        f.seek(self.pointer)
        value = read_multi(f, length=4)
        self.palette = (value & 0x1C0000) >> 18
        self.bg2_scroll = (value & 0x200000) >> 21
        self.membit = (value & 0x1C00000) >> 22
        self.memaddr = (value & 0xFE000000) >> 25
        self.event_addr = value & 0x3FFFF

        byte4 = ord(f.read(1))
        self.x = byte4 & 0x7f
        self.show_on_vehicle = (byte4 & 0x80) >> 7

        byte5 = ord(f.read(1))
        self.y = byte5 & 0x3F
        self.speed = (byte5 & 0xC0) >> 6

        self.graphics = ord(f.read(1))

        byte7 = ord(f.read(1))
        self.move_type = byte7 & 0xF
        self.sprite_priority = (byte7 & 0x30) >> 4
        self.vehicle = (byte7 & 0xC0) >> 6

        byte8 = ord(f.read(1))
        self.facing = byte8 & 0x03
        self.no_turn_when_speaking = (byte8 & 0x4) >> 2
        self.layer_priority = (byte8 & 0x18) >> 3
        self.special_anim = (byte8 & 0xe0) >> 5
        f.close()
示例#16
0
 def recolor_palette(pointer, size):
     fout.seek(pointer)
     palette = [read_multi(fout, length=2) for _ in range(size)]
     palette = transformer(palette)
     fout.seek(pointer)
     for c in palette:
         write_multi(fout, c, length=2)
示例#17
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.x = ord(f.read(1))
     self.y = ord(f.read(1))
     self.event_addr = read_multi(f, length=3)
     f.close()
示例#18
0
 def read_data(self):
     #f = open(self.imgname, 'r+b')
     f = file_from_sectors(self.imgname, self.initial_sector)
     f.seek(self.pointer)
     peek = f.read(1)
     if not peek:
         raise EOFError
     self.size = ord(peek)
     if self.size == 0:
         raise FileEntryReadException
     self.num_ear = ord(f.read(1))
     assert self.num_ear == 0
     assert not self.size % 2
     self.target_sector = read_multi(f, length=4)
     f.seek(4, 1)
     self.filesize = read_multi(f, length=4)
     f.seek(4, 1)
     self.year = ord(f.read(1)) + 1900
     self.month = ord(f.read(1))
     self.day = ord(f.read(1))
     self.hour = ord(f.read(1))
     self.minute = ord(f.read(1))
     self.second = ord(f.read(1))
     self.tz_offset = ord(f.read(1)) / 4.0
     self.flags = ord(f.read(1))
     assert not self.flags & 0xFC
     self.hidden = self.flags & 1
     self.is_directory = self.flags & 0x2
     self.interleaved_unit_size = ord(f.read(1))
     self.interleaved_gap_size = ord(f.read(1))
     assert not self.interleaved_unit_size or self.interleaved_gap_size
     self.one = read_multi(f, length=2)
     assert self.one == 1
     f.seek(2, 1)
     self.name_length = ord(f.read(1))
     self.name = f.read(self.name_length)
     if not self.name_length % 2:
         p = ord(f.read(1))
         assert p == 0
     self.pattern = f.read(14)
     if self.is_directory:
         assert self.pattern == DIRECTORY_PATTERN
     else:
         assert self.name[-2:] == ";1"
         #assert self.pattern == FILE_PATTERN
     assert f.tell() == self.pointer + self.size
     f.close()
示例#19
0
def decompress_at_location(filename, address):
    f = open(filename, 'r+b')
    f.seek(address)
    size = read_multi(f, length=2)
    #print "Size is %s" % size
    bytestring = f.read(size)
    decompressed = decompress(bytestring, complicated=True)
    return decompressed
示例#20
0
def decompress_at_location(filename, address):
    f = open(filename, 'r+b')
    f.seek(address)
    size = read_multi(f, length=2)
    #print "Size is %s" % size
    bytestring = f.read(size)
    decompressed = decompress(bytestring, complicated=True)
    return decompressed
示例#21
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.x = ord(f.read(1))
     self.y = ord(f.read(1))
     self.dest = read_multi(f, length=2)
     self.destx = ord(f.read(1))
     self.desty = ord(f.read(1))
     f.close()
示例#22
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.x = ord(f.read(1))
     self.y = ord(f.read(1))
     self.dest = read_multi(f, length=2)
     self.destx = ord(f.read(1))
     self.desty = ord(f.read(1))
     f.close()
示例#23
0
    def read_stats(self, filename):
        global all_spells, valid_spells, items, itemids

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        for key in stat_order:
            self.stats[key] = ord(f.read(1))
        self.stats['hp'] = read_multi(f, length=2)
        self.stats['mp'] = read_multi(f, length=2)
        self.stats['xp'] = read_multi(f, length=2)
        self.stats['gp'] = read_multi(f, length=2)
        self.stats['level'] = ord(f.read(1))

        self.morph = ord(f.read(1))
        self.misc1 = ord(f.read(1))
        self.misc2 = ord(f.read(1))

        f.seek(self.pointer + 20)
        self.immunities = map(ord, f.read(3))
        self.absorb = ord(f.read(1))
        self.null = ord(f.read(1))
        self.weakness = ord(f.read(1))

        f.seek(self.pointer + 27)
        self.statuses = map(ord, f.read(4))
        self.special = ord(f.read(1))

        f.seek(self.itemptr)
        self.items = map(ord, f.read(4))

        f.seek(self.controlptr)
        self.controls = map(ord, f.read(4))

        f.seek(self.sketchptr)
        self.sketches = map(ord, f.read(2))

        f.seek(self.rageptr)
        self.rages = map(ord, f.read(2))

        f.seek(self.aiptr)
        self.ai = read_multi(f, length=2)

        f.close()
def read_dialogue(fout):
    #load existing script & pointer table
    fout.seek(0xD0000)
    script_bin = fout.read(0x1F0FF)

    #TODO battle script

    fout.seek(0xCE600)
    bankidx = read_multi(fout, 2)
    for idx in range(0xC0C):  #C0D through CFF pointers are repurposed
        script_ptrs[idx] = read_multi(fout,
                                      2) + (0x10000 if idx >= bankidx else 0)

    for idx in range(0xC0C):  #C0D through CFF pointers are repurposed
        start = script_ptrs[idx]
        end = script_ptrs.get(idx + 1, 0)
        if end == 0:
            end = script_bin.find(b'\x00', start)
        script[idx] = bytes_to_dialogue(script_bin[start:end])
示例#25
0
    def read_stats(self, filename):
        global all_spells, valid_spells, items, itemids

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        for key in stat_order:
            self.stats[key] = ord(f.read(1))
        self.stats['hp'] = read_multi(f, length=2)
        self.stats['mp'] = read_multi(f, length=2)
        self.stats['xp'] = read_multi(f, length=2)
        self.stats['gp'] = read_multi(f, length=2)
        self.stats['level'] = ord(f.read(1))

        self.morph = ord(f.read(1))
        self.misc1 = ord(f.read(1))
        self.misc2 = ord(f.read(1))

        f.seek(self.pointer + 20)
        self.immunities = map(ord, f.read(3))
        self.absorb = ord(f.read(1))
        self.null = ord(f.read(1))
        self.weakness = ord(f.read(1))

        f.seek(self.pointer + 27)
        self.statuses = map(ord, f.read(4))
        self.special = ord(f.read(1))

        f.seek(self.itemptr)
        self.items = map(ord, f.read(4))

        f.seek(self.controlptr)
        self.controls = map(ord, f.read(4))

        f.seek(self.sketchptr)
        self.sketches = map(ord, f.read(2))

        f.seek(self.rageptr)
        self.rages = map(ord, f.read(2))

        f.seek(self.aiptr)
        self.ai = read_multi(f, length=2)

        f.close()
示例#26
0
    def read_stats(self, filename):
        global all_spells

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        self.itemtype = ord(f.read(1))

        # throwable = self.itemtype & 0x10
        # usable_battle = self.itemtype & 0x20
        # usable_field = self.itemtype & 0x40

        self.equippable = read_multi(f, length=2)
        self.heavy = bool(self.equippable & 0x8000)

        stats = list(f.read(len(ITEM_STATS)))
        self.features = dict(list(zip(ITEM_STATS, stats)))

        # move flags for "randomly cast" and "destroy if used"
        # so breakeffect can use the full range of spells
        if not self.is_consumable:
            break_flags = self.features["breakeffect"] & 0xC0
            self.features["otherproperties"] |= break_flags >> 4
            self.features["breakeffect"] &= ~0xC0

        self.price = read_multi(f, length=2)

        if all_spells is None:
            all_spells = get_ranked_spells(filename)
            all_spells = [s for s in all_spells if s.valid]

        f.seek(0x2CE408 + (8 * self.itemid))
        self.weapon_animation = list(f.read(8))

        f.seek(0x12B300 + (13 * self.itemid))
        self.dataname = list(f.read(13))

        # unhardcoded tintinabar patch moves the tintinabar flag
        if self.features["fieldeffect"] & 0x80:
            self.features["fieldeffect"] &= ~0x80
            self.features["special2"] |= 0x80

        f.close()
示例#27
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.formids = []
     if self.setid <= 0xFF:
         num_encounters = 4
     else:
         num_encounters = 2
     for i in xrange(num_encounters):
         self.formids.append(read_multi(f, length=2))
     f.close()
示例#28
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.formids = []
     if self.setid <= 0xFF:
         num_encounters = 4
     else:
         num_encounters = 2
     for i in xrange(num_encounters):
         self.formids.append(read_multi(f, length=2))
     f.close()
示例#29
0
文件: dialogue.py 项目: abyssonym/gg3
 def read_data(self, filename, header=False):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     if header:
         unknown = f.read(4)
         self.y = f.read(2)
         self.x = f.read(2)
         self.ff = f.read(1)
     nowpointer = f.tell()
     message = []
     while True:
         f.seek(nowpointer)
         byte = ord(f.read(1))
         if byte & 0xF0 == 0xF0:
             # stored word
             wordlength = (byte & 0x0F) + 4
             ctrlptr = read_multi(f, length=2)
             #if ctrlptr >= 0x8000:
             #    print "WARNING1 %x" % ctrlptr
             f.seek((nowpointer & 0xff0000) | ctrlptr)
             word = map(ord, f.read(wordlength))
             message.extend(word)
             nowpointer += 3
         elif byte & 0xF0 == 0xE0:
             # repeated character
             wordlength = (byte & 0x0F) + 3
             char = ord(f.read(1))
             message.extend([char]*wordlength)
             nowpointer += 2
         elif byte & 0xF0 == 0xB0:
             # control command
             nowpointer += 3
         elif byte & 0xF0 == 0xC0:
             message.append(0x18)
             nowpointer += 1
         elif byte & 0xF0 == 0xD0:
             message.append(byte)
             nowpointer += 1
         elif byte == 0x00:
             # termination
             nowpointer += 1
             break
         elif byte < 0xb0:
             # ordinary character
             message.append(byte)
             nowpointer += 1
         else:
             print "WARNING2 %x" % byte
             nowpointer += 1
     f.close()
     self.message = message
     return nowpointer
示例#30
0
 def read_mould(self, filename):
     mouldspecsptrs = 0x2D01A
     f = open(filename, 'r+b')
     pointer = mouldspecsptrs + (2*self.mould)
     f.seek(pointer)
     pointer = read_multi(f, length=2) | 0x20000
     for i in xrange(6):
         f.seek(pointer + (i*4))
         a, b = tuple(map(ord, f.read(2)))
         width = ord(f.read(1))
         height = ord(f.read(1))
         enemy = self.enemies[i]
         if enemy:
             enemy.update_size(width, height)
示例#31
0
 def read_mould(self, filename):
     mouldspecsptrs = 0x2D01A
     f = open(filename, 'r+b')
     pointer = mouldspecsptrs + (2 * self.mould)
     f.seek(pointer)
     pointer = read_multi(f, length=2) | 0x20000
     for i in xrange(6):
         f.seek(pointer + (i * 4))
         a, b = tuple(map(ord, f.read(2)))
         width = ord(f.read(1))
         height = ord(f.read(1))
         enemy = self.enemies[i]
         if enemy:
             enemy.update_size(width, height)
def read_location_names(f):
    #load existing script & pointer table
    f.seek(0xEF100)
    location_name_bin = f.read(0x4ff)

    f.seek(0x268400)
    for idx in range(0x49):
        location_name_ptrs[idx] = read_multi(f, 2)

    for idx in range(0x49):
        start = location_name_ptrs[idx]
        end = location_name_ptrs.get(idx + 1,
                                     location_name_bin.find(b'\0', start) + 1)
        location_names[idx] = bytes_to_dialogue(location_name_bin[start:end])
示例#33
0
    def read_data(self, filename):
        f = open(filename, 'r+b')
        f.seek(self.pointer)

        self.name_id = ord(f.read(1))
        self.layers_to_animate = ord(f.read(1))
        self._battlebg = ord(f.read(1))
        self.unknown0 = ord(f.read(1))
        self.tileproperties = ord(f.read(1))  # mult by 2
        self.attacks = ord(f.read(1))
        self.unknown1 = ord(f.read(1))
        self.graphic_sets = map(ord, f.read(4))
        self.tileformations = read_multi(f, length=2, reverse=True)
        self.mapdata = read_multi(f, length=4)
        self.unknown2 = ord(f.read(1))
        self.bgshift = map(ord, f.read(4))
        self.unknown3 = ord(f.read(1))
        self.layer12dimensions = ord(f.read(1))
        self.unknown4 = ord(f.read(1))
        self.palette_index = read_multi(f, length=3)
        self.music = ord(f.read(1))
        self.unknown5 = ord(f.read(1))
        self.width = ord(f.read(1))
        self.height = ord(f.read(1))
        self.layerpriorities = ord(f.read(1))
        assert f.tell() == self.pointer + 0x21

        f.seek(0xf5600 + self.locid)
        self.setid = ord(f.read(1))

        f.close()
        self.entrance_set.read_data(filename)
        self.backup_entrances()
        self.read_chests(filename)
        self.read_npcs(filename)
        self.read_events(filename)
示例#34
0
    def read_data(self, filename):
        f = open(filename, 'r+b')
        f.seek(self.pointer)

        self.name_id = ord(f.read(1))
        self.layers_to_animate = ord(f.read(1))
        self._battlebg = ord(f.read(1))
        self.unknown0 = ord(f.read(1))
        self.tileproperties = ord(f.read(1))  # mult by 2
        self.attacks = ord(f.read(1))
        self.unknown1 = ord(f.read(1))
        self.graphic_sets = list(f.read(4))
        self.tileformations = read_multi(f, length=2, reverse=True)
        self.mapdata = read_multi(f, length=4)
        self.unknown2 = ord(f.read(1))
        self.bgshift = list(f.read(4))
        self.unknown3 = ord(f.read(1))
        self.layer12dimensions = ord(f.read(1))
        self.unknown4 = ord(f.read(1))
        self.palette_index = read_multi(f, length=3)
        self.music = ord(f.read(1))
        self.unknown5 = ord(f.read(1))
        self.width = ord(f.read(1))
        self.height = ord(f.read(1))
        self.layerpriorities = ord(f.read(1))
        assert f.tell() == self.pointer + 0x21

        f.seek(0xf5600 + self.locid)
        self.setid = ord(f.read(1))

        f.close()
        self.entrance_set.read_data(filename)
        self.backup_entrances()
        self.read_chests(filename)
        self.read_npcs(filename)
        self.read_events(filename)
示例#35
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     value = read_multi(f, length=4)
     self.palette = (value & 0x1C0000) >> 18
     self.unknown = (value & 0x200000) >> 21
     self.membit = (value & 0x1C00000) >> 22
     self.memaddr = (value & 0xFE000000) >> 25
     self.event_addr = value & 0x3FFFF
     self.x = ord(f.read(1))
     self.y = ord(f.read(1))
     self.graphics = ord(f.read(1))
     self.graphics_index = ord(f.read(1))
     self.facing = ord(f.read(1))
     f.close()
示例#36
0
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     value = read_multi(f, length=4)
     self.palette = (value & 0x1C0000) >> 18
     self.unknown = (value & 0x200000) >> 21
     self.membit = (value & 0x1C00000) >> 22
     self.memaddr = (value & 0xFE000000) >> 25
     self.event_addr = value & 0x3FFFF
     self.x = ord(f.read(1))
     self.y = ord(f.read(1))
     self.graphics = ord(f.read(1))
     self.graphics_index = ord(f.read(1))
     self.facing = ord(f.read(1))
     f.close()
示例#37
0
    def read_data(self, filename):
        global extra_miabs

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        self.position = read_multi(f, length=2)
        self.memid = ord(f.read(1))
        self.content_type = ord(f.read(1))
        self.contents = ord(f.read(1))
        f.close()
        self.oldid = self.memid | ((self.content_type & 1) << 8)

        mark_taken_id(self.effective_id)
        if self.monster:
            add_extra_miab(self.contents)
示例#38
0
    def read_data(self, filename):
        global extra_miabs

        f = open(filename, 'r+b')
        f.seek(self.pointer)
        self.position = read_multi(f, length=2)
        self.memid = ord(f.read(1))
        self.contenttype = ord(f.read(1))
        self.contents = ord(f.read(1))
        f.close()
        self.oldid = self.memid | ((self.contenttype & 1) << 8)

        mark_taken_id(self.effective_id)
        if self.monster:
            add_extra_miab(self.contents)
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.pointer)
     self.formids = []
     if self.setid <= 0xFF:
         num_encounters = 4
     else:
         num_encounters = 2
     for _ in range(num_encounters):
         self.formids.append(read_multi(f, length=2))
     if any([f & 0x8000 for f in self.formids]):
         assert all([f & 0x8000 for f in self.formids])
         self.sixteen_pack = True
     else:
         self.sixteen_pack = False
     f.close()
 def read_data(self, filename):
     f = open(filename, 'r+b')
     f.seek(self.ptrptr)
     pointer = read_multi(f, length=3)
     # 84d1f2 -> 251f2
     assert pointer & 0xFF8000 == 0x848000
     pointer = (pointer & 0x7FFF) | 0x20000
     self.pointer = pointer
     f.seek(self.pointer)
     num_objects = ord(f.read(1))
     f.close()
     self.levelobjects = []
     for i in xrange(num_objects):
         pointer = self.pointer + 1 + (6 * i)
         l = LevelObject(index=i, pointer=pointer)
         l.read_data(filename)
         self.levelobjects.append(l)
示例#41
0
def manage_dialogue_patches(fout):
    global script_bin

    #don't do anything unless we need to
    if not dialogue_patches and not dialogue_patches_battle:
        return

    #load existing script & pointer table
    fout.seek(0xD0000)
    script_bin = fout.read(0x1F0FF)
    script = {}
    #TODO battle script

    fout.seek(0xCE600)
    bankidx = read_multi(fout, 2)
    for idx in range(0xC0C):  #C0D through CFF pointers are repurposed
        script_ptrs[idx] = read_multi(fout,
                                      2) + (0x10000 if idx > bankidx else 0)
        script[idx] = read_script(idx)

    #TODO battle pointers

    #print(f"original script size is ${len(script_bin):X} bytes")

    #apply changes to dialogue
    for idx, patches in dialogue_patches.items():
        line = split_line(script[idx])

        #print(f"patching line {idx}")
        #print(f"  original: {script[idx]}")
        token_counter = {}
        for i, token in enumerate(line):
            if token.lower() not in token_counter:
                token_counter[token.lower()] = 1
            else:
                token_counter[token.lower()] += 1
            if (token.lower(), token_counter[token.lower()]) in patches:
                line[i] = patch(patches[token.lower(),
                                        token_counter[token.lower()]])
            elif (token.lower(), None) in patches:
                line[i] = patch(patches[token.lower(), None])
            #handle removing text along with following space
            if line[i] is None:
                line[i] = ""
                try:
                    if line[i + 1][0] == " ":
                        line[i +
                             1] = "" if len(line[i + 1]) < 2 else line[i +
                                                                       1][1:]
                except IndexError:
                    pass
        new_text = "".join(line)
        #print(f"  new: {new_text}")
        script[idx] = new_text

    new_script = b""
    new_ptrs = b""
    offset = 0
    first_high_index = None
    for idx, text in script.items():
        ####TODO!!! rewrite to only re-encode dialogue if it is changed?
        lastlength = len(new_script) - offset
        if first_high_index: lastlength -= 0x10000
        offset += lastlength
        if offset > 0xFFFF:
            if offset > 0x1FFFF or first_high_index is not None:
                print(f"script addressing overflow at index {idx}")
                raise IndexError
            offset -= 0x10000
            first_high_index = idx
            #print(f"first high index at {first_high_index}")
        new_script += dialogue_to_bytes(text)
        new_ptrs += bytes([offset & 0xFF, (offset >> 8) & 0xFF])
    #print(f"new script: ${len(new_script):X} bytes")

    #write to file
    fout.seek(0xD0000)
    assert len(new_script) <= 0x1F0FF
    fout.write(new_script)

    fout.seek(0xCE600)
    write_multi(fout, first_high_index)
    assert len(new_ptrs) <= 0x19FE
    fout.write(new_ptrs)
示例#42
0
def manage_ancient(options_, fout, sourcefile, form_music_overrides={}):
    change_battle_commands = [41, 42, 43]
    if not options_.shuffle_commands:
        alrs = AutoLearnRageSub(require_gau=True)
        alrs.set_location(0x23b73)
        alrs.write(fout)

        enable_morph_sub = Substitution()
        enable_morph_sub.bytestring = bytes([0xEA] * 2)
        enable_morph_sub.set_location(0x25410)
        enable_morph_sub.write(fout)

        enable_mpoint_sub = Substitution()
        enable_mpoint_sub.bytestring = bytes([0xEA] * 2)
        enable_mpoint_sub.set_location(0x25E38)
        enable_mpoint_sub.write(fout)

        change_battle_commands += list(range(18, 28))

    moogle_commands = [
        0x03, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x10,
        0x12, 0x13, 0x16, 0x18, 0x1a, 0x1b, 0x1d
    ]
    for i in change_battle_commands:
        commands = random.sample(moogle_commands, 2)
        c = get_character(i)
        c.battle_commands = [0x00, commands[0], commands[1], 0x01]
        c.write_battle_commands(fout)

    for i in [32, 33]:
        c = get_character(i)
        c.battle_commands = [0x00, 0x1D, 0xFF, 0x01]
        c.write_battle_commands(fout)

    characters = get_characters()
    gau = [c for c in characters if c.id == 11][0]
    if not options_.replace_commands and gau.battle_commands[1] in [
            0x11, None
    ]:
        gau.battle_commands[1] = 0xFF
        gau.write_battle_commands(fout)

    to_dummy = [get_item(0xF6), get_item(0xF7)]
    dummy_names = ["Pebble", "Tissue"]
    for dummy_name, item in zip(dummy_names, to_dummy):
        name = bytes([0xFF]) + name_to_bytes(dummy_name, 12)
        item.dataname = name
        item.price = 4
        item.itemtype = 6
        item.write_stats(fout)
    blank_sub = Substitution()
    blank_sub.set_location(0x2D76C1)
    blank_sub.bytestring = bytearray([0xFF] * (0x2D76F5 - blank_sub.location))
    blank_sub.bytestring[blank_sub.size // 2] = 0
    blank_sub.write(fout)

    goddess_save_sub = Substitution()
    goddess_save_sub.bytestring = bytes([0xFD, 0xFD])
    goddess_save_sub.set_location(0xC170A)
    goddess_save_sub.write(fout)
    goddess_save_sub.set_location(0xC1743)
    goddess_save_sub.write(fout)
    goddess_save_sub.set_location(0xC1866)
    goddess_save_sub.write(fout)

    # decrease exp needed for level up
    if options_.is_code_active('racecave'):
        maxlevel = 49
        divisor = 12.0
    elif options_.is_code_active('speedcave'):
        maxlevel = 49
        divisor = 8.0
    else:
        maxlevel = 49
        divisor = 2.0

    for level in range(maxlevel):
        ratio = (float(level) / maxlevel)**2
        ratio = min(ratio, 1.0)
        xptr = 0x2d8220 + (level * 2)
        fout.seek(xptr)
        exp = read_multi(fout, length=2)
        newexp = (exp / divisor)
        remaining = exp - newexp
        newexp = int(round(newexp + (ratio * remaining)))
        newexp = max(newexp, 1)
        fout.seek(xptr)
        write_multi(fout, newexp, length=2)

    startsub = Substitution()
    startsub.bytestring = bytearray([
        0xD7,
        0xF3,  # remove Daryl
        0xD5,
        0xF0,  # remove Terra from party
        0xD5,
        0xE0,  # remove Terra from party
        0xDC,
        0x7E,  # fix ending? $1F4F bit 6
        0xB8,
        0x43,  # show magic points after battle
        0x3F,
        0x0E,
        0x00,
        0x3F,
        0x0F,
        0x00,
    ])
    if options_.is_code_active('racecave'):
        num_starting = 9 + random.randint(0, 2) + random.randint(0, 1)
    elif options_.is_code_active('speedcave'):
        num_starting = 4 + random.randint(0, 3) + random.randint(0, 2)
    else:
        num_starting = 4 + random.randint(0, 1) + random.randint(0, 1)
    starting = random.sample(list(range(14)), num_starting)
    for c in starting:
        startsub.bytestring += bytearray([0xD4, 0xF0 | c])
        startsub.bytestring += bytearray([0xD4, 0xE0 | c])

    for c in characters:
        i = c.id
        cptr = 0x2d7ca0 + 0x15 + (i * 22)
        fout.flush()
        fout.seek(cptr)
        level = ord(fout.read(1))
        level &= 0xF3
        if i >= 14 or options_.is_code_active(
                "speedcave") and i not in starting:
            level |= 0b1000
        fout.seek(cptr)
        fout.write(bytes([level]))
    fout.seek(0xa5e74)
    fout.write(b'\x00')  # remove Terra's magitek

    tempcands = [
        14, 15,
        random.choice(list(range(18, 28))),
        random.choice([32, 33])
    ]
    if options_.is_code_active('speedcave'):
        tempcands.append(random.choice([16, 17]))
        tempcands.append(random.choice([41, 42, 43]))
    charcands = list(range(14)) + random.sample(tempcands, 2)
    chargraphics = {
        14: 0x11,
        15: 0x10,
        16: 0x14,
        17: 0x14,
        32: 0xE,
        33: 0xE,
        41: 0x15,
        42: 0x15,
        43: 0x15
    }
    for c in range(14):
        chargraphics[c] = c
    for c in range(18, 28):
        chargraphics[c] = 0xA
    for n, i in enumerate(charcands):
        c = [x for x in characters if x.id == i][0]
        if i in chargraphics:
            g = chargraphics[i]
        else:
            g = i
        startsub.bytestring.extend(
            [0x7F, n, i, 0x37, n, g, 0x43, n, c.palette, 0x40, n, i])
        c.slotid = n

    runaway = random.choice([
        c for c in characters if hasattr(c, "slotid") and c.id == c.slotid
    ]).slotid
    if runaway in starting:
        byte, bit = runaway // 8, runaway % 8
        mem_addr = ((0x1b + byte) << 3) | bit
        startsub.bytestring += bytearray([0xD7, mem_addr])
    shadow_leaving_sub = Substitution()
    shadow_leaving_sub.set_location(0x248A6)
    shadow_leaving_sub.bytestring = bytearray([
        0x1C,
        0xDE + (runaway // 8),
        0x1E,  # TRB $1ede
        0x20,
        0xE3,
        0x47,
        0xAD,
        0xFB + (runaway // 8),
        0x1E,  # LDA $1efb
        0x09,
        1 << (runaway % 8),  # ORA #$08
        0x8D,
        0xFB + (runaway // 8),
        0x1E,  # STA $1efb
        0xAD,
        0xDE + (runaway // 8),
        0x1E,  # LDA $1ede
        0x29,
        0xFF ^ (1 << (runaway % 8)),  # AND #$F7
        0x8D,
        0xDE + (runaway // 8),
        0x1E,  # STA $1ede
    ])
    while len(shadow_leaving_sub.bytestring) < 23:
        shadow_leaving_sub.bytestring.append(0xEA)
    shadow_leaving_sub.bytestring += bytearray([0xA9, 0xFE, 0x20, 0x92, 0x07])
    shadow_leaving_sub.write(fout)
    shadow_leaving_sub.set_location(0x24861)
    shadow_leaving_sub.bytestring = bytearray([
        0xAE,
        runaway,
        0x30,
        0x30,
        0x26,
        0x20,
        0x5A,
        0x4B,
        0xC9,
        random.choice([0x20, 0x10, 0x8, 0x4, 0x2, 0x1]),
        0xB0,
        0x1F,
        0xAD,
        0x1F,
        0x20,
        0xD0,
        0x1A,
        0xAD,
        0x76,
        0x3A,
        0xC9,
        0x02,
        0x90,
        0x13,
        0xBD,
        0xE4,
        0x3E,
        0x89,
        0xC2,
        0xD0,
        0x0C,
        0xA9,
        1 << (runaway % 8),
        0x2C,
        0xBD + (runaway // 8),
        0x3E,
        0xD0,
        0x05,
        0x2C,
        0xDE + (runaway // 8),
        0x1E,
    ])
    shadow_leaving_sub.write(fout)
    shadow_leaving_sub.set_location(0x10A851)
    shadow_leaving_sub.bytestring = bytearray([
        0x0E,
        0x03,
        runaway,
        0x6A,
        0xA8,
        0x0F,
        0x11,
        0x01,
        0xFB,
        0x0E,
        0x03,
        runaway,
        0x7E,
        0xA8,
        0x0F,
        0x01,
        0xFC,
        0x0E,
        0x03,
        runaway,
        0x92,
        0xA8,
        0x0F,
        0x10,
        0xFF,
    ])
    shadow_leaving_sub.write(fout)
    shadow_leaving_sub.bytestring = bytearray([runaway])
    shadow_leaving_sub.set_location(0x10FC2F)
    shadow_leaving_sub.write(fout)
    shadow_leaving_sub.set_location(0x10FC5D)
    shadow_leaving_sub.write(fout)

    esperevents = [
        "Ramuh", "Ifrit", "Shiva", "Siren", "Terrato", "Shoat", "Maduin",
        "Bismark", "Stray", "Palidor", "Tritoch", "Odin", "Raiden", "Bahamut",
        "Alexandr", "Crusader", "Ragnarok", "Kirin", "ZoneSeek", "Carbunkl",
        "Phantom", "Sraphim", "Golem", "Unicorn", "Fenrir", "Starlet",
        "Phoenix"
    ]
    esperevents = dict([(n, i) for (i, n) in enumerate(esperevents)])
    espers = list(get_espers(sourcefile))
    num_espers = 3
    for i in range(num_espers):
        if options_.is_code_active("speedcave"):
            esperrank = 999
        else:
            esperrank = 0
            while random.randint(1, 3) == 3:
                esperrank += 1
        candidates = [e for e in espers if e.rank <= esperrank]
        esper = random.choice(candidates)
        espers.remove(esper)
        event_value = esperevents[esper.name] + 0x36
        startsub.bytestring += bytearray([0x86, event_value])
    for i in range(27):  # espers
        byte, bit = i // 8, i % 8
        mem_addr = ((0x17 + byte) << 3) | bit
        startsub.bytestring += bytearray([0xD6, mem_addr])
    for i in range(16):  # characters
        if i in starting:
            continue
        byte, bit = i // 8, i % 8
        mem_addr = ((0x1b + byte) << 3) | bit
        startsub.bytestring += bytearray([0xD6, mem_addr])
    startsub.bytestring += bytearray([
        0xB2,
        0x09,
        0x21,
        0x02,  # start on airship
    ])
    startsub.bytestring.append(0xFE)
    startsub.set_location(0xADD1E)
    startsub.write(fout)

    startsub0 = Substitution()
    startsub0.bytestring = bytearray([0xB2, 0x1E, 0xDD, 0x00, 0xFE])
    startsub0.set_location(0xC9A4F)
    startsub0.write(fout)

    set_airship_sub = Substitution()
    set_airship_sub.bytestring = bytearray([0xB2, 0xD6, 0x02, 0x00, 0xFE])
    set_airship_sub.set_location(0xAF53A)  # need first branch for button press
    set_airship_sub.write(fout)

    tower_msg_sub = Substitution()
    tower_msg_sub.bytestring = bytearray([0xD6, 0xE6, 0xD6,
                                          0xE7])  # reset temp chars
    while len(tower_msg_sub.bytestring) < 12:
        tower_msg_sub.bytestring.append(0xFD)
    tower_msg_sub.set_location(0xA03A7)
    tower_msg_sub.write(fout)

    from locationrandomizer import NPCBlock, EventBlock
    falcon = get_location(0xb)
    save_point = NPCBlock(pointer=None, locid=falcon.locid)
    attributes = {
        "graphics": 0x6f,
        "palette": 6,
        "x": 20,
        "y": 8,
        "show_on_vehicle": False,
        "speed": 0,
        "event_addr": 0x5eb3,
        "facing": 3,
        "no_turn_when_speaking": False,
        "layer_priority": 0,
        "special_anim": 2,
        "memaddr": 0,
        "membit": 0,
        "bg2_scroll": 0,
        "move_type": 0,
        "sprite_priority": 1,
        "vehicle": 0
    }
    for key, value in attributes.items():
        setattr(save_point, key, value)
    save_point.set_id(len(falcon.npcs))
    falcon.npcs.append(save_point)
    save_event = EventBlock(pointer=None, locid=falcon.locid)
    attributes = {"event_addr": 0x29aeb, "x": 20, "y": 8}
    for key, value in attributes.items():
        setattr(save_event, key, value)
    falcon.events.append(save_event)
    partyswitch = NPCBlock(pointer=None, locid=falcon.locid)
    attributes = {
        "graphics": 0x17,
        "palette": 0,
        "x": 16,
        "y": 6,
        "show_on_vehicle": False,
        "speed": 0,
        "event_addr": 0x047d,
        "facing": 2,
        "no_turn_when_speaking": False,
        "layer_priority": 0,
        "special_anim": 0,
        "memaddr": 0,
        "membit": 0,
        "bg2_scroll": 0,
        "move_type": 0,
        "sprite_priority": 0,
        "vehicle": 0,
        "npcid": 2
    }
    for key, value in attributes.items():
        setattr(partyswitch, key, value)
    falcon.npcs.append(partyswitch)

    pilot = random.choice([s for s in starting if s < 12])
    pilot_sub = Substitution()
    pilot_sub.bytestring = bytearray([0x3D, pilot, 0x45, 0x3F, pilot, 0x01])
    for i in range(14):
        if i == pilot:
            continue
        pilot_sub.bytestring += bytearray([0x3F, i, 0x00])
    pilot_sub.set_location(0xC2110)
    pilot_sub.write(fout)

    if options_.is_code_active("racecave"):
        randomize_tower(filename=sourcefile, ancient=True, nummaps=50)
    elif options_.is_code_active("speedcave"):
        randomize_tower(filename=sourcefile, ancient=True, nummaps=85)
    else:
        randomize_tower(filename=sourcefile, ancient=True, nummaps=300)
    manage_map_names(fout)

    unused_enemies = [u for u in get_monsters() if u.id in REPLACE_ENEMIES]

    def safe_boss_validator(formation):
        if formation.is_fanatics:
            return False
        if set(formation.present_enemies) & set(unused_enemies):
            return False
        if formation.formid in NOREPLACE_FORMATIONS:
            return False
        if not (any([m.boss_death for m in formation.present_enemies])
                or formation.get_music() in [1, 2, 5]):
            return False
        if formation.get_music() == 0:
            return False
        if formation.formid in [0x1b0, 0x1b3, 0x1d9, 0x1db, 0x1d7]:
            return False
        if formation.formid in [
                0x1a4, 0x1d4, 0x1d5, 0x1d6, 0x1e4, 0x1e2, 0x1ff, 0x1bd, 0x1be
        ]:
            return False
        if (options_.is_code_active("racecave")
                and formation.formid in [0x162, 0x1c8, 0x1d3]):
            return False
        return True

    def challenge_battle_validator(formation):
        if len(formation.present_enemies) == 0:
            return False
        if set(formation.present_enemies) & set(unused_enemies):
            return False
        if formation.formid in NOREPLACE_FORMATIONS:
            return False
        if formation.battle_event:
            return False
        if formation.formid in [
                0x1a4, 0x1ff, 0x1bd, 0x1d7, 0x200, 0x201, 0x23f
        ]:
            return False
        if formation.get_music() == 0:
            if any([
                    f for f in formations
                    if f.formid != formation.formid and set(f.enemy_ids) ==
                    set(formation.enemy_ids) and f.get_music() != 0
            ]):
                return False
        best_drop = formation.get_best_drop()
        if best_drop and (best_drop.price <= 2 or best_drop.price >= 30000
                          or options_.is_code_active("madworld")):
            return True
        return False

    formations = sorted(get_formations(), key=lambda f: f.rank())
    enemy_formations = [
        f for f in formations if f.is_fanatics or (
            f.present_enemies and not f.has_event and not f.has_boss)
    ]
    enemy_formations = [
        f for f in enemy_formations
        if f.formid not in REPLACE_FORMATIONS + NOREPLACE_FORMATIONS
    ]
    boss_formations = [f for f in formations if safe_boss_validator(f)]
    used_formations = []

    challenges = sorted(
        [f for f in formations if challenge_battle_validator(f)],
        key=lambda f: f.get_best_drop().rank())[-48:]
    challenges = sorted(random.sample(challenges, 24), key=lambda f: f.rank())
    challenges = [f.formid for f in challenges]
    challenges = {
        1: challenges[:6],
        2: challenges[6:12],
        3: challenges[12:18],
        4: challenges[18:24]
    }
    ch_bgs = list(range(0x31)) + [0x36, 0x37]
    waters = [0xD, 0x1F, 0x23]
    snows = [0x12]
    ch_bgs = random.sample(ch_bgs, 10) + [random.choice(waters), snows[0]]
    random.shuffle(ch_bgs)

    for l in get_locations():
        if not hasattr(l, "ancient_rank"):
            l.entrance_set.entrances = []
            l.entrance_set.longentrances = []
            l.chests = []
            l.attacks = 0
            l.write_data(fout)

    pointer = 0xB4E35
    if options_.is_code_active('racecave'):
        candidates = [c for c in starting if c != runaway]
        leaders = random.sample(candidates, 3)
        subptr = pointer - 0xa0000
        leader_sub = Substitution()

        # makes switching impossible and makes row change instant
        # could freeze the game d+pad and A on same frame tho
        leader_sub.set_location(0x324b7)
        leader_sub.bytestring = bytes([0xEA, 0xEA, 0xEA])
        leader_sub.write(fout)
        leader_sub.set_location(0x32473)
        leader_sub.bytestring = bytes([0xEA, 0xEA])
        leader_sub.write(fout)

        leader_sub.set_location(0xa02da)
        leader_sub.bytestring = bytes(
            [0xB2, subptr & 0xFF, (subptr >> 8) & 0xFF, subptr >> 16])
        leader_sub.write(fout)
        leader_sub.set_location(pointer)
        leader_sub.bytestring = bytearray([])
        locked = 0
        for i, c in enumerate(leaders):
            leader_sub.bytestring += bytearray([0x3F, c, i + 1])
            locked |= (1 << c)
        for c in range(16):
            if c in leaders:
                continue
            leader_sub.bytestring += bytearray([0x3F, c, 0x00])
            leader_sub.bytestring += bytearray([0x3E, c])
        leader_sub.bytestring += bytearray(
            [0x47, 0xE1, 0xB2, 0x0B, 0xC9, 0x00, 0x45])
        for i, c in enumerate(leaders):
            leader_sub.bytestring += bytearray([0x3F, c, 0])
            leader_sub.bytestring += bytearray([0x3F, c, i + 1])
        leader_sub.bytestring += bytearray(
            [0x99, 0x03, locked & 0xFF, locked >> 8])
        for i in [14, 15]:
            byte, bit = i // 8, i % 8
            mem_addr = ((0x1b + byte) << 3) | bit
            leader_sub.bytestring += bytearray([0xD6, mem_addr])
        leader_sub.bytestring += bytearray([0x96, 0xFE])
        leader_sub.write(fout)
        pswitch_ptr = pointer - 0xa0000
        pointer += len(leader_sub.bytestring)

    espersubs = {}
    for esper, event_value in esperevents.items():
        byte, bit = event_value // 8, event_value % 8
        mem_addr = ((0x17 + byte) << 3) | bit
        espersub = Substitution()
        espersub.set_location(pointer)
        espersub.bytestring = [
            0xF4, 0x8D, 0x86, event_value + 0x36, 0xD7, mem_addr, 0x3E, None,
            0xFE
        ]
        espersubs[esper] = espersub
        pointer += espersub.size

    inn_template = [
        0x4B, None, None, 0x4B, 0x11, 0x81, 0xB6, None, None, None, None, None,
        None, 0xFE
    ]
    inn_template2 = [
        0x85, None, None, 0xC0, 0xBE, 0x81, 0xFF, 0x69, 0x01, 0x31, 0x84, 0xC3,
        0x8F, 0x84, 0xFF, 0xF4, 0x2C, 0x73, 0x30, 0x0E, 0x01, 0x02, 0x06, 0x16,
        0x31, 0x86, 0xC3, 0x9C, 0x80, 0x8D, 0xCE, 0xFF, 0xB2, 0x67, 0xCF, 0x00,
        0xF0, 0xB8, 0xFA, 0x31, 0x85, 0xD5, 0x36, 0x05, 0xCE, 0xFF, 0xB2, 0x96,
        0xCF, 0x00, 0xFE
    ]

    prices = {
        1: (500, 0xA6E),
        2: (2000, 0xA71),
        3: (8000, 0xA5F),
        4: (30000, 0xA64)
    }

    if options_.is_code_active("racecave"):
        partyswitch_template = [
            0x4B, None, None, 0x4B, 0x86, 0x83, 0xB6, None, None, None, None,
            None, None, 0xFE
        ]

        partyswitch_template2 = [
            0x85, None, None, 0xC0,
            0xBE, 0x81, 0xFF, 0x69, 0x01, 0xB2, pswitch_ptr & 0xFF,
            (pswitch_ptr >> 8) & 0xFF, pswitch_ptr >> 16, 0xFE
        ]

    save_template = [
        0x4B, None, None, 0x4B, 0x24, 0x85, 0xB6, None, None, None, None, None,
        None, 0xFE
    ]
    save_template2 = [
        0x85, None, None, 0xC0, 0xBE, 0x81, 0xFF, 0x69, 0x01, 0xB2, 0xEB, 0x9A,
        0x02, 0xFE
    ]

    enemy_template = [
        0x4B, 0x0B, 0x07, 0xB6, None, None, None, None, None, None, 0xA0, None,
        None, 0xB3, 0x5E, 0x70, 0x4D, None, None, 0xA1, 0x00, 0x96, 0x5C, 0xFE
    ]

    def make_challenge_event(loc, ptr):
        bg = ch_bgs.pop()
        formids = random.sample(challenges[loc.restrank], 2)
        formations = [get_formation(formid) for formid in formids]
        for formid in formids:
            challenges[loc.restrank].remove(formid)
        setcands = [f for f in get_fsets() if f.setid >= 0x100 and f.unused]
        fset = setcands.pop()
        fset.formids = formids
        fset.write_data(fout)
        timer = max(
            [e.stats['hp'] for f in formations for e in f.present_enemies])
        reverse = False
        if timer >= 32768:
            reverse = True
            timer = 65535 - timer
        timer = max(timer, 3600)
        half = None
        while half is None or random.randint(1, 5) == 5:
            half = timer // 2
            timer = half + random.randint(0, half) + random.randint(0, half)
        if reverse:
            timer = 65535 - timer
        timer = int(round(timer / 1800.0))
        timer = max(2, min(timer, 36))
        timer = timer * 1800
        timer = [timer & 0xFF, timer >> 8]
        addr1 = ptr + 10 - 0xa0000
        addr2 = ptr + (len(enemy_template) - 1) - 0xa0000
        addr1 = [addr1 & 0xFF, (addr1 >> 8) & 0xFF, addr1 >> 16]
        addr2 = [addr2 & 0xFF, (addr2 >> 8) & 0xFF, addr2 >> 16]
        bytestring = list(enemy_template)
        bytestring[4:7] = addr1
        bytestring[7:10] = addr2
        bytestring[11:13] = timer
        bytestring[17] = fset.setid & 0xFF
        bytestring[18] = bg
        assert None not in bytestring
        sub = Substitution()
        sub.set_location(ptr)
        sub.bytestring = bytes(bytestring)
        sub.write(fout)
        return ptr + len(enemy_template)

    shops = get_shops(sourcefile)
    shopranks = {}
    itemshops = [s for s in shops if s.shoptype_pretty in ["items", "misc"]]
    othershops = [s for s in shops if s not in itemshops]
    othershops = othershops[random.randint(0, len(othershops) // 2):]
    itemshops = sorted(random.sample(itemshops, 5), key=lambda p: p.rank())
    othershops = sorted(random.sample(othershops, 7), key=lambda p: p.rank())
    for i in range(1, 5):
        if i > 1:
            shopranks[i] = othershops[:2] + itemshops[:1]
            othershops = othershops[2:]
            itemshops = itemshops[1:]
        else:
            shopranks[i] = othershops[:1] + itemshops[:2]
            othershops = othershops[1:]
            itemshops = itemshops[2:]
        assert len(shopranks[i]) == 3
        random.shuffle(shopranks[i])
    shopranks[random.randint(1, 4)][random.randint(0, 2)] = None

    levelmusic = {}
    dungeonmusics = [23, 24, 33, 35, 55, 71, 40, 41, 75, 77, 78]
    random.shuffle(dungeonmusics)
    for i in range(5):
        levelmusic[i] = dungeonmusics.pop()

    locations = [l for l in get_locations() if hasattr(l, "ancient_rank")]
    locations = sorted(locations, key=lambda l: l.ancient_rank)
    restlocs = [l for l in locations if hasattr(l, "restrank")]
    ban_musics = [0, 36, 56, 57, 58, 73, 74, 75] + list(levelmusic.values())
    restmusics = [m for m in range(1, 85) if m not in ban_musics]
    random.shuffle(restmusics)

    optional_chars = [c for c in characters if hasattr(c, "slotid")]
    optional_chars = [
        c for c in optional_chars
        if c.slotid == runaway or (c.id not in starting and c.id in charcands)
    ]
    if options_.is_code_active("speedcave"):
        while len(optional_chars) < 24:
            if random.choice([True, True, False]):
                supplement = [
                    c for c in optional_chars
                    if c.id >= 14 or c.slotid == runaway
                ]
            else:
                supplement = list(optional_chars)
            supplement = sorted(set(supplement), key=lambda c: c.id)
            optional_chars.append(random.choice(supplement))
    random.shuffle(optional_chars)

    ptr = pointer - 0xA0000
    c0, b0, a0 = ptr & 0xFF, (ptr >> 8) & 0xFF, ptr >> 16
    ptr = (pointer + 10) - 0xA0000
    c1, b1, a1 = ptr & 0xFF, (ptr >> 8) & 0xFF, ptr >> 16
    ptr = (pointer + 20) - 0xA0000
    c2, b2, a2 = ptr & 0xFF, (ptr >> 8) & 0xFF, ptr >> 16
    num_in_party_sub = Substitution()
    num_in_party_sub.set_location(0xAC654)
    num_in_party_sub.bytestring = [0xB2, c0, b0, a0]
    num_in_party_sub.write(fout)
    num_in_party_sub.set_location(pointer)
    num_in_party_sub.bytestring = bytes([
        0xC0, 0xAE, 0x01, c1, b1, a1, 0xB2, 0x80, 0xC6, 0x00, 0xC0, 0xAF, 0x01,
        c2, b2, a2, 0xB2, 0x80, 0xC6, 0x00, 0xD3, 0xA3, 0xD3, 0xA2, 0xFE
    ])
    num_in_party_sub.write(fout)
    pointer += len(num_in_party_sub.bytestring)
    ally_addrs = {}
    for chosen in set(optional_chars):
        byte, bit = chosen.slotid // 8, chosen.slotid % 8
        mem_addr = ((0x1b + byte) << 3) | bit
        allysub = Substitution()
        for party_id in range(1, 4):
            for npc_id in range(4, 6):
                allysub.set_location(pointer)
                allysub.bytestring = [
                    0xB2,
                    0xC1,
                    0xC5,
                    0x00,  # set caseword
                    0xC0,
                    0xA3,
                    0x81,
                    None,
                    None,
                    None
                ]
                allysub.bytestring += [
                    0xD4, 0xF0 | chosen.slotid, 0xD4, 0xE0 | chosen.slotid,
                    0xD7, mem_addr
                ]
                if chosen.id >= 14 or options_.is_code_active("speedcave"):
                    allysub.bytestring += [
                        0x77, chosen.slotid, 0x8b, chosen.slotid, 0x7F, 0x8c,
                        chosen.slotid, 0x7F, 0x88, chosen.slotid, 0x00, 0x00
                    ]
                allysub.bytestring += [
                    0x3E, 0x10 | npc_id, 0x3D, chosen.slotid, 0x3F,
                    chosen.slotid, party_id, 0x47, 0x45, 0xF4, 0xD0, 0xFE
                ]
                pointer = pointer + len(allysub.bytestring)
                uptr = (pointer - 1) - 0xa0000
                a, b, c = (uptr >> 16, (uptr >> 8) & 0xFF, uptr & 0xFF)
                allysub.bytestring[7:10] = [c, b, a]
                allysub.write(fout)
                event_addr = (allysub.location - 0xa0000) & 0x3FFFF
                ally_addrs[chosen.id, party_id, npc_id] = event_addr

    npc_palettes = get_npc_palettes()
    for g in npc_palettes:
        npc_palettes[g] = [v for v in npc_palettes[g] if 0 <= v <= 5]
    for g in range(14, 63):
        if g not in npc_palettes or not npc_palettes[g]:
            npc_palettes[g] = list(range(6))

    def make_paysub(template, template2, loc, ptr):
        sub = Substitution()
        sub.set_location(ptr)
        price, message = prices[loc.restrank]
        message |= 0x8000
        sub.bytestring = list(template)
        ptr += len(template)
        price = [price & 0xFF, price >> 8]
        message = [message & 0xFF, message >> 8]
        p = (ptr - 0xA0000) & 0x3FFFF
        p2 = p - 1
        ptrbytes = [p & 0xFF, (p >> 8) & 0xFF, p >> 16]
        ptrbytes2 = [p2 & 0xFF, (p2 >> 8) & 0xFF, p2 >> 16]
        mapid = [loc.locid & 0xFF, loc.locid >> 8]
        mapid[1] |= 0x23
        sub.bytestring[1:3] = message
        sub.bytestring[7:10] = ptrbytes
        sub.bytestring[10:13] = ptrbytes2
        assert None not in sub.bytestring
        assert len(sub.bytestring) == 14
        sub.bytestring += template2
        ptr += len(template2)
        sub.bytestring[15:17] = price
        assert None not in sub.bytestring
        sub.bytestring = bytes(sub.bytestring)
        sub.write(fout)
        return sub

    random.shuffle(restlocs)
    for l in restlocs:
        assert l.ancient_rank == 0
        l.music = restmusics.pop()
        l.make_warpable()

        innsub = make_paysub(inn_template, inn_template2, l, pointer)
        pointer += innsub.size
        savesub = make_paysub(save_template, save_template2, l, pointer)
        pointer += savesub.size
        if options_.is_code_active('racecave'):
            pswitch_sub = make_paysub(partyswitch_template,
                                      partyswitch_template2, l, pointer)
            pointer += pswitch_sub.size

        event_addr = (innsub.location - 0xa0000) & 0x3FFFF
        innkeeper = NPCBlock(pointer=None, locid=l.locid)
        graphics = random.randint(14, 62)
        palette = random.choice(npc_palettes[graphics])
        attributes = {
            "graphics": graphics,
            "palette": palette,
            "x": 52,
            "y": 16,
            "show_on_vehicle": False,
            "speed": 0,
            "event_addr": event_addr,
            "facing": 2,
            "no_turn_when_speaking": False,
            "layer_priority": 0,
            "special_anim": 0,
            "memaddr": 0,
            "membit": 0,
            "bg2_scroll": 0,
            "move_type": 0,
            "sprite_priority": 0,
            "vehicle": 0
        }
        for key, value in attributes.items():
            setattr(innkeeper, key, value)
        l.npcs.append(innkeeper)

        unequipper = NPCBlock(pointer=None, locid=l.locid)
        attributes = {
            "graphics": 0x1e,
            "palette": 3,
            "x": 49,
            "y": 16,
            "show_on_vehicle": False,
            "speed": 0,
            "event_addr": 0x23510,
            "facing": 2,
            "no_turn_when_speaking": False,
            "layer_priority": 0,
            "special_anim": 0,
            "memaddr": 0,
            "membit": 0,
            "bg2_scroll": 0,
            "move_type": 0,
            "sprite_priority": 0,
            "vehicle": 0
        }
        for key, value in attributes.items():
            setattr(unequipper, key, value)
        l.npcs.append(unequipper)

        event_addr = (savesub.location - 0xa0000) & 0x3FFFF
        pay_to_save = NPCBlock(pointer=None, locid=l.locid)
        attributes = {
            "graphics": 0x6f,
            "palette": 6,
            "x": 47,
            "y": 4,
            "show_on_vehicle": False,
            "speed": 0,
            "event_addr": event_addr,
            "facing": 3,
            "no_turn_when_speaking": False,
            "layer_priority": 0,
            "special_anim": 2,
            "memaddr": 0,
            "membit": 0,
            "bg2_scroll": 0,
            "move_type": 0,
            "sprite_priority": 0,
            "vehicle": 0
        }
        for key, value in attributes.items():
            setattr(pay_to_save, key, value)
        l.npcs.append(pay_to_save)

        if l.restrank == 4:
            final_loc = get_location(412)
            if len(final_loc.npcs) < 2:
                final_save = NPCBlock(pointer=None, locid=l.locid)
                attributes = {
                    "graphics": 0x6f,
                    "palette": 6,
                    "x": 82,
                    "y": 43,
                    "show_on_vehicle": False,
                    "speed": 0,
                    "event_addr": event_addr,
                    "facing": 3,
                    "no_turn_when_speaking": False,
                    "layer_priority": 0,
                    "special_anim": 2,
                    "memaddr": 0,
                    "membit": 0,
                    "bg2_scroll": 0,
                    "move_type": 0,
                    "sprite_priority": 0,
                    "vehicle": 0,
                    "npcid": 1
                }
                for key, value in attributes.items():
                    setattr(final_save, key, value)
                final_loc.npcs.append(final_save)

        shop = shopranks[l.restrank].pop()
        if shop is not None:
            shopsub = Substitution()
            shopsub.set_location(pointer)
            shopsub.bytestring = bytes([0x9B, shop.shopid, 0xFE])
            shopsub.write(fout)
            pointer += len(shopsub.bytestring)
            event_addr = (shopsub.location - 0xa0000) & 0x3FFFF
        else:
            event_addr = 0x178cb
            colsub = Substitution()
            colsub.set_location(0xb78ea)
            colsub.bytestring = bytes([0x59, 0x04, 0x5C, 0xFE])
            colsub.write(fout)
        shopkeeper = NPCBlock(pointer=None, locid=l.locid)
        graphics = random.randint(14, 62)
        palette = random.choice(npc_palettes[graphics])
        attributes = {
            "graphics": graphics,
            "palette": palette,
            "x": 39,
            "y": 11,
            "show_on_vehicle": False,
            "speed": 0,
            "event_addr": event_addr,
            "facing": 1,
            "no_turn_when_speaking": False,
            "layer_priority": 0,
            "special_anim": 0,
            "memaddr": 0,
            "membit": 0,
            "bg2_scroll": 0,
            "move_type": 0,
            "sprite_priority": 0,
            "vehicle": 0
        }
        for key, value in attributes.items():
            setattr(shopkeeper, key, value)
        l.npcs.append(shopkeeper)

        if optional_chars:
            chosen = optional_chars.pop()
            assert chosen.palette is not None
            if chosen.id >= 14 and False:
                byte, bit = 0, 0
            else:
                byte, bit = (chosen.slotid // 8) + 0x1b, chosen.slotid % 8
            event_addr = ally_addrs[chosen.id, l.party_id, len(l.npcs)]
            ally = NPCBlock(pointer=None, locid=l.locid)
            attributes = {
                "graphics": chargraphics[chosen.id],
                "palette": chosen.palette,
                "x": 54,
                "y": 18,
                "show_on_vehicle": False,
                "speed": 0,
                "event_addr": event_addr,
                "facing": 2,
                "no_turn_when_speaking": False,
                "layer_priority": 0,
                "special_anim": 0,
                "memaddr": byte,
                "membit": bit,
                "bg2_scroll": 0,
                "move_type": 0,
                "sprite_priority": 0,
                "vehicle": 0
            }
            for key, value in attributes.items():
                setattr(ally, key, value)
            l.npcs.append(ally)
            if (len(optional_chars) == 12
                    or (len(optional_chars) > 0
                        and options_.is_code_active('speedcave'))):
                temp = optional_chars.pop()
                if chosen.id != temp.id:
                    chosen = temp
                    if chosen.id >= 14 and False:
                        byte, bit = 0, 0
                    else:
                        byte, bit = (chosen.slotid //
                                     8) + 0x1b, chosen.slotid % 8
                    event_addr = ally_addrs[chosen.id, l.party_id, len(l.npcs)]
                    attributes = {
                        "graphics": chargraphics[chosen.id],
                        "palette": chosen.palette,
                        "x": 53,
                        "y": 18,
                        "show_on_vehicle": False,
                        "speed": 0,
                        "event_addr": event_addr,
                        "facing": 2,
                        "no_turn_when_speaking": False,
                        "layer_priority": 0,
                        "special_anim": 0,
                        "memaddr": byte,
                        "membit": bit,
                        "bg2_scroll": 0,
                        "move_type": 0,
                        "sprite_priority": 0,
                        "vehicle": 0
                    }
                    ally = NPCBlock(pointer=None, locid=l.locid)
                    for key, value in attributes.items():
                        setattr(ally, key, value)
                    l.npcs.append(ally)

        if l.restrank == 1:
            num_espers = 3
        elif l.restrank in [2, 3]:
            num_espers = 2
        elif l.restrank == 4:
            num_espers = 1
        for i in range(num_espers):
            if len(espers) == 0:
                break
            if options_.is_code_active('speedcave'):
                candidates = espers
            else:
                esperrank = l.restrank
                if random.randint(1, 7) == 7:
                    esperrank += 1
                candidates = []
                while not candidates:
                    candidates = [e for e in espers if e.rank == esperrank]
                    if not candidates or random.randint(1, 3) == 3:
                        candidates = [e for e in espers if e.rank <= esperrank]
                    if not candidates:
                        esperrank += 1
            esper = random.choice(candidates)
            espers.remove(esper)
            espersub = espersubs[esper.name]
            index = espersub.bytestring.index(None)
            espersub.bytestring[index] = 0x10 | len(l.npcs)
            espersub.write(fout)
            event_addr = (espersub.location - 0xa0000) & 0x3FFFF
            event_value = esperevents[esper.name]
            byte, bit = event_value // 8, event_value % 8
            magicite = NPCBlock(pointer=None, locid=l.locid)
            attributes = {
                "graphics": 0x5B,
                "palette": 2,
                "x": 44 + i,
                "y": 16,
                "show_on_vehicle": False,
                "speed": 0,
                "event_addr": event_addr,
                "facing": 0,
                "no_turn_when_speaking": True,
                "layer_priority": 2,
                "special_anim": 2,
                "memaddr": byte + 0x17,
                "membit": bit,
                "bg2_scroll": 0,
                "move_type": 0,
                "sprite_priority": 0,
                "vehicle": 0
            }
            for key, value in attributes.items():
                setattr(magicite, key, value)
            l.npcs.append(magicite)

        event_addr = pointer - 0xa0000
        pointer = make_challenge_event(l, pointer)
        enemy = NPCBlock(pointer=None, locid=l.locid)
        attributes = {
            "graphics": 0x3e,
            "palette": 2,
            "x": 42,
            "y": 6,
            "show_on_vehicle": False,
            "speed": 0,
            "event_addr": event_addr,
            "facing": 2,
            "no_turn_when_speaking": False,
            "layer_priority": 0,
            "special_anim": 0,
            "memaddr": 0,
            "membit": 0,
            "bg2_scroll": 0,
            "move_type": 0,
            "sprite_priority": 0,
            "vehicle": 0
        }
        for key, value in attributes.items():
            setattr(enemy, key, value)
        l.npcs.append(enemy)

        if options_.is_code_active('racecave'):
            event_addr = (pswitch_sub.location - 0xa0000) & 0x3FFFF
            partyswitch = NPCBlock(pointer=None, locid=l.locid)
            attributes = {
                "graphics": 0x17,
                "palette": 0,
                "x": 55,
                "y": 16,
                "show_on_vehicle": False,
                "speed": 0,
                "event_addr": event_addr,
                "facing": 2,
                "no_turn_when_speaking": False,
                "layer_priority": 0,
                "special_anim": 0,
                "memaddr": 0,
                "membit": 0,
                "bg2_scroll": 0,
                "move_type": 0,
                "sprite_priority": 0,
                "vehicle": 0
            }
            for key, value in attributes.items():
                setattr(partyswitch, key, value)
            l.npcs.append(partyswitch)

    assert len(optional_chars) == 0

    if pointer >= 0xb6965:
        raise Exception("Cave events out of bounds. %x" % pointer)

    # lower encounter rate
    dungeon_rates = [
        0x38, 0, 0x20, 0, 0xb0, 0, 0x00, 1, 0x1c, 0, 0x10, 0, 0x58, 0, 0x80, 0
    ] + ([0] * 16)
    assert len(dungeon_rates) == 32
    encrate_sub = Substitution()
    encrate_sub.set_location(0xC2BF)
    encrate_sub.bytestring = bytes(dungeon_rates)
    encrate_sub.write(fout)

    maxrank = max(locations, key=lambda l: l.ancient_rank).ancient_rank
    for l in locations:
        if l not in restlocs and (l.npcs or l.events):
            for n in l.npcs:
                if n == final_save:
                    continue
                if n.graphics == 0x6F:
                    n.memaddr, n.membit, n.event_addr = 0x73, 1, 0x5EB3
                    success = False
                    for e in l.events:
                        if e.x % 128 == n.x % 128 and e.y % 128 == n.y % 128:
                            if success:
                                raise Exception("Duplicate events found.")
                            e.event_addr = 0x5EB3
                            success = True
                    if not success:
                        raise Exception("No corresponding event found.")
        for e in l.entrances:
            e.dest |= 0x800
        rank = l.ancient_rank
        l.name_id = min(rank, 0xFF)

        if not hasattr(l, "restrank"):
            if hasattr(l, "secret_treasure") and l.secret_treasure:
                pass
            elif l.locid == 334 or not hasattr(l, "routerank"):
                l.music = 58
            elif l.routerank in levelmusic:
                l.music = levelmusic[l.routerank]
            else:
                raise Exception

        l.setid = rank
        if rank == 0:
            l.attacks = 0
        elif rank > 0xFF:
            l.setid = random.randint(0xF0, 0xFF)
        else:

            def enrank(r):
                mr = min(maxrank, 0xFF)
                r = max(0, min(r, mr))
                if options_.is_code_active('racecave'):
                    half = r // 2
                    quarter = half // 2
                    r = (half + random.randint(0, quarter) +
                         random.randint(0, quarter))
                if r <= 0:
                    return 0
                elif r >= mr:
                    return 1.0
                ratio = float(r) / mr
                return ratio

            low = enrank(rank - 3)
            high = enrank(rank + 2)
            high = int(round(high * len(enemy_formations)))
            low = int(round(low * len(enemy_formations)))
            while high - low < 4:
                high = min(high + 1, len(enemy_formations))
                low = max(low - 1, 0)
            candidates = enemy_formations[low:high]
            chosen_enemies = random.sample(candidates, 4)

            chosen_enemies = sorted(chosen_enemies, key=lambda f: f.rank())

            if options_.is_code_active('racecave'):
                bossify = False
            elif rank >= maxrank * 0.9:
                bossify = True
            else:
                if options_.is_code_active('speedcave'):
                    thresh = 0.5
                else:
                    thresh = 0.1
                bossify = rank >= random.randint(int(maxrank * thresh),
                                                 int(maxrank * 0.9))
                bossify = bossify and random.randint(1, 3) == 3
            if bossify:
                formrank = chosen_enemies[0].rank()
                candidates = [
                    c for c in boss_formations if c.rank() >= formrank
                ]
                if candidates:
                    if rank < maxrank * 0.75:
                        candidates = candidates[:random.randint(2, 4)]
                    chosen_boss = random.choice(candidates)
                    chosen_enemies[3] = chosen_boss

            if options_.is_code_active('speedcave'):
                thresh, bossthresh = 2, 1
            else:
                # allow up to three of the same formation
                thresh, bossthresh = 3, 2
            for c in chosen_enemies:
                used_formations.append(c)
                if used_formations.count(c) >= bossthresh:
                    if c in boss_formations:
                        boss_formations.remove(c)
                    if used_formations.count(c) >= thresh:
                        if c in enemy_formations:
                            enemy_formations.remove(c)

            fset = get_fset(rank)
            fset.formids = [f.formid for f in chosen_enemies]
            for formation in fset.formations:
                if formation.get_music() == 0:
                    formation.set_music(6)
                    formation.set_continuous_music()
                    formation.write_data(fout)
            fset.write_data(fout)

        if not (hasattr(l, "secret_treasure") and l.secret_treasure):
            if options_.is_code_active('speedcave') or rank == 0:
                low = random.randint(0, 400)
                high = random.randint(low, low * 5)
                high = random.randint(low, high)
            else:
                low = rank * 2
                high = low * 1.5
                while random.choice([True, False, False]):
                    high = high * 1.5
            if rank < maxrank * 0.4:
                monster = False
            else:
                monster = None
            if 0 < rank < maxrank * 0.75:
                enemy_limit = sorted([f.rank() for f in fset.formations])[-2]
                enemy_limit *= 1.5
            else:
                enemy_limit = None
            l.unlock_chests(int(low),
                            int(high),
                            monster=monster,
                            guarantee_miab_treasure=True,
                            enemy_limit=enemy_limit,
                            uncapped_monsters=options_.is_code_active('bsiab'))

        l.write_data(fout)

    final_cut = Substitution()
    final_cut.set_location(0xA057D)
    final_cut.bytestring = bytearray([
        0x3F,
        0x0E,
        0x00,
        0x3F,
        0x0F,
        0x00,
    ])
    if not options_.is_code_active("racecave"):
        final_cut.bytestring += bytearray(
            [0x9D, 0x4D, 0x65, 0x33, 0xB2, 0xA9, 0x5E, 0x00])
    else:
        for i in range(16):
            final_cut.bytestring += bytearray([0x3F, i, 0x00])
        locked = 0
        protected = random.sample(starting, 4)
        assignments = {0: [], 1: [], 2: [], 3: []}
        for i, c in enumerate(protected):
            if 1 <= i <= 3 and random.choice([True, False]):
                assignments[i].append(c)

        chars = list(range(16))
        random.shuffle(chars)
        for c in chars:
            if c in protected:
                continue
            if c >= 14 and random.choice([True, False]):
                continue
            if random.choice([True, True, False]):
                i = random.randint(0, 3)
                if len(assignments[i]) >= 3:
                    continue
                elif len(assignments[i]) == 2 and random.choice([True, False]):
                    continue
                assignments[i].append(c)

        for key in assignments:
            for c in assignments[key]:
                locked |= (1 << c)
                if key > 0:
                    final_cut.bytestring += bytearray([0x3F, c, key])
        final_cut.bytestring += bytearray(
            [0x99, 0x03, locked & 0xFF, locked >> 8])
        from chestrandomizer import get_2pack
        event_bosses = {
            1: [0xC18A4, 0xC184B],
            2: [0xC16DD, 0xC171D, 0xC1756],
            3: [None, None, None]
        }
        fout.seek(0xA0F6F)
        fout.write(bytes([0x36]))
        candidates = sorted(boss_formations, key=lambda b: b.rank())
        candidates = [c for c in candidates if c.inescapable]
        candidates = candidates[random.randint(0, len(candidates) - 16):]
        chosens = random.sample(candidates, 8)
        chosens = sorted(chosens, key=lambda b: b.rank())
        for rank in sorted(event_bosses):
            num = len(event_bosses[rank])
            rankchosens, chosens = chosens[:num], chosens[num:]
            assert len(rankchosens) == num
            random.shuffle(rankchosens)
            if rank == 3:
                bgs = random.sample([
                    0x07, 0x0D, 0x17, 0x18, 0x19, 0x1C, 0x1F, 0x21, 0x22, 0x23,
                    0x29, 0x2C, 0x30, 0x36, 0x37
                ], 3)
            for i, (address,
                    chosen) in enumerate(zip(event_bosses[rank], rankchosens)):
                if rank == 3:
                    chosen.set_music(5)
                elif rank == 2:
                    chosen.set_music(2)
                else:
                    chosen.set_music(4)
                form_music_overrides[chosen.formid] = chosen.get_music()
                chosen.set_appearing([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13])
                fset = get_2pack(chosen)
                if address is not None:
                    fout.seek(address)
                    fout.write(bytes([fset.setid & 0xFF]))
                else:
                    bg = bgs.pop()
                    final_cut.bytestring += bytearray([
                        0x46, i + 1, 0x4D, fset.setid & 0xFF, bg, 0xB2, 0xA9,
                        0x5E, 0x00
                    ])

        assert len(chosens) == 0

    final_cut.bytestring += bytearray([0xB2, 0x64, 0x13, 0x00])
    final_cut.write(fout)
示例#43
0
def get_table_objects(objtype, filename=None):
    pointer = objtype.specspointer
    number = objtype.specscount
    grouped = objtype.specsgrouped
    pointed = objtype.specspointed
    identifier = (objtype, pointer, number)
    if identifier in already_gotten:
        return already_gotten[identifier]

    if filename is None:
        filename = GLOBAL_OUTPUT
    objects = []

    def add_objects(n, groupindex=0, p=None):
        if p is None:
            p = pointer
        accumulated_size = 0
        for i in xrange(n):
            obj = objtype(filename, p, index=len(objects),
                          groupindex=groupindex)
            objects.append(obj)
            p += obj.total_size
            accumulated_size += obj.total_size
        return accumulated_size

    def add_variable_object(p1, p2):
        size = p2 - p1
        obj = objtype(filename, p1, index=len(objects),
                      groupindex=0, size=size)
        objects.append(obj)
        return size

    if not grouped and not pointed:
        add_objects(number)
    elif grouped:
        counter = 0
        while len(objects) < number:
            if objtype.specs.groupednum is None:
                f = open(filename, 'r+b')
                f.seek(pointer)
                value = ord(f.read(1))
                f.close()
                pointer += 1
            else:
                value = objtype.specs.groupednum
            pointer += add_objects(value, groupindex=counter)
            counter += 1
    elif pointed and objtype.total_size > 0:
        size = objtype.specspointedsize
        counter = 0
        f = open(filename, 'r+b')
        while counter < number:
            f.seek(pointer)
            subpointer = read_multi(f, size) + objtype.specspointedpointer
            f.seek(pointer + size)
            subpointer2 = read_multi(f, size) + objtype.specspointedpointer
            groupcount = (subpointer2 - subpointer) / objtype.total_size
            if objtype.specspointedpoint1:
                groupcount = 1
            add_objects(groupcount, groupindex=counter, p=subpointer)
            pointer += size
            counter += 1
    elif pointed and objtype.total_size == 0:
        size = objtype.specspointedsize
        counter = 0
        f = open(filename, 'r+b')
        while counter < number:
            f.seek(pointer + (size*counter))
            subpointer = read_multi(f, size) + objtype.specspointedpointer
            f.seek(pointer + (size*counter) + size)
            subpointer2 = read_multi(f, size) + objtype.specspointedpointer
            add_variable_object(subpointer, subpointer2)
            counter += 1

    already_gotten[identifier] = objects

    return get_table_objects(objtype, filename=filename)
示例#44
0
def recolor_character_palette(fout,
                              pointer,
                              palette=None,
                              flesh=False,
                              middle=True,
                              santa=False,
                              skintones=None,
                              char_hues=None,
                              trance=False):
    fout.seek(pointer)
    if palette is None:
        palette = [read_multi(fout, length=2) for _ in range(16)]
        outline, eyes, hair, skintone, outfit1, outfit2, NPC = (palette[:2],
                                                                palette[2:4],
                                                                palette[4:6],
                                                                palette[6:8],
                                                                palette[8:10],
                                                                palette[10:12],
                                                                palette[12:])

        def components_to_color(xxx_todo_changeme):
            (red, green, blue) = xxx_todo_changeme
            return red | (green << 5) | (blue << 10)

        new_style_palette = None
        if skintones and char_hues:
            new_style_palette = generate_character_palette(skintones,
                                                           char_hues,
                                                           trance=trance)
            # aliens, available in palette 5 only
            if flesh and random.randint(1, 20) == 1:
                transformer = get_palette_transformer(middle=middle)
                new_style_palette = transformer(new_style_palette)
        elif trance:
            new_style_palette = generate_character_palette(trance=True)

        new_palette = new_style_palette if new_style_palette else []
        if not flesh:
            pieces = (outline, eyes, hair, skintone, outfit1, outfit2,
                      NPC) if not new_style_palette else [NPC]
            for piece in pieces:
                transformer = get_palette_transformer(middle=middle)
                piece = list(piece)
                piece = transformer(piece)
                new_palette += piece

            if not new_style_palette:
                new_palette[6:8] = skintone
            if options_.is_code_active('christmas'):
                if santa:
                    # color kefka's palette to make him look santa-ish
                    new_palette = palette
                    new_palette[8] = components_to_color((0x18, 0x18, 0x16))
                    new_palette[9] = components_to_color((0x16, 0x15, 0x0F))
                    new_palette[10] = components_to_color((0x1C, 0x08, 0x03))
                    new_palette[11] = components_to_color((0x18, 0x02, 0x05))
                else:
                    # give them red & green outfits
                    red = [
                        components_to_color((0x19, 0x00, 0x05)),
                        components_to_color((0x1c, 0x02, 0x04))
                    ]
                    green = [
                        components_to_color((0x07, 0x12, 0x0b)),
                        components_to_color((0x03, 0x0d, 0x07))
                    ]

                    random.shuffle(red)
                    random.shuffle(green)
                    outfit = [red, green]
                    random.shuffle(outfit)
                    new_palette[8:10] = outfit[0]
                    new_palette[10:12] = outfit[1]

        else:
            transformer = get_palette_transformer(middle=middle)
            new_palette = transformer(palette)
            if new_style_palette:
                new_palette = new_style_palette[0:12] + new_palette[12:]

        palette = new_palette

    fout.seek(pointer)
    for p in palette:
        write_multi(fout, p, length=2)
    return palette
示例#45
0
def manage_character_appearance(fout, preserve_graphics=False):
    characters = get_characters()
    wild = options_.is_code_active('partyparty')
    sabin_mode = options_.is_code_active('suplexwrecks')
    tina_mode = options_.is_code_active('bravenudeworld')
    soldier_mode = options_.is_code_active('quikdraw')
    moogle_mode = options_.is_code_active('kupokupo')
    ghost_mode = options_.is_code_active('halloween')
    christmas_mode = options_.is_code_active('christmas')
    sprite_swap_mode = options_.is_code_active('makeover') and not (
        sabin_mode or tina_mode or soldier_mode or moogle_mode or ghost_mode)
    new_palette_mode = not options_.is_code_active('sometimeszombies')

    if new_palette_mode:
        # import recolors for incompatible base sprites
        recolors = [("cyan", 0x152D40, 0x16A0), ("mog", 0x15E240, 0x16A0),
                    ("umaro", 0x162620, 0x16A0), ("dancer", 0x1731C0, 0x5C0),
                    ("lady", 0x1748C0, 0x5C0)]
        for rc in recolors:
            filename = os.path.join("data", "sprites", "RC" + rc[0] + ".bin")
            try:
                with open_mei_fallback(filename, "rb") as f:
                    sprite = f.read()
            except OSError:
                continue
            if len(sprite) >= rc[2]:
                sprite = sprite[:rc[2]]
            fout.seek(rc[1])
            fout.write(sprite)

    if (wild or tina_mode or sabin_mode or christmas_mode):
        if christmas_mode:
            char_ids = list(range(0, 0x15))  # don't replace kefka
        else:
            char_ids = list(range(0, 0x16))
    else:
        char_ids = list(range(0, 0x0E))

    male = None
    female = None
    if tina_mode:
        change_to = dict(list(zip(char_ids, [0x12] * 100)))
    elif sabin_mode:
        change_to = dict(list(zip(char_ids, [0x05] * 100)))
    elif soldier_mode:
        change_to = dict(list(zip(char_ids, [0x0e] * 100)))
    elif ghost_mode:
        change_to = dict(list(zip(char_ids, [0x14] * 100)))
    elif moogle_mode:
        # all characters are moogles except Mog, Imp, and Esper Terra
        if wild:
            # make mog human
            mog = random.choice(
                list(range(0, 0x0A)) + list(range(0x0B, 0x0F)) +
                [0x10, 0x11, 0x13, 0x15])
            #esper terra and imp neither human nor moogle
            esper_terra, imp = random.sample([0x0F, 0x12, 0x14], 2)
        else:
            mog = random.choice(list(range(0, 0x0A)) + list(range(0x0B, 0x0E)))
            esper_terra = 0x12
            imp = 0x0F
        change_to = dict(list(zip(char_ids, [0x0A] * 100)))
        change_to[0x0A] = mog
        change_to[0x12] = esper_terra
        change_to[0x0F] = imp
    else:
        female = [0, 0x06, 0x08]
        female += [
            c for c in [0x03, 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x14]
            if random.choice([True, False])
        ]
        female = [c for c in char_ids if c in female]
        male = [c for c in char_ids if c not in female]
        if preserve_graphics:
            change_to = dict(list(zip(char_ids, char_ids)))
        elif wild:
            change_to = list(char_ids)
            random.shuffle(change_to)
            change_to = dict(list(zip(char_ids, change_to)))
        else:
            random.shuffle(female)
            random.shuffle(male)
            change_to = dict(
                list(zip(sorted(male), male)) +
                list(zip(sorted(female), female)))

    manage_character_names(fout, change_to, male)

    swap_to = get_sprite_swaps(char_ids, male, female, change_to)

    for c in characters:
        if c.id < 14:
            if sprite_swap_mode and c.id in swap_to:
                c.new_appearance = swap_to[c.id].name
            elif not preserve_graphics:
                c.new_appearance = NAME_ID_DICT[change_to[c.id]]
            else:
                c.new_appearance = c.original_appearance

    sprite_ids = list(range(0x16))

    ssizes = ([0x16A0] * 0x10) + ([0x1560] * 6)
    spointers = dict([(c, sum(ssizes[:c]) + 0x150000) for c in sprite_ids])
    ssizes = dict(list(zip(sprite_ids, ssizes)))

    char_portraits = {}
    char_portrait_palettes = {}
    sprites = {}

    riding_sprites = {}
    try:
        f = open(RIDING_SPRITE_TABLE, "r")
    except IOError:
        pass
    else:
        for line in f.readlines():
            char_id, filename = line.strip().split(',', 1)
            try:
                g = open_mei_fallback(
                    os.path.join("custom", "sprites", filename), "rb")
            except IOError:
                continue

            riding_sprites[int(char_id)] = g.read(0x140)
            g.close()
        f.close()

    for c in sprite_ids:
        fout.seek(0x36F1B + (2 * c))
        portrait = read_multi(fout, length=2)
        char_portraits[c] = portrait
        fout.seek(0x36F00 + c)
        portrait_palette = fout.read(1)
        char_portrait_palettes[c] = portrait_palette
        fout.seek(spointers[c])
        sprite = fout.read(ssizes[c])

        if c in riding_sprites:
            sprite = sprite[:0x1560] + riding_sprites[c]
        sprites[c] = sprite

    if tina_mode:
        char_portraits[0x12] = char_portraits[0]
        char_portrait_palettes[0x12] = char_portrait_palettes[0]

    portrait_data = []
    portrait_palette_data = []

    fout.seek(0x2D1D00)

    for _ in range(19):
        portrait_data.append(fout.read(0x320))

    fout.seek(0x2D5860)
    for _ in range(19):
        portrait_palette_data.append(fout.read(0x20))

    free_portrait_ids, merchant = get_free_portrait_ids(
        swap_to, change_to, char_ids, char_portraits)

    for c in char_ids:
        new = change_to[c]
        portrait = char_portraits[new]
        portrait_palette = char_portrait_palettes[new]

        if c == 0x13 and sprite_swap_mode and not merchant:
            new_soldier = change_to[0xE]
            portrait = char_portraits[new_soldier]
            portrait_palette = char_portrait_palettes[new_soldier]
        elif (char_portraits[c] == 0 and c != 0):
            portrait = char_portraits[0xE]
            portrait_palette = char_portrait_palettes[0xE]
        elif sprite_swap_mode and c in swap_to:
            use_fallback = True
            fallback_portrait_id = swap_to[c].fallback_portrait_id
            if fallback_portrait_id < 0 or fallback_portrait_id > 18:
                fallback_portrait_id = 0xE

            portrait = fallback_portrait_id * 0x320
            portrait_palette = bytes([fallback_portrait_id])
            new_portrait_data = portrait_data[fallback_portrait_id]
            new_portrait_palette_data = portrait_palette_data[
                fallback_portrait_id]

            if swap_to[c].has_custom_portrait():
                use_fallback = False

                try:
                    g = open_mei_fallback(
                        os.path.join("custom", "sprites",
                                     swap_to[c].portrait_filename), "rb")
                    h = open_mei_fallback(
                        os.path.join("custom", "sprites",
                                     swap_to[c].portrait_palette_filename),
                        "rb")
                except IOError:
                    use_fallback = True
                    print("failed to load portrait %s for %s, using fallback" %
                          (swap_to[c].portrait_filename, swap_to[c].name))
                else:
                    new_portrait_data = g.read(0x320)
                    new_portrait_palette_data = h.read(0x20)
                    h.close()
                    g.close()

            if not use_fallback or fallback_portrait_id in free_portrait_ids:
                portrait_id = free_portrait_ids[0]
                portrait = portrait_id * 0x320
                portrait_palette = bytes([portrait_id])
                free_portrait_ids.remove(free_portrait_ids[0])
                fout.seek(0x2D1D00 + portrait)
                fout.write(new_portrait_data)
                fout.seek(0x2D5860 + portrait_id * 0x20)
                fout.write(new_portrait_palette_data)

        elif portrait == 0 and wild and change_to[c] != 0:
            portrait = char_portraits[0xE]
            portrait_palette = char_portrait_palettes[0xE]
        fout.seek(0x36F1B + (2 * c))
        write_multi(fout, portrait, length=2)
        fout.seek(0x36F00 + c)
        fout.write(portrait_palette)

        if wild:
            fout.seek(spointers[c])
            fout.write(sprites[0xE][:ssizes[c]])
        fout.seek(spointers[c])

        if sprite_swap_mode and c in swap_to:
            try:
                g = open_mei_fallback(
                    os.path.join("custom", "sprites", swap_to[c].file), "rb")
            except IOError:
                newsprite = sprites[change_to[c]]
                for ch in characters:
                    if ch.id == c:
                        ch.new_appearance = NAME_ID_DICT[change_to[c]]
            else:
                newsprite = g.read(min(ssizes[c], swap_to[c].size))
                # if it doesn't have riding sprites, it probably doesn't have a death sprite either
                if swap_to[c].size < 0x16A0:
                    newsprite = newsprite[:0xAE0] + sprites[0xE][
                        0xAE0:0xBA0] + newsprite[0xBA0:]
                g.close()
        else:
            newsprite = sprites[change_to[c]]
        newsprite = newsprite[:ssizes[c]]
        fout.write(newsprite)

    # celes in chains
    fout.seek(0x159500)
    chains = fout.read(192)
    fout.seek(0x17D660)
    fout.write(chains)

    manage_palettes(fout, change_to, char_ids)