mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 00:56:39 +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.
 | 
						|
    */
 | 
						|
  }
 | 
						|
}
 |