Skip to main content
FFXIVClientStructs provides several Python scripts for IDA Pro to automate the reverse engineering process. These scripts work with both Python 2 and Python 3 versions of IDA Pro.

Installation

Python Dependencies

Install required packages for the Python version used by your IDA Pro installation:
pip install pyyaml anytree
Or explicitly:
python -m pip install pyyaml anytree
IDA Pro 7.x typically uses Python 2, while IDA Pro 8.x and newer use Python 3. The scripts support both versions.
If you use Poetry for package management:
poetry install --extras idarename

Configuration File

IDA Pro restricts certain characters in symbol names. To allow the full range of characters used in FFXIVClientStructs:
  1. Locate the file idauser.cfg in the FFXIVClientStructs repository (source/ida/idauser.cfg)
  2. Copy it to %AppData%\Hex-Rays\IDA Pro\ (Windows)
This configuration enables characters like colons (:) in class names.

Available Scripts

ffxiv_idarename.py

The primary script for applying names from data.yml to your IDA database.

What It Does

  • Renames global variables and constants
  • Names standalone functions
  • Creates class hierarchies with proper vtable layouts
  • Names virtual functions with inheritance tracking
  • Names member functions
  • Labels global class instances
  • Adds inheritance tree comments to vtables

Usage

  1. Open ffxiv_dx11.exe in IDA Pro
  2. Wait for initial auto-analysis to complete
  3. Load the script: File → Script file... or Alt+F7
  4. Navigate to FFXIVClientStructs/ida/ffxiv_idarename.py
  5. Execute
Output:
Executing
[Processing messages...]
Done

Re-running the Script

You can safely re-run ffxiv_idarename.py multiple times:
  • Updates names if data.yml has changed
  • Fixes inheritance issues
  • Applies new findings
The script modifies your IDA database. Save your work before running it for the first time.

Example Transformation

Before:
void * FUN_1400901d0(void *param_1)
{
  // ...
}
After running the script:
void * Client::System::Framework::Framework.ctor(void *param_1)
{
  // ...
}

Function Naming Behavior

The script intelligently handles different function name prefixes:
Current NameAction
sub_*Rename to class.function
j_* (jump stub)Strip prefix and rename
qword_* in vtableConvert to code and rename
nullsub_*Rename with _nullsub suffix
loc_*, locret_*Rename with _loc/_locret suffix
_purecallSkip (will be named in derived class)
Already namedUpdate if different

Virtual Table Handling

The script processes vtables with inheritance support:
  1. Size Detection: Scans from vtable start until finding non-offset or cross-reference
  2. Inheritance: Applies base class names to inherited vfuncs
  3. Comments: Adds inheritance tree visualization
  4. Validation: Checks for size mismatches between base and derived classes
Example vtable comment:
Component::GUI::AtkModule
└── Component::GUI::AtkModuleInterface::AtkEventInterface
    └── Client::UI::Agent::AgentInterface

ffxiv_sigmaker.py

This script is no longer maintained. Use caraxi’s sigmaker-x64 instead.
Historical script for generating byte signatures from data.yml. Dependencies (Python 3 only):
pip install dacite pyyaml ruamel-yaml
Or with Poetry:
poetry install --extras sigmaker

ffxiv_exdgetters.py

Does not work with IDA 9 due to API changes.
Ingests game Excel sheet definitions (.exh files) and renames functions that fetch specific sheets.

What It Does

Transforms generic Exd getter calls into typed functions: Before:
undefined8 FUN_1406cb6c0(undefined4 param_1)
{
  undefined8 *puVar1;
  puVar1 = Component::Exd::ExdModule.GetRowBySheetIndexAndRowIndex(
    *(g_Framework + 0x2b38), 0x3b, param_1);
  if (puVar1 == 0x0) return 0;
  return *puVar1;
}
After:
undefined8 Client::ExdData::GetClassJob(undefined4 param_1)
{
  undefined8 *puVar1;
  puVar1 = Component::Exd::ExdModule.GetRowBySheetIndexAndRowIndex(
    *(g_Framework + 0x2b38), 0x3b, param_1);
  if (puVar1 == 0x0) return 0;
  return *puVar1;
}
Sheet index 0x3b corresponds to the ClassJob sheet, so the function is renamed accordingly.

