Ejemplo n.º 1
0
        /**
         * Reads the next script element from the stream.
         * This could be an entire command or a single parameter.
         */
        ScriptElement NextScriptElement(Stream s)
        {
            BinaryReader  br   = new BinaryReader(s);
            ScriptElement elem = null;

            // read the next data element.
            DataElement cmd = NextDataElement(s);

            // if no data element could be found, ...
            if (cmd == null)
            {
                // ...return nothing.
                return(null);
            }

            // if the element is...
            switch (cmd.type)
            {
            // ...a function call, then...
            case DataElement.TYPE_FUNC: {
                // ...determine how many parameters the function call takes.
                int             paramCount = br.ReadInt32();
                ScriptElement[] parameters = new ScriptElement[paramCount];

                // for each parameter...
                for (var j = 0; j < paramCount; j++)
                {
                    // ...read it's value
                    DataElement param = NextDataElement(s);

                    // if the parameter is a temporary variable reference, then...
                    if (param is VarTempRefDataElement)
                    {
                        // ...replace it by the appropriate substitution expression.
                        parameters[j] = substituteExpressions[((VarTempRefDataElement)param).id];
                        // after that, continue with the next parameter.
                        continue;
                    }

                    // if the function is called...
                    // ..."StrOut" and this parameter is a string, ...
                    if ((((FuncDataElement)cmd).name.Equals("StrOut") || ((FuncDataElement)cmd).name.Equals("StrOutNW")) && param is StringDataElement)
                    {
                        // (...then this is a line of text to be displayed.)
                        // (we want to store those in a separate file to make it easier to translate them.)

                        // get the text
                        string line = ((StringDataElement)param).data;
                        // generate a new line number
                        string id = "L" + (++lineCount);
                        // add those to the list of known strings...
                        stringTable.Add(id, new ScriptLine(fontID, speaker, line));
                        // ...and replace the string parameter with reference to the line number
                        parameters[j] = new DataScriptElement(new ExternalStringDataElement(id, stringTable));
                    }
                    // ..."StrOutNWC", ...
                    else if (((FuncDataElement)cmd).name.Equals("StrOutNWC"))
                    {
                        // (...then this parameter represents a name.)
                        // if the parameter is...
                        // ...a string, then...
                        if (param is StringDataElement)
                        {
                            // (...we want to store it separately to make it easier to translate.)
                            string name = ((StringDataElement)param).data;
                            // set the current speaker to this name.
                            speaker = name;
                            string id = null;
                            // for each entry on our list of known strings...
                            foreach (var entry in stringTable)
                            {
                                // ...check if it is a name.
                                if (entry.Key[0] == 'N' && entry.Value.Text.Equals(name))
                                {
                                    // if it is, remember it's ID...
                                    id = entry.Key;
                                    // ...and stop searching.
                                    break;
                                }
                            }
                            // if you didn't find a matching ID, ...
                            if (id == null)
                            {
                                // generate a new one...
                                id = "N" + (++nameCount);
                                // ...and add it - together with the name - to the list of known strings.
                                stringTable.Add(id, new ScriptLine(-1, null, name));
                            }
                            // finally, replace the parameter by a reference to the name ID.
                            parameters[j] = new DataScriptElement(new ExternalStringDataElement(id, stringTable));
                        }
                        // ...a variable string reference, then...
                        else if (param is VarStringRefDataElement)
                        {
                            // ...set the current speaker to "me"...
                            speaker = "me";
                            // ...and keep the parameter as it is.
                            parameters[j] = new DataScriptElement(param);
                        }
                        // ...something else, ...
                        else
                        {
                            // ...then we just keep it as it is.
                            parameters[j] = new DataScriptElement(param);
                        }
                    }
                    // ...none of the above, ...
                    else
                    {
                        // ...then we just keep the parameter as it is.
                        parameters[j] = new DataScriptElement(param);
                    }
                }

                // if the function is called "PF" (PrintFlush?), then...
                if (((FuncDataElement)cmd).name.Equals("PF"))
                {
                    // ...reset the current speaker.
                    speaker = "";
                }

                // if the function is called "FontSet" and the second parameter is an integer, then...
                if (((FuncDataElement)cmd).name.Equals("FontSet") && parameters.Length >= 2 && parameters[1] is DataScriptElement dataElem && dataElem.elem is IntDataElement intElem)
                {
                    // ...keep track of the current font
                    fontID = intElem.data;
                }

                // save the current position in the code stream.
                long pos = s.Position;
                // read the next data element
                DataElement next = NextDataElement(s);
                // check if the next data element is a opening control flow element.
                // if it is, then...
                if (next is ControlDataElement && ((ControlDataElement)next).name.Equals("{"))
                {
                    // ...create an empty list of script elements.
                    List <ScriptElement> body = new List <ScriptElement>();

                    ScriptElement cur;
                    // (read script elements and add them to the list of commands until you encounter a closing flow control element.)
                    // repeat:
                    while (true)
                    {
                        // read the next script element.
                        cur = NextScriptElement(s);
                        // if it is a closing flow control element, ...
                        if (cur is JumpLabelScriptElement && ((JumpLabelScriptElement)cur).name.Equals("}"))
                        {
                            // ...stop repeating immediately
                            break;
                        }
                        // otherwise, add it to the list of commands.
                        body.Add(cur);
                    }

                    // if the function is a branching operation, then...
                    if (((FuncDataElement)cmd).name.Equals("if"))
                    {
                        // ...save the current position in the code stream.
                        pos = s.Position;
                        // read the next data element
                        next = NextDataElement(s);
                        // check if the next data element is a else clause.
                        // if it is, then...
                        if (next is ControlDataElement && ((ControlDataElement)next).name.Equals("else"))
                        {
                            // ...discard the opening flow control element.
                            NextDataElement(s);
                            // ...create an empty list of script elements.
                            List <ScriptElement> falsebody = new List <ScriptElement>();

                            // (read script elements and add them to the list of commands until you encounter a closing flow control element.)
                            // repeat:
                            while (true)
                            {
                                // read the next script element.
                                cur = NextScriptElement(s);
                                // if it is a closing flow control element, ...
                                if (cur is JumpLabelScriptElement && ((JumpLabelScriptElement)cur).name.Equals("}"))
                                {
                                    // ...stop repeating immediately
                                    break;
                                }
                                // otherwise, add it to the list of commands.
                                falsebody.Add(cur);
                            }
                            // use the first (and only) parameter of the function call as branching condition, wrap it - together with the code blocks - in a script element and save it as the result.
                            elem = new BranchScriptElement(parameters[0], body, falsebody);
                        }
                        // it it's not, then...
                        else
                        {
                            // ...jump back to the previously saved code position.
                            s.Seek(pos, SeekOrigin.Begin);
                            // use the first (and only) parameter of the function call as branching condition, wrap it - together with the code block - in a script element and save it as the result.
                            elem = new BranchScriptElement(parameters[0], body, null);
                        }
                    }
                    // if it it not, then
                    else
                    {
                        elem = new SwitchFunctionScriptElement(((FuncDataElement)cmd).name, parameters, body);
                    }
                }
                // if not, ...
                else
                {
                    // ...jump back to the previously saved code position.
                    s.Seek(pos, SeekOrigin.Begin);
                    // wrap the method name and and list of parameters in a script element and save it as the result.
                    elem = new FuncCallScriptElement(((FuncDataElement)cmd).name, parameters);
                }
                break;
            }

            // ...a flow control element, then...
            case DataElement.TYPE_CTRL: {
                // ...create a new jump label with the same name and save it as the result.
                elem = new JumpLabelScriptElement(((ControlDataElement)cmd).name);
                break;
            }

            // ...a variable int or string reference, then...
            case DataElement.TYPE_VINT:
            case DataElement.TYPE_VSTR: {
                // (...we expect an assignment operation to follow.)
                // read assigned expression.
                ScriptElement expression = NextAssignment(s);
                // wrap the variable int/string reference and the expression in a new script element and save it as the result.
                elem = new AssignmentScriptElement(cmd, expression);
                break;
            }

            // ...a temporary variable id, then...
            case DataElement.TYPE_VTMP: {
                // (...we expect an assignment operation to follow.)
                // (after that we expect a command which uses this temporary variable.)
                // read assigned expression.
                ScriptElement expression = NextAssignment(s);
                // set the current temp variable expression to the one we just read...
                substituteExpressions[((VarTempRefDataElement)cmd).id] = expression;
                // ...and return nothing this time.
                break;
            }

            // ...an integer constant, then...
            case DataElement.TYPE_CINT: {
                // (...we have reached a very confusing part of this binary script format.)
                // (we expect an assignment to follow, basically "0 = <something>".)
                // (this sets the current flag pointer (represented by the 0) to <something>.)
                // (the flag pointed to by the pointer can be a accessed by using flag_$, globalflag_$, str_$ or globalstr_$.)
                // read the next script element (the expected assignment).
                FuncCallScriptElement assignment = NextScriptElement(s) as FuncCallScriptElement;
                ScriptElement         expression = null;

                // if the assignment has...
                // ...more than 1 element, ...
                if (assignment.parameters.Length > 1)
                {
                    // ...then create an expression from those.
                    expression = new ExpressionScriptElement(assignment.parameters);
                }
                // ...exactly 1 element, ...
                else if (assignment.parameters.Length == 1)
                {
                    // ...then it already is a valid expression.
                    expression = assignment.parameters[0];
                }
                // ...no elements, ...
                else
                {
                    // ...then read the next script element and use it as expression.
                    expression = NextScriptElement(s);
                }

                // wrap the integer and the expression in a new script element and save it as the result.
                elem = new AssignmentScriptElement(cmd, expression);
                break;
            }

            // ...something else, ...
            default: {
                // the script is probably damaged, so we throw an error.
                throw new Exception("Script format invalid");
            }
            }

            // return the saved result
            return(elem);
        }
