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];