commit c9f25e7b6ab6690583d20634737c93381aa6b670 Author: ☙◦ The Tablet ❀ GamerGirlandCo ◦❧ Date: Sat Jan 10 17:49:22 2026 -0500 hello world 🌸✨ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93ad4b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +build/ +.idea/ +.vscode/ +.cache/ +compile_commands.json +*.log +log*.txt +*.not \ No newline at end of file diff --git a/3rdparty/QtScript.lib b/3rdparty/QtScript.lib new file mode 100644 index 0000000..1eab634 Binary files /dev/null and b/3rdparty/QtScript.lib differ diff --git a/3rdparty/ToonBoomActionManager.lib b/3rdparty/ToonBoomActionManager.lib new file mode 100644 index 0000000..b80c3c6 Binary files /dev/null and b/3rdparty/ToonBoomActionManager.lib differ diff --git a/3rdparty/ToonBoomLayout.lib b/3rdparty/ToonBoomLayout.lib new file mode 100644 index 0000000..121edad Binary files /dev/null and b/3rdparty/ToonBoomLayout.lib differ diff --git a/3rdparty/ToonBoomPlugInManager.lib b/3rdparty/ToonBoomPlugInManager.lib new file mode 100644 index 0000000..1e47819 Binary files /dev/null and b/3rdparty/ToonBoomPlugInManager.lib differ diff --git a/3rdparty/dll2def.ps1 b/3rdparty/dll2def.ps1 new file mode 100644 index 0000000..baab2fa --- /dev/null +++ b/3rdparty/dll2def.ps1 @@ -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" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0649652 --- /dev/null +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..d3227e3 --- /dev/null +++ b/CMakePresets.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/docs/HarmonyPremium_QtScript.md b/docs/HarmonyPremium_QtScript.md new file mode 100644 index 0000000..4757e4e --- /dev/null +++ b/docs/HarmonyPremium_QtScript.md @@ -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__ = ""; + // 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__ = ` (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(, 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) + + diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..8ebdb2c --- /dev/null +++ b/docs/README.md @@ -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. + + diff --git a/docs/ToonBoomLayout_Classes.md b/docs/ToonBoomLayout_Classes.md new file mode 100644 index 0000000..3af8f09 --- /dev/null +++ b/docs/ToonBoomLayout_Classes.md @@ -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 start | +| 0x30 | 8 | ptr | m_views.end | std::vector end | +| 0x38 | 8 | ptr | m_views.capacity | std::vector 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 | m_buttonConfig | Button configuration | +| 0x50 | 24 | QList | 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 | +| 0x78 | 24 | vector | m_frames | std::vector | +| 0x90 | 24 | vector | m_areas | std::vector | +| 0xA8 | 24 | vector | m_pluginAreas | std::vector | +| 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 all layouts | +| 0x30 | 24 | vector | m_toolbarLayouts | std::vector 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` and embeds `TULayoutView` at offset +104. + +### Inheritance +`AC_ResponderTemplateWidget` + `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 | +| 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 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 base + AC_ResponderTemplateWidget::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 \ No newline at end of file diff --git a/docs/ToonBoomLayout_ViewUsage.md b/docs/ToonBoomLayout_ViewUsage.md new file mode 100644 index 0000000..dd4886f --- /dev/null +++ b/docs/ToonBoomLayout_ViewUsage.md @@ -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 + +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(static_cast(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 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` diff --git a/docs/ToonBoomPlugInManager_PLUG_Services.md b/docs/ToonBoomPlugInManager_PLUG_Services.md new file mode 100644 index 0000000..f01fd27 --- /dev/null +++ b/docs/ToonBoomPlugInManager_PLUG_Services.md @@ -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` diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt new file mode 100644 index 0000000..49ddd78 --- /dev/null +++ b/framework/CMakeLists.txt @@ -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 $) +add_library(libtoonboom SHARED $) +link_libs_and_set_properties(libtoonboom_objs) +link_libs_and_set_properties(libtoonboom_static) +link_libs_and_set_properties(libtoonboom) \ No newline at end of file diff --git a/framework/hook/harmony_signatures.cpp b/framework/hook/harmony_signatures.cpp new file mode 100644 index 0000000..975af59 --- /dev/null +++ b/framework/hook/harmony_signatures.cpp @@ -0,0 +1,199 @@ +#include "harmony_signatures.hpp" + +#include "sigscan.hpp" + +#include +#include +#include +#include +#include + +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(*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(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 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(addr), &image_base, nullptr); + if (!rf || image_base == 0) return std::nullopt; + + // BeginAddress/EndAddress are RVAs from image_base. + const auto begin = static_cast(image_base + rf->BeginAddress); + const auto end = static_cast(image_base + rf->EndAddress); + if (begin >= end) return std::nullopt; + + return FunctionRange{begin, end}; +} + +} // namespace + +std::optional 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 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(filtered[0]); +} + +std::optional 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 candidates; + candidates.reserve(hits.size()); + + for (const auto* hit : hits) { + const auto hit_addr = reinterpret_cast(hit); + auto fr = function_range_from_unwind(target_module, hit_addr); + if (!fr) continue; + + const auto size = static_cast(fr->end - fr->begin); + if (size < kMinSize || size > kMaxSize) continue; + + // Ensure the function is inside .text. + const auto text_begin = reinterpret_cast(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 + + diff --git a/framework/hook/hook.cpp b/framework/hook/hook.cpp new file mode 100644 index 0000000..cbe04b5 --- /dev/null +++ b/framework/hook/hook.cpp @@ -0,0 +1,82 @@ +#include "../include/hooks/toon_boom_hooks.hpp" +#include "../include/internal/harmony_signatures.hpp" +#include +#include + +QScriptEngine *global_engine_ptr = NULL; +bool is_first_load = true; +SCR_ScriptManager_ctor_t SCR_ScriptManager_ctor_original_ptr = NULL; +std::vector 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 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_ScripRuntime_getEngine_original.value()); + + void* mgr_data = *reinterpret_cast(reinterpret_cast(_this) + 0x20); + if (!mgr_data) { + std::cerr << "SCR_ScriptManager data pointer was null" << std::endl; + return result; + } + void* runtime_handle = *reinterpret_cast(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_ptr.value()); + MH_STATUS status = MH_CreateHook( + reinterpret_cast(SCR_ScriptManager_ctor_original_ptr_val), + reinterpret_cast(&SCR_ScriptManager_ctor_hook), + reinterpret_cast(&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(SCR_ScriptManager_ctor_original_ptr_val)); + MH_Uninitialize(); + return FALSE; + } + std::cout << "Hooks initialized and enabled" << std::endl; + is_first_load = false; + return TRUE; +} diff --git a/framework/hook/sigscan.cpp b/framework/hook/sigscan.cpp new file mode 100644 index 0000000..f8308bb --- /dev/null +++ b/framework/hook/sigscan.cpp @@ -0,0 +1,130 @@ +#include "../include/internal/sigscan.hpp" + +#include +#include +#include +#include +#include + +namespace toon_boom_module::sigscan { +namespace { + +bool is_hex_digit(char c) { + return std::isxdigit(static_cast(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(c - '0'); + c = static_cast(std::tolower(static_cast(c))); + if (c >= 'a' && c <= 'f') return static_cast(10 + (c - 'a')); + throw std::invalid_argument("invalid hex digit"); + }; + + return static_cast((nybble(hi) << 4) | nybble(lo)); +} + +std::vector split_ws(std::string_view s) { + std::vector out; + std::size_t i = 0; + while (i < s.size()) { + while (i < s.size() && std::isspace(static_cast(s[i]))) ++i; + if (i >= s.size()) break; + std::size_t j = i; + while (j < s.size() && !std::isspace(static_cast(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 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(module); + const auto* dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return std::nullopt; + + const auto* nt = reinterpret_cast(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(sec[i].Misc.VirtualSize); + return SectionView{begin, size}; + } + } + + return std::nullopt; +} + +std::vector find_all(SectionView region, const Pattern& pat) { + std::vector 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(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 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 + + diff --git a/framework/include/hooks/toon_boom_hooks.hpp b/framework/include/hooks/toon_boom_hooks.hpp new file mode 100644 index 0000000..dd4522f --- /dev/null +++ b/framework/include/hooks/toon_boom_hooks.hpp @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +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(); \ No newline at end of file diff --git a/framework/include/internal/harmony_signatures.hpp b/framework/include/internal/harmony_signatures.hpp new file mode 100644 index 0000000..91016d7 --- /dev/null +++ b/framework/include/internal/harmony_signatures.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include + +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 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 find_SCR_ScriptManager_ctor(HMODULE target_module); + +} // namespace toon_boom_module::harmony + + diff --git a/framework/include/internal/sigscan.hpp b/framework/include/internal/sigscan.hpp new file mode 100644 index 0000000..0e8b49e --- /dev/null +++ b/framework/include/internal/sigscan.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace toon_boom_module::sigscan { + +struct Pattern { + std::vector bytes; // pattern bytes (wildcard bytes may be any value) + std::vector 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 get_pe_section(HMODULE module, std::string_view section_name); + +// Returns all matches in the provided memory region. +std::vector find_all(SectionView region, const Pattern& pat); + +// Returns the single match or std::nullopt (0 or >1 matches). +std::optional find_unique(SectionView region, const Pattern& pat); + +} // namespace toon_boom_module::sigscan + + diff --git a/framework/include/toon_boom/PLUG_Services.hpp b/framework/include/toon_boom/PLUG_Services.hpp new file mode 100644 index 0000000..25be4a2 --- /dev/null +++ b/framework/include/toon_boom/PLUG_Services.hpp @@ -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 +#include +#include +#include + +// 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 +} diff --git a/framework/include/toon_boom/toon_boom_layout.hpp b/framework/include/toon_boom/toon_boom_layout.hpp new file mode 100644 index 0000000..20504ec --- /dev/null +++ b/framework/include/toon_boom/toon_boom_layout.hpp @@ -0,0 +1,1045 @@ +/** + * @file toon_boom_layout.hpp + * @brief Reconstructed header files for ToonBoomLayout.dll classes + * + * These class definitions are reverse-engineered from ToonBoomLayout.dll + * used in Toon Boom Harmony Premium and Storyboard Pro. + * + * WARNING: This is a reconstruction based on binary analysis. Member offsets + * and sizes have been verified but exact types may differ from the original. + * + * @see RE/ToonBoomLayout_Classes.md for detailed analysis + */ + +#ifndef TOON_BOOM_LAYOUT_HPP +#define TOON_BOOM_LAYOUT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Forward declarations +class TULayoutView; +class TULayoutViewHolder; +class TULayoutFrame; +class TULayoutManager; +class TULayoutManager_Private; +class TULayoutArea; +class TULayoutSplitter; +class TULayoutMainWindow; +class TULayoutDisplayTools; +class TULayout; +class AC_Manager; +class AC_Menu; +class AC_Toolbar; +class AC_ActionInfo; +class AC_ToolbarItemGenerator; +class AC_Responder; +class AC_ResponderTemplate; +class TUWidgetLayoutView; +class GUT_TabBar; +class GUT_Tab; +class LAY_ToolbarInfo; +class UI_Splitter; +class WID_VBoxLayout; +class WID_HBoxLayout; + +/** + * @brief Result code for action command handling + */ +enum class AC_Result { + NotHandled = 0, + Handled = 1, + Error = 2 +}; + +// ============================================================================= +// AC_Responder, AC_ResponderTemplate, AC_ResponderTemplateWidget +// ============================================================================= +// +// These classes are implemented in Toon Boom DLLs (ToonBoomActionManager.dll +// or similar). Since we don't have access to those libraries for linking, +// we provide forward declarations only. +// +// For reference, the full interface is documented in RE/ToonBoomLayout_Classes.md +// +// Key vtable addresses (in ToonBoomLayout.dll): +// - AC_Responder vtable: 0x18004cd28 +// - AC_ResponderTemplate vtable: 0x18004cdc8 +// - AC_ResponderTemplateWidget vtable: 0x18004ce68 +// +// AC_Responder is a pure abstract interface with the following virtual methods: +// - perform(AC_ActionInfo*) -> AC_Result +// - performDownToChildren(AC_ActionInfo*) -> AC_Result +// - parentResponder() -> AC_Responder* +// - proxyResponder() -> AC_Responder* +// - acceptsFirstResponder() -> bool +// - acceptsSelectionResponder() -> bool +// - becomeFirstResponder() -> bool +// - becomeSelectionResponder() -> bool +// - resignFirstResponder() -> bool +// - handleShortcuts() const -> bool +// - shouldReceiveMessages() const -> bool +// - responderIdentity() const -> const QString& +// - responderDescription() const -> const QString& +// - setResponderDescription(const QString&) -> void +// - actionManager() const -> AC_Manager* +// +// AC_ResponderTemplate (sizeof 0x38): +// - Inherits AC_Responder +// - +0x08: QString m_identity +// - +0x20: QString m_description +// - +0x30: AC_Manager* m_manager +// +// AC_ResponderTemplateWidget (sizeof ~0x68 for T=QWidget): +// - Inherits T (QWidget, QFrame, etc.) and AC_ResponderTemplate +// - Combines a Qt widget with action responder capabilities +// ============================================================================= + +// Forward declarations only - implemented in Toon Boom DLLs +class AC_Responder; +class AC_ResponderTemplate; + +// Template class - forward declaration for common instantiations +// The actual template is implemented in Toon Boom DLLs +template +class AC_ResponderTemplateWidget; + +/** + * @brief Toolbar configuration and state for a view + * + * Stores position, visibility, and button configuration for a toolbar + * associated with a TULayoutView. + * + * sizeof(LAY_ToolbarInfo) = 0x68 (104 bytes) on x64 + */ +class LAY_ToolbarInfo { +public: + LAY_ToolbarInfo(); + LAY_ToolbarInfo(const LAY_ToolbarInfo &other); + LAY_ToolbarInfo(QString name, int x, int y, int width, int height, int index, + bool visible, bool isDefault); + ~LAY_ToolbarInfo(); + + LAY_ToolbarInfo &operator=(const LAY_ToolbarInfo &other); + + // Getters + int getHeight() const; // +0x10 + const QString &getName() const; // +0x18 + int getNewline() const; // +0x14 (bool as int) + Qt::Orientation getOrientation() const; // +0x30 + const Qt::ToolBarArea &getToolBarArea() const; // +0x34 + bool isDefault() const; // +0x16 + bool isVisible() const; // +0x15 + + // Setters + void setHeight(int height); // +0x10 + void setIndex(int index); // +0x08 + void setName(const QString &name); // +0x18 + void setNewline(int newline); // +0x14 + void setOrientation(Qt::Orientation orientation); // +0x30 + void setToolBarArea(Qt::ToolBarArea area); // +0x34 + void setVisible(bool visible); // +0x15 + void setWidth(int width); // +0x0C + void setX(int x); // +0x00 + void setY(int y); // +0x04 + + // Serialization + void fromXml(const QDomElement &element); + void toXml(QDomElement &element) const; + + // Button configuration + const QList *getButtonConfig() const; // +0x38 + void setButtonConfig(const QList *config); // +0x38 + void setButtonDefaultConfig(const QList *config); // +0x50 + +private: + // Member layout (x64): + int m_x; // +0x00 + int m_y; // +0x04 + int m_index; // +0x08 + int m_width; // +0x0C + int m_height; // +0x10 + bool m_newline; // +0x14 + bool m_visible; // +0x15 + bool m_isDefault; // +0x16 + char _padding1; // +0x17 + QString m_name; // +0x18 (24 bytes) + Qt::Orientation m_orientation; // +0x30 + Qt::ToolBarArea m_toolBarArea; // +0x34 + QList m_buttonConfig; // +0x38 (24 bytes) + QList m_buttonDefaultConfig; // +0x50 (24 bytes) +}; + +/** + * @brief Abstract base class for all layout views in Toon Boom + * + * Represents a dockable/tabbable view panel that can be displayed in a + * TULayoutFrame. This is an abstract class - subclasses must implement + * the pure virtual methods to provide actual functionality. + * + * LINKING: Requires ToonBoomLayout.lib - constructor/destructor are exported. + * + * sizeof(TULayoutView) = 0xB8 (184 bytes) on x64 + * + * VTABLE ORDER (32 slots total): + * 0: ~TULayoutView() + * 1: widget() = 0 + * 2: initiate() + * 3: getWidget() const = 0 + * 4: getWidget() = 0 + * 5: getParentHolderWidget() const + * 6: getParentHolderWidget() + * 7: hasMenu() + * 8: setMenu(AC_Manager*, ...) + * 9: setMenu(AC_Menu*, ...) + * 10: menu() + * 11: toolbar() + * 12: setToolbarInfo() + * 13: connectView() + * 14: disconnectView() + * 15: initializedFromCopy() + * 16: getCaption() + * 17: getDynamicTextForCaption() + * 18: wantEditionStack() + * 19: displayName() + * 20: compositeChanged() + * 21: dropOverComposite() + * 22: wantComposites() + * 23: initActionManager() + * 24: wantDisplaySelector() + * 25: isUsingDefaultDisplay() + * 26: storeViewPreferences() + * 27: loadViewPreferences() + * 28: cshHelpId() + * 29: triggerMenuChanged() = 0 + * 30: copy() + * 31: isTULayoutView() = 0 + */ +class TULayoutView { +public: + /** + * @brief Menu type enumeration for TULayoutView + */ + enum class MenuType : int { + Primary = 0, // Main context menu + Secondary = 1 // Secondary/overflow menu + }; + + // Constructors - implemented in ToonBoomLayout.dll + TULayoutView(); + TULayoutView(const TULayoutView &other); + virtual ~TULayoutView(); // slot 0 + + TULayoutView &operator=(const TULayoutView &other); + + // ===== PURE VIRTUAL METHODS (MUST override all 5) ===== + virtual QWidget *widget() = 0; // slot 1 + virtual TULayoutView *initiate(QWidget *parent); // slot 2 + virtual const QWidget *getWidget() const = 0; // slot 3 + virtual QWidget *getWidget() = 0; // slot 4 + + // ===== Virtual methods with default implementations ===== + virtual const TULayoutViewHolder *getParentHolderWidget() const; // slot 5 + virtual TULayoutViewHolder *getParentHolderWidget(); // slot 6 + virtual bool hasMenu(); // slot 7 + virtual void setMenu(AC_Manager *manager, const char *menuName, MenuType type); // slot 8 + virtual void setMenu(AC_Menu *menu, MenuType type); // slot 9 + virtual AC_Menu *menu(MenuType type); // slot 10 + virtual QDomElement toolbar(); // slot 11 + virtual void setToolbarInfo(const LAY_ToolbarInfo &info); // slot 12 + virtual void connectView() {} // slot 13 (empty impl) + virtual void disconnectView() {} // slot 14 (empty impl) + virtual bool initializedFromCopy(); // slot 15 + virtual QString getCaption(bool includeAdvanced) const; // slot 16 + virtual QString getDynamicTextForCaption() const; // slot 17 + virtual bool wantEditionStack() const { return false; } // slot 18 (returns false) + virtual QString displayName() const; // slot 19 + virtual void compositeChanged(const QString &) {} // slot 20 (empty impl) + virtual void dropOverComposite(QDropEvent *, const QString &) {} // slot 21 (empty) + virtual bool wantComposites() const { return false; } // slot 22 (returns false) + virtual void initActionManager(AC_Manager *) {} // slot 23 (empty impl) + virtual bool wantDisplaySelector() const { return false; } // slot 24 (returns false) + virtual bool isUsingDefaultDisplay() const { return false; } // slot 25 (returns false) + virtual bool storeViewPreferences(QDomElement &) const { return false; } // slot 26 + virtual void loadViewPreferences(const QDomElement &) {} // slot 27 (empty impl) + virtual QString cshHelpId(); // slot 28 + + // ===== MORE PURE VIRTUALS ===== + virtual void triggerMenuChanged() = 0; // slot 29 - MUST override! + + // Non-virtual methods + void setCaption(const QString &caption); + const LAY_ToolbarInfo &getToolbarInfo(); + TULayoutFrame *getLayoutFrame(const QWidget *widget) const; + + // Static + static bool inClosingState(); + +protected: + virtual void copy(const TULayoutView &other); // slot 30 + virtual void isTULayoutView() = 0; // slot 31 - MUST override! + +private: + // Member layout (x64): + // vptr at +0x00 (8 bytes) + QString m_internalName; // +0x08 (24 bytes) - "View{N}" by default + LAY_ToolbarInfo m_toolbarInfo; // +0x20 (104 bytes) + AC_Menu *m_menuByType[2]; // +0x88 (16 bytes) - indexed by MenuType + bool m_initializedFromCopy; // +0x98 (1 byte) + char _padding[7]; // +0x99 + QString m_caption; // +0xA0 (24 bytes) + + static int _uniqueId; // Static counter for unique names +}; + +// Note: AC_ResponderTemplateWidget is a template class defined in Toon Boom DLLs +// that combines a QWidget subclass with AC_Responder capabilities. +// Memory layout for AC_ResponderTemplateWidget: +// - +0x00: vptr (QObject) +// - +0x10: vptr (QPaintDevice) +// - +0x18-0x27: QWidget members +// - +0x28: vptr (AC_ResponderTemplateWidget) +// - +0x30: AC_Manager* m_actionManager +// - +0x38: QString m_responderIdentity +// - +0x50: QString m_responderDescription +// sizeof(AC_ResponderTemplateWidget) ≈ 0x68 (104 bytes) on x64 + +// ============================================================================= +// TUWidgetLayoutView +// ============================================================================= +// +// TUWidgetLayoutView is implemented in ToonBoomLayout.dll. It inherits from +// AC_ResponderTemplateWidget and embeds a TULayoutView at offset +0x68. +// +// Since the base classes are implemented in Toon Boom DLLs, we cannot define +// a compilable C++ class here. Use this as a reference for the memory layout +// when working with TUWidgetLayoutView pointers obtained from the application. +// +// Memory layout (x64 MSVC): +// - +0x00: vptr (QObject) - from AC_ResponderTemplateWidget +// - +0x10: vptr (QPaintDevice) +// - +0x18-0x27: QWidget members +// - +0x28: vptr (AC_ResponderTemplateWidget) +// - +0x30: AC_Manager* m_actionManager +// - +0x38: QString m_responderIdentity +// - +0x50: QString m_responderDescription +// - +0x68: vptr (TULayoutView) - TULayoutView base starts here +// - +0x70: QString m_internalName (from TULayoutView) +// - +0x88: LAY_ToolbarInfo m_toolbarInfo (from TULayoutView) +// - +0xF0: AC_Menu* m_menuByType[2] (from TULayoutView) +// - +0x100: bool m_initializedFromCopy (from TULayoutView) +// - +0x108: QString m_caption (from TULayoutView) +// +// sizeof(TUWidgetLayoutView) ≈ 0x120 (288 bytes) on x64 +// +// Key methods (in ToonBoomLayout.dll): +// - Constructor at 0x1800300A0 +// - Destructor at 0x180030480 +// - getWidget() at 0x180030E90 - returns (this - 104) from TULayoutView* +// +// To get TULayoutView* from TUWidgetLayoutView*: +// TULayoutView* layoutView = reinterpret_cast( +// reinterpret_cast(widgetLayoutView) + 104); +// +// To get TUWidgetLayoutView* from TULayoutView*: +// TUWidgetLayoutView* widget = reinterpret_cast( +// reinterpret_cast(layoutView) - 104); +// ============================================================================= + +// Forward declaration - implemented in ToonBoomLayout.dll +// Use the helper functions below for pointer conversion +class TUWidgetLayoutView; + +/** + * @brief Convert TUWidgetLayoutView* to its embedded TULayoutView* + * @param widget Pointer to TUWidgetLayoutView + * @return Pointer to embedded TULayoutView at offset +104 + */ +inline TULayoutView *TUWidgetLayoutView_getLayoutView(TUWidgetLayoutView *widget) { + return reinterpret_cast(reinterpret_cast(widget) + 104); +} + +/** + * @brief Convert TULayoutView* back to containing TUWidgetLayoutView* + * @param view Pointer to TULayoutView (must be embedded in TUWidgetLayoutView) + * @return Pointer to containing TUWidgetLayoutView at offset -104 + */ +inline TUWidgetLayoutView *TULayoutView_getWidgetLayoutView(TULayoutView *view) { + return reinterpret_cast(reinterpret_cast(view) - 104); +} + +/** + * @brief Get the QWidget* from a TUWidgetLayoutView* + * @param widget Pointer to TUWidgetLayoutView + * @return The QWidget* (same address, different type) + */ +inline QWidget *TUWidgetLayoutView_getWidget(TUWidgetLayoutView *widget) { + return reinterpret_cast(widget); +} + +/** + * @brief Container widget that holds 1-2 TULayoutView instances + * + * When holding 2 views, they are displayed in a vertical splitter. + * Single views are displayed directly in the layout. + * + * sizeof(TULayoutViewHolder) = 0x70 (112 bytes) on x64 + */ +class TULayoutViewHolder : public QWidget { + Q_OBJECT + +public: + explicit TULayoutViewHolder(QWidget *parent = nullptr); + virtual ~TULayoutViewHolder(); + + /** + * @brief Add a view to this holder + * @param view The view to add + * @param splitterRatio Initial splitter ratio (only used when adding 2nd + * view) + * @return true if added successfully, false if holder is full (max 2) + */ + bool addView(TULayoutView *view, double splitterRatio = 0.5); + + /** + * @brief Remove a view from this holder + * @param view The view to remove + * @return true if removed successfully + */ + bool removeView(TULayoutView *view); + + /** + * @brief Get the number of views in this holder + * @return 0, 1, or 2 + */ + int nbViews() const; + + /** + * @brief Get the current splitter ratio + * @return Ratio between 0.0 and 1.0 (left/total) + */ + double splitterRatio(); + + /** + * @brief Set focus to the first view's widget + */ + void setFocusToChild(); + + /** + * @brief Update widget visibility and layout + */ + void updateWidgets(); + + // Static translation helper + +private: + bool removeWidgetFromLayout(QWidget *widget, QLayout *layout); + + // Member layout (x64): + // QWidget members at +0x00 to +0x27 + std::vector m_views; // +0x28 (24 bytes) + double m_savedSplitterRatio; // +0x40 (8 bytes) - default 0.5 + UI_Splitter *m_splitter; // +0x48 (8 bytes) - vertical splitter + WID_VBoxLayout *m_leftLayout; // +0x50 (8 bytes) + WID_VBoxLayout *m_rightLayout; // +0x58 (8 bytes) + QFrame *m_leftFrame; // +0x60 (8 bytes) + QFrame *m_rightFrame; // +0x68 (8 bytes) +}; + +/** + * @brief Top-level dockable/floatable frame with tabbed view holders + * + * Contains a tab bar for switching between view holders, toolbar area, + * and various control buttons. + * + * Inherits from QFrame for border painting and styling. + */ +class TULayoutFrame : public QFrame { + Q_OBJECT + +public: + /** + * @brief Construct a TULayoutFrame + * @param manager Action/menu manager for creating menus + * @param parent Parent widget + * @param name Object name for this frame + * @param layoutManager The TULayoutManager managing this frame + * @param floating True if this is a floating window (false = docked) + * @param unknown Unknown parameter + * @param singleViewMode True for single view mode + */ + TULayoutFrame(AC_Manager *manager, QWidget *parent, const QString &name, + TULayoutManager *layoutManager, bool floating, bool unknown, + bool singleViewMode); + virtual ~TULayoutFrame(); + + // Accessors + TULayoutManager *getLayoutManager(); + QStackedWidget *getStack() const; + GUT_TabBar *getTab(); + AC_Toolbar *getToolbar() const; + bool isDocked() const; + bool isInSingleViewMode() const; + bool isVisibleFullScreen() const; + + // Tab management + int currentTab() const; + QString currentTabName() const; + TULayoutViewHolder *currentTabViewHolder(); + const GUT_Tab *addTab(TULayoutViewHolder *holder, const QString &name); + void delTab(TULayoutViewHolder *holder); + void setCurrentTab(int index); + void setCurrentTab(TULayoutViewHolder *holder); + bool updateTabName(TULayoutViewHolder *holder); + void setTabVisible(bool visible); + + // View management + void delView(QWidget *widget); + void delViewHolder(TULayoutViewHolder *holder); + TULayoutView *newTab(int areaIndex); + void newView(int areaIndex); + QWidget *getViewWidget(int index); + void setCurrentViewByIndex(unsigned int index); + + // State management + void setCurrent(bool current); + void setFocusToChild() const; + void updateCaption(); + void updateFocus(); + void updateVisibilityStatus(); + void updateTabHolderSplitters(); + void emitViewAdded(); + void postCustomRaiseEvent(); + + // Toolbar management + const LAY_ToolbarInfo *getToolbarInfoForView(const QString &viewName) const; + void setToolbarInfoForView(const QString &viewName, + const LAY_ToolbarInfo &info); + void showViewToolBar(); + void removeViewToolBar(); + void showToolBarMenu(); + void loadToolbars(QDomElement &element); + void saveToolbars(QDomElement &element); + void saveLayoutToolbar(QDomElement &element, QToolBar *toolbar); + void createEmptyToolBar(const QString &name); + + // Composite/display management + void enableComposite(bool enable); + void enableEditionStack(bool enable); + void compositeAdded(const QString &name); + void compositeDeleted(const QString &name); + void compositeOrGroupRenamed(); + void resetDisplayList(); + void resetDisplayList(const QString &name, const std::vector &items); + + // Static + +signals: + void viewAdded(); + +public slots: + void handleCloseViewButton(); + void onActionCloseAllTabs(); + void onActionCloseCurrentTab(); + void onActionCloseTab(QString name); + void onActionRaiseArea(QString name); + void onActionRenameTab(); + void renameTab(int index); + void syncCurrentTab(); + + virtual void contextMenuEvent(QContextMenuEvent *event) override; + virtual void enterEvent(QEnterEvent *event) override; + virtual bool eventFilter(QObject *watched, QEvent *event) override; + virtual void mousePressEvent(QMouseEvent *event) override; +protected: + // Qt event overrides + virtual void closeEvent(QCloseEvent *event) override; + virtual void paintEvent(QPaintEvent *event) override; + + // Internal methods + void borderPaintEvent(QWidget *widget); + void filterComposite(const QString &name); + int findCompositeName(const QString &name); + int findName(const QString &name); + bool getCurToolbarState(LAY_ToolbarInfo &info); + QString getFrameCaption() const; + bool isLastDockFrame() const; + void updateDisplaySelectorStatus(); + void updateEditionStackStatus(); + void updateMenuStatus(); + +private slots: + void onDisplayChanged(int index); + void onShowLayoutFrameMenu(AC_Menu *menu); + void compositeChanged(const QString &name) const; + void toolbarWasCustomized(); + +private: + // Member layout (x64): + // QFrame members at +0x00 to +0x27 + TULayoutManager *m_layoutManager; // +0x28 + WID_VBoxLayout *m_mainLayout; // +0x30 + WID_HBoxLayout *m_topHBoxLayout; // +0x38 + QToolButton *m_menuButton; // +0x40 - "View Menu" button + TULayoutMainWindow *m_mainWindow; // +0x48 - contains toolbar area + TULayoutDisplayTools *m_displayTools; // +0x50 - display selector + QComboBox *m_compositeCombo; // +0x58 - composite selector + GUT_TabBar *m_tabBar; // +0x60 - tab bar + QStackedWidget *m_stack; // +0x68 - stacked widget for tab content + QWidget *m_editionStackWidget; // +0x70 - symbol/edition stack + QToolButton *m_closeButton; // +0x78 - close button + QToolButton *m_createViewMenuButton; // +0x80 - "Create View Menu" button + bool m_isCurrent; // +0x88 - is focused frame + bool m_isDocked; // +0x89 - true if docked (not floating) + bool m_isInSingleViewMode; // +0x8A - single view mode + char _padding1; // +0x8B + int _reserved1; // +0x8C + AC_Toolbar *m_toolbar; // +0x90 - view-specific toolbar + int m_unknown; // +0x98 - initialized to -1 + int _reserved2; // +0x9C + // Map/list structure at +0xA0-0xAF for toolbar info per view + QWidget *m_parentWidget; // +0xB0 + AC_Menu *m_layoutFrameMenu; // +0xB8 - context menu + AC_Toolbar *m_viewToolbar; // +0xC0 - currently active toolbar +}; + +/** + * @brief Lightweight QMainWindow for toolbar hosting inside TULayoutFrame + * + * Used inside TULayoutFrame to provide toolbar docking areas for + * view-specific toolbars. The QStackedWidget is set as its central widget. + * + * sizeof(TULayoutMainWindow) = 0x30 (48 bytes) on x64 + */ +class TULayoutMainWindow : public QMainWindow { + Q_OBJECT + +public: + explicit TULayoutMainWindow(AC_Manager *manager, QWidget *parent = nullptr); + virtual ~TULayoutMainWindow(); + + AC_Manager *getManager() const { return m_manager; } + +private: + // Member layout (x64): + // QMainWindow members at +0x00 to +0x27 + AC_Manager *m_manager; // +0x28 - Action manager reference +}; + +/** + * @brief Base class for layout persistence and configuration + * + * Provides layout loading, saving, and toolbar configuration management. + * TULayoutManager inherits from this class. + * + * sizeof(TULayoutStorage) = 0x88 (136 bytes) on x64 + */ +class TULayoutStorage { +public: + /** + * @brief Toolbar button configuration + */ + struct ToolbarButtonConfig { + QList buttons; + bool isDefault; + }; + + TULayoutStorage(const QString &layoutPath); + TULayoutStorage(const TULayoutStorage &other); + TULayoutStorage &operator=(const TULayoutStorage &other); + virtual ~TULayoutStorage(); + + // Layout management + TULayout *addLayout(const QString &name, int index = -1); + virtual void delLayout(const QString &name, int index, bool notify); + virtual void delLayout(TULayout *layout, bool notify); + void delLayouts(); + TULayout *duplicateLayout(TULayout *layout); + TULayout *findLayout(const QString &name, int index = -1); + int findLayoutIndex(const QString &name, int index = -1); + int findLayoutIndex(TULayout *layout); + TULayout *loadLayout(const QString &name, int index); + virtual void loadLayouts(bool reloadFromDisk); + void loadLayouts(const QDir &dir, int index); + void renameLayout(TULayout *layout, const QString &newName); + bool resetLayouts(const QDir &sourceDir); + void saveLayout(const QString &name, int index); + void saveLayout(TULayout *layout, bool notify); + void saveLayoutToolbar(); + + // Layout toolbar (workspace bar) + void addToLayoutToolbar(TULayout *layout); + void clearLayoutToolbar(); + std::vector &getToolbarLayouts(); + void setLayoutToolbarIndex(TULayout *layout, int index); + + // Accessors + TULayout *getCurrentLayout(); + TULayout *getPreviousLayout(); + size_t getLayoutCount(); + void setCurrentLayout(TULayout *layout); + QString getFileNameForLayout(TULayout *layout, TULayout *parent = nullptr); + bool isLayoutsModified() const; + bool isLocked(); + + // Toolbar configuration + const QList *getGlobalToolbarConfig(const QString &name, + bool defaultConfig) const; + const QList *getViewToolbarConfig(const QString &name, + bool defaultConfig) const; + void setGlobalToolbarConfig(const QString &name, const QList &config, + bool isDefault); + void setViewToolbarConfig(const QString &name, const QList &config, + bool isDefault); + +protected: + void loadLayoutInfo(const QString &path, bool initial); + void selectDefaultLayout(); + void setUniqueLayout(QString &name); + static void setUniqueLayout(const QList &existing, QString &name); + void addToolbarConfig( + std::map &configMap, const QString &name, + const QList &config, bool isDefault); + void clearToolbarConfig(std::map &configMap, + bool defaultOnly); + +private: + // Member layout (x64): + // vptr at +0x00 + TULayout *m_currentLayout; // +0x08 + TULayout *m_previousLayout; // +0x10 + std::vector m_layouts; // +0x18 (24 bytes) + std::vector m_toolbarLayouts; // +0x30 (24 bytes) + QString m_layoutPath; // +0x50 (24 bytes) + std::map *m_globalToolbarConfig; // +0x68 + void *_reserved; // +0x70 + std::map *m_viewToolbarConfig; // +0x78 + int m_flags; // +0x80 +}; + +/** + * @brief Service interface for toolbar management + * + * Empty interface class for toolbar-related plugin services. + */ +class PLUG_ToolbarService { +public: + PLUG_ToolbarService() = default; + virtual ~PLUG_ToolbarService() = default; +}; + +/** + * @brief Service interface for menu management + * + * Empty interface class for menu-related plugin services. + */ +class PLUG_MenuService { +public: + PLUG_MenuService() = default; + virtual ~PLUG_MenuService() = default; +}; + +/** + * @brief Private implementation data for TULayoutManager + * + * Holds the internal state including frame, area, and splitter vectors. + * + * sizeof(TULayoutManager_Private) = 0x110 (272 bytes) on x64 + */ +class TULayoutManager_Private : public QObject { + Q_OBJECT + +public: + explicit TULayoutManager_Private(TULayoutManager *owner); + virtual ~TULayoutManager_Private(); + + // Accessors + QFrame *getMainFrame() const { return m_mainFrame; } + WID_VBoxLayout *getMainLayout() const { return m_mainLayout; } + const std::vector &getSplitters() const { + return m_splitters; + } + const std::vector &getFrames() const { return m_frames; } + const std::vector &getAreas() const { return m_areas; } + const std::vector &getPluginAreas() const { + return m_pluginAreas; + } + TULayoutFrame *getCurrentLayoutFrame() const { return m_currentLayoutFrame; } + AC_Manager *getActionManager() const { return m_actionManager; } + QPoint getSavedPos() const { return m_savedPos; } + QSize getSavedSize() const { return m_savedSize; } + + // Mutators + void setMainFrame(QFrame *frame) { m_mainFrame = frame; } + void setMainLayout(WID_VBoxLayout *layout) { m_mainLayout = layout; } + void setCurrentLayoutFrame(TULayoutFrame *frame) { + m_currentLayoutFrame = frame; + } + void setActionManager(AC_Manager *manager) { m_actionManager = manager; } + void setSavedPos(const QPoint &pos) { m_savedPos = pos; } + void setSavedSize(const QSize &size) { m_savedSize = size; } + +private: + // Member layout (x64): + // QObject members at +0x00 to +0x0F + QFrame *m_mainFrame; // +0x10 + WID_VBoxLayout *m_mainLayout; // +0x18 + char _reserved[0x48]; // +0x20-0x5F (padding) + std::vector m_splitters; // +0x60 + std::vector m_frames; // +0x78 + std::vector m_areas; // +0x90 + std::vector m_pluginAreas; // +0xA8 + QPoint m_savedPos; // +0xC0 + QSize m_savedSize; // +0xC8 + int m_stateFlags; // +0xD0 + int _padding; // +0xD4 + TULayoutFrame *m_currentLayoutFrame; // +0xD8 + char _reserved2[0x10]; // +0xE0-0xEF (padding) + TULayoutManager *m_owner; // +0xF0 + AC_Manager *m_actionManager; // +0xF8 +}; + +/** + * @brief Central manager for the entire layout system + * + * Manages all frames, areas, splitters, toolbars, and layout persistence. + * Inherits from QMainWindow, TULayoutStorage, PLUG_ToolbarService, and + * PLUG_MenuService to provide comprehensive layout management functionality. + * + * This is the main entry point for the Toon Boom layout system. + */ +class TULayoutManager : public QMainWindow, + public TULayoutStorage, + public PLUG_ToolbarService, + public PLUG_MenuService { + Q_OBJECT + +public: + explicit TULayoutManager(const QString &layoutPath); + virtual ~TULayoutManager(); + + // Frame management + TULayoutFrame *addFrame(const QString &name, bool floating, bool unknown, + bool singleViewMode); + TULayoutFrame *addFrame(QWidget *parent, const double &ratio, bool floating, + bool unknown, bool singleViewMode); + void delFrame(TULayoutFrame *frame); + bool closeFrame(TULayoutFrame *frame); + TULayoutFrame *findFrame(int index) const; + TULayoutFrame *findFrame(QWidget *widget) const; + TULayoutFrame *findFrame(TULayoutView *view) const; + TULayoutFrame *findFirstChildFrame(QWidget *widget); + int findNoFrame(TULayoutFrame *frame); + int getNbFrame() const; + int getNbFrameDocked(); + const std::vector &getFrames() const; + TULayoutFrame *getCurrentLayoutFrame() const; + void setCurrentLayoutFrame(TULayoutFrame *frame); + + // Area management + bool addArea(const char *type, const QString &name, TULayoutView *view, + bool visible, bool createFrame, bool docked, const QSize &minSize, + bool useMinSize, bool isPlugin, bool defaultVisible, bool unknown); + bool addPluginArea(const char *type, const QString &name, TULayoutView *view, + bool visible, bool createFrame, bool docked, + const QSize &minSize, bool useMinSize, bool defaultVisible); + void delAreas(TULayoutFrame *frame = nullptr); + TULayoutArea *findArea(const QString &name); + TULayoutArea *findArea(QWidget *widget); + TULayoutArea *findArea(TULayoutFrame *frame); + TULayoutArea *findArea(TULayoutView *view) const; + int findNoArea(QWidget *widget); + int findNoArea(TULayoutFrame *frame); + int findNoArea(const char *name); + int getNbArea() const; + bool isValidAreaID(int id); + int isValidAreaName(const QString &name); + const std::vector &getAreas() const; + void getAvailableWindows(std::vector &windows); + void commitPluginAreas(); + + // View management + bool addView(const char *type, const QString &name, TULayoutView *&view, + TULayoutFrame *&frame, bool visible, double ratio); + bool addView(const char *type, const QString &name, TULayoutFrame *frame, + double ratio, Qt::Orientation orientation, TULayoutView *&view, + TULayoutFrame *&newFrame, bool visible, double splitRatio); + bool addViewHorizontal(const char *type, const QString &name, + TULayoutFrame *frame, double ratio, TULayoutView *&view, + TULayoutFrame *&newFrame, bool visible, double splitRatio); + bool addViewVertical(const char *type, const QString &name, + TULayoutFrame *frame, double ratio, TULayoutView *&view, + TULayoutFrame *&newFrame, bool visible, double splitRatio); + bool addDetached(const QString &name, const QString &type, TULayoutView *&view, + TULayoutFrame *&frame); + void deleteAllViews(); + TULayoutView *findInstance(const QString &name); + TULayoutView *findInstance(const QWidget *widget); + TULayoutView *findInstance(const TULayoutFrame *frame, bool current); + TULayoutView *findInstance(const TULayoutViewHolder *holder, bool current); + TULayoutView *findInstanceOfType(const QString &typeName); + TULayoutView *findFirstInstance(); + void findAllInstances(const QString &typeName, + std::vector &results); + QString findInstanceName(TULayoutFrame *frame); + void getViews(const TULayoutFrame *frame, + std::vector *views) const; + bool hasView(const char *name); + TULayoutView *currentView(); + bool validateView(const QString &name, TULayoutView *view); + + // Splitter management + TULayoutSplitter *getRootSplitter(); + void delSplitter(TULayoutSplitter *splitter); + int getNbSplitter() const; + const std::vector &getSplitters() const; + void sortFramesRecursive(TULayoutSplitter *splitter, int &index); + int isFrame(QWidget *widget); + int isSplitter(QWidget *widget); + + // Layout operations + virtual bool changeLayout(const QString &name, int index); + void deleteLayout(); + void updateLayout(); + void expandMainWindow(); + void showFrames(); + + // Toolbar management + virtual AC_Toolbar *addToolbar(const char *name); + virtual void addToolbarFromFile(const QString &path, const QString &name, + bool global); + virtual void addToolbarFromElement(const QDomElement &element, + const QString &name, bool global); + virtual AC_Toolbar *showToolbar(const char *name, bool show, bool global, + AC_ToolbarItemGenerator *generator); + virtual bool setToolbarVisibility(const QString &name, bool visible); + virtual bool removeToolbar(const char *name); + virtual AC_Toolbar *toolbar(const char *name); + void addGlobalToolbar(QToolBar *toolbar); + void registerGlobalToolbar(const QToolBar *toolbar); + void clearGlobalToolBarList(); + void getAllToolbars(std::list &viewToolbars, + std::list &globalToolbars, bool includeHidden); + bool isGlobalToolbar(const QString &name) const; + bool isGlobalToolbar(const QToolBar *toolbar) const; + virtual bool getMainToolbarDefaultVisibility(const QToolBar &toolbar) const; + void showViewToolBars(); + void removeViewToolBar(); + void showDisplayToolBar(bool show); + void showPortNameToolBar(bool show); + void hideLayoutToolBar(); + void createWorkspaceToolbar(); + void destroyWorkspaceToolbar(); + void customizeToolBar(); + + // Menu management + virtual bool addMenuFromFile(const QString &path, const QString &name); + virtual bool addMenuFromElement(const QString &name, + const QDomElement &element); + + // Fullscreen + void fullScreenFrame(TULayoutFrame *frame); + void fullScreenMainFrame(TULayoutFrame *frame); + void restoreNormalScreen(); + + // Accessors + void setActionManager(AC_Manager *manager); + QFrame *getMainFrame() const; + QString getDefaultLayoutsPath() const; + virtual const unsigned int &getCurrentViewBorderColor(); + QString unconnectedDisplayNodeName() const; + QString unconnectedDisplayNodeNameTranslated() const; + QString noPortFilterNameTranslated() const; + + // Event handlers + virtual void closeEvent(QCloseEvent *event) override; + virtual void moveEvent(QMoveEvent *event) override; + virtual void resizeEvent(QResizeEvent *event) override; + virtual QMenu *createPopupMenu() override; + + // Layout operations + virtual void loadLayouts(bool reloadFromDisk) override; + virtual void updateLayoutBar(); + virtual void updateLayoutBarSelection(); + virtual void onPreferenceChanged(); + virtual void onActionFullscreen(); + virtual void onActionFullscreenValidate(AC_ActionInfo *info); + + // Composite management + void compositeAdded(const QString &name); + void compositeDeleted(const QString &name); + void raiseWindow(); + TULayoutView *raiseArea(const QString &name, TULayoutFrame *frame, + bool createNew, const QPoint &pos); + TULayoutView *raiseArea(int areaIndex, TULayoutFrame *frame, bool createNew, + const QPoint &pos); + TULayoutView *raiseArea(TULayoutArea *area, TULayoutFrame *frame, + bool createNew, const QPoint &pos); + TULayoutView *newDetached(TULayoutArea *area, QPoint pos); + void saveCurrentLayout(); + +signals: + void backgroundImageChanged(); + void fullScreenStateChanged(); + void layoutChanged(TULayout *layout); + void preferencesChange(); + void sceneSaved(); + +public slots: + void onActionRaiseArea(QString name, bool createNew); + void onActionSaveLayouts(); + void onActionSaveWorkspaceAs(); + void onActionShowLayoutManager(); + void onLayoutMenu(int index); + void onPortNamesChanged(int index); + void onPortNamesChanged(QString name); + void onDefaultDisplayChanged(int index); + +protected slots: + void closeLayoutCheck(); + void refresh(); + void resetDisplayLists(); + void resetPortNamesLists(); + void compositeOrGroupRenamed(); + void updateAutoSaveLayoutVisibilityStatus(); + +protected: + void layoutFrameBeingDeleted(TULayoutFrame *frame); + +private: + void sortToolbarList(QList list, + std::list &viewToolbars, + std::list &globalToolbars, bool includeHidden); + QToolBar *getToolbarUnderMouse(); + void moveGlobalToolbar(); + void toolbarWasCustomized(QString name); + void updateToolBarScriptIcons(const QString &oldName, const QString &newName, + const QString &path); + + // Member layout (x64): + // QMainWindow members at +0x00 to +0x27 + // TULayoutStorage members at +0x28 to +0xAF + // PLUG_ToolbarService at +0xB0 + // PLUG_MenuService at +0xB8 + bool m_unknown; // +0xC0 + char _padding[7]; // +0xC1-0xC7 + TULayoutManager_Private *m_private; // +0xC8 +}; + +#endif // TOON_BOOM_LAYOUT_HPP diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..53b61fa --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "minhook" + ] +}