How do we identify events with the API Calls window?
There’s only numbers in the arguments for the calls.
How do we identify events with the API Calls window?
There’s only numbers in the arguments for the calls.
I don’t think that’s possible, but that would be handy…
Navigation of the API log is not super easy, you might need to navigate into the [project]\Metadata\ProfilerSession folder and look at the .txt capture in a text editor.
The first argument to the EventInstance functions is its ID, to match that ID back to the EventDescription GUID look for the first instance of that ID in the log, it should be a EventDescription::createInstance call, where the ID is the second argument. The first argument to that call is the EventDescription ID. Same process, find the first instance of that ID, it should be a Bank::getEventListItem call with the ID as the last argument. The first argument is the Event guid which you can copy and paste into the event browser to find the event in question.
I hadn’t considered it before, but having the IDs as hyperlinks that jump to their respective editor, i.e. EventInstance IDs jump to the event browser, BusInstances jump to the mixer could be a pretty nice feature.
Hello,
I had to use recently the API log and it would be great if, next to the ids, the name of the event or bus could be added and also it would be a good information to add, the call origin: if the call was made internaly (from an event command or virtualization manager) or with the API from game code.
We do have plans for making the API view richer but nothing immediately scheduled.
As for your comment about command instruments and the like, the API capture only includes commands issued by the game, not internal commands such as command instruments. It’s our public API that does the capturing and all internal operations do not use that, hence they aren’t recorded.
I just made (with chatGPT) a python script which make the logs way more readable, by referencing every event and instances. It duplicates and annotates each log file in the profiler folder, based on the GUIDs file.
For exemple,
Bank::getEventListItem, 2097152, 30, {a6d54fd3-bae9-47e0-ae2c-99953aa66e4f}, 2103040
EventDescription::createInstance, 2103040, 2109184
EventInstance::start, 2109184
EventInstance::stop, 2109184, 1
becomes
Bank::getEventListItem, 2097152, 30, {a6d54fd3-bae9-47e0-ae2c-99953aa66e4f}, 2103040 -- event:/UI/characterSelected
EventDescription::createInstance, 2103040, 2109184 -- createInstance characterSelected #3
EventInstance::start, 2109184 -- start characterSelected #3
EventInstance::stop, 2109184, 1 -- stop characterSelected #3
import os
import re
from pathlib import Path
# --- Configuration des chemins ---
AUDIO_DIR = Path(r"C:\Program Files (x86)\Steam\steamapps\common\zodiaclegion\assets\audio")
GUIDS_FILE = AUDIO_DIR / "GUIDs.txt"
PROFILER_DIR = Path(r"C:\Users\Alcibiade\Documents\FMOD Studio\Zodiac Legion\Metadata\ProfilerSession")
# --- Chargement du mapping GUID → chemin FMOD depuis GUIDs.txt ---
def load_guid_map(path):
guid_map = {}
pattern = re.compile(r"\{([0-9a-fA-F\-]+)\}\s+(.+)")
with open(path, 'r', encoding='utf-8') as f:
for line in f:
m = pattern.match(line.strip())
if m:
guid = m.group(1).lower()
fmod_path = m.group(2).strip()
guid_map[guid] = fmod_path
return guid_map
# --- Extraction du short name à partir du chemin FMOD ---
def short_name(path):
return path.rstrip('/').split('/')[-1]
# --- Fonction pour aligner le commentaire à la colonne 55 ---
def align_comment(line, comment):
if len(line) < 55:
return line + ' ' * (55 - len(line)) + comment
else:
return line + ' ' + comment
# --- Annotation d’un seul fichier log ---
def annotate_file(log_path, guid_map):
handle_to_path = {} # desc_handle et inst_handle → fmod_path
instance_counters = {} # fmod_path → nombre d’instances créées
handle_to_instance = {} # inst_handle → numéro d’instance
re_get = re.compile(r"Bank::getEventListItem.*\{([0-9a-fA-F\-]+)\},\s*(\d+)")
re_create = re.compile(r"EventDescription::createInstance.*,\s*(\d+)\s*,\s*(\d+)")
re_eventinst= re.compile(r"EventInstance::(\w+).*?,\s*(\d+)")
out_lines = []
with open(log_path, 'r', encoding='utf-8') as f:
for raw in f:
content = raw.rstrip('\n')
# 1) Bank::getEventListItem → commenter full path
m = re_get.search(content)
if m:
guid, desc_handle = m.group(1).lower(), m.group(2)
if guid in guid_map:
fmod_path = guid_map[guid]
handle_to_path[desc_handle] = fmod_path
comment = f"-- {fmod_path}"
out_lines.append(align_comment(content, comment) + "\n")
else:
out_lines.append(content + "\n")
continue
# 2) createInstance → commenter full path + #N
m = re_create.search(content)
if m:
desc_handle, inst_handle = m.group(1), m.group(2)
fmod_path = handle_to_path.get(desc_handle, "")
cnt = instance_counters.get(fmod_path, 0) + 1
instance_counters[fmod_path] = cnt
handle_to_instance[inst_handle] = cnt
handle_to_path[inst_handle] = fmod_path
name = short_name(fmod_path)
comment = f"-- createInstance {name}" + (f" #{cnt}" if cnt > 1 else "")
out_lines.append(align_comment(content, comment) + "\n")
continue
# 3) EventInstance::<command> → commenter <command> <short_name>#N
m = re_eventinst.search(content)
if m:
cmd, inst_handle = m.group(1), m.group(2)
fmod_path = handle_to_path.get(inst_handle, "")
cnt = handle_to_instance.get(inst_handle, 1)
name = short_name(fmod_path)
comment = f"-- {cmd} {name}" + (f" #{cnt}" if cnt > 1 else "")
out_lines.append(align_comment(content, comment) + "\n")
continue
# 4) autres lignes → pas d’annotation
out_lines.append(content + "\n")
out_path = log_path.with_name(log_path.stem + "_annotated.txt")
with open(out_path, 'w', encoding='utf-8') as out_f:
out_f.writelines(out_lines)
print(f" → créé : {out_path.name}")
# --- Parcours des logs + ouverture dossier ---
def main():
if not GUIDS_FILE.is_file():
print("Erreur : GUIDs.txt introuvable à", GUIDS_FILE)
return
global guid_map
guid_map = load_guid_map(GUIDS_FILE)
for txt in PROFILER_DIR.rglob("*.txt"):
if "annotated" in txt.stem.lower():
continue
print("Traitement de", txt.name)
annotate_file(txt, guid_map)
os.startfile(str(PROFILER_DIR))
if __name__ == "__main__":
main()
Of course, change the paths at the beginning, expand, and translate french terms as you want!
By the way, why is this basic stuff just not built in by default?