Compressing FEGBA battle animation framedata more efficiently

This would be intended for buildfile users.

So on the FEU discord (#dev_lounge IIRC) there was some talk regarding optimizing how AA produces framedata, and I was thinking, what about a program, AA2, that takes:

  • A battle animation script.
  • Frames, the images that are referred to in the script.
  • A ROM offset, O_i.

and produces:

  • An EA file with a generated anim struct. Also incbinning…
  • .dmps for sectiondata, framedata, palettedata, rtlOAMdata, ltrOAMdata and sheets. In my own experience, incbinning hex files is faster than inserting them byte-by-byte via EA.
  • Another ROM offset, O_o.

The idea then is that the first generated sheet, sheet_0 will be inserted at offset offset(sheet_0) = O_i - size(sheet_0). Next, sheet_1 would be inserted at offset offset(sheet_0) - size(sheet_1), etc. Instead of using a label for a sheet, AA2 would actually know its offset. Framedata can now (and will) be compressed.

Then AA2 incbins the other stuff like rtlOAMdata, ltrOAMdata, palettedata, framedata and sectiondata in a similar fashion. Finally, it produces an offset, O_o, which the EA file will ORG to (after the anim struct) and where everything will be written to in your ROM, if you include the EA file in your buildfile. You can then re-use O_o as input to AA2 for the next animation you’d like to add.

I’d think it makes sense for the first O_i to be 0x1FF FFFF (or 0x200 0000 depending on how we should handle this edge case (gimme suggestions pls)). This way, animations will be at the end of ROM. You can then compare CurrentOffset to O_o for the last animation in an EA MESSAGE. if CurrentOffset > O_o, you’ve written over animation graphics and you can throw an error. Alternatively you can PROTECT O_o 0x200 0000 or PROTECT O_o O_o+4.

So here’s what the first animation would look like in your ROM if you build its EA file into a ROM:

the EA file AA2 produces would then look something like this:

#ifndef ClassAnimTable
  #define ClassAnimTable (0xc00008-0x20)
#endif
#ifndef AnimTableEntry
  #define AnimTableEntry(index) "ORG ClassAnimTable + ((index) * 0x20)"
#endif

PUSH
AnimTableEntry(0x0)   // CHANGE THIS TO THE SLOT YOU WISH TO CONSUME.
String("CoolAnim")    // Or leave name empty, boo.
WORD 0
POIN Anim_<animname>_sectiondata
POIN Anim_<animname>_framedata
POIN Anim_<animname>_rtl Anim_<animname>_ltr
POIN Anim_<animname>_pal

ORG <O_o>

Anim_<animname>_sectiondata:
#incbin "Anim_<animname>_sectiondata.dmp"

Anim_<animname>_framedata:
#incbin "Anim_<animname>_framedata.dmp"

Anim_<animname>_pal:
#incbin "Anim_<animname>_pal.dmp"

Anim_<animname>_ltr:
#incbin "Anim_<animname>_ltr.dmp"

Anim_<animname>_rtl:
#incbin "Anim_<animname>_rtl.dmp"

// SHEETS
#incbin "Anim_<animname>_Sheetn"
.
.
.
#incbin "Anim_<animname>_Sheet1"
#incbin "Anim_<animname>_Sheet0"

POP

Incidentally, I’ve recently made something that already takes a script and frames to produce an EA installer. It shouldn’t be too much work to make a separate version that can be used in the way I described above.

I’m looking for feedback and suggestions. I’d like to hear what I can improve about this concept before I start working on this. I’d also like to know if anybody would be interested in using it, otherwise it’ll possibly get weird idiosyncrasies that suit my specific needs.

8 Likes

This is really cool, I’ve recently been thinking it’s a feature I’d like to have but I don’t have the patience with Python to do it anytime soon.

Feature suggestions:

  • Arbitrary start offset - if not defined, write from the “bottom up” from the end of ROM as you’ve described. If defined, write from the “top down” starting at offset. I’m thinking that with the better compression it may be possible to fit all a hack’s animations within the vanilla animation table, which would free up 0xC02000 - 0xEDFFFF for inserted animations.
    • For example, all of Sacred Echoes’ non-monster classes (140 animation files) fit comfortably in that space, and this is with a lot of Nura’s/SALVAGED’s animations that use many more sheets than vanilla. Even with monsters, I was able to fit 156 of my 177 total animations in that space. For hacks that don’t use monsters at all it would be reasonable to overwrite in the vanilla range.
  • Batch processing of some sort, where shared elements are possible - we can generally assume that palettes will be shared between weapon types for a given class, and (unless using two different community animation variants) between genders for two adjacent classes with the same name string pointer. Since the current buildfile method does every animation individually and wrapped in brackets to contain labels to local scope, the same palette gets written twice or more.
    • Maybe a stretch, but being able to share sheet space across weapon types? Unarmed animations are typically only a few frames and could theoretically be squished into the empty space at the end of an armed animation.
    • This folds into both of the above, but processing subdirectories to make one installer for all animations belonging to a classID vs needing separate ones for each weapon type would be cool.
  • Arbitrary internal ASCII identifiers in place of "CoolAnim" - I like having them because it keeps me organized. Commented animation scripts include them on line 2 after a # and the animation index. So maybe adding a check for whether the script is commented or not, and if so, using the first 8 characters for the ASCII identifier. Otherwise it could just be specified from the command line.

This is all I can think of at the moment, and even without these it’d be a big help to my freespace!

2 Likes

A personal half implementation of AA (from the feditor bin file, not frames and sheets), hope this helps

anyway use PyFastGBALz77 instead of lzss.py for a huge performance boost, and maybe use numpy?

For structure I suggest a parse folder feature

Animations:
    Soldier/
        Lance/ ...
        Axe/ ...
    Cavalier/
        ...

And we can predefine things like

#define SoldierLanceAnim 0xbc
#define CavalierSwordAnim 0xcd

run

 AA Animations

And get labels like

SolderAnim:
    Something
2 Likes