Skip to main content

infinity_build_package/
lib.rs

1//! `fspackagetool.exe` wrapper. Windows-only, requires MSFS 2024
2//! installed locally — the package tool drives a partial sim instance
3//! to do the actual asset compilation.
4
5use anyhow::{Result, bail};
6use infinity_build_core::Runner;
7use serde::Deserialize;
8use std::{
9    path::{Path, PathBuf},
10    process::Command,
11};
12
13/// One MSFS project file fed to `fspackagetool.exe`.
14#[derive(Debug, Deserialize, Clone)]
15pub struct SimPackage {
16    pub name: String,
17    pub project_xml: String,
18
19    #[serde(default)]
20    pub output_dir: Option<String>,
21
22    #[serde(default)]
23    pub temp_dir: Option<String>,
24
25    #[serde(default)]
26    pub marketplace_dir: Option<String>,
27
28    #[serde(default)]
29    pub force_rebuild: bool,
30
31    #[serde(default)]
32    pub mirror_output: bool,
33
34    #[serde(default)]
35    pub prefer_steam: bool,
36}
37
38#[derive(Debug, Default, Clone)]
39pub struct PackageOverrides {
40    pub force_rebuild: bool,
41    pub mirror_output: bool,
42    pub prefer_steam: bool,
43    pub marketplace_dir: Option<String>,
44}
45
46pub fn locate_fspackagetool() -> Result<PathBuf> {
47    let sdk = infinity_build_sdk::sdk_path().map_err(|e| anyhow::anyhow!(e))?;
48    let candidate = PathBuf::from(&sdk)
49        .join("Tools")
50        .join("bin")
51        .join("fspackagetool.exe");
52    if !candidate.exists() {
53        bail!(
54            "fspackagetool.exe not found at {}.\n\
55             The cached SDK only contains the WASM/SimConnect subset.\n\
56             Install the full MSFS 2024 SDK from sdk.flightsimulator.com\n\
57             and set MSFS2024_SDK to its root.",
58            candidate.display()
59        );
60    }
61    Ok(candidate)
62}
63
64pub fn build_one(
65    runner: &dyn Runner,
66    root: &Path,
67    tool: &Path,
68    entry: &SimPackage,
69    overrides: &PackageOverrides,
70) -> Result<()> {
71    let project = root.join(&entry.project_xml);
72    if !project.exists() {
73        bail!("project XML not found at {}", project.display());
74    }
75
76    let mut cmd = Command::new(tool);
77    cmd.current_dir(root).arg(&project);
78
79    if let Some(out) = entry.output_dir.as_deref() {
80        cmd.arg("-outputdir").arg(root.join(out));
81    }
82    if let Some(temp) = entry.temp_dir.as_deref() {
83        cmd.arg("-tempdir").arg(root.join(temp));
84    }
85
86    let marketplace = overrides
87        .marketplace_dir
88        .as_deref()
89        .or(entry.marketplace_dir.as_deref());
90    if let Some(mp) = marketplace {
91        cmd.arg("-marketplace").arg(root.join(mp));
92    }
93
94    if entry.force_rebuild || overrides.force_rebuild {
95        cmd.arg("-rebuild");
96    }
97    if entry.mirror_output || overrides.mirror_output {
98        cmd.arg("-mirroring");
99    }
100    if entry.prefer_steam || overrides.prefer_steam {
101        cmd.arg("-forcesteam");
102    }
103
104    cmd.arg("-nopause");
105
106    runner
107        .run(&mut cmd, &format!("fspackagetool {}", entry.name))
108        .map_err(|e| anyhow::anyhow!(e.to_string()))
109}