From 0ce07a8a1e5e5787917435037c326c7bd67adbc7 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Tue, 31 Jul 2018 04:18:28 +1000 Subject: (nw) more render work: * Clean up some corner cases in layouts with repeating blocks * Make complay.py validate many more elements and attributes * Make complay.py easier to use for just validating a layout * Remove redundant view from Sega VMU layout * Make buttons visually respond to input in whousetc.lay * Add view with LED displays as well as terminal for aim65_40 and use repeats * Clean up some outdated "game" terminology in clifront.cpp * Initiaise a couple of members in tap/tun network module * Start documenting layout format --- scripts/build/complay.py | 112 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 30 deletions(-) (limited to 'scripts/build/complay.py') diff --git a/scripts/build/complay.py b/scripts/build/complay.py index f6e7db4320e..c6cea3be251 100644 --- a/scripts/build/complay.py +++ b/scripts/build/complay.py @@ -4,6 +4,7 @@ ## copyright-holders:Vas Crabb import os +import os.path import re import sys import xml.sax @@ -95,7 +96,7 @@ class LayoutChecker(Minifyer): BADTAGPATTERN = re.compile('[^abcdefghijklmnopqrstuvwxyz0123456789_.:^$]') VARPATTERN = re.compile('^.*~[0-9A-Za-z_]+~.*$') FLOATCHARS = re.compile('^.*[.eE].*$') - SHAPES = frozenset(('disk', 'led14seg', 'led14segsc', 'led16seg', 'led16segsc', 'led7seg', 'led8seg_gts1', 'rect')) + SHAPES = frozenset(('disk', 'dotmatrix', 'dotmatrix5dot', 'dotmatrixdot', 'led14seg', 'led14segsc', 'led16seg', 'led16segsc', 'led7seg', 'led8seg_gts1', 'rect')) OBJECTS = frozenset(('backdrop', 'bezel', 'cpanel', 'marquee', 'overlay')) def __init__(self, output, **kwargs): @@ -182,10 +183,10 @@ class LayoutChecker(Minifyer): self.checkNumericAttribute('param', attrs, 'increment', None) lshift = self.checkIntAttribute('param', attrs, 'lshift', None) if (lshift is not None) and (0 > lshift): - self.handleError('Element param attribute lshift "%s" is negative', (attrs['lshift'], )) + self.handleError('Element param attribute lshift "%s" is negative' % (attrs['lshift'], )) rshift = self.checkIntAttribute('param', attrs, 'rshift', None) if (rshift is not None) and (0 > rshift): - self.handleError('Element param attribute rshift "%s" is negative', (attrs['rshift'], )) + self.handleError('Element param attribute rshift "%s" is negative' % (attrs['rshift'], )) if self.repeat_depth and self.repeat_depth[-1]: if 'start' in attrs: if 'value' in attrs: @@ -194,7 +195,7 @@ class LayoutChecker(Minifyer): if name not in self.variable_scopes[-1]: self.variable_scopes[-1][name] = True elif not self.VARPATTERN.match(name): - self.handleError('Incrementing parameter "%s" redefined', (name, )) + self.handleError('Generator parameter "%s" redefined' % (name, )) else: if 'value' not in attrs: self.handleError('Element param missing attribute value') @@ -204,7 +205,7 @@ class LayoutChecker(Minifyer): if not self.variable_scopes[-1].get(name, False): self.variable_scopes[-1][name] = False elif not self.VARPATTERN.match(name): - self.handleError('Incrementing parameter "%s" redefined', (name, )) + self.handleError('Generator parameter "%s" redefined' % (name, )) else: if ('start' in attrs) or ('increment' in attrs) or ('lshift' in attrs) or ('rshift' in attrs): self.handleError('Element param with start/increment/lshift/rshift attribute(s) not in repeat scope') @@ -261,6 +262,14 @@ class LayoutChecker(Minifyer): if tag.find('::') >= 0: self.handleError('Element %s attribute %s "%s" contains double separator' % (element, attr, tag)) + def checkComponent(self, name, attrs): + state = self.checkIntAttribute(name, attrs, 'state', None) + if (state is not None) and (0 > state): + self.handleError('Element %s attribute state "%s" is negative' % (name, attrs['state'])) + self.handlers.append((self.componentStartHandler, self.componentEndHandler)) + self.have_bounds.append(False) + self.have_color.append(False) + def rootStartHandler(self, name, attrs): if 'mamelayout' != name: self.ignored_depth = 1 @@ -292,6 +301,9 @@ class LayoutChecker(Minifyer): self.elements[attrs['name']] = self.formatLocation() elif not generated_name: self.handleError('Element element has duplicate name (previous %s)' % (self.elements[attrs['name']], )) + defstate = self.checkIntAttribute(name, attrs, 'defstate', None) + if (defstate is not None) and (0 > defstate): + self.handleError('Element element attribute defstate "%s" is negative' % (attrs['defstate'], )) self.handlers.append((self.elementStartHandler, self.elementEndHandler)) elif 'group' == name: if 'name' not in attrs: @@ -308,7 +320,7 @@ class LayoutChecker(Minifyer): self.variable_scopes.append({ }) self.repeat_depth.append(0) self.have_bounds.append(False) - elif 'view' == name: + elif ('view' == name) and (not self.repeat_depth[-1]): if 'name' not in attrs: self.handleError('Element view missing attribute name') else: @@ -326,13 +338,13 @@ class LayoutChecker(Minifyer): else: count = self.checkIntAttribute(name, attrs, 'count', None) if (count is not None) and (0 >= count): - self.handleError('Element repeat attribute count "%s" is negative' % (attrs['count'], )) + self.handleError('Element repeat attribute count "%s" is not positive' % (attrs['count'], )) self.variable_scopes.append({ }) self.repeat_depth[-1] += 1 elif 'param' == name: self.checkParameter(attrs) self.ignored_depth = 1 - elif 'script' == name: + elif ('script' == name) and (not self.repeat_depth[-1]): self.ignored_depth = 1 else: self.handleError('Encountered unexpected element %s' % (name, )) @@ -355,31 +367,55 @@ class LayoutChecker(Minifyer): def elementStartHandler(self, name, attrs): if name in self.SHAPES: - self.handlers.append((self.shapeStartHandler, self.shapeEndHandler)) - self.have_bounds.append(False) - self.have_color.append(False) + self.checkComponent(name, attrs) elif 'text' == name: if 'string' not in attrs: - self.handleError('Element bounds missing attribute string') - if 'align' in attrs: - align = self.checkIntAttribute(name, attrs, 'align', None) - if (align is not None) and ((0 > align) or (2 < align)): - self.handleError('Element text attribute align "%s" not in valid range 0-2' % (attrs['align'], )) - self.handlers.append((self.shapeStartHandler, self.shapeEndHandler)) - self.have_bounds.append(False) - self.have_color.append(False) + self.handleError('Element text missing attribute string') + align = self.checkIntAttribute(name, attrs, 'align', None) + if (align is not None) and ((0 > align) or (2 < align)): + self.handleError('Element text attribute align "%s" not in valid range 0-2' % (attrs['align'], )) + self.checkComponent(name, attrs) + elif 'simplecounter' == name: + maxstate = self.checkIntAttribute(name, attrs, 'maxstate', None) + if (maxstate is not None) and (0 > maxstate): + self.handleError('Element simplecounter attribute maxstate "%s" is negative' % (attrs['maxstate'], )) + digits = self.checkIntAttribute(name, attrs, 'digits', None) + if (digits is not None) and (0 >= digits): + self.handleError('Element simplecounter attribute digits "%s" is not positive' % (attrs['digits'], )) + align = self.checkIntAttribute(name, attrs, 'align', None) + if (align is not None) and ((0 > align) or (2 < align)): + self.handleError('Element simplecounter attribute align "%s" not in valid range 0-2' % (attrs['align'], )) + self.checkComponent(name, attrs) + elif 'image' == name: + if 'file' not in attrs: + self.handleError('Element image missing attribute file') + self.checkComponent(name, attrs) + elif 'reel' == name: + # TODO: validate symbollist and improve validation of other attributes + self.checkIntAttribute(name, attrs, 'stateoffset', None) + numsymbolsvisible = self.checkIntAttribute(name, attrs, 'numsymbolsvisible', None) + if (numsymbolsvisible is not None) and (0 >= numsymbolsvisible): + self.handleError('Element reel attribute numsymbolsvisible "%s" not positive' % (attrs['numsymbolsvisible'], )) + reelreversed = self.checkIntAttribute(name, attrs, 'reelreversed', None) + if (reelreversed is not None) and ((0 > reelreversed) or (1 < reelreversed)): + self.handleError('Element reel attribute reelreversed "%s" not in valid range 0-1' % (attrs['reelreversed'], )) + beltreel = self.checkIntAttribute(name, attrs, 'beltreel', None) + if (beltreel is not None) and ((0 > beltreel) or (1 < beltreel)): + self.handleError('Element reel attribute beltreel "%s" not in valid range 0-1' % (attrs['beltreel'], )) + self.checkComponent(name, attrs) else: + self.handleError('Encountered unexpected element %s' % (name, )) self.ignored_depth = 1 def elementEndHandler(self, name): self.handlers.pop() - def shapeStartHandler(self, name, attrs): + def componentStartHandler(self, name, attrs): if 'bounds' == name: self.checkBounds(attrs) elif 'color' == name: if self.have_color[-1]: - self.handleError('Duplicate bounds element') + self.handleError('Duplicate color element') else: self.have_color[-1] = True self.checkColorChannel(attrs, 'red') @@ -388,7 +424,7 @@ class LayoutChecker(Minifyer): self.checkColorChannel(attrs, 'alpha') self.ignored_depth = 1 - def shapeEndHandler(self, name): + def componentEndHandler(self, name): self.have_bounds.pop() self.have_color.pop() self.handlers.pop() @@ -396,7 +432,7 @@ class LayoutChecker(Minifyer): def groupViewStartHandler(self, name, attrs): if name in self.OBJECTS: if 'element' not in attrs: - self.handleError('Element %s missing attribute element', (name, )) + self.handleError('Element %s missing attribute element' % (name, )) elif attrs['element'] not in self.referenced_elements: self.referenced_elements[attrs['element']] = self.formatLocation() if 'inputtag' in attrs: @@ -549,19 +585,33 @@ def compressLayout(src, dst, comp): return state[1], state[0] +class BlackHole(object): + def write(self, *args): + pass + def close(self): + pass + + if __name__ == '__main__': - if len(sys.argv) != 4: + if (len(sys.argv) > 4) or (len(sys.argv) < 2): print('Usage:') - print(' complay ') + print(' complay [ []]') sys.exit(0 if len(sys.argv) <= 1 else 1) srcfile = sys.argv[1] - dstfile = sys.argv[2] - varname = sys.argv[3] + dstfile = sys.argv[2] if len(sys.argv) >= 3 else None + if len(sys.argv) >= 4: + varname = sys.argv[3] + else: + varname = os.path.basename(srcfile) + base, ext = os.path.splitext(varname) + if ext.lower() == '.lay': + varname = base + varname = 'layout_' + re.sub('[^0-9A-Za-z_]', '_', varname) comp_type = 1 try: - dst = open(dstfile,'w') + dst = open(dstfile,'w') if dstfile is not None else BlackHole() dst.write('static const unsigned char %s_data[] = {\n' % (varname)) byte_count, comp_size = compressLayout(srcfile, lambda x: dst.write(x), zlib.compressobj()) dst.write('};\n\n') @@ -571,10 +621,12 @@ if __name__ == '__main__': dst.close() except XmlError: dst.close() - os.remove(dstfile) + if dstfile is not None: + os.remove(dstfile) sys.exit(2) except IOError: sys.stderr.write("Unable to open output file '%s'\n" % dstfile) - os.remove(dstfile) dst.close() + if dstfile is not None: + os.remove(dstfile) sys.exit(3) -- cgit v1.2.3-70-g09d2