1
Fork 0
mirror of https://github.com/awgil/ffxiv_reverse.git synced 2025-04-20 06:07:46 +00:00
- bumped CS version to contain a bunch of fixes
- correctly strip namespace element if it is the last one
- do not export types marked as obsolete
- removed test.yml generation
- added a pass to dump nested unions
- a bunch of TODOs on python side
This commit is contained in:
Andrew Gilewsky 2023-05-10 21:59:01 +01:00
parent 2c2f94d591
commit 1d4f9ff7f3
6 changed files with 37 additions and 18 deletions

2
.gitignore vendored
View file

@ -3,3 +3,5 @@ obj/
bin/ bin/
*.user *.user
logs/ logs/
idbtoolkit/info.yml
idbtoolkit/__pycache__/

@ -1 +1 @@
Subproject commit b67538d428032c0fab9bce8062f2c2b79bc6e7fc Subproject commit 9375140795326c929c5eaeb5283082f1eb1909a1

View file

@ -10,7 +10,7 @@ namespace idapopulate;
internal static class CSImportExt internal static class CSImportExt
{ {
public static string WithoutPrefix(this string str, string prefix) => str.StartsWith(prefix) ? str.Substring(prefix.Length) : str; public static string WithoutPrefix(this string str, string prefix, string sep) => str == prefix ? "" : str.StartsWith(prefix + sep) ? str.Substring(prefix.Length + sep.Length) : str;
// if [FieldOffset] is not specified, assume sequential layout... // if [FieldOffset] is not specified, assume sequential layout...
public static int GetFieldOffset(this FieldInfo fi) => fi.GetCustomAttribute<FieldOffsetAttribute>()?.Value ?? Marshal.OffsetOf(fi.DeclaringType!, fi.Name).ToInt32(); public static int GetFieldOffset(this FieldInfo fi) => fi.GetCustomAttribute<FieldOffsetAttribute>()?.Value ?? Marshal.OffsetOf(fi.DeclaringType!, fi.Name).ToInt32();
@ -64,6 +64,8 @@ internal class CSImport
{ {
if (type.FullName == null) if (type.FullName == null)
return false; return false;
if (type.GetCustomAttribute<ObsoleteAttribute>() != null)
return false;
if (!type.FullName.StartsWith("FFXIVClientStructs.FFXIV.") && !type.FullName.StartsWith("FFXIVClientStructs.Havok.")) if (!type.FullName.StartsWith("FFXIVClientStructs.FFXIV.") && !type.FullName.StartsWith("FFXIVClientStructs.Havok."))
return false; return false;
if (type.DeclaringType != null && (type.Name is "Addresses" or "MemberFunctionPointers" or "StaticAddressPointers" || type.Name == type.DeclaringType.Name + "VTable")) if (type.DeclaringType != null && (type.Name is "Addresses" or "MemberFunctionPointers" or "StaticAddressPointers" || type.Name == type.DeclaringType.Name + "VTable"))
@ -266,7 +268,7 @@ internal class CSImport
private string TypeNameComplex(Type type) private string TypeNameComplex(Type type)
{ {
var baseName = type.DeclaringType != null ? TypeNameComplex(type.DeclaringType) : type.Namespace?.WithoutPrefix("FFXIVClientStructs.").WithoutPrefix("FFXIV.").WithoutPrefix("Havok.").Replace(".", "::") ?? ""; var baseName = type.DeclaringType != null ? TypeNameComplex(type.DeclaringType) : type.Namespace?.WithoutPrefix("FFXIVClientStructs", ".").WithoutPrefix("FFXIV", ".").WithoutPrefix("Havok", ".").Replace(".", "::") ?? "";
var leafName = type.Name; var leafName = type.Name;
if (type.IsGenericType) if (type.IsGenericType)
{ {
@ -281,7 +283,7 @@ internal class CSImport
// hack for std // hack for std
if (fullName.StartsWith("STD::Std")) if (fullName.StartsWith("STD::Std"))
{ {
fullName = fullName.WithoutPrefix("STD::Std"); fullName = fullName.WithoutPrefix("STD::Std", "");
fullName = "std::"+ fullName.Substring(0, 1).ToLower() + fullName.Substring(1); fullName = "std::"+ fullName.Substring(0, 1).ToLower() + fullName.Substring(1);
} }
return fullName; return fullName;

View file

@ -8,25 +8,12 @@ if (outDir == null)
return; return;
} }
var testRes = new Result();
var testEnum1 = new Result.Enum() { IsSigned = true, Width = 2 };
testEnum1.Values.Add(new() { Name = "E1V1", Value = 1 });
testEnum1.Values.Add(new() { Name = "E1V1dup", Value = 1 });
testEnum1.Values.Add(new() { Name = "E1V2", Value = 2 });
testEnum1.Values.Add(new() { Name = "E1V3", Value = 10 });
testRes.Enums["test::Enum1"] = testEnum1;
var testEnum2 = new Result.Enum() { IsBitfield = true, IsSigned = false, Width = 4 };
testEnum2.Values.Add(new() { Name = "E2V1", Value = 0x1 });
testEnum2.Values.Add(new() { Name = "E2V2", Value = 0x4 });
testRes.Enums["test::Enum2"] = testEnum2;
testRes.Enums["test::Enum3"] = new() { Width = 1 };
testRes.Write(outDir.FullName + "/test.yml");
var gameRoot = PathUtils.FindGameRoot(); var gameRoot = PathUtils.FindGameRoot();
var resolver = new SigResolver(gameRoot + "\\ffxiv_dx11.exe"); var resolver = new SigResolver(gameRoot + "\\ffxiv_dx11.exe");
var res = new Result(); var res = new Result();
new CSImport().Populate(res, resolver); new CSImport().Populate(res, resolver);
res.DumpNestedUnions();
var dataYml = PathUtils.FindFileAmongParents("/FFXIVClientStructs/ida/data.yml"); var dataYml = PathUtils.FindFileAmongParents("/FFXIVClientStructs/ida/data.yml");
if (dataYml != null) if (dataYml != null)

View file

@ -1,6 +1,7 @@
using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
namespace idapopulate; namespace idapopulate;
@ -144,6 +145,28 @@ internal class Result
File.WriteAllText(path, data); File.WriteAllText(path, data);
} }
public void DumpNestedUnions()
{
foreach (var (name, s) in Structs)
{
if (s.Fields.All(f => f.Offset == 0))
continue; // it's a simple union, we handle that on IDA side
foreach (var g in s.Fields.GroupBy(f => f.Offset))
{
if (g.Count() > 1)
{
var firstType = g.First().Type;
if (!g.All(f => f.Type == firstType))
{
Debug.WriteLine($"Nested union found at {name}+0x{g.Key:X}: {string.Join(", ", g.Select(f => f.Name))}");
}
// else: all elements of the same type, we don't create union for that on IDA side
}
}
}
}
public bool ValidateUniqueEaName() public bool ValidateUniqueEaName()
{ {
Dictionary<ulong, string> uniqueEAs = new(); Dictionary<ulong, string> uniqueEAs = new();

View file

@ -145,6 +145,7 @@ def populate_function_names(data):
ensure_function_at(ea, "function") # if function is not referenced from anywhere, define one manually ensure_function_at(ea, "function") # if function is not referenced from anywhere, define one manually
set_custom_name(ea, g['name'], g['address']) set_custom_name(ea, g['name'], g['address'])
# TODO: calculate masks for bitfield values properly
def populate_enums(data): def populate_enums(data):
msg('** Populating exported enums **') msg('** Populating exported enums **')
@ -170,6 +171,7 @@ def populate_enums(data):
except Exception as e: except Exception as e:
msg(f'Enum error: {e}') msg(f'Enum error: {e}')
# TODO: have c# app generate correctly ordered list
def populate_vtables(data): def populate_vtables(data):
# vtable population is done in several passes # vtable population is done in several passes
Vtable = collections.namedtuple("VTable", "primaryEA secondaryEAs vFuncs base") Vtable = collections.namedtuple("VTable", "primaryEA secondaryEAs vFuncs base")
@ -316,6 +318,9 @@ def populate_vtables(data):
pass3() pass3()
return vtables return vtables
# TODO: if all fields are at offset 0, just create union directly
# TODO: if all fields in the nested union are of the same type, don't create a union, instead just add member comment with alt names
# TODO: have c# app generate correctly ordered list
def populate_structs(data): def populate_structs(data):
# structure creation is done in two passes # structure creation is done in two passes
res = {} # base/substruct always ordered before referencing struct; key = class name res = {} # base/substruct always ordered before referencing struct; key = class name