HOI4_randomchanges/dodo.py

285 lines
9.0 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
from pathlib import Path
from doit import get_var
DOIT_CONFIG = {
2024-09-27 22:51:44 +00:00
'default_tasks': ['build'],
'action_string_formatting': 'both'
}
2024-09-27 22:51:44 +00:00
def files(p):
for i in p:
if i.is_file():
yield i
2024-09-27 22:51:44 +00:00
class RawFiles(list):
def __init__(self, *args, **kwargs):
self.src_path = Path(kwargs.pop('src_path'))
self.image_path = Path(kwargs.pop('image_path'))
super().__init__(*args, **kwargs)
def sources(self):
for i in self:
yield self.src_path/i
2024-09-27 22:51:44 +00:00
def targets(self):
for i in self:
yield self.image_path/i
2024-09-27 22:51:44 +00:00
class BasePatchwork(dict):
patches_path = None
patchwork_path = None
vanilla_path = None
2024-09-27 22:51:44 +00:00
def __init__(self, *arkg, **kwargs):
self.patches_path = Path(kwargs.pop('patches_path'))
self.patchwork_path = Path(kwargs.pop('patchwork_path'))
self.vanilla_path = Path(kwargs.pop('vanilla_path'))
super().__init__(*arkg, **kwargs)
2024-09-27 22:51:44 +00:00
def targets(self):
for i in self.keys():
yield self.patchwork_path/i
2024-09-27 22:51:44 +00:00
def key_vanilla(self, orig_key):
return self.vanilla_path/orig_key
2024-09-27 22:51:44 +00:00
def key_target(self, orig_key):
return self.patchwork_path/orig_key
2024-09-27 22:51:44 +00:00
def tasks(self, actions):
def repack_action(action, **kwargs):
if callable(action):
return (action, [], kwargs)
elif isinstance(action, str):
return action.format(**kwargs)
else:
return action
for k, v in self.items():
original = self.vanilla_path/k
patch = self.patches_path/v
target = self.patchwork_path/k
yield {
2024-09-27 22:51:44 +00:00
'name': k,
'file_dep': [original, patch],
'targets': [target],
'actions': [repack_action(action, original=original, patch=patch, target=target)
for action in actions],
'clean': True
}
class Patchwork(BasePatchwork):
def tasks_(self, actions):
for k, v in self.items():
yield {
2024-09-27 22:51:44 +00:00
'name': k,
'file_dep': [self.patches_path/v, self.vanilla_path/k],
'targets': [self.patchwork_path/k],
'actions': [
f"mkdir -p {self.patchwork_path}/{Path(k).parent}",
2024-09-27 22:51:44 +00:00
f"patch -u --binary -N -o '{self.patchwork_path}/{k}' "
f"'{self.vanilla_path}/{k}' '{self.patches_path}/{v}'"
2024-09-27 22:51:44 +00:00
], # action ( mkdir, patch )
'clean': True
}
class ScriptedPatchwork(BasePatchwork):
def __init__(self, *args, **kwargs):
from yaml import safe_load
file = kwargs.pop('source_file')
super().__init__(*args, **kwargs)
with open(file) as s:
for key, data in safe_load(s).items():
2024-09-27 22:51:44 +00:00
try:
discovered_source = next(
(Path(self.vanilla_path)/'history'/'states').glob(f"{key}*"))
relative_path = discovered_source.relative_to(
self.vanilla_path)
self[relative_path] = data
except StopIteration:
print(f"{key}: not found")
class RasterPatchwork(BasePatchwork):
@staticmethod
def patch_action(target, original, patch):
from PIL import Image
with Image.open(original) as base, Image.open(patch) as patch:
def mask():
for bg, p in zip(base.tobytes(), patch.tobytes()):
if p == 35:
yield bg
else:
yield p
output = Image.frombytes(base.mode, base.size, bytes(mask()))
output.putpalette(base.palette)
2023-11-30 21:06:10 +00:00
target.parent.mkdir(exist_ok=True, parents=True)
output.save(target)
output.close()
class Patchset(list):
def __init__(self, *args, **kwargs):
self.modimage_path = Path(kwargs.pop('modimage_path'))
super().__init__(*args, **kwargs)
def image_files(self):
for patches in self:
if isinstance(patches, BasePatchwork):
for target in patches:
yield self.modimage_path/target
elif isinstance(patches, RawFiles):
for target in patches:
yield self.modimage_path/target.relative_to(patches.src_path)
def image_sources(self):
for patches in self:
if isinstance(patches, BasePatchwork):
for target in patches:
yield patches.patchwork_path/target
elif isinstance(patches, RawFiles):
for target in patches:
yield target
mod_name = 'randchgs'
2024-09-27 22:51:44 +00:00
mod_install_path = get_var(
'descriptor_mod_path', 'C:/Users/User/Documents/Paradox Interactive/Hearts of Iron IV/mod/randchgs')
dir_vanilla = Path(get_var('vanilla_path', './vanilla/current'))
if not dir_vanilla.is_dir():
raise EnvironmentError(dir_vanilla)
dir_image = Path('build/image')
dir_modimage = dir_image/mod_name
dir_src_raw = Path('src/raw')
dir_src_raw_files = RawFiles(
files(Path(dir_src_raw).rglob("*")),
2024-09-27 22:51:44 +00:00
src_path=dir_src_raw,
image_path=dir_image
)
dir_patches_path = Path('src/patches')
dir_patchwork_path = Path('build/patched')
patchwork = Patchwork(
{
'history/countries/ISR - Israel.txt': 'history_countries_ISR.patch',
'history/states/454-Israel.txt': 'history_states_454.patch',
2024-09-27 22:51:44 +00:00
},
patches_path=dir_patches_path,
patchwork_path=dir_patchwork_path,
vanilla_path=dir_vanilla
)
patchwork_scripted = ScriptedPatchwork(
2024-09-27 22:51:44 +00:00
source_file=dir_patches_path/'extrapoint.yaml',
patches_path='.',
patchwork_path=dir_patchwork_path,
vanilla_path=dir_vanilla
)
patchwork_raster = RasterPatchwork(
{
2024-09-27 22:51:44 +00:00
'map/terrain.bmp': 'terrain.bmp',
},
2024-09-27 22:51:44 +00:00
patches_path=dir_patches_path,
patchwork_path=dir_patchwork_path,
vanilla_path=dir_vanilla
)
patch_set = Patchset(
(
dir_src_raw_files,
patchwork,
patchwork_scripted,
patchwork_raster,
),
2024-09-27 22:51:44 +00:00
modimage_path=dir_modimage
)
2024-09-27 22:51:44 +00:00
def task_patch_history_states_scripted():
def history_state_patch(task, provinces):
source = Path(next(iter(task.file_dep)))
target = Path(task.targets[0])
target.parent.mkdir(parents=True, exist_ok=True)
seen_history = False
wrote_history = False
with source.open(newline='') as infile, target.open('w') as outfile:
for line_in in infile:
outfile.write(line_in)
2024-09-27 22:51:44 +00:00
seen_history = seen_history or ('history' in line_in)
if not wrote_history and seen_history and '{' in line_in:
for province_id, province_info in provinces.items():
outfile.write(
2024-09-27 22:51:44 +00:00
"\t\tvictory_points = {\r\n"
f"\t\t\t{province_id} {
province_info['points']}\r\n"
"\t\t}\r\n"
)
wrote_history = True
2024-09-27 22:51:44 +00:00
for state_src, state_data in patchwork_scripted.items():
yield {
2024-09-27 22:51:44 +00:00
'name': state_src,
'file_dep': [patchwork_scripted.key_vanilla(state_src)],
'targets': [patchwork_scripted.key_target(state_src)],
'actions': [(history_state_patch, [], {'provinces': state_data})],
'clean': True
}
2024-09-27 22:51:44 +00:00
def task_patch_raster():
yield from patchwork_raster.tasks([patchwork_raster.patch_action])
2024-09-27 22:51:44 +00:00
def task_patch():
def mkdir(target, original, patch):
Path(target).parent.mkdir(parents=True, exist_ok=True)
yield from patchwork.tasks([
2024-09-27 22:51:44 +00:00
mkdir,
"patch -u --binary -N -o '{target}' '{original}' '{patch}'"
])
def task_image():
return {
2024-09-27 22:51:44 +00:00
'file_dep': list(patch_set.image_sources()),
'targets': list(patch_set.image_files()),
'actions': [f"mkdir -p {dir_modimage}",
f"rsync -rv {dir_src_raw}/ {patchwork.patchwork_path}/"
f" {dir_modimage}/"],
'clean': True
}
2024-09-27 22:51:44 +00:00
def task_image_mod():
def prepare_modname_mod(task):
source = Path(next(iter(task.file_dep)))
target = Path(task.targets[0])
target.parent.mkdir(parents=True, exist_ok=True)
with source.open() as s:
with target.open('w') as o:
o.write(s.read())
o.write(f"\npath=\"{mod_install_path}\"\n")
return {
2024-09-27 22:51:44 +00:00
'file_dep': ['src/raw/descriptor.mod'],
'targets': [f"{dir_image}/{mod_name}.mod"],
'actions': [prepare_modname_mod],
'clean': True
}
2024-09-27 22:51:44 +00:00
def task_build():
return {
2024-09-27 22:51:44 +00:00
'file_dep': [f"{dir_image}/{mod_name}.mod"] + list(patch_set.image_files()),
'targets': [f"{dir_image}/{mod_name}.zip"],
'actions': ["rm -f %(targets)s",
f"cd {dir_image} && zip -r {mod_name}.zip {mod_name}",
f"cd {dir_image} && zip -r {mod_name}.zip {mod_name}.mod"],
'clean': ['rm -rf build']
}