Add A25 to A26 scripts

This commit is contained in:
Stanislas Daniel Claude Dolcini 2023-01-16 23:39:45 +01:00
parent 0af8f6b051
commit 675591324c
Signed by untrusted user: Stan
GPG Key ID: 697155C99A989EC2
16 changed files with 895 additions and 0 deletions

0
.normalized_ao_public Normal file
View File

68
A25ToA26.py Normal file
View File

@ -0,0 +1,68 @@
#!/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 A25_A26.ElevationBonusFixer import ElevationBonusFixer
from A25_A26.PlayerXMLFixer import PlayerXMLFixer
from A25_A26.SessionIconPathFixer import SessionIconPathFixer
from A25_A26.FixNewPlayerParent import FixNewPlayerParent
from A25_A26.FormationFixer import FormationFixer
from A25_A26.PlayerXMLFormationFixer import PlayerXMLFormationFixer
from A25_A26.P256 import TemplateFixer, ModifiersFixer
from A25_A26.P259 import convert_recursively
from A25_A26.P261 import TemplateFixer as TemplateFixerP261, SedLike
from A25_A26.FancyGrassRemover import FancyGrassRemover
from argparse import ArgumentParser
from pathlib import Path
import os
if __name__ == '__main__':
parser = ArgumentParser(description='A25 to A26 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("Running P256...")
template_fixer = TemplateFixer(path)
template_fixer.run()
modifier_fixer = ModifiersFixer(path)
modifier_fixer.run()
if not os.path.exists('.normalized_ao_' + mod_name) and os.path.isdir(path):
print("Running P259...")
convert_recursively(path)
(Path('.normalized_ao_' + mod_name)).touch()
else:
print("Skipping P259...")
print("Fixing r26074...")
elevationFixer = ElevationBonusFixer(path, args.verbose)
elevationFixer.run()
print("Running P261...")
template_fixer = TemplateFixerP261(path)
template_fixer.run()
print("Running FancyGrassRemover...")
remover = FancyGrassRemover(path, args.verbose)
remover.run()
print("Fixing r26298...")
fixer = PlayerXMLFixer(path, args.verbose)
fixer.run()
print("Fixing r26299...")
fixer = PlayerXMLFormationFixer(path, args.verbose)
fixer.run()
print("Fixing r26317...")
fixer = SessionIconPathFixer(path, args.verbose)
fixer.run()
print("Fixing r26458 ...")
fixer = FixNewPlayerParent(path, args.verbose)
fixer.run()
print("Fixing r26476...")
fixer = FormationFixer(path, args.verbose)
fixer.run()

View File

@ -0,0 +1,54 @@
#!/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 xml.etree.ElementTree as ET
import os
class ElevationBonusFixer(BaseFixer):
def __init__(self, vfs_root, verbose=False):
BaseFixer.__init__(self, vfs_root, verbose, __name__)
self.add_files(os.path.join('simulation', 'templates'), tuple(".xml"))
def fixEffectDelay(self, root):
cmpAttack = root.find('Attack')
changed = False
if cmpAttack is not None:
for attack_type in cmpAttack:
delay = attack_type.find('Delay')
if delay is not None:
delay.tag = 'EffectDelay'
changed = True
return changed
def fixElevationBonus(self, root):
cmpAttack = root.find('Attack')
changed = False
if cmpAttack is not None:
for attack_type in cmpAttack:
bonus = attack_type.find('ElevationBonus')
if bonus is not None:
ET.SubElement(bonus, 'X').text = '0'
ET.SubElement(bonus, 'Y').text = bonus.text
ET.SubElement(bonus, 'Z').text = '0'
bonus.tag = 'Origin'
bonus.text = ''
changed = True
return changed
def run(self):
for file in self.files:
tree = ET.parse(file)
root = tree.getroot()
if self.fixElevationBonus(root) or self.fixEffectDelay(root):
self.logger.info(f'Saving {file}')
self.save_xml_file(tree, root, file)

View File

@ -0,0 +1,43 @@
#!/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
from utils.PMPMap import PmpMap
import os
from pathlib import Path
class FancyGrassRemover(BaseFixer):
def __init__(self, vfs_root, verbose=False):
BaseFixer.__init__(self, vfs_root, verbose, __name__)
self.vfs_root = Path(vfs_root)
self.verbose = verbose
self.add_files(os.path.join("maps"), tuple(".pmp"))
def run(self):
for filePath in self.files:
with open(filePath, "r+b") as f1:
pmpMap = PmpMap(f1)
hasChanged = False
if (self.verbose):
self.logger.info(f'Parsing {filePath}')
for textureIndex in range(0, len(pmpMap.textures)):
texture = pmpMap.textures[textureIndex]
if '_fancy' in texture:
pmpMap.header.data_size -= len(texture)
if (self.verbose):
self.logger.info(f'Replacing {texture} by {texture.replace("_fancy", "")}')
pmpMap.textures[textureIndex] = texture.replace('_fancy', '')
pmpMap.header.data_size += len(texture)
hasChanged = True
if hasChanged:
self.logger.info(f"Patching {filePath}...")
f1.seek(0)
pmpMap.write_to_stream(f1)
else:
f1.close()

View File

@ -0,0 +1,23 @@
#!/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 xml.etree.ElementTree as ET
import os
class FixNewPlayerParent(BaseFixer):
def __init__(self, vfs_root, verbose=False):
BaseFixer.__init__(self, vfs_root, verbose, __name__)
self.add_files(os.path.join('simulation', 'templates', 'special', 'players'), tuple(".xml"))
def run(self):
for file in self.files:
tree = ET.parse(file)
root = tree.getroot()
root.set('parent', "template_player")
self.save_xml_file(tree, root, file)

40
A25_A26/FormationFixer.py Normal file
View File

@ -0,0 +1,40 @@
#!/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 xml.etree.ElementTree as ET
import os
class FormationFixer(BaseFixer):
def __init__(self, vfs_root, verbose=False):
BaseFixer.__init__(self, vfs_root, verbose, __name__)
self.add_files(os.path.join('simulation', 'templates'), tuple(".xml"))
def run(self):
for file in self.files:
tree = ET.parse(file)
root = tree.getroot()
cmp_formation = root.find('Formation')
if cmp_formation is None:
continue
cmp_identity = self.create_tag_if_not_exist(root, 'Identity')
formation_icon_tag = cmp_formation.find('Icon')
if formation_icon_tag is not None:
icon_tag = self.create_tag_if_not_exist(cmp_identity, 'Icon')
icon_tag.text = formation_icon_tag.text
cmp_formation.remove(formation_icon_tag)
formation_name_tag = cmp_formation.find('FormationName')
if formation_name_tag is not None:
generic_name_tag = self.create_tag_if_not_exist(cmp_identity, 'GenericName')
generic_name_tag.text = formation_name_tag.text
cmp_formation.remove(formation_name_tag)
self.save_xml_file(tree, root, file)

113
A25_A26/P256.py Normal file
View File

@ -0,0 +1,113 @@
#!/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
import xml.etree.ElementTree as ET
class SedLike:
def sed(path, changes):
for line in fileinput.input(path, inplace=True, encoding='utf-8'):
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()
production_queue = root.find('ProductionQueue')
if production_queue == None:
return False
technologies = production_queue.find('Technologies')
tech_cost_multiplier = production_queue.find('TechCostMultiplier')
if technologies != None or tech_cost_multiplier != None:
researcher = ET.Element('Researcher')
if (technologies != None):
researcher.append(technologies)
production_queue.remove(technologies)
if (tech_cost_multiplier != None):
researcher.append(tech_cost_multiplier)
production_queue.remove(tech_cost_multiplier)
researcher[:] = sorted(researcher, key=lambda x: x.tag)
root.append(researcher)
entities = production_queue.find('Entities')
batch_time_modifier = production_queue.find('BatchTimeModifier')
if entities != None or batch_time_modifier != None:
trainer = ET.Element('Trainer')
if (entities != None):
trainer.append(entities)
production_queue.remove(entities)
if (batch_time_modifier != None):
trainer.append(batch_time_modifier)
production_queue.remove(batch_time_modifier)
trainer[:] = sorted(trainer, key=lambda x: x.tag)
root.append(trainer)
if production_queue.get('disable') != None:
for element in ["Researcher", "Trainer"]:
existing_element = root.find(element)
if existing_element is None:
functionality = ET.Element(element)
functionality.set('disable', "")
root.append(functionality)
else:
existing_element.set('disable', "")
else:
root.remove(production_queue)
root[:] = sorted(root, key=lambda x: x.tag)
ET.indent(tree)
tree.write(template_path, xml_declaration=True, encoding='utf-8')
return True
def fix_style(self, template_path):
self.changes = [
[' />', '/>'],
["version='1.0'", 'version="1.0"'],
["'utf-8'", '"utf-8"']
]
SedLike.sed(template_path, self.changes)
with open(template_path, 'a' , encoding='utf-8') as file:
file.write('\n')
def run(self):
for template in glob.iglob(self.template_folder + '/**/*.xml', recursive=True):
if self.fix_template(template):
print(template)
self.fix_style(template)
class ModifiersFixer:
def __init__(self, vfs_root):
self.data_folder = os.path.join(vfs_root, 'simulation', 'data')
self.changes = [
["ProductionQueue/Batch", "Trainer/Batch"],
["ProductionQueue/Ent", "Trainer/Ent"],
["ProductionQueue/Train", "Trainer/Train"],
["ProductionQueue/Tech", "Researcher/Tech"],
]
def run(self):
for modification in glob.iglob(self.data_folder + '/**/*.json', recursive=True):
SedLike.sed(modification, self.changes)
if __name__ == '__main__':
script_dir = os.path.dirname(os.path.realpath(__file__))
template_fixer = TemplateFixer(script_dir)
template_fixer.run()
modifier_fixer = ModifiersFixer(script_dir)
modifier_fixer.run()

52
A25_A26/P259.py Normal file
View File

@ -0,0 +1,52 @@
#!/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
from PIL import Image, ImageDraw
def convert_ao(path):
if 'ao' not in path.lower():
return
print(path)
image = Image.open(path)
image_out = Image.new('L', image.size)
for y in range(image.size[1]):
for x in range(image.size[0]):
if image.mode in ['1', 'L', 'P']:
ao = float(image.getpixel((x, y))) / 255.0
else:
ao = float(image.getpixel((x, y))[0]) / 255.0
ao = 0.3 + ao * 2.0 * 0.7 # Might be adjusted for a particular mod.
gray = int(ao * 255)
if gray < 0:
gray = 0
elif gray > 255:
gray = 255
image_out.putpixel((x, y), (gray))
image_out.save(path)
def convert_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() in ['.png']:
convert_ao(file_path)
elif os.path.isdir(file_path):
convert_recursively(file_path)
if __name__ == '__main__':
paths = sys.argv[1:]
for path in paths:
if os.path.isfile(path):
convert_ao(path)
elif os.path.isdir(path):
convert_recursively(path)

70
A25_A26/P261.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
import xml.etree.ElementTree as ET
class SedLike:
def sed(path, changes):
for line in fileinput.input(path, inplace=True, encoding='utf-8'):
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()
cmp_identity = root.find('Identity')
if cmp_identity == None:
return False
formations = cmp_identity.find('Formations')
if formations == None:
return False
cmp_unitai = root.find('UnitAI')
if cmp_unitai == None:
cmp_unitai = ET.Element('UnitAI')
root.append(cmp_unitai)
cmp_unitai.append(formations)
cmp_identity.remove(formations)
if cmp_identity.__len__() == 0:
root.remove(cmp_identity)
root[:] = sorted(root, key=lambda x: x.tag)
ET.indent(tree)
tree.write(template_path, xml_declaration=True, encoding='utf-8')
return True
def fix_style(self, template_path):
self.changes = [
[' />', '/>'],
["version='1.0'", 'version="1.0"'],
["'utf-8'", '"utf-8"']
]
SedLike.sed(template_path, self.changes)
with open(template_path, 'a', encoding='utf-8') as file:
file.write('\n')
def run(self):
for template in glob.iglob(self.template_folder + '/**/*.xml', recursive=True):
if self.fix_template(template):
print(template)
self.fix_style(template)
if __name__ == '__main__':
script_dir = os.path.dirname(os.path.realpath(__file__))
template_fixer = TemplateFixer(script_dir)
template_fixer.run()

91
A25_A26/PlayerXMLFixer.py Normal file
View File

@ -0,0 +1,91 @@
#!/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
from pathlib import Path
import os
import json
import xml.etree.ElementTree as ET
class PlayerXMLFixer(BaseFixer):
def __init__(self, vfs_root, verbose=False, name=__name__):
BaseFixer.__init__(self, vfs_root, verbose, name)
self.add_files(os.path.join('simulation', 'templates', 'special', 'player'), tuple(".xml"))
def list_civs(self):
civs = []
for root, _, files in os.walk(str(self.vfs_root)):
for name in files:
file_path = os.path.join(root, name)
if os.path.isfile(file_path) and os.path.join('simulation', 'data', 'civs') in file_path and '.json' in name:
civs.append({"Path" : file_path , "Code" : name.split('.')[0]})
return civs
def create_player_file(self):
root = ET.Element("Entity ")
root.set('parent', "special/player")
return root
def run(self):
for civ in self.list_civs():
tree = None
root = None
xml_file = None
for file in self.files:
if civ["Code"] in file:
xml_file = file
break
if xml_file is not None:
tree = ET.parse(xml_file)
root = tree.getroot()
root.set('parent', "special/player")
else:
root = self.create_player_file()
tree = ET.ElementTree(root)
data = None
with open(civ["Path"], 'r', encoding='utf-8') as f:
data = json.load(f)
cmp_identity = self.create_tag_if_not_exist(root, 'Identity')
if "History" in data.keys() and data["History"] is not None:
history_tag = self.create_tag_if_not_exist(cmp_identity, 'History')
history_tag.text = data["History"]
del data["History"]
if "Code" in data.keys():
civ_tag = self.create_tag_if_not_exist(cmp_identity, 'Civ')
civ_tag.text = data["Code"]
if "Name" in data.keys():
generic_name_tag = self.create_tag_if_not_exist(cmp_identity, 'GenericName')
generic_name_tag.text = data["Name"]
del data["Name"]
if "Emblem" in data.keys():
icon_tag = self.create_tag_if_not_exist(cmp_identity, 'Icon')
icon_tag.text = data["Emblem"]
del data["Emblem"]
outputFolder = Path(self.vfs_root) / 'simulation' / 'templates' / 'special' / 'players'
os.makedirs(outputFolder, exist_ok=True)
outputFile = outputFolder / (civ["Code"] + '.xml')
outputFile.touch()
self.save_xml_file(tree, root, outputFile)
with open(civ["Path"], 'w', encoding='utf-8') as outfile:
json.dump(data, outfile, indent='\t')
oldPlayerFolder = Path(self.vfs_root) / 'simulation' / 'templates' / 'special' / 'player'
if oldPlayerFolder.exists():
shutil.rmtree(oldPlayerFolder)

View File

@ -0,0 +1,60 @@
#!/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 .PlayerXMLFixer import PlayerXMLFixer
from pathlib import Path
import xml.etree.ElementTree as ET
import json
import os
class PlayerXMLFormationFixer(PlayerXMLFixer):
def __init__(self, vfs_root, verbose=False):
super(PlayerXMLFixer, self).__init__(vfs_root, verbose, __name__)
self.add_files(os.path.join('simulation', 'templates', 'special', 'players'), tuple(".xml"))
def run(self):
for civ in self.list_civs():
tree = None
root = None
xml_file = None
for file in self.files:
if civ["Code"] in file:
xml_file = file
break
if xml_file is not None:
tree = ET.parse(xml_file)
root = tree.getroot()
root.set('parent', "special/player")
else:
root = self.create_player_file()
tree = ET.ElementTree(root)
data = None
with open(civ["Path"], 'r', encoding='utf-8') as f:
data = json.load(f)
cmp_player = self.create_tag_if_not_exist(root, 'Player')
if "Formations" in data.keys() and data["Formations"] is not None:
formations_tag = self.create_tag_if_not_exist(cmp_player, 'Formations')
formations_tag.text = " ".join(data["Formations"])
del data["Formations"]
outputFolder = Path(self.vfs_root) / 'simulation' / 'templates' / 'special' / 'players'
os.makedirs(outputFolder, exist_ok=True)
outputFile = outputFolder / (civ["Code"] + '.xml')
outputFile.touch()
self.save_xml_file(tree, root, outputFile)
with open(civ["Path"], 'w', encoding='utf-8') as outfile:
json.dump(data, outfile, indent='\t', ensure_ascii=False)

View File

@ -0,0 +1,35 @@
#!/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 SessionIconPathFixer(BaseFixer):
def __init__(self, vfs_root, verbose=False):
BaseFixer.__init__(self, vfs_root, verbose, __name__)
self.add_files(os.path.join('simulation', 'templates', 'special', 'players'), tuple(".xml"))
def run(self):
for file in self.files:
tree = ET.parse(file)
root = tree.getroot()
cmp_identity = root.find('Identity')
if cmp_identity is None:
continue
icon_tag = cmp_identity.find('Icon')
if icon_tag is None:
continue
icon_tag.text = str(icon_tag.text).replace("session/portraits/", "")
self.save_xml_file(tree, root, file)
self.fix_style(file)

95
utils/PMPMap.py Normal file
View File

@ -0,0 +1,95 @@
#!/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 io import BufferedReader, BufferedWriter
class PmpHeader():
def __init__(self, stream : BufferedReader):
self.magic = int.from_bytes(stream.read(4), byteorder='little')
self.version = int.from_bytes(stream.read(4), byteorder='little')
self.data_size = int.from_bytes(stream.read(4), byteorder='little')
self.map_size = int.from_bytes(stream.read(4), byteorder='little')
def write_to_stream(self, stream : BufferedWriter):
stream.write(self.magic.to_bytes(4, 'little'))
stream.write(self.version.to_bytes(4, 'little'))
stream.write(self.data_size.to_bytes(4, 'little'))
stream.write(self.map_size.to_bytes(4, 'little'))
class PmpHeightMap(list):
def __init__(self, stream : BufferedReader, width : int, height : int):
self.capacity = (width * 16 + 1) * (height * 16 + 1)
for _ in range (0, self.capacity):
self.append(int.from_bytes(stream.read(2), byteorder='little'))
def write_to_stream(self, stream : BufferedWriter):
for height_data in self:
stream.write(height_data.to_bytes(2, byteorder='little'))
class PmpTextures(list):
def __init__(self, stream : BufferedReader):
self.capacity = int.from_bytes(stream.read(4), byteorder='little')
for _ in range (0, self.capacity):
length = int.from_bytes(stream.read(4), byteorder='little')
self.append(stream.read(length).decode())
def write_to_stream(self, stream : BufferedWriter):
stream.write((len(self)).to_bytes(4, byteorder='little'))
for texture in self:
stream.write(len(texture).to_bytes(4, byteorder='little'))
stream.write(texture.encode())
class PmpTiles(list):
def __init__(self, stream : BufferedReader, capacity : int):
for _ in range(0, capacity):
self.append(PmpTile(stream))
def write_to_stream(self, stream : BufferedWriter):
for tile in self:
tile.write_to_stream(stream)
class PmpTile():
def __init__(self, stream : BufferedReader):
self.texture1 = int.from_bytes(stream.read(2), byteorder='little')
self.texture2 = int.from_bytes(stream.read(2), byteorder='little')
self.priority = int.from_bytes(stream.read(4), byteorder='little')
def write_to_stream(self, stream : BufferedWriter):
stream.write(self.texture1.to_bytes(2, byteorder='little'))
stream.write(self.texture2.to_bytes(2, byteorder='little'))
stream.write(self.priority.to_bytes(4, byteorder='little'))
class PmpPatch():
def __init__(self, stream : BufferedReader):
self.TILE_SIZE = 16 * 16
self.tiles = PmpTiles(stream, self.TILE_SIZE)
def write_to_stream(self, stream : BufferedWriter):
self.tiles.write_to_stream(stream)
class PmpPatches(list):
def __init__(self, stream, width, height):
self.capacity = width * height
for _ in range (0, self.capacity):
self.append(PmpPatch(stream))
def write_to_stream(self, stream : BufferedWriter):
for patch in self:
patch.write_to_stream(stream)
class PmpMap():
def __init__(self, stream : BufferedReader):
self.header = PmpHeader(stream)
self.heightMap = PmpHeightMap(stream, self.header.map_size, self.header.map_size)
self.textures = PmpTextures(stream)
self.patches = PmpPatches(stream, self.header.map_size, self.header.map_size)
def write_to_stream(self, stream : BufferedWriter):
self.header.write_to_stream(stream)
self.heightMap.write_to_stream(stream)
self.textures.write_to_stream(stream)
self.patches.write_to_stream(stream)

97
utils/fixers/BaseFixer.py Normal file
View File

@ -0,0 +1,97 @@
#!/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 ..logger_utils.InterceptableLogger import InterceptableLogger
from pathlib import Path
import fileinput
import os
class BaseFixer():
def __init__(self, vfs_root, verbose=False, name=__name__):
self.vfs_root = Path(vfs_root)
self.verbose = verbose
self.files = []
self.logger = InterceptableLogger(name)
def fix_style(self, xml_path):
changes = [
[' />', '/>'],
["version='1.0'", 'version="1.0"'],
["'utf-8'", '"utf-8"']
]
for line in fileinput.input(xml_path, inplace=True):
for change in changes:
line = line.replace(change[0], change[1])
print(line, end="")
with open(xml_path, 'a', encoding='utf-8') as file:
file.write('\n')
def indent(self, elem, level=0, more_sibs=False):
i = "\n"
if level:
i += (level-1) * ' '
num_kids = len(elem)
if num_kids:
if not elem.text or not elem.text.strip():
elem.text = i + " "
if level:
elem.text += ' '
count = 0
for kid in elem:
self.indent(kid, level+1, count < num_kids - 1)
count += 1
if not elem.tail or not elem.tail.strip():
elem.tail = i
if more_sibs:
elem.tail += ' '
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
if more_sibs:
elem.tail += ' '
def sort(self, root):
# sort the first layer
root[:] = sorted(root, key=lambda child: (child.tag,child.get('name')))
# sort the second layer
for c in root:
c[:] = sorted(c, key=lambda child: (child.tag,child.get('name')))
for cp in c:
cp[:] = sorted(cp, key=lambda child: (child.tag,child.get('name')))
for scp in cp:
scp[:] = sorted(scp, key=lambda child: (child.tag,child.get('name')))
def save_xml_file(self, tree, root, xml_file, sort=True):
if sort:
self.sort(root)
self.indent(root)
tree.write(xml_file, xml_declaration=True, encoding='utf-8')
self.fix_style(xml_file)
def create_tag_if_not_exist(self, parent, name):
tag = parent.find(name)
if tag is None:
tag = ET.SubElement(parent, name)
return tag
def add_files(self, path, extensions : tuple[str]):
self.files = []
if os.path.isfile(str(self.vfs_root)):
self.files.append(self.vfs_root)
elif os.path.isdir(str(self.vfs_root)):
for root, _, files in os.walk(str(self.vfs_root)):
for name in files:
file_path = os.path.join(root, name)
if os.path.isfile(file_path) and path in file_path and name.endswith(extensions):
self.files.append(file_path)
if self.verbose:
if len(self.files) > 0:
self.logger.info(f"Found {len(self.files)} file(s).")
else:
self.logger.info(f"No files were found.")

View File

@ -0,0 +1,35 @@
#!/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 .SingleLevelFilter import SingleLevelFilter
from logging import getLogger, StreamHandler, INFO, WARNING, Formatter
from sys import stdout, stderr
class InterceptableLogger():
def __init__(self, logger_name : str):
self.logger = getLogger(logger_name)
self.logger.setLevel(INFO)
ch = StreamHandler(stdout)
ch.setLevel(INFO)
ch.setFormatter(Formatter('%(levelname)s - %(message)s'))
f1 = SingleLevelFilter(INFO, False)
ch.addFilter(f1)
self.logger.addHandler(ch)
errorch = StreamHandler(stderr)
errorch.setLevel(WARNING)
errorch.setFormatter(Formatter('%(levelname)s - %(message)s'))
self.logger.addHandler(errorch)
def info(self, message):
self.logger.info(message)
def warn(self, message):
self.logger.warn(message)
def error(self, message):
self.logger.error(message)

View File

@ -0,0 +1,19 @@
#!/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 logging import Filter
class SingleLevelFilter(Filter):
def __init__(self, passlevel, reject):
self.passlevel = passlevel
self.reject = reject
def filter(self, record):
if self.reject:
return (record.levelno != self.passlevel)
else:
return (record.levelno == self.passlevel)