Add A26 to A27 scripts

This commit is contained in:
Stanislas Daniel Claude Dolcini 2023-01-17 00:32:20 +01:00
parent 675591324c
commit 418b077ea6
Signed by: Stan
GPG Key ID: 697155C99A989EC2
5 changed files with 465 additions and 0 deletions

View File

38
A26ToA27.py Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- mode: python-mode; python-indent-offset: 4; -*-
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: © 2023 Wildfire Games
# SPDX-FileCopyrightText: © 2023 Stanislas Daniel Claude Dolcini
from A26_A27.ActorsWithMaterialsWithNoSpecMapFixer import ActorsWithMaterialsWithNoSpecMapFixer
from A26_A27.P265 import TemplateFixer as RequirementsFixer
from A26_A27.P292 import remove_terrain_materials_recursively, remove_actor_materials_recursively
from argparse import ArgumentParser
from pathlib import Path
import os
if __name__ == '__main__':
parser = ArgumentParser(description='A26 to A27 converter.')
parser.add_argument('-r', '--root', action='store', dest='root', default=os.path.dirname(os.path.realpath(__file__)))
parser.add_argument('-m', '--mod', action='store', dest='mod', default='public')
parser.add_argument('-v', '--verbose', action='store_true', default=False, help="Be verbose.")
args = parser.parse_args()
script_dir = args.root
mod_name = args.mod
path = Path(script_dir) / mod_name
print(f"Running in {path}")
print("Fixing rP27245...")
template_fixer = RequirementsFixer(path)
template_fixer.run()
print("Fixing Actors using no specmap (rP27308)...")
template_fixer = ActorsWithMaterialsWithNoSpecMapFixer(path)
template_fixer.run()
print("Fixing actors using old deprecated materials (rP27368)")
if os.path.exists(path / 'art' / 'terrains') and os.path.isdir(path / 'art' / 'terrains'):
remove_terrain_materials_recursively(path / 'art' / 'terrains')
if os.path.exists(path / 'art' / 'actors') and os.path.isdir(path / 'art' / 'actors'):
remove_actor_materials_recursively(path / 'art' / 'actors')

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- mode: python-mode; python-indent-offset: 4; -*-
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: © 2023 Wildfire Games
# SPDX-FileCopyrightText: © 2023 Stanislas Daniel Claude Dolcini
from utils.fixers.BaseFixer import BaseFixer
import os
import xml.etree.ElementTree as ET
class ActorsWithMaterialsWithNoSpecMapFixer(BaseFixer):
def __init__(self, vfs_root, verbose=False):
BaseFixer.__init__(self, vfs_root, verbose)
self.add_files(os.path.join('art', 'actors'), tuple(".xml"))
self.changes = [
['basic_spec', 'no_trans_spec'],
["blend_spec", 'basic_trans_spec'],
["objectcolor_spec", 'objectcolor_specmap'],
["playercolor_spec", 'player_trans_spec'],
]
def process_actor(self, actorNode):
materialCmp = actorNode.find('material')
if materialCmp is not None:
changed = False
if (materialCmp.text == 'basic_spec.xml' or
materialCmp.text == 'blend_spec.xml' or
materialCmp.text == 'objectcolor_spec.xml' or
materialCmp.text == 'playercolor_spec.xml'):
for group in actorNode:
for variant in group:
cmpTextures = variant.find('textures')
if cmpTextures is not None:
found = False
for texture in cmpTextures:
if texture.attrib['name']== 'specTex':
found = True
break
if found:
continue
tag = ET.SubElement(cmpTextures, 'texture')
tag.attrib['name'] = 'specTex'
tag.attrib['file'] = 'null_white.dds'
changed = True
for change in self.changes:
materialCmp.text = materialCmp.text.replace(change[0], change[1])
return changed
return False
def run(self):
count = 0
for file in self.files:
tree = ET.parse(file)
root = tree.getroot()
changed = False
if root.tag == 'actor':
changed = self.process_actor(root)
elif root.tag == 'qualitylevels':
for actor in root:
changed |= self.process_actor(actor)
if changed:
self.save_xml_file(tree, root, file, False)
count = count + 1
self.logger.info(f"Fixed {count} file(s).")

