aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/themeparser.py242
1 files changed, 192 insertions, 50 deletions
diff --git a/tools/themeparser.py b/tools/themeparser.py
index d462280b87..98fefcb314 100644
--- a/tools/themeparser.py
+++ b/tools/themeparser.py
@@ -32,6 +32,31 @@ import xml.dom.minidom as DOM
import struct
from UserDict import UserDict
+def printBinaryDump(buf):
+ LINE_WIDTH = 16
+
+ def buildRepr(line):
+ out = ""
+ for c in line:
+ out += '.' if ord(c) < 0x20 or ord(c) > 0x7E else c
+ return out
+
+ print "Binary dump of %d BYTES:\n" % len(buf)
+ for i in xrange(len(buf) // LINE_WIDTH):
+ bufLine = buf[LINE_WIDTH * i : LINE_WIDTH * (i + 1)]
+
+ print "| %s | %s |" % (" ".join(["%0.2X" % ord(c) for c in bufLine]), buildRepr(bufLine))
+
+ bufLine = buf[-(len(buf) % LINE_WIDTH):]
+ print "| %s%s | %s%s |" % (
+ " ".join(["%0.2X" % ord(c) for c in bufLine]),
+ ' ' * (LINE_WIDTH - len(bufLine)),
+ buildRepr(bufLine),
+ ' ' * (LINE_WIDTH - len(bufLine))
+ )
+
+ print ""
+
def pbin(data):
return str(map(lambda c: hex(ord(c)), data))
# return " ".join(["%0.2X" % ord(c) for c in data])
@@ -43,6 +68,12 @@ class STXBinaryFile:
class InvalidResolution(Exception):
pass
+ class InvalidFontIdentifier(Exception):
+ pass
+
+ class InvalidBitmapName(Exception):
+ pass
+
class DrawStepData:
def __init__(self, isDefault, packFormat, function):
self.isDefault = isDefault
@@ -54,12 +85,10 @@ class STXBinaryFile:
TRUTH_VALUES = {"" : 0, "true" : 1, "false" : 0, "True" : 1, "False" : 0}
BLOCK_HEADERS = {
- "palette" : 0x0100000A,
- "bitmaps" : 0x0100000B,
- "fonts" : 0x0100000C,
- "defaults" : 0x0100000D,
- "cursor" : 0x0100000E,
- "drawdata" : 0x0100000F
+ "bitmaps" : 0x0100000A,
+ "fonts" : 0x0100000B,
+ "cursor" : 0x0100000C,
+ "drawdata" : 0x0100000D
}
DS_triangleOrientations = {"top" : 0x1, "bottom" : 0x2, "left" : 0x3, "right" : 0x4}
@@ -71,12 +100,19 @@ class STXBinaryFile:
"line" : 0x5, "triangle" : 0x6, "fill" : 0x7, "tab" : 0x8, "bitmap" : 0x9, "cross" : 0xA
}
+ DS_fonts = {
+ "text_default" : 0x0, "text_hover" : 0x1, "text_disabled" : 0x2,
+ "text_inverted" : 0x3, "text_button" : 0x4, "text_button_hover" : 0x5, "text_normal" : 0x6
+ }
+
+ DS_text_alignV = {"bottom" : 0x0, "center" : 0x1, "top" : 0x2}
+ DS_text_alignH = {"left" : 0x0, "center" : 0x1, "right" : 0x2}
+
DS_list = [
"func", "fill", "stroke", "gradient_factor",
"width", "height", "xpos", "ypos",
- "radius", "bevel", "shadow", "orientation",
- "fg_color", "bg_color", "gradient_start", "gradient_end", "bevel_color",
- "file"
+ "radius", "bevel", "shadow", "orientation", "file",
+ "fg_color", "bg_color", "gradient_start", "gradient_end", "bevel_color"
]
def __init__(self, themeName, autoLoad = True, verbose = False):
@@ -94,16 +130,16 @@ class STXBinaryFile:
"height" : self.DrawStepData(False, "i", lambda h: -1 if h == 'width' else 0 if h == 'auto' else int(h)),
"xpos" : self.DrawStepData(False, "i", lambda pos: self.DS_vectorAlign[pos] if pos in self.DS_vectorAlign else int(pos)),
"ypos" : self.DrawStepData(False, "i", lambda pos: self.DS_vectorAlign[pos] if pos in self.DS_vectorAlign else int(pos)),
- "radius" : self.DrawStepData(False, "B", lambda r: 0xFF if r == 'auto' else int(r)),
+ "radius" : self.DrawStepData(False, "i", lambda r: 0xFF if r == 'auto' else int(r)),
"bevel" : self.DrawStepData(True, "B", int),
"shadow" : self.DrawStepData(True, "B", int),
"orientation" : self.DrawStepData(False, "B", lambda o: self.DS_triangleOrientations[o]),
+ "file" : self.DrawStepData(False, "B", self.__getBitmap),
"fg_color" : self.DrawStepData(True, "4s", self.__parseColor),
"bg_color" : self.DrawStepData(True, "4s", self.__parseColor),
"gradient_start" : self.DrawStepData(True, "4s", self.__parseColor),
"gradient_end" : self.DrawStepData(True, "4s", self.__parseColor),
- "bevel_color" : self.DrawStepData(True, "4s", self.__parseColor),
- "file" : self.DrawStepData(False, "%ds", str),
+ "bevel_color" : self.DrawStepData(True, "4s", self.__parseColor)
}
if autoLoad:
@@ -149,6 +185,17 @@ class STXBinaryFile:
stxDom.unlink()
+ def __getBitmap(self, bmp):
+ bmp = str(bmp)
+
+ if bmp == "":
+ return 0x0
+
+ if bmp not in self._bitmaps:
+ raise self.InvalidBitmapName
+
+ return self._bitmaps[bmp]
+
def __parseDrawStep(self, drawstepDom, localDefaults = {}):
dstable = {}
@@ -177,7 +224,7 @@ class STXBinaryFile:
def __parseDrawStepToBin(self, stepDict):
"""
- /BBBBiiiiBBBB4s4s4s4s4ss/ == 32 + 4 * 32 + 32 + 5 * 32 = 352 / 4 bits = 88 bytes + var string
+ /BBBBiiiiiBBB4s4s4s4s4sB/ ==
function (byte)
fill (byte)
stroke (byte)
@@ -186,16 +233,16 @@ class STXBinaryFile:
height (int32)
xpos (int32)
ypos (int32)
- radius (byte)
+ radius (int32)
bevel (byte)
shadow (byte)
orientation (byte)
+ file (byte)
fg_color (4 byte)
bg_color (4 byte)
gradient_start (4 byte)
gradient_end (4 byte)
bevel_color (4 byte)
- file (byte string, null terminated)
"""
packLayout = ""
@@ -204,15 +251,8 @@ class STXBinaryFile:
for ds in self.DS_list:
layout = self.DS_data[ds].packFormat
data = stepDict[ds]
-
- if layout == "%ds":
- if data:
- packLayout += layout % (len(data) + 1)
- packData.append(data)
- else:
- packLayout += "B"
- packData.append(0)
- elif not data:
+
+ if not data:
size = struct.calcsize(layout)
packLayout += "B" * size
@@ -224,25 +264,23 @@ class STXBinaryFile:
stepBin = struct.pack(self.BYTEORDER + packLayout, *tuple(packData))
-# self.debugBinary(stepBin)
return stepBin
def __parseResolutionToBin(self, resString):
"""
- /i xxxbII xxxbII xxxbII/ == 32 bits + x * 32 bits
- number of resolution sections (int32)
- padding (byte)
+ /B bHH bHH bHH/ == 1 byte + x * 9 bytes
+ number of resolution sections (byte)
exclude resolution (byte)
- resolution X (int32)
- resolution Y (int32)
+ resolution X (half)
+ resolution Y (half)
"""
if resString == "":
- return struct.pack(self.BYTEORDER + "ixxxbII", 1, 0, 0, 0)
+ return struct.pack(self.BYTEORDER + "BbHH", 1, 0, 0, 0)
resolutions = resString.split(", ")
- packFormat = "i" + "xxxbII" * len(resolutions)
+ packFormat = "B" + "bHH" * len(resolutions)
packData = [len(resolutions)]
for res in resolutions:
@@ -263,8 +301,6 @@ class STXBinaryFile:
packData.append(y)
buff = struct.pack(self.BYTEORDER + packFormat, *tuple(packData))
- self.debug("Pack format: %s => %s" % (packFormat, str(packData)))
-# self.debugBinary(buff)
return buff
def __parseRGBToBin(self, color):
@@ -316,29 +352,97 @@ class STXBinaryFile:
def __parseBitmaps(self, bitmapsDom):
- self._bitmaps = []
+ self._bitmaps = {}
+ idCount = 0xA0
+ packLayout = ""
+ packData = []
for bitmap in bitmapsDom.getElementsByTagName("bitmap"):
- bmpName = bitmap.getAttribute("filename")
- resolution = self.__parseResolutionToBin(bitmap.getAttribute("resolution"))
+ bmpName = str(bitmap.getAttribute("filename"))
+ self._bitmaps[bmpName] = idCount
+
+ packLayout += "B%ds" % (len(bmpName) + 1)
+ packData.append(idCount)
+ packData.append(bmpName)
+ idCount += 1
- self._bitmaps.append((bmpName, resolution))
- self.debug("BITMAP: %s" % bmpName)
+
+ bitmapBinary = struct.pack(
+ self.BYTEORDER + "IB" + packLayout,
+ self.BLOCK_HEADERS['bitmaps'],
+ len(bitmapsDom.getElementsByTagName("bitmap")),
+ *tuple(packData)
+ )
+
+ self.debug("BITMAPS:\n%s\n\n" % pbin(bitmapBinary))
+ return bitmapBinary
def __parseFonts(self, fontsDom):
- self._fonts = []
+ """
+ /IB Bs4ss .../
+ section header (uint32)
+ number of font definitions (byte)
+
+ id for font definition (byte)
+ resolution for font (byte array)
+ color for font (4 bytes)
+ font filename (byte array, null terminated)
+ """
+ packLayout = ""
+ packData = []
+
for font in fontsDom.getElementsByTagName("font"):
ident = font.getAttribute("id")
+
+ if ident not in self.DS_fonts:
+ raise self.InvalidFontIdentifier
+
color = self.__parseColor(font.getAttribute("color"))
- filename = font.getAttribute("file")
+ filename = str(font.getAttribute("file"))
+
+ if filename == 'default':
+ filename = ''
resolution = self.__parseResolutionToBin(font.getAttribute("resolution"))
- self.debug("FONT: %s @ %s" % (ident, filename))
- self._fonts.append((ident, filename, color, resolution))
+
+ packLayout += "B%ds4s%ds" % (len(resolution), len(filename) + 1)
+ packData.append(self.DS_fonts[ident])
+ packData.append(resolution)
+ packData.append(color)
+ packData.append(filename)
+
+ fontsBinary = struct.pack(self.BYTEORDER + \
+ "IB" + packLayout,
+ self.BLOCK_HEADERS['fonts'],
+ len(fontsDom.getElementsByTagName("font")),
+ *tuple(packData)
+ )
+
+
+ self.debug("FONTS DATA:\n%s\n\n" % pbin(fontsBinary))
+ return fontsBinary
def __parseTextToBin(self, textDom):
- return ""
+ """
+ /BBBx/
+ font identifier (byte)
+ vertical alignment (byte)
+ horizontal alignment (byte)
+ padding until word (byte)
+ """
+
+ font = textDom.getAttribute("font")
+ if font not in self.DS_fonts:
+ raise self.InvalidFontIdentifier
+
+ textBin = struct.pack(self.BYTEORDER + "BBBx",
+ self.DS_fonts[font],
+ self.DS_text_alignV[textDom.getAttribute("vertical_align")],
+ self.DS_text_alignH[textDom.getAttribute("horizontal_align")]
+ )
+
+ return textBin
def __parseDrawData(self, ddDom):
"""
@@ -349,7 +453,7 @@ class STXBinaryFile:
Cached (byte)
has text section? (byte)
number of DD sections (uint16)
- ** text segment (byte array)
+ ** text segment (4 bytes)
drawstep segments (byte array)
"""
@@ -372,11 +476,11 @@ class STXBinaryFile:
text = ddDom.getElementsByTagName("text")
text = self.__parseTextToBin(text[0]) if text else ""
- id_hash = str.__hash__(str(ddDom.getAttribute("id")))
+ id_hash = str.__hash__(str(ddDom.getAttribute("id"))) & 0xFFFFFFFF
cached = self.TRUTH_VALUES[ddDom.getAttribute("cached")]
ddBinary = struct.pack(self.BYTEORDER + \
- "I%dsiBBH%ds%ds" % (len(resolution), len(text), len(stepByteArray)),
+ "I%dsIBBH4s%ds" % (len(resolution), len(stepByteArray)),
self.BLOCK_HEADERS['drawdata'], # Section Header (uint32)
resolution, # Resolution (byte array, word-aligned)
@@ -391,6 +495,33 @@ class STXBinaryFile:
self.debug("DRAW DATA %s (%X): \n" % (ddDom.getAttribute("id"), id_hash) + pbin(ddBinary) + "\n\n")
return ddBinary
+ def __parseCursor(self, cursorDom):
+ """
+ /IsBBhh/
+ section header (uint32)
+ resolution string (byte array)
+ bitmap id (byte)
+ scale (byte)
+ hotspot X (half)
+ hotspot Y (half)
+ """
+
+ resolution = self.__parseResolutionToBin(cursorDom.getAttribute("resolution"))
+ scale = int(cursorDom.getAttribute("scale"))
+ hsX, hsY = cursorDom.getAttribute("hotspot").split(", ")
+
+ cursorBin = struct.pack(self.BYTEORDER + "I%dsBBhh" % len(resolution),
+ self.BLOCK_HEADERS['cursor'],
+ resolution,
+ self.__getBitmap(cursorDom.getAttribute("file")),
+ scale,
+ int(hsX),
+ int(hsY)
+ )
+
+ self.debug("CURSOR:\n%s\n\n" % pbin(cursorBin))
+ return cursorBin
+
def __parseLayout(self, layoutDom):
self.debug("GLOBAL SECTION: LAYOUT INFO.")
@@ -404,14 +535,25 @@ class STXBinaryFile:
defaultsDom = renderDom.getElementsByTagName("defaults")[0]
self.__parsePalette(paletteDom)
- self.__parseBitmaps(bitmapsDom)
- self.__parseFonts(fontsDom)
+
+ bitmapBIN = self.__parseBitmaps(bitmapsDom)
+ fontsBIN = self.__parseFonts(fontsDom)
+ cursorBIN = ""
+ drawdataBIN = ""
+
+ for cur in renderDom.getElementsByTagName("cursor"):
+ cursorBIN += self.__parseCursor(cur)
self._globalDefaults = self.__parseDrawStep(defaultsDom)
for dd in renderDom.getElementsByTagName("drawdata"):
- self.__parseDrawData(dd)
+ drawdataBIN += self.__parseDrawData(dd)
+
+ renderInfoBIN = bitmapBIN + fontsBIN + cursorBIN + drawdataBIN
+ printBinaryDump(renderInfoBIN)
+
+ return renderInfoBIN
if __name__ == '__main__':
bin = STXBinaryFile('../gui/themes/scummmodern', True, True)