diff --git a/.normalized_ao_public b/.normalized_ao_public deleted file mode 100644 index e69de29..0000000 diff --git a/A26ToA27.py b/A26ToA27.py new file mode 100644 index 0000000..d2d5986 --- /dev/null +++ b/A26ToA27.py @@ -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') + diff --git a/A26_A27/ActorsWithMaterialsWithNoSpecMapFixer.py b/A26_A27/ActorsWithMaterialsWithNoSpecMapFixer.py new file mode 100644 index 0000000..bba6299 --- /dev/null +++ b/A26_A27/ActorsWithMaterialsWithNoSpecMapFixer.py @@ -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).") diff --git a/A26_A27/P265.py b/A26_A27/P265.py new file mode 100644 index 0000000..4e2eb6b --- /dev/null +++ b/A26_A27/P265.py @@ -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) diff --git a/A26_A27/P292.py b/A26_A27/P292.py new file mode 100644 index 0000000..fbf9365 --- /dev/null +++ b/A26_A27/P292.py @@ -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('\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('') == 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 '' in xml: + assert xml.count('') == 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 '