1from glob import glob
2import json
3import xml.etree.ElementTree
4import os.path
5import traceback
6import sys
7import fnmatch
8import tokenize
9import io
10
11class UnknownArtemisTagError(Exception):
12    def __init__(self, node):
13        super().__init__('%s: %s' % (node.tag, node.attrib))
14
15def convertString(s):
16    return s.replace('\n', '\\n').replace('\'', '\\\'').replace('"', '\\"').replace('^', '\\n').strip()
17
18def convertFloat(f):
19    try:
20        return str(float(eval(str(f), {}, {})))
21    except NameError:
22        pass
23    result = '('
24    for token in tokenize.tokenize(io.BytesIO(str(f).encode('UTF-8')).readline):
25        if token.type == tokenize.ENCODING or token.type == tokenize.ENDMARKER:
26            pass
27        elif token.type == tokenize.NAME:
28            result += 'variable_%s' % (convertName(token.string))
29        elif token.type == tokenize.OP:
30            result += '%s' % (token.string)
31        elif token.type == tokenize.NUMBER:
32            result += '%s' % (token.string)
33        else:
34            raise ValueError(token)
35    result += ')'
36    return result
37
38def convertPosition(x, z):
39    return convertFloat('20000-(%s)' % (x)), convertFloat('(%s)-100000' % (z))
40
41def convertName(name):
42    return '%s' % (name.replace(' ', '_').replace('-', '_').replace('*', 'X').replace('.', '__'))
43
44def convertRaceKeys(node, default=None):
45    keys = node.get('raceKeys', default)
46    keys = keys.lower().split(' ')
47    if 'biomech' in keys:
48        return "Ghosts"
49    elif 'friendly' in keys:
50        return "Human Navy"
51    elif 'enemy' in keys:
52        return "Kraylor"
53    elif 'neutral' in keys:
54        return "Independent"
55    raise UnknownArtemisTagError(node)
56
57def convertComparator(node):
58    comparator = node.get('comparator').lower()
59    if comparator == "equals" or comparator == "=":
60        return "=="
61    elif comparator == "not" or comparator == "!=":
62        return "~="
63    elif comparator == "greater" or comparator == ">":
64        return ">"
65    elif comparator == "less" or comparator == "<":
66        return "<"
67    elif comparator == "greater_equal" or comparator == "<=":
68        return "<="
69    elif comparator == "less_equal" or comparator == ">=":
70        return ">="
71    raise UnknownArtemisTagError(node)
72
73def convertSystemName(node):
74    system = node.get('systemType')
75    if system == 'systemBeam':
76        return 'beamweapons'
77    elif system == 'systemTorpedo':
78        return 'missilesystem'
79    elif system == 'systemTactical': # Sensors, we map it to reactor, as we don't have sensor power/damage
80        return 'reactor'
81    elif system == 'systemTurning':
82        return 'maneuver'
83    elif system == 'systemImpulse':
84        return 'impulse'
85    elif system == 'systemWarp':
86        return 'warp'
87    elif system == 'systemFrontShield':
88        return 'frontshield'
89    elif system == 'systemBackShield':
90        return 'rearshield'
91
92    raise UnknownArtemisTagError(node)
93
94class Event:
95    def __init__(self, main_node):
96        self._valid = True
97        self._body = []
98        self._conditions = []
99        self._warnings = []
100        self._done = {}
101        self._ai_info = {}
102
103        for node in main_node:
104            if node.tag == 'big_message':
105                message = convertString(node.get('title', ''))
106                if node.get('subtitle1') is not None:
107                    message += '\\n%s' % (convertString(node.get('subtitle1')))
108                if node.get('subtitle2') is not None:
109                    message += '\\n%s' % (convertString(node.get('subtitle2')))
110                self._body.append('globalMessage("%s");' % (message));
111            elif node.tag == 'incoming_comms_text':
112                self._body.append('temp_transmission_object:setCallSign("%s"):sendCommsMessage(getPlayerShip(-1), "%s")' % (convertString(node.get('from')), convertString(node.text)));
113            elif node.tag == 'warning_popup_message':
114                self.warning('Ignore', node)
115            elif node.tag == 'start_getting_keypresses_from':
116                self.warning('Ignore', node)
117            elif node.tag == 'end_getting_keypresses_from':
118                self.warning('Ignore', node)
119            elif node.tag == 'set_damcon_members':
120                self.warning('Ignore', node)
121            elif node.tag == 'incoming_message':
122                self.warning('Ignore', node)
123            elif node.tag == 'set_difficulty_level':
124                self.warning('Ignore', node)
125            elif node.tag == 'log':
126                self._body.append('print("%s")' % (convertString(node.get('text'))));
127
128            elif node.tag == 'set_skybox_index':
129                pass #We don't have other skyboxes. So ignore this command.
130            elif node.tag == 'create':
131                self.parseCreate(node)
132            elif node.tag == 'clear_ai':
133                name = convertName(node.get('name'))
134                if name not in self._ai_info:
135                    self._ai_info[name] = {}
136                self._ai_info[name]['CLEAR'] = True
137            elif node.tag == 'add_ai':
138                if node.get('name') is None:
139                    self.warning('Ignore', node)
140                else:
141                    name = convertName(node.get('name'))
142                    if name not in self._ai_info:
143                        self._ai_info[name] = {}
144                    self._ai_info[name][node.get('type').upper()] = node.attrib
145            elif node.tag == 'set_object_property':
146                name = convertName(node.get('name'))
147                property = node.get('property')
148                self._body.append('if %s ~= nil and %s:isValid() then' % (name, name))
149                if property == 'positionX':
150                    self._body.append('    local x, y = %s:getPosition()' % (name))
151                    x, y = convertPosition(node.get('value'), 0)
152                    self._body.append('    %s:setPosition(%s, y)' % (name, x))
153                elif property == 'positionY':
154                    pass
155                elif property == 'positionZ':
156                    self._body.append('    local x, y = %s:getPosition()' % (name))
157                    x, y = convertPosition(0, node.get('value'))
158                    self._body.append('    %s:setPosition(x, %s)' % (name, y))
159                elif property == 'shieldStateFront':
160                    self._body.append('    %s:setFrontShield(%f)' % (name, float(node.get('value'))))
161                elif property == 'shieldStateBack':
162                    self._body.append('    %s:setRearShield(%f)' % (name, float(node.get('value'))))
163                elif property == 'shieldMaxStateFront':
164                    self._body.append('    %s:setFrontShieldMax(%f)' % (name, float(node.get('value'))))
165                elif property == 'shieldMaxStateBack':
166                    self._body.append('    %s:setRearShieldMax(%f)' % (name, float(node.get('value'))))
167                elif property == 'systemDamageBeam':
168                    self._body.append('    %s:setSystemHealth("beamweapons", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
169                elif property == 'systemDamageTorpedo':
170                    self._body.append('    %s:setSystemHealth("missilesystem", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
171                elif property == 'systemDamageTactical':
172                    self.warning('Reactor instead of sensors', node)
173                    self._body.append('    %s:setSystemHealth("reactor", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
174                elif property == 'systemDamageTurning':
175                    self._body.append('    %s:setSystemHealth("maneuver", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
176                elif property == 'systemDamageImpulse':
177                    self._body.append('    %s:setSystemHealth("impulse", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
178                elif property == 'systemDamageWarp':
179                    self._body.append('    %s:setSystemHealth("warp", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
180                    self._body.append('    %s:setSystemHealth("jumpdrive", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
181                elif property == 'systemDamageFrontShield':
182                    self._body.append('    %s:setSystemHealth("frontshield", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
183                elif property == 'systemDamageBackShield':
184                    self._body.append('    %s:setSystemHealth("rearshield", %f)' % (name, 1.0 - float(node.get('value')) / 100.0))
185                elif property == 'willAcceptCommsOrders':
186                    self.warning('Ignore', node)
187                elif property == 'eliteAIType':
188                    self.warning('Ignore', node)
189                elif property == 'eliteAbilityBits':
190                    bits = int(node.get('value'))
191                    if (bits & 8) or (bits & 64):
192                        self._body.append('    %s:setJumpDrive(True)' % (name))
193                    if bits & 32:
194                        self._body.append('    %s:setWarpDrive(True)' % (name))
195                else:
196                    self.warning('Ignore', node)
197                    #raise UnknownArtemisTagError(node)
198                self._body.append('end')
199            elif node.tag == 'set_fleet_property':
200                self.warning('Ignore', node)
201            elif node.tag == 'set_timer':
202                self._body.append('timers["%s"] = %f' % (convertName(node.get('name')), float(node.get('seconds'))))
203            elif node.tag == 'set_variable':
204                if node.get('randomIntHigh') is not None:
205                    self._body.append('variable_%s = random(%d, %d) --Should be random int...' % (convertName(node.get('name')), int(node.get('randomIntLow')), int(node.get('randomIntHigh'))))
206                elif node.get('randomFloatHigh') is not None:
207                    self._body.append('variable_%s = random(%d, %d)' % (convertName(node.get('name')), float(node.get('randomFloatLow')), int(node.get('randomFloatHigh'))))
208                else:
209                    self._body.append('variable_%s = %s' % (convertName(node.get('name')), convertFloat(node.get('value'))))
210            elif node.tag == 'set_ship_text':
211                self.warning('Ignore', node)
212            elif node.tag == 'set_relative_position':
213                self._body.append('tmp_x, tmp_y = %s:getPosition()' % (convertName(node.get('name1'))));
214                self._body.append('tmp_x2, tmp_y2 = vectorFromAngle(%s:getRotation() + %f, %f)' % (convertName(node.get('name1')), float(node.get('angle')), float(node.get('distance'))));
215                self._body.append('%s:setPosition(x, y);' % (convertName(node.get('name2'))));
216            elif node.tag == 'end_mission':
217                self._body.append('victory("Independent")')
218            elif node.tag == 'set_player_grid_damage':
219                if convertSystemName(node) == 'warp':
220                    self._body.append('getPlayerShip(-1):setSystemHealth("%s", %f)' % ('jumpdrive', 1.0 - float(node.get('value')) * 2.0))
221                self._body.append('getPlayerShip(-1):setSystemHealth("%s", %f)' % (convertSystemName(node), 1.0 - float(node.get('value')) * 2.0))
222            elif node.tag == 'destroy':
223                name = convertName(node.get('name'))
224                self._body.append('if %s ~= nil and %s:isValid() then %s:destroy() end' % (name, name, name))
225            elif node.tag == 'destroy_near':
226                obj_type = node.get('type')
227                if obj_type == 'nebulas':
228                    obj_type = 'Nebula'
229                elif obj_type == 'asteroids':
230                    obj_type = 'Asteroid'
231                elif obj_type == 'mines':
232                    obj_type = 'Mine'
233                else:
234                    raise UnknownArtemisTagError(node)
235                if node.get('name'):
236                    name = convertName(node.get('name'))
237                    self._body.append('if %s ~= nil and %s:isValid() then' % (name, name))
238                    self._body.append('    for _, obj in ipairs(%s:getObjectsInRange(%f)) do' % (name, float(node.get('radius'))))
239                    self._body.append('        if obj.typeName == "%s" then obj:destroy() end' % (obj_type))
240                    self._body.append('    end')
241                    self._body.append('end')
242                else:
243                    x, y = convertPosition(node.get('centerX', 0), node.get('centerZ', 0))
244                    r = float(node.get('radius'))
245                    self._body.append('for _, obj in ipairs(getObjectsInRadius(%s, %s, %f)) do' % (x, y, float(node.get('radius'))))
246                    self._body.append('    if obj.typeName == "%s" then obj:destroy() end' % (obj_type))
247                    self._body.append('end')
248
249            elif node.tag == 'if_gm_key':
250                self._conditions.append('0') # gm key triggers are never run.
251                self.warning('Ignore', node)
252            elif node.tag == 'if_client_key':
253                self._conditions.append('0')
254                self.warning('Ignore', node)
255            elif node.tag == 'if_variable':
256                self._conditions.append("variable_%s %s (%s)" % (convertName(node.get("name")), convertComparator(node), node.get("value")))
257            elif node.tag == 'if_timer_finished':
258                self._conditions.append('(timers["%s"] ~= nil and timers["%s"] < 0.0)' % (convertName(node.get("name")), convertName(node.get("name"))))
259            elif node.tag == 'if_outside_box':
260                x1, y1 = convertPosition(node.get('leastX'), node.get('leastZ'))
261                x2, y2 = convertPosition(node.get('mostX'), node.get('mostZ'))
262                self._conditions.append('ifOutsideBox(%s, %s, %s, %s, %s)' % (convertName(node.get("name")), x1, y1, x2, y2))
263            elif node.tag == 'if_inside_box':
264                x1, y1 = convertPosition(node.get('leastX'), node.get('leastZ'))
265                x2, y2 = convertPosition(node.get('mostX'), node.get('mostZ'))
266                self._conditions.append('ifInsideBox(%s, %s, %s, %s, %s)' % (convertName(node.get("name")), x1, y1, x2, y2))
267            elif node.tag == 'if_inside_sphere':
268                x1, y1 = convertPosition(node.get('centerX'), node.get('centerZ'))
269                r = float(node.get('radius'))
270                self._conditions.append('ifInsideSphere(%s, %s, %s, %f)' % (convertName(node.get("name")), x1, y1, r))
271            elif node.tag == 'if_outside_sphere':
272                x1, y1 = convertPosition(node.get('centerX'), node.get('centerZ'))
273                r = float(node.get('radius'))
274                self._conditions.append('ifOutsideSphere(%s, %s, %s, %f)' % (convertName(node.get("name")), x1, y1, r))
275            elif node.tag == 'if_docked':
276                self._conditions.append('ifdocked(%s)' % (convertName(node.get("name"))))
277            elif node.tag == 'if_fleet_count':
278                self._conditions.append('countFleet(%d) %s %f' % (int(node.get('fleetnumber', 0)), convertComparator(node), float(node.get('value'))))
279            elif node.tag == 'if_distance':
280                self._conditions.append('(%s ~= nil and %s ~= nil and %s:isValid() and %s:isValid() and distance(%s, %s) %s %f)' % (convertName(node.get('name1')), convertName(node.get('name2')), convertName(node.get('name1')), convertName(node.get('name2')), convertName(node.get('name1')), convertName(node.get('name2')), convertComparator(node), float(node.get('value'))))
281            elif node.tag == 'if_exists':
282                self._conditions.append('(%s ~= nil and %s:isValid())' % (convertName(node.get('name')), convertName(node.get('name'))))
283            elif node.tag == 'if_not_exists':
284                self._conditions.append('(%s == nil or not %s:isValid())' % (convertName(node.get('name')), convertName(node.get('name'))))
285            elif node.tag == 'if_player_is_targeting':
286                self._conditions.append('(%s ~= nil and %s:isValid() and getPlayerShip(-1):getTarget() == %s)' % (convertName(node.get('name')), convertName(node.get('name')), convertName(node.get('name'))))
287            else:
288                raise UnknownArtemisTagError(node)
289                self.warning('Ignore', node)
290
291        # Convert the AI statements to EE AI.
292        for name, ai in self._ai_info.items():
293            ai_list = sorted(list(ai.keys()))
294            if ai_list == ['ATTACK'] or ai_list == ['ATTACK', 'ELITE_AI'] or ai_list == ['ATTACK', 'CLEAR'] or ai_list == ['ATTACK', 'CHASE_NEUTRAL']:
295                self._body.append('%s:orderAttack(%s)' % (name, convertName(ai['ATTACK']['targetName'])))
296            elif ai_list == ['POINT_THROTTLE'] or ai_list == ['FOLLOW_COMMS_ORDERS', 'POINT_THROTTLE'] or ai_list == ['CHASE_PLAYER', 'POINT_THROTTLE']:
297                x, y = convertPosition(ai['POINT_THROTTLE']['value1'], ai['POINT_THROTTLE']['value3'])
298                self._body.append('%s:orderFlyTowards(%s, %s)' % (name, x, y))
299            elif ai_list == ['CLEAR']:
300                self._body.append('%s:orderIdle()' % (name))
301            elif ai_list == ['ELITE_AI']:
302                pass
303            else:
304                self.warning('Unknown AI: %s: %s' % (name, ai))
305
306    def parseCreate(self, node):
307        if node.get('use_gm_position') is not None:
308            return
309        create_type = node.get('type')
310        if create_type == 'player':
311            name = convertName(node.get('name'))
312            x, y = convertPosition(node.get('x'), node.get('z'))
313            self._body.append('%s = PlayerSpaceship():setFaction("Human Navy"):setTemplate("Player Cruiser"):setCallSign("%s"):setPosition(%s, %s)' % (name, node.get('name'), x, y))
314        elif create_type == 'neutral':
315            name = convertName(node.get('name'))
316            x, y = convertPosition(node.get('x'), node.get('z'))
317            self._body.append('%s = CpuShip():setTemplate("Tug"):setCallSign("%s"):setFaction("%s"):setPosition(%s, %s):orderRoaming()' % (name, node.get('name'), convertRaceKeys(node, 'neutral'), x, y))
318            self.addToFleet(name, node)
319        elif create_type == 'enemy':
320            name = convertName(node.get('name', 'temp_enemy_name'))
321            x, y = convertPosition(node.get('x'), node.get('z'))
322            self._body.append('%s = CpuShip():setTemplate("Cruiser"):setCallSign("%s"):setFaction("%s"):setPosition(%s, %s):orderRoaming()' % (name, node.get('name'), convertRaceKeys(node, 'enemy'), x, y))
323            self.addToFleet(name, node)
324
325            self.addToFleet(name, node, 0) # Add every enemy ship to fleet 0
326        elif create_type == 'station':
327            name = convertName(node.get('name'))
328            x, y = convertPosition(node.get('x'), node.get('z'))
329            self._body.append('%s = SpaceStation():setTemplate("Small Station"):setCallSign("%s"):setFaction("%s"):setPosition(%s, %s)' % (name, node.get('name'), convertRaceKeys(node, 'friendly'), x, y))
330        elif create_type == 'blackHole':
331            name = convertName(node.get('name', 'temp_blackhole_name'))
332            x, y = convertPosition(node.get('x'), node.get('z'))
333            self._body.append('%s = BlackHole():setPosition(%s, %s)' % (name, x, y))
334        elif create_type == 'whale':
335            self.warning('Ignore', node)
336        elif create_type == 'monster':
337            self.warning('Ignore', node)
338        elif create_type == 'genericMesh':
339            self.warning('Ignore', node)
340        elif create_type == 'anomaly':
341            # Using a supply drop instead of an anomaly
342            output = ""
343            if node.get('name') is not None:
344                name = convertName(node.get('name'))
345                output = "%s = " % (name)
346            x, y = convertPosition(node.get('x'), node.get('z'))
347            output += 'SupplyDrop():setFaction("Human Navy"):setPosition(%s, %s):setEnergy(500):setWeaponStorage("Nuke", 0):setWeaponStorage("Homing", 0):setWeaponStorage("Mine", 0):setWeaponStorage("EMP", 0)' % (x, y)
348            self._body.append(output)
349        elif create_type == 'asteroids':
350            self.parseCreateCount('Asteroid()', node)
351        elif create_type == 'mines':
352            self.parseCreateCount('Mine()', node)
353        elif create_type == 'nebulas':
354            node.set('count', '(%s + 24) / 25' % convertFloat(node.get('count')))
355            if node.get('randomRange') is not None:
356                node.set('randomRange', '%s - 2500' % convertFloat(node.get('randomRange')))
357            self.parseCreateCount('Nebula()', node)
358        else:
359            raise UnknownArtemisTagError(node)
360
361    def parseCreateCount(self, object_create_script, node):
362        count = convertFloat(node.get('count'))
363        x, y = convertPosition(node.get('startX', 0), node.get('startZ', 0))
364        self._body.append('tmp_count = %s' % (count))
365        self._body.append('for tmp_counter=1,tmp_count do')
366        if node.get('radius') is not None:
367            radius = convertFloat(node.get('radius'))
368            start_angle = float(node.get('startAngle', 0)) - 90
369            end_angle = float(node.get('endAngle', 360)) - 90
370            self._body.append('    tmp_x, tmp_y = vectorFromAngle(%s + (%s - %s) * (tmp_counter - 1) / tmp_count, %s)' % (start_angle, end_angle, start_angle, radius))
371            self._body.append('    tmp_x, tmp_y = tmp_x + %s, tmp_y + %s' % (x, y))
372        else:
373            x2, y2 = convertPosition(node.get('endX'), node.get('endZ'))
374            self._body.append('    tmp_x = %s + (%s - %s) * (tmp_counter - 1) / tmp_count' % (x, x2, x))
375            self._body.append('    tmp_y = %s + (%s - %s) * (tmp_counter - 1) / tmp_count' % (y, y2, y))
376        if node.get('randomRange') is not None:
377            random_range = convertFloat(node.get('randomRange', 0))
378            self._body.append('    tmp_x2, tmp_y2 = vectorFromAngle(random(0, 360), random(0, %s))' % (random_range))
379            self._body.append('    tmp_x, tmp_y = tmp_x + tmp_x2, tmp_y + tmp_y2')
380        self._body.append('    %s:setPosition(tmp_x, tmp_y)' % (object_create_script))
381        self._body.append('end')
382
383    def addToFleet(self, name, node, fleetnumber=-1):
384        if fleetnumber != -1:
385            fleetnumber = int(node.get('fleetnumber', -1))
386        if fleetnumber > 0:
387            if 'fleet_check_%d' % (fleetnumber) not in self._done:
388                self._done['fleet_check_%d' % (fleetnumber)] = True
389                self._body.append('if fleet[%d] == nil then fleet[%d] = {} end' % (fleetnumber, fleetnumber))
390            self._body.append('table.insert(fleet[%d], %s)' % (fleetnumber, name))
391
392    def warning(self, *args):
393        message = ''
394        for arg in args:
395            if isinstance(arg, str):
396                message += arg + ' '
397            elif isinstance(arg, xml.etree.ElementTree.Element):
398                message += '<' + arg.tag + '> ' + str(arg.attrib) + ' '
399                if arg.text is not None:
400                    message += convertString(arg.text)
401            else:
402                message += str(arg)
403        self._body.append('--WARNING: %s' % (message))
404        self._warnings.append(args)
405
406    def getBody(self, indent=1):
407        body = ''
408        for line in self._body:
409            body += ('    ' * indent) + line + '\n';
410        return body
411
412    def getCondition(self):
413        return ' and '.join(self._conditions)
414
415    def getWarnings(self):
416        return self._warnings
417
418    def isValid(self):
419        return self._valid
420
421class Converter:
422    def __init__(self, filename):
423        self._data = xml.etree.ElementTree.XML(open(filename, 'rb').read().replace(b'"<="', b'"&lt;="').replace(b'"<"', b'"&lt;"').replace(b'">="', b'"&gt;="').replace(b'">"', b'"&gt;"'))
424
425        self._events = []
426        self._start_event = Event(self._data.find("start"))
427        for node in self._data.findall("event"):
428            self._events.append(Event(node))
429
430    def export(self, name, filename):
431        f = open(filename, "w")
432        f.write('-- Name: %s\n' % (name))
433        f.write('-- Description: Converted Artemis mission\n')
434        warnings = []
435        for line in open("artemis_mission_convert_template.lua", "r"):
436            if line.strip() == '###START###':
437                f.write(self._start_event.getBody())
438                warnings += self._start_event.getWarnings()
439            elif line.strip() == '###EVENTS###':
440                for event in self._events:
441                    if not event.isValid():
442                        continue
443                    if event.getCondition() != "":
444                        f.write("    if %s then\n" % event.getCondition())
445                        f.write(event.getBody(2))
446                        warnings += event.getWarnings()
447                        f.write("    end\n")
448                    else:
449                        f.write(event.getBody(1))
450                        warnings += event.getWarnings()
451            else:
452                f.write(line)
453        print('Written: %s with %d warnings' % (filename, len(warnings)))
454        warning_types = {}
455        for warning in warnings:
456            for item in warning:
457                if isinstance(item, xml.etree.ElementTree.Element):
458                    if item.tag not in warning_types:
459                        warning_types[item.tag] = 0
460                    warning_types[item.tag] += 1
461        for key, count in warning_types.items():
462            print("Warning: %s %dx" % (key, count))
463        return len(warning_types) == 0
464
465if __name__ == "__main__":
466    count = 0
467    success = 0
468    for arg in sys.argv[1:]:
469        if os.path.isfile(arg):
470            filename = arg
471            count += 1
472            print("========================================================");
473            print("Converting: ", filename);
474            try:
475                c = Converter(filename)
476                name = os.path.splitext(os.path.basename(filename))[0].replace("MISS_", "")
477                c.export(name, "scripts/scenario_99_%s.lua" % (name))
478                success += 1
479            except:
480                traceback.print_exc()
481        for root, dirnames, filenames in os.walk(arg):
482            for filename in fnmatch.filter(filenames, '*.xml'):
483                filename = os.path.join(root, filename)
484                count += 1
485                print("========================================================");
486                print("Converting: ", filename);
487                try:
488                    c = Converter(filename)
489                    name = os.path.splitext(os.path.basename(filename))[0].replace("MISS_", "")
490                    if c.export(name, "scripts/scenario_99_%s.lua" % (name)):
491                        sys.exit(1)
492                    success += 1
493                except:
494                    traceback.print_exc()
495                    sys.exit(1)
496    print("Converted %d of the %d scripts" % (success, count))
497