/// <summary> /// Decompiles the CST scene script and writes it to the text writer. /// </summary> /// <param name="writer">The text writer to write the decompiled script to.</param> private void DecompileInternal(TextWriter writer) { // Header must state the output .cst script file name writer.WriteLine($"#{Path.GetFileNameWithoutExtension(FileName)}"); writer.WriteLine(); for (int i = 0; i < Count; i++) { ISceneLine line = Lines[i]; switch (line.Type) { case SceneLineType.Command: writer.WriteLine($"\t{line.Content}"); break; case SceneLineType.Message: // When a scene file has a message written on a new line after another message, // a '\n' is automatically prefixed. We need to remove that. if (line.Content.StartsWith("\\n")) { writer.WriteLine($"\t{line.Content.Substring(2)}"); } // Empty messages are the result of '\n' as the line input else if (line.Content.Length == 0) { writer.WriteLine("\t\\n"); } else { writer.WriteLine($"\t{line.Content}"); } break; case SceneLineType.Name: // Write the name without tab first writer.Write(line.Content); // See if we can continue by writing the preceding message on the same line. if (i + 1 < Count && Lines[i + 1].Type == SceneLineType.Message) { writer.WriteLine($"\t{Lines[i + 1].Content}"); i++; // Skip the next entry, we just wrote it } break; case SceneLineType.Input: // Input is specified by an empty line, whitespace is allowed writer.WriteLine(); break; case SceneLineType.Page: // Page break in novel view, denoted with \p writer.WriteLine(); writer.WriteLine("\t\\p"); writer.WriteLine(); break; } } writer.Flush(); }
/// <summary> /// Decompiles the CST scene script and writes it to the human readable text writer. /// </summary> /// <param name="writer">The text writer to write the human readable script to.</param> private void HumanReadableInternal(TextWriter writer) { // Header must state the output .cst script file name writer.WriteLine($"#{Path.GetFileNameWithoutExtension(FileName)}"); writer.WriteLine(); bool isChoice = false; List <string> choices = new List <string>(); StringBuilder name = new StringBuilder(); StringBuilder message = new StringBuilder(); bool firstInput = true; void FlushText() { if (name.Length != 0 || message.Length != 0 || isChoice) { if (firstInput) { firstInput = false; } else { writer.WriteLine(); } // The first replace prevents duplicate carraige returns name.Replace("\n\r", "\n").Replace("\n", "\n\r"); message.Replace("\n\r", "\n").Replace("\n", "\n\r"); if (!isChoice && name.Length != 0) { writer.WriteLine(name); } if (message.Length != 0) { writer.WriteLine(message); } if (isChoice) { if (message.Length != 0) { writer.WriteLine(); } writer.WriteLine("Choice:"); foreach (string choice in choices) { writer.WriteLine($"• {choice}"); } } } message.Clear(); name.Clear(); choices.Clear(); isChoice = false; } for (int i = 0; i < Count; i++) { ISceneLine line = Lines[i]; switch (line.Type) { case SceneLineType.Command: Match match; if (line.Content == "fselect") { FlushText(); isChoice = true; } else if (isChoice && (match = ChoiceRegex.Match(line.Content)).Success) { //writer.WriteLine($"• {match.Groups["text"].Value}"); choices.Add(CatUtils.UnescapeString(match.Groups["text"].Value)); } break; case SceneLineType.Message: message.Append(CatUtils.UnescapeMessage(line.Content, false)); break; case SceneLineType.Name: name.Append(CatUtils.UnescapeMessage(line.Content, true)); break; case SceneLineType.Input: FlushText(); break; case SceneLineType.Page: FlushText(); writer.WriteLine(); writer.WriteLine(); break; } } FlushText(); writer.Flush(); }
internal SceneLine(ISceneLine line) { Type = line.Type; Content = line.Content; }
/*private void ReadNovelRanges(List<NovelRange> novelRanges) { * NovelCommand on = null; * for (int i = 0; i < Script.Count; i++) { * if (Script.Lines[i] is NovelCommand novel) { * if (on == null && novel.State) { * on = novel; * } * else if (on != null && !novel.State) { * novelRanges.Add(new NovelRange(on, novel)); * on = null; * } * } * } * if (on != null) * novelRanges.Add(new NovelRange(on)); * }*/ private void ReadScene(List <Message> messages, List <Choice> choices) { bool mesdraw = true; bool novel = false; Message.Builder msgBuilder = new Message.Builder(this); for (int i = 0; i < Script.Count; i++) { ISceneLine line = Script.Lines[i]; switch (line) { case ScenePage _: case SceneInput _: if (msgBuilder.HasMessage) { Message msg = msgBuilder.Build(); if (mesdraw && msg != null) { messages.Add(msg); } } break; case NovelCommand novelCmd: novel = novelCmd.State; msgBuilder.IsNovel = novelCmd.State; break; case MesdrawCommand mesdrawCmd: mesdraw = mesdrawCmd.State; break; case SceneName name: msgBuilder.Names.Add(name); break; case SceneMessage message: msgBuilder.Messages.Add(message); break; case SoundPlayCommand play: if (play.SoundType == SoundType.Pcm) { msgBuilder.Voices.Add(play); } break; case ConditionCommand condition: if (condition.Command is SoundPlayCommand conPlay && conPlay.SoundType == SoundType.Pcm) { Console.WriteLine(condition.Content); } break; case ChoiceCommand choice: if (!JpUtils.Check(choice.ChoiceContent)) { choices.Add(new Choice(this, choice)); } else { Console.WriteLine(""); } break; } } }