feat: Implement terrain SVG export functionality via FFI and integrate it into the Arma server extension's command groups.
This commit is contained in:
parent
abde6649ac
commit
082f0c6f8f
@ -21,6 +21,7 @@ pub mod locker;
|
||||
mod log;
|
||||
pub mod org;
|
||||
pub mod redis;
|
||||
pub mod terrain;
|
||||
pub mod v_garage;
|
||||
pub mod v_locker;
|
||||
|
||||
@ -66,6 +67,7 @@ fn init() -> Extension {
|
||||
.group("icom", icom::group())
|
||||
.group("locker", locker::group())
|
||||
.group("org", org::group())
|
||||
.group("terrain", terrain::group())
|
||||
.group(
|
||||
"owned",
|
||||
Group::new()
|
||||
|
||||
183
arma/server/extension/src/terrain.rs
Normal file
183
arma/server/extension/src/terrain.rs
Normal file
@ -0,0 +1,183 @@
|
||||
//! Terrain SVG export functionality via FFI to external C++ library.
|
||||
//!
|
||||
//! Provides commands to export terrain data to SVG format with various
|
||||
//! 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;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn group() -> Group {
|
||||
Group::new().command("exportSVG", export_svg)
|
||||
}
|
||||
|
||||
/// Exports terrain data to an SVG file with configurable rendering options.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `file_path`: Output SVG file path
|
||||
/// - `draw_location_names`: Include location/place names
|
||||
/// - `draw_grid`: Include grid overlay
|
||||
/// - `draw_contourlines`: Include elevation contour lines
|
||||
/// - `draw_tree_objects`: Include vegetation/tree objects
|
||||
/// - `draw_mountain_heightpoints`: Include mountain peak elevation markers
|
||||
/// - `simple_roads`: Use simplified road rendering
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(())` on success
|
||||
/// - `Err(String)` with error message on failure
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn export_terrain_svg(
|
||||
file_path: String,
|
||||
draw_location_names: bool,
|
||||
draw_grid: bool,
|
||||
draw_contourlines: bool,
|
||||
draw_tree_objects: bool,
|
||||
draw_mountain_heightpoints: bool,
|
||||
simple_roads: bool,
|
||||
) -> Result<(), String> {
|
||||
unsafe {
|
||||
// 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());
|
||||
}
|
||||
|
||||
// Convert to function pointer
|
||||
let export_svg: FnExportSVG = std::mem::transmute(export_svg_proc);
|
||||
|
||||
// Convert file path to C string
|
||||
let file_path_cstr =
|
||||
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,
|
||||
draw_grid,
|
||||
draw_contourlines,
|
||||
draw_tree_objects,
|
||||
draw_mountain_heightpoints,
|
||||
simple_roads,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn export_terrain_svg(
|
||||
_file_path: String,
|
||||
_draw_location_names: bool,
|
||||
_draw_grid: bool,
|
||||
_draw_contourlines: bool,
|
||||
_draw_tree_objects: bool,
|
||||
_draw_mountain_heightpoints: bool,
|
||||
_simple_roads: bool,
|
||||
) -> Result<(), String> {
|
||||
Err("Terrain SVG export is only available on Windows".to_string())
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ExportSvgOptions {
|
||||
file_path: String,
|
||||
#[serde(default)]
|
||||
draw_location_names: bool,
|
||||
#[serde(default)]
|
||||
draw_grid: bool,
|
||||
#[serde(default)]
|
||||
draw_contourlines: bool,
|
||||
#[serde(default)]
|
||||
draw_tree_objects: bool,
|
||||
#[serde(default)]
|
||||
draw_mountain_heightpoints: bool,
|
||||
#[serde(default)]
|
||||
simple_roads: bool,
|
||||
}
|
||||
|
||||
/// Arma command handler for terrain SVG export.
|
||||
///
|
||||
/// # 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;
|
||||
/// ```
|
||||
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) => {
|
||||
return serde_json::json!({
|
||||
"status": "error",
|
||||
"message": format!("Invalid JSON options: {}", e)
|
||||
})
|
||||
.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
match export_terrain_svg(
|
||||
options.file_path,
|
||||
options.draw_location_names,
|
||||
options.draw_grid,
|
||||
options.draw_contourlines,
|
||||
options.draw_tree_objects,
|
||||
options.draw_mountain_heightpoints,
|
||||
options.simple_roads,
|
||||
) {
|
||||
Ok(_) => serde_json::json!({
|
||||
"status": "success",
|
||||
"message": "Terrain exported successfully"
|
||||
})
|
||||
.to_string(),
|
||||
Err(e) => serde_json::json!({
|
||||
"status": "error",
|
||||
"message": e
|
||||
})
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user