Wizardless FOMOD installer with automatic selection inference
🎀 Features | 💃 Quick Start | 📘 Documentation | 🤝 Contributing
A mod installer for Skyrim built with C++23 and powered by Crow and React. It pairs a C DLL that handles archive extraction and FOMOD processing with a Crow HTTP server and React web interface for interactive installations. salma integrates with Mod Organizer 2 via a Python plugin, automatically inferring FOMOD selections by comparing archives against installed files - no wizard clicks required.
Important
Windows 10/11 Only - salma targets Mod Organizer 2 on Windows and will not build on other platforms.
- Requires vcpkg with the toolchain file at a known path.
- Requires a Mod Organizer 2 instance for plugin integration and FOMOD testing.
/* ============================================================================================== *
*
* :::::::: ::: ::: :::: :::: ::: ⢠⣤⣤⣀ ⠀⠀⠀⠀⠀⠀ ⣀⣤⣤⡄
* :+: :+: :+: :+: :+: +:+:+: :+:+:+ :+: :+: ⢸⣿⣿⣿⣿⣦⣄⣀⣠⣴⣿⣿⣿⣿⡇⠀⊹
* +:+ +:+ +:+ +:+ +:+ +:+:+ +:+ +:+ +:+ ⣸⣿⣿⣿⣿⣿⡽⣿⣯⣿⣿⣿⣿⣿⣇
* +#++:++#++ +#++:++#++: +#+ +#+ +:+ +#+ +#++:++#++: ⢻⣿⣿⣿⠿⣻⣵⡟⣮⣟⠿⣿⣿⣿⡟
* +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ ⠀⠀⠀⠀⣼⣿⡿ ⠀⢿⣿⣷⡀
* #+# #+# #+# #+# #+# #+# #+# #+# #+# ⊹⠀⣠⣾⣿⣿⠃ ⠀⠈⢿⣿⣿⣦⡀
* ######## ### ### ########## ### ### ### ### ⠀⠈⠉⠹⡿⠁⠀⠀⠀⠀⠈⢻⡇⠉⠉
*
* << M O D I N S T A L L E R >>
*
* ============================================================================================== */
salma ships with a C DLL for direct integration, a REST API for programmatic access, and a React web UI for interactive installations.
---
config:
look: handDrawn
theme: mc
themeVariables:
fontSize: 18px
layout: elk
---
graph LR
classDef dll fill:#134e3a,stroke:#10b981,color:#e2e8f0
classDef api fill:#1e3a5f,stroke:#3b82f6,color:#e2e8f0
classDef web fill:#2e1f5e,stroke:#8b5cf6,color:#e2e8f0
subgraph DLL["C DLL (mo2-salma.dll)"]
Install["install()"]:::dll
Infer["inferFomodSelections()"]:::dll
Config["installWithConfig()"]:::dll
end
subgraph API["Crow REST API"]
Upload["Upload & Install"]:::api
Scan["FOMOD Scan"]:::api
Status["Job Status"]:::api
end
subgraph Web["React Frontend"]
UI["Interactive Installer"]:::web
FomodView["FOMOD Browser"]:::web
Logs["Log Viewer"]:::web
end
Web --> API
API --> DLL
---
config:
look: handDrawn
theme: mc
themeVariables:
fontSize: 18px
layout: elk
---
graph LR
classDef parse fill:#7c2d12,stroke:#f97316,color:#fef3c7
classDef eval fill:#4c1d95,stroke:#e879f9,color:#e2e8f0
classDef ops fill:#064e3b,stroke:#34d399,color:#e2e8f0
classDef detect fill:#713f12,stroke:#facc15,color:#fef9c3
P["XML Parser"]:::parse
D["Dependency Evaluator"]:::eval
F["File Operations"]:::ops
S["Structure Detector"]:::detect
P --- D --- F --- S
- 📄 XML Parser — Parses
fomod/ModuleConfig.xmlfor installation steps and options - 🧩 Dependency Evaluator — Resolves flag-based and file-based FOMOD dependencies
- 📂 File Operations — Priority-sorted file copy, folder creation, and patching
- 🔎 Structure Detector — Identifies mod folder layout (meshes/, textures/, SKSE/, etc.)
salma's inference service compares an archive's FOMOD options against an already-installed mod to determine which selections were originally chosen:
- 🌳 Walks every permutation of FOMOD steps and flags
- 🎯 Matches expected file output against the installed file tree
- 📋 Returns a JSON configuration that reproduces the original install
- 🧪 Powers the round-trip test suite for validation
- 🐍 Python Plugin —
mo2-salma.pyloads the DLL via ctypes and exposes tools inside MO2 - 🔬 Scan FOMOD Choices — Batch-scans all installed mods for FOMOD selections
- 📁 Centralized Output — Reinstalled mods go to a dedicated "Salma FOMODs Output" folder
- ⚡ Deploy & Purge —
deploy.batandpurge.batscripts for plugin lifecycle management
| Format | Backend |
|---|---|
| 7z | bit7z (native 7-Zip SDK wrapper) |
| ZIP | libarchive |
| RAR | libarchive |
| TAR.* | libarchive (bzip2, lz4, lzma, zstd) |
- 🌐 REST API — Full programmatic access for custom installers and automation
- 🎨 Web UI — React SPA with dark/light theme, served directly by the Crow backend
- 📝 Logging — Unified thread-safe logger with subsystem tags (
[archive][install][fomod][infer][server]) - 🧪 Round-Trip Testing — Infer selections, reinstall, and diff against the original mod
| Component | Technology |
|---|---|
| Language | C++23 |
| HTTP Framework | Crow (with CORS handler) |
| Frontend | React 18 + TypeScript + Vite |
| Styling | Tailwind CSS 4 |
| XML Parsing | pugixml |
| JSON | nlohmann-json |
| Archive | libarchive + bit7z |
| Formatting | clang-format (Google-based) |
| Build System | CMake 3.20+ |
| Package Manager | vcpkg |
| Documentation | Doxide + MkDocs |
| Plugin | Python 3 (MO2 ctypes bridge) |
| CI/CD | GitHub Actions |
| Platform | Windows 10/11 (64-bit) |
- Windows 10/11 (64-bit)
- Visual Studio 2022 (MSVC v143, C++23)
- CMake 3.20+
- vcpkg with the toolchain at a known path
- Node.js (for building the React frontend)
- Python 3 (for MO2 plugin and documentation post-processing)
- clang-format (optional, for code formatting — CI enforces it)
- doxide + mkdocs (optional, for API docs generation)
# 1. Clone the repository
git clone https://github.com/lextpf/salma.git
cd salma
# 2. Build (format + configure + compile + docs)
.\build.bat
# 3. Run the server
.\build\bin\Release\mo2-server.exeOutput:
- DLL:
build/bin/Release/mo2-salma.dll - Server:
build/bin/Release/mo2-server.exe
# Copy DLL + Python plugin to your MO2 instance
.\deploy.bat---
config:
look: handDrawn
theme: mc
themeVariables:
fontSize: 18px
layout: elk
---
graph TB
classDef server fill:#1e3a5f,stroke:#3b82f6,color:#e2e8f0
classDef core fill:#134e3a,stroke:#10b981,color:#e2e8f0
classDef fomod fill:#4a3520,stroke:#f59e0b,color:#e2e8f0
classDef ext fill:#2e1f5e,stroke:#8b5cf6,color:#e2e8f0
Main["main.cpp (Crow)"]:::server
subgraph Server["REST API Layer"]
InstCtrl["InstallationController"]:::server
Mo2Ctrl["Mo2Controller"]:::server
Static["StaticFileHandler"]:::server
end
subgraph Core["Core Services"]
InstSvc["InstallationService"]:::core
Archive["ArchiveService"]:::core
FileOps["FileOperations"]:::core
Detect["ModStructureDetector"]:::core
end
subgraph FOMOD["FOMOD Engine"]
FomodSvc["FomodService"]:::fomod
DepEval["FomodDependencyEvaluator"]:::fomod
Infer["FomodInferenceService"]:::fomod
end
subgraph External["External Integration"]
CApi["C API (DLL Export)"]:::ext
Plugin["Python Plugin (MO2)"]:::ext
WebUI["React Frontend"]:::ext
end
Main --> Server
Server --> Core
Core --> FOMOD
CApi --> Core
Plugin --> CApi
Static --> WebUI
| File | Purpose |
|---|---|
main.cpp |
Crow HTTP server entry point |
InstallationService |
Main orchestrator for mod installation |
ArchiveService |
Archive extraction (libarchive + bit7z) |
FomodService |
FOMOD XML parsing and installation logic |
FomodInferenceService |
Infers FOMOD selections from installed files |
FomodDependencyEvaluator |
Evaluates FOMOD flag and file dependencies |
FileOperations |
Priority-sorted file copy and patching |
ModStructureDetector |
Detects mod folder layout |
CApi |
C-linkage DLL exports for ctypes |
Logger |
Thread-safe logging with callback support |
ConfigService |
Configuration management |
salma/
|-- .github/
| +-- workflows/
| |-- build.yml # CI: format check + build
| +-- test.yml # CI: round-trip integration tests
|-- src/ # C++ source code
| |-- main.cpp # Crow HTTP server entry point
| |-- CApi.h/cpp # C-linkage DLL API (ctypes)
| |-- Export.h # MO2_API export macro
| |-- Types.h # Shared type definitions
| |-- Logger.h/cpp # Thread-safe logging
| |-- ArchiveService.h/cpp # Archive extraction
| |-- FileOperations.h/cpp # Queued file operations
| |-- ModStructureDetector.h/cpp # Mod folder structure detection
| |-- FomodService.h/cpp # FOMOD installation logic
| |-- FomodDependencyEvaluator.h/cpp # FOMOD dependency evaluation
| |-- FomodInferenceService.h/cpp # Selection inference engine
| |-- InstallationService.h/cpp # Main orchestrator
| |-- InstallationController.h/cpp # REST endpoint handlers
| |-- Mo2Controller.h/cpp # MO2 status endpoints
| |-- ConfigService.h/cpp # Configuration management
| |-- MultipartHandler.h/cpp # Form data parsing
| +-- StaticFileHandler.h/cpp # SPA serving
|-- web/ # React frontend
| |-- src/ # TypeScript source
| |-- dist/ # Built SPA (served by Crow)
| |-- package.json # Dependencies
| +-- vite.config.ts # Dev proxy to :5000
|-- scripts/ # MO2 plugin & utilities
| |-- mo2-salma.py # MO2 Python plugin
| |-- common.py # Shared utilities
| +-- _clean_docs.py # Doc post-processing
|-- logs/ # Runtime logs
| |-- salma.log # Application log
| +-- test.log # Test suite output
|-- .clang-format # Code formatting rules
|-- CMakeLists.txt # Build configuration
|-- CMakePresets.json # Build presets (vcpkg)
|-- vcpkg.json # Dependency manifest
|-- build.bat # Build pipeline
|-- deploy.bat # Deploy to MO2
|-- purge.bat # Remove plugin & clean output
|-- test.bat # Run test suite
|-- test.py # Test runner
|-- doxide.yml # API doc config
+-- mkdocs.yml # Documentation site config
API documentation is generated via a three-stage pipeline:
# 1. Generate markdown from C++ headers
doxide build
# 2. Post-process (strip noise, fix formatting)
python scripts/_clean_docs.py
# 3. Build the documentation site
mkdocs buildThe site is output to site/ and can be served locally with mkdocs serve.
| Problem | Solution |
|---|---|
| vcpkg toolchain not found | Set VCPKG_ROOT or update the path in CMakePresets.json |
| Crow port already in use | Another process is on port 5000, kill it or change the port |
| DLL not found by plugin | Run deploy.bat or manually copy mo2-salma.dll to MO2 plugins |
| FOMOD inference mismatch | Mod may have been manually edited post-install, check .mohidden files |
| Vite proxy errors | Ensure the Crow server is running on port 5000 before starting Vite |
Contributions are welcome! Please read the Contributing Guidelines before submitting pull requests.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and ensure the build passes
- Commit with descriptive messages
- Push to your fork and open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Crow - C++ HTTP framework
- React - Frontend UI library
- libarchive - Multi-format archive extraction
- bit7z - 7-Zip SDK wrapper
- pugixml - XML parsing
- nlohmann-json - JSON for Modern C++
- vcpkg - C++ package manager
- Doxide - API documentation generator
- MkDocs Material - Documentation theme
- Tailwind CSS - Utility-first CSS framework
- Claude - AI coding assistant by Anthropic
