view mde/file/mergetag/DefaultData.d @ 88:01f4f5f1acc9

Changes to init and to allow compiling with gdc. Tweaked init code to allow using circular iterators (disabled until my patch makes it into tango). Changes to allow compiling with gdc. Building is successful and unittests complete, but in my experience a SIGSEGV occurs within SDL.
author Diggory Hardy <diggory.hardy@gmail.com>
date Mon, 29 Sep 2008 12:09:44 +0100
parents d8fccaa45d5f
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 contains the DefaultData class, and some notes possibly useful for implementing
* other types of DataSection.
*/
module mde.file.mergetag.DefaultData;

public import mde.file.mergetag.iface.IDataSection;
import mde.file.serialize;


/*************************************************************************************************
 * Default DataSection class.
 * 
 * Supported types are given by dataTypes.
 * 
 * Currently DefaultData is only used for headers, and thus the list of supported types has been
 * reduced to just those used in headers. Load order is HIGH_LOW, i.e. existing entries aren't
 * overwritten.
 *************************************************************************************************/
/* The implementation now uses a fair bit of generic programming. Adjusting the types supported
* should be as simple as adjusting the list dataTypes, and possibly implemting new conversions in
* parseFrom and parseTo if you add new types (e.g. for cent or imaginary/complex types, or user types).
*
* There shouldn't really be any need to adjust the implementation, except perhaps to add new
* functions to the class (such as another type of output where the delegate used in writeAll isn't
* enough).
*/
class DefaultData : IDataSection
{
    //BEGIN META
    /* These functions are used to generate code. Compile-time functions rather than templates are
    * used because they are easier to write and understand. Mixins are used to compile the resultant
    * code. Must be declared before used since forward references aren't supported for compile-time
    * functions. */
    
    // Generate the correct name for each variable type.
    static char[] varName (char[] type) {
        char[] append = "";
        while (type.length >= 2 && type[$-2..$] == "[]") {
            type = type[0..$-2];
            append ~= "A";
        }
        return "_" ~ type ~ append;
    }
    
    // Int-to-string converter, which may not be efficient but will run at compile time.
    static char[] int2str (uint i) {
        char[] ret;
        const digits = "0123456789";
        if (i == 0) ret = "0";
        else for (; i > 0; i /= 10) ret = digits[i%10] ~ ret;
        return ret;
    }
    
    // Generate the code for variable declarations.
    static char[] declerations (char[][] types) {
        char[] ret = "";
        foreach (char[] type; types) ret ~= type ~ "[ID]\t" ~ varName(type) ~ ";\n";
        return ret;
    }
    
    // Purely to add indentation. Could just return "" without affecting functionality.
    static char[] indent (uint i) {
        char[] ret = "";
        for (; i > 0; --i) ret ~= "  ";
        // This is not executable at compile time:
        //ret.length = i * 4;		// number of characters for each indentation
        //ret[] = ' ';		// character to indent with
        return ret;
    }
    
    /* Generates a binary search algorithm.
    *
    * Currently this is tailored to it's particular use (addTag). */
    static char[] binarySearch (char[] var, char[][] consts, int indents = 0) {
        if (consts.length > 3) {
            return indent(indents) ~ "if (" ~ var ~ " <= \"" ~ consts[$/2 - 1] ~ "\") {\n" ~
                binarySearch (var, consts[0 .. $/2], indents + 1) ~
            indent(indents) ~ "} else {\n" ~
                binarySearch (var, consts[$/2 .. $], indents + 1) ~
            indent(indents) ~ "}\n";
        } else {
            char[] ret;
            ret ~= indent(indents);
            foreach (c; consts) {
                ret ~= "if (" ~ var ~ " == \"" ~ c ~ "\") {\n" ~
                    //indent(indents+1) ~ varName(c) ~ "[id] = parseTo!(" ~ c ~ ") (dt);\n" ~
                    indent(indents+1) ~ "if ((id in "~varName(c)~") is null)\n" ~
                    indent(indents+2) ~ varName(c)~"[id] = parseTo!(" ~ c ~ ") (dt);\n" ~
                indent(indents) ~ "} else ";
            }
            ret = ret[0..$-6] ~ '\n';  // remove last else
            return ret;
        }
    }
    
    // Generates the code to write data members (writeAll).
    static char[] writeVars () {
        char[] code = "";
        foreach (i,type; dataTypes) {
            code ~= "foreach (id, dt; " ~ varName(type) ~ ") itemdlg (dataTypes[" ~ int2str(i) ~ "], id, parseFrom!(" ~ type ~ ")(dt));\n";
        }
        return code;
    }
    //END META
    
    /** Data Members
    *
    * These types are all stored directly, as below, are available for direct access. The variable
    * names are created dynamically at compile-time based on the dataTypes list.
    * ------------------
    * int[ID] _int;		// name is type prefixed by _
    * char[][ID] _charA;	// [] is replaced by A
    * ------------------
    */
    /+ All types previously supported. Most of these weren't used.
    const char[][] dataTypes = ["bool","bool[]",
                                "byte","byte[]",
                                "char","char[]","char[][]",
                                "double","double[]",
                                "float","float[]",
                                "int","int[]",
                                "long","long[]",
                                "real","real[]",
                                "short","short[]",
                                "ubyte","ubyte[]",
                                "uint","uint[]",
                                "ulong","ulong[]",
                                "ushort","ushort[]"];
    +/
    const char[][] dataTypes = ["char[]", "char[][]"];
    
    mixin (declerations (dataTypes));	// Declare all the variables.
    
    void addTag (char[] type, ID id, char[] dt) {	/// Supports all types listed in dataTypes.
        mixin (binarySearch ("type", dataTypes));
    }
    
    void writeAll (ItemDelg itemdlg) {	/// Supports all types listed in dataTypes.
        mixin (writeVars ());
    }
}