-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathProgram.cs
More file actions
137 lines (119 loc) · 5.19 KB
/
Program.cs
File metadata and controls
137 lines (119 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System.CommandLine;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace OpcodeExtractor;
public enum OutputFormat
{
All = -1,
FFXIV_ACT_Plugin,
OverlayPlugin
}
public class Program
{
static async Task<int> Main(string[] args)
{
var opcodeFileMapArgument = new Argument<FileInfo?>(
name: "opcodeMapFile",
description: "The opcode map to use.");
var gameExecutableArgument = new Argument<FileInfo?>(
name: "gameExecutable",
description: "The game executable to map.");
var dumpAllOpcodesArgument = new Argument<bool>(
name: "dumpAllOpcodes",
description: "Should all opcodes be dumped or just those specified in the map file. Default = \"False\"");
var outputFormatArgument = new Argument<OutputFormat>(
name: "outputFormat",
description: "Which output format to use. Default = \"All\"");
var inputMapKeyArgument = new Argument<string>(
name: "inputMapKey",
description: "Which opcode map key to use. Default = \"FFXIV_ACT_Plugin\"");
dumpAllOpcodesArgument.SetDefaultValue(false);
outputFormatArgument.SetDefaultValue(OutputFormat.All);
inputMapKeyArgument.SetDefaultValue("FFXIV_ACT_Plugin");
var rootCommand = new RootCommand("Map opcodes as defined in opcodeMapFile for executable gameExecutable");
rootCommand.AddArgument(opcodeFileMapArgument);
rootCommand.AddArgument(gameExecutableArgument);
rootCommand.AddArgument(dumpAllOpcodesArgument);
rootCommand.AddArgument(outputFormatArgument);
rootCommand.AddArgument(inputMapKeyArgument);
rootCommand.SetHandler(CommandHandler, opcodeFileMapArgument, gameExecutableArgument, dumpAllOpcodesArgument, outputFormatArgument, inputMapKeyArgument);
return await rootCommand.InvokeAsync(args);
}
private static void CommandHandler(FileInfo? opcodeMapFile, FileInfo? gameExecutable, bool dumpAllOpcodes, OutputFormat outputFormat, string inputMapKey)
{
var opcodeMapData = JsonSerializer.Deserialize<JsonNode>(File.ReadAllText(opcodeMapFile!.FullName), new JsonSerializerOptions()
{
ReadCommentHandling = JsonCommentHandling.Skip,
});
if (opcodeMapData == null)
{
Console.Error.WriteLine("Invalid opcodes file");
return;
}
var opcodes = ExtractOpcodes(opcodeMapData, gameExecutable!, dumpAllOpcodes, inputMapKey);
var sortOrder = opcodeMapData[inputMapKey]?.AsObject().ToDictionary().Keys.ToArray();
if (sortOrder == null)
{
Console.Error.WriteLine("Invalid data type for \"map\" in opcodes file");
sortOrder = [];
}
OutputOpcodes(opcodes, outputFormat, sortOrder);
}
private static void OutputOpcodes(Dictionary<int, string> opcodes, OutputFormat outputFormat, string[] sortOrder)
{
if (outputFormat == OutputFormat.All || outputFormat == OutputFormat.FFXIV_ACT_Plugin)
{
OutputOpcodesForFFXIV_ACT_Plugin(opcodes, sortOrder);
}
if (outputFormat == OutputFormat.All || outputFormat == OutputFormat.OverlayPlugin)
{
OutputOpcodesForOverlayPlugin(opcodes, sortOrder);
}
}
private static void OutputOpcodesForFFXIV_ACT_Plugin(Dictionary<int, string> opcodes, string[] sortOrder)
{
var entries = opcodes.ToList().OrderBy(x => Array.IndexOf(sortOrder, x.Value)).ToList();
foreach (var entry in entries)
{
Console.WriteLine($"{entry.Value}|{entry.Key:x}");
}
}
private static void OutputOpcodesForOverlayPlugin(Dictionary<int, string> opcodes, string[] sortOrder)
{
Dictionary<string, Dictionary<string, int>?> overlayPluginMap = [];
foreach (var key in sortOrder)
{
overlayPluginMap[key] = null;
}
foreach (var entry in opcodes)
{
Dictionary<string, int> opEntry = new Dictionary<string, int>()
{
["opcode"] = entry.Key,
["size"] = 0,
};
overlayPluginMap[entry.Value] = opEntry;
}
Console.WriteLine(JsonSerializer.Serialize(overlayPluginMap, new JsonSerializerOptions()
{
WriteIndented = true
}));
}
/// <summary>
/// Map opcodes as defined in opcodeMapFile for executable gameExecutable
/// </summary>
/// <param name="opcodeMapData">The opcode map to use</param>
/// <param name="gameExecutable">The game executable to map</param>
/// <param name="dumpAllOpcodes">Whether to dump all opcodes, or just the mapped opcodes</param>
public static Dictionary<int, string> ExtractOpcodes(JsonNode opcodeMapData, FileInfo gameExecutable, bool dumpAllOpcodes, string inputMapKey)
{
var opcodeMethod = opcodeMapData["method"]?.ToString() ?? "";
byte[] gameData = File.ReadAllBytes(gameExecutable.FullName);
switch (opcodeMethod)
{
case "vtable":
return OpcodeExtractorVTable.Extract(opcodeMapData, gameData, dumpAllOpcodes, inputMapKey);
}
return [];
}
}