// Argument parsing + path resolution. Paths resolve relative to --root (default: // the exe's own directory, i.e. the deployed game dir), with dev fallbacks so // the same exe runs against the emulator/ source tree via `--root `. using System; using System.Collections.Generic; using System.IO; namespace VwePod { internal enum LayoutMode { Cockpit, Explode } internal sealed class Options { public Mode Mode; public LayoutMode Layout = LayoutMode.Cockpit; public string Root; public string Work; public string DosBox; public string DosBoxDir; public string Conf; public string BridgeScript; // dev: live_bridge.py via pyw public string RendererExe; // deploy: frozen renderer exe (else null) public string AweRom; public bool NoBridge; public bool NoSound; public bool Mipmap; public bool NoFocus; public bool DryRun; public string BridgePos = "0,0"; // main out-the-window head // 832x512 = the dPL3 board's native framebuffer res (the game's view // flush / pick coords are 832x512). Render 1:1 until the 800x600 // presentation question is settled (see LAUNCH.md). public int BridgeW = 832; public int BridgeH = 512; public int DosBoxX = 10; public int DosBoxY = 10; // Cockpit head rects (deployment variant of pod_deploy.ps1's RECTS). public string RadarRect = "800,0,640,480"; // VPX_WIN0 radar (HEAD C) public string Mfd3Rect = "1440,0,640,480"; // VPX_WIN4 3-color MFD (HEAD B) public string Mfd2Rect = "2080,0,640,480"; // VPX_WIN3 2-color MFD (HEAD A) public static Options Parse(string[] args) { var o = new Options(); string modeName = null, confArg = null, rootArg = null, dosboxArg = null, bridgeScriptArg = null, rendererArg = null, aweArg = null; for (int i = 0; i < args.Length; i++) { string a = args[i]; string Next(string name) { if (i + 1 >= args.Length) throw new ArgumentException(name + " needs a value"); return args[++i]; } if (!a.StartsWith("-")) { modeName = a; continue; } // positional mode switch (a) { case "--mode": modeName = Next(a); break; case "--conf": confArg = Next(a); break; case "--root": rootArg = Next(a); break; case "--work": o.Work = Next(a); break; case "--dosbox": dosboxArg = Next(a); break; case "--bridge-script": bridgeScriptArg = Next(a); break; case "--renderer": rendererArg = Next(a); break; case "--awe-rom": aweArg = Next(a); break; case "--layout": o.Layout = Next(a).Equals("explode", StringComparison.OrdinalIgnoreCase) ? LayoutMode.Explode : LayoutMode.Cockpit; break; case "--bridge-pos": o.BridgePos = Next(a); break; case "--bridge-size": ParseWH(Next(a), out o.BridgeW, out o.BridgeH); break; case "--dosbox-xy": ParseWH(Next(a), out o.DosBoxX, out o.DosBoxY); break; case "--no-bridge": o.NoBridge = true; break; case "--no-sound": o.NoSound = true; break; case "--mipmap": o.Mipmap = true; break; case "--no-focus": o.NoFocus = true; break; case "--dry-run": o.DryRun = true; break; case "-h": case "--help": Usage(); Environment.Exit(0); break; default: throw new ArgumentException("unknown arg: " + a); } } o.Root = rootArg ?? AppContext.BaseDirectory; o.Mode = Modes.Find(modeName ?? "bt"); if (o.Mode == null) throw new ArgumentException("unknown mode '" + modeName + "'; valid: " + Modes.Names()); // Unsupported modes: return with only the Mode set; Program prints the // reason and exits without touching the launch env. if (!o.Mode.Supported) return o; o.DosBox = dosboxArg ?? FirstExisting( Path.Combine(o.Root, "dosbox-x.exe"), Path.Combine(o.Root, "src", "src", "dosbox-x.exe")) ?? throw new ArgumentException("dosbox-x.exe not found under root; pass --dosbox"); o.DosBoxDir = Path.GetDirectoryName(o.DosBox); o.Conf = confArg ?? FirstExisting(Path.Combine(o.Root, o.Mode.ConfBase + ".conf")); if (o.Conf == null) { // A .tmpl sitting there means the install was extracted but never // configured -- point at the real fix, not at --conf. bool unrendered = File.Exists(Path.Combine(o.Root, o.Mode.ConfBase + ".conf.tmpl")); throw new ArgumentException(unrendered ? o.Mode.ConfBase + ".conf not rendered yet: this install hasn't been configured.\n" + "Run postinstall.bat (elevated) from the extracted zip root, or:\n" + " powershell -File \\deploy\\configure.ps1 -Root " : o.Mode.ConfBase + ".conf not found under root; pass --conf"); } o.RendererExe = rendererArg ?? FirstExisting( Path.Combine(o.Root, "renderer.exe"), Path.Combine(o.Root, "render-bridge", "renderer.exe")); o.BridgeScript = bridgeScriptArg ?? FirstExisting( Path.Combine(o.Root, "live_bridge.py"), Path.Combine(o.Root, "render-bridge", "live_bridge.py")); if (!o.NoBridge && o.RendererExe == null && o.BridgeScript == null) throw new ArgumentException("no renderer found; pass --renderer, --bridge-script, or --no-bridge"); o.AweRom = aweArg ?? FirstExisting( Path.Combine(o.Root, "roms", "awe32.raw"), Path.Combine(o.Root, "..", "roms", "awe32.raw")); o.Work = o.Work ?? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Temp", "vwe-pod"); return o; } private static void ParseWH(string s, out int a, out int b) { var parts = s.Split(','); if (parts.Length != 2 || !int.TryParse(parts[0], out a) || !int.TryParse(parts[1], out b)) throw new ArgumentException("expected 'x,y': " + s); } private static string FirstExisting(params string[] paths) { foreach (var p in paths) { if (p == null) continue; var full = Path.GetFullPath(p); if (File.Exists(full)) return full; } return null; } public static void Usage() { Console.Error.WriteLine(@"pod-launch [mode] [options] Supervising entry-point: launches DOSBox-X + the render bridge into a job object and blocks; killing this process kills them too (no orphans). mode bt | rp (default bt). bt/rp also serve the camera ship + live mission-review roles -- the console picks the role via the egg hostType, so no separate mode. Also recognized (not yet wired): review, test-plasma, reset-rio, audio-test. --mode same as the positional mode --conf explicit conf (overrides the mode's conf) --root base dir for dosbox/conf/bridge (default: exe dir) --work runtime/work dir (default: %LOCALAPPDATA%\Temp\vwe-pod) --layout cockpit|explode window layout (default cockpit) --dosbox explicit dosbox-x.exe --renderer frozen renderer exe (else falls back to pyw live_bridge) --bridge-script live_bridge.py (dev) --awe-rom AWE32 SoundFont ROM --bridge-pos x,y main render window position (default 0,0) --bridge-size w,h main render window size (default 832,512 = dPL3-native) --dosbox-xy x,y DOSBox window position + focus (default 10,10) --no-bridge pod only, no GL render window --no-sound skip the ~4-min SoundFont upload. WARNING: no AWE32 = no SOS ticks; bt/rp FREEZE at RIO init on the default FAST-clock confs (only safe with a SLOW-clock conf) --mipmap VRVIEW_MIPMAP=1 (RP floor shimmer fix; non-authentic) --no-focus skip the window layout/focus pass --dry-run resolve paths + print the launch plan, then exit"); } } }