protected override void OnReplace(object oldData) { Model oldDataT = ( Model )oldData; // Replace the mesh name with the one we replaced. if (oldDataT.Meshes.Count == Data.Meshes.Count) { Data.Meshes[0].Name = oldDataT.Meshes[0].Name; Data.Meshes[0].ID = oldDataT.Meshes[0].ID; } // Pass the ex data to meshes if they don't have them. // Game crashes without the ex data if it tries to make use of them. // SMH sega, can't even do a null check!! foreach (var mesh in Data.Meshes) { var oldMesh = oldDataT.Meshes.FirstOrDefault(x => x.Name.Equals(mesh.Name, StringComparison.OrdinalIgnoreCase)); if (oldMesh != null) { if (mesh.Skin != null && mesh.Skin.ExData == null) { mesh.Skin.ExData = oldMesh.Skin?.ExData; } mesh.Name = oldMesh.Name; mesh.ID = oldMesh.ID; } } // Pass the format/endianness Data.Format = oldDataT.Format; Data.Endianness = oldDataT.Endianness; // Randomize texture IDs if we are modern if (BinaryFormatUtilities.IsModern(Data.Format) && Data.TextureSet != null) { var newIDs = new List <int>(Data.TextureSet.Textures.Count); var random = new Random(); for (int i = 0; i < Data.TextureSet.Textures.Count; i++) { int currentID; do { currentID = random.Next(int.MinValue, int.MaxValue); } while (newIDs.Contains(currentID)); newIDs.Add(currentID); } TextureUtilities.ReAssignTextureIDs(Data, newIDs); } Textures?.Replace(Data.TextureSet); base.OnReplace(oldData); }
public void Save(Stream destination, ObjectDatabase objectDatabase, TextureDatabase textureDatabase, BoneDatabase boneDatabase, bool leaveOpen = false) { if (objectDatabase != null) { foreach (var mesh in Meshes) { mesh.ID = objectDatabase.GetMesh(mesh.Name)?.ID ?? mesh.ID; } } if (boneDatabase != null) { string fileName = (destination is FileStream fileStream) ? Path.GetFileName(fileStream.Name) : string.Empty; // Assume we are exporting in game's style var skeleton = boneDatabase.Skeletons.FirstOrDefault(x => fileName.StartsWith(x.Name, StringComparison.OrdinalIgnoreCase)); // If we couldn't find it, default to CMN skeleton if (skeleton == null) { skeleton = boneDatabase.Skeletons.FirstOrDefault(x => x.Name.Equals("CMN", StringComparison.OrdinalIgnoreCase)); } // Still?? Then default to the first skeleton (this is unlikely to happen though) if (skeleton == null) { skeleton = boneDatabase.Skeletons[0]; } // Pretty much impossible to miss if (skeleton != null) { foreach (var skin in Meshes.Where(x => x.Skin != null).Select(x => x.Skin)) { foreach (var bone in skin.Bones) { int index = skin.ExData?.BoneNames?.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)) ?? -1; if (index == -1) { index = skeleton.BoneNames1.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)); } else { index = 0x8000 | index; } if (index != -1) { // Before we do this, fix the child bones foreach (var childBone in skin.Bones.Where(x => x.ParentID.Equals(bone.ID))) { childBone.ParentID = index; } // Now replace the ID bone.ID = index; } else { Debug.WriteLine($"Model.Save: Bone wasn't found in bone database or ex-data: {bone.Name}"); } } } } } if (textureDatabase != null && TextureSet != null) { var newIDs = new List <int>(TextureSet.Textures.Count); int currentID = textureDatabase.Textures.Max(x => x.ID) + 1; foreach (var texture in TextureSet.Textures) { var textureEntry = !string.IsNullOrEmpty(texture.Name) ? textureDatabase.GetTexture(texture.Name) : textureDatabase.GetTexture(texture.ID); if (textureEntry == null) { textureDatabase.Textures.Add(textureEntry = new TextureEntry { ID = currentID++, Name = texture.Name ?? $"Texture{currentID}", }); } newIDs.Add(textureEntry.ID); } if (!newIDs.SequenceEqual(TextureIDs)) { TextureUtilities.ReAssignTextureIDs(this, newIDs); } } Save(destination, leaveOpen); }