Setting up a Mod Config File - X-Hax/SADXModdingGuide GitHub Wiki
Both the SADX and SA2 Mod Loaders support Mod Configuration which can be set up by making a XML file (ConfigSchema.xml
) that the Manager will read and create a custom Configuration menu for a mod.
The SAModManager, as of version 1.2.9, now supports codeless config creation. This is not supported by the legacy Mod Managers.
In this guide, we will go over how to create a Codeless Configuration and a Coded Configuration. These tutorials do assume you already have a mod created and some basic understanding on how a mod is created.
For this example, we will implement two different options with multiple choices in one mod:
- An option to swap the Super Sonic music with multiple choices
- A simple option to enable or disable a Sonic Skin.
First of all, you need to open the SAModManager, select your mod, right click -> Edit Mod.
Once the edit window shows up, scroll down to the field Include Directories
. This is what we want to edit. If you don't see it, make sure your Mod Manager is version 1.2.9 or higher. The Mod Config uses comma (,
) separated strings to set the default directories that will be used for EACH of the different options. Please note that you can have spaces to help distinguish the options when writing them. These will automatically be trimmed when saving.
In this example here, the Superstars
folder will be the default option for the Super Sonic music replacement while Klonoa
will be the default option for the Sonic Skin replacement.
Please note that "default directory" refers to the default subfolder found in the mod for that specific option. You do not need to include all of the subfolders being used for different options.
Once you press OK, head over to your Mod's folder, and you should see the new folders that you listed in the Include Directories
.
These subfolders will act like a standard mod directory (without a mod.ini file) for file replacement. This means that you can create your system
, textures
, replacetex
, etc folders within each directory as necessary. The way this works is that the Mod Manager will take the supplied Include Directories
and load files found within them just like it would for a normal mod.
Now, in this example, let's say you want to have more choices for the Super Sonic music. You would need to create additional subfolders for any options you wanted to include. If you don't intend on having multiple options and are only wanting a simple Enable / Disable
setup, then you can simply use one folder per option.
Once this has been set up, you can put any mod related files into their respective folders.
Super Sonic music replacement (Example):
Sonic skin replacement (Example):
With the mod files setup, the last step is creating the ConfigSchema (XML) file that will be used by the Manager the mod's configuration.
In the root of your mod's folder (where mod.ini
is), create a new file called configschema.xml
. You can use the following base to create your configuration schema:
<?xml version="1.0" encoding="utf-8"?>
<ConfigSchema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.sonicretro.org">
<Groups>
<Group name="General">
<Property name="IncludeDir0" type="MusicE" display="Music Style" defaultvalue="Superstars">
<HelpText>Pick the music style.</HelpText>
</Property>
<Property name="IncludeDir1" type="skinE" display="Skin Style" defaultvalue="Klonoa">
<HelpText>Pick the skin style.</HelpText>
</Property>
</Group>
</Groups>
<Enums>
<Enum name="MusicE">
<EnumMember name="Superstars" />
<EnumMember name="Mania" />
</Enum>
<Enum name="skinE">
<EnumMember name="Klonoa" />
<EnumMember name="DX Sonic" />
</Enum>
</Enums>
</ConfigSchema>
This xml file will make the config option in the Mod Manager like this:
Let's break all of these down:
These groups hold different properties for configuration, most commonly they are bool
properties, but they can also be int
, string
, enum
, and float
. For codeless configuration mods, only enum
types can be used, and that is what we will be going over in this section.
-
Groups
is the main array needed for storing property groups. These do not have additional groups. -
Group
is a grouping of properties. You can have several of these within aGroups
array.-
name
is what will be saved to the config.ini file as the Group Header. You can use any name here. -
display
can be used to create a display name that is different to yourname
. It is not required.
-
-
Property
is where you actually define a configuration item.-
name
is the name of the property. These are reserved underIncludeDirX
(where X is a number) for codeless configurations. X refers to the index of your folder in theInclude Directories
list. These start at 0, so the first folder in yourInclude Directories
would beIncludeDir0
. -
display
is what is displayed in the Configuration menu for the property. If one is not set, this will default to thename
. It's highly recommended to set adisplay
value. -
type
is what the property actually is. -
defaultvalue
is what the default option should be. In most cases, this would be the first listing in anenum
, but it can be any entry. It must match theEnumMember
's name. -
HelpText
is an element nested within aProperty
. These are essentially descriptions of what the property does.
-
-
Enums
is the main array where anyEnum
elements are stored. -
Enum
is the group where you can create an enum. In the example,MusicE
andskinE
are called by their name in the respectiveProperties
that utilize them.-
name
is how the enum is referenced by aProperty
.
-
-
EnumMembers
-
name
is what is actually written to theconfig.ini
file. These MUST match any subfolder names you create if you want swappable options.- If you only want to have an Enable / Disable system as mentioned earlier, you can have one of the
EnumMembers
as an invalid name for a folder. When users select an option that doesn't have a corresponding folder, the Mod Loader will not load anything from that folder. e.g., if you add an option namedDisabled
to theEnumMembers
, when selected, it will not load anyInclude Directories
folders as none have that name.
- If you only want to have an Enable / Disable system as mentioned earlier, you can have one of the
-
display
will be what is displayed in the dropdown box for enum selection in the property over thename
property if it's set. These are great for making things read better to the user.
-
With all of this in mind, you should be ready to make your own configuration for your own mod. You can always work from the example as a base, and play around with the values to get a better understanding of how it all works.
NOTE: If the Mod Manager happens to crash when you click the Configure
button with your mod selected, that means your ConfigSchema.xml
file is incorrectly formatted. This is a common mistake. Be sure to pay attention to the syntax used, namely when to use ""
and <>
.
Even with these options, you can still have a system
, textures
, or replacetex
folder (or any other supported folders or data files) in the root of your mod's folder like any standard mod. This effectively allows you to have universal files that are loaded no matter what options the user has selected. This has the added benefit of reducing duplicated files and will help keep your mod's filesize down.
One final note: while testing your mod configuration, you will notice that a config.ini
file is created when saving your configuration. You do not want to ship this file when releasing your mod, or in any updates. This can cause issues for users looking to play with your mod where they may have settings they don't want enabled just by downloading it, or their configuration may accidentally be overwritten due to an update.
Hopefully you have enough information from this guide to make basic options without needing to learn how to create a DLL mod.
If your options don't work in game, please check the following steps:
- Make sure each of your
Property name
usesIncludeDirX
, this is mandatory for your config to work.
- Check if
mod.ini
is referencing your current enabled options (It should always do it automatically when you swap Config in the Manager.)
- Make sure each of your
Enum Member name
matches your subfolder name.
Feel free to join the X-Hax Discord Server if you need any help!
If you want more possibilities than just basic file replacement, it is possible to use custom code with a DLL mod to implement basically anything depending on the current option.
The configschema file has a lot more features that can be combined with a DLL mod, we used enum for basic replacement, but if you use code you can use string, float, int, bool etc.
Additionally, more elements that you can implement in your XML file in the <Property>
part:
-
minvalue
: the minimum value for integer/float property values. -
maxvalue
: the maximum value for integer/float property values. -
alwaysinclude
: whether the property should be written in the ini if it's using the default value.
First, you will have to set up a DLL Mod if you haven't already.
Once it's done you will need to make your code read the ini file that the Mod Loader will yield, you can use the IniFile class that MainMemory wrote in mod-manager-common.
Easy method:
- Copy
IniFile.hpp
,IniFile.cpp
,Utils.hpp
,TextConv.hpp
andTextConv.cpp
in your project folder. - Move the two cpp files next to your other source files in Visual Studio.
- If you have
pch.h
instead ofstdafx.h
, rename thestdafx.h
includes in IniFile.cpp and TextConv.cpp topch.h
. - Remove security warnings (or resolve them.)
Git method:
- Add the mod-manager-common repository as a submodule.
- In Visual Studio, right click your solution, click "add existing project", and add
ModLoaderCommon.vcxproj
. - Then right click your project, click on build order, and make "mod-loader-common" as a dependency of your project.
- Open the properties of your project, go to additional includes and add
$(SolutionDir)mod-loader-common/ModLoaderCommon
. - In the Linker -> Input -> Additional Dependencies, add
$(SolutionDir)bin\ModLoaderCommon.lib
.
It should now build. Note that you can add sadx-mod-loader as a submodule instead, as mod-loader-common is already part of it.
Head over to the cpp file with the Init function and add #include "IniFile.hpp"
below the pch/stdafx include.
Then you can adapt the following code to your need in the Init function:
const IniFile* config = new IniFile(std::string(path) + "\\config.ini");
bool value_bool = config->getBool("", "ConfigBool", true);
int value_int = config->getInt("", "ConfigInt", 120);
float value_float = config->getFloat("", "ConfigFloat", 1.25f);
std::string value_string = config->getString("", ConfigCharacter, "Sonic");
delete config;
Any of the get function work as follows: it first take the group name (we only use an empty named group), then the parameter name and finally the default value that will be used if config.ini
is missing or if the setting is missing from it.
That's it!