Skip to content

Contributing

Cerppo edited this page Jul 13, 2026 · 2 revisions

Contributing Guidelines

Thank you for your interest in contributing to this C# project! We welcome all contributions, whether it's bug reports, feature requests, code improvements, or documentation updates.


How to Contribute

Prerequisites

  • Git
  • Visual Studio
  • .NET 8 SDK
  • DCS-BIOS installed in your DCS World Saved Games folder (see Home for details)

Recommended Tools

Bort or any other DCS-BIOS reference tool to help you find the right addresses and values. This is where you get identifiers like CDU_BRT that you pass to RegisterUInt / RegisterStr / RegisterLight:

RegisterUInt("CDU_BRT", v => { /* handle v */ });
RegisterStr("UFC_LINE1", s => { /* handle s */ });

1. Fork & Clone

  • Fork this repository.
  • Clone your fork locally:
    git clone --recurse-submodules <your-forked-repo-url>

2. Create a Branch

If you use Visual Studio, select the McduDcsBiosBridge repository

  • Create a new branch for your feature or bug fix:
    git checkout -b my-feature-branch

Branch naming examples:

  • fix-issue-123
  • add-new-feature
  • feat(aircraft)/add-new-feature
  • fix/issue-123
  • docs/update-readme

3. Make Changes

  • Follow the existing coding style and conventions.
  • Target .NET 8.
  • Naming convention for private fields: prefix private DCSBIOSOutput? fields with _ (e.g. _CDU_BRT, _IAS).

Adding a New Aircraft

Aircraft are driven by a single registry and detected automatically from the DCS-BIOS MetadataStart/_ACFT_NAME value — there is no CDU menu to edit anymore.

1. Find the module id and name

Look up the aircraft's module number in dcs-bios_modules.txt (for example: F-14 = 16) and its _ACFT_NAME value (use Bort, or run the program and start the aircraft in DCS — the wait screen shows the name as "unsupported").

2. Create a listener

