[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: 48K stage-loaded to 64K single-load?



Michael <michael.pohoreski@gmail.com> writes:

> On May 9, 6:57 am, Steve Nickolas
> <lyricalnan...@usotsuki.hoshinet.org> wrote:
>> Some games have been cracked to use the language card or Apple //e
>> extended memory to allow themselves to be single-loaded into memory.
>
> Sounds like a fun project!  Please keep us posted -- Lode Runner is
> one of my favorites!
>
> Each Tile is 10x11 pixels. Each Map is 28x16 tiles = 448 bytes/level =
> 67,200 bytes uncompressed.
>
> I would try compressing the levels.  Even using RLE you should be able
> to save some significant space.
> i.e. top NIB = Length, bot NIB = Sprite
>
> If you can't store all 150 levels on one file, I wouldn't mind if
> there were 3 files.
>   Part A = Levels 1 - 50
>   Part B = Levels 51 - 100
>   Part C = Levels 101 - 150
>
> I haven't gotten around to doing an Apple Games Disassembly Project
> for it, yet.  I still want to annotate the sprites, map drawing code,
> analyze the AI and write a C version, along with the cool "Iris In/
> Out" level transition. =)
>
> If there are any features in AppleWin's Debugger that would help this
> project, let me know, and I'll bump up its priority.

On a somewhat related note, years ago I wrote a little Python program to
produce PNG files of all the levels.

I'll post it here, even though this isn't the programmer's group, since
it's easy to use to map out all the levels (including the hidden trap
doors).

Here's how to use it:

$ ./lrLevel.py LRLEVS2.DSK
Usage: lrLevel.py [options] disk-image LEVEL-NUMBER [ENDING-LEVEL]

Options:
  -h, --help            show this help message and exit
  -s SKEW, --skew=SKEW  Set sector skew on disk image

$ ./lrLevel.py LRLEVS2.DSK 1 50
1
2
...
50
$ ls
level_001.png  level_012.png  level_023.png  level_034.png  level_045.png
level_002.png  level_013.png  level_024.png  level_035.png  level_046.png
level_003.png  level_014.png  level_025.png  level_036.png  level_047.png
level_004.png  level_015.png  level_026.png  level_037.png  level_048.png
level_005.png  level_016.png  level_027.png  level_038.png  level_049.png
level_006.png  level_017.png  level_028.png  level_039.png  level_050.png
level_007.png  level_018.png  level_029.png  level_040.png  lrLevel.py*
level_008.png  level_019.png  level_030.png  level_041.png  LRLEVS2.DSK
level_009.png  level_020.png  level_031.png  level_042.png
level_010.png  level_021.png  level_032.png  level_043.png
level_011.png  level_022.png  level_033.png  level_044.png
$


-----

#!/usr/bin/env python
"""
2009 Jerry Penner  (awanderin@yahoo.ca)

Freely use as you wish.


----------------------------------------

Convert raw LODE RUNNER levels into images.

By default, it reads them from DOS 3.3-order disk images.

The level data starts on track 3 (offset 0x3000) with one level
per 256-byte sector.
"""
import Image, optparse, sys


# ------------------------------------------------------------