70
A26_A27/P265.py Normal file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- mode: python-mode; python-indent-offset: 4; -*-
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: © 2023 Wildfire Games
# SPDX-FileCopyrightText: © 2023 Freagarach
import fileinput
import glob
import os
from lxml import etree as ET
class StyleFixer:
def fix_template_style(template_path):
changes = [
["version='1.0'", 'version="1.0"'],
["'UTF-8'", '"utf-8"']
]
StyleFixer.sed(template_path, changes)
def sed(path, changes):
for line in fileinput.input(path, inplace=True):
for change in changes:
line = line.replace(change[0], change[1])
print(line, end="")
class TemplateFixer:
def __init__(self, vfs_root):
self.template_folder = os.path.join(vfs_root, 'simulation', 'templates')
def fix_template(self, template_path):
tree = ET.parse(template_path)
root = tree.getroot()
changed = False
cmp_identity = root.find('Identity')
if cmp_identity != None:
if self.fix_requirements(cmp_identity):
changed = True
cmp_upgrade = root.find('Upgrade')
if cmp_upgrade != None:
for upgrade in cmp_upgrade.iterfind("./"):
if self.fix_requirements(upgrade):
changed = True
if not changed:
return False
root[:] = sorted(root, key=lambda x: x.tag)
ET.indent(tree)
tree.write(template_path, xml_declaration=True, pretty_print=True, encoding='utf-8')
return True
def fix_requirements(self, cmp):
req_tech = cmp.find('RequiredTechnology')
if req_tech == None:
return False
ET.SubElement(ET.SubElement(cmp, 'Requirements'), 'Techs').text = req_tech.text
cmp.remove(req_tech)
return True
def run(self):
for template in glob.iglob(self.template_folder + '/**/*.xml', recursive=True):
if self.fix_template(template):
StyleFixer.fix_template_style(template)

283
A26_A27/P292.py Normal file
View File

