# HG changeset patch # User Diggory Hardy # Date 1211544788 -3600 # Node ID e0839643ff52c636ac674cebe62a3bef9e78167a # Parent 03fa79a48c4874ab257f69e93ece4132592de0de 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 diff -r 03fa79a48c48 -r e0839643ff52 codeDoc/jobs.txt --- 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... diff -r 03fa79a48c48 -r e0839643ff52 data/conf/gui.mtt --- 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} - +! {W1} @@ -11,4 +11,4 @@ {WEmbedded} - + diff -r 03fa79a48c48 -r e0839643ff52 dsss.conf --- 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 diff -r 03fa79a48c48 -r e0839643ff52 mde/gui/Gui.d --- 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; diff -r 03fa79a48c48 -r e0839643ff52 mde/mergetag/Reader.d --- 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 " ~ 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); } diff -r 03fa79a48c48 -r e0839643ff52 mde/resource/paths.d --- 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 diff -r 03fa79a48c48 -r e0839643ff52 util/mtcp.d --- /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 . */ + +/************************************************************************************************** + * 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; +}