From 4d7e3520ebfe9cea63f2c293519caa0578ecaf0c Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Mon, 16 Feb 2026 19:42:03 -0600 Subject: [PATCH] feat: Introduce terrain SVG export functionality via FFI to a Windows DLL, providing an Arma 3 `exportSVG` command. --- arma/server/extension/src/terrain.rs | 74 +++++++++++++++++----------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/arma/server/extension/src/terrain.rs b/arma/server/extension/src/terrain.rs index 7c8eeaa..aad479b 100644 --- a/arma/server/extension/src/terrain.rs +++ b/arma/server/extension/src/terrain.rs @@ -4,29 +4,31 @@ //! rendering options (location names, grid, contour lines, etc.). use arma_rs::Group; -use std::ffi::CString; -use std::os::raw::{c_char, c_void}; #[cfg(target_os = "windows")] -#[link(name = "kernel32")] -unsafe extern "stdcall" { - fn GetModuleHandleA(lpModuleName: *const u8) -> *mut c_void; - fn LoadLibraryA(lpLibFileName: *const u8) -> *mut c_void; - fn GetProcAddress(hModule: *mut c_void, lpProcName: *const u8) -> *mut c_void; +mod windows_ffi { + use std::os::raw::{c_char, c_void}; + + #[link(name = "kernel32")] + unsafe extern "system" { + pub(super) fn GetModuleHandleA(lpModuleName: *const u8) -> *mut c_void; + pub(super) fn LoadLibraryA(lpLibFileName: *const u8) -> *mut c_void; + pub(super) fn GetProcAddress(hModule: *mut c_void, lpProcName: *const u8) -> *mut c_void; + } + + /// Mangled C++ name for the ExportSVG function. + /// Format: `?ExportSVG@@YAXPEBD_N11111@Z` matches signature: + /// `void ExportSVG(const char*, bool, bool, bool, bool, bool, bool)` + pub(super) const EXPORT_SVG_PROC_NAME: &[u8] = b"?ExportSVG@@YAXPEBD_N11111@Z\0"; + + /// Name of the DLL containing the ExportSVG function. + /// Update this to match your actual DLL name. + pub(super) const TERRAIN_DLL_NAME: &[u8] = b"TerrainExport.dll\0"; + + pub(super) type FnExportSVG = + extern "system" fn(*const c_char, bool, bool, bool, bool, bool, bool) -> *const c_void; } -/// Mangled C++ name for the ExportSVG function. -/// Format: `?ExportSVG@@YAXPEBD_N11111@Z` matches signature: -/// `void ExportSVG(const char*, bool, bool, bool, bool, bool, bool)` -const EXPORT_SVG_PROC_NAME: &[u8] = b"?ExportSVG@@YAXPEBD_N11111@Z\0"; - -/// Name of the DLL containing the ExportSVG function. -/// Update this to match your actual DLL name. -const TERRAIN_DLL_NAME: &[u8] = b"TerrainExport.dll\0"; - -type FnExportSVG = - extern "stdcall" fn(*const c_char, bool, bool, bool, bool, bool, bool) -> *const c_void; - /// Creates the Arma 3 command group for the terrain module. /// /// Registers the `exportSVG` command with the Arma 3 extension. @@ -59,6 +61,8 @@ pub fn export_terrain_svg( simple_roads: bool, ) -> Result<(), String> { unsafe { + use windows_ffi::*; + // Try to get already loaded module, or load the DLL let module = GetModuleHandleA(TERRAIN_DLL_NAME.as_ptr()); let module = if module.is_null() { @@ -85,7 +89,7 @@ pub fn export_terrain_svg( // Convert file path to C string let file_path_cstr = - CString::new(file_path).map_err(|e| format!("Invalid file path: {}", e))?; + std::ffi::CString::new(file_path).map_err(|e| format!("Invalid file path: {}", e))?; // Call the export function export_svg( @@ -137,15 +141,27 @@ struct ExportSvgOptions { /// /// # SQF Usage /// ```sqf -/// ["forge", ["terrain", "exportSVG", "{ -/// ""filePath"": ""C:\\path\\to\\output.svg"", -/// ""drawLocationNames"": true, -/// ""drawGrid"": true, -/// ""drawContourlines"": true, -/// ""drawTreeObjects"": false, -/// ""drawMountainHeightpoints"": true, -/// ""simpleRoads"": false -/// }"]] call forge_fnc_callExtension; +/// // Register callback handler (optional, for async result) +/// ["terrain:exportSVG", { +/// params ["_response"]; +/// systemChat format ["Export %1: %2", +/// _response get "status", +/// _response get "message" +/// ]; +/// }] call forge_x_extension_fnc_setHandler; +/// +/// // Create options and call extension +/// private _options = createHashMapFromArray [ +/// ["filePath", "C:\terrain.svg"], +/// ["drawLocationNames", true], +/// ["drawGrid", true], +/// ["drawContourlines", true], +/// ["drawTreeObjects", false], +/// ["drawMountainHeightpoints", true], +/// ["simpleRoads", false] +/// ]; +/// +/// ["terrain:exportSVG", [toJSON _options]] call forge_x_extension_fnc_extCall; /// ``` fn export_svg(options_json: String) -> String { // Parse options from JSON