nbsdgames/display/playback.py
2022-02-03 13:46:22 +03:30

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])