You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
14 KiB
D
438 lines
14 KiB
D
/*
|
|
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
Copyright (C) 2008 Nicolay Korslund
|
|
Email: < korslund@gmail.com >
|
|
WWW: http://openmw.snaptoad.com/
|
|
|
|
This file (config.d) is part of the OpenMW package.
|
|
|
|
OpenMW is distributed as free software: you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License
|
|
version 3, as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
version 3 along with this program. If not, see
|
|
http://www.gnu.org/licenses/ .
|
|
|
|
*/
|
|
|
|
module core.config;
|
|
|
|
import std.string;
|
|
import std.file;
|
|
import std.path;
|
|
import std.stdio;
|
|
|
|
import monster.monster;
|
|
import monster.util.string;
|
|
|
|
import core.inifile;
|
|
import core.filefinder;
|
|
|
|
import sound.audio;
|
|
|
|
import input.keys;
|
|
import input.ois;
|
|
|
|
import ogre.ogre;
|
|
|
|
ConfigManager config;
|
|
|
|
/*
|
|
* Structure that handles all user adjustable configuration options,
|
|
* including things like file paths, plugins, graphics resolution,
|
|
* game settings, window positions, etc. It is also responsible for
|
|
* reading and writing configuration files, for importing settings
|
|
* from Morrowind.ini and for configuring OGRE. It doesn't currently
|
|
* DO all of this, but it is supposed to in the future.
|
|
*/
|
|
|
|
struct ConfigManager
|
|
{
|
|
MonsterObject *mo;
|
|
|
|
IniWriter iniWriter;
|
|
|
|
// Mouse sensitivity
|
|
float *mouseSensX;
|
|
float *mouseSensY;
|
|
bool *flipMouseY;
|
|
|
|
// Ogre configuration
|
|
bool showOgreConfig; // The configuration setting
|
|
// The actual result, overridable by a command line switch, and also
|
|
// set to true if firstRun is true.
|
|
bool finalOgreConfig;
|
|
|
|
// Other settings
|
|
bool firstRun;
|
|
|
|
// Set to true if sound is completely disabled
|
|
bool noSound = false;
|
|
|
|
// Number of current screen shot. Saved upon exit, so that shots
|
|
// from separate sessions don't overwrite each other.
|
|
int screenShotNum;
|
|
|
|
// Game files to load (max 255)
|
|
char[][] gameFiles;
|
|
|
|
// Directories
|
|
char[] dataDir;
|
|
char[] esmDir;
|
|
char[] bsaDir;
|
|
char[] sndDir;
|
|
char[] fontDir;
|
|
char[] musDir; // Explore music
|
|
char[] musDir2; // Battle music
|
|
|
|
// Configuration file
|
|
char[] confFile = "openmw.ini";
|
|
|
|
// Cell to load at startup
|
|
char[] defaultCell;
|
|
|
|
// These set the volume to a new value and updates all sounds to
|
|
// take notice.
|
|
void setMusicVolume(float vol)
|
|
{
|
|
stack.pushFloat(vol);
|
|
mo.call("setMusicVolume");
|
|
}
|
|
float getMusicVolume()
|
|
{ return mo.getFloat("musicVolume"); }
|
|
|
|
void setSfxVolume(float vol)
|
|
{
|
|
stack.pushFloat(vol);
|
|
mo.call("setSfxVolume");
|
|
}
|
|
float getSfxVolume()
|
|
{ return mo.getFloat("sfxVolume"); }
|
|
|
|
void setMainVolume(float vol)
|
|
{
|
|
stack.pushFloat(vol);
|
|
mo.call("setMainVolume");
|
|
}
|
|
float getMainVolume()
|
|
{ return mo.getFloat("mainVolume"); }
|
|
|
|
// Initialize the config manager. Send a 'true' parameter to reset
|
|
// all keybindings to the default. A lot of this stuff will be moved
|
|
// to script code at some point. In general, all input mechanics and
|
|
// distribution of key events should happen in native code, while
|
|
// all setup and control should be handled in script code.
|
|
void initialize(bool reset = false)
|
|
{
|
|
// Initialize variables from Monster.
|
|
assert(mo !is null);
|
|
mouseSensX = mo.getFloatPtr("mouseSensX");
|
|
mouseSensY = mo.getFloatPtr("mouseSensY");
|
|
flipMouseY = mo.getBoolPtr("flipMouseY");
|
|
|
|
// Initialize the key binding manager
|
|
keyBindings.initKeys();
|
|
|
|
/* Disable this at the moment. It's a good idea to put
|
|
configuration in a central location, but it's useless as long
|
|
as Ogre expects to find it's files in the current working
|
|
directory. The best permanent solution would be to let the
|
|
locations of ogre.cfg and plugins.cfg be determined by
|
|
openmw.ini - I will fix that later.
|
|
|
|
version(Posix)
|
|
{
|
|
if(!exists(confFile))
|
|
confFile = expandTilde("~/.openmw/openmw.ini");
|
|
}
|
|
*/
|
|
|
|
readIni(reset);
|
|
}
|
|
|
|
// Read config from morro.ini, if it exists. The reset parameter is
|
|
// set to true if we should use default key bindings instead of the
|
|
// ones from the config file.
|
|
void readIni(bool reset)
|
|
{
|
|
// Read configuration file, if it exists.
|
|
IniReader ini;
|
|
|
|
ini.readFile(confFile);
|
|
|
|
screenShotNum = ini.getInt("General", "Screenshots", 0);
|
|
float mainVolume = saneVol(ini.getFloat("Sound", "Main Volume", 0.7));
|
|
float musicVolume = saneVol(ini.getFloat("Sound", "Music Volume", 0.5));
|
|
float sfxVolume = saneVol(ini.getFloat("Sound", "SFX Volume", 0.5));
|
|
bool useMusic = ini.getBool("Sound", "Enable Music", true);
|
|
|
|
|
|
lightConst = ini.getInt("LightAttenuation", "UseConstant", 0);
|
|
lightConstValue = ini.getFloat("LightAttenuation", "ConstantValue", 0.0);
|
|
|
|
lightLinear = ini.getInt("LightAttenuation", "UseLinear", 1);
|
|
lightLinearMethod = ini.getInt("LightAttenuation", "LinearMethod", 1);
|
|
lightLinearValue = ini.getFloat("LightAttenuation", "LinearValue", 3.0);
|
|
lightLinearRadiusMult = ini.getFloat("LightAttenuation", "LinearRadiusMult", 1.0);
|
|
|
|
lightQuadratic = ini.getInt("LightAttenuation", "UseQuadratic", 0);
|
|
lightQuadraticMethod = ini.getInt("LightAttenuation", "QuadraticMethod", 2);
|
|
lightQuadraticValue = ini.getFloat("LightAttenuation", "QuadraticValue", 16.0);
|
|
lightQuadraticRadiusMult = ini.getFloat("LightAttenuation", "QuadraticRadiusMult", 1.0);
|
|
|
|
lightOutQuadInLin = ini.getInt("LightAttenuation", "OutQuadInLin", 0);
|
|
|
|
|
|
*mouseSensX = ini.getFloat("Controls", "Mouse Sensitivity X", 0.2);
|
|
*mouseSensY = ini.getFloat("Controls", "Mouse Sensitivity Y", 0.2);
|
|
*flipMouseY = ini.getBool("Controls", "Flip Mouse Y Axis", false);
|
|
|
|
mo.setFloat("mainVolume", mainVolume);
|
|
mo.setFloat("musicVolume", musicVolume);
|
|
mo.setFloat("sfxVolume", sfxVolume);
|
|
mo.setBool("useMusic", useMusic);
|
|
|
|
defaultCell = ini.getString("General", "Default Cell", "Assu");
|
|
|
|
firstRun = ini.getBool("General", "First Run", true);
|
|
showOgreConfig = ini.getBool("General", "Show Ogre Config", false);
|
|
|
|
// This flag determines whether we will actually show the Ogre
|
|
// config dialogue. The EITHER of the following are true, the
|
|
// config box will be shown:
|
|
// - The program is being run for the first time
|
|
// - The "Show Ogre Config" option in openmw.ini is set.
|
|
// - The -oc option is specified on the command line
|
|
// - The file ogre.cfg is missing
|
|
|
|
finalOgreConfig = showOgreConfig || firstRun ||
|
|
!exists("ogre.cfg");
|
|
|
|
// Set default key bindings first.
|
|
with(keyBindings)
|
|
{
|
|
// Bind some default keys
|
|
bind(Keys.MoveLeft, KC.A, KC.LEFT);
|
|
bind(Keys.MoveRight, KC.D, KC.RIGHT);
|
|
bind(Keys.MoveForward, KC.W, KC.UP);
|
|
bind(Keys.MoveBackward, KC.S, KC.DOWN);
|
|
bind(Keys.MoveUp, KC.LSHIFT);
|
|
bind(Keys.MoveDown, KC.LCONTROL);
|
|
|
|
bind(Keys.MainVolUp, KC.ADD);
|
|
bind(Keys.MainVolDown, KC.SUBTRACT);
|
|
bind(Keys.MusVolDown, KC.N1);
|
|
bind(Keys.MusVolUp, KC.N2);
|
|
bind(Keys.SfxVolDown, KC.N3);
|
|
bind(Keys.SfxVolUp, KC.N4);
|
|
bind(Keys.Mute, KC.M);
|
|
|
|
bind(Keys.Fullscreen, KC.F);
|
|
|
|
bind(Keys.ToggleBattleMusic, KC.SPACE);
|
|
bind(Keys.PhysMode, KC.T);
|
|
bind(Keys.Nighteye, KC.N);
|
|
bind(Keys.ToggleGui, KC.Mouse1);
|
|
bind(Keys.Console, KC.F1, KC.GRAVE);
|
|
bind(Keys.Debug, KC.G);
|
|
|
|
bind(Keys.Pause, KC.PAUSE, KC.P);
|
|
bind(Keys.ScreenShot, KC.SYSRQ);
|
|
bind(Keys.Exit, KC.Q, KC.ESCAPE);
|
|
}
|
|
|
|
// Unless the ini file was missing or we were asked to reset all
|
|
// keybindings to default, replace all present bindings with the
|
|
// values from the ini.
|
|
if(!reset && ini.wasRead)
|
|
{
|
|
// Read key bindings
|
|
for(int i; i<Keys.Length; i++)
|
|
{
|
|
char[] s = keyToString[i];
|
|
if(s.length)
|
|
{
|
|
char[] iniVal = ini.getString("Bindings", s, "_def");
|
|
|
|
// Was the setting present in the ini file?
|
|
if(iniVal != "_def")
|
|
// If so, bind it!
|
|
keyBindings.bindComma(cast(Keys)i, iniVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read data file directory
|
|
dataDir = ini.getString("General", "Data Directory", "data/");
|
|
|
|
// Make sure there's a trailing slash at the end. The forward slash
|
|
// / works on all platforms, while the backslash \ does not. This
|
|
// isn't super robust, but we will fix a general path handle
|
|
// mechanism later (or use an existing one.)
|
|
if(dataDir.ends("\\")) dataDir[$-1] = '/';
|
|
if(!dataDir.ends("/")) dataDir ~= '/';
|
|
|
|
bsaDir = dataDir;
|
|
esmDir = dataDir;
|
|
sndDir = dataDir ~ "Sound/";
|
|
fontDir = dataDir ~ "Fonts/";
|
|
musDir = dataDir ~ "Music/Explore/";
|
|
musDir2 = dataDir ~ "Music/Battle/";
|
|
|
|
// A maximum of 255 game files are allowed. Search the whole range
|
|
// in case some holes developed in the number sequence. This isn't
|
|
// a great way of specifying files (it's just a copy of the flawed
|
|
// model that Morrowind uses), but it will do for the time being.
|
|
FileFinder srch = new FileFinder(esmDir, null, Recurse.No);
|
|
for(int i = 0;i < 255;i++)
|
|
{
|
|
char[] s = ini.getString("Game Files", format("GameFile[%d]",i), null);
|
|
if(s != null && srch.has(s))
|
|
gameFiles ~= esmDir ~ s;
|
|
}
|
|
delete srch;
|
|
|
|
if(gameFiles.length == 0)
|
|
{
|
|
// No game files set. Look in the esmDir for Morrowind.esm.
|
|
// We can add Tribunal.esm, and Bloodmoon.esm as defaults too
|
|
// later, when we're out of testing mode.
|
|
char[][] baseFiles = ["Morrowind.esm"];
|
|
//char[][] baseFiles = ["Morrowind.esm","Tribunal.esm","Bloodmoon.esm"];
|
|
srch = new FileFinder(esmDir, "esm", Recurse.No);
|
|
|
|
foreach(ref s; baseFiles)
|
|
{
|
|
if(srch.has(s))
|
|
{
|
|
writefln("Adding game file %s", s);
|
|
gameFiles ~= esmDir ~ s;
|
|
}
|
|
}
|
|
delete srch;
|
|
}
|
|
|
|
// FIXME: Must sort gameFiles so that ESMs come first, then ESPs.
|
|
// I don't know if this needs to be done by filename, or by the
|
|
// actual file type..
|
|
// Further sort the two groups by file date (oldest first).
|
|
|
|
/* Don't bother reading every directory seperately
|
|
bsaDir = ini.getString("General", "BSA Directory", "data/");
|
|
esmDir = ini.getString("General", "ESM Directory", "data/");
|
|
sndDir = ini.getString("General", "SFX Directory", "data/Sound/");
|
|
musDir = ini.getString("General", "Explore Music Directory", "data/Music/Explore/");
|
|
musDir2 = ini.getString("General", "Battle Music Directory", "data/Music/Battle/");
|
|
*/
|
|
}
|
|
|
|
// Create the config file
|
|
void writeConfig()
|
|
{
|
|
//writefln("writeConfig(%s)", confFile);
|
|
with(iniWriter)
|
|
{
|
|
openFile(confFile);
|
|
|
|
comment("Don't write your own comments in this file, they");
|
|
comment("will disappear when the file is rewritten.");
|
|
section("General");
|
|
writeString("Data Directory", dataDir);
|
|
/*
|
|
writeString("ESM Directory", esmDir);
|
|
writeString("BSA Directory", bsaDir);
|
|
writeString("SFX Directory", sndDir);
|
|
writeString("Explore Music Directory", musDir);
|
|
writeString("Battle Music Directory", musDir2);
|
|
*/
|
|
writeInt("Screenshots", screenShotNum);
|
|
writeString("Default Cell", defaultCell);
|
|
|
|
// Save the setting as it appeared in the input. The setting
|
|
// you specify in the ini is persistent, specifying the -oc
|
|
// parameter does not change it.
|
|
writeBool("Show Ogre Config", showOgreConfig);
|
|
|
|
// The next run is never the first run.
|
|
writeBool("First Run", false);
|
|
|
|
section("Controls");
|
|
writeFloat("Mouse Sensitivity X", *mouseSensX);
|
|
writeFloat("Mouse Sensitivity Y", *mouseSensY);
|
|
writeBool("Flip Mouse Y Axis", *flipMouseY);
|
|
|
|
section("Bindings");
|
|
comment("Key bindings. The strings must match exactly.");
|
|
foreach(int i, KeyBind b; keyBindings.bindings)
|
|
{
|
|
char[] s = keyToString[i];
|
|
if(s.length)
|
|
writeString(s, b.getString());
|
|
}
|
|
|
|
section("Sound");
|
|
writeFloat("Main Volume", mo.getFloat("mainVolume"));
|
|
writeFloat("Music Volume", mo.getFloat("musicVolume"));
|
|
writeFloat("SFX Volume", mo.getFloat("sfxVolume"));
|
|
writeBool("Enable Music", mo.getBool("useMusic"));
|
|
|
|
section("LightAttenuation");
|
|
comment("For constant attenuation");
|
|
writeInt("UseConstant", lightConst);
|
|
writeFloat("ConstantValue", lightConstValue);
|
|
comment("For linear attenuation");
|
|
writeInt("UseLinear", lightLinear);
|
|
writeInt("LinearMethod", lightLinearMethod);
|
|
writeFloat("LinearValue", lightLinearValue);
|
|
writeFloat("LinearRadiusMult", lightLinearRadiusMult);
|
|
comment("For quadratic attenuation");
|
|
writeInt("UseQuadratic", lightQuadratic);
|
|
writeInt("QuadraticMethod", lightQuadraticMethod);
|
|
writeFloat("QuadraticValue", lightQuadraticValue);
|
|
writeFloat("QuadraticRadiusMult", lightQuadraticRadiusMult);
|
|
comment("For quadratic in exteriors and linear in interiors");
|
|
writeInt("OutQuadInLin", lightOutQuadInLin);
|
|
|
|
section("Game Files");
|
|
foreach(int i, ref s; gameFiles)
|
|
writeString(format("GameFile[%d]",i), s[esmDir.length..$]);
|
|
|
|
close();
|
|
}
|
|
}
|
|
|
|
// In the future this will import settings from Morrowind.ini, as
|
|
// far as this is sensible.
|
|
void importIni()
|
|
{
|
|
/*
|
|
IniReader ini;
|
|
ini.readFile("../Morrowind.ini");
|
|
|
|
// Example of sensible options to convert:
|
|
|
|
tryArchiveFirst = ini.getInt("General", "TryArchiveFirst");
|
|
useAudio = ( ini.getInt("General", "Disable Audio") == 0 );
|
|
footStepVolume = ini.getFloat("General", "PC Footstep Volume");
|
|
subtitles = ini.getInt("General", "Subtitles") == 1;
|
|
|
|
The plugin list (all esm and esp files) would be handled a bit
|
|
differently. In our system they might be a per-user (per
|
|
"character") setting, or even per-savegame. It should be safe and
|
|
intuitive to try out a new mod without risking your savegame data
|
|
or original settings. So these would be handled in a separate
|
|
plugin manager.
|
|
|
|
In any case, the import should be interactive and user-driven, so
|
|
there is no use in making it before we have a gui of some sort up
|
|
and running.
|
|
*/
|
|
}
|
|
}
|