mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 23:26:41 +00:00 
			
		
		
		
	git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@138 ea6a568a-9f4f-0410-981a-c910a81bb256
		
			
				
	
	
		
			437 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			D
		
	
	
	
	
	
			
		
		
	
	
			437 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.
 | |
|     */
 | |
|   }
 | |
| }
 |