Frosty Toolsuite’s plugin system provides a flexible architecture for extending functionality without modifying core code. Plugins can add support for new asset types, custom editors, menu items, and more.
Plugin Architecture
Plugins are .NET assemblies loaded dynamically at runtime.
Plugin Discovery:
public class PluginManager
{
private List < Plugin > m_plugins = new List < Plugin >();
private List < Plugin > m_loadedPlugins = new List < Plugin >();
public PluginManager ( ILogger logger , PluginManagerType context )
{
m_managerType = context ;
// Scan Plugins directory
foreach ( string item in Directory . EnumerateFiles (
"Plugins" , "*.dll" , SearchOption . AllDirectories ))
{
FileInfo fileInfo = new FileInfo ( item );
m_plugins . Add ( LoadPlugin ( fileInfo . FullName , PluginLoadType . Startup ));
}
}
}
From PluginManager.cs:122.
Plugin Lifecycle
Plugins are loaded in two phases:
Startup Phase
Loaded before game profile selection:
Profile definitions
Global type editors
Type overrides
Startup actions
Initialize Phase
Loaded after profile is selected:
Asset definitions (game-specific)
Custom handlers
Menu extensions
Tab extensions
Options pages
Loading Process:
public void Initialize ()
{
foreach ( Plugin plugin in m_plugins )
{
if ( IsValidToLoadPlugin ( plugin . Assembly ))
{
LoadDefinitionsFromAssembly ( PluginLoadType . Initialize , plugin . Assembly );
m_loadedPlugins . Add ( plugin );
}
else
{
plugin . Status = PluginLoadStatus . LoadedInvalid ;
}
}
}
From PluginManager.cs:151.
Plugin Attributes
Plugins use assembly-level attributes to declare functionality:
Core Attributes
Extension Types
Asset Definitions
Define how asset types are displayed and edited:
[ RegisterAssetDefinition ( "MeshAsset" , typeof ( MeshAssetDefinition ))]
public class MeshAssetDefinition : AssetDefinition
{
public override void GetSupportedFileTypes ( List < string > extensions )
{
extensions . Add ( ".fbx" );
extensions . Add ( ".obj" );
}
public override bool CanExport ( object asset )
{
return asset is MeshAsset ;
}
public override async Task < bool > Export (
EbxAssetEntry entry ,
string path ,
string filterType )
{
MeshAsset meshAsset = App . AssetManager . GetEbx ( entry ). RootObject ;
// Export logic...
return true ;
}
}
From PluginManager.cs:452.
Asset definitions registered for a base type automatically apply to all derived types via the TypeLibrary.
Custom Handlers
Provide specialized editing for specific asset types:
EBX Handler
Resource Handler
[ RegisterCustomHandler (
CustomHandlerType . Ebx ,
"TextureAsset" ,
typeof ( TextureAssetHandler ))]
public class TextureAssetHandler : ICustomActionHandler
{
public bool CanEdit ( object asset )
{
return asset is TextureAsset ;
}
public void Edit ( EbxAssetEntry entry )
{
// Open texture editor
TextureEditorWindow window = new TextureEditorWindow ( entry );
window . ShowDialog ();
}
}
[ RegisterCustomHandler (
CustomHandlerType . Res ,
ResourceType . SwfMovie ,
typeof ( SwfMovieHandler ))]
public class SwfMovieHandler : ICustomActionHandler
{
public void Import ( ResAssetEntry entry , string filename )
{
// Import SWF file
byte [] data = File . ReadAllBytes ( filename );
App . AssetManager . ModifyRes ( entry . Name , data );
}
public void Export ( ResAssetEntry entry , string filename )
{
using ( Stream stream = App . AssetManager . GetRes ( entry ))
{
using ( FileStream fs = new FileStream (
filename , FileMode . Create ))
{
stream . CopyTo ( fs );
}
}
}
}
From PluginManager.cs:540.
Add custom menu items to the editor:
[ RegisterMenuExtension ( typeof ( MyMenuExtension ))]
public class MyMenuExtension : MenuExtension
{
public override string TopLevelMenuName => "Tools" ;
public override string SubLevelMenuName => null ;
public override string MenuItemName => "My Custom Tool" ;
public override void MenuItemClicked ( ILogger logger , object dataContext )
{
// Handle menu click
CustomToolWindow window = new CustomToolWindow ();
window . ShowDialog ();
}
}
From PluginManager.cs:485.
Tab Extensions
Add custom tabs to the main editor window:
[ RegisterTabExtension ( typeof ( MyCustomTab ))]
public class MyCustomTab : TabExtension
{
public override string TabItemName => "My Tab" ;
public override int TabItemPriority => 100 ;
public override FrostyTabItem CreateTabItem ()
{
return new FrostyTabItem
{
Content = new MyCustomTabControl (),
Header = TabItemName
};
}
}
From PluginManager.cs:524.
Add right-click menu items in the asset browser:
[ RegisterDataExplorerContextMenu ( typeof ( ExportAllTexturesMenu ))]
public class ExportAllTexturesMenu : DataExplorerContextMenuExtension
{
public override string ContextItemName => "Export All Textures" ;
public override bool IsValidForSelection (
IEnumerable < EbxAssetEntry > selectedAssets )
{
return selectedAssets . Any (
a => a . Type == "TextureAsset" );
}
public override void ContextItemClicked (
IEnumerable < EbxAssetEntry > selectedAssets ,
ILogger logger )
{
foreach ( var asset in selectedAssets )
{
if ( asset . Type == "TextureAsset" )
{
// Export texture...
}
}
}
}
From PluginManager.cs:530.
Startup Actions
Execute code during application startup:
[ RegisterStartupAction ( typeof ( MyStartupAction ))]
public class MyStartupAction : StartupAction
{
public override void Run ( ILogger logger , PluginManagerType type )
{
// Initialize libraries
// Load configuration
// Register handlers
logger . Log ( "MyPlugin initialized" );
}
}
From PluginManager.cs:445.
Execution Actions (Mod Manager)
Execute during mod application:
[ RegisterExecutionAction ( typeof ( MyExecutionAction ))]
public class MyExecutionAction : ExecutionAction
{
public override string ActionName => "Custom Asset Patcher" ;
public override void Execute (
ILogger logger ,
IEnumerable < EbxAssetEntry > modifiedAssets )
{
// Patch assets during mod application
foreach ( var asset in modifiedAssets )
{
// Custom processing...
}
}
}
From PluginManager.cs:536.
Type System Integration
Global Type Editors
Custom editors for specific field types:
[ RegisterGlobalTypeEditor ( "Vec3" , typeof ( Vec3Editor ))]
public class Vec3Editor : FrostyTypeEditor
{
public override void CreateUI ( object value )
{
Vec3 vec = ( Vec3 ) value ;
// Create X, Y, Z input controls
}
public override object GetValue ()
{
return new Vec3 ( xValue , yValue , zValue );
}
}
From PluginManager.cs:441.
Type Overrides
Replace type handling entirely:
[ RegisterTypeOverride ( "LinearTransform" , typeof ( CustomTransformType ))]
public class CustomTransformType
{
// Custom implementation...
}
From PluginManager.cs:437.
Shader Registration
Register custom shaders for rendering:
[ assembly : RegisterShader (
ShaderType . VertexShader ,
"MyShader" ,
"Shaders.MyVertexShader.cso" )]
[ assembly : RegisterShader (
ShaderType . PixelShader ,
"MyShader" ,
"Shaders.MyPixelShader.cso" )]
Shader bytecode must be embedded as manifest resources.
Custom Asset Managers
Handle non-standard asset storage:
[ RegisterCustomAssetManager (
"legacy" ,
typeof ( LegacyAssetManager ))]
public class LegacyAssetManager : ICustomAssetManager
{
public void Initialize ( ILogger logger )
{
// Load legacy file formats
}
public AssetEntry GetAssetEntry ( string key )
{
// Retrieve from custom storage
}
public Stream GetAsset ( AssetEntry entry )
{
// Read asset data
}
public void ModifyAsset ( string key , byte [] data )
{
// Save modifications
}
public IEnumerable < AssetEntry > EnumerateAssets ( bool modifiedOnly )
{
// Iterate custom assets
}
}
From PluginManager.cs:576 and AssetManager.cs:430.
Profile Registration
Add support for new games:
[ RegisterProfile ( typeof ( MyGameProfile ))]
public class MyGameProfile : IProfile
{
public Profile CreateProfile ()
{
return new Profile
{
Name = "MyGame" ,
DisplayName = "My Game (2024)" ,
DataVersion = 20240101 ,
Deobfuscator = "MyGameDeobfuscator" ,
AssetLoader = "MyGameAssetLoader" ,
Sources = new List < FileSystemSource >
{
new FileSystemSource { Path = "Data" , SubDirs = false }
},
// Additional configuration...
};
}
}
From PluginManager.cs:433.
Plugin Example: Texture Plugin
Complete example of a texture handling plugin:
using Frosty . Core ;
using Frosty . Core . Attributes ;
using FrostySdk . Managers ;
[ assembly : PluginDisplayName ( "Texture Plugin" )]
[ assembly : PluginAuthor ( "Plugin Author" )]
[ assembly : PluginVersion ( "1.0.0.0" )]
// Register for all Frostbite games
[ assembly : RegisterAssetDefinition (
"TextureAsset" ,
typeof ( TextureAssetDefinition ))]
[ assembly : RegisterCustomHandler (
CustomHandlerType . Res ,
ResourceType . Texture ,
typeof ( TextureResourceHandler ))]
namespace TexturePlugin
{
public class TextureAssetDefinition : AssetDefinition
{
public override void GetSupportedFileTypes ( List < string > extensions )
{
extensions . Add ( ".dds" );
extensions . Add ( ".png" );
extensions . Add ( ".tga" );
}
public override async Task < bool > Export (
EbxAssetEntry entry ,
string path ,
string filterType )
{
// Get texture resource
TextureAsset texAsset =
App . AssetManager . GetEbx ( entry ). RootObject ;
ResAssetEntry resEntry =
App . AssetManager . GetResEntry ( texAsset . Resource );
Texture texture =
App . AssetManager . GetResAs < Texture >( resEntry );
// Export based on filter type
if ( filterType == ".dds" )
{
texture . SaveToDds ( path );
}
else if ( filterType == ".png" )
{
texture . SaveToPng ( path );
}
return true ;
}
public override async Task < bool > Import (
EbxAssetEntry entry ,
string path ,
ILogger logger )
{
// Load texture from file
Texture newTexture = Texture . LoadFromFile ( path );
// Get existing texture resource
TextureAsset texAsset =
App . AssetManager . GetEbx ( entry ). RootObject ;
ResAssetEntry resEntry =
App . AssetManager . GetResEntry ( texAsset . Resource );
// Modify resource with new data
App . AssetManager . ModifyRes (
resEntry . ResRid ,
newTexture );
logger . Log ( $"Imported texture: { entry . Name } " );
return true ;
}
}
public class TextureResourceHandler : ICustomActionHandler
{
public bool CanEdit ( object asset )
{
return asset is Texture ;
}
public void Edit ( ResAssetEntry entry )
{
// Open texture viewer/editor
TextureEditorWindow editor =
new TextureEditorWindow ( entry );
editor . ShowDialog ();
}
}
}
Plugin Best Practices
Recommendations:
Use appropriate load phases - Startup for core functionality, Initialize for game-specific
Validate profile compatibility - Use PluginValidForProfile attributes
Handle errors gracefully - Catch exceptions and log to ILogger
Mark dependencies - Use RegisterThirdPartyDll for libraries
Follow naming conventions - Clear, descriptive class and attribute names
Document public APIs - Add XML comments for plugin developers
Test across games - Verify behavior with different profiles
Version appropriately - Use semantic versioning
Plugin Development Workflow
Create Project
Create a .NET Framework 4.6.1+ class library project
Reference Frosty
Add references to FrostySdk.dll and FrostyCore.dll
Implement Extensions
Add classes with appropriate attributes
Build Plugin
Compile to DLL
Deploy
Copy DLL to Plugins folder in Frosty installation
Test
Launch Frosty Editor/Mod Manager and verify plugin loads
Plugin Debugging
Attach Visual Studio debugger to Frosty process:
<!-- project.csproj -->
< PropertyGroup >
< DebugType > full </ DebugType >
< DebugSymbols > true </ DebugSymbols >
< OutputPath > C:\Path\To\Frosty\Plugins\MyPlugin\ </ OutputPath >
</ PropertyGroup >
Set breakpoints and use Debug → Attach to Process → FrostyEditor.exe
Common Plugin Patterns
[ RegisterMenuExtension ( typeof ( BatchProcessMenu ))]
public class BatchProcessMenu : MenuExtension
{
public override void MenuItemClicked ( ILogger logger , object context )
{
foreach ( var entry in App . AssetManager . EnumerateEbx ( "MyAssetType" ))
{
EbxAsset asset = App . AssetManager . GetEbx ( entry );
// Process asset
App . AssetManager . ModifyEbx ( entry . Name , asset );
}
logger . Log ( "Batch processing complete" );
}
}
Custom File Format Import
public class ResourcePreviewTab : TabExtension
{
public override FrostyTabItem CreateTabItem ()
{
var tab = new FrostyTabItem
{
Header = "Preview" ,
Content = new PreviewControl ()
};
// Update preview when asset selection changes
App . SelectedAssetChanged += ( s , e ) =>
{
if ( e . Asset is ResAssetEntry resEntry )
{
UpdatePreview ( resEntry );
}
};
return tab ;
}
}
Next Steps
Architecture Understand the overall system architecture
Asset System Learn about EBX, RES, and chunk asset management