diff --git a/Mo3ModManager/IO.cs b/Mo3ModManager/IO.cs index b55a630..2a898e9 100644 --- a/Mo3ModManager/IO.cs +++ b/Mo3ModManager/IO.cs @@ -113,13 +113,26 @@ public static bool RemoveEmptyFolders(string directory) } + /// + /// Create hard links for every files, just like copying folders. + /// + /// The source folder. + /// The destination folder. + public static void CreateHardLinksOfFiles(string SrcDirectory, string DestDirectory) + { + CreateHardLinksOfFiles(SrcDirectory, DestDirectory, false, null); + } /// /// Create hard links for every files, just like copying folders. /// /// The source folder. /// The destination folder. /// Whether override files if exist - public static void CreateHardLinksOfFiles(string SrcDirectory, string DestDirectory, bool Override = false) + public static void CreateHardLinksOfFiles(string SrcDirectory, string DestDirectory, bool Override) + { + CreateHardLinksOfFiles(SrcDirectory, DestDirectory, Override); + } + public static void CreateHardLinksOfFiles(string SrcDirectory, string DestDirectory, bool Override, List skipExtensionsWithDotUpper) { Debug.WriteLine("Source: " + SrcDirectory); //Create all folders @@ -137,21 +150,46 @@ public static void CreateHardLinksOfFiles(string SrcDirectory, string DestDirect { string relativeName = srcFullName.Substring(SrcDirectory.Length + 1); string destFullName = Path.Combine(DestDirectory, relativeName); - if (!File.Exists(destFullName)) + + CreateHardLinkOrCopy(destFullName, srcFullName, Override, skipExtensionsWithDotUpper); + //if (!File.Exists(destFullName)) + //{ + // Win32.NativeMethods.CreateHardLinkW(destFullName, srcFullName, IntPtr.Zero); + //} + //else + //{ + // if (Override) + // { + // //File exists + // Debug.WriteLine("Overrided: " + relativeName); + // File.Delete(destFullName); + // Win32.NativeMethods.CreateHardLinkW(destFullName, srcFullName, IntPtr.Zero); + // } + //} + } + } + + private static void CreateHardLinkOrCopy(string destFile, string srcFile, bool Override, List skipExtensionsWithDot) + { + var ext = Path.GetExtension(destFile).ToUpperInvariant(); + if (File.Exists(destFile) && Override) + { + Debug.WriteLine("Overrided: " + destFile); + File.Delete(destFile); + } + + if (!File.Exists(destFile)) { + + if (skipExtensionsWithDot != null && skipExtensionsWithDot.Contains(ext)) { - Win32.NativeMethods.CreateHardLinkW(destFullName, srcFullName, IntPtr.Zero); + File.Copy(srcFile, destFile, Override); } else { - if (Override) - { - //File exists - Debug.WriteLine("Overrided: " + relativeName); - File.Delete(destFullName); - Win32.NativeMethods.CreateHardLinkW(destFullName, srcFullName, IntPtr.Zero); - } + Win32.NativeMethods.CreateHardLinkW(destFile, srcFile, IntPtr.Zero); } - } + } + } } diff --git a/Mo3ModManager/MainWindow.xaml b/Mo3ModManager/MainWindow.xaml index 5636dc2..ef94509 100644 --- a/Mo3ModManager/MainWindow.xaml +++ b/Mo3ModManager/MainWindow.xaml @@ -6,7 +6,7 @@ xmlns:local="clr-namespace:Mo3ModManager" mc:Ignorable="d" FontSize="14" - Title="Mental Omega Mod Manager" Height="450" Width="800" Icon="Icon.ico" WindowStartupLocation="CenterScreen"> + Title="Mental Omega Mod Manager" Height="450" Width="800" Icon="Icon.ico" WindowStartupLocation="CenterScreen" Closing="Window_Closing"> diff --git a/Mo3ModManager/MainWindow.xaml.cs b/Mo3ModManager/MainWindow.xaml.cs index faa53b2..22d6563 100644 --- a/Mo3ModManager/MainWindow.xaml.cs +++ b/Mo3ModManager/MainWindow.xaml.cs @@ -23,7 +23,7 @@ public partial class MainWindow : Window public MainWindow() { - InitializeComponent(); + this.InitializeComponent(); this.Title += " v" + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion; @@ -132,7 +132,7 @@ private void On_ProgProfilesListView_SelectionChanged() private void ProfilesListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { - On_ProgProfilesListView_SelectionChanged(); + this.On_ProgProfilesListView_SelectionChanged(); } private void RunButton_Click(object sender, RoutedEventArgs e) { @@ -145,7 +145,9 @@ private void RunButton_Click(object sender, RoutedEventArgs e) //RunningDirectory = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game-" + Guid.NewGuid().ToString().Substring(0, 8)), RunningDirectory = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game"), - ProfileDirectory = (this.ProfilesListView.SelectedItem as ProfileItem).Directory + //ProfileDirectory = (this.ProfilesListView.SelectedItem as ProfileItem).Directory + ProfileDirectory = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles", (this.ProfilesListView.SelectedItem as ProfileItem).Name, (this.ModTreeView.SelectedItem as ModItem).Node.ID) + }; @@ -245,7 +247,7 @@ private void RenameProfileButton_Click(object sender, RoutedEventArgs e) } System.IO.Directory.Move(selectedItem.Directory, newProfilePath); selectedItem.ReplaceFrom(new ProfileItem(new System.IO.DirectoryInfo(newProfilePath))); - On_ProgProfilesListView_SelectionChanged(); + this.On_ProgProfilesListView_SelectionChanged(); } catch (Exception ex) { @@ -361,5 +363,18 @@ private void InstallModButton_Click(object sender, RoutedEventArgs e) } } + + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + if (!this.IsEnabled) + { + var result = System.Windows.MessageBox.Show(this, "Only close Mod Manager when the game has exited, otherwise you will lose your game data. Click \"Yes\" if you do want to exit now.", "Warning", + System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxImage.Exclamation); + if (result != MessageBoxResult.Yes) + { + e.Cancel = true; + } + } + } } } diff --git a/Mo3ModManager/ModProcessManager.cs b/Mo3ModManager/ModProcessManager.cs index 4500ffe..cf7b93a 100644 --- a/Mo3ModManager/ModProcessManager.cs +++ b/Mo3ModManager/ModProcessManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -48,7 +49,7 @@ public enum ProcessStatus /// Note, if a file was deleted and rebuilt, they will be considered as respective files, and the file will be reserved. /// /// The node - private void CleanNode(Node Node) + private void CleanNode(Node Node,object addition =null) { //source and destination is the same with "PrepareNode" string sourceDirectory = Node.FilesDirectory; @@ -56,6 +57,11 @@ private void CleanNode(Node Node) string destinationDirectory = this.RunningDirectory; //Remove a NTFS hard link is equal to remove a file + Exception ex = null; + if (addition != null) + { + ex = (Exception)addition; + } foreach (string srcFullName in Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories)) { string relativeName = srcFullName.Substring(sourceDirectory.Length + 1); @@ -66,7 +72,15 @@ private void CleanNode(Node Node) if (IO.IsSameFile(srcFullName, destFullName)) { //Debug.WriteLine("Delete file: " + destFullName); - File.Delete(destFullName); + try + { + File.Delete(destFullName); + } + catch (Exception e) + { + ex = e; + } + } else { @@ -79,7 +93,11 @@ private void CleanNode(Node Node) //Recursive for its parent if (!Node.IsRoot) { - this.CleanNode(Node.Parent); + this.CleanNode(Node.Parent,ex); + } + if (ex != null) + { + throw ex; } } @@ -92,7 +110,7 @@ private void PrepareNode(Node Node) string srcDirectory = Node.FilesDirectory; string destDirectory = this.RunningDirectory; - IO.CreateHardLinksOfFiles(srcDirectory, destDirectory); + IO.CreateHardLinksOfFiles(srcDirectory, destDirectory, false, new List() { ".INI" }); //Recursive for its parent if (!Node.IsRoot) @@ -120,7 +138,7 @@ private void RunStep2_RunAndWait() /// The working directory. private void RunStep2_RunAndWait(string Fullname, string Arguments, string WorkingDirectory) { - string commandLine = '"' + Fullname + '"' + (Arguments == string.Empty ? string.Empty : ' ' + Arguments); + string commandLine = '"' + Fullname + '"' + (String.IsNullOrEmpty(Arguments) ? string.Empty : ' ' + Arguments); //See : https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743 @@ -247,6 +265,10 @@ private void SetCompatibility(string Fullname, string Compatibility) private void RunStep1_Prepare() { + if (!Directory.Exists(this.ProfileDirectory)) + { + Directory.CreateDirectory(this.ProfileDirectory); + } Trace.WriteLine("[Note] Clearing Running Directory..."); //RunningDirectory should be empty. Delete all of them otherwise if (Directory.Exists(this.RunningDirectory)) @@ -260,7 +282,7 @@ private void RunStep1_Prepare() Trace.WriteLine("[Note] Create hard links for profiles..."); //Move profiles - IO.CreateHardLinksOfFiles(this.ProfileDirectory, this.RunningDirectory); + IO.CreateHardLinksOfFiles(this.ProfileDirectory, this.RunningDirectory, false, new List() { ".INI" }); Trace.WriteLine("[Note] Create hard links for game files..."); //Create hard links recursively - from leaf node to root @@ -272,6 +294,7 @@ private void RunStep1_Prepare() this.Node.Compatibility); //TODO: Set the registry to avoid firewall + } private void RunStep3_Clean() @@ -290,7 +313,7 @@ private void RunStep3_Clean() { Trace.WriteLine("[Note] Save profiles..."); //Save profiles - IO.CreateHardLinksOfFiles(this.RunningDirectory, this.ProfileDirectory, true); + IO.CreateHardLinksOfFiles(this.RunningDirectory, this.ProfileDirectory, true, new List() { ".INI" }); } IO.ClearDirectory(this.RunningDirectory); @@ -302,44 +325,6 @@ private void RunStep3_Clean() } - /// - /// Do everything to run the game from specified arguments. - /// Run and wait for user's confirmation that the game has exited. Will block the thread. - /// This is a workaround for Windows 7 and earlier. - /// This method MUST be run in the UI thread. - /// - /// The parent window of MessageBox. - public void RunLegacy(System.Windows.Window parent) - { - RunStep1_Prepare(); - - new Process() - { - StartInfo = new ProcessStartInfo() - { - FileName = Path.Combine(this.RunningDirectory, this.Node.MainExecutable), - Arguments = this.Node.Arguments, - WorkingDirectory = this.RunningDirectory - } - }.Start(); - - //sleep for 10s - System.Threading.Thread.Sleep(10000); - System.Windows.MessageBox.Show(parent, "You are still running Windows 7 or earlier.\n It's too old so we can't know whether the game has exited or not.\n Click the OK button when the game has exited.", - "You should consider upgrading to Windows 10", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Exclamation); - System.Windows.MessageBox.Show(parent, "Only click the OK button when the game has exited.", "Double check", - System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Exclamation); - - Trace.WriteLine("[Note] Game exited."); - - //wait for 3s. just in case - System.Threading.Thread.Sleep(3000); - RunStep3_Clean(); - - } - - - /// /// Do everything to run the game from specified arguments. /// Run and wait for user's confirmation that the game has exited. Will not block the thread. @@ -382,6 +367,35 @@ public void RunLegacyAsync(System.Windows.Window parent) System.Windows.MessageBox.Show(parent, "Only click the OK button when the game has exited.", "Double check", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Exclamation); + // notice for dumbs + for (var i = 0; i < 5; ++i) + { + Process[] ps = Process.GetProcesses(); + if (ps.Count() == 0) break; + + foreach (Process p in ps) + { + string exePath; + try { + exePath = Path.GetDirectoryName(p.MainModule.FileName); + } + catch (Exception) + { + continue; + } + + System.Diagnostics.Debug.WriteLine(exePath); + System.Diagnostics.Debug.WriteLine(this.RunningDirectory); + + if (this.RunningDirectory.Contains(exePath)) + { + System.Windows.MessageBox.Show(parent, "Only click the OK button when the game has exited.", "Double check", + System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Exclamation); + break; + } + } + } + Trace.WriteLine("[Note] Game exited."); System.ComponentModel.BackgroundWorker worker2 = new System.ComponentModel.BackgroundWorker(); diff --git a/Mo3ModManager/Properties/AssemblyInfo.cs b/Mo3ModManager/Properties/AssemblyInfo.cs index da6062c..fe6f93f 100644 --- a/Mo3ModManager/Properties/AssemblyInfo.cs +++ b/Mo3ModManager/Properties/AssemblyInfo.cs @@ -52,4 +52,4 @@ // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.1.0.4")] +[assembly: AssemblyFileVersion("1.1.0.5")]