From 6cd0be06b1ed939eca628391aad80f55c9f48cb0 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 26 Feb 2023 14:41:58 +0300 Subject: [PATCH] Game data loads by copying every chunk to the buffer. --- UndertaleModLib/Models/UndertaleCode.cs | 18 +- .../Models/UndertaleEmbeddedAudio.cs | 2 +- .../Models/UndertaleEmbeddedTexture.cs | 4 +- UndertaleModLib/Models/UndertaleExtension.cs | 4 +- UndertaleModLib/Models/UndertaleRoom.cs | 22 +- UndertaleModLib/Models/UndertaleSequence.cs | 16 +- UndertaleModLib/Models/UndertaleShader.cs | 20 +- .../Models/UndertaleTextureGroupInfo.cs | 10 +- UndertaleModLib/Models/UndertaleTimeline.cs | 2 +- UndertaleModLib/UndertaleChunkTypes.cs | 14 +- UndertaleModLib/UndertaleChunks.cs | 70 ++--- UndertaleModLib/UndertaleIO.cs | 18 +- UndertaleModLib/UndertaleLists.cs | 14 +- UndertaleModLib/Util/AdaptiveBinaryReader.cs | 162 +++++++++++ UndertaleModLib/Util/BufferBinaryReader.cs | 266 +++++++++++++----- UndertaleModLib/Util/FileBinaryReader.cs | 10 +- 16 files changed, 478 insertions(+), 174 deletions(-) create mode 100644 UndertaleModLib/Util/AdaptiveBinaryReader.cs diff --git a/UndertaleModLib/Models/UndertaleCode.cs b/UndertaleModLib/Models/UndertaleCode.cs index 98d7dde82..4bd245549 100644 --- a/UndertaleModLib/Models/UndertaleCode.cs +++ b/UndertaleModLib/Models/UndertaleCode.cs @@ -1114,11 +1114,11 @@ public void Unserialize(UndertaleReader reader) Instructions.Clear(); Instructions.Capacity = reader.InstructionArraysLengths[CurrCodeIndex]; - uint here = reader.Position; + uint here = reader.AbsPosition; uint stop = here + Length; - while (reader.Position < stop) + while (reader.AbsPosition < stop) { - uint a = (reader.Position - here) / 4; + uint a = (reader.AbsPosition - here) / 4; UndertaleInstruction instr = reader.ReadUndertaleObject(); instr.Address = a; Instructions.Add(instr); @@ -1135,7 +1135,7 @@ public void Unserialize(UndertaleReader reader) WeirdLocalFlag = true; } int BytecodeRelativeAddress = reader.ReadInt32(); - _bytecodeAbsoluteAddress = (uint)((int)reader.Position - 4 + BytecodeRelativeAddress); + _bytecodeAbsoluteAddress = (uint)((int)reader.AbsPosition - 4 + BytecodeRelativeAddress); if (Length > 0 && reader.GMS2_3 && reader.GetOffsetMap().TryGetValue(_bytecodeAbsoluteAddress, out var i)) { @@ -1146,14 +1146,14 @@ public void Unserialize(UndertaleReader reader) return; } - uint here = reader.Position; - reader.Position = _bytecodeAbsoluteAddress; + uint here = reader.AbsPosition; + reader.AbsPosition = _bytecodeAbsoluteAddress; Instructions.Clear(); Instructions.Capacity = reader.InstructionArraysLengths[CurrCodeIndex]; - while (reader.Position < _bytecodeAbsoluteAddress + Length) + while (reader.AbsPosition < _bytecodeAbsoluteAddress + Length) { - uint a = (reader.Position - _bytecodeAbsoluteAddress) / 4; + uint a = (reader.AbsPosition - _bytecodeAbsoluteAddress) / 4; UndertaleInstruction instr = reader.ReadUndertaleObject(); instr.Address = a; Instructions.Add(instr); @@ -1161,7 +1161,7 @@ public void Unserialize(UndertaleReader reader) if (ParentEntry == null && Instructions.Count != 0) Instructions[0].Entry = this; - reader.Position = here; + reader.AbsPosition = here; Offset = reader.ReadUInt32(); } diff --git a/UndertaleModLib/Models/UndertaleEmbeddedAudio.cs b/UndertaleModLib/Models/UndertaleEmbeddedAudio.cs index dc9d58bf3..8a91a1907 100644 --- a/UndertaleModLib/Models/UndertaleEmbeddedAudio.cs +++ b/UndertaleModLib/Models/UndertaleEmbeddedAudio.cs @@ -44,7 +44,7 @@ public void Unserialize(UndertaleReader reader) /// public void UnserializePadding(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); } diff --git a/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs b/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs index 7fab41a29..cce356ad8 100644 --- a/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs +++ b/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs @@ -166,7 +166,7 @@ public void UnserializeBlob(UndertaleReader reader) if (_textureData == null || TextureExternal) return; - while (reader.Position % 0x80 != 0) + while (reader.AbsPosition % 0x80 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -414,7 +414,7 @@ public void Unserialize(UndertaleReader reader) /// /// Unserializes the texture from any type of reader (can be from any source). /// - public void Unserialize(FileBinaryReader reader, bool is_2022_5) + public void Unserialize(IBinaryReader reader, bool is_2022_5) { sharedStream ??= new(); diff --git a/UndertaleModLib/Models/UndertaleExtension.cs b/UndertaleModLib/Models/UndertaleExtension.cs index 3efbafb63..d255082ab 100644 --- a/UndertaleModLib/Models/UndertaleExtension.cs +++ b/UndertaleModLib/Models/UndertaleExtension.cs @@ -382,9 +382,9 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) uint filesPtr = reader.ReadUInt32(); uint optionsPtr = reader.ReadUInt32(); - reader.Position = filesPtr; + reader.AbsPosition = filesPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); - reader.Position = optionsPtr; + reader.AbsPosition = optionsPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); } else diff --git a/UndertaleModLib/Models/UndertaleRoom.cs b/UndertaleModLib/Models/UndertaleRoom.cs index 903600fa6..cdca1286d 100644 --- a/UndertaleModLib/Models/UndertaleRoom.cs +++ b/UndertaleModLib/Models/UndertaleRoom.cs @@ -275,7 +275,7 @@ private static void CheckForGMS2_2_2_302(UndertaleReader reader) uint gameObjPtr = reader.ReadUInt32(); uint tilePtr = reader.ReadUInt32(); - reader.Position = gameObjPtr; // "GameObjects" + reader.AbsPosition = gameObjPtr; // "GameObjects" uint objCount = reader.ReadUInt32(); if (objCount > 0) { @@ -462,23 +462,23 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) sequencesPtr = reader.ReadUInt32(); } - reader.Position = backgroundPtr; + reader.AbsPosition = backgroundPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); - reader.Position = viewsPtr; + reader.AbsPosition = viewsPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); - reader.Position = gameObjsPtr; + reader.AbsPosition = gameObjsPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); - reader.Position = tilesPtr; + reader.AbsPosition = tilesPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); if (reader.GMS2) { - reader.Position = layersPtr; + reader.AbsPosition = layersPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); if (reader.GMS2_3) { - reader.Position = sequencesPtr; + reader.AbsPosition = sequencesPtr; count += 1 + UndertaleSimpleList> .UnserializeChildObjectCount(reader); } @@ -1858,17 +1858,17 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) nineSlicesPtr = reader.ReadUInt32(); } - reader.Position = legacyTilesPtr; + reader.AbsPosition = legacyTilesPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); - reader.Position = spritesPtr; + reader.AbsPosition = spritesPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); if (reader.GMS2_3) { - reader.Position = sequencesPtr; + reader.AbsPosition = sequencesPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); if (!reader.undertaleData.GMS2_3_2) { - reader.Position = nineSlicesPtr; + reader.AbsPosition = nineSlicesPtr; count += 1 + UndertalePointerList.UnserializeChildObjectCount(reader); } } diff --git a/UndertaleModLib/Models/UndertaleSequence.cs b/UndertaleModLib/Models/UndertaleSequence.cs index 2327cc596..e8ba9633a 100644 --- a/UndertaleModLib/Models/UndertaleSequence.cs +++ b/UndertaleModLib/Models/UndertaleSequence.cs @@ -333,7 +333,7 @@ UndertaleString ForceReadString() return res; uint returnTo = reader.Position; - reader.Position = reader.GetOffsetMapRev()[res]; + reader.AbsPosition = reader.GetOffsetMapRev()[res]; reader.ReadUndertaleObject(); reader.Position = returnTo; return res; @@ -415,7 +415,7 @@ string ForceReadString() uint strPtr = reader.ReadUInt32(); uint returnTo = reader.Position; - reader.Position = strPtr - 4; + reader.AbsPosition = strPtr - 4; string res = reader.ReadGMString(); reader.Position = returnTo; return res; @@ -500,7 +500,7 @@ public virtual void Serialize(UndertaleWriter writer) /// public virtual void Unserialize(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -511,7 +511,7 @@ public virtual void Unserialize(UndertaleReader reader) /// public static uint UnserializeChildObjectCount(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) reader.Position++; return UndertaleSimpleList>.UnserializeChildObjectCount(reader); @@ -756,7 +756,7 @@ public override void Serialize(UndertaleWriter writer) /// public override void Unserialize(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -769,7 +769,7 @@ public override void Unserialize(UndertaleReader reader) /// public static new uint UnserializeChildObjectCount(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) reader.Position++; reader.Position += 4; // "Interpolation" @@ -823,7 +823,7 @@ public override void Serialize(UndertaleWriter writer) /// public override void Unserialize(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -836,7 +836,7 @@ public override void Unserialize(UndertaleReader reader) /// public static new uint UnserializeChildObjectCount(UndertaleReader reader) { - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) reader.Position++; reader.Position += 4; // "Interpolation" diff --git a/UndertaleModLib/Models/UndertaleShader.cs b/UndertaleModLib/Models/UndertaleShader.cs index 3b6b11e97..271d824c4 100644 --- a/UndertaleModLib/Models/UndertaleShader.cs +++ b/UndertaleModLib/Models/UndertaleShader.cs @@ -201,7 +201,7 @@ private static void WritePadding(UndertaleWriter writer, int amount) private static void ReadPadding(UndertaleReader reader, int amount) { - while ((reader.Position & amount) != 0) + while ((reader.AbsPosition & amount) != 0) { if (reader.ReadByte() != 0) throw new UndertaleSerializationException("Failed to read shader padding: should be some zero bytes"); @@ -335,7 +335,7 @@ public void Unserialize(UndertaleReader reader) next = HLSL11_PixelData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); HLSL11_VertexData.ReadData(reader, length); } if (!HLSL11_PixelData.IsNull) @@ -348,7 +348,7 @@ public void Unserialize(UndertaleReader reader) next = PSSL_VertexData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); HLSL11_PixelData.ReadData(reader, length); } @@ -362,7 +362,7 @@ public void Unserialize(UndertaleReader reader) next = PSSL_PixelData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); PSSL_VertexData.ReadData(reader, length); } if (!PSSL_PixelData.IsNull) @@ -375,7 +375,7 @@ public void Unserialize(UndertaleReader reader) next = Cg_PSVita_VertexData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); PSSL_PixelData.ReadData(reader, length); } @@ -389,7 +389,7 @@ public void Unserialize(UndertaleReader reader) next = Cg_PSVita_PixelData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); Cg_PSVita_VertexData.ReadData(reader, length); } if (!Cg_PSVita_PixelData.IsNull) @@ -402,7 +402,7 @@ public void Unserialize(UndertaleReader reader) next = Cg_PS3_VertexData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); Cg_PSVita_PixelData.ReadData(reader, length); } @@ -418,7 +418,7 @@ public void Unserialize(UndertaleReader reader) next = Cg_PS3_PixelData._Position; else next = EntryEnd; - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); Cg_PS3_VertexData.ReadData(reader, length); } if (!Cg_PS3_PixelData.IsNull) @@ -427,7 +427,7 @@ public void Unserialize(UndertaleReader reader) // Calculate length of data uint next = EntryEnd; // final possible data, nothing else to check for - int length = (int)(next - reader.Position); + int length = (int)(next - reader.AbsPosition); Cg_PS3_PixelData.ReadData(reader, length); } } @@ -513,7 +513,7 @@ public void Serialize(UndertaleWriter writer, bool writeLength = true) public void Unserialize(UndertaleReader reader, bool readLength = true) { - _PointerLocation = reader.Position; + _PointerLocation = reader.AbsPosition; _Position = reader.ReadUInt32(); if (readLength) _Length = reader.ReadUInt32(); diff --git a/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs b/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs index 1d5d98ec2..62e90f95b 100644 --- a/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs +++ b/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs @@ -187,23 +187,23 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) uint fontsPtr = reader.ReadUInt32(); uint tilesetsPtr = reader.ReadUInt32(); - reader.Position = texPagesPtr; + reader.AbsPosition = texPagesPtr; count += 1 + UndertaleSimpleResourcesList .UnserializeChildObjectCount(reader); - reader.Position = spritesPtr; + reader.AbsPosition = spritesPtr; count += 1 + UndertaleSimpleResourcesList .UnserializeChildObjectCount(reader); - reader.Position = spineSpritesPtr; + reader.AbsPosition = spineSpritesPtr; count += 1 + UndertaleSimpleResourcesList .UnserializeChildObjectCount(reader); - reader.Position = fontsPtr; + reader.AbsPosition = fontsPtr; count += 1 + UndertaleSimpleResourcesList .UnserializeChildObjectCount(reader); - reader.Position = tilesetsPtr; + reader.AbsPosition = tilesetsPtr; count += 1 + UndertaleSimpleResourcesList .UnserializeChildObjectCount(reader); diff --git a/UndertaleModLib/Models/UndertaleTimeline.cs b/UndertaleModLib/Models/UndertaleTimeline.cs index 45f00947c..c649903a9 100644 --- a/UndertaleModLib/Models/UndertaleTimeline.cs +++ b/UndertaleModLib/Models/UndertaleTimeline.cs @@ -136,7 +136,7 @@ public void Unserialize(UndertaleReader reader) // Read the actions for each moment for (int i = 0; i < momentCount; i++) { - if (reader.Position != unnecessaryPointers[i]) + if (reader.AbsPosition != unnecessaryPointers[i]) throw new UndertaleSerializationException("Invalid action list pointer"); // Read action list and assign time point (put into list) diff --git a/UndertaleModLib/UndertaleChunkTypes.cs b/UndertaleModLib/UndertaleChunkTypes.cs index 7f37dc720..9282419ab 100644 --- a/UndertaleModLib/UndertaleChunkTypes.cs +++ b/UndertaleModLib/UndertaleChunkTypes.cs @@ -86,6 +86,7 @@ public static UndertaleChunk Unserialize(UndertaleReader reader) reader.SubmitMessage("Reading chunk " + chunk.Name); var lenReader = reader.EnsureLengthFromHere(chunk.Length); + reader.CopyChunkToBuffer(length); chunk.UnserializeChunk(reader); if (name != "FORM" && name != reader.LastChunkName) @@ -98,12 +99,12 @@ public static UndertaleChunk Unserialize(UndertaleReader reader) { int e = reader.undertaleData.PaddingAlignException; uint pad = (e == -1 ? 16 : (uint)e); - while (reader.Position % pad != 0) + while (reader.AbsPosition % pad != 0) { if (reader.ReadByte() != 0) { reader.Position -= 1; - if (reader.Position % 4 == 0) + if (reader.AbsPosition % 4 == 0) reader.undertaleData.PaddingAlignException = 4; else reader.undertaleData.PaddingAlignException = 1; @@ -113,6 +114,7 @@ public static UndertaleChunk Unserialize(UndertaleReader reader) } } + reader.SwitchReaderType(false); lenReader.ToHere(); return chunk; @@ -123,7 +125,7 @@ public static UndertaleChunk Unserialize(UndertaleReader reader) } catch (Exception e) { - throw new UndertaleSerializationException(e.Message + "\nat " + reader.Position.ToString("X8") + " while reading chunk " + name, e); + throw new UndertaleSerializationException(e.Message + "\nat " + reader.AbsPosition.ToString("X8") + " while reading chunk " + name, e); } } public static uint CountChunkChildObjects(UndertaleReader reader) @@ -146,8 +148,10 @@ public static uint CountChunkChildObjects(UndertaleReader reader) uint chunkStart = reader.Position; reader.SubmitMessage("Counting objects of chunk " + chunk.Name); + reader.CopyChunkToBuffer(length); uint count = chunk.UnserializeObjectCount(reader); + reader.SwitchReaderType(false); reader.Position = chunkStart + chunk.Length; return count; @@ -158,7 +162,7 @@ public static uint CountChunkChildObjects(UndertaleReader reader) } catch (Exception e) { - throw new UndertaleSerializationException(e.Message + "\nat " + reader.Position.ToString("X8") + " while counting objects of chunk " + name, e); + throw new UndertaleSerializationException(e.Message + "\nat " + reader.AbsPosition.ToString("X8") + " while counting objects of chunk " + name, e); } } } @@ -284,7 +288,7 @@ internal override void UnserializeChunk(UndertaleReader reader) { if (Align) { - while (reader.Position % Alignment != 0) + while (reader.AbsPosition % Alignment != 0) if (reader.ReadByte() != 0) throw new IOException("AlignUpdatedListChunk padding error"); } diff --git a/UndertaleModLib/UndertaleChunks.cs b/UndertaleModLib/UndertaleChunks.cs index 0b864f7f9..d5565d3c4 100644 --- a/UndertaleModLib/UndertaleChunks.cs +++ b/UndertaleModLib/UndertaleChunks.cs @@ -162,22 +162,22 @@ private void CheckFor2022_6(UndertaleReader reader) uint firstExtPtr = reader.ReadUInt32(); uint firstExtEndPtr = (extCount >= 2) ? reader.ReadUInt32() /* second ptr */ : (returnPosition + this.Length); - reader.Position = firstExtPtr + 12; + reader.AbsPosition = firstExtPtr + 12; uint newPointer1 = reader.ReadUInt32(); uint newPointer2 = reader.ReadUInt32(); - if (newPointer1 != reader.Position) + if (newPointer1 != reader.AbsPosition) definitely2022_6 = false; // first pointer mismatch - else if (newPointer2 <= reader.Position || newPointer2 >= (returnPosition + this.Length)) + else if (newPointer2 <= reader.AbsPosition || newPointer2 >= (returnPosition + this.Length)) definitely2022_6 = false; // second pointer out of bounds else { // Check ending position - reader.Position = newPointer2; + reader.AbsPosition = newPointer2; uint optionCount = reader.ReadUInt32(); if (optionCount > 0) { - long newOffsetCheck = reader.Position + (4 * (optionCount - 1)); + long newOffsetCheck = reader.AbsPosition + (4 * (optionCount - 1)); if (newOffsetCheck >= (returnPosition + this.Length)) { // Option count would place us out of bounds @@ -194,7 +194,7 @@ private void CheckFor2022_6(UndertaleReader reader) } else { - reader.Position = (uint)newOffsetCheck; + reader.AbsPosition = (uint)newOffsetCheck; } } } @@ -203,10 +203,10 @@ private void CheckFor2022_6(UndertaleReader reader) if (extCount == 1) { reader.Position += 16; // skip GUID data (only one of them) - if (reader.Position % 16 != 0) - reader.Position += 16 - (reader.Position % 16); // align to chunk end + if (reader.AbsPosition % 16 != 0) + reader.Position += 16 - (reader.AbsPosition % 16); // align to chunk end } - if (reader.Position != firstExtEndPtr) + if (reader.AbsPosition != firstExtEndPtr) definitely2022_6 = false; } } @@ -335,7 +335,7 @@ internal override void UnserializeChunk(UndertaleReader reader) { reader.Position -= 4; int chunkLength = reader.ReadInt32(); - uint chunkEnd = reader.Position + (uint)chunkLength; + uint chunkEnd = reader.AbsPosition + (uint)chunkLength; uint beginPosition = reader.Position; @@ -388,7 +388,7 @@ private void CheckForGM2022_2(UndertaleReader reader) if (reader.ReadUInt32() > 0) // Font count { uint firstFontPointer = reader.ReadUInt32(); - reader.Position = firstFontPointer + 48; // There are 48 bytes of existing metadata. + reader.AbsPosition = firstFontPointer + 48; // There are 48 bytes of existing metadata. uint glyphsLength = reader.ReadUInt32(); reader.undertaleData.GMS2022_2 = true; if ((glyphsLength * 4) > this.Length) @@ -402,7 +402,7 @@ private void CheckForGM2022_2(UndertaleReader reader) glyphPointers.Add(reader.ReadUInt32()); foreach (uint pointer in glyphPointers) { - if (reader.Position != pointer) + if (reader.AbsPosition != pointer) { reader.undertaleData.GMS2022_2 = false; break; @@ -475,7 +475,7 @@ private void CheckFor2022_5(UndertaleReader reader) if (reader.ReadUInt32() > 0) // Object count { uint firstObjectPointer = reader.ReadUInt32(); - reader.Position = firstObjectPointer + 64; + reader.AbsPosition = firstObjectPointer + 64; uint vertexCount = reader.ReadUInt32(); // If any of these checks fail, it's 2022.5 @@ -489,7 +489,7 @@ private void CheckFor2022_5(UndertaleReader reader) { uint subEventPointer = reader.ReadUInt32(); // Should start right after the list - if (reader.Position + 56 == subEventPointer) + if (reader.AbsPosition + 56 == subEventPointer) reader.undertaleData.GM2022_5 = false; } } @@ -574,12 +574,12 @@ private void CheckForEffectData(UndertaleReader reader) // Advance to room data we're interested in (and grab pointer for next room) reader.Position = returnTo + 4 + (4 * roomIndex); uint roomPtr = (uint)reader.ReadInt32(); - reader.Position = roomPtr + (22 * 4); + reader.AbsPosition = roomPtr + (22 * 4); // Get the pointer for this room's layer list, as well as pointer to sequence list uint layerListPtr = (uint)reader.ReadInt32(); int seqnPtr = reader.ReadInt32(); - reader.Position = layerListPtr; + reader.AbsPosition = layerListPtr; int layerCount = reader.ReadInt32(); if (layerCount >= 1) { @@ -594,25 +594,25 @@ private void CheckForEffectData(UndertaleReader reader) nextOffset = reader.ReadInt32(); // (pointer to next element in the layer list) // Actually perform the length checks, depending on layer data - reader.Position = jumpOffset; + reader.AbsPosition = jumpOffset; switch ((LayerType)reader.ReadInt32()) { case LayerType.Background: - if (nextOffset - reader.Position > 16 * 4) + if (nextOffset - reader.AbsPosition > 16 * 4) reader.undertaleData.GMS2022_1 = true; finished = true; break; case LayerType.Instances: reader.Position += 6 * 4; int instanceCount = reader.ReadInt32(); - if (nextOffset - reader.Position != (instanceCount * 4)) + if (nextOffset - reader.AbsPosition != (instanceCount * 4)) reader.undertaleData.GMS2022_1 = true; finished = true; break; case LayerType.Assets: reader.Position += 6 * 4; int tileOffset = reader.ReadInt32(); - if (tileOffset != reader.Position + 8) + if (tileOffset != reader.AbsPosition + 8) reader.undertaleData.GMS2022_1 = true; finished = true; break; @@ -620,14 +620,14 @@ private void CheckForEffectData(UndertaleReader reader) reader.Position += 7 * 4; int tileMapWidth = reader.ReadInt32(); int tileMapHeight = reader.ReadInt32(); - if (nextOffset - reader.Position != (tileMapWidth * tileMapHeight * 4)) + if (nextOffset - reader.AbsPosition != (tileMapWidth * tileMapHeight * 4)) reader.undertaleData.GMS2022_1 = true; finished = true; break; case LayerType.Effect: reader.Position += 7 * 4; int propertyCount = reader.ReadInt32(); - if (nextOffset - reader.Position != (propertyCount * 3 * 4)) + if (nextOffset - reader.AbsPosition != (propertyCount * 3 * 4)) reader.undertaleData.GMS2022_1 = true; finished = true; break; @@ -898,7 +898,7 @@ internal override void UnserializeChunk(UndertaleReader reader) base.UnserializeChunk(reader); // padding - while (reader.Position % 0x80 != 0) + while (reader.AbsPosition % 0x80 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error in STRG"); } @@ -1037,7 +1037,7 @@ internal override void UnserializeChunk(UndertaleReader reader) } // padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); } @@ -1130,7 +1130,7 @@ private void CheckFor2022_8(UndertaleReader reader) { uint tginPtr = reader.ReadUInt32(); uint secondTginPtr = (tginCount >= 2) ? reader.ReadUInt32() : (returnPosition + this.Length); - reader.Position = tginPtr + 4; + reader.AbsPosition = tginPtr + 4; // Check to see if the pointer located at this address points within this object // If not, then we know we're using a new format! @@ -1242,7 +1242,7 @@ internal override void UnserializeChunk(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1260,7 +1260,7 @@ internal override uint UnserializeObjectCount(UndertaleReader reader) checkedForGMS2_3_1 = false; // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1302,7 +1302,7 @@ internal override void UnserializeChunk(UndertaleReader reader) return; // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1323,7 +1323,7 @@ internal override uint UnserializeObjectCount(UndertaleReader reader) return 0; // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1359,7 +1359,7 @@ internal override void UnserializeChunk(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1375,7 +1375,7 @@ internal override uint UnserializeObjectCount(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1410,7 +1410,7 @@ internal override void UnserializeChunk(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1426,7 +1426,7 @@ internal override uint UnserializeObjectCount(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1460,7 +1460,7 @@ internal override void UnserializeChunk(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); @@ -1473,7 +1473,7 @@ internal override uint UnserializeObjectCount(UndertaleReader reader) throw new InvalidOperationException(); // Padding - while (reader.Position % 4 != 0) + while (reader.AbsPosition % 4 != 0) if (reader.ReadByte() != 0) throw new IOException("Padding error!"); diff --git a/UndertaleModLib/UndertaleIO.cs b/UndertaleModLib/UndertaleIO.cs index d107f4bba..60234f20b 100644 --- a/UndertaleModLib/UndertaleIO.cs +++ b/UndertaleModLib/UndertaleIO.cs @@ -145,7 +145,7 @@ public void Unserialize(UndertaleReader reader) } } - public class UndertaleReader : Util.FileBinaryReader + public class UndertaleReader : AdaptiveBinaryReader { /// /// function to delegate warning messages to @@ -557,12 +557,12 @@ public uint GetAddressForUndertaleObject(UndertaleObject obj) try { var expectedAddress = GetAddressForUndertaleObject(obj); - if (expectedAddress != Position) + if (expectedAddress != AbsPosition) { - SubmitWarning("Reading misaligned at " + Position.ToString("X8") + ", realigning back to " + expectedAddress.ToString("X8") + "\nHIGH RISK OF DATA LOSS! The file is probably corrupted, or uses unsupported features\nProceed at your own risk"); - Position = expectedAddress; + SubmitWarning("Reading misaligned at " + AbsPosition.ToString("X8") + ", realigning back to " + expectedAddress.ToString("X8") + "\nHIGH RISK OF DATA LOSS! The file is probably corrupted, or uses unsupported features\nProceed at your own risk"); + AbsPosition = expectedAddress; } - unreadObjects.Remove(Position); + unreadObjects.Remove(AbsPosition); obj.Unserialize(this); } catch (Exception e) @@ -573,7 +573,7 @@ public uint GetAddressForUndertaleObject(UndertaleObject obj) public T ReadUndertaleObject() where T : UndertaleObject, new() { - T obj = GetUndertaleObjectAtAddress(Position); + T obj = GetUndertaleObjectAtAddress(AbsPosition); ReadUndertaleObject(obj); return obj; } @@ -623,7 +623,7 @@ public void ToHere() int diff = (int)expectedLength - (int)length; Console.WriteLine("WARNING: File specified length " + expectedLength + ", but read only " + length + " (" + diff + " padding?)"); if (diff > 0) - reader.Position = reader.Position + (uint)diff; + reader.Position += (uint)diff; else throw new IOException("Read underflow"); } @@ -632,7 +632,7 @@ public void ToHere() public void Align(int alignment, byte paddingbyte = 0x00) { - while ((Position & (alignment - 1)) != paddingbyte) + while ((AbsPosition & (alignment - 1)) != paddingbyte) { DebugUtil.Assert(ReadByte() == paddingbyte, "Invalid alignment padding"); } @@ -644,7 +644,7 @@ public EnsureLengthOperation EnsureLengthFromHere(uint expectedLength) } } - public class UndertaleWriter : Util.FileBinaryWriter + public class UndertaleWriter : FileBinaryWriter { internal UndertaleData undertaleData; diff --git a/UndertaleModLib/UndertaleLists.cs b/UndertaleModLib/UndertaleLists.cs index d510142ab..e10b55f03 100644 --- a/UndertaleModLib/UndertaleLists.cs +++ b/UndertaleModLib/UndertaleLists.cs @@ -336,13 +336,13 @@ public override void Unserialize(UndertaleReader reader) if (Count > 0) { uint pos = reader.GetAddressForUndertaleObject(this[0]); - if (reader.Position != pos) + if (reader.AbsPosition != pos) { - uint skip = pos - reader.Position; + uint skip = pos - reader.AbsPosition; if (skip > 0) { //Console.WriteLine("Skip " + skip + " bytes of blobs"); - reader.Position += skip; + reader.AbsPosition += skip; } else throw new IOException("First list item starts inside the pointer list?!?!"); @@ -396,11 +396,11 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) pointers[i] = reader.ReadUInt32(); uint pos = pointers[0]; - if (reader.Position != pos) + if (reader.AbsPosition != pos) { - uint skip = pos - reader.Position; + uint skip = pos - reader.AbsPosition; if (skip > 0) - reader.Position += skip; + reader.AbsPosition += skip; else throw new IOException("First list item starts inside the pointer list?!?!"); } @@ -410,7 +410,7 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) { try { - reader.Position = pointers[i]; + reader.AbsPosition = pointers[i]; totalCount += 1 + unserializeFunc(reader); } catch (UndertaleSerializationException e) diff --git a/UndertaleModLib/Util/AdaptiveBinaryReader.cs b/UndertaleModLib/Util/AdaptiveBinaryReader.cs new file mode 100644 index 000000000..764c7d8fb --- /dev/null +++ b/UndertaleModLib/Util/AdaptiveBinaryReader.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UndertaleModLib.Util +{ + public interface IBinaryReader : IDisposable + { + public abstract Stream Stream { get; set; } + public abstract long Length { get; } + public abstract uint Position { get; set; } + + public abstract byte ReadByte(); + public virtual bool ReadBoolean() => false; + public abstract string ReadChars(int count); + public abstract byte[] ReadBytes(int count); + public abstract short ReadInt16(); + public abstract ushort ReadUInt16(); + public abstract int ReadInt24(); + public abstract uint ReadUInt24(); + public abstract int ReadInt32(); + public abstract uint ReadUInt32(); + public abstract float ReadSingle(); + public abstract double ReadDouble(); + public abstract long ReadInt64(); + public abstract ulong ReadUInt64(); + public abstract string ReadGMString(); + public abstract void SkipGMString(); + + public virtual void Dispose() { } + } + + public class AdaptiveBinaryReader : IBinaryReader + { + private readonly FileBinaryReader fileBinaryReader; + private readonly BufferBinaryReader bufferBinaryReader; + private IBinaryReader _currentReader; + private bool isUsingBufferReader = false; + private IBinaryReader CurrentReader + { + get => _currentReader; + set + { + _currentReader = value; + isUsingBufferReader = value is BufferBinaryReader; + } + } + + private readonly Encoding encoding = new UTF8Encoding(false); + public Encoding Encoding { get => encoding; } + public Stream Stream { get; set; } + public long Length { get; private set; } + + // I've done some benchmarks, and they show that + // "if..else" is faster than using interfaces here. + // (at least in C# 10) + public uint Position + { + get + { + if (isUsingBufferReader) + return bufferBinaryReader.Position; + else + return fileBinaryReader.Position; + } + set + { + if (isUsingBufferReader) + bufferBinaryReader.Position = value; + else + fileBinaryReader.Position = value; + } + } + public uint AbsPosition + { + get + { + if (isUsingBufferReader) + return fileBinaryReader.Position + bufferBinaryReader.Position - 8; + else + return fileBinaryReader.Position; + } + set + { + if (isUsingBufferReader) + { + if (value > Length) + throw new IOException("Reading out of bounds."); + + bufferBinaryReader.Position = value - fileBinaryReader.Position + 8; + } + else + fileBinaryReader.Position = value; + } + } + + public AdaptiveBinaryReader(Stream stream, Encoding encoding = null) + { + fileBinaryReader = new(stream, encoding); + bufferBinaryReader = new(stream, encoding); + CurrentReader = fileBinaryReader; + + Length = stream.Length; + Stream = stream; + + if (stream.Position != 0) + stream.Seek(0, SeekOrigin.Begin); + + if (encoding is not null) + this.encoding = encoding; + } + + public void CopyChunkToBuffer(uint length) + { + if (length <= 10 * 1024 * 1024) + { + CurrentReader = bufferBinaryReader; + bufferBinaryReader.CopyChunkToBuffer(length); + } + else + CurrentReader = fileBinaryReader; + } + + public void SwitchReaderType(bool isBufferBinaryReader) + { + if (!isBufferBinaryReader && CurrentReader == bufferBinaryReader) + fileBinaryReader.Position = AbsPosition; + + CurrentReader = isBufferBinaryReader ? bufferBinaryReader : fileBinaryReader; + } + + public byte ReadByte() => CurrentReader.ReadByte(); + public virtual bool ReadBoolean() => CurrentReader.ReadBoolean(); + public string ReadChars(int count) => CurrentReader.ReadChars(count); + public byte[] ReadBytes(int count) => CurrentReader.ReadBytes(count); + public short ReadInt16() => CurrentReader.ReadInt16(); + public ushort ReadUInt16() => CurrentReader.ReadUInt16(); + public int ReadInt24() => CurrentReader.ReadInt24(); + public uint ReadUInt24() => CurrentReader.ReadUInt24(); + public int ReadInt32() => CurrentReader.ReadInt32(); + public uint ReadUInt32() => CurrentReader.ReadUInt32(); + public float ReadSingle() => CurrentReader.ReadSingle(); + public double ReadDouble() => CurrentReader.ReadDouble(); + public long ReadInt64() => CurrentReader.ReadInt64(); + public ulong ReadUInt64() => CurrentReader.ReadUInt64(); + public string ReadGMString() => CurrentReader.ReadGMString(); + public void SkipGMString() => CurrentReader.SkipGMString(); + + public void Dispose() + { + if (Stream is not null) + { + Stream.Close(); + Stream.Dispose(); + } + bufferBinaryReader.Dispose(); + } + } +} diff --git a/UndertaleModLib/Util/BufferBinaryReader.cs b/UndertaleModLib/Util/BufferBinaryReader.cs index 7e7a04b70..2d8d526a5 100644 --- a/UndertaleModLib/Util/BufferBinaryReader.cs +++ b/UndertaleModLib/Util/BufferBinaryReader.cs @@ -1,48 +1,166 @@ using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Text; namespace UndertaleModLib.Util { - // Reimplemented based on DogScepter's implementation - public class BufferBinaryReader + // Initial implementation was based on DogScepter's implementation + public class BufferBinaryReader : IBinaryReader { - private readonly byte[] buffer; - private Encoding encoding; - public Encoding Encoding { get => encoding; } - public int Offset { get; set; } - public long Length { get; private set; } - public byte[] Buffer { get => buffer; } + // A faster implementation of "MemoryStream" + private class ChunkBuffer + { + private readonly byte[] _buffer; + + public int Position { get; set; } + public int Length { get; private set; } + public int Capacity { get; set; } + + public ChunkBuffer(int capacity) + { + _buffer = new byte[capacity]; + } + + public int Read(byte[] buffer, int count) + { + int n = Length - Position; + if (n > count) + n = count; + if (n <= 0) + return 0; + + if (n <= 8) + { + int byteCount = n; + while (--byteCount >= 0) + buffer[byteCount] = _buffer[Position + byteCount]; + } + else + Buffer.BlockCopy(_buffer, Position, buffer, 0, n); + Position += n; + + return n; + } + public int Read(Span buffer) + { + int n = Math.Min(Length - Position, buffer.Length); + if (n <= 0) + return 0; + + new Span(_buffer, Position, n).CopyTo(buffer); + + Position += n; + return n; + } + public int ReadByte() + { + if (Position >= Length) + return -1; + + return _buffer[Position++]; + } + + public void Write(byte[] buffer, int count) + { + int i = Position + count; + if (i < 0) + throw new IOException("Writing out of the chunk buffer bounds."); + + // "MemoryStream" also extends the buffer if + // the length becomes greater than the capacity + Length = i; + + if ((count <= 8) && (buffer != _buffer)) + { + int byteCount = count; + while (--byteCount >= 0) + { + _buffer[Position + byteCount] = buffer[byteCount]; + } + } + else + { + Buffer.BlockCopy(buffer, 0, _buffer, Position, count); + } + + Position = i; + } + } + + + private readonly byte[] buffer = new byte[16]; + private readonly ChunkBuffer chunkBuffer; + private readonly byte[] chunkCopyBuffer = new byte[81920]; + + private readonly Encoding encoding = new UTF8Encoding(false); + public Stream Stream { get; set; } + + public long Length { get; } public uint Position { - get => (uint)Offset; - set => Offset = (int)value; + get => (uint)chunkBuffer.Position; + set + { + if (value > chunkBuffer.Length) + throw new IOException("Reading out of the chunk bounds."); + + chunkBuffer.Position = (int)value; + } } - public BufferBinaryReader(Stream stream) + public BufferBinaryReader(Stream stream, Encoding encoding = null) { Length = stream.Length; - buffer = new byte[Length]; - Offset = 0; + Stream = stream; + + // Check data file length + if (Length < 10 * 1024 * 1024) // 10 MB + chunkBuffer = new(5 * 1024 * 1024); + else + chunkBuffer = new(10 * 1024 * 1024); if (stream.Position != 0) stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, (int)Length); - stream.Close(); - encoding = new UTF8Encoding(false); + if (encoding is not null) + this.encoding = encoding; + } + + public void CopyChunkToBuffer(uint length) + { + Stream.Position -= 8; // Chunk name + length + chunkBuffer.Position = 0; + + // Source - https://stackoverflow.com/a/13022108/12136394 + int read; + int remaining = (int)length + 8; + while (remaining > 0 && + (read = Stream.Read(chunkCopyBuffer, 0, Math.Min(chunkCopyBuffer.Length, remaining))) > 0) + { + chunkBuffer.Write(chunkCopyBuffer, read); + remaining -= read; + } + + Stream.Position -= length; + chunkBuffer.Position -= (int)length; + } + private ReadOnlySpan ReadToBuffer(int count) + { + chunkBuffer.Read(buffer, count); + return buffer; } public byte ReadByte() { #if DEBUG - if (Offset < 0 || Offset + 1 > Length) + if (Position + 1 > Length) throw new IOException("Reading out of bounds"); #endif - return buffer[Offset++]; + return (byte)chunkBuffer.ReadByte(); } public virtual bool ReadBoolean() @@ -53,147 +171,169 @@ public virtual bool ReadBoolean() public string ReadChars(int count) { #if DEBUG - if (Offset < 0 || Offset + count > Length) + if (Position + count > Length) throw new IOException("Reading out of bounds"); #endif - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < count; i++) - sb.Append(Convert.ToChar(buffer[Offset++])); - return sb.ToString(); + if (count > 1024) + { + byte[] buf = new byte[count]; + chunkBuffer.Read(buf, count); + + return encoding.GetString(buf); + } + else + { + Span buf = stackalloc byte[count]; + chunkBuffer.Read(buf); + + return encoding.GetString(buf); + } } public byte[] ReadBytes(int count) { #if DEBUG - if (Offset < 0 || Offset + count > Length) + if (Position + count > Length) throw new IOException("Reading out of bounds"); #endif byte[] val = new byte[count]; - System.Buffer.BlockCopy(buffer, Offset, val, 0, count); - Offset += count; + chunkBuffer.Read(val, count); return val; } public short ReadInt16() { #if DEBUG - if (Offset < 0 || Offset + 2 > Length) + if (Position + 2 > Length) throw new IOException("Reading out of bounds"); #endif - return (short)(buffer[Offset++] | buffer[Offset++] << 8); + return BinaryPrimitives.ReadInt16LittleEndian(ReadToBuffer(2)); } public ushort ReadUInt16() { #if DEBUG - if (Offset < 0 || Offset + 2 > Length) + if (Position + 2 > Length) throw new IOException("Reading out of bounds"); #endif - return (ushort)(buffer[Offset++] | buffer[Offset++] << 8); + return BinaryPrimitives.ReadUInt16LittleEndian(ReadToBuffer(2)); } public int ReadInt24() { #if DEBUG - if (Offset < 0 || Offset + 3 > Length) + if (Position + 3 > Length) throw new IOException("Reading out of bounds"); #endif - return (int)(buffer[Offset++] | buffer[Offset++] << 8 | (sbyte)buffer[Offset++] << 16); + ReadToBuffer(3); + return buffer[0] | buffer[1] << 8 | (sbyte)buffer[2] << 16; } public uint ReadUInt24() { #if DEBUG - if (Offset < 0 || Offset + 3 > Length) + if (Position + 3 > Length) throw new IOException("Reading out of bounds"); #endif - return (uint)(buffer[Offset++] | buffer[Offset++] << 8 | buffer[Offset++] << 16); + ReadToBuffer(3); + return (uint)(buffer[0] | buffer[1] << 8 | buffer[2] << 16); } public int ReadInt32() { #if DEBUG - if (Offset < 0 || Offset + 4 > Length) + if (Position + 4 > Length) throw new IOException("Reading out of bounds"); #endif - return (int)(buffer[Offset++] | buffer[Offset++] << 8 | - buffer[Offset++] << 16 | (sbyte)buffer[Offset++] << 24); + return BinaryPrimitives.ReadInt32LittleEndian(ReadToBuffer(4)); } public uint ReadUInt32() { #if DEBUG - if (Offset < 0 || Offset + 4 > Length) + if (Position + 4 > Length) throw new IOException("Reading out of bounds"); #endif - return (uint)(buffer[Offset++] | buffer[Offset++] << 8 | - buffer[Offset++] << 16 | buffer[Offset++] << 24); + return BinaryPrimitives.ReadUInt32LittleEndian(ReadToBuffer(4)); } public float ReadSingle() { #if DEBUG - if (Offset < 0 || Offset + 4 > Length) + if (Position + 4 > Length) throw new IOException("Reading out of bounds"); #endif - float val = BitConverter.ToSingle(buffer, Offset); - Offset += 4; - return val; + return BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(ReadToBuffer(4))); } public double ReadDouble() { #if DEBUG - if (Offset < 0 || Offset + 8 > Length) + if (Position + 8 > Length) throw new IOException("Reading out of bounds"); #endif - double val = BitConverter.ToDouble(buffer, Offset); - Offset += 8; - return val; + return BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(ReadToBuffer(8))); } public long ReadInt64() { #if DEBUG - if (Offset < 0 || Offset + 8 > Length) + if (Position + 8 > Length) throw new IOException("Reading out of bounds"); #endif - long val = BitConverter.ToInt64(buffer, Offset); - Offset += 8; - return val; + return BinaryPrimitives.ReadInt64LittleEndian(ReadToBuffer(8)); } public ulong ReadUInt64() { #if DEBUG - if (Offset < 0 || Offset + 8 > Length) + if (Position + 8 > Length) throw new IOException("Reading out of bounds"); #endif - ulong val = BitConverter.ToUInt64(buffer, Offset); - Offset += 8; - return val; + return BinaryPrimitives.ReadUInt64LittleEndian(ReadToBuffer(8)); } public string ReadGMString() { #if DEBUG - if (Offset < 0 || Offset + 8 > Length) + if (Position + 5 > Length) throw new IOException("Reading out of bounds"); #endif - int length = (int)(buffer[Offset++] | buffer[Offset++] << 8 | buffer[Offset++] << 16 | buffer[Offset++] << 24); + int length = BinaryPrimitives.ReadInt32LittleEndian(ReadToBuffer(4)); #if DEBUG - if (Offset + length + 1 >= Length) + if (Position + length + 1 >= Length) throw new IOException("Reading out of bounds"); #endif - string res = encoding.GetString(buffer, Offset, length); + string res; + if (length > 1024) + { + byte[] buf = new byte[length]; + chunkBuffer.Read(buf, length); + res = encoding.GetString(buf); + } + else + { + Span buf = stackalloc byte[length]; + chunkBuffer.Read(buf); + res = encoding.GetString(buf); + } + #if DEBUG - Offset += length; - if (buffer[Offset++] != 0) + if (ReadByte() != 0) throw new IOException("String not null terminated!"); #else - Offset += length + 1; + Position++; #endif return res; } + public void SkipGMString() + { + int length = BinaryPrimitives.ReadInt32LittleEndian(ReadToBuffer(4)); + Position += (uint)length + 1; + } + + public void Dispose() + { + } } } \ No newline at end of file diff --git a/UndertaleModLib/Util/FileBinaryReader.cs b/UndertaleModLib/Util/FileBinaryReader.cs index abf205a15..cf0a18b75 100644 --- a/UndertaleModLib/Util/FileBinaryReader.cs +++ b/UndertaleModLib/Util/FileBinaryReader.cs @@ -7,15 +7,14 @@ namespace UndertaleModLib.Util { // Reimplemented based on DogScepter's implementation - public class FileBinaryReader : IDisposable + public class FileBinaryReader : IBinaryReader { private readonly byte[] buffer = new byte[16]; - private Encoding encoding = new UTF8Encoding(false); - public Encoding Encoding { get => encoding; } + private readonly Encoding encoding = new UTF8Encoding(false); public Stream Stream { get; set; } - public long Length { get; private set; } + public long Length { get; } public uint Position { @@ -33,7 +32,6 @@ public FileBinaryReader(Stream stream, Encoding encoding = null) { Length = stream.Length; Stream = stream; - Position = 0; if (stream.Position != 0) stream.Seek(0, SeekOrigin.Begin); @@ -228,7 +226,7 @@ public void SkipGMString() public void Dispose() { - if (Stream is not null) + if (Stream?.CanRead == true) { Stream.Close(); Stream.Dispose();