view mde/content/ValueCache.d @ 156:36df0ffe34d2

Fix to reload translations.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 18 Apr 2009 21:51:03 +0200
parents 9f035cd139c6
children
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 as published by the Free Software
Foundation, either version 2 of the License, or (at your option) any later version.

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, see <http://www.gnu.org/licenses/>. */

/******************************************************************************
 * This module is used to load and save content values to/from a struct.
 *****************************************************************************/
module mde.content.ValueCache;

import mde.file.paths;
import mde.file.mergetag.MTTagReader;
import mde.file.mergetag.MTTagWriter;
import mde.file.serialize;

import tango.util.log.Log : Log, Logger;
private Logger logger;
static this () {
    logger = Log.getLogger ("mde.gui.content.ValueCache");
}

typedef char[] enumVal;	// use a char[] to save enum values (it's more resiliant to change of the enum type)

struct ValueCache {
    //BEGIN Templates
    package static {
	// All supported content types for generic saving and loading:
	template store(A...) { alias A store; }
	alias store!(bool, int, double, char[], enumVal) TYPES;
	
	// Get name of a type. Basically just stringof, but special handling for arrays.
        // Use TName!(T) for a valid symbol name, and T.stringof for a type.
        template TName(T : T[]) {
            const char[] TName = TName!(T) ~ "A";
        }
        template TName(T) {
            const char[] TName = T.stringof;
        }
        template Vars(A...) {
            static if (A.length) {
                static if (is (A[0] == enumVal))
                    const char[] Vars = `char[][char[]] `~TName!(A[0])~`Data;` ~ Vars!(A[1..$]);
                else
                    const char[] Vars = A[0].stringof~`[char[]] `~TName!(A[0])~`Data;` ~ Vars!(A[1..$]);
            } else
                const char[] Vars = ``;
        }
        
        // For reading tags
        template readTagMixin(T, A...) {
            const char[] ifBlock = `if (reader.tagType == "`~T.stringof~`") {
                auto p = symBuf[0..symEnd] in `~TName!(T)~`Data;
            	if (p is null)
                    `~TName!(T)~`Data[symBuf[0..symEnd].dup] = deserialize!(typeof(`~TName!(T)~`Data.values[0])) (reader.tagData);
            }`;
            static if (A.length)
                const char[] readTagMixin = ifBlock~` else `~readTagMixin!(A).readTagMixin;
            else
                const char[] readTagMixin = ifBlock;
        }
        
        // For writing tags
        template writeTagMixin(A...) {
            static if (A.length)
                const char[] writeTagMixin =
                `typeStr = "`~A[0].stringof~`";
                foreach (sym,val; `~TName!(A[0])~`Data) {
                    MTTagStrings tag;
                    tag.type = typeStr;
                    tag.data = serialize (val);
                    // Take section prefix as MT section
                    size_t dot;
                    while (dot < sym.length && sym[dot] != '.') ++dot;
                    if (dot == sym.length) {	// no prefix; use ""
                    	dot = 0;
                    	tag.id = sym;
                    } else
                    	tag.id = sym[dot+1..$];
                    secs[sym[0..dot]] ~= tag;
        	}` ~ writeTagMixin!(A[1..$]);
            else
                const char[] writeTagMixin = ``;
        }
    }
    //END Templates
    
    mixin (Vars!(TYPES));	// variable associative arrays
    
    // Load content values from mergetag file(s) conf/options.mt[tb]
    // Use HIGH_LOW priority to load all data, HIGH_ONLY for just user data
    void loadData (PRIORITY priority) {
        try {
            MTTagReader reader = confDir.makeMTTagReader ("options", priority);
            bool isSecTag;
            while (reader.readTag (isSecTag) && !isSecTag) {}	// skip header
            char[] symBuf = new char[64];
            size_t sym2, symEnd;	// start of second symbol in symBuf, end of symbol
            do {
                //debug logger.trace ("tag: <{}|{}={}> sec: {} ({})", reader.tagType, reader.tagID, reader.tagData, reader.section, isSecTag);
                if (isSecTag) {
                    sym2 = reader.section.length;
                    if (sym2 > 0) {	// named section; prefix symbol by "section."
                        if (symBuf.length <= sym2)
                            symBuf.length = sym2 * 2;
                        symBuf[0..sym2] = reader.section;
                        symBuf[sym2++] = '.';
                    }
                } else {
                    symEnd = reader.tagID.length;
                    if (symBuf.length - sym2 < symEnd)
                        symBuf.length = symBuf.length + symEnd * 2;
                    symEnd += sym2;
                    symBuf[sym2..symEnd] = reader.tagID;
                    mixin (readTagMixin!(TYPES).readTagMixin);
                }
            } while (reader.readTag (isSecTag))
        } catch (NoFileException e) {
            // Just return. Options file will be created on exit.
        } catch (Exception e) {
            logger.warn ("Loading options failed: "~e.msg);
            logger.warn ("If warning persists, delete the offending file.");
        }
    }
    
    // Save stored values to the user file
    void saveData () {
        try {
            MTTagWriter writer = confDir.makeMTTagWriter ("options.mtt");
            
            // First sort tags by section:
            struct MTTagStrings {
                char[] type, id, data;
            }
            MTTagStrings[][char[]] secs;
            char[] typeStr;
            mixin (writeTagMixin!(TYPES));
            
            // Now write sections:
            foreach (secName,sec; secs) {
            	writer.sectionTag (secName);
            	foreach (tag; sec)
                    writer.dataTag (tag.type, tag.id, tag.data);
            }
            writer.close;
        } catch (Exception e) {
            logger.error ("Saving options failed: "~e.msg);
        }
    }
}