Commit 2788ed36 authored by ElderLich's avatar ElderLich

Deck rename bug: case-only changes revert, trailing-dot names report saved but remain unsaved

Renaming a deck with only letter case changes (e.g. aDaaa -> ADaaa) did not persist on Windows because the underlying file casing was not updated. Also, names ending with a dot or space (e.g. DeckName.) could show a successful save message but still be treated as unsaved when exiting, causing confusing save-state mismatch.
parent 07f29ee7
...@@ -290,7 +290,10 @@ namespace MDPro3.Duel.YGOSharp ...@@ -290,7 +290,10 @@ namespace MDPro3.Duel.YGOSharp
var ydk = GetYDK(); var ydk = GetYDK();
try try
{ {
deckName = Path.GetFileNameWithoutExtension(deckName); deckName = NormalizeDeckFileName(deckName);
if (!IsValidDeckFileName(deckName))
return false;
var path = Program.PATH_DECK + (type == string.Empty ? string.Empty : $"{type}/") + deckName + Program.EXPANSION_YDK; var path = Program.PATH_DECK + (type == string.Empty ? string.Empty : $"{type}/") + deckName + Program.EXPANSION_YDK;
var dir = Path.GetDirectoryName(path); var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir)) if (!Directory.Exists(dir))
...@@ -308,6 +311,25 @@ namespace MDPro3.Duel.YGOSharp ...@@ -308,6 +311,25 @@ namespace MDPro3.Duel.YGOSharp
return true; return true;
} }
public static string NormalizeDeckFileName(string deckName)
{
deckName = deckName?.Trim() ?? string.Empty;
if (deckName.EndsWith(Program.EXPANSION_YDK, StringComparison.OrdinalIgnoreCase))
deckName = deckName[..^Program.EXPANSION_YDK.Length];
return deckName;
}
public static bool IsValidDeckFileName(string deckName)
{
if (string.IsNullOrWhiteSpace(deckName))
return false;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
if (deckName.EndsWith(" ", StringComparison.Ordinal) || deckName.EndsWith(".", StringComparison.Ordinal))
return false;
#endif
return deckName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0;
}
public string GetYDK() public string GetYDK()
{ {
var value = deckHint + "\r\n#main"; var value = deckHint + "\r\n#main";
......
...@@ -783,7 +783,8 @@ namespace MDPro3.UI ...@@ -783,7 +783,8 @@ namespace MDPro3.UI
if (condition == Condition.Editable && !deckLoaded) return false; if (condition == Condition.Editable && !deckLoaded) return false;
Deck = FromObjectDeckToCodedDeck(); Deck = FromObjectDeckToCodedDeck();
DeckFileSave(); if (!DeckFileSave())
return false;
SetCondition(Condition.Editable); SetCondition(Condition.Editable);
return true; return true;
} }
...@@ -1293,29 +1294,80 @@ namespace MDPro3.UI ...@@ -1293,29 +1294,80 @@ namespace MDPro3.UI
return DeckLocation.All; return DeckLocation.All;
} }
protected void DeckFileSave() protected bool DeckFileSave()
{ {
try try
{ {
var deckName = GetDeckName(); var deckName = MDPro3.Duel.YGOSharp.Deck.NormalizeDeckFileName(GetDeckName());
// TODO: 检查违法字符。 if (!MDPro3.Duel.YGOSharp.Deck.IsValidDeckFileName(deckName))
throw new InvalidOperationException($"Invalid deck name: \"{GetDeckName()}\"");
Deck.type = deckType; Deck.type = deckType;
Deck.Save(deckName, DateTime.UtcNow);
if (deckName != deckFileName) var oldDeckPath = GetDeckFilePath(deckFileName);
File.Delete(Program.PATH_DECK + this.deckNameWithType + Program.EXPANSION_YDK); if (!Deck.Save(deckName, DateTime.UtcNow))
throw new IOException($"Failed to save deck \"{deckName}\".");
var newDeckPath = GetDeckFilePath(deckName);
if (IsSameDeckPath(oldDeckPath, newDeckPath))
ApplyCaseOnlyRename(oldDeckPath, newDeckPath);
else if (File.Exists(oldDeckPath))
File.Delete(oldDeckPath);
deckFileName = deckName; deckFileName = deckName;
deckNameWithType = deckType == string.Empty ? string.Empty : $"/{deckType}" + deckName; deckNameWithType = deckType == string.Empty ? deckName : $"{deckType}/{deckName}";
InputDeckName.text = deckFileName;
TextDeckName.text = deckFileName;
MessageManager.Toast(InterString.Get("本地卡组「[?]」已保存。", deckName)); MessageManager.Toast(InterString.Get("本地卡组「[?]」已保存。", deckName));
Config.SetConfigDeck(deckName, true); Config.SetConfigDeck(deckName, true);
SetDirty(false); SetDirty(false);
return true;
} }
catch (Exception e) catch (Exception e)
{ {
MessageManager.Toast(InterString.Get("保存失败!")); MessageManager.Toast(InterString.Get("保存失败!"));
MessageManager.Cast(e.Message); MessageManager.Cast(e.Message);
return false;
} }
} }
private string GetDeckFilePath(string deckName)
{
return Program.PATH_DECK + (deckType == string.Empty ? string.Empty : $"{deckType}/")
+ deckName + Program.EXPANSION_YDK;
}
private static bool IsSameDeckPath(string leftPath, string rightPath)
{
var fullLeftPath = Path.GetFullPath(leftPath);
var fullRightPath = Path.GetFullPath(rightPath);
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
return string.Equals(fullLeftPath, fullRightPath, StringComparison.OrdinalIgnoreCase);
#else
return string.Equals(fullLeftPath, fullRightPath, StringComparison.Ordinal);
#endif
}
private static void ApplyCaseOnlyRename(string oldPath, string newPath)
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
var fullOldPath = Path.GetFullPath(oldPath);
var fullNewPath = Path.GetFullPath(newPath);
if (string.Equals(fullOldPath, fullNewPath, StringComparison.Ordinal))
return;
if (!string.Equals(fullOldPath, fullNewPath, StringComparison.OrdinalIgnoreCase))
return;
if (!File.Exists(fullOldPath))
return;
var directory = Path.GetDirectoryName(fullOldPath);
var tempPath = Path.Combine(directory ?? Program.PATH_DECK, $"__mdpro3_casefix_{Guid.NewGuid():N}.tmp");
File.Move(fullOldPath, tempPath);
File.Move(tempPath, fullNewPath);
#endif
}
#region Pickup #region Pickup
private void PrePick() private void PrePick()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment