Пример #1
0
        public void ToBinary_ShouldMatchSourceBinary_Version1BigEndian()
        {
            var binary    = MessageScriptBinary.FromFile("TestResources\\Version1BigEndian.bmd");
            var script    = MessageScript.FromBinary(binary);
            var newBinary = script.ToBinary();

            Compare(binary, newBinary);
        }
Пример #2
0
        private void WriteMessageScriptDisassembly()
        {
            mWriter.WriteLine(".msg");

            using (var messageScriptDecompiler = new MessageScriptDecompiler(mWriter))
            {
                messageScriptDecompiler.Decompile(MessageScript.FromBinary(mScript.MessageScriptSection));
            }
        }
Пример #3
0
        static void ScanForMessageScripts(string prettyFileName, Stream stream, CancellationToken cancellationToken)
        {
            byte[] magic = new byte[4];

            while (stream.Position <= stream.Length)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    break;
                }

                if (stream.Position + BinaryHeader.SIZE < stream.Length)
                {
                    // Read 4 bytes
                    magic[0] = ( byte )stream.ReadByte();
                    magic[1] = ( byte )stream.ReadByte();
                    magic[2] = ( byte )stream.ReadByte();
                    magic[3] = ( byte )stream.ReadByte();

                    if (magic.SequenceEqual(BinaryHeader.MAGIC_V0) ||
                        magic.SequenceEqual(BinaryHeader.MAGIC_V1) ||
                        magic.SequenceEqual(BinaryHeader.MAGIC_V1_BE))
                    {
                        long scriptStartPosition = stream.Position - 12;
                        var  scriptBinary        = MessageScriptBinary.FromStream(new StreamView(stream, scriptStartPosition, stream.Length - scriptStartPosition));
                        var  script = MessageScript.FromBinary(scriptBinary);

                        Console.WriteLine($"Found message script at 0x{scriptStartPosition:X8}. Writing to file...");

                        if (UseDecompiler)
                        {
                            WriteMessageScriptWithDecompiler($"{prettyFileName} @ 0x{scriptStartPosition:X8}", script);
                        }
                        else
                        {
                            WriteMessageScript($"{prettyFileName} @ 0x{scriptStartPosition:X8}", script);
                        }

                        stream.Position = scriptStartPosition + scriptBinary.Header.FileSize;
                    }
                    else if (DisableScanAlignment)
                    {
                        // Scan alignment is disabled, so we make sure to retry every byte
                        // 4 steps forward, 3 steps back
                        stream.Position -= 3;
                    }
                }
                else
                {
                    break;
                }
            }
        }
Пример #4
0
 public void FromBinary_ShouldNotThrow_Version1BigEndian()
 {
     var binary = MessageScriptBinary.FromFile("TestResources\\Version1BigEndian.bmd");
     var script = MessageScript.FromBinary(binary);
 }
Пример #5
0
        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);
                    }
                }
            }
        }
Пример #6
0
        /// <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);
        }