BG3 Script Extender macOS + Ghidra Development
Project Locations
| Project | Path |
|---------|------|
| bg3se-macos | /Users/tomdimino/Desktop/Programming/bg3se-macos |
| bg3se (Windows ref) | /Users/tomdimino/Desktop/Programming/bg3se |
Quick Start
# Build
cd /Users/tomdimino/Desktop/Programming/bg3se-macos/build
cmake .. && cmake --build .
# Test
./scripts/launch_bg3.sh
tail -f ~/Library/Application\ Support/BG3SE/bg3se.log
# Ghidra (headless, optimized)
./ghidra/scripts/run_analysis.sh <script.py>
Key Constraints (macOS)
- Cannot hook main binary - Hardened Runtime blocks
__TEXThooks - CAN hook libOsiris.dylib - 1,013 exported symbols
- ARM64 ABI - x8 register for structs >16 bytes
- No GetRawComponent - Must traverse ECS manually
See macos-patterns.md for detailed differences.
Module Structure
src/
├── injector/main.c # Core (~2900 lines): hooks, Osi.*, Lua state
├── entity/ # ECS: guid_lookup, component_lookup, arm64_call
├── lua/ # Ext.* APIs: stats, debug, osiris, json
├── osiris/ # Osiris types, functions, custom_functions
├── stats/ # RPGStats, GlobalStringTable
├── console/ # Socket server, file-based console
└── input/ # CGEventTap keyboard capture
API Surface (v0.22.0)
| Namespace | Status | Notes |
|-----------|--------|-------|
| Osi.* | 95% | Dynamic metatable, Query/Call/Event |
| Ext.Osiris | 95% | RegisterListener, NewCall/Query/Event |
| Ext.Stats | 95% | Property read/write |
| Ext.Entity | 50% | Get, GetComponent, GetAllEntitiesWithComponent |
| Ext.Events | 75% | 7 events |
| Ext.Timer | 100% | Complete |
| Ext.Debug | 100% | Memory introspection |
Not implemented: Ext.Net, Ext.UI, Ext.Level, Client Lua State
Key Offsets
| Symbol | Address/Offset |
|--------|----------------|
| esv::EocServer::m_ptr | 0x10898e8b8 |
| EntityWorld | EocServer+0x288 |
| RPGStats::m_ptr | base+0x89c5730 |
| RPGStats.FixedStrings | +0x348 |
| GlobalStringTable | base+0x8aeccd8 |
osgrep Usage
# IMPORTANT: cd into project first
cd /Users/tomdimino/Desktop/Programming/bg3se-macos
osgrep "how does entity lookup work"
osgrep "Ext.Stats property resolution"
# Windows reference
cd /Users/tomdimino/Desktop/Programming/bg3se
osgrep "CustomFunctionManager"
Common Tasks
Add new Ext. API:*
- Implement in
src/lua/lua_*.c - Register in
lua_*_register()function - Test with EntityTest mod
Discover offset:
- Search strings in Ghidra
- Find XREFs, trace ADRP+LDR pattern
- Document in
ghidra/offsets/*.md
Port Windows feature:
- Search Windows BG3SE:
osgrep "feature" -p /path/to/bg3se - Understand pattern, find ARM64 equivalents
- Adapt for macOS constraints
Reference Documentation
- macos-patterns.md - macOS vs Windows, GUID byte order, component traversal
- bg3se-architecture.md - Windows BG3SE structure
- arm64-patterns.md - x8 register, calling conventions
- ghidra-workflows.md - Script usage, analysis patterns
- offset-discovery.md - Finding game addresses
Troubleshooting
Crashes on launch: Check dylib signing (codesign -dv), verify ARM64 (file)
Hooks not called: Only hook libOsiris, not main binary
Entity lookup NULL: EoCServer may not be initialized; verify offset 0x288
osgrep no results: Run from project directory or use -p flag
Scan to join WeChat group