diff --git a/VPet.ModMaker/Models/ModModel/ModInfoModel.cs b/VPet.ModMaker/Models/ModModel/ModInfoModel.cs index eee7dea..207014e 100644 --- a/VPet.ModMaker/Models/ModModel/ModInfoModel.cs +++ b/VPet.ModMaker/Models/ModModel/ModInfoModel.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Media.Imaging; using VPet.ModMaker.Models.ModModel; +using VPet.ModMaker.Views.ModEdit.I18nEdit; using VPet_Simulator.Core; using VPet_Simulator.Windows.Interface; @@ -578,11 +579,42 @@ public class ModInfoModel : I18nModel } } #endregion + /// + /// 关闭 + /// public void Close() { Image.Value.CloseStream(); foreach (var food in Foods) food.Close(); + foreach (var pet in Pets) + pet.Close(); + Current = null; + } + + public void SaveTranslationMod(string path, IEnumerable cultures) + { + // 保存模型信息 + SaveModInfo(path); + // 保存文化数据 + var langPath = Path.Combine(path, "lang"); + Directory.CreateDirectory(langPath); + foreach (var cultureName in cultures) + { + var culturePath = Path.Combine(langPath, cultureName); + Directory.CreateDirectory(culturePath); + var cultureFile = Path.Combine(culturePath, $"{cultureName}.lps"); + File.Create(cultureFile).Close(); + var lps = new LPS(); + foreach (var data in I18nEditWindow.Current.ViewModel.AllI18nDatas) + lps.Add( + new Line( + data.Key, + data.Value.Datas[I18nHelper.Current.CultureNames.IndexOf(cultureName)].Value + ) + ); + File.WriteAllText(cultureFile, lps.ToString()); + } } } diff --git a/VPet.ModMaker/Models/ModModel/PetModel.cs b/VPet.ModMaker/Models/ModModel/PetModel.cs index 42a30bc..5468723 100644 --- a/VPet.ModMaker/Models/ModModel/PetModel.cs +++ b/VPet.ModMaker/Models/ModModel/PetModel.cs @@ -206,7 +206,13 @@ public class PetModel : I18nModel IsSimplePetModel = isSimplePet; } - public void Close() { } + public void Close() + { + foreach (var anime in Animes) + anime.Close(); + foreach (var anime in FoodAnimes) + anime.Close(); + } #region Save /// diff --git a/VPet.ModMaker/Models/Utils.cs b/VPet.ModMaker/Models/Utils.cs index 7b83ebe..8c4575c 100644 --- a/VPet.ModMaker/Models/Utils.cs +++ b/VPet.ModMaker/Models/Utils.cs @@ -47,7 +47,7 @@ public static class Utils /// /// 载入图片至内存流 /// - /// + /// 图片路径 /// public static BitmapImage LoadImageToMemoryStream(string imagePath) { @@ -65,4 +65,25 @@ public static class Utils } return bitmapImage; } + + /// + /// 载入图片至内存流 + /// + /// 图片流 + /// + public static BitmapImage LoadImageToMemoryStream(Stream imageStream) + { + BitmapImage bitmapImage = new(); + bitmapImage.BeginInit(); + try + { + bitmapImage.StreamSource = imageStream; + bitmapImage.DecodePixelWidth = DecodePixelWidth; + } + finally + { + bitmapImage.EndInit(); + } + return bitmapImage; + } } diff --git a/VPet.ModMaker/Resources/NativeResources.cs b/VPet.ModMaker/Resources/NativeResources.cs new file mode 100644 index 0000000..8df8dbf --- /dev/null +++ b/VPet.ModMaker/Resources/NativeResources.cs @@ -0,0 +1,64 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; + +namespace VPet.ModMaker.Resources; + +/// +/// 本地资源 +/// +internal class NativeResources +{ + /// + /// 资源基路径 + /// + public const string ResourcePath = $"{nameof(VPet)}.{nameof(ModMaker)}.{nameof(Resources)}"; + + /// + /// 食物图片 + /// + public const string FoodImage = $"{ResourcePath}.food.png"; + + #region Native + private static readonly Assembly _assembly = Assembly.GetExecutingAssembly(); + + /// + /// 获取资源流 + /// + /// 资源名 + /// 资源流 + public static Stream GetStream(string resourceName) => + _assembly.GetManifestResourceStream(resourceName)!; + + /// + /// 尝试获取资源流 + /// + /// 资源名 + /// 资源流 + /// 成功为 失败为 + public static bool TryGetStream(string resourceName, out Stream resourceStream) + { + resourceStream = null; + if (_assembly.GetManifestResourceStream(resourceName) is not Stream stream) + return false; + resourceStream = stream; + return true; + } + + /// + /// 将流保存至文件 + /// + /// 资源名 + /// 文件路径 + /// 成功为 失败为 + public static bool SaveTo(string resourceName, string path) + { + if (_assembly.GetManifestResourceStream(resourceName) is not Stream stream) + return false; + using var sr = new StreamReader(stream); + using var sw = new StreamWriter(path); + sr.BaseStream.CopyTo(sw.BaseStream); + return true; + } + #endregion +} diff --git a/VPet.ModMaker/Resources/food.png b/VPet.ModMaker/Resources/food.png new file mode 100644 index 0000000..691ea8a Binary files /dev/null and b/VPet.ModMaker/Resources/food.png differ diff --git a/VPet.ModMaker/Styles.xaml b/VPet.ModMaker/Styles.xaml index db4084f..5903eb4 100644 --- a/VPet.ModMaker/Styles.xaml +++ b/VPet.ModMaker/Styles.xaml @@ -94,4 +94,43 @@ + \ No newline at end of file diff --git a/VPet.ModMaker/VPet.ModMaker.csproj b/VPet.ModMaker/VPet.ModMaker.csproj index 6d7f858..70a5348 100644 --- a/VPet.ModMaker/VPet.ModMaker.csproj +++ b/VPet.ModMaker/VPet.ModMaker.csproj @@ -125,6 +125,7 @@ + @@ -144,6 +145,7 @@ + @@ -218,6 +220,9 @@ PetPage.xaml + + SaveTranslationModWindow.xaml + SelectTextPage.xaml @@ -329,6 +334,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -366,6 +375,8 @@ false - + + + \ No newline at end of file diff --git a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs index ff22edd..e170c37 100644 --- a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs +++ b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs @@ -11,6 +11,7 @@ using System.Windows; using System.Windows.Media.Imaging; using VPet.ModMaker.Models; using VPet.ModMaker.Models.ModModel; +using VPet.ModMaker.Resources; using VPet_Simulator.Core; namespace VPet.ModMaker.ViewModels.ModEdit.AnimeEdit; @@ -22,13 +23,12 @@ public class FoodAnimeEditWindowVM /// public PetModel CurrentPet { get; set; } + // TODO: 使用内部资源 /// /// 默认食物图片 /// public static BitmapImage DefaultFoodImage { get; } = - Utils.LoadImageToMemoryStream( - "C:\\Users\\HKW\\Desktop\\TestPicture\\0000_core\\image\\food.png" - ); + Utils.LoadImageToMemoryStream(NativeResources.GetStream(NativeResources.FoodImage)); /// /// 食物图片 @@ -377,16 +377,6 @@ public class FoodAnimeEditWindowVM CurrentFrontImageModel.Value.Close(); CurrentFrontImageModel.Value.Image.Value = newImage; } - - private void ShowFrontImagesPathInfo(FoodImagesPath imagesPath) - { - MessageBox.Show( - "此顶层动画源位于其它位置\n请去源位置修改此动画\n源位置 模式:{0} 索引:{1}".Translate( - imagesPath.Mode, - imagesPath.Index - ) - ); - } #endregion #region BackImageCommand diff --git a/VPet.ModMaker/ViewModels/ModEdit/ModEditWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/ModEditWindowVM.cs index 08c16a1..785c54a 100644 --- a/VPet.ModMaker/ViewModels/ModEdit/ModEditWindowVM.cs +++ b/VPet.ModMaker/ViewModels/ModEdit/ModEditWindowVM.cs @@ -72,12 +72,17 @@ public class ModEditWindowVM /// 编辑多语言内容 /// public ObservableCommand EditI18nCommand { get; } = new(); + + /// + /// 保存为翻译模组 + /// + public ObservableCommand SaveAsTranslationModCommand { get; } = new(); #endregion public ModEditWindowVM(ModEditWindow window) { ModEditWindow = window; - + new I18nEditWindow(); ChangeImageCommand.ExecuteEvent += ChangeImage; AddCultureCommand.ExecuteEvent += AddCulture; EditCultureCommand.ExecuteEvent += EditCulture; @@ -86,17 +91,21 @@ public class ModEditWindowVM SaveCommand.ExecuteEvent += Save; SaveToCommand.ExecuteEvent += SaveTo; + SaveAsTranslationModCommand.ExecuteEvent += SaveAsTranslationMod; + } + + private void SaveAsTranslationMod() + { + if (ValidationData(ModInfo.Value) is false) + return; + var window = new SaveTranslationModWindow(); + window.ShowDialog(); } private void EditI18n() { - if (I18nEditWindow.Instance is not null) - { - I18nEditWindow.Instance.Activate(); - return; - } - var window = new I18nEditWindow(ModInfo.Value); - window.Show(); + I18nEditWindow.Current.Visibility = Visibility.Visible; + I18nEditWindow.Current.Activate(); } /// @@ -105,7 +114,7 @@ public class ModEditWindowVM public void Close() { ModInfo.Value.Image.Value?.StreamSource?.Close(); - I18nEditWindow.Instance?.Close(); + I18nEditWindow.Current?.Close(true); } /// @@ -205,7 +214,7 @@ public class ModEditWindowVM SaveFileDialog saveFileDialog = new() { - Title = "保存 模组信息文件,并在文件夹内保存模组数据".Translate(), + Title = "保存模组信息文件,并在文件夹内保存模组数据".Translate(), Filter = $"LPS文件|*.lps;".Translate(), FileName = "info.lps".Translate() }; diff --git a/VPet.ModMaker/ViewModels/ModEdit/SaveTranslationModWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/SaveTranslationModWindowVM.cs new file mode 100644 index 0000000..55ecbd3 --- /dev/null +++ b/VPet.ModMaker/ViewModels/ModEdit/SaveTranslationModWindowVM.cs @@ -0,0 +1,83 @@ +using HKW.HKWViewModels.SimpleObservable; +using LinePutScript.Localization.WPF; +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using VPet.ModMaker.Models; + +namespace VPet.ModMaker.ViewModels.ModEdit; + +public class SaveTranslationModWindowVM +{ + #region Value + public ObservableValue CheckAll { get; } = new(true); + + public ObservableCollection CheckCultures { get; } = []; + #endregion + + #region Command + public ObservableCommand SaveCommand { get; } = new(); + #endregion + + public SaveTranslationModWindowVM() + { + foreach (var culture in I18nHelper.Current.CultureNames) + { + var model = new CheckCultureModel(); + model.CultureName.Value = culture; + CheckCultures.Add(model); + CheckAll.AddNotifySender(model.IsChecked); + } + CheckAll.SenderPropertyChanged += CheckAll_SenderPropertyChanged; + SaveCommand.ExecuteEvent += Save; + } + + private void CheckAll_SenderPropertyChanged(ObservableValue source, INotifyPropertyChanged sender) + { + var count = 0; + foreach (var model in CheckCultures) + if (model.IsChecked.Value) + count += 1; + + if (count == CheckCultures.Count) + source.Value = true; + else if (count == 0) + source.Value = false; + else + source.Value = null; + } + + public void Save() + { + SaveFileDialog saveFileDialog = new() + { + Title = "保存模组信息文件,并在文件夹内保存模组数据".Translate(), + Filter = $"LPS文件|*.lps;".Translate(), + FileName = "info.lps".Translate() + }; + if (saveFileDialog.ShowDialog() is not true) + return; + try + { + ModInfoModel.Current.SaveTranslationMod(Path.GetDirectoryName(saveFileDialog.FileName), CheckCultures.Where(m => m.IsChecked.Value).Select(m => m.CultureName.Value)); + MessageBox.Show("保存成功".Translate()); + } + catch (Exception ex) + { + MessageBox.Show("保存失败 错误信息:\n{0}".Translate(ex)); + } + } +} + +public class CheckCultureModel +{ + public ObservableValue IsChecked { get; } = new(true); + public ObservableValue CultureName { get; } = new(); +} diff --git a/VPet.ModMaker/ViewModels/ModMakerWindowVM.cs b/VPet.ModMaker/ViewModels/ModMakerWindowVM.cs index cd07ad9..89dbea5 100644 --- a/VPet.ModMaker/ViewModels/ModMakerWindowVM.cs +++ b/VPet.ModMaker/ViewModels/ModMakerWindowVM.cs @@ -202,7 +202,6 @@ public class ModMakerWindowVM SaveHistories(); } ModInfoModel.Current.Close(); - ModInfoModel.Current = null; I18nHelper.Current = new(); ModMakerWindow.Show(); }; diff --git a/VPet.ModMaker/Views/ModEdit/I18nEdit/I18nEditWindow.xaml.cs b/VPet.ModMaker/Views/ModEdit/I18nEdit/I18nEditWindow.xaml.cs index d46f1d2..3d6d4e6 100644 --- a/VPet.ModMaker/Views/ModEdit/I18nEdit/I18nEditWindow.xaml.cs +++ b/VPet.ModMaker/Views/ModEdit/I18nEdit/I18nEditWindow.xaml.cs @@ -20,28 +20,41 @@ public partial class I18nEditWindow : WindowX { public bool IsCancel { get; private set; } = true; - public static I18nEditWindow Instance { get; private set; } + public static I18nEditWindow Current { get; private set; } public I18nEditWindowVM ViewModel => (I18nEditWindowVM)DataContext; - public I18nEditWindow(ModInfoModel model) + public I18nEditWindow() { InitializeComponent(); DataContext = new I18nEditWindowVM(); ViewModel.CultureChanged += ViewModel_CultureChanged; - ViewModel.InitializeI18nData(model); - Instance = this; + ViewModel.InitializeI18nData(ModInfoModel.Current); + Current = this; + // 只隐藏, 不关闭 + Closing += (s, e) => + { + Visibility = Visibility.Hidden; + if (_close is false) + e.Cancel = true; + }; Closed += (s, e) => { ViewModel.Close(); try { DataContext = null; - Instance = null; + Current = null; } catch { } }; } + private bool _close = false; + public void Close(bool close) + { + _close = close; + Close(); + } private void ViewModel_CultureChanged(object sender, string newCulture) { @@ -60,7 +73,7 @@ public partial class I18nEditWindow : WindowX /// /// (culture, Column) /// - private readonly Dictionary _dataGridI18nColumns = new(); + private readonly Dictionary _dataGridI18nColumns = []; /// /// 添加文化列 diff --git a/VPet.ModMaker/Views/ModEdit/ModEditWindow.xaml b/VPet.ModMaker/Views/ModEdit/ModEditWindow.xaml index fae1b72..296e180 100644 --- a/VPet.ModMaker/Views/ModEdit/ModEditWindow.xaml +++ b/VPet.ModMaker/Views/ModEdit/ModEditWindow.xaml @@ -305,20 +305,30 @@