hello world 🌸✨
This commit is contained in:
commit
c9f25e7b6a
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
build/
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
.cache/
|
||||||
|
compile_commands.json
|
||||||
|
*.log
|
||||||
|
log*.txt
|
||||||
|
*.not
|
||||||
BIN
3rdparty/QtScript.lib
vendored
Normal file
BIN
3rdparty/QtScript.lib
vendored
Normal file
Binary file not shown.
BIN
3rdparty/ToonBoomActionManager.lib
vendored
Normal file
BIN
3rdparty/ToonBoomActionManager.lib
vendored
Normal file
Binary file not shown.
BIN
3rdparty/ToonBoomLayout.lib
vendored
Normal file
BIN
3rdparty/ToonBoomLayout.lib
vendored
Normal file
Binary file not shown.
BIN
3rdparty/ToonBoomPlugInManager.lib
vendored
Normal file
BIN
3rdparty/ToonBoomPlugInManager.lib
vendored
Normal file
Binary file not shown.
42
3rdparty/dll2def.ps1
vendored
Normal file
42
3rdparty/dll2def.ps1
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
param (
|
||||||
|
[parameter(mandatory = $true)]
|
||||||
|
[string]$dllpath,
|
||||||
|
[parameter(mandatory = $true)]
|
||||||
|
[string]$outputpath
|
||||||
|
)
|
||||||
|
|
||||||
|
# ensure dumpbin is available (must be run from vs developer command prompt or similar environment)
|
||||||
|
|
||||||
|
$dllname = split-path -path $dllpath -leaf
|
||||||
|
$basename = [system.io.path]::getfilenamewithoutextension($dllname)
|
||||||
|
$deffile = join-path -path $outputpath -childpath "$basename.def"
|
||||||
|
$exportsfile = join-path -path $outputpath -childpath "$basename-exports.txt"
|
||||||
|
|
||||||
|
write-host "dumping exports to $exportsfile..."
|
||||||
|
# execute dumpbin and redirect output to a temporary file
|
||||||
|
dumpbin /exports $dllpath > $exportsfile
|
||||||
|
|
||||||
|
write-host "creating $deffile..."
|
||||||
|
# start the .def file content
|
||||||
|
"library $basename" | out-file -filepath $deffile -encoding ascii
|
||||||
|
"exports" | out-file -filepath $deffile -encoding ascii -append
|
||||||
|
|
||||||
|
# process the dumpbin output to extract function names
|
||||||
|
# the output format can vary, so the token skip might need adjustment
|
||||||
|
# this example assumes the format where the name is the 4th token after skipping initial lines.
|
||||||
|
get-content $exportsfile | foreach-object {
|
||||||
|
if ($_ -match '^\s+\d+\s+[0-9a-fa-f]+\s+[0-9a-fa-f]+\s+([^\s=]+)') {
|
||||||
|
$functionname = $matches[1]
|
||||||
|
# handle c++ name mangling if necessary (often, mangled names are put directly in the def)
|
||||||
|
# for c-style linkage, names appear as expected.
|
||||||
|
"$functionname" | out-file -filepath $deffile -encoding ascii -append
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# clean up the temporary exports file
|
||||||
|
remove-item $exportsfile
|
||||||
|
|
||||||
|
write-host "successfully generated $deffile"
|
||||||
|
lib /def:$deffile /out:$outputpath/$basename.lib /machine:x64
|
||||||
|
# example usage (run within a developer powershell/command prompt):
|
||||||
|
# new-deffilefromdll -dllpath "c:\path\to\your\library.dll"
|
||||||
34
CMakeLists.txt
Normal file
34
CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
project(toon-boom-extension-framework
|
||||||
|
VERSION 0.1.0
|
||||||
|
DESCRIPTION "Toon Boom extension framework"
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT DEFINED VCPKG_ROOT)
|
||||||
|
message(FATAL_ERROR "VCPKG_ROOT is not defined. Please set the VCPKG_ROOT variable to the root directory of the vcpkg installation.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED QT5_ROOT_DIR)
|
||||||
|
message(FATAL_ERROR "QT5_ROOT_DIR is not defined. Please set the QT5_ROOT_DIR variable to the root directory of the Qt 5.15 installation.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED QT6_ROOT_DIR)
|
||||||
|
message(FATAL_ERROR "QT6_ROOT_DIR is not defined. Please set the QT6_ROOT_DIR variable to the root directory of the Qt 6 installation.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
|
||||||
|
list(APPEND CMAKE_PREFIX_PATH ${QT6_ROOT_DIR})
|
||||||
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20 /Zc:__cplusplus")
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui Core5Compat QUIET)
|
||||||
|
add_subdirectory(framework)
|
||||||
59
CMakePresets.json
Normal file
59
CMakePresets.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 4,
|
||||||
|
"minor": 2,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "vs2026",
|
||||||
|
"displayName": "Visual Studio 18 2026",
|
||||||
|
"generator": "Visual Studio 18 2026",
|
||||||
|
"binaryDir": "${sourceDir}/build/vs2026",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ninja",
|
||||||
|
"displayName": "Ninja",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build/ninja",
|
||||||
|
"toolchainFile": "C:/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"environment": {
|
||||||
|
"VCPKG_VISUAL_STUDIO_PATH": "C:/Program Files/Microsoft Visual Studio/18/Community",
|
||||||
|
"VCPKG_ROOT": "C:/vcpkg",
|
||||||
|
"CMAKE_GENERATOR_PLATFORM": "x64"
|
||||||
|
},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||||
|
"CMAKE_CXX_COMPILER": "cl.exe",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "C:/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"CMAKE_MAKE_PROGRAM": "C:/Program Files/Microsoft Visual Studio/18/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "vs2026-debug",
|
||||||
|
"configurePreset": "vs2026",
|
||||||
|
"configuration": "Debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vs2026-release",
|
||||||
|
"configurePreset": "vs2026",
|
||||||
|
"configuration": "Release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ninja-debug",
|
||||||
|
"configurePreset": "ninja",
|
||||||
|
"configuration": "RelWithDebInfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ninja-release",
|
||||||
|
"configurePreset": "ninja",
|
||||||
|
"configuration": "Release"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
210
docs/HarmonyPremium_QtScript.md
Normal file
210
docs/HarmonyPremium_QtScript.md
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
# HarmonyPremium: QtScript / `QScriptEngine` integration
|
||||||
|
|
||||||
|
This document captures the current understanding of Harmony Premium's QtScript integration as observed in `RE/HarmonyPremium.exe.i64`.
|
||||||
|
|
||||||
|
## Key takeaways (high signal)
|
||||||
|
|
||||||
|
- Harmony has an internal `QObject` wrapper around the scripting runtime named **`SCR_ScriptEngineImpl`**.
|
||||||
|
- A higher-level manager **`SCR_ScriptManager`** constructs `SCR_ScriptEngineImpl`, registers a large set of interfaces (via `SCR_Scripting_registerInterfaces` at `0x140914850`), then injects itself into script as a global object named **`"___scriptManager___"`**.
|
||||||
|
- Two global native functions are installed into the script global object:
|
||||||
|
- **`include(filename)`** → forwards to a virtual method on the script/scene manager interface (and tries to execute the included file in the caller's scope by temporarily adopting the parent context's activation/`this` objects).
|
||||||
|
- **`require(filename)`** → resolves/remaps the path, loads the file, wraps it in a JavaScript closure that returns `exports`, evaluates it, and returns the `exports` object (or returns the uncaught exception).
|
||||||
|
- There are small helper routines used throughout Harmony to bind C++ into the scripting global object:
|
||||||
|
- bind `QObject*` via `QScriptEngine::newQObject(...)` then `QScriptValue::setProperty(...)`
|
||||||
|
- bind native callbacks via `QScriptEngine::newFunction(...)` then `QScriptValue::setProperty(...)`
|
||||||
|
|
||||||
|
## Main components
|
||||||
|
|
||||||
|
### `SCR_ScriptEngineImpl`
|
||||||
|
|
||||||
|
Observed in `SCR_ScriptEngineImpl_ctor` (`0x14082AEC0`).
|
||||||
|
|
||||||
|
What it does:
|
||||||
|
|
||||||
|
- Is a `QObject` subclass (has `qt_metacast`, metaobject data, etc).
|
||||||
|
- Allocates an internal `QScriptEngine` and stores it in the object (other methods access it via a field at **byte offset `+40`** from `this`).
|
||||||
|
- Installs a custom `QScriptEngineAgent` (`SCR_ScriptEngineImpl::ScriptAgent`) and sets the engine's process-events interval from preference key `"SCR_EVENT_PROCESSING_INTERVAL"`.
|
||||||
|
- Maintains a recursion depth counter (checked against **`12`**) used to prevent runaway recursion / re-entrancy during evaluation and calls.
|
||||||
|
|
||||||
|
Evaluation-related methods (renamed in the IDA DB during this session):
|
||||||
|
|
||||||
|
- `SCR_ScriptEngineImpl_callWithTempGlobal` (`0x14082B330`)
|
||||||
|
- Temporarily swaps the engine global object (`setGlobalObject`), calls a `QScriptValue` callable (`QScriptValue::call`), restores the old global, and maps uncaught exceptions into the return value.
|
||||||
|
- `SCR_ScriptEngineImpl_evalInNewGlobal` (`0x14082BEB0`)
|
||||||
|
- Creates a fresh global object (`newObject`), sets its prototype to a provided prototype or to the previous global, swaps it in via `setGlobalObject`, evaluates a `QString` script body, restores the previous global.
|
||||||
|
- On unwind: triggers `collectGarbage()` when leaving the outermost evaluation.
|
||||||
|
- `SCR_ScriptEngineImpl_evalFile` (`0x14082C590`)
|
||||||
|
- Reads a script file as UTF-8 and evaluates it.
|
||||||
|
- Writes `__file__` into the current scope chain before evaluation (see helper below).
|
||||||
|
- Uses `SCR_ScriptEngineImpl_evalInNewGlobal` when not already nested; otherwise evaluates directly in the existing global object.
|
||||||
|
|
||||||
|
### `SCR_ScriptManager`
|
||||||
|
|
||||||
|
Observed in `SCR_ScriptManager_ctor` (`0x14081FD60`).
|
||||||
|
|
||||||
|
What it does:
|
||||||
|
|
||||||
|
- Constructs `SCR_ScriptEngineImpl`.
|
||||||
|
- Calls a **large** interface registration routine (`SCR_Scripting_registerInterfaces`, `0x140914850`) which populates the scripting environment with many bindings.
|
||||||
|
- Injects itself into script as **global property** `"___scriptManager___"`:
|
||||||
|
- Done via `SCR_ScriptRuntime_defineGlobalQObject` (`0x14082CB50`)
|
||||||
|
- Installs native global functions:
|
||||||
|
- `"include"` bound to `QS_include` (`0x1408246C0`)
|
||||||
|
- `"require"` bound to `QS_require` (`0x140827D60`)
|
||||||
|
- Done via `SCR_ScriptRuntime_defineGlobalFunction` (`0x14082CAC0`)
|
||||||
|
|
||||||
|
## Builtins: `include()` / `require()`
|
||||||
|
|
||||||
|
### `include(filename)` (`QS_include`, `0x1408246C0`)
|
||||||
|
|
||||||
|
Behavior (from decompilation):
|
||||||
|
|
||||||
|
- Validates `argumentCount == 1` and that the argument is a string.
|
||||||
|
- Retrieves the global `"___scriptManager___"` object, converts it to a `QObject*`, and `QMetaObject::cast`s it to the expected interface type (error message says “scene manager interface”).
|
||||||
|
- Calls a virtual function at vtable offset `+88` on that interface, passing the filename string.
|
||||||
|
- If a parent script context exists, it temporarily sets the current context's activation object and this-object to the parent context's ones while doing the include, then restores them.
|
||||||
|
|
||||||
|
### `require(filename)` (`QS_require`, `0x140827D60`)
|
||||||
|
|
||||||
|
Behavior (from decompilation):
|
||||||
|
|
||||||
|
- Validates `argumentCount == 1` and that the argument is a string.
|
||||||
|
- Retrieves/casts global `"___scriptManager___"` as above.
|
||||||
|
- Calls a virtual function at vtable offset `+96` to resolve the requested module path, then applies an OS remap (`oswRemapPath::RemapPath2`).
|
||||||
|
- If the resolved path is a directory, appends `"/index.js"`.
|
||||||
|
- Reads the file and wraps it into a JS closure of the form:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
(function()
|
||||||
|
{
|
||||||
|
var exports = {};
|
||||||
|
var __file__ = "<resolved path>";
|
||||||
|
// file contents
|
||||||
|
return exports;
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- Evaluates the wrapper; if it yields a function, calls it and returns the resulting `exports`.
|
||||||
|
- If the engine has an uncaught exception, returns the uncaught exception value.
|
||||||
|
|
||||||
|
## Helper routines used for global bindings
|
||||||
|
|
||||||
|
### Define global `QObject` (`SCR_ScriptRuntime_defineGlobalQObject`, `0x14082CB50`)
|
||||||
|
|
||||||
|
- Gets `engine->globalObject()`
|
||||||
|
- Wraps a `QObject*` using `QScriptEngine::newQObject(...)`
|
||||||
|
- Writes it into the global object with `QScriptValue::setProperty(...)`
|
||||||
|
- The property flags argument is passed as `2048` in the decompiler output.
|
||||||
|
|
||||||
|
### Define global native function (`SCR_ScriptRuntime_defineGlobalFunction`, `0x14082CAC0`)
|
||||||
|
|
||||||
|
- Gets `engine->globalObject()`
|
||||||
|
- Creates a `QScriptValue` function using `QScriptEngine::newFunction(callback)`
|
||||||
|
- Writes it into the global object via `setProperty(..., flags=2048)`
|
||||||
|
|
||||||
|
### Set `__file__` in scope chain (`SCR_ScriptRuntime_setScopeFileVar`, `0x14082CCB0`)
|
||||||
|
|
||||||
|
- Fetches `QScriptEngine::currentContext()->scopeChain()`
|
||||||
|
- Sets `scopeChain()[0].__file__ = <path>` (property flags passed as `2048`)
|
||||||
|
|
||||||
|
### Accessors used throughout registration
|
||||||
|
|
||||||
|
Several small helpers reveal how Harmony threads the live `QScriptEngine*` and other host pointers through its scripting subsystem. In decompiler output these are simple pointer-chasing accessors:
|
||||||
|
|
||||||
|
- `SCR_ScriptRuntime_getEngine` (`0x14082BCD0`)
|
||||||
|
- Returns `*(*a1 + 40)` (i.e., reads a `QScriptEngine*` stored at **byte offset `+40`** from the underlying script-engine wrapper object).
|
||||||
|
- `SCR_ScriptRuntime_getHostContext` (`0x14082CCA0`)
|
||||||
|
- Returns `*(*a1 + 24)` (a host/session/context pointer used to initialize many interface objects via a virtual method at vtable offset `+88`).
|
||||||
|
- `SCR_ScriptEngineImpl_setSceneInterface` (`0x14082CDA0`)
|
||||||
|
- Writes to `(*a1)+32`. Called from `SCR_ScriptManager_ctor` after interface registration; in the observed init flow it stores the singleton-like `"scene"` interface pointer (also injected as global `Scene`/`scene`).
|
||||||
|
|
||||||
|
## Why this matters for our injection goal
|
||||||
|
|
||||||
|
The above shows Harmony already has an established pattern for binding objects into the script global object (via `newQObject` + `setProperty`). The cleanest "natural" hook points to attach our own module object are:
|
||||||
|
|
||||||
|
- Right after `SCR_ScriptManager_ctor` injects `"___scriptManager___"` and registers `include/require`, or
|
||||||
|
- Anywhere we can get a pointer to the live `QScriptEngine` used for scripting (e.g. from a `SCR_ScriptEngineImpl` instance; other methods access it via the field at byte offset `+40`).
|
||||||
|
|
||||||
|
Next step: dig deeper into `SCR_Scripting_registerInterfaces` to map each global binding back to its concrete C++ type and owning subsystem (useful for choosing robust runtime hook points).
|
||||||
|
|
||||||
|
## Global namespace installed by Harmony
|
||||||
|
|
||||||
|
This section summarizes what `SCR_Scripting_registerInterfaces` (`0x140914850`) and `SCR_Scripting_registerUiTypes` (`0x140913920`) install into the engine.
|
||||||
|
|
||||||
|
### `SCR_Scripting_registerInterfaces` (`0x140914850`): core global objects
|
||||||
|
|
||||||
|
Direct `globalObject().setProperty(<name>, newQObject(...), flags=2048)` calls are visible in the decompilation for (non-exhaustive, but high-confidence) global names:
|
||||||
|
|
||||||
|
- `Scene` (capitalized; created from a singleton-like object named `"scene"`)
|
||||||
|
- `specialFolders`
|
||||||
|
- `exports` (set to a fresh script object created via `QScriptEngine::newObject`)
|
||||||
|
- `about`
|
||||||
|
- `Action` (registered at least once; later may be rebound if an action manager exists)
|
||||||
|
- `Drawing`
|
||||||
|
- `element`
|
||||||
|
- `fileMapper`
|
||||||
|
- `KeyModifiers`
|
||||||
|
- `MessageLog`
|
||||||
|
- `preferences`
|
||||||
|
- `scene` (lowercase; appears to alias the same underlying object as `Scene`)
|
||||||
|
- `System`
|
||||||
|
- `DrawingTools`
|
||||||
|
- `TimelineMarker`
|
||||||
|
- `Settings` (an instance of `SCR_SettingsInterface`)
|
||||||
|
- `column`
|
||||||
|
- `node`
|
||||||
|
- `selection`
|
||||||
|
- `PaletteObjectManager`
|
||||||
|
- `frame` (only when `WHO_Identity::family() != 4`)
|
||||||
|
- `compositionOrder` (backed by an object constructed with name `"composition"`)
|
||||||
|
- `copyPaste`
|
||||||
|
- `exporter`
|
||||||
|
- `func`
|
||||||
|
- `MovieImport`
|
||||||
|
- `render`
|
||||||
|
- `waypoint`
|
||||||
|
- `Backdrop`
|
||||||
|
- `sound`
|
||||||
|
- `stateUtil` (backed by an object constructed with name `"TB_StateUtil"`)
|
||||||
|
|
||||||
|
Also observed inside this function:
|
||||||
|
|
||||||
|
- Multiple `QMetaType` custom conversions registered via `QScriptEngine::registerCustomType(...)` (exact C++ types currently appear as `unk_140F7....` in the decompiler output).
|
||||||
|
- Additional interface initializers called (e.g. `SCR_DrawingKey::registerInterface(engine)`), plus many helper routines like `sub_14096.../sub_14097...` that likely register more types/enums.
|
||||||
|
|
||||||
|
### `SCR_Scripting_registerUiTypes` (`0x140913920`): UI/widget constructors and helpers
|
||||||
|
|
||||||
|
This function installs a number of `QMetaObject`-backed constructors into the global object using `newFunction(...)` + `newQMetaObject(...)` + `setProperty(..., flags=2048)`. Observed names include:
|
||||||
|
|
||||||
|
- `Dialog`
|
||||||
|
- `Label`
|
||||||
|
- `Button`
|
||||||
|
- `LineEdit`
|
||||||
|
- `NumberEdit`
|
||||||
|
- `DateEdit`
|
||||||
|
- `TimeEdit`
|
||||||
|
- `TextEdit`
|
||||||
|
- `SpinBox`
|
||||||
|
- `CheckBox`
|
||||||
|
- `RadioButton`
|
||||||
|
- `ComboBox`
|
||||||
|
- `GroupBox`
|
||||||
|
- `Slider`
|
||||||
|
- `ImportDrawingDlg` (conditional: `WHO_Identity::family() != 4`)
|
||||||
|
- `ExportVideoDlg` (conditional: `WHO_Identity::family() != 4`)
|
||||||
|
|
||||||
|
It also creates a `MessageBox` object with methods:
|
||||||
|
|
||||||
|
- `MessageBox.warning`
|
||||||
|
- `MessageBox.information`
|
||||||
|
- `MessageBox.critical`
|
||||||
|
|
||||||
|
And it registers several additional script-visible helpers:
|
||||||
|
|
||||||
|
- `DateEditEnum`
|
||||||
|
- `FileAccess`
|
||||||
|
- `DirSpec`
|
||||||
|
- `FileDialog` (conditional on an `a2` parameter in the decompiler output)
|
||||||
|
- `Input` (conditional on an `a2` parameter in the decompiler output)
|
||||||
|
|
||||||
|
|
||||||
7
docs/README.md
Normal file
7
docs/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
## Documents
|
||||||
|
|
||||||
|
- `HarmonyPremium_QtScript.md`: How HarmonyPremium wires up QtScript (`QScriptEngine`) and implements `include()` / `require()`, plus the main helper routines used to register C++ into the scripting global object.
|
||||||
|
- `ToonBoomLayout_Classes.md`: Detailed reconstruction of TULayoutView, TULayoutViewHolder, TULayoutFrame, TULayoutManager, TULayoutMainWindow, and TULayoutStorage classes from ToonBoomLayout.dll, including memory layouts, virtual tables, and method signatures.
|
||||||
|
- `ToonBoomLayout_ViewUsage.md`: Usage guide for creating and displaying custom TULayoutView instances with TUWidgetLayoutView, including code examples from HarmonyPremium.exe analysis.
|
||||||
|
|
||||||
|
|
||||||
516
docs/ToonBoomLayout_Classes.md
Normal file
516
docs/ToonBoomLayout_Classes.md
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
# ToonBoomLayout.dll Class Analysis
|
||||||
|
|
||||||
|
This document contains reverse engineering analysis of key classes from `ToonBoomLayout.dll` used in Toon Boom Harmony Premium and Storyboard Pro.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The layout system in Toon Boom uses a hierarchy of classes:
|
||||||
|
- **TULayoutView** - Base abstract class representing a view/panel that can be displayed
|
||||||
|
- **TULayoutViewHolder** - Widget container that can hold 1-2 TULayoutView instances with a splitter
|
||||||
|
- **TULayoutFrame** - Top-level frame (inherits QFrame) that contains tabs of view holders
|
||||||
|
|
||||||
|
## Class: TULayoutView
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Abstract base class for all layout views in Toon Boom applications. Represents a dockable/tabbable view panel.
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr | Virtual function table pointer |
|
||||||
|
| 0x08 | 24 | QString | m_internalName | Internal identifier (default: "View" + uniqueId) |
|
||||||
|
| 0x20 | 104 | LAY_ToolbarInfo | m_toolbarInfo | Toolbar configuration for this view |
|
||||||
|
| 0x88 | 16 | AC_Menu*[2] | m_menuByType | Menus indexed by MenuType enum |
|
||||||
|
| 0x98 | 1 | bool | m_initializedFromCopy | True if initialized via copy constructor |
|
||||||
|
| 0xA0 | 24 | QString | m_caption | User-visible caption/title |
|
||||||
|
|
||||||
|
**sizeof(TULayoutView) ≈ 0xB8 (184 bytes)**
|
||||||
|
|
||||||
|
### Virtual Function Table
|
||||||
|
|
||||||
|
| VTable Idx | Method | Signature |
|
||||||
|
|------------|--------|-----------|
|
||||||
|
| 0 | destructor | `virtual ~TULayoutView()` |
|
||||||
|
| 1 | (pure virtual) | `virtual QWidget* widget() = 0` |
|
||||||
|
| 2 | initiate | `virtual TULayoutView* initiate(QWidget* parent)` |
|
||||||
|
| 3 | (pure virtual) | Unknown |
|
||||||
|
| 4 | (pure virtual) | Unknown |
|
||||||
|
| 5 | getParentHolderWidget (const) | `virtual const TULayoutViewHolder* getParentHolderWidget() const` |
|
||||||
|
| 6 | getParentHolderWidget | `virtual TULayoutViewHolder* getParentHolderWidget()` |
|
||||||
|
| 7 | hasMenu | `virtual bool hasMenu()` |
|
||||||
|
| 8 | setMenu | `virtual void setMenu(AC_Manager*, const char*, MenuType)` |
|
||||||
|
| 9 | setMenu | `virtual void setMenu(AC_Menu*, MenuType)` |
|
||||||
|
| 10 | menu | `virtual AC_Menu* menu(MenuType)` |
|
||||||
|
| 11 | toolbar | `virtual QDomElement toolbar()` |
|
||||||
|
| 12 | setToolbarInfo | `virtual void setToolbarInfo(const LAY_ToolbarInfo&)` |
|
||||||
|
| 13-14 | (other virtuals) | Implementation-specific |
|
||||||
|
| 15 | initializedFromCopy | `virtual bool initializedFromCopy()` |
|
||||||
|
| 16 | getCaption | `virtual QString getCaption(bool includeAdvanced) const` |
|
||||||
|
| 17 | getDynamicTextForCaption | `virtual QString getDynamicTextForCaption() const` |
|
||||||
|
| 18 | wantEditionStack | `virtual bool wantEditionStack() const` |
|
||||||
|
| 19 | displayName | `virtual QString displayName() const` |
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
- **Constructor**: Generates unique internal name "View{N}" using static counter `TULayoutView::_uniqueId`
|
||||||
|
- **Copy Constructor**: Copies all members, sets `m_initializedFromCopy = true`
|
||||||
|
- **getCaption()**: Returns caption, optionally appending dynamic text in brackets if advanced display enabled
|
||||||
|
- **getParentHolderWidget()**: Traverses widget hierarchy to find parent TULayoutViewHolder
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: TULayoutViewHolder
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Container widget that can hold 1-2 TULayoutView instances with an optional splitter for split view.
|
||||||
|
|
||||||
|
### Inheritance
|
||||||
|
`QWidget` → `TULayoutViewHolder`
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr (QObject) | QObject vtable |
|
||||||
|
| 0x10 | 8 | ptr | vptr (QPaintDevice) | QPaintDevice vtable |
|
||||||
|
| 0x18-0x27 | | | (QWidget members) | Inherited from QWidget |
|
||||||
|
| 0x28 | 8 | ptr | m_views.begin | std::vector<TULayoutView*> start |
|
||||||
|
| 0x30 | 8 | ptr | m_views.end | std::vector<TULayoutView*> end |
|
||||||
|
| 0x38 | 8 | ptr | m_views.capacity | std::vector<TULayoutView*> capacity |
|
||||||
|
| 0x40 | 8 | double | m_savedSplitterRatio | Splitter ratio (default: 0.5) |
|
||||||
|
| 0x48 | 8 | ptr | m_splitter | UI_Splitter* (vertical orientation) |
|
||||||
|
| 0x50 | 8 | ptr | m_leftLayout | WID_VBoxLayout* for left side |
|
||||||
|
| 0x58 | 8 | ptr | m_rightLayout | WID_VBoxLayout* for right side |
|
||||||
|
| 0x60 | 8 | ptr | m_leftFrame | QFrame* for left pane |
|
||||||
|
| 0x68 | 8 | ptr | m_rightFrame | QFrame* for right pane |
|
||||||
|
|
||||||
|
**sizeof(TULayoutViewHolder) = 0x70 (112 bytes)**
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x180031150 | Constructor | Creates holder with parent widget, sets up splitter |
|
||||||
|
| 0x180031360 | Destructor | Frees view vector, destroys widget |
|
||||||
|
| 0x180031480 | addView | Adds a view (max 2), returns false if full |
|
||||||
|
| 0x180031610 | nbViews | Returns count of views in holder |
|
||||||
|
| 0x180031620 | removeView | Removes view from holder, adjusts layout |
|
||||||
|
| 0x180031850 | setFocusToChild | Sets focus to first view's widget |
|
||||||
|
| 0x180031890 | splitterRatio | Returns current splitter ratio |
|
||||||
|
| 0x1800319b0 | updateWidgets | Updates layout after view changes |
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
- When empty (0 views): Splitter hidden
|
||||||
|
- When 1 view: View added directly to main layout, splitter hidden
|
||||||
|
- When 2 views: First view in left frame, second in right frame, splitter visible
|
||||||
|
- Maximum capacity: 2 views (addView returns false if full)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: TULayoutFrame
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Top-level dockable/floatable frame that contains a tabbed interface of TULayoutViewHolder widgets.
|
||||||
|
|
||||||
|
### Inheritance
|
||||||
|
`QFrame` → `TULayoutFrame`
|
||||||
|
|
||||||
|
### Constructor Signature
|
||||||
|
```cpp
|
||||||
|
TULayoutFrame(
|
||||||
|
AC_Manager* manager, // Action/menu manager
|
||||||
|
QWidget* parent, // Parent widget
|
||||||
|
const QString& name, // Object name
|
||||||
|
TULayoutManager* layoutMgr, // Layout manager
|
||||||
|
bool floating, // True if floating window
|
||||||
|
bool unknown, // Unknown flag
|
||||||
|
bool singleViewMode // True for single view mode
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr (QObject) | QObject vtable |
|
||||||
|
| 0x10 | 8 | ptr | vptr (QPaintDevice) | QPaintDevice vtable |
|
||||||
|
| 0x18-0x27 | | | (QFrame members) | Inherited from QFrame |
|
||||||
|
| 0x28 | 8 | ptr | m_layoutManager | TULayoutManager* |
|
||||||
|
| 0x30 | 8 | ptr | m_mainLayout | WID_VBoxLayout* main vertical layout |
|
||||||
|
| 0x38 | 8 | ptr | m_topHBoxLayout | WID_HBoxLayout* top row |
|
||||||
|
| 0x40 | 8 | ptr | m_menuButton | QToolButton* "View Menu" button |
|
||||||
|
| 0x48 | 8 | ptr | m_mainWindow | TULayoutMainWindow* (contains toolbar area) |
|
||||||
|
| 0x50 | 8 | ptr | m_displayTools | TULayoutDisplayTools* display selector |
|
||||||
|
| 0x58 | 8 | ptr | m_compositeCombo | QComboBox* composite selector |
|
||||||
|
| 0x60 | 8 | ptr | m_tabBar | GUT_TabBar* tab bar |
|
||||||
|
| 0x68 | 8 | ptr | m_stack | QStackedWidget* for tab content |
|
||||||
|
| 0x70 | 8 | ptr | m_editionStackWidget | Symbol/edition stack widget |
|
||||||
|
| 0x78 | 8 | ptr | m_closeButton | QToolButton* close button |
|
||||||
|
| 0x80 | 8 | ptr | m_createViewMenuButton | QToolButton* "Create View Menu" |
|
||||||
|
| 0x88 | 1 | bool | m_isCurrent | Is this the current/focused frame |
|
||||||
|
| 0x89 | 1 | bool | m_isDocked | True if docked (not floating) |
|
||||||
|
| 0x8A | 1 | bool | m_isInSingleViewMode | Single view mode flag |
|
||||||
|
| 0x90 | 8 | ptr | m_toolbar | AC_Toolbar* view-specific toolbar |
|
||||||
|
| 0x98 | 4 | int | m_unknown | Initialized to -1 |
|
||||||
|
| 0xA0 | 16 | | m_toolbarInfoMap | Map/list for toolbar info per view |
|
||||||
|
| 0xB0 | 8 | ptr | m_parentWidget | Parent widget reference |
|
||||||
|
| 0xB8 | 8 | ptr | m_layoutFrameMenu | AC_Menu* context menu |
|
||||||
|
| 0xC0 | 8 | ptr | m_viewToolbar | AC_Toolbar* active toolbar |
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x180015DD0 | Constructor | Full initialization with all UI elements |
|
||||||
|
| 0x180016D10 | Destructor | Cleanup menu and toolbar info |
|
||||||
|
| 0x180010D60 | getLayoutManager | Returns m_layoutManager |
|
||||||
|
| 0x180010D70 | getStack | Returns m_stack (QStackedWidget) |
|
||||||
|
| 0x18001B660 | setCurrent | Sets m_isCurrent, triggers repaint |
|
||||||
|
| 0x180010DB0 | isDocked | Returns m_isDocked |
|
||||||
|
| 0x1800190C0 | isInSingleViewMode | Returns m_isInSingleViewMode |
|
||||||
|
| 0x180018F70 | getTab | Returns m_tabBar |
|
||||||
|
| 0x180018F80 | getToolbar | Returns m_viewToolbar |
|
||||||
|
| 0x180017FB0 | currentTab | Returns current tab index |
|
||||||
|
| 0x180017FC0 | currentTabName | Returns current tab name |
|
||||||
|
| 0x180018000 | currentTabViewHolder | Returns TULayoutViewHolder* for current tab |
|
||||||
|
| 0x1800172B0 | addTab | Adds tab with view holder |
|
||||||
|
| 0x1800180E0 | delTab | Removes tab by view holder |
|
||||||
|
| 0x180018170 | delView | Removes view widget |
|
||||||
|
| 0x180018280 | delViewHolder | Removes view holder |
|
||||||
|
| 0x18001B670 | setCurrentTab(int) | Sets current tab by index |
|
||||||
|
| 0x18001B6B0 | setCurrentTab(ViewHolder*) | Sets current tab by view holder |
|
||||||
|
| 0x18001C2B0 | updateTabName | Updates tab display text |
|
||||||
|
|
||||||
|
### Signals/Slots
|
||||||
|
|
||||||
|
Connected signals (from constructor analysis):
|
||||||
|
- `TULayoutDisplayTools::activated(int)` → `TULayoutFrame::onDisplayChanged(int)`
|
||||||
|
- `QComboBox::textActivated(QString)` → `TULayoutFrame::compositeChanged(QString)`
|
||||||
|
- `GUT_TabBar::clicked()` → `TULayoutFrame::updateFocus()`
|
||||||
|
- `GUT_TabBar::currentChanged(int)` → `TULayoutFrame::setCurrentTab(int)`
|
||||||
|
- `QToolButton::clicked()` → `TULayoutFrame::handleCloseViewButton()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supporting Class: LAY_ToolbarInfo
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Stores toolbar configuration and state for a view.
|
||||||
|
|
||||||
|
### Memory Layout
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 4 | int | m_x | X position |
|
||||||
|
| 0x04 | 4 | int | m_y | Y position |
|
||||||
|
| 0x08 | 4 | int | m_index | Toolbar index |
|
||||||
|
| 0x0C | 4 | int | m_width | Width |
|
||||||
|
| 0x10 | 4 | int | m_height | Height |
|
||||||
|
| 0x14 | 1 | bool | m_newline | Starts new row |
|
||||||
|
| 0x15 | 1 | bool | m_visible | Is visible |
|
||||||
|
| 0x16 | 1 | bool | m_isDefault | Is default config |
|
||||||
|
| 0x18 | 24 | QString | m_name | Toolbar name |
|
||||||
|
| 0x30 | 4 | Qt::Orientation | m_orientation | Horizontal/Vertical |
|
||||||
|
| 0x34 | 4 | Qt::ToolBarArea | m_toolBarArea | Dock area |
|
||||||
|
| 0x38 | 24 | QList<QString> | m_buttonConfig | Button configuration |
|
||||||
|
| 0x50 | 24 | QList<QString> | m_buttonDefaultConfig | Default button config |
|
||||||
|
|
||||||
|
**sizeof(LAY_ToolbarInfo) = 0x68 (104 bytes)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Analysis Methodology
|
||||||
|
|
||||||
|
1. **Constructor Analysis**: Traced member initialization order and sizes
|
||||||
|
2. **Accessor Analysis**: Used simple getter methods to confirm member offsets
|
||||||
|
3. **Destructor Analysis**: Verified cleanup order and dynamic allocations
|
||||||
|
4. **VTable Extraction**: Read vtable memory and resolved function addresses
|
||||||
|
5. **Signal/Slot Tracing**: Identified Qt connections in constructor
|
||||||
|
|
||||||
|
## Database Locations
|
||||||
|
|
||||||
|
- **TULayoutView vtable**: `0x180056F38`
|
||||||
|
- **TULayoutViewHolder vtable (QObject)**: `0x18005D600`
|
||||||
|
- **TULayoutViewHolder vtable (QPaintDevice)**: `0x18005D770`
|
||||||
|
- **TULayoutFrame vtable (QObject)**: `0x180058E18`
|
||||||
|
- **TULayoutFrame vtable (QPaintDevice)**: `0x180058F90`
|
||||||
|
- **TULayoutView::staticMetaObject**: N/A (not a QObject)
|
||||||
|
- **TULayoutViewHolder::staticMetaObject**: `0x1800646E0`
|
||||||
|
- **TULayoutFrame::staticMetaObject**: `0x180062AF0`
|
||||||
|
- **TULayoutManager vtable (QObject)**: `0x18005A1C8`
|
||||||
|
- **TULayoutManager vtable (TULayoutStorage)**: `0x18005A428`
|
||||||
|
- **TULayoutManager vtable (PLUG_ToolbarService)**: `0x18005A458`
|
||||||
|
- **TULayoutManager vtable (PLUG_MenuService)**: `0x18005A490`
|
||||||
|
- **TULayoutManager::staticMetaObject**: `0x180062BF0`
|
||||||
|
- **TULayoutMainWindow vtable**: `0x180058C60`
|
||||||
|
- **TUWidgetLayoutView vtable (QObject)**: `0x18005C0F8`
|
||||||
|
- **TUWidgetLayoutView vtable (QPaintDevice)**: `0x18005C268`
|
||||||
|
- **TUWidgetLayoutView vtable (AC_ResponderTemplateWidget)**: `0x18005C2A8`
|
||||||
|
- **TUWidgetLayoutView vtable (TULayoutView)**: `0x18005C348`
|
||||||
|
- **TUWidgetLayoutView::staticMetaObject**: `0x180064040`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: TULayoutManager
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Central manager for the entire layout system. Manages all frames, areas, splitters, toolbars, and layout persistence. Inherits from multiple base classes to provide comprehensive functionality.
|
||||||
|
|
||||||
|
### Inheritance
|
||||||
|
`QMainWindow` + `TULayoutStorage` + `PLUG_ToolbarService` + `PLUG_MenuService` → `TULayoutManager`
|
||||||
|
|
||||||
|
### Constructor Signature
|
||||||
|
```cpp
|
||||||
|
TULayoutManager(QString layoutPath);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr (QObject) | QObject vtable |
|
||||||
|
| 0x10 | 8 | ptr | vptr (QPaintDevice) | QPaintDevice vtable |
|
||||||
|
| 0x18-0x27 | | | (QMainWindow members) | Inherited from QMainWindow |
|
||||||
|
| 0x28 | 136 | TULayoutStorage | (base class) | Layout storage at +40 |
|
||||||
|
| 0xB0 | 8 | PLUG_ToolbarService | (base class) | Toolbar service interface |
|
||||||
|
| 0xB8 | 8 | PLUG_MenuService | (base class) | Menu service interface |
|
||||||
|
| 0xC0 | 1 | bool | m_unknown | Unknown flag |
|
||||||
|
| 0xC8 | 8 | ptr | m_private | TULayoutManager_Private* |
|
||||||
|
|
||||||
|
### TULayoutManager_Private Structure (0x110 = 272 bytes)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr | VTable pointer |
|
||||||
|
| 0x08-0x0F | | | (QObject members) | Inherited |
|
||||||
|
| 0x10 | 8 | ptr | m_mainFrame | QFrame* central frame |
|
||||||
|
| 0x18 | 8 | ptr | m_mainLayout | WID_VBoxLayout* |
|
||||||
|
| 0x20-0x5F | | | (reserved) | Initialized to null |
|
||||||
|
| 0x60 | 24 | vector | m_splitters | std::vector<TULayoutSplitter*> |
|
||||||
|
| 0x78 | 24 | vector | m_frames | std::vector<TULayoutFrame*> |
|
||||||
|
| 0x90 | 24 | vector | m_areas | std::vector<TULayoutArea*> |
|
||||||
|
| 0xA8 | 24 | vector | m_pluginAreas | std::vector<TULayoutArea*> |
|
||||||
|
| 0xC0 | 8 | QPoint | m_savedPos | Saved window position |
|
||||||
|
| 0xC8 | 8 | QSize | m_savedSize | Saved window size |
|
||||||
|
| 0xD0 | 4 | int | m_stateFlags | State flags |
|
||||||
|
| 0xD8 | 8 | ptr | m_currentLayoutFrame | TULayoutFrame* currently focused |
|
||||||
|
| 0xF0 | 8 | ptr | m_owner | TULayoutManager* back pointer |
|
||||||
|
| 0xF8 | 8 | ptr | m_actionManager | AC_Manager* action manager |
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x18001CBA0 | Constructor | Creates manager, initializes storage, creates private data |
|
||||||
|
| 0x18001D360 | Destructor | Cleans up all views, areas, frames |
|
||||||
|
| 0x18001DDB0 | addFrame(QString) | Creates new TULayoutFrame |
|
||||||
|
| 0x18001E030 | addFrame(QWidget*) | Creates frame with parent |
|
||||||
|
| 0x18001DB50 | addArea | Adds new TULayoutArea |
|
||||||
|
| 0x18001EC00 | addView | Adds view to frame |
|
||||||
|
| 0x180020130 | delFrame | Removes and deletes frame |
|
||||||
|
| 0x18001F4F0 | closeFrame | Closes frame, may delete |
|
||||||
|
| 0x180020D90 | findFrame(int) | Find frame by index |
|
||||||
|
| 0x180020DC0 | findFrame(QWidget*) | Find frame containing widget |
|
||||||
|
| 0x180020EA0 | findInstance | Find view instance by name |
|
||||||
|
| 0x180021FE0 | getCurrentLayoutFrame | Returns m_currentLayoutFrame |
|
||||||
|
| 0x1800220D0 | getMainFrame | Returns m_mainFrame |
|
||||||
|
| 0x1800220C0 | getFrames | Returns frames vector |
|
||||||
|
| 0x180021F40 | getAreas | Returns areas vector |
|
||||||
|
| 0x180022270 | getSplitters | Returns splitters vector |
|
||||||
|
| 0x1800241D0 | setCurrentLayoutFrame | Sets focused frame |
|
||||||
|
| 0x1800241C0 | setActionManager | Sets AC_Manager |
|
||||||
|
| 0x18001FE60 | currentView | Returns currently visible view |
|
||||||
|
| 0x18001F1C0 | changeLayout | Changes to named layout |
|
||||||
|
|
||||||
|
### Signals
|
||||||
|
|
||||||
|
- `backgroundImageChanged()`
|
||||||
|
- `fullScreenStateChanged()`
|
||||||
|
- `layoutChanged(TULayout*)`
|
||||||
|
- `preferencesChange()`
|
||||||
|
- `sceneSaved()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: TULayoutMainWindow
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Lightweight QMainWindow subclass used inside TULayoutFrame to provide toolbar docking area for view-specific toolbars.
|
||||||
|
|
||||||
|
### Inheritance
|
||||||
|
`QMainWindow` → `TULayoutMainWindow`
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr (QObject) | QObject vtable |
|
||||||
|
| 0x10 | 8 | ptr | vptr (QPaintDevice) | QPaintDevice vtable |
|
||||||
|
| 0x18-0x27 | | | (QMainWindow members) | Inherited |
|
||||||
|
| 0x28 | 8 | ptr | m_manager | AC_Manager* reference |
|
||||||
|
|
||||||
|
**sizeof(TULayoutMainWindow) = 0x30 (48 bytes)**
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- Created inside TULayoutFrame constructor
|
||||||
|
- Stores reference to AC_Manager for toolbar creation
|
||||||
|
- The QStackedWidget is set as central widget
|
||||||
|
- Provides toolbar docking areas (top, bottom, left, right)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: TULayoutStorage
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Base class providing layout persistence and toolbar configuration management.
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr | Virtual function table |
|
||||||
|
| 0x08 | 8 | ptr | m_currentLayout | TULayout* current active layout |
|
||||||
|
| 0x10 | 8 | ptr | m_previousLayout | TULayout* previous layout |
|
||||||
|
| 0x18 | 24 | vector | m_layouts | std::vector<TULayout*> all layouts |
|
||||||
|
| 0x30 | 24 | vector | m_toolbarLayouts | std::vector<TULayout*> toolbar layouts |
|
||||||
|
| 0x50 | 24 | QString | m_layoutPath | Path to layout storage |
|
||||||
|
| 0x68 | 8 | ptr | m_globalToolbarConfig | std::map for global toolbar configs |
|
||||||
|
| 0x70 | 8 | | (unused) | |
|
||||||
|
| 0x78 | 8 | ptr | m_viewToolbarConfig | std::map for view toolbar configs |
|
||||||
|
| 0x80 | 4 | int | m_flags | State flags |
|
||||||
|
|
||||||
|
**sizeof(TULayoutStorage) = 0x88 (136 bytes)**
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x18002B790 | Constructor | Initializes with layout path |
|
||||||
|
| 0x18002B890 | Destructor | Cleans up layouts and configs |
|
||||||
|
| 0x180009220 | getCurrentLayout | Returns m_currentLayout |
|
||||||
|
| 0x18002D0E0 | loadLayout | Loads layout from file |
|
||||||
|
| 0x18002E3B0 | loadLayouts | Loads all layouts from directory |
|
||||||
|
| 0x18002E8C0 | saveLayout | Saves layout to file |
|
||||||
|
| 0x18002C8F0 | findLayout | Finds layout by name |
|
||||||
|
| 0x18002BA70 | addLayout | Creates new layout |
|
||||||
|
| 0x18002C5D0 | delLayout | Deletes layout |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: TUWidgetLayoutView
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Primary concrete implementation of TULayoutView for wrapping QWidget content in the layout system. Inherits from `AC_ResponderTemplateWidget<QWidget>` and embeds `TULayoutView` at offset +104.
|
||||||
|
|
||||||
|
### Inheritance
|
||||||
|
`AC_ResponderTemplateWidget<QWidget>` + `TULayoutView` (embedded) → `TUWidgetLayoutView`
|
||||||
|
|
||||||
|
### Constructor Signature
|
||||||
|
```cpp
|
||||||
|
TUWidgetLayoutView::TUWidgetLayoutView(
|
||||||
|
AC_Manager* manager, // Action manager for menus/toolbars
|
||||||
|
const QString& viewName, // Internal name (e.g., "paletteView")
|
||||||
|
QWidget* parent, // Parent widget (usually nullptr)
|
||||||
|
const char* objectName, // QObject name (e.g., "PaletteLayoutView")
|
||||||
|
Qt::WindowFlags flags // Window flags (usually 0)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Size | Type | Member Name | Description |
|
||||||
|
|--------|------|------|-------------|-------------|
|
||||||
|
| 0x00 | 8 | ptr | vptr (QObject) | From AC_ResponderTemplateWidget<QWidget> |
|
||||||
|
| 0x10 | 8 | ptr | vptr (QPaintDevice) | |
|
||||||
|
| 0x18-0x27 | | | (QWidget members) | Inherited from QWidget |
|
||||||
|
| 0x28 | 8 | ptr | vptr (AC_ResponderTemplateWidget) | AC_Responder interface |
|
||||||
|
| 0x30 | 8 | ptr | m_actionManager | AC_Manager* |
|
||||||
|
| 0x38 | 24 | QString | m_responderIdentity | Responder ID |
|
||||||
|
| 0x50 | 24 | QString | m_responderDescription | Description |
|
||||||
|
| 0x68 | 8 | ptr | vptr (TULayoutView) | **TULayoutView interface starts here** |
|
||||||
|
| 0x70 | 24 | QString | m_internalName | Internal name from TULayoutView |
|
||||||
|
| 0x88 | 104 | LAY_ToolbarInfo | m_toolbarInfo | Toolbar info from TULayoutView |
|
||||||
|
| 0xF0 | 16 | AC_Menu*[2] | m_menuByType | Menus from TULayoutView |
|
||||||
|
| 0x100 | 1 | bool | m_initializedFromCopy | Copy flag from TULayoutView |
|
||||||
|
| 0x108 | 24 | QString | m_caption | Caption from TULayoutView |
|
||||||
|
|
||||||
|
**sizeof(TUWidgetLayoutView) ≈ 0x120 (288 bytes)**
|
||||||
|
|
||||||
|
### VTables
|
||||||
|
|
||||||
|
- **QObject vtable**: `0x18005C0F8`
|
||||||
|
- **QPaintDevice vtable**: `0x18005C268`
|
||||||
|
- **AC_ResponderTemplateWidget<QWidget> vtable**: `0x18005C2A8`
|
||||||
|
- **TULayoutView vtable**: `0x18005C348`
|
||||||
|
- **staticMetaObject**: `0x180064040`
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x1800300A0 | Constructor | Initializes widget, responder, and TULayoutView |
|
||||||
|
| 0x180030480 | Destructor | Destroys TULayoutView members, then base class |
|
||||||
|
| 0x180030E90 | getWidget() const | Returns `this - 104` (QWidget* from TULayoutView*) |
|
||||||
|
| 0x180030F10 | mousePressEvent | Calls base, sets focus if event not accepted |
|
||||||
|
| 0x180031140 | triggerMenuChanged | Emits menuChanged() signal |
|
||||||
|
| 0x18004A6D0 | menuChanged | Qt signal emission |
|
||||||
|
|
||||||
|
### Critical: The +104 Offset
|
||||||
|
|
||||||
|
When working with TUWidgetLayoutView and TULayoutManager:
|
||||||
|
|
||||||
|
1. **TULayoutView* is at offset +104 (0x68)** from TUWidgetLayoutView base
|
||||||
|
2. **getWidget() returns `this - 104`** when called on TULayoutView interface
|
||||||
|
3. When registering with `addArea`, pass `layoutView()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
TUWidgetLayoutView* widget = new MyCustomView(...);
|
||||||
|
TULayoutView* layoutView = widget->layoutView(); // returns this + 104
|
||||||
|
layoutManager->addArea("MyView", displayName, layoutView, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Constructor Analysis (0x1800300A0)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
TUWidgetLayoutView::TUWidgetLayoutView(
|
||||||
|
_QWORD* this, AC_Manager* manager, QString& viewName,
|
||||||
|
QWidget* parent, const char* objectName, int flags)
|
||||||
|
{
|
||||||
|
// Create objectName QString
|
||||||
|
QString objNameStr(objectName);
|
||||||
|
|
||||||
|
// Initialize AC_ResponderTemplateWidget<QWidget> base
|
||||||
|
AC_ResponderTemplateWidget<QWidget>::AC_ResponderTemplateWidget(
|
||||||
|
this, parent, objectName, objNameStr, flags);
|
||||||
|
|
||||||
|
// Initialize embedded TULayoutView at this+13*8 = this+104
|
||||||
|
TULayoutView::TULayoutView((TULayoutView*)(this + 13));
|
||||||
|
|
||||||
|
// Set up vtables
|
||||||
|
this[0] = &TUWidgetLayoutView::vftable_QObject;
|
||||||
|
this[2] = &TUWidgetLayoutView::vftable_QPaintDevice;
|
||||||
|
this[5] = &TUWidgetLayoutView::vftable_AC_ResponderTemplateWidget;
|
||||||
|
this[13] = &TUWidgetLayoutView::vftable_TULayoutView;
|
||||||
|
|
||||||
|
// Set minimum width
|
||||||
|
QWidget::setMinimumWidth(this, 150);
|
||||||
|
|
||||||
|
// Initialize action manager
|
||||||
|
if (parent && manager) {
|
||||||
|
AC_ResponderTemplateWidget::initActionManager(this, manager);
|
||||||
|
} else {
|
||||||
|
this[6] = manager; // m_actionManager at +0x30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Related Classes
|
||||||
|
|
||||||
|
- **TUVBoxLayoutView**: Similar to TUWidgetLayoutView but uses QVBoxLayout
|
||||||
|
- **TUScrollViewLayoutView**: Uses QScrollArea as base
|
||||||
|
- **TUTextEditLayoutView**: Uses QTextEdit as base
|
||||||
|
- **TUCanvasViewLayoutView**: Uses QGraphicsView as base
|
||||||
|
- **TUFrameLayoutView**: Uses QFrame as base
|
||||||
321
docs/ToonBoomLayout_ViewUsage.md
Normal file
321
docs/ToonBoomLayout_ViewUsage.md
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
# Creating and Displaying TULayoutView in Toon Boom
|
||||||
|
|
||||||
|
This document explains how to create custom views and display them in Toon Boom Harmony/Storyboard Pro using the TULayoutView system.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Toon Boom uses a hierarchical layout system:
|
||||||
|
1. **TULayoutManager** - Central manager for all views and frames
|
||||||
|
2. **TULayoutArea** - Metadata about a view type that can be instantiated
|
||||||
|
3. **TULayoutFrame** - A window/panel containing tabbed view holders
|
||||||
|
4. **TULayoutViewHolder** - Container holding 1-2 TULayoutView instances with optional splitter
|
||||||
|
5. **TULayoutView** - Abstract base class for actual view content
|
||||||
|
|
||||||
|
## Creating Custom Views by Subclassing TULayoutView
|
||||||
|
|
||||||
|
The recommended approach is to **directly subclass `TULayoutView`** and implement the required pure virtual methods.
|
||||||
|
|
||||||
|
### Critical Discovery: How widget() Works
|
||||||
|
|
||||||
|
**IMPORTANT**: The `widget()` method (vtable slot 1) is called by `TULayoutArea::add` and the return value is treated as a `TULayoutView*`, NOT a `QWidget*`. The actual displayable widget is obtained later via `getWidget()`.
|
||||||
|
|
||||||
|
This means:
|
||||||
|
- `widget()` should return `this` (the view itself, cast to QWidget*)
|
||||||
|
- `getWidget()` should return the actual QWidget for display
|
||||||
|
|
||||||
|
### Required Pure Virtual Methods
|
||||||
|
|
||||||
|
When subclassing `TULayoutView`, you must implement these 5 pure virtual methods:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Slot 1: Returns THIS view as the "widget" - treated as TULayoutView* by caller
|
||||||
|
virtual QWidget *widget() = 0;
|
||||||
|
|
||||||
|
// Slots 3-4: Return the actual displayable QWidget
|
||||||
|
virtual const QWidget *getWidget() const = 0;
|
||||||
|
virtual QWidget *getWidget() = 0;
|
||||||
|
|
||||||
|
// Slot 29: Called when menus need refresh
|
||||||
|
virtual void triggerMenuChanged() = 0;
|
||||||
|
|
||||||
|
// Slot 31 (protected): Marker method
|
||||||
|
virtual void isTULayoutView() = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Complete Custom View Implementation
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// test_frame.hpp
|
||||||
|
#pragma once
|
||||||
|
#include "toon_boom_layout.hpp"
|
||||||
|
|
||||||
|
class TestView : public TULayoutView {
|
||||||
|
public:
|
||||||
|
TestView();
|
||||||
|
~TestView() override;
|
||||||
|
|
||||||
|
// Pure virtuals - MUST implement all 5
|
||||||
|
QWidget *widget() override;
|
||||||
|
const QWidget *getWidget() const override;
|
||||||
|
QWidget *getWidget() override;
|
||||||
|
void triggerMenuChanged() override {}
|
||||||
|
|
||||||
|
// Override initiate to return this view
|
||||||
|
TULayoutView *initiate(QWidget *parent) override;
|
||||||
|
|
||||||
|
// Optional overrides
|
||||||
|
QString displayName() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void isTULayoutView() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFrame *m_frame; // The actual widget content
|
||||||
|
QVBoxLayout *m_mainLayout;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// test_frame.cpp
|
||||||
|
#include "test_frame.hpp"
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
|
||||||
|
TestView::TestView()
|
||||||
|
: TULayoutView() {
|
||||||
|
// Create the frame that will hold our content
|
||||||
|
m_frame = new QFrame();
|
||||||
|
m_frame->setMinimumSize(400, 300);
|
||||||
|
m_mainLayout = new QVBoxLayout(m_frame);
|
||||||
|
|
||||||
|
QLabel *label = new QLabel("Hello from custom view!");
|
||||||
|
label->setAlignment(Qt::AlignCenter);
|
||||||
|
m_mainLayout->addWidget(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *TestView::widget() {
|
||||||
|
// CRITICAL: TULayoutArea::add calls this via vtable[1] and expects
|
||||||
|
// a TULayoutView* return value, NOT QWidget*. The returned pointer
|
||||||
|
// is then used to call getWidget() for the actual widget.
|
||||||
|
// So we return `this` which IS-A TULayoutView*.
|
||||||
|
return reinterpret_cast<QWidget*>(static_cast<TULayoutView*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QWidget *TestView::getWidget() const {
|
||||||
|
return m_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *TestView::getWidget() {
|
||||||
|
return m_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
TULayoutView *TestView::initiate(QWidget *parent) {
|
||||||
|
// Return this view - it's already initialized
|
||||||
|
if (parent && m_frame) {
|
||||||
|
m_frame->setParent(parent);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TestView::displayName() const {
|
||||||
|
return QString("My Custom View");
|
||||||
|
}
|
||||||
|
|
||||||
|
TestView::~TestView() {
|
||||||
|
delete m_frame;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Registering Views with TULayoutManager
|
||||||
|
|
||||||
|
### TULayoutManager::addArea Signature
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool TULayoutManager::addArea(
|
||||||
|
const char* typeName, // Type identifier (e.g., "Colour", "Node View")
|
||||||
|
const QString& displayName, // Translated display name
|
||||||
|
TULayoutView* view, // View instance (your TestView*)
|
||||||
|
bool visible, // Initially visible
|
||||||
|
bool createFrame, // Create new frame for this view
|
||||||
|
bool docked, // Is docked (not floating)
|
||||||
|
const QSize& minSize, // Minimum size
|
||||||
|
bool useMinSize, // Whether to use minimum size
|
||||||
|
bool isPlugin, // Is this a plugin area
|
||||||
|
bool defaultVisible, // Default visibility state
|
||||||
|
bool unknown // Unknown flag (usually true)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complete Registration Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void showCustomView() {
|
||||||
|
auto lm = PLUG_Services::getLayoutManager();
|
||||||
|
if (!lm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create your custom view - it's a direct TULayoutView subclass
|
||||||
|
TestView* myView = new TestView();
|
||||||
|
|
||||||
|
// Register with the layout manager
|
||||||
|
bool success = lm->addArea(
|
||||||
|
"TestView", // typeName (unique ID)
|
||||||
|
QString("My Test View"), // displayName
|
||||||
|
myView, // TULayoutView* - pass directly, no offset needed!
|
||||||
|
true, // visible
|
||||||
|
true, // createFrame
|
||||||
|
true, // docked
|
||||||
|
QSize(500, 400), // minSize
|
||||||
|
true, // useMinSize
|
||||||
|
false, // isPlugin
|
||||||
|
true, // defaultVisible
|
||||||
|
true // unknown
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
// Raise the view to show it
|
||||||
|
auto area = lm->findArea(QString("TestView"));
|
||||||
|
if (area) {
|
||||||
|
lm->raiseArea(area, nullptr, false, QPoint(100, 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Call Flow Explained
|
||||||
|
|
||||||
|
When you call `TULayoutManager::addArea`, the following happens:
|
||||||
|
|
||||||
|
1. **addArea** stores your `TULayoutView*` in a new `TULayoutArea`
|
||||||
|
2. When the view needs to be displayed, **TULayoutArea::add** is called
|
||||||
|
3. `add` calls `view->widget()` (vtable slot 1) - **expects TULayoutView* return!**
|
||||||
|
4. The return value is passed to **TULayoutViewHolder::addView**
|
||||||
|
5. `addView` calls `view->getWidget()` to get the actual QWidget
|
||||||
|
6. The QWidget is reparented and displayed
|
||||||
|
|
||||||
|
This is why `widget()` must return `this` - the calling code treats the return as `TULayoutView*` to make further virtual calls.
|
||||||
|
|
||||||
|
## Opening Views at Runtime
|
||||||
|
|
||||||
|
To programmatically show a view that's already registered:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Using TULayoutManager::raiseArea
|
||||||
|
TULayoutView* view = layoutManager->raiseArea(
|
||||||
|
QString("TestView"), // Area name
|
||||||
|
targetFrame, // TULayoutFrame* (or nullptr for current)
|
||||||
|
true, // Create new instance if needed
|
||||||
|
QPoint(0, 0) // Position hint
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Patterns
|
||||||
|
|
||||||
|
### 1. DPI Scaling
|
||||||
|
|
||||||
|
For views with minimum size requirements, use `UT_DPI::scale()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
QSize baseSize(260, 450);
|
||||||
|
QSize scaledSize = UT_DPI::scale(baseSize);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Common View Sizes (from HarmonyPremium analysis)
|
||||||
|
|
||||||
|
| View Type | Base Size |
|
||||||
|
|-----------|-----------|
|
||||||
|
| Colour | 260 × 450 |
|
||||||
|
| Coord. And Control Points | 260 × 300 |
|
||||||
|
| Layer Properties | 260 × 450 |
|
||||||
|
| Onion Skin | 480 × 300 |
|
||||||
|
| Timeline | 800 × 400 |
|
||||||
|
| Tool Properties | 260 × 450 |
|
||||||
|
| Top | 300 × 400 |
|
||||||
|
| Camera, Drawing, Node View, etc. | 0 × 0 (no minimum) |
|
||||||
|
|
||||||
|
### 3. Menu Registration
|
||||||
|
|
||||||
|
Set menus on views using `TULayoutView::setMenu`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
TULayoutView::setMenu(
|
||||||
|
layoutView,
|
||||||
|
actionManager, // AC_Manager*
|
||||||
|
"MENU_ID", // Menu identifier
|
||||||
|
MenuType::Primary // 0 = Primary, 1 = Secondary
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## TULayoutViewHolder Usage
|
||||||
|
|
||||||
|
The `TULayoutViewHolder` is a QWidget container that can hold 1-2 TULayoutView instances with an optional vertical splitter.
|
||||||
|
|
||||||
|
### How Views Get Into TULayoutViewHolder
|
||||||
|
|
||||||
|
When you call `TULayoutManager::addArea` or `TULayoutManager::raiseArea`, the system:
|
||||||
|
|
||||||
|
1. Creates a `TULayoutFrame` if needed
|
||||||
|
2. Creates a `TULayoutViewHolder` within the frame
|
||||||
|
3. Calls `TULayoutViewHolder::addView(view, splitterRatio)` to add your view
|
||||||
|
4. The view's `getWidget()` method is called to get the actual QWidget
|
||||||
|
5. The widget is reparented and added to the holder's internal layout
|
||||||
|
|
||||||
|
### TULayoutViewHolder::addView
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool TULayoutViewHolder::addView(
|
||||||
|
TULayoutView* view, // View to add
|
||||||
|
double splitterRatio // Ratio for splitter (default 0.5)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `true` if added successfully, `false` if holder is full (max 2 views)
|
||||||
|
|
||||||
|
### Internal Structure (from ToonBoomLayout.dll)
|
||||||
|
|
||||||
|
```
|
||||||
|
TULayoutViewHolder (sizeof = 0x70 = 112 bytes)
|
||||||
|
├── QWidget base class (0x00-0x27)
|
||||||
|
├── std::vector<TULayoutView*> m_views (+0x28, 24 bytes)
|
||||||
|
├── double m_savedSplitterRatio (+0x40, default 0.5)
|
||||||
|
├── UI_Splitter* m_splitter (+0x48, vertical orientation)
|
||||||
|
├── WID_VBoxLayout* m_leftLayout (+0x50)
|
||||||
|
├── WID_VBoxLayout* m_rightLayout (+0x58)
|
||||||
|
├── QFrame* m_leftFrame (+0x60)
|
||||||
|
└── QFrame* m_rightFrame (+0x68)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
- **0 views**: Splitter hidden, empty container
|
||||||
|
- **1 view**: View widget added directly to main layout, splitter hidden
|
||||||
|
- **2 views**: First view in left frame, second in right frame, splitter visible
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. **Direct Subclassing**: Subclass `TULayoutView` directly - no need for complex multiple inheritance.
|
||||||
|
|
||||||
|
2. **widget() Returns this**: The `widget()` method must return `this` (cast to QWidget*) because the calling code treats it as `TULayoutView*`.
|
||||||
|
|
||||||
|
3. **getWidget() Returns Content**: The `getWidget()` method returns the actual displayable QWidget.
|
||||||
|
|
||||||
|
4. **TULayoutManager Access**: Get the TULayoutManager via `PLUG_Services::getLayoutManager()`.
|
||||||
|
|
||||||
|
5. **Memory Management**: Views registered with `addArea` are managed by the layout system. Don't delete them manually.
|
||||||
|
|
||||||
|
## Database Locations (HarmonyPremium.exe)
|
||||||
|
|
||||||
|
- **TULayoutManager::addArea import**: `0x140b22668`
|
||||||
|
- **TULayoutManager::raiseArea import**: `0x140b22a18`
|
||||||
|
- **View creation function (example)**: `0x1400375C0` (main session init)
|
||||||
|
|
||||||
|
## Database Locations (ToonBoomLayout.dll)
|
||||||
|
|
||||||
|
- **TULayoutView vtable**: `0x180056f38`
|
||||||
|
- **TULayoutView constructor**: `0x18002fc80`
|
||||||
|
- **TULayoutViewHolder constructor**: `0x180031150`
|
||||||
|
- **TULayoutViewHolder::addView**: `0x180031480`
|
||||||
|
- **TULayoutViewHolder::removeView**: `0x180031620`
|
||||||
|
- **TULayoutViewHolder::nbViews**: `0x180031610`
|
||||||
|
- **TULayoutViewHolder::splitterRatio**: `0x180031890`
|
||||||
|
- **TULayoutViewHolder::updateWidgets**: `0x1800319b0`
|
||||||
335
docs/ToonBoomPlugInManager_PLUG_Services.md
Normal file
335
docs/ToonBoomPlugInManager_PLUG_Services.md
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
# ToonBoomPlugInManager.dll - PLUG_Services Class Analysis
|
||||||
|
|
||||||
|
This document contains reverse engineering analysis of the `PLUG_Services` class hierarchy from `ToonBoomPlugInManager.dll` used in Toon Boom Harmony Premium and Storyboard Pro.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The plugin services system in Toon Boom uses a singleton pattern to provide access to various application services. The main classes are:
|
||||||
|
|
||||||
|
- **PLUG_Services** - Static class providing access to service interfaces
|
||||||
|
- **PLUG_ServicesPrivate** - Extended static functionality with setters
|
||||||
|
- **PLUG_ServicesPrivateImpl** - (alias name, same as PLUG_ManagerImpl)
|
||||||
|
- **PLUG_ManagerImpl** - Main singleton implementation containing all service pointers
|
||||||
|
- **PLUG_Manager** - Base class for the manager
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
PLUG_Services (static class)
|
||||||
|
│
|
||||||
|
│ getters return from
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ PLUG_ManagerImpl (singleton) │
|
||||||
|
│ (inherits: QObject → PLUG_Manager) │
|
||||||
|
│ │
|
||||||
|
│ +0x000: vftable (PLUG_ManagerImpl) │
|
||||||
|
│ +0x010: embedded interface object │
|
||||||
|
│ +0x188: QCoreApplication* │
|
||||||
|
│ +0x190: unknown interface │
|
||||||
|
│ +0x198-0x240: Service interface pointers │
|
||||||
|
│ +0x248: QString │
|
||||||
|
│ +0x260: bool flag │
|
||||||
|
└──────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌────────────────┼────────────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
AC_Manager* CM_Services* PLUG_ScriptingInterface*
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Global Variables
|
||||||
|
|
||||||
|
| Address | Name | Type | Description |
|
||||||
|
|---------|------|------|-------------|
|
||||||
|
| 0x180016590 | g_PLUG_ManagerImpl_instance | PLUG_ManagerImpl* | Main singleton instance |
|
||||||
|
| 0x180016570 | g_PLUG_ModuleLibraryInterface | PLUG_ModuleLibraryInterface* | Module library (separate global) |
|
||||||
|
| 0x180016578 | g_PLUG_SetupModeQueryInterface | PLUG_SetupModeQueryInterface* | Setup mode query interface |
|
||||||
|
| 0x180016580 | g_PLUG_PlaybackRange | PLUG_PlaybackRange | Static playback range instance |
|
||||||
|
| 0x180016538 | g_PLUG_InteractiveViewManagerImpl | PLUG_InteractiveViewManagerImpl* | Interactive view manager singleton |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_Services
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Static utility class providing access to all plugin service interfaces. All methods are static.
|
||||||
|
|
||||||
|
### Public Static Methods
|
||||||
|
|
||||||
|
| Address | Method | Return Type | Offset in Singleton |
|
||||||
|
|---------|--------|-------------|---------------------|
|
||||||
|
| 0x180005a50 | getActionManager() | AC_Manager* | +0x1A0 (416) |
|
||||||
|
| 0x180005a70 | getColorManagerServices() | CM_Services* | +0x218 (536) |
|
||||||
|
| 0x180005a90 | getCurrentFrameInterface() | SC_CurrentFrameInterface* | +0x1B0 (432) |
|
||||||
|
| 0x180005ab0 | getDataToolInterface() | SC_CVDataToolInterface* | +0x220 (544) |
|
||||||
|
| 0x180005ad0 | getDragDropInterface() | PLUG_DragDropInterface* | +0x1F8 (504) |
|
||||||
|
| 0x180005af0 | getEditionStackInterface() | SC_SceneEditionStackInterface* | +0x1B8 (440) |
|
||||||
|
| 0x180005b10 | getExpressionScriptingInterface() | AT_ExprScriptEngine* | +0x200 (512)* |
|
||||||
|
| 0x180005b40 | getHttpAPI() | SC_HttpAPI* | +0x228 (552) |
|
||||||
|
| 0x180005b60 | getImportEngine() | PLUG_ImportEngine* | +0x1E0 (480) |
|
||||||
|
| 0x180005b80 | getInteractiveRenderManager() | SC_InteractiveRenderManagerInterface* | +0x198 (408) |
|
||||||
|
| 0x180005ba0 | getInteractiveViewManager() | PLUG_InteractiveViewManager* | +0x210 (528)** |
|
||||||
|
| 0x180005bf0 | getKeyStateInterface() | PLUG_KeyStateInterface* | +0x1E8 (488) |
|
||||||
|
| 0x180005c10 | getLayoutManager() | TULayoutManager* | +0x1F0 (496) |
|
||||||
|
| 0x180005c30 | getMenuService() | PLUG_MenuService* | +0x1D8 (472) |
|
||||||
|
| 0x180005c50 | getModuleLibraryInterface() | PLUG_ModuleLibraryInterface* | (separate global) |
|
||||||
|
| 0x180005c60 | getNetworkViewInterface() | SC_NetworkViewInterface* | +0x240 (576) |
|
||||||
|
| 0x180005c80 | getOGLRenderPlaybackInterface() | PLUG_OGLRenderPlaybackInterface* | +0x230 (560) |
|
||||||
|
| 0x180005d00 | getPluginPath(const QString&) | QString | via vtable |
|
||||||
|
| 0x180005d50 | getPreference() | PLUG_PreferenceUI* | (thunk to PLUG_PreferenceUIImpl::instance) |
|
||||||
|
| 0x180005d60 | getScriptingInterface() | PLUG_ScriptingInterface* | +0x1C8 (456) |
|
||||||
|
| 0x180005d80 | getSelection() | SL_Selection* | +0x1A8 (424) |
|
||||||
|
| 0x180005da0 | getSessionContext() | SC_SessionContext* | +0x1C0 (448) |
|
||||||
|
| 0x180005dc0 | getToolbarService() | PLUG_ToolbarService* | +0x1D0 (464) |
|
||||||
|
| 0x180005de0 | getVectorizationInterface() | PLUG_VectorizationInterface* | +0x238 (568) |
|
||||||
|
| 0x180005e00 | getWidgetFactoryRegistry() | PLUG_WidgetFactoryRegistry* | +0x208 (520) |
|
||||||
|
|
||||||
|
\* `getExpressionScriptingInterface()` requires `SC_SessionContext` at +0x1C0 to be non-null
|
||||||
|
\*\* `getInteractiveViewManager()` lazily creates `PLUG_InteractiveViewManagerImpl` if null
|
||||||
|
|
||||||
|
### Implementation Pattern
|
||||||
|
|
||||||
|
Most getters follow this pattern:
|
||||||
|
```cpp
|
||||||
|
Type* PLUG_Services::getXXX() {
|
||||||
|
if (g_PLUG_ManagerImpl_instance)
|
||||||
|
return g_PLUG_ManagerImpl_instance->m_xxx; // at specific offset
|
||||||
|
return nullptr; // returns 0 if singleton not initialized
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_ServicesPrivate
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Extended static class with additional functionality and setters (typically used internally).
|
||||||
|
|
||||||
|
### Public Static Methods
|
||||||
|
|
||||||
|
| Address | Method | Return Type | Description |
|
||||||
|
|---------|--------|-------------|-------------|
|
||||||
|
| 0x180005cb0 | getPluginBinFilePath(const QString&) | QString | Gets binary file path for plugin |
|
||||||
|
| 0x180005fa0 | isSetupMode() | bool | Queries setup mode via g_PLUG_SetupModeQueryInterface |
|
||||||
|
| 0x180007390 | setModuleLibraryInterface(PLUG_ModuleLibraryInterface*) | bool | Sets g_PLUG_ModuleLibraryInterface |
|
||||||
|
| 0x1800073f0 | setSetupModeQueryInterface(PLUG_SetupModeQueryInterface*) | void | Sets g_PLUG_SetupModeQueryInterface |
|
||||||
|
| 0x1800072D0 | setColorManagerServices(CM_Services*) | void | Sets +0x218 offset |
|
||||||
|
| 0x1800072F0 | setDataToolInterface(SC_CVDataToolInterface*) | void | Sets +0x220 offset |
|
||||||
|
| 0x180007330 | setHttpAPI(SC_HttpAPI*) | void | Sets +0x228 offset |
|
||||||
|
| 0x1800073B0 | setOGLRenderPlaybackInterface(PLUG_OGLRenderPlaybackInterface*) | void | Sets +0x230 offset |
|
||||||
|
| 0x180007410 | setVectorizationInterface(PLUG_VectorizationInterface*) | void | Sets +0x238 offset |
|
||||||
|
| 0x180007420 | setWidgetFactoryRegistry(PLUG_WidgetFactoryRegistry*) | void | Sets +0x208 offset |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_ManagerImpl
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Main singleton implementation that holds all service interface pointers. Inherits from QObject and PLUG_Manager.
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
**Address**: `0x180004180`
|
||||||
|
|
||||||
|
**Signature**:
|
||||||
|
```cpp
|
||||||
|
PLUG_ManagerImpl(QObject* parent, UnknownInterface* iface1, UnknownInterface* iface2);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Layout (x64 MSVC)
|
||||||
|
|
||||||
|
| Offset | Hex | Size | Type | Member Name | Description |
|
||||||
|
|--------|-----|------|------|-------------|-------------|
|
||||||
|
| 0 | 0x000 | 8 | ptr | vftable | PLUG_ManagerImpl vtable |
|
||||||
|
| 8 | 0x008 | 8 | | (QObject data) | d_ptr from QObject |
|
||||||
|
| 16 | 0x010 | 8 | ptr | embedded_vftable | Embedded interface vtable |
|
||||||
|
| 24 | 0x018 | 72 | | (reserved) | Zeroed on construction |
|
||||||
|
| 96 | 0x060 | 304 | | (reserved block) | memset to 0, size 0x130 |
|
||||||
|
| 392 | 0x188 | 8 | ptr | m_coreApp | QCoreApplication* instance |
|
||||||
|
| 400 | 0x190 | 8 | ptr | m_unknownInterface | Constructor param a3 |
|
||||||
|
| 408 | 0x198 | 8 | ptr | m_interactiveRenderManager | SC_InteractiveRenderManagerInterface* |
|
||||||
|
| 416 | 0x1A0 | 8 | ptr | m_actionManager | AC_Manager* |
|
||||||
|
| 424 | 0x1A8 | 8 | ptr | m_selection | SL_Selection* |
|
||||||
|
| 432 | 0x1B0 | 8 | ptr | m_currentFrameInterface | SC_CurrentFrameInterface* |
|
||||||
|
| 440 | 0x1B8 | 8 | ptr | m_editionStackInterface | SC_SceneEditionStackInterface* |
|
||||||
|
| 448 | 0x1C0 | 8 | ptr | m_sessionContext | SC_SessionContext* |
|
||||||
|
| 456 | 0x1C8 | 8 | ptr | m_scriptingInterface | PLUG_ScriptingInterface* |
|
||||||
|
| 464 | 0x1D0 | 8 | ptr | m_toolbarService | PLUG_ToolbarService* |
|
||||||
|
| 472 | 0x1D8 | 8 | ptr | m_menuService | PLUG_MenuService* |
|
||||||
|
| 480 | 0x1E0 | 8 | ptr | m_importEngine | PLUG_ImportEngine* |
|
||||||
|
| 488 | 0x1E8 | 8 | ptr | m_keyStateInterface | PLUG_KeyStateInterface* |
|
||||||
|
| 496 | 0x1F0 | 8 | ptr | m_layoutManager | TULayoutManager* |
|
||||||
|
| 504 | 0x1F8 | 8 | ptr | m_dragDropInterface | PLUG_DragDropInterface* |
|
||||||
|
| 512 | 0x200 | 8 | ptr | m_exprScriptEngine | AT_ExprScriptEngine* |
|
||||||
|
| 520 | 0x208 | 8 | ptr | m_widgetFactoryRegistry | PLUG_WidgetFactoryRegistry* |
|
||||||
|
| 528 | 0x210 | 8 | ptr | m_interactiveViewManager | PLUG_InteractiveViewManager* |
|
||||||
|
| 536 | 0x218 | 8 | ptr | m_colorManagerServices | CM_Services* |
|
||||||
|
| 544 | 0x220 | 8 | ptr | m_dataToolInterface | SC_CVDataToolInterface* |
|
||||||
|
| 552 | 0x228 | 8 | ptr | m_httpAPI | SC_HttpAPI* |
|
||||||
|
| 560 | 0x230 | 8 | ptr | m_oglRenderPlaybackInterface | PLUG_OGLRenderPlaybackInterface* |
|
||||||
|
| 568 | 0x238 | 8 | ptr | m_vectorizationInterface | PLUG_VectorizationInterface* |
|
||||||
|
| 576 | 0x240 | 8 | ptr | m_networkViewInterface | SC_NetworkViewInterface* |
|
||||||
|
| 584 | 0x248 | 24 | QString | m_unknownString | QString member |
|
||||||
|
| 608 | 0x260 | 1 | bool | m_flag | Initialized to true |
|
||||||
|
|
||||||
|
**sizeof(PLUG_ManagerImpl) ≈ 0x268 (616 bytes minimum)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_Manager
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Base class for the manager singleton.
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
**Address**: `0x180001210`
|
||||||
|
|
||||||
|
### Memory Layout
|
||||||
|
| Offset | Size | Type | Description |
|
||||||
|
|--------|------|------|-------------|
|
||||||
|
| 0 | 8 | ptr | vftable (PLUG_Manager) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_ScriptingInterface
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Interface for scripting functionality. Base class for script execution.
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
**Address**: `0x180009cd0`
|
||||||
|
|
||||||
|
### Memory Layout
|
||||||
|
| Offset | Size | Type | Description |
|
||||||
|
|--------|------|------|-------------|
|
||||||
|
| 0 | 8 | ptr | vftable |
|
||||||
|
|
||||||
|
### Nested Struct: Program
|
||||||
|
|
||||||
|
Represents a script program to be executed.
|
||||||
|
|
||||||
|
**Memory Layout**:
|
||||||
|
| Offset | Size | Type | Member | Description |
|
||||||
|
|--------|------|------|--------|-------------|
|
||||||
|
| 0 | 24 | QString | path | Script path/name |
|
||||||
|
| 24 | 24 | QString | description | Script description |
|
||||||
|
| 48 | 24 | QString | content | Script content |
|
||||||
|
| 72 | 8+ | QDateTime | timestamp | Modification timestamp |
|
||||||
|
|
||||||
|
**sizeof(PLUG_ScriptingInterface::Program) ≈ 80+ bytes**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_InteractiveViewManager / PLUG_InteractiveViewManagerImpl
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages interactive view delegates for drawing and tool handling.
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
**Address**: `0x180001ef0`
|
||||||
|
|
||||||
|
### Memory Layout
|
||||||
|
| Offset | Size | Type | Description |
|
||||||
|
|--------|------|------|-------------|
|
||||||
|
| 0 | 8 | ptr | vftable |
|
||||||
|
| 8 | 8 | ptr | m_delegateList (linked list) |
|
||||||
|
| 16 | 8 | ptr | m_delegate2 |
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x180003060 | instance() | Returns singleton from g_PLUG_InteractiveViewManagerImpl |
|
||||||
|
| 0x180003180 | registerDelegate(PLUG_InteractiveViewDelegate*) | Registers a view delegate |
|
||||||
|
| 0x180003320 | unregisterDelegate(PLUG_InteractiveViewDelegate*) | Unregisters a view delegate |
|
||||||
|
| 0x1800030a0 | isDelegateTypeRegistered(PLUG_InteractiveViewDelegate*) | Checks if delegate type exists |
|
||||||
|
| 0x180003070 | invalidateAllViews(int) | Invalidates all views |
|
||||||
|
| 0x180003080 | invalidateTimeline() | Invalidates timeline view |
|
||||||
|
| 0x180002e20 | handleMouseDown(...) | Handles mouse down events |
|
||||||
|
| 0x180002ee0 | handleMouseMove(...) | Handles mouse move events |
|
||||||
|
| 0x180002fa0 | handleMouseUp(...) | Handles mouse up events |
|
||||||
|
| 0x180002d60 | handleGetCursor(...) | Gets cursor for position |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class: PLUG_PreferenceUI / PLUG_PreferenceUIImpl
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages preference panels and UI customization.
|
||||||
|
|
||||||
|
### Singleton Access
|
||||||
|
**Address**: `0x180007a20` (PLUG_PreferenceUIImpl::instance)
|
||||||
|
|
||||||
|
Uses thread-local storage for singleton initialization with `Init_thread_header`/`Init_thread_footer` pattern.
|
||||||
|
|
||||||
|
### Key Methods
|
||||||
|
|
||||||
|
| Address | Method | Description |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| 0x180007970 | addCustomizer(const PLUG_PreferenceUICustomizerInterface*) | Adds preference customizer |
|
||||||
|
| 0x180007aa0 | onCreateColorPreferencePanel(eAppContext, QWidget*) | Creates color pref panel |
|
||||||
|
| 0x180007b20 | onCreatePreferencePanel(eAppContext, QWidget*) | Creates general pref panel |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Service Interface Types Reference
|
||||||
|
|
||||||
|
### Action & Menu System
|
||||||
|
- **AC_Manager** - Action/command manager
|
||||||
|
- **PLUG_MenuService** - Menu service interface
|
||||||
|
- **PLUG_ToolbarService** - Toolbar service interface
|
||||||
|
|
||||||
|
### Scene & Session
|
||||||
|
- **SC_SessionContext** - Current session context
|
||||||
|
- **SC_CurrentFrameInterface** - Current frame access
|
||||||
|
- **SC_SceneEditionStackInterface** - Scene edition stack
|
||||||
|
- **SC_NetworkViewInterface** - Network view interface
|
||||||
|
- **SC_HttpAPI** - HTTP API for server communication
|
||||||
|
|
||||||
|
### Drawing & Rendering
|
||||||
|
- **SC_InteractiveRenderManagerInterface** - Render management
|
||||||
|
- **SC_CVDataToolInterface** - Drawing data tool interface
|
||||||
|
- **CM_Services** - Color manager services
|
||||||
|
- **PLUG_VectorizationInterface** - Vectorization tools
|
||||||
|
- **PLUG_OGLRenderPlaybackInterface** - OpenGL render playback
|
||||||
|
|
||||||
|
### Selection & Layout
|
||||||
|
- **SL_Selection** - Selection management
|
||||||
|
- **TULayoutManager** - Layout system manager
|
||||||
|
- **PLUG_InteractiveViewManager** - Interactive view management
|
||||||
|
|
||||||
|
### Scripting
|
||||||
|
- **PLUG_ScriptingInterface** - Script execution interface
|
||||||
|
- **AT_ExprScriptEngine** - Expression script engine
|
||||||
|
|
||||||
|
### Import/Export
|
||||||
|
- **PLUG_ImportEngine** - Import functionality
|
||||||
|
- **PLUG_ModuleLibraryInterface** - Module library access
|
||||||
|
|
||||||
|
### Input & UI
|
||||||
|
- **PLUG_KeyStateInterface** - Keyboard state tracking
|
||||||
|
- **PLUG_DragDropInterface** - Drag and drop handling
|
||||||
|
- **PLUG_WidgetFactoryRegistry** - Widget factory registration
|
||||||
|
- **PLUG_PreferenceUI** - Preference panels
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
- **TOONBOOM_PLUGINPATH** - Overrides plugin search path (checked in `sub_180005380`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Analysis Methodology
|
||||||
|
|
||||||
|
1. **Function List Analysis**: Extracted all functions matching `*PLUG_Services*` pattern
|
||||||
|
2. **Decompilation**: Analyzed each getter method to determine offsets in singleton
|
||||||
|
3. **Constructor Analysis**: Traced `PLUG_ManagerImpl` constructor to map memory layout
|
||||||
|
4. **Cross-Reference Analysis**: Found setters and initialization points via xrefs to globals
|
||||||
|
5. **Pattern Recognition**: Identified common getter/setter patterns
|
||||||
|
6. **Global Identification**: Named and documented global singleton variables
|
||||||
|
|
||||||
|
## Database File
|
||||||
|
- **Source**: `RE/ToonBoomPlugInManager.dll.i64`
|
||||||
|
- **Module Base**: `0x180000000`
|
||||||
56
framework/CMakeLists.txt
Normal file
56
framework/CMakeLists.txt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
file(GLOB_RECURSE FRAMEWORK_SOURCES CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.cc"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.cxx"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
|
||||||
|
)
|
||||||
|
file(GLOB_RECURSE FRAMEWORK_HOOK_SOURCES CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hook/*.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hook/*.cc"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hook/*.cxx"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hook/*.cpp"
|
||||||
|
)
|
||||||
|
file(GLOB_RECURSE FRAMEWORK_HEADERS CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.h"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.hpp"
|
||||||
|
)
|
||||||
|
list(FILTER FRAMEWORK_SOURCES EXCLUDE REGEX "/(out|build|cmake-build-|CMakeFiles)/")
|
||||||
|
|
||||||
|
file(COPY "${QT5_ROOT_DIR}/include/QtScript" DESTINATION ${CMAKE_BINARY_DIR}/include)
|
||||||
|
find_package(minhook CONFIG REQUIRED)
|
||||||
|
function(link_libs_and_set_properties target_name)
|
||||||
|
target_compile_features(${target_name} PUBLIC cxx_std_20)
|
||||||
|
target_include_directories(${target_name} PUBLIC "${CMAKE_BINARY_DIR}/include")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/QtScript.lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/ToonBoomActionManager.lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/ToonBoomLayout.lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/ToonBoomPluginManager.lib")
|
||||||
|
|
||||||
|
target_include_directories(${target_name} PUBLIC "${QT6_ROOT_DIR}/include")
|
||||||
|
target_include_directories(${target_name} PUBLIC "${QT6_ROOT_DIR}/include/QtGui")
|
||||||
|
|
||||||
|
target_include_directories(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/toon_boom")
|
||||||
|
target_include_directories(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/hooks")
|
||||||
|
target_include_directories(${target_name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/internal")
|
||||||
|
|
||||||
|
target_link_directories(${target_name} PUBLIC "${QT6_ROOT_DIR}/lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${QT6_ROOT_DIR}/lib/Qt6Core.lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${QT6_ROOT_DIR}/lib/Qt6Gui.lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${QT6_ROOT_DIR}/lib/Qt6Widgets.lib")
|
||||||
|
target_link_libraries(${target_name} PUBLIC "${QT6_ROOT_DIR}/lib/Qt6Core5Compat.lib")
|
||||||
|
target_compile_options(${target_name} PUBLIC "/EHsc")
|
||||||
|
target_link_libraries(${target_name} PUBLIC minhook::minhook)
|
||||||
|
set_target_properties(${target_name} PROPERTIES
|
||||||
|
AUTOMOC ON
|
||||||
|
AUTOUIC ON
|
||||||
|
AUTORCC ON
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_library(libtoonboom_objs OBJECT ${FRAMEWORK_SOURCES} ${FRAMEWORK_HEADERS} ${FRAMEWORK_HOOK_SOURCES})
|
||||||
|
set_property(TARGET libtoonboom_objs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
|
add_library(libtoonboom_static STATIC $<TARGET_OBJECTS:libtoonboom_objs>)
|
||||||
|
add_library(libtoonboom SHARED $<TARGET_OBJECTS:libtoonboom_objs>)
|
||||||
|
link_libs_and_set_properties(libtoonboom_objs)
|
||||||
|
link_libs_and_set_properties(libtoonboom_static)
|
||||||
|
link_libs_and_set_properties(libtoonboom)
|
||||||
199
framework/hook/harmony_signatures.cpp
Normal file
199
framework/hook/harmony_signatures.cpp
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#include "harmony_signatures.hpp"
|
||||||
|
|
||||||
|
#include "sigscan.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace toon_boom_module::harmony {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::size_t count_forward(const std::byte* p, const std::byte* end, std::uint8_t value) {
|
||||||
|
std::size_t n = 0;
|
||||||
|
while (p < end && static_cast<std::uint8_t>(*p) == value) {
|
||||||
|
++n;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t count_backward(const std::byte* begin, const std::byte* p, std::uint8_t value) {
|
||||||
|
std::size_t n = 0;
|
||||||
|
while (p > begin && static_cast<std::uint8_t>(p[-1]) == value) {
|
||||||
|
++n;
|
||||||
|
--p;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool looks_like_function_boundary(const std::byte* text_begin,
|
||||||
|
std::size_t text_size,
|
||||||
|
const std::byte* match_addr,
|
||||||
|
std::size_t pattern_size) {
|
||||||
|
const auto* text_end = text_begin + text_size;
|
||||||
|
if (match_addr < text_begin || match_addr + pattern_size > text_end) return false;
|
||||||
|
|
||||||
|
// For HarmonyPremium's SCR_ScriptRuntime_getEngine thunk, IDA shows:
|
||||||
|
// 48 8B 01 48 8B 40 28 C3 CC CC CC ...
|
||||||
|
// So: require a run of int3 padding immediately after the ret.
|
||||||
|
constexpr std::size_t kMinCcAfter = 4;
|
||||||
|
|
||||||
|
const auto* after = match_addr + pattern_size;
|
||||||
|
if (after >= text_end) return false;
|
||||||
|
|
||||||
|
const auto cc_after = count_forward(after, text_end, 0xCC);
|
||||||
|
if (cc_after < kMinCcAfter) return false;
|
||||||
|
|
||||||
|
// Optional: also prefer that the match is preceded by at least one 0xCC,
|
||||||
|
// unless it happens to be at the start of the section.
|
||||||
|
if (match_addr != text_begin) {
|
||||||
|
const auto cc_before = count_backward(text_begin, match_addr, 0xCC);
|
||||||
|
if (cc_before == 0) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FunctionRange {
|
||||||
|
std::uintptr_t begin{};
|
||||||
|
std::uintptr_t end{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<FunctionRange> function_range_from_unwind(HMODULE target_module, std::uintptr_t addr) {
|
||||||
|
if (!target_module) return std::nullopt;
|
||||||
|
|
||||||
|
DWORD64 image_base = 0;
|
||||||
|
const auto* rf = ::RtlLookupFunctionEntry(static_cast<DWORD64>(addr), &image_base, nullptr);
|
||||||
|
if (!rf || image_base == 0) return std::nullopt;
|
||||||
|
|
||||||
|
// BeginAddress/EndAddress are RVAs from image_base.
|
||||||
|
const auto begin = static_cast<std::uintptr_t>(image_base + rf->BeginAddress);
|
||||||
|
const auto end = static_cast<std::uintptr_t>(image_base + rf->EndAddress);
|
||||||
|
if (begin >= end) return std::nullopt;
|
||||||
|
|
||||||
|
return FunctionRange{begin, end};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t> find_SCR_ScriptRuntime_getEngine(HMODULE target_module) {
|
||||||
|
// Exact bytes from IDA at HarmonyPremium.exe:0x14082BCD0:
|
||||||
|
// 48 8B 01 48 8B 40 28 C3
|
||||||
|
constexpr std::string_view kPattern = "48 8B 01 48 8B 40 28 C3";
|
||||||
|
|
||||||
|
auto text = toon_boom_module::sigscan::get_pe_section(target_module, ".text");
|
||||||
|
if (!text) return std::nullopt;
|
||||||
|
|
||||||
|
const auto pat = toon_boom_module::sigscan::parse_ida_pattern(kPattern);
|
||||||
|
auto matches = toon_boom_module::sigscan::find_all(*text, pat);
|
||||||
|
if (matches.empty()) return std::nullopt;
|
||||||
|
|
||||||
|
// Filter for plausible function boundaries to reduce collisions with other
|
||||||
|
// identical byte sequences embedded in the middle of code.
|
||||||
|
std::vector<const std::byte*> filtered;
|
||||||
|
filtered.reserve(matches.size());
|
||||||
|
for (const auto* m : matches) {
|
||||||
|
if (looks_like_function_boundary(text->begin, text->size, m, pat.bytes.size())) {
|
||||||
|
filtered.push_back(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtered.size() != 1) return std::nullopt;
|
||||||
|
return reinterpret_cast<std::uintptr_t>(filtered[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t> find_SCR_ScriptManager_ctor(HMODULE target_module) {
|
||||||
|
// This is a mid-function signature extracted from HarmonyPremium.exe around:
|
||||||
|
// QString("___scriptManager___"); defineGlobalQObject(...)
|
||||||
|
// QString("include"); defineGlobalFunction(QS_include)
|
||||||
|
// QString("require"); defineGlobalFunction(QS_require)
|
||||||
|
//
|
||||||
|
// RIP-relative displacements and call targets are wildcarded.
|
||||||
|
//
|
||||||
|
// Source bytes were pulled from IDA around 0x14081FEE0.
|
||||||
|
constexpr std::string_view kPattern =
|
||||||
|
"48 8B 18 "
|
||||||
|
"48 8D 15 ?? ?? ?? ?? "
|
||||||
|
"48 8D 4C 24 30 "
|
||||||
|
"FF 15 ?? ?? ?? ?? "
|
||||||
|
"90 "
|
||||||
|
"4C 8B C6 "
|
||||||
|
"48 8D 54 24 30 "
|
||||||
|
"48 8B CB "
|
||||||
|
"E8 ?? ?? ?? ?? "
|
||||||
|
"90 "
|
||||||
|
"48 8D 4C 24 30 "
|
||||||
|
"FF 15 ?? ?? ?? ?? "
|
||||||
|
"48 8B 46 20 "
|
||||||
|
"48 8B 18 "
|
||||||
|
"48 8D 15 ?? ?? ?? ?? "
|
||||||
|
"48 8D 4C 24 30 "
|
||||||
|
"FF 15 ?? ?? ?? ?? "
|
||||||
|
"90 "
|
||||||
|
"4C 8D 05 ?? ?? ?? ?? "
|
||||||
|
"48 8D 54 24 30 "
|
||||||
|
"48 8B CB "
|
||||||
|
"E8 ?? ?? ?? ?? "
|
||||||
|
"90 "
|
||||||
|
"48 8D 4C 24 30 "
|
||||||
|
"FF 15 ?? ?? ?? ?? "
|
||||||
|
"48 8B 46 20 "
|
||||||
|
"48 8B 18 "
|
||||||
|
"48 8D 15 ?? ?? ?? ?? "
|
||||||
|
"48 8D 4C 24 30 "
|
||||||
|
"FF 15 ?? ?? ?? ?? "
|
||||||
|
"90 "
|
||||||
|
"4C 8D 05 ?? ?? ?? ?? "
|
||||||
|
"48 8D 54 24 30 "
|
||||||
|
"48 8B CB "
|
||||||
|
"E8 ?? ?? ?? ?? "
|
||||||
|
"90 "
|
||||||
|
"48 8D 4C 24 30 "
|
||||||
|
"FF 15 ?? ?? ?? ??";
|
||||||
|
|
||||||
|
auto text = toon_boom_module::sigscan::get_pe_section(target_module, ".text");
|
||||||
|
if (!text) return std::nullopt;
|
||||||
|
|
||||||
|
const auto pat = toon_boom_module::sigscan::parse_ida_pattern(kPattern);
|
||||||
|
auto hits = toon_boom_module::sigscan::find_all(*text, pat);
|
||||||
|
if (hits.empty()) return std::nullopt;
|
||||||
|
|
||||||
|
// Convert each hit to its containing function start via unwind info, and
|
||||||
|
// keep only plausible ctor-sized functions (~0x280 in the analyzed build).
|
||||||
|
constexpr std::size_t kMinSize = 0x200;
|
||||||
|
constexpr std::size_t kMaxSize = 0x400;
|
||||||
|
|
||||||
|
std::vector<std::uintptr_t> candidates;
|
||||||
|
candidates.reserve(hits.size());
|
||||||
|
|
||||||
|
for (const auto* hit : hits) {
|
||||||
|
const auto hit_addr = reinterpret_cast<std::uintptr_t>(hit);
|
||||||
|
auto fr = function_range_from_unwind(target_module, hit_addr);
|
||||||
|
if (!fr) continue;
|
||||||
|
|
||||||
|
const auto size = static_cast<std::size_t>(fr->end - fr->begin);
|
||||||
|
if (size < kMinSize || size > kMaxSize) continue;
|
||||||
|
|
||||||
|
// Ensure the function is inside .text.
|
||||||
|
const auto text_begin = reinterpret_cast<std::uintptr_t>(text->begin);
|
||||||
|
const auto text_end = text_begin + text->size;
|
||||||
|
if (fr->begin < text_begin || fr->end > text_end) continue;
|
||||||
|
|
||||||
|
candidates.push_back(fr->begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.empty()) return std::nullopt;
|
||||||
|
|
||||||
|
std::sort(candidates.begin(), candidates.end());
|
||||||
|
candidates.erase(std::unique(candidates.begin(), candidates.end()), candidates.end());
|
||||||
|
|
||||||
|
if (candidates.size() != 1) return std::nullopt;
|
||||||
|
return candidates[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace toon_boom_module::harmony
|
||||||
|
|
||||||
|
|
||||||
82
framework/hook/hook.cpp
Normal file
82
framework/hook/hook.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include "../include/hooks/toon_boom_hooks.hpp"
|
||||||
|
#include "../include/internal/harmony_signatures.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
QScriptEngine *global_engine_ptr = NULL;
|
||||||
|
bool is_first_load = true;
|
||||||
|
SCR_ScriptManager_ctor_t SCR_ScriptManager_ctor_original_ptr = NULL;
|
||||||
|
std::vector<ScriptEngine_hook_t> script_engine_hooks;
|
||||||
|
|
||||||
|
void *SCR_ScriptManager_ctor_hook(void *_this, void *_engine, void *_parent) {
|
||||||
|
std::cout << "SCR_ScriptManager_ctor_hook" << std::endl;
|
||||||
|
void *result = SCR_ScriptManager_ctor_original_ptr(_this, _engine, _parent);
|
||||||
|
HMODULE target_module = GetModuleHandle(NULL);
|
||||||
|
std::optional<std::uintptr_t> SCR_ScripRuntime_getEngine_original =
|
||||||
|
toon_boom_module::harmony::find_SCR_ScriptRuntime_getEngine(
|
||||||
|
target_module);
|
||||||
|
if (SCR_ScripRuntime_getEngine_original == std::nullopt) {
|
||||||
|
std::cerr << "Failed to find SCR_ScriptRuntime_getEngine" << std::endl;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
auto SCR_ScripRuntime_getEngine_original_ptr = reinterpret_cast<SCR_ScriptRuntime_getEngine_t>(SCR_ScripRuntime_getEngine_original.value());
|
||||||
|
|
||||||
|
void* mgr_data = *reinterpret_cast<void**>(reinterpret_cast<std::byte*>(_this) + 0x20);
|
||||||
|
if (!mgr_data) {
|
||||||
|
std::cerr << "SCR_ScriptManager data pointer was null" << std::endl;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
void* runtime_handle = *reinterpret_cast<void**>(mgr_data);
|
||||||
|
if (!runtime_handle) {
|
||||||
|
std::cerr << "SCR_ScriptManager runtime handle was null" << std::endl;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
QScriptEngine* engine = SCR_ScripRuntime_getEngine_original_ptr(runtime_handle);
|
||||||
|
if (!engine) {
|
||||||
|
std::cerr << "SCR_ScriptRuntime_getEngine returned null" << std::endl;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
global_engine_ptr = engine;
|
||||||
|
for(auto hook : script_engine_hooks) {
|
||||||
|
hook(engine);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add_ScriptEngine_hook(ScriptEngine_hook_t hook) {
|
||||||
|
script_engine_hooks.push_back(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL hookInit() {
|
||||||
|
if(!is_first_load) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if(MH_Initialize() != MH_OK) {
|
||||||
|
std::cerr << "Failed to initialize MinHook" << std::endl;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
auto scr_ScriptManager_ctor_ptr = toon_boom_module::harmony::find_SCR_ScriptManager_ctor(GetModuleHandle(NULL));
|
||||||
|
if(scr_ScriptManager_ctor_ptr == std::nullopt) {
|
||||||
|
std::cerr << "Failed to find SCR_ScriptManager_ctor" << std::endl;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
auto SCR_ScriptManager_ctor_original_ptr_val = reinterpret_cast<SCR_ScriptManager_ctor_t>(scr_ScriptManager_ctor_ptr.value());
|
||||||
|
MH_STATUS status = MH_CreateHook(
|
||||||
|
reinterpret_cast<LPVOID>(SCR_ScriptManager_ctor_original_ptr_val),
|
||||||
|
reinterpret_cast<LPVOID>(&SCR_ScriptManager_ctor_hook),
|
||||||
|
reinterpret_cast<LPVOID *>(&SCR_ScriptManager_ctor_original_ptr));
|
||||||
|
if(status != MH_OK) {
|
||||||
|
std::cerr << "Failed to create hook for SCR_ScriptManager_ctor" << std::endl;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
status = MH_EnableHook(MH_ALL_HOOKS);
|
||||||
|
if(status != MH_OK) {
|
||||||
|
std::cerr << "Failed to enable hooks" << std::endl;
|
||||||
|
MH_RemoveHook(reinterpret_cast<LPVOID>(SCR_ScriptManager_ctor_original_ptr_val));
|
||||||
|
MH_Uninitialize();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
std::cout << "Hooks initialized and enabled" << std::endl;
|
||||||
|
is_first_load = false;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
130
framework/hook/sigscan.cpp
Normal file
130
framework/hook/sigscan.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "../include/internal/sigscan.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace toon_boom_module::sigscan {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool is_hex_digit(char c) {
|
||||||
|
return std::isxdigit(static_cast<unsigned char>(c)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t hex_byte_from_2chars(char hi, char lo) {
|
||||||
|
auto nybble = [](char c) -> std::uint8_t {
|
||||||
|
if (c >= '0' && c <= '9') return static_cast<std::uint8_t>(c - '0');
|
||||||
|
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||||
|
if (c >= 'a' && c <= 'f') return static_cast<std::uint8_t>(10 + (c - 'a'));
|
||||||
|
throw std::invalid_argument("invalid hex digit");
|
||||||
|
};
|
||||||
|
|
||||||
|
return static_cast<std::uint8_t>((nybble(hi) << 4) | nybble(lo));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string_view> split_ws(std::string_view s) {
|
||||||
|
std::vector<std::string_view> out;
|
||||||
|
std::size_t i = 0;
|
||||||
|
while (i < s.size()) {
|
||||||
|
while (i < s.size() && std::isspace(static_cast<unsigned char>(s[i]))) ++i;
|
||||||
|
if (i >= s.size()) break;
|
||||||
|
std::size_t j = i;
|
||||||
|
while (j < s.size() && !std::isspace(static_cast<unsigned char>(s[j]))) ++j;
|
||||||
|
out.emplace_back(s.substr(i, j - i));
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Pattern parse_ida_pattern(std::string_view ida_pattern) {
|
||||||
|
Pattern p;
|
||||||
|
|
||||||
|
auto toks = split_ws(ida_pattern);
|
||||||
|
p.bytes.reserve(toks.size());
|
||||||
|
p.mask.reserve(toks.size());
|
||||||
|
|
||||||
|
for (auto tok : toks) {
|
||||||
|
if (tok == "?" || tok == "??") {
|
||||||
|
p.bytes.push_back(0);
|
||||||
|
p.mask.push_back(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok.size() != 2 || !is_hex_digit(tok[0]) || !is_hex_digit(tok[1])) {
|
||||||
|
throw std::invalid_argument("invalid IDA pattern token: expected 2 hex chars or ??");
|
||||||
|
}
|
||||||
|
|
||||||
|
p.bytes.push_back(hex_byte_from_2chars(tok[0], tok[1]));
|
||||||
|
p.mask.push_back(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.bytes.empty()) {
|
||||||
|
throw std::invalid_argument("empty pattern");
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<SectionView> get_pe_section(HMODULE module, std::string_view section_name) {
|
||||||
|
if (!module) return std::nullopt;
|
||||||
|
if (section_name.empty() || section_name.size() > 8) return std::nullopt;
|
||||||
|
|
||||||
|
const auto base = reinterpret_cast<const std::byte*>(module);
|
||||||
|
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
||||||
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return std::nullopt;
|
||||||
|
|
||||||
|
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
|
||||||
|
if (nt->Signature != IMAGE_NT_SIGNATURE) return std::nullopt;
|
||||||
|
|
||||||
|
const IMAGE_SECTION_HEADER* sec = IMAGE_FIRST_SECTION(nt);
|
||||||
|
for (unsigned i = 0; i < nt->FileHeader.NumberOfSections; ++i) {
|
||||||
|
char name_buf[9] = {};
|
||||||
|
std::memcpy(name_buf, sec[i].Name, 8);
|
||||||
|
if (section_name == name_buf) {
|
||||||
|
const auto* begin = base + sec[i].VirtualAddress;
|
||||||
|
const auto size = static_cast<std::size_t>(sec[i].Misc.VirtualSize);
|
||||||
|
return SectionView{begin, size};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const std::byte*> find_all(SectionView region, const Pattern& pat) {
|
||||||
|
std::vector<const std::byte*> matches;
|
||||||
|
|
||||||
|
if (!region.begin || region.size == 0) return matches;
|
||||||
|
if (pat.bytes.size() != pat.mask.size()) return matches;
|
||||||
|
if (pat.bytes.empty()) return matches;
|
||||||
|
if (region.size < pat.bytes.size()) return matches;
|
||||||
|
|
||||||
|
const auto* hay = reinterpret_cast<const std::uint8_t*>(region.begin);
|
||||||
|
const auto hay_size = region.size;
|
||||||
|
const auto n = pat.bytes.size();
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i + n <= hay_size; ++i) {
|
||||||
|
bool ok = true;
|
||||||
|
for (std::size_t j = 0; j < n; ++j) {
|
||||||
|
if (pat.mask[j] && hay[i + j] != pat.bytes[j]) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) matches.push_back(region.begin + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<const std::byte*> find_unique(SectionView region, const Pattern& pat) {
|
||||||
|
auto all = find_all(region, pat);
|
||||||
|
if (all.size() != 1) return std::nullopt;
|
||||||
|
return all[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace toon_boom_module::sigscan
|
||||||
|
|
||||||
|
|
||||||
15
framework/include/hooks/toon_boom_hooks.hpp
Normal file
15
framework/include/hooks/toon_boom_hooks.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <MinHook.h>
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
typedef QScriptEngine* (__stdcall *SCR_ScriptRuntime_getEngine_t)(void*);
|
||||||
|
|
||||||
|
typedef void* (__stdcall *SCR_ScriptManager_ctor_t)(void*, void*, void*);
|
||||||
|
|
||||||
|
|
||||||
|
QScriptEngine* SCR_ScriptRuntime_getEngine(void*);
|
||||||
|
|
||||||
|
typedef void (__stdcall *ScriptEngine_hook_t)(QScriptEngine*);
|
||||||
|
|
||||||
|
__declspec(dllexport) void Add_ScriptEngine_hook(ScriptEngine_hook_t hook);
|
||||||
|
__declspec(dllexport) BOOL hookInit();
|
||||||
33
framework/include/internal/harmony_signatures.hpp
Normal file
33
framework/include/internal/harmony_signatures.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace toon_boom_module::harmony {
|
||||||
|
|
||||||
|
// Returns the address of HarmonyPremium's internal helper:
|
||||||
|
// QScriptEngine* SCR_ScriptRuntime_getEngine(SCR_ScriptRuntime* rt)
|
||||||
|
//
|
||||||
|
// This is resolved by scanning the target module's .text section for the exact
|
||||||
|
// machine-code bytes observed in IDA:
|
||||||
|
// 48 8B 01 48 8B 40 28 C3
|
||||||
|
//
|
||||||
|
// If the pattern is not found uniquely, returns std::nullopt.
|
||||||
|
std::optional<std::uintptr_t> find_SCR_ScriptRuntime_getEngine(HMODULE target_module);
|
||||||
|
|
||||||
|
// Returns the address of HarmonyPremium's SCR_ScriptManager constructor.
|
||||||
|
//
|
||||||
|
// Resolution strategy:
|
||||||
|
// - Scan .text for a unique mid-function sequence that:
|
||||||
|
// - constructs QString("___scriptManager___") then calls defineGlobalQObject
|
||||||
|
// - constructs QString("include") then calls defineGlobalFunction(QS_include)
|
||||||
|
// - constructs QString("require") then calls defineGlobalFunction(QS_require)
|
||||||
|
// - Convert the match address to the containing function start using x64 unwind
|
||||||
|
// metadata via RtlLookupFunctionEntry, and sanity-check the function size.
|
||||||
|
std::optional<std::uintptr_t> find_SCR_ScriptManager_ctor(HMODULE target_module);
|
||||||
|
|
||||||
|
} // namespace toon_boom_module::harmony
|
||||||
|
|
||||||
|
|
||||||
40
framework/include/internal/sigscan.hpp
Normal file
40
framework/include/internal/sigscan.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace toon_boom_module::sigscan {
|
||||||
|
|
||||||
|
struct Pattern {
|
||||||
|
std::vector<std::uint8_t> bytes; // pattern bytes (wildcard bytes may be any value)
|
||||||
|
std::vector<bool> mask; // true = match byte, false = wildcard
|
||||||
|
};
|
||||||
|
|
||||||
|
// IDA-style pattern string parser. Examples:
|
||||||
|
// - "48 8B 01 48 8B 40 28 C3"
|
||||||
|
// - "48 8B ?? ?? 89"
|
||||||
|
Pattern parse_ida_pattern(std::string_view ida_pattern);
|
||||||
|
|
||||||
|
struct SectionView {
|
||||||
|
const std::byte* begin{};
|
||||||
|
std::size_t size{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reads a PE section by name (e.g. ".text") from a loaded module.
|
||||||
|
std::optional<SectionView> get_pe_section(HMODULE module, std::string_view section_name);
|
||||||
|
|
||||||
|
// Returns all matches in the provided memory region.
|
||||||
|
std::vector<const std::byte*> find_all(SectionView region, const Pattern& pat);
|
||||||
|
|
||||||
|
// Returns the single match or std::nullopt (0 or >1 matches).
|
||||||
|
std::optional<const std::byte*> find_unique(SectionView region, const Pattern& pat);
|
||||||
|
|
||||||
|
} // namespace toon_boom_module::sigscan
|
||||||
|
|
||||||
|
|
||||||
453
framework/include/toon_boom/PLUG_Services.hpp
Normal file
453
framework/include/toon_boom/PLUG_Services.hpp
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
/**
|
||||||
|
* @file PLUG_Services.hpp
|
||||||
|
* @brief Reconstructed header for Toon Boom PLUG_Services class hierarchy
|
||||||
|
*
|
||||||
|
* This header was reverse-engineered from ToonBoomPlugInManager.dll
|
||||||
|
* IDA Database: RE/ToonBoomPlugInManager.dll.i64
|
||||||
|
*
|
||||||
|
* @note All offsets and structures derived from decompilation analysis.
|
||||||
|
* This is NOT official Toon Boom code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./toon_boom_layout.hpp"
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
|
// Forward declarations of service interfaces
|
||||||
|
struct AT_ExprScriptEngine;
|
||||||
|
struct CM_Services;
|
||||||
|
struct PLUG_DragDropInterface;
|
||||||
|
struct PLUG_ImportEngine;
|
||||||
|
struct PLUG_InteractiveViewManager;
|
||||||
|
struct PLUG_KeyStateInterface;
|
||||||
|
struct PLUG_MenuService;
|
||||||
|
struct PLUG_ModuleLibraryInterface;
|
||||||
|
struct PLUG_OGLRenderPlaybackInterface;
|
||||||
|
struct PLUG_PlaybackRange;
|
||||||
|
struct PLUG_PreferenceUI;
|
||||||
|
struct PLUG_ScriptingInterface;
|
||||||
|
struct PLUG_SetupModeQueryInterface;
|
||||||
|
struct PLUG_ToolbarService;
|
||||||
|
struct PLUG_VectorizationInterface;
|
||||||
|
struct PLUG_WidgetFactoryRegistry;
|
||||||
|
struct SC_CurrentFrameInterface;
|
||||||
|
struct SC_CVDataToolInterface;
|
||||||
|
struct SC_HttpAPI;
|
||||||
|
struct SC_InteractiveRenderManagerInterface;
|
||||||
|
struct SC_NetworkViewInterface;
|
||||||
|
struct SC_SceneEditionStackInterface;
|
||||||
|
struct SC_SessionContext;
|
||||||
|
struct SL_Selection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_Services
|
||||||
|
* @brief Static class providing access to all Toon Boom plugin service interfaces
|
||||||
|
*
|
||||||
|
* This class provides static accessor methods to retrieve various service
|
||||||
|
* interfaces from the global PLUG_ManagerImpl singleton. All methods are static.
|
||||||
|
*
|
||||||
|
* The singleton is initialized during application startup and must be valid
|
||||||
|
* before calling any getters (except getModuleLibraryInterface which uses
|
||||||
|
* a separate global).
|
||||||
|
*/
|
||||||
|
class PLUG_Services {
|
||||||
|
public:
|
||||||
|
// Delete constructors - this is a static-only class
|
||||||
|
PLUG_Services() = delete;
|
||||||
|
PLUG_Services(const PLUG_Services&) = delete;
|
||||||
|
PLUG_Services& operator=(const PLUG_Services&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the action/command manager
|
||||||
|
* @return AC_Manager* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1A0 (416) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static AC_Manager* getActionManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get color manager services
|
||||||
|
* @return CM_Services* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x218 (536) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static CM_Services* getColorManagerServices();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current frame interface
|
||||||
|
* @return SC_CurrentFrameInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1B0 (432) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_CurrentFrameInterface* getCurrentFrameInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get data tool interface
|
||||||
|
* @return SC_CVDataToolInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x220 (544) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_CVDataToolInterface* getDataToolInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get drag and drop interface
|
||||||
|
* @return PLUG_DragDropInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1F8 (504) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_DragDropInterface* getDragDropInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get scene edition stack interface
|
||||||
|
* @return SC_SceneEditionStackInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1B8 (440) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_SceneEditionStackInterface* getEditionStackInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get expression scripting engine
|
||||||
|
* @return AT_ExprScriptEngine* or nullptr if singleton not initialized or no session context
|
||||||
|
* @note Requires SC_SessionContext (+0x1C0) to be non-null
|
||||||
|
* @note Offset: +0x200 (512) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static AT_ExprScriptEngine* getExpressionScriptingInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get HTTP API interface
|
||||||
|
* @return SC_HttpAPI* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x228 (552) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_HttpAPI* getHttpAPI();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get import engine
|
||||||
|
* @return PLUG_ImportEngine* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1E0 (480) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_ImportEngine* getImportEngine();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get interactive render manager
|
||||||
|
* @return SC_InteractiveRenderManagerInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x198 (408) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_InteractiveRenderManagerInterface* getInteractiveRenderManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get interactive view manager (lazily created)
|
||||||
|
* @return PLUG_InteractiveViewManager* - creates new instance if null
|
||||||
|
* @note Lazily creates PLUG_InteractiveViewManagerImpl if not set
|
||||||
|
* @note Offset: +0x210 (528) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_InteractiveViewManager* getInteractiveViewManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get keyboard state interface
|
||||||
|
* @return PLUG_KeyStateInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1E8 (488) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_KeyStateInterface* getKeyStateInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get layout manager
|
||||||
|
* @return TULayoutManager* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1F0 (496) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static TULayoutManager* getLayoutManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get menu service
|
||||||
|
* @return PLUG_MenuService* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1D8 (472) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_MenuService* getMenuService();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get module library interface
|
||||||
|
* @return PLUG_ModuleLibraryInterface* from separate global (not singleton)
|
||||||
|
* @note Uses g_PLUG_ModuleLibraryInterface global, not main singleton
|
||||||
|
*/
|
||||||
|
static PLUG_ModuleLibraryInterface* getModuleLibraryInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get network view interface
|
||||||
|
* @return SC_NetworkViewInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x240 (576) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_NetworkViewInterface* getNetworkViewInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get OpenGL render playback interface
|
||||||
|
* @return PLUG_OGLRenderPlaybackInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x230 (560) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_OGLRenderPlaybackInterface* getOGLRenderPlaybackInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get plugin path
|
||||||
|
* @param relativePath Relative path within plugin directory
|
||||||
|
* @return QString with full path
|
||||||
|
* @note Uses virtual call through embedded interface at +0x10
|
||||||
|
*/
|
||||||
|
static QString getPluginPath(const QString& relativePath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get preference UI singleton
|
||||||
|
* @return PLUG_PreferenceUI* - uses separate singleton pattern with TLS
|
||||||
|
* @note This is a thunk to PLUG_PreferenceUIImpl::instance()
|
||||||
|
*/
|
||||||
|
static PLUG_PreferenceUI* getPreference();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get scripting interface
|
||||||
|
* @return PLUG_ScriptingInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1C8 (456) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_ScriptingInterface* getScriptingInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get selection manager
|
||||||
|
* @return SL_Selection* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1A8 (424) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SL_Selection* getSelection();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get session context
|
||||||
|
* @return SC_SessionContext* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1C0 (448) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static SC_SessionContext* getSessionContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get toolbar service
|
||||||
|
* @return PLUG_ToolbarService* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x1D0 (464) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_ToolbarService* getToolbarService();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get vectorization interface
|
||||||
|
* @return PLUG_VectorizationInterface* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x238 (568) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_VectorizationInterface* getVectorizationInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get widget factory registry
|
||||||
|
* @return PLUG_WidgetFactoryRegistry* or nullptr if singleton not initialized
|
||||||
|
* @note Offset: +0x208 (520) in PLUG_ManagerImpl
|
||||||
|
*/
|
||||||
|
static PLUG_WidgetFactoryRegistry* getWidgetFactoryRegistry();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_ServicesPrivate
|
||||||
|
* @brief Extended static class with additional private functionality
|
||||||
|
*
|
||||||
|
* Provides setters and additional query methods not exposed through PLUG_Services.
|
||||||
|
*/
|
||||||
|
class PLUG_ServicesPrivate {
|
||||||
|
public:
|
||||||
|
PLUG_ServicesPrivate() = delete;
|
||||||
|
PLUG_ServicesPrivate(const PLUG_ServicesPrivate&) = delete;
|
||||||
|
PLUG_ServicesPrivate& operator=(const PLUG_ServicesPrivate&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get plugin binary file path
|
||||||
|
* @param relativePath Relative path within plugin binary directory
|
||||||
|
* @return QString with full path
|
||||||
|
*/
|
||||||
|
static QString getPluginBinFilePath(const QString& relativePath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get playback range (const)
|
||||||
|
* @return const PLUG_PlaybackRange* static instance
|
||||||
|
*/
|
||||||
|
static const PLUG_PlaybackRange* getPlaybackRange();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if application is in setup mode
|
||||||
|
* @return true if setup mode is active
|
||||||
|
* @note Queries g_PLUG_SetupModeQueryInterface via virtual call
|
||||||
|
*/
|
||||||
|
static bool isSetupMode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the module library interface global
|
||||||
|
* @param iface Pointer to module library interface
|
||||||
|
* @return true always
|
||||||
|
*/
|
||||||
|
static bool setModuleLibraryInterface(PLUG_ModuleLibraryInterface* iface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the setup mode query interface global
|
||||||
|
* @param iface Pointer to setup mode query interface
|
||||||
|
*/
|
||||||
|
static void setSetupModeQueryInterface(PLUG_SetupModeQueryInterface* iface);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_ScriptingInterface
|
||||||
|
* @brief Interface for script execution functionality
|
||||||
|
*
|
||||||
|
* Abstract base class providing scripting capabilities for plugins.
|
||||||
|
*/
|
||||||
|
class PLUG_ScriptingInterface {
|
||||||
|
public:
|
||||||
|
PLUG_ScriptingInterface();
|
||||||
|
virtual ~PLUG_ScriptingInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct Program
|
||||||
|
* @brief Represents a script program
|
||||||
|
*/
|
||||||
|
struct Program {
|
||||||
|
QString path; ///< Script path/name (offset +0x00)
|
||||||
|
QString description; ///< Script description (offset +0x18)
|
||||||
|
QString content; ///< Script content (offset +0x30)
|
||||||
|
QDateTime timestamp; ///< Modification timestamp (offset +0x48)
|
||||||
|
|
||||||
|
Program();
|
||||||
|
Program(const QString& path, const QString& description, const QString& content);
|
||||||
|
Program(const Program& other);
|
||||||
|
Program(Program&& other);
|
||||||
|
~Program();
|
||||||
|
|
||||||
|
Program& operator=(const Program& other);
|
||||||
|
Program& operator=(Program&& other);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_ScriptingModuleInterface
|
||||||
|
* @brief Interface for scripting modules that can be registered
|
||||||
|
*/
|
||||||
|
class PLUG_ScriptingModuleInterface {
|
||||||
|
public:
|
||||||
|
PLUG_ScriptingModuleInterface();
|
||||||
|
virtual ~PLUG_ScriptingModuleInterface();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_ModuleLibraryInterface
|
||||||
|
* @brief Interface for module library functionality
|
||||||
|
*/
|
||||||
|
class PLUG_ModuleLibraryInterface {
|
||||||
|
public:
|
||||||
|
PLUG_ModuleLibraryInterface();
|
||||||
|
virtual ~PLUG_ModuleLibraryInterface();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_DragDropInterface
|
||||||
|
* @brief Interface for drag and drop operations
|
||||||
|
*/
|
||||||
|
class PLUG_DragDropInterface {
|
||||||
|
public:
|
||||||
|
PLUG_DragDropInterface();
|
||||||
|
virtual ~PLUG_DragDropInterface();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_PreferenceUI
|
||||||
|
* @brief Abstract interface for preference panel UI
|
||||||
|
*/
|
||||||
|
class PLUG_PreferenceUI {
|
||||||
|
public:
|
||||||
|
PLUG_PreferenceUI();
|
||||||
|
virtual ~PLUG_PreferenceUI();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a preference UI customizer
|
||||||
|
* @param customizer Customizer interface to add
|
||||||
|
*/
|
||||||
|
virtual void addCustomizer(const class PLUG_PreferenceUICustomizerInterface* customizer) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_PreferenceUICustomizerInterface
|
||||||
|
* @brief Interface for customizing preference panels
|
||||||
|
*/
|
||||||
|
class PLUG_PreferenceUICustomizerInterface {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @enum eAppContext
|
||||||
|
* @brief Application context for preference panels
|
||||||
|
*/
|
||||||
|
enum eAppContext {
|
||||||
|
// Values to be determined via further RE
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUG_PreferenceUICustomizerInterface();
|
||||||
|
virtual ~PLUG_PreferenceUICustomizerInterface();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when creating preference panel
|
||||||
|
* @param context Application context
|
||||||
|
* @param parent Parent widget
|
||||||
|
*/
|
||||||
|
virtual void onCreatePreferencePanel(eAppContext context, QWidget* parent) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_InteractiveViewManager
|
||||||
|
* @brief Abstract base class for interactive view management
|
||||||
|
*/
|
||||||
|
class PLUG_InteractiveViewManager {
|
||||||
|
public:
|
||||||
|
virtual ~PLUG_InteractiveViewManager();
|
||||||
|
|
||||||
|
// Pure virtual methods (to be discovered)
|
||||||
|
virtual void invalidateAllViews(int flags) = 0;
|
||||||
|
virtual void invalidateTimeline() = 0;
|
||||||
|
virtual void registerDelegate(class PLUG_InteractiveViewDelegate* delegate) = 0;
|
||||||
|
virtual void unregisterDelegate(class PLUG_InteractiveViewDelegate* delegate) = 0;
|
||||||
|
virtual bool isDelegateTypeRegistered(class PLUG_InteractiveViewDelegate* delegate) = 0;
|
||||||
|
virtual void releaseAllDelegates() = 0;
|
||||||
|
virtual void clearAllDrawingSelection() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PLUG_InteractiveViewManager();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PLUG_Manager
|
||||||
|
* @brief Base class for the plugin manager singleton
|
||||||
|
*/
|
||||||
|
class PLUG_Manager {
|
||||||
|
public:
|
||||||
|
PLUG_Manager();
|
||||||
|
virtual ~PLUG_Manager();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Offset Constants (for reference/debugging)
|
||||||
|
// ============================================================================
|
||||||
|
namespace PLUG_ManagerImpl_Offsets {
|
||||||
|
constexpr size_t CoreApp = 0x188; // 392
|
||||||
|
constexpr size_t UnknownInterface = 0x190; // 400
|
||||||
|
constexpr size_t InteractiveRenderManager = 0x198; // 408
|
||||||
|
constexpr size_t ActionManager = 0x1A0; // 416
|
||||||
|
constexpr size_t Selection = 0x1A8; // 424
|
||||||
|
constexpr size_t CurrentFrameInterface = 0x1B0; // 432
|
||||||
|
constexpr size_t EditionStackInterface = 0x1B8; // 440
|
||||||
|
constexpr size_t SessionContext = 0x1C0; // 448
|
||||||
|
constexpr size_t ScriptingInterface = 0x1C8; // 456
|
||||||
|
constexpr size_t ToolbarService = 0x1D0; // 464
|
||||||
|
constexpr size_t MenuService = 0x1D8; // 472
|
||||||
|
constexpr size_t ImportEngine = 0x1E0; // 480
|
||||||
|
constexpr size_t KeyStateInterface = 0x1E8; // 488
|
||||||
|
constexpr size_t LayoutManager = 0x1F0; // 496
|
||||||
|
constexpr size_t DragDropInterface = 0x1F8; // 504
|
||||||
|
constexpr size_t ExprScriptEngine = 0x200; // 512
|
||||||
|
constexpr size_t WidgetFactoryRegistry = 0x208; // 520
|
||||||
|
constexpr size_t InteractiveViewManager = 0x210; // 528
|
||||||
|
constexpr size_t ColorManagerServices = 0x218; // 536
|
||||||
|
constexpr size_t DataToolInterface = 0x220; // 544
|
||||||
|
constexpr size_t HttpAPI = 0x228; // 552
|
||||||
|
constexpr size_t OGLRenderPlaybackInterface = 0x230; // 560
|
||||||
|
constexpr size_t VectorizationInterface = 0x238; // 568
|
||||||
|
constexpr size_t NetworkViewInterface = 0x240; // 576
|
||||||
|
constexpr size_t UnknownString = 0x248; // 584
|
||||||
|
constexpr size_t Flag = 0x260; // 608
|
||||||
|
}
|
||||||
1045
framework/include/toon_boom/toon_boom_layout.hpp
Normal file
1045
framework/include/toon_boom/toon_boom_layout.hpp
Normal file
File diff suppressed because it is too large
Load Diff
5
vcpkg.json
Normal file
5
vcpkg.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": [
|
||||||
|
"minhook"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user