mirror of
https://github.com/Scobalula/Tyrant
synced 2024-12-11 13:02:43 +00:00
RE7 Fixes
This commit is contained in:
parent
a545324002
commit
38dae7b9f2
6 changed files with 237 additions and 128 deletions
13
README.md
13
README.md
|
@ -45,4 +45,15 @@ Greyhound is distributed with an automatic updater that will check for updates e
|
|||
|
||||
## Reporting Bugs
|
||||
|
||||
Bugs can be reported through the Github issues or through the Discord server. Please make sure to provide a log when reporting bugs and any info you can possibly give.
|
||||
Bugs can be reported through the Github issues or through the Discord server. Please make sure to provide a log when reporting bugs and any info you can possibly give.
|
||||
|
||||
## FAQ
|
||||
|
||||
* **Q**: I can't find some models?
|
||||
* **A**: Not all assets have names as entries within the archives are hashed, currently you can enable the setting to show all assets and in the future the tool will attempt to convert these if they are supported, however currently they are dumped raw.
|
||||
|
||||
* **Q**: Some models export with no images?
|
||||
* **A**: The tool attempts to locate the material files based off the model name, this works for most models, but not all, in the future material defs will be exportable on their own to aid with models where the materials cannot be located.
|
||||
|
||||
* **Q**: Some models export with incorrect UVs?
|
||||
* **A**: A lot of materials rely on UV scales and offsets, currently it is unknown what data defines this. Usually this easy to fix by shifting the UVs yourself, or spliting the images, as it is usually used where 1 image contains multiple variants.
|
Binary file not shown.
|
@ -33,6 +33,27 @@ namespace Tyrant.Logic
|
|||
/// Resident Evil 7 Material Entry
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct MaterialEntryRE7
|
||||
{
|
||||
public long NamePointer;
|
||||
public uint Hash; // MurMur3
|
||||
public uint Unk01;
|
||||
public uint Unk02;
|
||||
public int SettingsBufferSize;
|
||||
public int SettingsInfoCount;
|
||||
public int TextureCount;
|
||||
public int Unk03;
|
||||
public int Unk04;
|
||||
public long SettingsInfoPointer;
|
||||
public long TexturesPointer;
|
||||
public long SettingsBufferPointer;
|
||||
public long ShaderNamePointer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resident Evil 2 Material Entry
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct MaterialEntryRE2
|
||||
{
|
||||
public long NamePointer;
|
||||
|
@ -76,27 +97,16 @@ namespace Tyrant.Logic
|
|||
/// <summary>
|
||||
/// Converts the given material file
|
||||
/// </summary>
|
||||
public static Dictionary<string, Model.Material> Convert(byte[] buffer)
|
||||
{
|
||||
using (var stream = new MemoryStream(buffer))
|
||||
{
|
||||
return Convert(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given material file
|
||||
/// </summary>
|
||||
public static Dictionary<string, Model.Material> Convert(Stream stream)
|
||||
public static Dictionary<string, Model.Material> ConvertRE7(BinaryReader reader)
|
||||
{
|
||||
var results = new Dictionary<string, Model.Material>();
|
||||
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
reader.BaseStream.Position = 0;
|
||||
var header = reader.ReadStruct<MaterialHeaderRE7>();
|
||||
var materials = reader.ReadArray<MaterialEntryRE2>(header.MaterialCount);
|
||||
var materials = reader.ReadArray<MaterialEntryRE7>(header.MaterialCount);
|
||||
|
||||
foreach(var material in materials)
|
||||
foreach (var material in materials)
|
||||
{
|
||||
var result = new Model.Material(reader.ReadUTF16NullTerminatedString(material.NamePointer));
|
||||
|
||||
|
@ -111,5 +121,65 @@ namespace Tyrant.Logic
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given material file
|
||||
/// </summary>
|
||||
public static Dictionary<string, Model.Material> ConvertRE2(BinaryReader reader)
|
||||
{
|
||||
var results = new Dictionary<string, Model.Material>();
|
||||
|
||||
{
|
||||
reader.BaseStream.Position = 0;
|
||||
var header = reader.ReadStruct<MaterialHeaderRE7>();
|
||||
var materials = reader.ReadArray<MaterialEntryRE2>(header.MaterialCount);
|
||||
|
||||
foreach (var material in materials)
|
||||
{
|
||||
var result = new Model.Material(reader.ReadUTF16NullTerminatedString(material.NamePointer));
|
||||
|
||||
foreach (var texture in reader.ReadArray<MaterialTextureEntryRE7>(material.TexturesPointer, material.TextureCount))
|
||||
result.Images[reader.ReadUTF16NullTerminatedString(texture.TypePointer)] = reader.ReadUTF16NullTerminatedString(texture.TextureNamePointer).ToLower();
|
||||
foreach (var setting in reader.ReadArray<MaterialSettingsInfoRE7>(material.SettingsInfoPointer, material.SettingsInfoCount))
|
||||
result.Settings[reader.ReadUTF16NullTerminatedString(setting.NamePointer)] = reader.ReadArray<float>(material.SettingsBufferPointer + setting.DataOffset, setting.DataCount);
|
||||
|
||||
results[result.Name] = result;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given material file
|
||||
/// </summary>
|
||||
public static Dictionary<string, Model.Material> Convert(byte[] buffer)
|
||||
{
|
||||
using (var stream = new MemoryStream(buffer))
|
||||
{
|
||||
return Convert(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given material file
|
||||
/// </summary>
|
||||
public static Dictionary<string, Model.Material> Convert(Stream stream)
|
||||
{
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
reader.BaseStream.Position = 28;
|
||||
|
||||
// Check size of buffer
|
||||
if(reader.ReadUInt32() != 0)
|
||||
{
|
||||
return ConvertRE2(reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ConvertRE7(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -325,9 +325,7 @@ namespace Tyrant.Logic
|
|||
foreach (var boneNameIndex in reader.ReadArray<ushort>(header.BoneNamesPointer, boneDataHeader.BoneCount))
|
||||
boneNames.Add(strings[boneNameIndex]);
|
||||
|
||||
Model model = new Model();
|
||||
|
||||
|
||||
var bones = new List<Model.Bone>(boneDataHeader.BoneCount);
|
||||
|
||||
if (header.BoneDataHeaderPointer > 0)
|
||||
{
|
||||
|
@ -341,144 +339,160 @@ namespace Tyrant.Logic
|
|||
boneMatrices[i].W.Y,
|
||||
boneMatrices[i].W.Z), boneMatrices[i].ToQuaternion());
|
||||
|
||||
model.Bones.Add(bone);
|
||||
bones.Add(bone);
|
||||
}
|
||||
}
|
||||
|
||||
var modelHeader = reader.ReadStruct<ModelHeaderRE7>(header.ModelPointers[0]);
|
||||
var vertexSize = 20 + (modelHeader.UVCount * 4) + (boneDataHeader.BoneCount > 0 ? 16 : 0);
|
||||
bool firstMdlProcessed = false;
|
||||
|
||||
reader.BaseStream.Position = header.ModelPointers[0] + 64;
|
||||
var materialIndices = reader.ReadArray<short>(header.MaterialNamesPointer, modelHeader.MaterialCount);
|
||||
|
||||
var lodPointers = reader.ReadArray<long>(reader.ReadInt64(), modelHeader.LODCount);
|
||||
|
||||
var uniqueMaterials = new List<string>(modelHeader.MaterialCount);
|
||||
|
||||
foreach (var lodPointer in lodPointers)
|
||||
for (int mdl = 0; mdl < 3; mdl++)
|
||||
{
|
||||
if (header.ModelPointers[mdl] == 0)
|
||||
continue;
|
||||
|
||||
var lodHeader = reader.ReadStruct<LODHeaderRE7>(lodPointer);
|
||||
var boneIndices = reader.ReadArray<short>(lodPointer + 16, lodHeader.BoneCount);
|
||||
if (firstMdlProcessed)
|
||||
break;
|
||||
|
||||
var meshPointers = reader.ReadArray<long>(lodHeader.MeshesPointer, lodHeader.MeshCount);
|
||||
var lods = new List<Model>();
|
||||
|
||||
foreach (var meshPointer in meshPointers)
|
||||
var modelHeader = reader.ReadStruct<ModelHeaderRE7>(header.ModelPointers[mdl]);
|
||||
var vertexSize = 20 + (modelHeader.UVCount * 4) + (boneDataHeader.BoneCount > 0 ? 16 : 0);
|
||||
|
||||
reader.BaseStream.Position = header.ModelPointers[mdl] + (firstMdlProcessed ? 16 : 64);
|
||||
var materialIndices = reader.ReadArray<short>(header.MaterialNamesPointer, modelHeader.MaterialCount);
|
||||
|
||||
firstMdlProcessed = true;
|
||||
|
||||
var lodPointers = reader.ReadArray<long>(reader.ReadInt64(), modelHeader.LODCount);
|
||||
|
||||
foreach (var lodPointer in lodPointers)
|
||||
{
|
||||
var mesh = reader.ReadStruct<LODMeshRE7>(meshPointer);
|
||||
var subMeshes = reader.ReadArray<LODSubMeshRE7>(meshPointer + 16, mesh.SubMeshCount);
|
||||
int verticesRead = 0;
|
||||
|
||||
for (int i = 0; i < subMeshes.Length; i++)
|
||||
var model = new Model()
|
||||
{
|
||||
var materialName = strings[materialIndices[subMeshes[i].MaterialIndex]];
|
||||
Bones = bones
|
||||
};
|
||||
|
||||
if (!uniqueMaterials.Contains(materialName))
|
||||
uniqueMaterials.Add(materialName);
|
||||
var uniqueMaterials = new List<string>(modelHeader.MaterialCount);
|
||||
|
||||
var subMesh = new Model.Mesh();
|
||||
var lodHeader = reader.ReadStruct<LODHeaderRE7>(lodPointer);
|
||||
var boneIndices = reader.ReadArray<short>(lodPointer + 16, lodHeader.BoneCount);
|
||||
|
||||
subMesh.MaterialIndices.Add(uniqueMaterials.IndexOf(materialName));
|
||||
var meshPointers = reader.ReadArray<long>(lodHeader.MeshesPointer, lodHeader.MeshCount);
|
||||
|
||||
int subMeshVertexCount = 0;
|
||||
int subMeshFaceCount = subMeshes[i].FaceCount;
|
||||
foreach (var meshPointer in meshPointers)
|
||||
{
|
||||
var mesh = reader.ReadStruct<LODMeshRE7>(meshPointer);
|
||||
var subMeshes = reader.ReadArray<LODSubMeshRE7>(meshPointer + 16, mesh.SubMeshCount);
|
||||
int verticesRead = 0;
|
||||
|
||||
// Since the counts aren't stored in each, we can use this to determine the counts
|
||||
if (i != subMeshes.Length - 1)
|
||||
subMeshVertexCount = subMeshes[i + 1].VertexIndex - subMeshes[i].VertexIndex;
|
||||
else
|
||||
subMeshVertexCount = mesh.VertexCount - verticesRead;
|
||||
|
||||
|
||||
verticesRead += subMeshVertexCount;
|
||||
|
||||
reader.BaseStream.Position = header.GeometryPointer + 48 + (vertexSize * subMeshes[i].VertexIndex);
|
||||
|
||||
for (int v = 0; v < subMeshVertexCount; v++)
|
||||
for (int i = 0; i < subMeshes.Length; i++)
|
||||
{
|
||||
// Base vertex data
|
||||
var vertex = new Model.Vertex(
|
||||
reader.ReadStruct<Vector3>(),
|
||||
reader.ReadStruct<PackedVector3>().Unpack(),
|
||||
reader.ReadStruct<PackedVector3>().Unpack());
|
||||
vertex.UVs.Add(new Vector2(reader.ReadStruct<Half>(), reader.ReadStruct<Half>()));
|
||||
// Skip unnsupported UV layers
|
||||
reader.BaseStream.Position += 4 * (modelHeader.UVCount - 1);
|
||||
var materialName = strings[materialIndices[subMeshes[i].MaterialIndex]];
|
||||
|
||||
// Check if we have bones
|
||||
if (lodHeader.BoneCount > 0)
|
||||
if (!uniqueMaterials.Contains(materialName))
|
||||
uniqueMaterials.Add(materialName);
|
||||
|
||||
var subMesh = new Model.Mesh();
|
||||
|
||||
subMesh.MaterialIndices.Add(uniqueMaterials.IndexOf(materialName));
|
||||
|
||||
int subMeshVertexCount = 0;
|
||||
int subMeshFaceCount = subMeshes[i].FaceCount;
|
||||
|
||||
// Since the counts aren't stored in each, we can use this to determine the counts
|
||||
if (i != subMeshes.Length - 1)
|
||||
subMeshVertexCount = subMeshes[i + 1].VertexIndex - subMeshes[i].VertexIndex;
|
||||
else
|
||||
subMeshVertexCount = mesh.VertexCount - verticesRead;
|
||||
|
||||
|
||||
verticesRead += subMeshVertexCount;
|
||||
|
||||
reader.BaseStream.Position = header.GeometryPointer + 48 + (vertexSize * subMeshes[i].VertexIndex);
|
||||
|
||||
for (int v = 0; v < subMeshVertexCount; v++)
|
||||
{
|
||||
var localBoneIndices = reader.ReadBytes(8);
|
||||
var weights = reader.ReadBytes(8);
|
||||
var weightSum = 0.0f;
|
||||
// Base vertex data
|
||||
var vertex = new Model.Vertex(
|
||||
reader.ReadStruct<Vector3>(),
|
||||
reader.ReadStruct<PackedVector3>().Unpack(),
|
||||
reader.ReadStruct<PackedVector3>().Unpack());
|
||||
vertex.UVs.Add(new Vector2(reader.ReadStruct<Half>(), reader.ReadStruct<Half>()));
|
||||
// Skip unnsupported UV layers
|
||||
reader.BaseStream.Position += 4 * (modelHeader.UVCount - 1);
|
||||
|
||||
for (int w = 0; w < 8 && weights[w] != 0; w++)
|
||||
// Check if we have bones
|
||||
if (lodHeader.BoneCount > 0)
|
||||
{
|
||||
vertex.Weights.Add(new Model.Vertex.Weight()
|
||||
{
|
||||
BoneIndex = boneIndices[localBoneIndices[w]],
|
||||
Influence = weights[w] / 255.0f
|
||||
});
|
||||
var localBoneIndices = reader.ReadBytes(8);
|
||||
var weights = reader.ReadBytes(8);
|
||||
var weightSum = 0.0f;
|
||||
|
||||
weightSum += vertex.Weights[w].Influence;
|
||||
for (int w = 0; w < 8 && weights[w] != 0; w++)
|
||||
{
|
||||
vertex.Weights.Add(new Model.Vertex.Weight()
|
||||
{
|
||||
BoneIndex = boneIndices[localBoneIndices[w]],
|
||||
Influence = weights[w] / 255.0f
|
||||
});
|
||||
|
||||
weightSum += vertex.Weights[w].Influence;
|
||||
}
|
||||
|
||||
var multiplier = 1.0f / weightSum;
|
||||
|
||||
foreach (var weight in vertex.Weights)
|
||||
weight.Influence *= multiplier;
|
||||
}
|
||||
|
||||
var multiplier = 1.0f / weightSum;
|
||||
|
||||
foreach (var weight in vertex.Weights)
|
||||
weight.Influence *= multiplier;
|
||||
subMesh.Vertices.Add(vertex);
|
||||
}
|
||||
|
||||
subMesh.Vertices.Add(vertex);
|
||||
switch (lodHeader.Flags)
|
||||
{
|
||||
case 0:
|
||||
reader.BaseStream.Position = geometryHeader.FaceBufferOffset + (2 * subMeshes[i].FaceIndex);
|
||||
|
||||
for (int f = 0; f < subMeshes[i].FaceCount / 3; f++)
|
||||
{
|
||||
var v1 = reader.ReadUInt16();
|
||||
var v2 = reader.ReadUInt16();
|
||||
var v3 = reader.ReadUInt16();
|
||||
|
||||
if (v1 != v2 && v2 != v3 && v3 != v1)
|
||||
subMesh.Faces.Add(new Model.Face(v1, v2, v3));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
reader.BaseStream.Position = geometryHeader.FaceBufferOffset + (4 * subMeshes[i].FaceIndex);
|
||||
|
||||
for (int f = 0; f < subMeshes[i].FaceCount / 3; f++)
|
||||
{
|
||||
var v1 = reader.ReadInt32();
|
||||
var v2 = reader.ReadInt32();
|
||||
var v3 = reader.ReadInt32();
|
||||
|
||||
if (v1 != v2 && v2 != v3 && v3 != v1)
|
||||
subMesh.Faces.Add(new Model.Face(v1, v2, v3));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
model.Meshes.Add(subMesh);
|
||||
}
|
||||
|
||||
switch (lodHeader.Flags)
|
||||
{
|
||||
case 0:
|
||||
reader.BaseStream.Position = geometryHeader.FaceBufferOffset + (2 * subMeshes[i].FaceIndex);
|
||||
|
||||
for (int f = 0; f < subMeshes[i].FaceCount / 3; f++)
|
||||
{
|
||||
var v1 = reader.ReadUInt16();
|
||||
var v2 = reader.ReadUInt16();
|
||||
var v3 = reader.ReadUInt16();
|
||||
|
||||
if (v1 != v2 && v2 != v3 && v3 != v1)
|
||||
subMesh.Faces.Add(new Model.Face(v1, v2, v3));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
reader.BaseStream.Position = geometryHeader.FaceBufferOffset + (4 * subMeshes[i].FaceIndex);
|
||||
|
||||
for (int f = 0; f < subMeshes[i].FaceCount / 3; f++)
|
||||
{
|
||||
var v1 = reader.ReadInt32();
|
||||
var v2 = reader.ReadInt32();
|
||||
var v3 = reader.ReadInt32();
|
||||
|
||||
if (v1 != v2 && v2 != v3 && v3 != v1)
|
||||
subMesh.Faces.Add(new Model.Face(v1, v2, v3));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
model.Meshes.Add(subMesh);
|
||||
}
|
||||
|
||||
foreach (var materialName in uniqueMaterials)
|
||||
{
|
||||
model.Materials.Add(new Model.Material(materialName));
|
||||
}
|
||||
|
||||
lods.Add(model);
|
||||
}
|
||||
|
||||
foreach (var materialName in uniqueMaterials)
|
||||
{
|
||||
var material = new Model.Material(materialName);
|
||||
}
|
||||
|
||||
break;
|
||||
results.Add(lods);
|
||||
}
|
||||
|
||||
results.Add(new List<Model>
|
||||
{
|
||||
model
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
@ -548,6 +562,9 @@ namespace Tyrant.Logic
|
|||
if (header.ModelPointers[mdl] == 0)
|
||||
continue;
|
||||
|
||||
if (firstMdlProcessed)
|
||||
break;
|
||||
|
||||
var lods = new List<Model>();
|
||||
|
||||
var modelHeader = reader.ReadStruct<ModelHeaderRE7>(header.ModelPointers[mdl]);
|
||||
|
|
|
@ -51,5 +51,5 @@ using System.Windows;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyVersion("1.0.0.1")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.1")]
|
||||
|
|
|
@ -79,8 +79,11 @@ namespace Tyrant
|
|||
{
|
||||
".mdf2.10",
|
||||
"_mat.mdf2.10",
|
||||
".mdf2.6",
|
||||
"_mat.mdf2.6",
|
||||
".10",
|
||||
".11",
|
||||
".8",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -361,6 +364,13 @@ namespace Tyrant
|
|||
}
|
||||
}
|
||||
|
||||
private void ExportUnknownFile(Package.Entry entry, string name)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(name));
|
||||
|
||||
File.WriteAllBytes(name, ActivePackage.LoadEntry(entry));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a Model Asset
|
||||
/// </summary>
|
||||
|
@ -468,6 +478,7 @@ namespace Tyrant
|
|||
ExportMotionList(asset.PackageEntry, Path.Combine("exported_files", asset.Name));
|
||||
break;
|
||||
default:
|
||||
ExportUnknownFile(asset.PackageEntry, Path.Combine("exported_files", asset.Name));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue