예제 #1
0
    buf = buf + " |"
    print buf
    print "+------------+-------------+-------------+-------------+-------------+"
    color_address = 0x0
else:
    grayscale = True
    bitdepth = 2
    palette = None
    color_address = 0x0
    
#In some levels (e.g. Lava Lagoon, Slime Climb, Clapper's Cavern, Toxic Tower, Ugly Ducting, and Haunted Hollows,
#the water is shaded
rom.seek(level_header+3)
print "Reading bit 6 from from offset {:#x} for water shading.".format(level_header+3)
water_shade = rom_utilities.readbyte(rom)
water_shade = rom_utilities.bit_test(water_shade,6)
if water_shade == 1:
    print "Water is shaded in this level."
    rom.seek(level_header+5)
    water_level = rom_utilities.readbyte(rom)
    print "Reading water level from offset {:#x}.".format(level_header+5)
    print "Water level: {:#x} (from top, in multiples of 0x20 pixels)".format(water_level)
else:
    print "Water is not shaded in this level."
    water_level = 0xFF

#Write map tiles to a PNG file
if settings.maptiles == True:
    map_image = []
    for i in range(0x100):
        map_image.append([])
예제 #2
0
def decompress(f, offset, game, region, level, level_type):
    """Returns a decompressed map.
    f = file
    offset = Self explanatory.
    game = Name of game, used for the filename for logging.
    level = Name of level, used for the filename for logging.
    level_type = Type of level (e.g. Stilt), used for the filename for logging."""
    width = rom_utilities.readbyte(f)
    height = rom_utilities.readbyte(f)
    print "Width: {}; Height: {}".format(width,height)
    offset += 2

    read_map = True
    level_map = []
    for i in range(height):
        level_map.append([])
    x = 0
    y = 0
    buf = []

    #Log to external file for debugging
    if settings.maplog == True:
        if not os.path.exists(os.path.join(os.getcwd(), game)):
            os.makedirs(os.path.join(os.getcwd(), game))
        if not os.path.exists(os.path.join(os.getcwd(), game, 'Logs')):
            os.makedirs(os.path.join(os.getcwd(), game, 'Logs'))

        filename = "{}_{}_Map_{}_log.txt".format(game,region,level_info.level_names[game][level].replace(" ","_"))
        filename = os.path.join(os.getcwd(), game, 'Logs', filename)
        maplog = open(filename, "wb")
        print "Writing log of map decompression to {}.".format(filename)

        rom_utilities.write_log("Width: {}; Height: {}".format(width,height),maplog)
    else:
        maplog = None

    #Decompression starts here, read one nibble (4 bits) at a time
    nib = Nibble(f, maplog, offset, settings.maplog)

    while read_map == True:
        nibble = nib.readnibble()
        rom_utilities.write_log("Nibble read: {:#x}".format(nibble),maplog)
        #Nibbles 0xC, 0xD, 0xE, and 0xF are special cases, as are nibble pairs 0xBE and 0xBF
        #(Note: Figuring this out would have been IMPOSSIBLE without BGB's debugger!!)
        if nibble == 0xB:
            temp = nibble
            nibble = nib.readnibble()
            rom_utilities.write_log("Nibble read: {:#x}".format(nibble),maplog)
            if nibble < 0xE: #Normal byte, this means nibble pair is from 0xB0-0xBD -- this is uncompressed
                buf.append((temp << 4) + nibble)
            elif nibble == 0xE: #0xBE
                #Decompress certain tiles, increasing tile value by 1 each time (useful for long tile structures)
                tile = nib.readnibble(count=2)
                rom_utilities.write_log("Tile: {:#04x}".format(tile),maplog)
                length = nib.readnibble()
                rom_utilities.write_log("Length: {:#x} + 0x3 -> {:#x}".format(length,length+0x3),maplog)
                for i in range(length+3):
                    buf.append(tile)
                    tile += 1
                    
            elif nibble == 0xF: #0xBF
                #Meant for 32x16-tile structures
                tile1 = nib.readnibble(count=2)
                tile2 = nib.readnibble(count=2)

                length = nib.readnibble()

                rom_utilities.write_log("Tile 1: {:#04x}; Tile 2: {:#04x}; Length: {:#x} + 0x2 -> {:#x}".format(tile1, tile2, length, length+0x2),maplog)
                for i in range(length+2):
                    buf.append(tile1)
                    buf.append(tile2)
        elif nibble == 0xC:
            #Repeat previously used tile sequence, allows the same structure over and over
            rew = nib.readnibble(count=2)

            if rew % 2 == 1: #Odd number
                rew = rew / 2 + 1
                rew = rew + (nib.readnibble() << 7)
            else: #Even number
                rew = rew / 2 + 1

            rom_utilities.write_log("Rewind: {:#04x}".format(rew),maplog)
            
            length = nib.readnibble()

            if length == 0xF:
                length = nib.readnibble(count=2)

            rom_utilities.write_log("Length: {:#04x} + 0x04 -> {:#04x}".format(length, length+0x4),maplog)

            for i in range(length+4):
                ptr = y*width+x - rew
                ptr_y = ptr / width
                ptr_x = ptr % width
                tile = level_map[ptr_y][ptr_x]
                rom_utilities.write_log("Pointer: {}; x: {}; y: {}; tile: {:#04x}; RAM: {:#x} -> {:#x}".format(ptr, ptr_x, ptr_y, tile, 0xC600+(height+4)*2+ptr, 0xC600+(height+4)*2+y*width+x),maplog)
                if x >= width:
                    y += 1
                    x = 0
                try:
                    level_map[y].append(tile)
                except IndexError:
                    if settings.warnings == True:
                        #Error handler
                        print "WARNING: Out of range!\n\
There are more bytes than the map can handle.\n\
This is either due to a badly hacked ROM, a bug in the game itself,\n\
or an error in the program's decompression function.\n\
If this results in a buggy map, please report this.\n\
x: {}; y: {}; i: {}; buffer: {}; map width: {}; map height: {}; RAM: {:#x}; ROM: {:#x}\n\
Press Enter to continue or Ctrl+C to quit.".format(x,y,i,buf,len(level_map[0]),len(level_map),0xC600+(height+4)*2+y*width+x,offset)
                    read_map = False
                    byte = 0xEE
                    f.seek(1,1)
                    raw_input()
                    break
                x += 1

        elif nibble == 0xD:
            #Here, tiles only take up four bits -- useful when lots of tiles in a row are next to each other
            high_nibble = nib.readnibble() << 4
            rom_utilities.write_log("High nibble: {:#x}".format(high_nibble),maplog)
            length = nib.readnibble(count=2)
            rom_utilities.write_log("Length: {:#04x} + 0x14 -> {:#x}".format(length,length+0x14),maplog)

            for i in range(length+0x14):
                low_nibble = nib.readnibble()
                tile = high_nibble | low_nibble
                rom_utilities.write_log("Low nibble: {:#x}; Tile: {:#04x}".format(low_nibble, tile),maplog)
                buf.append(tile)
        
        elif nibble == 0xE:
            #Same case as before, but for shorter structures (also takes slightly up less space in ROM)
            high_nibble = nib.readnibble() << 4
            rom_utilities.write_log("High nibble: {:#x}".format(high_nibble),maplog)
            if high_nibble == 0xE0: #We are done!! Stop reading map tiles
                read_map = False
            else:
                length = nib.readnibble()

                rom_utilities.write_log("Length: {:#x} + 0x4 -> {:#x}".format(length,length+0x4),maplog)

                for i in range(length+4):
                    low_nibble = nib.readnibble()
                    tile = high_nibble | low_nibble
                    rom_utilities.write_log("Low nibble: {:#x}; Tile: {:#04x}".format(low_nibble, tile),maplog)
                    buf.append(tile)

        elif nibble == 0xF:
            #Same tile repeated over and over
            tile = nib.readnibble(count=2)
            length = nib.readnibble()

            rom_utilities.write_log("Tile: {:#04x}".format(tile),maplog)

            if length >= 8:
                length = ((length & 0x7) << 4) + nib.readnibble()
                rom_utilities.write_log("Length: {:#04x} + 0x0b -> {:#04x}".format(length,length+0xb),maplog)

                for i in range(length+11):
                    buf.append(tile)
            else:
                rom_utilities.write_log("Length: {:#x} + 0x3 -> {:#x}".format(length,length+0x3),maplog)
                for i in range(length+3):
                    buf.append(tile)
        else: #Normal case!! Uncompressed tile (0x00-0xAF)
            temp = nibble
            nibble = nib.readnibble()
            rom_utilities.write_log("Nibble read: {:#x}".format(nibble),maplog)
            tile = (temp << 4) + nibble
            buf.append(tile)
            rom_utilities.write_log("Uncompressed tile! ({:#04x})".format(tile),maplog)
        if len(buf) > 1:
            rom_utilities.write_log("Buffer: {}; x: {}; y: {}; RAM: {:#x} - {:#x}".format(buf,x,y,0xC600+(height+4)*2+y*width+x,0xC5FF+(height+4)*2+y*width+x+len(buf)),maplog)
        elif len(buf) == 1:
            rom_utilities.write_log("Buffer: {}; x: {}; y: {}; RAM: {:#x}".format(buf,x,y,0xC600+(height+4)*2+y*width+x),maplog)
        else:
            rom_utilities.write_log("x: {}; y: {}; RAM: {:#x}".format(x-1,y,0xC5FF+(height+4)*2+y*width+x),maplog)
        #Write the tiles
        for i in buf:
            if x >= width:
                y += 1
                x = 0
            try:
                level_map[y].append(i)
            except IndexError:
                if settings.warnings == True:
                    #Error handler
                    print "WARNING: Out of range!\n\
There are more bytes than the map can handle.\n\
This is either due to a badly hacked ROM, a bug in the game itself,\n\
or an error in the program's decompression function.\n\
If this results in a buggy map, please report this.\n\
x: {}; y: {}; i: {}; buffer: {}; map width: {}; map height: {}; RAM: {:#x}; ROM: {:#x}\n\
Press Enter to continue or Ctrl+C to quit.".format(x,y,i,buf,len(level_map[0]),len(level_map),0xC600+(height+4)*2+y*width+x,offset)
                read_map = False
                nib.byte = 0xEE
                f.seek(1,1)
                raw_input()
                break
            x += 1
        buf = []

    print "Done decompressing the map tiles."
    if settings.maplog == True:
        print "Done writing to log."
        maplog.close()
    #print level_map

    print "Note: Sprites are not supported yet."

    #Initial code for sprites
    if nib.byte & 0xF0 == 0xE0 and nib.byte != 0xE0 and nib.byte != 0xEE:
        #Not sure if this ever actually occurs, but it imitates the game
        #This function exists because sometimes the EE string ends on a high nibble (e.g. _E E_); sometimes on a low one (e.g. __ EE __)
        f.seek(-1,1)
    #Initially, any tile with a sprite is initialized to 0x80. We must find the sprite table to use the correct graphics (and add sprites when that gets implemented)
    #Sprite pointers take place just after the initial map data, and they are read each time the game finds a tile with value 0x80 or greater
    for i,v in enumerate(level_map):
        for j,w in enumerate(level_map[i]):
            tile_id = w
            if tile_id == 0xFF:
                break
            if rom_utilities.bit_test(tile_id,7) == 1:
                sprite_id = rom_utilities.readbyte(f)
                level_map[i][j] = sprite_id

    sprite_address = rom_utilities.get_abs_ptr(f, 0x10, 0x4000B, level_type)
    sprite_table = []
    #Now the sprite pointers are used to find tuples with the right tile graphics and the sprites (latter is not implemented yet)
    for i,v in enumerate(level_map):
        for j,w in enumerate(level_map[i]):
            sprite_id = w
            if rom_utilities.bit_test(sprite_id,7) == 1:
                sprite_id &= 0x7F
                f.seek(sprite_address + sprite_id*3)
                tile_id = rom_utilities.readbyte(f)
                level_map[i][j] = tile_id
                sprite_id = struct.unpack("<BB",f.read(2))
                sprite_table.append((i,j,sprite_id[0],sprite_id[1]))
                #Used to implement sprites later on!!!
    return width, height, level_map, sprite_table
예제 #3
0
print "Sprite offset: {:#x}".format(offset)
if grayscale == False:
    offset_color = 0x34800 + (denjuu_no-1)*8
    print "Color palette offset: {:#x}".format(offset_color)

rom.seek(offset)
image=rom.read(0x380)
imageheight = 56
imagewidth = 64
pixelmap = []
for i in range(imageheight):
    pixelmap.append([])
for i in range(0x1C0):
    v_pos = i / 0x40 * 0x8 + i % 0x8
    for j in range(7,-1,-1):
        color = 3 - (rom_utilities.bit_test(ord(image[i*2]),j) + 2 * rom_utilities.bit_test(ord(image[i*2+1]),j))
        if inverted == False:
            pixelmap[v_pos].append(color)
        else:
            pixelmap[v_pos].insert(0,color)
if grayscale == False:
    rom.seek(offset_color)
    rawpalettes=rom.read(8)
    palette=[]
    rawcolors=[]
    s = "["
    for i in range(4):
        color = struct.unpack("<H",rawpalettes[i*2:i*2+2])[0]
        s = "{0}0x{1:0>4X}, ".format(s, color)
        color = rom_utilities.gbc2rgb(color)
        palette.insert(0,color)