Beispiel #1
0
        private void BoolConditionListItem(ConditionData.BoolVersion b, InstructionTranslator.FunctionDoc doc, InstructionTranslator.FunctionDoc baseDoc)
        {
            sb.Append($"<li><code>{Escape(doc.Name)}(");
            FunctionArguments(doc.Args, doc.OptionalArgs, false);
            sb.AppendLine($")</code>");

            EMEDF.ArgDoc  negateArg  = baseDoc.Args.Find(a => a.Name == doc.ConditionDoc.NegateField);
            EMEDF.EnumDoc negateEnum = doc.NegateEnum;
            List <string> details    = new List <string>();

            string getReq(EMEDF.ArgDoc arg, object val, object val2)
            {
                string showVal(object v) => Escape(arg.GetDisplayValue(v).ToString());

                return($"<code>{Escape(arg.DisplayName)} = {showVal(val)}{(val2 == null ? "" : " or " + showVal(val2))}</code>");
            }

            if (b.Required != null)
            {
                foreach (ConditionData.FieldValue req in b.Required)
                {
                    EMEDF.ArgDoc reqArg = baseDoc.Args.Find(a => a.Name == req.Field);
                    if (reqArg != null)
                    {
                        details.Add(getReq(reqArg, req.Value, null));
                    }
                }
            }
            if (negateArg != null && negateEnum != null)
            {
                if (b.True != null)
                {
                    int trueNum = int.Parse(negateEnum.Values.FirstOrDefault(e => e.Value == b.True).Key);
                    if (b.False == null)
                    {
                        details.Add(getReq(negateArg, trueNum, InstructionTranslator.OppositeOp(doc.ConditionDoc, negateEnum, trueNum)));
                    }
                    else
                    {
                        int falseNum = int.Parse(negateEnum.Values.FirstOrDefault(e => e.Value == b.False).Key);
                        details.Add(getReq(negateArg, trueNum, falseNum));
                    }
                }
                else
                {
                    details.Add($"{Escape(negateArg.DisplayName)} = true or false");
                }
            }
            sb.AppendLine($"<br/><span class=\"conddetails\">Where <code>{string.Join(" and ", details)}</code></span></li>");
        }
