From 2dbcb98817736a738a21c31cb90c346ba04e10fb Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Mon, 16 Feb 2026 20:26:40 -0600 Subject: [PATCH] feat: Add Windows-only terrain SVG export functionality via FFI with configurable rendering options. --- arma/server/extension/src/terrain.rs | 40 ++++++---------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/arma/server/extension/src/terrain.rs b/arma/server/extension/src/terrain.rs index aad479b..675ee63 100644 --- a/arma/server/extension/src/terrain.rs +++ b/arma/server/extension/src/terrain.rs @@ -12,19 +12,11 @@ mod windows_ffi { #[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; } @@ -63,35 +55,20 @@ pub fn export_terrain_svg( 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() { - let loaded = LoadLibraryA(TERRAIN_DLL_NAME.as_ptr()); - if loaded.is_null() { - return Err(format!( - "Failed to load DLL: {}", - std::str::from_utf8_unchecked(&TERRAIN_DLL_NAME[..TERRAIN_DLL_NAME.len() - 1]) - )); - } - loaded - } else { - module - }; - - // Get the ExportSVG function address - let export_svg_proc = GetProcAddress(module, EXPORT_SVG_PROC_NAME.as_ptr()); - if export_svg_proc.is_null() { - return Err("Failed to find ExportSVG function in DLL".to_string()); + let module = GetModuleHandleA(std::ptr::null()); + if module.is_null() { + return Err("Failed to get game engine module handle".to_string()); } - // Convert to function pointer - let export_svg: FnExportSVG = std::mem::transmute(export_svg_proc); + let export_svg_proc = GetProcAddress(module, EXPORT_SVG_PROC_NAME.as_ptr()); + if export_svg_proc.is_null() { + return Err("Failed to find ExportSVG function in game engine".to_string()); + } - // Convert file path to C string + let export_svg: FnExportSVG = std::mem::transmute(export_svg_proc); let file_path_cstr = std::ffi::CString::new(file_path).map_err(|e| format!("Invalid file path: {}", e))?; - // Call the export function export_svg( file_path_cstr.as_ptr(), draw_location_names, @@ -164,7 +141,6 @@ struct ExportSvgOptions { /// ["terrain:exportSVG", [toJSON _options]] call forge_x_extension_fnc_extCall; /// ``` fn export_svg(options_json: String) -> String { - // Parse options from JSON let options: ExportSvgOptions = match serde_json::from_str(&options_json) { Ok(opts) => opts, Err(e) => {