# https://github.com/alexandergitter/theme-hospital-spec/blob/master/format-specification.md
class Reader:
def __init__(self, fname):
self.load(fname)
def load(self, fname):
handle = open(fname, 'rb')
self.data = handle.read()
self.offset = 0
handle.close()
def reset(self):
self.offset = 0
def size(self):
return len(self.data)
def get(self, index):
return self.data[index]
def byte(self):
assert self.offset < self.size()
b = self.data[self.offset]
self.offset = self.offset + 1
return b
def word(self):
b = self.byte()
c = self.byte()
return b | (c << 8)
def long(self):
b = self.word()
c = self.word()
return b | (c << 16)
class Palette:
def __init__(self):
self.palette = None
def load(self, reader):
reader.reset()
assert reader.size() == 3 * 256
self.palette = []
for i in range(256):
r = reader.byte()
g = reader.byte()
b = reader.byte()
self.palette.append((r, g, b))
def make_palette(fname):
reader = Reader(fname)
p = Palette()
p.load(reader)
return p
class Frame:
"""
Meta information of a single frame.
frame_offset: Offset of the frame in the image file.
width: Width of the image
height: Height of the image
soundindex: If not 0, index into the filetable of the sounds data file
start_animation: Whether this frame is the start of an animation
next_frame: Index of the next frame in the L{Frames} list.
"""
def __init__(self, listindex, width, height, soundindex, flags, nextindex):
self.frame_offset = listindex * 2
self.width = width
self.height = height
self.soundindex = soundindex
self.start_animation = (flags & 1) != 0
self.next_frame = nextindex
class Frames:
"""
All meta information of fromes in a file.
"""
def __init__(self):
self.frames = []
def load(self, reader):
reader.reset()
length = reader.size()
assert length % 10 == 0
self.frames = []
while length > 0:
lind = reader.long()
w = reader.byte()
h = reader.byte()
sindex = reader.byte()
flags = reader.byte()
nextframe = reader.word()
self.frames.append(Frame(lind, w, h, sindex, flags, nextframe))
length = length - 10
class FrameList:
def __init__(self):
self.list = []
def load(self, reader):
reader.reset()
assert reader.size() % 2 == 0
self.list = []
for i in range(reader.size() // 2):
w = reader.word()
if w == 0xffff:
self.list.append(None)
else:
self.list.append(w) # Position in the sprite elements.
class SpriteElement:
"""
sprite_pos: Absolute position in the sprite table.
offset_x: Horizontal offset of the image.
offset_y: Vertical offset of the image.
layer_class: Layer class.
flags: Render flags.
layerid: Layer of this sprite.
"""
def __init__(self, sprite_pos, offset_x, offset_y, layer_class, flags, layerid):
self.sprite_pos = sprite_pos
self.offset_x = offset_x
self.offset_y = offset_y
self.layer_class = layer_class
self.flags = flags
self.layerid = layerid
def flip_vert(self):
"""Whether to flip the image vertically on rendering"""
return (self.flags & 1) != 0
def flip_hor(self):
"""Whether to flip the image horizontally on rendering"""
return (self.flags & 2) != 0
def alpha50(self):
"""Whether to draw the inage with 50% alpha."""
return (self.flags & 4) != 0
def alpha75(self):
"""Whether to draw the inage with 75% alpha."""
return (self.flags & 8) != 0
class SpriteElements:
def __init__(self):
self.elements = []
# XXX Finish me!
class SpriteTableElement:
def __init__(self, offset, width, height):
self.offset = offset
self.width = width
self.height = height
def __str__(self):
return "offset={}, width={}, height={}".format(self.offset, self.width, self.height)
class SpriteTable:
def __init__(self):
self.sprites = []
def load(self, reader):
reader.reset()
length = reader.size()
print(length)
assert length % 6 == 0
self.sprites = []
while length > 0:
offset = reader.long()
width = reader.byte()
height = reader.byte()
length = length - 6
self.sprites.append(SpriteTableElement(offset, width, height))
def MakeSpriteTable(fname):
reader = Reader(fname)
sprt = SpriteTable()
sprt.load(reader)
return sprt
#data = load('CD/HOSP/DATA/VFRA-1.ANI')
#Looks like Frames
pal = make_palette('MPALETTE.DAT.decoded')
dat = Reader('VSPR-0.DAT.decoded')
sprt = MakeSpriteTable("VSPR-0.TAB.decoded")
print("Number of sprites: {}".format(len(sprt.sprites)))
#print("\n".join(str(s) for s in sprt.sprites))
from PIL import Image
# Raw graphics
def raw_graphics(spr, pal, reader):
print(spr)
im = Image.new("RGBA", (spr.width, spr.height), (0, 0, 0, 0))
offset = spr.offset
for y in range(spr.height):
for x in range(spr.width):
v = reader.get(offset)
col = pal.palette[v]
col = (col[0], col[1], col[2], 255)
offset = offset + 1
im.putpixel((x, y), col)
return im
def extra_chunk_graphics(spr, pal, reader):
if spr.width == 0 or spr.height == 0:
return None
print(spr)
im = Image.new("RGBA", (spr.width, spr.height), (200, 0, 200, 255))
offset = spr.offset
x, y = 0, 0
while y < spr.height:
v = reader.get(offset)
offset = offset + 1
if v == 0: # End of the row.
x, y = 0, y+1
continue
if v <= 63: # v opaque pixels
while v > 0:
w = reader.get(offset)
offset = offset + 1
col = pal.palette[w]
col = (col[0], col[1], col[2], 255)
im.putpixel((x, y), col)
x = x + 1
if x >= spr.width:
x, y = 0, y+1
if y >= spr.height:
break;
v = v - 1
continue
if v <= 127: # (v-60) times the same pixel
v = v - 60
w = reader.get(offset)
offset = offset + 1
col = pal.palette[w]
col = (col[0], col[1], col[2], 255)
while v > 0:
im.putpixel((x, y), col)
x = x + 1
if x >= spr.width:
x, y = 0, y+1
if y >= spr.height:
break;
v = v - 1
continue
if v < 192: # (v-128) transparent pixels
v = v - 128
while v > 0:
x = x + 1
if x >= spr.width:
x, y = 0, y+1
if y >= spr.height:
break;
v = v - 1
continue
if v < 255: # (v - 124) times the same pixel
v = v - 124
w = reader.get(offset)
offset = offset + 1
col = pal.palette[w]
col = (col[0], col[1], col[2], 255)
while v > 0:
im.putpixel((x, y), col)
x = x + 1
if x >= spr.width:
x, y = 0, y+1
if y >= spr.height:
break;
v = v - 1
continue
assert v == 255
V = reader.get(offset)
w = reader.get(offset)
col = pal.palette[w]
col = (col[0], col[1], col[2], 255)
while v > 0:
im.putpixel((x, y), col)
x = x + 1
if x >= spr.width:
x, y = 0, y+1
if y >= spr.height:
break;
v = v - 1
return im
for sprite in [5, 21, 345]:
#im = raw_graphics(sprt.sprites[sprite], pal, dat)
im = extra_chunk_graphics(sprt.sprites[sprite], pal, dat)
im.save('sprite_{}.png'.format(sprite))