Beispiel #2
0
        public void AppendEMEDF(InstructionDocs docs, string emevdDir = null)
        {
            string game     = gameNames.Keys.FirstOrDefault(g => docs.ResourceString.StartsWith(g + "-common"));
            string gameName = game == null ? docs.ResourceString : gameNames[game];

            Dictionary <string, Usages> symbolUsages = new Dictionary <string, Usages>();
            bool showUsages = false;

            if (emevdDir != null)
            {
                symbolUsages = GetSymbolUsages(game, emevdDir, docs);
                showUsages   = true;
                // Hack to merge PTDE and DS1R into one
                if (emevdDir.Contains("DARK SOULS REMASTERED"))
                {
                    string ptdeDir = emevdDir.Replace(@"DARK SOULS REMASTERED\event", @"Dark Souls Prepare to Die Edition\DATA\event");
                    if (ptdeDir != emevdDir)
                    {
                        Dictionary <string, Usages> ptdeUsages = GetSymbolUsages(game, ptdeDir, docs);
                        foreach (string symbol in symbolUsages.Keys.Union(ptdeUsages.Keys).ToList())
                        {
                            symbolUsages.TryGetValue(symbol, out Usages ds1rUse);
                            ptdeUsages.TryGetValue(symbol, out Usages ptdeUse);
                            symbolUsages[symbol] = Usages.Reconcile(ds1rUse, "in DS1R", ptdeUse, "in PTDE");
                        }
                    }
                }
            }

            PageHeader(gameName + " EMEDF for DarkScript3", docs.Translator != null);

            string mainCondName(ConditionData.ConditionDoc condDoc)
            {
                if (condDoc.Name == "Compare")
                {
                    return(condDoc.Name);
                }
                string name = condDoc.AllBools.FirstOrDefault()?.Name;

                if (name != null)
                {
                    return(name);
                }
                name = condDoc.AllCompares.FirstOrDefault()?.Name;
                if (name != null)
                {
                    return(name);
                }
                return(condDoc.Name);
            }

            // Instructions
            // Classes section
            // Instructions per xyz
            // For each instruction: one-line method signature, links to enums, equivalent conditions' one-line signatures,
            // equivalent fancy commands, usages
            BigSectionHeader("Instructions");
            foreach (EMEDF.ClassDoc classDoc in docs.DOC.Classes)
            {
                string className = $"{classDoc.Index} - {classDoc.Name}";
                SubHeader(className, className);
                foreach (EMEDF.InstrDoc instrDoc in classDoc.Instructions)
                {
                    string id   = InstructionTranslator.InstructionID(classDoc.Index, instrDoc.Index);
                    string name = instrDoc.DisplayName;

                    InstructionTranslator.ConditionSelector condSelect = null;
                    docs.Translator?.Selectors.TryGetValue(id, out condSelect);

                    List <string> tags = new List <string> {
                        "instr"
                    };
                    if (condSelect != null || (docs.Translator?.LabelDocs.ContainsKey(id) ?? false))
                    {
                        tags.Add("condinstr");
                    }
                    bool unused = showUsages && !symbolUsages.ContainsKey(name);
                    if (unused)
                    {
                        tags.Add("unused");
                    }

                    Section(name, "Instruction " + id, tags, classDoc.Index != 1014, () =>
                    {
                        if (showUsages)
                        {
                            symbolUsages.TryGetValue(name, out Usages usages);
                            SectionUsageDetails(usages);
                        }
                        FunctionSignature(name, instrDoc.Arguments.ToList(), 0);
                        if (condSelect != null)
                        {
                            if (!condSelect.Cond.Hidden)
                            {
                                string condName = mainCondName(condSelect.Cond);
                                sb.Append($"<p>Condition function: <code>");
                                Link(condName, condName);
                                sb.AppendLine("</code></p>");
                            }
                            else if (specialCommands.TryGetValue(id, out string alt))
                            {
                                sb.Append($"<p><code>{Escape(alt)}</code> in MattScript</p>");
                            }
                        }
                    });
                }
            }
            BigSectionFooter();

            // Condition functions. Main head is first bool/compare if it exists
            if (docs.Translator != null)
            {
                BigSectionHeader("Condition Functions");
                // Reread it to get the original order and grouping and names, but use InstructionTranslator for everything else
                ConditionData         conds = ConditionData.ReadStream("conditions.json");
                InstructionTranslator info  = docs.Translator;
                // There are duplicate names for different games, so just bail out if the same name encountered again
                HashSet <string> condNames = new HashSet <string>();
                foreach (ConditionData.ConditionDoc storedCondDoc in conds.Conditions)
                {
                    if (storedCondDoc.Hidden || condNames.Contains(storedCondDoc.Name))
                    {
                        continue;
                    }
                    condNames.Add(storedCondDoc.Name);
                    if (!info.CondDocs.TryGetValue(storedCondDoc.Name, out InstructionTranslator.FunctionDoc baseDoc))
                    {
                        // Corresponding instructions do not exist in this game
                        continue;
                    }
                    // Make sure we have the right one for this game
                    ConditionData.ConditionDoc condDoc = baseDoc.ConditionDoc;
                    Usages usages = Usages.UnionAll(
                        baseDoc.Variants.Values
                        .Select(id => symbolUsages.TryGetValue(info.InstrDocs[id].DisplayName, out Usages instrUsages) ? instrUsages : null));

                    List <string> tags = new List <string> {
                        "cond"
                    };
                    bool unused = showUsages && usages == null;
                    if (unused)
                    {
                        tags.Add("unused");
                    }

                    Section(mainCondName(condDoc), "Condition function", tags, true, () =>
                    {
                        if (showUsages)
                        {
                            SectionUsageDetails(usages);
                        }
                        FunctionSignature(condDoc.Name, baseDoc.Args, baseDoc.OptionalArgs);

                        int variantCount = condDoc.AllBools.Count + condDoc.AllCompares.Count;
                        if (variantCount > 0)
                        {
                            sb.AppendLine($"<p class=\"liststart\">Simpler version{(variantCount == 1 ? "" : "s")}:</p><ul class=\"condlist\">");
                            foreach (ConditionData.BoolVersion b in condDoc.AllBools)
                            {
                                BoolConditionListItem(b, info.CondDocs[b.Name], baseDoc);
                            }
                            foreach (ConditionData.CompareVersion c in condDoc.AllCompares)
                            {
                                CompareConditionListItem(c, info.CondDocs[c.Name], baseDoc);
                            }
                            sb.AppendLine("</ul>");
                        }
                    });
                }
                BigSectionFooter();
            }

            // Enums
            // Name, all values. exclude bools tho
            BigSectionHeader("Enums");
            foreach (EMEDF.EnumDoc enumDoc in docs.DOC.Enums)
            {
                if (enumDoc.Name == "BOOL")
                {
                    continue;
                }

                string        name = enumDoc.DisplayName;
                List <string> tags = new List <string> {
                    "enum"
                };
                if (showUsages && !symbolUsages.ContainsKey(name))
                {
                    tags.Add("unused");
                }

                Section(name, "Enum", tags, true, () =>
                {
                    symbolUsages.TryGetValue(name, out Usages enumUsages);
                    if (showUsages)
                    {
                        SectionUsageDetails(enumUsages);
                    }
                    bool showDetails = !noDetailsEnums.Contains(name) && !enumDoc.DisplayValues.All(
                        e => symbolUsages.TryGetValue(e.Value, out Usages entryUsages) && Usages.Equals(entryUsages, enumUsages));
                    sb.AppendLine("<ul class=\"enumlist\">");
                    foreach (KeyValuePair <string, string> entry in enumDoc.DisplayValues)
                    {
                        string entryNum    = entry.Key;
                        string entryName   = entry.Value;
                        string unusedClass = showDetails && !symbolUsages.ContainsKey(entryName) ? " class=\"enumunused\"" : "";
                        sb.Append($"<li><code{unusedClass}>{entryName} = {entryNum}</code>");
                        if (showDetails && symbolUsages.TryGetValue(entryName, out Usages entryUsages))
                        {
                            sb.Append($" <span class=\"enumusage usageinfo\">Used in {entryUsages}</span>");
                        }
                        sb.AppendLine("</li>");
                    }
                    sb.AppendLine("</ul>");
                });
            }
            BigSectionFooter();

            PageFooter();
        }