def makeSprites():
    """
    Each Lode Runner cell is a 20x22 pixel image.  Colors may be black
    (_), blue (B), red (R), or white (W).
    """
    colors = { '_': (  0,   0,   0),
               'B': ( 34,  34, 255),
               'R': (255, 102,   0),
               'W': (255, 255, 255),
               }
    rawData = ['____________________'  # 0: blank
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________',
               'BBBBBBBBBB______BBBB'  # 1: normal brick
               'BBBBBBBBBB______BBBB'
               'BBBBBBBBBB______BBBB'
               'BBBBBBBBBB______BBBB'
               'BBBBBBBBBB______BBBB'
               'BBBBBBBBBB______BBBB'
               'BBBBBBBBBB______BBBB'
               'BBBBBBBBBB______BBBB'
               '____________________'
               '____________________'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               'BB______BBBBBBBBBBBB'
               '____________________'
               '____________________',
               'BBBBBBBBBBBBBBBBBBBB'  # 2: solid brick
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               '____________________'
               '____________________',
               '__WWWW________WWWW__'  # 3: ladder
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__',
               '____________________'  # 4: climbing bar
               '____________________'
               'WWWWWWWWWWWWWWWWWWWW'
               'WWWWWWWWWWWWWWWWWWWW'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________',
               'BBBBBBBBBBBBBBBBBBBB'  # 5: trap-door brick
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               '____________________'
               '____________________'
               '____WWWWWWWWWWWW____'
               '____WWWWWWWWWWWW____'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               'BBBBBBBBBBBBBBBBBBBB'
               '____________________'
               '____________________',
               '__WWWW______________'  # 6: hidden/appearing ladder
               '__WWWW______________'
               '__WWWW______________'
               '__WWWW______________'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '______________WWWW__'
               '______________WWWW__'
               '______________WWWW__'
               '______________WWWW__'
               '__WWWW________WWWW__'
               '__WWWW________WWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWWWWWWWWWWWWWW__'
               '__WWWW______________'
               '__WWWW______________'
               '__WWWW______________'
               '__WWWW______________'
               '__WWWW______________'
               '__WWWW______________',
               '____________________'  # 7: gold
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '____________________'
               '______WWWWWWWWWW____'
               '______WWWWWWWWWW____'
               '______WW__WW__WW____'
               '______WW__WW__WW____'
               '______WWWWWWWWWW____'
               '______WWWWWWWWWW____'
               '______WW__WW__WW____'
               '______WW__WW__WW____'
               '______WWWWWWWWWW____'
               '______WWWWWWWWWW____'
               '____________________'
               '____________________',
               '________BB__________'  # 8: enemy
               '________BB__________'
               '______RRRRRR________'
               '______RRRRRR________'
               '______RRRRRR________'
               '______RRRRRR________'
               '__________RR________'
               '__________RR________'
               '______RRRRRRRRRR____'
               '______RRRRRRRRRR____'
               '__RR______RR______RR'
               '__RR______RR______RR'
               '__________WWWW______'
               '__________WWWW______'
               '________WWWWWW______'
               '________WWWWWW______'
               '______WWWW__WWWWWWWW'
               '______WWWW__WWWWWWWW'
               '______WWWW__________'
               '______WWWW__________'
               '______WWWW__________'
               '______WWWW__________',
               '____________BB______'  # 9: player
               '____________BB______'
               '__________WWWWWW____'
               '__________WWWWWW____'
               '________WWWWWW______'
               '________WWWWWW______'
               '____WWWW__WWWWWW____'
               '____WWWW__WWWWWW____'
               '__WWWW__WWWW____WWWW'
               '__WWWW__WWWW____WWWW'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWW________'
               '________WWWWWW______'
               '________WWWWWW______'
               '__WWWWWWWW__WWWW____'
               '__WWWWWWWW__WWWW____'
               '____________WWWW____'
               '____________WWWW____'
               '____________WWWW____'
               '____________WWWW____',
               ]
    WIDTH = 20
    HEIGHT = 22

    sprites = []

    for raw in rawData:
        sprite = Image.new( 'RGB', (WIDTH, HEIGHT) )
        for y in range( HEIGHT ):
            for x in range( WIDTH ):
                ix = y * WIDTH + x
                sprite.putpixel( (x, y), colors[raw[ix]] )
        sprites.append( sprite )

    return sprites

               
# ------------------------------------------------------------

def convertLevel( levelData, sprites ):
    levelImg = Image.new( 'RGB', (28 * width, 16 * height) )
    
    ix = 0
    for row in range( 16 ):
        for col in range( 0, 28, 2 ):
            byte = ord( data[ix] )
            ix += 1
            left = byte & 0x0f
            right = (byte >> 4) & 0x0f
            levelImg.paste( sprites[left], (col * width, row * height) )
            levelImg.paste( sprites[right], ((col + 1) * width, row * height) )
            

    levelImg.save( 'level_%03d.png' % level )


# ------------------------------------------------------------

def buildSkewTable( skew, maxSectors = 16 ):
    skewTable = [-1] * maxSectors
    skewTable[0] = 0

    for sector in range( 1, maxSectors ):
        newSector = skewTable[sector - 1] + skew
        if newSector < 0:
            newSector += maxSectors - 1
        if newSector >= maxSectors:
            newSector -= maxSectors
        while newSector in skewTable:
            newSector += 1
        skewTable[sector] = newSector

    return skewTable


# ------------------------------------------------------------

if __name__ == '__main__':
    usage = 'Usage: %prog [options] disk-image LEVEL-NUMBER [ENDING-LEVEL]'
    parser = optparse.OptionParser( usage )
    parser.add_option( '-s', '--skew', dest = 'skew', default = 1,
                       help = 'Set sector skew on disk image' )

    opts, args = parser.parse_args()

    if len( args ) not in [2, 3]:
        parser.print_help()
        sys.exit( 2 )
        
    diskImage = args[0]
    endLevel = startLevel = int( args[1] )
    if len( args ) == 3:
        endLevel = int( args[2] )

    skew = buildSkewTable( int( opts.skew ) )
    sprites = makeSprites()

    for level in range( startLevel, endLevel + 1 ):
        print level
        offset = 256 * ((3 + (level - 1) / 16) * 16 + skew[level % 16])
        fp = file( diskImage, 'r' )
        fp.seek( offset )
        data = fp.read( 256 )
        fp.close()

        width = sprites[0].size[0]
        height = sprites[0].size[1]

        convertLevel( data, sprites )


# ------------------------------------------------------------

-----

-- 
--
Jerry    awanderin at yahoo dot ca