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;
|
mod log;
|
||||||
pub mod org;
|
pub mod org;
|
||||||
pub mod redis;
|
pub mod redis;
|
||||||
|
pub mod terrain;
|
||||||
pub mod v_garage;
|
pub mod v_garage;
|
||||||
pub mod v_locker;
|
pub mod v_locker;
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ fn init() -> Extension {
|
|||||||
.group("icom", icom::group())
|
.group("icom", icom::group())
|
||||||
.group("locker", locker::group())
|
.group("locker", locker::group())
|
||||||
.group("org", org::group())
|
.group("org", org::group())
|
||||||
|
.group("terrain", terrain::group())
|
||||||
.group(
|
.group(
|
||||||
"owned",
|
"owned",
|
||||||
Group::new()
|
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