// Usages // Usages for display names of these symbols: instruction name, enum name, enum value private Dictionary <string, Usages> GetSymbolUsages(string game, string emevdDir, InstructionDocs docs) { Dictionary <string, HashSet <string> > symbolsByFile = new Dictionary <string, HashSet <string> >(); HashSet <string> allSymbols = new HashSet <string>(); interestingEmevds.TryGetValue(game, out Regex mainRegex); List <string> files = new List <string>(); Console.WriteLine($"------ Usages from [{emevdDir}]"); foreach (string emevdPath in Directory.GetFiles(emevdDir)) { if (mainRegex != null && !mainRegex.Match(Path.GetFileName(emevdPath)).Success) { continue; } string name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(emevdPath)); files.Add(name); Console.WriteLine($"--- {name}"); HashSet <string> symbols = symbolsByFile[name] = new HashSet <string>(); EMEVD emevd = EMEVD.Read(emevdPath); foreach (EMEVD.Event evt in emevd.Events) { for (int insIndex = 0; insIndex < evt.Instructions.Count; insIndex++) { // This is all very best-effort EMEVD.Instruction ins = evt.Instructions[insIndex]; EMEDF.InstrDoc doc = docs.DOC[ins.Bank]?[ins.ID]; if (doc == null) { continue; } symbols.Add(doc.DisplayName); Dictionary <EMEVD.Parameter, string> paramNames = docs.ParamNames(evt); try { // A slight abuse of this function, ignoring the returned list docs.UnpackArgsWithParams(ins, insIndex, doc, paramNames, (argDoc, val) => { if (argDoc.GetDisplayValue(val) is string displayStr) { symbols.Add(displayStr); } return(val); }); // Also add a usage if the enum is present at all, even if parameterized foreach (EMEDF.ArgDoc argDoc in doc.Arguments) { if (argDoc.EnumDoc != null && argDoc.EnumName != "BOOL") { symbols.Add(argDoc.EnumDoc.DisplayName); } } } catch { } } } allSymbols.UnionWith(symbols); } Dictionary <string, Usages> symbolUsages = new Dictionary <string, Usages>(); List <string> primaryFiles = null; if (secondaryEmevd.TryGetValue(game, out Regex secondaryRegex)) { primaryFiles = files.Where(f => !secondaryRegex.Match(f).Success).ToList(); } foreach (string symbol in allSymbols) { List <string> matchFiles = files.Where(f => symbolsByFile[f].Contains(symbol)).ToList(); List <string> target = files; if (primaryFiles != null) { List <string> primaryMatchFiles = matchFiles.Intersect(primaryFiles).ToList(); if (primaryMatchFiles.Count > 0) { matchFiles = primaryMatchFiles; target = primaryFiles; } } // Combining PTDE and DS1R is done after. symbolUsages[symbol] = new Usages { Files = matchFiles, AllFiles = target }; } return(symbolUsages); }
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(); } }