private void FileMenuOpen_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "Decompressed NUTR file|*.nutr.decompressed"
            };

            if (openFileDialog.ShowDialog() == true)
            {
                Nutr.FileTypeBPLength fileType = Nutr.FileTypeBPLength.DRAMA;
                if (openFileDialog.FileName.Contains("interface", StringComparison.OrdinalIgnoreCase))
                {
                    fileType = Nutr.FileTypeBPLength.INTERFACE;
                }
                else if (openFileDialog.FileName.Contains("item", StringComparison.OrdinalIgnoreCase))
                {
                    fileType = Nutr.FileTypeBPLength.ITEM;
                }
                Title    = $"Sailor Moon La Luna Splende Script Editor - {Path.GetFileName(openFileDialog.FileName)}";
                NutrFile = Nutr.ParseFromFile(openFileDialog.FileName, fileType);
                commandsListBox.ItemsSource = NutrFile.PostScriptCommands.Select(c => $"{c.Line(NutrFile.Script.Select(n => n.Text).ToList())} (0x{c.LineNumber:X2}):\t\t" +
                                                                                 $"0x{string.Join(" ", c.CommandBytes.Select(cb => $"{cb:X2}"))}");

                dialogueListBox.ItemsSource = NutrFile.DialogueBoxes;
            }
        }
        public static Nutr ParseFromFile(string file, FileTypeBPLength fileType)
        {
            byte[] data = File.ReadAllBytes(file);
            Nutr   nutr = new Nutr();
            int    filePtr;

            nutr.BoilerPlate = data.Take((int)fileType).ToArray();
            for (filePtr = (int)fileType; filePtr < data.Length;)
            {
                int length = BitConverter.ToInt32(new byte[] { data[filePtr], data[filePtr + 1], data[filePtr + 2], data[filePtr + 3] });
                filePtr += 4;

                byte[] textBytes = new byte[length];
                for (int j = 0; j < length; j++)
                {
                    textBytes[j] = data[filePtr];
                    filePtr++;
                }
                string text = Encoding.UTF8.GetString(textBytes);

                if ((fileType == FileTypeBPLength.DRAMA && text == "suspend") || (fileType != FileTypeBPLength.DRAMA && text == "getString"))
                {
                    nutr.Script.Add(new NutrLine {
                        Text = text
                    });
                    break;
                }

                byte[] lineEnd = new byte[] { data[filePtr], data[filePtr + 1], data[filePtr + 2], data[filePtr + 3] };
                if (!lineEnd.SequenceEqual(NutrLine.LineEnd))
                {
                    throw new FileFormatException($"Expected line end after string '{text}' but found sequence '0x{lineEnd[0]:X2} 0x{lineEnd[1]:X2} 0x{lineEnd[2]:X2} 0x{lineEnd[3]:X2}' instead.");
                }
                filePtr += 4;

                nutr.Script.Add(new NutrLine {
                    Text = text
                });
            }

            bool encounteredTrapTrap = false;

            for (; ; filePtr++)
            {
                if (data[filePtr - 8] == 'T' && data[filePtr - 7] == 'R' && data[filePtr - 6] == 'A' && data[filePtr - 5] == 'P' &&
                    data[filePtr - 4] == 'T' && data[filePtr - 3] == 'R' && data[filePtr - 2] == 'A' && data[filePtr - 1] == 'P')
                {
                    if (encounteredTrapTrap)
                    {
                        break;
                    }
                    else
                    {
                        encounteredTrapTrap = true;
                    }
                }

                nutr.PostScript.Add(data[filePtr]);
            }

            for (; !(data[filePtr] == 'T' && data[filePtr + 1] == 'R' && data[filePtr + 2] == 'A' && data[filePtr + 3] == 'P');)
            {
                int lineNumber = BitConverter.ToInt32(new byte[] { data[filePtr], data[filePtr + 1], data[filePtr + 2], data[filePtr + 3] });
                if (lineNumber > nutr.Script.Count)
                {
                    break;
                }

                nutr.PostScriptCommands.Add(new PostScriptCommand
                {
                    LineNumber   = lineNumber,
                    CommandBytes = new byte[] { data[filePtr + 4], data[filePtr + 5], data[filePtr + 6], data[filePtr + 7] },
                });
                filePtr += 8;
            }

            for (; filePtr < data.Length; filePtr++)
            {
                nutr.PostPostScript.Add(data[filePtr]);
            }

            if (fileType == FileTypeBPLength.DRAMA)
            {
                DialogueBox currentDialogueBox = new DialogueBox();;
                bool        msg = false;
                foreach (var line in nutr.PostScriptCommands)
                {
                    string text = line.Line(nutr.Script.Select(l => l.Text).ToList());

                    if (text == "msg")
                    {
                        msg = true;
                    }
                    else if (msg)
                    {
                        currentDialogueBox.DialogueLineIndices.Add(line.LineNumber);
                        msg = false;
                    }
                    else if (currentDialogueBox != null && text == "talk")
                    {
                        nutr.DialogueBoxes.Add(currentDialogueBox);
                        currentDialogueBox = new DialogueBox();
                    }
                }
            }
            else
            {
                for (int i = 0; i < nutr.Script.Count; i++)
                {
                    nutr.DialogueBoxes.Add(new DialogueBox {
                        DialogueLineIndices = new List <int> {
                            i
                        }
                    });
                }
            }

            return(nutr);
        }
 public List <string> DialogueLineStrings(Nutr nutr)
 {
     return(DialogueLineIndices.Select(i => nutr.Script[i].Text).ToList());
 }