From c9f25e7b6ab6690583d20634737c93381aa6b670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=99=E2=97=A6=20The=20Tablet=20=E2=9D=80=20GamerGirla?= =?UTF-8?q?ndCo=20=E2=97=A6=E2=9D=A7?= Date: Sat, 10 Jan 2026 17:49:22 -0500 Subject: [PATCH] =?UTF-8?q?hello=20world=20=F0=9F=8C=B8=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 8 + 3rdparty/QtScript.lib | Bin 0 -> 163020 bytes 3rdparty/ToonBoomActionManager.lib | Bin 0 -> 351176 bytes 3rdparty/ToonBoomLayout.lib | Bin 0 -> 568848 bytes 3rdparty/ToonBoomPlugInManager.lib | Bin 0 -> 74988 bytes 3rdparty/dll2def.ps1 | 42 + CMakeLists.txt | 34 + CMakePresets.json | 59 + docs/HarmonyPremium_QtScript.md | 210 ++++ docs/README.md | 7 + docs/ToonBoomLayout_Classes.md | 516 ++++++++ docs/ToonBoomLayout_ViewUsage.md | 321 +++++ docs/ToonBoomPlugInManager_PLUG_Services.md | 335 ++++++ framework/CMakeLists.txt | 56 + framework/hook/harmony_signatures.cpp | 199 ++++ framework/hook/hook.cpp | 82 ++ framework/hook/sigscan.cpp | 130 ++ framework/include/hooks/toon_boom_hooks.hpp | 15 + .../include/internal/harmony_signatures.hpp | 33 + framework/include/internal/sigscan.hpp | 40 + framework/include/toon_boom/PLUG_Services.hpp | 453 +++++++ .../include/toon_boom/toon_boom_layout.hpp | 1045 +++++++++++++++++ vcpkg.json | 5 + 23 files changed, 3590 insertions(+) create mode 100644 .gitignore create mode 100644 3rdparty/QtScript.lib create mode 100644 3rdparty/ToonBoomActionManager.lib create mode 100644 3rdparty/ToonBoomLayout.lib create mode 100644 3rdparty/ToonBoomPlugInManager.lib create mode 100644 3rdparty/dll2def.ps1 create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 docs/HarmonyPremium_QtScript.md create mode 100644 docs/README.md create mode 100644 docs/ToonBoomLayout_Classes.md create mode 100644 docs/ToonBoomLayout_ViewUsage.md create mode 100644 docs/ToonBoomPlugInManager_PLUG_Services.md create mode 100644 framework/CMakeLists.txt create mode 100644 framework/hook/harmony_signatures.cpp create mode 100644 framework/hook/hook.cpp create mode 100644 framework/hook/sigscan.cpp create mode 100644 framework/include/hooks/toon_boom_hooks.hpp create mode 100644 framework/include/internal/harmony_signatures.hpp create mode 100644 framework/include/internal/sigscan.hpp create mode 100644 framework/include/toon_boom/PLUG_Services.hpp create mode 100644 framework/include/toon_boom/toon_boom_layout.hpp create mode 100644 vcpkg.json 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 0000000000000000000000000000000000000000..1eab6342d3f6dd10b67306a6d81c2b7045a8f5a8 GIT binary patch literal 163020 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 z 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 L^Igb+UPt(Us((pk literal 0 HcmV?d00001 diff --git a/3rdparty/ToonBoomActionManager.lib b/3rdparty/ToonBoomActionManager.lib new file mode 100644 index 0000000000000000000000000000000000000000..b80c3c68fd03008567cfcb9f2a21832f04b98a0a GIT binary patch literal 351176 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

FuY=9V%>&Tqf(nH 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%0Dh?Y#X1YBp&wJe=9V1d*3v_U2E?F`w)G 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 yY30}2L2zZTF8O` literal 0 HcmV?d00001 diff --git a/3rdparty/ToonBoomLayout.lib b/3rdparty/ToonBoomLayout.lib new file mode 100644 index 0000000000000000000000000000000000000000..121edad36201ec9cad4f95bd98320af272abdf5c GIT binary patch literal 568848 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

h})E4Wjm$=nX$LSJD@^)`( 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

-ETsffW~LK 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ℑgMU9#Fh5;x?(0@?9)eC96iBUu-Gdp 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 b5{>j0EQzO7-363$gyEwMgYK3~3x@v%diO(~ literal 0 HcmV?d00001 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" + ] +}