PMDG Livery Manager
A small Windows utility I built to make PMDG livery installs in Microsoft Flight Simulator 2020 much less tedious while I was learning .NET.
Project Overview
Turning an annoying flight-sim ritual into a one-click Windows tool while learning how to build a real desktop app in .NET.
I built PMDG Livery Manager as a self project for two reasons at the same time. First, I genuinely wanted to learn .NET by making something real instead of following another tutorial. Second, I kept running into the same headache in Microsoft Flight Simulator 2020 whenever I wanted to install third-party PMDG liveries: unzip the package, inspect `livery.json`, rename `options.ini`, copy the file into PMDG's work folder, and regenerate `layout.json` again. It was repetitive enough that it stopped being a quick hobby task, so I turned it into a small Windows utility that solves a problem I actually have.
Key Highlights
- Project type
- Self project built to learn .NET by solving a real personal annoyance
- UI model
- Single-window WPF app using MVVM and CommunityToolkit.Mvvm
- Key workflow
- ZIP validation, install/uninstall, PMDG INI sync, layout regeneration
- Distribution
- Windows x64 self-contained publish for non-developer users
The Challenges
- Installing PMDG liveries manually in MSFS 2020 is much more awkward than dropping a folder into Community. You have to extract the ZIP, inspect `livery.json`, rename `options.ini` to the correct ATC ID, copy it into a separate PMDG work directory, and then rebuild the package layout.
- The path logic changes depending on whether the simulator came from Steam, Microsoft Store, or a custom install, which makes the process even more error-prone when you just want to try a repaint quickly.
- Because I was also using this project to learn .NET, I wanted something practical enough to teach me desktop state management, async workflows, and file-system edge cases instead of another toy CRUD app.
Project Goals
- Collapse the full livery install flow into a simple desktop experience that feels like picking a target aircraft and clicking install.
- Make the tool safe enough for real use by detecting installed PMDG packages, validating ZIP contents against the selected aircraft, and handling both install and uninstall cleanly.
- Use the project as a serious .NET learning exercise, including WPF, MVVM, dependency boundaries, async UI work, and Windows publishing.
System Architecture
Desktop shell and MVVM orchestration
The app is a native Windows WPF application with one main window and one main view model. `MainViewModel` owns UI state such as selected store type, detected aircraft, installed liveries, loading state, search text, and status messaging, while the file-system-heavy behavior stays behind service interfaces instead of leaking into code-behind.
Path resolution and aircraft discovery
`PathService` resolves the Community folder and PMDG work folder for Steam, Microsoft Store, or custom installs. `AircraftDiscoveryService` then scans the Community directory for `pmdg-aircraft-*-liveries` packages and builds the list of aircraft the UI can manage.
Livery discovery and browsing
Once an aircraft is selected, `LiveryDiscoveryService` enumerates the installed livery folders under `SimObjects/Airplanes`, parses `livery.json` for each package, and tries several thumbnail locations so the app can present something more useful than just a raw folder list.
Install safety and package mutation
Before installation, `LiveryPackageInspectionService` opens the ZIP, scans supported text files, extracts the ATC ID, and tries to identify the intended PMDG aircraft through `productPackage`, regex matching, and alias tokens. That validation step is important because it reduces the chance of installing the wrong repaint into the wrong aircraft package. If validation passes, `LiveryInstallationService` extracts the ZIP, renames `options.ini` to `{atcId}.ini`, and copies that file into the PMDG work folder when available.
Layout and manifest regeneration
After every install or uninstall, `LayoutService` rebuilds the aircraft package `layout.json` and updates `manifest.json` total size so the livery package stays consistent with how MSFS expects it. The implementation even includes a short-path move to `C:\_LM_TEMP` plus a `\\?\` fallback because Community folder paths can get long enough to hit Windows path limits.
Persistence and lightweight app composition
The app keeps its architecture intentionally simple. `App.OnStartup` wires the services manually instead of bringing in a full IoC container, and `ConfigService` stores the selected store type, custom path, and last aircraft inside `%APPDATA%/LiveryManager/config.json` so the app opens back into a useful state.
Technology Rationale
Tradeoffs & Constraints
Native Windows fit vs. cross-platform reach
WPF was the right learning and UX fit for a Windows-only MSFS helper, but it also means the app is intentionally tied to one platform.
Simple architecture vs. future scale
Manual DI and a single main view model keep the project approachable, but they would need more structure if the tool grew into a larger multi-window desktop application.
Direct file manipulation vs. edge-case handling
Working directly with ZIP contents and simulator package folders makes the utility genuinely useful, but it also forces the app to care about path length limits, missing work folders, malformed metadata, and aircraft-package mismatches.
Shipped utility vs. test coverage
The project is strong as a practical tool and learning vehicle, but it currently leans more on careful service boundaries and manual verification than on automated tests.
Lessons Learned
- Learning a framework becomes much easier when the project solves a real annoyance you keep coming back to, because every architectural choice has an immediate payoff.
- Desktop utilities still benefit from clean boundaries; putting path logic, ZIP inspection, and layout regeneration into separate services made the app much easier to extend later.
- Windows file-system work gets messy fast once deep simulator folders and generated package metadata are involved, so long-path handling and defensive validation are not optional details.
- Good utility UX is mostly about removing fragile manual steps and giving clear status feedback, not about filling the screen with controls.
Outcomes & Next Steps
The result is a working WPF utility that auto-detects the simulator store path, discovers installed PMDG livery packages, shows installed liveries with thumbnail previews, supports drag-and-drop or multi-ZIP installs, validates packages before copying files, and regenerates both `layout.json` and `manifest.json` after every change. More importantly for me, it turned .NET from something I was only reading about into something I had actually used to ship a tool I personally wanted.
- Expand compatibility and metadata handling for more PMDG aircraft variants and more livery package shapes found in the wild.
- Improve the browsing experience further with richer livery details and better duplicate or conflict handling during install.