mirror of
https://github.com/abakh/nbsdgames
synced 2025-04-28 14:09:32 -04:00
205 lines
6.6 KiB
Python
205 lines
6.6 KiB
Python
#! /usr/bin/env python
|
|
|
|
import sys, os, gzip
|
|
from socket import *
|
|
from select import select
|
|
import io, struct, zlib
|
|
import time
|
|
sys.path.insert(0, os.pardir)
|
|
from common.msgstruct import *
|
|
from common import hostchooser
|
|
from . import modes
|
|
from .modes import KeyPressed, KeyReleased
|
|
|
|
#import psyco; psyco.full()
|
|
|
|
SOURCEDIR = os.pardir
|
|
|
|
|
|
def loadpixmap(dpy, data, colorkey=None):
|
|
f = io.StringIO(data)
|
|
sig = f.readline().strip()
|
|
assert sig == "P6"
|
|
while 1:
|
|
line = f.readline().strip()
|
|
if not line.startswith('#'):
|
|
break
|
|
wh = line.split()
|
|
w, h = list(map(int, wh))
|
|
sig = f.readline().strip()
|
|
assert sig == "255"
|
|
data = f.read()
|
|
f.close()
|
|
if colorkey is None:
|
|
colorkey = -1
|
|
elif colorkey < 0:
|
|
r, g, b = struct.unpack("BBB", self.data[:3])
|
|
colorkey = b | (g<<8) | (r<<16)
|
|
return dpy.pixmap(w, h, data, colorkey)
|
|
|
|
class Icon:
|
|
def __init__(self, bitmap, xxx_todo_changeme, alpha):
|
|
(x, y, w, h) = xxx_todo_changeme
|
|
self.rect = x, y, w, h
|
|
self.size = w, h
|
|
self.bitmap = bitmap
|
|
self.alpha = alpha
|
|
|
|
|
|
class Playback:
|
|
gameident = 'Playback'
|
|
|
|
def __init__(self, filename, mode=('x', 'off', {})):
|
|
f = gzip.open(filename, 'rb')
|
|
self.screenmode = mode
|
|
self.width = None
|
|
self.deffiles = {}
|
|
self.defbitmaps = {}
|
|
self.deficons = {}
|
|
self.icons = {}
|
|
self.frames = []
|
|
inbuf = ''
|
|
while 1:
|
|
values, inbuf = decodemessage(inbuf)
|
|
if not values:
|
|
# incomplete message
|
|
data = f.read(8192)
|
|
if not data:
|
|
break
|
|
inbuf += data
|
|
else:
|
|
#print values[0],
|
|
fn = Playback.MESSAGES.get(values[0], self.msg_unknown)
|
|
fn(self, *values[1:])
|
|
print('%d frames in file.' % len(self.frames))
|
|
f.close()
|
|
assert self.width, "no playfield definition found in file"
|
|
|
|
self.dpy = modes.open_dpy(self.screenmode,
|
|
self.width, self.height, self.gameident)
|
|
self.dpy.clear() # backcolor is ignored
|
|
self.sprites = []
|
|
self.buildicons()
|
|
self.go(0)
|
|
|
|
def buildicons(self):
|
|
bitmaps = {}
|
|
for bmpcode, (data, colorkey) in list(self.defbitmaps.items()):
|
|
if isinstance(data, str):
|
|
data = zlib.decompress(data)
|
|
else:
|
|
data = self.deffiles[data]
|
|
bitmaps[bmpcode] = loadpixmap(self.dpy, data, colorkey)
|
|
for icocode, (bmpcode, rect, alpha) in list(self.deficons.items()):
|
|
self.icons[icocode] = Icon(bitmaps[bmpcode], rect, alpha)
|
|
|
|
def go(self, n):
|
|
self.n = n
|
|
self.update_sprites(self.frames[n])
|
|
self.dpy.flip()
|
|
|
|
def save(self, filename=None):
|
|
"shm only!"
|
|
w, h, data, reserved = self.dpy.getppm((0, 0, self.width, self.height))
|
|
f = open(filename or ('frame%d.ppm' % self.n), 'wb')
|
|
print('P6', file=f)
|
|
print(w, h, file=f)
|
|
print(255, file=f)
|
|
for i in range(0, len(data), 4):
|
|
f.write(data[i+2]+data[i+1]+data[i])
|
|
f.close()
|
|
|
|
def update_sprites(self, udpdata):
|
|
sprites = self.sprites
|
|
unpack = struct.unpack
|
|
base = 0
|
|
for j in range(len(sprites)):
|
|
if sprites[j][0] != udpdata[base:base+6]:
|
|
removes = sprites[j:]
|
|
del sprites[j:]
|
|
removes.reverse()
|
|
eraser = self.dpy.putppm
|
|
for reserved, eraseargs in removes:
|
|
eraser(*eraseargs)
|
|
break
|
|
base += 6
|
|
try:
|
|
overlayer = self.dpy.overlayppm
|
|
except AttributeError:
|
|
getter = self.dpy.getppm
|
|
setter = self.dpy.putppm
|
|
#print "%d sprites redrawn" % (len(udpdata)/6-j)
|
|
for j in range(base, len(udpdata)-5, 6):
|
|
info = udpdata[j:j+6]
|
|
x, y, icocode = unpack("!hhh", info[:6])
|
|
try:
|
|
ico = self.icons[icocode]
|
|
sprites.append((info, (x, y, getter((x, y) + ico.size))))
|
|
setter(x, y, ico.bitmap, ico.rect)
|
|
except KeyError:
|
|
#print "bad ico code", icocode
|
|
pass # ignore sprites with bad ico (probably not defined yet)
|
|
else:
|
|
for j in range(base, len(udpdata)-5, 6):
|
|
info = udpdata[j:j+6]
|
|
x, y, icocode = unpack("!hhh", info[:6])
|
|
try:
|
|
ico = self.icons[icocode]
|
|
overlay = overlayer(x, y, ico.bitmap, ico.rect, ico.alpha)
|
|
sprites.append((info, overlay))
|
|
except KeyError:
|
|
#print "bad ico code", icocode
|
|
pass # ignore sprites with bad ico (probably not defined yet)
|
|
|
|
def msg_unknown(self, *rest):
|
|
pass
|
|
|
|
def msg_patch_file(self, fileid, position, data, lendata=None, *rest):
|
|
try:
|
|
s = self.deffiles[fileid]
|
|
except KeyError:
|
|
s = ''
|
|
if len(s) < position:
|
|
s += '\x00' * (position-len(s))
|
|
s = s[:position] + data + s[position+len(s):]
|
|
self.deffiles[fileid] = s
|
|
|
|
def msg_zpatch_file(self, fileid, position, data, *rest):
|
|
data1 = zlib.decompress(data)
|
|
self.msg_patch_file(fileid, position, data1, len(data), *rest)
|
|
|
|
def msg_md5_file(self, fileid, filename, position, length, checksum, *rest):
|
|
fn = os.path.join(SOURCEDIR, filename)
|
|
f = open(fn, 'rb')
|
|
f.seek(position)
|
|
data = f.read(length)
|
|
f.close()
|
|
assert len(data) == length
|
|
self.msg_patch_file(fileid, position, data)
|
|
|
|
def msg_def_playfield(self, width, height, *rest):
|
|
self.width, self.height = width, height
|
|
|
|
def msg_def_icon(self, bmpcode, icocode, x, y, w, h, alpha=255, *rest):
|
|
self.deficons[icocode] = bmpcode, (x, y, w, h), alpha
|
|
|
|
def msg_def_bitmap(self, bmpcode, data, colorkey=None, *rest):
|
|
self.defbitmaps[bmpcode] = data, colorkey
|
|
|
|
def msg_recorded(self, data):
|
|
self.frames.append(data)
|
|
|
|
MESSAGES = {
|
|
MSG_PATCH_FILE : msg_patch_file,
|
|
MSG_ZPATCH_FILE : msg_zpatch_file,
|
|
MSG_MD5_FILE : msg_md5_file,
|
|
MSG_DEF_PLAYFIELD: msg_def_playfield,
|
|
MSG_DEF_ICON : msg_def_icon,
|
|
MSG_DEF_BITMAP : msg_def_bitmap,
|
|
MSG_RECORDED : msg_recorded,
|
|
}
|
|
|
|
|
|
if __name__ == '__main__' and len(sys.argv) > 1:
|
|
p = Playback(sys.argv[1])
|