From b4f0a99bfaf9d35e45f536a8c4100b1805a65488 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 15 May 2026 19:46:50 +0000 Subject: [PATCH 1/6] Upgrade OpenMcdf from 2.4.1 to 3.1.4 and migrate to v3 API Agent-Logs-Url: https://github.com/CnCNet/xna-cncnet-client/sessions/8e91dcf4-2a0a-477c-8fe2-08120e2a67c5 Co-authored-by: SadPencil <11227602+SadPencil@users.noreply.github.com> --- DXMainClient/Domain/SavedGame.cs | 20 +++++++++++++++----- Directory.Packages.props | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/DXMainClient/Domain/SavedGame.cs b/DXMainClient/Domain/SavedGame.cs index 911ba183a..24f39ddaf 100644 --- a/DXMainClient/Domain/SavedGame.cs +++ b/DXMainClient/Domain/SavedGame.cs @@ -36,15 +36,25 @@ public bool ParseInfo() FileInfo savedGameFileInfo = SafePath.GetFile(ProgramConstants.GamePath, SAVED_GAME_PATH, FileName); using (Stream file = savedGameFileInfo.Open(FileMode.Open, FileAccess.Read)) + using (RootStorage root = RootStorage.Open(file, StorageModeFlags.LeaveOpen)) { - var cf = new CompoundFile(file); + using (CfbStream scenarioDescStream = root.OpenStream("Scenario Description")) + { + byte[] scenarioDescData = new byte[scenarioDescStream.Length]; + scenarioDescStream.ReadExactly(scenarioDescData); + GUIName = System.Text.Encoding.Unicode.GetString(scenarioDescData).TrimEnd(['\0']); + } - GUIName = System.Text.Encoding.Unicode.GetString(cf.RootStorage.GetStream("Scenario Description").GetData()).TrimEnd(['\0']); - try + if (root.TryOpenStream("CustomMissionID", out CfbStream? customMissionIdStream)) { - CustomMissionID = BinaryPrimitives.ReadInt32LittleEndian(cf.RootStorage.GetStream("CustomMissionID").GetData()); + using (customMissionIdStream) + { + byte[] customMissionIdData = new byte[customMissionIdStream.Length]; + customMissionIdStream.ReadExactly(customMissionIdData); + CustomMissionID = BinaryPrimitives.ReadInt32LittleEndian(customMissionIdData); + } } - catch (CFItemNotFound) + else { CustomMissionID = 0; } diff --git a/Directory.Packages.props b/Directory.Packages.props index 6dc351d0c..13ad80dd9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,7 +15,7 @@ - + From 2b8377f3101062083bc926a25de8a6e2e2457ed0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 15 May 2026 19:50:32 +0000 Subject: [PATCH 2/6] Remove unnecessary StorageModeFlags.LeaveOpen Agent-Logs-Url: https://github.com/CnCNet/xna-cncnet-client/sessions/8e91dcf4-2a0a-477c-8fe2-08120e2a67c5 Co-authored-by: SadPencil <11227602+SadPencil@users.noreply.github.com> --- DXMainClient/Domain/SavedGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DXMainClient/Domain/SavedGame.cs b/DXMainClient/Domain/SavedGame.cs index 24f39ddaf..23e6e44fe 100644 --- a/DXMainClient/Domain/SavedGame.cs +++ b/DXMainClient/Domain/SavedGame.cs @@ -36,7 +36,7 @@ public bool ParseInfo() FileInfo savedGameFileInfo = SafePath.GetFile(ProgramConstants.GamePath, SAVED_GAME_PATH, FileName); using (Stream file = savedGameFileInfo.Open(FileMode.Open, FileAccess.Read)) - using (RootStorage root = RootStorage.Open(file, StorageModeFlags.LeaveOpen)) + using (RootStorage root = RootStorage.Open(file)) { using (CfbStream scenarioDescStream = root.OpenStream("Scenario Description")) { From 272beb2272aa0c280c38df15f9efe2076ef6658e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 06:37:10 +0000 Subject: [PATCH 3/6] Handle OpenMcdf stream reads without ReadExactly Agent-Logs-Url: https://github.com/CnCNet/xna-cncnet-client/sessions/c2da22a9-b237-4e29-bdf6-08a0209467f7 Co-authored-by: SadPencil <11227602+SadPencil@users.noreply.github.com> --- DXMainClient/Domain/SavedGame.cs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/DXMainClient/Domain/SavedGame.cs b/DXMainClient/Domain/SavedGame.cs index 23e6e44fe..da9ab6efa 100644 --- a/DXMainClient/Domain/SavedGame.cs +++ b/DXMainClient/Domain/SavedGame.cs @@ -14,6 +14,7 @@ namespace DTAClient.Domain public class SavedGame { const string SAVED_GAME_PATH = "Saved Games/"; + const int MAX_SCENARIO_DESCRIPTION_BYTES = 1024 * 1024; public SavedGame(string fileName) { @@ -40,8 +41,21 @@ public bool ParseInfo() { using (CfbStream scenarioDescStream = root.OpenStream("Scenario Description")) { - byte[] scenarioDescData = new byte[scenarioDescStream.Length]; - scenarioDescStream.ReadExactly(scenarioDescData); + if (scenarioDescStream.Length > MAX_SCENARIO_DESCRIPTION_BYTES) + throw new InvalidDataException($"Scenario Description stream was unexpectedly large: {scenarioDescStream.Length} bytes."); + + int scenarioDescLength = checked((int)scenarioDescStream.Length); + byte[] scenarioDescData = new byte[scenarioDescLength]; + int bytesRead = 0; + while (bytesRead < scenarioDescLength) + { + int readCount = scenarioDescStream.Read(scenarioDescData, bytesRead, scenarioDescLength - bytesRead); + if (readCount == 0) + throw new EndOfStreamException("Unexpected end of stream while reading Scenario Description."); + + bytesRead += readCount; + } + GUIName = System.Text.Encoding.Unicode.GetString(scenarioDescData).TrimEnd(['\0']); } @@ -49,9 +63,11 @@ public bool ParseInfo() { using (customMissionIdStream) { - byte[] customMissionIdData = new byte[customMissionIdStream.Length]; - customMissionIdStream.ReadExactly(customMissionIdData); - CustomMissionID = BinaryPrimitives.ReadInt32LittleEndian(customMissionIdData); + byte[] customMissionIdData = new byte[sizeof(int)]; + int bytesRead = customMissionIdStream.Read(customMissionIdData, 0, customMissionIdData.Length); + CustomMissionID = bytesRead < customMissionIdData.Length + ? 0 + : BinaryPrimitives.ReadInt32LittleEndian(customMissionIdData); } } else From 4a69b0e4590f33a02718e19f2d0b321f33757888 Mon Sep 17 00:00:00 2001 From: SadPencil Date: Sat, 16 May 2026 20:15:50 +0800 Subject: [PATCH 4/6] Update SavedGame.cs --- DXMainClient/Domain/SavedGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DXMainClient/Domain/SavedGame.cs b/DXMainClient/Domain/SavedGame.cs index da9ab6efa..d89475fbb 100644 --- a/DXMainClient/Domain/SavedGame.cs +++ b/DXMainClient/Domain/SavedGame.cs @@ -42,7 +42,7 @@ public bool ParseInfo() using (CfbStream scenarioDescStream = root.OpenStream("Scenario Description")) { if (scenarioDescStream.Length > MAX_SCENARIO_DESCRIPTION_BYTES) - throw new InvalidDataException($"Scenario Description stream was unexpectedly large: {scenarioDescStream.Length} bytes."); + throw new InvalidDataException($"Scenario Description stream was unexpectedly large: {scenarioDescStream.Length} bytes. File: {FileName}"); int scenarioDescLength = checked((int)scenarioDescStream.Length); byte[] scenarioDescData = new byte[scenarioDescLength]; From 384ae64b0e0744f0e5352ef3a8fc6d289a8e17e3 Mon Sep 17 00:00:00 2001 From: SadPencil Date: Sat, 16 May 2026 20:21:11 +0800 Subject: [PATCH 5/6] Update SavedGame.cs --- DXMainClient/Domain/SavedGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DXMainClient/Domain/SavedGame.cs b/DXMainClient/Domain/SavedGame.cs index d89475fbb..aa7502f43 100644 --- a/DXMainClient/Domain/SavedGame.cs +++ b/DXMainClient/Domain/SavedGame.cs @@ -66,7 +66,7 @@ public bool ParseInfo() byte[] customMissionIdData = new byte[sizeof(int)]; int bytesRead = customMissionIdStream.Read(customMissionIdData, 0, customMissionIdData.Length); CustomMissionID = bytesRead < customMissionIdData.Length - ? 0 + ? throw new EndOfStreamException("Unexpected end of stream while reading CustomMissionID.") : BinaryPrimitives.ReadInt32LittleEndian(customMissionIdData); } } From e34320297e4e8edb4a3972a57c25c5eab8b0814e Mon Sep 17 00:00:00 2001 From: SadPencil Date: Sat, 16 May 2026 20:22:38 +0800 Subject: [PATCH 6/6] Update SavedGame.cs --- DXMainClient/Domain/SavedGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DXMainClient/Domain/SavedGame.cs b/DXMainClient/Domain/SavedGame.cs index aa7502f43..672f6a82a 100644 --- a/DXMainClient/Domain/SavedGame.cs +++ b/DXMainClient/Domain/SavedGame.cs @@ -42,7 +42,7 @@ public bool ParseInfo() using (CfbStream scenarioDescStream = root.OpenStream("Scenario Description")) { if (scenarioDescStream.Length > MAX_SCENARIO_DESCRIPTION_BYTES) - throw new InvalidDataException($"Scenario Description stream was unexpectedly large: {scenarioDescStream.Length} bytes. File: {FileName}"); + throw new InvalidDataException($"Scenario Description stream was unexpectedly large: {scenarioDescStream.Length} bytes."); int scenarioDescLength = checked((int)scenarioDescStream.Length); byte[] scenarioDescData = new byte[scenarioDescLength];