ffxiv_structimporter.py

Does not work with IDA 9 due to API changes.
Ingests ffxiv_structs.yml and creates IDA struct definitions with proper member types and function signatures.

ffxiv_fullrun_ida.py

Does not work with IDA 9 due to API changes.
Convenience script that runs in sequence:
  1. ffxiv_idarename.py
  2. ffxiv_exdgetters.py
  3. ffxiv_structimporter.py

Script Architecture

API Abstraction Layer

All scripts use an abstraction layer (class BaseApi) that allows the same code to work across IDA Pro, Ghidra, and Binary Ninja. Key methods:
  • get_image_base() - Get current executable base address
  • get_addr_name(ea) - Get symbol name at address
  • set_addr_name(ea, name) - Set symbol name
  • get_qword(ea) - Read 8 bytes at address
  • xrefs_to(ea) - Get cross-references
  • is_offset(ea) - Check if address contains pointer

IDA-Specific Implementation

The scripts use IDA Python API:
import idaapi
import idc
import idautils
import ida_funcs

class IdaApi(BaseApi):
    def get_image_base(self):
        return idaapi.get_imagebase()
    
    def set_addr_name(self, ea, name):
        return bool(idc.set_name(ea, name))
    # ...
See /home/daytona/workspace/source/ida/ffxiv_idarename.py:208-314 for the complete IDA implementation.

Rebasing Support

If your IDA database uses a different image base than 0x140000000, the scripts automatically adjust all addresses:
STANDARD_IMAGE_BASE = 0x140000000
current_image_base = api.get_image_base()
if STANDARD_IMAGE_BASE != current_image_base:
    rebase_offset = current_image_base - STANDARD_IMAGE_BASE
    vtbl.ea += rebase_offset

Error Messages

Common Warnings

“Multiple vtables are defined at 0xXXXXXXXX”
  • Two classes claim the same vtable address in data.yml
  • Check for duplicate entries
“Inherited class ‘ClassName’ is not documented”
  • Referenced base class missing from data.yml
  • Add a placeholder entry for the base class
“overwrites the name of inherited function”
  • Derived class explicitly names a vfunc that’s already named in base class
  • Usually informational, not an error

Common Errors

“Function at 0xXXXXXXXX had unexpected name ‘YYY’ during naming of ZZZ”
  • Function has an unexpected existing name
  • May indicate wrong address or naming conflict
  • Review the function manually
“The sum of ‘ClassName’s base vtbl sizes (X) is greater than the actual class itself (Y)”
  • Vtable size detection found fewer functions than the base class
  • May indicate incorrect inheritance or missing vtable entries

Tips and Tricks

Finding Vtables

Look for constructor functions that write pointers:
mov     [rax], offset vtbl_Component_GUI_AtkResNode
The pointer written is the vtable address.

Identifying Virtual Functions

Virtual function calls use vtable offsets:
mov     rax, [rcx]      ; Load vtable pointer
call    qword ptr [rax+18h]  ; Call vfunc at index 3 (0x18/8)
Index 3 (0x18 ÷ 8 bytes per pointer) corresponds to vfunc[3].

Handling Inheritance

When a class has multiple vtables, the first is the main vtable, and additional ones represent base classes:
DerivedClass:
  vtbls:
    - ea: 0x141234567  # Main vtable (extends BaseClass1)
      base: BaseClass1
    - ea: 0x141234600  # Secondary vtable for BaseClass2
      base: BaseClass2

Testing Changes

  1. Make changes to data.yml
  2. Re-run ffxiv_idarename.py
  3. Navigate to renamed functions to verify
  4. Check IDA Output window for errors

Next Steps

Build docs developers (and LLMs) love