aboutsummaryrefslogtreecommitdiff
path: root/tools/themeparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/themeparser.py')
-rw-r--r--tools/themeparser.py351
1 files changed, 351 insertions, 0 deletions
diff --git a/tools/themeparser.py b/tools/themeparser.py
new file mode 100644
index 0000000000..c2d0011854
--- /dev/null
+++ b/tools/themeparser.py
@@ -0,0 +1,351 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+"""
+ " ScummVM - Graphic Adventure Engine
+ "
+ " ScummVM is the legal property of its developers, whose names
+ " are too numerous to list here. Please refer to the COPYRIGHT
+ " file distributed with this source distribution.
+ "
+ " This program is free software; you can redistribute it and/or
+ " modify it under the terms of the GNU General Public License
+ " as published by the Free Software Foundation; either version 2
+ " of the License, or (at your option) any later version.
+ "
+ " This program is distributed in the hope that it will be useful,
+ " but WITHOUT ANY WARRANTY; without even the implied warranty of
+ " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ " GNU General Public License for more details.
+ "
+ " You should have received a copy of the GNU General Public License
+ " along with this program; if not, write to the Free Software
+ " Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ "
+ " $URL$
+ " $Id$
+"""
+
+from __future__ import with_statement
+import os
+import xml.dom.minidom as DOM
+import struct
+
+class STXBinaryFile:
+ class InvalidRGBColor(Exception):
+ pass
+
+ class InvalidResolution(Exception):
+ pass
+
+ BLOCK_HEADERS = {
+ "palette" : 0x0100000A,
+ "bitmaps" : 0x0100000B,
+ "fonts" : 0x0100000C,
+ "defaults" : 0x0100000D,
+ "cursor" : 0x0100000E,
+ "drawdata" : 0x0100000F
+ }
+
+ DRAWSTEP_FORMAT_DEF = [
+ "stroke", "shadow",
+ "bevel", "factor",
+ "fg_color", "bg_color",
+ "fill", "gradient_start",
+ "gradient_end", "bevel_color",
+ "gradient_factor",
+ ]
+
+ DRAWSTEP_FORMAT = [
+ "func", "radius", "width", "height",
+ "xpos", "ypos", "file", "orientation",
+ ] + DRAWSTEP_FORMAT_DEF
+
+ def __init__(self, themeName, autoLoad = True, verbose = False):
+ self._themeName = themeName
+ self._stxFiles = []
+ self._verbose = verbose
+
+ if autoLoad:
+ if not os.path.isdir(themeName) or not os.path.isfile(os.path.join(themeName, "THEMERC")):
+ raise IOError
+
+ for filename in os.listdir(themeName):
+ filename = os.path.join(themeName, filename)
+ if os.path.isfile(filename) and filename.endswith('.stx'):
+ self._stxFiles.append(filename)
+
+ def debug(self, text):
+ if self._verbose: print text
+
+ def debugBinary(self, data):
+ if self._verbose:
+ print "BINARY OUTPUT (%d bytes):" % len(data), " ".join(["%0.2X" % ord(c) for c in data])
+
+ def addSTXFile(self, filename):
+ if not os.path.isfile(filename):
+ raise IOError
+ else:
+ self._stxFiles.append(filename)
+
+ def parse(self):
+ if not self._stxFiles:
+ self.debug("No files have been loaded for parsing on the theme.")
+ raise IOError
+
+ for f in self._stxFiles:
+ self.debug("Parsing %s." % f)
+ with open(f) as stxFile:
+ self.__parseFile(stxFile)
+
+ def __parseFile(self, xmlFile):
+ stxDom = DOM.parse(xmlFile)
+
+ for layout in stxDom.getElementsByTagName("layout_info"):
+ self.__parseLayout(layout)
+
+ for render in stxDom.getElementsByTagName("render_info"):
+ self.__parseRender(render)
+
+ stxDom.unlink()
+
+ def __parseDrawStep(self, drawstepDom, localDefaults = {}):
+
+ triangleOrientations = {"top" : 0x1, "bottom" : 0x2, "left" : 0x3, "right" : 0x4}
+ fillModes = {"none" : 0x0, "foreground" : 0x1, "background" : 0x2, "gradient" : 0x3}
+ vectorAlign = {"left" : 0x1, "right" : 0x2, "bottom" : 0x3, "top" : 0x4, "center" : 0x5}
+
+ functions = {
+ "void" : 0x0, "circle" : 0x1, "square" : 0x2, "roundedsq" : 0x3, "bevelsq" : 0x4,
+ "line" : 0x5, "triangle" : 0x6, "fill" : 0x7, "tab" : 0x8, "bitmap" : 0x9, "cross" : 0xA
+ }
+
+ parseAttribute = {
+ "stroke" : int,
+ "bevel" : int,
+ "shadow" : int,
+ "gradient_factor" : int,
+
+ "fg_color" : self.__parseColor,
+ "bg_color" : self.__parseColor,
+ "gradient_start" : self.__parseColor,
+ "gradient_end" : self.__parseColor,
+ "bevel_color" : self.__parseColor,
+
+ "radius" : lambda r: 0xFF if r == 'auto' else int(r),
+ "file" : str,
+ "orientation" : lambda o: triangleOrientations[o],
+ "fill" : lambda f: fillModes[f],
+ "func" : lambda f: functions[f],
+
+ "width" : lambda w: -1 if w == 'height' else 0 if w == 'auto' else int(w),
+ "height" : lambda h: -1 if h == 'width' else 0 if h == 'auto' else int(h),
+
+ "xpos" : lambda pos: vectorAlign[pos] if pos in vectorAlign else int(pos),
+ "ypos" : lambda pos: vectorAlign[pos] if pos in vectorAlign else int(pos),
+ }
+
+ dstable = {}
+
+ attributes = []
+
+ if drawstepDom.tagName == "defaults":
+ isGlobal = drawstepDom.parentNode.tagName == "render_info"
+
+ for attr in self.DRAWSTEP_FORMAT_DEF:
+ if drawstepDom.hasAttribute(attr):
+ self.debug("P: %s <= '%s'" % (attr, drawstepDom.getAttribute(attr)))
+ dstable[attr] = parseAttribute[attr](drawstepDom.getAttribute(attr))
+
+ elif isGlobal:
+ dstable[attr] = 0x0
+
+ else:
+ for attr in self.DRAWSTEP_FORMAT:
+ if drawstepDom.hasAttribute(attr):
+ self.debug("P: %s <= '%s'" % (attr, drawstepDom.getAttribute(attr)))
+ dstable[attr] = parseAttribute[attr](drawstepDom.getAttribute(attr))
+ elif attr in self.DRAWSTEP_FORMAT_DEF:
+ dstable[attr] = localDefaults[attr] if attr in localDefaults else self._globalDefaults[attr]
+ else:
+ dstable[attr] = 0x0
+
+ return dstable
+
+
+ def __parseDrawStepToBin(self, stepDict, isDefault):
+ DRAWSTEP_BININFO = {
+ "stroke" : "B", "shadow" : "B", "bevel" : "B",
+ "factor" : "B", "fg_color" : "BBB", "bg_color" : "BBB",
+ "fill" : "B", "func" : "B", "radius" : "i",
+ "width" : "i", "height" : "i", "xpos" : "i",
+ "ypos" : "i", "orientation" : "B", "file" : "B%ds",
+
+ "gradient_start" : "BBB", "gradient_end" : "BBB",
+ "bevel_color" : "BBB", "gradient_factor" : "B",
+ }
+
+ packLayout = ""
+ packData = []
+ attributes = self.DRAWSTEP_FORMAT_DEF if isDefault else self.DRAWSTEP_FORMAT
+
+ for attr in attributes:
+ layout = DRAWSTEP_BININFO[attr]
+ data = stepDict[attr]
+
+ if layout == "B%ds":
+ packLayout += layout % (len(data) + 1)
+ packData.append(len(data))
+ packData.append(data)
+
+ else:
+ packLayout += stepDict[attr]
+
+ if isinstance(data, tuple):
+ for d in data:
+ packData.append(d)
+ else:
+ packData.append(data)
+
+ stepBin = struct.pack(packLayout, *tuple(packData))
+ self.debugBinary(stepBin)
+ return stepBin
+
+
+ def __parseResolutionToBin(self, resString):
+ """
+ /b bII bII bII/
+ b => number of resolution sections
+ bII => exclude byte, X resolution, Y resolution
+ """
+
+ if resString == "":
+ return struct.pack("bbII", 1, 0, 0, 0)
+
+ resolutions = resString.split(", ")
+ packFormat = "b" + "bII" * len(resolutions)
+ packData = [len(resolutions)]
+
+ for res in resolutions:
+ exclude = 0
+ if res[0] == '-':
+ exclude = 1
+ res = res[1:]
+
+ try:
+ x, y = res.split('x')
+ x = 0 if x == 'X' else int(x)
+ y = 0 if y == 'Y' else int(y)
+ except ValueError:
+ raise InvalidResolution
+
+ packData.append(exclude)
+ packData.append(x)
+ packData.append(y)
+
+ buff = struct.pack(packFormat, *tuple(packData))
+ self.debug("Pack format: %s => %s" % (packFormat, str(packData)))
+ self.debugBinary(buff)
+ return buff
+
+ def __parseRGBToBin(self, color):
+ """
+ /BBB/
+ B => red color byte
+ B => green color byte
+ B => blue color byte
+ """
+
+ try:
+ rgb = tuple(map(int, color.split(", ")))
+ except ValueError:
+ raise self.InvalidRGBColor
+
+ if len(rgb) != 3:
+ raise self.InvalidRGBColor
+
+ for c in rgb:
+ if c < 0 or c > 255:
+ raise self.InvalidRGBColor
+
+ rgb = struct.pack("BBB", *tuple(rgb))
+
+ self.debugBinary(rgb)
+ return rgb
+
+ def __parseColor(self, color):
+ try:
+ color = self.__parseRGBToBin(color)
+ except self.InvalidRGBColor:
+ if color not in self._colors:
+ raise self.InvalidRGBColor
+ color = self._colors[color]
+
+ return color
+
+
+ def __parsePalette(self, paletteDom):
+ self._colors = {}
+
+ for color in paletteDom.getElementsByTagName("color"):
+ color_name = color.getAttribute('name')
+ color_rgb = self.__parseRGBToBin(color.getAttribute('rgb'))
+
+ self._colors[color_name] = color_rgb
+ self.debug("COLOR: %s" % (color_name))
+
+
+ def __parseBitmaps(self, bitmapsDom):
+ self._bitmaps = []
+
+ for bitmap in bitmapsDom.getElementsByTagName("bitmap"):
+ bmpName = bitmap.getAttribute("filename")
+ resolution = self.__parseResolutionToBin(bitmap.getAttribute("resolution"))
+
+ self._bitmaps.append((bmpName, resolution))
+ self.debug("BITMAP: %s" % bmpName)
+
+ def __parseFonts(self, fontsDom):
+ self._fonts = []
+
+ for font in fontsDom.getElementsByTagName("font"):
+ ident = font.getAttribute("id")
+ color = self.__parseColor(font.getAttribute("color"))
+ filename = font.getAttribute("file")
+
+ resolution = self.__parseResolutionToBin(font.getAttribute("resolution"))
+ self.debug("FONT: %s @ %s" % (ident, filename))
+ self._fonts.append((ident, filename, color, resolution))
+
+ def __parseDrawData(self, ddDom):
+
+ for ds in ddDom.getElementsByTagName("drawstep"):
+ dstable = self.__parseDrawStep(ds)
+ print dstable
+
+
+ def __parseLayout(self, layoutDom):
+ self.debug("GLOBAL SECTION: LAYOUT INFO.")
+
+ def __parseRender(self, renderDom):
+ self.debug("GLOBAL SECTION: RENDER INFO.")
+
+ paletteDom = renderDom.getElementsByTagName("palette")[0]
+ bitmapsDom = renderDom.getElementsByTagName("bitmaps")[0]
+ fontsDom = renderDom.getElementsByTagName("fonts")[0]
+ defaultsDom = renderDom.getElementsByTagName("defaults")[0]
+
+ self.__parsePalette(paletteDom)
+ self.__parseBitmaps(bitmapsDom)
+ self.__parseFonts(fontsDom)
+
+ self._globalDefaults = self.__parseDrawStep(defaultsDom)
+
+ for dd in renderDom.getElementsByTagName("drawdata"):
+ self.__parseDrawData(dd)
+
+
+if __name__ == '__main__':
+ bin = STXBinaryFile('scummmodern', True, True)
+ bin.parse()
+ \ No newline at end of file