mirror of
https://github.com/wesnoth/wesnoth
synced 2024-09-21 00:13:57 +00:00
229 lines
7.4 KiB
Python
Executable File
229 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python
|
|
"""
|
|
wmlflip -- return the sizes of maps listed on the command line.
|
|
|
|
Flip macro coordinate arguments on a map. Use this if you've mirror-reversed
|
|
a map and need to change coordinate-using macros. Takes a cross-reference
|
|
of all known macros and looks for formals that are either X, Y, *_X, or _Y,
|
|
so it's guaranteed to catch everything.
|
|
"""
|
|
|
|
import sys, os, time, getopt, cStringIO
|
|
from wesnoth.wmltools import *
|
|
|
|
class ParseArgs:
|
|
"Mine macro argument locations out of a .cfg file."
|
|
def __init__(self, fp, verbose=False):
|
|
self.fp = fp
|
|
self.verbose = verbose
|
|
self.parsed = []
|
|
self.namestack = []
|
|
self.pushback = None
|
|
self.lead = ""
|
|
self.parse_until([''])
|
|
def getchar(self):
|
|
if self.pushback:
|
|
c = self.pushback
|
|
self.pushback = None
|
|
return c
|
|
else:
|
|
return self.fp.read(1)
|
|
def ungetchar(self, c):
|
|
if verbose:
|
|
print "pushing back", c
|
|
self.pushback = c
|
|
def parse_until(self, enders):
|
|
"Parse until we reach specified terminator."
|
|
if self.verbose:
|
|
self.lead += "*"
|
|
print self.lead + " parse_until(%s) starts" % enders
|
|
while True:
|
|
c = self.getchar()
|
|
if self.verbose:
|
|
print self.lead + "I see", c
|
|
if c in enders:
|
|
if self.verbose:
|
|
print self.lead + "parse_until(%s) ends" % enders
|
|
self.lead = self.lead[:-1]
|
|
return c
|
|
elif c == '{':
|
|
self.parse_call()
|
|
def parse_call(self):
|
|
"We see a start of call."
|
|
if self.verbose:
|
|
self.lead += "*"
|
|
print self.lead + "parse_call()"
|
|
self.namestack.append(["", []])
|
|
# Fill in the name of the called macro
|
|
while True:
|
|
c = self.getchar()
|
|
if c.isalnum() or c == '_':
|
|
self.namestack[-1][0] += c
|
|
else:
|
|
break
|
|
if self.verbose:
|
|
print self.lead + "name", self.namestack[-1]
|
|
# Discard if no arguments
|
|
if c == '}':
|
|
self.namestack.pop()
|
|
if self.verbose:
|
|
print self.lead + "parse_call() ends"
|
|
self.lead = self.lead[:-1]
|
|
return
|
|
# If non-space, this is something like a filename include;
|
|
# skip until closing }
|
|
if not c.isspace():
|
|
while True:
|
|
c = self.getchar()
|
|
if c == '}':
|
|
if self.verbose:
|
|
print self.lead + "parse_call() ends"
|
|
self.lead = self.lead[:-1]
|
|
return
|
|
# It's a macro call with arguments;
|
|
# parse them, recording the character offsets
|
|
while self.parse_actual():
|
|
continue
|
|
# Discard trailing }
|
|
self.getchar()
|
|
# Record the scope we just parsed
|
|
self.parsed.append(self.namestack.pop())
|
|
if self.verbose:
|
|
print self.lead + "parse_call() ends"
|
|
self.lead = self.lead[:-1]
|
|
def parse_actual(self):
|
|
"Parse an actual argument."
|
|
# Skip leading whitespace
|
|
if self.verbose:
|
|
self.lead += "*"
|
|
print self.lead + "parse_actual() begins"
|
|
while True:
|
|
c = self.getchar()
|
|
if not c.isspace():
|
|
break
|
|
if c == '}':
|
|
if self.verbose:
|
|
print "** parse_actual() returns False"
|
|
self.lead = self.lead[:-1]
|
|
return False
|
|
# Looks like we have a real argument
|
|
argstart = self.fp.tell() - 1
|
|
# Skip leading translation mark, if any
|
|
if c == "_":
|
|
c = self.getchar()
|
|
# Get the argument itself
|
|
if c == '{':
|
|
self.parse_call()
|
|
argend = self.fp.tell()
|
|
elif c == '(':
|
|
self.parse_until([")"])
|
|
argend = self.fp.tell()
|
|
elif c == '"':
|
|
if verbose:
|
|
print self.lead + "starting string argument"
|
|
self.parse_until(['"'])
|
|
argend = self.fp.tell()
|
|
else:
|
|
ender = self.parse_until(['', ' ', '\t', '\r', '\n', '}'])
|
|
argend = self.fp.tell() - 1
|
|
self.ungetchar(ender)
|
|
self.namestack[-1][1].append((argstart, argend))
|
|
if self.verbose:
|
|
print self.lead + "parse_actual() returns True"
|
|
self.lead = self.lead[:-1]
|
|
return True
|
|
|
|
def relevant_macros():
|
|
"Compute indices of (X, Y) pairs in formals of all mainline macros."
|
|
# Cross-reference all files.
|
|
here = os.getcwd()
|
|
pop_to_top("wmlflip")
|
|
cref = CrossRef(scopelist())
|
|
os.chdir(here)
|
|
|
|
# Look at all definitions. Extract those with in "_?X" or "_?Y".
|
|
# Generate a dictionary mapping definition names to the indices
|
|
# the X Y formal arguments.
|
|
relevant = {}
|
|
for name in cref.xref:
|
|
for ref in cref.xref[name]:
|
|
have_x = have_y = None
|
|
for (i, arg) in enumerate(ref.args):
|
|
if arg == "X" or arg.endswith("_X"):
|
|
have_x = i
|
|
if arg == "Y" or arg.endswith("_Y"):
|
|
have_y = i
|
|
if have_x is not None and have_y is not None:
|
|
relevant[name] = (have_x, have_y)
|
|
return relevant
|
|
|
|
def transformables(filename, relevant):
|
|
"Return a list of transformable (X,Y) regions in the specified file."
|
|
# Grab the content
|
|
fp = open(filename, "r")
|
|
content = fp.read()
|
|
fp.close()
|
|
|
|
# Get argument offsets from it.
|
|
calls = ParseArgs(cStringIO.StringIO(content), verbose)
|
|
|
|
# Filter out irrelevant calls.
|
|
parsed = filter(lambda x: x[0] in relevant, calls.parsed)
|
|
|
|
# Derive list of coordinate pair locations.
|
|
pairs = []
|
|
for (name, arglocs) in parsed:
|
|
(have_x, have_y) = relevant[name]
|
|
pairs.append((arglocs[have_x], arglocs[have_y]))
|
|
|
|
# Return the file content as a string and the transformable extents in it.
|
|
return (content, pairs)
|
|
|
|
def mapsize(filename):
|
|
"Return the size of a specified mappfile."
|
|
x = y = 0
|
|
for line in open(filename):
|
|
if "," in line:
|
|
y += 1
|
|
nx = line.count(",") + 1
|
|
assert(x == 0 or x == nx)
|
|
x = nx
|
|
return (x, y)
|
|
|
|
if __name__ == '__main__':
|
|
flip_x = flip_y = verbose = False
|
|
mapfile = None
|
|
(options, arguments) = getopt.getopt(sys.argv[1:], "m:xyv")
|
|
|
|
for (switch, val) in options:
|
|
if switch in ('-h', '--help'):
|
|
sys.stderr.write(__doc__)
|
|
sys.exit(0)
|
|
elif switch in ('-m'):
|
|
mapfile = val
|
|
elif switch in ('-x'):
|
|
flip_x = True
|
|
elif switch in ('-y'):
|
|
print >>sys.stderr, "Vertical flip is not yet supported."
|
|
sys.exit(0)
|
|
elif switch == '-v':
|
|
verbose = True
|
|
if verbose:
|
|
print "Debugging output enabled."
|
|
|
|
if mapfile:
|
|
(mx, my) = mapsize(mapfile)
|
|
print"%s is %d wide by %d high" % (mapfile, mx, my)
|
|
|
|
# Are we doing file transformations?
|
|
if arguments:
|
|
relevant = relevant_macros()
|
|
# For each file named on the command line...
|
|
for filename in arguments:
|
|
if verbose:
|
|
print "Processing file", filename
|
|
(content, pairs) = transformables(filename, relevant)
|
|
|
|
# MORE GOES HERE
|
|
print pairs
|