Events Editor

Even though implementing events support for the add-on is doable, I have no plans to support them right now. But instead, I made this simple script to read and write events into animation files. I cannot tell you which event does what, as there simply too many events and I have yet to confirm what each one does. Events is currently out of my scope, since my initial plan for the add-on was to support import and exporting the 3d formats, not putting the entire game into the Blender.

Downloads

Windows only, preferably win10

Events Editor (Windows executable)
events_editor.exeEXE5.63 MB

Tested on python 3.10

Events Editor script
events_editor.pyPY4.62 KB

events_editor.py
#!/usr/bin/env python3

import os
import struct
import sys

if __name__ == '__main__':
    ani_file_location = input("file location: ").strip('"')
    ani = ani_file_location

    stem = os.path.splitext(os.path.basename(ani_file_location))
    ani_file_location = stem[0] + '_edit' + stem[1]

    with open(ani, 'rb') as f:
        header = f.read(4)
        if header != b'ANI\0':
            input('Not an ani file. Press enter to exit')
            sys.exit()

        ani = header + f.read()

    anim_header_binary = ani[:8]
    event_count_binary = ani[8:12]
    event_count = struct.unpack('<I', event_count_binary)[0]

    offset = 12
    anim_events = []
    for _ in range(event_count):
        event_type_length = struct.unpack('<I', ani[offset:offset+4])[0]
        offset += 4

        event_type = ani[offset:offset+event_type_length]
        offset += event_type_length

        event_name_length = struct.unpack('<I', ani[offset:offset+4])[0]
        offset += 4

        event_name = ani[offset:offset+event_name_length]
        offset += event_name_length

        event_time = int(struct.unpack('<f', ani[offset:offset+4])[0])
        offset += 4

        anim_events.append((event_time, event_type.decode('utf-8'), event_name.decode('utf-8')))

    leftover_data = ani[offset:]

    while True:
        print()
        print('------- Events -------')
        for index, (time, type, name) in enumerate(anim_events):
            print(str(index) + ': ' + str(time) + ' | ' + type + ' | ' + name)

        print()
        user_input = input('Actions:\n> new [time (in ms), event type, event name]\n> remove [index]\n> insert/edit [index] [time (in ms), event type, event name]\n> exit/save (save the file)\n>>> ')
        if not any(user_input.lower().startswith(item) for item in ('new', 'remove', 'insert', 'edit', 'exit', 'save')):
            print('Not a valid option\n')
            continue

        if user_input.lower().startswith('new'):
            user_args = user_input.split(maxsplit=3)
            if not len(user_args) >= 4:
                print('Missing argument')
                continue

            _, time, event_type, event_name = user_args
            try:
                time = int(time)
            except ValueError:
                print('Time is not a number')
                continue

            anim_events.append((time, event_type, event_name))

        elif user_input.lower().startswith('remove'):
            user_args = user_input.split()
            if not len(user_args) >= 2:
                print('Missing argument')
                continue
            _, index = user_args
            try:
                index = int(index)
            except ValueError:
                print('Index is not a number')
                continue
            try:
                anim_events.pop(index)
            except IndexError:
                print('Out of range')
                continue

        elif user_input.lower().startswith('insert') or user_input.lower().startswith('edit'):
            user_args = user_input.split(maxsplit=4)
            if not len(user_args) >= 5:
                print('Missing argument')
                continue

            _, index, time, event_type, event_name = user_args
            try:
                index = int(index)
                time = int(time)
            except ValueError:
                print('Index or Time is not a number')
                continue

            try:
                anim_events[index] = (time, event_type, event_name)
            except IndexError:
                print('Out of range')

        elif user_input.lower().startswith('exit') or user_input.lower().startswith('save'):
            break

        anim_events = sorted(anim_events, key=lambda x: x[0])

    with open(ani_file_location, 'wb') as ani:
        ani.write(anim_header_binary)
        ani.write(struct.pack('<I', len(anim_events)))

        for time, event_type, event_name in anim_events:
            ani.write(struct.pack('<I', len(event_type)))
            ani.write(event_type.encode('utf-8'))
            ani.write(struct.pack('<I', len(event_name)))
            ani.write(event_name.encode('utf-8'))
            ani.write(struct.pack('<f', float(time)))
        ani.write(leftover_data)

    print()
    print('Saved file as: ' + ani_file_location)    
    input('Press enter to exit...')
    sys.exit()