public static void Resolve(State state, string filePath, string[] tokens, ref int i, string[] lines, List <string> outputLines) { i++; var inputLine = i; var texts = new List <string>(); var lastIsEmpty = false; while (true) { if (Config.nextStatement.IsMatch(lines[i])) { if (lastIsEmpty) { texts.RemoveAt(texts.Count - 1); i--; break; } else { throw new InvalidSyntaxException($"Unexpected statement after text block at line {i + 1} of file '{filePath}"); } } if (lines[i].Trim() == "") { lastIsEmpty = true; } else { lastIsEmpty = false; } texts.Add(lines[i]); i++; } var text = string.Join("\r\n", texts); text = Config.inlineCommand1.Replace(text, m => { return($"�{m}�"); }); string innerText = null; List <TextToken> textTokens = new List <TextToken>(); var fragments = text.Split(new[] { '�' }, StringSplitOptions.RemoveEmptyEntries); var isChoice = false; foreach (var fragment in fragments) { if (isChoice == true) { throw new InvalidSyntaxException($"This range has other commands beside {{choice}} at line {inputLine + 1} of file '{filePath}'"); } var m = Config.inlineCommand2.Match(fragment); if (!m.Success) { // This is a text fragment string name = ""; var _fragment = Config.nameTag.Replace(fragment, (_m) => { name = _m.Groups[1].Value; var name_comment = name; name = Config.jsInvalidChar1.Replace(name, "_"); name = Config.jsInvalidChar2.Replace(name, ""); if (Config.codeProvider.IsValidIdentifier(name) == false) { if (name[0] == '?') { name = "Anon"; } else { throw new InvalidSyntaxException($"Unknown name tag format at line {inputLine + 1} of file '{filePath}'"); } } if (name != name_comment) { name = $"//{name_comment}\r\n{Config.tab}{name}"; } return(""); }); _fragment = Kits.FixJPTextInEnVer(_fragment); textTokens.Add(new TextToken { Name = "Text", CharaName = name, Text = Kits.EscapeBackTick(_fragment).Replace("\r\n", "\n").Replace("\r", "\n"), }); continue; } var command = m.Groups[1].Value; var _tokens = command.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var foundName = ""; var foundParams = new object[0]; switch (_tokens[0]) { case "waitForClick": case "clearText": case "appendText": case "waitForSound": case "marker": { foundName = _tokens[0]; break; } case "delay": { foundName = "delay"; foundParams = new object[] { int.Parse(_tokens[1]) }; break; } case "sound": { foundName = "sound"; foundParams = new object[] { Kits.EscapeBackTick(_tokens[1]) }; break; } case "nextPage": { foundName = "nextPage"; foundParams = new object[] { int.Parse(_tokens[1]) }; break; } case "bigChar": { foundName = "bigChar"; break; } case "choice": { isChoice = true; innerText = ""; var selections = command.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Skip(1); foreach (var selection in selections) { string variable = null; string realSelection = null; var _selection = Config.condBlock.Replace(selection, (_m) => { variable = _m.Groups[1].Value; realSelection = _m.Groups[2].Value; return(""); }); if (selection == _selection) { innerText += Config.tab + $"`{Kits.EscapeBackTick(selection)}`,\r\n"; } else { var num = Convert.ToUInt32(variable, 16); var type = ""; if (num > 0 && num <= 0x1F) { type = "g_"; } else { type = "l_"; } var subt = Kits.SubstituteVar(type, variable); innerText += Config.tab + $"cond({type}{subt.var} != 0)`{Kits.EscapeBackTick(realSelection)}`,\r\n"; } } innerText = innerText.TrimEnd(); break; } default: { throw new InvalidSyntaxException($"Unknown command '{_tokens[0]}' at line {inputLine + 1} of file '{filePath}'"); } } textTokens.Add(new TextToken { Name = foundName, Params = foundParams, }); } if (isChoice) { state.dialogCompleted = true; outputLines.Add("choice("); outputLines.Add(innerText); outputLines.Add(");"); return; } IEnumerable <List <TextToken> > groupedToken; var clearTextHitOuter = false; groupedToken = textTokens .Aggregate(new List <List <TextToken> >() { new List <TextToken>() }, (acc, token) => { if ((state.textMode == TextMode.NVL && (token.Name == "marker" || token.Name == "appendText")) || (state.textMode == TextMode.ADV && (token.Name == "marker"))) { acc.Add(new List <TextToken>()); } else { if (token.Name == "clearText") { clearTextHitOuter = true; } else { if (clearTextHitOuter == true && state.textMode == TextMode.ADV) { acc.Add(new List <TextToken>()); } clearTextHitOuter = false; } acc.Last().Add(token); } return(acc); }) .Where(e => e.Count > 0) .Select(_tokens => { var splittedDialogToken = _tokens.Aggregate(new List <List <TextToken> >() { new List <TextToken>() }, (acc, token) => { if (token.Name != "Text") { acc.Last().Add(token); } else { var newLineIdxs = token.Text.AllIndexOf("\n"); if (newLineIdxs.Count == 0) { acc.Last().Add(token); } else { foreach (var(idx, i) in newLineIdxs.Select((idx, i) => (idx, i))) { var subToken = token.Clone() as TextToken; var previousIdx = i == 0 ? 0 : newLineIdxs[i - 1] + 1; subToken.Text = subToken.Text.Substring(previousIdx, idx - previousIdx + 1); subToken.CharaName = i == 0 ? subToken.CharaName : ""; acc.Last().Add(subToken); acc.Add(new List <TextToken>()); if (i + 1 == newLineIdxs.Count) { var lastSubToken = token.Clone() as TextToken; lastSubToken.Text = lastSubToken.Text.Substring(idx + 1); lastSubToken.CharaName = ""; acc.Last().Add(lastSubToken); } } } }