private static bool TryDoFlowScriptDisassembly() { // load binary file Logger.Info("Loading binary FlowScript file..."); FlowScriptBinary script = null; var format = GetFlowScriptFormatVersion(); if (!TryPerformAction("Failed to load flow script from file.", () => { script = FlowScriptBinary.FromFile(InputFilePath, (BinaryFormatVersion)format); })) { return(false); } Logger.Info("Disassembling FlowScript..."); if (!TryPerformAction("Failed to disassemble flow script to file.", () => { var disassembler = new FlowScriptBinaryDisassembler(OutputFilePath); disassembler.Disassemble(script); disassembler.Dispose(); })) { return(false); } return(true); }
private void FromFile_ResultNotNullAndFormatIsEqualToParameter(BinaryFormatVersion version, BinaryFormatVersion actualVersion) { var script = FlowScriptBinary.FromFile($"TestResources\\{actualVersion}.bf", version); Assert.IsNotNull(script, "Script object should not be null"); Assert.AreEqual(actualVersion, script.FormatVersion); }
public void WriteBinary(FlowScriptBinary binary) { WriteHeader(ref binary.mHeader); WriteSectionHeaders(binary.mSectionHeaders); for (int i = 0; i < binary.mSectionHeaders.Length; i++) { ref var sectionHeader = ref binary.mSectionHeaders[i]; switch (sectionHeader.SectionType) { case BinarySectionType.ProcedureLabelSection: WriteLabelSection(ref sectionHeader, binary.mProcedureLabelSection); break; case BinarySectionType.JumpLabelSection: WriteLabelSection(ref sectionHeader, binary.mJumpLabelSection); break; case BinarySectionType.TextSection: WriteTextSection(ref sectionHeader, binary.mTextSection); break; case BinarySectionType.MessageScriptSection: WriteMessageScriptSection(ref sectionHeader, binary.mMessageScriptSection); break; case BinarySectionType.StringSection: WriteStringSection(ref sectionHeader, binary.mStringSection); break; default: throw new Exception("Unknown section type"); } }
public void DisassembleTest() { var script = FlowScriptBinary.FromFile("TestResources\\Version1.bf"); using (var disassembler = new FlowScriptBinaryDisassembler(new StringWriter())) disassembler.Disassemble(script); }
private void DisassembleToFileTestBase(string path) { var script = FlowScriptBinary.FromFile(path, BinaryFormatVersion.Unknown); using (var disassembler = new FlowScriptBinaryDisassembler(Path.ChangeExtension(path, "asm"))) disassembler.Disassemble(script); }
public void Disassemble(FlowScriptBinary script) { mScript = script ?? throw new ArgumentNullException(nameof(script)); mInstructionIndex = 0; WriteDisassembly(); }
public void FromFileTest_Batch() { foreach (var path in Directory.EnumerateFiles("TestResources\\Batch\\", "*.bf")) { var script = FlowScriptBinary.FromFile(path, BinaryFormatVersion.Version3BigEndian); Assert.IsNotNull(script); } }
public void FromStreamTest() { using (var fileStream = File.OpenRead("TestResources\\Version3BigEndian.bf")) { var script = FlowScriptBinary.FromStream(fileStream, BinaryFormatVersion.Version3BigEndian); Assert.IsNotNull(script); Assert.AreEqual(BinaryFormatVersion.Version3BigEndian, script.FormatVersion); } }
public void FromBinary_ContentsShouldMatchThatOfBinary_Version3BigEndian() { var binary = FlowScriptBinary.FromFile("TestResources\\Version3BigEndian.bf", BinaryFormatVersion.Version3BigEndian); var script = FlowScript.FromBinary(binary); Assert.AreEqual(script.UserId, binary.Header.UserId); // Compare label names for (int i = 0; i < script.Procedures.Count; i++) { Assert.AreEqual(binary.ProcedureLabelSection[i].Name, script.Procedures[i].Name); } /* * for ( int i = 0; i < script.JumpLabels.Count; i++ ) * { * Assert.AreEqual( binary.JumpLabelSection[i].Name, script.JumpLabels[i].Name ); * } */ // Compare instructions int binaryIndex = 0; foreach (var instruction in script.EnumerateInstructions()) { var binaryInstruction = binary.TextSection[binaryIndex++]; Assert.AreEqual(binaryInstruction.Opcode, instruction.Opcode); if (instruction.Operand != null) { switch (instruction.Operand.Kind) { case Operand.ValueKind.Int16: if (instruction.Opcode != Opcode.IF && instruction.Opcode != Opcode.GOTO) { Assert.AreEqual(binaryInstruction.OperandShort, instruction.Operand.Int16Value); } break; case Operand.ValueKind.Int32: Assert.AreEqual(binary.TextSection[binaryIndex++].OperandInt, instruction.Operand.Int32Value); break; case Operand.ValueKind.Single: Assert.AreEqual(binary.TextSection[binaryIndex++].OperandFloat, instruction.Operand.SingleValue); break; case Operand.ValueKind.String: break; } } } }
public FlowScriptBinary ReadBinary() { FlowScriptBinary instance = new FlowScriptBinary { mHeader = ReadHeader() }; instance.mSectionHeaders = ReadSectionHeaders(ref instance.mHeader); for (int i = 0; i < instance.mSectionHeaders.Length; i++) { ref var sectionHeader = ref instance.mSectionHeaders[i]; switch (sectionHeader.SectionType) { case BinarySectionType.ProcedureLabelSection: instance.mProcedureLabelSection = ReadLabelSection(ref sectionHeader); break; case BinarySectionType.JumpLabelSection: instance.mJumpLabelSection = ReadLabelSection(ref sectionHeader); break; case BinarySectionType.TextSection: instance.mTextSection = ReadTextSection(ref sectionHeader); break; case BinarySectionType.MessageScriptSection: instance.mMessageScriptSection = ReadMessageScriptSection(ref sectionHeader); break; case BinarySectionType.StringSection: // fix for early, broken files // see: nocturne e500.bf if (sectionHeader.FirstElementAddress == instance.mHeader.FileSize) { instance.mHeader.FileSize = ( int )(mReader.BaseStreamLength - mPositionBase); sectionHeader.ElementCount = instance.mHeader.FileSize - sectionHeader.FirstElementAddress; } instance.mStringSection = ReadStringSection(ref sectionHeader); break; default: throw new InvalidDataException("Unknown section type"); } }
public FieldLocalData(string directoryPath, int major, int minor) : this() { var fieldPak = LoadFieldPak(directoryPath, major, minor, "f{0:D3}_{1:D3}.pac"); var fieldNpcPak = LoadFieldPak(directoryPath, major, minor, "fnpc{0:D3}_{1:D3}.pac"); DefaultCamera = TryLoadFile(fieldPak, $"data/f{major:D3}_{minor:D3}.CMR", stream => new CmrBinary(stream, true)); // Load layers for (int layerIndex = 0; true; layerIndex++) { var layer = new FieldLayer(); layer.ObjectPlacement = TryLoadFile(fieldPak, $"data/f{major:D3}_{minor:D3}_{layerIndex:D2}.FBN", stream => new FbnBinary(stream, true)); layer.HitTable = TryLoadFile(fieldPak, $"hit/f{major:D3}_{minor:D3}_{layerIndex:D2}.HTB", stream => new HtbBinary(stream, true)); layer.HitScript = TryLoadFile(fieldPak, $"hit/fhit_{major:D3}_{minor:D3}_{layerIndex:D2}.bf", stream => FlowScriptBinary.FromStream(stream, true)); layer.NpcScript = TryLoadFile(fieldNpcPak, $"npc/fnpc{major:D3}_{minor:D3}_{layerIndex:D2}.bf", stream => FlowScriptBinary.FromStream(stream, true)); //layer.Fnt = TryLoadFile( fieldNpcPak, $"npc/fnt{major:D3}_{minor:D3}_{layerIndex:D2}.bf", stream => new FntBinary( stream, true ) ); //layer.Fpt = TryLoadFile( fieldNpcPak, $"npc/fpt{major:D3}_{minor:D3}_{layerIndex:D2}.bf", stream => new FptBinary( stream, true ) ); if (layer.ObjectPlacement == null && layer.HitTable == null && layer.HitScript == null && layer.NpcScript == null && layer.Fnt == null && layer.Fpt == null) { // Layer was empty, so assume there are no more layers break; } Layers.Add(layer); } //for ( int i = 1; i < 9; i++ ) // Pcds[ i ] = TryLoadFile( fieldPak, $"data/f{major:D3}_{minor:D3}_{i:D3}.PCD", stream => new PcdBinary( stream, true ) ); //Fpa = TryLoadFile( fieldPak, $"data/f{major:D3}_{minor:D3}.FPA", stream => new FpaBinary( stream, true ) ); //Culling = TryLoadFile( fieldPak, $"culling/f{major:D3}_{minor:D3}.CHD", stream => new ChdBinary( stream, true ) ); //Enemies = TryLoadFile( fieldPak, $"enemy/f{major:D3}_{minor:D3}.FEL", stream => new FelBinary( stream, true ) ); InitScript = TryLoadFile(fieldPak, $"init/fini_{major:D3}_{minor:D3}.bf", stream => FlowScriptBinary.FromStream(stream, true)); //Map = TryLoadFile( fieldPak, $"map/d{major:D3}_{minor:D3}.map", stream => new MapBinary( stream, true ) ); //Objects = TryLoadFile( fieldPak, $"map/d{major:D3}_{minor:D3}.OBL", stream => new OblBinary( stream, true ) ); ObjectHitTable = TryLoadFile(fieldPak, $"object_hit/f{major:D3}_{minor:D3}.HTB", stream => new HtbBinary(stream, true)); ObjectHitScript = TryLoadFile(fieldPak, $"object_hit/fhit{major:D3}_{minor:D3}.bf", stream => FlowScriptBinary.FromStream(stream, true)); //Sht = TryLoadFile( fieldPak, $"sht/f{major:D3}_{minor:D3}.SHT", stream => new ShtBinary( stream, true ) ); //Tbl = TryLoadFile( fieldPak, $"fext{major:D3}_{minor:D3}.tbl", stream => new TblBinary( stream, true ) ); AddUncategorizedFiles(fieldPak, UncategorizedFiles); if (fieldNpcPak != null) { AddUncategorizedFiles(fieldNpcPak, UncategorizedNpcFiles); } }
public void FromFileTest_InvalidFileFormat_Big() { Assert.ThrowsException <InvalidDataException>(() => FlowScriptBinary.FromFile("TestResources\\dummy_big.bin", BinaryFormatVersion.Unknown)); }
public void ToBinary_ContentsShouldMatchThatOfSourceBinary_Version3BigEndian() { var binaryIn = FlowScriptBinary.FromFile("TestResources\\Version3BigEndian.bf"); var script = FlowScript.FromBinary(binaryIn); var binaryOut = script.ToBinary(); // Compare headers Assert.AreEqual(binaryIn.Header.FileType, binaryOut.Header.FileType); Assert.AreEqual(binaryIn.Header.Compressed, binaryOut.Header.Compressed); Assert.AreEqual(binaryIn.Header.UserId, binaryOut.Header.UserId); Assert.AreEqual(binaryIn.Header.FileSize, binaryOut.Header.FileSize); Assert.IsTrue(binaryIn.Header.Magic.SequenceEqual(binaryOut.Header.Magic)); Assert.AreEqual(binaryIn.Header.Field0C, binaryOut.Header.Field0C); Assert.AreEqual(binaryIn.Header.SectionCount, binaryOut.Header.SectionCount); Assert.AreEqual(binaryIn.Header.LocalIntVariableCount, binaryOut.Header.LocalIntVariableCount); Assert.AreEqual(binaryIn.Header.LocalFloatVariableCount, binaryOut.Header.LocalFloatVariableCount); Assert.AreEqual(binaryIn.Header.Endianness, binaryOut.Header.Endianness); Assert.AreEqual(binaryIn.Header.Field1A, binaryOut.Header.Field1A); Assert.AreEqual(binaryIn.Header.Padding, binaryOut.Header.Padding); // Compare section headers for (int i = 0; i < binaryIn.SectionHeaders.Count; i++) { Assert.AreEqual(binaryIn.SectionHeaders[i].SectionType, binaryOut.SectionHeaders[i].SectionType); Assert.AreEqual(binaryIn.SectionHeaders[i].ElementSize, binaryOut.SectionHeaders[i].ElementSize); Assert.AreEqual(binaryIn.SectionHeaders[i].ElementCount, binaryOut.SectionHeaders[i].ElementCount); Assert.AreEqual(binaryIn.SectionHeaders[i].FirstElementAddress, binaryOut.SectionHeaders[i].FirstElementAddress); } // Compare labels for (int i = 0; i < binaryIn.ProcedureLabelSection.Count; i++) { Assert.AreEqual(binaryIn.ProcedureLabelSection[i].Name, binaryOut.ProcedureLabelSection[i].Name); Assert.AreEqual(binaryIn.ProcedureLabelSection[i].InstructionIndex, binaryOut.ProcedureLabelSection[i].InstructionIndex); Assert.AreEqual(binaryIn.ProcedureLabelSection[i].Reserved, binaryOut.ProcedureLabelSection[i].Reserved); } for (int i = 0; i < binaryIn.JumpLabelSection.Count; i++) { Assert.AreEqual(binaryIn.JumpLabelSection[i].Name, binaryOut.JumpLabelSection[i].Name); Assert.AreEqual(binaryIn.JumpLabelSection[i].InstructionIndex, binaryOut.JumpLabelSection[i].InstructionIndex); Assert.AreEqual(binaryIn.JumpLabelSection[i].Reserved, binaryOut.JumpLabelSection[i].Reserved); } // Compare instructions for (int i = 0; i < binaryIn.TextSection.Count; i++) { var inInstruction = binaryIn.TextSection[i]; var outInstruction = binaryOut.TextSection[i]; Assert.AreEqual(inInstruction.Opcode, outInstruction.Opcode); if (inInstruction.Opcode == Opcode.PUSHI || inInstruction.Opcode == Opcode.PUSHF) { ++i; continue; } if (inInstruction.Opcode == Opcode.IF || inInstruction.Opcode == Opcode.GOTO) { Assert.AreEqual(binaryIn.JumpLabelSection[inInstruction.OperandShort].Name, binaryOut.JumpLabelSection[outInstruction.OperandShort].Name); } else { Assert.AreEqual(inInstruction.OperandShort, outInstruction.OperandShort); } } // Compare message script //Assert.IsTrue(binaryIn.MessageScriptSection.SequenceEqual(binaryOut.MessageScriptSection)); // Compare strings Assert.IsTrue(binaryIn.StringSection.SequenceEqual(binaryOut.StringSection)); }
static void ExtractMessageScript(string file, Stream stream, string parentArchiveFile) { string prettyFileName; if (parentArchiveFile == null) { prettyFileName = file.Remove(0, DirectoryPath.Length); } else { prettyFileName = Path.Combine(parentArchiveFile, file); } // print some useful info if (parentArchiveFile == null) { Console.WriteLine($"Processing file: {prettyFileName}"); } else { Console.WriteLine($"Processing archive file: {prettyFileName}"); } // extract script MessageScript script = null; string fileExtension = Path.GetExtension(file); // Check if it is a plain message script file if (fileExtension.Equals(".bmd", StringComparison.InvariantCultureIgnoreCase)) { script = MessageScript.FromStream(stream, FormatVersion.Detect, Encoding, true); } // Check if it is a flow script file that can maybe contain a message script else if (fileExtension.Equals(".bf", StringComparison.InvariantCultureIgnoreCase)) { var flowScriptBinary = FlowScriptBinary.FromStream(stream, true); if (flowScriptBinary.MessageScriptSection != null) { script = MessageScript.FromBinary(flowScriptBinary.MessageScriptSection, FormatVersion.Detect, Encoding); } else { return; } } if (script != null) { // We have found a script, yay! Console.WriteLine("Writing message script to file..."); if (UseDecompiler) { WriteMessageScriptWithDecompiler(prettyFileName, script); } else { WriteMessageScript(prettyFileName, script); } } else { // Try to open the file as an archive if (!Archive.TryOpenArchive(stream, out var archive)) { // If we can't open the file as an archive, try brute force scanning if it is enabled if (EnableBruteforceScanning && (BruteforceExclusionList == null || BruteforceExclusionList != null && !BruteforceExclusionList.Any(x => x.Equals(fileExtension, StringComparison.InvariantCultureIgnoreCase))) && (BruteforceInclusionList == null || BruteforceInclusionList != null && BruteforceInclusionList.Any(x => x.Equals(fileExtension, StringComparison.InvariantCultureIgnoreCase))) ) { Console.WriteLine($"Bruteforce scanning..."); var scanCancel = new CancellationTokenSource(); var scanTask = Task.Factory.StartNew(() => ScanForMessageScripts(prettyFileName, stream, scanCancel.Token)); while (!scanTask.IsCompleted) { // Don't want to block, so wait for key to be available if (Console.KeyAvailable) { // Blocking is fine after this point var key = Console.ReadKey(true); if (key.Key == ConsoleKey.S) { Console.WriteLine("Do you want to skip scanning this file? Y/N"); if (Console.ReadKey(true).Key == ConsoleKey.Y) { scanCancel.Cancel(); Console.WriteLine("Do you want to add this file extension to the list of excluded files? Y/N"); if (Console.ReadKey(true).Key == ConsoleKey.Y) { if (BruteforceExclusionList == null) { BruteforceExclusionList = new List <string>(); } BruteforceExclusionList.Add(Path.GetExtension(prettyFileName)); } } } } } } } else { foreach (var entry in archive) { ExtractMessageScript(entry, archive.OpenFile(entry), prettyFileName); } } } }
/// <summary> /// Creates a <see cref="FlowScript"/> from a <see cref="FlowScriptBinary"/> object. /// </summary> /// <param name="binary">A <see cref="FlowScriptBinary"/> instance.</param> /// <returns>A <see cref="FlowScript"/> instance.</returns> public static FlowScript FromBinary(FlowScriptBinary binary, Encoding encoding = null) { var instance = new FlowScript { mUserId = binary.Header.UserId }; // assign labels later after convert the instructions because we need to update the instruction indices // to reference the instructions in the list, and not the instructions in the array of instructions in the binary // assign strings before instructions so we can assign proper string indices as we convert the instructions var stringBinaryIndexToListIndexMap = new Dictionary <short, short>(); var strings = new List <string>(); if (binary.StringSection != null) { short curStringBinaryIndex = 0; var curStringBytes = new List <byte>(); for (short i = 0; i < binary.StringSection.Count; i++) { // check for string terminator or end of string section if (binary.StringSection[i] == 0 || i + 1 == binary.StringSection.Count) { strings.Add(ShiftJISEncoding.Instance.GetString(curStringBytes.ToArray())); stringBinaryIndexToListIndexMap[curStringBinaryIndex] = ( short )(strings.Count - 1); // next string will start at the next byte if there are any left curStringBinaryIndex = ( short )(i + 1); curStringBytes = new List <byte>(); } else { curStringBytes.Add(binary.StringSection[i]); } } } var instructionBinaryIndexToListIndexMap = new Dictionary <int, int>(); var instructions = new List <Instruction>(); // assign instructions // TODO: optimize this away later as i don't feel like it right now if (binary.TextSection != null) { int instructionIndex = 0; int instructionBinaryIndex = 0; while (instructionBinaryIndex < binary.TextSection.Count) { // Convert each instruction var binaryInstruction = binary.TextSection[instructionBinaryIndex]; Instruction instruction; // Handle instructions we need to alter seperately if (binaryInstruction.Opcode == Opcode.PUSHSTR) { // Update the string offset to reference the strings inside of the string list instruction = Instruction.PUSHSTR(strings[stringBinaryIndexToListIndexMap[binaryInstruction.OperandShort]]); } else if (binaryInstruction.Opcode == Opcode.PUSHI) { instruction = Instruction.PUSHI(binary.TextSection[instructionBinaryIndex + 1].OperandInt); } else if (binaryInstruction.Opcode == Opcode.PUSHF) { instruction = Instruction.PUSHF(binary.TextSection[instructionBinaryIndex + 1].OperandFloat); } else { instruction = Instruction.FromBinaryInstruction(binaryInstruction); } // Add to list instructions.Add(instruction); instructionBinaryIndexToListIndexMap[instructionBinaryIndex] = instructionIndex++; // Increment the instruction binary index by 2 if the current instruction takes up 2 instructions if (instruction.UsesTwoBinaryInstructions) { instructionBinaryIndex += 2; } else { instructionBinaryIndex += 1; } } } // assign labels as the instruction index remap table has been built var sortedProcedureLabels = binary.ProcedureLabelSection.OrderBy(x => x.InstructionIndex).ToList(); for (int i = 0; i < binary.ProcedureLabelSection.Count; i++) { var procedureLabel = binary.ProcedureLabelSection[i]; int procedureListStartIndex = instructionBinaryIndexToListIndexMap[procedureLabel.InstructionIndex]; int nextProcedureLabelIndex = sortedProcedureLabels.FindIndex(x => x.InstructionIndex == procedureLabel.InstructionIndex) + 1; int procedureInstructionCount; int procedureBinaryEndIndex; // inclusive // Calculate the number of instructions in the procedure bool isLastProcedure = nextProcedureLabelIndex == binary.ProcedureLabelSection.Count; if (isLastProcedure) { procedureInstructionCount = (instructions.Count - procedureListStartIndex); procedureBinaryEndIndex = binary.TextSection.Count - 1; } else { var nextProcedureLabel = binary.ProcedureLabelSection[nextProcedureLabelIndex]; procedureInstructionCount = (instructionBinaryIndexToListIndexMap[nextProcedureLabel.InstructionIndex] - procedureListStartIndex); procedureBinaryEndIndex = nextProcedureLabel.InstructionIndex - 1; } // Copy the instruction range var procedureInstructions = new List <Instruction>(procedureInstructionCount); for (int j = 0; j < procedureInstructionCount; j++) { procedureInstructions.Add(instructions[procedureListStartIndex + j]); } // Create the new procedure representation Procedure procedure; if (binary.JumpLabelSection != null) { // Find jump labels within instruction range of procedure var procedureBinaryJumpLabels = binary.JumpLabelSection .Where(x => x.InstructionIndex >= procedureLabel.InstructionIndex && x.InstructionIndex <= procedureBinaryEndIndex) .ToList(); if (procedureBinaryJumpLabels.Count > 0) { // Generate mapping between label name and the procedure-local index of the label var procedureJumpLabelNameToLocalIndexMap = new Dictionary <string, int>(procedureBinaryJumpLabels.Count); for (int k = 0; k < procedureBinaryJumpLabels.Count; k++) { procedureJumpLabelNameToLocalIndexMap[procedureBinaryJumpLabels[k].Name] = k; } // Convert the labels to the new representation var procedureJumpLabels = new List <Label>(procedureBinaryJumpLabels.Count); foreach (var procedureBinaryJumpLabel in procedureBinaryJumpLabels) { int globalInstructionListIndex = instructionBinaryIndexToListIndexMap[procedureBinaryJumpLabel.InstructionIndex]; int localInstructionListIndex = globalInstructionListIndex - procedureListStartIndex; procedureJumpLabels.Add(new Label(procedureBinaryJumpLabel.Name, localInstructionListIndex)); } // Create the procedure procedure = new Procedure(procedureLabel.Name, procedureInstructions, procedureJumpLabels); // Loop over the instructions and update the instructions that reference labels // so that they refer to the proper procedure-local label index for (int j = 0; j < procedure.Instructions.Count; j++) { var instruction = procedure.Instructions[j]; if (instruction.Opcode == Opcode.GOTO || instruction.Opcode == Opcode.IF) { short globalIndex = instruction.Operand.Int16Value; var binaryLabel = binary.JumpLabelSection[globalIndex]; short localIndex = ( short )procedureJumpLabelNameToLocalIndexMap[binaryLabel.Name]; instruction.Operand.Int16Value = localIndex; Debug.Assert(procedure.Labels[localIndex].Name == binaryLabel.Name); } procedure.Instructions[j] = instruction; } } else { // Create the procedure procedure = new Procedure(procedureLabel.Name, procedureInstructions); } } else { // Create the procedure procedure = new Procedure(procedureLabel.Name, procedureInstructions); } instance.mProcedures.Add(procedure); } // assign message script if (binary.MessageScriptSection != null) { instance.mMessageScript = MessageScript.FromBinary(binary.MessageScriptSection, MessageScriptLanguage.FormatVersion.Detect, encoding); } // strings have already been assigned previously, // so last up is the version instance.mFormatVersion = (FormatVersion)binary.FormatVersion; // everything is assigned, return the constructed instance return(instance); }
/// <summary> /// Creates a <see cref="FlowScript"/> by loading it from a stream in the specified format version. /// </summary> /// <param name="stream">Data stream.</param> /// <param name="version">Format version the loader should use.</param> /// <returns>A <see cref="FlowScript"/> instance.</returns> public static FlowScript FromStream(Stream stream, Encoding encoding, FormatVersion version, bool leaveOpen) { FlowScriptBinary binary = FlowScriptBinary.FromStream(stream, (BinaryFormatVersion)version, leaveOpen); return(FromBinary(binary, encoding)); }