@ -0,0 +1,283 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- mode: python-mode; python-indent-offset: 4; -*-
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: © 2023 Wildfire Games
# SPDX-FileCopyrightText: © 2023 Vladislav Belov
import os
import sys
import xml.etree.ElementTree as ET
def output_xml_tree(tree, path):
with open(path, 'wt') as handle:
handle.write('<?xml version="1.0" encoding="utf-8"?>\n')
def output_xml_node(node, handle, depth):
indent = ' ' * depth
attributes = ''
for attribute_name in node.attrib.keys():
attributes += ' {}="{}"'.format(attribute_name, node.attrib[attribute_name])
if len(node) != 0:
assert (node.text is None) or (not node.text.strip())
handle.write('{}<{}{}>\n'.format(indent, node.tag, attributes))
for child in node:
output_xml_node(child, handle, depth + 1)
handle.write('{}</{}>\n'.format(indent, node.tag))
else:
if node.text is None:
handle.write('{}<{}{}/>\n'.format(indent, node.tag, attributes))
else:
handle.write('{}<{}{}>{}</{}>\n'.format(indent, node.tag, attributes, node.text, node.tag))
output_xml_node(tree.getroot(), handle, 0)
def remove_terrain_materials(path):
with open(path, 'rt') as handle:
xml = handle.read()
terrain_base = 'terrain_base.xml' in xml
terrain_norm = 'terrain_norm.xml' in xml
terrain_spec = 'terrain_spec.xml' in xml
terrain_triplanar = 'terrain_triplanar.xml' in xml
should_save = terrain_base or terrain_norm or terrain_spec or terrain_triplanar
assert xml.count('<material>') == 1
tree = ET.parse(path)
root = tree.getroot()
for element in root:
if len(element) != 0:
assert element.tag == 'textures'
for child in element:
assert child.tag == 'texture'
if terrain_base or terrain_spec or terrain_triplanar:
texture_element = ET.SubElement(element, 'texture')
texture_element.set('name', 'normTex')
texture_element.set('file', 'types/default_norm.png')
if terrain_base or terrain_norm or terrain_triplanar:
texture_element = ET.SubElement(element, 'texture')
texture_element.set('name', 'specTex')
texture_element.set('file', 'types/blackness.dds')
elif element.tag == 'material':
if terrain_base or terrain_norm or terrain_spec:
element.text = 'terrain_norm_spec.xml'
elif terrain_triplanar:
element.text = 'terrain_triplanar_norm_spec.xml'
if should_save:
output_xml_tree(tree, path)
def get_materials():
return {
'aura.xml': {'norm': False, 'spec': True, 'replacement': 'aura_norm_spec.xml'},
'aura_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'aura_norm_spec.xml'},
'basic_glow.xml': {'norm': False, 'spec': True, 'replacement': 'basic_glow_norm_spec.xml'},
'basic_glow_norm.xml': {'norm': True, 'spec': True, 'replacement': 'basic_glow_norm_spec.xml'},
'basic_glow_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_glow_norm_spec.xml'},
'basic_glow_wind.xml': {'norm': False, 'spec': True, 'replacement': 'basic_glow_wind_norm_spec.xml'},
'basic_glow_wind_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_glow_wind_norm_spec.xml'},
'basic_specmap.xml': {'norm': False, 'spec': True, 'replacement': 'no_trans_norm_spec.xml'},
'basic_trans.xml': {'norm': False, 'spec': False, 'replacement': 'basic_trans_norm_spec.xml'},
'basic_trans_ao.xml': {'norm': False, 'spec': False, 'replacement': 'basic_trans_ao_norm_spec.xml'},
'basic_trans_ao_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_trans_ao_norm_spec.xml'},
'basic_trans_ao_parallax_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_trans_ao_parallax_spec.xml'},
'basic_trans_ao_spec.xml': {'norm': False, 'spec': True, 'replacement': 'basic_trans_ao_norm_spec.xml'},
'basic_trans_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_trans_norm_spec.xml'},
'basic_trans_parallax_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_trans_parallax_spec.xml'},
'basic_trans_spec.xml': {'norm': False, 'spec': True, 'replacement': 'basic_trans_norm_spec.xml'},
'basic_trans_wind.xml': {'norm': False, 'spec': False, 'replacement': 'basic_trans_wind_norm_spec.xml'},
'basic_trans_wind_grain.xml': {'norm': False, 'spec': False, 'replacement': 'basic_trans_wind_grain_norm_spec.xml'},
'basic_trans_wind_grain_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_trans_wind_grain_norm_spec.xml'},
'basic_trans_wind_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'basic_trans_wind_norm_spec.xml'},
'default.xml': {'norm': False, 'spec': False, 'replacement': 'no_trans_norm_spec.xml'},
'no_trans_ao.xml': {'norm': False, 'spec': False, 'replacement': 'no_trans_ao_norm_spec.xml'},
'no_trans_ao_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'no_trans_ao_norm_spec.xml'},
'no_trans_ao_parallax_spec.xml': {'norm': True, 'spec': True, 'replacement': 'no_trans_ao_parallax_spec.xml'},
'no_trans_ao_spec.xml': {'norm': False, 'spec': True, 'replacement': 'no_trans_ao_norm_spec.xml'},
'no_trans_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'no_trans_norm_spec.xml'},
'no_trans_parallax_ao.xml': {'norm': True, 'spec': False, 'replacement': 'no_trans_ao_parallax_spec.xml'},
'no_trans_parallax_spec.xml': {'norm': True, 'spec': True, 'replacement': 'no_trans_parallax_spec.xml'},
'no_trans_spec.xml': {'norm': False, 'spec': True, 'replacement': 'no_trans_norm_spec.xml'},
'objectcolor.xml': {'norm': False, 'spec': False, 'replacement': 'objectcolor_norm_spec.xml'},
'objectcolor_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'objectcolor_norm_spec.xml'},
'objectcolor_specmap.xml': {'norm': False, 'spec': True, 'replacement': 'objectcolor_norm_spec.xml'},
'player_trans.xml': {'norm': False, 'spec': False, 'replacement': 'player_trans_norm_spec.xml'},
'player_trans_ao.xml': {'norm': False, 'spec': False, 'replacement': 'player_trans_ao_norm_spec.xml'},
'player_trans_ao_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_ao_norm_spec.xml'},
'player_trans_ao_parallax.xml': {'norm': True, 'spec': False, 'replacement': 'player_trans_ao_parallax_spec.xml'},
'player_trans_ao_parallax_spec.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_ao_parallax_spec.xml'},
'player_trans_ao_spec.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_ao_spec.xml'},
'player_trans_norm.xml': {'norm': True, 'spec': False, 'replacement': 'player_trans_norm_spec.xml'},
'player_trans_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_norm_spec.xml'},
'player_trans_norm_spec_helmet.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_norm_spec_helmet.xml'},
'player_trans_parallax.xml': {'norm': True, 'spec': False, 'replacement': 'player_trans_parallax_spec.xml'},
'player_trans_parallax_spec.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_parallax_spec.xml'},
'player_trans_parallax_spec_helmet.xml': {'norm': True, 'spec': True, 'replacement': 'player_trans_parallax_spec_helmet.xml'},
'player_trans_spec.xml': {'norm': False, 'spec': True, 'replacement': 'player_trans_norm_spec.xml'},
'player_trans_spec_helmet.xml': {'norm': False, 'spec': True, 'replacement': 'player_trans_norm_spec_helmet.xml'},
'player_water.xml': {'norm': False, 'spec': False, 'replacement': 'player_water.xml'},
'rock_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'rock_norm_spec.xml'},
'rock_norm_spec_ao.xml': {'norm': True, 'spec': True, 'replacement': 'rock_norm_spec_ao.xml'},
'rock_normstrong_spec.xml': {'norm': True, 'spec': True, 'replacement': 'rock_normstrong_spec.xml'},
'rock_normstrong_spec_ao.xml': {'norm': True, 'spec': True, 'replacement': 'rock_normstrong_spec_ao.xml'},
'terrain_base.xml': {'norm': False, 'spec': False, 'replacement': 'terrain_norm_spec.xml'},
'terrain_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'terrain_norm_spec.xml'},
'terrain_normstrong_spec.xml': {'norm': True, 'spec': True, 'replacement': 'terrain_normstrong_spec.xml'},
'terrain_normweak_spec.xml': {'norm': True, 'spec': True, 'replacement': 'terrain_normweak_spec.xml'},
'terrain_triplanar.xml': {'norm': False, 'spec': False, 'replacement': 'terrain_triplanar_norm_spec.xml'},
'terrain_triplanar_norm_spec.xml': {'norm': True, 'spec': True, 'replacement': 'terrain_triplanar_norm_spec.xml'},
'trans_wind.xml': {'norm': False, 'spec': False, 'replacement': 'basic_trans_wind_norm_spec.xml'},
'waterfall.xml': {'norm': False, 'spec': False, 'replacement': 'waterfall.xml'},
}
def get_material(material):
return get_materials()[material]
def remove_actor_materials(path):
with open(path, 'rt') as handle:
xml = handle.read()
should_save = False
tree = ET.parse(path)
root = tree.getroot()
material_name = None
if '<material>' in xml:
assert xml.count('<material>') == 1
if root.tag == 'actor':
for child in root:
if child.tag == 'material':
material_name = child.text.strip()
child.text = get_material(material_name)['replacement']
else:
assert root.tag == 'qualitylevels'
for root_child in root:
assert root_child.tag == 'inline' or root_child.tag == 'actor'
for child in root_child:
if child.tag == 'material':
material_name = child.text.strip()
child.text = get_material(material_name)['replacement']
else:
assert root.tag == 'actor'
material_name = 'default.xml'
material_element = ET.SubElement(root, 'material')
material_element.text = get_material(material_name)['replacement']
assert material_name is not None
material = get_material(material_name)
if material_name == material['replacement']:
return
replacement_material = get_material(material['replacement'])
assert (not material['norm']) or (not material['spec'])
assert replacement_material['norm'] and replacement_material['spec']
if material['norm'] != ('normTex' in xml) or material['spec'] != ('specTex' in xml) or '<!--' in xml:
sys.stdout.write(
'Actor "{}" should be handled manually. It should use "{}" instead of "{}".\n'.format(
path, material['replacement'], material_name))
return
should_save = True
# If there are no textures it means the material isn't really useful. We'll with them later.
if xml.count('<textures>') > 0:
def add_default_norm_element(textures_element, index=None):
if not index:
norm_element = ET.SubElement(textures_element, 'texture')
else:
norm_element = ET.Element('texture')
norm_element.set('file', 'default_norm.png')
norm_element.set('name', 'normTex')
if index:
textures_element.insert(index, norm_element)
def add_default_spec_element(textures_element, index=None):
if not index:
spec_element = ET.SubElement(textures_element, 'texture')
else:
spec_element = ET.Element('texture')
spec_element.set('file', 'null_black.dds')
spec_element.set('name', 'specTex')
if index:
textures_element.insert(index, spec_element)
def add_missing_textures(element, needs_norm, needs_spec):
for child in element:
if child.tag == 'textures':
expected_len = 1
if not needs_norm:
expected_len += 1
if not needs_spec:
expected_len += 1
if expected_len > 1:
only_base = True
for texture_child in child:
if texture_child.get('name') == 'normTex' or texture_child.get('name') == 'specTex':
only_base = False
if only_base:
continue
if needs_norm and needs_spec:
add_default_norm_element(child)
add_default_spec_element(child)
elif needs_norm:
add_default_norm_element(child)
elif needs_spec:
add_default_spec_element(child)
else:
add_missing_textures(child, needs_norm, needs_spec)
if not material['norm'] and not material['spec']:
if xml.count('<textures>') > 1:
group_element = ET.Element('group')
variant_element = ET.SubElement(group_element, 'variant')
textures_element = ET.SubElement(variant_element, 'textures')
add_default_norm_element(textures_element)
add_default_spec_element(textures_element)
if root.tag == 'actor':
last_index = len(root) - 1
assert root[last_index].tag == 'material'
root.insert(last_index, group_element)
else:
assert root.tag == 'qualitylevels'
for root_child in root:
assert root_child.tag == 'inline' or root_child.tag == 'actor'
last_index = len(root_child) - 1
assert root_child[last_index].tag == 'material'
root_child.insert(last_index, group_element)
else:
add_missing_textures(root, True, True)
else:
add_missing_textures(root, not material['norm'], not material['spec'])
if should_save:
sys.stdout.write('Actor "{}" is fixed.\n'.format(path))
output_xml_tree(tree, path)
def remove_terrain_materials_recursively(path):
for file_name in os.listdir(path):
file_path = os.path.join(path, file_name)
if os.path.isfile(file_path):
name, ext = os.path.splitext(file_name)
if ext.lower() == '.xml' and name.lower() != 'terrains':
remove_terrain_materials(file_path)
elif os.path.isdir(file_path):
remove_terrain_materials_recursively(file_path)
def remove_actor_materials_recursively(path):
for file_name in os.listdir(path):
file_path = os.path.join(path, file_name)
if os.path.isfile(file_path):
name, ext = os.path.splitext(file_name)
if ext.lower() == '.xml':
remove_actor_materials(file_path)
elif os.path.isdir(file_path):
remove_actor_materials_recursively(file_path)
if __name__ == '__main__':
# paths should be a list of paths to mods folders
paths = sys.argv[1:]
for path in paths:
remove_terrain_materials_recursively(os.path.join(path, 'art', 'terrains'))
remove_actor_materials_recursively(os.path.join(path, 'art', 'actors'))