changeset 47:e0839643ff52

New mtcp utility and changes to paths. Changed some of mde.resource.paths and mde.mergetag.Reader's handling of resolving mergetag files, and allowed getting a list of files. Created a simple mergetag-file copy utility. Fixed mergetag's MTTReader getting array out of bounds exceptions (now throws a mergetag exception). committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 23 May 2008 13:13:08 +0100
parents 03fa79a48c48
children a98ffb64f066
files codeDoc/jobs.txt data/conf/gui.mtt dsss.conf mde/gui/Gui.d mde/mergetag/Reader.d mde/resource/paths.d util/mtcp.d
diffstat 7 files changed, 213 insertions(+), 98 deletions(-) [+]
line wrap: on
line diff
--- a/codeDoc/jobs.txt	Thu May 22 12:51:47 2008 +0100
+++ b/codeDoc/jobs.txt	Fri May 23 13:13:08 2008 +0100
@@ -6,8 +6,6 @@
 Implementing font rendering
 Reading ft tutorial
 Use cartesian coordinates? Or not?
-Make layout's adjustCellSizes method call setSize?
-More resizing stuff...
 
 
 
--- a/data/conf/gui.mtt	Thu May 22 12:51:47 2008 +0100
+++ b/data/conf/gui.mtt	Fri May 23 13:13:08 2008 +0100
@@ -1,5 +1,5 @@
 {MT01}
-<char[]|Renderer="Simple">
+!<char[]|Renderer="Simple">
 {W1}
 <int|x=30>
 <int|y=80>
@@ -11,4 +11,4 @@
 {WEmbedded}
 <int|x=20>
 <int|y=100>
-<int[][int]|widgetData=[1:[0x4010,50,50],2:[0xB004,3,1,3,1,3],3:[0x3001],0:[0xB004,3,1,2,1,2]]>
+<int[][int]|widgetData=[1:[0x4010,50,50],2:[0xB004,3,1,1,3,1],3:[0x3001],0:[0xB004,7,1,3,2,3,1,3,2,3]]>
--- a/dsss.conf	Thu May 22 12:51:47 2008 +0100
+++ b/dsss.conf	Fri May 23 13:13:08 2008 +0100
@@ -1,11 +1,7 @@
 # Copyright © 2007-2008 Diggory Hardy
 # License: GNU General Public License version 2 or later (see COPYING)
 
-version (Posix) {
-    defaulttargets = mde/mde.d
-} else version (Win32) {
-    defaulttargets = mde\mde.d
-}
+defaulttargets = mde/mde.d
 
 [*]
 version (Posix) {
@@ -15,8 +11,8 @@
 [mde/mde.d]
 target=bin/mde
 
-#[mde\mde.d]
-#target=bin\mde
+[util/mtcp.d]
+target=bin/mtcp
 
 [test/mdeTest.d]
 buildflags=-debug -debug=mdeUnitTest -unittest
--- a/mde/gui/Gui.d	Thu May 22 12:51:47 2008 +0100
+++ b/mde/gui/Gui.d	Fri May 23 13:13:08 2008 +0100
@@ -63,11 +63,17 @@
         }
         
         IReader reader;
-        reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true);
-        reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) {
-            return new Window (id);
-        };
-        reader.read;
+        try {
+            reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true);
+            reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) {
+                return new Window (id);
+            };
+            reader.read;
+        } catch (Exception e) {
+            logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):");
+            logger.error (e.msg);
+            throw new GuiException ("Failure parsing config file");
+        }
         
         // Get the renderer
         char[]* p = RENDERER in reader.dataset.header.Arg!(char[]).Arg;
--- a/mde/mergetag/Reader.d	Thu May 22 12:51:47 2008 +0100
+++ b/mde/mergetag/Reader.d	Fri May 23 13:13:08 2008 +0100
@@ -16,7 +16,6 @@
 /**************************************************************************************************
  * This module contains all reading functions, for both binary and text MergeTag files.
  *************************************************************************************************/
-
 module mde.mergetag.Reader;
 
 // package imports
