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 文件,仅用于判断拖拽行为。