Add a class in Aircrafts/ deriving from AircraftListener. Implement two methods: wire the CDU and wire the front panels. Use RegisterUInt / RegisterStr to resolve and register in one call — no field declarations needed for single-use outputs. (See the existing Aircrafts/*_Listener.cs files for real examples.)

using WwDevicesDotNet;

namespace WCtrlDcsBiosBridge.Aircrafts;

internal class F14_Listener : AircraftListener
{
    // Only declare a field when the same output is used in MORE than one method.
    private DCSBIOSOutput? _CONSOLE_BRT;

    public F14_Listener(UserOptions options)
        : base(AircraftRegistry.F14, options)
    {
    }

    // Only override InitializeDcsBiosOutputs when you need array loops or
    // multi-use fields. Leave it out entirely for simple listeners.
    protected override void InitializeDcsBiosOutputs()
    {
        _CONSOLE_BRT = ResolveUInt("CONSOLE_BRT");
    }

    // Wire CDU outputs. Runs only when a CDU is connected.
    protected override void RegisterCduControls()
    {
        RegisterStr("PLT_CAP_DISPLAY", s =>
            GetCompositor(DEFAULT_PAGE).Line(0).Green().WriteLine(s));

        RegisterUInt("MASTER_CAUTION", v => SetCduLeds(fail: v == 1));

        RegisterLight(_CONSOLE_BRT, v =>
        {
            int percent = (int)(v * 100 / _CONSOLE_BRT!.MaxValue);
            SetBacklightBrightnessPercent(percent);
            SetDisplayBrightnessPercent(percent);
            SetLedBrightnessPercent(percent);
        });
    }

    // Wire frontpanel outputs (FCU/EFIS, PAP3, AGP32...). Always runs, even
    // with no CDU, because a frontpanel-only setup still needs the data.
    protected override void RegisterFrontpanelControls()
    {
        // Leave empty if the aircraft has no frontpanel data, or populate
        // FlightDeck, e.g.:
        // RegisterUInt("IAS_US_INT", v => FlightDeck.Speed = (int)v);
    }
}

3. Register one descriptor

Add an AircraftDescriptor in Aircrafts/AircraftRegistry.cs and include it in the All list (registry order = menu order). Provide the module id, display name, JSON and font files, the HasSeatSelection flag, the DCS-BIOS name(s) for auto-detection, and the listener factory.

public static readonly AircraftDescriptor F14 = new(
    16,                                   // module id (dcs-bios_modules.txt)
    "F-14",                               // display name
    "F-14.json",                          // DCS-BIOS json file for this module
    "resources/a10c-font-21x31.json",     // a font from resources/
    false,                                // HasSeatSelection (true only for CH-47F today)
    new[] { "F-14B", "F-14A-135-GR" },    // _ACFT_NAME value(s) for auto-detection
    c => new F14_Listener(c.Options));    // listener factory

// ...then add it to the All list:
public static readonly IReadOnlyList<AircraftDescriptor> All = new[]
{
    A10C, AH64D, FA18C, CH47, F15E, M2000C, F16C, OH58D, F14,
};

Dual-seat aircraft: set HasSeatSelection: true and give the listener an isPilot parameter, like the CH-47F: c => new CH47F_Listener(c.Options, c.IsPilot, c.Ch47SwitchWithSeat).

Font: start by reusing an existing resource — resources/a10c-font-21x31.json or resources/ah64d-font-21x31.json. A font file maps a fixed character set to bitmap glyphs; if every character you render already looks correct, there is no point duplicating a font just to give it a new name. Create a dedicated font only when you need to design a special bitmap for a specific character code.

4. (Optional) Add aircraft-specific options

Only if your aircraft needs user settings (page-switch keys, toggles). You only touch two files — the options panel wires itself up from these.

a. An options class in Config/UserOptions.cs: add one property to UserOptions plus a small class.

// in UserOptions:
public F15EOptions F15E { get; set; } = new();

public class F15EOptions
{
    public string ShowXxxKey { get; set; } = "NextPage";
}

b. One UI section in UI/OptionsPanel.xaml: a HeaderedContentControl with the shared AircraftSection style. The Header must equal the descriptor's DisplayName — that alone gives you the title and the automatic "disabled while this aircraft is running" behaviour.

<HeaderedContentControl Style="{StaticResource AircraftSection}" Header="F-15E">
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0,4">
            <TextBlock Text="Show Xxx Key:" VerticalAlignment="Center" Width="120"/>
            <ComboBox ItemsSource="{x:Static ui:OptionsPanel.McduKeyNames}"
                      SelectedValue="{Binding F15E.ShowXxxKey, Mode=TwoWay}"
                      SelectionChanged="ComboBox_Changed" Width="150"/>
        </StackPanel>
    </StackPanel>
</HeaderedContentControl>

Adding or Fixing a Translation

UI strings live in WinUI .resw resource files, one folder per language, under Strings/:

Strings/en-us/Resources.resw   (English — source of truth for keys)
Strings/fr-fr/Resources.resw   (French)
Strings/de-de/Resources.resw   (German)
Strings/es-es/Resources.resw   (Spanish)

Every <data name="KeyName"> entry has the same name across all languages; only the <value> text differs. Strings are looked up at runtime through Common/Strings.cs (Strings.Get("KeyName")), not standard XAML x:Uid — see the comment at the top of that file for why.

Fix an existing translation

  1. Open the Resources.resw for the language you want to fix (e.g. Strings/fr-fr/Resources.resw).
  2. Find the <data name="..."> entry and edit its <value> text. Leave the name attribute untouched — it's the lookup key shared across all languages.
  3. Run the app and use the language toggle button (top-right, cycles System → your Windows language → the other supported languages) to switch to the language you edited, then check the affected screen.

Add a new language

  1. New resource file: copy Strings/en-us/Resources.resw to Strings/<locale>/Resources.resw (e.g. Strings/it-it/) and translate every <value>, keeping all name keys identical to the English file.
  2. Enum: add a member to LanguagePreference in Config/LanguagePreference.cs.
  3. Detection: add it to LanguageDetector.SupportedLanguages and to the two-letter mapping in LanguageDetector.MatchLanguage in Common/LanguageDetector.cs.
  4. Resource context: add the enum → locale-tag mapping (e.g. "it-IT") in the switch inside Strings.BuildContext() in Common/Strings.cs.
  5. Toggle UI: add the short code (e.g. "IT") and tooltip case in MainWindow.xaml.cs (UpdateLanguageToggleIcon, near the existing LanguagePreference.French => "FR" cases). The tooltip needs a matching LanguageTooltip_<Language> key added to every Resources.resw, including the new one.

No csproj changes are needed — .resw files under Strings/ are picked up automatically by the build.


Helpers available from AircraftListener

Helper Use
RegisterUInt(id, v => ...) Resolve + register an integer output in one call.
RegisterUInt(id, (ctrl, v) => ...) Same, but passes the resolved DCSBIOSOutput as ctrl so you can read ctrl.MaxValue inside the handler.
RegisterStr(id, s => ...) Resolve + register a string output in one call.
ResolveUInt(id) Resolve an integer output for later use (multi-use fields or array loops).
ResolveStr(id) Resolve a string output for later use.
RegisterUInt(output, v => ...) Register a pre-resolved integer output.
RegisterStr(output, s => ...) Register a pre-resolved string output.
RegisterLight(id, v => ...) Like RegisterUInt, but skipped automatically when the user has Disable Lighting Management turned on. Use for brightness knobs and cockpit light controls.
RegisterLight(id, (ctrl, v) => ...) Same, but passes the resolved DCSBIOSOutput as ctrl.
RegisterLight(output, v => ...) Same, for a pre-resolved output.
RegisterRaw(address, v => ...) Low-level handler for raw bitfield registers when named DCS-BIOS outputs are unavailable or have incorrect mask/shift definitions. v is the raw unmasked 16-bit register value; apply bitmasks manually.
GetCompositor(DEFAULT_PAGE).Line(n)... Write a CDU line (.Green(), .White(), .WriteLine(...), ...).
SetCduLeds(fail:, fm1:, fm2:, fm:, ind:, rdy:) Set CDU status LEDs.
SetDisplayBrightnessPercent / SetBacklightBrightnessPercent / SetLedBrightnessPercent CDU brightness (0–100).
FlightDeck Semantic front-panel state — see table below.
HasCdu, CduDevice Whether a CDU is connected / the underlying device.

FlightDeck properties

Populate these in RegisterFrontpanelControls(). Renderers read whatever properties their device can display; leave anything your aircraft does not provide as null.

Property Type Meaning
Speed, Heading, Altitude, VerticalSpeed int? Indicated airspeed (kt), heading (°), altitude (ft), vertical speed (ft/min).
BaroPressure int? Barometric pressure in inHg × 100 (e.g. 2992 = 29.92 inHg).
GearLeftDown, GearNoseDown, GearRightDown bool? Gear down-and-locked indicators.
GearWarning bool? Gear handle red warning light.
ClockUtcTime string? UTC time as "HHMMSS" (e.g. "123456" = 12:34:56).
ClockChrono string? Chronograph as "MMSS" (e.g. "0145" = 1 min 45 s).
ClockElapsedTime string? Elapsed time as "HHMM" (e.g. "0012" = 12 min).
ConsoleBrightness byte? Cockpit console backlight, 0–255.
SegmentBrightnessPercent int 7-segment display brightness, 0–100 (default 100).

Testing

There is no automated test suite (most tests require the physical device). Please:

  • Review your code carefully for errors or typos.
  • Test with DCS running and your hardware connected.

Commit & Push

git commit -m "Description of my changes"
git push origin my-feature-branch

Then open a Pull Request on GitHub with a clear description of the changes and why they are needed.


Guidelines

  • Adhere to the existing code style and conventions.
  • Write clear, concise commit messages.
  • Keep changes focused on one issue or feature.

FAQ

Q: What if I found a bug? Check if the bug is already reported. If not, open a new issue with steps to reproduce it.

Q: Can I contribute to the documentation? Absolutely! Fork, clone, branch, edit the wiki or docs, and submit a PR.

Q: How do I know if my changes are acceptable? Follow the coding standards, test your changes, and ensure they are meaningful and well-explained.

Clone this wiki locally