This document describes the plugin API for ResourceDragon, which allows you to create custom archive format handlers as dynamically loaded plugins.
The plugin system allows you to extend ResourceDragon with support for custom archive formats without modifying the core application. Plugins are compiled as shared libraries (.so on Linux, .dll on Windows) and loaded at runtime.
A plugin must export the following symbols:
- Plugin Metadata
extern "C" const char* RD_PluginName = "Your Plugin Name";
extern "C" const char* RD_PluginVersion = "1.0.0";- Plugin Functions
RD_EXPORT bool RD_PluginInit(HostAPI* api);
RD_EXPORT void RD_PluginShutdown();
RD_EXPORT const ArchiveFormatVTable* RD_GetArchiveFormat(struct sdk_ctx* ctx);The HostAPI structure provides access to host application functions:
struct HostAPI {
sdk_ctx* (*get_sdk_context)();
LogFn_t log;
LogFn_t warn;
LogFn_t error;
};get_sdk_context(): Returns the SDK context for this pluginlog(ctx, msg, ...): Log an informational messagewarn(ctx, msg, ...): Log a warning messageerror(ctx, msg, ...): Log an error message
For formatted output, call the C-friendly rd_log_fmtv(level, fmt, args, arg_count) helper. Build the args array with the provided helpers (e.g. rd_log_make_cstring, rd_log_make_s64, rd_log_make_u64, rd_log_make_f64, rd_log_make_bool) so the host can safely interpret each placeholder.
C plugins (and any language using the C ABI) can include SDK/util/rd_log_helpers.h to avoid manually computing the argument count. That header provides macros such as:
#include "SDK/util/rd_log_helpers.h"
RD_LOG_INFO(
"Opened '{}' with {} entries",
rd_log_make_cstring(name),
rd_log_make_u64(entry_count)
);The macro builds the temporary RD_LogArg[], calculates its length, and forwards everything to rd_log_fmtv. Use RD_LOGF, RD_LOG_INFOF, RD_LOG_WARNF, RD_LOG_ERRORF, or their *F0 counterparts when you have no placeholders.
If you prefer the manual steps (or are targeting another FFI), follow this flow instead:
- Populate an
RD_LogArg fmt_args[]array using the helpers that match your value types. - Pass
fmt_argsalong with its element count tord_log_fmtv. - Let the host expand the format string and route the message through its logger.
C++ plugins may include SDK/util/Logger.h and use the fmt-style overloads (Logger::log, Logger::warn, Logger::error) just like the native app code (e.g., Logger::log("some float: {}", 3.14);).
The sdk_ctx structure contains plugin runtime information:
struct sdk_ctx {
int version;
struct Logger* logger;
struct ArchiveFormatWrapper* archiveFormat;
};The core of your plugin is the ArchiveFormatVTable which defines how your format behaves:
typedef struct ArchiveFormatVTable {
ArchiveHandle (*New)(struct sdk_ctx* ctx);
int (*CanHandleFile)(ArchiveHandle inst, u8* buffer, u64 size, const char* ext);
ArchiveBaseHandle (*TryOpen)(ArchiveHandle inst, u8* buffer, u64 size, const char* file_name);
const char *(*GetTag)(ArchiveHandle inst);
const char *(*GetDescription)(ArchiveHandle inst);
} ArchiveFormatVTable;- New: Create a new instance of your archive handler
- CanHandleFile: Return non-zero if your plugin can handle the given file
- TryOpen: Attempt to open a file as your archive format
- GetTag: Return a short identifier for your format (e.g., "ZIP", "TAR")
- GetDescription: Return a human-readable description
When TryOpen succeeds, it returns an ArchiveBaseHandle containing:
struct ArchiveBaseVTable {
usize (*GetEntryCount)(ArchiveInstance inst);
const char* (*GetEntryName)(ArchiveInstance inst, usize index);
usize (*GetEntrySize)(ArchiveInstance inst, usize index);
u8* (*OpenStream)(ArchiveInstance inst, usize index, usize* out_size);
};- GetEntryCount: Return the number of files in the archive
- GetEntryName: Return the name of the file at the given index
- GetEntrySize: Return the size of the file at the given index
- OpenStream: Extract and return the data for the file at the given index
cmake_minimum_required(VERSION 3.10)
project(YourPlugin VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 23)
# SDK paths
set(SDK_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../../SDK)
set(SDK_UTIL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../../SDK/util)
add_library(your_plugin SHARED
main.cpp
)
target_include_directories(your_plugin PRIVATE
${SDK_INCLUDE_DIR}
${SDK_UTIL_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/../..
)
set_target_properties(your_plugin PROPERTIES
PREFIX ""
OUTPUT_NAME "your_plugin"
)
if(WIN32)
set_target_properties(your_plugin PROPERTIES SUFFIX ".dll")
else()
set_target_properties(your_plugin PROPERTIES SUFFIX ".so")
endif()
set_target_properties(your_plugin PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../../plugins
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../../plugins
)- Create your plugin directory, should be a separate project.
- Write your plugin implementation
- Create a CMakeLists.txt file
- Build with:
mkdir build && cd build && cmake .. -G Ninja && ninja - The resulting shared library should be placed in the
plugins/directory
See plugins/example/example_plugin.cpp for a complete working example
Plugins are automatically loaded from the plugins/ directory when ResourceDragon starts. The application will:
- Scan for shared libraries (.so/.dll files)
- Load each library and check for required exports
- Initialize plugins that pass validation
- Register their archive formats with the main application
- Use the logging functions provided in the HostAPI for debugging
- Return
falsefromRD_PluginInitif initialization fails - Ensure proper cleanup in
RD_PluginShutdown - Handle null pointers and invalid parameters gracefully
- Data returned by
OpenStreamshould always be allocated withmalloc, it will be freed by ResourceDragon itself usingfreelater. - The caller will free the memory, so ensure it's compatible
- Use
.dllextension - Export functions with
__declspec(dllexport) - Consider calling conventions (
__cdecl)
- Use
.soextension - Compile with
-fPIC