Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f32310d05e | ||
|
|
29f7ff3c3b | ||
|
|
da502b93b1 | ||
|
|
94966979c3 | ||
|
|
eed41bbcc7 | ||
|
|
238cb9e984 |
@@ -1,7 +1,7 @@
|
||||
# WPFLinkTool
|
||||
WPF MVVM version of my link tool, faster, better, stronger.
|
||||
|
||||
If you used my old versions, your xml instances won't load but your DB folder will be compatible.
|
||||
Just use the old tools to make links somewhere and add them as instances (copy files) using the WPF version.
|
||||
The main purpose of this tool is to save disk space for AC data (can use it for anything but that was the main goal).
|
||||
You add your games' `data` folder as an instance, then the tool will calculate the MD5 hash of each file and store it in its `DB` folder along with storing each file's location inside the `data` folder (ex. (`\sound\`, `\movie\`). Each new instance will only add unique files to the `DB`.
|
||||
Then you can link your instance to wherever you want and stop copying large amounts of data.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace WPFLinkTool
|
||||
{
|
||||
public class Logger
|
||||
{
|
||||
public ObservableCollection<string> LogEntries;
|
||||
public ScrollViewer Scroller;
|
||||
|
||||
public Logger(ScrollViewer scroller)
|
||||
{
|
||||
LogEntries = new();
|
||||
Scroller = scroller;
|
||||
}
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
LogEntries.Add(DateTime.Now.ToString() + " " + message);
|
||||
Scroller.ScrollToBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
<RowDefinition MaxHeight="24" MinHeight="24"></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition MaxHeight="24" MinHeight="24"></RowDefinition>
|
||||
<RowDefinition MaxHeight="16" MinHeight="16"></RowDefinition>
|
||||
<RowDefinition Height="0.3*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid>
|
||||
@@ -53,7 +53,15 @@
|
||||
</DataGrid>
|
||||
<ProgressBar Grid.Row="3" Value="{Binding Progress}" MinHeight="20"></ProgressBar>
|
||||
|
||||
<TextBlock Grid.Row="4" Text="{Binding HeaderText}"></TextBlock>
|
||||
<ScrollViewer x:Name="Scroller" Grid.Row="4">
|
||||
<ItemsControl Grid.Row="1" ItemsSource="{Binding LogEntries}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Text="{Binding Mode=OneWay}" TextWrapping="Wrap" Background="Transparent" BorderThickness="0" IsReadOnly="True"></TextBox>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace WPFLinkTool
|
||||
{
|
||||
InitializeComponent();
|
||||
SharedViewModel shared = new();
|
||||
DataContext = new MainWindowViewModel(shared);
|
||||
DataContext = new MainWindowViewModel(shared, Scroller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace WPFLinkTool
|
||||
foreach (var file in dBFiles)
|
||||
{
|
||||
await Task.Run(() => File.Delete(dbDir + @"\" + file));
|
||||
//vm.Loggerr.Log($"Deleting {file}");
|
||||
vm.Progress += progressStep;
|
||||
}
|
||||
}
|
||||
@@ -58,6 +59,7 @@ namespace WPFLinkTool
|
||||
vm.UIEnabled = true;
|
||||
vm.LinkButtonEnabled = true;
|
||||
IsExecuting = false;
|
||||
vm.Loggerr.Log("Delete operation finished.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace WPFLinkTool
|
||||
public class MakeHardLinksCommand : AsyncCommandBase
|
||||
{
|
||||
private readonly MainWindowViewModel vm;
|
||||
private bool proceed = false;
|
||||
private bool delet = false;
|
||||
private string target;
|
||||
|
||||
@@ -25,10 +24,8 @@ namespace WPFLinkTool
|
||||
IsExecuting = true;
|
||||
vm.UIEnabled = false;
|
||||
vm.LinkButtonEnabled = false;
|
||||
DataInstance selectedInstance = vm.SelectedInstance;
|
||||
|
||||
|
||||
var prompt = MessageBox.Show($"Creating hard links from {selectedInstance.InstanceName}.\nProceed to link target selection?", "Make hard links", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
var prompt = MessageBox.Show($"Creating hard links from {vm.SelectedInstance.InstanceName}.\nProceed to link target selection?", "Make hard links", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (prompt == DialogResult.Yes)
|
||||
{
|
||||
FolderBrowserDialog dialog = new();
|
||||
@@ -36,55 +33,47 @@ namespace WPFLinkTool
|
||||
{
|
||||
target = dialog.SelectedPath;
|
||||
|
||||
foreach (var file in selectedInstance.InstanceFiles)
|
||||
{
|
||||
if (File.Exists(target + file.Location + @"\" + file.OriginalFileName))
|
||||
{
|
||||
var prompt2 = MessageBox.Show(@$"{file.Location}\{file.OriginalFileName} already exists in the target directory!" + "\n"
|
||||
+ "Do you want to delete all encountered dupes before making hard links? (If you didn't know or forgot, overwriting hard linked files changes the originals) 'No' cancels the whole operation.", "Duplicate files detected!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||
vm.Loggerr.Log("Checking for duplicates...");
|
||||
|
||||
if (prompt2 == DialogResult.Yes)
|
||||
{
|
||||
proceed = true;
|
||||
delet = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool proceed = await Task.Run(() => CheckDupes());
|
||||
|
||||
if (delet)
|
||||
{
|
||||
foreach (var file in selectedInstance.InstanceFiles)
|
||||
foreach (var file in vm.SelectedInstance.InstanceFiles)
|
||||
{
|
||||
if (File.Exists(target + file.Location + @"\" + file.OriginalFileName))
|
||||
{
|
||||
//vm.Loggerr.Log(@$"Deleting {target + file.Location + @"\" + file.OriginalFileName}");
|
||||
File.Delete(target + file.Location + @"\" + file.OriginalFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vm.Loggerr.Log("Linking...");
|
||||
|
||||
if (proceed)
|
||||
{
|
||||
var uniqueFolders = (from folders in selectedInstance.InstanceFiles
|
||||
var uniqueFolders = (from folders in vm.SelectedInstance.InstanceFiles
|
||||
where folders.Location != string.Empty
|
||||
select folders.Location).Distinct();
|
||||
|
||||
double progressStep = 100D / (selectedInstance.InstanceFiles.Count + uniqueFolders.Count());
|
||||
double progressStep = 100D / (vm.SelectedInstance.InstanceFiles.Count + uniqueFolders.Count());
|
||||
|
||||
foreach (var folder in uniqueFolders)
|
||||
{
|
||||
if (!Directory.Exists(target + folder))
|
||||
{
|
||||
//vm.Loggerr.Log($@"Creating directory {target + folder}");
|
||||
await Task.Run(() => Directory.CreateDirectory(target + folder));
|
||||
}
|
||||
vm.Progress += progressStep;
|
||||
}
|
||||
|
||||
foreach (var file in selectedInstance.InstanceFiles)
|
||||
foreach (var file in vm.SelectedInstance.InstanceFiles)
|
||||
{
|
||||
if (!File.Exists(target + file.Location + @"\" + file.OriginalFileName))
|
||||
{
|
||||
//vm.Loggerr.Log($@"Linking {target + file.Location + @"\" + file.OriginalFileName}");
|
||||
await Task.Run(() => CreateHardLink(target + file.Location + @"\" + file.OriginalFileName,
|
||||
dbDir + @"\" + file.HashedFileName, IntPtr.Zero));
|
||||
}
|
||||
@@ -97,13 +86,37 @@ namespace WPFLinkTool
|
||||
|
||||
vm.Progress = 0;
|
||||
|
||||
|
||||
vm.Loggerr.Log("Link operation finished");
|
||||
|
||||
vm.UIEnabled = true;
|
||||
vm.LinkButtonEnabled = true;
|
||||
IsExecuting = false;
|
||||
}
|
||||
|
||||
bool CheckDupes()
|
||||
{
|
||||
foreach (var file in vm.SelectedInstance.InstanceFiles)
|
||||
{
|
||||
if (File.Exists(target + file.Location + @"\" + file.OriginalFileName))
|
||||
{
|
||||
var prompt2 = MessageBox.Show(@$"{file.Location}\{file.OriginalFileName} already exists in the target directory!" + "\n"
|
||||
+ "Do you want to delete all encountered dupes before making hard links? (If you didn't know or forgot, overwriting hard linked files changes the originals). Nothing was linked yet. 'No' cancels the whole operation.", "Duplicate files detected!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||
|
||||
if (prompt2 == DialogResult.Yes)
|
||||
{
|
||||
delet = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
static extern bool CreateHardLink(
|
||||
string lpFileName,
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Xml;
|
||||
@@ -18,6 +19,22 @@ namespace WPFLinkTool
|
||||
{
|
||||
public SharedViewModel Shared;
|
||||
|
||||
public Logger Loggerr;
|
||||
|
||||
public ObservableCollection<string> LogEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
return _logEntries;
|
||||
}
|
||||
set
|
||||
{
|
||||
_logEntries = value;
|
||||
OnPropertyChanged(nameof(LogEntries));
|
||||
}
|
||||
}
|
||||
private ObservableCollection<string> _logEntries;
|
||||
|
||||
private string _dBSize;
|
||||
public string DBSize
|
||||
{
|
||||
@@ -130,18 +147,25 @@ namespace WPFLinkTool
|
||||
public ICommand MakeHardLinksCommand { get; private set; }
|
||||
public ICommand DeleteUnusedCommand { get; private set; }
|
||||
|
||||
public MainWindowViewModel(SharedViewModel shared)
|
||||
public ScrollViewer Scroller;
|
||||
|
||||
public MainWindowViewModel(SharedViewModel shared, ScrollViewer scroller)
|
||||
{
|
||||
if (!Directory.Exists(dbDir))
|
||||
Directory.CreateDirectory(dbDir);
|
||||
Shared = shared;
|
||||
Scroller = scroller;
|
||||
Loggerr = new(Scroller);
|
||||
OpenAddInstanceWindow = new(OpenInstanceWindow);
|
||||
DeleteInstanceCommand = new(DeleteInstance);
|
||||
MakeHardLinksCommand = new MakeHardLinksCommand(this, (ex) => HeaderText = ex.Message);
|
||||
DeleteUnusedCommand = new DeleteUnusedCommand(this, (ex) => HeaderText = ex.Message);
|
||||
MakeHardLinksCommand = new MakeHardLinksCommand(this, (ex) => Loggerr.Log(ex.Message));
|
||||
DeleteUnusedCommand = new DeleteUnusedCommand(this, (ex) => Loggerr.Log(ex.Message));
|
||||
LoadDBInfo();
|
||||
Task.Run(() => UpdateDBSize());
|
||||
Task.Run(() => UpdateSumSize());
|
||||
//HeaderText = "Loading finished, probably... Have a nice day!";
|
||||
LogEntries = Loggerr.LogEntries;
|
||||
Loggerr.Log("Loading finished, probably... Have a nice day!");
|
||||
}
|
||||
|
||||
private void LoadDBInfo()
|
||||
@@ -171,7 +195,7 @@ namespace WPFLinkTool
|
||||
|
||||
public void DeleteInstance(object o)
|
||||
{
|
||||
var prompt = MessageBox.Show($"Delete {SelectedInstance.InstanceName}?", "Delete instance", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
var prompt = MessageBox.Show($"Delete {SelectedInstance.InstanceName}? This will not delete the files.", "Delete instance", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (prompt == DialogResult.Yes)
|
||||
{
|
||||
Shared.Info.InstanceList.Remove(SelectedInstance);
|
||||
@@ -179,6 +203,7 @@ namespace WPFLinkTool
|
||||
LinkButtonEnabled = false;
|
||||
UpdateSumSize();
|
||||
OnPropertyChanged(nameof(InstanceList));
|
||||
Loggerr.Log("Deleting instance operation finished.");
|
||||
}
|
||||
}
|
||||
public void UpdateDBSize()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>otoge.ico</ApplicationIcon>
|
||||
<Version>$(VersionPrefix)</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user