Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe02874536 | ||
|
|
f0e60b418f | ||
|
|
7b79485ccc | ||
|
|
5ce853bc5f | ||
|
|
9178e9ad72 | ||
|
|
ce9a8e11e0 | ||
|
|
080a727530 | ||
|
|
a410cb1d22 |
@@ -7,8 +7,8 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<ApplicationIcon>LL_logo.ico</ApplicationIcon>
|
||||
<AssemblyVersion>2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.0.0</FileVersion>
|
||||
<AssemblyVersion>2.2.0</AssemblyVersion>
|
||||
<FileVersion>2.2.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -110,10 +110,13 @@ namespace LincleLINK
|
||||
dialog.Dispose();
|
||||
}
|
||||
|
||||
public void MakeInstance(object o)
|
||||
public async void MakeInstance(object o)
|
||||
{
|
||||
LogList.Add("Validating...");
|
||||
if (ValidateInstance())
|
||||
UIEnabled = false;
|
||||
bool validate_result = await Task.Run(() => ValidateInstance());
|
||||
UIEnabled = true;
|
||||
if (validate_result)
|
||||
{
|
||||
LogList.Add("Good to go!");
|
||||
try
|
||||
@@ -128,7 +131,7 @@ namespace LincleLINK
|
||||
}
|
||||
|
||||
public bool ValidateInstance()
|
||||
{
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(InstanceName))
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show("Instance name cannot be empty.",
|
||||
@@ -174,6 +177,42 @@ namespace LincleLINK
|
||||
}
|
||||
}
|
||||
|
||||
// check if there is enough space for copying
|
||||
if (IsCopyChecked)
|
||||
{
|
||||
DirectoryInfo files = new(DataPath);
|
||||
long size_to_copy = 0;
|
||||
foreach (var file in files.GetFiles("*", SearchOption.AllDirectories))
|
||||
{
|
||||
size_to_copy += file.Length;
|
||||
}
|
||||
|
||||
var drives = DriveInfo.GetDrives();
|
||||
long free_space = 0;
|
||||
foreach (var drive in drives)
|
||||
{
|
||||
if (MainWindowLogic.currentDir.StartsWith(drive.Name))
|
||||
{
|
||||
free_space = drive.AvailableFreeSpace;
|
||||
}
|
||||
}
|
||||
|
||||
if (size_to_copy + 100000000 > free_space) // add a 100MB wiggle room
|
||||
{
|
||||
if (MessageBox.Show($"Current drive is low on disk space, do you want to continue? " +
|
||||
$"Free space: {Instance.ReadableSize(free_space)}, Size of files about to copy: {Instance.ReadableSize(size_to_copy)}", "Low disk space!",
|
||||
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace LincleLINK
|
||||
{
|
||||
public static readonly string currentDir = Directory.GetCurrentDirectory();
|
||||
//public static readonly string currentDir = @"F:\LincleLINK";
|
||||
//public static readonly string currentDir = @"F:\LinkTool";
|
||||
public static readonly string dbDir = Path.Join(currentDir, "db");
|
||||
public static readonly string instanceDir = Path.Join(currentDir, "instance");
|
||||
|
||||
@@ -179,6 +180,17 @@ namespace LincleLINK
|
||||
}
|
||||
}
|
||||
|
||||
private string _freeSpace;
|
||||
public string FreeSpace
|
||||
{
|
||||
get { return _freeSpace; }
|
||||
set
|
||||
{
|
||||
_freeSpace = value;
|
||||
OnPropertyChanged(nameof(FreeSpace));
|
||||
}
|
||||
}
|
||||
|
||||
public SynchronizationContext UIContext { get; set; }
|
||||
public Dictionary<PassedFileInfo, HashSet<long>>? FilePieceMap { get; set; }
|
||||
public List<long>? BadPieces { get; set; }
|
||||
@@ -193,6 +205,7 @@ namespace LincleLINK
|
||||
public RelayCommand FolderBrowseTorrentFileCommand { get; set; }
|
||||
public RelayCommand FolderBrowseTorrentDLPathCommand { get; set; }
|
||||
public AsyncRelayCommand LinkToTorrentCommand { get; set; }
|
||||
public RelayCommand CopyFilesCommand { get; set; }
|
||||
|
||||
public MainWindowLogic(MainWindowControls controls, SynchronizationContext uicontext)
|
||||
{
|
||||
@@ -209,6 +222,7 @@ namespace LincleLINK
|
||||
CheckPiecesCommand = new(CheckPieces, (ex) => LogList.Add(ex.Message));
|
||||
FolderBrowseTorrentFileCommand = new(FolderBrowseTorrentPath);
|
||||
FolderBrowseTorrentDLPathCommand = new(FolderBrowserTorrentDLPath);
|
||||
CopyFilesCommand = new(CopyFiles, CanDoActionWithInstance);
|
||||
LinkToTorrentCommand = new(LinkToTorrent, (ex) => LogList.Add(ex.Message));
|
||||
MatchedList = new();
|
||||
RelativePath = string.Empty;
|
||||
@@ -216,13 +230,19 @@ namespace LincleLINK
|
||||
IsFree = true;
|
||||
}
|
||||
|
||||
public async Task<Instance> GetSelectedInstance()
|
||||
{
|
||||
using FileStream openStream = File.OpenRead(Path.Combine(instanceDir, SelectedInstance.InstanceName + ".json"));
|
||||
return await JsonSerializer.DeserializeAsync<Instance>(openStream);
|
||||
}
|
||||
|
||||
public void OpenAddInstanceWindow(object o)
|
||||
{
|
||||
AddInstanceWindow window = new();
|
||||
window.ShowDialog();
|
||||
UpdateInstanceList();
|
||||
}
|
||||
|
||||
|
||||
public void DeleteInstance(object o)
|
||||
{
|
||||
try
|
||||
@@ -408,6 +428,7 @@ namespace LincleLINK
|
||||
}
|
||||
IsFree = true;
|
||||
}
|
||||
|
||||
public bool CanDoActionWithInstance(object o)
|
||||
{
|
||||
if (SelectedInstance == null || !IsFree)
|
||||
@@ -744,6 +765,58 @@ namespace LincleLINK
|
||||
instTotal += inst.TotalFileSize;
|
||||
}
|
||||
Savings = Instance.ReadableSize(instTotal - dbSize);
|
||||
|
||||
var drives = DriveInfo.GetDrives();
|
||||
foreach (var drive in drives)
|
||||
{
|
||||
if (currentDir.StartsWith(drive.Name))
|
||||
{
|
||||
FreeSpace = Instance.ReadableSize(drive.AvailableFreeSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void CopyFiles(object o)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsFree = false;
|
||||
|
||||
if (MessageBox.Show("This will copy all hashed files of an instance to your selected destination. " +
|
||||
"Does not overwrite files. Proceed to destination selection?", "Copy files", MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question) == DialogResult.Yes)
|
||||
{
|
||||
FolderBrowserDialog folderDialog = new();
|
||||
if (folderDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string destDir = folderDialog.SelectedPath;
|
||||
Progress = 0;
|
||||
ProgressStep = 100D / SelectedInstance.FileCount;
|
||||
|
||||
Instance instance = await GetSelectedInstance();
|
||||
foreach (var instanceFile in instance.FileList)
|
||||
{
|
||||
string destinationFile = Path.Combine(destDir, instanceFile.HashedFileName);
|
||||
if (!File.Exists(destinationFile))
|
||||
{
|
||||
await Task.Run(() => File.Copy(Path.Combine(dbDir, instanceFile.HashedFileName), destinationFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogList.Add(destinationFile + " already exists.");
|
||||
}
|
||||
Progress += ProgressStep;
|
||||
}
|
||||
}
|
||||
folderDialog.Dispose();
|
||||
}
|
||||
Progress = 0;
|
||||
IsFree = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogList.Add(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<ColumnDefinition Width="1*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="1*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="1*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="1*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
@@ -47,8 +48,9 @@
|
||||
|
||||
<Button Grid.Column="0" Content="Add instance" Command="{Binding OpenAddInstanceCommand}" IsEnabled="{Binding IsFree}"/>
|
||||
<Button Grid.Column="1" Content="Link files" Command="{Binding CreateHardLinksCommand}"/>
|
||||
<Button Grid.Column="2" Content="Check for unused files" Command="{Binding CheckUnusedCommand}" IsEnabled="{Binding IsFree}"></Button>
|
||||
<Button Grid.Column="3" Content="Delete instance" Command="{Binding DeleteInstanceCommand}"></Button>
|
||||
<Button Grid.Column="2" Content="Copy hashed files" Command="{Binding CopyFilesCommand}"/>
|
||||
<Button Grid.Column="3" Content="Check for unused files" Command="{Binding CheckUnusedCommand}" IsEnabled="{Binding IsFree}"></Button>
|
||||
<Button Grid.Column="4" Content="Delete instance" Command="{Binding DeleteInstanceCommand}"></Button>
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -158,11 +160,12 @@
|
||||
<RowDefinition Height="20"></RowDefinition>
|
||||
<RowDefinition Height="20"></RowDefinition>
|
||||
<RowDefinition Height="20"></RowDefinition>
|
||||
<RowDefinition Height="20"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="{Binding DBSize, StringFormat=db folder size: {0}}"></TextBlock>
|
||||
<TextBlock Grid.Row="1" Text="{Binding Savings, StringFormat=You are saving: {0}}"></TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="2" Text="{Binding FreeSpace, StringFormat=Free drive space {0}}"></TextBlock>
|
||||
<Button Grid.Row="3" Content="Import DBInfo.xml" IsEnabled="{Binding IsFree}" Command="{Binding ImportLegacyCommand}"></Button>
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
# WPFLinkTool
|
||||
# LincleLINK
|
||||
|
||||
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.
|
||||
# How does it work?
|
||||
|
||||

|
||||
## File storage
|
||||
The tool's main purpose is to store all your data in its own `db` folder. The files are renamed to their MD5 hash which ensures only unique files are added to the `db` folder.
|
||||
|
||||
## Instances
|
||||
Instances are simply a record of: file names, their hashes and their location whithin the original data structure. When adding an instance, you have an option of choosing to copy or move files to the `db`. Moving is advised to reduce space and disk stress. ***It is highly recommended to only add the `data` folder of a game as an instance.***
|
||||
Here's an example of one file record:
|
||||
```
|
||||
{
|
||||
"FileName": "25063_pre.2dx",
|
||||
"RelativePath": "sound\\25063",
|
||||
"FileSize": 463806,
|
||||
"HashedFileName": "7AFE6AC1B80128D44BA5357D4349B21A.2dx"
|
||||
},
|
||||
```
|
||||
|
||||
## Linking
|
||||
When you've added at least one instance, you can link it to your desired destination. The tool will make hard links and keep the original file names and directory structure of the original files. ***Hard links only work on the same partition. If you wish to edit/replace a hard-linked file, delete it first! Directly modifying hard-linked files will alter the originals in the `db`!***
|
||||
|
||||
## Link to torrent
|
||||
This feature was designed to allow you to prepare files for downloading a new style/version of a game. It uses your selected instance as a "base" and checks files against the pieces found in the `.torrent` file (https://en.wikipedia.org/wiki/Torrent_file). When the check is finished, you can link matched files to your desired destination and point your torrent client to check and download missing files. Using this feature ensures only files present in pieces with exact 100% match will be linked, this means your client should not overwrite your original files.
|
||||
|
||||
`Instance to link from` - The instance you want to use as a "base", use the one that will have the most common files with the torrent.
|
||||
`Torrent file path` - The torrent file you wish to download.
|
||||
`Torrent relative data path` - Location of the `data` folder within the torrent file structure. Usually it will be `contents\data\`. Using `Check files` will perform a quick file name and size comarsion and will let you know if you got the right path.
|
||||
`Torrent download and link target location` - Your desired download location. The tool will recreate the folder structure found in the `.torrent` file. This is the location where you want to point your torrent client to download.
|
||||
|
||||

|
||||
|
||||
# How does it save space?
|
||||
|
||||
When adding subsequent instances, only unique files will be added to the `db`. For example say you have IIDX28 added as an instance, then you add your IIDX27 data as well. Only files unique to IIDX27 will be copied/moved, in turn `db` size will increase by about 4GB instead of 60GB.
|
||||
|
||||

|
||||
|
||||
Reference in New Issue
Block a user