public static DataNode Load(string file, DataLoaderConfig config = null, DataNode initialNode = null, bool testing = false) { if (config == null) { config = DataLoaderConfig.IniFormat2(); } if (string.IsNullOrWhiteSpace(file)) { return(initialNode ?? new DataNode()); } return(CreateData(file, config, initialNode, testing)); }
private static bool HandlePreprocessor(ref string key, ref string newValue, DataLoaderConfig cfg, DataNode node) { if (key.ToUpperInvariant() == "#INCLUDE") { if (cfg.PreprocessorInclude != null) { DataLoader.Load(cfg.PreprocessorInclude(newValue), cfg, node); } return(true); } if (key.ToUpperInvariant() == "VAR") { node.Lines.Add($"{key} {newValue.Trim()}"); return(true); } return(false); }
private static void EndKeyValuePair(string vague, Stack <DataNode> stack, ref string key, StringBuilder spaceTemp, DataLoaderConfig cfg) { DataNode curNode = stack.Peek(); if (key.Length > 0) { string keyToUpper = key.ToUpperInvariant(); if (cfg.StatesHack && keyToUpper == "DROPITEM") { curNode.Lines.Add($"DropItem {vague}"); key = string.Empty; spaceTemp.Clear(); } else if (cfg.StatesHack && keyToUpper == "STARTITEM") { curNode.Lines.Add($"Player.StartItem {vague}"); key = string.Empty; spaceTemp.Clear(); } else if (cfg.StatesHack && keyToUpper == "PLAYER.STARTITEM") { curNode.Lines.Add($"Player.StartItem {vague}"); key = string.Empty; spaceTemp.Clear(); } else if (cfg.StatesHack && keyToUpper == "INT") { curNode.Lines.Add($"int {vague}"); key = string.Empty; spaceTemp.Clear(); } else if (cfg.StatesHack && keyToUpper == "CONST") { curNode.Lines.Add($"const {vague}"); key = string.Empty; spaceTemp.Clear(); } else if (cfg.StatesHack && keyToUpper == "DOUBLE") { curNode.Lines.Add($"double {vague}"); key = string.Empty; spaceTemp.Clear(); } else { curNode.Values[key.ToUpper(CultureInfo.InvariantCulture).Trim()] = vague.Trim(); key = string.Empty; spaceTemp.Clear(); } } }
private static DataNode CreateData(string file, DataLoaderConfig cfg, DataNode initialNode = null, bool testing = false) { Stack <DataNode> stack = new Stack <DataNode>(); stack.Push(initialNode ?? new DataNode()); StringBuilder vague = new StringBuilder(); StringBuilder spaceTemp = new StringBuilder(); string key = string.Empty; char insideString = '\0'; bool insideSingleLineComment = false; bool insideMultiLineComment = false; char lastChar = ' '; char eatenChar = ' '; int stateHackDepth = 0; bool hadHitNewline = false; bool hadHitNewlinePopAfterwards = false; bool skipAppendCharacter = false; char c; for (int index = 0; index <= file.Length + 1; index++) { if (index == file.Length + 1) { c = '\0'; } else if (index == file.Length) { c = '\x0A'; } else { c = file[index]; } if (testing) { Console.Write(c); } if (insideSingleLineComment) { switch (c) { case '\x0D': // CR c = lastChar; break; case '\x0A': // LF insideSingleLineComment = false; hadHitNewline = true; break; } } else if (insideMultiLineComment) { switch (c) { case '/': if (eatenChar == '*') { eatenChar = ' '; insideMultiLineComment = false; } break; default: eatenChar = c; hadHitNewline = true; break; } } else if (stateHackDepth > 0) { if (c == '}') { stateHackDepth--; vague.Append(c); } else if (c == '{') { stateHackDepth++; vague.Append(c); } else { vague.Append(c); } } else if (insideString != '\0') { if (lastChar == '\\') { if (c == '\\') { eatenChar = '\\'; c = ' '; // So that lastChar will get set to this. skipAppendCharacter = true; goto LABEL_APPEND; } if (c == '"' || c == '\'') { eatenChar = ' '; } goto LABEL_APPEND; } if (c == insideString) { if (cfg.IncludeQuotationMarks) { vague.Append(insideString); eatenChar = ' '; // Is this needed? Could it hurt? } insideString = '\0'; } else if (c == '\\') { eatenChar = '\\'; } else { goto LABEL_APPEND; } goto LABEL_FALLTHROUGH; LABEL_APPEND: if (eatenChar != ' ') { vague.Append(eatenChar); eatenChar = ' '; } if (!skipAppendCharacter) { vague.Append(c); } else { skipAppendCharacter = false; } } else { switch (c) { case '\0': skipAppendCharacter = true; goto default; case '\t': case ' ': if (vague.Length > 0 && !hadHitNewline) { c = ' '; goto default; } break; case '\x0D': // CR c = lastChar; break; case '\x0A': // LF if (cfg.NewlineEndsKeyValuePair) // We're exiting a key=value pair. { if (!(cfg.CommaFollowedByNewlineEatsTheNewline && lastChar == ',')) { goto LABEL_EndKeyValuePair; } } break; case '}': if (cfg.UseSubnodes) // We're exiting a node. { hadHitNewline = true; hadHitNewlinePopAfterwards = true; skipAppendCharacter = true; //break; } goto default; case '/': if (cfg.UseCSingleLineComments || cfg.UseCMultiLineComments) { if (cfg.UseCSingleLineComments) { if (lastChar == '/') { eatenChar = ' '; goto LABEL_SingleLineComment; } eatenChar = '/'; break; } if (cfg.UseCMultiLineComments) { eatenChar = '/'; break; } } goto default; case '*': if (cfg.UseCMultiLineComments && lastChar == '/') { goto LABEL_MultiLineComment; } goto default; case '{': if (cfg.UseSubnodes) // We're entering a Subnode. { hadHitNewline = false; string vagueToUpper = vague.ToString().ToUpperInvariant(); if (cfg.StatesHack && (vagueToUpper == "STATES" || vagueToUpper.StartsWith("EXTEND ", StringComparison.Ordinal) || vagueToUpper.StartsWith("ACTION ", StringComparison.Ordinal) || vagueToUpper.StartsWith("PROTECTED ", StringComparison.Ordinal) || vagueToUpper.StartsWith("PRIVATE ", StringComparison.Ordinal) || vagueToUpper.StartsWith("PUBLIC ", StringComparison.Ordinal))) { key = EnteringValue(new StringBuilder(vagueToUpper), spaceTemp); vague.Clear(); stateHackDepth++; goto default; } if (key.Length > 0) { vague.Clear(); vague.Append(key); key = string.Empty; EnteringNode(stack, vague, spaceTemp); } else { EnteringNode(stack, vague, spaceTemp); } break; } goto default; case '[': if (cfg.UseSections) // We're entering a section header. { if (stack.Count > 1) { stack.Pop(); } vague.Clear(); break; } goto default; case ']': if (cfg.UseSections) // We're entering a section. { EnteringNode(stack, vague, spaceTemp); break; } goto default; case '=': if (cfg.EqualsSignMarksEquality) // We're entering a value. { goto LABEL_EnteringValue; } goto default; case ':': if (cfg.ColonMarksEquality) // We're entering a value. { goto LABEL_EnteringValue; } goto default; case ';': if (cfg.SemicolonEndsKeyValuePair) // We're exiting a key=value pair. { goto LABEL_EndKeyValuePair; } if (cfg.SemicolonStartsComment) // We're entering a single-line comment { goto LABEL_SingleLineComment; } goto default; case '#': if (cfg.HashtagStartsComment) // We're entering a single-line comment { goto LABEL_SingleLineComment; } goto default; case '"': // We're entering a string. if (cfg.SpaceMarksEquality) { if (key.Length == 0 && vague.Length > 0 && !vague.ToString().Contains(" ")) { key = vague.ToString(); vague.Clear(); } } insideString = '"'; spaceTemp.Clear(); // TODO: Does this help or make things worse? if (cfg.IncludeQuotationMarks) { vague.Append('"'); } break; case '\'': // We're entering a string. if (vague.Length > 0) { key = vague.ToString(); } insideString = '\''; vague.Clear(); spaceTemp.Clear(); // TODO: Does this help or make things worse? if (cfg.IncludeQuotationMarks) { vague.Append('\''); } break; default: if (hadHitNewline) { hadHitNewline = false; if (vague.Length > 0 && key.Length == 0) { string vagueToString = vague.ToString(); /*int quotePosition = Math.Min(vagueToString.IndexOf('"'), vagueToString.IndexOf('\'')); * int quotePosition2 = Math.Max(vagueToString.LastIndexOf('"'), vagueToString.LastIndexOf('\'')); * int spacePosition = vagueToString.IndexOf(' '); * if (vagueToString.Contains("#") || key.Contains("#")) * { * Console.Write(' '); * }*/ int spacePos = vagueToString.IndexOf(" ", StringComparison.Ordinal); if (cfg.SpaceMarksEquality && spacePos != -1) //&& spacePosition != -1 && (quotePosition == -1 || (quotePosition > spacePosition))) { string newKey = vagueToString.Substring(0, spacePos); string newValue = vagueToString.Substring(spacePos, vagueToString.Length - spacePos); if (HandlePreprocessor(ref newKey, ref newValue, cfg, stack.Peek())) { key = string.Empty; spaceTemp.Clear(); vague.Clear(); } else { key = newKey; EndKeyValuePair(newValue, stack, ref key, spaceTemp, cfg); vague.Clear(); } } else if (cfg.AllowFlags) { AddFlag(vague, stack, spaceTemp); } } else if (key.Length > 0 || cfg.NewlineEndsKeyValuePairEvenIfValueIsEmpty) { string newValue = vague.ToString(); if (HandlePreprocessor(ref key, ref newValue, cfg, stack.Peek())) { key = string.Empty; vague.Clear(); } else { EndKeyValuePair(newValue, stack, ref key, spaceTemp, cfg); vague.Clear(); } } if (hadHitNewlinePopAfterwards) { stack.Pop(); hadHitNewlinePopAfterwards = false; } } if (skipAppendCharacter) { skipAppendCharacter = false; break; } //LABEL_defaultSkipNewlineHandling: if (eatenChar != ' ') { AppendCharacter(vague, '/', spaceTemp); eatenChar = ' '; } AppendCharacter(vague, c, spaceTemp); break; LABEL_SingleLineComment: // HACK: Yup, this is goto. It is being used as intended for C#. hadHitNewline = true; insideSingleLineComment = true; break; LABEL_MultiLineComment: /*if (key.Length > 0) * { * EndKeyValuePair(vague, stack, ref key, spaceTemp); * }*/ insideMultiLineComment = true; break; LABEL_EnteringValue: key = EnteringValue(vague, spaceTemp); break; LABEL_EndKeyValuePair: hadHitNewline = true; //EndKeyValuePair(vague.ToString(), stack, ref key, spaceTemp, cfg); //vague.Clear(); break; } } LABEL_FALLTHROUGH: lastChar = c; } while (stack.Count > 1) { stack.Pop(); } return(stack.Count > 0 ? stack.Pop() : null); }
public string ToStringVerbose(DataLoaderConfig cfg, bool includeOuter = true) { string equalsTxt = " ", endTxt = ""; if (cfg.EqualsSignMarksEquality) { equalsTxt = " = "; } if (cfg.SemicolonEndsKeyValuePair) { endTxt = ";"; } StringBuilder s = new StringBuilder(); if (includeOuter) { s.AppendLine(Header); s.AppendLine("{"); } if (Lines.Count > 0) { foreach (string line in Lines) { s.AppendLine($"{line}{endTxt}"); } } /*if (flagsHack && Values.ContainsKey("FLAGS")) * { * string flags = Values["FLAGS"]; * if (flags.Length != 0) * { * if (flags.IndexOf(',') == -1) * { * s.AppendLine(flags.Trim()); * } * else * { * string[] flagsSplit = flags.Split(','); * foreach (string flag in flagsSplit) * { * s.AppendLine(flag.Trim()); * } * } * } * }*/ if (Values.Count > 0) { foreach (KeyValuePair <string, string> kvp in Values) { //if (!(flagsHack && kvp.Key == "FLAGS")) { if (kvp.Value.IndexOf('{') == 0) { s.AppendLine($"{kvp.Key}{equalsTxt}{kvp.Value}"); } else { s.AppendLine($"{kvp.Key}{equalsTxt}{kvp.Value}{endTxt}"); /*switch (GetValueType(kvp.Value)) * { * case DataType.Integer: * case DataType.Float: * s.AppendLine($"{kvp.Key}{equalsTxt}{kvp.Value}"); * break; * case DataType.QuotedString: * case DataType.Keyword: * case DataType.Nil: * s.AppendLine($"{kvp.Key}{equalsTxt}\"{kvp.Value}\""); * break; * }*/ } } } } if (Subnodes.Count > 0) { foreach (DataNode subnode in Subnodes) { s.AppendLine(subnode.ToStringVerbose(cfg)); } } /*if (Lists.Count > 0) * { * s += $", Lists: {Lists.Count}"; * }*/ if (includeOuter) { s.AppendLine("}"); } return(s.ToString()); }