diff --git a/src/app/gameMan.py b/src/app/gameMan.py index f6f5ac61d..439ea1ade 100644 --- a/src/app/gameMan.py +++ b/src/app/gameMan.py @@ -666,7 +666,7 @@ def export( # Count the files. export_screen.set_length( 'RES', - sum(1 for file in res_system.walk_folder_repeat()), + sum(1 for _ in res_system.walk_folder_repeat()), ) else: export_screen.skip_stage('RES') @@ -681,6 +681,7 @@ def export( all_items = style.items.copy() renderables = style.renderables.copy() + resources: dict[str, bytes] = {} export_screen.step('EXP') @@ -701,6 +702,7 @@ def export( renderables=renderables, vbsp_conf=vbsp_config, selected_style=style, + resources=resources, )) except packages.NoVPKExport: # Raised by StyleVPK to indicate it failed to copy. @@ -880,6 +882,14 @@ def export( resource_gen.make_cube_colourizer_legend(Path(self.abs_path('bee2'))) export_screen.step('EXP') + # Write generated resources, after the regular ones have been copied. + for filename, data in resources.items(): + LOGGER.info('Writing {}...') + loc = Path(self.abs_path(filename)) + loc.parent.mkdir(parents=True, exist_ok=True) + with loc.open('wb') as f: + f.write(data) + if self.steamID == utils.STEAM_IDS['APERTURE TAG']: os.makedirs(self.abs_path('sdk_content/maps/instances/bee2/'), exist_ok=True) with open(self.abs_path('sdk_content/maps/instances/bee2/tag_coop_gun.vmf'), 'w') as f: diff --git a/src/packages/__init__.py b/src/packages/__init__.py index e32041cc9..e9ce14532 100644 --- a/src/packages/__init__.py +++ b/src/packages/__init__.py @@ -144,6 +144,10 @@ class ExportData: renderables: dict[RenderableType, Renderable] # The error/connection icons vbsp_conf: Property game: Game + # As objects export, they may fill this to include additional resources + # to be written to the game folder. This way it can be deferred until + # after regular resources are copied. + resources: dict[str, bytes] @attr.define diff --git a/src/packages/signage.py b/src/packages/signage.py index 447e647d9..5602c6862 100644 --- a/src/packages/signage.py +++ b/src/packages/signage.py @@ -1,5 +1,7 @@ """Implements a dynamic item allowing placing the various test chamber signages.""" from __future__ import annotations + +from io import BytesIO from pathlib import Path from typing import NamedTuple, Optional, TYPE_CHECKING @@ -19,6 +21,7 @@ LOGGER = srctools.logger.get_logger(__name__) LEGEND_SIZE = (512, 1024) CELL_SIZE = 102 +SIGN_LOC = 'bee2/materials/BEE2/models/props_map_editor/signage/signage.vtf' class SignStyle(NamedTuple): @@ -215,7 +218,10 @@ def export(exp_data: ExportData) -> None: sel_icons[int(tim_id)] = sty_sign.icon exp_data.vbsp_conf.append(conf) - build_texture(exp_data.game, exp_data.selected_style, sel_icons) + exp_data.resources[SIGN_LOC] = build_texture( + exp_data.game, exp_data.selected_style, + sel_icons, + ) def _serialise(self, parent: Property, style: Style) -> Optional[SignStyle]: """Write this sign's data for the style to the provided property.""" @@ -245,7 +251,7 @@ def build_texture( game: gameMan.Game, sel_style: Style, icons: dict[int, ImgHandle], -) -> None: +) -> bytes: """Construct the legend texture for the signage.""" legend = Image.new('RGBA', LEGEND_SIZE, (0, 0, 0, 0)) @@ -285,11 +291,7 @@ def build_texture( vtf.get().copy_from(legend.tobytes(), ImageFormats.RGBA8888) vtf.clear_mipmaps() vtf.flags |= vtf.flags.ANISOTROPIC - vtf_loc = game.abs_path( - 'bee2/materials/BEE2/models/' - 'props_map_editor/signage/signage.vtf' - ) - Path(vtf_loc).parent.mkdir(parents=True, exist_ok=True) - with open(vtf_loc, 'wb') as f: - LOGGER.info('Exporting "{}"...', vtf_loc) - vtf.save(f) + with BytesIO() as buf: + vtf.save(buf) + return buf.getvalue() +