From e8955af47ec3b689218ce0f4a99b443a4c6fe94b Mon Sep 17 00:00:00 2001 From: lahm86 <33758420+lahm86@users.noreply.github.com> Date: Mon, 6 May 2024 18:03:38 +0100 Subject: [PATCH] Refactor data transport (#666) Part of #653. --- ModelExport/Program.cs | 38 +- TRDataControl/Blob/Control/TRBlobControl.cs | 48 ++ TRDataControl/Blob/Control/TRBlobConverter.cs | 57 +++ TRDataControl/Blob/Control/TRBlobResolver.cs | 20 + TRDataControl/Blob/TRBlobBase.cs | 33 +- TRDataControl/Blob/TRBlobType.cs | 8 + TRDataControl/Blob/Types/TR1Blob.cs | 11 +- TRDataControl/Blob/Types/TR2Blob.cs | 11 +- TRDataControl/Blob/Types/TR3Blob.cs | 11 +- TRDataControl/Blob/Types/TR4Blob.cs | 18 + TRDataControl/Blob/Types/TR5Blob.cs | 18 + TRDataControl/Data/IDataProvider.cs | 76 ++- TRDataControl/Data/TR1DataProvider.cs | 416 ++++++++-------- TRDataControl/Data/TR2DataProvider.cs | 457 +++++++++-------- TRDataControl/Data/TR3DataProvider.cs | 340 ++++++------- TRDataControl/Data/TR4DataProvider.cs | 159 ++++++ TRDataControl/Data/TR5DataProvider.cs | 159 ++++++ .../Types/Models/EMImportModelFunction.cs | 7 +- .../EMImportNonGraphicsModelFunction.cs | 7 +- .../Handlers/CinematicTransportHandler.cs | 65 --- .../Handlers/ColourTransportHandler.cs | 130 ----- .../Handlers/ModelTransportHandler.cs | 120 ----- .../Handlers/Sound/SoundTransportHandler.cs | 109 ---- .../Textures/AbstractTextureExportHandler.cs | 147 ------ .../Textures/AbstractTextureImportHandler.cs | 369 -------------- .../Textures/TR1/TR1TextureExportHandler.cs | 17 - .../Textures/TR1/TR1TextureImportHandler.cs | 113 ----- .../Textures/TR2/TR2TextureExportHandler.cs | 17 - .../Textures/TR2/TR2TextureImportHandler.cs | 145 ------ .../Textures/TR3/TR3TextureExportHandler.cs | 16 - .../Textures/TR3/TR3TextureImportHandler.cs | 97 ---- TRDataControl/Helpers/TRModelExtensions.cs | 52 +- TRDataControl/Mass/TRMassExporter.cs | 63 +-- TRDataControl/Mass/Types/TR1MassExporter.cs | 78 +-- TRDataControl/Mass/Types/TR2MassExporter.cs | 76 +-- TRDataControl/Mass/Types/TR3MassExporter.cs | 71 ++- TRDataControl/Mass/Types/TR4MassExporter.cs | 20 + TRDataControl/Mass/Types/TR5MassExporter.cs | 20 + TRDataControl/Remapping/TRTextureRemapper.cs | 130 +++++ .../Remapping/Types/TR1TextureRemapper.cs | 21 + .../Remapping/Types/TR2TextureRemapper.cs | 21 + .../Remapping/Types/TR3TextureRemapper.cs | 21 + .../Remapping/Types/TR4TextureRemapper.cs | 21 + .../Remapping/Types/TR5TextureRemapper.cs | 21 + .../Transport/TR1/TR1DataExporter.cs | 169 ++++--- .../Transport/TR1/TR1DataImporter.cs | 95 ++-- .../Transport/TR2/TR2DataExporter.cs | 152 ++++-- .../Transport/TR2/TR2DataImporter.cs | 111 +++-- .../Transport/TR3/TR3DataExporter.cs | 67 ++- .../Transport/TR3/TR3DataImporter.cs | 90 ++-- .../Transport/TR4/TR4DataExporter.cs | 59 +++ .../Transport/TR4/TR4DataImporter.cs | 60 +++ .../Transport/TR5/TR5DataExporter.cs | 59 +++ .../Transport/TR5/TR5DataImporter.cs | 60 +++ TRDataControl/Transport/TRDataExporter.cs | 228 +++++---- TRDataControl/Transport/TRDataImporter.cs | 465 +++++++++++++----- TRDataControl/Transport/TRDataTransport.cs | 96 +--- TRDataControlTests/IO/ExportTests.cs | 239 +++++++++ TRDataControlTests/IO/ImportTests.cs | 190 +++++++ TRDataControlTests/TRDataControlTests.csproj | 28 ++ .../Packing/Textures/TRImageDeduplicator.cs | 69 +++ TRImageControl/Palette/TRPalette8Control.cs | 6 +- TRLevelControlTests/Levels/TR1/TEST1.PHD | Bin 2655833 -> 2685013 bytes TRRandomizer.sln | 8 +- .../Processors/TR3/TR3SequenceProcessor.cs | 8 +- .../Randomizers/TR1/TR1EnemyRandomizer.cs | 8 +- .../Randomizers/TR1/TR1OutfitRandomizer.cs | 12 +- .../Randomizers/TR1/TR1SecretRandomizer.cs | 4 +- .../Randomizers/TR2/TR2EnemyRandomizer.cs | 8 +- .../Randomizers/TR2/TR2ItemRandomizer.cs | 6 +- .../Randomizers/TR2/TR2OutfitRandomizer.cs | 10 +- .../Randomizers/TR3/TR3EnemyRandomizer.cs | 8 +- .../Randomizers/TR3/TR3ItemRandomizer.cs | 4 +- .../Randomizers/TR3/TR3OutfitRandomizer.cs | 10 +- .../Randomizers/TR3/TR3SecretRandomizer.cs | 6 +- .../Textures/DynamicTextureBuilder.cs | 7 +- 76 files changed, 3292 insertions(+), 2904 deletions(-) create mode 100644 TRDataControl/Blob/Control/TRBlobControl.cs create mode 100644 TRDataControl/Blob/Control/TRBlobConverter.cs create mode 100644 TRDataControl/Blob/Control/TRBlobResolver.cs create mode 100644 TRDataControl/Blob/TRBlobType.cs create mode 100644 TRDataControl/Blob/Types/TR4Blob.cs create mode 100644 TRDataControl/Blob/Types/TR5Blob.cs create mode 100644 TRDataControl/Data/TR4DataProvider.cs create mode 100644 TRDataControl/Data/TR5DataProvider.cs delete mode 100644 TRDataControl/Handlers/CinematicTransportHandler.cs delete mode 100644 TRDataControl/Handlers/ColourTransportHandler.cs delete mode 100644 TRDataControl/Handlers/ModelTransportHandler.cs delete mode 100644 TRDataControl/Handlers/Sound/SoundTransportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/AbstractTextureImportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/TR1/TR1TextureExportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/TR1/TR1TextureImportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/TR2/TR2TextureExportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/TR2/TR2TextureImportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/TR3/TR3TextureExportHandler.cs delete mode 100644 TRDataControl/Handlers/Textures/TR3/TR3TextureImportHandler.cs create mode 100644 TRDataControl/Mass/Types/TR4MassExporter.cs create mode 100644 TRDataControl/Mass/Types/TR5MassExporter.cs create mode 100644 TRDataControl/Remapping/TRTextureRemapper.cs create mode 100644 TRDataControl/Remapping/Types/TR1TextureRemapper.cs create mode 100644 TRDataControl/Remapping/Types/TR2TextureRemapper.cs create mode 100644 TRDataControl/Remapping/Types/TR3TextureRemapper.cs create mode 100644 TRDataControl/Remapping/Types/TR4TextureRemapper.cs create mode 100644 TRDataControl/Remapping/Types/TR5TextureRemapper.cs create mode 100644 TRDataControl/Transport/TR4/TR4DataExporter.cs create mode 100644 TRDataControl/Transport/TR4/TR4DataImporter.cs create mode 100644 TRDataControl/Transport/TR5/TR5DataExporter.cs create mode 100644 TRDataControl/Transport/TR5/TR5DataImporter.cs create mode 100644 TRDataControlTests/IO/ExportTests.cs create mode 100644 TRDataControlTests/IO/ImportTests.cs create mode 100644 TRDataControlTests/TRDataControlTests.csproj create mode 100644 TRImageControl/Packing/Textures/TRImageDeduplicator.cs diff --git a/ModelExport/Program.cs b/ModelExport/Program.cs index 6edca1aa..ea1f5203 100644 --- a/ModelExport/Program.cs +++ b/ModelExport/Program.cs @@ -1,4 +1,4 @@ -using TRModelTransporter.Utilities; +using TRDataControl.Utils; namespace ModelExport; @@ -12,52 +12,42 @@ static void Main(string[] args) return; } - bool exportSegments = args.Length > 1 && args[1].ToLower().Contains("segments"); switch (args[0].ToLower()) { case "tr1": - TR1Export(exportSegments); + TR1Export(); break; case "tr2": - TR2Export(exportSegments); + TR2Export(); break; case "tr3": - TR3Export(exportSegments); + TR3Export(); break; } } - static void TR1Export(bool exportSegments) + static void TR1Export() { TR1MassExporter exporter = new(); - string exportFolder = @"TR1\Models"; - string segmentFolder = exportSegments ? @"TR1\ModelSegments" : null; - - exporter.Export(Directory.GetCurrentDirectory(), exportFolder, segmentFolder); + exporter.Export(Directory.GetCurrentDirectory(), @"TR1\Objects"); } - static void TR2Export(bool exportSegments) + static void TR2Export() { TR2MassExporter exporter = new(); - string exportFolder = @"TR2\Models"; - string segmentFolder = exportSegments ? @"TR2\ModelSegments" : null; - - exporter.Export(Directory.GetCurrentDirectory(), exportFolder, segmentFolder); + exporter.Export(Directory.GetCurrentDirectory(), @"TR2\Objects"); } - static void TR3Export(bool exportSegments) + static void TR3Export() { TR3MassExporter exporter = new(); - string exportFolder = @"TR3\Models"; - string segmentFolder = exportSegments ? @"TR3\ModelSegments" : null; - - exporter.Export(Directory.GetCurrentDirectory(), exportFolder, segmentFolder); + exporter.Export(Directory.GetCurrentDirectory(), @"TR3\Objects"); } private static void Usage() { Console.WriteLine(); - Console.WriteLine("Usage: ModelExport [tr1 | tr2 | tr3] [segments]"); + Console.WriteLine("Usage: ModelExport [tr1 | tr2 | tr3]"); Console.WriteLine(); Console.WriteLine("Example"); @@ -66,11 +56,5 @@ private static void Usage() Console.ResetColor(); Console.WriteLine("\t\tExport defined models for TR1."); Console.WriteLine(); - - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("\tModelExport tr2 segments"); - Console.ResetColor(); - Console.WriteLine("\t\tExport defined models for TR2 and include all individual segments for each model."); - Console.WriteLine(); } } diff --git a/TRDataControl/Blob/Control/TRBlobControl.cs b/TRDataControl/Blob/Control/TRBlobControl.cs new file mode 100644 index 00000000..9247e536 --- /dev/null +++ b/TRDataControl/Blob/Control/TRBlobControl.cs @@ -0,0 +1,48 @@ +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using Newtonsoft.Json; +using System.Text; + +namespace TRDataControl; + +public class TRBlobControl +{ + private static readonly uint _blobMagic = 'T' | ('R' << 8) | ('B' << 16) | ('M' << 24); + + private static readonly TRBlobConverter _converter = new(); + private static readonly JsonSerializerSettings _serializer = new() + { + ContractResolver = new TRBlobResolver() + }; + + public static T Read(string file) + { + using BinaryReader reader = new(File.OpenRead(file)); + + uint magic = reader.ReadUInt32(); + if (magic != _blobMagic) + { + throw new Exception("Unrecognised blob data file."); + } + + using MemoryStream inflatedStream = new(); + using InflaterInputStream inflater = new(reader.BaseStream); + inflater.CopyTo(inflatedStream); + + string data = Encoding.Default.GetString(inflatedStream.ToArray()); + return JsonConvert.DeserializeObject(data, _converter); + } + + public static void Write(T blob, string file) + { + using BinaryWriter writer = new(File.Create(file)); + + writer.Write(_blobMagic); + + string data = JsonConvert.SerializeObject(blob, _serializer); + using MemoryStream inStream = new(Encoding.Default.GetBytes(data)); + using DeflaterOutputStream deflater = new(writer.BaseStream); + + inStream.CopyTo(deflater); + deflater.Finish(); + } +} diff --git a/TRDataControl/Blob/Control/TRBlobConverter.cs b/TRDataControl/Blob/Control/TRBlobConverter.cs new file mode 100644 index 00000000..df7df394 --- /dev/null +++ b/TRDataControl/Blob/Control/TRBlobConverter.cs @@ -0,0 +1,57 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TRBlobConverter : JsonConverter +{ + public override bool CanConvert(Type objectType) + { + return objectType == typeof(TRAnimCommand) || objectType == typeof(TRTexture); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject jo = JObject.Load(reader); + if (objectType == typeof(TRAnimCommand)) + { + return ReadAnimCommand(jo); + } + if (objectType == typeof(TRTexture)) + { + return ReadTexture(jo); + } + return existingValue; + } + + private static object ReadAnimCommand(JObject jo) + { + TRAnimCommandType type = (TRAnimCommandType)jo[nameof(TRAnimCommand.Type)].Value(); + return type switch + { + TRAnimCommandType.EmptyHands => JsonConvert.DeserializeObject(jo.ToString()), + TRAnimCommandType.Null => JsonConvert.DeserializeObject(jo.ToString()), + TRAnimCommandType.SetPosition => JsonConvert.DeserializeObject(jo.ToString()), + TRAnimCommandType.JumpDistance => JsonConvert.DeserializeObject(jo.ToString()), + TRAnimCommandType.Kill => JsonConvert.DeserializeObject(jo.ToString()), + TRAnimCommandType.PlaySound => JsonConvert.DeserializeObject(jo.ToString()), + TRAnimCommandType.FlipEffect => + jo[nameof(TRFootprintCommand.Foot)] == null + ? JsonConvert.DeserializeObject(jo.ToString()) + : JsonConvert.DeserializeObject(jo.ToString()), + _ => throw new InvalidDataException(), + }; + } + + private static TRTexture ReadTexture(JObject jo) + { + return jo[nameof(TRObjectTexture.Vertices)] == null + ? JsonConvert.DeserializeObject(jo.ToString()) + : JsonConvert.DeserializeObject(jo.ToString()); + } + + public override bool CanWrite => false; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { } +} diff --git a/TRDataControl/Blob/Control/TRBlobResolver.cs b/TRDataControl/Blob/Control/TRBlobResolver.cs new file mode 100644 index 00000000..796f8acd --- /dev/null +++ b/TRDataControl/Blob/Control/TRBlobResolver.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json; +using System.Reflection; +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TRBlobResolver : DefaultContractResolver +{ + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + // Don't output dynamic getters + IEnumerable props = type.GetProperties() + .Where(p => p.CanWrite || type.BaseType == typeof(TRAnimCommand)); + + return base.CreateProperties(type, memberSerialization) + ?.Where(p => !p.Ignored && props.Any(pi => pi.Name == p.PropertyName)) + .ToList(); + } +} diff --git a/TRDataControl/Blob/TRBlobBase.cs b/TRDataControl/Blob/TRBlobBase.cs index d349ada6..2552651f 100644 --- a/TRDataControl/Blob/TRBlobBase.cs +++ b/TRDataControl/Blob/TRBlobBase.cs @@ -1,26 +1,19 @@ -using Newtonsoft.Json; -using System.Drawing; -using TRImageControl; +using TRImageControl.Packing; using TRLevelControl.Model; -namespace TRModelTransporter.Model; +namespace TRDataControl; -public abstract class TRBlobBase where E : Enum +public abstract class TRBlobBase + where T : Enum { - [JsonIgnore] - public E Entity { get; set; } - [JsonIgnore] - public E Alias { get; set; } - [JsonIgnore] - public TRImage Image { get; set; } - [JsonIgnore] - public bool HasGraphics => false;// ObjectTextures.Count > 0; - [JsonIgnore] + public TRBlobType Type { get; set; } + public T ID { get; set; } + public T Alias { get; set; } public bool IsDependencyOnly { get; set; } - - public E[] Dependencies { get; set; } - //public Dictionary> ObjectTextures { get; set; } - //public Dictionary>> SpriteTextures { get; set; } - public Dictionary SpriteSequences { get; set; } - public Rectangle[] TextureSegments { get; set; } + public List Dependencies { get; set; } + public TRModel Model { get; set; } + public TRStaticMesh StaticMesh { get; set; } + public List SpriteOffsets { get; set; } + public List Textures { get; set; } + public List CinematicFrames { get; set; } } diff --git a/TRDataControl/Blob/TRBlobType.cs b/TRDataControl/Blob/TRBlobType.cs new file mode 100644 index 00000000..f054a7bd --- /dev/null +++ b/TRDataControl/Blob/TRBlobType.cs @@ -0,0 +1,8 @@ +namespace TRDataControl; + +public enum TRBlobType +{ + Model, + Sprite, + StaticMesh +} diff --git a/TRDataControl/Blob/Types/TR1Blob.cs b/TRDataControl/Blob/Types/TR1Blob.cs index 8f25df1c..9ddd6133 100644 --- a/TRDataControl/Blob/Types/TR1Blob.cs +++ b/TRDataControl/Blob/Types/TR1Blob.cs @@ -1,22 +1,19 @@ using TRLevelControl.Model; -namespace TRModelTransporter.Model.Definitions; +namespace TRDataControl; public class TR1Blob : TRBlobBase { - public TRCinematicFrame[] CinematicFrames { get; set; } - public Dictionary Colours { get; set; } - public List Meshes { get; set; } - public TRModel Model { get; set; } + public Dictionary Palette8 { get; set; } public SortedDictionary SoundEffects { get; set; } public override bool Equals(object obj) { - return obj is TR1Blob definition && Entity == definition.Entity; + return obj is TR1Blob blob && ID == blob.ID; } public override int GetHashCode() { - return 1674515507 + Entity.GetHashCode(); + return ID.GetHashCode(); } } diff --git a/TRDataControl/Blob/Types/TR2Blob.cs b/TRDataControl/Blob/Types/TR2Blob.cs index c8324d1f..230674b9 100644 --- a/TRDataControl/Blob/Types/TR2Blob.cs +++ b/TRDataControl/Blob/Types/TR2Blob.cs @@ -1,22 +1,19 @@ using TRLevelControl.Model; -namespace TRModelTransporter.Model.Definitions; +namespace TRDataControl; public class TR2Blob : TRBlobBase { - public TRCinematicFrame[] CinematicFrames { get; set; } - public Dictionary Colours { get; set; } - public List Meshes { get; set; } - public TRModel Model { get; set; } + public Dictionary Palette16 { get; set; } public SortedDictionary SoundEffects { get; set; } public override bool Equals(object obj) { - return obj is TR2Blob definition && Entity == definition.Entity; + return obj is TR2Blob blob && ID == blob.ID; } public override int GetHashCode() { - return 1875520522 + Entity.GetHashCode(); + return ID.GetHashCode(); } } diff --git a/TRDataControl/Blob/Types/TR3Blob.cs b/TRDataControl/Blob/Types/TR3Blob.cs index 99a3fe2e..3a81ed1b 100644 --- a/TRDataControl/Blob/Types/TR3Blob.cs +++ b/TRDataControl/Blob/Types/TR3Blob.cs @@ -1,22 +1,19 @@ using TRLevelControl.Model; -namespace TRModelTransporter.Model.Definitions; +namespace TRDataControl; public class TR3Blob : TRBlobBase { - public TRCinematicFrame[] CinematicFrames { get; set; } - public Dictionary Colours { get; set; } - public List Meshes { get; set; } - public TRModel Model { get; set; } + public Dictionary Palette16 { get; set; } public SortedDictionary SoundEffects { get; set; } public override bool Equals(object obj) { - return obj is TR3Blob definition && Entity == definition.Entity; + return obj is TR3Blob blob && ID == blob.ID; } public override int GetHashCode() { - return 1075520522 + Entity.GetHashCode(); + return ID.GetHashCode(); } } diff --git a/TRDataControl/Blob/Types/TR4Blob.cs b/TRDataControl/Blob/Types/TR4Blob.cs new file mode 100644 index 00000000..232208ce --- /dev/null +++ b/TRDataControl/Blob/Types/TR4Blob.cs @@ -0,0 +1,18 @@ +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR4Blob : TRBlobBase +{ + public SortedDictionary SoundEffects { get; set; } + + public override bool Equals(object obj) + { + return obj is TR4Blob blob && ID == blob.ID; + } + + public override int GetHashCode() + { + return ID.GetHashCode(); + } +} diff --git a/TRDataControl/Blob/Types/TR5Blob.cs b/TRDataControl/Blob/Types/TR5Blob.cs new file mode 100644 index 00000000..e497cc59 --- /dev/null +++ b/TRDataControl/Blob/Types/TR5Blob.cs @@ -0,0 +1,18 @@ +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR5Blob : TRBlobBase +{ + public SortedDictionary SoundEffects { get; set; } + + public override bool Equals(object obj) + { + return obj is TR5Blob blob && ID == blob.ID; + } + + public override int GetHashCode() + { + return ID.GetHashCode(); + } +} diff --git a/TRDataControl/Data/IDataProvider.cs b/TRDataControl/Data/IDataProvider.cs index 8b4cf163..5b092b95 100644 --- a/TRDataControl/Data/IDataProvider.cs +++ b/TRDataControl/Data/IDataProvider.cs @@ -1,104 +1,84 @@ -namespace TRModelTransporter.Data; +namespace TRDataControl; -public interface IDataProvider where E : Enum +public interface IDataProvider + where T : Enum + where S : Enum { int TextureTileLimit { get; set; } int TextureObjectLimit { get; set; } /// - /// Return all other model types on which the given type depends. + /// Get the blob type of a game type. /// - IEnumerable GetModelDependencies(E entity); + TRBlobType GetBlobType(T type); /// - /// Null meshes that determine if the main entity can be removed from a level. + /// Return all other model types on which the given type depends. /// - IEnumerable GetRemovalExclusions(E entity); + IEnumerable GetDependencies(T type); /// - /// Return model types that have a cyclic depencency on the given type. + /// Null meshes that determine if the main type can be removed from a level. /// - IEnumerable GetCyclicDependencies(E entity); + IEnumerable GetRemovalExclusions(T type); /// - /// Return any sprite types on which the given type depends. + /// Return model types that have a cyclic depencency on the given type. /// - IEnumerable GetSpriteDependencies(E entity); - + IEnumerable GetCyclicDependencies(T type); + /// /// Determines which alias has the priority in a family during import if another alias already exists. /// - Dictionary AliasPriority { get; set; } + Dictionary AliasPriority { get; set; } /// /// Return model types for which cinematic frames should be exported. /// - IEnumerable GetCinematicEntities(); + IEnumerable GetCinematicTypes(); /// - /// Return models that are dependent on Lara. + /// Whether or not the given type is an alias of another. /// - IEnumerable GetLaraDependants(); + bool IsAlias(T type); /// - /// Whether or not the given entity is an alias of another. + /// Whether or not the given type has aliases. /// - bool IsAlias(E entity); - - /// - /// Whether or not the given entity has aliases. - /// - bool HasAliases(E entity); + bool HasAliases(T type); /// /// Convert the given alias into its normal type. /// - E TranslateAlias(E entity); + T TranslateAlias(T type); /// - /// Returns all possible aliases for the given entity. + /// Returns all possible aliases for the given type. /// - IEnumerable GetAliases(E entity); + IEnumerable GetAliases(T type); /// /// Return the specific alias for an alias type given a particular level ID. /// - E GetLevelAlias(string level, E entity); + T GetLevelAlias(string level, T type); /// /// Duplicates on import will throw a TransportException unless they are permitted to replace existing models. /// - bool IsAliasDuplicatePermitted(E entity); + bool IsAliasDuplicatePermitted(T type); /// /// Similar to alias duplicates, but where we want to replace a non-aliased model with another. /// - bool IsOverridePermitted(E entity); - - /// - /// Models that cannot be replaced in the Model array and instead should be remapped to imported models. - /// - IEnumerable GetUnsafeModelReplacements(); + bool IsOverridePermitted(T type); /// /// Determine if the given type's graphics should be ignored on import. /// - bool IsNonGraphicsDependency(E entity); - - /// - /// Determine if the the given type's sound alone should be imported. - /// - bool IsSoundOnlyDependency(E entity); - - /// - /// Returns any hardcoded internal sound IDs for the given entity. - /// - short[] GetHardcodedSounds(E entity); + bool IsNonGraphicsDependency(T type); /// - /// Returns a list of texture indices that should be ignored for a given entity. - /// An emtpy list is translated as meaning all indices should be ignored. Null - /// indicates that no indices should be ignored. + /// Returns any hardcoded internal sound IDs for the given type. /// - IEnumerable GetIgnorableTextureIndices(E entity, string level); + IEnumerable GetHardcodedSounds(T type); } diff --git a/TRDataControl/Data/TR1DataProvider.cs b/TRDataControl/Data/TR1DataProvider.cs index b1ec0c52..c4bd3862 100644 --- a/TRDataControl/Data/TR1DataProvider.cs +++ b/TRDataControl/Data/TR1DataProvider.cs @@ -1,50 +1,63 @@ using TRLevelControl.Helpers; using TRLevelControl.Model; -namespace TRModelTransporter.Data; +namespace TRDataControl; -public class TR1DataProvider : IDataProvider +public class TR1DataProvider : IDataProvider { public int TextureTileLimit { get; set; } = 16; public int TextureObjectLimit { get; set; } = 2048; public Dictionary AliasPriority { get; set; } - public IEnumerable GetModelDependencies(TR1Type entity) + public TRBlobType GetBlobType(TR1Type type) { - return _entityDependencies.ContainsKey(entity) ? _entityDependencies[entity] : _emptyEntities; + if (_spriteTypes.Contains(type)) + { + return TRBlobType.Sprite; + } + if (type >= TR1Type.SceneryBase && type < TR1Type.BandagedAtlantean) + { + return TRBlobType.StaticMesh; + } + return TRBlobType.Model; } - public IEnumerable GetRemovalExclusions(TR1Type entity) + public IEnumerable GetDependencies(TR1Type type) { - return _removalExclusions.ContainsKey(entity) ? _removalExclusions[entity] : _emptyEntities; - } + if (_typeDependencies.ContainsKey(type)) + { + return _typeDependencies[type]; + } - public IEnumerable GetCyclicDependencies(TR1Type entity) - { - return _cyclicDependencies.ContainsKey(entity) ? _cyclicDependencies[entity] : _emptyEntities; + if (IsAlias(type)) + { + return GetDependencies(TranslateAlias(type)); + } + + return _emptyTypes; } - public IEnumerable GetSpriteDependencies(TR1Type entity) + public IEnumerable GetRemovalExclusions(TR1Type type) { - return _spriteDependencies.ContainsKey(entity) ? _spriteDependencies[entity] : _emptyEntities; + return _removalExclusions.ContainsKey(type) ? _removalExclusions[type] : _emptyTypes; } - public IEnumerable GetCinematicEntities() + public IEnumerable GetCyclicDependencies(TR1Type type) { - return _cinematicEntities; + return _cyclicDependencies.ContainsKey(type) ? _cyclicDependencies[type] : _emptyTypes; } - public IEnumerable GetLaraDependants() + public IEnumerable GetCinematicTypes() { - return _laraDependentModels; + return _cinematicTypes; } - public bool IsAlias(TR1Type entity) + public bool IsAlias(TR1Type type) { - foreach (List aliases in _entityAliases.Values) + foreach (List aliases in _typeAliases.Values) { - if (aliases.Contains(entity)) + if (aliases.Contains(type)) { return true; } @@ -53,207 +66,166 @@ public bool IsAlias(TR1Type entity) return false; } - public bool HasAliases(TR1Type entity) + public bool HasAliases(TR1Type type) { - return _entityAliases.ContainsKey(entity); + return _typeAliases.ContainsKey(type); } - public TR1Type TranslateAlias(TR1Type entity) + public TR1Type TranslateAlias(TR1Type type) { - foreach (TR1Type root in _entityAliases.Keys) + foreach (TR1Type root in _typeAliases.Keys) { - if (_entityAliases[root].Contains(entity)) + if (_typeAliases[root].Contains(type)) { return root; } } - return entity; + return type; } - public IEnumerable GetAliases(TR1Type entity) + public IEnumerable GetAliases(TR1Type type) { - return _entityAliases.ContainsKey(entity) ? _entityAliases[entity] : _emptyEntities; + return _typeAliases.ContainsKey(type) ? _typeAliases[type] : _emptyTypes; } - public TR1Type GetLevelAlias(string level, TR1Type entity) + public TR1Type GetLevelAlias(string level, TR1Type type) { - return TR1TypeUtilities.GetAliasForLevel(level, entity); + return TR1TypeUtilities.GetAliasForLevel(level, type); } - public bool IsAliasDuplicatePermitted(TR1Type entity) + public bool IsAliasDuplicatePermitted(TR1Type type) { - return _permittedAliasDuplicates.Contains(entity); + return _permittedAliasDuplicates.Contains(type); } - public bool IsOverridePermitted(TR1Type entity) + public bool IsOverridePermitted(TR1Type type) { - return _permittedOverrides.Contains(entity); + return _permittedOverrides.Contains(type); } - public IEnumerable GetUnsafeModelReplacements() + public bool IsNonGraphicsDependency(TR1Type type) { - return _unsafeModelReplacements; - } - - public bool IsNonGraphicsDependency(TR1Type entity) - { - return _nonGraphicsDependencies.Contains(entity); - } - - public bool IsSoundOnlyDependency(TR1Type entity) - { - return _soundOnlyDependencies.Contains(entity); - } - - public short[] GetHardcodedSounds(TR1Type entity) - { - return _hardcodedSoundIndices.ContainsKey(entity) ? _hardcodedSoundIndices[entity] : null; + return false; } - public IEnumerable GetIgnorableTextureIndices(TR1Type entity, string level) + public IEnumerable GetHardcodedSounds(TR1Type type) { - if (entity == TR1Type.LaraMiscAnim_H && level == TR1LevelNames.VALLEY) - { - // Mesh swap when Lara is killed by T-Rex - return null; - } - return _ignoreEntityTextures.ContainsKey(entity) ? _ignoreEntityTextures[entity] : null; + return _hardcodedSFX.ContainsKey(type) + ? _hardcodedSFX[type] + : _emptySFX; } #region Data - private static readonly IEnumerable _emptyEntities = new List(); + private static readonly List _emptyTypes = new(); + private static readonly List _emptySFX = new(); - private static readonly Dictionary _entityDependencies = new() + private static readonly Dictionary> _typeDependencies = new() { [TR1Type.Adam] - = new TR1Type[] { TR1Type.LaraMiscAnim_H_Pyramid }, + = new() { TR1Type.LaraMiscAnim_H_Pyramid, TR1Type.Explosion1_S_H }, + [TR1Type.AtlanteanLava] + = new() { TR1Type.Flame_S_H }, [TR1Type.BandagedAtlantean] - = new TR1Type[] { TR1Type.BandagedFlyer }, + = new() { TR1Type.BandagedFlyer }, [TR1Type.BandagedFlyer] - = new TR1Type[] { TR1Type.Missile2_H, TR1Type.Missile3_H }, + = new() { TR1Type.Missile2_H, TR1Type.Missile3_H, TR1Type.Explosion1_S_H }, [TR1Type.Centaur] - = new TR1Type[] { TR1Type.Missile3_H }, + = new() { TR1Type.Missile3_H, TR1Type.Explosion1_S_H }, [TR1Type.CentaurStatue] - = new TR1Type[] { TR1Type.Centaur }, + = new() { TR1Type.Centaur }, [TR1Type.CrocodileLand] - = new TR1Type[] { TR1Type.CrocodileWater }, + = new() { TR1Type.CrocodileWater }, [TR1Type.CrocodileWater] - = new TR1Type[] { TR1Type.CrocodileLand }, + = new() { TR1Type.CrocodileLand }, [TR1Type.DartEmitter] - = new TR1Type[] { TR1Type.Dart_H }, + = new() { TR1Type.Dart_H }, + [TR1Type.FlameEmitter_N] + = new() { TR1Type.Flame_S_H }, + [TR1Type.Key1_M_H] + = new() { TR1Type.Key1_S_P }, + [TR1Type.LavaEmitter_N] + = new() { TR1Type.LavaParticles_S_H }, [TR1Type.MeatyAtlantean] - = new TR1Type[] { TR1Type.MeatyFlyer }, + = new() { TR1Type.MeatyFlyer }, [TR1Type.MeatyFlyer] - = new TR1Type[] { TR1Type.Missile2_H, TR1Type.Missile3_H }, + = new() { TR1Type.Missile2_H, TR1Type.Missile3_H, TR1Type.Explosion1_S_H }, [TR1Type.MidasHand_N] - = new TR1Type[] { TR1Type.LaraMiscAnim_H_Midas }, + = new() { TR1Type.LaraMiscAnim_H_Midas, TR1Type.Sparkles_S_H }, + [TR1Type.Missile3_H] + = new() { TR1Type.Explosion1_S_H }, [TR1Type.Natla] - = new TR1Type[] { TR1Type.Missile2_H, TR1Type.Missile3_H }, + = new() { TR1Type.Missile2_H, TR1Type.Missile3_H, TR1Type.Explosion1_S_H }, [TR1Type.Pierre] - = new TR1Type[] { TR1Type.Key1_M_H, TR1Type.ScionPiece_M_H }, + = new() { TR1Type.Key1_M_H, TR1Type.ScionPiece_M_H }, [TR1Type.RatLand] - = new TR1Type[] { TR1Type.RatWater }, + = new() { TR1Type.RatWater }, [TR1Type.RatWater] - = new TR1Type[] { TR1Type.RatLand }, + = new() { TR1Type.RatLand }, + [TR1Type.ScionPiece_M_H] + = new() { TR1Type.ScionPiece2_S_P }, + [TR1Type.SecretAnkh_M_H] + = new() { TR1Type.ScionPiece4_S_P }, + [TR1Type.SecretGoldBar_M_H] + = new() { TR1Type.ScionPiece4_S_P }, + [TR1Type.SecretGoldIdol_M_H] + = new() { TR1Type.ScionPiece4_S_P }, + [TR1Type.SecretLeadBar_M_H] + = new() { TR1Type.ScionPiece4_S_P }, + [TR1Type.SecretScion_M_H] + = new() { TR1Type.ScionPiece4_S_P }, [TR1Type.ShootingAtlantean_N] - = new TR1Type[] { TR1Type.MeatyFlyer }, + = new() { TR1Type.MeatyFlyer }, [TR1Type.SkateboardKid] - = new TR1Type[] { TR1Type.Skateboard }, + = new() { TR1Type.Skateboard }, [TR1Type.ThorHammerHandle] - = new TR1Type[] { TR1Type.ThorHammerBlock }, + = new() { TR1Type.ThorHammerBlock }, [TR1Type.TRex] - = new TR1Type[] { TR1Type.LaraMiscAnim_H_Valley } + = new() { TR1Type.LaraMiscAnim_H_Valley }, }; - private static readonly Dictionary _cyclicDependencies = new() + private static readonly Dictionary> _cyclicDependencies = new() { [TR1Type.CrocodileLand] - = new TR1Type[] { TR1Type.CrocodileWater }, + = new() { TR1Type.CrocodileWater }, [TR1Type.CrocodileWater] - = new TR1Type[] { TR1Type.CrocodileLand }, + = new() { TR1Type.CrocodileLand }, [TR1Type.RatLand] - = new TR1Type[] { TR1Type.RatWater }, + = new() { TR1Type.RatWater }, [TR1Type.RatWater] - = new TR1Type[] { TR1Type.RatLand }, + = new() { TR1Type.RatLand }, [TR1Type.Pierre] - = new TR1Type[] { TR1Type.ScionPiece_M_H }, + = new() { TR1Type.ScionPiece_M_H }, }; private static readonly Dictionary> _removalExclusions = new() { [TR1Type.FlyingAtlantean] - = new List { TR1Type.NonShootingAtlantean_N, TR1Type.ShootingAtlantean_N } + = new() { TR1Type.NonShootingAtlantean_N, TR1Type.ShootingAtlantean_N } }; - private static readonly Dictionary> _spriteDependencies = new() - { - [TR1Type.SecretScion_M_H] - = new List { TR1Type.ScionPiece4_S_P }, - [TR1Type.SecretGoldIdol_M_H] - = new List { TR1Type.ScionPiece4_S_P }, - [TR1Type.SecretLeadBar_M_H] - = new List { TR1Type.ScionPiece4_S_P }, - [TR1Type.SecretGoldBar_M_H] - = new List { TR1Type.ScionPiece4_S_P }, - [TR1Type.SecretAnkh_M_H] - = new List { TR1Type.ScionPiece4_S_P }, - - [TR1Type.Adam] - = new List { TR1Type.Explosion1_S_H }, - [TR1Type.Centaur] - = new List { TR1Type.Explosion1_S_H }, - [TR1Type.FlyingAtlantean] - = new List { TR1Type.Explosion1_S_H }, - [TR1Type.Key1_M_H] - = new List { TR1Type.Key1_S_P }, - [TR1Type.Natla] - = new List { TR1Type.Explosion1_S_H }, - [TR1Type.ScionPiece_M_H] - = new List { TR1Type.ScionPiece2_S_P }, - [TR1Type.ShootingAtlantean_N] - = new List { TR1Type.Explosion1_S_H }, - [TR1Type.Missile3_H] - = new List { TR1Type.Explosion1_S_H }, - [TR1Type.FlameEmitter_N] - = new() { TR1Type.Flame_S_H }, - [TR1Type.MidasHand_N] - = new() { TR1Type.Sparkles_S_H }, - [TR1Type.LavaEmitter_N] - = new() { TR1Type.LavaParticles_S_H }, - [TR1Type.AtlanteanLava] - = new() { TR1Type.Flame_S_H } - }; - - private static readonly List _cinematicEntities = new() + private static readonly List _cinematicTypes = new() { TR1Type.MidasHand_N }; - // These are models that use Lara's hips as placeholders - private static readonly List _laraDependentModels = new() + private static readonly Dictionary> _typeAliases = new() { - TR1Type.NonShootingAtlantean_N, TR1Type.ShootingAtlantean_N, TR1Type.Earthquake_N, TR1Type.FlameEmitter_N, - TR1Type.LavaEmitter_N, TR1Type.AtlanteanLava, TR1Type.MidasHand_N - }; - - private static readonly Dictionary> _entityAliases = new() - { - [TR1Type.FlyingAtlantean] = new List + [TR1Type.FlyingAtlantean] = new() { TR1Type.BandagedFlyer, TR1Type.MeatyFlyer }, - [TR1Type.LaraMiscAnim_H] = new List + [TR1Type.LaraMiscAnim_H] = new() { TR1Type.LaraMiscAnim_H_General, TR1Type.LaraMiscAnim_H_Valley, TR1Type.LaraMiscAnim_H_Qualopec, TR1Type.LaraMiscAnim_H_Midas, TR1Type.LaraMiscAnim_H_Sanctuary, TR1Type.LaraMiscAnim_H_Atlantis, TR1Type.LaraMiscAnim_H_Pyramid }, - [TR1Type.NonShootingAtlantean_N] = new List + [TR1Type.NonShootingAtlantean_N] = new() { TR1Type.BandagedAtlantean, TR1Type.MeatyAtlantean }, - [TR1Type.Cowboy] = new List + [TR1Type.Cowboy] = new() { TR1Type.CowboyOG, TR1Type.CowboyHeadless } @@ -269,67 +241,121 @@ public IEnumerable GetIgnorableTextureIndices(TR1Type entity, string level) TR1Type.LaraPonytail_H_U, TR1Type.ScionPiece_M_H }; - private static readonly List _unsafeModelReplacements = new() - { - }; - - private static readonly List _nonGraphicsDependencies = new() - { - }; - - // If these are imported into levels that already have another alias for them, only their hardcoded sounds will be imported - protected static readonly List _soundOnlyDependencies = new() - { - }; - - private static readonly Dictionary _hardcodedSoundIndices = new() + private static readonly Dictionary> _hardcodedSFX = new() { - [TR1Type.Adam] = new short[] { 104, 137, 138, 140, 141, 142 }, - [TR1Type.BandagedFlyer] = new short[] { 104 }, - [TR1Type.Bear] = new short[] { 12, 16 }, - [TR1Type.Centaur] = new short[] { 104 }, - [TR1Type.DamoclesSword] = new short[] { 103 }, - [TR1Type.DartEmitter] = new short[] { 151 }, - [TR1Type.Earthquake_N] = new short[] { 70, 147 }, - [TR1Type.FlameEmitter_N] = new short[] { 150 }, - [TR1Type.Gorilla] = new short[] { 90, 91, 101 }, - [TR1Type.Larson] = new short[] { 78 }, - [TR1Type.LavaEmitter_N] = new short[] { 149 }, - [TR1Type.Lion] = new short[] { 85, 86, 87 }, - [TR1Type.Lioness] = new short[] { 85, 86, 87 }, - [TR1Type.MeatyAtlantean] = new short[] { 104 }, - [TR1Type.MeatyFlyer] = new short[] { 104, 120, 121, 122, 123, 124, 125, 126 }, - [TR1Type.Missile3_H] = new short[] { 104 }, - [TR1Type.Natla] = new short[] { 104, 123, 124, 202 }, - [TR1Type.ShootingAtlantean_N] = new short[] { 104 }, - [TR1Type.SkateboardKid] = new short[] { 132, 204 }, - [TR1Type.TeethSpikes] = new short[] { 145 }, - [TR1Type.ThorHammerHandle] = new short[] { 70 }, - [TR1Type.ThorLightning] = new short[] { 98 }, - [TR1Type.UnderwaterSwitch] = new short[] { 61 }, - [TR1Type.Wolf] = new short[] { 20 } + [TR1Type.Adam] = new() + { + TR1SFX.TorsoHit, + }, + [TR1Type.Bear] = new() + { + TR1SFX.BearFeet, + TR1SFX.BearHurt, + }, + [TR1Type.DamoclesSword] = new() + { + TR1SFX.DamoclesSword, + }, + [TR1Type.DartEmitter] = new() + { + TR1SFX.Darts, + }, + [TR1Type.Larson] = new() + { + TR1SFX.LarsonRicochet, + }, + [TR1Type.Earthquake_N] = new() + { + TR1SFX.RollingBall, + TR1SFX.TRexStomp, + }, + [TR1Type.FlameEmitter_N] = new() + { + TR1SFX.Fire, + }, + [TR1Type.Explosion1_S_H] = new() + { + TR1SFX.AtlanteanExplode, + }, + [TR1Type.LavaEmitter_N] = new() + { + TR1SFX.LavaFountain, + }, + [TR1Type.Lion] = new() + { + TR1SFX.LionHurt, + }, + [TR1Type.Lioness] = new() + { + TR1SFX.LionHurt, + }, + [TR1Type.Natla] = new() + { + TR1SFX.AtlanteanNeedle, + TR1SFX.AtlanteanBall, + TR1SFX.NatlaSpeech, + }, + [TR1Type.Panther] = new() + { + TR1SFX.LionHurt, + }, + [TR1Type.SkateboardKid] = new() + { + TR1SFX.SkateKidHit, + TR1SFX.SkateKidSpeech, + }, + [TR1Type.TeethSpikes] = new() + { + TR1SFX.LaraSpikeDeath, + }, + [TR1Type.ThorLightning] = new() + { + TR1SFX.Thunder, + }, + [TR1Type.UnderwaterSwitch] = new() + { + TR1SFX.UnderwaterSwitch, + }, + [TR1Type.Wolf] = new() + { + TR1SFX.WolfHurt, + }, }; - private static readonly Dictionary> _ignoreEntityTextures = new() - { - [TR1Type.LaraMiscAnim_H] - = new(), // empty list indicates to ignore everything - [TR1Type.Earthquake_N] - = new(), - [TR1Type.FlameEmitter_N] - = new() { 179, 180, 181, 183, 184, 185, 188, 189, 194 }, - [TR1Type.MidasHand_N] - = new() { 179, 180, 181, 183, 184, 185, 188, 189, 194 }, - [TR1Type.LavaEmitter_N] - = new() { 184, 185, 188, 189, 190, 192, 193, 195, 197 }, - [TR1Type.NonShootingAtlantean_N] - = new(), - [TR1Type.ShootingAtlantean_N] - = new(), - [TR1Type.Mummy] - = new() { 130, 131, 132, 133, 134, 135, 136, 137, 140, 141 }, - [TR1Type.ThorLightning] - = new() { 150, 151, 154, 155, 156, 157, 158, 159, 160, 161 } + private static readonly List _spriteTypes = new() + { + TR1Type.Pistols_S_P, + TR1Type.Shotgun_S_P, + TR1Type.Magnums_S_P, + TR1Type.Uzis_S_P, + TR1Type.PistolAmmo_S_P, + TR1Type.ShotgunAmmo_S_P, + TR1Type.MagnumAmmo_S_P, + TR1Type.UziAmmo_S_P, + TR1Type.SmallMed_S_P, + TR1Type.LargeMed_S_P, + TR1Type.Puzzle1_S_P, + TR1Type.Puzzle2_S_P, + TR1Type.Puzzle3_S_P, + TR1Type.Puzzle4_S_P, + TR1Type.LeadBar_S_P, + TR1Type.Key1_S_P, + TR1Type.Key2_S_P, + TR1Type.Key3_S_P, + TR1Type.Key4_S_P, + TR1Type.ScionPiece1_S_P, + TR1Type.ScionPiece2_S_P, + TR1Type.Explosion1_S_H, + TR1Type.WaterRipples1_S_H, + TR1Type.Bubbles1_S_H, + TR1Type.Bubbles2_S_H, + TR1Type.Blood1_S_H, + TR1Type.DartEffect_S_H, + TR1Type.Ricochet_S_H, + TR1Type.Sparkles_S_H, + TR1Type.LavaParticles_S_H, + TR1Type.Flame_S_H, + TR1Type.FontGraphics_S_H, }; #endregion diff --git a/TRDataControl/Data/TR2DataProvider.cs b/TRDataControl/Data/TR2DataProvider.cs index 9d464dcf..20064576 100644 --- a/TRDataControl/Data/TR2DataProvider.cs +++ b/TRDataControl/Data/TR2DataProvider.cs @@ -1,50 +1,63 @@ using TRLevelControl.Helpers; using TRLevelControl.Model; -namespace TRModelTransporter.Data; +namespace TRDataControl; -public class TR2DataProvider : IDataProvider +public class TR2DataProvider : IDataProvider { public int TextureTileLimit { get; set; } = 16; public int TextureObjectLimit { get; set; } = 2048; public Dictionary AliasPriority { get; set; } - public IEnumerable GetModelDependencies(TR2Type entity) + public TRBlobType GetBlobType(TR2Type type) { - return _entityDependencies.ContainsKey(entity) ? _entityDependencies[entity] : _emptyEntities; + if (_spriteTypes.Contains(type)) + { + return TRBlobType.Sprite; + } + if (type >= TR2Type.SceneryBase && type < TR2Type.BengalTiger) + { + return TRBlobType.StaticMesh; + } + return TRBlobType.Model; } - public IEnumerable GetRemovalExclusions(TR2Type entity) + public IEnumerable GetDependencies(TR2Type type) { - return _emptyEntities; - } + if (_typeDependencies.ContainsKey(type)) + { + return _typeDependencies[type]; + } - public IEnumerable GetCyclicDependencies(TR2Type entity) - { - return _emptyEntities; + if (IsAlias(type)) + { + return GetDependencies(TranslateAlias(type)); + } + + return _emptyTypes; } - public IEnumerable GetSpriteDependencies(TR2Type entity) + public IEnumerable GetRemovalExclusions(TR2Type type) { - return _spriteDependencies.ContainsKey(entity) ? _spriteDependencies[entity] : _emptyEntities; + return _emptyTypes; } - public IEnumerable GetCinematicEntities() + public IEnumerable GetCyclicDependencies(TR2Type type) { - return _cinematicEntities; + return _emptyTypes; } - public IEnumerable GetLaraDependants() + public IEnumerable GetCinematicTypes() { - return _laraDependentModels; + return _cinematicTypes; } - public bool IsAlias(TR2Type entity) + public bool IsAlias(TR2Type type) { - foreach (List aliases in _entityAliases.Values) + foreach (List aliases in _typeAliases.Values) { - if (aliases.Contains(entity)) + if (aliases.Contains(type)) { return true; } @@ -53,230 +66,196 @@ public bool IsAlias(TR2Type entity) return false; } - public bool HasAliases(TR2Type entity) + public bool HasAliases(TR2Type type) { - return _entityAliases.ContainsKey(entity); + return _typeAliases.ContainsKey(type); } - public TR2Type TranslateAlias(TR2Type entity) + public TR2Type TranslateAlias(TR2Type type) { - foreach (TR2Type root in _entityAliases.Keys) + foreach (TR2Type root in _typeAliases.Keys) { - if (_entityAliases[root].Contains(entity)) + if (_typeAliases[root].Contains(type)) { return root; } } - return entity; + return type; } - public IEnumerable GetAliases(TR2Type entity) + public IEnumerable GetAliases(TR2Type type) { - return _entityAliases.ContainsKey(entity) ? _entityAliases[entity] : _emptyEntities; + return _typeAliases.ContainsKey(type) ? _typeAliases[type] : _emptyTypes; } - public TR2Type GetLevelAlias(string level, TR2Type entity) + public TR2Type GetLevelAlias(string level, TR2Type type) { - return TR2TypeUtilities.GetAliasForLevel(level, entity); + return TR2TypeUtilities.GetAliasForLevel(level, type); } - public bool IsAliasDuplicatePermitted(TR2Type entity) + public bool IsAliasDuplicatePermitted(TR2Type type) { - return _permittedAliasDuplicates.Contains(entity); + return _permittedAliasDuplicates.Contains(type); } - public bool IsOverridePermitted(TR2Type entity) + public bool IsOverridePermitted(TR2Type type) { - return _permittedOverrides.Contains(entity); + return _permittedOverrides.Contains(type); } - public IEnumerable GetUnsafeModelReplacements() + public bool IsNonGraphicsDependency(TR2Type type) { - return _unsafeModelReplacements; + return _nonGraphicsDependencies.Contains(type); } - public bool IsNonGraphicsDependency(TR2Type entity) + public IEnumerable GetHardcodedSounds(TR2Type type) { - return _nonGraphicsDependencies.Contains(entity); - } - - public bool IsSoundOnlyDependency(TR2Type entity) - { - return _soundOnlyDependencies.Contains(entity); - } - - public short[] GetHardcodedSounds(TR2Type entity) - { - return _hardcodedSoundIndices.ContainsKey(entity) ? _hardcodedSoundIndices[entity] : null; - } - - public IEnumerable GetIgnorableTextureIndices(TR2Type entity, string level) - { - return _ignoreEntityTextures.ContainsKey(entity) ? _ignoreEntityTextures[entity] : null; + return _hardcodedSFX.ContainsKey(type) + ? _hardcodedSFX[type] + : _emptySFX; } #region Data - private static readonly IEnumerable _emptyEntities = new List(); + private static readonly List _emptyTypes = new(); + private static readonly List _emptySFX = new(); - private static readonly Dictionary _entityDependencies = new() + private static readonly Dictionary> _typeDependencies = new() { + [TR2Type.Autos_M_H] = + new() { TR2Type.LaraAutoAnim_H, TR2Type.Gunflare_H, TR2Type.Automags_S_P, TR2Type.AutoAmmo_M_H, TR2Type.AutoAmmo_S_P }, + [TR2Type.Boat] = + new() { TR2Type.LaraBoatAnim_H, TR2Type.BoatWake_S_H }, + [TR2Type.BlackSnowmob] = + new() { TR2Type.RedSnowmobile }, + [TR2Type.FlamethrowerGoon] + = new() { TR2Type.Flame_S_H }, + [TR2Type.GrenadeLauncher_M_H] = + new() { TR2Type.LaraGrenadeAnim_H, TR2Type.GrenadeProjectile_H, TR2Type.GrenadeLauncher_S_P, TR2Type.Grenades_M_H, TR2Type.Grenades_S_P, TR2Type.Explosion_S_H }, + [TR2Type.Harpoon_M_H] = + new() { TR2Type.LaraHarpoonAnim_H, TR2Type.HarpoonProjectile_H, TR2Type.Harpoon_S_P, TR2Type.HarpoonAmmo_M_H, TR2Type.HarpoonAmmo_S_P }, + [TR2Type.Key2_M_H] + = new() { TR2Type.Key2_S_P }, + [TR2Type.Knifethrower] = + new() { TR2Type.KnifeProjectile_H }, [TR2Type.LaraSun] = - new TR2Type[] { TR2Type.LaraPistolAnim_H_Sun, TR2Type.LaraAutoAnim_H_Sun, TR2Type.LaraUziAnim_H_Sun }, + new() { TR2Type.LaraPistolAnim_H_Sun, TR2Type.LaraAutoAnim_H_Sun, TR2Type.LaraUziAnim_H_Sun }, [TR2Type.LaraUnwater] = - new TR2Type[] { TR2Type.LaraPistolAnim_H_Unwater, TR2Type.LaraAutoAnim_H_Unwater, TR2Type.LaraUziAnim_H_Unwater }, + new() { TR2Type.LaraPistolAnim_H_Unwater, TR2Type.LaraAutoAnim_H_Unwater, TR2Type.LaraUziAnim_H_Unwater }, [TR2Type.LaraSnow] = - new TR2Type[] { TR2Type.LaraPistolAnim_H_Snow, TR2Type.LaraAutoAnim_H_Snow, TR2Type.LaraUziAnim_H_Snow }, + new() { TR2Type.LaraPistolAnim_H_Snow, TR2Type.LaraAutoAnim_H_Snow, TR2Type.LaraUziAnim_H_Snow }, [TR2Type.LaraHome] = - new TR2Type[] { TR2Type.LaraPistolAnim_H_Home, TR2Type.LaraAutoAnim_H_Home, TR2Type.LaraUziAnim_H_Home }, - - [TR2Type.Pistols_M_H] = - new TR2Type[] { TR2Type.LaraPistolAnim_H, TR2Type.Gunflare_H }, - [TR2Type.Shotgun_M_H] = - new TR2Type[] { TR2Type.LaraShotgunAnim_H, TR2Type.Gunflare_H }, - [TR2Type.Autos_M_H] = - new TR2Type[] { TR2Type.LaraAutoAnim_H, TR2Type.Gunflare_H }, - [TR2Type.Uzi_M_H] = - new TR2Type[] { TR2Type.LaraUziAnim_H, TR2Type.Gunflare_H }, + new() { TR2Type.LaraPistolAnim_H_Home, TR2Type.LaraAutoAnim_H_Home, TR2Type.LaraUziAnim_H_Home }, [TR2Type.M16_M_H] = - new TR2Type[] { TR2Type.LaraM16Anim_H, TR2Type.M16Gunflare_H }, - [TR2Type.Harpoon_M_H] = - new TR2Type[] { TR2Type.LaraHarpoonAnim_H, TR2Type.HarpoonProjectile_H }, - [TR2Type.GrenadeLauncher_M_H] = - new TR2Type[] { TR2Type.LaraGrenadeAnim_H, TR2Type.GrenadeProjectile_H }, - - [TR2Type.TRex] = - new TR2Type[] { TR2Type.LaraMiscAnim_H_Wall }, + new() { TR2Type.LaraM16Anim_H, TR2Type.M16Gunflare_H, TR2Type.M16_S_P, TR2Type.M16Ammo_M_H, TR2Type.M16Ammo_S_P }, + [TR2Type.MarcoBartoli] = + new() + { + TR2Type.DragonExplosionEmitter_N, TR2Type.DragonExplosion1_H, TR2Type.DragonExplosion2_H, TR2Type.DragonExplosion3_H, + TR2Type.DragonFront_H, TR2Type.DragonBack_H, TR2Type.DragonBonesFront_H, TR2Type.DragonBonesBack_H, TR2Type.LaraMiscAnim_H_Xian, + TR2Type.Puzzle2_M_H_Dagger, TR2Type.Flame_S_H + }, [TR2Type.MaskedGoon2] = - new TR2Type[] { TR2Type.MaskedGoon1 }, + new() { TR2Type.MaskedGoon1 }, [TR2Type.MaskedGoon3] = - new TR2Type[] { TR2Type.MaskedGoon1 }, + new() { TR2Type.MaskedGoon1 }, + [TR2Type.Mercenary3] = + new() { TR2Type.Mercenary2 }, + [TR2Type.MercSnowmobDriver] = + new() { TR2Type.BlackSnowmob }, + [TR2Type.Pistols_M_H] = + new() { TR2Type.LaraPistolAnim_H, TR2Type.Gunflare_H, TR2Type.Pistols_S_P }, + [TR2Type.RedSnowmobile] = + new() { TR2Type.SnowmobileBelt, TR2Type.LaraSnowmobAnim_H, TR2Type.SnowmobileWake_S_H }, + [TR2Type.Shotgun_M_H] = + new() { TR2Type.LaraShotgunAnim_H, TR2Type.Gunflare_H, TR2Type.Shotgun_S_P, TR2Type.ShotgunAmmo_M_H, TR2Type.ShotgunAmmo_S_P }, [TR2Type.ScubaDiver] = - new TR2Type[] { TR2Type.ScubaHarpoonProjectile_H }, + new() { TR2Type.ScubaHarpoonProjectile_H }, [TR2Type.Shark] = - new TR2Type[] { TR2Type.LaraMiscAnim_H_Unwater }, + new() { TR2Type.LaraMiscAnim_H_Unwater }, [TR2Type.StickWieldingGoon2] = - new TR2Type[] { TR2Type.StickWieldingGoon1GreenVest }, - [TR2Type.MercSnowmobDriver] = - new TR2Type[] { TR2Type.BlackSnowmob }, - [TR2Type.BlackSnowmob] = - new TR2Type[] { TR2Type.RedSnowmobile }, - [TR2Type.RedSnowmobile] = - new TR2Type[] { TR2Type.SnowmobileBelt, TR2Type.LaraSnowmobAnim_H }, - [TR2Type.Boat] = - new TR2Type[] { TR2Type.LaraBoatAnim_H }, - [TR2Type.Mercenary3] = - new TR2Type[] { TR2Type.Mercenary2 }, - [TR2Type.Yeti] = - new TR2Type[] { TR2Type.LaraMiscAnim_H_Ice }, + new() { TR2Type.StickWieldingGoon1GreenVest }, + [TR2Type.TRex] = + new() { TR2Type.LaraMiscAnim_H_Wall }, + [TR2Type.Uzi_M_H] = + new() { TR2Type.LaraUziAnim_H, TR2Type.Gunflare_H, TR2Type.Uzi_S_P, TR2Type.UziAmmo_M_H, TR2Type.Uzi_S_P }, + [TR2Type.WaterfallMist_N] + = new() { TR2Type.WaterRipples_S_H }, [TR2Type.XianGuardSpear] = - new TR2Type[] { TR2Type.LaraMiscAnim_H_Xian, TR2Type.XianGuardSpearStatue }, + new() { TR2Type.LaraMiscAnim_H_Xian, TR2Type.XianGuardSpearStatue }, [TR2Type.XianGuardSword] = - new TR2Type[] { TR2Type.XianGuardSwordStatue }, - [TR2Type.Knifethrower] = - new TR2Type[] { TR2Type.KnifeProjectile_H }, - [TR2Type.MarcoBartoli] = - new TR2Type[] - { - TR2Type.DragonExplosionEmitter_N, TR2Type.DragonExplosion1_H, TR2Type.DragonExplosion2_H, TR2Type.DragonExplosion3_H, - TR2Type.DragonFront_H, TR2Type.DragonBack_H, TR2Type.DragonBonesFront_H, TR2Type.DragonBonesBack_H, TR2Type.LaraMiscAnim_H_Xian, - TR2Type.Puzzle2_M_H_Dagger - } - }; - - private static readonly Dictionary> _spriteDependencies = new() - { - [TR2Type.FlamethrowerGoon] - = new List { TR2Type.Flame_S_H }, - [TR2Type.MarcoBartoli] - = new List { TR2Type.Flame_S_H }, - [TR2Type.Boat] - = new List { TR2Type.BoatWake_S_H }, - [TR2Type.RedSnowmobile] - = new List { TR2Type.SnowmobileWake_S_H }, - [TR2Type.XianGuardSword] - = new List { TR2Type.XianGuardSparkles_S_H }, - [TR2Type.WaterfallMist_N] - = new List { TR2Type.WaterRipples_S_H }, - [TR2Type.Key2_M_H] - = new List { TR2Type.Key2_S_P } + new() { TR2Type.XianGuardSwordStatue, TR2Type.XianGuardSparkles_S_H }, + [TR2Type.Yeti] = + new() { TR2Type.LaraMiscAnim_H_Ice }, + }; - private static readonly List _cinematicEntities = new() + private static readonly List _cinematicTypes = new() { TR2Type.DragonExplosionEmitter_N }; - // These are models that use Lara's hips as placeholders - private static readonly List _laraDependentModels = new() + private static readonly Dictionary> _typeAliases = new() { - TR2Type.CameraTarget_N, TR2Type.FlameEmitter_N, TR2Type.LaraCutscenePlacement_N, - TR2Type.DragonExplosionEmitter_N, TR2Type.BartoliHideoutClock_N, TR2Type.SingingBirds_N, - TR2Type.WaterfallMist_N, TR2Type.DrippingWater_N, TR2Type.LavaAirParticleEmitter_N, - TR2Type.AlarmBell_N, TR2Type.DoorBell_N - }; - - private static readonly Dictionary> _entityAliases = new() - { - [TR2Type.Lara] = new List + [TR2Type.Lara] = new() { TR2Type.LaraSun, TR2Type.LaraUnwater, TR2Type.LaraSnow, TR2Type.LaraHome }, - [TR2Type.LaraPistolAnim_H] = new List + [TR2Type.LaraPistolAnim_H] = new() { TR2Type.LaraPistolAnim_H_Sun, TR2Type.LaraPistolAnim_H_Unwater, TR2Type.LaraPistolAnim_H_Snow, TR2Type.LaraPistolAnim_H_Home }, - [TR2Type.LaraAutoAnim_H] = new List + [TR2Type.LaraAutoAnim_H] = new() { TR2Type.LaraAutoAnim_H_Sun, TR2Type.LaraAutoAnim_H_Unwater, TR2Type.LaraAutoAnim_H_Snow, TR2Type.LaraAutoAnim_H_Home }, - [TR2Type.LaraUziAnim_H] = new List + [TR2Type.LaraUziAnim_H] = new() { TR2Type.LaraUziAnim_H_Sun, TR2Type.LaraUziAnim_H_Unwater, TR2Type.LaraUziAnim_H_Snow, TR2Type.LaraUziAnim_H_Home }, - [TR2Type.LaraMiscAnim_H] = new List + [TR2Type.LaraMiscAnim_H] = new() { TR2Type.LaraMiscAnim_H_Wall, TR2Type.LaraMiscAnim_H_Unwater, TR2Type.LaraMiscAnim_H_Ice, TR2Type.LaraMiscAnim_H_Xian, TR2Type.LaraMiscAnim_H_HSH, TR2Type.LaraMiscAnim_H_Venice }, - [TR2Type.TigerOrSnowLeopard] = new List + [TR2Type.TigerOrSnowLeopard] = new() { TR2Type.BengalTiger, TR2Type.SnowLeopard, TR2Type.WhiteTiger }, - [TR2Type.StickWieldingGoon1] = new List + [TR2Type.StickWieldingGoon1] = new() { TR2Type.StickWieldingGoon1Bandana, TR2Type.StickWieldingGoon1BlackJacket, TR2Type.StickWieldingGoon1BodyWarmer, TR2Type.StickWieldingGoon1GreenVest, TR2Type.StickWieldingGoon1WhiteVest }, - [TR2Type.FlamethrowerGoon] = new List + [TR2Type.FlamethrowerGoon] = new() { TR2Type.FlamethrowerGoonOG, TR2Type.FlamethrowerGoonTopixtor }, - [TR2Type.Gunman1] = new List + [TR2Type.Gunman1] = new() { TR2Type.Gunman1OG, TR2Type.Gunman1TopixtorORC, TR2Type.Gunman1TopixtorCAC }, - [TR2Type.Barracuda] = new List + [TR2Type.Barracuda] = new() { TR2Type.BarracudaIce, TR2Type.BarracudaUnwater, TR2Type.BarracudaXian }, - [TR2Type.Puzzle1_M_H] = new List + [TR2Type.Puzzle1_M_H] = new() { TR2Type.Puzzle1_M_H_CircuitBoard, TR2Type.Puzzle1_M_H_CircuitBreaker, TR2Type.Puzzle1_M_H_Dagger, TR2Type.Puzzle1_M_H_DragonSeal, TR2Type.Puzzle1_M_H_MysticPlaque, TR2Type.Puzzle1_M_H_PrayerWheel, TR2Type.Puzzle1_M_H_RelayBox, TR2Type.Puzzle1_M_H_TibetanMask }, - [TR2Type.Puzzle2_M_H] = new List + [TR2Type.Puzzle2_M_H] = new() { TR2Type.Puzzle2_M_H_CircuitBoard, TR2Type.Puzzle2_M_H_Dagger, TR2Type.Puzzle2_M_H_GemStone, TR2Type.Puzzle2_M_H_MysticPlaque }, - [TR2Type.Puzzle4_M_H] = new List + [TR2Type.Puzzle4_M_H] = new() { TR2Type.Puzzle4_M_H_Seraph } @@ -292,121 +271,139 @@ public IEnumerable GetIgnorableTextureIndices(TR2Type entity, string level) TR2Type.MarcoBartoli }; - private static readonly List _unsafeModelReplacements = new() - { - }; - private static readonly List _nonGraphicsDependencies = new() { TR2Type.StickWieldingGoon1GreenVest, TR2Type.MaskedGoon1, TR2Type.Mercenary2 }; - // If these are imported into levels that already have another alias for them, only their hardcoded sounds will be imported - protected static readonly List _soundOnlyDependencies = new() + private static readonly Dictionary> _hardcodedSFX = new() { - TR2Type.StickWieldingGoon1GreenVest - }; - - private static readonly Dictionary _hardcodedSoundIndices = new() - { - [TR2Type.DragonExplosionEmitter_N] = new short[] + [TR2Type.Boat] = new() + { + TR2SFX.BoatIdle, + TR2SFX.BoatMoving, + TR2SFX.BoatEngine, + TR2SFX.BoatIntoWater, + }, + [TR2Type.DragonExplosion1_H] = new() { - 341 // Explosion when dragon spawns + TR2SFX.SphereOfDoom, }, - [TR2Type.DragonFront_H] = new short[] - { - 298, // Footstep - 299, // Growl 1 - 300, // Growl 2 - 301, // Body falling - 302, // Dying breath - 303, // Growl 3 - 304, // Grunt - 305, // Fire-breathing - 306, // Leg lift - 307 // Leg hit + [TR2Type.DragonFront_H] = new() + { + TR2SFX.DragonFire, }, - [TR2Type.Boat] = new short[] - { - 194, // Start - 195, // Idling - 196, // Accelerating - 197, // High RPM - 198, // Shut off - 199, // Engine hit - 200, // Body hit - 336 // Dry land + [TR2Type.Flame_S_H] = new() + { + TR2SFX.LoopForSmallFires, }, - [TR2Type.LaraSnowmobAnim_H] = new short[] + [TR2Type.RedSnowmobile] = new() { - 153, // Snowmobile idling - 155 // Snowmobile accelerating + TR2SFX.SkidooIdle, + TR2SFX.SkidooMoving, }, - [TR2Type.StickWieldingGoon1Bandana] = new short[] + [TR2Type.StickWieldingGoon1BodyWarmer] = new() { - 71, // Thump 1 - 72, // Thump 2 + TR2SFX.EnemyHit1, + TR2SFX.EnemyHit2, + TR2SFX.EnemyThump, }, - [TR2Type.StickWieldingGoon1BlackJacket] = new short[] + [TR2Type.StickWieldingGoon1WhiteVest] = new() { - 71, // Thump 1 - 72, // Thump 2 + TR2SFX.EnemyHit1, + TR2SFX.EnemyHit2, + TR2SFX.EnemyThump, }, - [TR2Type.StickWieldingGoon1BodyWarmer] = new short[] + [TR2Type.StickWieldingGoon1Bandana] = new() { - 69, // Footstep - 70, // Grunt - 71, // Thump 1 - 72, // Thump 2 - 121 // Thump 3 + TR2SFX.EnemyHit1, + TR2SFX.EnemyHit2, + TR2SFX.EnemyThump, }, - [TR2Type.StickWieldingGoon1GreenVest] = new short[] + [TR2Type.StickWieldingGoon1GreenVest] = new() { - 71, // Thump 1 - 72, // Thump 2 + TR2SFX.EnemyHit1, + TR2SFX.EnemyHit2, + TR2SFX.EnemyThump, }, - [TR2Type.StickWieldingGoon1WhiteVest] = new short[] + [TR2Type.StickWieldingGoon1BlackJacket] = new() { - 71, // Thump 1 - 72, // Thump 2, - 121 // Thump 3 + TR2SFX.EnemyHit1, + TR2SFX.EnemyHit2, + TR2SFX.EnemyThump, }, - [TR2Type.StickWieldingGoon2] = new short[] - { - 69, // Footstep - 70, // Grunt - 71, // Thump 1 - 72, // Thump 2 - 180, // Chains - 181, // Chains - 182, // Footstep? - 183 // Another thump? + [TR2Type.StickWieldingGoon2] = new() + { + TR2SFX.EnemyFeet, + TR2SFX.EnemyGrunt, + TR2SFX.EnemyHit1, + TR2SFX.EnemyHit2, + TR2SFX.EnemyBeltJingle, + TR2SFX.EnemyWrench, + TR2SFX.Footstep, + TR2SFX.FootstepHit, }, - [TR2Type.XianGuardSword] = new short[] + [TR2Type.XianGuardSword] = new() { - 312 // Hovering + TR2SFX.WarriorHover, }, - [TR2Type.Winston] = new short[] + [TR2Type.Winston] = new() { - 344, // Scared - 345, // Huff - 346, // Bumped - 347 // Cups clattering + TR2SFX.WinstonGrunt1, + TR2SFX.WinstonGrunt2, + TR2SFX.WinstonGrunt3, + TR2SFX.WinstonCups, } }; - private static readonly Dictionary> _ignoreEntityTextures = new() + private static readonly List _spriteTypes = new() { - [TR2Type.LaraMiscAnim_H] - = new List(), // empty list indicates to ignore everything - [TR2Type.WaterfallMist_N] - = new List { 0, 1, 2, 3, 4, 5, 6, 11, 15, 20, 22 }, - [TR2Type.LaraSnowmobAnim_H] - = new List { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 20, 21, 23 }, - [TR2Type.SnowmobileBelt] - = new List { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 20, 21, 23 }, - [TR2Type.DragonExplosionEmitter_N] - = new List { 0, 1, 2, 3, 4, 5, 6, 8, 13, 14, 16, 17, 19 } + TR2Type.BoatWake_S_H, + TR2Type.SnowmobileWake_S_H, + TR2Type.UIFrame_H, + TR2Type.Pistols_S_P, + TR2Type.Shotgun_S_P, + TR2Type.Automags_S_P, + TR2Type.Uzi_S_P, + TR2Type.Harpoon_S_P, + TR2Type.M16_S_P, + TR2Type.GrenadeLauncher_S_P, + TR2Type.ShotgunAmmo_S_P, + TR2Type.AutoAmmo_S_P, + TR2Type.UziAmmo_S_P, + TR2Type.HarpoonAmmo_S_P, + TR2Type.M16Ammo_S_P, + TR2Type.Grenades_S_P, + TR2Type.SmallMed_S_P, + TR2Type.LargeMed_S_P, + TR2Type.Flares_S_P, + TR2Type.Puzzle1_S_P, + TR2Type.Puzzle2_S_P, + TR2Type.Puzzle4_S_P, + TR2Type.GoldSecret_S_P, + TR2Type.JadeSecret_S_P, + TR2Type.StoneSecret_S_P, + TR2Type.Key1_S_P, + TR2Type.Key2_S_P, + TR2Type.Key3_S_P, + TR2Type.Key4_S_P, + TR2Type.Quest1_S_P, + TR2Type.Quest2_S_P, + TR2Type.ExtraFire_S_H, + TR2Type.GrayDisk_S_H, + TR2Type.Explosion_S_H, + TR2Type.WaterRipples_S_H, + TR2Type.Bubbles1_S_H, + TR2Type.Blood_S_H, + TR2Type.DartEffect_S_H, + TR2Type.Glow_S_H, + TR2Type.Ricochet_S_H, + TR2Type.XianGuardSparkles_S_H, + TR2Type.FireBlast_S_H, + TR2Type.LavaParticles_S_H, + TR2Type.Flame_S_H, + TR2Type.FontGraphics_S_H, + TR2Type.AssaultNumbers, }; #endregion diff --git a/TRDataControl/Data/TR3DataProvider.cs b/TRDataControl/Data/TR3DataProvider.cs index a511a1f1..30cb13d5 100644 --- a/TRDataControl/Data/TR3DataProvider.cs +++ b/TRDataControl/Data/TR3DataProvider.cs @@ -1,50 +1,63 @@ using TRLevelControl.Helpers; using TRLevelControl.Model; -namespace TRModelTransporter.Data; +namespace TRDataControl; -public class TR3DataProvider : IDataProvider +public class TR3DataProvider : IDataProvider { public int TextureTileLimit { get; set; } = 32; public int TextureObjectLimit { get; set; } = 4096; public Dictionary AliasPriority { get; set; } - public IEnumerable GetModelDependencies(TR3Type entity) + public TRBlobType GetBlobType(TR3Type type) { - return _entityDependencies.ContainsKey(entity) ? _entityDependencies[entity] : _emptyEntities; + if (_spriteTypes.Contains(type)) + { + return TRBlobType.Sprite; + } + if (type >= TR3Type.SceneryBase && type < TR3Type.CobraIndia) + { + return TRBlobType.StaticMesh; + } + return TRBlobType.Model; } - public IEnumerable GetRemovalExclusions(TR3Type entity) + public IEnumerable GetDependencies(TR3Type type) { - return _emptyEntities; - } + if (_typeDependencies.ContainsKey(type)) + { + return _typeDependencies[type]; + } - public IEnumerable GetCyclicDependencies(TR3Type entity) - { - return _emptyEntities; + if (IsAlias(type)) + { + return GetDependencies(TranslateAlias(type)); + } + + return _emptyTypes; } - public IEnumerable GetSpriteDependencies(TR3Type entity) + public IEnumerable GetRemovalExclusions(TR3Type type) { - return _spriteDependencies.ContainsKey(entity) ? _spriteDependencies[entity] : _emptyEntities; + return _emptyTypes; } - public IEnumerable GetCinematicEntities() + public IEnumerable GetCyclicDependencies(TR3Type type) { - return _cinematicEntities; + return _emptyTypes; } - public IEnumerable GetLaraDependants() + public IEnumerable GetCinematicTypes() { - return _laraDependentModels; + return _emptyTypes; } - public bool IsAlias(TR3Type entity) + public bool IsAlias(TR3Type type) { - foreach (List aliases in _entityAliases.Values) + foreach (List aliases in _typeAliases.Values) { - if (aliases.Contains(entity)) + if (aliases.Contains(type)) { return true; } @@ -53,191 +66,172 @@ public bool IsAlias(TR3Type entity) return false; } - public bool HasAliases(TR3Type entity) + public bool HasAliases(TR3Type type) { - return _entityAliases.ContainsKey(entity); + return _typeAliases.ContainsKey(type); } - public TR3Type TranslateAlias(TR3Type entity) + public TR3Type TranslateAlias(TR3Type type) { - foreach (TR3Type root in _entityAliases.Keys) + foreach (TR3Type root in _typeAliases.Keys) { - if (_entityAliases[root].Contains(entity)) + if (_typeAliases[root].Contains(type)) { return root; } } - return entity; + return type; } - public IEnumerable GetAliases(TR3Type entity) + public IEnumerable GetAliases(TR3Type type) { - return _entityAliases.ContainsKey(entity) ? _entityAliases[entity] : _emptyEntities; + return _typeAliases.ContainsKey(type) ? _typeAliases[type] : _emptyTypes; } - public TR3Type GetLevelAlias(string level, TR3Type entity) + public TR3Type GetLevelAlias(string level, TR3Type type) { - return TR3TypeUtilities.GetAliasForLevel(level, entity); + return TR3TypeUtilities.GetAliasForLevel(level, type); } - public bool IsAliasDuplicatePermitted(TR3Type entity) + public bool IsAliasDuplicatePermitted(TR3Type type) { - return _permittedAliasDuplicates.Contains(entity); + return _permittedAliasDuplicates.Contains(type); } - public bool IsOverridePermitted(TR3Type entity) + public bool IsOverridePermitted(TR3Type type) { - return _permittedOverrides.Contains(entity); + return _permittedOverrides.Contains(type); } - public IEnumerable GetUnsafeModelReplacements() + public bool IsNonGraphicsDependency(TR3Type type) { - return _unsafeModelReplacements; + return _nonGraphicsDependencies.Contains(type); } - public bool IsNonGraphicsDependency(TR3Type entity) + public IEnumerable GetHardcodedSounds(TR3Type type) { - return _nonGraphicsDependencies.Contains(entity); - } - - public bool IsSoundOnlyDependency(TR3Type entity) - { - return _soundOnlyDependencies.Contains(entity); - } - - public short[] GetHardcodedSounds(TR3Type entity) - { - return _hardcodedSoundIndices.ContainsKey(entity) ? _hardcodedSoundIndices[entity] : null; - } - - public IEnumerable GetIgnorableTextureIndices(TR3Type entity, string level) - { - return _ignoreEntityTextures.ContainsKey(entity) ? _ignoreEntityTextures[entity] : null; + return _hardcodedSFX.ContainsKey(type) + ? _hardcodedSFX[type] + : _emptySFX; } #region Data - private static readonly IEnumerable _emptyEntities = new List(); + private static readonly List _emptyTypes = new(); + private static readonly List _emptySFX = new(); - private static readonly Dictionary _entityDependencies = new() + private static readonly Dictionary> _typeDependencies = new() { [TR3Type.LaraIndia] - = new TR3Type[] { TR3Type.LaraSkin_H_India, TR3Type.LaraPistolAnimation_H_India, TR3Type.LaraDeagleAnimation_H_India, TR3Type.LaraUziAnimation_H_India }, + = new() { TR3Type.LaraSkin_H_India, TR3Type.LaraPistolAnimation_H_India, TR3Type.LaraDeagleAnimation_H_India, TR3Type.LaraUziAnimation_H_India }, [TR3Type.LaraCoastal] - = new TR3Type[] { TR3Type.LaraSkin_H_Coastal, TR3Type.LaraPistolAnimation_H_Coastal, TR3Type.LaraDeagleAnimation_H_Coastal, TR3Type.LaraUziAnimation_H_Coastal }, + = new() { TR3Type.LaraSkin_H_Coastal, TR3Type.LaraPistolAnimation_H_Coastal, TR3Type.LaraDeagleAnimation_H_Coastal, TR3Type.LaraUziAnimation_H_Coastal }, [TR3Type.LaraLondon] - = new TR3Type[] { TR3Type.LaraSkin_H_London, TR3Type.LaraPistolAnimation_H_London, TR3Type.LaraDeagleAnimation_H_London, TR3Type.LaraUziAnimation_H_London }, + = new() { TR3Type.LaraSkin_H_London, TR3Type.LaraPistolAnimation_H_London, TR3Type.LaraDeagleAnimation_H_London, TR3Type.LaraUziAnimation_H_London }, [TR3Type.LaraNevada] - = new TR3Type[] { TR3Type.LaraSkin_H_Nevada, TR3Type.LaraPistolAnimation_H_Nevada, TR3Type.LaraDeagleAnimation_H_Nevada, TR3Type.LaraUziAnimation_H_Nevada }, + = new() { TR3Type.LaraSkin_H_Nevada, TR3Type.LaraPistolAnimation_H_Nevada, TR3Type.LaraDeagleAnimation_H_Nevada, TR3Type.LaraUziAnimation_H_Nevada }, [TR3Type.LaraAntarc] - = new TR3Type[] { TR3Type.LaraSkin_H_Antarc, TR3Type.LaraPistolAnimation_H_Antarc, TR3Type.LaraDeagleAnimation_H_Antarc, TR3Type.LaraUziAnimation_H_Antarc }, + = new() { TR3Type.LaraSkin_H_Antarc, TR3Type.LaraPistolAnimation_H_Antarc, TR3Type.LaraDeagleAnimation_H_Antarc, TR3Type.LaraUziAnimation_H_Antarc }, [TR3Type.LaraHome] - = new TR3Type[] { TR3Type.LaraSkin_H_Home, TR3Type.LaraPistolAnimation_H_Home/*, TR3Entities.LaraDeagleAnimation_H_Home, TR3Entities.LaraUziAnimation_H_Home*/ }, + = new() { TR3Type.LaraSkin_H_Home, TR3Type.LaraPistolAnimation_H_Home }, [TR3Type.Monkey] - = new TR3Type[] { TR3Type.MonkeyMedMeshswap, TR3Type.MonkeyKeyMeshswap }, + = new() { TR3Type.MonkeyMedMeshswap, TR3Type.MonkeyKeyMeshswap }, [TR3Type.Shiva] - = new TR3Type[] { TR3Type.ShivaStatue, TR3Type.LaraExtraAnimation_H, TR3Type.Monkey }, + = new() { TR3Type.ShivaStatue, TR3Type.LaraExtraAnimation_H }, [TR3Type.Quad] - = new TR3Type[] { TR3Type.LaraVehicleAnimation_H_Quad }, + = new() { TR3Type.LaraVehicleAnimation_H_Quad }, [TR3Type.Kayak] - = new TR3Type[] { TR3Type.LaraVehicleAnimation_H_Kayak }, + = new() { TR3Type.LaraVehicleAnimation_H_Kayak }, [TR3Type.UPV] - = new TR3Type[] { TR3Type.LaraVehicleAnimation_H_UPV }, + = new() { TR3Type.LaraVehicleAnimation_H_UPV }, [TR3Type.Boat] - = new TR3Type[] { TR3Type.LaraVehicleAnimation_H_Boat }, + = new() { TR3Type.LaraVehicleAnimation_H_Boat }, [TR3Type.Tyrannosaur] - = new TR3Type[] { TR3Type.LaraExtraAnimation_H }, + = new() { TR3Type.LaraExtraAnimation_H }, [TR3Type.Willie] - = new TR3Type[] { TR3Type.LaraExtraAnimation_H, TR3Type.AIPath_N }, + = new() { TR3Type.LaraExtraAnimation_H, TR3Type.AIPath_N }, [TR3Type.Infada_P] - = new TR3Type[] { TR3Type.Infada_M_H }, + = new() { TR3Type.Infada_M_H }, [TR3Type.OraDagger_P] - = new TR3Type[] { TR3Type.OraDagger_M_H }, + = new() { TR3Type.OraDagger_M_H }, [TR3Type.EyeOfIsis_P] - = new TR3Type[] { TR3Type.EyeOfIsis_M_H }, + = new() { TR3Type.EyeOfIsis_M_H }, [TR3Type.Element115_P] - = new TR3Type[] { TR3Type.Element115_M_H }, + = new() { TR3Type.Element115_M_H }, [TR3Type.Quest1_P] - = new TR3Type[] { TR3Type.Quest1_M_H }, + = new() { TR3Type.Quest1_M_H }, [TR3Type.Quest2_P] - = new TR3Type[] { TR3Type.Quest2_M_H }, - - [TR3Type.MP5_P] - = new TR3Type[] { TR3Type.GunflareMP5_H }, - [TR3Type.RocketLauncher_P] - = new TR3Type[] { TR3Type.RocketSingle }, - [TR3Type.GrenadeLauncher_P] - = new TR3Type[] { TR3Type.GrenadeSingle }, - [TR3Type.Harpoon_P] - = new TR3Type[] { TR3Type.HarpoonSingle2 } - }; - - private static readonly Dictionary> _spriteDependencies = new() - { - - }; - - private static readonly List _cinematicEntities = new() - { - - }; - - private static readonly List _laraDependentModels = new() - { - + = new() { TR3Type.Quest2_M_H }, + + [TR3Type.Pistols_P] = + new() { TR3Type.LaraPistolAnimation_H, TR3Type.Gunflare_H, TR3Type.Pistols_M_H, TR3Type.PistolAmmo_P, TR3Type.PistolAmmo_M_H }, + [TR3Type.Shotgun_P] = + new() { TR3Type.LaraShotgunAnimation_H, TR3Type.Gunflare_H, TR3Type.Shotgun_M_H, TR3Type.ShotgunAmmo_P, TR3Type.ShotgunAmmo_M_H }, + [TR3Type.Deagle_P] = + new() { TR3Type.LaraDeagleAnimation_H, TR3Type.Gunflare_H, TR3Type.Deagle_M_H, TR3Type.DeagleAmmo_P, TR3Type.DeagleAmmo_M_H }, + [TR3Type.Uzis_P] = + new() { TR3Type.LaraUziAnimation_H, TR3Type.Gunflare_H, TR3Type.Uzis_M_H, TR3Type.UziAmmo_P, TR3Type.UziAmmo_M_H }, + [TR3Type.Harpoon_P] = + new() { TR3Type.LaraHarpoonAnimation_H, TR3Type.Harpoon_M_H, TR3Type.Harpoons_P, TR3Type.Harpoons_M_H, TR3Type.HarpoonSingle2 }, + [TR3Type.MP5_P] = + new() { TR3Type.LaraMP5Animation_H, TR3Type.GunflareMP5_H, TR3Type.MP5_M_H, TR3Type.MP5Ammo_P, TR3Type.MP5Ammo_M_H }, + [TR3Type.RocketLauncher_P] = + new() { TR3Type.LaraRocketAnimation_H, TR3Type.RocketLauncher_M_H, TR3Type.Rockets_P, TR3Type.Rockets_M_H, TR3Type.RocketSingle }, + [TR3Type.GrenadeLauncher_P] = + new() { TR3Type.LaraGrenadeAnimation_H, TR3Type.GrenadeLauncher_M_H, TR3Type.Grenades_P, TR3Type.Grenades_M_H, TR3Type.GrenadeSingle }, }; - private static readonly Dictionary> _entityAliases = new() + private static readonly Dictionary> _typeAliases = new() { - [TR3Type.Lara] = new List + [TR3Type.Lara] = new() { TR3Type.LaraIndia, TR3Type.LaraCoastal, TR3Type.LaraLondon, TR3Type.LaraNevada, TR3Type.LaraAntarc, TR3Type.LaraHome }, - [TR3Type.LaraSkin_H] = new List + [TR3Type.LaraSkin_H] = new() { TR3Type.LaraSkin_H_India, TR3Type.LaraSkin_H_Coastal, TR3Type.LaraSkin_H_London, TR3Type.LaraSkin_H_Nevada, TR3Type.LaraSkin_H_Antarc, TR3Type.LaraSkin_H_Home }, - [TR3Type.LaraPistolAnimation_H] = new List + [TR3Type.LaraPistolAnimation_H] = new() { TR3Type.LaraPistolAnimation_H_India, TR3Type.LaraPistolAnimation_H_Coastal, TR3Type.LaraPistolAnimation_H_London, TR3Type.LaraPistolAnimation_H_Nevada, TR3Type.LaraPistolAnimation_H_Antarc, TR3Type.LaraPistolAnimation_H_Home }, - [TR3Type.LaraDeagleAnimation_H] = new List + [TR3Type.LaraDeagleAnimation_H] = new() { TR3Type.LaraDeagleAnimation_H_India, TR3Type.LaraDeagleAnimation_H_Coastal, TR3Type.LaraDeagleAnimation_H_London, TR3Type.LaraDeagleAnimation_H_Nevada, TR3Type.LaraDeagleAnimation_H_Antarc, TR3Type.LaraDeagleAnimation_H_Home }, - [TR3Type.LaraUziAnimation_H] = new List + [TR3Type.LaraUziAnimation_H] = new() { TR3Type.LaraUziAnimation_H_India, TR3Type.LaraUziAnimation_H_Coastal, TR3Type.LaraUziAnimation_H_London, TR3Type.LaraUziAnimation_H_Nevada, TR3Type.LaraUziAnimation_H_Antarc, TR3Type.LaraUziAnimation_H_Home }, - [TR3Type.LaraVehicleAnimation_H] = new List + [TR3Type.LaraVehicleAnimation_H] = new() { TR3Type.LaraVehicleAnimation_H_Quad, TR3Type.LaraVehicleAnimation_H_BigGun, TR3Type.LaraVehicleAnimation_H_Kayak, TR3Type.LaraVehicleAnimation_H_UPV, TR3Type.LaraVehicleAnimation_H_Boat }, - [TR3Type.Cobra] = new List + [TR3Type.Cobra] = new() { TR3Type.CobraIndia, TR3Type.CobraNevada }, - [TR3Type.Dog] = new List + [TR3Type.Dog] = new() { TR3Type.DogLondon, TR3Type.DogNevada } @@ -253,118 +247,106 @@ public IEnumerable GetIgnorableTextureIndices(TR3Type entity, string level) TR3Type.Infada_M_H, TR3Type.EyeOfIsis_M_H, TR3Type.OraDagger_M_H, TR3Type.Element115_M_H }; - private static readonly List _unsafeModelReplacements = new() - { - TR3Type.Lara, TR3Type.LaraSkin_H, TR3Type.LaraPistolAnimation_H, TR3Type.LaraUziAnimation_H, TR3Type.LaraDeagleAnimation_H - }; - private static readonly List _nonGraphicsDependencies = new() { TR3Type.Monkey }; - // If these are imported into levels that already have another alias for them, only their hardcoded sounds will be imported - protected static readonly List _soundOnlyDependencies = new() + private static readonly Dictionary> _hardcodedSFX = new() { - - }; - - private static readonly Dictionary _hardcodedSoundIndices = new() - { - [TR3Type.Quad] = new short[] + [TR3Type.Quad] = new() { - 152, // Starting - 153, // Idling - 154, // Switch off 1 - 155, // High RPM, - 156 // Switch off 2 + TR3SFX.QuadStart, + TR3SFX.QuadIdle, + TR3SFX.QuadAccelerate, + TR3SFX.QuadMove, + TR3SFX.QuadStop, }, - [TR3Type.TonyFirehands] = new short[] + [TR3Type.TonyFirehands] = new() { - 76, // Powering down - 234, // Dying - 235, // Dying - 236, // Laughing - 366, // Fireball1 - 367, // Fireball2 - 368 // Fireball3 + TR3SFX.BlastCircle, + TR3SFX.TonyBossStoneDeath, + TR3SFX.TonyBossNormalDeath, }, - [TR3Type.Puna] = new short[] + [TR3Type.Puna] = new() { - 359 // Hoo-uh! + TR3SFX.BlastCircle, + TR3SFX.TribossTurnChair, }, - [TR3Type.LondonMerc] = new short[] + [TR3Type.LondonMerc] = new() { - 299 // Hey/Oi! + TR3SFX.EnglishOi, }, - [TR3Type.LondonGuard] = new short[] + [TR3Type.LondonGuard] = new() { - 299, // Hey/Oi! - 305 // Gunshot + TR3SFX.EnglishOi, }, - [TR3Type.Punk] = new short[] + [TR3Type.Punk] = new() { - 299 // Hey/Oi! + TR3SFX.EnglishOi, }, - [TR3Type.UPV] = new short[] + [TR3Type.UPV] = new() { - 346, // Starting - 347, // Running - 348 // Stopping + TR3SFX.UPVLoop, + TR3SFX.UPVStart, + TR3SFX.UPVStop, }, - [TR3Type.MPWithStick] = new short[] + [TR3Type.MPWithStick] = new() { - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.MPWithGun] = new short[] + [TR3Type.MPWithGun] = new() { - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.MPWithMP5] = new short[] + [TR3Type.MPWithMP5] = new() { - 137, // Gunshot - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.DamGuard] = new short[] + [TR3Type.DamGuard] = new() { - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.Prisoner] = new short[] + [TR3Type.Prisoner] = new() { - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.RXRedBoi] = new short[] + [TR3Type.RXRedBoi] = new() { - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.RXGunLad] = new short[] + [TR3Type.RXGunLad] = new() { - 300 // Hey! + TR3SFX.AmercanHey, }, - [TR3Type.Boat] = new short[] + [TR3Type.Boat] = new() { - 194, // Starting - 195, // Idling - 196, // Accelerating - 197, // High RPM - 198, // Stopping - 199 // Hitting something + TR3SFX.BoatStart, + TR3SFX.BoatIdle, + TR3SFX.BoatAccelerate, + TR3SFX.BoatMoving, + TR3SFX.BoatStop, + TR3SFX.BoatSlowDown, }, - [TR3Type.Winston] = new short[] + [TR3Type.Winston] = new() { - 308, // Shuffle - 311, // Hit by shield - 314 // General grunt - } + TR3SFX.WinstonBrushOff, + TR3SFX.WinstonBulletTray, + TR3SFX.WinstonGetUp, + }, }; - private static readonly Dictionary> _ignoreEntityTextures = new() + private static readonly List _spriteTypes = new() { - [TR3Type.LaraVehicleAnimation_H] - = new List(), // empty list indicates to ignore everything - [TR3Type.LaraExtraAnimation_H] - = new List() + TR3Type.UIFrame_S_H, + TR3Type.ShadowSprite_S_H, + TR3Type.MiscSprites_S_H, + TR3Type.Bubble_S_H, + TR3Type.Glow_S_H, + TR3Type.Glow2_S_H, + TR3Type.FontGraphics_S_H, + TR3Type.TimerFontGraphics_S_H, }; #endregion -} +} \ No newline at end of file diff --git a/TRDataControl/Data/TR4DataProvider.cs b/TRDataControl/Data/TR4DataProvider.cs new file mode 100644 index 00000000..3cfa6606 --- /dev/null +++ b/TRDataControl/Data/TR4DataProvider.cs @@ -0,0 +1,159 @@ +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR4DataProvider : IDataProvider +{ + public int TextureTileLimit { get; set; } = short.MaxValue; + public int TextureObjectLimit { get; set; } = short.MaxValue; + + public Dictionary AliasPriority { get; set; } + + public TRBlobType GetBlobType(TR4Type type) + { + if (_spriteTypes.Contains(type)) + { + return TRBlobType.Sprite; + } + if (type >= TR4Type.SceneryBase) + { + return TRBlobType.StaticMesh; + } + return TRBlobType.Model; + } + + public IEnumerable GetDependencies(TR4Type type) + { + if (_typeDependencies.ContainsKey(type)) + { + return _typeDependencies[type]; + } + + if (IsAlias(type)) + { + return GetDependencies(TranslateAlias(type)); + } + + return _emptyTypes; + } + + public IEnumerable GetRemovalExclusions(TR4Type type) + { + return _emptyTypes; + } + + public IEnumerable GetCyclicDependencies(TR4Type type) + { + return _emptyTypes; + } + + public IEnumerable GetCinematicTypes() + { + return _emptyTypes; + } + + public bool IsAlias(TR4Type type) + { + foreach (List aliases in _typeAliases.Values) + { + if (aliases.Contains(type)) + { + return true; + } + } + + return false; + } + + public bool HasAliases(TR4Type type) + { + return _typeAliases.ContainsKey(type); + } + + public TR4Type TranslateAlias(TR4Type type) + { + foreach (TR4Type root in _typeAliases.Keys) + { + if (_typeAliases[root].Contains(type)) + { + return root; + } + } + + return type; + } + + public IEnumerable GetAliases(TR4Type type) + { + return _typeAliases.ContainsKey(type) ? _typeAliases[type] : _emptyTypes; + } + + public TR4Type GetLevelAlias(string level, TR4Type type) + { + return level switch { _ => type }; + } + + public bool IsAliasDuplicatePermitted(TR4Type type) + { + return _permittedAliasDuplicates.Contains(type); + } + + public bool IsOverridePermitted(TR4Type type) + { + return _permittedOverrides.Contains(type); + } + + public bool IsNonGraphicsDependency(TR4Type type) + { + return _nonGraphicsDependencies.Contains(type); + } + + public IEnumerable GetHardcodedSounds(TR4Type type) + { + return _hardcodedSFX.ContainsKey(type) + ? _hardcodedSFX[type] + : _emptySFX; + } + + #region Data + + private static readonly List _emptyTypes = new(); + private static readonly List _emptySFX = new(); + + private static readonly Dictionary> _typeDependencies = new() + { + + }; + + private static readonly Dictionary> _typeAliases = new() + { + + }; + + private static readonly List _permittedAliasDuplicates = new() + { + + }; + + private static readonly List _permittedOverrides = new() + { + + }; + + private static readonly List _nonGraphicsDependencies = new() + { + + }; + + private static readonly Dictionary> _hardcodedSFX = new() + { + + }; + + private static readonly List _spriteTypes = new() + { + + }; + + #endregion +} diff --git a/TRDataControl/Data/TR5DataProvider.cs b/TRDataControl/Data/TR5DataProvider.cs new file mode 100644 index 00000000..41a6b807 --- /dev/null +++ b/TRDataControl/Data/TR5DataProvider.cs @@ -0,0 +1,159 @@ +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR5DataProvider : IDataProvider +{ + public int TextureTileLimit { get; set; } = short.MaxValue; + public int TextureObjectLimit { get; set; } = short.MaxValue; + + public Dictionary AliasPriority { get; set; } + + public TRBlobType GetBlobType(TR5Type type) + { + if (_spriteTypes.Contains(type)) + { + return TRBlobType.Sprite; + } + if (type >= TR5Type.SceneryBase) + { + return TRBlobType.StaticMesh; + } + return TRBlobType.Model; + } + + public IEnumerable GetDependencies(TR5Type type) + { + if (_typeDependencies.ContainsKey(type)) + { + return _typeDependencies[type]; + } + + if (IsAlias(type)) + { + return GetDependencies(TranslateAlias(type)); + } + + return _emptyTypes; + } + + public IEnumerable GetRemovalExclusions(TR5Type type) + { + return _emptyTypes; + } + + public IEnumerable GetCyclicDependencies(TR5Type type) + { + return _emptyTypes; + } + + public IEnumerable GetCinematicTypes() + { + return _emptyTypes; + } + + public bool IsAlias(TR5Type type) + { + foreach (List aliases in _typeAliases.Values) + { + if (aliases.Contains(type)) + { + return true; + } + } + + return false; + } + + public bool HasAliases(TR5Type type) + { + return _typeAliases.ContainsKey(type); + } + + public TR5Type TranslateAlias(TR5Type type) + { + foreach (TR5Type root in _typeAliases.Keys) + { + if (_typeAliases[root].Contains(type)) + { + return root; + } + } + + return type; + } + + public IEnumerable GetAliases(TR5Type type) + { + return _typeAliases.ContainsKey(type) ? _typeAliases[type] : _emptyTypes; + } + + public TR5Type GetLevelAlias(string level, TR5Type type) + { + return level switch { _ => type }; + } + + public bool IsAliasDuplicatePermitted(TR5Type type) + { + return _permittedAliasDuplicates.Contains(type); + } + + public bool IsOverridePermitted(TR5Type type) + { + return _permittedOverrides.Contains(type); + } + + public bool IsNonGraphicsDependency(TR5Type type) + { + return _nonGraphicsDependencies.Contains(type); + } + + public IEnumerable GetHardcodedSounds(TR5Type type) + { + return _hardcodedSFX.ContainsKey(type) + ? _hardcodedSFX[type] + : _emptySFX; + } + + #region Data + + private static readonly List _emptyTypes = new(); + private static readonly List _emptySFX = new(); + + private static readonly Dictionary> _typeDependencies = new() + { + + }; + + private static readonly Dictionary> _typeAliases = new() + { + + }; + + private static readonly List _permittedAliasDuplicates = new() + { + + }; + + private static readonly List _permittedOverrides = new() + { + + }; + + private static readonly List _nonGraphicsDependencies = new() + { + + }; + + private static readonly Dictionary> _hardcodedSFX = new() + { + + }; + + private static readonly List _spriteTypes = new() + { + + }; + + #endregion +} diff --git a/TRDataControl/Environment/Model/Types/Models/EMImportModelFunction.cs b/TRDataControl/Environment/Model/Types/Models/EMImportModelFunction.cs index 29a21663..09b6a9a4 100644 --- a/TRDataControl/Environment/Model/Types/Models/EMImportModelFunction.cs +++ b/TRDataControl/Environment/Model/Types/Models/EMImportModelFunction.cs @@ -1,5 +1,4 @@ using TRLevelControl.Model; -using TRModelTransporter.Transport; namespace TRDataControl.Environment; @@ -14,7 +13,7 @@ public override void ApplyToLevel(TR1Level level) TR1DataImporter importer = new(Tags?.Contains(EMTag.CommunityPatchOnly) ?? false) { Level = level, - EntitiesToImport = Models.Select(m => (TR1Type)m), + TypesToImport = new(Models.Select(m => (TR1Type)m)), DataFolder = @"Resources\TR1\Models", ForceCinematicOverwrite = ForceCinematicOverwrite }; @@ -28,7 +27,7 @@ public override void ApplyToLevel(TR2Level level) TR2DataImporter importer = new() { Level = level, - EntitiesToImport = Models.Select(m => (TR2Type)m), + TypesToImport = new(Models.Select(m => (TR2Type)m)), DataFolder = @"Resources\TR2\Models", ForceCinematicOverwrite = ForceCinematicOverwrite }; @@ -42,7 +41,7 @@ public override void ApplyToLevel(TR3Level level) TR3DataImporter importer = new() { Level = level, - EntitiesToImport = Models.Select(m => (TR3Type)m), + TypesToImport = new(Models.Select(m => (TR3Type)m)), DataFolder = @"Resources\TR3\Models", ForceCinematicOverwrite = ForceCinematicOverwrite }; diff --git a/TRDataControl/Environment/Model/Types/Models/EMImportNonGraphicsModelFunction.cs b/TRDataControl/Environment/Model/Types/Models/EMImportNonGraphicsModelFunction.cs index 8a723527..ad1797ce 100644 --- a/TRDataControl/Environment/Model/Types/Models/EMImportNonGraphicsModelFunction.cs +++ b/TRDataControl/Environment/Model/Types/Models/EMImportNonGraphicsModelFunction.cs @@ -1,5 +1,4 @@ using TRLevelControl.Model; -using TRModelTransporter.Transport; namespace TRDataControl.Environment; @@ -19,7 +18,7 @@ public override void ApplyToLevel(TR1Level level) { Level = level, ClearUnusedSprites = false, - EntitiesToImport = data.Select(m => (TR1Type)m.ModelID), + TypesToImport = new(data.Select(m => (TR1Type)m.ModelID)), DataFolder = @"Resources\TR1\Models", IgnoreGraphics = true }; @@ -40,7 +39,7 @@ public override void ApplyToLevel(TR2Level level) { Level = level, ClearUnusedSprites = false, - EntitiesToImport = data.Select(m => (TR2Type)m.ModelID), + TypesToImport = new(data.Select(m => (TR2Type)m.ModelID)), DataFolder = @"Resources\TR2\Models", IgnoreGraphics = true }; @@ -61,7 +60,7 @@ public override void ApplyToLevel(TR3Level level) { Level = level, ClearUnusedSprites = false, - EntitiesToImport = data.Select(m => (TR3Type)m.ModelID), + TypesToImport = new(data.Select(m => (TR3Type)m.ModelID)), DataFolder = @"Resources\TR3\Models", IgnoreGraphics = true }; diff --git a/TRDataControl/Handlers/CinematicTransportHandler.cs b/TRDataControl/Handlers/CinematicTransportHandler.cs deleted file mode 100644 index 5b54eaad..00000000 --- a/TRDataControl/Handlers/CinematicTransportHandler.cs +++ /dev/null @@ -1,65 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class CinematicTransportHandler -{ - public static void Export(TR1Level level, TR1Blob definition, IEnumerable entityTypes) - { - List frames = new(); - if (entityTypes != null && entityTypes.Contains(definition.Entity)) - { - frames.AddRange(level.CinematicFrames); - } - - definition.CinematicFrames = frames.ToArray(); - } - - public static void Export(TR2Level level, TR2Blob definition, IEnumerable entityTypes) - { - List frames = new(); - if (entityTypes != null && entityTypes.Contains(definition.Entity)) - { - frames.AddRange(level.CinematicFrames); - } - - definition.CinematicFrames = frames.ToArray(); - } - - public static void Export(TR3Level level, TR3Blob definition, IEnumerable entityTypes) - { - List frames = new(); - if (entityTypes != null && entityTypes.Contains(definition.Entity)) - { - frames.AddRange(level.CinematicFrames); - } - - definition.CinematicFrames = frames.ToArray(); - } - - public static void Import(TR1Level level, TR1Blob definition, bool forceOverwrite) - { - // We only import frames if the level doesn't have any already. - if (level.CinematicFrames.Count == 0 || forceOverwrite) - { - level.CinematicFrames = new(definition.CinematicFrames); - } - } - - public static void Import(TR2Level level, TR2Blob definition, bool forceOverwrite) - { - if (level.CinematicFrames.Count == 0 || forceOverwrite) - { - level.CinematicFrames = new(definition.CinematicFrames); - } - } - - public static void Import(TR3Level level, TR3Blob definition, bool forceOverwrite) - { - if (level.CinematicFrames.Count == 0 || forceOverwrite) - { - level.CinematicFrames = new(definition.CinematicFrames); - } - } -} diff --git a/TRDataControl/Handlers/ColourTransportHandler.cs b/TRDataControl/Handlers/ColourTransportHandler.cs deleted file mode 100644 index d84cb78c..00000000 --- a/TRDataControl/Handlers/ColourTransportHandler.cs +++ /dev/null @@ -1,130 +0,0 @@ -using TRImageControl; -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class ColourTransportHandler -{ - public static void Export(TR1Level level, TR1Blob definition) - { - definition.Colours = GetUsedMeshColours(definition.Meshes, level.Palette); - } - - public static void Export(TR2Level level, TR2Blob definition) - { - definition.Colours = GetUsedMeshColours(definition.Meshes, level.Palette16); - } - - public static void Export(TR3Level level, TR3Blob definition) - { - definition.Colours = GetUsedMeshColours(definition.Meshes, level.Palette16); - } - - private static Dictionary GetUsedMeshColours(List meshes, List colours) - { - ISet colourIndices = GetAllColourIndices(meshes, false); - - Dictionary usedColours = new(); - foreach (int i in colourIndices) - { - usedColours[i] = colours[i]; - } - - return usedColours; - } - - private static Dictionary GetUsedMeshColours(List meshes, List colours) - { - ISet colourIndices = GetAllColourIndices(meshes, true); - - Dictionary usedColours = new(); - foreach (int i in colourIndices) - { - usedColours[i] = colours[i]; - } - - return usedColours; - } - - private static ISet GetAllColourIndices(List meshes, bool has16Bit) - { - ISet colourIndices = new SortedSet(); - foreach (TRMesh mesh in meshes) - { - foreach (TRMeshFace face in mesh.ColouredFaces) - { - colourIndices.Add(has16Bit ? face.Texture >> 8 : face.Texture); - } - } - - return colourIndices; - } - - public static void Import(TR1Blob definition, TRPalette8Control paletteManager) - { - Dictionary indexMap = new(); - - foreach (int paletteIndex in definition.Colours.Keys) - { - TRColour newColour = definition.Colours[paletteIndex]; - indexMap[paletteIndex] = paletteManager.GetOrAddPaletteIndex(newColour); - } - - paletteManager.WritePalletteToLevel(); - ReindexMeshTextures(definition.Meshes, indexMap, false); - } - - public static void Import(TR2Level level, TR2Blob definition) - { - Dictionary indexMap = new(); - TRPalette16Control tracker = new(level); - - foreach (int paletteIndex in definition.Colours.Keys) - { - TRColour4 newColour = definition.Colours[paletteIndex]; - indexMap[paletteIndex] = tracker.Import(newColour); - } - - ReindexMeshTextures(definition.Meshes, indexMap, true); - } - - public static void Import(TR3Level level, TR3Blob definition) - { - Dictionary indexMap = new(); - TRPalette16Control tracker = new(level); - - foreach (int paletteIndex in definition.Colours.Keys) - { - TRColour4 newColour = definition.Colours[paletteIndex]; - indexMap[paletteIndex] = tracker.Import(newColour); - } - - ReindexMeshTextures(definition.Meshes, indexMap, true); - } - - private static void ReindexMeshTextures(List meshes, Dictionary indexMap, bool has16Bit) - { - foreach (TRMesh mesh in meshes) - { - foreach (TRMeshFace face in mesh.ColouredFaces) - { - face.Texture = ReindexTexture(face.Texture, indexMap, has16Bit); - } - } - } - - private static ushort ReindexTexture(ushort value, Dictionary indexMap, bool has16Bit) - { - int p16 = value; - if (has16Bit) - { - p16 >>= 8; - } - if (indexMap.ContainsKey(p16)) - { - return (ushort)(has16Bit ? (indexMap[p16] << 8 | (value & 0xFF)) : indexMap[p16]); - } - return value; - } -} diff --git a/TRDataControl/Handlers/ModelTransportHandler.cs b/TRDataControl/Handlers/ModelTransportHandler.cs deleted file mode 100644 index 307c90eb..00000000 --- a/TRDataControl/Handlers/ModelTransportHandler.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Diagnostics; -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class ModelTransportHandler -{ - public static void Export(TR1Level level, TR1Blob definition, TR1Type entity) - { - definition.Model = level.Models[entity]; - } - - public static void Export(TR2Level level, TR2Blob definition, TR2Type entity) - { - definition.Model = level.Models[entity]; - } - - public static void Export(TR3Level level, TR3Blob definition, TR3Type entity) - { - definition.Model = level.Models[entity]; - } - - public static void Import(TR1Level level, TR1Blob definition, Dictionary aliasPriority, IEnumerable laraDependants) - { - if (!level.Models.ContainsKey(definition.Entity)) - { - level.Models[definition.Entity] = definition.Model; - } - else if (!aliasPriority.ContainsKey(definition.Entity) || aliasPriority[definition.Entity] == definition.Alias) - { - if (!definition.HasGraphics) - { - // The original mesh data may still be needed so don't overwrite - definition.Model.MeshTrees = level.Models[definition.Entity].MeshTrees; - definition.Model.Meshes = level.Models[definition.Entity].Meshes; - } - level.Models[definition.Entity] = definition.Model; - } - - if (laraDependants != null) - { - if (definition.Entity == TR1Type.Lara) - { - ReplaceLaraDependants(level.Models, definition.Model, laraDependants); - } - else if (laraDependants.Contains(definition.Entity)) - { - ReplaceLaraDependants(level.Models, level.Models[TR1Type.Lara], new TR1Type[] { definition.Entity }); - } - } - } - - public static void Import(TR2Level level, TR2Blob definition, Dictionary aliasPriority, IEnumerable laraDependants) - { - if (!level.Models.ContainsKey(definition.Entity)) - { - level.Models[definition.Entity] = definition.Model; - } - else if (!aliasPriority.ContainsKey(definition.Entity) || aliasPriority[definition.Entity] == definition.Alias) - { - // Replacement occurs for the likes of aliases taking the place of another - // e.g. WhiteTiger replacing BengalTiger in GW, or if we have a specific - // alias that should always have a higher priority than its peers. - level.Models[definition.Entity] = definition.Model; - } - - // If we have replaced Lara, we need to update models such as CameraTarget, FlameEmitter etc - // as these use Lara's hips as placeholders. This means we can avoid texture corruption in - // TRView but it's also needed for the shower cutscene in HSH. If these entities are found, - // their starting mesh and mesh tree indices are just remapped to Lara's. - if (definition.Entity == TR2Type.Lara && laraDependants != null) - { - ReplaceLaraDependants(level.Models, definition.Model, laraDependants); - } - } - - public static void Import(TR3Level level, TR3Blob definition, Dictionary aliasPriority, IEnumerable laraDependants, IEnumerable unsafeReplacements) - { - if (!level.Models.ContainsKey(definition.Entity)) - { - level.Models[definition.Entity] = definition.Model; - } - else if (!aliasPriority.ContainsKey(definition.Entity) || aliasPriority[definition.Entity] == definition.Alias) - { - if (!unsafeReplacements.Contains(definition.Entity)) - { - level.Models[definition.Entity] = definition.Model; - } - else - { - // #234 Replacing Lara entirely can cause locking issues after pressing buttons or crouching - // where she refuses to come out of her stance. TR3 seems bound to having Lara's animations start - // at 0, so because these don't change per skin, we just replace the meshes and frames here. - level.Models[definition.Entity].Meshes = definition.Model.Meshes; - level.Models[definition.Entity].MeshTrees = definition.Model.MeshTrees; - } - } - - if (definition.Entity == TR3Type.Lara && laraDependants != null) - { - ReplaceLaraDependants(level.Models, definition.Model, laraDependants); - } - } - - private static void ReplaceLaraDependants(SortedDictionary models, TRModel lara, IEnumerable entityIDs) - where T : Enum - { - foreach (T dependant in entityIDs) - { - models.TryGetValue(dependant, out TRModel dependentModel); - if (dependentModel != null) - { - Debug.Assert(dependentModel.Meshes.Count == 1); - dependentModel.MeshTrees = lara.MeshTrees; - dependentModel.Meshes = new() { lara.Meshes.First() }; - } - } - } -} diff --git a/TRDataControl/Handlers/Sound/SoundTransportHandler.cs b/TRDataControl/Handlers/Sound/SoundTransportHandler.cs deleted file mode 100644 index e02d458a..00000000 --- a/TRDataControl/Handlers/Sound/SoundTransportHandler.cs +++ /dev/null @@ -1,109 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class SoundTransportHandler -{ - public static void Export(TR1Level level, TR1Blob definition, short[] hardcodedSounds) - { - if (hardcodedSounds == null || hardcodedSounds.Length == 0) - { - return; - } - - definition.SoundEffects ??= new(); - foreach (short soundID in hardcodedSounds) - { - TR1SFX sfxID = (TR1SFX)soundID; - if (level.SoundEffects.ContainsKey(sfxID)) - { - definition.SoundEffects[sfxID] = level.SoundEffects[sfxID]; - } - } - } - - public static void Export(TR2Level level, TR2Blob definition, short[] hardcodedSounds) - { - if (hardcodedSounds == null || hardcodedSounds.Length == 0) - { - return; - } - - definition.SoundEffects ??= new(); - foreach (short soundID in hardcodedSounds) - { - TR2SFX sfxID = (TR2SFX)soundID; - if (level.SoundEffects.ContainsKey(sfxID)) - { - definition.SoundEffects[sfxID] = level.SoundEffects[sfxID]; - } - } - } - - public static void Export(TR3Level level, TR3Blob definition, short[] hardcodedSounds) - { - if (hardcodedSounds == null || hardcodedSounds.Length == 0) - { - return; - } - - definition.SoundEffects ??= new(); - foreach (short soundID in hardcodedSounds) - { - TR3SFX sfxID = (TR3SFX)soundID; - if (level.SoundEffects.ContainsKey(sfxID)) - { - definition.SoundEffects[sfxID] = level.SoundEffects[sfxID]; - } - } - } - - public static void Import(TR1Level level, IEnumerable definitions) - { - foreach (TR1Blob definition in definitions) - { - if (definition.SoundEffects == null) - { - continue; - } - - foreach (TR1SFX sfxID in definition.SoundEffects.Keys) - { - level.SoundEffects[sfxID] = definition.SoundEffects[sfxID]; - } - } - } - - public static void Import(TR2Level level, IEnumerable definitions) - { - foreach (TR2Blob definition in definitions) - { - if (definition.SoundEffects == null) - { - continue; - } - - foreach (TR2SFX sfxID in definition.SoundEffects.Keys) - { - level.SoundEffects[sfxID] = definition.SoundEffects[sfxID]; - } - } - } - - public static void Import(TR3Level level, IEnumerable definitions) - { - foreach (TR3Blob definition in definitions) - { - if (definition.SoundEffects == null) - { - continue; - } - - foreach (TR3SFX sfxID in definition.SoundEffects.Keys) - { - level.SoundEffects[sfxID] = definition.SoundEffects[sfxID]; - } - } - } -} diff --git a/TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs b/TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs deleted file mode 100644 index 5917c057..00000000 --- a/TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs +++ /dev/null @@ -1,147 +0,0 @@ -using TRImageControl.Packing; -using TRLevelControl.Model; -using TRModelTransporter.Events; -using TRModelTransporter.Model; -using TRModelTransporter.Utilities; - -namespace TRModelTransporter.Handlers; - -public abstract class AbstractTextureExportHandler - where E : Enum - where L : TRLevelBase - where D : TRBlobBase -{ - protected const int _exportBitmapWidth = 320; - protected const int _exportBitmapHeight = 640; - - protected L _level; - protected D _definition; - protected IEnumerable _spriteDependencies; - protected IEnumerable _ignoreableTextureIndices; - - protected TRTexturePacker _packer; - - protected List _allSegments; - public event EventHandler SegmentExported; - public event EventHandler SegmentRemapped; - - public void Export(L level, D definition, IEnumerable spriteDependencies, IEnumerable ignoreableTextureIndices) - { - _level = level; - _definition = definition; - _spriteDependencies = spriteDependencies; - _ignoreableTextureIndices = ignoreableTextureIndices; - - _allSegments = new List(); - - //_packer = CreatePacker(); - //CollateSegments(); - //ExportSegments(); - } - - //protected abstract TRTexturePacker CreatePacker(); - - //protected abstract TRSpriteSequence GetSprite(E entity); - - //protected virtual void CollateSegments() - //{ - // Dictionary> textureSegments = _packer.GetModelSegments(_definition.Entity); - - // TRTextureDeduplicator deduplicator = new() - // { - // SegmentMap = textureSegments, - // UpdateGraphics = false, - // SegmentRemapped = SegmentRemapped - // }; - // deduplicator.Deduplicate(); - - // _definition.ObjectTextures = new(); - // _definition.SpriteSequences = new(); - // _definition.SpriteTextures = new(); - - // int bitmapIndex = 0; - // foreach (List segments in textureSegments.Values) - // { - // for (int i = 0; i < segments.Count; i++) - // { - // TRTextileRegion segment = segments[i]; - // if (!deduplicator.ShouldIgnoreSegment(_ignoreableTextureIndices, segment)) - // { - // _allSegments.Add(segment); - // _definition.ObjectTextures[bitmapIndex++] = segment.Textures.Cast().ToArray(); - // } - // } - // } - - // foreach (E spriteEntity in _spriteDependencies) - // { - // TRSpriteSequence sequence = GetSprite(spriteEntity); - // if (sequence != null) - // { - // _definition.SpriteSequences[spriteEntity] = sequence; - // } - - // Dictionary> spriteSegments = _packer.GetSpriteSegments(spriteEntity); - // _definition.SpriteTextures[spriteEntity] = new Dictionary>(); - // foreach (List segments in spriteSegments.Values) - // { - // for (int i = 0; i < segments.Count; i++) - // { - // TRTextileRegion segment = segments[i]; - // _allSegments.Add(segment); - // _definition.SpriteTextures[spriteEntity][bitmapIndex++] = new List(segment.Textures.Cast().ToArray()); - // } - // } - // } - //} - - //protected virtual void ExportSegments() - //{ - // if (_allSegments.Count == 0) - // { - // return; - // } - - // DefaultTexturePacker segmentPacker = new(); - // segmentPacker.AddRectangles(_allSegments); - - // segmentPacker.Options = new PackingOptions - // { - // FillMode = PackingFillMode.Horizontal, - // OrderMode = PackingOrderMode.Area, - // Order = PackingOrder.Descending, - // GroupMode = PackingGroupMode.Squares - // }; - // segmentPacker.TileWidth = _exportBitmapWidth; - // segmentPacker.TileHeight = _exportBitmapHeight; - // segmentPacker.MaximumTiles = 1; - - // segmentPacker.Pack(); - - // if (segmentPacker.OrphanedRectangles.Count > 0) - // { - // throw new PackingException(string.Format("Failed to export textures for {0}.", _definition.Entity)); - // } - - // TRTextile tile = segmentPacker.Tiles[0]; - // List rects = new(); - // foreach (TRTextileRegion segment in _allSegments) - // { - // rects.Add(segment.MappedBounds); - // } - - // _definition.TextureSegments = rects.ToArray(); - - // Rectangle region = tile.GetOccupiedRegion(); - // _definition.Image = tile.Image.Export(region); - - // foreach (TRTextileRegion segment in _allSegments) - // { - // SegmentExported?.Invoke(this, new SegmentEventArgs - // { - // SegmentIndex = segment.FirstTextureIndex, - // Image = segment.Image - // }); - // } - //} -} diff --git a/TRDataControl/Handlers/Textures/AbstractTextureImportHandler.cs b/TRDataControl/Handlers/Textures/AbstractTextureImportHandler.cs deleted file mode 100644 index 0cd6d519..00000000 --- a/TRDataControl/Handlers/Textures/AbstractTextureImportHandler.cs +++ /dev/null @@ -1,369 +0,0 @@ -using TRImageControl.Packing; -using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Model; - -namespace TRModelTransporter.Handlers; - -public abstract class AbstractTextureImportHandler - where E : Enum - where L : TRLevelBase - where D : TRBlobBase -{ - public IDataProvider Data { get; set; } - - protected Dictionary> _importSegments; - - protected L _level; - protected IEnumerable _definitions; - protected IEnumerable _entitiesToRemove; - protected AbstractTextureRemapGroup _textureRemap; - protected bool _clearUnusedSprites; - protected ITexturePositionMonitor _positionMonitor; - - public void Import(L level, IEnumerable definitions, IEnumerable entitiesToRemove, AbstractTextureRemapGroup textureRemap, bool clearUnusedSprites, ITexturePositionMonitor positionMonitor) - { - _level = level; - _definitions = definitions; - _entitiesToRemove = entitiesToRemove; - _textureRemap = textureRemap; - _clearUnusedSprites = clearUnusedSprites; - _positionMonitor = positionMonitor; - - // Pull together all of the texture segments for each of the definitions - //CollateSegments(); - - //// Pack the textures into the level, or bail if it wasn't possible - //PackingResult packingResult = Pack(); - //if (packingResult.OrphanCount > 0) - //{ - // List entityNames = new(); - // foreach (D def in _definitions) - // { - // entityNames.Add(def.Entity.ToString()); - // } - // throw new PackingException(string.Format - // ( - // "Failed to pack {0} rectangles for model types [{1}].", - // packingResult.OrphanCount, - // string.Join(", ", entityNames) - // )); - //} - - //// Update the level with any new ObjectTextures and update meshes accordingly - //MergeObjectTextures(); - - //// Update the level with any new SpriteTextures and SpriteSequences - //MergeSpriteTextures(); - - //// Inform the texture position monitor of the new location of tracked textures - //NotifyTextureWatcher(); - } - - //protected virtual void CollateSegments() - //{ - // // Rebuild the segment list. We assume the list of IndexedTRObjectTextures has been - // // ordered by area descending to preserve the "master" texture for each segment. - // _importSegments = new Dictionary>(); - - // // Track existing sprite sequences to avoid duplication - // List existingSequences = GetExistingSpriteSequences().Keys.ToList(); - // foreach (D definition in _definitions) - // { - // if (!definition.HasGraphics || definition.IsDependencyOnly) - // { - // continue; - // } - - // _importSegments[definition] = new List(); - // foreach (int segmentIndex in definition.ObjectTextures.Keys) - // { - // TRImage segmentClip = definition.Image.Export(definition.TextureSegments[segmentIndex]); - // TRTextileRegion region = null; - // foreach (IndexedTRObjectTexture texture in definition.ObjectTextures[segmentIndex]) - // { - // if (region == null) - // { - // _importSegments[definition].Add(region = new TRTextileRegion(texture, segmentClip)); - // } - // else - // { - // region.AddTexture(texture); - // } - // } - // } - - // List spriteEntities = new(definition.SpriteSequences.Keys); - // foreach (E spriteEntity in spriteEntities) - // { - // if (existingSequences.Contains(spriteEntity)) - // { - // definition.SpriteSequences.Remove(spriteEntity); - // continue; - // } - // else - // { - // // Add it to the tracking list in case we are importing 2 or more models - // // that share a sequence e.g. Dragon/Flamethrower and Flame_S_H - // existingSequences.Add(spriteEntity); - // } - - // // The sequence will be merged later when we know the sprite texture offsets. - // // For now, add the segments we need for packing. - // Dictionary> spriteTextures = definition.SpriteTextures[spriteEntity]; - // foreach (int segmentIndex in spriteTextures.Keys) - // { - // TRImage segmentClip = definition.Image.Export(definition.TextureSegments[segmentIndex]); - // TRTextileRegion segment = null; - // foreach (IndexedTRSpriteTexture texture in spriteTextures[segmentIndex]) - // { - // if (segment == null) - // { - // _importSegments[definition].Add(segment = new TRTextileRegion(texture, segmentClip)); - // } - // else - // { - // segment.AddTexture(texture); - // } - // } - // } - // } - // } - //} - - //protected virtual PackingResult Pack() - //{ - // TRTexturePacker packer = CreatePacker(); - // packer.MaximumTiles = Data.TextureTileLimit; - - // ProcessRemovals(packer); - - // List allSegments = new(); - // foreach (List segmentList in _importSegments.Values) - // { - // // We only add unique segments, so if another segment already exists, - // // remap the definition's segment to that one. Example of when this is - // // needed is importing the dragon as DragonBack duplicates a lot of - // // DragonFront, so this will greatly reduce the import cost. - // for (int i = 0; i < segmentList.Count; i++) - // { - // TRTextileRegion segment = segmentList[i]; - // int j = FindMatchingSegment(allSegments, segment); - // if (j == -1) - // { - // allSegments.Add(segment); - // } - // else - // { - // TRTextileRegion otherSegment = allSegments[j]; - // segmentList[i] = allSegments[j]; - // foreach (TRTextileSegment texture in segment.Textures) - // { - // if (!otherSegment.IsObjectTextureFor(texture.Index)) - // { - // otherSegment.AddTexture(texture); - // } - // } - // } - // } - // } - - // packer.AddRectangles(allSegments); - - // return packer.Pack(true); - //} - - //protected virtual void MergeObjectTextures() - //{ - // // Add each ObjectTexture to the level and store a map of old index to new index. - // // Make use of any invalid texture first because we are limited to 2048 entries - // List levelObjectTextures = GetExistingObjectTextures(); - // Queue reusableIndices = new(GetInvalidObjectTextureIndices()); - - // Dictionary> indexMap = new(); - // foreach (D definition in _definitions) - // { - // if (!_importSegments.ContainsKey(definition)) - // { - // continue; - // } - - // indexMap[definition] = new Dictionary(); - // foreach (TRTextileRegion segment in _importSegments[definition]) - // { - // foreach (TRTextileSegment texture in segment.Textures) - // { - // if (texture is not IndexedTRObjectTexture objTexture) // Sprites handled later - // { - // continue; - // } - - // int newIndex; - // if (reusableIndices.Count > 0) - // { - // newIndex = reusableIndices.Dequeue(); - // levelObjectTextures[newIndex] = objTexture.Texture; - // } - // else if (levelObjectTextures.Count < Data.TextureObjectLimit) - // { - // levelObjectTextures.Add(objTexture.Texture); - // newIndex = levelObjectTextures.Count - 1; - // } - // else - // { - // throw new PackingException(string.Format("Limit of {0} textures reached.", Data.TextureObjectLimit)); - // } - - // indexMap[definition][texture.Index] = newIndex; - // } - // } - // } - - // // Change the definition's meshes so that the textured rectangles and triangles point - // // to the correct object texture. - // RemapMeshTextures(indexMap); - //} - - //protected virtual void MergeSpriteTextures() - //{ - // TRDictionary levelSpriteSequences = GetExistingSpriteSequences(); - - // foreach (D definition in _definitions) - // { - // if (!_importSegments.ContainsKey(definition) || definition.SpriteSequences.Count == 0) - // { - // continue; - // } - - // foreach (E spriteEntity in definition.SpriteSequences.Keys) - // { - // TRSpriteSequence sequence = definition.SpriteSequences[spriteEntity]; - // levelSpriteSequences[spriteEntity] = new(); - - // foreach (int bitmapIndex in definition.SpriteTextures[spriteEntity].Keys) - // { - // List textures = definition.SpriteTextures[spriteEntity][bitmapIndex]; - // levelSpriteSequences[spriteEntity].Textures.AddRange(textures.Select(t => t.Texture)); - // } - // } - // } - //} - - //protected abstract TRDictionary GetExistingSpriteSequences(); - - //protected abstract TRTexturePacker CreatePacker(); - - //protected abstract void ProcessRemovals(TRTexturePacker packer); - - //protected abstract List GetExistingObjectTextures(); - - //protected abstract IEnumerable GetInvalidObjectTextureIndices(); - - //protected abstract void RemapMeshTextures(Dictionary> indexMap); - - //public abstract void ResetUnusedTextures(); - - //protected abstract IEnumerable CollateWatchedTextures(IEnumerable watchedEntities, D definition); - - //protected virtual void NotifyTextureWatcher() - //{ - // if (_positionMonitor == null) - // { - // return; - // } - - // // Notify if anything has been removed first - // if (_entitiesToRemove != null) - // { - // _positionMonitor.EntityTexturesRemoved(new List(_entitiesToRemove)); - // } - - // // If the monitor isn't interested in any new entities, skip the actual processing - // Dictionary> watchedTextures = _positionMonitor.GetMonitoredTextureIndices(); - // if (watchedTextures.Count == 0) - // { - // return; - // } - - // Dictionary> textureResults = new(); - - // foreach (D definition in _importSegments.Keys) - // { - // // Does this definition have any entities we are interested in? - // List entities = new(); - // if (watchedTextures.ContainsKey(definition.Alias)) - // { - // entities.Add(definition.Alias); - // } - - // // Allow subclasses to add to the list if required - // entities.AddRange(CollateWatchedTextures(watchedTextures.Keys, definition)); - - // if (entities.Count == 0) - // { - // continue; - // } - - // foreach (E entity in entities) - // { - // foreach (int watchedIndex in watchedTextures[entity]) - // { - // foreach (TRTextileRegion segment in _importSegments[definition]) - // { - // TRTextileSegment texture = segment.GetTexture(watchedIndex); - // if (texture == null) - // { - // continue; - // } - - // if (!textureResults.ContainsKey(entity)) - // { - // textureResults[entity] = new List(); - // } - // textureResults[entity].Add(new PositionedTexture(texture)); - // break; - // } - // } - // } - // } - - // _positionMonitor.MonitoredTexturesPositioned(textureResults); - //} - - //protected int FindMatchingSegment(List segments, TRTextileRegion segment) - //{ - // for (int i = 0; i < segments.Count; i++) - // { - // TRTextileRegion otherSegment = segments[i]; - // if - // ( - // otherSegment.FirstTextureIndex == segment.FirstTextureIndex && - // otherSegment.FirstClassification == segment.FirstClassification - // ) - // { - // return i; - // } - // } - // return -1; - //} - - //protected void RemapMeshTextures(TRMesh[] meshes, Dictionary indexMap) - //{ - // foreach (TRMesh mesh in meshes) - // { - // foreach (TRMeshFace face in mesh.TexturedFaces) - // { - // face.Texture = ConvertTextureReference(face.Texture, indexMap); - // } - // } - //} - - //protected ushort ConvertTextureReference(ushort textureReference, Dictionary indexMap) - //{ - // if (indexMap.ContainsKey(textureReference)) - // { - // return (ushort)indexMap[textureReference]; - // } - // return 0; - //} -} diff --git a/TRDataControl/Handlers/Textures/TR1/TR1TextureExportHandler.cs b/TRDataControl/Handlers/Textures/TR1/TR1TextureExportHandler.cs deleted file mode 100644 index 79c14912..00000000 --- a/TRDataControl/Handlers/Textures/TR1/TR1TextureExportHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers.Textures; - -public class TR1TextureExportHandler : AbstractTextureExportHandler -{ - //protected override TRTexturePacker CreatePacker() - //{ - // return new TR1TexturePacker(_level, _classifier); - //} - - //protected override TRSpriteSequence GetSprite(TR1Type entity) - //{ - // return _level.Sprites[entity]; - //} -} diff --git a/TRDataControl/Handlers/Textures/TR1/TR1TextureImportHandler.cs b/TRDataControl/Handlers/Textures/TR1/TR1TextureImportHandler.cs deleted file mode 100644 index a72e2ff6..00000000 --- a/TRDataControl/Handlers/Textures/TR1/TR1TextureImportHandler.cs +++ /dev/null @@ -1,113 +0,0 @@ -using TRImageControl; -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers.Textures; - -public class TR1TextureImportHandler : AbstractTextureImportHandler -{ - public TRPalette8Control PaletteManager { get; set; } - - //protected override TRDictionary GetExistingSpriteSequences() - //{ - // if (_level.Sprites[TR1Type.Explosion1_S_H]?.Textures.Count == 1) - // { - // // Allow replacing the Explosion sequence in Vilcabamba (it's there but empty, originally dynamite?) - // _level.Sprites.Remove(TR1Type.Explosion1_S_H); - // } - // return _level.Sprites; - //} - - //protected override TRTexturePacker CreatePacker() - //{ - // return new TR1TexturePacker(_level) - // { - // PaletteManager = PaletteManager - // }; - //} - - //protected override void ProcessRemovals(TRTexturePacker packer) - //{ - // List removals = new(); - // if (_clearUnusedSprites) - // { - // removals.Add(TR1Type.Map_M_U); - // } - - // if (_entitiesToRemove != null) - // { - // removals.AddRange(_entitiesToRemove); - // } - // packer.RemoveModelSegments(removals, _textureRemap); - - // if (_clearUnusedSprites) - // { - // RemoveUnusedSprites(packer); - // } - //} - - //private void RemoveUnusedSprites(TRTexturePacker packer) - //{ - // List unusedItems = new() - // { - // TR1Type.PistolAmmo_S_P, - // TR1Type.Map_M_U - // }; - - // ISet allEntities = new HashSet(); - // for (int i = 0; i < _level.Entities.Count; i++) - // { - // allEntities.Add(_level.Entities[i].TypeID); - // } - - // for (int i = unusedItems.Count - 1; i >= 0; i--) - // { - // if (allEntities.Contains(unusedItems[i])) - // { - // unusedItems.RemoveAt(i); - // } - // } - - // //packer.RemoveSpriteSegments(unusedItems); - //} - - //protected override List GetExistingObjectTextures() - //{ - // return _level.ObjectTextures; - //} - - //protected override IEnumerable GetInvalidObjectTextureIndices() - //{ - // return _level.GetInvalidObjectTextureIndices(); - //} - - //protected override void RemapMeshTextures(Dictionary> indexMap) - //{ - // foreach (TR1Blob definition in indexMap.Keys) - // { - // foreach (TRMesh mesh in definition.Meshes) - // { - // foreach (TRMeshFace face in mesh.TexturedFaces) - // { - // face.Texture = ConvertTextureReference(face.Texture, indexMap[definition]); - // } - // } - // } - //} - - //public override void ResetUnusedTextures() - //{ - // // Patch - this doesn't break the game, but it prevents the level being - // // opened in trview. Some textures will now be unused, but rather than - // // removing them and having to reindex everything that points to the - // // the object textures, we'll just reset them to atlas 0, and set all - // // coordinates to 0. - - // _level.ResetUnusedTextures(); - //} - - //protected override IEnumerable CollateWatchedTextures(IEnumerable watchedEntities, TR1Blob definition) - //{ - // return new List(); - //} -} diff --git a/TRDataControl/Handlers/Textures/TR2/TR2TextureExportHandler.cs b/TRDataControl/Handlers/Textures/TR2/TR2TextureExportHandler.cs deleted file mode 100644 index bbc7952a..00000000 --- a/TRDataControl/Handlers/Textures/TR2/TR2TextureExportHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class TR2TextureExportHandler : AbstractTextureExportHandler -{ - //protected override TRTexturePacker CreatePacker() - //{ - // return new TR2TexturePacker(_level, _classifier); - //} - - //protected override TRSpriteSequence GetSprite(TR2Type entity) - //{ - // return _level.Sprites[entity]; - //} -} diff --git a/TRDataControl/Handlers/Textures/TR2/TR2TextureImportHandler.cs b/TRDataControl/Handlers/Textures/TR2/TR2TextureImportHandler.cs deleted file mode 100644 index d4d8bcde..00000000 --- a/TRDataControl/Handlers/Textures/TR2/TR2TextureImportHandler.cs +++ /dev/null @@ -1,145 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class TR2TextureImportHandler : AbstractTextureImportHandler -{ - //protected override TRDictionary GetExistingSpriteSequences() - //{ - // return _level.Sprites; - //} - - //protected override TRTexturePacker CreatePacker() - //{ - // return new TR2TexturePacker(_level); - //} - - //protected override void ProcessRemovals(TRTexturePacker packer) - //{ - // List removals = new(); - // if (_clearUnusedSprites) - // { - // removals.Add(TR2Type.Map_M_U); - // } - - // // Marco is in Floaters by default but he isn't used. Removing the textures will break precompiled deduplication - // // so this remains unimplemented for the time being. - // //List models = _level.Models.ToList(); - // //if (models.Find(m => m.ID == (uint)TR2Entities.MarcoBartoli) != null && models.Find(m => m.ID == (uint)TR2Entities.DragonBack_H) == null) - // //{ - // // removals.Add(TR2Entities.MarcoBartoli); - // //} - - // if (_entitiesToRemove != null) - // { - // removals.AddRange(_entitiesToRemove); - // } - // packer.RemoveModelSegments(removals, _textureRemap); - - // ApplyFlamePatch(); - - // if (_clearUnusedSprites) - // { - // RemoveUnusedSprites(packer); - // } - //} - - //private void ApplyFlamePatch() - //{ - // // TextureDeduplicator will have removed the extra flame blasts present in DA and Lair (Flamethrower and Dragon). - // // We need to ensure that if these models are present in any level, that the sprite sequences for the blasts point - // // to the same as the grenade blast instead. - - // List flameEnemies = new() - // { - // TR2Type.FlamethrowerGoon, TR2Type.DragonExplosionEmitter_N - // }; - - // if - // ( - // _definitions.ToList().FindIndex(d => flameEnemies.Contains(d.Entity)) != -1 || - // _level.Models.Keys.Any(flameEnemies.Contains) - // ) - // { - // TRSpriteSequence blastSequence = _level.Sprites[TR2Type.FireBlast_S_H]; - // TRSpriteSequence explosionSequence = _level.Sprites[TR2Type.Explosion_S_H]; - - // if (blastSequence != null) - // { - // _level.Sprites[TR2Type.Explosion_S_H] = blastSequence; - // } - // } - //} - - //private void RemoveUnusedSprites(TRTexturePacker packer) - //{ - // List unusedItems = new() - // { - // TR2Type.PistolAmmo_S_P, - // TR2Type.Map_M_U, - // TR2Type.GrayDisk_S_H - // }; - - // ISet allEntities = new HashSet(); - // for (int i = 0; i < _level.Entities.Count; i++) - // { - // allEntities.Add(_level.Entities[i].TypeID); - // } - - // for (int i = unusedItems.Count - 1; i >= 0; i--) - // { - // if (unusedItems[i] != TR2Type.GrayDisk_S_H && allEntities.Contains(unusedItems[i])) - // { - // unusedItems.RemoveAt(i); - // } - // } - - // packer.RemoveSpriteSegments(unusedItems); - //} - - //protected override List GetExistingObjectTextures() - //{ - // return _level.ObjectTextures; - //} - - //protected override IEnumerable GetInvalidObjectTextureIndices() - //{ - // return _level.GetInvalidObjectTextureIndices(); - //} - - //protected override void RemapMeshTextures(Dictionary> indexMap) - //{ - // foreach (TR2Blob definition in indexMap.Keys) - // { - // foreach (TRMesh mesh in definition.Meshes) - // { - // foreach (TRMeshFace face in mesh.TexturedFaces) - // { - // face.Texture = ConvertTextureReference(face.Texture, indexMap[definition]); - // } - // } - // } - //} - - //public override void ResetUnusedTextures() - //{ - // _level.ResetUnusedTextures(); - //} - - //protected override IEnumerable CollateWatchedTextures(IEnumerable watchedEntities, TR2Blob definition) - //{ - // // Ensure the likes of the flamethrower having been imported triggers the fact that - // // the flame sprite sequence has been positioned. - // List entities = new(); - // foreach (TR2Type spriteEntity in definition.SpriteSequences.Keys) - // { - // if (watchedEntities.Contains(spriteEntity)) - // { - // entities.Add(spriteEntity); - // } - // } - - // return entities; - //} -} diff --git a/TRDataControl/Handlers/Textures/TR3/TR3TextureExportHandler.cs b/TRDataControl/Handlers/Textures/TR3/TR3TextureExportHandler.cs deleted file mode 100644 index 5e65f628..00000000 --- a/TRDataControl/Handlers/Textures/TR3/TR3TextureExportHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class TR3TextureExportHandler : AbstractTextureExportHandler -{ - //protected override TRTexturePacker CreatePacker() - //{ - // return new TR3TexturePacker(_level, _classifier); - //} - //protected override TRSpriteSequence GetSprite(TR3Type entity) - //{ - // return _level.Sprites[entity]; - //} -} diff --git a/TRDataControl/Handlers/Textures/TR3/TR3TextureImportHandler.cs b/TRDataControl/Handlers/Textures/TR3/TR3TextureImportHandler.cs deleted file mode 100644 index 78e17718..00000000 --- a/TRDataControl/Handlers/Textures/TR3/TR3TextureImportHandler.cs +++ /dev/null @@ -1,97 +0,0 @@ -using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; - -namespace TRModelTransporter.Handlers; - -public class TR3TextureImportHandler : AbstractTextureImportHandler -{ - //protected override TRDictionary GetExistingSpriteSequences() - //{ - // return _level.Sprites; - //} - - //protected override TRTexturePacker CreatePacker() - //{ - // return new TR3TexturePacker(_level); - //} - - //protected override void ProcessRemovals(TRTexturePacker packer) - //{ - // List removals = new(); - // if (_clearUnusedSprites) - // { - // removals.Add(TR3Type.Map_H); - // } - - // if (_entitiesToRemove != null) - // { - // removals.AddRange(_entitiesToRemove); - // } - // packer.RemoveModelSegments(removals, _textureRemap); - - // if (_clearUnusedSprites) - // { - // RemoveUnusedSprites(packer); - // } - //} - - //private void RemoveUnusedSprites(TRTexturePacker packer) - //{ - // List unusedItems = new() - // { - // TR3Type.PistolAmmo_M_H, - // TR3Type.Map_H, - // TR3Type.Disc_H - // }; - - // ISet allEntities = new HashSet(); - // for (int i = 0; i < _level.Entities.Count; i++) - // { - // allEntities.Add(_level.Entities[i].TypeID); - // } - - // for (int i = unusedItems.Count - 1; i >= 0; i--) - // { - // if (unusedItems[i] != TR3Type.Disc_H && allEntities.Contains(unusedItems[i])) - // { - // unusedItems.RemoveAt(i); - // } - // } - - // packer.RemoveSpriteSegments(unusedItems); - //} - - //protected override List GetExistingObjectTextures() - //{ - // return _level.ObjectTextures; - //} - - //protected override IEnumerable GetInvalidObjectTextureIndices() - //{ - // return _level.GetInvalidObjectTextureIndices(); - //} - - //protected override void RemapMeshTextures(Dictionary> indexMap) - //{ - // foreach (TR3Blob definition in indexMap.Keys) - // { - // foreach (TRMesh mesh in definition.Meshes) - // { - // foreach (TRMeshFace face in mesh.TexturedFaces) - // { - // face.Texture = ConvertTextureReference(face.Texture, indexMap[definition]); - // } - // } - // } - //} - - //public override void ResetUnusedTextures() - //{ - // _level.ResetUnusedTextures(); - //} - - //protected override IEnumerable CollateWatchedTextures(IEnumerable watchedEntities, TR3Blob definition) - //{ - // return new List(); - //} -} diff --git a/TRDataControl/Helpers/TRModelExtensions.cs b/TRDataControl/Helpers/TRModelExtensions.cs index b0894f56..2f6797d4 100644 --- a/TRDataControl/Helpers/TRModelExtensions.cs +++ b/TRDataControl/Helpers/TRModelExtensions.cs @@ -7,6 +7,8 @@ namespace TRModelTransporter.Helpers; public static class TRModelExtensions { + // This should be handled by TRTextureRemapper + public static void ResetUnusedTextures(this TR1Level level) { ResetUnusedObjectTextures(level.ObjectTextures); @@ -169,56 +171,6 @@ public static void ReindexTextures(this TR2Level level, Dictionary ind } } - public static void ReindexTextures(this TR3Level level, Dictionary indexMap, bool defaultToOriginal = true) - { - if (indexMap.Count == 0) - { - return; - } - - foreach (TRMesh mesh in level.DistinctMeshes) - { - foreach (TRMeshFace face in mesh.TexturedFaces) - { - face.Texture = ConvertTextureReference(face.Texture, indexMap, defaultToOriginal); - } - } - - foreach (TR3Room room in level.Rooms) - { - foreach (TRFace face in room.Mesh.Faces) - { - face.Texture = ConvertTextureReference(face.Texture, indexMap, defaultToOriginal); - } - } - - List textures = level.ObjectTextures.ToList(); - foreach (TRAnimatedTexture anim in level.AnimatedTextures) - { - for (int i = 0; i < anim.Textures.Count; i++) - { - anim.Textures[i] = ConvertTextureReference(anim.Textures[i], indexMap, defaultToOriginal); - } - - ushort previousIndex = anim.Textures[0]; - for (int i = 1; i < anim.Textures.Count; i++) - { - if (anim.Textures[i] == previousIndex && textures.Count < 2048) - { - textures.Add(textures[anim.Textures[i]]); - anim.Textures[i] = (ushort)(textures.Count - 1); - } - previousIndex = anim.Textures[i]; - } - } - - if (textures.Count > level.ObjectTextures.Count) - { - level.ObjectTextures.Clear(); - level.ObjectTextures.AddRange(textures); - } - } - private static ushort ConvertTextureReference(ushort textureReference, Dictionary indexMap, bool defaultToOriginal) { if (indexMap.ContainsKey(textureReference)) diff --git a/TRDataControl/Mass/TRMassExporter.cs b/TRDataControl/Mass/TRMassExporter.cs index 76787ca5..c8ea85ab 100644 --- a/TRDataControl/Mass/TRMassExporter.cs +++ b/TRDataControl/Mass/TRMassExporter.cs @@ -1,60 +1,51 @@ using TRLevelControl.Model; -using TRModelTransporter.Model; -using TRModelTransporter.Transport; -namespace TRModelTransporter.Utilities; +namespace TRDataControl.Utils; -public abstract class TRMassExporter - where E : Enum +public abstract class TRMassExporter where L : TRLevelBase - where D : TRBlobBase + where T : Enum + where S : Enum + where B : TRBlobBase { - public abstract List LevelNames { get; } - public abstract Dictionary> ExportTypes { get; } + public abstract Dictionary> Data { get; } - private TRDataExporter _exporter; - private List _processedEntities; + private TRDataExporter _exporter; + private List _processedTypes; - public void Export(string levelFileDirectory, string exportDirectory, string segmentsDirectory = null) + public void Export(string levelFileDirectory, string exportDirectory) { _exporter = CreateExporter(); - _exporter.ExportIndividualSegments = segmentsDirectory != null; - _exporter.SegmentsDataFolder = segmentsDirectory; _exporter.DataFolder = exportDirectory; - _processedEntities = new List(); + _exporter.BaseLevelDirectory = levelFileDirectory; + _processedTypes = new(); - foreach (string lvlName in LevelNames) + foreach (string level in Data.Keys) { - _exporter.LevelName = lvlName; - if (ExportTypes.ContainsKey(lvlName)) + _exporter.LevelName = level; + string levelPath = Path.Combine(levelFileDirectory, level); + foreach (T type in Data[level]) { - string levelPath = Path.Combine(levelFileDirectory, lvlName); - foreach (E entity in ExportTypes[lvlName]) - { - Export(levelPath, entity); - } + Export(levelPath, type); } } } - private void Export(string levelPath, E entity) + private void Export(string levelPath, T type) { - if (!_processedEntities.Contains(entity)) + if (_processedTypes.Contains(type)) + return; + + L level = ReadLevel(levelPath); + B blob = _exporter.Export(level, type, _exporter.Data.GetBlobType(type)); + _processedTypes.Add(type); + + foreach (T dependency in blob.Dependencies) { - // The level has to be re-read per entity because TextureTransportHandler can modify ObjectTextures - // which when shared between entities is difficult to undo. - //_exporter.TextureClassifier = new TRTextureClassifier(levelPath); - L level = ReadLevel(levelPath); - D definition = _exporter.Export(level, entity); - _processedEntities.Add(entity); - - foreach (E dependency in definition.Dependencies) - { - Export(levelPath, dependency); - } + Export(levelPath, dependency); } } - protected abstract TRDataExporter CreateExporter(); + protected abstract TRDataExporter CreateExporter(); protected abstract L ReadLevel(string path); } diff --git a/TRDataControl/Mass/Types/TR1MassExporter.cs b/TRDataControl/Mass/Types/TR1MassExporter.cs index 85cc4a57..26df1dbd 100644 --- a/TRDataControl/Mass/Types/TR1MassExporter.cs +++ b/TRDataControl/Mass/Types/TR1MassExporter.cs @@ -1,107 +1,83 @@ using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; -using TRModelTransporter.Transport; -namespace TRModelTransporter.Utilities; +namespace TRDataControl.Utils; -public class TR1MassExporter : TRMassExporter +public class TR1MassExporter : TRMassExporter { - private static readonly List _sourceLevels = TR1LevelNames.AsListWithAssault.Concat(new List - { - // https://trcustoms.org/users/854 by Leoc1995 - "LEOC.TR2" - }).ToList(); - - public override List LevelNames => _sourceLevels; - - public override Dictionary> ExportTypes => _exportModelTypes; + public override Dictionary> Data => _data; - private readonly TR1LevelControl _reader; - - public TR1MassExporter() - { - _reader = new(); - } - - protected override TRDataExporter CreateExporter() - { - return new TR1DataExporter(); - } + protected override TRDataExporter CreateExporter() + => new TR1DataExporter(); protected override TR1Level ReadLevel(string path) - { - return _reader.Read(path); - } + => new TR1LevelControl().Read(path); - private static readonly Dictionary> _exportModelTypes = new() + private static readonly Dictionary> _data = new() { - [TR1LevelNames.CAVES] = new List + [TR1LevelNames.CAVES] = new() { TR1Type.Pistols_M_H, TR1Type.Shotgun_M_H, TR1Type.Magnums_M_H, TR1Type.Uzis_M_H, TR1Type.Lara, TR1Type.Bat, TR1Type.Bear, TR1Type.Wolf, TR1Type.FallingBlock, TR1Type.DartEmitter, TR1Type.WallSwitch, TR1Type.LaraMiscAnim_H_General }, - [TR1LevelNames.VILCABAMBA] = new List + [TR1LevelNames.VILCABAMBA] = new() { TR1Type.PushBlock1, TR1Type.SwingingBlade, TR1Type.Trapdoor1, TR1Type.UnderwaterSwitch }, - [TR1LevelNames.VALLEY] = new List + [TR1LevelNames.VALLEY] = new() { TR1Type.TRex, TR1Type.Raptor, TR1Type.LaraPonytail_H_U }, - [TR1LevelNames.QUALOPEC] = new List + [TR1LevelNames.QUALOPEC] = new() { TR1Type.Mummy, TR1Type.RollingBall, TR1Type.FallingCeiling1, TR1Type.MovingBlock, TR1Type.TeethSpikes }, - [TR1LevelNames.FOLLY] = new List + [TR1LevelNames.FOLLY] = new() { TR1Type.CrocodileLand, TR1Type.CrocodileWater, TR1Type.Gorilla, TR1Type.Lion, TR1Type.Lioness, TR1Type.ThorHammerHandle, TR1Type.ThorLightning, TR1Type.DamoclesSword }, - [TR1LevelNames.COLOSSEUM] = new List - { - - }, - [TR1LevelNames.MIDAS] = new List + [TR1LevelNames.MIDAS] = new() { TR1Type.PushBlock2, TR1Type.Door7, TR1Type.FlameEmitter_N, TR1Type.MidasHand_N }, - [TR1LevelNames.CISTERN] = new List + [TR1LevelNames.CISTERN] = new() { TR1Type.RatLand, TR1Type.RatWater }, - [TR1LevelNames.TIHOCAN] = new List - { - TR1Type.CentaurStatue, TR1Type.Centaur, TR1Type.Pierre, TR1Type.ScionPiece_M_H, - TR1Type.SlammingDoor - }, - [TR1LevelNames.KHAMOON] = new List + [TR1LevelNames.KHAMOON] = new() { TR1Type.Panther }, - [TR1LevelNames.OBELISK] = new List + [TR1LevelNames.OBELISK] = new() { TR1Type.BandagedAtlantean }, - [TR1LevelNames.SANCTUARY] = new List + [TR1LevelNames.SANCTUARY] = new() { TR1Type.MeatyFlyer, TR1Type.MeatyAtlantean, TR1Type.ShootingAtlantean_N, TR1Type.Larson }, - [TR1LevelNames.MINES] = new List + [TR1LevelNames.TIHOCAN] = new() + { + // Off sequence so we get the normal meatballs from Sanctuary first + TR1Type.CentaurStatue, TR1Type.Centaur, TR1Type.Pierre, TR1Type.ScionPiece_M_H, + TR1Type.SlammingDoor + }, + [TR1LevelNames.MINES] = new() { TR1Type.CowboyOG, TR1Type.Kold, TR1Type.SkateboardKid, TR1Type.LavaEmitter_N }, - [TR1LevelNames.ATLANTIS] = new List + [TR1LevelNames.ATLANTIS] = new() { TR1Type.Doppelganger, TR1Type.AtlanteanEgg, TR1Type.AtlanteanLava }, - [TR1LevelNames.PYRAMID] = new List + [TR1LevelNames.PYRAMID] = new() { TR1Type.Adam, TR1Type.AdamEgg, TR1Type.Natla, TR1Type.Earthquake_N }, - ["LEOC.PHD"] = new List + ["LEOC.PHD"] = new() { TR1Type.CowboyHeadless } diff --git a/TRDataControl/Mass/Types/TR2MassExporter.cs b/TRDataControl/Mass/Types/TR2MassExporter.cs index 417dc55b..fefd81a9 100644 --- a/TRDataControl/Mass/Types/TR2MassExporter.cs +++ b/TRDataControl/Mass/Types/TR2MassExporter.cs @@ -1,122 +1,100 @@ using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; -using TRModelTransporter.Transport; -namespace TRModelTransporter.Utilities; +namespace TRDataControl.Utils; -public class TR2MassExporter : TRMassExporter +public class TR2MassExporter : TRMassExporter { - private static readonly List _sourceLevels = TR2LevelNames.AsListWithAssault.Concat(new List - { - // https://trcustoms.org/levels/3013 by Topixtor - "TOPIORC.TR2", - "TOPICAC.TR2", - }).ToList(); - - public override List LevelNames => _sourceLevels; - - public override Dictionary> ExportTypes => _exportModelTypes; - - private readonly TR2LevelControl _reader; + public override Dictionary> Data => _data; - public TR2MassExporter() - { - _reader = new(); - } - - protected override TRDataExporter CreateExporter() - { - return new TR2DataExporter(); - } + protected override TRDataExporter CreateExporter() + => new TR2DataExporter(); protected override TR2Level ReadLevel(string path) - { - return _reader.Read(path); - } + => new TR2LevelControl().Read(path); - private static readonly Dictionary> _exportModelTypes = new() + private static readonly Dictionary> _data = new() { - [TR2LevelNames.GW] = new List + [TR2LevelNames.GW] = new() { TR2Type.Pistols_M_H, TR2Type.Shotgun_M_H, TR2Type.Uzi_M_H, TR2Type.Autos_M_H, TR2Type.Harpoon_M_H, TR2Type.M16_M_H, TR2Type.GrenadeLauncher_M_H, TR2Type.LaraSun, TR2Type.Crow, TR2Type.Spider, TR2Type.BengalTiger, TR2Type.TRex, TR2Type.RollingBall }, - [TR2LevelNames.VENICE] = new List + [TR2LevelNames.VENICE] = new() { TR2Type.Boat, TR2Type.Doberman, TR2Type.MaskedGoon1, TR2Type.MaskedGoon2, TR2Type.MaskedGoon3, TR2Type.Rat, TR2Type.StickWieldingGoon1BodyWarmer }, - [TR2LevelNames.BARTOLI] = new List + [TR2LevelNames.BARTOLI] = new() { TR2Type.StickWieldingGoon1WhiteVest }, - [TR2LevelNames.OPERA] = new List + [TR2LevelNames.OPERA] = new() { TR2Type.ShotgunGoon }, - [TR2LevelNames.RIG] = new List + [TR2LevelNames.RIG] = new() { TR2Type.Gunman1OG, TR2Type.Gunman2, TR2Type.ScubaDiver, TR2Type.StickWieldingGoon1Bandana }, - [TR2LevelNames.DA] = new List + [TR2LevelNames.DA] = new() { TR2Type.FlamethrowerGoonOG }, - [TR2LevelNames.FATHOMS] = new List + [TR2LevelNames.FATHOMS] = new() { TR2Type.LaraUnwater, TR2Type.BarracudaUnwater, TR2Type.Shark }, - [TR2LevelNames.DORIA] = new List + [TR2LevelNames.DORIA] = new() { TR2Type.StickWieldingGoon1GreenVest, TR2Type.YellowMorayEel }, - [TR2LevelNames.LQ] = new List + [TR2LevelNames.LQ] = new() { TR2Type.BlackMorayEel, TR2Type.StickWieldingGoon2 }, - [TR2LevelNames.TIBET] = new List + [TR2LevelNames.TIBET] = new() { TR2Type.LaraSnow, TR2Type.Eagle, TR2Type.Mercenary2, TR2Type.Mercenary3, TR2Type.MercSnowmobDriver, TR2Type.SnowLeopard }, - [TR2LevelNames.MONASTERY] = new List + [TR2LevelNames.MONASTERY] = new() { TR2Type.Mercenary1, TR2Type.MonkWithKnifeStick, TR2Type.MonkWithLongStick }, - [TR2LevelNames.COT] = new List + [TR2LevelNames.COT] = new() { TR2Type.BarracudaIce, TR2Type.Yeti }, - [TR2LevelNames.CHICKEN] = new List + [TR2LevelNames.CHICKEN] = new() { TR2Type.BirdMonster, TR2Type.WhiteTiger }, - [TR2LevelNames.XIAN] = new List + [TR2LevelNames.XIAN] = new() { TR2Type.BarracudaXian, TR2Type.GiantSpider }, - [TR2LevelNames.FLOATER] = new List + [TR2LevelNames.FLOATER] = new() { TR2Type.Knifethrower, TR2Type.XianGuardSword, TR2Type.XianGuardSpear }, - [TR2LevelNames.LAIR] = new List + [TR2LevelNames.LAIR] = new() { TR2Type.MarcoBartoli }, - [TR2LevelNames.HOME] = new List + [TR2LevelNames.HOME] = new() { TR2Type.LaraHome, TR2Type.StickWieldingGoon1BlackJacket }, - [TR2LevelNames.ASSAULT] = new List + [TR2LevelNames.ASSAULT] = new() { TR2Type.Winston }, - ["TOPIORC.TR2"] = new List + ["TOPIORC.TR2"] = new() { TR2Type.FlamethrowerGoonTopixtor, TR2Type.Gunman1TopixtorORC }, - ["TOPICAC.TR2"] = new List + ["TOPICAC.TR2"] = new() { TR2Type.Gunman1TopixtorCAC } diff --git a/TRDataControl/Mass/Types/TR3MassExporter.cs b/TRDataControl/Mass/Types/TR3MassExporter.cs index 30b481d1..16d47df8 100644 --- a/TRDataControl/Mass/Types/TR3MassExporter.cs +++ b/TRDataControl/Mass/Types/TR3MassExporter.cs @@ -1,118 +1,103 @@ using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Model.Definitions; -using TRModelTransporter.Transport; -namespace TRModelTransporter.Utilities; +namespace TRDataControl.Utils; -public class TR3MassExporter : TRMassExporter +public class TR3MassExporter : TRMassExporter { - public override List LevelNames => TR3LevelNames.AsListWithAssault; + public override Dictionary> Data => _data; - public override Dictionary> ExportTypes => _exportModelTypes; - - private readonly TR3LevelControl _reader; - - public TR3MassExporter() - { - _reader = new(); - } - - protected override TRDataExporter CreateExporter() - { - return new TR3DataExporter(); - } + protected override TRDataExporter CreateExporter() + => new TR3DataExporter(); protected override TR3Level ReadLevel(string path) - { - return _reader.Read(path); - } + => new TR3LevelControl().Read(path); - private static readonly Dictionary> _exportModelTypes = new() + private static readonly Dictionary> _data = new() { - [TR3LevelNames.JUNGLE] = new List + [TR3LevelNames.JUNGLE] = new() { TR3Type.LaraIndia, TR3Type.Monkey, TR3Type.Tiger, TR3Type.Door1 }, - [TR3LevelNames.RUINS] = new List + [TR3LevelNames.RUINS] = new() { TR3Type.Shiva, TR3Type.CobraIndia }, - [TR3LevelNames.GANGES] = new List + [TR3LevelNames.GANGES] = new() { TR3Type.Quad, TR3Type.Vulture }, - [TR3LevelNames.CAVES] = new List + [TR3LevelNames.CAVES] = new() { TR3Type.TonyFirehands, TR3Type.Infada_P }, - [TR3LevelNames.COASTAL] = new List + [TR3LevelNames.COASTAL] = new() { TR3Type.LaraCoastal, TR3Type.Croc, TR3Type.TribesmanAxe, TR3Type.TribesmanDart }, - [TR3LevelNames.CRASH] = new List + [TR3LevelNames.CRASH] = new() { TR3Type.Compsognathus, TR3Type.Mercenary, TR3Type.Raptor, TR3Type.Tyrannosaur }, - [TR3LevelNames.MADUBU] = new List + [TR3LevelNames.MADUBU] = new() { TR3Type.Kayak, TR3Type.LizardMan }, - [TR3LevelNames.PUNA] = new List + [TR3LevelNames.PUNA] = new() { TR3Type.Puna, TR3Type.OraDagger_P }, - [TR3LevelNames.THAMES] = new List + [TR3LevelNames.THAMES] = new() { TR3Type.LaraLondon, TR3Type.Crow, TR3Type.LondonGuard, TR3Type.LondonMerc, TR3Type.Rat }, - [TR3LevelNames.ALDWYCH] = new List + [TR3LevelNames.ALDWYCH] = new() { TR3Type.Punk, TR3Type.DogLondon }, - [TR3LevelNames.LUDS] = new List + [TR3LevelNames.LUDS] = new() { TR3Type.ScubaSteve, TR3Type.UPV }, - [TR3LevelNames.CITY] = new List + [TR3LevelNames.CITY] = new() { TR3Type.SophiaLee, TR3Type.EyeOfIsis_P }, - [TR3LevelNames.NEVADA] = new List + [TR3LevelNames.NEVADA] = new() { TR3Type.LaraNevada, TR3Type.DamGuard, TR3Type.CobraNevada }, - [TR3LevelNames.HSC] = new List + [TR3LevelNames.HSC] = new() { TR3Type.MPWithStick, TR3Type.MPWithGun, TR3Type.Prisoner, TR3Type.DogNevada }, - [TR3LevelNames.AREA51] = new List + [TR3LevelNames.AREA51] = new() { TR3Type.KillerWhale, TR3Type.MPWithMP5, TR3Type.Element115_P }, - [TR3LevelNames.ANTARC] = new List + [TR3LevelNames.ANTARC] = new() { TR3Type.LaraAntarc, TR3Type.CrawlerMutantInCloset, TR3Type.Boat, TR3Type.RXRedBoi, TR3Type.DogAntarc } , - [TR3LevelNames.RXTECH] = new List + [TR3LevelNames.RXTECH] = new() { TR3Type.Crawler, TR3Type.RXTechFlameLad, TR3Type.BruteMutant }, - [TR3LevelNames.TINNOS] = new List + [TR3LevelNames.TINNOS] = new() { TR3Type.TinnosMonster, TR3Type.TinnosWasp, TR3Type.Door4 }, - [TR3LevelNames.WILLIE] = new List + [TR3LevelNames.WILLIE] = new() { TR3Type.Willie, TR3Type.RXGunLad }, - [TR3LevelNames.HALLOWS] = new List + [TR3LevelNames.HALLOWS] = new() { }, - [TR3LevelNames.ASSAULT] = new List + [TR3LevelNames.ASSAULT] = new() { TR3Type.LaraHome, TR3Type.Winston, TR3Type.WinstonInCamoSuit } diff --git a/TRDataControl/Mass/Types/TR4MassExporter.cs b/TRDataControl/Mass/Types/TR4MassExporter.cs new file mode 100644 index 00000000..02bbe7c4 --- /dev/null +++ b/TRDataControl/Mass/Types/TR4MassExporter.cs @@ -0,0 +1,20 @@ +using TRLevelControl; +using TRLevelControl.Model; + +namespace TRDataControl.Utils; + +public class TR4MassExporter : TRMassExporter +{ + public override Dictionary> Data => _data; + + protected override TRDataExporter CreateExporter() + => new TR4DataExporter(); + + protected override TR4Level ReadLevel(string path) + => new TR4LevelControl().Read(path); + + private static readonly Dictionary> _data = new() + { + + }; +} diff --git a/TRDataControl/Mass/Types/TR5MassExporter.cs b/TRDataControl/Mass/Types/TR5MassExporter.cs new file mode 100644 index 00000000..abb5a1bf --- /dev/null +++ b/TRDataControl/Mass/Types/TR5MassExporter.cs @@ -0,0 +1,20 @@ +using TRLevelControl; +using TRLevelControl.Model; + +namespace TRDataControl.Utils; + +public class TR5MassExporter : TRMassExporter +{ + public override Dictionary> Data => _data; + + protected override TRDataExporter CreateExporter() + => new TR5DataExporter(); + + protected override TR5Level ReadLevel(string path) + => new TR5LevelControl().Read(path); + + private static readonly Dictionary> _data = new() + { + + }; +} diff --git a/TRDataControl/Remapping/TRTextureRemapper.cs b/TRDataControl/Remapping/TRTextureRemapper.cs new file mode 100644 index 00000000..8a08883f --- /dev/null +++ b/TRDataControl/Remapping/TRTextureRemapper.cs @@ -0,0 +1,130 @@ +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl.Remapping; + +public abstract class TRTextureRemapper + where L : TRLevelBase +{ + protected L _level; + + protected abstract TRTexturePacker CreatePacker(); + public abstract List AnimatedTextures { get; } + public abstract List ObjectTextures { get; } + public abstract IEnumerable Faces { get; } + + public void Remap(L level) + { + _level = level; + TRTexturePacker packer = CreatePacker(); + + Dictionary regionToTileMap = new(); + void CacheRegion(TRTextileRegion region, int index) + { + region.GenerateID(); + regionToTileMap[region] = index; + } + + foreach (TRTextile tile in packer.Tiles) + { + foreach (TRTextileRegion region in tile.Rectangles) + { + CacheRegion(region, tile.Index); + } + } + + // Group each identical region + IEnumerable> groupedRegions = regionToTileMap + .Select(t => t.Key) + .GroupBy(t => t.ID) + .Where(r => r.Count() > 1 && r.All(t => t.Segments[0].Texture is TRObjectTexture)) + .Select(g => g.ToList()); + + // Move identical regions to the parent in each case + foreach (List regions in groupedRegions) + { + TRTextileRegion first = regions.First(); + for (int i = 1; i < regions.Count; i++) + { + TRTextileRegion region = regions[i]; + packer.Tiles[regionToTileMap[region]].Remove(region); + region.MoveTo(first.Bounds.Location, regionToTileMap[first]); + } + } + + // Update the textiles + packer.AllowEmptyPacking = true; + packer.Pack(true); + + static string Hash(TRObjectTexture t) + { + return $"A{t.Atlas}B{t.Bounds}M{t.BlendingMode}T{t.HasTriangleVertex}U{t.UVMode}"; + } + + // Group each object texture + IEnumerable> groupedTextures = ObjectTextures.GroupBy(t => Hash(t)) + .Where(g => g.Count() > 1) + .Select(g => g.ToList()); + + // Map identical object textures to the root in each case + Dictionary remap = new(); + foreach (List copies in groupedTextures) + { + int rootIndex = ObjectTextures.IndexOf(copies[0]); + for (int i = 1; i < copies.Count; i++) + { + int j = ObjectTextures.IndexOf(copies[i]); + remap[j] = rootIndex; + ObjectTextures[j] = null; + } + } + + // Update all faces + RemapTextures(remap); + + // Find every unused object texture and null it + List allIndices = Enumerable.Range(0, ObjectTextures.Count).ToList(); + List usedIndices = Faces.Select(f => (int)f.Texture).Distinct().ToList(); + foreach (int unused in allIndices.Except(usedIndices)) + { + ObjectTextures[unused] = null; + } + + // Remap all indices again after deleting the nulls + remap.Clear(); + List oldTextures = new(ObjectTextures); + ObjectTextures.RemoveAll(o => o == null); + for (int i = 0; i < oldTextures.Count; i++) + { + if (oldTextures[i] != null) + { + remap[i] = ObjectTextures.IndexOf(oldTextures[i]); + } + } + + // Final face update + RemapTextures(remap); + } + + private void RemapTextures(Dictionary remap) + { + foreach (TRFace face in Faces) + { + if (remap.ContainsKey(face.Texture)) + { + face.Texture = (ushort)remap[face.Texture]; + } + } + + foreach (TRAnimatedTexture t in AnimatedTextures) + { + for (int i = 0; i < t.Textures.Count; i++) + { + if (remap.ContainsKey(t.Textures[i])) + { + t.Textures[i] = (ushort)remap[t.Textures[i]]; + } + } + } + } +} diff --git a/TRDataControl/Remapping/Types/TR1TextureRemapper.cs b/TRDataControl/Remapping/Types/TR1TextureRemapper.cs new file mode 100644 index 00000000..b9257978 --- /dev/null +++ b/TRDataControl/Remapping/Types/TR1TextureRemapper.cs @@ -0,0 +1,21 @@ +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl.Remapping; + +public class TR1TextureRemapper : TRTextureRemapper +{ + public override List AnimatedTextures + => _level.AnimatedTextures; + + public override List ObjectTextures + => _level.ObjectTextures; + + public override IEnumerable Faces + => _level.Rooms.Select(r => r.Mesh) + .SelectMany(m => m.Faces) + .Concat(_level.DistinctMeshes.SelectMany(m => m.TexturedFaces)); + + protected override TRTexturePacker CreatePacker() + => new TR1TexturePacker(_level, 32); +} diff --git a/TRDataControl/Remapping/Types/TR2TextureRemapper.cs b/TRDataControl/Remapping/Types/TR2TextureRemapper.cs new file mode 100644 index 00000000..92dda72c --- /dev/null +++ b/TRDataControl/Remapping/Types/TR2TextureRemapper.cs @@ -0,0 +1,21 @@ +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl.Remapping; + +public class TR2TextureRemapper : TRTextureRemapper +{ + public override List AnimatedTextures + => _level.AnimatedTextures; + + public override List ObjectTextures + => _level.ObjectTextures; + + public override IEnumerable Faces + => _level.Rooms.Select(r => r.Mesh) + .SelectMany(m => m.Faces) + .Concat(_level.DistinctMeshes.SelectMany(m => m.TexturedFaces)); + + protected override TRTexturePacker CreatePacker() + => new TR2TexturePacker(_level, 32); +} diff --git a/TRDataControl/Remapping/Types/TR3TextureRemapper.cs b/TRDataControl/Remapping/Types/TR3TextureRemapper.cs new file mode 100644 index 00000000..28d2c51b --- /dev/null +++ b/TRDataControl/Remapping/Types/TR3TextureRemapper.cs @@ -0,0 +1,21 @@ +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl.Remapping; + +public class TR3TextureRemapper : TRTextureRemapper +{ + public override List AnimatedTextures + => _level.AnimatedTextures; + + public override List ObjectTextures + => _level.ObjectTextures; + + public override IEnumerable Faces + => _level.Rooms.Select(r => r.Mesh) + .SelectMany(m => m.Faces) + .Concat(_level.DistinctMeshes.SelectMany(m => m.TexturedFaces)); + + protected override TRTexturePacker CreatePacker() + => new TR3TexturePacker(_level, 32); +} diff --git a/TRDataControl/Remapping/Types/TR4TextureRemapper.cs b/TRDataControl/Remapping/Types/TR4TextureRemapper.cs new file mode 100644 index 00000000..5aa78b38 --- /dev/null +++ b/TRDataControl/Remapping/Types/TR4TextureRemapper.cs @@ -0,0 +1,21 @@ +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl.Remapping; + +public class TR4TextureRemapper : TRTextureRemapper +{ + public override List AnimatedTextures + => _level.AnimatedTextures; + + public override List ObjectTextures + => _level.ObjectTextures; + + public override IEnumerable Faces + => _level.Rooms.Select(r => r.Mesh) + .SelectMany(m => m.Faces) + .Concat(_level.DistinctMeshes.SelectMany(m => m.TexturedFaces)); + + protected override TRTexturePacker CreatePacker() + => new TR4TexturePacker(_level, TRGroupPackingMode.Object, 32); +} diff --git a/TRDataControl/Remapping/Types/TR5TextureRemapper.cs b/TRDataControl/Remapping/Types/TR5TextureRemapper.cs new file mode 100644 index 00000000..8fd2c7b5 --- /dev/null +++ b/TRDataControl/Remapping/Types/TR5TextureRemapper.cs @@ -0,0 +1,21 @@ +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl.Remapping; + +public class TR5TextureRemapper : TRTextureRemapper +{ + public override List AnimatedTextures + => _level.AnimatedTextures; + + public override List ObjectTextures + => _level.ObjectTextures; + + public override IEnumerable Faces + => _level.Rooms.Select(r => r.Mesh) + .SelectMany(m => m.Faces) + .Concat(_level.DistinctMeshes.SelectMany(m => m.TexturedFaces)); + + protected override TRTexturePacker CreatePacker() + => new TR5TexturePacker(_level, TRGroupPackingMode.Object, 32); +} diff --git a/TRDataControl/Transport/TR1/TR1DataExporter.cs b/TRDataControl/Transport/TR1/TR1DataExporter.cs index 6b3c1174..1fadbfe6 100644 --- a/TRDataControl/Transport/TR1/TR1DataExporter.cs +++ b/TRDataControl/Transport/TR1/TR1DataExporter.cs @@ -1,49 +1,71 @@ -using TRLevelControl; +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Handlers.Textures; -using TRModelTransporter.Model.Definitions; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public class TR1DataExporter : TRDataExporter +public class TR1DataExporter : TRDataExporter { public TR1DataExporter() { Data = new TR1DataProvider(); } - protected override AbstractTextureExportHandler CreateTextureHandler() + protected override TR1Blob CreateBlob(TR1Level level, TR1Type id, TRBlobType blobType) { - return new TR1TextureExportHandler(); + return new() + { + Type = blobType, + ID = Data.TranslateAlias(id), + Alias = id, + Palette8 = new(), + SpriteOffsets = new(), + SoundEffects = new() + }; } - protected override TR1Blob CreateModelDefinition(TR1Level level, TR1Type modelEntity) + protected override TRTextureRemapper CreateRemapper() + => new TR1TextureRemapper(); + + protected override bool IsMasterType(TR1Type type) + => type == TR1Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR1Type.Lara].Meshes[0]; + + protected override void StoreColour(ushort index, TR1Blob blob) { - TR1Blob definition = new() - { - Alias = modelEntity - }; + blob.Palette8[index] = Level.Palette[index]; + } - if (Data.IsAlias(modelEntity)) + protected override void StoreSFX(TR1SFX sfx, TR1Blob blob) + { + if (Level.SoundEffects.ContainsKey(sfx)) { - modelEntity = Data.TranslateAlias(modelEntity); + blob.SoundEffects[sfx] = Level.SoundEffects[sfx]; } + } - ModelTransportHandler.Export(level, definition, modelEntity); - ColourTransportHandler.Export(level, definition); - _textureHandler.Export(level, definition, Data.GetSpriteDependencies(modelEntity), Data.GetIgnorableTextureIndices(modelEntity, LevelName)); - CinematicTransportHandler.Export(level, definition, Data.GetCinematicEntities()); - SoundTransportHandler.Export(level, definition, Data.GetHardcodedSounds(definition.Alias)); + protected override TRTexturePacker CreatePacker() + => new TR1TexturePacker(Level, Data.TextureTileLimit); - return definition; - } + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; - protected override void PreDefinitionCreation(TR1Level level, TR1Type modelEntity) + protected override List CinematicFrames + => Level.CinematicFrames; + + protected override void PreCreation(TR1Level level, TR1Type type, TRBlobType blobType) { - switch (modelEntity) + switch (type) { case TR1Type.Pierre: AmendPierreGunshot(level); @@ -55,130 +77,107 @@ protected override void PreDefinitionCreation(TR1Level level, TR1Type modelEntit case TR1Type.SkateboardKid: AmendSkaterBoyDeath(level); break; + case TR1Type.CowboyHeadless: + //AmendDXtre3DTextures(definition); + break; case TR1Type.Natla: AmendNatlaDeath(level); break; case TR1Type.MovingBlock: - AddMovingBlockSFX(level); + AddMovingBlockSFX(level, BaseLevelDirectory); break; } } - protected override void ModelExportReady(TR1Blob definition) + protected override void PostCreation(TR1Blob blob) { - switch (definition.Alias) + switch (blob.ID) { - case TR1Type.Kold: - if (definition.Colours.ContainsKey(123)) - { - // Incorrect orange colouring on head and hands - definition.Colours[123].Red = 28; - definition.Colours[123].Green = 18; - definition.Colours[123].Blue = 4; - } - break; case TR1Type.SkateboardKid: - if (definition.Colours.ContainsKey(182)) + if (blob.Palette8.ContainsKey(60)) { // Incorrect yellow colouring on his arm - definition.Colours[182].Red = 51; - definition.Colours[182].Green = 33; - definition.Colours[182].Blue = 22; + blob.Palette8[60].Red = 204; + blob.Palette8[60].Green = 132; + blob.Palette8[60].Blue = 88; } break; - case TR1Type.CowboyHeadless: - AmendDXtre3DTextures(definition); - break; - default: + case TR1Type.Kold: + if (blob.Palette8.ContainsKey(185)) + { + // Incorrect orange colouring on head and hands + blob.Palette8[185].Red = 112; + blob.Palette8[185].Green = 72; + blob.Palette8[185].Blue = 16; + } break; } } public static void AmendPierreGunshot(TR1Level level) { - TRModel model = level.Models[TR1Type.Pierre]; - // Get his shooting animation - TRAnimation anim = model.Animations[10]; - - // On the 2nd frame, play SFX 44 (magnums) - anim.Commands.Add(new TRSFXCommand + level.Models[TR1Type.Pierre].Animations[10].Commands.Add(new TRSFXCommand { - FrameNumber = 1, SoundID = (short)TR1SFX.LaraMagnums, + FrameNumber = 1 }); } public static void AmendPierreDeath(TR1Level level) { - TRModel model = level.Models[TR1Type.Pierre]; - // Get his death animation - TRAnimation anim = model.Animations[12]; - - // On the 61st frame, play SFX 159 (death) - anim.Commands.Add(new TRSFXCommand + level.Models[TR1Type.Pierre].Animations[12].Commands.Add(new TRSFXCommand { - FrameNumber = 60, SoundID = (short)TR1SFX.PierreDeath, + FrameNumber = 60 }); } public static void AmendLarsonDeath(TR1Level level) { - TRModel model = level.Models[TR1Type.Larson]; - // Get his death animation - TRAnimation anim = model.Animations[15]; - - // On the 2nd frame, play SFX 158 (death) - anim.Commands.Add(new TRSFXCommand + level.Models[TR1Type.Larson].Animations[15].Commands.Add(new TRSFXCommand { - FrameNumber = 1, SoundID = (short)TR1SFX.LarsonDeath, + FrameNumber = 1 }); } public static void AmendSkaterBoyDeath(TR1Level level) { - TRModel model = level.Models[TR1Type.SkateboardKid]; - // Get his death animation - TRAnimation anim = model.Animations[13]; - // Play the death sound on the 2nd frame (doesn't work on the 1st, which is OG). - (anim.Commands[2] as TRSFXCommand).FrameNumber++; + TRAnimCommand cmd = level.Models[TR1Type.SkateboardKid].Animations[13].Commands + .Find(c => c is TRSFXCommand sfx && sfx.SoundID == (short)TR1SFX.SkateKidDeath); + if (cmd is TRSFXCommand sfxCmd) + { + // OG has frame number 0, but this is skipped by the engine + sfxCmd.FrameNumber = 1; + } } public static void AmendNatlaDeath(TR1Level level) { - TRModel model = level.Models[TR1Type.Natla]; - // Get her death animation - TRAnimation anim = model.Animations[13]; - - // On the 5th frame, play SFX 160 (death) - anim.Commands.Add(new TRSFXCommand + level.Models[TR1Type.Natla].Animations[13].Commands.Add(new TRSFXCommand { - FrameNumber = 4, SoundID = (short)TR1SFX.NatlaDeath, + FrameNumber = 4 }); } - public static void AddMovingBlockSFX(TR1Level level) + public static void AddMovingBlockSFX(TR1Level level, string baseLevelDirectory) { // ToQ moving blocks are silent but we want them to scrape along the floor when they move. // Import the trapdoor closing SFX from Vilcabamba and adjust the animations accordingly. if (!level.SoundEffects.ContainsKey(TR1SFX.TrapdoorClose)) { - TR1Level vilcabamba = new TR1LevelControl().Read(TR1LevelNames.VILCABAMBA); + TR1Level vilcabamba = new TR1LevelControl().Read(Path.Combine(baseLevelDirectory, TR1LevelNames.VILCABAMBA)); level.SoundEffects[TR1SFX.TrapdoorClose] = vilcabamba.SoundEffects[TR1SFX.TrapdoorClose]; } - TRModel model = level.Models[TR1Type.MovingBlock]; for (int i = 2; i < 4; i++) { - TRAnimation anim = model.Animations[i]; - - // On the 1st frame, play SFX 162 - anim.Commands.Add(new TRSFXCommand + level.Models[TR1Type.MovingBlock].Animations[i].Commands.Add(new TRSFXCommand { SoundID = (short)TR1SFX.TrapdoorClose, + FrameNumber = 0 }); } } diff --git a/TRDataControl/Transport/TR1/TR1DataImporter.cs b/TRDataControl/Transport/TR1/TR1DataImporter.cs index b7204833..940a1ee2 100644 --- a/TRDataControl/Transport/TR1/TR1DataImporter.cs +++ b/TRDataControl/Transport/TR1/TR1DataImporter.cs @@ -1,24 +1,17 @@ -using Newtonsoft.Json; +using TRDataControl.Remapping; using TRImageControl; using TRImageControl.Packing; using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Handlers.Textures; -using TRModelTransporter.Model.Definitions; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public class TR1DataImporter : TRDataImporter +public class TR1DataImporter : TRDataImporter { - public TRPalette8Control PaletteManager { get; set; } + private TRPalette8Control _paletteControl; public TR1DataImporter(bool isCommunityPatch = false) { Data = new TR1DataProvider(); - SortModels = true; - PaletteManager = new(); - if (isCommunityPatch) { Data.TextureTileLimit = 128; @@ -26,50 +19,68 @@ public TR1DataImporter(bool isCommunityPatch = false) } } - protected override AbstractTextureImportHandler CreateTextureHandler() + protected override List GetExistingTypes() { - return new TR1TextureImportHandler(); + if (Level.Sprites[TR1Type.Explosion1_S_H]?.Textures.Count == 1) + { + // Allow replacing the Explosion sequence in Vilcabamba (it's there but empty, originally dynamite?) + Level.Sprites.Remove(TR1Type.Explosion1_S_H); + } + return new(Level.Models.Keys.Concat(Level.Sprites.Keys)); } - protected override List GetExistingModelTypes() - { - return Level.Models.Keys.ToList(); - } + protected override TRTextureRemapper CreateRemapper() + => new TR1TextureRemapper(); - protected override void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions) - { - TR1TextureRemapGroup remap = null; - if (TextureRemapPath != null) - { - remap = JsonConvert.DeserializeObject(File.ReadAllText(TextureRemapPath)); - } + protected override bool IsMasterType(TR1Type type) + => type == TR1Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR1Type.Lara].Meshes[0]; - if (!IgnoreGraphics) + protected override TRTexturePacker CreatePacker() + => new TR1TexturePacker(Level, Data.TextureTileLimit) { - PaletteManager.Level = Level; - PaletteManager.ObsoleteModels = EntitiesToRemove.Select(e => Data.TranslateAlias(e)).ToList(); + PaletteControl = _paletteControl = new() + { + Level = Level, + ObsoleteTypes = new(TypesToRemove.Select(t => Data.TranslateAlias(t))) + } + }; - (_textureHandler as TR1TextureImportHandler).PaletteManager = PaletteManager; - _textureHandler.Import(Level, standardDefinitions, EntitiesToRemove, remap, ClearUnusedSprites, TexturePositionMonitor); - } + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; - SoundTransportHandler.Import(Level, standardDefinitions.Concat(soundOnlyDefinitions)); + protected override List CinematicFrames + => Level.CinematicFrames; - Dictionary aliasPriority = Data.AliasPriority ?? new Dictionary(); + protected override List ObjectTextures + => Level.ObjectTextures; + + protected override ushort ImportColour(TR1Blob blob, ushort currentIndex) + { + return blob.Palette8.ContainsKey(currentIndex) + ? (ushort)_paletteControl.GetOrAddPaletteIndex(blob.Palette8[currentIndex]) + : currentIndex; + } - foreach (TR1Blob definition in standardDefinitions) + protected override void ImportSound(TR1Blob blob) + { + if (blob.SoundEffects == null) + return; + + foreach (TR1SFX sfx in blob.SoundEffects.Keys) { - if (!IgnoreGraphics) + if (!Level.SoundEffects.ContainsKey(sfx)) { - ColourTransportHandler.Import(definition, PaletteManager); + Level.SoundEffects[sfx] = blob.SoundEffects[sfx]; } - CinematicTransportHandler.Import(Level, definition, ForceCinematicOverwrite); - ModelTransportHandler.Import(Level, definition, aliasPriority, Data.GetLaraDependants()); - } - - if (!IgnoreGraphics) - { - //_textureHandler.ResetUnusedTextures(); } } } diff --git a/TRDataControl/Transport/TR2/TR2DataExporter.cs b/TRDataControl/Transport/TR2/TR2DataExporter.cs index 24942439..25278f37 100644 --- a/TRDataControl/Transport/TR2/TR2DataExporter.cs +++ b/TRDataControl/Transport/TR2/TR2DataExporter.cs @@ -1,60 +1,154 @@ -using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model.Definitions; +using System.Diagnostics; +using System.Drawing; +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public class TR2DataExporter : TRDataExporter +public class TR2DataExporter : TRDataExporter { public TR2DataExporter() { Data = new TR2DataProvider(); } - protected override AbstractTextureExportHandler CreateTextureHandler() + protected override TR2Blob CreateBlob(TR2Level level, TR2Type id, TRBlobType blobType) { - return new TR2TextureExportHandler(); + return new() + { + Type = blobType, + ID = Data.TranslateAlias(id), + Alias = id, + Palette16 = new(), + SpriteOffsets = new(), + SoundEffects = new() + }; } - protected override TR2Blob CreateModelDefinition(TR2Level level, TR2Type modelEntity) + protected override TRTextureRemapper CreateRemapper() + => new TR2TextureRemapper(); + + protected override bool IsMasterType(TR2Type type) + => type == TR2Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR2Type.Lara].Meshes[0]; + + protected override void StoreColour(ushort index, TR2Blob blob) { - TR2Blob definition = new() - { - Alias = modelEntity - }; + blob.Palette16[index] = Level.Palette16[index >> 8]; + } - if (Data.IsAlias(modelEntity)) + protected override void StoreSFX(TR2SFX sfx, TR2Blob blob) + { + if (Level.SoundEffects.ContainsKey(sfx)) { - modelEntity = Data.TranslateAlias(modelEntity); + blob.SoundEffects[sfx] = Level.SoundEffects[sfx]; } + } - ModelTransportHandler.Export(level, definition, modelEntity); - ColourTransportHandler.Export(level, definition); - _textureHandler.Export(level, definition, Data.GetSpriteDependencies(modelEntity), Data.GetIgnorableTextureIndices(modelEntity, LevelName)); - CinematicTransportHandler.Export(level, definition, Data.GetCinematicEntities()); - SoundTransportHandler.Export(level, definition, Data.GetHardcodedSounds(definition.Alias)); + protected override TRTexturePacker CreatePacker() + => new TR2TexturePacker(Level, Data.TextureTileLimit); - return definition; - } + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; - protected override void ModelExportReady(TR2Blob definition) + protected override List CinematicFrames + => Level.CinematicFrames; + + protected override void PostCreation(TR2Blob blob) { - switch (definition.Alias) + switch (blob.Alias) { - case TR2Type.FlamethrowerGoonTopixtor: - AmendDXtre3DTextures(definition); - //AmendDXtre3DFlameTextures(definition); + case TR2Type.DragonExplosion1_H: + case TR2Type.DragonExplosion2_H: + ScaleSphereOfDoom(blob); break; case TR2Type.Gunman1TopixtorORC: case TR2Type.Gunman1TopixtorCAC: - AmendDXtre3DTextures(definition); + //AmendDXtre3DTextures(blob); break; - default: + case TR2Type.FlamethrowerGoonTopixtor: + //AmendDXtre3DTextures(blob); + //AmendDXtre3DFlameTextures(blob); break; + } } + private static void ScaleSphereOfDoom(TR2Blob blob) + { + // Scale down the Sphere of Doom textures for a better chance of + // importing into levels later. + + Debug.Assert(blob.Textures.Count == 1); + Debug.Assert(blob.Textures[0].Width == 128); + Debug.Assert(blob.Textures[0].Height == 128); + + TRTextileRegion region = blob.Textures[0]; + Size newSize = new(64, 64); + + using Bitmap scaledBmp = new(region.Image.ToBitmap(), newSize); + + region.Image = new(scaledBmp); + region.Bounds = new(0, 0, newSize.Width, newSize.Height); + region.GenerateID(); + region.Segments.ForEach(s => s.Texture.Size = newSize); + } + + //protected void AmendDXtre3DTextures(TR2Blob definition) + //{ + // // Dxtre3D can produce faulty UV mapping which can cause casting issues + // // when used in model IO, so fix coordinates at this stage. + // // This may no longer be the case... + // foreach (List textureList in definition.ObjectTextures.Values) + // { + // foreach (IndexedTRObjectTexture texture in textureList) + // { + // Dictionary points = new(); + // foreach (TRObjectTextureVert vertex in texture.Texture.Vertices) + // { + // int x = vertex.XCoordinate.Fraction; + // if (vertex.XCoordinate.Whole == byte.MaxValue) + // { + // x++; + // } + + // int y = vertex.YCoordinate.Fraction; + // if (vertex.YCoordinate.Whole == byte.MaxValue) + // { + // y++; + // } + // points[vertex] = new Point(x, y); + // } + + // int maxX = points.Values.Max(p => p.X); + // int maxY = points.Values.Max(p => p.Y); + // foreach (TRObjectTextureVert vertex in texture.Texture.Vertices) + // { + // Point p = points[vertex]; + // if (p.X == maxX && maxX != byte.MaxValue) + // { + // vertex.XCoordinate.Fraction--; + // vertex.XCoordinate.Whole = byte.MaxValue; + // } + // if (p.Y == maxY && maxY != byte.MaxValue) + // { + // vertex.YCoordinate.Fraction--; + // vertex.YCoordinate.Whole = byte.MaxValue; + // } + // } + // } + // } + //} + //private static void AmendDXtre3DFlameTextures(TR2Blob definition) //{ // if (!definition.SpriteSequences.ContainsKey(TR2Type.Flame_S_H)) diff --git a/TRDataControl/Transport/TR2/TR2DataImporter.cs b/TRDataControl/Transport/TR2/TR2DataImporter.cs index 066ed9e4..4b7142bb 100644 --- a/TRDataControl/Transport/TR2/TR2DataImporter.cs +++ b/TRDataControl/Transport/TR2/TR2DataImporter.cs @@ -1,70 +1,95 @@ -using Newtonsoft.Json; +using TRDataControl.Remapping; +using TRImageControl; using TRImageControl.Packing; using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model.Definitions; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public class TR2DataImporter : TRDataImporter +public class TR2DataImporter : TRDataImporter { - public TR2DataImporter() + private TRPalette16Control _paletteControl; + + public TR2DataImporter(bool isCommunityPatch = false) { Data = new TR2DataProvider(); + if (isCommunityPatch) + { + Data.TextureTileLimit = 32; + Data.TextureObjectLimit = 8192; + } } - protected override AbstractTextureImportHandler CreateTextureHandler() - { - return new TR2TextureImportHandler(); - } + protected override List GetExistingTypes() + => new(Level.Models.Keys.Concat(Level.Sprites.Keys)); - protected override List GetExistingModelTypes() - { - return Level.Models.Keys.ToList(); - } + protected override TRTextureRemapper CreateRemapper() + => new TR2TextureRemapper(); - protected override void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions) - { - // Textures first, which will remap Mesh rectangles/triangles to the new texture indices. - // This is called using the entire entity list to import so that RectanglePacker packer has - // the best chance to organise the tiles. - TR2TextureRemapGroup remap = null; - if (TextureRemapPath != null) - { - remap = JsonConvert.DeserializeObject(File.ReadAllText(TextureRemapPath)); - } + protected override bool IsMasterType(TR2Type type) + => type == TR2Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR2Type.Lara].Meshes[0]; + + protected override TRTexturePacker CreatePacker() + => new TR2TexturePacker(Level, Data.TextureTileLimit); + + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; - if (!IgnoreGraphics) + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => Level.CinematicFrames; + + protected override List ObjectTextures + => Level.ObjectTextures; + + protected override ushort ImportColour(TR2Blob blob, ushort currentIndex) + { + if (!blob.Palette16.ContainsKey(currentIndex)) { - _textureHandler.Import(Level, standardDefinitions, EntitiesToRemove, remap, ClearUnusedSprites, TexturePositionMonitor); + return currentIndex; } - // Hardcoded sounds are also imported en-masse to ensure the correct SoundMap indices are assigned - // before any animation sounds are dealt with. - SoundTransportHandler.Import(Level, standardDefinitions.Concat(soundOnlyDefinitions)); + _paletteControl ??= new(Level.Palette16, Level.DistinctMeshes); + return (ushort)_paletteControl.Import(blob.Palette16[currentIndex]); + } - // Allow external alias model priorities to be defined - Dictionary aliasPriority = Data.AliasPriority ?? new Dictionary(); + protected override void ImportSound(TR2Blob blob) + { + if (blob.SoundEffects == null) + return; - foreach (TR2Blob definition in standardDefinitions) + foreach (TR2SFX sfx in blob.SoundEffects.Keys) { - if (!IgnoreGraphics) + if (!Level.SoundEffects.ContainsKey(sfx)) { - // Colours next, again to remap Mesh rectangles/triangles to any new palette indices - ColourTransportHandler.Import(Level, definition); + Level.SoundEffects[sfx] = blob.SoundEffects[sfx]; } + } + } - // Cinematic frames - CinematicTransportHandler.Import(Level, definition, ForceCinematicOverwrite); - - // Add the model, which will have the correct StartingMesh, MeshTree, Frame and Animation offset. - ModelTransportHandler.Import(Level, definition, aliasPriority, Data.GetLaraDependants()); + protected override void BlobImported(TR2Blob blob) + { + switch (blob.ID) + { + case TR2Type.FlamethrowerGoon: + case TR2Type.MarcoBartoli: + AddFlameSprites(); + break; } + } - if (!IgnoreGraphics) + private void AddFlameSprites() + { + if (!Level.Sprites.ContainsKey(TR2Type.FireBlast_S_H) && Level.Sprites.ContainsKey(TR2Type.Explosion_S_H)) { - //_textureHandler.ResetUnusedTextures(); + Level.Sprites[TR2Type.FireBlast_S_H] = Level.Sprites[TR2Type.Explosion_S_H].Clone(); } } } diff --git a/TRDataControl/Transport/TR3/TR3DataExporter.cs b/TRDataControl/Transport/TR3/TR3DataExporter.cs index e097e1d1..00e1d992 100644 --- a/TRDataControl/Transport/TR3/TR3DataExporter.cs +++ b/TRDataControl/Transport/TR3/TR3DataExporter.cs @@ -1,40 +1,63 @@ -using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model.Definitions; +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public class TR3DataExporter : TRDataExporter +public class TR3DataExporter : TRDataExporter { public TR3DataExporter() { Data = new TR3DataProvider(); } - protected override AbstractTextureExportHandler CreateTextureHandler() + protected override TR3Blob CreateBlob(TR3Level level, TR3Type id, TRBlobType blobType) { - return new TR3TextureExportHandler(); + return new() + { + Type = blobType, + ID = Data.TranslateAlias(id), + Alias = id, + Palette16 = new(), + SpriteOffsets = new(), + SoundEffects = new() + }; } - protected override TR3Blob CreateModelDefinition(TR3Level level, TR3Type modelEntity) + protected override TRTextureRemapper CreateRemapper() + => new TR3TextureRemapper(); + + protected override bool IsMasterType(TR3Type type) + => type == TR3Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR3Type.Lara].Meshes[0]; + + protected override void StoreColour(ushort index, TR3Blob blob) { - TR3Blob definition = new() - { - Alias = modelEntity - }; + blob.Palette16[index] = Level.Palette16[index >> 8]; + } - if (Data.IsAlias(modelEntity)) + protected override void StoreSFX(TR3SFX sfx, TR3Blob blob) + { + if (Level.SoundEffects.ContainsKey(sfx)) { - modelEntity = Data.TranslateAlias(modelEntity); + blob.SoundEffects[sfx] = Level.SoundEffects[sfx]; } + } - ModelTransportHandler.Export(level, definition, modelEntity); - ColourTransportHandler.Export(level, definition); - _textureHandler.Export(level, definition, Data.GetSpriteDependencies(modelEntity), Data.GetIgnorableTextureIndices(modelEntity, LevelName)); - CinematicTransportHandler.Export(level, definition, Data.GetCinematicEntities()); - SoundTransportHandler.Export(level, definition, Data.GetHardcodedSounds(definition.Alias)); + protected override TRTexturePacker CreatePacker() + => new TR3TexturePacker(Level, Data.TextureTileLimit); - return definition; - } + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => Level.CinematicFrames; } diff --git a/TRDataControl/Transport/TR3/TR3DataImporter.cs b/TRDataControl/Transport/TR3/TR3DataImporter.cs index f483a81d..31875739 100644 --- a/TRDataControl/Transport/TR3/TR3DataImporter.cs +++ b/TRDataControl/Transport/TR3/TR3DataImporter.cs @@ -1,60 +1,76 @@ -using Newtonsoft.Json; +using TRDataControl.Remapping; +using TRImageControl; using TRImageControl.Packing; using TRLevelControl.Model; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model.Definitions; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public class TR3DataImporter : TRDataImporter +public class TR3DataImporter : TRDataImporter { - public TR3DataImporter() + private TRPalette16Control _paletteControl; + + public TR3DataImporter(bool isCommunityPatch = false) { Data = new TR3DataProvider(); - SortModels = true; + if (isCommunityPatch) + { + Data.TextureTileLimit = 128; + Data.TextureObjectLimit = 16384; + } } - protected override AbstractTextureImportHandler CreateTextureHandler() - { - return new TR3TextureImportHandler(); - } + protected override List GetExistingTypes() + => new(Level.Models.Keys.Concat(Level.Sprites.Keys)); - protected override List GetExistingModelTypes() - { - return Level.Models.Keys.ToList(); - } + protected override TRTextureRemapper CreateRemapper() + => new TR3TextureRemapper(); - protected override void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions) - { - TR3TextureRemapGroup remap = null; - if (TextureRemapPath != null) - { - remap = JsonConvert.DeserializeObject(File.ReadAllText(TextureRemapPath)); - } + protected override bool IsMasterType(TR3Type type) + => type == TR3Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR3Type.Lara].Meshes[0]; + + protected override TRTexturePacker CreatePacker() + => new TR3TexturePacker(Level, Data.TextureTileLimit); - if (!IgnoreGraphics) + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => Level.CinematicFrames; + + protected override List ObjectTextures + => Level.ObjectTextures; + + protected override ushort ImportColour(TR3Blob blob, ushort currentIndex) + { + if (!blob.Palette16.ContainsKey(currentIndex)) { - _textureHandler.Import(Level, standardDefinitions, EntitiesToRemove, remap, ClearUnusedSprites, TexturePositionMonitor); + return currentIndex; } - SoundTransportHandler.Import(Level, standardDefinitions.Concat(soundOnlyDefinitions)); + _paletteControl ??= new(Level.Palette16, Level.DistinctMeshes); + return (ushort)_paletteControl.Import(blob.Palette16[currentIndex]); + } - Dictionary aliasPriority = Data.AliasPriority ?? new Dictionary(); + protected override void ImportSound(TR3Blob blob) + { + if (blob.SoundEffects == null) + return; - foreach (TR3Blob definition in standardDefinitions) + foreach (TR3SFX sfx in blob.SoundEffects.Keys) { - if (!IgnoreGraphics) + if (!Level.SoundEffects.ContainsKey(sfx)) { - ColourTransportHandler.Import(Level, definition); + Level.SoundEffects[sfx] = blob.SoundEffects[sfx]; } - CinematicTransportHandler.Import(Level, definition, ForceCinematicOverwrite); - ModelTransportHandler.Import(Level, definition, aliasPriority, Data.GetLaraDependants(), Data.GetUnsafeModelReplacements()); - } - - if (!IgnoreGraphics) - { - //_textureHandler.ResetUnusedTextures(); } } } diff --git a/TRDataControl/Transport/TR4/TR4DataExporter.cs b/TRDataControl/Transport/TR4/TR4DataExporter.cs new file mode 100644 index 00000000..a0f6b5d1 --- /dev/null +++ b/TRDataControl/Transport/TR4/TR4DataExporter.cs @@ -0,0 +1,59 @@ +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR4DataExporter : TRDataExporter +{ + public TR4DataExporter() + { + Data = new TR4DataProvider(); + } + + protected override TR4Blob CreateBlob(TR4Level level, TR4Type id, TRBlobType blobType) + { + return new() + { + Type = blobType, + ID = Data.TranslateAlias(id), + Alias = id, + SpriteOffsets = new(), + SoundEffects = new() + }; + } + + protected override TRTextureRemapper CreateRemapper() + => new TR4TextureRemapper(); + + protected override bool IsMasterType(TR4Type type) + => type == TR4Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR4Type.Lara].Meshes[0]; + + protected override void StoreColour(ushort index, TR4Blob blob) { } + + protected override void StoreSFX(TR4SFX sfx, TR4Blob blob) + { + if (Level.SoundEffects.ContainsKey(sfx)) + { + blob.SoundEffects[sfx] = Level.SoundEffects[sfx]; + } + } + + protected override TRTexturePacker CreatePacker() + => new TR4TexturePacker(Level, TRGroupPackingMode.Object, Data.TextureTileLimit); + + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => null; +} diff --git a/TRDataControl/Transport/TR4/TR4DataImporter.cs b/TRDataControl/Transport/TR4/TR4DataImporter.cs new file mode 100644 index 00000000..d21503ec --- /dev/null +++ b/TRDataControl/Transport/TR4/TR4DataImporter.cs @@ -0,0 +1,60 @@ +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR4DataImporter : TRDataImporter +{ + public TR4DataImporter() + { + Data = new TR4DataProvider(); + } + + protected override List GetExistingTypes() + => new(Level.Models.Keys.Concat(Level.Sprites.Keys)); + + protected override TRTextureRemapper CreateRemapper() + => new TR4TextureRemapper(); + + protected override bool IsMasterType(TR4Type type) + => type == TR4Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR4Type.Lara].Meshes[0]; + + protected override TRTexturePacker CreatePacker() + => new TR4TexturePacker(Level, TRGroupPackingMode.Object, Data.TextureTileLimit); + + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => null; + + protected override List ObjectTextures + => Level.ObjectTextures; + + protected override ushort ImportColour(TR4Blob blob, ushort currentIndex) + => 0; + + protected override void ImportSound(TR4Blob blob) + { + if (blob.SoundEffects == null) + return; + + foreach (TR4SFX sfx in blob.SoundEffects.Keys) + { + if (!Level.SoundEffects.ContainsKey(sfx)) + { + Level.SoundEffects[sfx] = blob.SoundEffects[sfx]; + } + } + } +} diff --git a/TRDataControl/Transport/TR5/TR5DataExporter.cs b/TRDataControl/Transport/TR5/TR5DataExporter.cs new file mode 100644 index 00000000..75403f90 --- /dev/null +++ b/TRDataControl/Transport/TR5/TR5DataExporter.cs @@ -0,0 +1,59 @@ +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR5DataExporter : TRDataExporter +{ + public TR5DataExporter() + { + Data = new TR5DataProvider(); + } + + protected override TR5Blob CreateBlob(TR5Level level, TR5Type id, TRBlobType blobType) + { + return new() + { + Type = blobType, + ID = Data.TranslateAlias(id), + Alias = id, + SpriteOffsets = new(), + SoundEffects = new() + }; + } + + protected override TRTextureRemapper CreateRemapper() + => new TR5TextureRemapper(); + + protected override bool IsMasterType(TR5Type type) + => type == TR5Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR5Type.Lara].Meshes[0]; + + protected override void StoreColour(ushort index, TR5Blob blob) { } + + protected override void StoreSFX(TR5SFX sfx, TR5Blob blob) + { + if (Level.SoundEffects.ContainsKey(sfx)) + { + blob.SoundEffects[sfx] = Level.SoundEffects[sfx]; + } + } + + protected override TRTexturePacker CreatePacker() + => new TR5TexturePacker(Level, TRGroupPackingMode.Object, Data.TextureTileLimit); + + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => null; +} diff --git a/TRDataControl/Transport/TR5/TR5DataImporter.cs b/TRDataControl/Transport/TR5/TR5DataImporter.cs new file mode 100644 index 00000000..91364b49 --- /dev/null +++ b/TRDataControl/Transport/TR5/TR5DataImporter.cs @@ -0,0 +1,60 @@ +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; + +namespace TRDataControl; + +public class TR5DataImporter : TRDataImporter +{ + public TR5DataImporter() + { + Data = new TR5DataProvider(); + } + + protected override List GetExistingTypes() + => new(Level.Models.Keys.Concat(Level.Sprites.Keys)); + + protected override TRTextureRemapper CreateRemapper() + => new TR5TextureRemapper(); + + protected override bool IsMasterType(TR5Type type) + => type == TR5Type.Lara; + + protected override TRMesh GetDummyMesh() + => Level.Models[TR5Type.Lara].Meshes[0]; + + protected override TRTexturePacker CreatePacker() + => new TR5TexturePacker(Level, TRGroupPackingMode.Object, Data.TextureTileLimit); + + protected override TRDictionary Models + => Level.Models; + + protected override TRDictionary StaticMeshes + => Level.StaticMeshes; + + protected override TRDictionary SpriteSequences + => Level.Sprites; + + protected override List CinematicFrames + => null; + + protected override List ObjectTextures + => Level.ObjectTextures; + + protected override ushort ImportColour(TR5Blob blob, ushort currentIndex) + => 0; + + protected override void ImportSound(TR5Blob blob) + { + if (blob.SoundEffects == null) + return; + + foreach (TR5SFX sfx in blob.SoundEffects.Keys) + { + if (!Level.SoundEffects.ContainsKey(sfx)) + { + Level.SoundEffects[sfx] = blob.SoundEffects[sfx]; + } + } + } +} diff --git a/TRDataControl/Transport/TRDataExporter.cs b/TRDataControl/Transport/TRDataExporter.cs index a5b8774c..a38ebf47 100644 --- a/TRDataControl/Transport/TRDataExporter.cs +++ b/TRDataControl/Transport/TRDataExporter.cs @@ -1,138 +1,166 @@ -using Newtonsoft.Json; -using System.Drawing; -using System.Drawing.Imaging; -using TRImageControl.Textures; +using System.Diagnostics; +using TRImageControl.Packing; using TRLevelControl.Model; -using TRModelTransporter.Events; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model; -using TRModelTransporter.Utilities; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public abstract class TRDataExporter : TRDataTransport - where E : Enum +public abstract class TRDataExporter : TRDataTransport where L : TRLevelBase - where D : TRBlobBase + where T : Enum + where S : Enum + where B : TRBlobBase { - protected static readonly string _defaultSegmentsFolder = @"Resources\ModelSegments"; + public string BaseLevelDirectory { get; set; } - public bool ExportIndividualSegments { get; set; } - public string SegmentsDataFolder { get; set; } + public B Export(L level, T type, TRBlobType blobType) + { + Level = level; - protected AbstractTextureExportHandler _textureHandler; + CreateRemapper()?.Remap(level); - public TRDataExporter() - { - SegmentsDataFolder = _defaultSegmentsFolder; - _textureHandler = CreateTextureHandler(); - } + PreCreation(level, type, blobType); + + B blob = CreateBlob(level, type, blobType); + blob.Dependencies = new(Data.GetDependencies(blob.Alias)); + + CompileTextures(blob, CreatePacker()); + + switch (blobType) + { + case TRBlobType.Model: + BuildModel(blob); + break; + case TRBlobType.StaticMesh: + BuildStaticMesh(blob); + break; + case TRBlobType.Sprite: + BuildSprite(blob); + break; + } + + foreach (S sfx in Data.GetHardcodedSounds(blob.Alias)) + { + StoreSFX(sfx, blob); + } - protected abstract AbstractTextureExportHandler CreateTextureHandler(); + PostCreation(blob); + StoreBlob(blob); - public D Export(L level, E entity) + return blob; + } + + protected void CompileTextures(B blob, TRTexturePacker packer) { - EventHandler segmentDelegate = null; - EventHandler segmentRemapped = null; - List duplicateClips = null; - string segmentDir = Path.Combine(SegmentsDataFolder, entity.ToString()); - if (ExportIndividualSegments) + // Get the object or sprite textures from the packer + Dictionary> textures; + if (blob.Type == TRBlobType.Sprite) + { + textures = packer.GetSpriteRegions(SpriteSequences[blob.ID]); + } + else { - if (Directory.Exists(segmentDir)) + List meshes = new(); + if (blob.Type == TRBlobType.Model) { - Directory.Delete(segmentDir, true); + meshes.AddRange(Models[blob.ID].Meshes); } - Directory.CreateDirectory(segmentDir); - - _textureHandler.SegmentExported += segmentDelegate = delegate (object sender, SegmentEventArgs e) + else if (blob.Type == TRBlobType.StaticMesh) { - e.Image.Save(Path.Combine(segmentDir, e.SegmentIndex + ".png"), ImageFormat.Png); - }; + meshes.Add(StaticMeshes[blob.ID].Mesh); + } + + // We don't ignore the dummy mesh if this is the master type + TRMesh dummyMesh = IsMasterType(blob.ID) ? null : GetDummyMesh(); + textures = packer.GetMeshRegions(meshes, dummyMesh); + + ExtractMeshColours(meshes, dummyMesh, blob); + } + + // Deduplicate for efficient import later + new TRImageDeduplicator().Deduplicate(textures); + + // Classify each texture to allow identical texture matching during import + blob.Textures = new(textures.SelectMany(r => r.Value)); + foreach (TRTextileRegion region in blob.Textures) + { + region.GenerateID(); + } + + Debug.Assert(blob.Textures.Select(t => t.ID).Distinct().Count() == blob.Textures.Count); + } + + protected void BuildModel(B blob) + { + blob.Model = Models[blob.ID]; - duplicateClips = new List(); - _textureHandler.SegmentRemapped += segmentRemapped = delegate (object sender, TRTextureRemapEventArgs e) + if (!IsMasterType(blob.ID)) + { + TRMesh dummyMesh = GetDummyMesh(); + for (int i = 0; i < blob.Model.Meshes.Count; i++) { - duplicateClips.Add(new StaticTextureTarget + if (blob.Model.Meshes[i] == dummyMesh) { - Segment = e.NewSegment.Segments.First().Index, - Tile = e.OldTile.Index, - X = e.OldBounds.X, - Y = e.OldBounds.Y, - Clip = new Rectangle(e.AdjustmentPoint.X - e.NewBounds.X, e.AdjustmentPoint.Y - e.NewBounds.Y, e.OldBounds.Width, e.OldBounds.Height) - }); - }; + blob.Model.Meshes[i] = null; + } + } } - PreDefinitionCreation(level, entity); - D definition = CreateModelDefinition(level, entity); - ExportDependencies(definition); - ModelExportReady(definition); - StoreDefinition(definition); + HashSet modelSFX = blob.Model.Animations + .SelectMany(a => a.Commands.Where(c => c is TRSFXCommand)) + .Select(s => (S)(object)(uint)((TRSFXCommand)s).SoundID) + .ToHashSet(); - if (ExportIndividualSegments) + foreach (S sfx in modelSFX) { - _textureHandler.SegmentExported -= segmentDelegate; - _textureHandler.SegmentRemapped -= segmentRemapped; - - File.WriteAllText(Path.Combine(segmentDir, "DuplicateClips.json"), JsonConvert.SerializeObject(duplicateClips, Formatting.Indented)); + StoreSFX(sfx, blob); } - return definition; + if (Data.GetCinematicTypes().Contains(blob.Alias)) + { + blob.CinematicFrames = CinematicFrames; + } } - protected virtual void PreDefinitionCreation(L level, E modelEntity) { } - protected abstract D CreateModelDefinition(L level, E modelEntity); - protected virtual void ModelExportReady(D definition) { } + protected void BuildStaticMesh(B blob) + { + blob.StaticMesh = StaticMeshes[blob.ID]; + } - private void ExportDependencies(D definition) + protected void ExtractMeshColours(IEnumerable meshes, TRMesh dummyMesh, B blob) { - List dependencies = new(Data.GetModelDependencies(definition.Alias)); - definition.Dependencies = dependencies.ToArray(); + IEnumerable faces = meshes + .Where(m => m != dummyMesh) + .SelectMany(m => m.ColouredFaces) + .Select(f => f.Texture) + .Distinct(); + + foreach (ushort texture in faces) + { + StoreColour(texture, blob); + } } - protected void AmendDXtre3DTextures(D _) + protected void BuildSprite(B blob) { - // Dxtre3D can produce faulty UV mapping which can cause casting issues - // when used in model IO, so fix coordinates at this stage. - // This may no longer be the case... - /*foreach (List textureList in definition.ObjectTextures.Values) + // Track where sprite textures are to retain sequencing + blob.SpriteOffsets = new(); + TRSpriteSequence sequence = SpriteSequences[blob.ID]; + foreach (TRSpriteTexture texture in sequence.Textures) { - foreach (IndexedTRObjectTexture texture in textureList) + foreach (TRTextileSegment segment in blob.Textures.SelectMany(r => r.Segments)) { - Dictionary points = new(); - foreach (TRObjectTextureVert vertex in texture.Texture.Vertices) + if (segment.Texture == texture) { - int x = vertex.XCoordinate.Fraction; - if (vertex.XCoordinate.Whole == byte.MaxValue) - { - x++; - } - - int y = vertex.YCoordinate.Fraction; - if (vertex.YCoordinate.Whole == byte.MaxValue) - { - y++; - } - points[vertex] = new Point(x, y); - } - - int maxX = points.Values.Max(p => p.X); - int maxY = points.Values.Max(p => p.Y); - foreach (TRObjectTextureVert vertex in texture.Texture.Vertices) - { - Point p = points[vertex]; - if (p.X == maxX && maxX != byte.MaxValue) - { - vertex.XCoordinate.Fraction--; - vertex.XCoordinate.Whole = byte.MaxValue; - } - if (p.Y == maxY && maxY != byte.MaxValue) - { - vertex.YCoordinate.Fraction--; - vertex.YCoordinate.Whole = byte.MaxValue; - } + blob.SpriteOffsets.Add(segment.Index); + break; } } - }*/ + } } + + protected virtual void PreCreation(L level, T type, TRBlobType blobType) { } + protected abstract B CreateBlob(L level, T type, TRBlobType blobType); + protected abstract void StoreColour(ushort index, B blob); + protected abstract void StoreSFX(S sfx, B blob); + protected virtual void PostCreation(B blob) { } } diff --git a/TRDataControl/Transport/TRDataImporter.cs b/TRDataControl/Transport/TRDataImporter.cs index 8c196623..6da1ddc8 100644 --- a/TRDataControl/Transport/TRDataImporter.cs +++ b/TRDataControl/Transport/TRDataImporter.cs @@ -1,97 +1,75 @@ -using TRImageControl.Packing; +using RectanglePacker.Events; +using TRImageControl.Packing; using TRLevelControl.Model; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model; +using TRModelTransporter.Transport; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public abstract class TRDataImporter : TRDataTransport - where E : Enum +public abstract class TRDataImporter : TRDataTransport where L : TRLevelBase - where D : TRBlobBase + where T : Enum + where S : Enum + where B : TRBlobBase { - public IEnumerable EntitiesToImport { get; set; } - public IEnumerable EntitiesToRemove { get; set; } + public List TypesToImport { get; set; } = new(); + public List TypesToRemove { get; set; } = new(); public bool ClearUnusedSprites { get; set; } public string TextureRemapPath { get; set; } - public ITexturePositionMonitor TexturePositionMonitor { get; set; } - public bool SortModels { get; set; } public bool IgnoreGraphics { get; set; } public bool ForceCinematicOverwrite { get; set; } - protected AbstractTextureImportHandler _textureHandler; - - public TRDataImporter() - { - EntitiesToImport = new List(); - EntitiesToRemove = new List(); - ClearUnusedSprites = false; - } - - protected abstract AbstractTextureImportHandler CreateTextureHandler(); - public void Import() { - if (_textureHandler == null) - { - _textureHandler = CreateTextureHandler(); - _textureHandler.Data = Data; - } - - List existingEntities = GetExistingModelTypes(); - - if (EntitiesToRemove != null) - { - AdjustEntities(); - } + List existingTypes = GetExistingTypes(); + CleanRemovalList(); CleanAliases(); - List standardModelDefinitions = new(); - List soundModelDefinitions = new(); - foreach (E entity in EntitiesToImport) + List blobs = new(); + foreach (T type in TypesToImport) { - BuildDefinitionList(standardModelDefinitions, soundModelDefinitions, existingEntities, entity, false); + BuildBlobList(blobs, existingTypes, type, false); } // Check for alias duplication - ValidateDefinitionList(existingEntities, standardModelDefinitions); + ValidateBlobList(existingTypes, blobs); - if (SortModels) + if (blobs.Count == 0) { - standardModelDefinitions.Sort(delegate (D d1, D d2) - { - return d1.Entity.CompareTo(d2.Entity); - }); - soundModelDefinitions.Sort(delegate (D d1, D d2) - { - return d1.Entity.CompareTo(d2.Entity); - }); + return; } - if (standardModelDefinitions.Count + soundModelDefinitions.Count > 0) - { - Import(standardModelDefinitions, soundModelDefinitions); - } + // Store the current dummy mesh in case we are replacing the master type. + TRMesh dummyMesh = GetDummyMesh(); + + // Remove old types first and tidy up stale textures + RemoveData(); + + // Try to pack the textures collectively now that we have cleared some space. + // This will throw if it fails. + ImportTextures(blobs); + + // Success - import the remaining data. + ImportData(blobs, dummyMesh); } - private void AdjustEntities() + private void CleanRemovalList() { // If an entity is marked to be removed but is also in the list // to import, don't remove it in the first place. - List cleanedEntities = new(); - foreach (E entity in EntitiesToRemove) + List cleanedEntities = new(); + foreach (T type in TypesToRemove) { bool entityClean = false; - if (Data.HasAliases(entity)) + if (Data.HasAliases(type)) { // Check if we have another alias in the import list different from any // in the current level - E alias = Data.GetLevelAlias(LevelName, entity); - E importAlias = default; - foreach (E a in Data.GetAliases(entity)) + T alias = Data.GetLevelAlias(LevelName, type); + T importAlias = default; + foreach (T a in Data.GetAliases(type)) { - if (EntitiesToImport.Contains(a)) + if (TypesToImport.Contains(a)) { importAlias = a; break; @@ -103,7 +81,7 @@ private void AdjustEntities() entityClean = true; } } - else if (!EntitiesToImport.Contains(entity)) + else if (!TypesToImport.Contains(type)) { entityClean = true; } @@ -112,8 +90,8 @@ private void AdjustEntities() { // There may be null meshes dependent on this removal, so we can only remove it if they're // being removed as well. - IEnumerable exclusions = Data.GetRemovalExclusions(entity); - if (exclusions.Any() && exclusions.All(EntitiesToRemove.Contains)) + IEnumerable exclusions = Data.GetRemovalExclusions(type); + if (exclusions.Any() && exclusions.All(TypesToRemove.Contains)) { entityClean = false; } @@ -121,119 +99,127 @@ private void AdjustEntities() if (entityClean) { - cleanedEntities.Add(entity); + cleanedEntities.Add(type); } } - EntitiesToRemove = cleanedEntities; + TypesToRemove = cleanedEntities; } private void CleanAliases() { - List cleanedEntities = new(); + List cleanedTypes = new(); // Do we have any aliases? - foreach (E importEntity in EntitiesToImport) + foreach (T importType in TypesToImport) { - if (Data.HasAliases(importEntity)) + if (Data.HasAliases(importType)) { throw new TransportException(string.Format ( "Cannot import ambiguous entity {0} - choose an alias from [{1}].", - importEntity.ToString(), - string.Join(", ", Data.GetAliases(importEntity)) + importType.ToString(), + string.Join(", ", Data.GetAliases(importType)) )); } - bool entityIsValid = true; - if (Data.IsAlias(importEntity)) + bool typeIsValid = true; + if (Data.IsAlias(importType)) { - E existingEntity = Data.GetLevelAlias(LevelName, Data.TranslateAlias(importEntity)); + T existingType = Data.GetLevelAlias(LevelName, Data.TranslateAlias(importType)); // This entity is only valid if the alias it's for is not already there - entityIsValid = !Equals(importEntity, existingEntity); + typeIsValid = !Equals(importType, existingType); } - if (entityIsValid) + if (typeIsValid) { - cleanedEntities.Add(importEntity); + cleanedTypes.Add(importType); } } // #139 Ensure that aliases are added last so to avoid dependency issues - cleanedEntities.Sort(delegate (E e1, E e2) - { - return Data.TranslateAlias(e1).CompareTo(Data.TranslateAlias(e2)); - }); + cleanedTypes.Sort((t1, t2) => Data.TranslateAlias(t1).CompareTo(Data.TranslateAlias(t2))); - EntitiesToImport = cleanedEntities; + TypesToImport = cleanedTypes; } - private void ValidateDefinitionList(List modelEntities, List importDefinitions) + private void ValidateBlobList(List modelTypes, List importBlobs) { - Dictionary> detectedAliases = new(); - foreach (E entity in modelEntities) + Dictionary> detectedAliases = new(); + foreach (T entity in modelTypes) { if (Data.IsAlias(entity)) { - E masterEntity = Data.TranslateAlias(entity); - if (!detectedAliases.ContainsKey(masterEntity)) + T masterType = Data.TranslateAlias(entity); + if (!detectedAliases.ContainsKey(masterType)) { - detectedAliases[masterEntity] = new List(); + detectedAliases[masterType] = new(); } - detectedAliases[masterEntity].Add(entity); + detectedAliases[masterType].Add(entity); } } - foreach (E masterEntity in detectedAliases.Keys) + foreach (T masterType in detectedAliases.Keys) { - if (detectedAliases[masterEntity].Count > 1) + if (detectedAliases[masterType].Count > 1) { - if (!Data.IsAliasDuplicatePermitted(masterEntity)) + if (!Data.IsAliasDuplicatePermitted(masterType)) { throw new TransportException(string.Format ( "Only one alias per entity can exist in the same level. [{0}] were found as aliases for {1}.", - string.Join(", ", detectedAliases[masterEntity]), - masterEntity.ToString() + string.Join(", ", detectedAliases[masterType]), + masterType.ToString() )); } - else if (Data.AliasPriority.ContainsKey(masterEntity)) + else if (Data.AliasPriority.ContainsKey(masterType)) { // If we are importing two aliases such as LaraMiscAnim_Unwater and LaraMiscAnim_Xian, // allow the priority list to define exactly what imports. Otherwise while the prioritised // model will be imported, other aspects such as texture import will try to import both. - E prioritisedType = Data.AliasPriority[masterEntity]; - importDefinitions.RemoveAll(d => detectedAliases[masterEntity].Contains(d.Alias) && !Equals(d.Alias, prioritisedType)); + T prioritisedType = Data.AliasPriority[masterType]; + importBlobs.RemoveAll(d => detectedAliases[masterType].Contains(d.Alias) && !Equals(d.Alias, prioritisedType)); } } } + + // If we are importing a master model, ensure it is handled first - this will usually be + // Lara, so when replacing her we need to define her new model first to get the new dummy + // mesh for other models. + B masterBlob = importBlobs.Find(b => IsMasterType(b.ID)); + if (masterBlob != null) + { + importBlobs.Remove(masterBlob); + importBlobs.Insert(0, masterBlob); + } } - private void BuildDefinitionList(List standardModelDefinitions, List soundModelDefinitions, List modelEntities, E nextEntity, bool isDependency) + private void BuildBlobList(List standardBlobs, List modelTypes, T nextType, bool isDependency) { - if (modelEntities.Contains(nextEntity)) + if (modelTypes.Contains(nextType)) { // Are we allowed to replace it? - if (!Data.IsOverridePermitted(nextEntity)) + if (!Data.IsOverridePermitted(nextType)) { // If the model already in the list is a dependency only, but the new one to add isn't, switch it - D definition = standardModelDefinitions.Find(m => Equals(m.Alias, nextEntity)); - if (definition != null && definition.IsDependencyOnly && !isDependency) + B blob = standardBlobs.Find(m => Equals(m.Alias, nextType)); + if (blob != null && blob.IsDependencyOnly && !isDependency) { - definition.IsDependencyOnly = false; + blob.IsDependencyOnly = false; } - else if (EntitiesToRemove.Contains(nextEntity)) + else if (TypesToRemove.Contains(nextType)) { - EntitiesToRemove = new List(EntitiesToRemove).Except(new List { nextEntity }); + TypesToRemove = new(TypesToRemove); + TypesToRemove.Remove(nextType); } // Avoid issues with cyclic dependencies by adding separately. The caveat here is // cyclic dependencies can't have further sub-dependencies. - IEnumerable cyclicDependencies = Data.GetCyclicDependencies(nextEntity); - foreach (E cyclicDependency in cyclicDependencies) + IEnumerable cyclicDependencies = Data.GetCyclicDependencies(nextType); + foreach (T cyclicDependency in cyclicDependencies) { - if (!modelEntities.Contains(cyclicDependency) || Data.IsOverridePermitted(cyclicDependency)) + if (!modelTypes.Contains(cyclicDependency) || Data.IsOverridePermitted(cyclicDependency)) { - modelEntities.Add(cyclicDependency); - standardModelDefinitions.Add(LoadDefinition(cyclicDependency)); + modelTypes.Add(cyclicDependency); + standardBlobs.Add(LoadBlob(cyclicDependency)); } } @@ -241,25 +227,25 @@ private void BuildDefinitionList(List standardModelDefinitions, List sound } } - D nextDefinition = LoadDefinition(nextEntity); - nextDefinition.IsDependencyOnly = isDependency; - modelEntities.Add(nextEntity); + B nextBlob = LoadBlob(nextType); + nextBlob.IsDependencyOnly = isDependency; + modelTypes.Add(nextType); // Add dependencies first - foreach (E dependency in nextDefinition.Dependencies) + foreach (T dependency in nextBlob.Dependencies) { // If it's a non-graphics dependency, but we are importing another alias // for it, or the level already contains the dependency, we don't need it. bool nonGraphics = Data.IsNonGraphicsDependency(dependency); - E aliasFor = Data.TranslateAlias(dependency); + T aliasFor = Data.TranslateAlias(dependency); if (!Equals(aliasFor, dependency) && nonGraphics) { bool required = true; // #139 check entire model list for instances where alias and dependencies cause clashes - foreach (E entity in modelEntities) + foreach (T type in modelTypes) { // If this entity and the dependency are in the same family - if (Equals(aliasFor, Data.TranslateAlias(entity))) + if (Equals(aliasFor, Data.TranslateAlias(type))) { // Skip it required = false; @@ -269,22 +255,253 @@ private void BuildDefinitionList(List standardModelDefinitions, List sound if (!required) { - // We don't need the graphics, but do we need hardcoded sound? - if (Data.IsSoundOnlyDependency(dependency) && standardModelDefinitions.Find(m => Equals(m.Alias, dependency)) == null) + continue; + } + } + + BuildBlobList(standardBlobs, modelTypes, dependency, nonGraphics); + } + + standardBlobs.Add(nextBlob); + } + + protected void RemoveData() + { + bool removed = false; + foreach (T type in TypesToRemove) + { + T id = Data.TranslateAlias(type); + TRBlobType blobType = Data.GetBlobType(id); + switch (blobType) + { + case TRBlobType.Model: + removed |= Models.Remove(id); + break; + case TRBlobType.StaticMesh: + removed |= StaticMeshes.Remove(id); + break; + case TRBlobType.Sprite: + removed |= SpriteSequences.Remove(id); + break; + } + } + + if (removed) + { + // Clear unused textures + CreateRemapper()?.Remap(Level); + } + } + + protected void ImportTextures(List blobs) + { + List importRegions = new(); + + foreach (B blob in blobs) + { + if (blob.IsDependencyOnly) + continue; + + foreach (TRTextileRegion region in blob.Textures) + { + // Identical textures may exist between models that were exported separately, so merge them here. + if (importRegions.Find(r => r.ID == region.ID) is TRTextileRegion otherRegion) + { + region.MoveTo(otherRegion.Bounds.Location); + otherRegion.Segments.AddRange(region.Segments); + region.Segments.Clear(); + } + else + { + importRegions.Add(region); + } + } + } + + TRTexturePacker packer = CreatePacker(); + packer.AddRectangles(importRegions); + PackingResult packingResult = packer.Pack(true); + + if (packingResult.OrphanCount > 0) + { + throw new TransportException($"Failed to pack {packingResult.OrphanCount} rectangles for types [{string.Join(", ", blobs.Select(b => b.Alias))}]."); + } + + // Packing passed, so remap mesh textures to their new object references + Dictionary> globalRemap = new(); + foreach (TRTextileRegion region in importRegions) + { + globalRemap[region.ID] = new(); + foreach (TRTextileSegment segment in region.Segments.Where(s => s.Texture is TRObjectTexture)) + { + if (globalRemap[region.ID].ContainsKey(segment.Index)) + continue; + + if (ObjectTextures.Count >= Data.TextureObjectLimit) + { + throw new TransportException($"Limit of {Data.TextureObjectLimit} textures reached."); + } + + globalRemap[region.ID][segment.Index] = ObjectTextures.Count; + ObjectTextures.Add(segment.Texture as TRObjectTexture); + } + } + + foreach (B blob in blobs) + { + if (blob.IsDependencyOnly) + continue; + + Dictionary remap = new(); + foreach (TRTextileRegion region in blob.Textures) + { + foreach (int oldIndex in globalRemap[region.ID].Keys) + { + remap[oldIndex] = globalRemap[region.ID][oldIndex]; + } + } + + List meshes = new(); + if (blob.Model != null) + { + meshes.AddRange(blob.Model.Meshes); + } + if (blob.StaticMesh != null) + { + meshes.Add(blob.StaticMesh.Mesh); + } + + IEnumerable faces = meshes + .Where(m => m != null) + .SelectMany(m => m.TexturedFaces); + foreach (TRMeshFace face in faces) + { + face.Texture = (ushort)remap[face.Texture]; + } + + faces = meshes + .Where(m => m != null) + .SelectMany(m => m.ColouredFaces); + foreach (TRMeshFace face in faces) + { + face.Texture = ImportColour(blob, face.Texture); + } + } + } + + protected void ImportData(List blobs, TRMesh oldDummyMesh) + { + foreach (B blob in blobs) + { + switch (blob.Type) + { + case TRBlobType.Model: + ImportModel(blob, oldDummyMesh); + break; + case TRBlobType.StaticMesh: + ImportStaticMesh(blob); + break; + case TRBlobType.Sprite: + ImportSprite(blob); + break; + } + + ImportSound(blob); + ImportCinematics(blob); + + BlobImported(blob); + } + } + + protected void ImportModel(B blob, TRMesh oldDummyMesh) + { + if (blob.Model == null) + return; + + if (IsMasterType(blob.ID)) + { + Models[blob.ID] = blob.Model; + TRMesh newDummyMesh = GetDummyMesh(); + foreach (TRModel model in Models.Values) + { + if (model == blob.Model) + continue; + + for (int i = 0; i < model.Meshes.Count; i++) + { + if (model.Meshes[i] == oldDummyMesh) { - soundModelDefinitions.Add(LoadDefinition(dependency)); + model.Meshes[i] = newDummyMesh; } + } + } + } + // Is this condition really needed? + else if (!Models.ContainsKey(blob.ID) + || (!Data.AliasPriority?.ContainsKey(blob.ID) ?? true) + || Equals(Data.AliasPriority[blob.ID], blob.Alias)) + { + Models[blob.ID] = blob.Model; - continue; + // Restore dummy meshes + TRMesh dummyMesh = GetDummyMesh(); + for (int i = 0; i < blob.Model.Meshes.Count; i++) + { + if (blob.Model.Meshes[i] == null || blob.IsDependencyOnly) + { + blob.Model.Meshes[i] = dummyMesh; } } + } + } - BuildDefinitionList(standardModelDefinitions, soundModelDefinitions, modelEntities, dependency, nonGraphics); + protected void ImportCinematics(B blob) + { + if (blob.CinematicFrames == null) + { + return; } - standardModelDefinitions.Add(nextDefinition); + if (CinematicFrames.Count == 0 || ForceCinematicOverwrite) + { + CinematicFrames.Clear(); + CinematicFrames.AddRange(blob.CinematicFrames); + } } - protected abstract List GetExistingModelTypes(); - protected abstract void Import(IEnumerable standardDefinitions, IEnumerable soundOnlyDefinitions); + protected void ImportStaticMesh(B blob) + { + if (blob.StaticMesh != null) + { + StaticMeshes[blob.ID] = blob.StaticMesh; + } + } + + protected void ImportSprite(B blob) + { + List textures = new(); + foreach (int spriteOffset in blob.SpriteOffsets) + { + foreach (TRTextileSegment segment in blob.Textures.SelectMany(r => r.Segments)) + { + if (segment.Index == spriteOffset) + { + textures.Add(segment.Texture as TRSpriteTexture); + break; + } + } + } + + SpriteSequences[blob.ID] = new() + { + Textures = textures, + }; + } + + protected virtual void BlobImported(B blob) { } + protected abstract ushort ImportColour(B blob, ushort currentIndex); + protected abstract void ImportSound(B blob); + + protected abstract List GetExistingTypes(); + protected abstract List ObjectTextures { get; } } diff --git a/TRDataControl/Transport/TRDataTransport.cs b/TRDataControl/Transport/TRDataTransport.cs index 5124eccf..9365d334 100644 --- a/TRDataControl/Transport/TRDataTransport.cs +++ b/TRDataControl/Transport/TRDataTransport.cs @@ -1,84 +1,40 @@ -using Newtonsoft.Json; -using System.Drawing.Imaging; -using TRModelTransporter.Data; -using TRModelTransporter.Handlers; -using TRModelTransporter.Model; +using TRDataControl.Remapping; +using TRImageControl.Packing; +using TRLevelControl.Model; -namespace TRModelTransporter.Transport; +namespace TRDataControl; -public abstract class TRDataTransport - where E : Enum - where L : class - where D : TRBlobBase +public abstract class TRDataTransport + where L : TRLevelBase + where T : Enum + where S : Enum + where B : TRBlobBase { - protected static readonly string _defaultDataFolder = @"Resources\Models"; - protected static readonly string _dataFileName = "Data.json"; - protected static readonly string _imageFileName = "Segments.png"; + protected static readonly string _defaultDataFolder = @"Resources\Objects"; + protected static readonly string _blobExt = ".TRB"; - public IDataProvider Data { get; set; } + public IDataProvider Data { get; set; } public L Level { get; set; } - public string LevelName { get; set; } + public string DataFolder { get; set; } = _defaultDataFolder; - public string DataFolder { get; set; } - - protected readonly CinematicTransportHandler _cinematicHandler; - protected readonly ModelTransportHandler _modelHandler; - protected readonly ColourTransportHandler _colourHandler; - protected readonly SoundTransportHandler _soundHandler; - - public TRDataTransport() + public void StoreBlob(B blob) { - DataFolder = _defaultDataFolder; - - _cinematicHandler = new CinematicTransportHandler(); - _modelHandler = new ModelTransportHandler(); - _colourHandler = new ColourTransportHandler(); - _soundHandler = new SoundTransportHandler(); + Directory.CreateDirectory(DataFolder); + TRBlobControl.Write(blob, Path.Combine(DataFolder, blob.Alias.ToString().ToUpper() + _blobExt)); } - protected void StoreDefinition(D definition) + public B LoadBlob(T type) { - string directory = Path.Combine(DataFolder, definition.Alias.ToString()); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } - - if (definition.HasGraphics) - { - definition.Image.Save(Path.Combine(directory, _imageFileName), ImageFormat.Png); - } - File.WriteAllText(Path.Combine(directory, _dataFileName), JsonConvert.SerializeObject(definition, Formatting.None)); + return TRBlobControl.Read(Path.Combine(DataFolder, type.ToString().ToUpper() + _blobExt)); } - public D LoadDefinition(E modelEntity) - { - string directory = Path.Combine(DataFolder, modelEntity.ToString()); - string dataFilePath = Path.Combine(directory, _dataFileName); - string imageFilePath = Path.Combine(directory, _imageFileName); - - if (!File.Exists(dataFilePath)) - { - throw new IOException(string.Format("Missing model data JSON file ({0})", dataFilePath)); - } - - D definition = JsonConvert.DeserializeObject(File.ReadAllText(dataFilePath)); - definition.Alias = modelEntity; - - if (definition.HasGraphics) - { - if (!File.Exists(imageFilePath)) - { - throw new IOException(string.Format("Missing model data image file ({0})", imageFilePath)); - } - definition.Image = new(imageFilePath); - } - return definition; - } - - protected bool Equals(E e1, E e2) - { - return EqualityComparer.Default.Equals(e1, e2); - } + protected abstract TRTexturePacker CreatePacker(); + protected abstract TRTextureRemapper CreateRemapper(); + protected abstract bool IsMasterType(T type); + protected abstract TRMesh GetDummyMesh(); + protected abstract TRDictionary Models { get; } + protected abstract TRDictionary StaticMeshes { get; } + protected abstract TRDictionary SpriteSequences { get; } + protected abstract List CinematicFrames { get; } } diff --git a/TRDataControlTests/IO/ExportTests.cs b/TRDataControlTests/IO/ExportTests.cs new file mode 100644 index 00000000..1bfa14c9 --- /dev/null +++ b/TRDataControlTests/IO/ExportTests.cs @@ -0,0 +1,239 @@ +using Newtonsoft.Json; +using TRDataControl; +using TRLevelControl.Model; +using TRLevelControlTests; + +namespace TRDataControlTests.IO; + +[TestClass] +[TestCategory("DataTransport")] +public class ExportTests : TestBase +{ + [TestMethod] + [Description("Test creating a TR1 model export.")] + public void TestTR1ExportProperties() + { + TR1Level level = GetTR1TestLevel(); + TR1DataExporter exporter = new(); + TR1Blob blob = exporter.Export(level, TR1Type.Bear, TRBlobType.Model); + + Assert.AreEqual(TR1Type.Bear, blob.ID); + Assert.AreEqual(TR1Type.Bear, blob.Alias); + Assert.AreEqual(TRBlobType.Model, blob.Type); + Assert.IsFalse(blob.IsDependencyOnly); + Assert.AreEqual(0, blob.Dependencies.Count); + Assert.AreEqual(level.Models[TR1Type.Bear], blob.Model); + Assert.IsNull(blob.CinematicFrames); + + Assert.AreEqual(6, blob.SoundEffects.Count); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR1SFX.BearGrowl)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR1SFX.BearFeet)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR1SFX.BearAttack)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR1SFX.BearSnarl)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR1SFX.BearHurt)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR1SFX.BearDeath)); + } + + [TestMethod] + [Description("Test TR1 model export IO.")] + public void TestTR1ExportIO() + { + TR1Level level = GetTR1TestLevel(); + TR1DataExporter exporter = new() + { + DataFolder = @"Objects\TR1" + }; + TR1Blob blob1 = exporter.Export(level, TR1Type.Bear, TRBlobType.Model); + exporter.StoreBlob(blob1); + + TR1Blob blob2 = exporter.LoadBlob(TR1Type.Bear); + + string json1 = JsonConvert.SerializeObject(blob1); + string json2 = JsonConvert.SerializeObject(blob2); + + Assert.AreEqual(json1, json2); + } + + [TestMethod] + [Description("Test creating a TR2 model export.")] + public void TestTR2ExportProperties() + { + TR2Level level = GetTR2TestLevel(); + TR2DataExporter exporter = new(); + TR2Blob blob = exporter.Export(level, TR2Type.BengalTiger, TRBlobType.Model); + + Assert.AreEqual(TR2Type.TigerOrSnowLeopard, blob.ID); + Assert.AreEqual(TR2Type.BengalTiger, blob.Alias); + Assert.AreEqual(TRBlobType.Model, blob.Type); + Assert.IsFalse(blob.IsDependencyOnly); + Assert.AreEqual(0, blob.Dependencies.Count); + Assert.AreEqual(level.Models[TR2Type.TigerOrSnowLeopard], blob.Model); + Assert.IsNull(blob.CinematicFrames); + + Assert.AreEqual(9, blob.SoundEffects.Count); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.LaraWetFeet)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.LaraSplash)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.BodySlam)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.LeopardFeet)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.TigerRoar)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.TigerBite)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.TigerStrike)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.TigerDeath)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR2SFX.TigerGrowl)); + } + + [TestMethod] + [Description("Test TR2 model export IO.")] + public void TestTR2ExportIO() + { + TR2Level level = GetTR2TestLevel(); + TR2DataExporter exporter = new() + { + DataFolder = @"Objects\TR2" + }; + TR2Blob blob1 = exporter.Export(level, TR2Type.BengalTiger, TRBlobType.Model); + exporter.StoreBlob(blob1); + + TR2Blob blob2 = exporter.LoadBlob(TR2Type.BengalTiger); + + string json1 = JsonConvert.SerializeObject(blob1); + string json2 = JsonConvert.SerializeObject(blob2); + + Assert.AreEqual(json1, json2); + } + + [TestMethod] + [Description("Test creating a TR3 model export.")] + public void TestTR3ExportProperties() + { + TR3Level level = GetTR3TestLevel(); + TR3DataExporter exporter = new(); + TR3Blob blob = exporter.Export(level, TR3Type.Monkey, TRBlobType.Model); + + Assert.AreEqual(TR3Type.Monkey, blob.ID); + Assert.AreEqual(TR3Type.Monkey, blob.Alias); + Assert.AreEqual(TRBlobType.Model, blob.Type); + Assert.IsFalse(blob.IsDependencyOnly); + Assert.AreEqual(2, blob.Dependencies.Count); + Assert.IsTrue(blob.Dependencies.Contains(TR3Type.MonkeyKeyMeshswap)); + Assert.IsTrue(blob.Dependencies.Contains(TR3Type.MonkeyMedMeshswap)); + Assert.AreEqual(level.Models[TR3Type.Monkey], blob.Model); + Assert.IsNull(blob.CinematicFrames); + + Assert.AreEqual(7, blob.SoundEffects.Count); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyStandWait)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyAttackLow)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyAttackJump)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyJump)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyDeath)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyChatter)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR3SFX.MonkeyRoll)); + } + + [TestMethod] + [Description("Test TR3 model export IO.")] + public void TestTR3ExportIO() + { + TR3Level level = GetTR3TestLevel(); + TR3DataExporter exporter = new() + { + DataFolder = @"Objects\TR3" + }; + TR3Blob blob1 = exporter.Export(level, TR3Type.Monkey, TRBlobType.Model); + exporter.StoreBlob(blob1); + + TR3Blob blob2 = exporter.LoadBlob(TR3Type.Monkey); + + string json1 = JsonConvert.SerializeObject(blob1); + string json2 = JsonConvert.SerializeObject(blob2); + + Assert.AreEqual(json1, json2); + } + + [TestMethod] + [TestCategory("Model")] + [Description("Test creating a TR4 model export.")] + public void TestTR4ExportProperties() + { + TR4Level level = GetTR4TestLevel(); + TR4DataExporter exporter = new(); + TR4Blob blob = exporter.Export(level, TR4Type.Dog, TRBlobType.Model); + + Assert.AreEqual(TR4Type.Dog, blob.ID); + Assert.AreEqual(TR4Type.Dog, blob.Alias); + Assert.AreEqual(TRBlobType.Model, blob.Type); + Assert.IsFalse(blob.IsDependencyOnly); + Assert.AreEqual(0, blob.Dependencies.Count); + Assert.AreEqual(level.Models[TR4Type.Dog], blob.Model); + Assert.IsNull(blob.CinematicFrames); + + Assert.AreEqual(3, blob.SoundEffects.Count); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR4SFX.DogHowl)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR4SFX.DogDeath)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR4SFX.DogBite)); + } + + [TestMethod] + [TestCategory("Model")] + [Description("Test TR4 model export IO.")] + public void TestTR4ExportIO() + { + TR4Level level = GetTR4TestLevel(); + TR4DataExporter exporter = new() + { + DataFolder = @"Objects\TR4" + }; + TR4Blob blob1 = exporter.Export(level, TR4Type.Dog, TRBlobType.Model); + exporter.StoreBlob(blob1); + + TR4Blob blob2 = exporter.LoadBlob(TR4Type.Dog); + + string json1 = JsonConvert.SerializeObject(blob1); + string json2 = JsonConvert.SerializeObject(blob2); + + Assert.AreEqual(json1, json2); + } + + [TestMethod] + [Description("Test creating a TR5 model export.")] + public void TestTR5ExportProperties() + { + TR5Level level = GetTR5TestLevel(); + TR5DataExporter exporter = new(); + TR5Blob blob = exporter.Export(level, TR5Type.Huskie, TRBlobType.Model); + + Assert.AreEqual(TR5Type.Huskie, blob.ID); + Assert.AreEqual(TR5Type.Huskie, blob.Alias); + Assert.AreEqual(TRBlobType.Model, blob.Type); + Assert.IsFalse(blob.IsDependencyOnly); + Assert.AreEqual(0, blob.Dependencies.Count); + Assert.AreEqual(level.Models[TR5Type.Huskie], blob.Model); + Assert.IsNull(blob.CinematicFrames); + + Assert.AreEqual(4, blob.SoundEffects.Count); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR5SFX.LaraSplash)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR5SFX.DogHowl)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR5SFX.DogDeath)); + Assert.IsTrue(blob.SoundEffects.ContainsKey(TR5SFX.DogAttack1)); + } + + [TestMethod] + [Description("Test TR5 model export IO.")] + public void TestTR5ExportIO() + { + TR5Level level = GetTR5TestLevel(); + TR5DataExporter exporter = new() + { + DataFolder = @"Objects\TR5" + }; + TR5Blob blob1 = exporter.Export(level, TR5Type.Huskie, TRBlobType.Model); + exporter.StoreBlob(blob1); + + TR5Blob blob2 = exporter.LoadBlob(TR5Type.Huskie); + + string json1 = JsonConvert.SerializeObject(blob1); + string json2 = JsonConvert.SerializeObject(blob2); + + Assert.AreEqual(json1, json2); + } +} diff --git a/TRDataControlTests/IO/ImportTests.cs b/TRDataControlTests/IO/ImportTests.cs new file mode 100644 index 00000000..a617cf12 --- /dev/null +++ b/TRDataControlTests/IO/ImportTests.cs @@ -0,0 +1,190 @@ +using TRDataControl; +using TRLevelControl.Model; +using TRLevelControlTests; + +namespace TRDataControlTests.IO; + +[TestClass] +[TestCategory("DataTransport")] +public class ImportTests : TestBase +{ + [TestMethod] + [Description("Test importing a TR1 model.")] + public void TestTR1Import() + { + ExportTR1Model(TR1Type.Bear, TRBlobType.Model); + + TR1Level level = GetTR1AltTestLevel(); + Assert.IsFalse(level.Models.ContainsKey(TR1Type.Bear)); + + TR1DataImporter importer = new() + { + DataFolder = @"Objects\TR1", + Level = level, + TypesToImport = new() { TR1Type.Bear }, + }; + importer.Import(); + + Assert.IsTrue(level.Models.ContainsKey(TR1Type.Bear)); + } + + [TestMethod] + [Description("Test importing a TR2 model.")] + public void TestTR2Import() + { + ExportTR2Model(TR2Type.BengalTiger, TRBlobType.Model); + + TR2Level level = GetTR2AltTestLevel(); + Assert.IsFalse(level.Models.ContainsKey(TR2Type.TigerOrSnowLeopard)); + + TR2DataImporter importer = new() + { + DataFolder = @"Objects\TR2", + Level = level, + TypesToImport = new() { TR2Type.BengalTiger }, + }; + importer.Import(); + + Assert.IsTrue(level.Models.ContainsKey(TR2Type.TigerOrSnowLeopard)); + } + + [TestMethod] + [Description("Test importing a TR3 model.")] + public void TestTR3Import() + { + ExportTR3Model(TR3Type.Monkey, TRBlobType.Model); + + TR3Level level = GetTR3AltTestLevel(); + Assert.IsFalse(level.Models.ContainsKey(TR3Type.Monkey)); + + TR3DataImporter importer = new() + { + DataFolder = @"Objects\TR3", + Level = level, + TypesToImport = new() { TR3Type.Monkey }, + }; + importer.Import(); + + Assert.IsTrue(level.Models.ContainsKey(TR3Type.Monkey)); + } + + [TestMethod] + [Description("Test importing a TR4 model.")] + public void TestTR4Import() + { + ExportTR4Model(TR4Type.Dog, TRBlobType.Model); + + TR4Level level = GetTR4AltTestLevel(); + Assert.IsFalse(level.Models.ContainsKey(TR4Type.Dog)); + + TR4DataImporter importer = new() + { + DataFolder = @"Objects\TR4", + Level = level, + TypesToImport = new() { TR4Type.Dog }, + }; + importer.Import(); + + Assert.IsTrue(level.Models.ContainsKey(TR4Type.Dog)); + } + + [TestMethod] + [Description("Test importing a TR5 model.")] + public void TestTR5Import() + { + ExportTR5Model(TR5Type.Huskie, TRBlobType.Model); + + TR5Level level = GetTR5AltTestLevel(); + Assert.IsFalse(level.Models.ContainsKey(TR5Type.Huskie)); + + TR5DataImporter importer = new() + { + DataFolder = @"Objects\TR5", + Level = level, + TypesToImport = new() { TR5Type.Huskie }, + }; + importer.Import(); + + Assert.IsTrue(level.Models.ContainsKey(TR5Type.Huskie)); + } + + private static void ExportTR1Model(TR1Type type, TRBlobType blobType) + { + TR1Level level = GetTR1TestLevel(); + TR1DataExporter exporter = new() + { + DataFolder = @"Objects\TR1" + }; + TR1Blob blob = exporter.Export(level, type, blobType); + exporter.StoreBlob(blob); + + foreach (TR1Type dependency in blob.Dependencies) + { + ExportTR1Model(dependency, exporter.Data.GetBlobType(dependency)); + } + } + + private static void ExportTR2Model(TR2Type type, TRBlobType blobType) + { + TR2Level level = GetTR2TestLevel(); + TR2DataExporter exporter = new() + { + DataFolder = @"Objects\TR2" + }; + TR2Blob blob = exporter.Export(level, type, blobType); + exporter.StoreBlob(blob); + + foreach (TR2Type dependency in blob.Dependencies) + { + ExportTR2Model(dependency, exporter.Data.GetBlobType(dependency)); + } + } + + private static void ExportTR3Model(TR3Type type, TRBlobType blobType) + { + TR3Level level = GetTR3TestLevel(); + TR3DataExporter exporter = new() + { + DataFolder = @"Objects\TR3" + }; + TR3Blob blob = exporter.Export(level, type, blobType); + exporter.StoreBlob(blob); + + foreach (TR3Type dependency in blob.Dependencies) + { + ExportTR3Model(dependency, exporter.Data.GetBlobType(dependency)); + } + } + + private static void ExportTR4Model(TR4Type type, TRBlobType blobType) + { + TR4Level level = GetTR4TestLevel(); + TR4DataExporter exporter = new() + { + DataFolder = @"Objects\TR4" + }; + TR4Blob blob = exporter.Export(level, type, blobType); + exporter.StoreBlob(blob); + + foreach (TR4Type dependency in blob.Dependencies) + { + ExportTR4Model(dependency, exporter.Data.GetBlobType(dependency)); + } + } + + private static void ExportTR5Model(TR5Type type, TRBlobType blobType) + { + TR5Level level = GetTR5TestLevel(); + TR5DataExporter exporter = new() + { + DataFolder = @"Objects\TR5" + }; + TR5Blob blob = exporter.Export(level, type, blobType); + exporter.StoreBlob(blob); + + foreach (TR5Type dependency in blob.Dependencies) + { + ExportTR5Model(dependency, exporter.Data.GetBlobType(dependency)); + } + } +} diff --git a/TRDataControlTests/TRDataControlTests.csproj b/TRDataControlTests/TRDataControlTests.csproj new file mode 100644 index 00000000..83b56c0f --- /dev/null +++ b/TRDataControlTests/TRDataControlTests.csproj @@ -0,0 +1,28 @@ + + + net6.0-windows + enable + disable + false + false + true + false + Copyright © Tomb Raider Community 2024 + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/TRImageControl/Packing/Textures/TRImageDeduplicator.cs b/TRImageControl/Packing/Textures/TRImageDeduplicator.cs new file mode 100644 index 00000000..a2c367bb --- /dev/null +++ b/TRImageControl/Packing/Textures/TRImageDeduplicator.cs @@ -0,0 +1,69 @@ +using System.Drawing; + +namespace TRImageControl.Packing; + +public class TRImageDeduplicator +{ + private Dictionary> _tiles; + + public void Deduplicate(Dictionary> tiles) + { + _tiles = tiles; + foreach (TRTextile tile in _tiles.Keys) + { + List regions = _tiles[tile]; + for (int i = regions.Count - 1; i >= 0; i--) + { + if (Move(tile, regions[i], i)) + { + regions.RemoveAt(i); + } + } + } + } + + private bool Move(TRTextile sourceTile, TRTextileRegion region, int index) + { + foreach (TRTextile tile in _tiles.Keys) + { + List regions = _tiles[tile]; + for (int i = 0; i < regions.Count; i++) + { + if (i == index && tile == sourceTile) + continue; + + TRTextileRegion candidate = regions[i]; + Point? p = LocateSubRegion(region, candidate); + if (p.HasValue) + { + candidate.InheritTextures(region, p.Value, tile.Index); + return true; + } + } + } + + return false; + } + + private static Point? LocateSubRegion(TRTextileRegion region, TRTextileRegion candidate) + { + int xEnd = candidate.Bounds.Width - region.Bounds.Width; + int yEnd = candidate.Bounds.Height - region.Bounds.Height; + Rectangle rect = new(0, 0, region.Bounds.Width, region.Bounds.Height); + + for (int x = 0; x <= xEnd; x++) + { + rect.X = x; + for (int y = 0; y <= yEnd; y++) + { + rect.Y = y; + TRImage clip = candidate.Image.Export(rect); + if (clip.Equals(region.Image)) + { + return new(candidate.Bounds.X + x, candidate.Bounds.Y + y); + } + } + } + return null; + } +} diff --git a/TRImageControl/Palette/TRPalette8Control.cs b/TRImageControl/Palette/TRPalette8Control.cs index fee54273..97c21563 100644 --- a/TRImageControl/Palette/TRPalette8Control.cs +++ b/TRImageControl/Palette/TRPalette8Control.cs @@ -8,14 +8,14 @@ public class TRPalette8Control { public TR1Level Level { get; set; } public Dictionary ChangedTiles { get; set; } - public List ObsoleteModels { get; set; } + public List ObsoleteTypes { get; set; } private List _palette, _predefinedPalette; public TRPalette8Control() { ChangedTiles = new(); - ObsoleteModels = new(); + ObsoleteTypes = new(); } public TRImage GetOriginalTile(int tileIndex) @@ -57,7 +57,7 @@ public void MergeTiles() // Grab meshes we aren't interested in - but don't remove Lara's hips e.g. Atlantean spawns List ignoredMeshes = new(); Level.Models.TryGetValue(TR1Type.Lara, out TRModel lara); - foreach (TR1Type entity in ObsoleteModels) + foreach (TR1Type entity in ObsoleteTypes) { Level.Models.TryGetValue(entity, out TRModel model); if (model == null) diff --git a/TRLevelControlTests/Levels/TR1/TEST1.PHD b/TRLevelControlTests/Levels/TR1/TEST1.PHD index 203cc29608505fbd7d1e163bf60cc7defeccf71c..29e5e5722c833a5119e5e90c75ed1c19bf910261 100644 GIT binary patch delta 30288 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 Lr%+UUPVxT$1jQ#} delta 1283 zcmYk3eP|nX7{|X!?=HzDxmU-E*lEEPB_GE4m%ZL4g^^?6-5RGp$wE2nV^Eyb`1>+Tlah;4kw()ZW=oiVw+R{D?gJE2`}Lz{Dql> zbmLNWspNwnCF>_4U_Ht7UydLQZCQ%7m{AKhV+&$vMH|}DflkEHg{?I1NpvHD9`vHm z_+?pA%bvp1=EFwg#z<+aT9!J+`Nx#-jwRx}v>-RACnigrpKC8hQ^Q%%c$7|c{ zlJ3G>kJGkW=CyuS53qa(s|T5fn1?M3ba&=_L8cqD&X=)sj4c%enr&^8(e&V8V-1is z?%xU2mo*6731xDS6%`tbfoKD_*$AGiMXxs&eZQ;lH-u0(6~Y_tN_=s=5 { TR1Type.LaraMiscAnim_H_General }, + TypesToImport = new List { TR1Type.LaraMiscAnim_H_General }, DataFolder = _outer.GetResourcePath(@"TR1\Models") }; @@ -880,8 +880,8 @@ private void ConvertToMauledOutfit(TR1CombinedLevel level) Level = level.CutSceneLevel.Data, LevelName = level.CutSceneLevel.Name, ClearUnusedSprites = false, - EntitiesToImport = _mauledEntities, - TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.CutSceneLevel.Name, _mauledEntities), + TypesToImport = _mauledEntities, + //TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.CutSceneLevel.Name, _mauledEntities), DataFolder = _outer.GetResourcePath(@"TR1\Models") }; diff --git a/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs index 7ee6f25e..2c491970 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs @@ -1,12 +1,12 @@ using Newtonsoft.Json; using System.Diagnostics; +using TRDataControl; using TRDataControl.Environment; using TRGE.Core; using TRGE.Core.Item.Enums; using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Processors; @@ -827,7 +827,7 @@ protected override void ProcessImpl() { Level = level.Data, LevelName = level.Name, - EntitiesToImport = allocation.ImportModels, + TypesToImport = allocation.ImportModels, DataFolder = _outer.GetResourcePath(@"TR1\Models"), }; diff --git a/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs index 42a2f92c..22fc58ca 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs @@ -1,9 +1,9 @@ using System.Diagnostics; +using TRDataControl; using TRGE.Core; using TRImageControl.Packing; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Processors; @@ -1030,13 +1030,13 @@ private bool Import(TR2CombinedLevel level, EnemyTransportCollection enemies) TR2DataImporter importer = new() { ClearUnusedSprites = true, - EntitiesToImport = enemies.EntitiesToImport, - EntitiesToRemove = enemies.EntitiesToRemove, + TypesToImport = enemies.EntitiesToImport, + TypesToRemove = enemies.EntitiesToRemove, Level = level.Data, LevelName = level.Name, DataFolder = _outer.GetResourcePath(@"TR2\Models"), TextureRemapPath = _outer.GetResourcePath(@"TR2\Textures\Deduplication\" + level.JsonID + "-TextureRemap.json"), - TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, enemies.EntitiesToImport) + //TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, enemies.EntitiesToImport) }; importer.Data.AliasPriority = TR2EnemyUtilities.GetAliasPriority(level.Name, enemies.EntitiesToImport); diff --git a/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs index ba39a6f7..10834032 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs @@ -1,10 +1,10 @@ using Newtonsoft.Json; +using TRDataControl; using TRGE.Core; using TRGE.Core.Item.Enums; using TRImageControl.Packing; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Textures; @@ -617,9 +617,9 @@ private void RandomizeVehicles() Level = _levelInstance.Data, LevelName = _levelInstance.Name, ClearUnusedSprites = false, - EntitiesToImport = vehicles.Keys, + TypesToImport = new(vehicles.Keys), DataFolder = GetResourcePath(@"TR2\Models"), - TexturePositionMonitor = TextureMonitor.CreateMonitor(_levelInstance.Name, vehicles.Keys.ToList()) + //TexturePositionMonitor = TextureMonitor.CreateMonitor(_levelInstance.Name, vehicles.Keys.ToList()) }; diff --git a/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs index cb85a1c7..e6d35fce 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2OutfitRandomizer.cs @@ -1,8 +1,8 @@ -using TRGE.Core; +using TRDataControl; +using TRGE.Core; using TRImageControl.Packing; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Processors; @@ -257,9 +257,9 @@ private bool Import(TR2CombinedLevel level, TR2Type lara) Level = level.Data, LevelName = level.Name, ClearUnusedSprites = false, - EntitiesToImport = laraImport, - EntitiesToRemove = laraRemovals, - TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, laraImport), + TypesToImport = laraImport, + TypesToRemove = laraRemovals, + //TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, laraImport), DataFolder = _outer.GetResourcePath(@"TR2\Models") }; diff --git a/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs index e76ca967..46530c1b 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs @@ -1,10 +1,10 @@ using Newtonsoft.Json; using System.Diagnostics; +using TRDataControl; using TRGE.Core; using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Processors; @@ -729,12 +729,12 @@ protected override void ProcessImpl() EnemyTransportCollection enemies = _enemyMapping[level]; TR3DataImporter importer = new() { - EntitiesToImport = enemies.EntitiesToImport, - EntitiesToRemove = enemies.EntitiesToRemove, + TypesToImport = enemies.EntitiesToImport, + TypesToRemove = enemies.EntitiesToRemove, Level = level.Data, LevelName = level.Name, DataFolder = _outer.GetResourcePath(@"TR3\Models"), - TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, enemies.EntitiesToImport) + //TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, enemies.EntitiesToImport) }; string remapPath = @"TR3\Textures\Deduplication\" + level.Name + "-TextureRemap.json"; diff --git a/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs index e813e41f..ef2dac4f 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs @@ -1,9 +1,9 @@ using Newtonsoft.Json; +using TRDataControl; using TRGE.Core; using TRImageControl.Packing; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Secrets; @@ -100,7 +100,7 @@ private bool ImportAssaultModels(TR3CombinedLevel level) Level = level.Data, LevelName = level.Name, ClearUnusedSprites = false, - EntitiesToImport = new List + TypesToImport = new List { TR3Type.LaraShotgunAnimation_H, TR3Type.LaraDeagleAnimation_H_Home, diff --git a/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs index 0a728d47..bb32a30e 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3OutfitRandomizer.cs @@ -1,8 +1,8 @@ -using TRGE.Core; +using TRDataControl; +using TRGE.Core; using TRImageControl.Packing; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Processors; @@ -231,9 +231,9 @@ private bool Import(TR3CombinedLevel level, TR3Type lara) Level = level.Data, LevelName = level.Name, ClearUnusedSprites = false, - EntitiesToImport = laraImport, - EntitiesToRemove = laraRemovals, - TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, laraImport), + TypesToImport = laraImport, + TypesToRemove = laraRemovals, + //TexturePositionMonitor = _outer.TextureMonitor.CreateMonitor(level.Name, laraImport), DataFolder = _outer.GetResourcePath(@"TR3\Models") }; diff --git a/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs index 19a2a9b5..1cb39321 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3SecretRandomizer.cs @@ -1,11 +1,11 @@ using Newtonsoft.Json; using System.Diagnostics; +using TRDataControl; using TRGE.Core; using TRGE.Core.Item.Enums; using TRLevelControl; using TRLevelControl.Helpers; using TRLevelControl.Model; -using TRModelTransporter.Transport; using TRRandomizerCore.Helpers; using TRRandomizerCore.Levels; using TRRandomizerCore.Processors; @@ -751,9 +751,9 @@ protected override void ProcessImpl() { Level = level.Data, LevelName = level.Name, - EntitiesToImport = allocation.ImportModels, + TypesToImport = allocation.ImportModels, DataFolder = _outer.GetResourcePath(@"TR3\Models"), - TexturePositionMonitor = monitor + //TexturePositionMonitor = monitor }; importer.Import(); diff --git a/TRRandomizerCore/Textures/DynamicTextureBuilder.cs b/TRRandomizerCore/Textures/DynamicTextureBuilder.cs index 6aa36a4d..fd6ac765 100644 --- a/TRRandomizerCore/Textures/DynamicTextureBuilder.cs +++ b/TRRandomizerCore/Textures/DynamicTextureBuilder.cs @@ -1,11 +1,10 @@ using System.Drawing; +using TRDataControl; using TRImageControl.Packing; using TRImageControl.Textures; using TRLevelControl.Helpers; using TRLevelControl.Model; using TRModelTransporter.Helpers; -using TRModelTransporter.Model.Definitions; -using TRModelTransporter.Transport; using TRRandomizerCore.Levels; using TRRandomizerCore.Utilities; @@ -247,9 +246,9 @@ private void AddModelTextures(TR1Level level, TR1Type modelID, TRModel model, TR TR1Blob adam = new TR1DataImporter { DataFolder = @"Resources\TR1\Models" - }.LoadDefinition(modelID); + }.LoadBlob(modelID); - if (model.Meshes[3].CollRadius != adam.Meshes[3].CollRadius) + if (model.Meshes[3].CollRadius != adam.Model.Meshes[3].CollRadius) { try {