@@ -45,48 +44,42 @@
 
 /** Make an IReader class.
 *
-* If no extension is given, search for a file using each extension (.mtt and .mtb) appended to
-* path, and set path to the most recent file name. If neither suffix added resolves a valid file,
-* throw an MTFileIOException with a suitible error message.
-*
-* When an extension is available (either after the above or when supplied), use the appropriate
-* reader (MTT or MTB).
+* Create an appropriate reader: MTTReader or MTBReader.
 *
 * Throws:
 *  $(TABLE
 *  $(TR $(TH Exception) $(TH Thrown when))
-*  $(TR $(TD MTFileFormatException) $(TD Unable to determine format (only analysing file name)))
-*  $(TR $(TD MTFileIOException) $(TD When no extension is given, neither appending .mtt nor
-*   appending .mtb resolves a valid file))
+*  $(TR $(TD MTFileIOException) $(TD When extension given is neither mtt nor mtb))
 *  )
 *
 */
-IReader makeReader (char[] path, DataSet ds = null, bool rdHeader = false) {
-    return makeReader (new FilePath(path), ds, rdHeader);
-}
 IReader makeReader (PathView path, DataSet ds = null, bool rdHeader = false) {
-    if (path.ext.length == 0) {
-        PathView tPath = new FilePath (path.toString ~ ".mtt");
-        PathView bPath = new FilePath (path.toString ~ ".mtb");
-        
-        bool bPathExists = bPath.exists;
-        
-        if (tPath.exists) {
-            if (bPathExists) {
-                // take the latest version (roughly speaking...)
-                path = tPath.modified > bPath.modified ? tPath : bPath;
-            } else path = tPath;
-        } else {
-            if (bPathExists) path = bPath;
-            else {
-                throw new MTFileIOException ("No file exists: "~path.toString~"[.mtt|.mtb]");
-            }
-        }
-    }
-    
     if      (path.ext == "mtb") return new MTBReader (path, ds, rdHeader);
     else if (path.ext == "mtt") return new MTTReader (path, ds, rdHeader);
-    else throw new MTFileFormatException;
+    else throw new MTFileIOException ("Invalid mergetag extension");
+}
+
+/** Resolve a file path.
+ *
+ * Tries adding both ".mtt" and ".mtb" extensions, returning whichever exists (the most recently
+ * modified if both exist), or returns null if neither exist. */
+PathView findFile (char[] path) {
+    if (path is null) return null;
+    
+    FilePath tPath = new FilePath (path ~ ".mtt");
+    FilePath bPath = new FilePath (path ~ ".mtb");
+        
+    bool bPathExists = bPath.exists;
+        
+    if (tPath.exists) {
+        if (bPathExists) {
+                // take the latest version (roughly speaking...)
+            return (tPath.modified > bPath.modified ? tPath : bPath);
+        } else return tPath;
+    } else {
+        if (bPathExists) return bPath;
+        else return null;
+    }
 }
 
 /**
@@ -166,7 +159,7 @@
     
     IDataSection delegate (ID) _dataSecCreator = null;   // see property setter above
     
-    uint endOfHeader;
+    size_t endOfHeader;
     bool allRead = false;		// true if endOfHeader == fbuf.length or read([]) has run
     bool fatal = false;			// a fatal file error occured; don't try to recover
     /* If the file is scanned for sections, the starting position of all sections are stored
@@ -174,13 +167,13 @@
     * or a section scan has not been run (read() with no section names doesn't need to do so).
     */
     struct SecMD {	// sec meta data
-        static SecMD opCall (uint _pos, bool _read) {
+        static SecMD opCall (size_t _pos, bool _read) {
             SecMD ret;
             ret.pos = _pos;
             ret.read = _read;
             return ret;
         }
-        uint pos;			// position to start reading
+        size_t pos;			// position to start reading
         bool read;			// true if already read
     }
     SecMD [ID] secTable;
@@ -301,7 +294,7 @@
                 }
             }
         } else {					// this time we don't need to use secTable
-            for (uint pos = endOfHeader; pos < fbuf.length;) {
+            for (size_t pos = endOfHeader; pos < fbuf.length;) {
                 ID id = fbufReadSecMarker (pos);
                 IDataSection ds = getOrCreateSec (id);
                 pos = parseSection (pos, ds);
@@ -330,7 +323,7 @@
                 }
             }
         } else {
-            for (uint pos = endOfHeader; pos < fbuf.length;) {
+            for (size_t pos = endOfHeader; pos < fbuf.length;) {
                 ID id = fbufReadSecMarker (pos);
                 secTable[id] = SecMD(pos,false);	// add to table
                 if (secSet.contains(id)) {
@@ -371,24 +364,28 @@
     NOTE: from performance tests on indexing char[]'s and dereferencing char*'s, the char*'s are
     slightly faster, but a tiny difference isn't worth the extra effort/risk of using char*'s.
     */
-    private uint parseSection (uint pos, IDataSection dsec) {
+    private size_t parseSection (size_t pos, IDataSection dsec) {
+        debug scope (failure)
+                logger.trace ("MTTReader.parseSection: failure");
         /* Searches fbuf starting from start to find one of <=>| and stops at its index.
     
         If quotable then be quote-aware for single and double quotes.
         Note: there's no length restriction for the content of the quote since it could be a single
         non-ascii UTF-8 char which would look like several chars.
         */
-        void fbufLocateDataTagChar (ref uint pos, bool quotable) {
-            for (; pos < fbuf.length; ++pos) {
+        void fbufLocateDataTagChar (ref size_t pos, bool quotable) {
+            while (true) {
+                fbufIncrement (pos);
+                
                 if ((fbuf[pos] >= '<' && fbuf[pos] <= '>') || fbuf[pos] == '|') return;
                 else if (quotable) {
                     char c = fbuf[pos];
                     if (c == '\'' || c == '"') {
-                        ++pos;
+                        fbufIncrement(pos);
                         while (fbuf[pos] != c) {
                             if (fbuf[pos] == '\\') ++pos;	// escape seq.
                             fbufIncrement(pos);
-                        } 
+                        }
                     }
                 }
             }
@@ -401,26 +398,20 @@
             else if (fbuf[pos] == '<') {		// data tag
                 char[] ErrDTAG = "Bad data tag format: not <type|id=data>" ~ ErrInFile;
                 
-                fbufIncrement (pos);
-                
                 // Type section of tag:
-                uint pos_s = pos;
+                size_t pos_s = pos + 1;
                 fbufLocateDataTagChar (pos, false);	// find end of type section
                 if (fbuf[pos] != '|') throwMTErr (ErrDTAG, new MTSyntaxException);
                 char[] type = fbuf[pos_s..pos];
                 
-                fbufIncrement (pos);
-                
                 // ID section of tag:
-                pos_s = pos;
+                pos_s = pos + 1;
                 fbufLocateDataTagChar (pos, false);	// find end of type section
                 if (fbuf[pos] != '=') throwMTErr (ErrDTAG, new MTSyntaxException);
                 ID tagID = cast(ID) fbuf[pos_s..pos];
                 
-                fbufIncrement (pos);
-                
                 // Data section of tag:
-                pos_s = pos;
+                pos_s = pos + 1;
                 fbufLocateDataTagChar (pos, true);      // find end of data section
                 if (fbuf[pos] != '>') throwMTErr (ErrDTAG, new MTSyntaxException);
                 char[] data = fbuf[pos_s..pos];
@@ -433,6 +424,7 @@
                     catch (TextException e) {
                         logger.error ("TextException while reading " ~ ErrFile ~ ":");	// following a parse error
                         logger.error (e.msg);
+                        // No throw: tag is just ignored
                     }
                     catch (Exception e) {
                         logger.error ("Unknown error occured" ~ ErrInFile ~ ':');
@@ -470,12 +462,12 @@
     
     /* Parses fbuf for a section marker. Already knows fbuf[pos] == '{'.
     */
-    private ID fbufReadSecMarker (ref uint pos) {
+    private ID fbufReadSecMarker (ref size_t pos) {
         // at this point pos is whatever a parseSection run returned
         // since we haven't hit EOF, fbuf[pos] MUST be '{' so no need to check
         fbufIncrement(pos);
         
-        uint start = pos;
+        size_t start = pos;
         for (; pos < fbuf.length; ++pos)
             if (fbuf[pos] == '}' || fbuf[pos] == '{') break;
         
@@ -488,7 +480,7 @@
     }
     
     /* Increments pos and checks it hasn't hit fbuf.length . */
-    private void fbufIncrement(ref uint pos) {
+    private void fbufIncrement(ref size_t pos) {
         ++pos;
         if (pos >= fbuf.length) throwMTErr("Unexpected EOF" ~ ErrInFile, new MTSyntaxException);
     }
--- a/mde/resource/paths.d	Thu May 22 12:51:47 2008 +0100
+++ b/mde/resource/paths.d	Fri May 23 13:13:08 2008 +0100
@@ -72,16 +72,11 @@
     */
     IReader makeMTReader (char[] file, PRIORITY readOrder, DataSet ds = null, bool rdHeader = false)
     {
-        if (readOrder == PRIORITY.HIGH_ONLY) {
-            foreach_reverse (path; paths) {     // starting with highest-priority path...
-                try {
-                    return makeReader (path~file, ds, rdHeader);
-                }
-                catch (MTFileIOException) {}    // Ignore errors regarding no file for now.
-            }
+        PathView[] files = getFiles (file, readOrder);
+        if (files is null)
             throw new MTFileIOException ("Unable to find the file: "~file~"[.mtt|mtb]");
-        }
-        else return new mdeReader (file, readOrder, ds, rdHeader, paths);
+        
+        return new mdeReader (files, ds, rdHeader);
     }
     
     /** Creates an MT writer for file deciding on the best path to use.
@@ -97,6 +92,20 @@
         return makeWriter (paths[pathsLen-1] ~ file, ds, WriterMethod.Text);
     }
     
+    /** Returns a string listing the file name or names (if readOrder is not HIGH_ONLY and multiple
+      * matches are found), or "no file found". Intended for user output only. */
+    char[] getFileName (char[] file, PRIORITY readOrder)
+    {
+        PathView[] files = getFiles (file, readOrder);
+        if (files is null)
+            return "no file found";
+        
+        char[] ret = files[0].toString;
+        foreach (f; files[1..$])
+            ret ~= ", " ~ f.toString;
+        return ret;
+    }
+    
     /** Check whether the given file exists under any path with either .mtt or .mtb suffix. */
     bool exists (char[] file) {
         for (uint i = 0; i < pathsLen; ++i) {
@@ -107,6 +116,30 @@
     }
     
 private:
+    PathView[] getFiles (char[] filename, PRIORITY readOrder)
+    in {
+        assert (readOrder == PRIORITY.LOW_HIGH ||
+                readOrder == PRIORITY.HIGH_LOW ||
+                readOrder == PRIORITY.HIGH_ONLY );
+    } body {
+        PathView[] ret;
+        if (readOrder == PRIORITY.LOW_HIGH) {
+            for (size_t i = 0; i < pathsLen; ++i) {
+                PathView file = findFile (paths[i]~filename);
+                if (file !is null)
+                    ret ~= file;
+            }
+        } else {
+            for (int i = pathsLen - 1; i >= 0; --i) {
+                PathView file = findFile (paths[i]~filename);
+                if (file !is null) {
+                    ret ~= file;
+                    if (readOrder == PRIORITY.HIGH_ONLY) break;
+                }
+            }
+        }
+        return ret;
+    }
     
     // Unconditionally add a path
     void addPath (char[] path) {
@@ -238,27 +271,17 @@
 */
 class mdeReader : IReader
 {
-    private this (char[] file, PRIORITY readOrder, DataSet ds, bool rdHeader, char[][MAX_PATHS] paths)
-    in { assert (readOrder == PRIORITY.LOW_HIGH || readOrder == PRIORITY.HIGH_LOW); }
-    body {
-        rdOrder = readOrder;
+    private this (PathView[] files, DataSet ds, bool rdHeader)
+    in {
+        assert (files !is null, "mdeReader.this: files is null");
+    } body {
         if (ds is null) ds = new DataSet;
         
-        foreach (path; paths) {
-            try {
-                IReader r = makeReader (path~file, ds, rdHeader);
-                
-                readers[readersLen++] = r;
-            }
-            catch (MTFileIOException) {}    // Ignore errors regarding no file for now.
+        foreach (file; files) {
+            IReader r = makeReader (file, ds, rdHeader);
+            
+            readers[readersLen++] = r;
         }
-        
-        if (readersLen == 0) {          // totally failed to find any valid files
-            throw new MTFileIOException ("Unable to find the file: "~file~"[.mtt|mtb]");
-        }
-        
-        // This is simply the easiest way of adjusting the reading order:
-        if (readOrder == PRIORITY.HIGH_LOW) readers[0..readersLen].reverse;
     }
     
     DataSet dataset () {                /// Get the DataSet
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/mtcp.d	Fri May 23 13:13:08 2008 +0100
@@ -0,0 +1,100 @@
+/* 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/>. */
+
+/**************************************************************************************************
+ * A small program for reading and writing mergetag files.
+ *
+ * Note that it's limited to copying data types which DefaultData can handle due to the nature of
+ * mergetag's extendability.
+ *************************************************************************************************/
+module ut.mtcp;
+
+import mde.mergetag.Reader;
+import mde.mergetag.Writer;
+import mde.mergetag.exception;
+
+import tango.io.Stdout;
+import tango.io.FilePath;
+
+// Logger, used by mergetag:
+import tango.util.log.Log : Log, Logger;
+import tango.util.log.ConsoleAppender : ConsoleAppender;
+static this() {
+    Logger root = Log.getRootLogger();
+    root.addAppender(new ConsoleAppender);
+}
+
+int main (char[][] args)
+{
+    char[] inFile, outFile;
+    bool badOpts = false;
+    bool helpOnly = false;
+    
+    foreach (arg; args[1..$]) {
+        if (arg == "-h" || arg == "--help")
+            helpOnly = true;
+        else {
+            if (inFile is null)
+                inFile = arg;
+            else if (outFile is null)
+                outFile = arg;
+            else {
+                badOpts = true;
+                helpOnly = true;
+            }
+        }
+    }
+    if (outFile is null)
+        helpOnly = badOpts = true;
+    if (helpOnly) {
+        if (badOpts)
+            Stdout ("Error: bad options!").newline;
+        Stdout ("Usage:").newline.opCall(args[0])(" [options] input_file output_file").newline;
+        Stdout ("Options:").newline.opCall("-h\t--help\tPrint help message").newline;
+        if (badOpts)
+            return 1;
+        else return 0;
+    }
+    
+    IReader reader;
+    try {
+        reader = makeReader (FilePath(inFile), null, true);
+        reader.read;
+    } catch (MTException mte) {
+        Stdout ("While reading "~inFile~":").newline;
+        Stdout ("Mergetag exception:").newline.opCall(mte.msg).newline;
+        return 2;
+    } catch (Exception e) {
+        Stdout ("While reading "~inFile~":").newline;
+        Stdout ("Unhandled exception:").newline.opCall(e.msg).newline;
+        return 2;
+    }
+    
+    try {
+        IWriter writer = makeWriter (outFile, reader.dataset, WriterMethod.FromExtension);
+        writer.write;
+    } catch (MTException mte) {
+        Stdout ("While writing "~outFile~":").newline;
+        Stdout ("Mergetag exception:").newline.opCall(mte.msg).newline;
+        return 3;
+    } catch (Exception e) {
+        Stdout ("While writing "~outFile~":").newline;
+        Stdout ("Unhandled exception:").newline.opCall(e.msg).newline;
+        return 3;
+    }
+    
+    Stdout ("Copy successful: "~inFile~" -> "~outFile).newline;
+    return 0;
+}