//Thanks to Dimy for this method. It's nasty but it gets textures. //The game uses 100s of varied presets so we should try a work around for undefined presets. static void SetMaterialsHacky(Stream materialStream, Stream lookupStream, List <ModelChunk.MeshInfo> meshes) { List <uint[]> materialPointers = GetLookupPointers(meshes, lookupStream); List <uint> globalPointers = new List <uint>(); foreach (var pointerList in materialPointers.OrderBy(x => x[0])) { for (int i = 0; i < pointerList.Length; i++) { if (pointerList[i] != uint.MaxValue && pointerList[i] != 0) { globalPointers.Add(pointerList[i]); } } } //Get lookup pointers using (var reader = new FileReader(materialStream, true)) { for (int i = 0; i < meshes.Count; i++) { var mat = new ModelChunk.MaterialData(); meshes[i].Material = mat; uint[] pointers = materialPointers[i]; uint hash = meshes[i].MeshHeader.MaterialHash; string materialPreset = Hashing.CreateHashString(hash); List <uint> matPointers = new List <uint>(); for (int j = 0; j < pointers.Length; j++) { if (pointers[j] != uint.MaxValue) { matPointers.Add(pointers[j]); break; } } var matInfo = new MaterialInfo(); if (!MaterialPresetInfos.ContainsKey(materialPreset)) { MaterialPresetInfos.Add(materialPreset, matInfo); } var refList = pointers.ToList(); for (int j = 0; j < matPointers.Count; j++) { int pointerIndex = refList.IndexOf(matPointers[j]); if (matPointers[j] % 448 == 0 || globalPointers.Contains(matPointers[j] + 448)) { reader.SeekBegin(matPointers[j]); reader.ReadBytes(12); uint texhash = reader.ReadUInt32(); matInfo.LookupTexPointer = pointerIndex; if (texhash == 0x81800000) { reader.Seek(-8); texhash = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash))) { matInfo.textureSlots.Add(TEXTURE_SLOT.DIFFUSE, (uint)reader.Position - matPointers[j] - 16); mat.DiffuseHash = texhash; } reader.ReadBytes(4); uint texhash2 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash2))) { matInfo.textureSlots.Add(TEXTURE_SLOT.NORMAL, (uint)reader.Position - matPointers[j] - 16); mat.NormalMapHash = texhash2; } } else { if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash))) { matInfo.textureSlots.Add(TEXTURE_SLOT.NORMAL, (uint)reader.Position - matPointers[j] - 16); mat.NormalMapHash = texhash; } reader.ReadBytes(8); reader.ReadBytes(0xC); uint texhash2 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash2))) { matInfo.textureSlots.Add(TEXTURE_SLOT.DIFFUSE, (uint)reader.Position - matPointers[j] - 16); mat.DiffuseHash = texhash2; } } } else if (matPointers[j] % 192 == 0 || globalPointers.Contains(matPointers[j] + 192)) { reader.SeekBegin(matPointers[j]); reader.ReadBytes(8); uint texhash = reader.ReadUInt32(); uint a = reader.ReadUInt32(); Console.WriteLine($"{materialPreset} {pointerIndex}"); if (texhash == 0x81800000) { if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash))) { matInfo.textureSlots.Add(TEXTURE_SLOT.NORMAL, (uint)reader.Position - matPointers[j] - 12); mat.NormalMapHash = texhash; } reader.ReadBytes(4); reader.ReadBytes(0xC); uint texhash2 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash2))) { Console.WriteLine($"DIFFUSE {reader.Position - matPointers[j] - 12}"); mat.DiffuseHash = texhash2; } } else { if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash))) { matInfo.textureSlots.Add(TEXTURE_SLOT.DIFFUSE, (uint)reader.Position - matPointers[j] - 12); mat.DiffuseHash = texhash; } reader.ReadBytes(4); uint texhash2 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash2))) { matInfo.textureSlots.Add(TEXTURE_SLOT.NORMAL, (uint)reader.Position - matPointers[j] - 12); mat.NormalMapHash = texhash2; } } } else { reader.SeekBegin(matPointers[j]); uint a = 0; while (a != 0x81800000 && a != 0x01800000 && reader.Position < reader.BaseStream.Length - 4) { a = reader.ReadUInt32(); } if (a == 0x81800000) { Console.WriteLine($"{materialPreset} {pointerIndex}"); reader.Seek(-8); uint hash1 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(hash1))) { matInfo.textureSlots.Add(TEXTURE_SLOT.DIFFUSE, (uint)reader.Position - matPointers[j] - 4); mat.DiffuseHash = hash1; } reader.ReadBytes(8); uint texhash2 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash2))) { matInfo.textureSlots.Add(TEXTURE_SLOT.NORMAL, (uint)reader.Position - matPointers[j] - 4); mat.NormalMapHash = texhash2; } } else if (a == 0x01800000) { reader.Seek(-8); reader.ReadBytes(0x18); uint hash1 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(hash1))) { matInfo.textureSlots.Add(TEXTURE_SLOT.DIFFUSE, (uint)reader.Position - matPointers[j] - 4); mat.DiffuseHash = hash1; } reader.ReadBytes(8); uint texhash2 = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(texhash2))) { matInfo.textureSlots.Add(TEXTURE_SLOT.NORMAL, (uint)reader.Position - matPointers[j] - 4); mat.NormalMapHash = texhash2; } } } } if (MaterialPresetInfos[materialPreset].textureSlots.Count < matInfo.textureSlots.Count) { MaterialPresetInfos[materialPreset] = matInfo; } } } }
public static void ParseMaterials(Stream materialStream, Stream lookupStream, List <ModelChunk.MeshInfo> meshes) { SetMaterialsHacky(materialStream, lookupStream, meshes); foreach (var preset in MaterialPresetInfos) { Console.WriteLine($"preset {preset.Key} ptr {preset.Value.LookupTexPointer}"); foreach (var slot in preset.Value.textureSlots) { Console.WriteLine($"slot {slot.Key} {slot.Value}"); } } List <uint[]> materialPointers = GetLookupPointers(meshes, lookupStream); //Get lookup pointers using (var reader = new FileReader(materialStream, true)) { for (int i = 0; i < meshes.Count; i++) { var mat = new ModelChunk.MaterialData(); meshes[i].Material = mat; Console.WriteLine($"MESH {i}"); uint[] pointers = materialPointers[i]; uint hash = meshes[i].MeshHeader.MaterialHash; string materialPreset = Hashing.CreateHashString(hash); List <uint> matPointers = new List <uint>(); for (int j = 0; j < pointers.Length; j++) { if (pointers[j] != uint.MaxValue && pointers[j] != 0) { matPointers.Add(pointers[j]); } } var refList = pointers.ToList(); for (int j = 0; j < matPointers.Count; j++) { if (j + 1 < matPointers.Count) { uint size = matPointers[j + 1] - matPointers[j]; reader.SeekBegin(matPointers[j]); while (reader.Position < matPointers[j] + size) { uint hashCheck = reader.ReadUInt32(); if (Runtime.TextureCache.Any(x => x.Name == Hashing.CreateHashString(hashCheck))) { if (mat.DiffuseHash == 0) { mat.DiffuseHash = hashCheck; } Console.WriteLine($"{materialPreset} TEXTURE {hashCheck} pointer {refList.IndexOf(matPointers[j])} position {reader.Position - 4 - matPointers[j]}"); } } } } int textureSlotIndex = GetTextureSlotLookupIndex(materialPreset); Console.WriteLine($"textureSlotIndex"); if (textureSlotIndex != -1) { var textureSlots = GetTextureSlotsOffset(materialPreset); if (textureSlots.ContainsKey(TEXTURE_SLOT.DIFFUSE)) { reader.SeekBegin(pointers[textureSlotIndex] + textureSlots[TEXTURE_SLOT.DIFFUSE]); mat.DiffuseHash = reader.ReadUInt32(); } if (textureSlots.ContainsKey(TEXTURE_SLOT.NORMAL)) { reader.SeekBegin(pointers[textureSlotIndex] + textureSlots[TEXTURE_SLOT.NORMAL]); mat.NormalMapHash = reader.ReadUInt32(); } } } } }