[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