class SpriteGroupModule(EbModule.EbModule): _name = "Sprite Groups" def __init__(self): self._grPtrTbl = EbTable(0xef133f) self._grPalTbl = EbTable(0xc30000) self._groups = None def freeRanges(self): return [(0x2f1a7f, 0x2f4a3f), (0x110000, 0x11ffff), (0x120000, 0x12ffff), (0x130000, 0x13ffff), (0x140000, 0x14ffff), (0x150000, 0x154fff)] def free(self): del(self._grPtrTbl) del(self._grPalTbl) def readFromRom(self, rom): self._grPtrTbl.readFromRom(rom) updateProgress(5) self._grPalTbl.readFromRom(rom) updateProgress(5) # Load the sprite groups self._groups = [] pct = 40.0/self._grPtrTbl.height() for i in range(self._grPtrTbl.height()): # Note: this assumes that the SPT is written contiguously numSprites = 8 # Assume that the last group only has 8 sprites if i < self._grPtrTbl.height()-1: numSprites = (self._grPtrTbl[i+1,0].val() - self._grPtrTbl[i,0].val() - 9) / 2 g = SpriteGroup(numSprites) g.readFromRom(rom, EbModule.toRegAddr(self._grPtrTbl[i,0].val())) self._groups.append(g) updateProgress(pct) def writeToProject(self, resourceOpener): # Write the palettes self._grPalTbl.writeToProject(resourceOpener) updateProgress(5) out = { } i = 0 pct = 40.0/len(self._groups) for g in self._groups: out[i] = g.dump() img = g.toImage(self._grPalTbl[g.palette(),0].val()) imgFile = resourceOpener("SpriteGroups/" + str(i).zfill(3), 'png') img.save(imgFile, 'png', transparency=0) imgFile.close() del(img) i += 1 updateProgress(pct) yaml.dump(out, resourceOpener("sprite_groups", "yml"), Dumper=yaml.CSafeDumper) updateProgress(5) def readFromProject(self, resourceOpener): self._grPalTbl.readFromProject(resourceOpener) updateProgress(5) input = yaml.load(resourceOpener("sprite_groups", "yml"), Loader=yaml.CSafeLoader) numGroups = len(input) self._groups = [] pct = 45.0/numGroups for i in range(numGroups): g = SpriteGroup(16) g.load(input[i]) img = Image.open( resourceOpener("SpriteGroups/" + str(i).zfill(3), "png")) g.fromImage(img) palData = img.getpalette() del(img) self._groups.append(g) pal = [ ] # Read the palette from the image for i in range(1, 16): pal.append((palData[i*3], palData[i*3+1], palData[i*3+2])) # Assign the palette number to the sprite for i in range(8): if pal == self._grPalTbl[i,0].val()[1:]: g.setPalette(i) break else: # Error, this image uses an invalid palette raise RuntimeException("Sprite Group #" + i + "uses an invalid palette.") updateProgress(pct) def writeToRom(self, rom): numGroups = len(self._groups) self._grPtrTbl.clear(numGroups) with DataBlock(sum(map( lambda x: x.blockSize(), self._groups))) as block: loc = 0 i = 0 # Write all the groups to the block, and sprites to rom pct = 40.0 / numGroups for g in self._groups: g.writeSpritesToFree(rom) g.writeToBlock(block, loc) self._grPtrTbl[i,0].setVal(loc) loc += g.blockSize() i += 1 updateProgress(pct) # Write the block to rom and correct the group pointers addr = EbModule.toSnesAddr(block.writeToFree(rom)) for i in range(self._grPtrTbl.height()): self._grPtrTbl[i,0].setVal( self._grPtrTbl[i,0].val() + addr) # Write the pointer table self._grPtrTbl.writeToRom(rom) updateProgress(5) # Write the palettes self._grPalTbl.writeToRom(rom) updateProgress(5)
class DoorModule(EbModule.EbModule): _name = "Doors" def __init__(self): self._ptrTbl = EbTable("DOOR_POINTER_TABLE") self._entries = [ ] def readFromRom(self, rom): self._ptrTbl.readFromRom(rom) updateProgress(5) pct = 45.0/(40*32) for i in range(self._ptrTbl.height()): loc = EbModule.toRegAddr(self._ptrTbl[i,0].val()) entry = [ ] numDoors = rom.readMulti(loc, 2) loc += 2 for j in range(numDoors): d = Door() try: d.readFromRom(rom, loc) except ValueError: # Invalid door entry. Some entries in EB are invalid. # When we encounter one, just assume we've reached the end # of this entry. break entry.append(d) loc += 5 self._entries.append(entry) i += 1 updateProgress(pct) def writeToProject(self, resourceOpener): out = dict() x = y = 0 rowOut = dict() pct = 45.0/(40*32) for entry in self._entries: if not entry: rowOut[x%32] = None else: rowOut[x%32] = map(lambda z: z.dump(), entry) if (x % 32) == 31: # Start new row out[y] = rowOut x = 0 y += 1 rowOut = dict() else: x += 1 updateProgress(pct) with resourceOpener("map_doors", "yml") as f: s = yaml.dump(out, default_flow_style=False, Dumper=yaml.CSafeDumper) s = sub("Event Flag: (\d+)", lambda i: "Event Flag: " + hex(int(i.group(0)[12:])), s) f.write(s) updateProgress(5) def readFromProject(self, resourceOpener): self._entries = [] pct = 45.0/(40*32) with resourceOpener("map_doors", "yml") as f: updateProgress(5) input = yaml.load(f, Loader=yaml.CSafeLoader) for y in input: row = input[y] for x in row: if row[x] == None: self._entries.append(None) else: entry = [] for door in row[x]: d = Door() d.load(door) entry.append(d) self._entries.append(entry) updateProgress(pct) def writeToRom(self, rom): self._ptrTbl.clear(32*40) destWriteLoc = 0xF0000 destRangeEnd = 0xF58EE # TODO Is this correct? Can we go more? destLocs = dict() emptyEntryPtr = EbModule.toSnesAddr(rom.writeToFree([0, 0])) pct = 45.0/(40*32) i=0 for entry in self._entries: if (entry == None) or (not entry): self._ptrTbl[i,0].setVal(emptyEntryPtr) else: entryLen = len(entry) writeLoc = rom.getFreeLoc(2 + entryLen*5) self._ptrTbl[i,0].setVal(EbModule.toSnesAddr(writeLoc)) rom[writeLoc] = entryLen & 0xff rom[writeLoc+1] = entryLen >> 8 writeLoc += 2 for door in entry: destWriteLoc += door.writeToRom(rom, writeLoc, destWriteLoc, destRangeEnd, destLocs) writeLoc += 5 i += 1 updateProgress(pct) self._ptrTbl.writeToRom(rom) # Mark any remaining space as free if destWriteLoc < destRangeEnd: rom.addFreeRanges([(destWriteLoc, destRangeEnd)]) updateProgress(5)
class BattleBgModule(EbModule.EbModule): _name = "Battle Backgrounds" _ASMPTRS_GFX = [0x2d1ba, 0x2d4dc, 0x2d8c3, 0x4a3ba] _ASMPTRS_ARR = [0x2d2c1, 0x2d537, 0x2d91f, 0x4a416] _ASMPTRS_PAL = [0x2d3bb, 0x2d61b, 0x2d7e8, 0x2d9e8, 0x4a4d0] def __init__(self): self._bbgGfxPtrTbl = EbTable("BATTLEBG_GFX_POINTERS") self._bbgArrPtrTbl = EbTable("BATTLEBG_ARR_POINTERS") self._bbgPalPtrTbl = EbTable("BATTLEBG_PALETTE_POINTERS") self._bbgScrollTbl = EbTable("BG_SCROLLING_TABLE") self._bbgDistorTbl = EbTable("BG_DISTORTION_TABLE") self._bbgTbl = EbTable("BG_DATA_TABLE") def free(self): del(self._bbgGfxPtrTbl) del(self._bbgArrPtrTbl) del(self._bbgPalPtrTbl) del(self._bbgTbl) del(self._bbgGfxArrs) del(self._bbgPals) def readFromRom(self, rom): self._bbgTbl.readFromRom(rom) pct = 50.0/(6+self._bbgTbl.height()) self._bbgGfxPtrTbl.readFromRom(rom, EbModule.toRegAddr(EbModule.readAsmPointer(rom, self._ASMPTRS_GFX[0]))) updateProgress(pct) self._bbgArrPtrTbl.readFromRom(rom, EbModule.toRegAddr(EbModule.readAsmPointer(rom, self._ASMPTRS_ARR[0]))) updateProgress(pct) self._bbgPalPtrTbl.readFromRom(rom, EbModule.toRegAddr(EbModule.readAsmPointer(rom, self._ASMPTRS_PAL[0]))) updateProgress(pct) self._bbgGfxArrs = [ None for i in range(self._bbgGfxPtrTbl.height()) ] self._bbgPals = [ None for i in range(self._bbgPalPtrTbl.height()) ] updateProgress(pct) self._bbgScrollTbl.readFromRom(rom) updateProgress(pct) self._bbgDistorTbl.readFromRom(rom) updateProgress(pct) for i in range(self._bbgTbl.height()): gfxNum = self._bbgTbl[i,0].val() colorDepth = self._bbgTbl[i,2].val() if (self._bbgGfxArrs[gfxNum] == None): # Max size used in rom: 421 (2bpp) 442 (4bpp) tg = EbTileGraphics(512, 8, colorDepth) with EbCompressedData() as tgb: tgb.readFromRom(rom, EbModule.toRegAddr( self._bbgGfxPtrTbl[gfxNum,0].val())) tg.readFromBlock(tgb) a = EbArrangement(32, 32) with EbCompressedData() as ab: ab.readFromRom(rom, EbModule.toRegAddr( self._bbgArrPtrTbl[gfxNum,0].val())) a.readFromBlock(ab) self._bbgGfxArrs[gfxNum] = (tg, a) palNum = self._bbgTbl[i,1].val() if (self._bbgPals[palNum] == None): with DataBlock(32) as pb: pb.readFromRom(rom, EbModule.toRegAddr(self._bbgPalPtrTbl[palNum,0].val())) p = EbPalettes(1, 16) p.readFromBlock(pb) self._bbgPals[palNum] = p updateProgress(pct) def writeToProject(self, resourceOpener): pct = 50.0/(3+self._bbgTbl.height()) self._bbgTbl.writeToProject(resourceOpener, hiddenColumns=[0,1]) updateProgress(pct) self._bbgScrollTbl.writeToProject(resourceOpener) updateProgress(pct) self._bbgDistorTbl.writeToProject(resourceOpener) updateProgress(pct) # Export BGs by table entry for i in range(self._bbgTbl.height()): (tg, a) = self._bbgGfxArrs[self._bbgTbl[i,0].val()] pal = self._bbgTbl[i,1].val() img = a.toImage(tg, self._bbgPals[pal]) imgFile = resourceOpener('BattleBGs/' + str(i).zfill(3), 'png') img.save(imgFile, 'png') imgFile.close() del(img) updateProgress(pct) def readFromProject(self, resourceOpener): self._bbgTbl.readFromProject(resourceOpener) pct = 50.0/(2+self._bbgTbl.height()) self._bbgScrollTbl.readFromProject(resourceOpener) updateProgress(pct) self._bbgDistorTbl.readFromProject(resourceOpener) updateProgress(pct) self._bbgGfxArrs = [] self._bbgPals = [] for i in range(self._bbgTbl.height()): img = Image.open( resourceOpener('BattleBGs/' + str(i).zfill(3), 'png')) np = EbPalettes(1, 16) colorDepth = self._bbgTbl[i,2].val() # Max size used in rom: 421 (2bpp) 442 (4bpp) ntg = EbTileGraphics(512, 8, colorDepth) na = EbArrangement(32, 32) na.readFromImage(img, np, ntg) j=0 for (tg, a) in self._bbgGfxArrs: if (tg == ntg) and (a == na): self._bbgTbl[i,0].setVal(j) break j += 1 else: self._bbgGfxArrs.append((ntg, na)) self._bbgTbl[i,0].setVal(j) j=0 for p in self._bbgPals: if (p == np): self._bbgTbl[i,1].setVal(j) break j += 1 else: self._bbgPals.append((np)) self._bbgTbl[i,1].setVal(j) updateProgress(pct) def freeRanges(self): return [(0xa0000,0xadca0), (0xb0000, 0xbd899)] def writeToRom(self, rom): self._bbgGfxPtrTbl.clear(len(self._bbgGfxArrs)) self._bbgArrPtrTbl.clear(len(self._bbgGfxArrs)) self._bbgPalPtrTbl.clear(len(self._bbgPals)) # Write gfx+arrs i = 0 pct = (50.0/3)/len(self._bbgGfxArrs) for (tg, a) in self._bbgGfxArrs: with EbCompressedData(tg.sizeBlock()) as tgb: tg.writeToBlock(tgb) self._bbgGfxPtrTbl[i,0].setVal(EbModule.toSnesAddr( tgb.writeToFree(rom))) with EbCompressedData(a.sizeBlock()) as ab: a.writeToBlock(ab) self._bbgArrPtrTbl[i,0].setVal(EbModule.toSnesAddr( ab.writeToFree(rom))) i += 1 updateProgress(pct) EbModule.writeAsmPointers(rom, self._ASMPTRS_GFX, EbModule.toSnesAddr(self._bbgGfxPtrTbl.writeToFree(rom))) EbModule.writeAsmPointers(rom, self._ASMPTRS_ARR, EbModule.toSnesAddr(self._bbgArrPtrTbl.writeToFree(rom))) # Write pals i = 0 pct = (50.0/3)/len(self._bbgPals) for p in self._bbgPals: with DataBlock(32) as pb: p.writeToBlock(pb) self._bbgPalPtrTbl[i,0].setVal(EbModule.toSnesAddr( pb.writeToFree(rom))) i += 1 updateProgress(pct) EbModule.writeAsmPointers(rom, self._ASMPTRS_PAL, EbModule.toSnesAddr(self._bbgPalPtrTbl.writeToFree(rom))) # Write the data table pct = (50.0/3)/3 self._bbgTbl.writeToRom(rom) updateProgress(pct) self._bbgScrollTbl.writeToRom(rom) updateProgress(pct) self._bbgDistorTbl.writeToRom(rom) updateProgress(pct)
class MapModule(EbModule.EbModule): _name = "Map" _MAP_PTRS_PTR_ADDR = 0xa1db _LOCAL_TSET_ADDR = 0x175000 _MAP_HEIGHT = 320 _MAP_WIDTH = 256 def __init__(self): self._tiles = [] self._mapSecTsetPalsTbl = EbTable(0xD7A800) self._mapSecMusicTbl = EbTable(0xDCD637) self._mapSecMiscTbl = EbTable(0xD7B200) self._mapSecTownMapTbl = EbTable(0xEFA70F) self.teleport = ValuedIntTableEntry(None, None, ["Enabled", "Disabled"]) self.townmap = ValuedIntTableEntry(None, None, ["None", "Onett", "Twoson", "Threed", "Fourside", "Scaraba", "Summers", "None 2"]) self.setting = ValuedIntTableEntry(None, None, ["None", "Indoors", "Exit Mouse usable", "Lost Underworld sprites", "Magicant sprites", "Robot sprites", "Butterflies", "Indoors and Butterflies"]) self.townmap_image = ValuedIntTableEntry(None, None, ["None", "Onett", "Twoson", "Threed", "Fourside", "Scaraba", "Summers" ]) self.townmap_arrow = ValuedIntTableEntry(None, None, ["None", "Up", "Down", "Right", "Left"]) def readFromRom(self, rom): # Read map tiles map_ptrs_addr = \ EbModule.toRegAddr(rom.readMulti(self._MAP_PTRS_PTR_ADDR, 3)) map_addrs = map(lambda x: \ EbModule.toRegAddr(rom.readMulti(map_ptrs_addr+x*4,4)), \ range(8)) self._tiles = map( lambda y: rom.readList(map_addrs[y%8] + ((y>>3)<<8), self._MAP_WIDTH).tolist(), range(self._MAP_HEIGHT)) k = self._LOCAL_TSET_ADDR for i in range(self._MAP_HEIGHT>>3): for j in range(self._MAP_WIDTH): self._tiles[i<<3][j] |= (rom[k] & 3) << 8 self._tiles[(i<<3)|1][j] |= ((rom[k] >> 2) & 3) << 8 self._tiles[(i<<3)|2][j] |= ((rom[k] >> 4) & 3) << 8 self._tiles[(i<<3)|3][j] |= ((rom[k] >> 6) & 3) << 8 self._tiles[(i<<3)|4][j] |= (rom[k+0x3000] & 3) << 8 self._tiles[(i<<3)|5][j] |= ((rom[k+0x3000] >> 2) & 3) << 8 self._tiles[(i<<3)|6][j] |= ((rom[k+0x3000] >> 4) & 3) << 8 self._tiles[(i<<3)|7][j] |= ((rom[k+0x3000] >> 6) & 3) << 8 k += 1 updateProgress(25) # Read sector data self._mapSecTsetPalsTbl.readFromRom(rom) updateProgress(25.0/4) self._mapSecMusicTbl.readFromRom(rom) updateProgress(25.0/4) self._mapSecMiscTbl.readFromRom(rom) updateProgress(25.0/4) self._mapSecTownMapTbl.readFromRom(rom) updateProgress(25.0/4) def writeToRom(self, rom): map_ptrs_addr = \ EbModule.toRegAddr(rom.readMulti(self._MAP_PTRS_PTR_ADDR, 3)) map_addrs = map(lambda x: \ EbModule.toRegAddr(rom.readMulti(map_ptrs_addr+x*4,4)), \ range(8)) for i in range(self._MAP_HEIGHT): rom.write(map_addrs[i%8] + ((i>>3)<<8), map(lambda x: x & 0xff, self._tiles[i])) k = self._LOCAL_TSET_ADDR for i in range(self._MAP_HEIGHT>>3): for j in range(self._MAP_WIDTH): c = ((self._tiles[i<<3][j] >> 8) | ((self._tiles[(i<<3)|1][j] >> 8) << 2) | ((self._tiles[(i<<3)|2][j] >> 8) << 4) | ((self._tiles[(i<<3)|3][j] >> 8) << 6)) rom.write(k, c) c = ((self._tiles[(i<<3)|4][j] >> 8) | ((self._tiles[(i<<3)|5][j] >> 8) << 2) | ((self._tiles[(i<<3)|6][j] >> 8) << 4) | ((self._tiles[(i<<3)|7][j] >> 8) << 6)) rom.write(k+0x3000, c) k += 1 updateProgress(25) # Write sector data self._mapSecTsetPalsTbl.writeToRom(rom) updateProgress(25.0/4) self._mapSecMusicTbl.writeToRom(rom) updateProgress(25.0/4) self._mapSecMiscTbl.writeToRom(rom) updateProgress(25.0/4) self._mapSecTownMapTbl.writeToRom(rom) updateProgress(25.0/4) def writeToProject(self, resourceOpener): # Write map tiles with resourceOpener("map_tiles", "map") as f: for row in self._tiles: f.write(hex(row[0])[2:].zfill(3)) for tile in row[1:]: f.write(" ") f.write(hex(tile)[2:].zfill(3)) f.write("\n") updateProgress(25.0) # Write sector data out = dict() for i in range(self._mapSecTsetPalsTbl.height()): self.teleport.setVal(self._mapSecMiscTbl[i,0].val() >> 7) self.townmap.setVal((self._mapSecMiscTbl[i,0].val() >> 3) & 7) self.setting.setVal(self._mapSecMiscTbl[i,0].val() & 3) self.townmap_image.setVal(self._mapSecTownMapTbl[i,0].val() & 0xf) self.townmap_arrow.setVal(self._mapSecTownMapTbl[i,0].val() >> 4) out[i] = { "Tileset": self._mapSecTsetPalsTbl[i,0].val() >> 3, "Palette": self._mapSecTsetPalsTbl[i,0].val() & 7, "Music": self._mapSecMusicTbl[i,0].dump(), "Teleport": self.teleport.dump(), "Town Map": self.townmap.dump(), "Setting": self.setting.dump(), "Item": self._mapSecMiscTbl[i,1].dump(), "Town Map Image": self.townmap_image.dump(), "Town Map Arrow": self.townmap_arrow.dump(), "Town Map X": self._mapSecTownMapTbl[i,1].dump(), "Town Map Y": self._mapSecTownMapTbl[i,2].dump() } updateProgress(12.5) with resourceOpener("map_sectors", "yml") as f: yaml.dump(out, f, Dumper=yaml.CSafeDumper, default_flow_style=False) updateProgress(12.5) def readFromProject(self, resourceOpener): # Read map data with resourceOpener("map_tiles", "map") as f: self._tiles = map(lambda y: map(lambda x: int(x, 16), y.split(" ")), f.readlines()) updateProgress(25) # Read sector data self._mapSecTsetPalsTbl.clear(2560) self._mapSecMusicTbl.clear(2560) self._mapSecMiscTbl.clear(2560) self._mapSecTownMapTbl.clear(2560) pct = (25.0/2560) with resourceOpener("map_sectors", "yml") as f: input = yaml.load(f, Loader=yaml.CSafeLoader) for i in input: entry = input[i] self._mapSecTsetPalsTbl[i,0].setVal( (entry["Tileset"] << 3) | entry["Palette"]) self._mapSecMusicTbl[i,0].load(entry["Music"]) self._mapSecMiscTbl[i,1].load(entry["Item"]) self.teleport.load(entry["Teleport"]) self.townmap.load(entry["Town Map"]) self.setting.load(entry["Setting"]) self._mapSecMiscTbl[i,0].setVal((self.teleport.val() << 7) | (self.townmap.val() << 3) | self.setting.val()) self.townmap_image.load(entry["Town Map Image"]) self.townmap_arrow.load(entry["Town Map Arrow"]) self._mapSecTownMapTbl[i,0].setVal( (self.townmap_arrow.val() << 4) | (self.townmap_image.val() & 0xf)) self._mapSecTownMapTbl[i,1].load(entry["Town Map X"]) self._mapSecTownMapTbl[i,2].load(entry["Town Map Y"]) updateProgress(pct) def upgradeProject(self, oldVersion, newVersion, rom, resourceOpenerR, resourceOpenerW): global updateProgress def replaceField(fname, oldField, newField, valueMap): if newField == None: newField = oldField valueMap = dict((k, v) for k,v in valueMap.iteritems()) with resourceOpenerR(fname, 'yml') as f: data = yaml.load(f, Loader=yaml.CSafeLoader) for i in data: if data[i][oldField] in valueMap: data[i][newField] = valueMap[data[i][oldField]].lower() else: data[i][newField] = data[i][oldField] if newField != oldField: del data[i][oldField] with resourceOpenerW(fname, 'yml') as f: yaml.dump(data, f, Dumper=yaml.CSafeDumper, default_flow_style=False) if oldVersion == newVersion: updateProgress(100) return elif oldVersion <= 2: replaceField("map_sectors", "Town Map", None, { "scummers": "summers" }) # Need to add the Town Map Image/Arrow/X/Y fields tmp = updateProgress updateProgress = lambda x: None self.readFromRom(rom) updateProgress = tmp with resourceOpenerR("map_sectors", 'yml') as f: data = yaml.load(f, Loader=yaml.CSafeLoader) for i in data: self.townmap_image.setVal(self._mapSecTownMapTbl[i,0].val() & 0xf) self.townmap_arrow.setVal(self._mapSecTownMapTbl[i,0].val() >> 4) data[i]["Town Map Image"] = self.townmap_image.dump() data[i]["Town Map Arrow"] = self.townmap_arrow.dump() data[i]["Town Map X"] = self._mapSecTownMapTbl[i,1].dump() data[i]["Town Map Y"] = self._mapSecTownMapTbl[i,2].dump() with resourceOpenerW("map_sectors", 'yml') as f: yaml.dump(data, f, Dumper=yaml.CSafeDumper, default_flow_style=False) self.upgradeProject(3, newVersion, rom, resourceOpenerR, resourceOpenerW) else: self.upgradeProject(oldVersion+1, newVersion, rom, resourceOpenerR, resourceOpenerW)
class EnemyModule(EbModule.EbModule): _name = "Enemies" _ASMPTR_GFX = 0x2ee0b _REGPTR_GFX = [ 0x2ebe0, 0x2f014, 0x2f065 ] _ASMPTR_PAL = 0x2ef74 def __init__(self): self._enemyCfgTable = EbTable(0xd59589) self._bsPtrTbl = EbTable(0xce62ee) self._bsPalsTable = EbTable(0xce6514) self._enemyGroupTbl = EbTable(0xD0C60D) self._enemyGroupBgTbl = EbTable(0xCBD89A) self._bsprites = [ ] self._bsPals = [ ] self._enemyGroups = [ ] def readFromRom(self, rom): self._bsPtrTbl.readFromRom(rom, EbModule.toRegAddr(EbModule.readAsmPointer(rom, self._ASMPTR_GFX))) self._bsPalsTable.readFromRom(rom, EbModule.toRegAddr(EbModule.readAsmPointer(rom, self._ASMPTR_PAL))) pct = 45.0/(self._bsPtrTbl.height() + self._bsPalsTable.height() + 1) self._enemyCfgTable.readFromRom(rom) updateProgress(pct) # Read the palettes for i in range(self._bsPalsTable.height()): pal = EbPalettes(1,16) pal.set(0, self._bsPalsTable[i,0].val()) self._bsPals.append(pal) updateProgress(pct) # Read the sprites for i in range(self._bsPtrTbl.height()): with EbCompressedData() as bsb: bsb.readFromRom(rom, EbModule.toRegAddr(self._bsPtrTbl[i,0].val())) bs = EbBattleSprite() bs.readFromBlock(bsb, self._bsPtrTbl[i,1].val()) self._bsprites.append(bs) updateProgress(pct) # Read the group data self._enemyGroupTbl.readFromRom(rom) self._enemyGroupBgTbl.readFromRom(rom) self._enemyGroups = [ ] pct = 5.0/self._enemyGroupTbl.height() for i in range(self._enemyGroupTbl.height()): group = [ ] ptr = EbModule.toRegAddr(self._enemyGroupTbl[i,0].val()) while(rom[ptr] != 0xff): group.append((rom.readMulti(ptr+1,2), rom[ptr])) ptr += 3 self._enemyGroups.append(group) updateProgress(pct) def freeRanges(self): return [(0x0d0000, 0x0dffff), # Battle Sprites (0x0e0000, 0x0e6913), # Battle Sprites Cont'd & Btl Spr. Pals (0x10d52d, 0x10dfb3)] # Enemy Group Data def writeToRom(self, rom): pct = 40.0/(len(self._bsprites) + len(self._bsPals) + 3) # Write the main table self._enemyCfgTable.writeToRom(rom) updateProgress(pct) # Write the gfx ptr table self._bsPtrTbl.clear(len(self._bsprites)) i = 0 for bs in self._bsprites: with EbCompressedData(bs.sizeBlock()) as bsb: bs.writeToBlock(bsb) self._bsPtrTbl[i,0].setVal(EbModule.toSnesAddr( bsb.writeToFree(rom))) self._bsPtrTbl[i,1].setVal(bs.size()) i += 1 updateProgress(pct) gfxAddr = EbModule.toSnesAddr(self._bsPtrTbl.writeToFree(rom)) EbModule.writeAsmPointer(rom, self._ASMPTR_GFX, gfxAddr) updateProgress(pct) for p in self._REGPTR_GFX: rom.writeMulti(p, gfxAddr, 3) # Write the pal table self._bsPalsTable.clear(len(self._bsPals)) i = 0 for p in self._bsPals: self._bsPalsTable[i,0].setVal(p.getSubpal(0)) i += 1 updateProgress(pct) EbModule.writeAsmPointer(rom, self._ASMPTR_PAL, EbModule.toSnesAddr(self._bsPalsTable.writeToFree(rom))) updateProgress(pct) # Write the groups self._enemyGroupBgTbl.writeToRom(rom) updateProgress(5) i=0 for group in self._enemyGroups: loc = rom.getFreeLoc(len(group)*3 + 1) self._enemyGroupTbl[i,0].setVal(EbModule.toSnesAddr(loc)) i += 1 for enemyID, amount in group: rom[loc] = amount rom[loc+1] = enemyID & 0xff rom[loc+2] = enemyID >> 8 loc += 3 rom[loc] = 0xff self._enemyGroupTbl.writeToRom(rom) updateProgress(5) def writeToProject(self, resourceOpener): pct = 40.0/(self._enemyCfgTable.height() + 1) # First, write the Enemy Configuration Table self._enemyCfgTable.writeToProject(resourceOpener, [4,14]) updateProgress(pct) # Next, write the battle sprite images for i in range(self._enemyCfgTable.height()): if self._enemyCfgTable[i,4].val() > 0: self._bsprites[self._enemyCfgTable[i,4].val()-1].writeToProject( resourceOpener, i, self._bsPals[self._enemyCfgTable[i,14].val()].getSubpal(0)) updateProgress(pct) # Now write the groups out = dict() i = 0 pct = 5.0/len(self._enemyGroups) for group in self._enemyGroups: entry = dict() for j in range(1,4): field = self._enemyGroupTbl[i,j] entry[field.name] = field.dump() for j in range(2): field = self._enemyGroupBgTbl[i,j] entry[field.name] = field.dump() enemyList = dict() j = 0 for enemyID, amount in group: enemyEntry = dict() enemyEntry["Enemy"] = enemyID enemyEntry["Amount"] = amount enemyList[j] = enemyEntry j += 1 entry["Enemies"] = enemyList out[i] = entry i += 1 updateProgress(pct) with resourceOpener("enemy_groups", "yml") as f: yaml.dump(out, f, Dumper=yaml.CSafeDumper) updateProgress(5) def readFromProject(self, resourceOpener): # First, read the Enemy Configuration Table self._enemyCfgTable.readFromProject(resourceOpener) pct = 40.0/(self._enemyCfgTable.height()) # Second, read the Battle Sprites bsHashes = dict() bsNextNum = 1 palNextNum = 0 for i in range(self._enemyCfgTable.height()): bs = EbBattleSprite() pal = EbPalettes(1,16) try: bs.readFromProject(resourceOpener, i, pal) # Add the battle sprite try: #self._enemyCfgTable[i,4].set(self._bsprites.index(bs)) bsNum = bsHashes[bs._sprite._spriteHash] self._enemyCfgTable[i,4].setVal(bsNum) except KeyError: self._bsprites.append(bs) self._enemyCfgTable[i,4].setVal(bsNextNum) bsHashes[bs._sprite._spriteHash] = bsNextNum bsNextNum += 1 # Add the palette # TODO should probably use hash table here too? # then again, I don't think it's actually a bottleneck try: self._enemyCfgTable[i,14].setVal(self._bsPals.index(pal)) except ValueError: self._bsPals.append(pal) self._enemyCfgTable[i,14].setVal(palNextNum) palNextNum += 1 except IOError: # No battle sprite PNG self._enemyCfgTable[i,4].setVal(0) self._enemyCfgTable[i,14].setVal(0) updateProgress(pct) # Third, read the groups self._enemyGroupTbl.readFromProject(resourceOpener, "enemy_groups") updateProgress(2) self._enemyGroupBgTbl.readFromProject(resourceOpener, "enemy_groups") updateProgress(2) self._enemyGroups = [ ] pct = 4.0/484 with resourceOpener("enemy_groups", "yml") as f: input = yaml.load(f, Loader=yaml.CSafeLoader) updateProgress(2) for group in input: tmp1 = input[group]["Enemies"] enemyList = [ ] i = 0 for enemy in tmp1: tmp2 = tmp1[i] enemyList.append((tmp2["Enemy"], tmp2["Amount"])) i += 1 self._enemyGroups.append(enemyList) updateProgress(pct)
class TilesetModule(EbModule.EbModule): _name = "Tilesets" def __init__(self): self._gfxPtrTbl = EbTable("MAP_DATA_TILESET_PTR_TABLE") self._arrPtrTbl = EbTable("MAP_DATA_TILE_ARRANGEMENT_PTR_TABLE") self._colPtrTbl = EbTable("MAP_DATA_TILE_COLLISION_PTR_TABLE") self._mapTsetTbl = EbTable("TILESET_TABLE") self._palPtrTbl = EbTable("MAP_PALETTE_PTR_TABLE") self._tsets = [ Tileset() for i in range(20) ] def freeRanges(self): return [(0x17c600, 0x17fbe7), (0x190000, 0x19fc17), (0x1b0000, 0x1bf2ea), (0x1c0000, 0x1cd636), (0x1d0000, 0x1dfecd), (0x1e0000, 0x1ef0e6), (0x1f0000, 0x1fc242)] def readFromRom(self, rom): self._gfxPtrTbl.readFromRom(rom) updateProgress(2) self._arrPtrTbl.readFromRom(rom) updateProgress(2) self._colPtrTbl.readFromRom(rom) updateProgress(2) self._mapTsetTbl.readFromRom(rom) updateProgress(2) self._palPtrTbl.readFromRom(rom) updateProgress(2) # Read tilesets pct = 30.0/len(self._tsets) i=0 for tset in self._tsets: # Read data tset.readMinitilesFromRom(rom, EbModule.toRegAddr(self._gfxPtrTbl[i,0].val())) tset.readArrangementsFromRom(rom, EbModule.toRegAddr(self._arrPtrTbl[i,0].val())) tset.readCollisionsFromRom(rom, EbModule.toRegAddr(self._colPtrTbl[i,0].val())) i += 1 updateProgress(pct) # Read palettes pct = 10.0/self._mapTsetTbl.height() for i in range(self._mapTsetTbl.height()): drawTset = self._mapTsetTbl[i,0].val() # Each map tset has 8 maximum palettes # We'll just assume they all use 8 and read the garbage #romLoc = self._palPtrTbl[i,0].val() #for j in xrange(8): # # Read the palette # self._tsets[drawTset].readPaletteFromRom(rom, i, j, # EbModule.toRegAddr(romLoc)) # romLoc += 0xc0 # OK, as it turns out, all palettes need to be in the 1A bank # So we actually need to conserve space and not read garbage # Estimate the number of palettes for this map tileset if i == 31: #k = 0xDAFAA7 - self._palPtrTbl[i,0].val() k = 7 else: k = self._palPtrTbl[i+1,0].val() - self._palPtrTbl[i,0].val() k /= 0xc0 # Add the palettes romLoc = EbModule.toRegAddr(self._palPtrTbl[i,0].val()) for j in range(k): # Read the palette self._tsets[drawTset].readPaletteFromRom(rom, i, j, romLoc) romLoc += 0xc0 updateProgress(pct) def writeToRom(self, rom): numTsets = len(self._tsets) self._gfxPtrTbl.clear(numTsets) self._arrPtrTbl.clear(numTsets) self._colPtrTbl.clear(numTsets) self._mapTsetTbl.clear(32) self._palPtrTbl.clear(32) # Write gfx & arrs pct = 30.0/numTsets i=0 for tset in self._tsets: self._gfxPtrTbl[i,0].setVal(EbModule.toSnesAddr( tset.writeMinitilesToFree(rom))) self._arrPtrTbl[i,0].setVal(EbModule.toSnesAddr( tset.writeArrangementsToFree(rom))) i += 1 updateProgress(pct) self._gfxPtrTbl.writeToRom(rom) updateProgress(2) self._arrPtrTbl.writeToRom(rom) updateProgress(2) # Write collissions pct = 6.0/numTsets colLocs = dict() colWriteLoc = 0x180000 colRangeEnd = 0x18f05d i=0 for tset in self._tsets: with DataBlock(len(tset.col)*2) as colTable: j=0 for c in tset.col: hash = crc32(c) try: addr = colLocs[hash] except KeyError: if (colWriteLoc + 16) > colRangeEnd: # TODO Error, not enough space for collisions print "Ran out of collision space" raise Exception addr = 0 else: colLocs[hash] = colWriteLoc addr = colWriteLoc rom.write(colWriteLoc, c) colWriteLoc += 16 colTable[j] = addr & 0xff colTable[j+1] = (addr >> 8) & 0xff j += 2 self._colPtrTbl[i,0].setVal(EbModule.toSnesAddr( colTable.writeToFree(rom))) i += 1 updateProgress(pct) self._colPtrTbl.writeToRom(rom) updateProgress(1) # Write the palettes, they need to be in the DA bank pct = 7.0/32 palWriteLoc = 0x1a0000 palRangeEnd = 0x1afaa6 # can we go more? # Write maps/drawing tilesets associations and map tset pals for i in range(32): # For each map tileset # Find the drawing tileset number for this map tileset drawTset = -1 j = 0 for tset in self._tsets: for (mt,mp,pal) in tset.pals: if mt == i: drawTset = j break if drawTset != -1: break j += 1 else: # TODO Error, this drawing tileset isn't associated drawTset = 0 self._mapTsetTbl[i,0].setVal(drawTset) # Write the palette data for this map tileset mtset_pals = [(mp,pal) for (mt,mp,pal) in self._tsets[drawTset].pals if mt == i] mtset_pals.sort() # Let's take the easy way out and just write redundant flag pals # This will waste space but oh well # First, write the flag pals for (mp,pal) in mtset_pals: if pal.flag != 0: if palWriteLoc + 0xc0 > palRangeEnd: # TODO Error, not enough space for all these palettes raise RuntimeError("Too many palettes") pal.flagPal.writeToBlock(rom, palWriteLoc) pal.flagPalPtr = palWriteLoc & 0xffff palWriteLoc += 0xc0 self._palPtrTbl[i,0].setVal(EbModule.toSnesAddr(palWriteLoc)) # Now write the regular pals for (mp,pal) in mtset_pals: if palWriteLoc + 0xc0 > palRangeEnd: # TODO Error, not enough space for all these palettes raise RuntimeException("Too many palettes") pal.writeToBlock(rom, palWriteLoc) palWriteLoc += 0xc0 updateProgress(pct) self._mapTsetTbl.writeToRom(rom) updateProgress(1) self._palPtrTbl.writeToRom(rom) updateProgress(1) # Might as well use any extra leftover space ranges = [(colWriteLoc, colRangeEnd), (palWriteLoc, palRangeEnd)] ranges = [(a,b) for (a,b) in ranges if a < b] rom.addFreeRanges(ranges) def writeToProject(self, resourceOpener): # Dump an additional YML with color0 data out = dict() for i in range(0,32): # For each map tset entry = dict() tset = None for ts in self._tsets: if ts.hasMapTileset(i): tset = ts break for (pN,p) in [(mp,p) for (mt,mp,p) in tset.pals if mt == i]: entry[pN] = p.dump() out[i] = entry with resourceOpener('map_palette_settings', 'yml') as f: s = yaml.dump(out, default_flow_style=False, Dumper=yaml.CSafeDumper) s = sub("Event Flag: (\d+)", lambda i: "Event Flag: " + hex(int(i.group(0)[12:])), s) f.write(s) updateProgress(5) # Dump the FTS files pct=45.0/len(self._tsets) i=0 for tset in self._tsets: with resourceOpener('Tilesets/' + str(i).zfill(2), 'fts') as f: tset.writeToFTS(f) i += 1 updateProgress(pct) def readFromProject(self, resourceOpener): i=0 pct = 45.0/len(self._tsets) for tset in self._tsets: with resourceOpener('Tilesets/' + str(i).zfill(2), 'fts') as f: tset.readFromFTS(f) i += 1 updateProgress(pct) with resourceOpener('map_palette_settings', 'yml') as f: input = yaml.load(f, Loader=yaml.CSafeLoader) for mtset in input: # For each map tileset # Get the draw (normal) tileset tset = None for ts in self._tsets: if ts.hasMapTileset(mtset): tset = ts break # For each map palette mtset_pals = [(mp,p) for (mt,mp,p) in tset.pals if mt == mtset] for (pN,mtset_pal) in mtset_pals: entry = input[mtset][pN] mtset_pal.flag = entry["Event Flag"] mtset_pal.flashEffect = entry["Flash Effect"] mtset_pal.spritePalNum = entry["Sprite Palette"] if mtset_pal.flag != 0: mtset_pal.flagPal = MapPalette() mtset_pal.flagPal.setFromString(entry["Event Palette"]) mtset_pal.flagPal.spritePalNum = entry["Sprite Palette"] updateProgress(5.0/32)
class MapEnemyModule(EbModule.EbModule): _name = "Map Enemies" def __init__(self): self._mapGroupPtrTbl = EbTable("ENEMY_PLACEMENT_GROUPS_PTR_TABLE") self._mapEnemyTbl = EbTable("ENEMY_PLACEMENT_DATA") def freeRanges(self): return [(0x10BBAC, 0x10C6AC)] # Groups data def readFromRom(self, rom): self._mapEnemyTbl.readFromRom(rom) updateProgress(2.5) self._mapGroupPtrTbl.readFromRom(rom) updateProgress(2.5) # Read the groups pct = 45.0 / (self._mapGroupPtrTbl.height()) self._mapGroups = [] for i in range(self._mapGroupPtrTbl.height()): loc = EbModule.toRegAddr(self._mapGroupPtrTbl[i, 0].val()) flag = rom.readMulti(loc, 2) rate1 = rom[loc + 2] rate2 = rom[loc + 3] loc += 4 # Read the enemies/probabilities group1 = [] if rate1 > 0: sum = 0 while sum < 8: prob = rom[loc] enemy = rom.readMulti(loc + 1, 2) sum += prob loc += 3 group1.append((prob, enemy)) group2 = [] if rate2 > 0: sum = 0 while sum < 8: prob = rom[loc] enemy = rom.readMulti(loc + 1, 2) sum += prob loc += 3 group2.append((prob, enemy)) # Add to the list self._mapGroups.append((flag, rate1, rate2, group1, group2)) updateProgress(pct) def writeToRom(self, rom): self._mapEnemyTbl.writeToRom(rom) updateProgress(2.5) self._mapGroupPtrTbl.clear(len(self._mapGroups)) updateProgress(2.5) pct = 42.5 / len(self._mapGroups) i = 0 for (flag, rate1, rate2, subg1, subg2) in self._mapGroups: size = 4 if rate1 > 0: size += len(subg1) * 3 if rate2 > 0: size += len(subg2) * 3 loc = rom.getFreeLoc(size) self._mapGroupPtrTbl[i, 0].setVal(EbModule.toSnesAddr(loc)) rom.writeMulti(loc, flag, 2) rom[loc + 2] = rate1 rom[loc + 3] = rate2 loc += 4 for prob, egroup in subg1: rom[loc] = prob rom.writeMulti(loc + 1, egroup, 2) loc += 3 for prob, egroup in subg2: rom[loc] = prob rom.writeMulti(loc + 1, egroup, 2) loc += 3 i += 1 updateProgress(pct) self._mapGroupPtrTbl.writeToRom(rom) updateProgress(2.5) def writeToProject(self, resourceOpener): self._mapEnemyTbl.writeToProject(resourceOpener) updateProgress(2.5) # Write the groups pct = 42.5 / len(self._mapGroups) out = dict() i = 0 for (flag, rate1, rate2, group1, group2) in self._mapGroups: # Generate first enemy/prob list g1out = dict() j = 0 for prob, enemy in group1: g1out[j] = {"Enemy Group": enemy, "Probability": prob} j += 1 g2out = dict() j = 0 for prob, enemy in group2: g2out[j] = {"Enemy Group": enemy, "Probability": prob} j += 1 out[i] = { "Event Flag": flag, "Sub-Group 1 Rate": rate1, "Sub-Group 1": g1out, "Sub-Group 2 Rate": rate2, "Sub-Group 2": g2out, } i += 1 updateProgress(pct) s = yaml.dump(out, Dumper=yaml.CSafeDumper) updateProgress(2.5) s = sub("Event Flag: (\d+)", lambda i: "Event Flag: " + hex(int(i.group(0)[12:])), s) with resourceOpener("map_enemy_groups", "yml") as f: f.write(s) updateProgress(2.5) def readFromProject(self, resourceOpener): self._mapEnemyTbl.readFromProject(resourceOpener) updateProgress(5) pct = 40.0 / 203 self._mapGroups = [] with resourceOpener("map_enemy_groups", "yml") as f: input = yaml.load(f, Loader=yaml.CSafeLoader) updateProgress(5) for gid in input: group = input[gid] flag = group["Event Flag"] rate1 = group["Sub-Group 1 Rate"] rate2 = group["Sub-Group 2 Rate"] subg1 = [] if rate1 > 0: for eid in group["Sub-Group 1"]: entry = group["Sub-Group 1"][eid] subg1.append((entry["Probability"], entry["Enemy Group"])) subg2 = [] if rate2 > 0: for eid in group["Sub-Group 2"]: entry = group["Sub-Group 2"][eid] subg2.append((entry["Probability"], entry["Enemy Group"])) self._mapGroups.append((flag, rate1, rate2, subg1, subg2)) updateProgress(pct)
class MapModule(EbModule.EbModule): _name = "Map" _MAP_PTRS_PTR_ADDR = 0xa1db _LOCAL_TSET_ADDR = 0x175000 _MAP_HEIGHT = 320 _MAP_WIDTH = 256 def __init__(self): self._tiles = [] self._mapSecTsetPalsTbl = EbTable("GLOBAL_MAP_TILESETPALETTE_DATA") self._mapSecMusicTbl = EbTable("MAP_DATA_PER-SECTOR_MUSIC") self._mapSecMiscTbl = EbTable("MAP_DATA_PER-SECTOR_ATTRIBUTES_TABLE") self.teleport = ValuedIntTableEntry(None, None, ["Enabled", "Disabled"]) self.townmap = ValuedIntTableEntry(None, None, ["None", "Onett", "Twoson", "Threed", "Fourside", "Scaraba", "Scummers", "None 2"]) self.setting = ValuedIntTableEntry(None, None, ["None", "Indoors", "Exit Mouse usable", "Lost Underworld sprites", "Magicant sprites", "Robot sprites", "Butterflies", "Indoors and Butterflies"]) def readFromRom(self, rom): # Read map tiles map_ptrs_addr = \ EbModule.toRegAddr(rom.readMulti(self._MAP_PTRS_PTR_ADDR, 3)) map_addrs = map(lambda x: \ EbModule.toRegAddr(rom.readMulti(map_ptrs_addr+x*4,4)), \ range(8)) self._tiles = map( lambda y: rom.readList(map_addrs[y%8] + ((y>>3)<<8), self._MAP_WIDTH).tolist(), range(self._MAP_HEIGHT)) k = self._LOCAL_TSET_ADDR for i in range(self._MAP_HEIGHT>>3): for j in range(self._MAP_WIDTH): self._tiles[i<<3][j] |= (rom[k] & 3) << 8 self._tiles[(i<<3)|1][j] |= ((rom[k] >> 2) & 3) << 8 self._tiles[(i<<3)|2][j] |= ((rom[k] >> 4) & 3) << 8 self._tiles[(i<<3)|3][j] |= ((rom[k] >> 6) & 3) << 8 self._tiles[(i<<3)|4][j] |= (rom[k+0x3000] & 3) << 8 self._tiles[(i<<3)|5][j] |= ((rom[k+0x3000] >> 2) & 3) << 8 self._tiles[(i<<3)|6][j] |= ((rom[k+0x3000] >> 4) & 3) << 8 self._tiles[(i<<3)|7][j] |= ((rom[k+0x3000] >> 6) & 3) << 8 k += 1 updateProgress(25) # Read sector data self._mapSecTsetPalsTbl.readFromRom(rom) updateProgress(25.0/3) self._mapSecMusicTbl.readFromRom(rom) updateProgress(25.0/3) self._mapSecMiscTbl.readFromRom(rom) updateProgress(25.0/3) def writeToRom(self, rom): map_ptrs_addr = \ EbModule.toRegAddr(rom.readMulti(self._MAP_PTRS_PTR_ADDR, 3)) map_addrs = map(lambda x: \ EbModule.toRegAddr(rom.readMulti(map_ptrs_addr+x*4,4)), \ range(8)) for i in range(self._MAP_HEIGHT): rom.write(map_addrs[i%8] + ((i>>3)<<8), map(lambda x: x & 0xff, self._tiles[i])) k = self._LOCAL_TSET_ADDR for i in range(self._MAP_HEIGHT>>3): for j in range(self._MAP_WIDTH): c = ((self._tiles[i<<3][j] >> 8) | ((self._tiles[(i<<3)|1][j] >> 8) << 2) | ((self._tiles[(i<<3)|2][j] >> 8) << 4) | ((self._tiles[(i<<3)|3][j] >> 8) << 6)) rom.write(k, c) c = ((self._tiles[(i<<3)|4][j] >> 8) | ((self._tiles[(i<<3)|5][j] >> 8) << 2) | ((self._tiles[(i<<3)|6][j] >> 8) << 4) | ((self._tiles[(i<<3)|7][j] >> 8) << 6)) rom.write(k+0x3000, c) k += 1 updateProgress(25) # Write sector data self._mapSecTsetPalsTbl.writeToRom(rom) updateProgress(25.0/3) self._mapSecMusicTbl.writeToRom(rom) updateProgress(25.0/3) self._mapSecMiscTbl.writeToRom(rom) updateProgress(25.0/3) def writeToProject(self, resourceOpener): # Write map tiles with resourceOpener("map_tiles", "map") as f: for row in self._tiles: f.write(hex(row[0])[2:].zfill(3)) for tile in row[1:]: f.write(" ") f.write(hex(tile)[2:].zfill(3)) f.write("\n") updateProgress(25.0) # Write sector data out = dict() for i in range(self._mapSecTsetPalsTbl.height()): self.teleport.setVal(self._mapSecMiscTbl[i,0].val() >> 7) self.townmap.setVal((self._mapSecMiscTbl[i,0].val() >> 3) & 7) self.setting.setVal(self._mapSecMiscTbl[i,0].val() & 3) out[i] = { "Tileset": self._mapSecTsetPalsTbl[i,0].val() >> 3, "Palette": self._mapSecTsetPalsTbl[i,0].val() & 7, "Music": self._mapSecMusicTbl[i,0].dump(), "Teleport": self.teleport.dump(), "Town Map": self.townmap.dump(), "Setting": self.setting.dump(), "Item": self._mapSecMiscTbl[i,1].dump() } updateProgress(12.5) with resourceOpener("map_sectors", "yml") as f: yaml.dump(out, f, Dumper=yaml.CSafeDumper, default_flow_style=False) updateProgress(12.5) def readFromProject(self, resourceOpener): # Read map data with resourceOpener("map_tiles", "map") as f: self._tiles = map(lambda y: map(lambda x: int(x, 16), y.split(" ")), f.readlines()) updateProgress(25) # Read sector data self._mapSecTsetPalsTbl.clear(2560) self._mapSecMusicTbl.clear(2560) self._mapSecMiscTbl.clear(2560) pct = (25.0/2560) with resourceOpener("map_sectors", "yml") as f: input = yaml.load(f, Loader=yaml.CSafeLoader) for i in input: entry = input[i] self._mapSecTsetPalsTbl[i,0].setVal( (entry["Tileset"] << 3) | entry["Palette"]) self._mapSecMusicTbl[i,0].load(entry["Music"]) self._mapSecMiscTbl[i,1].load(entry["Item"]) self.teleport.load(entry["Teleport"]) self.townmap.load(entry["Town Map"]) self.setting.load(entry["Setting"]) self._mapSecMiscTbl[i,0].setVal((self.teleport.val() << 7) | (self.townmap.val() << 3) | self.setting.val()) updateProgress(pct)
class SpriteGroupModule(EbModule.EbModule): _name = "Sprite Groups" def __init__(self): self._grPtrTbl = EbTable(0xef133f) self._grPalTbl = EbTable(0xc30000) self._groups = None def freeRanges(self): return [(0x2f1a7f, 0x2f4a3f), (0x110000, 0x11ffff), (0x120000, 0x12ffff), (0x130000, 0x13ffff), (0x140000, 0x14ffff), (0x150000, 0x154fff)] def free(self): del(self._grPtrTbl) del(self._grPalTbl) def readFromRom(self, rom): self._grPtrTbl.readFromRom(rom) updateProgress(5) self._grPalTbl.readFromRom(rom) updateProgress(5) # Load the sprite groups self._groups = [] pct = 40.0/self._grPtrTbl.height() for i in range(self._grPtrTbl.height()): # Note: this assumes that the SPT is written contiguously numSprites = 8 # Assume that the last group only has 8 sprites if i < self._grPtrTbl.height()-1: numSprites = (self._grPtrTbl[i+1,0].val() - self._grPtrTbl[i,0].val() - 9) / 2 g = SpriteGroup(numSprites) g.readFromRom(rom, EbModule.toRegAddr(self._grPtrTbl[i,0].val())) self._groups.append(g) updateProgress(pct) def writeToProject(self, resourceOpener): # Write the palettes self._grPalTbl.writeToProject(resourceOpener) updateProgress(5) out = { } i = 0 pct = 40.0/len(self._groups) for g in self._groups: out[i] = g.dump() img = g.toImage(self._grPalTbl[g.palette(),0].val()) imgFile = resourceOpener("SpriteGroups/" + str(i).zfill(3), 'png') img.save(imgFile, 'png', transparency=0) imgFile.close() del(img) i += 1 updateProgress(pct) yaml.dump(out, resourceOpener("sprite_groups", "yml"), Dumper=yaml.CSafeDumper) updateProgress(5) def readFromProject(self, resourceOpener): self._grPalTbl.readFromProject(resourceOpener) updateProgress(5) input = yaml.load(resourceOpener("sprite_groups", "yml"), Loader=yaml.CSafeLoader) numGroups = len(input) self._groups = [] pct = 45.0/numGroups for i in range(numGroups): g = SpriteGroup(16) g.load(input[i]) try: img = Image.open( resourceOpener("SpriteGroups/" + str(i).zfill(3), "png")) except IOError: print "Could not load Sprite Group #" + str(i) raise if img.mode != 'P': raise RuntimeError("SpriteGroups/" + str(i).zfill(3) + " is not an indexed PNG.") g.fromImage(img) palData = img.getpalette() del(img) self._groups.append(g) pal = [ ] # Read the palette from the image for j in range(1, 16): pal.append((palData[j*3], palData[j*3+1], palData[j*3+2])) # Assign the palette number to the sprite for j in range(8): if pal == self._grPalTbl[j,0].val()[1:]: g.setPalette(j) break else: # Error, this image uses an invalid palette for j in range(8): print j, ":", self._grPalTbl[j,0].val()[1:] raise RuntimeError("Sprite Group #" + str(i) + " uses an invalid palette: " + str(pal)) updateProgress(pct) def writeToRom(self, rom): numGroups = len(self._groups) self._grPtrTbl.clear(numGroups) with DataBlock(sum(map( lambda x: x.blockSize(), self._groups))) as block: loc = 0 i = 0 # Write all the groups to the block, and sprites to rom pct = 40.0 / numGroups for g in self._groups: g.writeSpritesToFree(rom) g.writeToBlock(block, loc) self._grPtrTbl[i,0].setVal(loc) loc += g.blockSize() i += 1 updateProgress(pct) # Write the block to rom and correct the group pointers addr = EbModule.toSnesAddr(block.writeToFree(rom)) for i in range(self._grPtrTbl.height()): self._grPtrTbl[i,0].setVal( self._grPtrTbl[i,0].val() + addr) # Write the pointer table self._grPtrTbl.writeToRom(rom) updateProgress(5) # Write the palettes self._grPalTbl.writeToRom(rom) updateProgress(5) def upgradeProject(self, oldVersion, newVersion, rom, resourceOpenerR, resourceOpenerW): def replaceField(fname, oldField, newField, valueMap): if newField == None: newField = oldField valueMap = dict((k, v) for k,v in valueMap.iteritems()) with resourceOpenerR(fname, 'yml') as f: data = yaml.load(f, Loader=yaml.CSafeLoader) for i in data: if data[i][oldField] in valueMap: data[i][newField] = valueMap[data[i][oldField]].lower() else: data[i][newField] = data[i][oldField] if newField != oldField: del data[i][oldField] with resourceOpenerW(fname, 'yml') as f: yaml.dump(data, f, Dumper=yaml.CSafeDumper) def replaceFieldName(fname, oldField, newField): if newField == None: newField = oldField with resourceOpenerR(fname, 'yml') as f: data = yaml.load(f, Loader=yaml.CSafeLoader) for i in data: data[i][newField] = data[i][oldField] del data[i][oldField] with resourceOpenerW(fname, 'yml') as f: yaml.dump(data, f, Dumper=yaml.CSafeDumper) if oldVersion == newVersion: updateProgress(100) return elif oldVersion == 2: replaceField("sprite_groups", "Unknown A", "Size", { 0: "16x16", 1: "16x16 2", 2: "24x16", 3: "32x16", 4: "48x16", 5: "16x24", 6: "24x24", 7: "16x32", 8: "32x32", 9: "48x32", 10: "24x40", 11: "16x48", 12: "32x48", 13: "48x48", 14: "64x48", 15: "64x64", 16: "64x80" }) replaceFieldName("sprite_groups", "Unknown B", "Collision Settings") self.upgradeProject(oldVersion+1, newVersion, rom, resourceOpenerR, resourceOpenerW) elif oldVersion == 1: self.upgradeProject(oldVersion+1, newVersion, rom, resourceOpenerR, resourceOpenerW)