Beispiel #3
0
        private void Decompile(TextWriter writer)
        {
            EMEDF DOC = docs.DOC;
            InstructionTranslator info = docs.Translator;

            for (int i = 0; i < scripter.EVD.Events.Count; i++)
            {
                Event  evt          = scripter.EVD.Events[i];
                string id           = evt.ID.ToString();
                string restBehavior = evt.RestBehavior.ToString();

                Dictionary <Parameter, string> paramNames = docs.ParamNames(evt);
                List <string> argNameList = paramNames.Values.Distinct().ToList();
                Dictionary <Parameter, ParamArg> paramArgs = paramNames.ToDictionary(e => e.Key, e => new ParamArg {
                    Name = e.Value
                });

                EventFunction func = new EventFunction {
                    ID = (int)evt.ID, RestBehavior = evt.RestBehavior, Params = argNameList
                };

                string eventName = scripter.EventName(evt.ID);
                if (eventName != null)
                {
                    writer.WriteLine($"// {eventName}");
                }

                for (int insIndex = 0; insIndex < evt.Instructions.Count; insIndex++)
                {
                    Instruction    ins = evt.Instructions[insIndex];
                    EMEDF.InstrDoc doc = docs.DOC[ins.Bank]?[ins.ID];
                    if (doc == null)
                    {
                        throw new Exception($"Unknown instruction in event {id}: {ins.Bank}[{ins.ID}] {string.Join(" ", ins.ArgData.Select(b => $"{b:x2}"))}");
                    }
                    string funcName = doc.DisplayName;

                    IEnumerable <ArgType> argStruct = doc.Arguments.Select(arg => arg.Type == 8 ? ArgType.UInt32 : (ArgType)arg.Type);

                    Layers layers = ins.Layer is uint l ? new Layers {
                        Mask = l
                    } : null;
                    Instr instr = new Instr {
                        Inner = ins, Cmd = InstructionID(ins.Bank, ins.ID), Name = funcName, Layers = layers
                    };

                    try
                    {
                        instr.Args = docs.UnpackArgsWithParams(ins, insIndex, doc, paramArgs, (argDoc, val) =>
                        {
                            if (argDoc.GetDisplayValue(val) is string displayStr)
                            {
                                return(new DisplayArg {
                                    DisplayValue = displayStr, Value = val
                                });
                            }
                            return(val);
                        });
                    }
                    catch (Exception ex)
                    {
                        var sb = new StringBuilder();
                        sb.AppendLine($@"Unable to unpack arguments for {funcName} at Event {evt.ID}[{insIndex}]");
                        sb.AppendLine($@"Instruction arg data: {InstructionDocs.InstrDebugString(ins)}");
                        sb.AppendLine(ex.Message);
                        throw new Exception(sb.ToString());
                    }
                    func.Body.Add(instr);
                }
                EventCFG f = new EventCFG((int)evt.ID, options);
                try
                {
                    // This returns warnings, many of which exist in vanilla emevd.
                    // Ignored until we have a nice way to show them.
                    f.Decompile(func, info);
                }
                catch (FancyNotSupportedException)
                {
                    // For the moment, swallow this.
                    // Can find a way to expose the error, but these are basically intentional bail outs, also existing in vanilla emevd.
                    StringBuilder code = new StringBuilder();
                    scripter.UnpackEvent(evt, code);
                    writer.Write(code.ToString());
                    continue;
                }
                catch (Exception ex)
                {
                    var sb = new StringBuilder();
                    sb.AppendLine($@"Error decompiling Event {evt.ID}");
                    sb.AppendLine(ex.ToString());
                    throw new Exception(sb.ToString());
                }
                func.Print(writer);
                writer.WriteLine();
            }
        }