Ejemplo n.º 2
0
        ScriptElement NextAssignment(Stream s)
        {
            // read the next script element (the expected assignment).
            FuncCallScriptElement assignment = NextScriptElement(s) as FuncCallScriptElement;

            ScriptElement[] parameters = new ScriptElement[assignment.parameters.Length];

            // go through all parameters of the assignment.
            for (int i = 0; i < assignment.parameters.Length; i++)
            {
                ScriptElement param = assignment.parameters[i];
                // if the parameter is...
                // ...a string constant, then...
                if (param is DataScriptElement && ((DataScriptElement)param).elem is StringDataElement)
                {
                    // ...retrieve it's value.
                    string str = ((StringDataElement)((DataScriptElement)param).elem).data;
                    // if it contains only whitespace, if it is a number, or if it is part of a file name, then...
                    if (str.Trim().Length == 0 || str.All(char.IsDigit) || str.Contains("\\") || str.ToLower().Contains(".yk") || str.ToLower().Contains(".ogg"))
                    {
                        // ...just use it in the expression.
                        parameters[i] = param;
                    }
                    // otherwise...
                    else
                    {
                        string id = null;
                        // ...generate a new one...
                        id = "S" + (++stringCount);
                        // ...and add it to the list of known strings.
                        stringTable.Add(id, new ScriptLine(-1, null, str));
                        // then use an external string reference as the new expression.
                        parameters[i] = new DataScriptElement(new ExternalStringDataElement(id, stringTable));
                    }
                }
                // ...a temporary variable reference, then...
                else if (param is DataScriptElement && ((DataScriptElement)param).elem is VarTempRefDataElement)
                {
                    // ...replace it by the current substitution expression.
                    parameters[i] = substituteExpressions[((VarTempRefDataElement)((DataScriptElement)param).elem).id];
                }
                // ...something else, then...
                else
                {
                    // ...just use it in the expression.
                    parameters[i] = param;
                }
            }

            ScriptElement expression = null;

            // if the assignment has...
            // ...more than 1 elements, then...
            if (parameters.Length > 1)
            {
                // ...create an expression from those.
                expression = new ExpressionScriptElement(parameters);
            }
            // ...exactly 1 element, then...
            else if (assignment.parameters.Length == 1)
            {
                expression = parameters[0];
            }
            // ...no elements, ...
            else
            {
                // ...then read the next script element and use it as expression.
                expression = NextScriptElement(s);
            }
            return(expression);
        }