Files
CydandClaude Fable 5 80ee1d26ea Console: move DOSBox IP shift from game pages into game defaults
The per-page "DOSBox IPs (+100)" checkbox becomes a single machine-level
setting per game, edited in Settings > Change <game> Defaults (a "DOSBox
Build" row below Autotranslocate Delay) and persisted in the game's
defaults file (RPDefaults.rpd / BTDefaults.btd) as a top-level
DosBoxAddressShift element.

The game pages no longer carry the checkbox; each RPGame/BTGame reads the
stored default once at construction into a readonly field and applies it
when enabling a pod (MungaGame.DosBoxAddressShift) and when building the
mission egg (MissionAddress). Since it is read at construction, an
already-open game page keeps its shift until reopened -- consistent with
how every other game default behaves.

Verified against vPOD: enabling the DOSBox default drives a freshly
opened game page's pod connection to 127.0.0.101 and the egg to
pilot=127.0.0.101 with no per-page control; unchecking round-trips to
False in the defaults file. All 103 diff tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:33:44 -05:00

434 lines
8.8 KiB
C#

using System;
using System.IO;
using System.Windows.Forms;
using System.Xml;
namespace TeslaConsole.RedPlanet;
internal static class RPDefaults
{
public abstract class ScenarioDefaults
{
private bool mScoreCompression;
private TimeSpan mMissionLength = new TimeSpan(0, 10, 0);
private string mMapKey;
private string mVehicleKey;
private string mTimeOfDayKey;
private string mWeatherKey;
private int mLaunchDelay = 15;
public bool ScoreCompression
{
get
{
return mScoreCompression;
}
set
{
mScoreCompression = value;
}
}
public TimeSpan MissionLength
{
get
{
return mMissionLength;
}
set
{
mMissionLength = value;
}
}
public string MapKey
{
get
{
return mMapKey;
}
set
{
mMapKey = value;
}
}
public string VehicleKey
{
get
{
return mVehicleKey;
}
set
{
mVehicleKey = value;
}
}
public string TimeOfDayKey
{
get
{
return mTimeOfDayKey;
}
set
{
mTimeOfDayKey = value;
}
}
public string WeatherKey
{
get
{
return mWeatherKey;
}
set
{
mWeatherKey = value;
}
}
public int LaunchDelay
{
get
{
return mLaunchDelay;
}
set
{
mLaunchDelay = value;
}
}
public ScenarioDefaults()
{
}
public abstract void Save(XmlNode parrentNode);
protected void SaveCommonData(XmlNode scenarioNode)
{
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("ScoreCompression")).InnerText = mScoreCompression.ToString();
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("MissionLength")).InnerText = mMissionLength.ToString();
if (!string.IsNullOrEmpty(mMapKey))
{
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("MapKey")).InnerText = mMapKey;
}
if (!string.IsNullOrEmpty(mVehicleKey))
{
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("VehicleKey")).InnerText = mVehicleKey;
}
if (!string.IsNullOrEmpty(mTimeOfDayKey))
{
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("TimeOfDayKey")).InnerText = mTimeOfDayKey;
}
if (!string.IsNullOrEmpty(mWeatherKey))
{
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("WeatherKey")).InnerText = mWeatherKey;
}
scenarioNode.AppendChild(scenarioNode.OwnerDocument.CreateElement("LaunchDelay")).InnerText = mLaunchDelay.ToString();
}
public abstract void Parse(XmlNode scenarioNode);
protected bool ParseCommonData(XmlNode node)
{
switch (node.Name)
{
case "ScoreCompression":
{
if (bool.TryParse(node.InnerText, out var result2))
{
mScoreCompression = result2;
}
return true;
}
case "MissionLength":
{
if (TimeSpan.TryParse(node.InnerText, out var result3))
{
mMissionLength = result3;
}
return true;
}
case "MapKey":
if (!string.IsNullOrEmpty(node.InnerText))
{
mMapKey = node.InnerText;
}
return true;
case "VehicleKey":
if (!string.IsNullOrEmpty(node.InnerText))
{
mVehicleKey = node.InnerText;
}
return true;
case "TimeOfDayKey":
if (!string.IsNullOrEmpty(node.InnerText))
{
mTimeOfDayKey = node.InnerText;
}
return true;
case "WeatherKey":
if (!string.IsNullOrEmpty(node.InnerText))
{
mWeatherKey = node.InnerText;
}
return true;
case "LaunchDelay":
{
if (int.TryParse(node.InnerText, out var result))
{
mLaunchDelay = result;
}
return true;
}
default:
return false;
}
}
}
public class DeathRaceDefaults : ScenarioDefaults
{
public const string DeathRaceNode = "DeathRaceDefaults";
private string mColorKey;
private string mBadgeKey;
public string ColorKey
{
get
{
return mColorKey;
}
set
{
mColorKey = value;
}
}
public string BadgeKey
{
get
{
return mBadgeKey;
}
set
{
mBadgeKey = value;
}
}
public override void Save(XmlNode parrentNode)
{
XmlNode xmlNode = parrentNode.AppendChild(parrentNode.OwnerDocument.CreateElement("DeathRaceDefaults"));
SaveCommonData(xmlNode);
if (!string.IsNullOrEmpty(mColorKey))
{
xmlNode.AppendChild(xmlNode.OwnerDocument.CreateElement("ColorKey")).InnerText = mColorKey;
}
if (!string.IsNullOrEmpty(mBadgeKey))
{
xmlNode.AppendChild(xmlNode.OwnerDocument.CreateElement("BadgeKey")).InnerText = mBadgeKey;
}
}
public override void Parse(XmlNode scenarioNode)
{
foreach (XmlNode childNode in scenarioNode.ChildNodes)
{
if (ParseCommonData(childNode))
{
continue;
}
switch (childNode.Name)
{
case "ColorKey":
if (!string.IsNullOrEmpty(childNode.InnerText))
{
mColorKey = childNode.InnerText;
}
break;
case "BadgeKey":
if (!string.IsNullOrEmpty(childNode.InnerText))
{
mBadgeKey = childNode.InnerText;
}
break;
}
}
}
}
public class FootballDefaults : ScenarioDefaults
{
public const string FootballNode = "FootballDefaults";
private string mTeamKey;
private string mPositionKey;
public string TeamKey
{
get
{
return mTeamKey;
}
set
{
mTeamKey = value;
}
}
public string PositionKey
{
get
{
return mPositionKey;
}
set
{
mPositionKey = value;
}
}
public override void Save(XmlNode parrentNode)
{
XmlNode xmlNode = parrentNode.AppendChild(parrentNode.OwnerDocument.CreateElement("FootballDefaults"));
SaveCommonData(xmlNode);
if (!string.IsNullOrEmpty(mTeamKey))
{
xmlNode.AppendChild(xmlNode.OwnerDocument.CreateElement("TeamKey")).InnerText = mTeamKey;
}
if (!string.IsNullOrEmpty(mPositionKey))
{
xmlNode.AppendChild(xmlNode.OwnerDocument.CreateElement("PositionKey")).InnerText = mPositionKey;
}
}
public override void Parse(XmlNode scenarioNode)
{
foreach (XmlNode childNode in scenarioNode.ChildNodes)
{
if (ParseCommonData(childNode))
{
continue;
}
switch (childNode.Name)
{
case "TeamKey":
if (!string.IsNullOrEmpty(childNode.InnerText))
{
mTeamKey = childNode.InnerText;
}
break;
case "PositionKey":
if (!string.IsNullOrEmpty(childNode.InnerText))
{
mPositionKey = childNode.InnerText;
}
break;
}
}
}
}
private static readonly string sDefaultsFilePath;
private static readonly DeathRaceDefaults sDeathRace;
private static readonly FootballDefaults sFootball;
public static DeathRaceDefaults DeathRace => sDeathRace;
public static FootballDefaults Football => sFootball;
/// <summary>
/// When set, the Red Planet game pages target each pod's DOSBox-X guest
/// (last octet +100) instead of the pod itself. A machine-level setting for
/// the emulator preservation pods, so it is stored once per defaults file
/// rather than per scenario.
/// </summary>
public static bool DosBoxAddressShift { get; set; }
static RPDefaults()
{
sDefaultsFilePath = Path.Combine(Program.GetCommonAppDataDirectory(), "RPDefaults.rpd");
sDeathRace = new DeathRaceDefaults();
sFootball = new FootballDefaults();
if (File.Exists(sDefaultsFilePath))
{
try
{
Import(sDefaultsFilePath);
}
catch (Exception)
{
MessageBox.Show("The Red Planet defaults file appears to be corrupt. Please open the Red Planet Defaults Dialog and save to create a new defaults file.", "Error Loading Red Planet Defaults!", MessageBoxButtons.OK);
}
}
}
public static void Import(string fileName)
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
foreach (XmlNode childNode in xmlDocument.DocumentElement.ChildNodes)
{
switch (childNode.Name)
{
case "DeathRaceDefaults":
sDeathRace.Parse(childNode);
break;
case "FootballDefaults":
sFootball.Parse(childNode);
break;
case "DosBoxAddressShift":
{
if (bool.TryParse(childNode.InnerText, out var result))
{
DosBoxAddressShift = result;
}
break;
}
}
}
}
public static void Save()
{
try
{
Export(sDefaultsFilePath);
}
catch (Exception)
{
MessageBox.Show("The Red Planet defaults file could not be saved. These defaults will only be remembered until the application is closed.", "Error Saving Red Planet Defaults!", MessageBoxButtons.OK);
}
}
public static void Export(string filePath)
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.AppendChild(xmlDocument.CreateElement("RPDefaults"));
sDeathRace.Save(xmlDocument.DocumentElement);
sFootball.Save(xmlDocument.DocumentElement);
xmlDocument.DocumentElement.AppendChild(xmlDocument.CreateElement("DosBoxAddressShift")).InnerText = DosBoxAddressShift.ToString();
string directoryName = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
xmlDocument.Save(filePath);
}
}