view mde/options.d @ 18:56a42ec95024

Changes to Init and logging. Moved an Init function; hence events now depends on Init rather than vice-versa. Paths and logging set up by Init's static this(). Logging location set at compile time. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Tue, 18 Mar 2008 17:51:52 +0000
parents 5f90774ea1ef
children a60cbb7359dd
line wrap: on
line source

/* LICENSE BLOCK
Part of mde: a Modular D game-oriented Engine
Copyright © 2007-2008 Diggory Hardy

This program is free software; you can redistribute it and/or modify it under the terms of
the GNU General Public License, version 2, 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 along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

/** This module handles stored options, currently all except input maps.
*
* The purpose of having all options centrally controlled is to allow generic handling by the GUI
* and ease saving and loading of values. The Options class is only really designed around handling
* small numbers of variables for now.
*/
module mde.options;

import mde.exception;

import mde.mergetag.Reader;
import mde.mergetag.Writer;
import mde.mergetag.DataSet;
import mde.mergetag.exception;
import mde.resource.paths;

import tango.scrapple.text.convert.parseTo : parseTo;
import tango.scrapple.text.convert.parseFrom : parseFrom;

import tango.util.log.Log : Log, Logger;

/** Base class for handling options. This class itself will handle no options and should not be
* instantiated, but sub-classes handle options.
*
* Each sub-class provides named variables for maximal-speed reading & writing. Sub-class references
* are stored by name, and will be available after pre-init has run (DO NOT access before this).
*
* The static class keeps track of all Options class instances for global loading and saving.
*
* Details: Options sub-classes hold associative arrays of pointers to all option variables, with a
* char[] id. This list is used for saving, loading and to provide generic GUI options screens. The
* built-in support in Options is only for bool, int and char[] types (a float type may get added).
*/
class Options : IDataSection
{
    // No actual options are stored by this class. However, much of the infrastructure is
    // present since it need not be redefined in sub-classes.
    
    // The "pointer lists":
    protected bool*  [ID]   optsBool;
    protected char[]*[ID]   optsCharA;
    protected int*   [ID]   optsInt;
    
    //BEGIN Mergtag loading/saving code
    void addTag (char[] tp, ID id, char[] dt) {
        if (tp == "bool") {
            bool** p = id in optsBool;
            if (p !is null) **p = parseTo!(bool) (dt);
        } else if (tp == "char[]") {
            char[]** p = id in optsCharA;
            if (p !is null) **p = parseTo!(char[]) (dt);
        } else if (tp == "int") {
            int** p = id in optsInt;
            if (p !is null) **p = parseTo!(int) (dt);
        }
    }
    void writeAll (ItemDelg dlg) {
        foreach (ID id, bool*   val; optsBool)  dlg ("bool"  , id, parseFrom!(bool  ) (*val));
        foreach (ID id, char[]* val; optsCharA) dlg ("char[]", id, parseFrom!(char[]) (*val));
        foreach (ID id, int*    val; optsInt)   dlg ("int"   , id, parseFrom!(int   ) (*val));
    }
    //END Mergtag loading/saving code
        
    //BEGIN Static
    // Each individual section
    static OptionsMisc  misc;
    
    /* Load/save options from file.
    *
    * If the file doesn't exist, no reading is attempted (options are left at default values).
    */
    private static const fileName = "options";
    static void load () {
        // Create all uncreated sections now, so that if we return early they are still created.
        if (misc is null) misc = new OptionsMisc;
        
        // Check it exists (if not it should still be created on exit).
        // Don't bother checking it's not a folder, because it could still be a block or something.
        if (!confDir.exists (fileName)) return;
        
        IReader reader;
        try {
            reader = confDir.makeMTReader (fileName, PRIORITY.LOW_HIGH);
            reader.dataSecCreator = delegate IDataSection(ID id) {
                /* Recognise each defined section, and return null for unrecognised sections. */
                
                if (id == cast(ID) "misc") return misc;
                else return null;
            };
            reader.read;
        } catch (MTException e) {
            logger.error ("Mergetag exception occurred:");
            logger.error (e.msg);
            throw new optionsLoadException ("Loading aborted: mergetag exception");
        }
        
        if (misc is null) throw new optionsLoadException ("Loading failed: section \"misc\" not found");
    }
    static void save () {
        DataSet ds = new DataSet();
        ds.sec[cast(ID) "misc"] = misc;
        
        IWriter writer;
        try {
            writer = confDir.makeMTWriter (fileName, ds);
            writer.write();
        } catch (MTException e) {
            logger.error ("Mergetag exception occurred; saving aborted:");
            logger.error (e.msg);
            //FIXME: currently nothing done besides logging the error
        }
    }
    
    private static Logger logger;
    static this() {
        logger = Log.getLogger ("mde.options");
    }
    //END Static
}

/** A home for all miscellaneous options, at least for now. */
class OptionsMisc : Options {
    bool useThreads;    // set 0 to disable threading
    char[] L10n;        // locale, e.g. en-GB
    
    int logLevel;       // tango logger level
    
    this () {
        optsBool    = ["useThreads":&useThreads];
        optsCharA   = ["L10n":&L10n];
        optsInt     = ["logLevel":&logLevel];
    }
}