1
0
mirror of synced 2024-12-18 09:15:54 +01:00
bemaniutils/bemani/format/afp/README.md

211 lines
14 KiB
Markdown
Raw Normal View History

2021-08-11 19:56:23 +02:00
# AFP Animation Format
As far back as I can see, Konami has used an animation format that I refer to as
AFP in virtually all of their games. Basically, if it runs on a PC, it probably
has a variant of AFP running on it. This also goes for several of their commercial
PC titles, some console titles and mobile titles as well. The name "AFP" comes
from the library that implements most of the format but I have no idea what this
actually stands for. In very old PC-based arcade games such as The\*BishiBashi the
library code makes references to Flash. If it encounters a plain SWF file it returns
an error stating that old data is not supported and encouraging the artist to
convert the data to the "new" format. Many other similarities between SWF and AFP
are present including a legacy bytecode table that is identical to Flash, a tag ID
to string function that matches Flash exactly up to the point where Konami started
adding their own, and the fact that the core engine works identically down to the
affine transformations, additive and multiplicative colors, tag system, blending
modes, masking and a whole host of other things.
At some point, most of the old SWF code was removed from the project. A modern
AFP library is almost unrecognizeable from a classic one, save for some basic
code that parses the still-supported tags. The legacy SWF bytecode parser has
been removed completely, support for recognizing vanilla SWF tags has been removed,
and support for bytecode that is interactive in nature has been removed leaving
only the shell of a bytecode engine that can update properties for effects such
as applying masks, setting objects visible or invisible and rewinding/advancing
individual sprites on the main canvas. While Konami appears to have put a decent
effort into making all of their format changes purely additive, if one was to
try to render an animation from an older game inside a newer game's engine it
would not be a surprise if the resulting graphics were subtly wrong.
The AFP file format itself is not an all-encompassing container. It superficially
resembles a SWF file but has an entirely different header and different ways of
encoding the tags and optional features of the format. Curiously, one cannot decode
a chunk of AFP data without the corresponding BSI data which is simply a list of
locations to byteswap as well as the length to byteswap. This appears to be a
rudimentary way of hiding plain strings in the AFP data itself so that they can't
be found in a hex editor. The byteswap instructions are trivial as well as reversible.
Along with AFP data, one would need shape structures as well as textures. These are
not found inside the AFP data but are instead inside a parent container. For older
games this is a TXP2 file which contains special sections for texture sheets,
individual textures, shapes, fonts, AFP data and BSI data. For newer games this is
a standard Konami IFS file where the AFP data is found in the `afp/` folder, the
BSI data is found in the `afp/bsi/` folder, the shapes are found in the `geo/`
folder and the textures are found in the `tex/` folder. Aside from container format
differences the underlying files are identically parsed and have equivalent
meanings regardless of where one unpacks them from.
The basic concept of how an animation is stored and represented is fairly simple.
Each animation has a specified number of frames and there is a lookup table in
the AFP header specifying which tags should be acted upon for each frame. To play
an animation, one must act upon each tag for a frame and then display or save the
resulting image. Tags have actions such as defining a shape or sprite for later
use, requests to place a previously defined shape or sprite on the canvas,
requests to update the properties of a previously placed object on the canvas,
requests to delete a previously placed object from the canvas and arbitrary
bytecode to execute as part of the frame's processing. Placed objects on the
canvas each have an associated additive and multiplicative color to apply to them
before blending, a blending mode such as normal, additive or subtractive, an
affine or perspective transform matrix used to determine how to position the
object and finally either a bounding box for a solid color rectangle, a reference
to a texture or a list of child objects to treat as a single sprite. Objects are
placed onto their own depth planes and rendered from lowest plane to highest plane
one by one until the final frame has been constructed.
# Tags
Since tags are the heart of the format (as they are in SWF files), I go into a bit
more detail about the crucial ones here. In the older versions of the format
vanilla SWF tags could also be used but they are documented elsewhere online and
in practice have never been seen inside an AFP animation.
## AP2 Shape Tag
The shape tag defines a shape (either a reference to a texture quad or a solid
color rectangle) and assigns it an internal ID for later use. Both texture quads
and solid rectangles are represented by the same shape structure which contains
the shape's bounding rectangle, UV vertex points and draw parameters (such as whether
this shape is a rectangle or a quad in the first place). If the shape is a texture
quad the structure will also include a string reference to the name of the texture
itself. The UV coordinates and bounding rectangle are always identical to the size
of the referenced texture in the case of texture quad shapes and appear to be in
the format simply to pass forward to a graphics card. Acting upon a shape tag means
adding the shape to an internal library of objects that can be placed on the canvas.
## AP2 Image Tag
This appears to be functionally identical to a shape, save for missing all of the
graphics card coordinates. Instead of pointing at a shape structure, image tags point
directly at a texture itself. Acting upon an image tag means adding the image to an
internal library of objects that can be placed on the canvas in a similar manner to
a shape.
## AP2 Sprite Tag
The sprite tag defines a sprite, which in SWF and AFP nomenclature just means an
embedded animation complete with its own list of frames and tags. In reality, the root
animation itself can be seen as a sprite tag, and in fact the format allows importing
another animation as a sprite to be placed on the canvas just like an image or shape.
Sprites are handy because they allow an animator to define things like characters with
their own shapes, transform and move the objects that make up those characters, and then
later place the character on the canvas just like a shape or image. As you might expect,
since a sprite is just a set of tags to act upon each frame, sprites can include their
own child sprites indefinitely. Acting upon a sprite tag means adding the sprite to
an internal library of objects that can be placed on the canvas.
## AP2 Place Object Tag
The place object tag is the meat of the format. Place object tags specify a placed
object ID to refer later to the placed object as well as a depth to place the object
on. They refer to sprites, images and shapes by their registered ID. They can come in
one of two flavors: create or update. In create mode, a new object is placed onto the
canvas at the specified depth. That object is looked up in the internal library of
objects that was previously added to in a shape, image or sprite tag. In update mode,
a previously placed object is looked up by object ID and depth and its properties are
updated to match the update tag's specifications. In both cases, the place object tag
can include a blend mode, an affine or projection transform, an additive and
multiplicative color and a few other properties. The blend mode and colors specify to
the renderer how to combine the pixels of the placed object with other objects that are
placed on the canvas. The transform specifies how the object is positioned, rotated and
stretched. Using a series of updates, an animator could rotate an object, move its
position on the canvas, change its transparency to fade it in or out and basically any
other operation.
Acting upon a place object tag means placing a new object on the canvas to be
rendered this frame or updating an existing object with new properties which will be
rendered this frame. Objects that are already on the canvas and not updated by a
place object tag will keep their properties and be rendered identically on subsequent
frames. The only exception to this is placed sprites, which automatically advance to
the next frame of their own embedded animation in order to render correctly.
## AP2 Remove Object Tag
The remove object tag looks up an object previously placed by a place object tag and
removes it from the canvas. It can either remove an object by object ID and depth or
it can remove the last placed object on a particular depth. Acting upon a remove object
tag means removing an existing object from the canvas so that it is not rendered on
this frame or subsequent frames.
## AP2 Do Action Tag
The do action tag executes AFP bytecode in order to perform some action. This can mean
requesting a mask to be applied to a placed object, requesting a placed sprite be
advanced or rewound to a specific frame, requesting a placed object be made invisible
or a host of other useful effects. AFP animations use this to provide looping effects
on sprites since the format does not natively support loops. They also use this to
apply masks to placed objects as the format does not natively support defining a mask.
Originally do action tags could also provide interactivity such as in The\*BishiBashi
where the levels themselves were implemented entirely in AFP files and played using
bytecode to read inputs, check game scores and the like. Modern games have stripped
this functionality out and only use it to provide a few useful hooks such as the above
documented effects and things such as triggering sounds to play or informing other
systems what frame of the animation is being rendered.
Acting upon a do action tag means using a bytecode interpreter to execute the bytecode
and apply the desired property changes to the currently placed objects on the canvas.
Note also that the place object tag supports bytecode triggers including an "on load"
trigger that requires bytecode to be executed when the object is placed. So, bytecode
can be found in two different locations but is executed identically either way.
## AP2 Place Camera Tag
The place camera tag registers a camera by specifying where in 3D the camera is and
the focal length from the camera to the render plane. The camera always looks straight
down at the canvas and the focal length in practice alwys matches the Z offset of the
camera. This is only used in conjunction with objects that have a perspective transform
to correctly render the object based on its depth in the Z plane. Acting upon a place
camera tag means storing where the camera is for future projection perspective renders
when the engine goes to display all of the placed objects for a frame.
# Quirks
No format that is this complex will be without its quirks. At some point, Konami
hacked in 3D perspective support to the format. This is extremely rudimentary
and only allows for a camera looking straight down at the canvas and for 2D
quads to be transformed using a 3D perspective transform. Its my best guess that
artists were tired of having to render out individual frames and specify them
in order to achieve correct 3D rotation of arbitrary objects so this system was
put in place. There is no culling, no normals, no depth buffer, FOV, nothing. It
exists simply as a way to specify what depth to project a 2D quad that has been
rotated in 3D space. This is used in newer games mostly for perspective-correct
rotation of objects such as records.
Now, unlike the 2D portion which was based on Flash and has had some 15 or so
years of testing to mature, the 3D portion is much more hacked in and has a host
of quriks. For instance, it appears that they did not do perspective-based masking
correctly and as such some animations which should have masking animations applied
to them appear to do so incorrectly. They also appear to have a bug where specifying
3D perspective projection on an object in a sprite and then placing that sprite
down on the main canvas results in a 2D affine projection instead of a 3D one.
These are all subtle enough that its quite possible QA never caught the problem or
the artist complained and was told it was not a big enough deal to fix.
I briefly considered having a compatibility quirks flag system in order to attempt
to fix these issues and present a renderer that exactly matches what games do.
The problem with that is that this renderer already attempts to be compatible
with about a decade and a half of format changes. Given how much of the format
has been ripped out for newer games, this is already an impossible task. I would
also have to spend a great deal more time trying to figure out exactly HOW they
got these things subtly wrong to replicate them versus understanding how the format
was intended to work. I've made the decision that it is not worth my time to do so.
# References
Much of the understanding of this format came from the various online SWF file format
references as so much of the format mirrors Flash even today. Detailed documentation
on the format itself is entirely missing but the code in this directory provides a
working reference implementation of the format. Wherever this document is vague or
confusing, please refer to the code in `swf.py` for how to parse AFP, `render.py` for
how to render AFP, `container.py` for how to parse TXP2 files and `geo.py` for how to
parse shape structures. Please also refer to `decompile.py` for a ton of details on
how the bytecode itself works.