From 9f19845d02c6cac3a668433d154f1fc6985fb498 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 14:54:51 +0000
Subject: [PATCH 1/3] Initial plan
From 505acd192b10bb069b1194e712112ebdc2eb0d51 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 15:03:48 +0000
Subject: [PATCH 2/3] Fix cross-platform path separator issues and improve MD5
variable replacement
Co-authored-by: mrbbbaixue <17956756+mrbbbaixue@users.noreply.github.com>
---
Metadata/metadata.xml | 20 +++++++-------
Ra3.BattleNet.Metadata/Metadata.cs | 44 ++++++++++++++++++++++--------
2 files changed, 42 insertions(+), 22 deletions(-)
diff --git a/Metadata/metadata.xml b/Metadata/metadata.xml
index 0787c57..63ee08e 100644
--- a/Metadata/metadata.xml
+++ b/Metadata/metadata.xml
@@ -1,12 +1,12 @@
-
+
-
-
- ${ENV:CF_PAGES_COMMIT_SHA}
-
-
-
-
-
-
+
+
+ ${ENV:CF_PAGES_COMMIT_SHA}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Ra3.BattleNet.Metadata/Metadata.cs b/Ra3.BattleNet.Metadata/Metadata.cs
index 7ff35e5..560c904 100644
--- a/Ra3.BattleNet.Metadata/Metadata.cs
+++ b/Ra3.BattleNet.Metadata/Metadata.cs
@@ -25,7 +25,12 @@ public Metadata()
{
foreach (System.Collections.DictionaryEntry env in Environment.GetEnvironmentVariables())
{
- _envVars[env.Key.ToString()] = env.Value.ToString();
+ var key = env.Key?.ToString();
+ var value = env.Value?.ToString();
+ if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
+ {
+ _envVars[key] = value;
+ }
}
}
@@ -141,7 +146,7 @@ public void ReplaceVariablesInFile(string filePath)
var root = doc.Root ?? throw new InvalidOperationException("无效的XML结构: 缺少根节点");
// 验证所有Include/Module资源
- ValidateResources(root, Path.GetDirectoryName(filePath));
+ ValidateResources(root, Path.GetDirectoryName(filePath) ?? string.Empty);
// 替换变量
ReplaceVariables(root);
@@ -155,14 +160,16 @@ public void ReplaceVariablesInFile(string filePath)
}
}
- private void ValidateResources(XElement element, string basePath)
+ private void ValidateResources(XElement element, string? basePath)
{
+ if (string.IsNullOrEmpty(basePath)) return;
+
foreach (var include in element.Elements("Include").Concat(element.Elements("Module")))
{
var path = include.Attribute("Path")?.Value ?? include.Attribute("Source")?.Value;
if (string.IsNullOrEmpty(path)) continue;
- var fullPath = Path.Combine(basePath, path.Replace('/', '\\'));
+ var fullPath = Path.Combine(basePath, path.Replace('/', Path.DirectorySeparatorChar));
if (!File.Exists(fullPath))
{
throw new FileNotFoundException($"引用的资源文件不存在: {fullPath} (来自元素: {include.Name.LocalName})");
@@ -207,7 +214,7 @@ private void ParseElement(XElement? element, string currentFilePath)
var includePath = child.Attribute("Path")?.Value ?? child.Attribute("Source")?.Value;
if (!string.IsNullOrEmpty(includePath))
{
- string normalizedPath = includePath.Replace('/', '\\');
+ string normalizedPath = includePath.Replace('/', Path.DirectorySeparatorChar);
var dir = Path.GetDirectoryName(currentFilePath);
if (string.IsNullOrEmpty(dir))
{
@@ -255,7 +262,7 @@ private void ProcessIncludes(XElement element)
{
throw new InvalidOperationException($"无法确定文件目录: {_currentFilePath}");
}
- var fullPath = Path.Combine(dir, source.Replace('/', '\\'));
+ var fullPath = Path.Combine(dir, source.Replace('/', Path.DirectorySeparatorChar));
if (!File.Exists(fullPath)) continue;
var includedDoc = XDocument.Load(fullPath);
@@ -279,7 +286,7 @@ private void ReplaceVariables(XElement element)
{
try
{
- attr.Value = ResolveVariables(attr.Value, element);
+ attr.Value = ResolveVariables(attr.Value, element, attr);
}
catch (Exception ex)
{
@@ -291,7 +298,7 @@ private void ReplaceVariables(XElement element)
{
try
{
- element.Value = ResolveVariables(element.Value, element);
+ element.Value = ResolveVariables(element.Value, element, null);
}
catch (Exception ex)
{
@@ -310,7 +317,7 @@ private void ReplaceVariables(XElement element)
}
}
- private string ResolveVariables(string input, XElement context)
+ private string ResolveVariables(string input, XElement context, XAttribute? currentAttr)
{
return System.Text.RegularExpressions.Regex.Replace(input, @"\$\{(.*?)\}", match =>
{
@@ -328,7 +335,20 @@ private string ResolveVariables(string input, XElement context)
var fileToHash = parts.Length > 1 ? parts[1] : "";
if (string.IsNullOrEmpty(fileToHash))
{
- return ComputeFileHash(_currentFilePath);
+ // ${MD5::} - check if we're in a Hash attribute and there's a Source attribute
+ if (currentAttr != null && currentAttr.Name.LocalName == "Hash")
+ {
+ var sourceAttr = context.Attribute("Source");
+ if (sourceAttr != null && !string.IsNullOrEmpty(sourceAttr.Value))
+ {
+ fileToHash = sourceAttr.Value;
+ }
+ }
+
+ if (string.IsNullOrEmpty(fileToHash))
+ {
+ return ComputeFileHash(_currentFilePath);
+ }
}
var dir = Path.GetDirectoryName(_currentFilePath);
if (string.IsNullOrEmpty(dir))
@@ -443,10 +463,10 @@ private string ComputeFileHash(string filePath)
if (parts.Length == 0)
return null;
- Metadata current = this;
+ Metadata? current = this;
foreach (var part in parts)
{
- current = current._children.FirstOrDefault(c => c.Name == part);
+ current = current?._children.FirstOrDefault(c => c.Name == part);
if (current == null)
return null;
}
From f51629e0f532f5b7018291dd40885325d9fdcd27 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 15:07:22 +0000
Subject: [PATCH 3/3] Add documentation for MD5 variable resolution logic
Co-authored-by: mrbbbaixue <17956756+mrbbbaixue@users.noreply.github.com>
---
Ra3.BattleNet.Metadata/Metadata.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Ra3.BattleNet.Metadata/Metadata.cs b/Ra3.BattleNet.Metadata/Metadata.cs
index 560c904..3f982f3 100644
--- a/Ra3.BattleNet.Metadata/Metadata.cs
+++ b/Ra3.BattleNet.Metadata/Metadata.cs
@@ -335,7 +335,9 @@ private string ResolveVariables(string input, XElement context, XAttribute? curr
var fileToHash = parts.Length > 1 ? parts[1] : "";
if (string.IsNullOrEmpty(fileToHash))
{
- // ${MD5::} - check if we're in a Hash attribute and there's a Source attribute
+ // ${MD5::} - When used in a Hash attribute, compute hash of file in Source attribute
+ // Example:
+ // This will compute the MD5 hash of "file.md"
if (currentAttr != null && currentAttr.Name.LocalName == "Hash")
{
var sourceAttr = context.Attribute("Source");