diff --git a/PCL.Core/App/Localization/Languages/en-US.xaml b/PCL.Core/App/Localization/Languages/en-US.xaml index c090e7a13..d43ace93a 100644 --- a/PCL.Core/App/Localization/Languages/en-US.xaml +++ b/PCL.Core/App/Localization/Languages/en-US.xaml @@ -1069,6 +1069,13 @@ Export info Export detailed version info for resources Favorite + {0} resource item(s) available + No other instance contains resources available for import + A resource named {0} already exists. Overwrite it? + Confirm resource overwrite + Select source instance + Imported {0} resource item(s) + Import from another instance Install from files Loading resource list Loading resource list @@ -3570,4 +3577,4 @@ Minecraft window loaded: {0} ({1}) Minecraft window maximized: {0} - \ No newline at end of file + diff --git a/PCL.Core/App/Localization/Languages/zh-CN.xaml b/PCL.Core/App/Localization/Languages/zh-CN.xaml index 9bae29f93..c78a5a492 100644 --- a/PCL.Core/App/Localization/Languages/zh-CN.xaml +++ b/PCL.Core/App/Localization/Languages/zh-CN.xaml @@ -1069,6 +1069,13 @@ 导出信息 导出详细的资源的版本信息 收藏 + {0} 个可导入资源 + 没有找到包含可导入资源的其他实例 + 已存在同名资源:{0},是否覆盖? + 资源覆盖确认 + 选择来源实例 + 已导入 {0} 个资源 + 从其他实例导入 从文件安装 正在加载资源列表 正在加载资源列表 @@ -3570,4 +3577,4 @@ Minecraft 窗口已加载:{0}({1}) 已最大化 Minecraft 窗口:{0} - \ No newline at end of file + diff --git a/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml b/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml index 185ead92a..4d6572ed7 100644 --- a/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml +++ b/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml @@ -20,6 +20,8 @@ + + - \ No newline at end of file + diff --git a/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml.cs b/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml.cs index 00ac8ee7d..06869c3e4 100644 --- a/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml.cs +++ b/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml.cs @@ -40,7 +40,9 @@ public PageInstanceCompResource() BtnHintOpen.Click += BtnManageOpen_Click; BtnManageSelectAll.Click += BtnManageSelectAll_Click; BtnManageInstall.Click += BtnManageInstall_Click; + BtnManageImport.Click += BtnManageImport_Click; BtnHintInstall.Click += BtnManageInstall_Click; + BtnHintImport.Click += BtnManageImport_Click; BtnManageInfoExport.Click += BtnManageInfoExport_Click; BtnManageDownload.Click += BtnManageDownload_Click; BtnHintDownload.Click += BtnManageDownload_Click; @@ -117,6 +119,12 @@ public PageInstanceCompResource(ModComp.CompType loadCompType) BtnSelectDisable.Visibility = Visibility.Collapsed; } + if (new[] { ModComp.CompType.Shader, ModComp.CompType.ResourcePack }.Contains(currentCompType)) + { + BtnManageImport.Visibility = Visibility.Visible; + BtnHintImport.Visibility = Visibility.Visible; + } + // 投影文件管理页隐藏下载按钮 if (currentCompType == ModComp.CompType.Schematic) { @@ -136,7 +144,9 @@ public PageInstanceCompResource(ModComp.CompType loadCompType) BtnHintOpen.Click += BtnManageOpen_Click; BtnManageSelectAll.Click += BtnManageSelectAll_Click; BtnManageInstall.Click += BtnManageInstall_Click; + BtnManageImport.Click += BtnManageImport_Click; BtnHintInstall.Click += BtnManageInstall_Click; + BtnHintImport.Click += BtnManageImport_Click; BtnManageDownload.Click += BtnManageDownload_Click; BtnHintDownload.Click += BtnManageDownload_Click; BtnManageInfoExport.Click += BtnManageInfoExport_Click; @@ -1021,6 +1031,147 @@ private void BtnManageInstall_Click(object sender, MouseButtonEventArgs e) InstallCompFiles(fileList, currentCompType, CurrentFolderPath); } + /// + /// 从其他实例导入资源包或光影包。 + /// + private void BtnManageImport_Click(object sender, MouseButtonEventArgs e) + { + var folderName = currentCompType switch + { + ModComp.CompType.ResourcePack => "resourcepacks", + ModComp.CompType.Shader => "shaderpacks", + _ => null + }; + if (folderName is null) return; + + var targetFolder = Path.GetFullPath(Path.Combine(PageInstanceLeft.McInstance.PathIndie, folderName)); + var sources = ModInstanceList.mcInstanceList.Values.SelectMany(list => list) + .Where(instance => instance.state != McInstanceState.Error && + !instance.Equals(PageInstanceLeft.McInstance)) + .Select(instance => + { + var sourceFolder = Path.GetFullPath(Path.Combine(instance.PathIndie, folderName)); + var entries = _GetImportEntries(instance, sourceFolder); + return (Instance: instance, Folder: sourceFolder, Entries: entries); + }) + .Where(source => !source.Folder.Equals(targetFolder, StringComparison.OrdinalIgnoreCase) && + source.Entries.Length > 0) + .OrderBy(source => source.Instance.Name) + .ToList(); + + if (sources.Count == 0) + { + HintService.Hint(Lang.Text("Instance.Resource.Import.NoAvailableInstance")); + return; + } + + var selection = sources.Select(source => (IMyRadio)new MyListItem + { + Title = source.Instance.Name, + Info = Lang.Text("Instance.Resource.Import.ItemCount", source.Entries.Length), + Type = MyListItem.CheckType.RadioBox + }).ToList(); + var selectedIndex = ModMain.MyMsgBoxSelect(selection, + Lang.Text("Instance.Resource.Import.SelectInstance"), + Lang.Text("Common.Action.Confirm"), Lang.Text("Common.Action.Cancel")); + if (selectedIndex is null) return; + + _ImportEntries(sources[selectedIndex.Value].Entries, targetFolder); + } + + private static string[] _GetImportEntries(McInstance instance, string sourceFolder) + { + try + { + return Directory.Exists(sourceFolder) + ? Directory.EnumerateFileSystemEntries(sourceFolder, "*", new EnumerationOptions + { + RecurseSubdirectories = false, + IgnoreInaccessible = false, + AttributesToSkip = FileAttributes.ReparsePoint + }).ToArray() + : []; + } + catch (Exception ex) + { + ModBase.Log(ex, $"读取实例 {instance.Name} 的资源文件失败"); + return []; + } + } + + private void _ImportEntries(IEnumerable entries, string targetFolder) + { + try + { + Directory.CreateDirectory(targetFolder); + var importedCount = 0; + foreach (var sourceEntry in entries) + { + var entryName = Path.GetFileName(Path.TrimEndingDirectorySeparator(sourceEntry)); + var targetEntry = Path.Combine(targetFolder, entryName); + if (File.Exists(targetEntry) || Directory.Exists(targetEntry)) + { + if (ModMain.MyMsgBox( + Lang.Text("Instance.Resource.Import.OverwriteConfirm.Message", entryName), + Lang.Text("Instance.Resource.Import.OverwriteConfirm.Title"), + Lang.Text("Common.Action.Overwrite"), Lang.Text("Common.Action.Cancel")) != 1) + continue; + _DeleteImportTarget(targetEntry); + } + + if (File.Exists(sourceEntry)) + ModBase.CopyFile(sourceEntry, targetEntry); + else if (Directory.Exists(sourceEntry)) + _CopyImportDirectory(sourceEntry, targetEntry); + else + continue; + importedCount += 1; + } + + if (importedCount > 0) + { + HintService.Hint(Lang.Text("Instance.Resource.Import.Success", importedCount), HintType.Success); + ReloadCompFileList(true); + } + } + catch (Exception ex) + { + ModBase.Log(ex, "从其他实例复制资源失败", ModBase.LogLevel.Msgbox, + userSummary: Lang.Text("Instance.Resource.Error.OperationFailed")); + } + } + + private static void _DeleteImportTarget(string targetEntry) + { + if (File.Exists(targetEntry)) + { + File.Delete(targetEntry); + return; + } + + if (!Directory.Exists(targetEntry)) return; + if (File.GetAttributes(targetEntry).HasFlag(FileAttributes.ReparsePoint)) + Directory.Delete(targetEntry); + else + ModBase.DeleteDirectory(targetEntry); + } + + private static void _CopyImportDirectory(string sourceFolder, string targetFolder) + { + Directory.CreateDirectory(targetFolder); + var options = new EnumerationOptions + { + RecurseSubdirectories = true, + IgnoreInaccessible = false, + AttributesToSkip = FileAttributes.ReparsePoint + }; + foreach (var sourceFile in Directory.EnumerateFiles(sourceFolder, "*", options)) + { + var targetFile = Path.Combine(targetFolder, Path.GetRelativePath(sourceFolder, sourceFile)); + ModBase.CopyFile(sourceFile, targetFile); + } + } + /// /// 尝试安装 Mod。 /// 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。