├── cmake ├── BMLConfig.cmake.in ├── BMLUtils.cmake └── FindVirtoolsSDK.cmake ├── src ├── Core │ ├── DllMain.cpp │ ├── ResourceApi.h │ ├── ApiRegistration.h │ ├── Microkernel.h │ ├── ModuleDiscovery.h │ ├── Logging.h │ ├── DiagnosticApi.cpp │ ├── ModHandle.h │ ├── ModuleLoader.h │ ├── DependencyResolver.h │ ├── DiagnosticManager.cpp │ ├── ModManifest.h │ ├── Export.cpp │ ├── SemanticVersion.h │ ├── LoggingApi.cpp │ ├── ModuleRuntime.h │ ├── CMakeLists.txt │ ├── MemoryApi.cpp │ ├── DiagnosticManager.h │ ├── ProfilingManager.h │ └── MemoryManager.h ├── Version.h.in ├── EventHook.h ├── Logger.h ├── IMod.cpp ├── Utils │ ├── CMakeLists.txt │ ├── HookUtils.h │ └── HookUtils.cpp ├── ModLoader │ ├── Behaviors │ │ ├── HookBlock.h │ │ └── HookBlock.cpp │ ├── CMakeLists.txt │ └── ModManager.h ├── SRTimer.h ├── Gui │ ├── Panel.cpp │ ├── KeyInput.cpp │ ├── Element.cpp │ ├── Label.cpp │ ├── Text.cpp │ ├── Input.cpp │ └── Button.cpp ├── BML.rc ├── SRTimer.cpp ├── FpsCounter.h ├── Overlay.h ├── imgui_impl_ck2.h ├── FpsCounter.cpp ├── ModManager.h ├── CommandBar.h ├── HookBlock.cpp ├── CommandContext.h ├── Logger.cpp ├── ModManager.cpp ├── ModMenu.h ├── MapMenu.h ├── DataShare.hpp ├── Macros.h ├── Errors.cpp └── Config.h ├── include ├── BML │ ├── Version.h │ ├── Guids │ │ ├── Hooks.h │ │ ├── MidiManager.h │ │ ├── TT_DatabaseManager_RT.h │ │ ├── TT_Gravity_RT.h │ │ ├── WorldEnvironments.h │ │ ├── Lights.h │ │ ├── Sounds.h │ │ ├── physics_RT.h │ │ ├── Grids.h │ │ ├── Controllers.h │ │ ├── Cameras.h │ │ ├── TT_ParticleSystems_RT.h │ │ ├── Narratives.h │ │ ├── BuildingBlocksAddons1.h │ │ ├── MeshModifiers.h │ │ ├── 3DTransfo.h │ │ ├── Collisions.h │ │ ├── Interface.h │ │ ├── Visuals.h │ │ └── Materials.h │ ├── ILogger.h │ ├── BMLAll.h │ ├── Gui.h │ ├── Gui │ │ ├── Panel.h │ │ ├── KeyInput.h │ │ ├── Input.h │ │ ├── Label.h │ │ ├── Element.h │ │ ├── Button.h │ │ └── Text.h │ ├── Guids.h │ ├── ICommand.h │ ├── IConfig.h │ ├── IMessageReceiver.h │ ├── RefCount.h │ ├── DataShare.h │ ├── DataBox.h │ └── InputHook.h ├── bml_version.h ├── bml_engine_events.h ├── bml_hot_reload.h ├── bml_loader.h ├── bml_module.h ├── bml.hpp └── bml_export.h ├── tests ├── TestLoggingStub.cpp ├── ExportStub.cpp ├── ModuleLoaderStub.cpp ├── HotReloadSampleMod.cpp └── ManifestParserTest.cpp ├── templates └── mod-template │ ├── CMakeLists.txt │ ├── README.md │ └── src │ └── HelloMod.cpp ├── .gitignore ├── .gitmodules ├── deps └── CMakeLists.txt ├── benchmarks └── CMakeLists.txt ├── LICENSE ├── .github └── workflows │ └── build.yml └── CMakeLists.txt /cmake/BMLConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | find_dependency(VirtoolsSDK) 5 | 6 | include("${CMAKE_CURRENT_LIST_DIR}/BMLTargets.cmake") -------------------------------------------------------------------------------- /src/Core/DllMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID /*reserved*/) { 4 | if (reason == DLL_PROCESS_ATTACH) 5 | DisableThreadLibraryCalls(module); 6 | return TRUE; 7 | } 8 | -------------------------------------------------------------------------------- /include/BML/Version.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_VERSION_H 2 | #define BML_VERSION_H 3 | 4 | #define BML_MAJOR_VERSION 0 5 | #define BML_MINOR_VERSION 3 6 | #define BML_PATCH_VERSION 10 7 | #define BML_VERSION "0.3.10" 8 | 9 | #endif // BML_VERSION_H 10 | -------------------------------------------------------------------------------- /include/BML/Guids/Hooks.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_HOOKS_H 2 | #define BML_GUIDS_HOOKS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Hooks 8 | // 9 | 10 | // Building Blocks 11 | #define HOOKS_HOOKBLOCK_GUID CKGUID(0x19038c0, 0x663902da) 12 | 13 | #endif // BML_GUIDS_HOOKS_H 14 | -------------------------------------------------------------------------------- /src/Version.h.in: -------------------------------------------------------------------------------- 1 | #ifndef BML_VERSION_H 2 | #define BML_VERSION_H 3 | 4 | #define BML_MAJOR_VERSION @CMAKE_PROJECT_VERSION_MAJOR@ 5 | #define BML_MINOR_VERSION @CMAKE_PROJECT_VERSION_MINOR@ 6 | #define BML_PATCH_VERSION @CMAKE_PROJECT_VERSION_PATCH@ 7 | #define BML_VERSION "@CMAKE_PROJECT_VERSION@" 8 | 9 | #endif // BML_VERSION_H -------------------------------------------------------------------------------- /include/BML/ILogger.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_ILOGGER_H 2 | #define BML_ILOGGER_H 3 | 4 | #include "BML/Defines.h" 5 | 6 | class BML_EXPORT ILogger { 7 | public: 8 | virtual void Info(const char* fmt, ...) = 0; 9 | virtual void Warn(const char* fmt, ...) = 0; 10 | virtual void Error(const char* fmt, ...) = 0; 11 | 12 | virtual ~ILogger() = default; 13 | }; 14 | 15 | #endif // BML_ILOGGER_H -------------------------------------------------------------------------------- /include/BML/BMLAll.h: -------------------------------------------------------------------------------- 1 | #ifndef BMLALL_H 2 | #define BMLALL_H 3 | 4 | #include "BML/Defines.h" 5 | #include "BML/Guids.h" 6 | 7 | #include "BML/IBML.h" 8 | #include "BML/ILogger.h" 9 | #include "BML/ICommand.h" 10 | #include "BML/IConfig.h" 11 | #include "BML/IMod.h" 12 | #include "BML/DataShare.h" 13 | 14 | #include "BML/Gui.h" 15 | #include "BML/InputHook.h" 16 | #include "BML/ExecuteBB.h" 17 | #include "BML/ScriptHelper.h" 18 | 19 | #endif // BMLALL_H 20 | -------------------------------------------------------------------------------- /cmake/BMLUtils.cmake: -------------------------------------------------------------------------------- 1 | # - Some useful macros and functions for creating mod 2 | 3 | macro(add_bml_mod NAME) 4 | add_library(${NAME} SHARED) 5 | target_sources(${NAME} PRIVATE ${ARGN}) 6 | target_link_libraries(${NAME} PRIVATE BML CK2 VxMath) 7 | set_target_properties(${NAME} PROPERTIES 8 | SUFFIX ".bmodp" 9 | FOLDER "Mods") 10 | endmacro() 11 | 12 | macro(install_bml_mod NAME) 13 | install(TARGETS ${NAME} RUNTIME DESTINATION mods) 14 | endmacro() 15 | -------------------------------------------------------------------------------- /include/BML/Gui.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_H 2 | #define BML_GUI_H 3 | 4 | #ifdef _MSC_VER 5 | #pragma warning(push) 6 | #pragma warning(disable:4251) 7 | #endif 8 | 9 | #include "BML/Gui/Element.h" 10 | #include "BML/Gui/Text.h" 11 | #include "BML/Gui/Panel.h" 12 | #include "BML/Gui/Label.h" 13 | #include "BML/Gui/Button.h" 14 | #include "BML/Gui/Input.h" 15 | #include "BML/Gui/KeyInput.h" 16 | #include "BML/Gui/Gui.h" 17 | 18 | #ifdef _MSC_VER 19 | #pragma warning(pop) 20 | #endif 21 | 22 | #endif // BML_GUI_H -------------------------------------------------------------------------------- /src/Core/ResourceApi.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_RESOURCE_API_H 2 | #define BML_CORE_RESOURCE_API_H 3 | 4 | #include 5 | 6 | #include "bml_resource.h" 7 | #include "bml_extension.h" 8 | 9 | namespace BML::Core { 10 | void RegisterResourceApis(); 11 | BML_Result RegisterResourceType(const BML_ResourceTypeDesc *desc, BML_HandleType *out_type); 12 | void UnregisterResourceTypesForProvider(const std::string &provider_id); 13 | } // namespace BML::Core 14 | 15 | #endif // BML_CORE_RESOURCE_API_H 16 | -------------------------------------------------------------------------------- /tests/TestLoggingStub.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/Logging.h" 2 | 3 | #include 4 | 5 | namespace BML::Core { 6 | 7 | void RegisterLoggingApis() {} 8 | 9 | void CoreLog(BML_LogSeverity, const char *, const char *, ...) {} 10 | 11 | void CoreLogVa(BML_LogSeverity, const char *, const char *, va_list) {} 12 | 13 | BML_Result RegisterLogSinkOverride(const BML_LogSinkOverrideDesc *) { 14 | return BML_RESULT_OK; 15 | } 16 | 17 | BML_Result ClearLogSinkOverride() { 18 | return BML_RESULT_OK; 19 | } 20 | 21 | } // namespace BML::Core 22 | -------------------------------------------------------------------------------- /include/BML/Gui/Panel.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_PANEL_H 2 | #define BML_GUI_PANEL_H 3 | 4 | #include "BML/Defines.h" 5 | 6 | #include "BML/Gui/Element.h" 7 | 8 | namespace BGui { 9 | class BML_EXPORT Panel : public Element { 10 | friend class Gui; 11 | 12 | public: 13 | explicit Panel(const char *name); 14 | ~Panel() override; 15 | 16 | VxColor GetColor(); 17 | void SetColor(VxColor color); 18 | 19 | protected: 20 | CKMaterial *m_Material; 21 | }; 22 | } 23 | 24 | #endif // BML_GUI_PANEL_H 25 | -------------------------------------------------------------------------------- /templates/mod-template/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(HelloMod VERSION 1.0.0 LANGUAGES CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | 9 | find_package(BML REQUIRED) 10 | 11 | add_library(HelloMod SHARED src/HelloMod.cpp) 12 | 13 | target_include_directories(HelloMod PRIVATE ${BML_INCLUDE_DIRS}) 14 | target_link_libraries(HelloMod PRIVATE BML) 15 | 16 | set_target_properties(HelloMod PROPERTIES 17 | OUTPUT_NAME "HelloMod" 18 | ) 19 | 20 | -------------------------------------------------------------------------------- /include/bml_version.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_VERSION_H 2 | #define BML_VERSION_H 3 | 4 | #include "bml_types.h" 5 | 6 | BML_BEGIN_CDECLS 7 | 8 | #define BML_API_VERSION_MAJOR 0u 9 | #define BML_API_VERSION_MINOR 4u 10 | #define BML_API_VERSION_PATCH 0u 11 | 12 | #define BML_API_VERSION_STR "0.4.0" 13 | 14 | static inline BML_Version bmlGetApiVersion(void) { 15 | return bmlMakeVersion(BML_API_VERSION_MAJOR, 16 | BML_API_VERSION_MINOR, 17 | BML_API_VERSION_PATCH); 18 | } 19 | 20 | BML_END_CDECLS 21 | 22 | #endif /* BML_VERSION_H */ 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## General build artifacts 4 | *.o 5 | *.obj 6 | *.exe 7 | 8 | ## Visual Studio artifacts 9 | .vs 10 | ipch 11 | *.opensdf 12 | *.log 13 | *.pdb 14 | *.ilk 15 | *.user 16 | *.sdf 17 | *.suo 18 | *.VC.db 19 | *.VC.VC.opendb 20 | 21 | ## CMake artifacts 22 | CMakeLists.txt.user 23 | CMakeCache.txt 24 | CMakeFiles 25 | CMakeScripts 26 | Testing 27 | Makefile 28 | cmake_install.cmake 29 | install_manifest.txt 30 | compile_commands.json 31 | CTestTestfile.cmake 32 | _deps 33 | build*/ 34 | 35 | ## JetBrains IDE artifacts 36 | .idea 37 | cmake-build-* 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /include/BML/Guids/MidiManager.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_MIDIMANAGER_H 2 | #define BML_GUIDS_MIDIMANAGER_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // MidiManager 8 | // 9 | 10 | // Building Blocks 11 | #define VT_MIDIMANAGER_READMIDI CKGUID(0x408c50ed, 0x68ba5988) 12 | #define VT_MIDIMANAGER_MIDIEVENT CKGUID(0x7c652f90, 0x64404377) 13 | #define VT_MIDIMANAGER_SWITCHONMIDI CKGUID(0x624b1bec, 0x509400a8) 14 | #define VT_MIDIMANAGER_MIDIPLAYER CKGUID(0x843cb43a, 0xa12dac48) 15 | #define VT_MIDIMANAGER_SETMIDIINPUTPORT CKGUID(0x42d96c11, 0x64242546) 16 | 17 | #endif // BML_GUIDS_MIDIMANAGER_H 18 | -------------------------------------------------------------------------------- /include/BML/Guids/TT_DatabaseManager_RT.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_TT_DATABASEMANAGER_RT_H 2 | #define BML_GUIDS_TT_DATABASEMANAGER_RT_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // TT_DatabaseManager_RT 8 | // 9 | 10 | // Building Blocks 11 | #define TT_DATABASEMANAGER_RT_TTREGISTERARRAY CKGUID(0x348773dc, 0x19ae6322) 12 | #define TT_DATABASEMANAGER_RT_TTLOADDATABASE CKGUID(0x5441494, 0x38ac7789) 13 | #define TT_DATABASEMANAGER_RT_TTSAVEDATABASE CKGUID(0x5d303e9d, 0x552c0af2) 14 | #define TT_DATABASEMANAGER_RT_TTSETDATABASEPROPERTIES CKGUID(0x1436624f, 0x34e1290a) 15 | 16 | #endif // BML_GUIDS_TT_DATABASEMANAGER_RT_H 17 | -------------------------------------------------------------------------------- /src/EventHook.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_EVENTHOOK_H 2 | #define BML_EVENTHOOK_H 3 | 4 | class BMLMod; 5 | class CKBehavior; 6 | class IBML; 7 | class IMessageReceiver; 8 | 9 | class EventHookRegistrar { 10 | public: 11 | EventHookRegistrar(BMLMod &mod, IBML &bml); 12 | 13 | void RegisterBaseEventHandler(CKBehavior *script); 14 | void RegisterGameplayIngame(CKBehavior *script); 15 | void RegisterGameplayEnergy(CKBehavior *script); 16 | void RegisterGameplayEvents(CKBehavior *script); 17 | 18 | private: 19 | BMLMod &m_Mod; 20 | IBML &m_BML; 21 | IMessageReceiver &m_Receiver; 22 | }; 23 | 24 | #endif // BML_EVENTHOOK_H 25 | -------------------------------------------------------------------------------- /src/Core/ApiRegistration.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_API_REGISTRATION_H 2 | #define BML_CORE_API_REGISTRATION_H 3 | 4 | namespace BML::Core { 5 | void RegisterCoreApis(); 6 | void RegisterLoggingApis(); 7 | void RegisterConfigApis(); 8 | void RegisterImcApis(); 9 | void RegisterResourceApis(); 10 | void RegisterExtensionApis(); 11 | void RegisterMemoryApis(); 12 | void RegisterDiagnosticApis(); 13 | void RegisterSyncApis(); 14 | void RegisterCapabilityApis(); 15 | void RegisterTracingApis(); 16 | void RegisterProfilingApis(); 17 | } // namespace BML::Core 18 | 19 | #endif // BML_CORE_API_REGISTRATION_H 20 | -------------------------------------------------------------------------------- /src/Logger.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_LOGGER_H 2 | #define BML_LOGGER_H 3 | 4 | #include 5 | 6 | #include "BML/ILogger.h" 7 | 8 | class BML_EXPORT Logger : public ILogger 9 | { 10 | public: 11 | static Logger *GetDefault(); 12 | static void SetDefault(Logger *logger); 13 | 14 | explicit Logger(const char *modName); 15 | 16 | void Info(const char *fmt, ...) override; 17 | void Warn(const char *fmt, ...) override; 18 | void Error(const char *fmt, ...) override; 19 | 20 | private: 21 | void Log(const char *level, const char *fmt, va_list args); 22 | 23 | const char *m_ModName; 24 | 25 | static Logger *m_DefaultLogger; 26 | }; 27 | 28 | #endif // BML_LOGGER_H -------------------------------------------------------------------------------- /src/IMod.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/IMod.h" 2 | #include "Logger.h" 3 | #include "Config.h" 4 | #include "ModContext.h" 5 | 6 | ILogger *IMod::GetLogger() { 7 | if (m_Logger == nullptr) 8 | m_Logger = new Logger(GetID()); 9 | return m_Logger; 10 | } 11 | 12 | IConfig *IMod::GetConfig() { 13 | if (m_Config == nullptr) { 14 | auto *config = new Config(this); 15 | m_Config = config; 16 | BML_GetModContext()->AddConfig(config); 17 | } 18 | return m_Config; 19 | } 20 | 21 | IMod::~IMod() { 22 | if (m_Logger) 23 | delete m_Logger; 24 | if (m_Config) 25 | delete m_Config; 26 | m_BML->ClearDependencies(this); 27 | } -------------------------------------------------------------------------------- /src/Utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(BML_UTILS_HEADERS 2 | IniFile.h 3 | StringUtils.h 4 | PathUtils.h 5 | HookUtils.h 6 | ) 7 | 8 | set(BML_UTILS_SOURCES 9 | IniFile.cpp 10 | StringUtils.cpp 11 | PathUtils.cpp 12 | HookUtils.cpp 13 | ) 14 | 15 | add_library(BMLUtils STATIC 16 | ${BML_UTILS_SOURCES} 17 | ${BML_UTILS_HEADERS} 18 | ) 19 | 20 | target_include_directories(BMLUtils 21 | PUBLIC 22 | ${CMAKE_CURRENT_SOURCE_DIR} 23 | ) 24 | 25 | target_link_libraries(BMLUtils 26 | PUBLIC 27 | utf8 zip 28 | ) 29 | 30 | set_target_properties(BMLUtils PROPERTIES 31 | FOLDER "Core" 32 | ) 33 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/zip"] 2 | path = deps/zip 3 | url = https://github.com/kuba--/zip.git 4 | [submodule "deps/minhook"] 5 | path = deps/minhook 6 | url = https://github.com/TsudaKageyu/minhook.git 7 | [submodule "deps/imgui"] 8 | path = deps/imgui 9 | url = https://github.com/ocornut/imgui.git 10 | [submodule "deps/utf8.h"] 11 | path = deps/utf8.h 12 | url = https://github.com/sheredom/utf8.h.git 13 | [submodule "deps/oniguruma"] 14 | path = deps/oniguruma 15 | url = https://github.com/kkos/oniguruma 16 | [submodule "deps/tomlplusplus"] 17 | path = deps/tomlplusplus 18 | url = https://github.com/marzer/tomlplusplus.git 19 | [submodule "deps/efsw"] 20 | path = deps/efsw 21 | url = https://github.com/SpartanJ/efsw.git 22 | -------------------------------------------------------------------------------- /include/BML/Gui/KeyInput.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_KEYINPUT_H 2 | #define BML_GUI_KEYINPUT_H 3 | 4 | #include 5 | 6 | #include "BML/Defines.h" 7 | 8 | #include "BML/Gui/Input.h" 9 | 10 | namespace BGui { 11 | class BML_EXPORT KeyInput : public Input { 12 | friend class Gui; 13 | public: 14 | explicit KeyInput(const char *name); 15 | 16 | void OnCharTyped(CKDWORD key) override; 17 | 18 | CKKEYBOARD GetKey(); 19 | void SetKey(CKKEYBOARD key); 20 | 21 | void GetFocus() override; 22 | void LoseFocus() override; 23 | 24 | protected: 25 | CKKEYBOARD m_Key; 26 | std::function m_KeyCallback; 27 | }; 28 | } 29 | 30 | #endif // BML_GUI_KEYINPUT_H 31 | -------------------------------------------------------------------------------- /deps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MinHook 2 | add_subdirectory(minhook EXCLUDE_FROM_ALL) 3 | 4 | # zip 5 | add_subdirectory(zip EXCLUDE_FROM_ALL) 6 | 7 | # tomlplusplus 8 | set(TOML_BUILD_TESTS OFF CACHE BOOL "") 9 | set(TOML_BUILD_EXAMPLES OFF CACHE BOOL "") 10 | add_subdirectory(tomlplusplus EXCLUDE_FROM_ALL) 11 | 12 | # Oniguruma 13 | set(BUILD_SHARED_LIBS OFF CACHE BOOL "") 14 | add_subdirectory(oniguruma EXCLUDE_FROM_ALL) 15 | 16 | # utf8.h 17 | add_library(utf8 INTERFACE) 18 | target_include_directories(utf8 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/utf8.h) 19 | 20 | # efsw 21 | set(EFSW_BUILD_TEST_APP OFF CACHE BOOL "" FORCE) 22 | add_subdirectory(efsw EXCLUDE_FROM_ALL) 23 | 24 | # ImGui 25 | set(IMGUI_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/imgui PARENT_SCOPE) -------------------------------------------------------------------------------- /src/Core/Microkernel.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MICROKERNEL_H 2 | #define BML_CORE_MICROKERNEL_H 3 | 4 | #include "bml_errors.h" 5 | 6 | #include "ModuleRuntime.h" 7 | 8 | namespace BML::Core { 9 | // Phase 0: Initialize BML Core (context, API registry) - safe in DllMain 10 | bool InitializeCore(); 11 | 12 | // Phase 1: Discover and validate modules (safe to call in DllMain) 13 | bool DiscoverModules(); 14 | 15 | // Phase 2: Load discovered modules (call when CKContext is available) 16 | bool LoadDiscoveredModules(); 17 | 18 | void ShutdownMicrokernel(); 19 | const ModuleBootstrapDiagnostics &GetBootstrapDiagnostics(); 20 | const BML_BootstrapDiagnostics &GetPublicDiagnostics(); 21 | } // namespace BML::Core 22 | 23 | #endif // BML_CORE_MICROKERNEL_H 24 | -------------------------------------------------------------------------------- /src/ModLoader/Behaviors/HookBlock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file HookBlock.h 3 | * @brief Header for HookBlock behavior 4 | */ 5 | 6 | #ifndef BML_MODLOADER_HOOKBLOCK_H 7 | #define BML_MODLOADER_HOOKBLOCK_H 8 | 9 | #include "CKAll.h" 10 | 11 | namespace ModLoader { 12 | 13 | /// Callback function type for HookBlock 14 | typedef int (*CKBehaviorCallback)(const CKBehaviorContext *behcontext, void *arg); 15 | 16 | /// Register HookBlock behavior with the engine 17 | CKObjectDeclaration *FillBehaviorHookBlockDecl(); 18 | 19 | /// Create HookBlock prototype 20 | CKERROR CreateHookBlockProto(CKBehaviorPrototype **pproto); 21 | 22 | /// HookBlock execution function 23 | int HookBlock(const CKBehaviorContext &behcontext); 24 | 25 | } // namespace ModLoader 26 | 27 | #endif // BML_MODLOADER_HOOKBLOCK_H 28 | -------------------------------------------------------------------------------- /include/BML/Guids/TT_Gravity_RT.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_TT_GRAVITY_RT_H 2 | #define BML_GUIDS_TT_GRAVITY_RT_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // TT_Gravity_RT 8 | // 9 | 10 | // Building Blocks 11 | #define TT_GRAVITY_RT_TTEXTRA CKGUID(0x36106bd9, 0x51813906) 12 | #define TT_GRAVITY_RT_TTPROXIMITYVOLUMECONTROL CKGUID(0x38571b61, 0x64cd2174) 13 | #define TT_GRAVITY_RT_TTSPEEDOMETER CKGUID(0x51bd5521, 0x672c67bf) 14 | #define TT_GRAVITY_RT_TTSIMPLESHADOW CKGUID(0x7f0517c2, 0x52cc76bb) 15 | #define TT_GRAVITY_RT_TTREALSHADOWMAPPING CKGUID(0x452816af, 0x681a3a81) 16 | #define TT_GRAVITY_RT_TTSKY CKGUID(0x36691920, 0x3b261630) 17 | #define TT_GRAVITY_RT_TT_TEXTURESINE CKGUID(0x9c1208, 0x3a8d779e) 18 | #define TT_GRAVITY_RT_TT_GETSOUNDPROPERTIES CKGUID(0x30fa6a70, 0x3b3f728a) 19 | 20 | #endif // BML_GUIDS_TT_GRAVITY_RT_H 21 | -------------------------------------------------------------------------------- /tests/ExportStub.cpp: -------------------------------------------------------------------------------- 1 | // Stub implementations for BML export functions used by tests 2 | // This file should be included INSTEAD of Export.cpp in tests 3 | 4 | #include "Core/ApiRegistry.h" 5 | #include "bml_export.h" 6 | 7 | // Provide implementations without using BML_API to avoid dllimport issues 8 | extern "C" { 9 | 10 | void *bmlGetProcAddress(const char *proc_name) { 11 | if (!proc_name) 12 | return nullptr; 13 | return BML::Core::ApiRegistry::Instance().Get(proc_name); 14 | } 15 | 16 | void *bmlGetProcAddressById(BML_ApiId api_id) { 17 | return BML::Core::ApiRegistry::Instance().GetById(api_id); 18 | } 19 | 20 | int bmlGetApiId(const char *proc_name, BML_ApiId *out_id) { 21 | if (!proc_name || !out_id) 22 | return 0; 23 | return BML::Core::ApiRegistry::Instance().GetApiId(proc_name, out_id) ? 1 : 0; 24 | } 25 | 26 | } // extern "C" 27 | -------------------------------------------------------------------------------- /include/BML/Guids/WorldEnvironments.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_WORLDENVIRONMENTS_H 2 | #define BML_GUIDS_WORLDENVIRONMENTS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // WorldEnvironments 8 | // 9 | 10 | // Building Blocks 11 | #define VT_WORLDENVIRONMENTS_SETBACKGROUNDIMAGE CKGUID(0x1254d1ce, 0x1d0a58) 12 | #define VT_WORLDENVIRONMENTS_SETBACKGROUNDMATERIAL CKGUID(0xddccefec, 0xd010c102) 13 | #define VT_WORLDENVIRONMENTS_SKYAROUND CKGUID(0xed453298, 0x1ae789f) 14 | #define VT_WORLDENVIRONMENTS_CLOUDSAROUND CKGUID(0x77c04509, 0xa36a46) 15 | #define VT_WORLDENVIRONMENTS_SETFOG CKGUID(0x151aaaaa, 0xffd5bdbc) 16 | #define VT_WORLDENVIRONMENTS_SETBACKGROUNDCOLOR CKGUID(0xf5faaaaa, 0xfdd5bd00) 17 | #define VT_WORLDENVIRONMENTS_SETAMBIENTLIGHTCOLOR CKGUID(0x136b3f22, 0xdc12786) 18 | #define VT_WORLDENVIRONMENTS_SKYAROUNDCUBEMAP CKGUID(0x1157f66, 0xaf04fd2) 19 | 20 | #endif // BML_GUIDS_WORLDENVIRONMENTS_H 21 | -------------------------------------------------------------------------------- /src/SRTimer.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_SRTIMER_H 2 | #define BML_SRTIMER_H 3 | 4 | class SRTimer { 5 | public: 6 | SRTimer(); 7 | ~SRTimer() = default; 8 | 9 | void Reset(); 10 | void Start(); 11 | void Pause(); 12 | 13 | void Update(float deltaTime); 14 | 15 | float GetTime() const; 16 | const char *GetFormattedTime() const; 17 | 18 | bool IsRunning() const; 19 | bool IsDirty() const { return m_Dirty; } 20 | void ClearDirty() { m_Dirty = false; } 21 | 22 | private: 23 | float m_Time; // Time in milliseconds 24 | bool m_Running; // Is timer running? 25 | mutable char m_FormattedTime[32] = {}; // Formatted time string 26 | mutable bool m_Dirty = true; // Whether formatted string changed since last clear 27 | 28 | void UpdateFormattedTime() const; 29 | }; 30 | 31 | #endif // BML_SRTIMER_H 32 | -------------------------------------------------------------------------------- /src/Core/ModuleDiscovery.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MODULE_DISCOVERY_H 2 | #define BML_CORE_MODULE_DISCOVERY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "DependencyResolver.h" 9 | #include "ModManifest.h" 10 | 11 | namespace BML::Core { 12 | struct ManifestLoadResult { 13 | std::vector> manifests; 14 | std::vector errors; 15 | }; 16 | 17 | bool LoadManifestsFromDirectory(const std::wstring &mods_dir, ManifestLoadResult &out_result); 18 | 19 | bool BuildLoadOrder(const ManifestLoadResult &manifests, 20 | std::vector &out_order, 21 | std::vector &out_warnings, 22 | DependencyResolutionError &out_error); 23 | } // namespace BML::Core 24 | 25 | #endif // BML_CORE_MODULE_DISCOVERY_H 26 | -------------------------------------------------------------------------------- /include/BML/Gui/Input.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_INPUT_H 2 | #define BML_GUI_INPUT_H 3 | 4 | #include 5 | #include 6 | 7 | #include "BML/Defines.h" 8 | 9 | #include "BML/Gui/Label.h" 10 | 11 | namespace BGui { 12 | class BML_EXPORT Input : public Label { 13 | friend class Gui; 14 | public: 15 | explicit Input(const char *name); 16 | 17 | void InvokeCallback(CKDWORD); 18 | void SetCallback(std::function callback); 19 | 20 | virtual void OnCharTyped(CKDWORD key); 21 | 22 | const char *GetText() override; 23 | void SetText(const char *text) override; 24 | 25 | virtual void GetFocus(); 26 | virtual void LoseFocus(); 27 | 28 | protected: 29 | std::string m_Text; 30 | unsigned int m_Caret = 0; 31 | std::function m_Callback; 32 | }; 33 | } 34 | 35 | #endif // BML_GUI_INPUT_H 36 | -------------------------------------------------------------------------------- /include/BML/Gui/Label.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_LABEL_H 2 | #define BML_GUI_LABEL_H 3 | 4 | #include "BML/Defines.h" 5 | #include "BML/ExecuteBB.h" 6 | 7 | #include "BML/Gui/Element.h" 8 | 9 | namespace BGui { 10 | class BML_EXPORT Label : public Element { 11 | friend class Gui; 12 | 13 | public: 14 | explicit Label(const char *name); 15 | ~Label() override; 16 | 17 | virtual const char *GetText(); 18 | virtual void SetText(const char *text); 19 | 20 | ExecuteBB::FontType GetFont(); 21 | void SetFont(ExecuteBB::FontType font); 22 | 23 | void SetAlignment(int align); 24 | 25 | int GetTextFlags(); 26 | void SetTextFlags(int flags); 27 | 28 | void SetOffset(Vx2DVector offset); 29 | 30 | void Process() override; 31 | 32 | protected: 33 | CKBehavior *m_Text2d; 34 | }; 35 | } 36 | 37 | #endif // BML_GUI_LABEL_H 38 | -------------------------------------------------------------------------------- /src/Core/Logging.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_LOGGING_H 2 | #define BML_CORE_LOGGING_H 3 | 4 | #include "bml_logging.h" 5 | #include "bml_extension.h" 6 | 7 | namespace BML::Core { 8 | void RegisterLoggingApis(); 9 | 10 | void CoreLog(BML_LogSeverity level, const char *tag, const char *fmt, ...); 11 | void CoreLogVa(BML_LogSeverity level, const char *tag, const char *fmt, va_list args); 12 | 13 | void LogMessage(BML_Context ctx, BML_LogSeverity level, const char *tag, const char *fmt, ...); 14 | void LogMessageVa(BML_Context ctx, BML_LogSeverity level, const char *tag, const char *fmt, va_list args); 15 | void SetLogFilter(BML_LogSeverity minimum_level); 16 | 17 | BML_Result GetLoggingCaps(BML_LogCaps *out_caps); 18 | BML_Result RegisterLogSinkOverride(const BML_LogSinkOverrideDesc *desc); 19 | BML_Result ClearLogSinkOverride(); 20 | } // namespace BML::Core 21 | 22 | #endif // BML_CORE_LOGGING_H 23 | -------------------------------------------------------------------------------- /include/BML/Gui/Element.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_ELEMENT_H 2 | #define BML_GUI_ELEMENT_H 3 | 4 | #include "Vx2dVector.h" 5 | #include "VxColor.h" 6 | #include "CKDefines.h" 7 | 8 | #include "BML/Defines.h" 9 | 10 | namespace BGui { 11 | class BML_EXPORT Element { 12 | friend class Gui; 13 | 14 | public: 15 | explicit Element(const char *name); 16 | virtual ~Element(); 17 | 18 | virtual Vx2DVector GetPosition(); 19 | virtual void SetPosition(Vx2DVector pos); 20 | 21 | virtual Vx2DVector GetSize(); 22 | virtual void SetSize(Vx2DVector size); 23 | 24 | virtual int GetZOrder(); 25 | virtual void SetZOrder(int z); 26 | 27 | virtual bool IsVisible(); 28 | virtual void SetVisible(bool visible); 29 | 30 | virtual void Process() {}; 31 | 32 | protected: 33 | CK2dEntity *m_2dEntity; 34 | }; 35 | } 36 | 37 | #endif // BML_GUI_ELEMENT_H 38 | -------------------------------------------------------------------------------- /templates/mod-template/README.md: -------------------------------------------------------------------------------- 1 | # BML+ Mod Template 2 | 3 | Minimal CMake-based template to build a BML+ mod with a sample command. 4 | 5 | ## Prerequisites 6 | 7 | - Windows + Visual Studio 2019+ (C++20) 8 | - CMake 3.14+ 9 | - BML installed (so that `BMLConfig.cmake` is available). 10 | 11 | If you built BML from source, run `cmake --install .` on the BML project first, then set `CMAKE_PREFIX_PATH` to BML's install prefix when configuring this template. 12 | 13 | ## Build 14 | 15 | ```bash 16 | cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="" 17 | cmake --build build --config Release 18 | ``` 19 | 20 | The resulting DLL will be named `HelloMod.dll`. Drop it into `ModLoader/Mods/HelloMod.bmodp` (or directly as a DLL depending on your workflow). 21 | 22 | ## Notes 23 | 24 | - Entry point: `BMLEntry(IBML*) -> IMod*` 25 | - Cleanup (optional): `BMLExit(IMod*)` 26 | - Registers a sample command: `hello [name]` 27 | 28 | -------------------------------------------------------------------------------- /src/Gui/Panel.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/Panel.h" 2 | 3 | #include "ModContext.h" 4 | 5 | using namespace BGui; 6 | 7 | Panel::Panel(const char *name) : Element(name) { 8 | CKContext *context = BML_GetCKContext(); 9 | m_Material = (CKMaterial *)context->CreateObject(CKCID_MATERIAL, (CKSTRING) ((std::string(name) + "_Mat").c_str())); 10 | context->GetCurrentLevel()->AddObject(m_Material); 11 | m_Material->EnableAlphaBlend(); 12 | m_Material->SetSourceBlend(VXBLEND_SRCALPHA); 13 | m_Material->SetDestBlend(VXBLEND_INVSRCALPHA); 14 | m_2dEntity->SetMaterial(m_Material); 15 | m_2dEntity->SetZOrder(0); 16 | } 17 | 18 | Panel::~Panel() { 19 | CKContext *context = BML_GetCKContext(); 20 | if (context) 21 | context->DestroyObject(CKOBJID(m_Material)); 22 | } 23 | 24 | VxColor Panel::GetColor() { 25 | return m_Material->GetDiffuse(); 26 | } 27 | 28 | void Panel::SetColor(VxColor color) { 29 | m_Material->SetDiffuse(color); 30 | } -------------------------------------------------------------------------------- /include/BML/Guids.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_H 2 | #define BML_GUIDS_H 3 | 4 | #include "BML/Guids/3DTransfo.h" 5 | #include "BML/Guids/BuildingBlocksAddons1.h" 6 | #include "BML/Guids/Cameras.h" 7 | #include "BML/Guids/Collisions.h" 8 | #include "BML/Guids/Controllers.h" 9 | #include "BML/Guids/Grids.h" 10 | #include "BML/Guids/Interface.h" 11 | #include "BML/Guids/Lights.h" 12 | #include "BML/Guids/Logics.h" 13 | #include "BML/Guids/Materials.h" 14 | #include "BML/Guids/MeshModifiers.h" 15 | #include "BML/Guids/MidiManager.h" 16 | #include "BML/Guids/Narratives.h" 17 | #include "BML/Guids/physics_RT.h" 18 | #include "BML/Guids/Sounds.h" 19 | #include "BML/Guids/TT_DatabaseManager_RT.h" 20 | #include "BML/Guids/TT_Gravity_RT.h" 21 | #include "BML/Guids/TT_InterfaceManager_RT.h" 22 | #include "BML/Guids/TT_ParticleSystems_RT.h" 23 | #include "BML/Guids/TT_Toolbox_RT.h" 24 | #include "BML/Guids/Visuals.h" 25 | #include "BML/Guids/WorldEnvironments.h" 26 | 27 | #endif // BML_GUIDS_H 28 | -------------------------------------------------------------------------------- /include/BML/Guids/Lights.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_LIGHTS_H 2 | #define BML_GUIDS_LIGHTS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Lights 8 | // 9 | 10 | // Building Blocks 11 | #define VT_LIGHTS_SETLIGHTATTENUATION CKGUID(0xd63d004d, 0xd54d8100) 12 | #define VT_LIGHTS_SPOTLIGHTPROPERTIES CKGUID(0x1ed010b, 0x1fd010b) 13 | #define VT_LIGHTS_SETLIGHTCOLOR CKGUID(0x32115590, 0x78951230) 14 | #define VT_LIGHTS_SETLIGHTRANGE CKGUID(0x22252552, 0x200020) 15 | #define VT_LIGHTS_SETLIGHTTARGET CKGUID(0x2580258, 0x2580258) 16 | #define VT_LIGHTS_SETLIGHTTYPE CKGUID(0x32165a2f, 0x70000000) 17 | #define VT_LIGHTS_SETLINEARATTENUATION CKGUID(0x45895551, 0x59486677) 18 | #define VT_LIGHTS_SETQUADRATICATTENUATION CKGUID(0xa0e0f18, 0x7a01ef09) 19 | #define VT_LIGHTS_SETSPECULARFLAG CKGUID(0x1ae11f1a, 0x1a1e1f11) 20 | #define VT_LIGHTS_LIGHTSPRITE CKGUID(0x4def3319, 0x75b5cc8) 21 | #define VT_LIGHTS_LIGHTCOLORPROGRESSION CKGUID(0x20171e01, 0x234a1b62) 22 | 23 | #endif // BML_GUIDS_LIGHTS_H 24 | -------------------------------------------------------------------------------- /src/Gui/KeyInput.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/KeyInput.h" 2 | 3 | #include "ModContext.h" 4 | #include "BML/InputHook.h" 5 | #include "ModContext.h" 6 | 7 | using namespace BGui; 8 | 9 | CKMaterial *g_Highlight = nullptr; 10 | 11 | KeyInput::KeyInput(const char *name) : Input(name), m_Key() { 12 | m_2dEntity->UseSourceRect(); 13 | VxRect rect(0.005f, 0.3804f, 0.4353f, 0.4549f); 14 | m_2dEntity->SetSourceRect(rect); 15 | } 16 | 17 | void KeyInput::OnCharTyped(CKDWORD key) { 18 | SetKey(static_cast(key)); 19 | InvokeCallback(key); 20 | } 21 | 22 | CKKEYBOARD KeyInput::GetKey() { 23 | return m_Key; 24 | } 25 | 26 | void KeyInput::SetKey(CKKEYBOARD key) { 27 | m_Key = key; 28 | char name[0x100]; 29 | BML_GetModContext()->GetInputManager()->GetKeyName(key, name); 30 | SetText(name); 31 | } 32 | 33 | void KeyInput::GetFocus() { 34 | m_2dEntity->SetMaterial(g_Highlight); 35 | } 36 | 37 | void KeyInput::LoseFocus() { 38 | m_2dEntity->SetMaterial(nullptr); 39 | } -------------------------------------------------------------------------------- /src/Core/DiagnosticApi.cpp: -------------------------------------------------------------------------------- 1 | #include "ApiRegistration.h" 2 | #include "ApiRegistry.h" 3 | #include "ApiRegistrationMacros.h" 4 | #include "CoreErrors.h" 5 | #include "DiagnosticManager.h" 6 | 7 | #include "bml_errors.h" 8 | #include "bml_api_ids.h" 9 | #include "bml_capabilities.h" 10 | 11 | namespace BML::Core { 12 | BML_Result BML_API_GetLastError(BML_ErrorInfo* out_error) { 13 | return DiagnosticManager::Instance().GetLastError(out_error); 14 | } 15 | 16 | void BML_API_ClearLastError() { 17 | DiagnosticManager::Instance().ClearLastError(); 18 | } 19 | 20 | void RegisterDiagnosticApis() { 21 | BML_BEGIN_API_REGISTRATION(); 22 | 23 | BML_REGISTER_API_GUARDED_WITH_CAPS(bmlGetLastError, "diagnostics", BML_API_GetLastError, BML_CAP_DIAGNOSTICS); 24 | BML_REGISTER_API_WITH_CAPS(bmlClearLastError, BML_API_ClearLastError, BML_CAP_DIAGNOSTICS); 25 | BML_REGISTER_API_WITH_CAPS(bmlGetErrorString, GetErrorString, BML_CAP_DIAGNOSTICS); 26 | } 27 | } // namespace BML::Core 28 | -------------------------------------------------------------------------------- /tests/ModuleLoaderStub.cpp: -------------------------------------------------------------------------------- 1 | // Stub implementation of ModuleLoader for tests that include Context.cpp 2 | // but don't need actual module loading functionality 3 | 4 | #include "Core/ModuleLoader.h" 5 | 6 | #include 7 | 8 | namespace BML::Core { 9 | 10 | bool LoadModules(const std::vector & /*order*/, 11 | Context & /*context*/, 12 | PFN_BML_GetProcAddress /*get_proc*/, 13 | std::vector &out_modules, 14 | ModuleLoadError &out_error) { 15 | // Stub: no modules loaded in tests 16 | out_modules.clear(); 17 | out_error = {}; 18 | return true; 19 | } 20 | 21 | void UnloadModules(std::vector &modules, BML_Context /*ctx*/) { 22 | for (auto &mod : modules) { 23 | if (mod.handle) { 24 | // Don't actually call entrypoint or FreeLibrary in stub 25 | mod.handle = nullptr; 26 | mod.entrypoint = nullptr; 27 | } 28 | } 29 | modules.clear(); 30 | } 31 | 32 | } // namespace BML::Core 33 | -------------------------------------------------------------------------------- /benchmarks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(ImcBenchmark 2 | ImcBenchmark.cpp 3 | ${CMAKE_SOURCE_DIR}/tests/ModuleLoaderStub.cpp 4 | ${BML_SOURCE_DIR}/Core/ImcBus.cpp 5 | ${BML_SOURCE_DIR}/Core/ImcApi.cpp 6 | ${BML_SOURCE_DIR}/Core/ApiRegistry.cpp 7 | ${BML_SOURCE_DIR}/Core/CoreErrors.cpp 8 | ${BML_SOURCE_DIR}/Core/DiagnosticManager.cpp 9 | ${BML_SOURCE_DIR}/Core/Logging.cpp 10 | ${BML_SOURCE_DIR}/Core/Context.cpp 11 | ${BML_SOURCE_DIR}/Core/ConfigStore.cpp 12 | ${BML_SOURCE_DIR}/Core/ResourceApi.cpp 13 | ) 14 | 15 | set_target_properties(ImcBenchmark PROPERTIES 16 | FOLDER "Benchmarks" 17 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 18 | ) 19 | 20 | target_include_directories(ImcBenchmark 21 | PRIVATE 22 | ${BML_INCLUDE_DIR} 23 | ${BML_SOURCE_DIR} 24 | ) 25 | 26 | target_link_libraries(ImcBenchmark 27 | PRIVATE 28 | BMLUtils 29 | tomlplusplus::tomlplusplus 30 | ) 31 | 32 | target_compile_definitions(ImcBenchmark PRIVATE "BML_EXPORTS") 33 | -------------------------------------------------------------------------------- /src/BML.rc: -------------------------------------------------------------------------------- 1 | #include "BML/Version.h" 2 | #include "winres.h" 3 | 4 | // Version 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | FILEVERSION BML_MAJOR_VERSION, BML_MINOR_VERSION, BML_PATCH_VERSION 8 | PRODUCTVERSION BML_MAJOR_VERSION, BML_MINOR_VERSION, BML_PATCH_VERSION 9 | FILEFLAGSMASK 0x3fL 10 | #ifdef _DEBUG 11 | FILEFLAGS 0x1L 12 | #else 13 | FILEFLAGS 0x0L 14 | #endif 15 | FILEOS 0x40004L 16 | FILETYPE 0x2L 17 | FILESUBTYPE 0x0L 18 | BEGIN 19 | BLOCK "StringFileInfo" 20 | BEGIN 21 | BLOCK "040904b0" 22 | BEGIN 23 | VALUE "CompanyName", "Kakuty & Gamepiaynmo" 24 | VALUE "FileDescription", "Mod Loader for Ballance" 25 | VALUE "FileVersion", BML_VERSION 26 | VALUE "InternalName", "BMLPlus.dll" 27 | VALUE "LegalCopyright", "Copyright (C) 2025" 28 | VALUE "OriginalFilename", "BMLPlus.dll" 29 | VALUE "ProductName", "Ballance Mod Loader Plus" 30 | VALUE "ProductVersion", BML_VERSION 31 | END 32 | END 33 | BLOCK "VarFileInfo" 34 | BEGIN 35 | VALUE "Translation", 0x409, 1200 36 | END 37 | END -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gamepiaynmo and Kakuty 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/BML/Guids/Sounds.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_SOUNDS_H 2 | #define BML_GUIDS_SOUNDS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Sounds 8 | // 9 | 10 | // Building Blocks 11 | #define VT_SOUNDS_CDPLAYER CKGUID(0x3dec4ae1, 0x72ae2cd6) 12 | #define VT_SOUNDS_WAVEPLAYER CKGUID(0x5bde0e45, 0x2e2107d5) 13 | #define VT_SOUNDS_PLAYSOUNDINSTANCE CKGUID(0x283a35, 0x38ef6b48) 14 | #define VT_SOUNDS_SOUNDMANAGERSETUP CKGUID(0x4c05c6a, 0x6454792b) 15 | #define VT_SOUNDS_POSITIONSOUND CKGUID(0x4ba42a5b, 0x3ca030dd) 16 | #define VT_SOUNDS_SETPRIORITY CKGUID(0x159630e5, 0x5a5a26c6) 17 | #define VT_SOUNDS_SETSOUNDRANGE CKGUID(0x27f50a7c, 0xee91d0e) 18 | #define VT_SOUNDS_SETSOUNDCONEVALUES CKGUID(0xabf60a8c, 0xff91d0d) 19 | #define VT_SOUNDS_SETSOUNDTYPE CKGUID(0x7734470d, 0x23b221f8) 20 | #define VT_SOUNDS_PANNINGCONTROL CKGUID(0x6a5f17af, 0x63480864) 21 | #define VT_SOUNDS_VOLUMECONTROL CKGUID(0x33613f21, 0x776b185b) 22 | #define VT_SOUNDS_PITCHCONTROL CKGUID(0x7b7c731e, 0x175b27a3) 23 | #define VT_SOUNDS_FADEIN CKGUID(0x742215ea, 0x8a06c46) 24 | #define VT_SOUNDS_FADEOUT CKGUID(0x1b82338f, 0x5a656975) 25 | #define VT_SOUNDS_SETLISTENER CKGUID(0x28cd6341, 0xcad5793) 26 | 27 | #endif // BML_GUIDS_SOUNDS_H 28 | -------------------------------------------------------------------------------- /src/Core/ModHandle.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MOD_HANDLE_H 2 | #define BML_CORE_MOD_HANDLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bml_types.h" 11 | #include "bml_logging.h" 12 | #include "bml_core.h" // For BML_ShutdownCallback 13 | 14 | namespace BML::Core { 15 | struct ModManifest; 16 | struct LoadedModule; 17 | 18 | struct LogFileCloser { 19 | void operator()(FILE *file) const { 20 | if (file) { 21 | fclose(file); 22 | } 23 | } 24 | }; 25 | } // namespace BML::Core 26 | 27 | struct BML_Mod_T { 28 | struct ShutdownHook { 29 | BML_ShutdownCallback callback{nullptr}; 30 | void *user_data{nullptr}; 31 | }; 32 | 33 | std::string id; 34 | BML_Version version{}; 35 | const BML::Core::ModManifest *manifest{nullptr}; 36 | std::vector capabilities; 37 | std::vector shutdown_hooks; 38 | std::wstring log_path; 39 | std::unique_ptr log_file; 40 | std::atomic minimum_severity{BML_LOG_INFO}; 41 | }; 42 | 43 | #endif // BML_CORE_MOD_HANDLE_H 44 | -------------------------------------------------------------------------------- /include/BML/Gui/Button.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_BUTTON_H 2 | #define BML_GUI_BUTTON_H 3 | 4 | #include 5 | 6 | #include "BML/Defines.h" 7 | 8 | #include "BML/Gui/Label.h" 9 | 10 | namespace BGui { 11 | enum ButtonType { 12 | BUTTON_NORMAL, 13 | BUTTON_BACK, 14 | BUTTON_SETTING, 15 | BUTTON_LEVEL, 16 | BUTTON_KEY, 17 | BUTTON_KEYSEL, 18 | BUTTON_SMALL, 19 | BUTTON_LEFT, 20 | BUTTON_RIGHT, 21 | BUTTON_PLUS, 22 | BUTTON_MINUS, 23 | }; 24 | 25 | class BML_EXPORT Button : public Label { 26 | friend class Gui; 27 | 28 | public: 29 | explicit Button(const char *name); 30 | 31 | ButtonType GetType(); 32 | void SetType(ButtonType type); 33 | 34 | bool IsActive(); 35 | void SetActive(bool active); 36 | 37 | void InvokeCallback(); 38 | void SetCallback(std::function callback); 39 | 40 | void OnMouseEnter(); 41 | void OnMouseLeave(); 42 | 43 | protected: 44 | ButtonType m_Type; 45 | bool m_Active = true; 46 | std::function m_Callback; 47 | }; 48 | } 49 | 50 | #endif // BML_GUI_BUTTON_H 51 | -------------------------------------------------------------------------------- /include/BML/ICommand.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_ICOMMAND_H 2 | #define BML_ICOMMAND_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "BML/Defines.h" 11 | 12 | class IBML; 13 | 14 | class BML_EXPORT ICommand { 15 | public: 16 | virtual std::string GetName() = 0; 17 | virtual std::string GetAlias() = 0; 18 | virtual std::string GetDescription() = 0; 19 | 20 | virtual bool IsCheat() = 0; 21 | 22 | virtual void Execute(IBML *bml, const std::vector &args) = 0; 23 | 24 | virtual const std::vector GetTabCompletion(IBML *bml, const std::vector &args) = 0; 25 | 26 | static int ParseInteger(const std::string &str, int mn = INT_MIN, int mx = INT_MAX) { 27 | return (std::max)(mn, (std::min)(mx, atoi(str.c_str()))); 28 | } 29 | static float ParseFloat(const std::string &str, float mn = FLT_MIN, float mx = FLT_MAX) { 30 | return (std::max)(mn, (std::min)(mx, (float) atof(str.c_str()))); 31 | } 32 | static bool ParseBoolean(const std::string &str) { 33 | return str == "true" || str == "on" || str == "1"; 34 | } 35 | }; 36 | 37 | #endif // BML_ICOMMAND_H -------------------------------------------------------------------------------- /include/BML/Gui/Text.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUI_TEXT_H 2 | #define BML_GUI_TEXT_H 3 | 4 | #include "BML/Defines.h" 5 | 6 | #include "BML/Gui/Element.h" 7 | 8 | namespace BGui { 9 | class BML_EXPORT Text : private Element { 10 | friend class Gui; 11 | 12 | public: 13 | explicit Text(const char *name); 14 | ~Text() override; 15 | 16 | Vx2DVector GetPosition() override; 17 | void SetPosition(Vx2DVector pos) override; 18 | 19 | Vx2DVector GetSize() override; 20 | void SetSize(Vx2DVector size) override; 21 | 22 | int GetZOrder() override; 23 | void SetZOrder(int z) override; 24 | 25 | bool IsVisible() override; 26 | void SetVisible(bool visible) override; 27 | 28 | const char *GetText(); 29 | void SetText(const char *text); 30 | 31 | void SetFont(const char *FontName, int FontSize, int Weight, CKBOOL italic, CKBOOL underline); 32 | 33 | void SetAlignment(CKSPRITETEXT_ALIGNMENT align); 34 | 35 | CKDWORD GetTextColor(); 36 | void SetTextColor(CKDWORD color); 37 | 38 | void UpdateFont(); 39 | 40 | protected: 41 | CKSpriteText *m_Sprite; 42 | }; 43 | } 44 | 45 | #endif // BML_GUI_TEXT_H 46 | -------------------------------------------------------------------------------- /include/BML/Guids/physics_RT.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_PHYSICS_RT_H 2 | #define BML_GUIDS_PHYSICS_RT_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // physics_RT 8 | // 9 | 10 | // Building Blocks 11 | #define PHYSICS_RT_PHYSICALIZE CKGUID(0x7522370e, 0x37ec15ec) 12 | #define PHYSICS_RT_PHYSICSIMPULSE CKGUID(0xc7e39bb, 0x16db20d5) 13 | #define PHYSICS_RT_PHYSICSFORCE CKGUID(0x56e20c57, 0xb926068) 14 | #define PHYSICS_RT_PHYSICSWAKEUP CKGUID(0x38b851b5, 0x72ca74ac) 15 | #define PHYSICS_RT_SETPHYSICSGLOBALS CKGUID(0x72af347c, 0x3da71e1) 16 | #define PHYSICS_RT_PHYSICSHINGE CKGUID(0x41cd3653, 0xde60c1d) 17 | #define PHYSICS_RT_SETPHYSICSSPRING CKGUID(0x24a06a3a, 0x7100fce) 18 | #define PHYSICS_RT_SETPHYSICSSLIDER CKGUID(0x2973360e, 0x23d31aa7) 19 | #define PHYSICS_RT_PHYSICSBALLJOINT CKGUID(0x5e624f0a, 0x35160450) 20 | #define PHYSICS_RT_PHYSICSCOLLDETECTION CKGUID(0x7435488d, 0x201d1188) 21 | #define PHYSICS_RT_PHYSICSCONTINUOUSCONTACT CKGUID(0x199e4cf1, 0x545a78fe) 22 | #define PHYSICS_RT_PHYSICSBUOYANCY CKGUID(0x2c015f2b, 0x5b147512) 23 | #define PHYSICS_RT_PHYSICSRESET CKGUID(0x5714345e, 0x792262ae) 24 | #define PHYSICS_RT_GETPROFILERVALUES CKGUID(0x1c8e61d1, 0x32723c6f) 25 | #define PHYSICS_RT_DELETECOLLISIONSURFACES CKGUID(0x53bf75aa, 0x770c7021) 26 | 27 | #endif // BML_GUIDS_PHYSICS_RT_H 28 | -------------------------------------------------------------------------------- /include/BML/Guids/Grids.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_GRIDS_H 2 | #define BML_GUIDS_GRIDS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Grids 8 | // 9 | 10 | // Building Blocks 11 | #define VT_GRIDS_FILLLAYER CKGUID(0x513344bb, 0x6b943d9f) 12 | #define VT_GRIDS_GETSQUAREFROM3DPOS CKGUID(0xa376e0, 0x326609e6) 13 | #define VT_GRIDS_GETPOSFROMVALUE CKGUID(0x670616ad, 0x2a5c54ac) 14 | #define VT_GRIDS_LAYERSLIDER CKGUID(0x7bac6da2, 0x1cbe76ed) 15 | #define VT_GRIDS_SETGRIDPRIORITY CKGUID(0x74412551, 0x6146504c) 16 | #define VT_GRIDS_SETSQUAREFROM3DPOS CKGUID(0x73a67252, 0x1d5b39c1) 17 | #define VT_GRIDS_SWITCHIFSQUARE CKGUID(0x254074c1, 0x22c0397e) 18 | #define VT_GRIDS_3DENTITYFILL CKGUID(0x672f0997, 0x563644cc) 19 | #define VT_GRIDS_GRIDPATHINIT CKGUID(0x74d12456, 0x6f481719) 20 | #define VT_GRIDS_GRIDPATHSOLVER CKGUID(0x17e75566, 0xc5625a6) 21 | #define VT_GRIDS_CHARACTERGRIDPATHFOLLOW CKGUID(0x6d0a6dd3, 0x43765df4) 22 | 23 | // Parameter Types 24 | #define CKPGUID_HEURISTIC CKGUID(0x72604f23, 0x5fe7f0f) 25 | #define CKPGUID_PATHTYPE CKGUID(0x3e7f7035, 0x7835401f) 26 | #define CKPGUID_FOLLOWMODE CKGUID(0x83e512e, 0x351c7933) 27 | #define CKPGUID_LINKERGRAPH_ENUM CKGUID(0x16a90c1b, 0x740d0f12) 28 | #define CKPGUID_PATHFINDINGCOLLISION CKGUID(0x638737d6, 0xf783eae) 29 | 30 | #endif // BML_GUIDS_GRIDS_H 31 | -------------------------------------------------------------------------------- /include/BML/Guids/Controllers.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_CONTROLLERS_H 2 | #define BML_GUIDS_CONTROLLERS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Controllers 8 | // 9 | 10 | // Building Blocks 11 | #define VT_CONTROLLERS_JOYSTICKCONTROLLER CKGUID(0x85ebc1, 0x21935422) 12 | #define VT_CONTROLLERS_JOYSTICKMAPPER CKGUID(0x3ed1bea, 0x25692ecc) 13 | #define VT_CONTROLLERS_JOYSTICKWAITER CKGUID(0x12d010b, 0x13d010b) 14 | #define VT_CONTROLLERS_KEYEVENT CKGUID(0x1af2274b, 0x6b8c1524) 15 | #define VT_CONTROLLERS_KEYWAITER CKGUID(0x16d010b, 0x17d010b) 16 | #define VT_CONTROLLERS_KEYBOARDCONTROLLER CKGUID(0x43f1d000, 0x1a2b3c4d) 17 | #define VT_CONTROLLERS_KEYBOARDMAPPER CKGUID(0x53b2bde, 0x2f8f1d74) 18 | #define VT_CONTROLLERS_SWITCHONKEY CKGUID(0x1f49612e, 0x6cd63de0) 19 | #define VT_CONTROLLERS_INPUTSTRING CKGUID(0x693e7e4b, 0x100104c4) 20 | #define VT_CONTROLLERS_GETMOUSEPOSITION CKGUID(0x5b1f1381, 0x5c640372) 21 | #define VT_CONTROLLERS_GETMOUSEDISPLACEMENT CKGUID(0x32d26c84, 0x3c741f89) 22 | #define VT_CONTROLLERS_MOUSEWAITER CKGUID(0x14d010b, 0x15d010b) 23 | 24 | // Parameter Types 25 | #define CKPGUID_JOYAXIS CKGUID(0x1ed64a6a, 0x5a24059a) 26 | #define CKPGUID_MOUSEEVENT CKGUID(0x7abe0373, 0x1c366a1b) 27 | #define CKPGUID_KEYBOARDPART CKGUID(0x7a4347dd, 0x59a82cce) 28 | 29 | #endif // BML_GUIDS_CONTROLLERS_H 30 | -------------------------------------------------------------------------------- /include/BML/Guids/Cameras.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_CAMERAS_H 2 | #define BML_GUIDS_CAMERAS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Cameras 8 | // 9 | 10 | // Building Blocks 11 | #define VT_CAMERAS_DOLLY CKGUID(0xb98b83bb, 0x87787878) 12 | #define VT_CAMERAS_ORTHOGRAPHICZOOM CKGUID(0xa125555, 0xeee5488) 13 | #define VT_CAMERAS_SETCAMERATARGET CKGUID(0x74123741, 0x4563ffff) 14 | #define VT_CAMERAS_SETCLIPPINGPLANES CKGUID(0x652316a2, 0x1aa09a9) 15 | #define VT_CAMERAS_SETFOV CKGUID(0xaaaacccc, 0xaaaacccc) 16 | #define VT_CAMERAS_SETPROJECTION CKGUID(0xe444e666, 0x4eee6eee) 17 | #define VT_CAMERAS_SETZOOM CKGUID(0x40540a0d, 0xaaaaa) 18 | #define VT_CAMERAS_CAMERACOLORFILTER CKGUID(0xcd010b, 0xdd010b) 19 | #define VT_CAMERAS_VERTIGO CKGUID(0xb00d010a, 0xc00d010a) 20 | #define VT_CAMERAS_GETCURRENTCAMERA CKGUID(0x368f22b1, 0x222957e4) 21 | #define VT_CAMERAS_SETASACTIVECAMERA CKGUID(0x368f0ab1, 0x2d8957e4) 22 | #define VT_CAMERAS_CAMERAORBIT CKGUID(0x777d999e, 0xdef777d8) 23 | #define VT_CAMERAS_KEYBOARDCAMERAORBIT CKGUID(0x7610f4d, 0x69747b9d) 24 | #define VT_CAMERAS_MOUSECAMERAORBIT CKGUID(0x2356342f, 0x9542674f) 25 | #define VT_CAMERAS_JOYSTICKCAMERAORBIT CKGUID(0x400f0e6f, 0x72822162) 26 | 27 | // Parameter Types 28 | #define CKPGUID_PROJECTIONTYPE CKGUID(0x1ee22148, 0x602c1ca1) 29 | #define CKPGUID_MOUSEBUTTON CKGUID(0x1ff24d5a, 0x122f2c1f) 30 | 31 | #endif // BML_GUIDS_CAMERAS_H 32 | -------------------------------------------------------------------------------- /src/ModLoader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ModLoader - Game Engine Integration Layer 2 | # Provides CK2 Manager, core hooks, and Virtools plugin entry points 3 | 4 | set(MODLOADER_SOURCES 5 | Entry.cpp 6 | ModManager.cpp 7 | Behaviors/HookBlock.cpp 8 | ) 9 | 10 | set(MODLOADER_HEADERS 11 | ModManager.h 12 | Behaviors/HookBlock.h 13 | ) 14 | 15 | add_library(ModLoader SHARED 16 | ${MODLOADER_SOURCES} 17 | ${MODLOADER_HEADERS} 18 | ) 19 | 20 | target_include_directories(ModLoader 21 | PUBLIC 22 | $ 23 | $ 24 | PRIVATE 25 | ${CMAKE_CURRENT_SOURCE_DIR} 26 | ) 27 | 28 | target_link_libraries(ModLoader 29 | PUBLIC 30 | CK2 VxMath 31 | PRIVATE 32 | minhook 33 | ) 34 | 35 | target_compile_definitions(ModLoader PRIVATE 36 | MODLOADER_EXPORTS 37 | ) 38 | 39 | set_target_properties(ModLoader PROPERTIES 40 | OUTPUT_NAME "ModLoader" 41 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 42 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" 43 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" 44 | ) 45 | 46 | # Install targets 47 | install(TARGETS ModLoader 48 | EXPORT ModLoaderTargets 49 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 50 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 51 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 52 | ) 53 | -------------------------------------------------------------------------------- /src/SRTimer.cpp: -------------------------------------------------------------------------------- 1 | #include "SRTimer.h" 2 | #include 3 | 4 | SRTimer::SRTimer() : m_Time(0.0f), m_Running(false) {} 5 | 6 | void SRTimer::Reset() { 7 | m_Time = 0.0f; 8 | m_Dirty = true; 9 | } 10 | 11 | void SRTimer::Start() { 12 | m_Running = true; 13 | } 14 | 15 | void SRTimer::Pause() { 16 | m_Running = false; 17 | } 18 | 19 | void SRTimer::Update(float deltaTime) { 20 | if (m_Running) { 21 | m_Time += deltaTime * 1000.0f; // Convert to milliseconds 22 | m_Dirty = true; 23 | } 24 | } 25 | 26 | float SRTimer::GetTime() const { 27 | return m_Time / 1000.0f; // Return in seconds 28 | } 29 | 30 | bool SRTimer::IsRunning() const { 31 | return m_Running; 32 | } 33 | 34 | const char *SRTimer::GetFormattedTime() const { 35 | UpdateFormattedTime(); 36 | return m_FormattedTime; 37 | } 38 | 39 | void SRTimer::UpdateFormattedTime() const { 40 | if (!m_Dirty) return; 41 | 42 | float totalSeconds = m_Time / 1000.0f; 43 | int hours = (int)(totalSeconds / 3600.0f); 44 | int minutes = (int)((totalSeconds - hours * 3600.0f) / 60.0f); 45 | float seconds = totalSeconds - hours * 3600.0f - minutes * 60.0f; 46 | int milliseconds = (int)((seconds - (int)seconds) * 1000.0f); 47 | 48 | sprintf_s(m_FormattedTime, sizeof(m_FormattedTime), " %02d:%02d:%02d.%03d", hours, minutes, (int)seconds, milliseconds); 49 | m_Dirty = false; 50 | } 51 | -------------------------------------------------------------------------------- /src/FpsCounter.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_FPSCOUNTER_H 2 | #define BML_FPSCOUNTER_H 3 | 4 | #include 5 | #include 6 | 7 | class FpsCounter { 8 | public: 9 | explicit FpsCounter(uint32_t sampleCount = 60); 10 | ~FpsCounter() = default; 11 | 12 | void Update(float frameTime); 13 | 14 | float GetAverageFps() const; 15 | const char *GetFormattedFps() const; 16 | bool IsDirty() const { return m_Dirty; } 17 | void ClearDirty() { m_Dirty = false; } 18 | 19 | void SetUpdateFrequency(uint32_t frames); 20 | uint32_t GetUpdateFrequency() const; 21 | 22 | private: 23 | std::array m_FrameTimes = {}; // Circular buffer of frame times 24 | uint32_t m_SampleCount; // Number of samples to average 25 | uint32_t m_CurrentIndex; // Current index in circular buffer 26 | uint32_t m_FrameCounter; // Frame counter for update frequency 27 | uint32_t m_UpdateFrequency; // How often to update the average 28 | float m_CurrentAverageFps; // Current average FPS 29 | mutable char m_FormattedFps[16] = {}; // Formatted FPS string 30 | int m_LastFpsInt = 60; // Last integer FPS used for string 31 | bool m_Dirty = true; // Whether formatted FPS changed since last clear 32 | 33 | void RecalculateAverage(); 34 | }; 35 | 36 | #endif // BML_FPSCOUNTER_H 37 | -------------------------------------------------------------------------------- /src/Core/ModuleLoader.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MODULE_LOADER_H 2 | #define BML_CORE_MODULE_LOADER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifndef NOMINMAX 9 | #define NOMINMAX 10 | #endif 11 | #ifndef WIN32_LEAN_AND_MEAN 12 | #define WIN32_LEAN_AND_MEAN 13 | #endif 14 | #include 15 | 16 | #include "bml_export.h" 17 | 18 | #include "DependencyResolver.h" 19 | #include "ModHandle.h" 20 | #include "ModManifest.h" 21 | 22 | namespace BML::Core { 23 | class Context; 24 | 25 | struct LoadedModule { 26 | std::string id; 27 | const ModManifest *manifest{nullptr}; 28 | HMODULE handle{nullptr}; 29 | PFN_BML_ModEntrypoint entrypoint{nullptr}; 30 | std::wstring path; 31 | std::unique_ptr mod_handle; 32 | }; 33 | 34 | struct ModuleLoadError { 35 | std::string id; 36 | std::wstring path; 37 | std::string message; 38 | long system_code{0}; 39 | }; 40 | 41 | bool LoadModules(const std::vector &order, 42 | Context &context, 43 | PFN_BML_GetProcAddress get_proc, 44 | std::vector &out_modules, 45 | ModuleLoadError &out_error); 46 | 47 | void UnloadModules(std::vector &modules, BML_Context ctx); 48 | } // namespace BML::Core 49 | 50 | #endif // BML_CORE_MODULE_LOADER_H 51 | -------------------------------------------------------------------------------- /src/Gui/Element.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/Element.h" 2 | 3 | #include "ModContext.h" 4 | 5 | using namespace BGui; 6 | 7 | Element::Element(const char *name) { 8 | m_2dEntity = (CK2dEntity *) BML_GetCKContext()->CreateObject(CKCID_2DENTITY, (CKSTRING) name); 9 | BML_GetCKContext()->GetCurrentLevel()->AddObject(m_2dEntity); 10 | m_2dEntity->SetHomogeneousCoordinates(); 11 | m_2dEntity->EnableClipToCamera(false); 12 | m_2dEntity->EnableRatioOffset(false); 13 | m_2dEntity->SetZOrder(20); 14 | } 15 | 16 | Element::~Element() { 17 | CKContext *context = BML_GetCKContext(); 18 | if (context) 19 | context->DestroyObject(CKOBJID(m_2dEntity)); 20 | } 21 | 22 | Vx2DVector Element::GetPosition() { 23 | Vx2DVector res; 24 | m_2dEntity->GetPosition(res, true); 25 | return res; 26 | } 27 | 28 | void Element::SetPosition(Vx2DVector pos) { 29 | m_2dEntity->SetPosition(pos, true); 30 | } 31 | 32 | Vx2DVector Element::GetSize() { 33 | Vx2DVector res; 34 | m_2dEntity->GetSize(res, true); 35 | return res; 36 | } 37 | 38 | void Element::SetSize(Vx2DVector size) { 39 | m_2dEntity->SetSize(size, true); 40 | } 41 | 42 | int Element::GetZOrder() { 43 | return m_2dEntity->GetZOrder(); 44 | } 45 | 46 | void Element::SetZOrder(int z) { 47 | m_2dEntity->SetZOrder(z); 48 | } 49 | 50 | bool Element::IsVisible() { 51 | return m_2dEntity->IsVisible(); 52 | } 53 | 54 | void Element::SetVisible(bool visible) { 55 | m_2dEntity->Show(visible ? CKSHOW : CKHIDE); 56 | } -------------------------------------------------------------------------------- /src/Overlay.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_OVERLAY_H 2 | #define BML_OVERLAY_H 3 | 4 | #include "imgui.h" 5 | 6 | #include "CKContext.h" 7 | 8 | namespace Overlay { 9 | ImGuiContext *GetImGuiContext(); 10 | 11 | class ImGuiContextScope { 12 | public: 13 | explicit ImGuiContextScope(ImGuiContext *context = nullptr) { 14 | m_ImGuiContext = (context != nullptr) ? context : ImGui::GetCurrentContext(); 15 | ImGui::SetCurrentContext(GetImGuiContext()); 16 | } 17 | 18 | ImGuiContextScope(const ImGuiContextScope &rhs) = delete; 19 | ImGuiContextScope(ImGuiContextScope &&rhs) noexcept = delete; 20 | 21 | ~ImGuiContextScope() { 22 | ImGui::SetCurrentContext(m_ImGuiContext); 23 | } 24 | 25 | ImGuiContextScope &operator=(const ImGuiContextScope &rhs) = delete; 26 | ImGuiContextScope &operator=(ImGuiContextScope &&rhs) noexcept = delete; 27 | 28 | private: 29 | ImGuiContext *m_ImGuiContext; 30 | }; 31 | 32 | bool ImGuiInstallWin32Hooks(); 33 | bool ImGuiUninstallWin32Hooks(); 34 | 35 | ImGuiContext *ImGuiCreateContext(); 36 | void ImGuiDestroyContext(); 37 | 38 | bool ImGuiInitPlatform(CKContext *context); 39 | bool ImGuiInitRenderer(CKContext *context); 40 | void ImGuiShutdownPlatform(CKContext *context); 41 | void ImGuiShutdownRenderer(CKContext *context); 42 | 43 | void ImGuiNewFrame(); 44 | void ImGuiEndFrame(); 45 | void ImGuiRender(); 46 | void ImGuiOnRender(); 47 | }; 48 | 49 | #endif // BML_OVERLAY_H 50 | -------------------------------------------------------------------------------- /src/Core/DependencyResolver.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_DEPENDENCY_RESOLVER_H 2 | #define BML_CORE_DEPENDENCY_RESOLVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ModManifest.h" 10 | 11 | namespace BML::Core { 12 | struct DependencyResolutionError { 13 | std::string message; 14 | std::vector chain; 15 | }; 16 | 17 | struct DependencyWarning { 18 | std::string message; 19 | std::string mod_id; 20 | std::string dependency_id; 21 | }; 22 | 23 | struct ResolvedNode { 24 | std::string id; 25 | const ModManifest *manifest; 26 | }; 27 | 28 | class DependencyResolver { 29 | public: 30 | void RegisterManifest(const ModManifest &manifest); 31 | void Clear(); 32 | 33 | bool Resolve(std::vector &out_order, 34 | std::vector &out_warnings, 35 | DependencyResolutionError &out_error) const; 36 | 37 | private: 38 | struct Node { 39 | const ModManifest *manifest{nullptr}; 40 | std::vector duplicates; 41 | std::vector dependents; // reverse edges for Kahn's algorithm 42 | uint32_t incoming_edges{0}; 43 | }; 44 | 45 | std::unordered_map m_Nodes; 46 | std::vector m_RegistrationOrder; 47 | }; 48 | } // namespace BML::Core 49 | 50 | #endif // BML_CORE_DEPENDENCY_RESOLVER_H 51 | -------------------------------------------------------------------------------- /include/bml_engine_events.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bml_engine_events.h 3 | * @brief Payload definitions for BML engine lifecycle IMC topics. 4 | */ 5 | 6 | #ifndef BML_ENGINE_EVENTS_H 7 | #define BML_ENGINE_EVENTS_H 8 | 9 | #include "bml_types.h" 10 | 11 | BML_BEGIN_CDECLS 12 | 13 | struct CKContext; 14 | struct CKRenderContext; 15 | 16 | /** 17 | * @brief Payload published on the "BML/Engine/Init" topic. 18 | */ 19 | typedef struct BML_EngineInitEvent { 20 | uint32_t struct_size; /**< Size of this struct for forward compatibility. */ 21 | CKContext *context; /**< CKContext pointer, valid for the rest of the run. */ 22 | void *main_window; /**< HWND for the main application window (may be NULL). */ 23 | void *reserved0; /**< Reserved for future expansion (must be NULL). */ 24 | } BML_EngineInitEvent; 25 | 26 | /** 27 | * @brief Payload published on the "BML/Engine/Play" topic. 28 | */ 29 | typedef struct BML_EnginePlayEvent { 30 | uint32_t struct_size; /**< Size of this struct for forward compatibility. */ 31 | CKContext *context; /**< CKContext pointer. */ 32 | CKRenderContext *render_context; /**< Active CKRenderContext pointer. */ 33 | void *render_window; /**< HWND for the render window (may be NULL). */ 34 | BML_Bool is_resume; /**< True if this play event follows a pause/resume. */ 35 | uint32_t reserved0; /**< Reserved for future expansion (must be zero). */ 36 | } BML_EnginePlayEvent; 37 | 38 | BML_END_CDECLS 39 | 40 | #endif /* BML_ENGINE_EVENTS_H */ 41 | -------------------------------------------------------------------------------- /include/bml_hot_reload.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_HOT_RELOAD_H 2 | #define BML_HOT_RELOAD_H 3 | 4 | #include "bml_types.h" 5 | #include "bml_version.h" 6 | 7 | BML_BEGIN_CDECLS 8 | 9 | #define BML_MOD_LIFECYCLE_SCHEMA_HASH 0x4d4c4345u /* 'MLCE' */ 10 | #define BML_MOD_LIFECYCLE_SCHEMA_VERSION 1u 11 | 12 | typedef struct BML_ModLifecycleEvent { 13 | BML_Version version; 14 | const char *mod_id; 15 | size_t mod_id_length; 16 | } BML_ModLifecycleEvent; 17 | 18 | #pragma pack(push, 1) 19 | typedef struct BML_ModLifecycleWireHeader { 20 | BML_Version version; 21 | uint32_t id_length; 22 | } BML_ModLifecycleWireHeader; 23 | #pragma pack(pop) 24 | 25 | static inline BML_Bool bmlParseModLifecycleEvent(const void *payload, 26 | size_t payload_len, 27 | BML_ModLifecycleEvent *out_event) { 28 | if (!payload || !out_event) 29 | return BML_FALSE; 30 | 31 | if (payload_len < sizeof(BML_ModLifecycleWireHeader)) 32 | return BML_FALSE; 33 | 34 | const BML_ModLifecycleWireHeader *header = (const BML_ModLifecycleWireHeader *)payload; 35 | size_t required = sizeof(BML_ModLifecycleWireHeader) + (size_t)header->id_length; 36 | if (required > payload_len) 37 | return BML_FALSE; 38 | 39 | out_event->version = header->version; 40 | out_event->mod_id_length = header->id_length; 41 | out_event->mod_id = (const char *)payload + sizeof(BML_ModLifecycleWireHeader); 42 | return BML_TRUE; 43 | } 44 | 45 | BML_END_CDECLS 46 | 47 | #endif /* BML_HOT_RELOAD_H */ 48 | -------------------------------------------------------------------------------- /src/imgui_impl_ck2.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for Virtools 2 | 3 | // Implemented features: 4 | // [X] Renderer: User texture binding. 5 | // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. 6 | // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). 7 | 8 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 9 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 10 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 11 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 12 | 13 | #pragma once 14 | #include "imgui.h" // IMGUI_IMPL_API 15 | 16 | class CKContext; 17 | 18 | IMGUI_IMPL_API bool ImGui_ImplCK2_Init(CKContext *context); 19 | IMGUI_IMPL_API void ImGui_ImplCK2_Shutdown(); 20 | IMGUI_IMPL_API void ImGui_ImplCK2_NewFrame(); 21 | IMGUI_IMPL_API void ImGui_ImplCK2_RenderDrawData(ImDrawData *draw_data); 22 | 23 | // Use if you want to reset your rendering device without losing Dear ImGui state. 24 | IMGUI_IMPL_API bool ImGui_ImplCK2_CreateDeviceObjects(); 25 | IMGUI_IMPL_API void ImGui_ImplCK2_InvalidateDeviceObjects(); 26 | 27 | // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. 28 | IMGUI_IMPL_API void ImGui_ImplCK2_UpdateTexture(ImTextureData* tex); -------------------------------------------------------------------------------- /src/Core/DiagnosticManager.cpp: -------------------------------------------------------------------------------- 1 | #include "DiagnosticManager.h" 2 | 3 | namespace BML::Core { 4 | // Thread-local error context 5 | static thread_local ErrorContext g_ThreadErrorContext; 6 | 7 | DiagnosticManager &DiagnosticManager::Instance() { 8 | static DiagnosticManager instance; 9 | return instance; 10 | } 11 | 12 | ErrorContext &DiagnosticManager::GetThreadContext() { 13 | return g_ThreadErrorContext; 14 | } 15 | 16 | BML_Result DiagnosticManager::GetLastError(BML_ErrorInfo *out_error) { 17 | if (!out_error) { 18 | return BML_RESULT_INVALID_ARGUMENT; 19 | } 20 | 21 | if (out_error->struct_size < sizeof(BML_ErrorInfo)) { 22 | return BML_RESULT_INVALID_SIZE; 23 | } 24 | 25 | auto &ctx = GetThreadContext(); 26 | if (!ctx.has_error) { 27 | return BML_RESULT_NOT_FOUND; 28 | } 29 | 30 | *out_error = ctx.info; 31 | return BML_RESULT_OK; 32 | } 33 | 34 | void DiagnosticManager::ClearLastError() { 35 | auto &ctx = GetThreadContext(); 36 | ctx.Clear(); 37 | } 38 | 39 | void DiagnosticManager::SetError(BML_Result code, 40 | const char *message, 41 | const char *api_name, 42 | const char *source_file, 43 | int source_line) { 44 | auto &ctx = GetThreadContext(); 45 | ctx.SetError(code, message, api_name, source_file, source_line); 46 | } 47 | } // namespace BML::Core 48 | -------------------------------------------------------------------------------- /include/BML/IConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_ICONFIG_H 2 | #define BML_ICONFIG_H 3 | 4 | #include "CKEnums.h" 5 | 6 | #include "BML/Defines.h" 7 | 8 | class BML_EXPORT IProperty { 9 | public: 10 | virtual const char *GetString() = 0; 11 | virtual bool GetBoolean() = 0; 12 | virtual int GetInteger() = 0; 13 | virtual float GetFloat() = 0; 14 | virtual CKKEYBOARD GetKey() = 0; 15 | 16 | virtual void SetString(const char *value) = 0; 17 | virtual void SetBoolean(bool value) = 0; 18 | virtual void SetInteger(int value) = 0; 19 | virtual void SetFloat(float value) = 0; 20 | virtual void SetKey(CKKEYBOARD value) = 0; 21 | 22 | virtual void SetComment(const char *comment) = 0; 23 | virtual void SetDefaultString(const char *value) = 0; 24 | virtual void SetDefaultBoolean(bool value) = 0; 25 | virtual void SetDefaultInteger(int value) = 0; 26 | virtual void SetDefaultFloat(float value) = 0; 27 | virtual void SetDefaultKey(CKKEYBOARD value) = 0; 28 | 29 | enum PropertyType { 30 | STRING, 31 | BOOLEAN, 32 | INTEGER, 33 | KEY, 34 | FLOAT, 35 | NONE 36 | }; 37 | 38 | virtual PropertyType GetType() = 0; 39 | }; 40 | 41 | class BML_EXPORT IConfig { 42 | public: 43 | virtual bool HasCategory(const char *category) = 0; 44 | virtual bool HasKey(const char *category, const char *key) = 0; 45 | virtual IProperty *GetProperty(const char *category, const char *key) = 0; 46 | virtual void SetCategoryComment(const char *category, const char *comment) = 0; 47 | virtual ~IConfig() = default; 48 | }; 49 | 50 | #endif // BML_ICONFIG_H -------------------------------------------------------------------------------- /src/FpsCounter.cpp: -------------------------------------------------------------------------------- 1 | #include "FpsCounter.h" 2 | #include 3 | #include 4 | 5 | FpsCounter::FpsCounter(uint32_t sampleCount) 6 | : m_SampleCount(std::min(sampleCount, (uint32_t) m_FrameTimes.size())), 7 | m_CurrentIndex(0), 8 | m_FrameCounter(0), 9 | m_UpdateFrequency(1), 10 | m_CurrentAverageFps(60.0f) { 11 | } 12 | 13 | void FpsCounter::Update(float frameTime) { 14 | m_FrameTimes[m_CurrentIndex] = frameTime; 15 | m_CurrentIndex = (m_CurrentIndex + 1) % m_SampleCount; 16 | m_FrameCounter++; 17 | 18 | if (m_FrameCounter >= m_UpdateFrequency) { 19 | m_FrameCounter = 0; 20 | RecalculateAverage(); 21 | m_Dirty = true; 22 | } 23 | } 24 | 25 | void FpsCounter::RecalculateAverage() { 26 | float totalTime = 0.0f; 27 | for (uint32_t i = 0; i < m_SampleCount; ++i) { 28 | totalTime += m_FrameTimes[i]; 29 | } 30 | m_CurrentAverageFps = m_SampleCount / totalTime; 31 | } 32 | 33 | float FpsCounter::GetAverageFps() const { 34 | return m_CurrentAverageFps; 35 | } 36 | 37 | const char *FpsCounter::GetFormattedFps() const { 38 | int currentFps = (int) m_CurrentAverageFps; 39 | if (currentFps != m_LastFpsInt) { 40 | const_cast(m_LastFpsInt) = currentFps; 41 | sprintf_s(m_FormattedFps, sizeof(m_FormattedFps), "FPS: %d", currentFps); 42 | } 43 | return m_FormattedFps; 44 | } 45 | 46 | void FpsCounter::SetUpdateFrequency(uint32_t frames) { 47 | m_UpdateFrequency = (frames > 0) ? frames : 1; 48 | } 49 | 50 | uint32_t FpsCounter::GetUpdateFrequency() const { 51 | return m_UpdateFrequency; 52 | } 53 | -------------------------------------------------------------------------------- /src/Core/ModManifest.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MODMANIFEST_H 2 | #define BML_CORE_MODMANIFEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "SemanticVersion.h" 9 | 10 | namespace BML::Core { 11 | struct ModDependency { 12 | std::string id; 13 | SemanticVersionRange requirement; 14 | bool optional{false}; 15 | }; 16 | 17 | struct ModConflict { 18 | std::string id; 19 | SemanticVersionRange requirement; 20 | std::string reason; 21 | }; 22 | 23 | struct ModPackage { 24 | std::string id; 25 | std::string name; 26 | std::string version; 27 | SemanticVersion parsed_version; 28 | std::vector authors; 29 | std::string description; 30 | std::string entry; 31 | }; 32 | 33 | struct ModManifest { 34 | ModPackage package; 35 | std::vector dependencies; 36 | std::vector conflicts; 37 | std::vector capabilities; 38 | std::wstring manifest_path; 39 | std::wstring directory; 40 | std::wstring source_archive; 41 | }; 42 | 43 | struct ManifestParseError { 44 | std::string message; 45 | std::optional file; 46 | std::optional line; 47 | std::optional column; 48 | }; 49 | 50 | class ManifestParser { 51 | public: 52 | ManifestParser() = default; 53 | 54 | bool ParseFile(const std::wstring &path, ModManifest &out_manifest, ManifestParseError &out_error) const; 55 | }; 56 | } // namespace BML::Core 57 | 58 | #endif // BML_CORE_MODMANIFEST_H 59 | -------------------------------------------------------------------------------- /src/Core/Export.cpp: -------------------------------------------------------------------------------- 1 | #include "bml_export.h" 2 | 3 | #include "ApiRegistry.h" 4 | #include "Microkernel.h" 5 | 6 | BML_BEGIN_CDECLS 7 | 8 | BML_API BML_Result bmlAttach(void) { 9 | // Phase 0: Initialize core only (safe in DllMain) 10 | // Creates context, registers core APIs 11 | if (!BML::Core::InitializeCore()) 12 | return BML_RESULT_FAIL; 13 | 14 | return BML_RESULT_OK; 15 | } 16 | 17 | BML_API BML_Result bmlDiscoverModules(void) { 18 | // Phase 1: Discover modules (call after bmlAttach) 19 | // Scans for mods, validates manifests, resolves dependencies 20 | return BML::Core::DiscoverModules() ? BML_RESULT_OK : BML_RESULT_FAIL; 21 | } 22 | 23 | BML_API BML_Result bmlLoadModules(void) { 24 | // Phase 2: Load discovered modules (call when CKContext is available) 25 | return BML::Core::LoadDiscoveredModules() ? BML_RESULT_OK : BML_RESULT_FAIL; 26 | } 27 | 28 | BML_API void bmlDetach(void) { 29 | BML::Core::ShutdownMicrokernel(); 30 | } 31 | 32 | BML_API void *bmlGetProcAddress(const char *proc_name) { 33 | if (!proc_name) 34 | return nullptr; 35 | return BML::Core::ApiRegistry::Instance().Get(proc_name); 36 | } 37 | 38 | BML_API void *bmlGetProcAddressById(BML_ApiId api_id) { 39 | return BML::Core::ApiRegistry::Instance().GetById(api_id); 40 | } 41 | 42 | BML_API int bmlGetApiId(const char *proc_name, BML_ApiId *out_id) { 43 | if (!proc_name || !out_id) 44 | return 0; 45 | return BML::Core::ApiRegistry::Instance().GetApiId(proc_name, out_id) ? 1 : 0; 46 | } 47 | 48 | BML_API const BML_BootstrapDiagnostics *bmlGetBootstrapDiagnostics(void) { 49 | return &BML::Core::GetPublicDiagnostics(); 50 | } 51 | 52 | BML_END_CDECLS -------------------------------------------------------------------------------- /include/BML/IMessageReceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_IMESSAGERECEIVER_H 2 | #define BML_IMESSAGERECEIVER_H 3 | 4 | #include "BML/Defines.h" 5 | 6 | class BML_EXPORT IMessageReceiver { 7 | public: 8 | virtual void OnPreStartMenu() {}; 9 | virtual void OnPostStartMenu() {}; 10 | 11 | virtual void OnExitGame() {}; 12 | 13 | virtual void OnPreLoadLevel() {}; 14 | virtual void OnPostLoadLevel() {}; 15 | 16 | virtual void OnStartLevel() {}; 17 | 18 | virtual void OnPreResetLevel() {}; 19 | virtual void OnPostResetLevel() {}; 20 | 21 | virtual void OnPauseLevel() {}; 22 | virtual void OnUnpauseLevel() {}; 23 | 24 | virtual void OnPreExitLevel() {}; 25 | virtual void OnPostExitLevel() {}; 26 | 27 | virtual void OnPreNextLevel() {}; 28 | virtual void OnPostNextLevel() {}; 29 | 30 | virtual void OnDead() {}; 31 | 32 | virtual void OnPreEndLevel() {}; 33 | virtual void OnPostEndLevel() {}; 34 | 35 | virtual void OnCounterActive() {}; 36 | virtual void OnCounterInactive() {}; 37 | 38 | virtual void OnBallNavActive() {}; 39 | virtual void OnBallNavInactive() {}; 40 | 41 | virtual void OnCamNavActive() {}; 42 | virtual void OnCamNavInactive() {}; 43 | 44 | virtual void OnBallOff() {}; 45 | 46 | virtual void OnPreCheckpointReached() {}; 47 | virtual void OnPostCheckpointReached() {}; 48 | 49 | virtual void OnLevelFinish() {}; 50 | 51 | virtual void OnGameOver() {}; 52 | 53 | virtual void OnExtraPoint() {}; 54 | 55 | virtual void OnPreSubLife() {}; 56 | virtual void OnPostSubLife() {}; 57 | 58 | virtual void OnPreLifeUp() {}; 59 | virtual void OnPostLifeUp() {}; 60 | }; 61 | 62 | #endif // BML_IMESSAGERECEIVER_H 63 | -------------------------------------------------------------------------------- /include/bml_loader.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_LOADER_H 2 | #define BML_LOADER_H 3 | 4 | #include "bml_types.h" 5 | #include "bml_export.h" 6 | #include "bml_errors.h" /* Includes diagnostics */ 7 | #include "bml_config.h" 8 | #include "bml_core.h" 9 | #include "bml_extension.h" 10 | #include "bml_imc.h" 11 | #include "bml_logging.h" 12 | #include "bml_resource.h" 13 | #include "bml_memory.h" 14 | #include "bml_sync.h" 15 | #include "bml_profiling.h" 16 | #include "bml_api_tracing.h" 17 | #include "bml_capabilities.h" 18 | 19 | BML_BEGIN_CDECLS 20 | 21 | /* 22 | * Usage (header-only, GLAD-style): 23 | * 24 | * In ONE .cpp file (typically your main .cpp): 25 | * #define BML_LOADER_IMPLEMENTATION 26 | * #include 27 | * 28 | * In other .cpp files that need to call BML APIs: 29 | * #include // Gets extern declarations 30 | */ 31 | 32 | /* 33 | * Call bmlLoadAPI at the very beginning of your mod entry point to populate 34 | * the global function pointers. If the return value is not OK, abort your 35 | * initialization and propagate the error back to the host. 36 | */ 37 | BML_Result bmlLoadAPI(PFN_BML_GetProcAddress get_proc); 38 | 39 | /* 40 | * Reset every global function pointer back to nullptr. Call this during mod 41 | * shutdown. 42 | */ 43 | void bmlUnloadAPI(void); 44 | 45 | /* Query whether the API is currently loaded (defensive check). */ 46 | BML_Bool bmlIsApiLoaded(void); 47 | 48 | /* Get total number of APIs in the loader. */ 49 | size_t bmlGetApiCount(void); 50 | 51 | /* Get number of required APIs that must be present for loading to succeed. */ 52 | size_t bmlGetRequiredApiCount(void); 53 | 54 | #include "bml_loader_autogen.h" 55 | 56 | BML_END_CDECLS 57 | 58 | #endif /* BML_LOADER_H */ 59 | -------------------------------------------------------------------------------- /src/Core/SemanticVersion.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_SEMANTIC_VERSION_H 2 | #define BML_CORE_SEMANTIC_VERSION_H 3 | 4 | #include 5 | 6 | namespace BML::Core { 7 | struct SemanticVersion { 8 | int major{0}; 9 | int minor{0}; 10 | int patch{0}; 11 | std::string prerelease; // e.g., "alpha", "beta.1", "rc.2" 12 | std::string build_metadata; // e.g., "build.45" 13 | }; 14 | 15 | enum class VersionOperator { 16 | Exact, 17 | GreaterEqual, // ">=" 18 | Greater, // ">" 19 | LessEqual, // "<=" 20 | Less, // "<" 21 | Compatible, // "^" 22 | ApproximatelyEquivalent // "~" 23 | }; 24 | 25 | struct SemanticVersionRange { 26 | std::string raw_expression; 27 | VersionOperator op{VersionOperator::Exact}; 28 | SemanticVersion version{}; 29 | bool parsed{false}; 30 | int parsed_components{0}; 31 | }; 32 | 33 | bool ParseSemanticVersion(const std::string &text, 34 | SemanticVersion &out_version, 35 | int *out_component_count = nullptr); 36 | bool ParseSemanticVersionRange(const std::string &text, 37 | SemanticVersionRange &out_range, 38 | std::string &out_error); 39 | bool IsVersionSatisfied(const SemanticVersionRange &range, 40 | const SemanticVersion &version); 41 | bool IsVersionOutdated(const SemanticVersionRange &range, 42 | const SemanticVersion &version, 43 | std::string &out_suggestion); 44 | } // namespace BML::Core 45 | 46 | #endif // BML_CORE_SEMANTIC_VERSION_H 47 | -------------------------------------------------------------------------------- /src/ModManager.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_MODMANAGER_H 2 | #define BML_MODMANAGER_H 3 | 4 | #include "CKBaseManager.h" 5 | #include "CKContext.h" 6 | 7 | #ifndef MOD_MANAGER_GUID 8 | #define MOD_MANAGER_GUID CKGUID(0x32a40332, 0x3bf12a51) 9 | #endif 10 | 11 | class ModContext; 12 | 13 | class ModManager : public CKBaseManager { 14 | public: 15 | explicit ModManager(CKContext *context); 16 | ~ModManager() override; 17 | 18 | CKERROR OnCKInit() override; 19 | CKERROR OnCKEnd() override; 20 | 21 | CKERROR OnCKPlay() override; 22 | CKERROR OnCKReset() override; 23 | 24 | CKERROR PreProcess() override; 25 | CKERROR PostProcess() override; 26 | 27 | CKERROR OnPostRender(CKRenderContext *dev) override; 28 | CKERROR OnPostSpriteRender(CKRenderContext *dev) override; 29 | 30 | CKDWORD GetValidFunctionsMask() override { 31 | return CKMANAGER_FUNC_OnCKInit | 32 | CKMANAGER_FUNC_OnCKEnd | 33 | CKMANAGER_FUNC_OnCKPlay | 34 | CKMANAGER_FUNC_OnCKReset | 35 | CKMANAGER_FUNC_PreProcess | 36 | CKMANAGER_FUNC_PostProcess | 37 | CKMANAGER_FUNC_OnPostRender | 38 | CKMANAGER_FUNC_OnPostSpriteRender; 39 | } 40 | 41 | int GetFunctionPriority(CKMANAGER_FUNCTIONS Function) override { 42 | if (Function == CKMANAGER_FUNC_PreProcess) 43 | return -10000; // Low Priority 44 | else if (Function == CKMANAGER_FUNC_PostProcess) 45 | return 10000; // High Priority 46 | else 47 | return 0; 48 | } 49 | 50 | static ModManager *GetManager(CKContext *context) { 51 | return (ModManager *)context->GetManagerByGuid(MOD_MANAGER_GUID); 52 | } 53 | 54 | protected: 55 | ModContext *m_ModContext; 56 | CKRenderContext *m_RenderContext = nullptr; 57 | }; 58 | 59 | #endif // BML_MODMANAGER_H 60 | -------------------------------------------------------------------------------- /src/Gui/Label.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/Label.h" 2 | 3 | #include "BML/ScriptHelper.h" 4 | #include "ModContext.h" 5 | 6 | namespace ExecuteBB { 7 | int GetFont(FontType type); 8 | FontType GetFontType(int font); 9 | } 10 | 11 | using namespace BGui; 12 | 13 | Label::Label(const char *name) : Element(name) { 14 | m_Text2d = ExecuteBB::Create2DText(BML_GetModContext()->GetScriptByName("Level_Init"), m_2dEntity); 15 | } 16 | 17 | Label::~Label() { 18 | CKContext *context = BML_GetCKContext(); 19 | if (context) 20 | context->DestroyObject(CKOBJID(m_Text2d)); 21 | } 22 | 23 | const char *Label::GetText() { 24 | return ScriptHelper::GetParamString(m_Text2d->GetInputParameter(1)->GetRealSource()); 25 | } 26 | 27 | void Label::SetText(const char *text) { 28 | ScriptHelper::SetParamString(m_Text2d->GetInputParameter(1)->GetRealSource(), text); 29 | } 30 | 31 | ExecuteBB::FontType Label::GetFont() { 32 | return ExecuteBB::GetFontType( 33 | ScriptHelper::GetParamValue(m_Text2d->GetInputParameter(0)->GetRealSource())); 34 | } 35 | 36 | void Label::SetFont(ExecuteBB::FontType font) { 37 | ScriptHelper::SetParamValue(m_Text2d->GetInputParameter(0)->GetRealSource(), ExecuteBB::GetFont(font)); 38 | } 39 | 40 | void Label::SetAlignment(int align) { 41 | ScriptHelper::SetParamValue(m_Text2d->GetInputParameter(2)->GetRealSource(), align); 42 | } 43 | 44 | int Label::GetTextFlags() { 45 | return ScriptHelper::GetParamValue(m_Text2d->GetLocalParameter(0)); 46 | } 47 | 48 | void Label::SetTextFlags(int flags) { 49 | ScriptHelper::SetParamValue(m_Text2d->GetLocalParameter(0), flags); 50 | } 51 | 52 | void Label::SetOffset(Vx2DVector offset) { 53 | ScriptHelper::SetParamValue(m_Text2d->GetInputParameter(4)->GetRealSource(), offset); 54 | } 55 | 56 | void Label::Process() { 57 | m_Text2d->ActivateInput(0); 58 | m_Text2d->Execute(0); 59 | } -------------------------------------------------------------------------------- /include/BML/Guids/TT_ParticleSystems_RT.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_TT_PARTICLESYSTEMS_RT_H 2 | #define BML_GUIDS_TT_PARTICLESYSTEMS_RT_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // TT_ParticleSystems_RT 8 | // 9 | 10 | // Building Blocks 11 | #define TT_PARTICLESYSTEMS_RT_POINTPARTICLESYSTEM CKGUID(0x506b40f7, 0x30852e46) 12 | #define TT_PARTICLESYSTEMS_RT_LINEARPARTICLESYSTEM CKGUID(0x1f1e7188, 0xec26d1f) 13 | #define TT_PARTICLESYSTEMS_RT_PLANARPARTICLESYSTEM CKGUID(0x49957bfe, 0xff27ffc) 14 | #define TT_PARTICLESYSTEMS_RT_CUBICPARTICLESYSTEM CKGUID(0x3d7453f9, 0x1dba691f) 15 | #define TT_PARTICLESYSTEMS_RT_DISCPARTICLESYSTEM CKGUID(0x3fb65480, 0x6ca450e2) 16 | #define TT_PARTICLESYSTEMS_RT_OBJECTPARTICLESYSTEM CKGUID(0x13172f30, 0x3a7876ed) 17 | #define TT_PARTICLESYSTEMS_RT_CURVEPARTICLESYSTEM CKGUID(0x4a6524c4, 0x13b55824) 18 | #define TT_PARTICLESYSTEMS_RT_CYLINDRICALPARTICLESYSTEM CKGUID(0x67c90f47, 0x1480721e) 19 | #define TT_PARTICLESYSTEMS_RT_SPHERICALPARTICLESYSTEM CKGUID(0x67c88f47, 0x8880721e) 20 | #define TT_PARTICLESYSTEMS_RT_TT_TIMEDEPENDENTPOINTPARTICLESYSTEM CKGUID(0x569d2cc2, 0x3bcb01b9) 21 | #define TT_PARTICLESYSTEMS_RT_TTWAVEPARTICLESYSTEM CKGUID(0x2da5a18, 0x52227285) 22 | 23 | // Parameter Types 24 | #define CKPGUID_RENDERMODES CKGUID(0x89e77d4, 0x2ef077d2) 25 | #define CKPGUID_LOOPMODE CKGUID(0x63942d15, 0x5ac51a7) 26 | 27 | #define CKPGUID_EVOLUTIONS CKGUID(0x270f7b39, 0x6e0b184c) 28 | #define CKPGUID_VARIANCES CKGUID(0x83e73d4, 0x2e3073d2) 29 | #define CKPGUID_INTERACTORS CKGUID(0x83231d4, 0x223173d2) 30 | #define CKPGUID_DEFLECTORS CKGUID(0x83348d4, 0x234873d2) 31 | 32 | #define CKPGUID_PARTICLEMUTATION CKGUID(0x468b2bec, 0x739211ce) 33 | #define CKPGUID_PARTICLETUNNEL CKGUID(0x479c2ceb, 0x729312ed) 34 | #define CKPGUID_PDEFLECTORS CKGUID(0x57de0fd9, 0x758a71d6) 35 | #define CKPGUID_PODEFLECTORS CKGUID(0x778d5bd9, 0x5da52335) 36 | 37 | #endif // BML_GUIDS_TT_PARTICLESYSTEMS_RT_H 38 | -------------------------------------------------------------------------------- /include/bml_module.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_MODULE_H 2 | #define BML_MODULE_H 3 | 4 | /** 5 | * @file bml_module.h 6 | * @brief Helper macros for BML module development 7 | * 8 | * Include this header in your module source file to define entry points correctly. 9 | */ 10 | 11 | #include "bml_export.h" 12 | 13 | /** 14 | * @brief Define this before including module headers to enable entry point definitions 15 | * 16 | * This prevents bml_export.h from trying to import module entry points. 17 | */ 18 | #define BML_DEFINING_MODULE_ENTRY_POINTS 19 | 20 | /** 21 | * @brief Declare module entry points with correct linkage 22 | * 23 | * Usage in your module .cpp file: 24 | * ```cpp 25 | * #include "bml_module.h" 26 | * #define BML_LOADER_IMPLEMENTATION 27 | * #include "bml_loader.h" 28 | * 29 | * namespace { 30 | * BML_Result OnAttach(const BML_ModAttachArgs *args) { 31 | * BML_Result res = bmlLoadAPI(args->get_proc); 32 | * if (res != BML_RESULT_OK) { 33 | * return res; 34 | * } 35 | * // ... initialization logic ... 36 | * return BML_RESULT_OK; 37 | * } 38 | * 39 | * BML_Result OnDetach(const BML_ModDetachArgs *args) { 40 | * // ... cleanup logic ... 41 | * bmlUnloadAPI(); 42 | * return BML_RESULT_OK; 43 | * } 44 | * } 45 | * 46 | * BML_MODULE_ENTRY BML_Result BML_ModEntrypoint(BML_ModEntrypointCommand cmd, void *data) { 47 | * switch (cmd) { 48 | * case BML_MOD_ENTRYPOINT_ATTACH: 49 | * return OnAttach(static_cast(data)); 50 | * case BML_MOD_ENTRYPOINT_DETACH: 51 | * return OnDetach(static_cast(data)); 52 | * default: 53 | * return BML_RESULT_INVALID_ARGUMENT; 54 | * } 55 | * } 56 | * ``` 57 | */ 58 | #ifdef __cplusplus 59 | # define BML_MODULE_ENTRY extern "C" __declspec(dllexport) 60 | #else 61 | # define BML_MODULE_ENTRY __declspec(dllexport) 62 | #endif 63 | 64 | #endif /* BML_MODULE_H */ 65 | -------------------------------------------------------------------------------- /templates/mod-template/src/HelloMod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | class CommandHello : public ICommand { 10 | public: 11 | std::string GetName() override { return "hello"; } 12 | std::string GetAlias() override { return ""; } 13 | std::string GetDescription() override { return "Print a greeting: hello [name]"; } 14 | bool IsCheat() override { return false; } 15 | 16 | void Execute(IBML *bml, const std::vector &args) override { 17 | const char *target = args.size() >= 2 ? args[1].c_str() : "world"; 18 | std::string line = std::string("\x1b[36mHello, ") + target + "!\x1b[0m"; 19 | bml->SendIngameMessage(line.c_str()); 20 | } 21 | 22 | const std::vector GetTabCompletion(IBML *, const std::vector &args) override { 23 | if (args.size() == 2) return {"world", "Ballance", "BML"}; 24 | return {}; 25 | } 26 | }; 27 | 28 | class HelloMod final : public IMod { 29 | public: 30 | explicit HelloMod(IBML *bml) : IMod(bml) {} 31 | 32 | const char *GetID() override { return "HelloMod"; } 33 | const char *GetVersion() override { return "1.0.0"; } 34 | const char *GetName() override { return "Hello Mod"; } 35 | const char *GetAuthor() override { return "Template"; } 36 | const char *GetDescription() override { return "Minimal example mod for BML+"; } 37 | DECLARE_BML_VERSION; 38 | 39 | void OnLoad() override { 40 | GetLogger()->Info("HelloMod loaded"); 41 | m_BML->RegisterCommand(new CommandHello()); 42 | m_BML->SendIngameMessage("\x1b[32mHelloMod loaded. Type 'hello' in command bar.\x1b[0m"); 43 | } 44 | 45 | void OnUnload() override { 46 | GetLogger()->Info("HelloMod unloaded"); 47 | } 48 | }; 49 | 50 | MOD_EXPORT IMod *BMLEntry(IBML *bml) { 51 | return new HelloMod(bml); 52 | } 53 | 54 | MOD_EXPORT void BMLExit(IMod *mod) { 55 | delete mod; 56 | } 57 | -------------------------------------------------------------------------------- /src/CommandBar.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_COMMANDBAR_H 2 | #define BML_COMMANDBAR_H 3 | 4 | #include "BML/Bui.h" 5 | 6 | class CommandBar : public Bui::Window { 7 | public: 8 | CommandBar(); 9 | ~CommandBar() override; 10 | 11 | ImGuiWindowFlags GetFlags() override; 12 | 13 | void OnPreBegin() override; 14 | void OnDraw() override; 15 | void OnPostEnd() override; 16 | void OnShow() override; 17 | void OnHide() override; 18 | 19 | void PrintHistory(); 20 | void ExecuteHistory(int index); 21 | void ClearHistory(); 22 | void LoadHistory(); 23 | void SaveHistory(); 24 | 25 | void ToggleCommandBar(bool on = true); 26 | 27 | void NextCandidate(); 28 | void PrevCandidate(); 29 | void NextPageOfCandidates(); 30 | void PrevPageOfCandidates(); 31 | void InvalidateCandidates(); 32 | void GenerateCandidatePages(); 33 | 34 | size_t OnCompletion(const char *lineStart, const char *lineEnd); 35 | int OnTextEdit(ImGuiInputTextCallbackData *data); 36 | 37 | static int TextEditCallback(ImGuiInputTextCallbackData *data); 38 | static void StripLine(const char *&lineStart, const char *&lineEnd); 39 | static int FirstToken(const char *tokenStart, const char *&tokenEnd); 40 | static int LastToken(const char *&tokenStart, const char *tokenEnd); 41 | static std::vector MakeArgs(const char *line); 42 | // Build args from a substring range [begin, end) without looking at trailing text 43 | static std::vector MakeArgsRange(const char *begin, const char *end); 44 | 45 | private: 46 | ImVec2 m_WindowPos; 47 | ImVec2 m_WindowSize; 48 | bool m_VisiblePrev = false; 49 | bool m_ShowHints = false; 50 | std::string m_Buffer; 51 | int m_CursorPos = 0; 52 | int m_HistoryIndex = -1; 53 | std::vector m_History; 54 | int m_CandidateSelected = -1; 55 | int m_CandidateIndex = 0; 56 | int m_CandidatePage = 0; 57 | std::vector m_CandidatePages; 58 | std::vector m_Candidates; 59 | }; 60 | 61 | #endif // BML_COMMANDBAR_H 62 | -------------------------------------------------------------------------------- /src/Core/LoggingApi.cpp: -------------------------------------------------------------------------------- 1 | #include "Logging.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ApiRegistry.h" 13 | #include "ApiRegistrationMacros.h" 14 | #include "Context.h" 15 | #include "bml_api_ids.h" 16 | #include "bml_capabilities.h" 17 | 18 | namespace BML::Core { 19 | BML_Result BML_API_LoggingGetCaps(BML_LogCaps *out_caps) { 20 | return GetLoggingCaps(out_caps); 21 | } 22 | 23 | BML_Result BML_API_RegisterLogSinkOverride(const BML_LogSinkOverrideDesc *desc) { 24 | return RegisterLogSinkOverride(desc); 25 | } 26 | 27 | BML_Result BML_API_ClearLogSinkOverride() { 28 | return ClearLogSinkOverride(); 29 | } 30 | 31 | void RegisterLoggingApis() { 32 | BML_BEGIN_API_REGISTRATION(); 33 | 34 | // Core logging APIs - variadic functions need manual registration with full metadata 35 | // Note: These are thread-safe (FREE threading model) 36 | detail::RegisterApiWithMetadata(registry, "bmlLog", BML_API_ID_bmlLog, 37 | reinterpret_cast(LogMessage), BML_CAP_LOGGING); 38 | detail::RegisterApiWithMetadata(registry, "bmlLogVa", BML_API_ID_bmlLogVa, 39 | reinterpret_cast(LogMessageVa), BML_CAP_LOGGING); 40 | detail::RegisterApiWithMetadata(registry, "bmlSetLogFilter", BML_API_ID_bmlSetLogFilter, 41 | reinterpret_cast(SetLogFilter), BML_CAP_LOGGING); 42 | 43 | // Guarded APIs with capabilities 44 | BML_REGISTER_API_GUARDED_WITH_CAPS(bmlLoggingGetCaps, "logging", BML_API_LoggingGetCaps, BML_CAP_LOGGING); 45 | BML_REGISTER_API_GUARDED_WITH_CAPS(bmlRegisterLogSinkOverride, "logging", BML_API_RegisterLogSinkOverride, BML_CAP_LOGGING); 46 | BML_REGISTER_API_GUARDED_WITH_CAPS(bmlClearLogSinkOverride, "logging", BML_API_ClearLogSinkOverride, BML_CAP_LOGGING); 47 | } 48 | } // namespace BML::Core 49 | -------------------------------------------------------------------------------- /include/BML/Guids/Narratives.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_NARRATIVES_H 2 | #define BML_GUIDS_NARRATIVES_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Narratives 8 | // 9 | 10 | // Building Blocks 11 | #define VT_NARRATIVES_GETCURRENTSCENE CKGUID(0xdc125f, 0x592b00a8) 12 | #define VT_NARRATIVES_LAUNCHSCENE CKGUID(0x188d6d43, 0x169613dd) 13 | #define VT_NARRATIVES_ADDOBJECTTOSCENE CKGUID(0x4f7d5221, 0x4c852520) 14 | #define VT_NARRATIVES_REMOVEOBJECTFROMSCENE CKGUID(0x683925f3, 0x6fdd3f16) 15 | #define VT_NARRATIVES_SAVEIC CKGUID(0x72671a24, 0x161a2347) 16 | #define VT_NARRATIVES_RESTOREIC CKGUID(0x766e4e44, 0x4fac6d52) 17 | #define VT_NARRATIVES_SAVESTATE CKGUID(0x200123d8, 0x65f802b6) 18 | #define VT_NARRATIVES_READSTATE CKGUID(0x3ab6273f, 0x578f7118) 19 | #define VT_NARRATIVES_ACTIVATEGLOBALSTATE CKGUID(0x373a615b, 0x769a6d53) 20 | #define VT_NARRATIVES_ISGLOBALSTATEACTIVE CKGUID(0x4e65bdd, 0x473c3681) 21 | #define VT_NARRATIVES_DEACTIVATEGLOBALSTATE CKGUID(0x520d14a5, 0x797502ae) 22 | #define VT_NARRATIVES_OBJECTCREATE CKGUID(0x271538e6, 0x2fae49ac) 23 | #define VT_NARRATIVES_OBJECTLOAD CKGUID(0x7bd977d7, 0x26396c0c) 24 | #define VT_NARRATIVES_OBJECTCOPY CKGUID(0x3f6b0ac7, 0x47d20f78) 25 | #define VT_NARRATIVES_OBJECTDELETE CKGUID(0x74120ded, 0x76524673) 26 | #define VT_NARRATIVES_OBJECTRENAME CKGUID(0x4cae18cc, 0x3520c5a) 27 | #define VT_NARRATIVES_DELETEDYNAMICOBJECTS CKGUID(0xcba3195, 0x53440e4c) 28 | #define VT_NARRATIVES_TEXTURELOAD CKGUID(0xe85ab1, 0x312a731c) 29 | #define VT_NARRATIVES_SOUNDLOAD CKGUID(0x2bc537c, 0x147c22e0) 30 | #define VT_NARRATIVES_ACTIVATEOBJECT CKGUID(0x5f003fba, 0x5e574f60) 31 | #define VT_NARRATIVES_ACTIVATESCRIPT CKGUID(0x4c7e7bc3, 0xb693155) 32 | #define VT_NARRATIVES_DEACTIVATEOBJECT CKGUID(0x160f4b7d, 0x67de224e) 33 | #define VT_NARRATIVES_DEACTIVATESCRIPT CKGUID(0x14367c05, 0x635b24f9) 34 | #define VT_NARRATIVES_EXECUTESCRIPT CKGUID(0x706c5a40, 0x5bb31a0b) 35 | #define VT_NARRATIVES_CALLSCRIPT CKGUID(0x1a7d5b17, 0x70630fed) 36 | #define VT_NARRATIVES_ATTACHSCRIPT CKGUID(0x1dba5f4a, 0x14481212) 37 | #define VT_NARRATIVES_DUMMY0 CKGUID(0xd0b7adf3, 0xd3ff3cf6) 38 | 39 | #endif // BML_GUIDS_NARRATIVES_H 40 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | matrix: 15 | configuration: [Release, Debug] 16 | 17 | env: 18 | PROJECT_NAME: BallanceModLoaderPlus 19 | BASE_BUILD_DIR: ${{ github.workspace }}/build 20 | BASE_DIST_DIR: ${{ github.workspace }}/dist 21 | 22 | steps: 23 | - name: Checkout source code 24 | uses: actions/checkout@v4 25 | with: 26 | submodules: recursive 27 | 28 | - name: Checkout Virtools SDK 29 | uses: actions/checkout@v4 30 | with: 31 | repository: doyaGu/Virtools-SDK-2.1 32 | path: ${{ github.workspace }}/Virtools-SDK-2.1 33 | 34 | - name: Get current branch and commit hash 35 | shell: bash 36 | run: | 37 | echo "GIT_BRANCH=$(echo ${GITHUB_REF#refs/heads/})" >> "$GITHUB_ENV" 38 | echo "GIT_SHA=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" 39 | 40 | - name: Setup CMake 41 | uses: lukka/get-cmake@latest 42 | 43 | - name: Setup MSVC 44 | uses: TheMrMilchmann/setup-msvc-dev@v3.0.0 45 | with: 46 | arch: x86 47 | 48 | - name: Configure CMake 49 | run: > 50 | cmake -A Win32 -B ${{ env.BASE_BUILD_DIR }}/${{ matrix.configuration }} 51 | -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} 52 | -DVIRTOOLS_SDK_PATH=${{ github.workspace }}/Virtools-SDK-2.1 53 | -DCMAKE_INSTALL_PREFIX=${{ env.BASE_DIST_DIR }}/${{ matrix.configuration }} 54 | 55 | - name: Build the project 56 | run: cmake --build ${{ env.BASE_BUILD_DIR }}/${{ matrix.configuration }} --config ${{ matrix.configuration }} 57 | 58 | - name: Install binaries to distribution folder 59 | run: > 60 | cmake --install ${{ env.BASE_BUILD_DIR }}/${{ matrix.configuration }} 61 | --prefix ${{ env.BASE_DIST_DIR }}/${{ matrix.configuration }} --config ${{ matrix.configuration }} 62 | 63 | - name: Upload build artifacts 64 | uses: actions/upload-artifact@v4.3.3 65 | with: 66 | name: ${{ env.PROJECT_NAME }}-${{ env.GIT_SHA }}-${{ matrix.configuration }} 67 | path: ${{ env.BASE_DIST_DIR }}/${{ matrix.configuration }} 68 | -------------------------------------------------------------------------------- /src/HookBlock.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////// 2 | ////////////////////////////////// 3 | // 4 | // Hook Block 5 | // 6 | ////////////////////////////////// 7 | ////////////////////////////////// 8 | #include "CKAll.h" 9 | 10 | typedef int (*CKBehaviorCallback)(const CKBehaviorContext *behcontext, void *arg); 11 | 12 | CKObjectDeclaration *FillBehaviorHookBlockDecl(); 13 | CKERROR CreateHookBlockProto(CKBehaviorPrototype **pproto); 14 | int HookBlock(const CKBehaviorContext &behcontext); 15 | 16 | CKObjectDeclaration *FillBehaviorHookBlockDecl() { 17 | CKObjectDeclaration *od = CreateCKObjectDeclaration("HookBlock"); 18 | od->SetDescription("Hook building blocks"); 19 | od->SetCategory("Hook"); 20 | od->SetType(CKDLL_BEHAVIORPROTOTYPE); 21 | od->SetGuid(CKGUID(0x19038c0, 0x663902da)); 22 | od->SetAuthorGuid(CKGUID(0x3a086b4d, 0x2f4a4f01)); 23 | od->SetAuthorName("Kakuty"); 24 | od->SetVersion(0x00010000); 25 | od->SetCreationFunction(CreateHookBlockProto); 26 | od->SetCompatibleClassId(CKCID_BEOBJECT); 27 | return od; 28 | } 29 | 30 | CKERROR CreateHookBlockProto(CKBehaviorPrototype **pproto) { 31 | CKBehaviorPrototype *proto = CreateCKBehaviorPrototype("HookBlock"); 32 | if (!proto) return CKERR_OUTOFMEMORY; 33 | 34 | proto->DeclareLocalParameter("Callback", CKPGUID_POINTER); 35 | proto->DeclareLocalParameter("Argument", CKPGUID_POINTER); 36 | 37 | proto->SetBehaviorFlags((CK_BEHAVIOR_FLAGS) (CKBEHAVIOR_VARIABLEINPUTS | CKBEHAVIOR_VARIABLEOUTPUTS)); 38 | proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL); 39 | proto->SetFunction(HookBlock); 40 | 41 | *pproto = proto; 42 | return CK_OK; 43 | } 44 | 45 | int HookBlock(const CKBehaviorContext &behcontext) { 46 | CKBehavior *beh = behcontext.Behavior; 47 | 48 | int i, count = beh->GetInputCount(); 49 | for (i = 0; i < count; ++i) { 50 | beh->ActivateInput(i, FALSE); 51 | } 52 | 53 | int ret = CKBR_OK; 54 | CKBehaviorCallback cb = nullptr; 55 | beh->GetLocalParameterValue(0, &cb); 56 | if (cb) { 57 | void *arg = nullptr; 58 | beh->GetLocalParameterValue(1, &arg); 59 | ret = cb(&behcontext, arg); 60 | } 61 | 62 | count = beh->GetOutputCount(); 63 | for (i = 0; i < count; ++i) { 64 | beh->ActivateOutput(i); 65 | } 66 | 67 | return ret; 68 | } -------------------------------------------------------------------------------- /include/BML/Guids/BuildingBlocksAddons1.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_BUILDINGBLOCKSADDONS1_H 2 | #define BML_GUIDS_BUILDINGBLOCKSADDONS1_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // BuildingBlocksAddons1 8 | // 9 | 10 | // Building Blocks 11 | #define VT_BUILDINGBLOCKSADDONS1_ANIMATIONRECORDER CKGUID(0x532d07, 0x1db631cc) 12 | #define VT_BUILDINGBLOCKSADDONS1_COPYBUFFERTOTEXTURE CKGUID(0xef6070d, 0x67166889) 13 | #define VT_BUILDINGBLOCKSADDONS1_SYNCHRORECORDER CKGUID(0x62ea3812, 0x1ed661a6) 14 | #define VT_BUILDINGBLOCKSADDONS1_SYNCHROPLAYER CKGUID(0x62da29d8, 0x1bbc6234) 15 | #define VT_BUILDINGBLOCKSADDONS1_LENSFLARE CKGUID(0x76926718, 0x7ee807d5) 16 | #define VT_BUILDINGBLOCKSADDONS1_REPLACERENDERING CKGUID(0x208f0030, 0x652f088e) 17 | #define VT_BUILDINGBLOCKSADDONS1_COMBINETEXTURE CKGUID(0x84be2329, 0xaed66ce1) 18 | #define VT_BUILDINGBLOCKSADDONS1_FILTERTEXTURE CKGUID(0x52df323c, 0x39dc1211) 19 | #define VT_BUILDINGBLOCKSADDONS1_WATERTEXTURE CKGUID(0x3df93ddc, 0x9c89c141) 20 | #define VT_BUILDINGBLOCKSADDONS1_BLENDTEXTURES CKGUID(0x53ee4292, 0x7d932bb1) 21 | #define VT_BUILDINGBLOCKSADDONS1_HARDWARELEVEL CKGUID(0x1b241485, 0x1f572aac) 22 | #define VT_BUILDINGBLOCKSADDONS1_VOLUMETRICFOG CKGUID(0xb0f1ca98, 0xe9dabe5f) 23 | #define VT_BUILDINGBLOCKSADDONS1_SHADOWSTENCIL CKGUID(0x60a873c5, 0x51245a67) 24 | #define VT_BUILDINGBLOCKSADDONS1_LIGHTMAP CKGUID(0x4a797d21, 0x214d37f9) 25 | #define VT_BUILDINGBLOCKSADDONS1_STATICLIGHTMAP CKGUID(0x4a797d21, 0x214d37fa) 26 | #define VT_BUILDINGBLOCKSADDONS1_GAUGE CKGUID(0x6a777fc4, 0x1dc17b23) 27 | #define VT_BUILDINGBLOCKSADDONS1_TIMESETTINGS CKGUID(0x60a0f8c, 0x9203b5f) 28 | #define VT_BUILDINGBLOCKSADDONS1_GETTEXTUREINFO CKGUID(0x250c4d8d, 0x288960d9) 29 | #define VT_BUILDINGBLOCKSADDONS1_MOVIELOAD CKGUID(0x798e26a9, 0x34e4169e) 30 | #define VT_BUILDINGBLOCKSADDONS1_PIXELVALUE CKGUID(0x473167b6, 0x48f75a1d) 31 | #define VT_BUILDINGBLOCKSADDONS1_OUTPUTTOCONSOLE CKGUID(0x18655b3f, 0x68291dc3) 32 | #define VT_BUILDINGBLOCKSADDONS1_SETLISTENERPROPERTIES CKGUID(0xbcfde546, 0xfeefd18b) 33 | #define VT_BUILDINGBLOCKSADDONS1_CREATEDECAL CKGUID(0x27b71ada, 0x22191538) 34 | #define VT_BUILDINGBLOCKSADDONS1_SETMATERIALEFFECT CKGUID(0x3bd73009, 0x7aa01dac) 35 | #define VT_BUILDINGBLOCKSADDONS1_READCONFIG CKGUID(0x4a888d94, 0x21ccccfa) 36 | #define VT_BUILDINGBLOCKSADDONS1_WRITECONFIG CKGUID(0x4a797d94, 0x21ab5cfa) 37 | 38 | #endif // BML_GUIDS_BUILDINGBLOCKSADDONS1_H 39 | -------------------------------------------------------------------------------- /cmake/FindVirtoolsSDK.cmake: -------------------------------------------------------------------------------- 1 | # Find the Virtools SDK header + import library 2 | # 3 | # VIRTOOLS_SDK_PATH - Path to Virtools SDK. 4 | # VIRTOOLS_SDK_INCLUDE_DIR - Where to find CKAll.h 5 | # VIRTOOLS_SDK_LIBRARIES - List of libraries when using VirtoolsSDK. 6 | # VirtoolsSDK_FOUND - True if Virtools SDK found. 7 | # VirtoolsSDK::CK2 - Imported library of CK2 8 | # VirtoolsSDK::VxMath - Imported library of VxMath 9 | 10 | find_path(VIRTOOLS_SDK_INCLUDE_DIR CKAll.h 11 | HINTS 12 | $ENV{VIRTOOLS_SDK_PATH} 13 | ${VIRTOOLS_SDK_PATH} 14 | PATH_SUFFIXES 15 | Include 16 | Includes 17 | DOC "Where the Virtools SDK headers can be found" 18 | ) 19 | 20 | set(VIRTOOLS_SDK_LIBRARY_NAMES CK2 VxMath) 21 | 22 | set(VIRTOOLS_SDK_LIBRARIES) 23 | foreach (LIBNAME IN LISTS VIRTOOLS_SDK_LIBRARY_NAMES) 24 | find_library(VIRTOOLS_SDK_${LIBNAME} NAMES ${LIBNAME} 25 | HINTS 26 | $ENV{VIRTOOLS_SDK_PATH} 27 | ${VIRTOOLS_SDK_PATH} 28 | PATH_SUFFIXES 29 | Lib 30 | DOC "Where the Virtools SDK libraries can be found" 31 | ) 32 | mark_as_advanced(VIRTOOLS_SDK_${LIBNAME}) 33 | list(APPEND VIRTOOLS_SDK_LIBRARIES ${VIRTOOLS_SDK_${LIBNAME}}) 34 | endforeach () 35 | 36 | mark_as_advanced(VIRTOOLS_SDK_INCLUDE_DIR) 37 | 38 | include(FindPackageHandleStandardArgs) 39 | find_package_handle_standard_args(VirtoolsSDK DEFAULT_MSG 40 | VIRTOOLS_SDK_LIBRARIES 41 | VIRTOOLS_SDK_INCLUDE_DIR 42 | ) 43 | 44 | if (VirtoolsSDK_FOUND) 45 | foreach (LIBNAME IN LISTS VIRTOOLS_SDK_LIBRARY_NAMES) 46 | if (NOT TARGET ${LIBNAME}) 47 | add_library(${LIBNAME} IMPORTED UNKNOWN) 48 | add_library(VirtoolsSDK::${LIBNAME} ALIAS ${LIBNAME}) 49 | set_target_properties(${LIBNAME} PROPERTIES 50 | IMPORTED_LOCATION "${VIRTOOLS_SDK_${LIBNAME}}" 51 | INTERFACE_INCLUDE_DIRECTORIES "${VIRTOOLS_SDK_INCLUDE_DIR}" 52 | IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" 53 | ) 54 | 55 | # Disable strict const-qualification conformance for pointers initialized by string literals 56 | target_compile_options(${LIBNAME} INTERFACE 57 | $<$:/Zc:strictStrings-> 58 | $<$:-fpermissive -Wno-write-strings> 59 | ) 60 | endif () 61 | endforeach () 62 | endif () -------------------------------------------------------------------------------- /include/BML/RefCount.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RefCount.h 3 | * @brief Intrusive reference counter with resurrection guard and clear memory model notes. 4 | */ 5 | #ifndef BML_REFCOUNT_H 6 | #define BML_REFCOUNT_H 7 | 8 | #include 9 | #include 10 | 11 | namespace BML { 12 | class RefCount { 13 | public: 14 | // Start at 1 by default for intrusive objects that are born owned; use 0 if you really pool-manage. 15 | explicit RefCount(uint32_t initial = 0) noexcept : m_RefCount(initial) {} 16 | ~RefCount() = default; 17 | 18 | RefCount(const RefCount &) = delete; 19 | RefCount &operator=(const RefCount &) = delete; 20 | 21 | // Reset for object pools; best used when count is 0. 22 | uint32_t Reset(uint32_t v = 0) noexcept { 23 | m_RefCount.store(v, std::memory_order_relaxed); 24 | return v; 25 | } 26 | 27 | // Fast path bump: relaxed is sufficient (doesn't publish object state). 28 | uint32_t AddRef() noexcept { 29 | return m_RefCount.fetch_add(1, std::memory_order_relaxed) + 1; 30 | } 31 | 32 | // Try to add only if object is still "live" (count > 0). Prevents resurrection. 33 | // Returns true and bumps the count on success. 34 | bool TryAddRef() noexcept { 35 | uint32_t cur = m_RefCount.load(std::memory_order_relaxed); 36 | while (cur != 0) { 37 | if (m_RefCount.compare_exchange_weak( 38 | cur, cur + 1, 39 | std::memory_order_relaxed, // success 40 | std::memory_order_relaxed)) // failure 41 | return true; 42 | // cur is reloaded; loop 43 | } 44 | return false; 45 | } 46 | 47 | // Returns the new (post-decrement) count. 48 | // Use pattern: 49 | // if (rc.Release() == 0) { std::atomic_thread_fence(std::memory_order_acquire); delete this; } 50 | uint32_t Release() noexcept { 51 | return m_RefCount.fetch_sub(1, std::memory_order_acq_rel) - 1; 52 | } 53 | 54 | uint32_t GetCount() const noexcept { 55 | // relaxed is fine for approximate diagnostics; not a liveness check 56 | return m_RefCount.load(std::memory_order_relaxed); 57 | } 58 | 59 | private: 60 | std::atomic m_RefCount; 61 | }; 62 | } // namespace BML 63 | 64 | #endif // BML_REFCOUNT_H 65 | -------------------------------------------------------------------------------- /src/CommandContext.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_COMMANDCONTEXT_H 2 | #define BML_COMMANDCONTEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "BML/ICommand.h" 10 | 11 | namespace BML { 12 | typedef void (*CommandOutputCallback)(const char *line, void *userdata); 13 | 14 | class CommandContext { 15 | public: 16 | CommandContext(); 17 | 18 | CommandContext(const CommandContext &rhs) = delete; 19 | CommandContext(CommandContext &&rhs) noexcept = delete; 20 | 21 | ~CommandContext(); 22 | 23 | CommandContext &operator=(const CommandContext &rhs) = delete; 24 | CommandContext &operator=(CommandContext &&rhs) noexcept = delete; 25 | 26 | bool RegisterCommand(ICommand *cmd); 27 | bool UnregisterCommand(const char *name); 28 | 29 | size_t GetCommandCount() const; 30 | ICommand *GetCommandByIndex(size_t index) const; 31 | ICommand *GetCommandByName(const char *name) const; 32 | 33 | void SortCommands(); 34 | void ClearCommands(); 35 | 36 | const char *GetVariable(const char *key) const; 37 | bool AddVariable(const char *key, const char *value); 38 | bool RemoveVariable(const char *key); 39 | 40 | bool SetOutputCallback(CommandOutputCallback callback, void *userdata); 41 | void ClearOutputCallback(); 42 | 43 | void Output(const char *message); 44 | void OutputV(const char *format, va_list args); 45 | void OutputF(const char *format, ...) { 46 | va_list args; 47 | va_start(args, format); 48 | OutputV(format, args); 49 | va_end(args); 50 | } 51 | 52 | static char *AllocPrintfV(const char *format, va_list args); 53 | static char *AllocPrintf(const char *format, ...); 54 | 55 | static std::vector ParseCommandLine(const char *cmd); 56 | 57 | private: 58 | static bool ValidateCommandName(const char *name); 59 | 60 | std::vector m_Commands; 61 | typedef std::unordered_map CommandMap; 62 | CommandMap m_CommandMap; 63 | typedef std::unordered_map VariableMap; 64 | VariableMap m_Variables; 65 | 66 | CommandOutputCallback m_OutputCallback = nullptr; 67 | void *m_OutputCallbackData = nullptr; 68 | }; 69 | } 70 | 71 | #endif // BML_COMMANDCONTEXT_H 72 | -------------------------------------------------------------------------------- /include/BML/Guids/MeshModifiers.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_MESHMODIFIERS_H 2 | #define BML_GUIDS_MESHMODIFIERS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // MeshModifiers 8 | // 9 | 10 | // Building Blocks 11 | #define VT_MESHMODIFIERS_SETVERTEXPROPERTIES CKGUID(0x696353b2, 0x40667087) 12 | #define VT_MESHMODIFIERS_SETFACEPROPERTIES CKGUID(0x1d8e2716, 0x64f47239) 13 | #define VT_MESHMODIFIERS_SETVERTEXCOUNT CKGUID(0x4bf4352e, 0x6c786db2) 14 | #define VT_MESHMODIFIERS_SETFACECOUNT CKGUID(0x15a14196, 0x69530840) 15 | #define VT_MESHMODIFIERS_PRECOMPUTELIGHTING CKGUID(0x53a5be9, 0x78cc3e15) 16 | #define VT_MESHMODIFIERS_BEND CKGUID(0x7c9a7008, 0x6b0f1d1f) 17 | #define VT_MESHMODIFIERS_EXPLODE CKGUID(0x2d010a, 0x3d010a) 18 | #define VT_MESHMODIFIERS_MESHMORPHER CKGUID(0xff0d010a, 0x1d010a) 19 | #define VT_MESHMODIFIERS_NOISE CKGUID(0x3918083f, 0x77d70f5c) 20 | #define VT_MESHMODIFIERS_SINEDEFORM CKGUID(0x45123704, 0x72f25769) 21 | #define VT_MESHMODIFIERS_MULTIMESHMORPHER CKGUID(0x53da65c4, 0x6f0f194a) 22 | #define VT_MESHMODIFIERS_SETPATCHMESHSTEPS CKGUID(0x50c12830, 0x18033a52) 23 | #define VT_MESHMODIFIERS_SKINJOIN CKGUID(0x38677c72, 0x4f4715cb) 24 | #define VT_MESHMODIFIERS_STRETCH CKGUID(0x22d774f6, 0x4ac542c3) 25 | #define VT_MESHMODIFIERS_TAPER CKGUID(0xe971a6b, 0x210f703a) 26 | #define VT_MESHMODIFIERS_TWIST CKGUID(0x75b066af, 0x70371b90) 27 | #define VT_MESHMODIFIERS_TEXTUREDISPLACE CKGUID(0x32145678, 0x32145678) 28 | #define VT_MESHMODIFIERS_CHANGEREFERENTIAL CKGUID(0x5f6428f2, 0x4e55c7a) 29 | #define VT_MESHMODIFIERS_VERTEXTRANSLATE CKGUID(0x8032ebc, 0x791960c9) 30 | #define VT_MESHMODIFIERS_INVERTWINDING CKGUID(0x31ce61cd, 0x69a44beb) 31 | #define VT_MESHMODIFIERS_ADDMESH CKGUID(0x7a1d593e, 0x12c5648d) 32 | #define VT_MESHMODIFIERS_REMOVEMESH CKGUID(0x2145e, 0x6e1b2592) 33 | #define VT_MESHMODIFIERS_SELECTMESH CKGUID(0x43890176, 0x58b2244e) 34 | #define VT_MESHMODIFIERS_LEVELOFDETAIL CKGUID(0x21465f42, 0x194c71a1) 35 | #define VT_MESHMODIFIERS_LODMANAGEROPTIONS CKGUID(0x2b557187, 0x2027baf) 36 | #define VT_MESHMODIFIERS_SETLODATTRIBUTE CKGUID(0xd360136, 0x63d0633b) 37 | #define VT_MESHMODIFIERS_GETLODATTRIBUTE CKGUID(0x62bf39fa, 0x53e264c6) 38 | #define VT_MESHMODIFIERS_SETPROGRESSIVEMESHOPTIONS CKGUID(0x10357830, 0x18099a52) 39 | 40 | // Parameter Types 41 | #define CKPGUID_COLORCHANNELTYPE CKGUID(0x39d40df7, 0x48433312) 42 | #define CKPGUID_LODMANAGEROPTIONS_SETTING CKGUID(0x3c5302cc, 0x2a611067) 43 | #define CKPGUID_SETLODATTRIBUTE_SETTING CKGUID(0x7e4417e1, 0x5d0d45b3) 44 | 45 | #endif // BML_GUIDS_MESHMODIFIERS_H 46 | -------------------------------------------------------------------------------- /src/Utils/HookUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_HOOKUTILS_H 2 | #define BML_HOOKUTILS_H 3 | 4 | #include 5 | 6 | namespace utils { 7 | void OutputDebugA(const char *format, ...); 8 | void OutputDebugW(const wchar_t *format, ...); 9 | 10 | template 11 | void *TypeErase(T target) { 12 | return *reinterpret_cast(&target); 13 | } 14 | 15 | template 16 | T ForceReinterpretCast(void *addr) { 17 | return *reinterpret_cast(&addr); 18 | } 19 | 20 | template 21 | T ForceReinterpretCast(void *base, size_t offset) { 22 | void *p = static_cast(base) + offset; 23 | return *reinterpret_cast(&p); 24 | } 25 | 26 | void *GetSelfModuleHandle(); 27 | 28 | void *GetModuleBaseAddress(void *hModule); 29 | void *GetModuleBaseAddress(const char *modulePath); 30 | 31 | uint32_t ProtectRegion(void *region, size_t size, uint32_t protection); 32 | uint32_t UnprotectRegion(void *region, size_t size); 33 | 34 | inline void **GetVTable(void *instance) { 35 | if (instance) { 36 | return *static_cast(instance); 37 | } else { 38 | return nullptr; 39 | } 40 | } 41 | 42 | template 43 | void LoadVTable(void *instance, T &table) { 44 | if (instance) { 45 | void **src = static_cast(*static_cast(instance)); 46 | void **dest = reinterpret_cast(&table); 47 | for (size_t i = 0; i < sizeof(T) / sizeof(void *); ++i) { 48 | dest[i] = src[i]; 49 | } 50 | } 51 | } 52 | 53 | template 54 | void SaveVTable(void *instance, T &table) { 55 | if (instance) { 56 | void **src = reinterpret_cast(&table); 57 | void **dest = static_cast(*static_cast(instance)); 58 | uint32_t originalProtection = UnprotectRegion(dest, sizeof(T)); 59 | for (size_t i = 0; i < sizeof(T) / sizeof(void *); ++i) { 60 | dest[i] = src[i]; 61 | } 62 | ProtectRegion(dest, sizeof(T), originalProtection); 63 | } 64 | } 65 | 66 | void *HookVirtualMethod(void *instance, void *hook, size_t offset); 67 | 68 | template 69 | void *HookVirtualMethod(void *instance, T hook, size_t offset) { 70 | return HookVirtualMethod(instance, TypeErase(hook), offset); 71 | } 72 | } 73 | 74 | #endif // BML_HOOKUTILS_H 75 | -------------------------------------------------------------------------------- /include/BML/DataShare.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_DATASHARE_H 2 | #define BML_DATASHARE_H 3 | 4 | #include "BML/Defines.h" 5 | 6 | BML_BEGIN_CDECLS 7 | 8 | typedef struct BML_DataShare BML_DataShare; 9 | 10 | typedef void (*BML_DataShareCallback)(const char *key, const void *data, size_t size, void *userdata); 11 | typedef void (*BML_DataShareCleanupCallback)(const char *key, void *userdata); 12 | 13 | BML_EXPORT BML_DataShare *BML_GetDataShare(const char *name); 14 | BML_EXPORT uint32_t BML_DataShare_AddRef(BML_DataShare *handle); 15 | BML_EXPORT uint32_t BML_DataShare_Release(BML_DataShare *handle); 16 | 17 | BML_EXPORT int BML_DataShare_Set(BML_DataShare *handle, const char *key, const void *data, size_t size); 18 | BML_EXPORT void BML_DataShare_Remove(BML_DataShare *handle, const char *key); 19 | 20 | // NOTE: The pointer returned by Get() is BORROWED and only valid until the next 21 | // Set()/Remove() for the same key or until the instance is destroyed. 22 | BML_EXPORT const void *BML_DataShare_Get(const BML_DataShare *handle, const char *key, size_t *outSize); 23 | 24 | // Copies if possible. Returns 1 on success, 0 if not present or invalid key. 25 | BML_EXPORT int BML_DataShare_Copy(const BML_DataShare *handle, const char *key, void *dst, size_t dstSize); 26 | 27 | // Single-call copy that reports required size: returns 1 on success, 0 if not present, 28 | // or -N (negative required size) if dst is too small. When non-null, outFullSize is 29 | // always set to the full size of the value (0 if key not present). 30 | BML_EXPORT int BML_DataShare_CopyEx(const BML_DataShare *handle, const char *key, void *dst, size_t dstSize, size_t *outFullSize); 31 | 32 | BML_EXPORT int BML_DataShare_Has(const BML_DataShare *handle, const char *key); 33 | BML_EXPORT size_t BML_DataShare_SizeOf(const BML_DataShare *handle, const char *key); 34 | 35 | // Enqueue one-shot waiter; (callback, userdata, cleanup) order is intentional. 36 | // If the key already exists, callback fires immediately; otherwise it fires once when set. 37 | // The callback is invoked out-of-lock. Cleanup is always invoked exactly once. 38 | BML_EXPORT void BML_DataShare_Request(BML_DataShare *handle, const char *key, 39 | BML_DataShareCallback callback, void *userdata, 40 | BML_DataShareCleanupCallback cleanup); 41 | 42 | // WARNING: Force-destroys all instances regardless of outstanding references. 43 | // All BML_DataShare* become invalid after this call. 44 | BML_EXPORT void BML_DataShare_DestroyAll(void); 45 | 46 | BML_END_CDECLS 47 | 48 | #endif // BML_DATASHARE_H 49 | -------------------------------------------------------------------------------- /include/BML/Guids/3DTransfo.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_3DTRANSFO_H 2 | #define BML_GUIDS_3DTRANSFO_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // 3DTransfo 8 | // 9 | 10 | // Building Blocks 11 | #define VT_3DTRANSFO_ADDCHILD CKGUID(0x45986587, 0x12654556) 12 | #define VT_3DTRANSFO_SCALE CKGUID(0x41236987, 0xa54a87a6) 13 | #define VT_3DTRANSFO_GETEULERORIENTATION CKGUID(0xc4977a8, 0x6c645d14) 14 | #define VT_3DTRANSFO_ROTATE CKGUID(0xffffffee, 0xeeffffff) 15 | #define VT_3DTRANSFO_ROTATEAROUND CKGUID(0xfdfd999e, 0xdefeded8) 16 | #define VT_3DTRANSFO_SETSCALE CKGUID(0x7a7a7a7a, 0x7a7a7a7a) 17 | #define VT_3DTRANSFO_SETEULERORIENTATION CKGUID(0xc4966d8, 0x6c0c6d14) 18 | #define VT_3DTRANSFO_SETLOCALMATRIX CKGUID(0x21f5f30d, 0x8d5a1db) 19 | #define VT_3DTRANSFO_SETORIENTATION CKGUID(0x625874aa, 0xaa694132) 20 | #define VT_3DTRANSFO_SETPARENT CKGUID(0x9d9d9d98, 0x7e7a7f75) 21 | #define VT_3DTRANSFO_SETPOSITION CKGUID(0xe456e78a, 0x456789aa) 22 | #define VT_3DTRANSFO_SETQUATERNIONORIENTATION CKGUID(0x5ae25172, 0x5c781faa) 23 | #define VT_3DTRANSFO_SETWORLDMATRIX CKGUID(0xaa4aa6f0, 0xddefdef4) 24 | #define VT_3DTRANSFO_TRANSLATE CKGUID(0xd000d, 0xd000d) 25 | #define VT_3DTRANSFO_BILLBOARD CKGUID(0x641822b0, 0x15aa42d7) 26 | #define VT_3DTRANSFO_KEEPATCONSTANTDISTANCE CKGUID(0xb7e6398a, 0x113c66dd) 27 | #define VT_3DTRANSFO_LOOKAT CKGUID(0x26843971, 0x86249317) 28 | #define VT_3DTRANSFO_LOOKATPOS CKGUID(0xee6e5e6e, 0xe6e6e6e6) 29 | #define VT_3DTRANSFO_MIMIC CKGUID(0x12db7b06, 0x74c514c3) 30 | #define VT_3DTRANSFO_MOVETO CKGUID(0x10d010b, 0x11d010b) 31 | #define VT_3DTRANSFO_ADDCONTROLPOINT CKGUID(0x1234eefa, 0xffaec112) 32 | #define VT_3DTRANSFO_GETCONTROLPOINTPROPERTIES CKGUID(0x1251effa, 0xaebec112) 33 | #define VT_3DTRANSFO_GETCURVEPROPERTIES CKGUID(0x1251adfa, 0xaebec002) 34 | #define VT_3DTRANSFO_CURVEFOLLOW CKGUID(0xed010b, 0xfd010b) 35 | #define VT_3DTRANSFO_REMOVECONTROLPOINT CKGUID(0x1234effa, 0xffbec112) 36 | #define VT_3DTRANSFO_SETCONTROLPOINTPROPERTIES CKGUID(0x1254effa, 0xfebec112) 37 | #define VT_3DTRANSFO_SETCURVEPROPERTIES CKGUID(0x1257effa, 0xf7bec112) 38 | #define VT_3DTRANSFO_POSITIONONCURVE CKGUID(0x676776d0, 0x20d457bd) 39 | #define VT_3DTRANSFO_PLAYANIMATION3DENTITY CKGUID(0x64221225, 0x769a143f) 40 | #define VT_3DTRANSFO_PLAYGLOBALANIMATION CKGUID(0x1c9236e1, 0x42f40996) 41 | #define VT_3DTRANSFO_SETANIMATIONSTEPENTITY CKGUID(0x14782b1d, 0x52692e21) 42 | #define VT_3DTRANSFO_SETGLOBALANIMATIONSTEP CKGUID(0x7ba27c0a, 0x7bd21db6) 43 | #define VT_3DTRANSFO_PORTALMANAGEMENT CKGUID(0x55041717, 0x30817370) 44 | #define VT_3DTRANSFO_SETPORTAL CKGUID(0x465c4732, 0x30996468) 45 | 46 | #endif // BML_GUIDS_3DTRANSFO_H 47 | -------------------------------------------------------------------------------- /include/BML/Guids/Collisions.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_COLLISIONS_H 2 | #define BML_GUIDS_COLLISIONS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Collisions 8 | // 9 | 10 | // Building Blocks 11 | #define VT_COLLISIONS_ADDOBSTACLE CKGUID(0x321a42c4, 0x6133d5) 12 | #define VT_COLLISIONS_DECLAREOBSTACLES CKGUID(0x21dc34e8, 0xd512cd89) 13 | #define VT_COLLISIONS_CHARACTERPREVENTFROMCOLLISION CKGUID(0x319971f0, 0x7919193f) 14 | #define VT_COLLISIONS_COLLISIONDETECTION CKGUID(0x56204f19, 0x682801e8) 15 | #define VT_COLLISIONS_MULTICOLLISIONDETECTION CKGUID(0x56204f09, 0x611101e8) 16 | #define VT_COLLISIONS_OBJECTSLIDER CKGUID(0x13603db0, 0x16bc321a) 17 | #define VT_COLLISIONS_PREVENTCOLLISION CKGUID(0x301971f0, 0x7919343f) 18 | #define VT_COLLISIONS_TESTBOUNDINGBOXYOVERLAPPING CKGUID(0x1ad010b, 0x1bd010b) 19 | #define VT_COLLISIONS_AVOIDOBSTACLES CKGUID(0x39460907, 0x30f329af) 20 | #define VT_COLLISIONS_HOMEONENTITY CKGUID(0x71234f96, 0x34321e45) 21 | #define VT_COLLISIONS_SPHERESPHEREINTERSECTION CKGUID(0x198531f9, 0x60f66c57) 22 | #define VT_COLLISIONS_BOXBOXINTERSECTION CKGUID(0x64154401, 0x76cf37af) 23 | #define VT_COLLISIONS_BOXFACEINTERSECTION CKGUID(0xf1c5480, 0x4a811cb) 24 | #define VT_COLLISIONS_FACEFACEINTERSECTION CKGUID(0x11426a61, 0x1a815584) 25 | #define VT_COLLISIONS_FRUSTUMOBJECTINTERSECTION CKGUID(0x4d6075e1, 0x2dda272e) 26 | #define VT_COLLISIONS_CHARACTERKEEPONFLOOR CKGUID(0xdbac2124, 0x6adcbfa4) 27 | #define VT_COLLISIONS_ENHANCEDCHARACTERKEEPONFLOOR CKGUID(0xdbde2424, 0x6addef12) 28 | #define VT_COLLISIONS_CHARACTERKEEPONFLOORLIMITS CKGUID(0x788061fb, 0x249f29a1) 29 | #define VT_COLLISIONS_OBJECTKEEPONFLOOR CKGUID(0x43617677, 0x7de15e12) 30 | #define VT_COLLISIONS_OBJECTKEEPONFLOORV2 CKGUID(0x3de853bd, 0x133d0d2e) 31 | #define VT_COLLISIONS_DECLAREFLOORS CKGUID(0x8e43cd12, 0x98dc215d) 32 | #define VT_COLLISIONS_FLOORMANAGERSETUP CKGUID(0x3a271561, 0x1d8c4b45) 33 | #define VT_COLLISIONS_GETNEARESTFLOORS CKGUID(0x6dad424e, 0x54b6003d) 34 | #define VT_COLLISIONS_ACTIVATELINK CKGUID(0x51df79d8, 0x294163a3) 35 | #define VT_COLLISIONS_ACTIVATENODE CKGUID(0x92a4e59, 0x4009769c) 36 | #define VT_COLLISIONS_CREATENODALPATH CKGUID(0x435b3d9c, 0x3a8e41f6) 37 | #define VT_COLLISIONS_ENTITYFINDNODALPATH CKGUID(0x170a5187, 0x42f10a3a) 38 | #define VT_COLLISIONS_FINDCURVEDNODALPATH CKGUID(0x72542b8a, 0x6fe901c7) 39 | #define VT_COLLISIONS_FINDPATH CKGUID(0x78c60de, 0x6c744614) 40 | #define VT_COLLISIONS_CHARACTERGOTONODE CKGUID(0x4c710f65, 0x32ed4ea6) 41 | #define VT_COLLISIONS_UPDATENODALPATH CKGUID(0x21647449, 0x51126c00) 42 | 43 | // Parameter Types 44 | #define CKPGUID_FRONTAL_DIRECTION CKGUID(0x286652d, 0x5ea709c2) 45 | 46 | #endif // BML_GUIDS_COLLISIONS_H 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(BallanceModLoaderPlus 4 | VERSION 0.3.10 5 | LANGUAGES CXX 6 | DESCRIPTION "Renovated Mod Loader for Ballance" 7 | HOMEPAGE_URL https://github.com/doyaGu/BallanceModLoaderPlus) 8 | 9 | set(CMAKE_CXX_STANDARD 20) 10 | set(CMAKE_CXX_STANDARD_REQUIRED YES) 11 | set(CMAKE_CXX_EXTENSIONS NO) 12 | 13 | # Add path for custom modules 14 | list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 15 | 16 | include(GenerateExportHeader) 17 | include(GNUInstallDirs) 18 | include(CMakePackageConfigHelpers) 19 | 20 | # Use folders to organize targets in an IDE 21 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 22 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets") 23 | 24 | if (NOT WIN32) 25 | message(FATAL_ERROR "Only support Windows.") 26 | endif () 27 | 28 | # Use relative paths 29 | if (WIN32) 30 | set(CMAKE_SUPPRESS_REGENERATION TRUE) 31 | endif () 32 | 33 | # Define build type if not specified 34 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 35 | message(STATUS "Setting build type to 'Release' as no build type was specified") 36 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the build type (Debug/Release)" FORCE) 37 | # Define standard build type options for GUI 38 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 39 | endif () 40 | 41 | # Set default install path 42 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 43 | set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/install" CACHE PATH "The root directory of the installation" FORCE) 44 | message(STATUS "Setting default install directory to ${CMAKE_INSTALL_PREFIX} as no install directory was specified") 45 | endif () 46 | 47 | # Disable msvc unsafe warnings globally 48 | add_compile_definitions( 49 | $<$:_CRT_SECURE_NO_WARNINGS> 50 | $<$:_CRT_NONSTDC_NO_WARNINGS> 51 | ) 52 | 53 | # Disable char8_t for MSVC to maintain compatibility with C++20 54 | add_compile_options( 55 | $<$:/Zc:char8_t-> 56 | ) 57 | 58 | option(BML_BUILD_TESTS "Build the BML test suite" OFF) 59 | option(BML_BUILD_BENCHMARKS "Build the BML benchmark suite" OFF) 60 | 61 | set(BML_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include" CACHE INTERNAL "") 62 | set(BML_SOURCE_DIR "${PROJECT_SOURCE_DIR}/src" CACHE INTERNAL "") 63 | 64 | find_package(VirtoolsSDK REQUIRED) 65 | 66 | add_subdirectory(deps) 67 | add_subdirectory(src) 68 | if (BML_BUILD_TESTS) 69 | include(CTest) 70 | enable_testing() 71 | add_subdirectory(tests) 72 | endif () 73 | if (BML_BUILD_BENCHMARKS) 74 | add_subdirectory(benchmarks) 75 | endif () -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef WIN32_LEAN_AND_MEAN 8 | #define WIN32_LEAN_AND_MEAN 9 | #endif 10 | #include 11 | 12 | #if defined(_MSC_VER) 13 | #define BML_SAFE_FPRINTF(stream, ...) ::fprintf_s(stream, __VA_ARGS__) 14 | #define BML_SAFE_VFPRINTF(stream, format, args) ::vfprintf_s(stream, format, args) 15 | #else 16 | #define BML_SAFE_FPRINTF(stream, ...) std::fprintf(stream, __VA_ARGS__) 17 | #define BML_SAFE_VFPRINTF(stream, format, args) std::vfprintf(stream, format, args) 18 | #endif 19 | 20 | #include "ModContext.h" 21 | 22 | static std::shared_mutex g_DefaultLoggerMutex; 23 | static std::mutex g_LogWriteMutex; 24 | 25 | Logger *Logger::m_DefaultLogger = nullptr; 26 | 27 | Logger *Logger::GetDefault() { 28 | std::shared_lock lock(g_DefaultLoggerMutex); 29 | return m_DefaultLogger; 30 | } 31 | 32 | void Logger::SetDefault(Logger *logger) { 33 | std::unique_lock lock(g_DefaultLoggerMutex); 34 | m_DefaultLogger = logger; 35 | } 36 | 37 | Logger::Logger(const char *modName) : m_ModName(modName) {} 38 | 39 | void Logger::Info(const char *fmt, ...) { 40 | va_list args; 41 | va_start(args, fmt); 42 | Log("INFO", fmt, args); 43 | va_end(args); 44 | } 45 | 46 | void Logger::Warn(const char *fmt, ...) { 47 | va_list args; 48 | va_start(args, fmt); 49 | Log("WARN", fmt, args); 50 | va_end(args); 51 | } 52 | 53 | void Logger::Error(const char *fmt, ...) { 54 | va_list args; 55 | va_start(args, fmt); 56 | Log("ERROR", fmt, args); 57 | va_end(args); 58 | } 59 | 60 | void Logger::Log(const char *level, const char *fmt, va_list args) { 61 | if (!level || !fmt) 62 | return; 63 | 64 | SYSTEMTIME sys; 65 | GetLocalTime(&sys); 66 | 67 | FILE *outFiles[] = { 68 | #ifdef _DEBUG 69 | stdout, 70 | #endif 71 | BML_GetModContext()->GetLogFile() 72 | }; 73 | 74 | std::lock_guard lock(g_LogWriteMutex); 75 | for (FILE *file : outFiles) { 76 | if (!file) 77 | continue; 78 | 79 | BML_SAFE_FPRINTF(file, 80 | "[%02d/%02d/%d %02d:%02d:%02d.%03d] ", 81 | sys.wMonth, 82 | sys.wDay, 83 | sys.wYear, 84 | sys.wHour, 85 | sys.wMinute, 86 | sys.wSecond, 87 | sys.wMilliseconds); 88 | BML_SAFE_FPRINTF(file, "[%s/%s]: ", m_ModName, level); 89 | 90 | va_list argsCopy; 91 | va_copy(argsCopy, args); 92 | BML_SAFE_VFPRINTF(file, fmt, argsCopy); 93 | va_end(argsCopy); 94 | 95 | fputc('\n', file); 96 | fflush(file); 97 | } 98 | } 99 | 100 | #undef BML_SAFE_FPRINTF 101 | #undef BML_SAFE_VFPRINTF -------------------------------------------------------------------------------- /include/BML/DataBox.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file DataBox.h 3 | * @brief Thread-safe storage for arbitrary user data slots keyed by a size_t "type". 4 | */ 5 | #ifndef BML_DATABOX_H 6 | #define BML_DATABOX_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace BML { 13 | /** 14 | * @brief A container for storing user data pointers keyed by a caller-provided "type". 15 | * 16 | * Notes: 17 | * - Uses an unordered_map under a single mutex: O(1) avg lookups. 18 | * - Never casts pointers to integers (avoids 32/64-bit truncation pitfalls). :contentReference[oaicite:2]{index=2} 19 | * - SetData returns the previous pointer (or nullptr if none). 20 | * - No ownership semantics: callers manage the lifetime of stored pointers. 21 | */ 22 | class DataBox { 23 | public: 24 | DataBox() = default; 25 | DataBox(const DataBox &) = delete; 26 | DataBox &operator=(const DataBox &) = delete; 27 | 28 | /** 29 | * @brief Get the pointer stored for @p type, or nullptr if absent. 30 | */ 31 | void *GetData(std::size_t type) const noexcept { 32 | std::lock_guard g(m_RWLock); 33 | const auto it = m_UserData.find(type); 34 | return (it == m_UserData.end()) ? nullptr : it->second; 35 | } 36 | 37 | /** 38 | * @brief Set the pointer for @p type. Returns the previous pointer (or nullptr). 39 | */ 40 | void *SetData(void *data, std::size_t type) noexcept { 41 | std::lock_guard g(m_RWLock); 42 | auto &slot = m_UserData[type]; 43 | void *prev = slot; 44 | slot = data; 45 | return prev; 46 | } 47 | 48 | /** 49 | * @brief Remove an entry; returns the removed pointer (or nullptr if none). 50 | */ 51 | void *RemoveData(std::size_t type) noexcept { 52 | std::lock_guard g(m_RWLock); 53 | const auto it = m_UserData.find(type); 54 | if (it == m_UserData.end()) return nullptr; 55 | void *prev = it->second; 56 | m_UserData.erase(it); 57 | return prev; 58 | } 59 | 60 | /// Clear all entries (does not free what pointers point to). 61 | void Clear() noexcept { 62 | std::lock_guard g(m_RWLock); 63 | m_UserData.clear(); 64 | } 65 | 66 | /// Number of stored entries. 67 | std::size_t Size() const noexcept { 68 | std::lock_guard g(m_RWLock); 69 | return m_UserData.size(); 70 | } 71 | 72 | private: 73 | mutable std::mutex m_RWLock; 74 | std::unordered_map m_UserData; 75 | }; 76 | } // namespace BML 77 | 78 | #endif // BML_DATABOX_H 79 | -------------------------------------------------------------------------------- /include/BML/Guids/Interface.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_INTERFACE_H 2 | #define BML_GUIDS_INTERFACE_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Interface 8 | // 9 | 10 | // Building Blocks 11 | #define VT_INTERFACE_ALIGN2D CKGUID(0x3385bc3, 0x1034102f) 12 | #define VT_INTERFACE_ALIGN3D CKGUID(0xf384bc3, 0x1b34102f) 13 | #define VT_INTERFACE_2DCURVELAYOUT CKGUID(0x2956342c, 0x95246746) 14 | #define VT_INTERFACE_3DCURVELAYOUT CKGUID(0x2557342c, 0x15246746) 15 | #define VT_INTERFACE_2DFLOWLAYOUT CKGUID(0x295f342c, 0x95e467e6) 16 | #define VT_INTERFACE_3DFLOWLAYOUT CKGUID(0x245fb72c, 0xa5e437e6) 17 | #define VT_INTERFACE_2DGRIDLAYOUT CKGUID(0x265f342c, 0x95e466e6) 18 | #define VT_INTERFACE_3DGRIDLAYOUT CKGUID(0x665f342c, 0x9e1366e6) 19 | #define VT_INTERFACE_DRAGANDDROP CKGUID(0x39c45869, 0x3dcf3fc1) 20 | #define VT_INTERFACE_PUSHBUTTON CKGUID(0x761b7e64, 0x2b4c6911) 21 | #define VT_INTERFACE_2DTEXT CKGUID(0x55b29fe, 0x662d5ca0) 22 | #define VT_INTERFACE_3DTEXT CKGUID(0x1f5b2fe7, 0x6d355175) 23 | #define VT_INTERFACE_SETCARETPOSITION CKGUID(0xf760d6e, 0x7ce7589a) 24 | #define VT_INTERFACE_BITMAPTEXTDISPLAY CKGUID(0x49d487b, 0x57565345) 25 | #define VT_INTERFACE_TEXTDISPLAY CKGUID(0xf22d010a, 0xf2cd010a) 26 | #define VT_INTERFACE_2DPICKING CKGUID(0x1616120f, 0x5b2f48ce) 27 | #define VT_INTERFACE_ADDITIONALVIEW CKGUID(0xcd159b, 0x164d010b) 28 | #define VT_INTERFACE_MOUSECURSOR CKGUID(0x140c3eac, 0x6e502a81) 29 | #define VT_INTERFACE_RESIZEVIEW CKGUID(0x40e1fe8, 0x79ed7c9b) 30 | #define VT_INTERFACE_SETPICKABLE CKGUID(0xf1499052, 0xebe9bbf1) 31 | #define VT_INTERFACE_GETPROPORTIONALSCREENPOS CKGUID(0x671e53a2, 0x5ba16d62) 32 | #define VT_INTERFACE_GETSCREENPROPORTIONALPOS CKGUID(0x7bdf0404, 0xf765834) 33 | #define VT_INTERFACE_CREATEFONT CKGUID(0x7d644088, 0x40dd0349) 34 | #define VT_INTERFACE_DELETEFONT CKGUID(0x153342c, 0xe543674f) 35 | #define VT_INTERFACE_SETFONTPROPERTIES CKGUID(0xdacfbd61, 0x7a6e65e7) 36 | #define VT_INTERFACE_CREATESYSTEMFONT CKGUID(0x936334fc, 0xf243684f) 37 | #define VT_INTERFACE_MOUSECURSORSYSTEM CKGUID(0x2483576d, 0x57c66324) 38 | #define VT_INTERFACE_DRAWRECTANGLE CKGUID(0x4095766f, 0x291161c1) 39 | 40 | // Parameter Types 41 | #define CKPGUID_ALIGNMENT CKGUID(0x2e1e2209, 0x47da44b5) 42 | #define CKPGUID_FONTPROPERTIES CKGUID(0x63223dd5, 0x6b5f68fc) 43 | #define CKPGUID_TEXTPROPERTIES CKGUID(0x4157001d, 0x4cc82922) 44 | 45 | #define CKPGUID_FONTWEIGHT CKGUID(0x4376013f, 0xb3462c0) 46 | #define CKPGUID_FONTRESOLUTION CKGUID(0x7157091d, 0x4fc82932) 47 | #define CKPGUID_FONTNAME CKGUID(0x7167091a, 0x7f482632) 48 | #define CKPGUID_FONT CKGUID(0x64fb5811, 0x33862d3b) 49 | 50 | #define CKPGUID_MOUSEPOINTERS CKGUID(0x37a05bdd, 0x1ed83f40) 51 | 52 | #define CKPGUID_FLOW_DIRECTION CKGUID(0x3e8b5e93, 0x28bc23b2) 53 | #define CKPGUID_FLOW_ALIGNMENT CKGUID(0x5e5b5493, 0xf8bc224f) 54 | #define CKPGUID_FLOW_SUPPORT CKGUID(0x8e5b5493, 0x98bd2245) 55 | 56 | #define CKPGUID_PUSHBUTTONOPTION CKGUID(0x25064124, 0x1fbe6aae) 57 | 58 | #endif // BML_GUIDS_INTERFACE_H 59 | -------------------------------------------------------------------------------- /src/Gui/Text.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/Text.h" 2 | 3 | #include "ModContext.h" 4 | 5 | using namespace BGui; 6 | 7 | const char *g_TextFont = nullptr; 8 | const char *g_AvailFonts[] = {"Microsoft YaHei UI", "Microsoft YaHei"}; 9 | 10 | Text::Text(const char *name) : Element(name) { 11 | CKContext *context = BML_GetCKContext(); 12 | m_Sprite = (CKSpriteText *)context->CreateObject(CKCID_SPRITETEXT, (CKSTRING) name); 13 | m_Sprite->ModifyObjectFlags(CK_OBJECT_NOTTOBELISTEDANDSAVED, 0); 14 | context->GetCurrentLevel()->AddObject(m_Sprite); 15 | m_Sprite->SetHomogeneousCoordinates(); 16 | m_Sprite->EnableClipToCamera(false); 17 | m_Sprite->EnableRatioOffset(false); 18 | m_Sprite->SetZOrder(20); 19 | m_Sprite->SetTextColor(0xffffffff); 20 | m_Sprite->SetAlign(CKSPRITETEXT_ALIGNMENT(CKSPRITETEXT_VCENTER | CKSPRITETEXT_LEFT)); 21 | m_Sprite->SetFont((CKSTRING) g_TextFont, context->GetPlayerRenderContext()->GetHeight() / 85, 400); 22 | } 23 | 24 | Text::~Text() { 25 | CKContext *context = BML_GetCKContext(); 26 | if (context) 27 | context->DestroyObject(CKOBJID(m_Sprite)); 28 | } 29 | 30 | void Text::UpdateFont() { 31 | CKContext *context = BML_GetCKContext(); 32 | m_Sprite->SetFont((CKSTRING) g_TextFont, context->GetPlayerRenderContext()->GetHeight() / 85, 400); 33 | } 34 | 35 | Vx2DVector Text::GetPosition() { 36 | Vx2DVector res; 37 | m_Sprite->GetPosition(res, true); 38 | return res; 39 | } 40 | 41 | void Text::SetPosition(Vx2DVector pos) { 42 | m_Sprite->SetPosition(pos, true); 43 | } 44 | 45 | Vx2DVector Text::GetSize() { 46 | Vx2DVector res; 47 | m_Sprite->GetSize(res, true); 48 | return res; 49 | } 50 | 51 | void Text::SetSize(Vx2DVector size) { 52 | m_Sprite->ReleaseAllSlots(); 53 | auto *rc = BML_GetRenderContext(); 54 | m_Sprite->Create((int)(rc->GetWidth() * size.x), (int)(rc->GetHeight() * size.y), 32); 55 | m_Sprite->SetSize(size, true); 56 | } 57 | 58 | int Text::GetZOrder() { 59 | return m_Sprite->GetZOrder(); 60 | } 61 | 62 | void Text::SetZOrder(int z) { 63 | m_Sprite->SetZOrder(z); 64 | } 65 | 66 | bool Text::IsVisible() { 67 | return m_Sprite->IsVisible(); 68 | } 69 | 70 | void Text::SetVisible(bool visible) { 71 | m_Sprite->Show(visible ? CKSHOW : CKHIDE); 72 | } 73 | 74 | const char *Text::GetText() { 75 | return m_Sprite->GetText(); 76 | } 77 | 78 | void Text::SetText(const char *text) { 79 | m_Sprite->SetText((CKSTRING) text); 80 | } 81 | 82 | void Text::SetFont(const char *FontName, int FontSize, int Weight, CKBOOL italic, CKBOOL underline) { 83 | m_Sprite->SetFont((CKSTRING) FontName, FontSize, Weight, italic, underline); 84 | } 85 | 86 | void Text::SetAlignment(CKSPRITETEXT_ALIGNMENT align) { 87 | m_Sprite->SetAlign(align); 88 | } 89 | 90 | CKDWORD Text::GetTextColor() { 91 | return m_Sprite->GetTextColor(); 92 | } 93 | 94 | void Text::SetTextColor(CKDWORD color) { 95 | m_Sprite->SetTextColor(color); 96 | } -------------------------------------------------------------------------------- /src/Core/ModuleRuntime.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MODULE_RUNTIME_H 2 | #define BML_CORE_MODULE_RUNTIME_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "HotReloadCoordinator.h" 11 | #include "ModuleDiscovery.h" 12 | #include "ModuleLoader.h" 13 | 14 | namespace BML::Core { 15 | struct ModuleBootstrapDiagnostics { 16 | std::vector manifest_errors; 17 | DependencyResolutionError dependency_error; 18 | std::vector dependency_warnings; 19 | ModuleLoadError load_error; 20 | std::vector load_order; 21 | }; 22 | 23 | class ModuleRuntime { 24 | public: 25 | ModuleRuntime() = default; 26 | ~ModuleRuntime(); // Defined in .cpp where HotReloadCoordinator is complete 27 | ModuleRuntime(const ModuleRuntime &) = delete; 28 | ModuleRuntime &operator=(const ModuleRuntime &) = delete; 29 | 30 | bool Initialize(const std::wstring &mods_dir, ModuleBootstrapDiagnostics &out_diag); 31 | 32 | // Phase 1: Discover and validate modules without loading DLLs 33 | bool DiscoverAndValidate(const std::wstring &mods_dir, ModuleBootstrapDiagnostics &out_diag); 34 | 35 | // Phase 2: Load previously discovered modules 36 | bool LoadDiscovered(ModuleBootstrapDiagnostics &out_diag); 37 | 38 | bool ReloadModules(ModuleBootstrapDiagnostics &out_diag); 39 | 40 | void Shutdown(); 41 | 42 | void SetDiagnosticsCallback(std::function callback); 43 | 44 | private: 45 | void RecordLoadOrder(const std::vector &order, ModuleBootstrapDiagnostics &diag) const; 46 | bool ReloadModulesInternal(ModuleBootstrapDiagnostics &out_diag); 47 | void BroadcastLifecycleEvent(const char *topic, const std::vector &modules) const; 48 | void UpdateHotReloadRegistration(); 49 | void EnsureHotReloadCoordinator(); 50 | void StopHotReloadCoordinator(); 51 | void HandleHotReloadNotify(const std::string &mod_id, ReloadResult result, 52 | unsigned int version, ReloadFailure failure); 53 | bool ShouldEnableHotReload() const; 54 | std::wstring GetHotReloadTempDirectory() const; 55 | void ApplyDiagnostics(const ModuleBootstrapDiagnostics &diag) const; 56 | 57 | // State for two-phase loading 58 | std::wstring m_DiscoveredModsDir; 59 | std::vector m_DiscoveredOrder; 60 | 61 | bool m_HotReloadEnabled{false}; 62 | std::unique_ptr m_HotReloadCoordinator; 63 | std::function m_DiagCallback; 64 | mutable std::mutex m_ReloadMutex; 65 | bool m_ReloadInProgress{false}; 66 | }; 67 | } // namespace BML::Core 68 | 69 | #endif // BML_CORE_MODULE_RUNTIME_H 70 | -------------------------------------------------------------------------------- /include/bml.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bml.hpp 3 | * @brief BML v2 C++ Unified Header 4 | * 5 | * Provides RAII-friendly, exception-safe, and idiomatic C++ interface 6 | * over the C API. Use this in C++ code for better ergonomics. 7 | * 8 | * This header includes all BML C++ wrapper components: 9 | * - bml_context.hpp - Context wrapper and API loading 10 | * - bml_config.hpp - Configuration access 11 | * - bml_imc.hpp - Inter-Mod Communication 12 | * - bml_extension.hpp - Extension management 13 | * - bml_logger.hpp - Logging utilities 14 | * - bml_capability.hpp - Capability queries and API discovery 15 | * - bml_result.hpp - Result type for error handling 16 | * - bml_core.hpp - Core utilities (version, mod, shutdown hooks) 17 | * - bml_sync.hpp - Synchronization primitives (mutex, rwlock, etc.) 18 | * - bml_memory.hpp - Memory allocation utilities 19 | * - bml_resource.hpp - Resource handle management 20 | * - bml_profiling.hpp - Profiling and tracing utilities 21 | * - bml_hot_reload.hpp - Hot reload lifecycle events 22 | * 23 | * Usage: 24 | * #include 25 | * 26 | * // Initialize (typically in BMLPlus or host) 27 | * if (!bml::LoadAPI(get_proc_fn)) { 28 | * // Handle error 29 | * } 30 | * 31 | * // Use wrapper classes 32 | * bml::Context ctx = bml::GetGlobalContext(); 33 | * bml::Config config(mod); 34 | * config.SetString("mymod", "key", "value"); 35 | * 36 | * auto value = config.GetString("mymod", "key").value_or("default"); 37 | * 38 | * For selective inclusion, include individual headers instead: 39 | * #include 40 | * #include 41 | */ 42 | 43 | #ifndef BML_HPP 44 | #define BML_HPP 45 | 46 | // Core components 47 | #include "bml_context.hpp" // Context, ScopedContext, LoadAPI, UnloadAPI, IsApiLoaded 48 | #include "bml_core.hpp" // Version utilities, Mod, ShutdownHook 49 | #include "bml_config.hpp" // Config 50 | #include "bml_logger.hpp" // Logger, LogLevel 51 | 52 | // Communication 53 | #include "bml_imc.hpp" // Imc, Imc::Subscription, ImcCallback 54 | 55 | // Extensions 56 | #include "bml_extension.hpp" // Extension 57 | 58 | // Capabilities and Discovery 59 | #include "bml_capability.hpp" // QueryCapabilities, HasCapability, CheckCompatibility, ApiDescriptor 60 | 61 | // Error Handling 62 | #include "bml_result.hpp" // Result, Ok, Err, BML_TRY, BML_TRY_ASSIGN 63 | 64 | // Synchronization 65 | #include "bml_sync.hpp" // Mutex, RwLock, Semaphore, CondVar, SpinLock, ThreadLocal 66 | 67 | // Memory 68 | #include "bml_memory.hpp" // Alloc, Free, MemoryPool, UniquePtr 69 | 70 | // Resources 71 | #include "bml_resource.hpp" // Handle, SharedHandle 72 | 73 | // Profiling 74 | #include "bml_profiling.hpp" // ScopedTrace, Timer, trace functions 75 | 76 | // Hot Reload 77 | #include "bml_hot_reload.hpp" // ModLifecycleEvent, ModLifecycleEventBuilder 78 | 79 | #endif /* BML_HPP */ 80 | -------------------------------------------------------------------------------- /src/ModManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ModManager.h" 2 | 3 | #include "ModContext.h" 4 | 5 | #include "BML/InputHook.h" 6 | #include "Overlay.h" 7 | 8 | ModManager::ModManager(CKContext *context) : CKBaseManager(context, MOD_MANAGER_GUID, (CKSTRING) "Mod Manager") { 9 | m_ModContext = new ModContext(m_Context); 10 | context->RegisterNewManager(this); 11 | } 12 | 13 | ModManager::~ModManager() { 14 | delete m_ModContext; 15 | } 16 | 17 | CKERROR ModManager::OnCKInit() { 18 | m_ModContext->Init(); 19 | return CK_OK; 20 | } 21 | 22 | CKERROR ModManager::OnCKEnd() { 23 | m_ModContext->Shutdown(); 24 | return CK_OK; 25 | } 26 | 27 | CKERROR ModManager::OnCKPlay() { 28 | if (m_Context->IsReseted() && m_Context->GetCurrentLevel() != nullptr && !m_RenderContext) { 29 | m_RenderContext = m_Context->GetPlayerRenderContext(); 30 | 31 | Overlay::ImGuiInitRenderer(m_Context); 32 | Overlay::ImGuiContextScope scope; 33 | 34 | m_ModContext->LoadMods(); 35 | m_ModContext->InitMods(); 36 | 37 | Overlay::ImGuiNewFrame(); 38 | } 39 | 40 | return CK_OK; 41 | } 42 | 43 | CKERROR ModManager::OnCKReset() { 44 | if (m_Context->GetCurrentLevel() != nullptr && m_RenderContext) { 45 | Overlay::ImGuiContextScope scope; 46 | Overlay::ImGuiEndFrame(); 47 | 48 | m_ModContext->ShutdownMods(); 49 | m_ModContext->UnloadMods(); 50 | 51 | Overlay::ImGuiShutdownRenderer(m_Context); 52 | 53 | m_RenderContext = nullptr; 54 | } 55 | 56 | return CK_OK; 57 | } 58 | 59 | CKERROR ModManager::PreProcess() { 60 | Overlay::ImGuiContextScope scope; 61 | 62 | Overlay::ImGuiNewFrame(); 63 | 64 | return CK_OK; 65 | } 66 | 67 | extern void PhysicsPostProcess(); 68 | 69 | CKERROR ModManager::PostProcess() { 70 | Overlay::ImGuiContextScope scope; 71 | 72 | PhysicsPostProcess(); 73 | 74 | m_ModContext->OnProcess(); 75 | 76 | auto *inputHook = m_ModContext->GetInputManager(); 77 | ImGuiIO &io = ImGui::GetIO(); 78 | 79 | static bool cursorVisibilityChanged = false; 80 | if (io.WantCaptureMouse) { 81 | if (!inputHook->GetCursorVisibility()) { 82 | inputHook->ShowCursor(TRUE); 83 | cursorVisibilityChanged = true; 84 | } 85 | } else { 86 | if (cursorVisibilityChanged) { 87 | if (inputHook->GetCursorVisibility()) { 88 | inputHook->ShowCursor(FALSE); 89 | cursorVisibilityChanged = false; 90 | } 91 | } 92 | } 93 | 94 | Overlay::ImGuiRender(); 95 | inputHook->Process(); 96 | return CK_OK; 97 | } 98 | 99 | CKERROR ModManager::OnPostRender(CKRenderContext *dev) { 100 | m_ModContext->OnRender(dev); 101 | 102 | return CK_OK; 103 | } 104 | 105 | CKERROR ModManager::OnPostSpriteRender(CKRenderContext *dev) { 106 | Overlay::ImGuiOnRender(); 107 | 108 | return CK_OK; 109 | } 110 | -------------------------------------------------------------------------------- /src/ModMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_MODMENU_H 2 | #define BML_MODMENU_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "BML/Bui.h" 9 | 10 | #include "Config.h" 11 | 12 | class ModMenu; 13 | 14 | class ModMenu : public Bui::Menu { 15 | public: 16 | void Init(); 17 | 18 | IMod *GetCurrentMod() const { return m_CurrentMod; } 19 | void SetCurrentMod(IMod *mod) { m_CurrentMod = mod; } 20 | 21 | Category *GetCurrentCategory() const { return m_CurrentCategory; } 22 | void SetCurrentCategory(Category *category) { m_CurrentCategory = category; } 23 | 24 | void OnOpen() override; 25 | void OnClose() override; 26 | 27 | static Config *GetConfig(IMod *mod); 28 | 29 | private: 30 | IMod *m_CurrentMod = nullptr; 31 | Category *m_CurrentCategory = nullptr; 32 | }; 33 | 34 | class ModListPage : public Bui::Page { 35 | public: 36 | explicit ModListPage() : Bui::Page("Mod List") {} 37 | 38 | void OnPostBegin() override; 39 | void OnDraw() override; 40 | }; 41 | 42 | class ModPage : public Bui::Page { 43 | public: 44 | explicit ModPage() : Bui::Page("Mod Page") {} 45 | 46 | void OnPostBegin() override; 47 | void OnDraw() override; 48 | 49 | protected: 50 | static void ShowCommentBox(Category *category); 51 | 52 | Config *m_Config = nullptr; 53 | char m_TextBuf[1024] = {}; 54 | }; 55 | 56 | class ModOptionPage : public Bui::Page { 57 | public: 58 | explicit ModOptionPage() : Bui::Page("Mod Options") {} 59 | 60 | void OnPostBegin() override; 61 | void OnDraw() override; 62 | void OnPreEnd() override; 63 | bool OnOpen() override; 64 | void OnClose() override; 65 | void OnPageChanged(int newPage, int oldPage) override; 66 | 67 | protected: 68 | struct PropertyState { 69 | Property *property = nullptr; 70 | IProperty::PropertyType type = IProperty::INTEGER; 71 | std::string stringValue; 72 | std::string originalString; 73 | bool boolValue = false; 74 | bool originalBool = false; 75 | int intValue = 0; 76 | int originalInt = 0; 77 | float floatValue = 0.0f; 78 | float originalFloat = 0.0f; 79 | ImGuiKeyChord keyChord = 0; 80 | ImGuiKeyChord originalKeyChord = 0; 81 | bool dirty = false; 82 | }; 83 | 84 | void BindPageStates(int pageIndex); 85 | void SaveChanges(); 86 | void RevertChanges(); 87 | bool HasPendingChanges() const; 88 | void UpdateStateDirty(PropertyState *state); 89 | 90 | static void ShowCommentBox(const Property *property); 91 | 92 | Category *m_Category = nullptr; 93 | bool m_HasPendingChanges = false; 94 | 95 | std::unordered_map m_PropertyStates; 96 | std::array m_CurrentStates = {}; 97 | 98 | static constexpr size_t BUFFER_SIZE = 4096; 99 | char m_StringBuffers[4][BUFFER_SIZE] = {}; 100 | bool m_KeyToggled[4] = {}; 101 | }; 102 | 103 | #endif // BML_MODMENU_H 104 | -------------------------------------------------------------------------------- /src/MapMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_MAPMENU_H 2 | #define BML_MAPMENU_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "BML/Bui.h" 9 | 10 | class BMLMod; 11 | class MapMenu; 12 | 13 | enum MapEntryType { 14 | MAP_ENTRY_DIR, 15 | MAP_ENTRY_FILE, 16 | }; 17 | 18 | struct MapEntry { 19 | MapEntry *parent = nullptr; 20 | MapEntryType type; 21 | std::string name; 22 | std::wstring path; 23 | std::vector children; 24 | bool m_BeingDeleted = false; 25 | 26 | explicit MapEntry(MapEntry *parent, MapEntryType entryType) : parent(parent), type(entryType) {} 27 | 28 | ~MapEntry(); 29 | 30 | bool operator<(const MapEntry &rhs) const; 31 | 32 | bool operator>(const MapEntry &rhs) const { 33 | return rhs < *this; 34 | } 35 | 36 | bool operator<=(const MapEntry &rhs) const { 37 | return !(rhs < *this); 38 | } 39 | 40 | bool operator>=(const MapEntry &rhs) const { 41 | return !(*this < rhs); 42 | } 43 | }; 44 | 45 | class MapListPage : public Bui::Page { 46 | public: 47 | MapListPage(): Page("Custom Maps") {} 48 | 49 | void OnPostBegin() override; 50 | void OnPreEnd() override; 51 | void OnDraw() override; 52 | void OnPostEnd() override; 53 | 54 | private: 55 | bool IsSearching() const; 56 | void ClearSearch(); 57 | void OnSearchMaps(); 58 | // Draw by direct entry pointer (used for both normal list and recursive search results) 59 | bool OnDrawEntry(MapEntry *entry, bool *v); 60 | 61 | bool m_ShouldClose = false; 62 | int m_Count = 0; 63 | char m_MapSearchBuf[1024] = {}; 64 | // Store pointers to entries to support recursive results across subfolders 65 | std::vector m_MapSearchResult; 66 | }; 67 | 68 | class MapMenu : public Bui::Menu { 69 | public: 70 | explicit MapMenu(BMLMod *mod); 71 | 72 | ~MapMenu() override; 73 | 74 | void Init(); 75 | 76 | void OnOpen() override; 77 | void OnClose() override; 78 | void OnClose(bool backToMenu); 79 | 80 | void LoadMap(const std::wstring &path); 81 | MapEntry *GetMaps() const { return m_Maps; } 82 | MapEntry *GetCurrentMaps() const { return m_Current; } 83 | void SetCurrentMaps(MapEntry *entry) { m_Current = entry; } 84 | void ResetCurrentMaps() { m_Current = m_Maps; } 85 | void RefreshMaps(); 86 | 87 | bool ShouldShowTooltip() const { return m_ShowTooltip; } 88 | void SetShowTooltip(bool show) { m_ShowTooltip = show; } 89 | 90 | int GetMaxDepth() const { return m_MaxDepth; } 91 | void SetMaxDepth(int depth) { m_MaxDepth = depth; } 92 | 93 | private: 94 | bool ExploreMaps(MapEntry *maps, int depth = 8); 95 | static bool IsSupportedFileType(const std::wstring &path); 96 | 97 | BMLMod *m_Mod; 98 | bool m_MapLoaded = false; 99 | bool m_ShowTooltip = false; 100 | int m_MaxDepth = 8; 101 | MapEntry *m_Maps = nullptr; 102 | MapEntry *m_Current = nullptr; 103 | }; 104 | 105 | #endif // BML_MAPMENU_H 106 | -------------------------------------------------------------------------------- /src/Gui/Input.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/Input.h" 2 | 3 | #include "BML/ScriptHelper.h" 4 | #include "ModContext.h" 5 | 6 | using namespace BGui; 7 | 8 | CKMaterial *g_Caret = nullptr; 9 | 10 | Input::Input(const char *name) : Label(name) { 11 | m_2dEntity->UseSourceRect(); 12 | ScriptHelper::SetParamObject(m_Text2d->GetInputParameter(8)->GetRealSource(), ::g_Caret); 13 | ScriptHelper::SetParamString(m_Text2d->GetInputParameter(1)->GetRealSource(), "\b"); 14 | } 15 | 16 | void Input::InvokeCallback(CKDWORD key) { 17 | m_Callback(key); 18 | } 19 | 20 | void Input::SetCallback(std::function callback) { 21 | m_Callback = std::move(callback); 22 | } 23 | 24 | void Input::OnCharTyped(CKDWORD key) { 25 | bool changed = false; 26 | 27 | switch (key) { 28 | case CKKEY_BACK: 29 | if (m_Caret > 0) { 30 | m_Text.erase(--m_Caret); 31 | changed = true; 32 | } 33 | break; 34 | case CKKEY_DELETE: 35 | if (m_Caret < m_Text.size()) { 36 | m_Text.erase(m_Caret); 37 | changed = true; 38 | } 39 | break; 40 | case CKKEY_LEFT: 41 | if (m_Caret > 0) { 42 | m_Caret--; 43 | changed = true; 44 | } 45 | break; 46 | case CKKEY_RIGHT: 47 | if (m_Caret < m_Text.size()) { 48 | m_Caret++; 49 | changed = true; 50 | } 51 | break; 52 | case CKKEY_HOME: 53 | if (m_Caret > 0) { 54 | m_Caret = 0; 55 | changed = true; 56 | } 57 | break; 58 | case CKKEY_END: 59 | if (m_Caret < m_Text.size()) { 60 | m_Caret = m_Text.size(); 61 | changed = true; 62 | } 63 | break; 64 | case CKKEY_ESCAPE: 65 | case CKKEY_TAB: 66 | case CKKEY_RETURN: 67 | case CKKEY_UP: 68 | case CKKEY_DOWN: 69 | InvokeCallback(key); 70 | break; 71 | default: 72 | char c = VxScanCodeToAscii(key, BML_GetModContext()->GetInputManager()->GetKeyboardState()); 73 | if (c) { 74 | m_Text.insert(m_Caret++, 1, c); 75 | changed = true; 76 | } 77 | } 78 | 79 | if (changed) { 80 | InvokeCallback(key); 81 | std::string str = m_Text; 82 | str.insert(m_Caret, 1, '\b'); 83 | ScriptHelper::SetParamString(m_Text2d->GetInputParameter(1)->GetRealSource(), str.c_str()); 84 | } 85 | } 86 | 87 | const char *Input::GetText() { 88 | return m_Text.c_str(); 89 | } 90 | 91 | void Input::SetText(const char *text) { 92 | m_Text = text; 93 | m_Caret = m_Text.size(); 94 | ScriptHelper::SetParamString(m_Text2d->GetInputParameter(1)->GetRealSource(), (m_Text + '\b').c_str()); 95 | } 96 | 97 | void Input::GetFocus() { 98 | SetTextFlags(GetTextFlags() | TEXT_SHOWCARET); 99 | } 100 | 101 | void Input::LoseFocus() { 102 | SetTextFlags(GetTextFlags() & ~TEXT_SHOWCARET); 103 | } 104 | -------------------------------------------------------------------------------- /src/Utils/HookUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "HookUtils.h" 2 | 3 | #ifndef WIN32_LEAN_AND_MEAN 4 | #define WIN32_LEAN_AND_MEAN 5 | #endif 6 | #include 7 | #include 8 | #include 9 | 10 | namespace utils { 11 | void OutputDebugA(const char *format, ...) { 12 | char buf[4096] = {0}; 13 | 14 | va_list args; 15 | va_start(args, format); 16 | ::StringCchVPrintfA(buf, ARRAYSIZE(buf) - 1, format, args); 17 | va_end(args); 18 | 19 | buf[ARRAYSIZE(buf) - 1] = '\0'; 20 | ::OutputDebugStringA(buf); 21 | } 22 | 23 | void OutputDebugW(const wchar_t *format, ...) { 24 | wchar_t buf[4096] = {0}; 25 | 26 | va_list args; 27 | va_start(args, format); 28 | ::StringCchVPrintfW(buf, ARRAYSIZE(buf) - 1, format, args); 29 | va_end(args); 30 | 31 | buf[ARRAYSIZE(buf) - 1] = L'\0'; 32 | ::OutputDebugStringW(buf); 33 | } 34 | 35 | void *GetSelfModuleHandle() { 36 | MEMORY_BASIC_INFORMATION mbi; 37 | return ((::VirtualQuery((LPVOID) &GetSelfModuleHandle, &mbi, sizeof(mbi)) != 0) 38 | ? (HMODULE) mbi.AllocationBase : nullptr); 39 | } 40 | 41 | void *GetModuleBaseAddress(void *hModule) { 42 | if (!hModule) 43 | return nullptr; 44 | 45 | MODULEINFO moduleInfo; 46 | ::GetModuleInformation(::GetCurrentProcess(), (HMODULE)hModule, &moduleInfo, sizeof(moduleInfo)); 47 | 48 | return moduleInfo.lpBaseOfDll; 49 | } 50 | 51 | void *GetModuleBaseAddress(const char *modulePath) { 52 | if (!modulePath) 53 | return nullptr; 54 | 55 | int size = ::MultiByteToWideChar(CP_UTF8, 0, modulePath, -1, nullptr, 0); 56 | if (size == 0) 57 | return nullptr; 58 | 59 | auto ws = new wchar_t[size]; 60 | ::MultiByteToWideChar(CP_UTF8, 0, modulePath, -1, ws, size); 61 | 62 | HMODULE hModule = ::GetModuleHandleW(ws); 63 | delete[] ws; 64 | if (!hModule) 65 | return nullptr; 66 | 67 | MODULEINFO moduleInfo; 68 | ::GetModuleInformation(::GetCurrentProcess(), hModule, &moduleInfo, sizeof(moduleInfo)); 69 | 70 | return moduleInfo.lpBaseOfDll; 71 | } 72 | 73 | uint32_t ProtectRegion(void *region, size_t size, uint32_t protection) { 74 | DWORD oldProtect; 75 | VirtualProtect(region, size, protection, &oldProtect); 76 | return oldProtect; 77 | } 78 | 79 | uint32_t UnprotectRegion(void *region, size_t size) { 80 | DWORD oldProtect; 81 | VirtualProtect(region, size, PAGE_EXECUTE_READWRITE, &oldProtect); 82 | return oldProtect; 83 | } 84 | 85 | void *HookVirtualMethod(void *instance, void *hook, size_t offset) { 86 | uintptr_t vtable = *((uintptr_t *) instance); 87 | uintptr_t entry = vtable + offset * sizeof(uintptr_t); 88 | uintptr_t original = *((uintptr_t *) entry); 89 | 90 | uint32_t originalProtection = UnprotectRegion((void *) entry, sizeof(void *)); 91 | *((uintptr_t *) entry) = (uintptr_t) hook; 92 | ProtectRegion((void *) entry, sizeof(void *), originalProtection); 93 | 94 | return (void *) original; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /include/BML/Guids/Visuals.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_VISUALS_H 2 | #define BML_GUIDS_VISUALS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Visuals 8 | // 9 | 10 | // Building Blocks 11 | #define VT_VISUALS_CHANGESPRITESLOT CKGUID(0x70f8028c, 0x723215e7) 12 | #define VT_VISUALS_DISPLAYPROGRESSIONBAR CKGUID(0x4cf37cd, 0x7a246b85) 13 | #define VT_VISUALS_EDIT2DENTITY CKGUID(0x7fc54c91, 0x3f486670) 14 | #define VT_VISUALS_SETCOLORKEY2D CKGUID(0x7bda540c, 0x39782384) 15 | #define VT_VISUALS_SETRECTANGLES CKGUID(0x3bcf7052, 0x59f40417) 16 | #define VT_VISUALS_SET2DPARENT CKGUID(0x14c70f07, 0x4f2a100a) 17 | #define VT_VISUALS_SET2DPOSITION CKGUID(0x32d30604, 0x23b163fc) 18 | #define VT_VISUALS_SET2DMATERIAL CKGUID(0x3f7e5bff, 0x2326a71) 19 | #define VT_VISUALS_BLINK CKGUID(0xf00d010a, 0xfa0d010a) 20 | #define VT_VISUALS_MAKETRANSPARENT CKGUID(0x16cc600c, 0x702836d1) 21 | #define VT_VISUALS_VERTEXRENDER CKGUID(0xcc030377, 0x897aaaaa) 22 | #define VT_VISUALS_MOTIONBLUR CKGUID(0x6d0b04b, 0x1d4ea106) 23 | #define VT_VISUALS_GLOBALBLUR CKGUID(0x37f1163d, 0x35ad3e6b) 24 | #define VT_VISUALS_PLANARFILTER CKGUID(0xcd320b, 0x32ed010b) 25 | #define VT_VISUALS_SETZBUFFER CKGUID(0x1d2d378d, 0x4a4f36b6) 26 | #define VT_VISUALS_SETRENDERORDER CKGUID(0x6f8b6782, 0x21554283) 27 | #define VT_VISUALS_SIMPLESHADOW CKGUID(0x79b95ed0, 0x3e4a01f5) 28 | #define VT_VISUALS_SOLIDTRAIL CKGUID(0xd00d010a, 0xe00d010a) 29 | #define VT_VISUALS_TEXTURERENDER CKGUID(0x67fc7084, 0x141154f7) 30 | #define VT_VISUALS_USEZINFORMATION CKGUID(0x1f60adc, 0x39b02cb8) 31 | #define VT_VISUALS_PLANARREFLECTION CKGUID(0x44fe1b3c, 0x29ba3445) 32 | #define VT_VISUALS_PLANARSHADOW CKGUID(0x731072fa, 0x56cf087f) 33 | #define VT_VISUALS_SHADOWCASTER CKGUID(0x718360b, 0x71e5002f) 34 | #define VT_VISUALS_RENDERCURVE CKGUID(0x215c7794, 0x7ac40f5d) 35 | #define VT_VISUALS_MARKSYSTEM CKGUID(0x9a06075, 0x760a4720) 36 | #define VT_VISUALS_HIDE CKGUID(0x31d97d82, 0x78d54d98) 37 | #define VT_VISUALS_HIDEHIERARCHY CKGUID(0x27447f6b, 0x22aa0c59) 38 | #define VT_VISUALS_HIDE2DENTITY CKGUID(0x13579753, 0x13579753) 39 | #define VT_VISUALS_SHOW CKGUID(0xa85a213a, 0xef78d52a) 40 | #define VT_VISUALS_SHOW2DENTITY CKGUID(0xababa123, 0x123ababa) 41 | #define VT_VISUALS_SHOWOBJECTINFORMATION CKGUID(0x17cb4c57, 0xf525fb) 42 | #define VT_VISUALS_SHOWMOUSECURSOR CKGUID(0x16f6368f, 0x506b60fc) 43 | #define VT_VISUALS_SET3DSPRITEMODE CKGUID(0xc5d0457, 0xc64c7000) 44 | #define VT_VISUALS_SPRITEMOVIEPLAYER CKGUID(0x4c1d16ac, 0x1877604a) 45 | #define VT_VISUALS_SPRITEMULTIANGLE CKGUID(0x1ef926bd, 0x1ad167f) 46 | #define VT_VISUALS_DISPLAYSCORE CKGUID(0xfc45543, 0x55b012c3) 47 | #define VT_VISUALS_FPS CKGUID(0xa58a313a, 0xe7f8d32a) 48 | #define VT_VISUALS_STATISTICS CKGUID(0x5fb70201, 0x65595af3) 49 | #define VT_VISUALS_SETRENDEROPTIONS CKGUID(0x4d586c55, 0x5250236a) 50 | 51 | // Parameter Types 52 | #define CKPGUID_TEXTALIGN CKGUID(0x11223faf, 0x1a9315f9) 53 | #define CKPGUID_CURVEMODE CKGUID(0x4d236301, 0x5be530bc) 54 | #define CKPGUID_SPRITEEDITION CKGUID(0x11cb48a5, 0x47e5424b) 55 | #define CKPGUID_GENERALSTAT CKGUID(0xfe82e7c, 0x98173f) 56 | #define CKPGUID_BEHAVIORSTAT CKGUID(0x3b542289, 0x2f627abb) 57 | #define CKPGUID_RENDERSTAT CKGUID(0x54a578a6, 0x53f144d4) 58 | #define CKPGUID_REFLECTED CKGUID(0x7e3745c9, 0x79a84e4a) 59 | 60 | #endif // BML_GUIDS_VISUALS_H 61 | -------------------------------------------------------------------------------- /src/DataShare.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file DataShare.hpp 3 | * @brief Thread-safe implementation of the DataShare backend with intrusive refcount. 4 | * 5 | * Guarantees / design: 6 | * - All shared state is guarded by a single mutex. 7 | * - User callbacks are never invoked while holding internal locks. 8 | * - Copy() fails (returns false) on truncation; use SizeOf()/CopyEx() for robust copies. 9 | */ 10 | #ifndef BML_DATASHARE_HPP 11 | #define BML_DATASHARE_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "BML/DataShare.h" 19 | #include "BML/RefCount.h" 20 | 21 | namespace BML { 22 | 23 | using BML_DataShareCallback = void (*)(const char*, const void*, size_t, void*); 24 | using BML_DataShareCleanupCallback = void (*)(const char*, void*); 25 | 26 | class RefCount; 27 | 28 | class DataShare { 29 | public: 30 | struct Callback { 31 | BML_DataShareCallback fn = nullptr; 32 | BML_DataShareCleanupCallback cleanup = nullptr; 33 | void* userdata = nullptr; 34 | Callback() = default; 35 | Callback(BML_DataShareCallback f, BML_DataShareCleanupCallback cl, void* ud) : fn(f), cleanup(cl), userdata(ud) {} 36 | }; 37 | 38 | static constexpr size_t kMaxKeyLen = 255; 39 | 40 | explicit DataShare(std::string name); 41 | ~DataShare(); 42 | 43 | static bool ValidateKey(const char *key) noexcept; 44 | 45 | uint32_t AddRef() const; 46 | uint32_t Release() const; 47 | 48 | // Data plane 49 | bool Set(const char *key, const void *data, size_t size); 50 | void Remove(const char *key); 51 | const void *Get(const char *key, size_t *outSize) const; 52 | bool Copy(const char *key, void *dst, size_t dstSize) const; 53 | // New: single-call copy with required-size reporting (-N on too small) 54 | int CopyEx(const char *key, void *dst, size_t dstSize, size_t *outFullSize) const; 55 | bool Has(const char *key) const; 56 | size_t SizeOf(const char *key) const; 57 | 58 | // One-shot waiter: if key exists now, fires immediately; else enqueues 59 | void Request(const char *key, BML_DataShareCallback cb, BML_DataShareCleanupCallback cleanup, void *userdata); 60 | 61 | static DataShare *GetInstance(const char *name); 62 | static void DestroyAllInstances(); 63 | 64 | private: 65 | // Contract: must be called while holding m_Mutex 66 | void AddCallbackLocked(const char *key, BML_DataShareCallback cb, BML_DataShareCleanupCallback cleanup, void *ud) const; 67 | void TriggerCallbacksUnlocked(const char *key, const void *data, size_t size) const; 68 | void CancelPendingCallbacks() const noexcept; 69 | 70 | mutable std::mutex m_Mutex; 71 | std::unordered_map> m_Data; 72 | mutable std::unordered_map> m_Cbs; 73 | 74 | mutable RefCount m_Ref; 75 | const std::string m_Name; 76 | 77 | static std::mutex s_RegistryMutex; 78 | static std::unordered_map s_Registry; 79 | }; 80 | 81 | } // namespace BML 82 | 83 | #endif // BML_DATASHARE_HPP 84 | -------------------------------------------------------------------------------- /include/BML/InputHook.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_INPUTHOOK_H 2 | #define BML_INPUTHOOK_H 3 | 4 | #include "CKInputManager.h" 5 | 6 | #include "BML/Defines.h" 7 | 8 | typedef enum CK_INPUT_DEVICE { 9 | CK_INPUT_DEVICE_KEYBOARD = 0, 10 | CK_INPUT_DEVICE_MOUSE = 1, 11 | CK_INPUT_DEVICE_JOYSTICK = 2, 12 | CK_INPUT_DEVICE_COUNT = 3, 13 | } CK_INPUT_DEVICE; 14 | 15 | class BML_EXPORT InputHook { 16 | public: 17 | explicit InputHook(CKInputManager *input); 18 | ~InputHook(); 19 | 20 | void EnableKeyboardRepetition(CKBOOL iEnable = TRUE); 21 | CKBOOL IsKeyboardRepetitionEnabled(); 22 | 23 | CKBOOL IsKeyDown(CKDWORD iKey, CKDWORD *oStamp = nullptr); 24 | CKBOOL IsKeyUp(CKDWORD iKey); 25 | CKBOOL IsKeyToggled(CKDWORD iKey, CKDWORD *oStamp = nullptr); 26 | CKBOOL IsKeyPressed(CKDWORD iKey); 27 | CKBOOL IsKeyReleased(CKDWORD iKey); 28 | 29 | void GetKeyName(CKDWORD iKey, CKSTRING oKeyName); 30 | CKDWORD GetKeyFromName(CKSTRING iKeyName); 31 | unsigned char *GetKeyboardState(); 32 | CKBOOL IsKeyboardAttached(); 33 | int GetNumberOfKeyInBuffer(); 34 | int GetKeyFromBuffer(int i, CKDWORD &oKey, CKDWORD *oTimeStamp = nullptr); 35 | 36 | CKBOOL IsMouseButtonDown(CK_MOUSEBUTTON iButton); 37 | CKBOOL IsMouseClicked(CK_MOUSEBUTTON iButton); 38 | CKBOOL IsMouseToggled(CK_MOUSEBUTTON iButton); 39 | void GetMouseButtonsState(CKBYTE oStates[4]); 40 | void GetMousePosition(Vx2DVector &oPosition, CKBOOL iAbsolute = TRUE); 41 | void GetMouseRelativePosition(VxVector &oPosition); 42 | void GetLastMousePosition(Vx2DVector &position); 43 | CKBOOL IsMouseAttached(); 44 | 45 | CKBOOL IsJoystickAttached(int iJoystick); 46 | void GetJoystickPosition(int iJoystick, VxVector *oPosition); 47 | void GetJoystickRotation(int iJoystick, VxVector *oRotation); 48 | void GetJoystickSliders(int iJoystick, Vx2DVector *oPosition); 49 | void GetJoystickPointOfViewAngle(int iJoystick, float *oAngle); 50 | CKDWORD GetJoystickButtonsState(int iJoystick); 51 | CKBOOL IsJoystickButtonDown(int iJoystick, int iButton); 52 | 53 | void Pause(CKBOOL pause); 54 | 55 | void ShowCursor(CKBOOL iShow); 56 | CKBOOL GetCursorVisibility(); 57 | VXCURSOR_POINTER GetSystemCursor(); 58 | void SetSystemCursor(VXCURSOR_POINTER cursor); 59 | 60 | CKBOOL oIsKeyDown(CKDWORD iKey, CKDWORD *oStamp = nullptr); 61 | CKBOOL oIsKeyUp(CKDWORD iKey); 62 | CKBOOL oIsKeyToggled(CKDWORD iKey, CKDWORD *oStamp = nullptr); 63 | CKBOOL oIsKeyPressed(CKDWORD iKey); 64 | CKBOOL oIsKeyReleased(CKDWORD iKey); 65 | 66 | unsigned char *oGetKeyboardState(); 67 | int oGetNumberOfKeyInBuffer(); 68 | int oGetKeyFromBuffer(int i, CKDWORD &oKey, CKDWORD *oTimeStamp = nullptr); 69 | 70 | CKBOOL oIsMouseButtonDown(CK_MOUSEBUTTON iButton); 71 | CKBOOL oIsMouseClicked(CK_MOUSEBUTTON iButton); 72 | CKBOOL oIsMouseToggled(CK_MOUSEBUTTON iButton); 73 | void oGetMouseButtonsState(CKBYTE oStates[4]); 74 | 75 | bool IsBlock(); 76 | void SetBlock(bool block); 77 | 78 | int IsBlocked(CK_INPUT_DEVICE device); 79 | void Block(CK_INPUT_DEVICE device); 80 | void Unblock(CK_INPUT_DEVICE device); 81 | 82 | void Process(); 83 | 84 | private: 85 | struct Impl; 86 | Impl *m_Impl; 87 | }; 88 | 89 | #endif // BML_INPUTHOOK_H -------------------------------------------------------------------------------- /tests/HotReloadSampleMod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define BML_LOADER_IMPLEMENTATION 8 | #include "bml_loader.h" 9 | 10 | #include "bml_logging.h" 11 | 12 | namespace { 13 | 14 | std::filesystem::path GetLogPath() { 15 | DWORD required = GetEnvironmentVariableW(L"BML_TEST_HOT_RELOAD_LOG", nullptr, 0); 16 | if (required == 0) 17 | return {}; 18 | 19 | std::wstring buffer; 20 | buffer.resize(required); 21 | DWORD copied = GetEnvironmentVariableW(L"BML_TEST_HOT_RELOAD_LOG", buffer.data(), required); 22 | if (copied == 0) 23 | return {}; 24 | if (copied < buffer.size()) 25 | buffer.resize(copied); 26 | if (!buffer.empty() && buffer.back() == L'\0') 27 | buffer.pop_back(); 28 | if (buffer.empty()) 29 | return {}; 30 | return std::filesystem::path(buffer); 31 | } 32 | 33 | int CountOccurrences(const std::filesystem::path &path, const std::string &prefix) { 34 | std::ifstream file(path); 35 | if (!file.is_open()) 36 | return 0; 37 | 38 | std::string line; 39 | int count = 0; 40 | while (std::getline(file, line)) { 41 | if (line.rfind(prefix, 0) == 0) 42 | ++count; 43 | } 44 | return count; 45 | } 46 | 47 | void AppendLine(const std::filesystem::path &path, const std::string &line) { 48 | std::ofstream file(path, std::ios::app); 49 | if (!file.is_open()) 50 | return; 51 | file << line << '\n'; 52 | } 53 | 54 | void LogLifecycleEvent(const std::filesystem::path &path, const char *prefix) { 55 | if (path.empty()) 56 | return; 57 | const int next = CountOccurrences(path, prefix) + 1; 58 | AppendLine(path, std::string(prefix) + std::to_string(next)); 59 | } 60 | 61 | struct SampleState { 62 | BML_Mod mod{nullptr}; 63 | std::filesystem::path log_path; 64 | } g_State; 65 | 66 | BML_Result HandleAttach(const BML_ModAttachArgs *args) { 67 | if (!args || args->struct_size < sizeof(BML_ModAttachArgs)) 68 | return BML_RESULT_INVALID_ARGUMENT; 69 | 70 | g_State.mod = args->mod; 71 | g_State.log_path = GetLogPath(); 72 | 73 | BML_Result res = bmlLoadAPI(args->get_proc); 74 | if (res != BML_RESULT_OK) 75 | return res; 76 | 77 | LogLifecycleEvent(g_State.log_path, "init:"); 78 | if (bmlLog) { 79 | bmlLog(nullptr, BML_LOG_INFO, "HotReloadSample", "Sample mod initialized"); 80 | } 81 | return BML_RESULT_OK; 82 | } 83 | 84 | BML_Result HandleDetach(const BML_ModDetachArgs *args) { 85 | if (!args || args->struct_size < sizeof(BML_ModDetachArgs)) 86 | return BML_RESULT_INVALID_ARGUMENT; 87 | 88 | LogLifecycleEvent(g_State.log_path, "shutdown:"); 89 | bmlUnloadAPI(); 90 | g_State = {}; 91 | return BML_RESULT_OK; 92 | } 93 | 94 | } // namespace 95 | 96 | extern "C" __declspec(dllexport) BML_Result BML_ModEntrypoint(BML_ModEntrypointCommand command, void *payload) { 97 | switch (command) { 98 | case BML_MOD_ENTRYPOINT_ATTACH: 99 | return HandleAttach(static_cast(payload)); 100 | case BML_MOD_ENTRYPOINT_DETACH: 101 | return HandleDetach(static_cast(payload)); 102 | default: 103 | return BML_RESULT_INVALID_ARGUMENT; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Core sources 2 | set(BML_CORE_SOURCES 3 | DllMain.cpp 4 | ApiRegistry.cpp 5 | CoreApi.cpp 6 | TracingApi.cpp 7 | CapabilityApi.cpp 8 | Context.cpp 9 | CoreErrors.cpp 10 | ConfigApi.cpp 11 | ConfigStore.cpp 12 | DependencyResolver.cpp 13 | DiagnosticManager.cpp 14 | DiagnosticApi.cpp 15 | Export.cpp 16 | ExtensionApi.cpp 17 | ResourceApi.cpp 18 | MemoryManager.cpp 19 | MemoryApi.cpp 20 | SyncManager.cpp 21 | SyncApi.cpp 22 | ProfilingManager.cpp 23 | ProfilingApi.cpp 24 | ModManifest.cpp 25 | ModuleDiscovery.cpp 26 | ModuleLoader.cpp 27 | ModuleRuntime.cpp 28 | FileSystemWatcher.cpp 29 | ReloadableModuleSlot.cpp 30 | HotReloadCoordinator.cpp 31 | Microkernel.cpp 32 | ImcBus.cpp 33 | ImcApi.cpp 34 | LoggingApi.cpp 35 | Logging.cpp 36 | SemanticVersion.cpp 37 | ) 38 | 39 | set(BML_CORE_HEADERS 40 | ApiRegistry.h 41 | ApiRegistration.h 42 | Context.h 43 | CoreErrors.h 44 | ConfigStore.h 45 | DependencyResolver.h 46 | DiagnosticManager.h 47 | ResourceApi.h 48 | FixedBlockPool.h 49 | ImcBus.h 50 | Logging.h 51 | MemoryManager.h 52 | SyncManager.h 53 | ProfilingManager.h 54 | Microkernel.h 55 | ModHandle.h 56 | ModManifest.h 57 | ModuleDiscovery.h 58 | ModuleLoader.h 59 | ModuleRuntime.h 60 | MpscRingBuffer.h 61 | SpscRingBuffer.h 62 | SemanticVersion.h 63 | ) 64 | 65 | # Main BML Core DLL 66 | add_library(BML SHARED 67 | ${BML_CORE_SOURCES} 68 | ${BML_CORE_HEADERS} 69 | ) 70 | 71 | target_include_directories(BML 72 | PUBLIC 73 | $ 74 | $ 75 | $ 76 | ) 77 | 78 | target_link_libraries(BML 79 | PRIVATE 80 | BMLUtils 81 | tomlplusplus::tomlplusplus 82 | efsw 83 | ) 84 | 85 | target_compile_definitions(BML PRIVATE 86 | BML_BUILD_DLL 87 | ) 88 | 89 | set_target_properties(BML PROPERTIES 90 | OUTPUT_NAME "BML" 91 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 92 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" 93 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" 94 | ) 95 | 96 | # Core driver executable for testing 97 | add_executable(BMLCoreDriver 98 | driver/CoreDriver.cpp 99 | ) 100 | 101 | target_include_directories(BMLCoreDriver PRIVATE ${BML_INCLUDE_DIR}) 102 | 103 | target_link_libraries(BMLCoreDriver PRIVATE BML) 104 | 105 | set_target_properties(BMLCoreDriver PROPERTIES 106 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 107 | ) 108 | 109 | # Install targets 110 | install(TARGETS BML 111 | EXPORT BMLCoreTargets 112 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 113 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 114 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 115 | ) 116 | 117 | install(EXPORT BMLCoreTargets 118 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/BMLCore" 119 | NAMESPACE BML:: 120 | ) 121 | -------------------------------------------------------------------------------- /src/Macros.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_MACROS_H 2 | #define BML_MACROS_H 3 | 4 | #define CP_HOOK_CLASS_NAME(Class) \ 5 | Class##Hook 6 | 7 | #define CP_HOOK_CLASS(Class) \ 8 | class CP_HOOK_CLASS_NAME(Class) : public Class 9 | 10 | #define CP_CLASS_VTABLE_NAME(Class) \ 11 | Class##VTable 12 | 13 | #define CP_HOOK_METHOD_NAME(Class, Name) \ 14 | CP_HOOK_CLASS_NAME(Class)::Name##Hook 15 | 16 | #define CP_FUNC_HOOK_NAME(Name) \ 17 | Name##Hook 18 | 19 | #define CP_FUNC_TYPE_NAME(Name) \ 20 | Name##Func 21 | 22 | #define CP_FUNC_PTR_NAME(Name) \ 23 | s_##Name##Func 24 | 25 | #define CP_FUNC_ORIG_PTR_NAME(Name) \ 26 | s_##Name##FuncOrig 27 | 28 | #define CP_FUNC_TARGET_PTR_NAME(Name) \ 29 | s_##Name##FuncTarget 30 | 31 | #define CP_DECLARE_FUNCTION_PTRS(Ret, Name, Args) \ 32 | typedef Ret(*CP_FUNC_TYPE_NAME(Name)) Args; \ 33 | extern CP_FUNC_TYPE_NAME(Name) CP_FUNC_PTR_NAME(Name); \ 34 | extern CP_FUNC_TYPE_NAME(Name) CP_FUNC_ORIG_PTR_NAME(Name); \ 35 | extern CP_FUNC_TYPE_NAME(Name) CP_FUNC_TARGET_PTR_NAME(Name) 36 | 37 | #define CP_DEFINE_FUNCTION_PTRS(Name) \ 38 | CP_FUNC_TYPE_NAME(Name) CP_FUNC_PTR_NAME(Name) = reinterpret_cast(&CP_FUNC_HOOK_NAME(Name)); \ 39 | CP_FUNC_TYPE_NAME(Name) CP_FUNC_ORIG_PTR_NAME(Name) = nullptr; \ 40 | CP_FUNC_TYPE_NAME(Name) CP_FUNC_TARGET_PTR_NAME(Name) = nullptr; 41 | 42 | #define CP_DECLARE_METHOD_PTR(Class, Ret, Name, Args) \ 43 | typedef Ret(Class::*CP_FUNC_TYPE_NAME(Name)) Args; \ 44 | CP_FUNC_TYPE_NAME(Name) Name 45 | 46 | #define CP_DECLARE_METHOD_HOOK(Ret, Name, Args) \ 47 | Ret CP_FUNC_HOOK_NAME(Name) Args 48 | 49 | #define CP_DECLARE_METHOD_PTRS(Class, Ret, Name, Args) \ 50 | typedef Ret(Class::*CP_FUNC_TYPE_NAME(Name)) Args; \ 51 | static CP_FUNC_TYPE_NAME(Name) CP_FUNC_PTR_NAME(Name); \ 52 | static CP_FUNC_TYPE_NAME(Name) CP_FUNC_ORIG_PTR_NAME(Name); \ 53 | static CP_FUNC_TYPE_NAME(Name) CP_FUNC_TARGET_PTR_NAME(Name) 54 | 55 | #define CP_DEFINE_METHOD_PTRS(Class, Name) \ 56 | Class::CP_FUNC_TYPE_NAME(Name) Class::CP_FUNC_PTR_NAME(Name) = \ 57 | reinterpret_cast(&Class::Name); \ 58 | Class::CP_FUNC_TYPE_NAME(Name) Class::CP_FUNC_ORIG_PTR_NAME(Name) = nullptr; \ 59 | Class::CP_FUNC_TYPE_NAME(Name) Class::CP_FUNC_TARGET_PTR_NAME(Name) = nullptr; 60 | 61 | #define CP_DEFINE_METHOD_HOOK_PTRS(Class, Name) \ 62 | CP_HOOK_CLASS_NAME(Class)::CP_FUNC_TYPE_NAME(Name) CP_HOOK_CLASS_NAME(Class)::CP_FUNC_PTR_NAME(Name) = \ 63 | reinterpret_cast(&CP_HOOK_CLASS_NAME(Class)::CP_FUNC_HOOK_NAME(Name)); \ 64 | CP_HOOK_CLASS_NAME(Class)::CP_FUNC_TYPE_NAME(Name) CP_HOOK_CLASS_NAME(Class)::CP_FUNC_ORIG_PTR_NAME(Name) = nullptr; \ 65 | CP_HOOK_CLASS_NAME(Class)::CP_FUNC_TYPE_NAME(Name) CP_HOOK_CLASS_NAME(Class)::CP_FUNC_TARGET_PTR_NAME(Name) = nullptr; 66 | 67 | #define CP_CALL_FUNCTION(Func, ...) \ 68 | (*Func)(__VA_ARGS__) 69 | 70 | #define CP_CALL_FUNCTION_ORIG(Name, ...) \ 71 | (*CP_FUNC_ORIG_PTR_NAME(Name))(__VA_ARGS__) 72 | 73 | #define CP_CALL_METHOD(Ref, Func, ...) \ 74 | ((&Ref)->*Func)(__VA_ARGS__) 75 | 76 | #define CP_CALL_METHOD_PTR(Ptr, Func, ...) \ 77 | (Ptr->*Func)(__VA_ARGS__) 78 | 79 | #define CP_CALL_METHOD_ORIG(Name, ...) \ 80 | (this->*CP_FUNC_ORIG_PTR_NAME(Name))(__VA_ARGS__) 81 | 82 | #define CP_CALL_METHOD_ORIG_FORCE(Class, Name, ...) \ 83 | (((Class *)this)->*CP_FUNC_ORIG_PTR_NAME(Name))(__VA_ARGS__) 84 | 85 | #endif // BML_MACROS_H 86 | -------------------------------------------------------------------------------- /include/BML/Guids/Materials.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_GUIDS_MATERIALS_H 2 | #define BML_GUIDS_MATERIALS_H 3 | 4 | #include "CKTypes.h" 5 | 6 | // 7 | // Materials 8 | // 9 | 10 | // Building Blocks 11 | #define VT_MATERIALS_MOVIEPLAYER CKGUID(0x778d16d4, 0x1dd60060) 12 | #define VT_MATERIALS_TEXTURESCROLLER CKGUID(0xf11d010a, 0xfb1d010a) 13 | #define VT_MATERIALS_TEXTURESINE CKGUID(0x4fe0646e, 0x7e0635c9) 14 | #define VT_MATERIALS_SETCURRENTSLOT CKGUID(0xaaaa213a, 0xeaa8d52a) 15 | #define VT_MATERIALS_SETAMBIENT CKGUID(0xaaaaaaaa, 0xaeae1287) 16 | #define VT_MATERIALS_SETCOLORKEY CKGUID(0x41bb5af1, 0x1eb26e9) 17 | #define VT_MATERIALS_SETDESTBLEND CKGUID(0x36d1a87, 0xdf0a670c) 18 | #define VT_MATERIALS_SETBLENDMODES CKGUID(0x2f572a66, 0x6a9a0088) 19 | #define VT_MATERIALS_SETDIFFUSE CKGUID(0xe1e1e1e1, 0x1e1e1e1e) 20 | #define VT_MATERIALS_SETEMISSIVE CKGUID(0xd1d1d1d1, 0x30303030) 21 | #define VT_MATERIALS_SETFILLMODE CKGUID(0xc5c4c3c2, 0x8c8c8c8c) 22 | #define VT_MATERIALS_SETLIGHTINGMODE CKGUID(0x45899665, 0x45211254) 23 | #define VT_MATERIALS_SETMATERIAL CKGUID(0x37865d50, 0x2e285fab) 24 | #define VT_MATERIALS_SETMATERIAL3DSPRITE CKGUID(0x3ddd6d33, 0xa54d5c48) 25 | #define VT_MATERIALS_SETSPECULARPOWER CKGUID(0x45454545, 0x15151515) 26 | #define VT_MATERIALS_SETPRELITCOLOR CKGUID(0x60415d44, 0xd0174ea) 27 | #define VT_MATERIALS_SETSHADEMODE CKGUID(0x95195195, 0x98795198) 28 | #define VT_MATERIALS_SETSPECULAR CKGUID(0x20202020, 0x30303030) 29 | #define VT_MATERIALS_SETSRCBLEND CKGUID(0x40234ddc, 0x12da700) 30 | #define VT_MATERIALS_SETTEXTURE CKGUID(0xeb123eb5, 0x5be321be) 31 | #define VT_MATERIALS_SETTEXTUREMAG CKGUID(0xda495631, 0xde155488) 32 | #define VT_MATERIALS_SETTEXTUREMIN CKGUID(0x12589321, 0x32587123) 33 | #define VT_MATERIALS_SETTRANSPARENT CKGUID(0x2fc37564, 0x299a2c9d) 34 | #define VT_MATERIALS_SETBOTHSIDED CKGUID(0x76319845, 0x48593399) 35 | #define VT_MATERIALS_SETWRAPMODE CKGUID(0x12a5498d, 0x65657512) 36 | #define VT_MATERIALS_SETMATERIALZBUFFER CKGUID(0x1144022f, 0x68fd055c) 37 | #define VT_MATERIALS_SETALPHATEST CKGUID(0x1cb5661e, 0x5b5d3fda) 38 | #define VT_MATERIALS_ACTIVATECHANNEL CKGUID(0x55566666, 0x66666444) 39 | #define VT_MATERIALS_ADDCHANNEL CKGUID(0x89658965, 0x36654789) 40 | #define VT_MATERIALS_REMOVECHANNEL CKGUID(0x4a462e58, 0x22e369b) 41 | #define VT_MATERIALS_SETCHANNELDESTBLEND CKGUID(0x2f5a0eb9, 0x27067e93) 42 | #define VT_MATERIALS_SETCHANNELMATERIAL CKGUID(0x2e0a6b92, 0x366a3f9b) 43 | #define VT_MATERIALS_SETCHANNELSRCBLEND CKGUID(0x3b9b0e7f, 0xb1316800) 44 | #define VT_MATERIALS_WRITEINTEXTURE CKGUID(0x1d57024d, 0x7c064238) 45 | #define VT_MATERIALS_SETMIPMAPLEVEL CKGUID(0x3ebe40f2, 0x3fa41377) 46 | #define VT_MATERIALS_CHANGETEXTURESIZE CKGUID(0x7a7c4d7b, 0x468d0f33) 47 | #define VT_MATERIALS_CHANGETEXTUREVIDEOFORMAT CKGUID(0xf56d92ef, 0x41eb20c1) 48 | #define VT_MATERIALS_ENVIRONMENTMAPPING CKGUID(0x10d1c997, 0x99dabef4) 49 | #define VT_MATERIALS_PLANARMAPPING CKGUID(0x2a6a3fa3, 0x3280407d) 50 | #define VT_MATERIALS_CYLINDRICALMAPPING CKGUID(0x770a5fc9, 0x7bbe2aed) 51 | #define VT_MATERIALS_SPHERICALMAPPING CKGUID(0x5e40778e, 0x44f36cf3) 52 | #define VT_MATERIALS_SCREENMAPPING CKGUID(0x58ef12ca, 0x26805940) 53 | #define VT_MATERIALS_COPYMAPPING CKGUID(0x86a487c, 0x3cd44acb) 54 | #define VT_MATERIALS_CREATEMIPMAPTEXTURE CKGUID(0x6e407fcf, 0xeff06a5) 55 | 56 | // Parameter Types 57 | #define CKPGUID_WRITEMODE CKGUID(0x675e3903, 0x7a06003c) 58 | #define CKPGUID_ENVMAPPINGFLIPMODE CKGUID(0x11416cd2, 0x293c701e) 59 | #define CKPGUID_TEXTUREVIDEOFORMAT CKGUID(0x2c1e5d83, 0x37b9284b) 60 | 61 | #endif // BML_GUIDS_MATERIALS_H 62 | -------------------------------------------------------------------------------- /src/ModLoader/Behaviors/HookBlock.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file HookBlock.cpp 3 | * @brief Building block hooking behavior for Virtools 4 | * 5 | * This behavior allows intercepting and extending building block execution 6 | * through callback registration. Used by ModLoader to provide mod callbacks 7 | * at specific points in the game's behavior graph execution. 8 | */ 9 | 10 | #include "CKAll.h" 11 | 12 | typedef int (*CKBehaviorCallback)(const CKBehaviorContext *behcontext, void *arg); 13 | 14 | // Forward declarations 15 | CKObjectDeclaration *FillBehaviorHookBlockDecl(); 16 | CKERROR CreateHookBlockProto(CKBehaviorPrototype **pproto); 17 | int HookBlock(const CKBehaviorContext &behcontext); 18 | 19 | /** 20 | * @brief Creates the HookBlock object declaration 21 | * 22 | * Registers the HookBlock building block with the Virtools engine. 23 | * This block is used to intercept behavior graph execution. 24 | */ 25 | CKObjectDeclaration *FillBehaviorHookBlockDecl() { 26 | CKObjectDeclaration *od = CreateCKObjectDeclaration("HookBlock"); 27 | od->SetDescription("Hook building blocks"); 28 | od->SetCategory("Hook"); 29 | od->SetType(CKDLL_BEHAVIORPROTOTYPE); 30 | od->SetGuid(CKGUID(0x19038c0, 0x663902da)); 31 | od->SetAuthorGuid(CKGUID(0x3a086b4d, 0x2f4a4f01)); 32 | od->SetAuthorName("Kakuty"); 33 | od->SetVersion(0x00010000); 34 | od->SetCreationFunction(CreateHookBlockProto); 35 | od->SetCompatibleClassId(CKCID_BEOBJECT); 36 | return od; 37 | } 38 | 39 | /** 40 | * @brief Creates the HookBlock behavior prototype 41 | * 42 | * Defines the behavior's parameters and settings: 43 | * - Local parameter 0: Callback function pointer 44 | * - Local parameter 1: User argument pointer 45 | * - Variable inputs/outputs for flexible hooking 46 | */ 47 | CKERROR CreateHookBlockProto(CKBehaviorPrototype **pproto) { 48 | CKBehaviorPrototype *proto = CreateCKBehaviorPrototype("HookBlock"); 49 | if (!proto) return CKERR_OUTOFMEMORY; 50 | 51 | proto->DeclareLocalParameter("Callback", CKPGUID_POINTER); 52 | proto->DeclareLocalParameter("Argument", CKPGUID_POINTER); 53 | 54 | proto->SetBehaviorFlags((CK_BEHAVIOR_FLAGS)(CKBEHAVIOR_VARIABLEINPUTS | CKBEHAVIOR_VARIABLEOUTPUTS)); 55 | proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL); 56 | proto->SetFunction(HookBlock); 57 | 58 | *pproto = proto; 59 | return CK_OK; 60 | } 61 | 62 | /** 63 | * @brief HookBlock behavior execution function 64 | * 65 | * Deactivates all inputs, invokes the registered callback (if any), 66 | * then activates all outputs. The callback can control the return value. 67 | * 68 | * @param behcontext The behavior context containing execution state 69 | * @return CKBR_OK on success, or callback-defined return value 70 | */ 71 | int HookBlock(const CKBehaviorContext &behcontext) { 72 | CKBehavior *beh = behcontext.Behavior; 73 | 74 | // Deactivate all inputs 75 | int i, count = beh->GetInputCount(); 76 | for (i = 0; i < count; ++i) { 77 | beh->ActivateInput(i, FALSE); 78 | } 79 | 80 | // Invoke registered callback 81 | int ret = CKBR_OK; 82 | CKBehaviorCallback cb = nullptr; 83 | beh->GetLocalParameterValue(0, &cb); 84 | if (cb) { 85 | void *arg = nullptr; 86 | beh->GetLocalParameterValue(1, &arg); 87 | ret = cb(&behcontext, arg); 88 | } 89 | 90 | // Activate all outputs 91 | count = beh->GetOutputCount(); 92 | for (i = 0; i < count; ++i) { 93 | beh->ActivateOutput(i); 94 | } 95 | 96 | return ret; 97 | } -------------------------------------------------------------------------------- /src/Core/MemoryApi.cpp: -------------------------------------------------------------------------------- 1 | #include "ApiRegistrationMacros.h" 2 | #include "MemoryManager.h" 3 | #include "bml_capabilities.h" 4 | 5 | #include "bml_memory.h" 6 | 7 | namespace BML::Core { 8 | void *BML_API_Alloc(size_t size) { 9 | return MemoryManager::Instance().Alloc(size); 10 | } 11 | 12 | void *BML_API_Calloc(size_t count, size_t size) { 13 | return MemoryManager::Instance().Calloc(count, size); 14 | } 15 | 16 | void *BML_API_Realloc(void *ptr, size_t old_size, size_t new_size) { 17 | return MemoryManager::Instance().Realloc(ptr, old_size, new_size); 18 | } 19 | 20 | void BML_API_Free(void *ptr) { 21 | MemoryManager::Instance().Free(ptr); 22 | } 23 | 24 | void BML_API_FreeWithSize(void *ptr, size_t size) { 25 | MemoryManager::Instance().FreeWithSize(ptr, size); 26 | } 27 | 28 | void *BML_API_AllocAligned(size_t size, size_t alignment) { 29 | return MemoryManager::Instance().AllocAligned(size, alignment); 30 | } 31 | 32 | void BML_API_FreeAligned(void *ptr) { 33 | MemoryManager::Instance().FreeAligned(ptr); 34 | } 35 | 36 | BML_Result BML_API_MemoryPoolCreate(size_t block_size, uint32_t initial_blocks, BML_MemoryPool *out_pool) { 37 | return MemoryManager::Instance().CreatePool(block_size, initial_blocks, out_pool); 38 | } 39 | 40 | void *BML_API_MemoryPoolAlloc(BML_MemoryPool pool) { 41 | return MemoryManager::Instance().PoolAlloc(pool); 42 | } 43 | 44 | void BML_API_MemoryPoolFree(BML_MemoryPool pool, void *ptr) { 45 | MemoryManager::Instance().PoolFree(pool, ptr); 46 | } 47 | 48 | void BML_API_MemoryPoolDestroy(BML_MemoryPool pool) { 49 | MemoryManager::Instance().DestroyPool(pool); 50 | } 51 | 52 | BML_Result BML_API_GetMemoryStats(BML_MemoryStats *out_stats) { 53 | return MemoryManager::Instance().GetStats(out_stats); 54 | } 55 | 56 | BML_Result BML_API_MemoryGetCaps(BML_MemoryCaps *out_caps) { 57 | return MemoryManager::Instance().GetCaps(out_caps); 58 | } 59 | 60 | void RegisterMemoryApis() { 61 | BML_BEGIN_API_REGISTRATION(); 62 | 63 | /* Basic allocation - direct functions, no error guard */ 64 | BML_REGISTER_API_WITH_CAPS(bmlAlloc, BML_API_Alloc, BML_CAP_MEMORY_BASIC); 65 | BML_REGISTER_API_WITH_CAPS(bmlCalloc, BML_API_Calloc, BML_CAP_MEMORY_BASIC); 66 | BML_REGISTER_API_WITH_CAPS(bmlRealloc, BML_API_Realloc, BML_CAP_MEMORY_BASIC); 67 | BML_REGISTER_API_WITH_CAPS(bmlFree, BML_API_Free, BML_CAP_MEMORY_BASIC); 68 | 69 | /* Aligned allocation */ 70 | BML_REGISTER_API_WITH_CAPS(bmlAllocAligned, BML_API_AllocAligned, BML_CAP_MEMORY_ALIGNED); 71 | BML_REGISTER_API_WITH_CAPS(bmlFreeAligned, BML_API_FreeAligned, BML_CAP_MEMORY_ALIGNED); 72 | 73 | /* Memory pools - guarded for error handling */ 74 | BML_REGISTER_API_GUARDED_WITH_CAPS(bmlMemoryPoolCreate, "memory.pool", BML_API_MemoryPoolCreate, BML_CAP_MEMORY_POOL); 75 | BML_REGISTER_API_WITH_CAPS(bmlMemoryPoolAlloc, BML_API_MemoryPoolAlloc, BML_CAP_MEMORY_POOL); 76 | BML_REGISTER_API_WITH_CAPS(bmlMemoryPoolFree, BML_API_MemoryPoolFree, BML_CAP_MEMORY_POOL); 77 | BML_REGISTER_API_WITH_CAPS(bmlMemoryPoolDestroy, BML_API_MemoryPoolDestroy, BML_CAP_MEMORY_POOL); 78 | 79 | /* Statistics and capabilities */ 80 | BML_REGISTER_CAPS_API_WITH_CAPS(bmlGetMemoryStats, "memory.stats", BML_API_GetMemoryStats, BML_CAP_MEMORY_BASIC); 81 | BML_REGISTER_CAPS_API_WITH_CAPS(bmlMemoryGetCaps, "memory.caps", BML_API_MemoryGetCaps, BML_CAP_MEMORY_BASIC); 82 | } 83 | } // namespace BML::Core 84 | -------------------------------------------------------------------------------- /tests/ManifestParserTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Core/ModManifest.h" 8 | 9 | namespace { 10 | 11 | class ManifestParserFixture : public ::testing::Test { 12 | protected: 13 | std::filesystem::path temp_dir; 14 | 15 | void SetUp() override { 16 | temp_dir = std::filesystem::temp_directory_path() / std::filesystem::path("bml_manifest_parser_test"); 17 | std::filesystem::create_directories(temp_dir); 18 | } 19 | 20 | void TearDown() override { 21 | std::error_code ec; 22 | std::filesystem::remove_all(temp_dir, ec); 23 | } 24 | 25 | std::filesystem::path WriteManifest(std::string_view content) { 26 | auto path = temp_dir / "mod.toml"; 27 | std::ofstream ofs(path, std::ios::trunc); 28 | ofs << content; 29 | ofs.close(); 30 | return path; 31 | } 32 | }; 33 | 34 | TEST_F(ManifestParserFixture, ParsesValidManifest) { 35 | constexpr auto kManifest = R"TOML( 36 | capabilities = ["imc", "logging"] 37 | 38 | [package] 39 | id = "example.mod" 40 | name = "Example Mod" 41 | version = "1.2.3" 42 | entry = "Example.dll" 43 | description = "Sample" 44 | authors = ["Alice", "Bob"] 45 | 46 | [dependencies] 47 | core = "^1.0" 48 | "optional.mod" = { version = ">=2.0", optional = true } 49 | )TOML"; 50 | 51 | auto path = WriteManifest(kManifest); 52 | BML::Core::ManifestParser parser; 53 | BML::Core::ModManifest manifest; 54 | BML::Core::ManifestParseError error; 55 | 56 | ASSERT_TRUE(parser.ParseFile(path.wstring(), manifest, error)) << error.message; 57 | EXPECT_EQ(manifest.package.id, "example.mod"); 58 | EXPECT_EQ(manifest.package.name, "Example Mod"); 59 | EXPECT_EQ(manifest.package.version, "1.2.3"); 60 | EXPECT_EQ(manifest.package.entry, "Example.dll"); 61 | ASSERT_EQ(manifest.package.authors.size(), 2u); 62 | EXPECT_EQ(manifest.package.authors[0], "Alice"); 63 | EXPECT_EQ(manifest.package.authors[1], "Bob"); 64 | 65 | ASSERT_EQ(manifest.dependencies.size(), 2u); 66 | EXPECT_EQ(manifest.dependencies[0].id, "core"); 67 | EXPECT_FALSE(manifest.dependencies[0].optional); 68 | EXPECT_TRUE(manifest.dependencies[0].requirement.parsed); 69 | EXPECT_EQ(manifest.dependencies[1].id, "optional.mod"); 70 | EXPECT_TRUE(manifest.dependencies[1].optional); 71 | 72 | ASSERT_EQ(manifest.capabilities.size(), 2u); 73 | EXPECT_EQ(manifest.capabilities[0], "imc"); 74 | EXPECT_EQ(manifest.capabilities[1], "logging"); 75 | 76 | EXPECT_EQ(std::filesystem::path(manifest.manifest_path).filename(), L"mod.toml"); 77 | EXPECT_EQ(std::filesystem::path(manifest.directory), temp_dir); 78 | } 79 | 80 | TEST_F(ManifestParserFixture, FailsWithoutPackageTable) { 81 | auto path = WriteManifest("[not_package]\nid=\"broken\""); 82 | BML::Core::ManifestParser parser; 83 | BML::Core::ModManifest manifest; 84 | BML::Core::ManifestParseError error; 85 | 86 | EXPECT_FALSE(parser.ParseFile(path.wstring(), manifest, error)); 87 | EXPECT_NE(error.message.find("Missing [package]"), std::string::npos); 88 | ASSERT_TRUE(error.file.has_value()); 89 | } 90 | 91 | TEST_F(ManifestParserFixture, RejectsInvalidDependencyShape) { 92 | constexpr auto kManifest = R"TOML( 93 | [package] 94 | id = "bad.deps" 95 | name = "Broken" 96 | version = "0.1.0" 97 | 98 | [dependencies] 99 | weird = 42 100 | )TOML"; 101 | 102 | auto path = WriteManifest(kManifest); 103 | BML::Core::ManifestParser parser; 104 | BML::Core::ModManifest manifest; 105 | BML::Core::ManifestParseError error; 106 | 107 | EXPECT_FALSE(parser.ParseFile(path.wstring(), manifest, error)); 108 | EXPECT_NE(error.message.find("Dependency 'weird'"), std::string::npos); 109 | } 110 | 111 | } // namespace 112 | -------------------------------------------------------------------------------- /src/ModLoader/ModManager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ModManager.h 3 | * @brief ModLoader CK Manager 4 | * 5 | * ModManager is a CKBaseManager that integrates with the Virtools engine 6 | * lifecycle. It handles: 7 | * - Engine context initialization and shutdown 8 | * - Module loading coordination 9 | * - IMC event publishing for game lifecycle events 10 | * - Frame processing coordination 11 | * 12 | * This replaces the ModManager from BMLPlus.dll, focusing purely on 13 | * engine integration without legacy mod management concerns. 14 | */ 15 | 16 | #ifndef BML_MOD_MANAGER_H 17 | #define BML_MOD_MANAGER_H 18 | 19 | #include "CKBaseManager.h" 20 | #include "CKContext.h" 21 | 22 | // ModLoader Manager GUID 23 | #ifndef MOD_MANAGER_GUID 24 | #define MOD_MANAGER_GUID CKGUID(0x32a40332, 0x3bf12a51) 25 | #endif 26 | 27 | /** 28 | * @brief CK Manager for ModLoader engine integration 29 | * 30 | * Responsibilities: 31 | * - Publish engine lifecycle events via IMC 32 | * - Coordinate module loading after CKContext is available 33 | * - Manage frame processing hooks 34 | */ 35 | class ModManager : public CKBaseManager { 36 | public: 37 | explicit ModManager(CKContext *context); 38 | ~ModManager() override; 39 | 40 | // CKBaseManager lifecycle overrides 41 | CKERROR OnCKInit() override; 42 | CKERROR OnCKEnd() override; 43 | CKERROR OnCKPlay() override; 44 | CKERROR OnCKPause() override; 45 | CKERROR OnCKReset() override; 46 | CKERROR OnCKPostReset() override; 47 | CKERROR PreProcess() override; 48 | CKERROR PostProcess() override; 49 | CKERROR OnPreRender(CKRenderContext *dev) override; 50 | CKERROR OnPostRender(CKRenderContext *dev) override; 51 | CKERROR OnPostSpriteRender(CKRenderContext *dev) override; 52 | 53 | CKDWORD GetValidFunctionsMask() override { 54 | return CKMANAGER_FUNC_OnCKInit | 55 | CKMANAGER_FUNC_OnCKEnd | 56 | CKMANAGER_FUNC_OnCKPlay | 57 | CKMANAGER_FUNC_OnCKPause | 58 | CKMANAGER_FUNC_OnCKReset | 59 | CKMANAGER_FUNC_OnCKPostReset | 60 | CKMANAGER_FUNC_PreProcess | 61 | CKMANAGER_FUNC_PostProcess | 62 | CKMANAGER_FUNC_OnPreRender | 63 | CKMANAGER_FUNC_OnPostRender | 64 | CKMANAGER_FUNC_OnPostSpriteRender; 65 | } 66 | 67 | int GetFunctionPriority(CKMANAGER_FUNCTIONS Function) override { 68 | // ModLoader runs before other managers to ensure hooks are ready 69 | if (Function == CKMANAGER_FUNC_PreProcess) 70 | return -10000; // Very low priority (runs early) 71 | else if (Function == CKMANAGER_FUNC_PostProcess) 72 | return 10000; // Very high priority (runs late) 73 | else 74 | return 0; 75 | } 76 | 77 | /** 78 | * @brief Get the ModManager instance from a CKContext 79 | */ 80 | static ModManager *GetManager(CKContext *context) { 81 | return (ModManager *) context->GetManagerByGuid(MOD_MANAGER_GUID); 82 | } 83 | 84 | /** 85 | * @brief Check if engine is in play state (game running) 86 | */ 87 | bool IsEngineReady() const { return m_EngineReady; } 88 | 89 | /** 90 | * @brief Get the render context 91 | */ 92 | CKRenderContext *GetRenderContext() const { return m_RenderContext; } 93 | 94 | private: 95 | // Initialize IMC topic IDs 96 | void InitializeIMCTopics(); 97 | 98 | // Register Virtools objects to BML context 99 | void RegisterVirtoolsObjects(); 100 | 101 | // Publish lifecycle events 102 | void PublishLifecycleEvent(const char *topic); 103 | void PublishProcessEvent(float deltaTime); 104 | 105 | CKRenderContext *m_RenderContext = nullptr; 106 | float m_LastTime = 0.0f; 107 | bool m_EngineReady = false; 108 | }; 109 | 110 | #endif // BML_MOD_MANAGER_H 111 | -------------------------------------------------------------------------------- /src/Errors.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Defines.h" 2 | 3 | 4 | const char *BML_GetErrorString(int errorCode) { 5 | switch (errorCode) { 6 | /* General error codes */ 7 | case BML_OK: 8 | return "Success"; 9 | case BML_ERROR_FAIL: 10 | return "Operation failed"; 11 | case BML_ERROR_FROZEN: 12 | return "Operation cannot be performed in current state"; 13 | case BML_ERROR_NOT_FOUND: 14 | return "Requested item not found"; 15 | case BML_ERROR_NOT_IMPLEMENTED: 16 | return "Feature not implemented"; 17 | case BML_ERROR_OUT_OF_MEMORY: 18 | return "Memory allocation failed"; 19 | case BML_ERROR_INVALID_PARAMETER: 20 | return "Invalid parameter provided"; 21 | case BML_ERROR_ACCESS_DENIED: 22 | return "Access to resource denied"; 23 | case BML_ERROR_TIMEOUT: 24 | return "Operation timed out"; 25 | case BML_ERROR_BUSY: 26 | return "Resource busy or locked"; 27 | case BML_ERROR_ALREADY_EXISTS: 28 | return "Item already exists"; 29 | 30 | /* Mod-specific error codes */ 31 | case BML_ERROR_MOD_LOAD_FAILED: 32 | return "Failed to load mod"; 33 | case BML_ERROR_MOD_INVALID: 34 | return "Invalid mod format or structure"; 35 | case BML_ERROR_MOD_INCOMPATIBLE: 36 | return "Mod is incompatible with current BML version"; 37 | case BML_ERROR_MOD_INITIALIZATION: 38 | return "Mod initialization failed"; 39 | 40 | /* Dependency-specific error codes */ 41 | case BML_ERROR_DEPENDENCY_CIRCULAR: 42 | return "Circular dependency detected"; 43 | case BML_ERROR_DEPENDENCY_MISSING: 44 | return "Required dependency not found"; 45 | case BML_ERROR_DEPENDENCY_VERSION: 46 | return "Dependency version incompatible"; 47 | case BML_ERROR_DEPENDENCY_RESOLUTION: 48 | return "Failed to resolve dependencies"; 49 | case BML_ERROR_DEPENDENCY_LIMIT: 50 | return "Too many dependencies"; 51 | case BML_ERROR_DEPENDENCY_INVALID: 52 | return "Invalid dependency specification"; 53 | case BML_ERROR_DEPENDENCY_CONFLICT: 54 | return "Conflicting dependencies detected"; 55 | 56 | /* Resource-specific error codes */ 57 | case BML_ERROR_RESOURCE_NOT_FOUND: 58 | return "Resource not found"; 59 | case BML_ERROR_RESOURCE_INVALID: 60 | return "Invalid resource format"; 61 | case BML_ERROR_RESOURCE_BUSY: 62 | return "Resource is busy or locked"; 63 | case BML_ERROR_RESOURCE_PERMISSION: 64 | return "Insufficient permission for resource"; 65 | 66 | /* Script-specific error codes */ 67 | case BML_ERROR_SCRIPT_INVALID: 68 | return "Invalid script"; 69 | case BML_ERROR_SCRIPT_EXECUTION: 70 | return "Script execution failed"; 71 | case BML_ERROR_SCRIPT_TIMEOUT: 72 | return "Script execution timed out"; 73 | 74 | /* Command-specific error codes */ 75 | case BML_ERROR_COMMAND_INVALID: 76 | return "Invalid command"; 77 | case BML_ERROR_COMMAND_PERMISSION: 78 | return "Insufficient permission for command"; 79 | case BML_ERROR_COMMAND_EXECUTION: 80 | return "Command execution failed"; 81 | 82 | /* Configuration-specific error codes */ 83 | case BML_ERROR_CONFIG_INVALID: 84 | return "Invalid configuration"; 85 | case BML_ERROR_CONFIG_READ: 86 | return "Failed to read configuration"; 87 | case BML_ERROR_CONFIG_WRITE: 88 | return "Failed to write configuration"; 89 | case BML_ERROR_CONFIG_FORMAT: 90 | return "Invalid configuration format"; 91 | 92 | /* Unknown error code */ 93 | default: 94 | return "Unknown error"; 95 | } 96 | } -------------------------------------------------------------------------------- /src/Core/DiagnosticManager.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_DIAGNOSTIC_MANAGER_H 2 | #define BML_CORE_DIAGNOSTIC_MANAGER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "bml_types.h" 8 | #include "bml_errors.h" 9 | 10 | namespace BML::Core { 11 | /** 12 | * @brief Thread-local error context 13 | * 14 | * Stores detailed error information for the current thread. 15 | * Automatically managed via TLS. 16 | */ 17 | struct ErrorContext { 18 | BML_ErrorInfo info{}; 19 | std::array message_buffer{}; 20 | std::array api_name_buffer{}; 21 | std::array source_file_buffer{}; 22 | bool has_error{false}; 23 | 24 | void Clear() { 25 | has_error = false; 26 | info = BML_ErrorInfo{}; 27 | info.struct_size = sizeof(BML_ErrorInfo); 28 | message_buffer.fill(0); 29 | api_name_buffer.fill(0); 30 | source_file_buffer.fill(0); 31 | } 32 | 33 | void SetError(BML_Result code, 34 | const char *message, 35 | const char *api_name = nullptr, 36 | const char *source_file = nullptr, 37 | int source_line = 0) { 38 | has_error = true; 39 | info.struct_size = sizeof(BML_ErrorInfo); 40 | info.result_code = code; 41 | info.source_line = source_line; 42 | 43 | // Copy strings to thread-local buffers 44 | if (message) { 45 | strncpy(message_buffer.data(), message, message_buffer.size() - 1); 46 | info.message = message_buffer.data(); 47 | } else { 48 | info.message = nullptr; 49 | } 50 | 51 | if (api_name) { 52 | strncpy(api_name_buffer.data(), api_name, api_name_buffer.size() - 1); 53 | info.api_name = api_name_buffer.data(); 54 | } else { 55 | info.api_name = nullptr; 56 | } 57 | 58 | if (source_file) { 59 | strncpy(source_file_buffer.data(), source_file, source_file_buffer.size() - 1); 60 | info.source_file = source_file_buffer.data(); 61 | } else { 62 | info.source_file = nullptr; 63 | } 64 | } 65 | }; 66 | 67 | /** 68 | * @brief Diagnostic manager for error tracking 69 | * 70 | * Manages thread-local error contexts and provides error query APIs. 71 | */ 72 | class DiagnosticManager { 73 | public: 74 | static DiagnosticManager &Instance(); 75 | 76 | DiagnosticManager(const DiagnosticManager &) = delete; 77 | DiagnosticManager &operator=(const DiagnosticManager &) = delete; 78 | 79 | // Error context management 80 | BML_Result GetLastError(BML_ErrorInfo *out_error); 81 | void ClearLastError(); 82 | 83 | // Set error (internal use) 84 | void SetError(BML_Result code, 85 | const char *message, 86 | const char *api_name = nullptr, 87 | const char *source_file = nullptr, 88 | int source_line = 0); 89 | 90 | private: 91 | DiagnosticManager() = default; 92 | ~DiagnosticManager() = default; 93 | 94 | // Get thread-local error context 95 | ErrorContext &GetThreadContext(); 96 | }; 97 | 98 | // Helper function for setting errors (simplified signature) 99 | inline BML_Result SetLastErrorDiag(BML_Result code, 100 | const char *message, 101 | const char *api_name = nullptr) { 102 | DiagnosticManager::Instance().SetError(code, message, api_name, nullptr, 0); 103 | return code; 104 | } 105 | } // namespace BML::Core 106 | 107 | #endif // BML_CORE_DIAGNOSTIC_MANAGER_H 108 | -------------------------------------------------------------------------------- /src/Core/ProfilingManager.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_PROFILING_MANAGER_H 2 | #define BML_CORE_PROFILING_MANAGER_H 3 | 4 | #include "bml_profiling.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef WIN32_LEAN_AND_MEAN 12 | #define WIN32_LEAN_AND_MEAN 13 | #endif 14 | #include 15 | 16 | namespace BML::Core { 17 | /** 18 | * @brief Manages performance profiling and tracing 19 | * 20 | * Provides Chrome Tracing JSON export and high-resolution timing. 21 | * Thread-safe for all trace operations. 22 | */ 23 | class ProfilingManager { 24 | public: 25 | static ProfilingManager &Instance(); 26 | 27 | ProfilingManager(const ProfilingManager &) = delete; 28 | ProfilingManager &operator=(const ProfilingManager &) = delete; 29 | 30 | /* Trace Events */ 31 | void TraceBegin(const char *name, const char *category); 32 | void TraceEnd(); 33 | void TraceInstant(const char *name, const char *category); 34 | void TraceSetThreadName(const char *name); 35 | void TraceCounter(const char *name, int64_t value); 36 | void TraceFrameMark(); 37 | 38 | /* Performance Counters */ 39 | uint64_t GetApiCallCount(const char *api_name); 40 | uint64_t GetTotalAllocBytes(); 41 | uint64_t GetTimestampNs(); 42 | uint64_t GetCpuFrequency(); 43 | 44 | /* Backend Control */ 45 | BML_ProfilerBackend GetProfilerBackend() const { return m_Backend; } 46 | BML_Result SetProfilingEnabled(BML_Bool enable); 47 | BML_Bool IsProfilingEnabled() const { return m_Enabled ? BML_TRUE : BML_FALSE; } 48 | BML_Result FlushProfilingData(const char *filename); 49 | 50 | /* Statistics */ 51 | BML_Result GetProfilingStats(BML_ProfilingStats *out_stats); 52 | BML_Result GetProfilingCaps(BML_ProfilingCaps *out_caps); 53 | 54 | private: 55 | ProfilingManager(); 56 | ~ProfilingManager(); 57 | 58 | struct TraceEvent { 59 | enum Type { BEGIN, END, INSTANT, COUNTER, FRAME }; 60 | 61 | Type type = BEGIN; 62 | std::string name; 63 | std::string category; 64 | uint64_t timestamp_ns = 0; 65 | uint64_t thread_id = 0; 66 | int64_t counter_value = 0; 67 | }; 68 | 69 | struct ThreadContext { 70 | std::string name; 71 | std::vector scope_stack; // Track nested scopes 72 | }; 73 | 74 | ThreadContext &GetThreadContext(); 75 | void WriteJsonEvent(const TraceEvent &evt, FILE *fp); 76 | 77 | // RAII wrapper for CRITICAL_SECTION to ensure proper unlock on all exit paths 78 | class CriticalSectionGuard { 79 | public: 80 | explicit CriticalSectionGuard(CRITICAL_SECTION &cs) : m_cs(cs) { 81 | EnterCriticalSection(&m_cs); 82 | } 83 | ~CriticalSectionGuard() { 84 | LeaveCriticalSection(&m_cs); 85 | } 86 | CriticalSectionGuard(const CriticalSectionGuard &) = delete; 87 | CriticalSectionGuard &operator=(const CriticalSectionGuard &) = delete; 88 | private: 89 | CRITICAL_SECTION &m_cs; 90 | }; 91 | 92 | BML_ProfilerBackend m_Backend; 93 | std::atomic m_Enabled; 94 | 95 | std::atomic m_TotalEvents; 96 | std::atomic m_TotalScopes; 97 | std::atomic m_DroppedEvents; 98 | 99 | std::vector m_EventBuffer; 100 | CRITICAL_SECTION m_BufferLock; 101 | 102 | uint64_t m_QpcFrequency; 103 | uint64_t m_StartupTimeNs; 104 | 105 | static constexpr size_t MAX_EVENTS = 100000; 106 | static constexpr size_t MAX_SCOPE_DEPTH = 64; 107 | }; 108 | } // namespace BML::Core 109 | 110 | #endif /* BML_CORE_PROFILING_MANAGER_H */ 111 | -------------------------------------------------------------------------------- /src/Core/MemoryManager.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CORE_MEMORY_MANAGER_H 2 | #define BML_CORE_MEMORY_MANAGER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bml_memory.h" 11 | 12 | namespace BML::Core { 13 | // Forward declarations 14 | class FixedBlockPool; 15 | 16 | /** 17 | * @brief Memory pool implementation wrapper 18 | */ 19 | struct MemoryPoolImpl { 20 | std::unique_ptr pool; 21 | size_t block_size; 22 | std::atomic alloc_count{0}; 23 | std::atomic free_count{0}; 24 | }; 25 | 26 | /** 27 | * @brief Global memory manager singleton 28 | * 29 | * Provides unified memory allocation across DLL boundaries. 30 | * Thread-safe and tracks statistics when enabled. 31 | */ 32 | class MemoryManager { 33 | public: 34 | static MemoryManager &Instance(); 35 | 36 | MemoryManager(const MemoryManager &) = delete; 37 | MemoryManager &operator=(const MemoryManager &) = delete; 38 | 39 | // Basic allocation 40 | // Note: Standard Free() cannot track deallocation size accurately. 41 | // Use FreeWithSize() for precise statistics when the size is known. 42 | void *Alloc(size_t size); 43 | void *Calloc(size_t count, size_t size); 44 | void *Realloc(void *ptr, size_t old_size, size_t new_size); 45 | void *ReallocUnknownSize(void *ptr, size_t new_size); // Legacy, inaccurate tracking 46 | void Free(void *ptr); 47 | void FreeWithSize(void *ptr, size_t size); // For accurate deallocation tracking 48 | 49 | // Aligned allocation 50 | void *AllocAligned(size_t size, size_t alignment); 51 | void FreeAligned(void *ptr); 52 | 53 | // Memory pools 54 | BML_Result CreatePool(size_t block_size, uint32_t initial_blocks, BML_MemoryPool *out_pool); 55 | void *PoolAlloc(BML_MemoryPool pool); 56 | void PoolFree(BML_MemoryPool pool, void *ptr); 57 | void DestroyPool(BML_MemoryPool pool); 58 | 59 | // Statistics 60 | BML_Result GetStats(BML_MemoryStats *out_stats); 61 | BML_Result GetCaps(BML_MemoryCaps *out_caps); 62 | 63 | // Configuration 64 | void SetTrackingEnabled(bool enabled) { m_tracking_enabled.store(enabled, std::memory_order_relaxed); } 65 | bool IsTrackingEnabled() const { return m_tracking_enabled.load(std::memory_order_relaxed); } 66 | 67 | #if defined(BML_TEST) 68 | void ResetStatsForTesting(); 69 | #endif 70 | 71 | private: 72 | MemoryManager(); 73 | ~MemoryManager() = default; 74 | 75 | // Allocation tracking 76 | void TrackAllocation(size_t size); 77 | void TrackDeallocation(size_t size); 78 | void TrackReallocation(size_t old_size, size_t new_size); 79 | void FreeInternal(void *ptr, size_t override_size, bool override_valid); 80 | 81 | // Pool management 82 | bool IsValidPool(BML_MemoryPool pool) const; 83 | 84 | void *ReallocInternal(void *ptr, size_t new_size); 85 | 86 | // Statistics 87 | std::atomic m_TotalAllocated{0}; 88 | std::atomic m_PeakAllocated{0}; 89 | std::atomic m_AllocCount{0}; 90 | std::atomic m_FreeCount{0}; 91 | std::atomic m_ActiveAllocCount{0}; 92 | 93 | // Configuration 94 | std::atomic m_tracking_enabled{true}; 95 | static constexpr size_t kDefaultAlignment = alignof(std::max_align_t); 96 | static constexpr size_t kMinPoolBlockSize = 8; 97 | static constexpr size_t kMaxPoolBlockSize = 1024 * 1024; // 1 MB 98 | 99 | // Pool registry 100 | std::mutex m_PoolMutex; 101 | std::vector> m_Pools; 102 | }; 103 | } // namespace BML::Core 104 | 105 | #endif // BML_CORE_MEMORY_MANAGER_H 106 | -------------------------------------------------------------------------------- /src/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_CONFIG_INTERNAL_H 2 | #define BML_CONFIG_INTERNAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "BML/IConfig.h" 10 | #include "BML/IMod.h" 11 | 12 | class Config; 13 | 14 | class Property : public IProperty { 15 | friend class Config; 16 | friend class Category; 17 | 18 | public: 19 | Property() = default; 20 | Property(Config *config, std::string category, std::string key); 21 | ~Property() = default; 22 | 23 | const char *GetName() const { return m_Key.c_str(); } 24 | 25 | const char *GetString() override; 26 | size_t GetStringSize(); 27 | bool GetBoolean() override; 28 | int GetInteger() override; 29 | float GetFloat() override; 30 | CKKEYBOARD GetKey() override; 31 | 32 | void SetString(const char *value) override; 33 | void SetBoolean(bool value) override; 34 | void SetInteger(int value) override; 35 | void SetFloat(float value) override; 36 | void SetKey(CKKEYBOARD value) override; 37 | 38 | const char *GetComment() const { return m_Comment.c_str(); } 39 | void SetComment(const char *comment) override { m_Comment = comment ? comment : ""; } 40 | 41 | void SetDefaultString(const char *value) override; 42 | void SetDefaultBoolean(bool value) override; 43 | void SetDefaultInteger(int value) override; 44 | void SetDefaultFloat(float value) override; 45 | void SetDefaultKey(CKKEYBOARD value) override; 46 | 47 | PropertyType GetType() override { return m_Type; } 48 | size_t GetHash() const; 49 | 50 | void CopyValue(Property *o); 51 | 52 | bool *GetBooleanPtr(); 53 | int *GetIntegerPtr(); 54 | float *GetFloatPtr(); 55 | CKKEYBOARD *GetKeyPtr(); 56 | 57 | void SetModified(); 58 | 59 | private: 60 | std::variant m_Value = 0; 61 | PropertyType m_Type = INTEGER; 62 | size_t m_Hash = 0; 63 | std::string m_Comment; 64 | std::string m_Category; 65 | std::string m_Key; 66 | Config *m_Config = nullptr; 67 | }; 68 | 69 | class Category { 70 | friend class Config; 71 | 72 | public: 73 | Category() = default; 74 | Category(Config *config, std::string name); 75 | ~Category(); 76 | 77 | const char *GetName() const { return m_Name.c_str(); } 78 | 79 | const char *GetComment() const { return m_Comment.c_str(); } 80 | void SetComment(const char *comment) { m_Comment = comment ? comment : ""; } 81 | 82 | size_t GetPropertyCount() const { return m_Properties.size(); } 83 | Property *GetProperty(size_t i); 84 | Property *GetProperty(const char *key); 85 | 86 | bool HasKey(const char *key) const; 87 | 88 | private: 89 | std::string m_Name; 90 | std::string m_Comment; 91 | Config *m_Config = nullptr; 92 | 93 | std::vector m_Properties; 94 | std::unordered_map m_PropertyMap; 95 | }; 96 | 97 | class Config : public IConfig { 98 | friend class Property; 99 | 100 | public: 101 | explicit Config(IMod *mod); 102 | ~Config() override; 103 | 104 | IMod *GetMod() const { return m_Mod; } 105 | 106 | size_t GetCategoryCount() const { return m_Categories.size(); } 107 | Category *GetCategory(size_t i); 108 | Category *GetCategory(const char *name); 109 | 110 | bool HasCategory(const char *category) override; 111 | bool HasKey(const char *category, const char *key) override; 112 | 113 | IProperty *GetProperty(const char *category, const char *key) override; 114 | const char *GetCategoryComment(const char *category); 115 | void SetCategoryComment(const char *category, const char *comment) override; 116 | 117 | bool Load(const wchar_t *path); 118 | bool Save(const wchar_t *path); 119 | 120 | private: 121 | IMod *m_Mod; 122 | std::string m_ModID; 123 | 124 | std::vector m_Categories; 125 | std::unordered_map m_CategoryMap; 126 | }; 127 | 128 | #endif // BML_CONFIG_INTERNAL_H 129 | -------------------------------------------------------------------------------- /src/Gui/Button.cpp: -------------------------------------------------------------------------------- 1 | #include "BML/Gui/Button.h" 2 | 3 | using namespace BGui; 4 | 5 | CKMaterial *g_Up = nullptr; 6 | CKMaterial *g_Over = nullptr; 7 | CKMaterial *g_Inactive = nullptr; 8 | 9 | Button::Button(const char *name) : Label(name), m_Type(BUTTON_NORMAL) { 10 | m_2dEntity->UseSourceRect(); 11 | } 12 | 13 | ButtonType Button::GetType() { 14 | return m_Type; 15 | } 16 | 17 | void Button::SetType(ButtonType type) { 18 | m_Type = type; 19 | m_2dEntity->SetMaterial(g_Up); 20 | 21 | VxRect rect; 22 | switch (type) { 23 | case BUTTON_NORMAL: 24 | SetFont(ExecuteBB::GAMEFONT_01); 25 | SetSize(Vx2DVector(0.3f, 0.0938f)); 26 | rect = VxRect(0.0f, 0.51372f, 1.0f, 0.7451f); 27 | m_2dEntity->SetSourceRect(rect); 28 | break; 29 | case BUTTON_BACK: 30 | SetFont(ExecuteBB::GAMEFONT_01); 31 | SetSize(Vx2DVector(0.1875f, 0.0938f)); 32 | rect = VxRect(0.2392f, 0.75294f, 0.8666f, 0.98431f); 33 | m_2dEntity->SetSourceRect(rect); 34 | break; 35 | case BUTTON_SETTING: 36 | SetFont(ExecuteBB::GAMEFONT_01); 37 | SetSize(Vx2DVector(0.3f, 0.1f)); 38 | rect = VxRect(0.0f, 0.0f, 1.0f, 0.24706f); 39 | m_2dEntity->SetSourceRect(rect); 40 | break; 41 | case BUTTON_LEVEL: 42 | SetFont(ExecuteBB::GAMEFONT_03); 43 | SetSize(Vx2DVector(0.1938f, 0.05f)); 44 | rect = VxRect(0.0f, 0.247f, 0.647f, 0.36863f); 45 | m_2dEntity->SetSourceRect(rect); 46 | break; 47 | case BUTTON_KEY: 48 | SetFont(ExecuteBB::GAMEFONT_03); 49 | SetSize(Vx2DVector(0.3f, 0.0396f)); 50 | rect = VxRect(0.0f, 0.40785f, 1.0f, 0.51f); 51 | m_2dEntity->SetSourceRect(rect); 52 | break; 53 | case BUTTON_KEYSEL: 54 | SetFont(ExecuteBB::GAMEFONT_03); 55 | SetSize(Vx2DVector(0.1450f, 0.0396f)); 56 | rect = VxRect(0.005f, 0.3804f, 0.4353f, 0.4549f); 57 | m_2dEntity->SetSourceRect(rect); 58 | break; 59 | case BUTTON_SMALL: 60 | SetFont(ExecuteBB::GAMEFONT_03); 61 | SetSize(Vx2DVector(0.0700f, 0.0354f)); 62 | rect = VxRect(0.0f, 0.82353f, 0.226f, 0.9098f); 63 | m_2dEntity->SetSourceRect(rect); 64 | break; 65 | case BUTTON_LEFT: 66 | SetSize(Vx2DVector(0.0363f, 0.0517f)); 67 | rect = VxRect(0.6392f, 0.24706f, 0.78823f, 0.40392f); 68 | m_2dEntity->SetSourceRect(rect); 69 | break; 70 | case BUTTON_RIGHT: 71 | SetSize(Vx2DVector(0.0363f, 0.0517f)); 72 | rect = VxRect(0.7921f, 0.24706f, 0.9412f, 0.40392f); 73 | m_2dEntity->SetSourceRect(rect); 74 | break; 75 | case BUTTON_PLUS: 76 | SetSize(Vx2DVector(0.0200f, 0.0267f)); 77 | rect = VxRect(0.88627f, 0.8902f, 0.96863f, 0.97255f); 78 | m_2dEntity->SetSourceRect(rect); 79 | break; 80 | case BUTTON_MINUS: 81 | SetSize(Vx2DVector(0.0200f, 0.0267f)); 82 | rect = VxRect(0.88627f, 0.77804f, 0.96863f, 0.8594f); 83 | m_2dEntity->SetSourceRect(rect); 84 | break; 85 | } 86 | } 87 | 88 | bool Button::IsActive() { 89 | return m_Active; 90 | } 91 | 92 | void Button::SetActive(bool active) { 93 | m_Active = active; 94 | m_2dEntity->SetMaterial(active ? g_Up : g_Inactive); 95 | } 96 | 97 | void Button::InvokeCallback() { 98 | m_Callback(); 99 | } 100 | 101 | void Button::SetCallback(std::function callback) { 102 | m_Callback = std::move(callback); 103 | } 104 | 105 | void Button::OnMouseEnter() { 106 | if (m_Active || m_Type == BUTTON_SMALL) 107 | m_2dEntity->SetMaterial(g_Over); 108 | } 109 | 110 | void Button::OnMouseLeave() { 111 | if (m_Active || m_Type == BUTTON_SMALL) 112 | m_2dEntity->SetMaterial(m_Active ? g_Up : g_Inactive); 113 | } 114 | -------------------------------------------------------------------------------- /include/bml_export.h: -------------------------------------------------------------------------------- 1 | #ifndef BML_EXPORT_H 2 | #define BML_EXPORT_H 3 | 4 | #include "bml_types.h" 5 | #include "bml_errors.h" /* Includes diagnostics */ 6 | #include "bml_version.h" 7 | #include "bml_api_ids.h" 8 | 9 | BML_BEGIN_CDECLS 10 | 11 | #ifndef BML_API 12 | # ifdef _WIN32 13 | # ifdef BML_BUILD_DLL 14 | # define BML_API __declspec(dllexport) 15 | # else 16 | # define BML_API __declspec(dllimport) 17 | # endif 18 | # else 19 | # define BML_API __attribute__((visibility("default"))) 20 | # endif 21 | #endif 22 | 23 | /** 24 | * @brief 32-bit API identifier for fast lookup 25 | * 26 | * CRITICAL: IDs are explicitly assigned and PERMANENT (like syscall numbers). 27 | * Once assigned, an ID never changes across BML versions, ensuring binary compatibility. 28 | * 29 | * All API IDs are defined in bml_api_ids.h and must remain stable. 30 | * Using integer IDs instead of string lookups provides: 31 | * - ~3-5x faster lookup (direct integer map access) 32 | * - Better cache locality 33 | * - Zero allocation overhead 34 | * - Guaranteed stability across versions 35 | */ 36 | typedef uint32_t BML_ApiId; 37 | 38 | typedef void *(*PFN_BML_GetProcAddress)(const char *proc_name); 39 | typedef void *(*PFN_BML_GetProcAddressById)(BML_ApiId api_id); 40 | typedef int (*PFN_BML_GetApiId)(const char *proc_name, BML_ApiId *out_id); 41 | 42 | typedef enum BML_ModEntrypointCommand { 43 | BML_MOD_ENTRYPOINT_ATTACH = 1, 44 | BML_MOD_ENTRYPOINT_DETACH = 2 45 | } BML_ModEntrypointCommand; 46 | 47 | #define BML_MOD_ENTRYPOINT_API_VERSION 1u 48 | 49 | typedef struct BML_ModAttachArgs { 50 | uint32_t struct_size; 51 | uint32_t api_version; 52 | BML_Mod mod; 53 | PFN_BML_GetProcAddress get_proc; 54 | PFN_BML_GetProcAddressById get_proc_by_id; /* Fast path (v0.4.1+) */ 55 | PFN_BML_GetApiId get_api_id; /* Get ID from name (v0.4.1+) */ 56 | void *reserved; 57 | } BML_ModAttachArgs; 58 | 59 | typedef struct BML_ModDetachArgs { 60 | uint32_t struct_size; 61 | uint32_t api_version; 62 | BML_Mod mod; 63 | void *reserved; 64 | } BML_ModDetachArgs; 65 | 66 | typedef BML_Result (*PFN_BML_ModEntrypoint)(BML_ModEntrypointCommand command, void *command_args); 67 | 68 | /* Runtime lifecycle entry points (Core exports) */ 69 | BML_API BML_Result bmlAttach(void); /**< Initialize BML Core only */ 70 | BML_API BML_Result bmlDiscoverModules(void); /**< Scan and validate mods (Phase 1) */ 71 | BML_API BML_Result bmlLoadModules(void); /**< Load discovered mods (Phase 2) */ 72 | BML_API void bmlDetach(void); 73 | 74 | /* API lookup - String-based (legacy, compatible) */ 75 | BML_API void *bmlGetProcAddress(const char *proc_name); 76 | 77 | /* API lookup - ID-based (fast path, v0.4.1+) */ 78 | /** 79 | * @brief Get API pointer using pre-computed ID (fast path) 80 | * 81 | * Performance: ~3-5x faster than bmlGetProcAddress() 82 | * 83 | * @param api_id Pre-computed API ID (from compile-time macro or runtime query) 84 | * @return API function pointer, or NULL if not registered 85 | * 86 | * @code 87 | * // C: Runtime query 88 | * BML_ApiId id; 89 | * if (bmlGetApiId("bmlImcPublish", &id)) { 90 | * PFN_BML_ImcPublish publish = (PFN_BML_ImcPublish)bmlGetProcAddressById(id); 91 | * } 92 | * @endcode 93 | */ 94 | BML_API void *bmlGetProcAddressById(BML_ApiId api_id); 95 | 96 | /** 97 | * @brief Get API ID for registered API name 98 | * 99 | * @param proc_name API function name (e.g., "bmlImcPublish") 100 | * @param out_id Receives the 32-bit API ID 101 | * @return 1 if found, 0 if not registered 102 | */ 103 | BML_API int bmlGetApiId(const char *proc_name, BML_ApiId *out_id); 104 | 105 | BML_API const BML_BootstrapDiagnostics *bmlGetBootstrapDiagnostics(void); 106 | 107 | /* 108 | * Mod entry points (Implemented by modules - DO NOT import) 109 | * 110 | * When compiling a module, these should be defined with BML_MODULE_ENTRY macro. 111 | * Do NOT declare or use BML_API here as it would cause import/export conflicts. 112 | */ 113 | 114 | BML_END_CDECLS 115 | 116 | #endif /* BML_EXPORT_H */ 117 | --------------------------------------------------------------------------------