Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
echo "version=$version" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append

- name: Upload artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: OpcodeExtractor-${{ env.version }}-${{ env.artifact_sha }}
path: bin/Release/publish/
Expand Down
60 changes: 39 additions & 21 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,39 +41,63 @@ static async Task<int> Main(string[] args)
rootCommand.AddArgument(outputFormatArgument);
rootCommand.AddArgument(inputMapKeyArgument);

rootCommand.SetHandler((opcodeMapFile, gameExecutable, dumpAllOpcodes, outputFormat, inputMapKey) =>
{
var opcodes = ExtractOpcodes(opcodeMapFile!, gameExecutable!, dumpAllOpcodes, inputMapKey);
OutputOpcodes(opcodes, outputFormat);
},
opcodeFileMapArgument, gameExecutableArgument, dumpAllOpcodesArgument, outputFormatArgument, inputMapKeyArgument);
rootCommand.SetHandler(CommandHandler, opcodeFileMapArgument, gameExecutableArgument, dumpAllOpcodesArgument, outputFormatArgument, inputMapKeyArgument);

return await rootCommand.InvokeAsync(args);
}

private static void OutputOpcodes(Dictionary<int, string> opcodes, OutputFormat outputFormat)
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);
OutputOpcodesForFFXIV_ACT_Plugin(opcodes, sortOrder);
}
if (outputFormat == OutputFormat.All || outputFormat == OutputFormat.OverlayPlugin)
{
OutputOpcodesForOverlayPlugin(opcodes);
OutputOpcodesForOverlayPlugin(opcodes, sortOrder);
}
}

private static void OutputOpcodesForFFXIV_ACT_Plugin(Dictionary<int, string> opcodes)
private static void OutputOpcodesForFFXIV_ACT_Plugin(Dictionary<int, string> opcodes, string[] sortOrder)
{
foreach (var entry in opcodes)
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)
private static void OutputOpcodesForOverlayPlugin(Dictionary<int, string> opcodes, string[] sortOrder)
{
Dictionary<string, Dictionary<string, int>> overlayPluginMap = [];
Dictionary<string, Dictionary<string, int>?> overlayPluginMap = [];

foreach (var key in sortOrder)
{
overlayPluginMap[key] = null;
}

foreach (var entry in opcodes)
{
Expand All @@ -95,17 +119,11 @@ private static void OutputOpcodesForOverlayPlugin(Dictionary<int, string> opcode
/// <summary>
/// Map opcodes as defined in opcodeMapFile for executable gameExecutable
/// </summary>
/// <param name="opcodeMapFile">The opcode map to use</param>
/// <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(FileInfo opcodeMapFile, FileInfo gameExecutable, bool dumpAllOpcodes, string inputMapKey)
public static Dictionary<int, string> ExtractOpcodes(JsonNode opcodeMapData, FileInfo gameExecutable, bool dumpAllOpcodes, string inputMapKey)
{
var opcodeMapData = JsonSerializer.Deserialize<JsonNode>(File.ReadAllText(opcodeMapFile.FullName), new JsonSerializerOptions()
{
ReadCommentHandling = JsonCommentHandling.Skip,
});
if (opcodeMapData == null) return [];

var opcodeMethod = opcodeMapData["method"]?.ToString() ?? "";

byte[] gameData = File.ReadAllBytes(gameExecutable.FullName);
Expand Down
30 changes: 15 additions & 15 deletions resources/global.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,39 @@
"FFXIV_ACT_Plugin": {
"StatusEffectList": 207,
"StatusEffectList2": 209,
"StatusEffectList3": 237,
"BossStatusEffectList": 211,
"Ability1": 228,
"Ability8": 231,
"Ability16": 232,
"Ability24": 233,
"Ability32": 234,
"ActorCast": 277,
"EffectResult": 213,
"EffectResultBasic": 218,
"ActorControl": 224,
"ActorControlSelf": 225,
"ActorControlTarget": 226,
"UpdateHpMpTp": 227,
"Ability1": 228,
"Ability8": 231,
"Ability16": 232,
"Ability24": 233,
"Ability32": 234,
"StatusEffectList3": 237,
"PlayerSpawn": 268,
"NpcSpawn": 269,
"NpcSpawn2": 270,
"ActorMove": 272,
"ActorSetPos": 275,
"ActorCast": 277,
"SystemLogMessage": 408,
"ActorGauge": 585,
"PresetWaymark": 534,
"Waymark": 535,
"ActorGauge": 585
"SystemLogMessage": 408
},
"OverlayPlugin": {
"MapEffect": 402,
"CEDirector": 649,
"RSVData": 127,
"NpcYell": 448,
"BattleTalk2": 414,
"Countdown": 147,
"CountdownCancel": 148,
"SpawnObject": 284,
"DespawnObject": 285,
"MapEffect": 402,
"BattleTalk2": 414,
"NpcYell": 448,
"CEDirector": 649
"DespawnObject": 285
}
}
}
Loading