mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-27 14:12:16 +00:00
0366f13c9a
UDMF map parser should work ~35% faster now. Texture browser form: keyboard focus was not updated when switching between textures using Tab key. "Graphics" folder is now checked when searching for texture patches. Various cosmetic changes here and there.
855 lines
24 KiB
C#
855 lines
24 KiB
C#
|
|
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
|
|
|
/*
|
|
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
|
* This program is released under GNU General Public License
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#endregion
|
|
|
|
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Globalization;
|
|
using System.Collections.Generic;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.IO
|
|
{
|
|
public sealed class UniversalParser
|
|
{
|
|
#region ================== Constants
|
|
|
|
// Path seperator
|
|
public const string DEFAULT_SEPERATOR = ".";
|
|
|
|
// Allowed characters in a key
|
|
public const string KEY_CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789_";
|
|
|
|
// Parse mode constants
|
|
private const int PM_NOTHING = 0;
|
|
private const int PM_ASSIGNMENT = 1;
|
|
private const int PM_NUMBER = 2;
|
|
private const int PM_STRING = 3;
|
|
private const int PM_KEYWORD = 4;
|
|
|
|
// Error strings
|
|
private const string ERROR_KEYMISSING = "Missing key name in assignment or scope.";
|
|
private const string ERROR_KEYCHARACTERS = "Invalid characters in key name.";
|
|
//private const string ERROR_ASSIGNINVALID = "Invalid assignment. Missing a previous terminator symbol?";
|
|
private const string ERROR_VALUEINVALID = "Invalid value in assignment. Missing a previous terminator symbol?";
|
|
private const string ERROR_VALUETOOBIG = "Value too big.";
|
|
private const string ERROR_KEYWITHOUTVALUE = "Key has no value assigned.";
|
|
private const string ERROR_KEYWORDUNKNOWN = "Unknown keyword in assignment. Missing a previous terminator symbol?";
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// Error result
|
|
private int cpErrorResult = 0;
|
|
private string cpErrorDescription = "";
|
|
private int cpErrorLine = 0;
|
|
|
|
// Configuration root
|
|
private UniversalCollection root = null;
|
|
|
|
private const string numbers = "0123456789";
|
|
private const string numbers2 = "0123456789-.&";
|
|
private const string newline = "\n";
|
|
private char[] newlineChar = new[] {'\n'};
|
|
private StringBuilder key; //mxd
|
|
private StringBuilder val; //mxd
|
|
private Dictionary<string, UniversalEntry> matches; //mxd
|
|
|
|
// Settings
|
|
private bool strictchecking = true;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
// Properties
|
|
public int ErrorResult { get { return cpErrorResult; } }
|
|
public string ErrorDescription { get { return cpErrorDescription; } }
|
|
public int ErrorLine { get { return cpErrorLine; } }
|
|
public UniversalCollection Root { get { return root; } }
|
|
public bool StrictChecking { get { return strictchecking; } set { strictchecking = value; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor / Destructor
|
|
|
|
// Constructor
|
|
public UniversalParser()
|
|
{
|
|
// Standard new configuration
|
|
NewConfiguration();
|
|
|
|
// We have no destructor
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
// Constructor to load a file immediately
|
|
public UniversalParser(string filename)
|
|
{
|
|
// Load configuration from file
|
|
LoadConfiguration(filename);
|
|
|
|
// We have no destructor
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Private Methods
|
|
|
|
// This returns a string added with escape characters
|
|
private string EscapedString(string str)
|
|
{
|
|
// Replace the \ with \\ first!
|
|
str = str.Replace("\\", "\\\\");
|
|
str = str.Replace(newline, "\\n");
|
|
str = str.Replace("\r", "\\r");
|
|
str = str.Replace("\t", "\\t");
|
|
str = str.Replace("\"", "\\\"");
|
|
|
|
// Return result
|
|
return str;
|
|
}
|
|
|
|
|
|
// This raises an error
|
|
private void RaiseError(int line, string description)
|
|
{
|
|
// Raise error
|
|
cpErrorResult = 1;
|
|
cpErrorDescription = description;
|
|
cpErrorLine = line;
|
|
}
|
|
|
|
|
|
// This validates a given key and sets
|
|
// error properties if key is invalid and errorline > -1
|
|
private bool ValidateKey(string key, int errorline)
|
|
{
|
|
bool validateresult = true;
|
|
|
|
// Check if key is an empty string
|
|
if(key.Length == 0)
|
|
{
|
|
// ERROR: Missing key name in statement
|
|
if(errorline > -1) RaiseError(errorline, ERROR_KEYMISSING);
|
|
validateresult = false;
|
|
}
|
|
else if(strictchecking) //Only when strict checking
|
|
{
|
|
// Check if all characters are valid
|
|
foreach(char c in key)
|
|
{
|
|
if(KEY_CHARACTERS.IndexOf(c) == -1)
|
|
{
|
|
// ERROR: Invalid characters in key name
|
|
if(errorline > -1) RaiseError(errorline, ERROR_KEYCHARACTERS);
|
|
validateresult = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return result
|
|
return validateresult;
|
|
}
|
|
|
|
|
|
// This parses a structure in the given data starting
|
|
// from the given pos and line and updates pos and line.
|
|
private UniversalCollection InputStructure(ref string[] data, ref int pos, ref int line, bool topLevel)
|
|
{
|
|
char c = '\0'; // current data character
|
|
int pm = PM_NOTHING; // current parse mode
|
|
key.Remove(0, key.Length);
|
|
val.Remove(0, val.Length);
|
|
string s;
|
|
bool escape = false; // escape sequence?
|
|
bool endofstruct = false; // true as soon as this level struct ends
|
|
UniversalCollection cs = new UniversalCollection();
|
|
|
|
// Go through all of the data until
|
|
// the end or until the struct closes
|
|
// or when an arror occurred
|
|
while ((cpErrorResult == 0) && (endofstruct == false))
|
|
{
|
|
// Get current character
|
|
if(line == data.Length - 1) break;
|
|
if(pos > data[line].Length - 1) {
|
|
pos = 0;
|
|
line++;
|
|
}
|
|
|
|
c = data[line][pos];
|
|
|
|
// ================ What parse mode are we at?
|
|
if(pm == PM_NOTHING)
|
|
{
|
|
// Now check what character this is
|
|
switch(c)
|
|
{
|
|
case '{': // Begin of new struct
|
|
// Validate key
|
|
s = key.ToString().Trim();
|
|
if(ValidateKey(s, line))
|
|
{
|
|
// Next character
|
|
pos++;
|
|
|
|
// Parse this struct and add it
|
|
cs.Add(new UniversalEntry(s.ToLowerInvariant(), InputStructure(ref data, ref pos, ref line, false)));
|
|
|
|
// Check the last character
|
|
pos--;
|
|
|
|
// Reset the key
|
|
key.Remove(0, key.Length);
|
|
}
|
|
|
|
// Leave switch
|
|
break;
|
|
|
|
case '}': // End of this struct
|
|
// Stop parsing in this struct
|
|
endofstruct = true;
|
|
|
|
// Leave the loop
|
|
break;
|
|
|
|
case '=': // Assignment
|
|
// Validate key
|
|
if(ValidateKey(key.ToString().Trim(), line))
|
|
{
|
|
// Now parsing assignment
|
|
pm = PM_ASSIGNMENT;
|
|
}
|
|
|
|
// Leave switch
|
|
break;
|
|
|
|
case ';': // Terminator
|
|
|
|
// Validate key
|
|
if(ValidateKey(key.ToString().Trim(), line))
|
|
{
|
|
// Error: No value
|
|
RaiseError(line, ERROR_KEYWITHOUTVALUE);
|
|
}
|
|
|
|
// Leave switch
|
|
break;
|
|
|
|
case '\n': // New line
|
|
// Count the line
|
|
line++;
|
|
pos = -1;
|
|
|
|
// Add this to the key as a space.
|
|
// Spaces are not allowed, but it will be trimmed
|
|
// when its the first or last character.
|
|
key.Append(" ");
|
|
|
|
// Leave switch
|
|
break;
|
|
|
|
case '\\': // Possible comment
|
|
case '/':
|
|
// Check for the line comment //
|
|
if(data[line].Substring(pos, 2) == "//")
|
|
{
|
|
// Have next line?
|
|
if(line < data.Length)
|
|
{
|
|
// Count the line
|
|
line++;
|
|
|
|
// Skip everything on this line
|
|
pos = -1;
|
|
}
|
|
else
|
|
{
|
|
// No end of line
|
|
// Skip everything else
|
|
pos = data.Length;
|
|
}
|
|
}
|
|
// Check for the block comment /* */
|
|
else if(data[line].Substring(pos, 2) == "/*")
|
|
{
|
|
// Find the next closing block comment
|
|
int np = data[line].IndexOf("*/", pos);
|
|
|
|
// Closing block comment found?
|
|
if(np > -1)
|
|
{
|
|
// Count the lines in the block comment
|
|
string blockdata = data[line].Substring(pos, np - pos + 2);
|
|
line += (blockdata.Split(newlineChar).Length - 1);
|
|
|
|
// Skip everything in this block
|
|
pos = np + 1;
|
|
}
|
|
else
|
|
{
|
|
// No end of line
|
|
// Skip everything else
|
|
pos = data.Length;
|
|
}
|
|
}
|
|
|
|
// Leave switch
|
|
break;
|
|
|
|
default: // Everything else
|
|
if (!topLevel && pos == 0) {
|
|
while(matches.ContainsKey(data[line])) {
|
|
cs.Add(matches[data[line]].Key, matches[data[line]].Value);
|
|
line++;
|
|
pos = -1;
|
|
}
|
|
}
|
|
|
|
// Add character to key
|
|
if(pos != -1) key.Append(c);
|
|
|
|
// Leave switch
|
|
break;
|
|
}
|
|
}
|
|
// ================ Parsing an assignment
|
|
else if(pm == PM_ASSIGNMENT)
|
|
{
|
|
// Check for string opening
|
|
if(c == '\"')
|
|
{
|
|
// Now parsing string
|
|
pm = PM_STRING; //numbers
|
|
}
|
|
// Check for numeric character numbers
|
|
else if(numbers2.IndexOf(c) > -1)
|
|
{
|
|
// Now parsing number
|
|
pm = PM_NUMBER;
|
|
|
|
// Go one byte back, because this
|
|
// byte is part of the number!
|
|
pos--;
|
|
}
|
|
// Check for new line
|
|
else if(c == '\n')
|
|
{
|
|
// Count the new line
|
|
line++;
|
|
}
|
|
// Check if assignment ends
|
|
else if(c == ';')
|
|
{
|
|
// End of assignment
|
|
pm = PM_NOTHING;
|
|
|
|
// Remove this if it causes problems
|
|
key.Remove(0, key.Length);
|
|
val.Remove(0, val.Length);
|
|
}
|
|
// Otherwise (if not whitespace) it will be a keyword
|
|
else if((c != ' ') && (c != '\t'))
|
|
{
|
|
// Now parsing a keyword
|
|
pm = PM_KEYWORD;
|
|
|
|
// Go one byte back, because this
|
|
// byte is part of the keyword!
|
|
pos--;
|
|
}
|
|
}
|
|
// ================ Parsing a number
|
|
else if(pm == PM_NUMBER)
|
|
{
|
|
// Check if number ends here
|
|
if(c == ';')
|
|
{
|
|
// Hexadecimal?
|
|
s = val.ToString();
|
|
if((s.Length > 2) && s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
// Convert to int
|
|
try
|
|
{
|
|
// Convert to value
|
|
int ival = System.Convert.ToInt32(s.Substring(2).Trim(), 16);
|
|
|
|
// Add it to struct
|
|
UniversalEntry entry = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), ival);
|
|
cs.Add(entry);
|
|
matches.Add(data[line], entry);
|
|
}
|
|
catch(System.OverflowException)
|
|
{
|
|
// Too large for Int32, try Int64
|
|
try
|
|
{
|
|
// Convert to value
|
|
long lval = System.Convert.ToInt64(s.Substring(2).Trim(), 16);
|
|
|
|
// Add it to struct
|
|
UniversalEntry entry = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), lval);
|
|
cs.Add(entry);
|
|
matches.Add(data[line], entry);
|
|
}
|
|
catch(System.OverflowException)
|
|
{
|
|
// Too large for Int64, return error
|
|
RaiseError(line, ERROR_VALUETOOBIG);
|
|
}
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
}
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
}
|
|
// Floating point?
|
|
else if(s.IndexOf('.') > -1)
|
|
{
|
|
float fval = 0;
|
|
|
|
// Convert to float (remove the f first)
|
|
try { fval = System.Convert.ToSingle(s.Trim(), CultureInfo.InvariantCulture); }
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
|
|
// Add it to struct
|
|
UniversalEntry entry = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), fval);
|
|
cs.Add(entry);
|
|
matches.Add(data[line], entry);
|
|
}
|
|
else
|
|
{
|
|
// Convert to int
|
|
try
|
|
{
|
|
// Convert to value
|
|
int ival = System.Convert.ToInt32(s.Trim(), CultureInfo.InvariantCulture);
|
|
|
|
// Add it to struct
|
|
UniversalEntry entry = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), ival);
|
|
cs.Add(entry);
|
|
matches.Add(data[line], entry);
|
|
}
|
|
catch(System.OverflowException)
|
|
{
|
|
// Too large for Int32, try Int64
|
|
try
|
|
{
|
|
// Convert to value
|
|
long lval = System.Convert.ToInt64(s.Trim(), CultureInfo.InvariantCulture);
|
|
|
|
// Add it to struct
|
|
UniversalEntry entry = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), lval);
|
|
cs.Add(entry);
|
|
matches.Add(data[line], entry);
|
|
}
|
|
catch(System.OverflowException)
|
|
{
|
|
// Too large for Int64, return error
|
|
RaiseError(line, ERROR_VALUETOOBIG);
|
|
}
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
}
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
}
|
|
|
|
// Reset key and value
|
|
key.Remove(0, key.Length);
|
|
val.Remove(0, val.Length);
|
|
|
|
// End of assignment
|
|
pm = PM_NOTHING;
|
|
}
|
|
// Check for new line
|
|
else if(c == '\n')
|
|
{
|
|
// Count the new line
|
|
line++;
|
|
pos = -1;
|
|
}
|
|
// Everything else is part of the value
|
|
else
|
|
{
|
|
val.Append(c);
|
|
}
|
|
}
|
|
// ================ Parsing a string
|
|
else if(pm == PM_STRING)
|
|
{
|
|
// Check if in an escape sequence
|
|
if(escape)
|
|
{
|
|
// What character?
|
|
switch(c)
|
|
{
|
|
case '\\': val.Append('\\'); break;
|
|
case 'n': val.Append(newline); break;
|
|
case '\"': val.Append('\"'); break;
|
|
case 'r': val.Append('\r'); break;
|
|
case 't': val.Append('\t'); break;
|
|
default:
|
|
// Is it a number?
|
|
if(numbers.IndexOf(c) > -1)
|
|
{
|
|
int vv = 0;
|
|
char vc = '0';
|
|
|
|
// Convert the next 3 characters to a number
|
|
string v = data[line].Substring(pos, 3);
|
|
try { vv = System.Convert.ToInt32(v.Trim(), CultureInfo.InvariantCulture); }
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
|
|
// Convert the number to a char
|
|
try { vc = System.Convert.ToChar(vv, CultureInfo.InvariantCulture); }
|
|
catch(System.FormatException)
|
|
{
|
|
// ERROR: Invalid value in assignment
|
|
RaiseError(line, ERROR_VALUEINVALID);
|
|
}
|
|
|
|
// Add the char
|
|
val.Append(vc);
|
|
}
|
|
else
|
|
{
|
|
// Add the character as it is
|
|
val.Append(c);
|
|
}
|
|
|
|
// Leave switch
|
|
break;
|
|
}
|
|
|
|
// End of escape sequence
|
|
escape = false;
|
|
}
|
|
else
|
|
{
|
|
// Check for sequence start
|
|
if(c == '\\')
|
|
{
|
|
// Next character is of escape sequence
|
|
escape = true;
|
|
}
|
|
// Check if string ends
|
|
else if(c == '\"')
|
|
{
|
|
// Add string to struct
|
|
UniversalEntry entry = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), val.ToString());
|
|
cs.Add(entry);
|
|
matches.Add(data[line], entry);
|
|
|
|
// End of assignment
|
|
pm = PM_ASSIGNMENT;
|
|
|
|
// Reset key and value
|
|
key.Remove(0, key.Length);
|
|
val.Remove(0, val.Length);
|
|
}
|
|
// Check for new line
|
|
else if(c == '\n')
|
|
{
|
|
// Count the new line
|
|
line++;
|
|
pos = -1;
|
|
}
|
|
// Everything else is just part of string
|
|
else
|
|
{
|
|
// Add to value
|
|
val.Append(c);
|
|
}
|
|
}
|
|
}
|
|
// ================ Parsing a keyword
|
|
else if(pm == PM_KEYWORD)
|
|
{
|
|
// Check if keyword ends
|
|
if(c == ';')
|
|
{
|
|
// Add to the struct depending on the keyword
|
|
switch(val.ToString().Trim().ToLowerInvariant())
|
|
{
|
|
case "true":
|
|
// Add boolean true
|
|
UniversalEntry t = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), true);
|
|
cs.Add(t);
|
|
matches.Add(data[line], t);
|
|
break;
|
|
|
|
case "false":
|
|
// Add boolean false
|
|
UniversalEntry f = new UniversalEntry(key.ToString().Trim().ToLowerInvariant(), false);
|
|
cs.Add(f);
|
|
matches.Add(data[line], f);
|
|
break;
|
|
|
|
default:
|
|
// Unknown keyword
|
|
RaiseError(line, ERROR_KEYWORDUNKNOWN + "\nUnrecognized token: '" + val.ToString().Trim().ToLowerInvariant() + "'");
|
|
break;
|
|
}
|
|
|
|
// End of assignment
|
|
pm = PM_NOTHING;
|
|
|
|
// Reset key and value
|
|
key.Remove(0, key.Length);
|
|
val.Remove(0, val.Length);
|
|
}
|
|
// Check for new line
|
|
else if(c == '\n')
|
|
{
|
|
// Count the new line
|
|
line++;
|
|
pos = -1;
|
|
}
|
|
// Everything else is just part of keyword
|
|
else
|
|
{
|
|
// Add to value
|
|
val.Append(c);
|
|
}
|
|
}
|
|
|
|
// Next character
|
|
pos++;
|
|
}
|
|
|
|
// Return the parsed result
|
|
return cs;
|
|
}
|
|
|
|
|
|
// This will create a data structure from the given object
|
|
private string OutputStructure(UniversalCollection cs, int level, string newline, bool whitespace)
|
|
{
|
|
string leveltabs = "";
|
|
string spacing = "";
|
|
StringBuilder db = new StringBuilder("");
|
|
|
|
// Check if this ConfigStruct is not empty
|
|
if(cs.Count > 0)
|
|
{
|
|
// Create whitespace
|
|
if(whitespace)
|
|
{
|
|
for(int i = 0; i < level; i++) leveltabs += "\t";
|
|
spacing = " ";
|
|
}
|
|
|
|
// Get enumerator
|
|
IEnumerator<UniversalEntry> de = cs.GetEnumerator();
|
|
|
|
// Go for each item
|
|
for(int i = 0; i < cs.Count; i++)
|
|
{
|
|
// Go to next item
|
|
de.MoveNext();
|
|
|
|
// Check if the value if of collection type
|
|
if(de.Current.Value is UniversalCollection)
|
|
{
|
|
UniversalCollection c = (UniversalCollection)de.Current.Value;
|
|
|
|
// Output recursive structure
|
|
if(whitespace) { db.Append(leveltabs); db.Append(newline); }
|
|
db.Append(leveltabs); db.Append(de.Current.Key);
|
|
if(!string.IsNullOrEmpty(c.Comment))
|
|
{
|
|
if(whitespace) db.Append("\t");
|
|
db.Append("// " + c.Comment);
|
|
}
|
|
db.Append(newline);
|
|
db.Append(leveltabs); db.Append("{"); db.Append(newline);
|
|
db.Append(OutputStructure(c, level + 1, newline, whitespace));
|
|
db.Append(leveltabs); db.Append("}"); db.Append(newline);
|
|
if(whitespace) { db.Append(leveltabs); db.Append(newline); }
|
|
}
|
|
// Check if the value is of boolean type
|
|
else if(de.Current.Value is bool)
|
|
{
|
|
db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("="); db.Append(spacing);
|
|
db.Append((bool)de.Current.Value ? "true;" : "false;"); db.Append(newline);
|
|
}
|
|
// Check if value is of float type
|
|
else if(de.Current.Value is float)
|
|
{
|
|
// Output the value as float (3 decimals)
|
|
float f = (float)de.Current.Value;
|
|
db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("=");
|
|
db.Append(spacing); db.Append(f.ToString("0.000", CultureInfo.InvariantCulture)); db.Append(";"); db.Append(newline);
|
|
}
|
|
// Check if value is of other numeric type
|
|
else if(de.Current.Value.GetType().IsPrimitive)
|
|
{
|
|
// Output the value unquoted
|
|
db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("=");
|
|
db.Append(spacing); db.Append(String.Format(CultureInfo.InvariantCulture, "{0}", de.Current.Value)); db.Append(";"); db.Append(newline);
|
|
}
|
|
else
|
|
{
|
|
// Output the value with quotes and escape characters
|
|
db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("=");
|
|
db.Append(spacing); db.Append("\""); db.Append(EscapedString(de.Current.Value.ToString())); db.Append("\";"); db.Append(newline);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the structure
|
|
return db.ToString();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Public Methods
|
|
|
|
// This clears the last error
|
|
public void ClearError()
|
|
{
|
|
// Clear error
|
|
cpErrorResult = 0;
|
|
cpErrorDescription = "";
|
|
cpErrorLine = 0;
|
|
}
|
|
|
|
|
|
// This creates a new configuration
|
|
public void NewConfiguration()
|
|
{
|
|
// Create new configuration
|
|
root = new UniversalCollection();
|
|
}
|
|
|
|
|
|
// This will save the current configuration to the specified file
|
|
public bool SaveConfiguration(string filename) { return SaveConfiguration(filename, "\r\n", true); }
|
|
public bool SaveConfiguration(string filename, string newline) { return SaveConfiguration(filename, newline, true); }
|
|
public bool SaveConfiguration(string filename, string newline, bool whitespace)
|
|
{
|
|
// Kill the file if it exists
|
|
if(File.Exists(filename)) File.Delete(filename);
|
|
|
|
// Open file stream for writing
|
|
FileStream fstream = File.OpenWrite(filename);
|
|
|
|
// Create output structure and write to file
|
|
string data = OutputConfiguration(newline, whitespace);
|
|
byte[] baData= Encoding.ASCII.GetBytes(data);
|
|
fstream.Write(baData, 0, baData.Length);
|
|
fstream.Flush();
|
|
fstream.Close();
|
|
|
|
// Return true when done, false when errors occurred
|
|
if(cpErrorResult == 0) return true; else return false;
|
|
}
|
|
|
|
|
|
// This will output the current configuration as a string
|
|
public string OutputConfiguration() { return OutputConfiguration("\r\n", true); }
|
|
public string OutputConfiguration(string newline) { return OutputConfiguration(newline, true); }
|
|
public string OutputConfiguration(string newline, bool whitespace)
|
|
{
|
|
// Simply return the configuration structure as string
|
|
return OutputStructure(root, 0, newline, whitespace);
|
|
}
|
|
|
|
|
|
// This will load a configuration from file
|
|
public bool LoadConfiguration(string filename)
|
|
{
|
|
// Check if the file is missing
|
|
if(!File.Exists(filename))
|
|
{
|
|
throw(new FileNotFoundException("File not found \"" + filename + "\"", filename));
|
|
}
|
|
else
|
|
{
|
|
// Load the file contents
|
|
List<string> data = new List<string>(100);
|
|
using(FileStream stream = File.OpenRead(filename)) {
|
|
StreamReader reader = new StreamReader(stream, Encoding.ASCII);
|
|
|
|
while(!reader.EndOfStream) {
|
|
string line = reader.ReadLine();
|
|
if(string.IsNullOrEmpty(line)) continue;
|
|
|
|
// Remove returns and tabs because the
|
|
// parser only uses newline for new lines.
|
|
line = line.Replace("\r", "");
|
|
line = line.Replace("\t", "");
|
|
|
|
data.Add(line);
|
|
}
|
|
}
|
|
|
|
// Load the configuration from this data
|
|
return InputConfiguration(data.ToArray());
|
|
}
|
|
}
|
|
|
|
|
|
// This will load a configuration from string
|
|
public bool InputConfiguration(string[] data)
|
|
{
|
|
// Clear errors
|
|
ClearError();
|
|
|
|
// Parse the data to the root structure
|
|
int pos = 0;
|
|
int line = 1;
|
|
matches = new Dictionary<string, UniversalEntry>(); //mxd
|
|
key = new StringBuilder(16); //mxd
|
|
val = new StringBuilder(16); //mxd
|
|
root = InputStructure(ref data, ref pos, ref line, true);
|
|
|
|
// Return true when done, false when errors occurred
|
|
if(cpErrorResult == 0) return true; else return false;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|