From 394a18aaa574cfc645c0f02e532c65e220286013 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Mon, 23 Jan 2023 21:43:51 +0300 Subject: [PATCH] Optimized most of "UndertaleLists.cs" lists. --- UndertaleModLib/UndertaleChunkTypes.cs | 2 +- UndertaleModLib/UndertaleLists.cs | 126 ++++++++++++++++++------- 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/UndertaleModLib/UndertaleChunkTypes.cs b/UndertaleModLib/UndertaleChunkTypes.cs index 0e5baad92..fcd1ef9fe 100644 --- a/UndertaleModLib/UndertaleChunkTypes.cs +++ b/UndertaleModLib/UndertaleChunkTypes.cs @@ -235,7 +235,7 @@ internal override void UnserializeChunk(UndertaleReader reader) if (reader.ReadByte() != 0) throw new IOException("AlignUpdatedListChunk padding error"); } - List.Add(reader.ReadUndertaleObject()); + List.internalList.Add(reader.ReadUndertaleObject()); } } } diff --git a/UndertaleModLib/UndertaleLists.cs b/UndertaleModLib/UndertaleLists.cs index f12a8d8ec..5c81af738 100644 --- a/UndertaleModLib/UndertaleLists.cs +++ b/UndertaleModLib/UndertaleLists.cs @@ -3,14 +3,53 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; +using System.Reflection; using UndertaleModLib.Models; namespace UndertaleModLib { - public class UndertaleSimpleList : ObservableCollection, UndertaleObject where T : UndertaleObject, new() + public abstract class UndertaleListBase : ObservableCollection { + public readonly List internalList; + + public UndertaleListBase() + { + try + { + FieldInfo itemsField = typeof(Collection) + .GetField("items", BindingFlags.NonPublic | BindingFlags.Instance); + internalList = (List)itemsField.GetValue(this); + + } + catch (Exception e) + { + throw new UndertaleSerializationException($"{e.Message}\nwhile trying to initialize \"UndertalePointerList<{typeof(T).FullName}>\"."); + } + } + + /// + public abstract void Serialize(UndertaleWriter writer); + /// - public void Serialize(UndertaleWriter writer) + public abstract void Unserialize(UndertaleReader reader); + + public void SetCapacity(uint capacity) + { + try + { + internalList.Capacity = (int)capacity; + } + catch (Exception e) + { + throw new UndertaleSerializationException($"{e.Message}\nwhile trying to \"SetCapacity()\" of \"UndertalePointerList<{typeof(T).FullName}>\"."); + } + } + } + + public class UndertaleSimpleList : UndertaleListBase, UndertaleObject where T : UndertaleObject, new() + { + /// + public override void Serialize(UndertaleWriter writer) { writer.Write((uint)Count); for (int i = 0; i < Count; i++) @@ -27,15 +66,16 @@ public void Serialize(UndertaleWriter writer) } /// - public void Unserialize(UndertaleReader reader) + public override void Unserialize(UndertaleReader reader) { uint count = reader.ReadUInt32(); Clear(); + SetCapacity(count); for (uint i = 0; i < count; i++) { try { - Add(reader.ReadUndertaleObject()); + internalList.Add(reader.ReadUndertaleObject()); } catch (UndertaleSerializationException e) { @@ -45,10 +85,10 @@ public void Unserialize(UndertaleReader reader) } } - public class UndertaleSimpleListString : ObservableCollection, UndertaleObject + public class UndertaleSimpleListString : UndertaleListBase, UndertaleObject { /// - public void Serialize(UndertaleWriter writer) + public override void Serialize(UndertaleWriter writer) { writer.Write((uint)Count); for (int i = 0; i < Count; i++) @@ -65,15 +105,16 @@ public void Serialize(UndertaleWriter writer) } /// - public void Unserialize(UndertaleReader reader) + public override void Unserialize(UndertaleReader reader) { uint count = reader.ReadUInt32(); Clear(); + SetCapacity(count); for (uint i = 0; i < count; i++) { try { - Add(reader.ReadUndertaleString()); + internalList.Add(reader.ReadUndertaleString()); } catch (UndertaleSerializationException e) { @@ -83,7 +124,7 @@ public void Unserialize(UndertaleReader reader) } } - public class UndertaleSimpleListShort : ObservableCollection, UndertaleObject where T : UndertaleObject, new() + public class UndertaleSimpleListShort : UndertaleListBase, UndertaleObject where T : UndertaleObject, new() { public UndertaleSimpleListShort() { @@ -97,7 +138,7 @@ private void EnsureShortCount(object sender, NotifyCollectionChangedEventArgs e) } /// - public void Serialize(UndertaleWriter writer) + public override void Serialize(UndertaleWriter writer) { writer.Write((ushort)Count); for (int i = 0; i < Count; i++) @@ -114,15 +155,16 @@ public void Serialize(UndertaleWriter writer) } /// - public void Unserialize(UndertaleReader reader) + public override void Unserialize(UndertaleReader reader) { ushort count = reader.ReadUInt16(); Clear(); + SetCapacity(count); for (ushort i = 0; i < count; i++) { try { - Add(reader.ReadUndertaleObject()); + internalList.Add(reader.ReadUndertaleObject()); } catch (UndertaleSerializationException e) { @@ -132,10 +174,10 @@ public void Unserialize(UndertaleReader reader) } } - public class UndertalePointerList : ObservableCollection, UndertaleObject where T : UndertaleObject, new() + public class UndertalePointerList : UndertaleListBase, UndertaleObject where T : UndertaleObject, new() { /// - public void Serialize(UndertaleWriter writer) + public override void Serialize(UndertaleWriter writer) { writer.Write((uint)Count); foreach (T obj in this) @@ -155,13 +197,15 @@ public void Serialize(UndertaleWriter writer) { try { - (this[i] as PrePaddedObject)?.SerializePrePadding(writer); + T obj = this[i]; - writer.WriteUndertaleObject(this[i]); + (obj as PrePaddedObject)?.SerializePrePadding(writer); + + writer.WriteUndertaleObject(obj); // The last object does NOT get padding (TODO: at least in AUDO) if (i != Count - 1) - (this[i] as PaddedObject)?.SerializePadding(writer); + (obj as PaddedObject)?.SerializePadding(writer); } catch (UndertaleSerializationException e) { @@ -171,15 +215,16 @@ public void Serialize(UndertaleWriter writer) } /// - public void Unserialize(UndertaleReader reader) + public override void Unserialize(UndertaleReader reader) { uint count = reader.ReadUInt32(); Clear(); + SetCapacity(count); for (uint i = 0; i < count; i++) { try { - Add(reader.ReadUndertaleObjectPointer()); + internalList.Add(reader.ReadUndertaleObjectPointer()); } catch (UndertaleSerializationException e) { @@ -205,10 +250,14 @@ public void Unserialize(UndertaleReader reader) { try { - (this[(int)i] as PrePaddedObject)?.UnserializePrePadding(reader); - reader.ReadUndertaleObject(this[(int)i]); + T obj = this[(int)i]; + + (obj as PrePaddedObject)?.UnserializePrePadding(reader); + + reader.ReadUndertaleObject(obj); + if (i != count - 1) - (this[(int)i] as PaddedObject)?.UnserializePadding(reader); + (obj as PaddedObject)?.UnserializePadding(reader); } catch (UndertaleSerializationException e) { @@ -225,42 +274,49 @@ public void Unserialize(UndertaleReader reader, uint endPosition) { uint count = reader.ReadUInt32(); Clear(); - List pointers = new List(); + SetCapacity(count); + List pointers = new((int)count); for (uint i = 0; i < count; i++) { try { uint ptr = reader.ReadUInt32(); pointers.Add(ptr); - Add(reader.GetUndertaleObjectAtAddress(ptr)); + internalList.Add(reader.GetUndertaleObjectAtAddress(ptr)); } catch (UndertaleSerializationException e) { throw new UndertaleSerializationException(e.Message + "\nwhile reading pointer to item " + (i + 1) + " of " + count + " in a list of " + typeof(T).FullName, e); } } - if (Count > 0 && reader.Position != reader.GetAddressForUndertaleObject(this[0])) + if (Count > 0) { - int skip = (int)reader.GetAddressForUndertaleObject(this[0]) - (int)reader.Position; - if (skip > 0) + uint pos = reader.GetAddressForUndertaleObject(this[0]); + if (reader.Position != pos) { - //Console.WriteLine("Skip " + skip + " bytes of blobs"); - reader.Position = reader.Position + (uint)skip; + uint skip = pos - reader.Position; + if (skip > 0) + { + //Console.WriteLine("Skip " + skip + " bytes of blobs"); + reader.Position += skip; + } + else + throw new IOException("First list item starts inside the pointer list?!?!"); } - else - throw new IOException("First list item starts inside the pointer list?!?!"); } for (uint i = 0; i < count; i++) { try { - (this[(int)i] as PrePaddedObject)?.UnserializePrePadding(reader); + T obj = this[(int)i]; + + (obj as PrePaddedObject)?.UnserializePrePadding(reader); if ((i + 1) < count) - reader.ReadUndertaleObject(this[(int)i], (int)(pointers[(int)i + 1] - reader.Position)); + reader.ReadUndertaleObject(obj, (int)(pointers[(int)i + 1] - reader.Position)); else - reader.ReadUndertaleObject(this[(int)i], (int)(endPosition - reader.Position)); + reader.ReadUndertaleObject(obj, (int)(endPosition - reader.Position)); if (i != count - 1) - (this[(int)i] as PaddedObject)?.UnserializePadding(reader); + (obj as PaddedObject)?.UnserializePadding(reader); } catch (UndertaleSerializationException e) {