From de5dfd2421c58e6e3acda1c64c838e86591db32c Mon Sep 17 00:00:00 2001 From: Jennifer Taylor Date: Fri, 16 Apr 2021 21:08:41 +0000 Subject: [PATCH] Hook up TXP2 container to renderer, provide a "list" option to list out possible rendering paths in a container. --- bemani/format/afp/render.py | 35 +++++++++++------ bemani/utils/afputils.py | 75 +++++++++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/bemani/format/afp/render.py b/bemani/format/afp/render.py index 0c61606..fc7dce2 100644 --- a/bemani/format/afp/render.py +++ b/bemani/format/afp/render.py @@ -75,6 +75,17 @@ class AFPRenderer(VerboseOutput): raise Exception(f'{path} not found in registered SWFs!') + def list_paths(self, verbose: bool = False) -> List[str]: + paths: List[str] = [] + + for name, swf in self.swfs.items(): + paths.append(swf.exported_name) + + for export_tag in swf.exported_tags: + paths.append(f"{swf.exported_name}.{export_tag}") + + return paths + def __place(self, tag: Tag, parent_sprite: Optional[int], prefix: str = "") -> List[Clip]: if isinstance(tag, AP2ShapeTag): self.vprint(f"{prefix} Loading {tag.reference} into shape slot {tag.id}") @@ -185,7 +196,8 @@ class AFPRenderer(VerboseOutput): return img if params.flags & 0x4 or params.flags & 0x8: - raise Exception("Don't support shape blend or uv coordinate color yet!") + # TODO: Need to support blending and UV coordinate colors here. + print("WARNING: Unhandled shape blend or UV coordinate color!") texture = None if params.flags & 0x2: @@ -194,17 +206,18 @@ class AFPRenderer(VerboseOutput): raise Exception(f"Cannot find texture reference {params.region}!") texture = self.textures[params.region] - # Now, render out the texture. - cutin = Point(offset.x, offset.y) - cutoff = Point.identity() - if cutin.x < 0: - cutoff.x = -cutin.x - cutin.x = 0 - if cutin.y < 0: - cutoff.y = -cutin.y - cutin.y = 0 + if texture is not None: + # Now, render out the texture. + cutin = Point(offset.x, offset.y) + cutoff = Point.identity() + if cutin.x < 0: + cutoff.x = -cutin.x + cutin.x = 0 + if cutin.y < 0: + cutoff.y = -cutin.y + cutin.y = 0 - img.alpha_composite(texture, cutin.as_tuple(), cutoff.as_tuple()) + img.alpha_composite(texture, cutin.as_tuple(), cutoff.as_tuple()) return img def __render(self, swf: SWF, export_tag: Optional[str]) -> Tuple[int, List[Any]]: diff --git a/bemani/utils/afputils.py b/bemani/utils/afputils.py index 891ded5..6f50ff4 100644 --- a/bemani/utils/afputils.py +++ b/bemani/utils/afputils.py @@ -168,6 +168,21 @@ def main() -> int: help="Display verbuse debugging output", ) + list_parser = subparsers.add_parser('list', help='List out the possible paths to render from a series of SWFs') + list_parser.add_argument( + "container", + metavar="CONTAINER", + type=str, + nargs='+', + help="A container file to use for loading SWF data. Can be either a TXP2 or IFS container.", + ) + list_parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="Display verbuse debugging output", + ) + args = parser.parse_args() if args.action == "extract": @@ -429,7 +444,7 @@ def main() -> int: print(geo, file=sys.stderr) print(json.dumps(geo.as_dict(), sort_keys=True, indent=4)) - if args.action == "render": + if args.action in ["render", "list"]: # This is a complicated one, as we need to be able to specify multiple # directories of files as well as support IFS files and TXP2 files. renderer = AFPRenderer() @@ -446,8 +461,54 @@ def main() -> int: pass if afpfile is not None: - # TODO: Load from afp container - pass + if args.verbose: + print(f"Loading files out of TXP2 container {container}...", file=sys.stderr) + + # First, load GE2D structures into the renderer. + for i, name in enumerate(afpfile.shapemap.entries): + shape = afpfile.shapes[i] + renderer.add_shape(name, shape) + + if args.verbose: + print(f"Added {name} to SWF shape library.", file=sys.stderr) + + # Now, split and load textures into the renderer. + sheets: Dict[str, Any] = {} + + for i, name in enumerate(afpfile.regionmap.entries): + if i < 0 or i >= len(afpfile.texture_to_region): + raise Exception(f"Out of bounds region {i}") + region = afpfile.texture_to_region[i] + texturename = afpfile.texturemap.entries[region.textureno] + + if texturename not in sheets: + for tex in afpfile.textures: + if tex.name == texturename: + sheets[texturename] = tex + break + else: + raise Exception("Could not find texture {texturename} to split!") + + if sheets[texturename].img: + sprite = sheets[texturename].img.crop( + (region.left // 2, region.top // 2, region.right // 2, region.bottom // 2), + ) + renderer.add_texture(name, sprite) + + if args.verbose: + print(f"Added {name} to SWF texture library.", file=sys.stderr) + else: + print(f"Cannot load {name} from {texturename} because it is not a supported format!") + + # Finally, load the SWF data itself into the renderer. + for i, name in enumerate(afpfile.swfmap.entries): + swf = afpfile.swfdata[i] + renderer.add_swf(name, swf) + + if args.verbose: + print(f"Added {name} to SWF library.", file=sys.stderr) + + continue ifsfile = None try: @@ -456,6 +517,8 @@ def main() -> int: pass if ifsfile is not None: + if args.verbose: + print(f"Loading files out of IFS container {container}...", file=sys.stderr) for fname in ifsfile.filenames: if fname.startswith(f"geo{os.sep}"): # Trim off directory. @@ -492,12 +555,18 @@ def main() -> int: if args.verbose: print(f"Added {afpname} to SWF library.", file=sys.stderr) + continue + if args.action == "render": duration, images = renderer.render_path(args.path, verbose=args.verbose) if len(images) == 0: raise Exception("Did not render any frames!") images[0].save(args.output, save_all=True, append_images=images[1:], loop=0, duration=duration) print(f"Wrote animation to {args.output}") + elif args.action == "list": + paths = renderer.list_paths(verbose=args.verbose) + for path in paths: + print(path) return 0