├── netz-bin ├── readme.txt ├── netz.exe ├── zip.dll ├── defcomp.dll ├── subsys.dll ├── net20comp.dll └── license.txt ├── TorrentHardLinkHelper ├── ico2.ico ├── TorrentHardLinkHelper.ico ├── TorrentHardLinkHelper_TemporaryKey.pfx ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── packages.config ├── App.xaml.cs ├── Models │ ├── FolderModel.cs │ ├── FileModel.cs │ └── EntityModel.cs ├── app.config ├── Views │ ├── HardLinkTool.xaml.cs │ ├── MainWindow.xaml.cs │ ├── HardLinkTool.xaml │ └── MainWindow.xaml ├── App.xaml ├── ViewModels │ ├── ViewModelLocator.cs │ └── HardLinkToolViewModel.cs └── TorrentHardLinkHelper.csproj ├── README.md ├── TorrentHardLinkHelper.Test ├── TestTorrents │ ├── [U2].13680.torrent │ └── [U2].14332.torrent ├── HardLinkTest.cs ├── Properties │ └── AssemblyInfo.cs ├── LocateTest.cs ├── TorrentTest.cs └── TorrentHardLinkHelper.Test.csproj ├── TorrentHardLinkHelper.Library ├── Locate │ ├── LocateState.cs │ ├── LinkState.cs │ ├── FileLinkPiece.cs │ ├── FileSystemFileSearcher.cs │ ├── FileSystemFileInfo.cs │ ├── LocateResult.cs │ ├── TorrentFileLink.cs │ ├── HashFileLinkPieces.cs │ └── TorrentFileLocater.cs ├── Messages │ ├── IMessage.cs │ ├── MessageException.cs │ └── Message.cs ├── Common │ ├── EncryptionTypes.cs │ ├── Toolbox.cs │ ├── UriHelper.cs │ └── Check.cs ├── Torrents │ ├── TorrentException.cs │ ├── HashAlgoFactory.cs │ ├── Enums.cs │ ├── RawTrackerTier.cs │ ├── RawTrackerTiers.cs │ ├── Hashes.cs │ ├── InfoHash.cs │ └── TorrentFile.cs ├── Properties │ └── AssemblyInfo.cs ├── BEncoding │ ├── BEncodingException.cs │ ├── RawReader.cs │ ├── BEncodedList.cs │ ├── BEncodedNumber.cs │ ├── BEncodedString.cs │ ├── IBEncodedValue.cs │ └── BEncodedDictionary.cs ├── TorrentHardLinkHelper.Library.csproj └── HardLink │ └── HardLinkHelper.cs ├── package.bat ├── LICENSE ├── TorrentHardLinkHelper.sln └── .gitignore /netz-bin/readme.txt: -------------------------------------------------------------------------------- 1 | http://madebits.com/netz/ 2 | 3 | -------------------------------------------------------------------------------- /netz-bin/netz.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/netz-bin/netz.exe -------------------------------------------------------------------------------- /netz-bin/zip.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/netz-bin/zip.dll -------------------------------------------------------------------------------- /netz-bin/defcomp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/netz-bin/defcomp.dll -------------------------------------------------------------------------------- /netz-bin/subsys.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/netz-bin/subsys.dll -------------------------------------------------------------------------------- /netz-bin/net20comp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/netz-bin/net20comp.dll -------------------------------------------------------------------------------- /TorrentHardLinkHelper/ico2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/TorrentHardLinkHelper/ico2.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TorrentHardLinkHelper 2 | ===================== 3 | 4 | A tool for locating files compared to torrent and make hard links. 5 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/TorrentHardLinkHelper.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/TorrentHardLinkHelper/TorrentHardLinkHelper.ico -------------------------------------------------------------------------------- /netz-bin/license.txt: -------------------------------------------------------------------------------- 1 | .NETZ Copyright (c) 2004-2011 by Vasian Cepa. All rights reserved. 2 | 3 | See complete license at http://madebits.com/netz/download.php#license 4 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/TestTorrents/[U2].13680.torrent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/TorrentHardLinkHelper.Test/TestTorrents/[U2].13680.torrent -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/TestTorrents/[U2].14332.torrent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/TorrentHardLinkHelper.Test/TestTorrents/[U2].14332.torrent -------------------------------------------------------------------------------- /TorrentHardLinkHelper/TorrentHardLinkHelper_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrywong/torrenthardlinkhelper/HEAD/TorrentHardLinkHelper/TorrentHardLinkHelper_TemporaryKey.pfx -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/LocateState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace TorrentHardLinkHelper.Locate 7 | { 8 | public enum LocateState 9 | { 10 | Succeed, 11 | Fail 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/LinkState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace TorrentHardLinkHelper.Locate 7 | { 8 | public enum LinkState 9 | { 10 | None, 11 | Fail, 12 | NeedConfirm, 13 | Located 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Messages/IMessage.cs: -------------------------------------------------------------------------------- 1 | namespace TorrentHardLinkHelper.Messages 2 | { 3 | interface IMessage 4 | { 5 | int ByteLength { get; } 6 | 7 | byte[] Encode(); 8 | int Encode(byte[] buffer, int offset); 9 | 10 | void Decode(byte[] buffer, int offset, int length); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace TorrentHardLinkHelper 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.bat: -------------------------------------------------------------------------------- 1 | netz-bin\netz.exe -s -z TorrentHardLinkHelper\bin\Release\TorrentHardLinkHelper.exe TorrentHardLinkHelper\bin\Release\GalaSoft.MvvmLight.Extras.dll TorrentHardLinkHelper\bin\Release\GalaSoft.MvvmLight.dll TorrentHardLinkHelper\bin\Release\Microsoft.Practices.ServiceLocation.dll TorrentHardLinkHelper\bin\Release\Ookii.Dialogs.Wpf.dll TorrentHardLinkHelper\bin\Release\System.Windows.Interactivity.dll TorrentHardLinkHelper\bin\Release\TorrentHardLinkHelper.Library.dll -so -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Common/EncryptionTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace TorrentHardLinkHelper.Common 7 | { 8 | [Flags] 9 | public enum EncryptionTypes 10 | { 11 | None = 0, 12 | PlainText = 1 << 0, 13 | RC4Header = 1 << 1, 14 | RC4Full = 1 << 2, 15 | All = PlainText | RC4Full | RC4Header 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Models/FolderModel.cs: -------------------------------------------------------------------------------- 1 | namespace TorrentHardLinkHelper.Models 2 | { 3 | public sealed class FolderModel : EntityModel 4 | { 5 | public FolderModel() 6 | { 7 | this.Set(() => this.Located, ref this._locked, false); 8 | this.Set(() => this.Type, ref this._type, "Folder"); 9 | } 10 | 11 | public FolderModel(string folderName) 12 | : this() 13 | { 14 | this.Set(() => this.Name, ref this._name, folderName); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/FileLinkPiece.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace TorrentHardLinkHelper.Locate 7 | { 8 | public class FileLinkPiece 9 | { 10 | public TorrentFileLink FileLink { get; set; } 11 | public ulong StartPos { get; set; } 12 | public ulong ReadLength { get; set; } 13 | 14 | public override string ToString() 15 | { 16 | return this.FileLink + ", startpos: " + this.StartPos + ", length: " + this.ReadLength; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Views/HardLinkTool.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Shapes; 13 | 14 | namespace TorrentHardLinkHelper.Views 15 | { 16 | /// 17 | /// Interaction logic for HardLinkTool.xaml 18 | /// 19 | public partial class HardLinkTool : Window 20 | { 21 | public HardLinkTool() 22 | { 23 | InitializeComponent(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/App.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Views/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | 15 | namespace TorrentHardLinkHelper.Views 16 | { 17 | /// 18 | /// Interaction logic for MainWindow.xaml 19 | /// 20 | public partial class MainWindow : Window 21 | { 22 | public MainWindow() 23 | { 24 | InitializeComponent(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Messages/MessageException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TorrentHardLinkHelper.Messages 4 | { 5 | public class MessageException : Exception 6 | { 7 | public MessageException() 8 | : base() 9 | { 10 | } 11 | 12 | 13 | public MessageException(string message) 14 | : base(message) 15 | { 16 | } 17 | 18 | 19 | public MessageException(string message, Exception innerException) 20 | : base(message, innerException) 21 | { 22 | } 23 | 24 | 25 | public MessageException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 26 | : base(info, context) 27 | { 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/TorrentException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // TorrentException.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | 9 | using System; 10 | 11 | namespace TorrentHardLinkHelper.Torrents 12 | { 13 | [Serializable] 14 | public class TorrentException : Exception 15 | { 16 | public TorrentException() 17 | : base() 18 | { 19 | } 20 | 21 | public TorrentException(string message) 22 | : base(message) 23 | { 24 | } 25 | 26 | public TorrentException(string message, Exception innerException) 27 | : base(message, innerException) 28 | { 29 | } 30 | 31 | public TorrentException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 32 | : base(info, context) 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Models/FileModel.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using TorrentHardLinkHelper.Torrents; 3 | 4 | namespace TorrentHardLinkHelper.Models 5 | { 6 | public sealed class FileModel : EntityModel 7 | { 8 | public FileModel() 9 | { 10 | this.Set(() => this.Located, ref this._locked, false); 11 | this.Set(() => this.Type, ref this._type, "File"); 12 | } 13 | 14 | public FileModel(string fullName) 15 | : this() 16 | { 17 | this.Set(() => this.Name, ref this._name, Path.GetFileName(fullName)); 18 | this.Set(() => this.FullName, ref this._fullName, fullName); 19 | } 20 | 21 | public FileModel(TorrentFile torrentFile) 22 | : this() 23 | { 24 | this.Set(() => this.Name, ref this._name, Path.GetFileName(torrentFile.Path)); 25 | this.Set(() => this.FullName, ref this._fullName, torrentFile.Path); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/FileSystemFileSearcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace TorrentHardLinkHelper.Locate 6 | { 7 | public class FileSystemFileSearcher 8 | { 9 | public static IList SearchFolder(string path) 10 | { 11 | if (path == null) 12 | { 13 | throw new ArgumentNullException("path"); 14 | } 15 | if (!Directory.Exists(path)) 16 | { 17 | throw new DirectoryNotFoundException(path + " cannot be found."); 18 | } 19 | 20 | var fsFileInfos = new List(); 21 | foreach (string fileName in Directory.GetFiles(path, "*", SearchOption.AllDirectories)) 22 | { 23 | var fileInfo = new FileInfo(fileName); 24 | fsFileInfos.Add(new FileSystemFileInfo(fileInfo)); 25 | } 26 | return fsFileInfos; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/HardLinkTest.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace TorrentHardLinkHelper.Test 5 | { 6 | [TestClass] 7 | public class HardLinkTest 8 | { 9 | [TestMethod] 10 | public void TestHardTest() 11 | { 12 | ProcessStartInfo procStartInfo = 13 | new ProcessStartInfo("cmd", "/c " + @"fsutil hardlink create D:\b.cs D:\a.cs"); 14 | 15 | // The following commands are needed to redirect the standard output. 16 | // This means that it will be redirected to the Process.StandardOutput StreamReader. 17 | procStartInfo.RedirectStandardOutput = true; 18 | procStartInfo.UseShellExecute = false; 19 | // Do not create the black window. 20 | procStartInfo.CreateNoWindow = true; 21 | // Now we create a process, assign its ProcessStartInfo and start it 22 | Process proc = new Process(); 23 | proc.StartInfo = procStartInfo; 24 | proc.Start(); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TorrentHardLinkHelper.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // 2 | // Torrent HardLink Helper 3 | // 4 | // Authors: 5 | // Harry Wong 6 | // 7 | // Copyright (C) 2013 Harry Wong 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/FileSystemFileInfo.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace TorrentHardLinkHelper.Locate 4 | { 5 | public class FileSystemFileInfo 6 | { 7 | private string _fileName; 8 | private string _filePath; 9 | private long _length; 10 | private bool _located; 11 | 12 | public FileSystemFileInfo() 13 | { 14 | this._located = false; 15 | } 16 | 17 | public FileSystemFileInfo(FileInfo fileInfo) 18 | { 19 | this._fileName = fileInfo.Name; 20 | this._filePath = fileInfo.FullName; 21 | this._length = fileInfo.Length; 22 | } 23 | 24 | public string FileName 25 | { 26 | get { return this._fileName; } 27 | internal set { this._fileName = value; } 28 | } 29 | 30 | public string FilePath 31 | { 32 | get { return this._filePath; } 33 | internal set { this._filePath = value; } 34 | } 35 | 36 | public long Length 37 | { 38 | get { return this._length; } 39 | internal set { this._length = value; } 40 | } 41 | 42 | public bool Located 43 | { 44 | get { return this._located; } 45 | set { this._located = value; } 46 | } 47 | 48 | public override string ToString() 49 | { 50 | return this._filePath + ", length: " + this._length; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/HashAlgoFactory.cs: -------------------------------------------------------------------------------- 1 | // 2 | // HashAlgoFactory.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2009 Alan McGovern 8 | 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Security.Cryptography; 13 | using TorrentHardLinkHelper.Common; 14 | 15 | namespace TorrentHardLinkHelper.Torrents 16 | { 17 | public static class HashAlgoFactory 18 | { 19 | static Dictionary algos = new Dictionary(); 20 | 21 | static HashAlgoFactory() 22 | { 23 | Register(); 24 | Register(); 25 | } 26 | 27 | public static void Register() 28 | where T : HashAlgorithm 29 | where U : HashAlgorithm 30 | { 31 | Register(typeof(T), typeof(U)); 32 | } 33 | 34 | public static void Register(Type baseType, Type specificType) 35 | { 36 | Check.BaseType(baseType); 37 | Check.SpecificType(specificType); 38 | 39 | lock (algos) 40 | algos[baseType] = specificType; 41 | } 42 | 43 | public static T Create() 44 | where T : HashAlgorithm 45 | { 46 | if (algos.ContainsKey(typeof(T))) 47 | return (T)Activator.CreateInstance(algos[typeof(T)]); 48 | return null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/ViewModels/ViewModelLocator.cs: -------------------------------------------------------------------------------- 1 | using GalaSoft.MvvmLight.Ioc; 2 | using Microsoft.Practices.ServiceLocation; 3 | 4 | namespace TorrentHardLinkHelper.ViewModels 5 | { 6 | /// 7 | /// This class contains static references to all the view models in the 8 | /// application and provides an entry point for the bindings. 9 | /// 10 | public class ViewModelLocator 11 | { 12 | /// 13 | /// Initializes a new instance of the ViewModelLocator class. 14 | /// 15 | public ViewModelLocator() 16 | { 17 | var container = new SimpleIoc(); 18 | container.Register(); 19 | container.Register(); 20 | 21 | //var builder = new ContainerBuilder(); 22 | //builder.RegisterType(); 23 | 24 | //IContainer container = builder.Build(); 25 | //var locator = new AutofacServiceLocator(container); 26 | 27 | ServiceLocator.SetLocatorProvider(() => container); 28 | } 29 | 30 | public MainViewModel Main 31 | { 32 | get { return ServiceLocator.Current.GetInstance(); } 33 | } 34 | 35 | public HardLinkToolViewModel HardLinkTool 36 | { 37 | get { return ServiceLocator.Current.GetInstance(); } 38 | } 39 | 40 | public static void Cleanup() 41 | { 42 | // TODO Clear the ViewModels 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TorrentHardLinkHelper.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TorrentHardLinkHelper.Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("dcee5c8a-e906-4aa1-b059-473505480d0a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TorrentHardLinkHelper.Library")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TorrentHardLinkHelper.Library")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c0378a11-3413-4204-90cb-8aafc86a2147")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/LocateResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace TorrentHardLinkHelper.Locate 5 | { 6 | public class LocateResult 7 | { 8 | private readonly IList _torrentFileLinks; 9 | private readonly LocateState _locateState; 10 | private readonly int _locatedCount; 11 | private readonly int _unlocatedCount; 12 | 13 | public LocateResult(IList torrentFileLinks) 14 | { 15 | this._torrentFileLinks = torrentFileLinks; 16 | this._locateState = this._torrentFileLinks.Any(c => c.State != LinkState.Located) 17 | ? LocateState.Fail 18 | : LocateState.Succeed; 19 | if (this._locateState == LocateState.Succeed) 20 | { 21 | this._locatedCount = this._torrentFileLinks.Count; 22 | this._unlocatedCount = 0; 23 | } 24 | else 25 | { 26 | this._locatedCount = this._torrentFileLinks.Count(c => c.State == LinkState.Located); 27 | this._unlocatedCount = this._torrentFileLinks.Count - this._locatedCount; 28 | } 29 | } 30 | 31 | public IList TorrentFileLinks 32 | { 33 | get { return this._torrentFileLinks; } 34 | } 35 | 36 | public LocateState LocateState 37 | { 38 | get { return this._locateState; } 39 | } 40 | 41 | public int LocatedCount 42 | { 43 | get { return this._locatedCount; } 44 | } 45 | 46 | public int UnlocatedCount 47 | { 48 | get { return this._unlocatedCount; } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/Enums.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Enums.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | 9 | namespace TorrentHardLinkHelper.Torrents 10 | { 11 | public enum ListenerStatus 12 | { 13 | Listening, 14 | PortNotFree, 15 | NotListening 16 | } 17 | 18 | public enum PeerStatus 19 | { 20 | Available, 21 | Connecting, 22 | Connected 23 | } 24 | 25 | public enum Direction 26 | { 27 | None, 28 | Incoming, 29 | Outgoing 30 | } 31 | 32 | public enum TorrentState 33 | { 34 | Stopped, 35 | Paused, 36 | Downloading, 37 | Seeding, 38 | Hashing, 39 | Stopping, 40 | Error, 41 | Metadata 42 | } 43 | 44 | public enum Priority 45 | { 46 | DoNotDownload = 0, 47 | Lowest = 1, 48 | Low = 2, 49 | Normal = 4, 50 | High = 8, 51 | Highest = 16, 52 | Immediate = 32 53 | } 54 | 55 | public enum TrackerState 56 | { 57 | Ok, 58 | Offline, 59 | InvalidResponse 60 | } 61 | 62 | public enum TorrentEvent 63 | { 64 | None, 65 | Started, 66 | Stopped, 67 | Completed 68 | } 69 | 70 | public enum PeerConnectionEvent 71 | { 72 | IncomingConnectionReceived, 73 | OutgoingConnectionCreated, 74 | Disconnected 75 | } 76 | 77 | public enum PieceEvent 78 | { 79 | BlockWriteQueued, 80 | BlockNotRequested, 81 | BlockWrittenToDisk, 82 | HashPassed, 83 | HashFailed 84 | } 85 | 86 | public enum PeerListType 87 | { 88 | NascentPeers, 89 | CandidatePeers, 90 | OptimisticUnchokeCandidatePeers 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TorrentHardLinkHelper", "TorrentHardLinkHelper\TorrentHardLinkHelper.csproj", "{F60A582A-C5E4-4D17-97AB-B30E24F22D08}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TorrentHardLinkHelper.Library", "TorrentHardLinkHelper.Library\TorrentHardLinkHelper.Library.csproj", "{C0E94189-B243-4492-A113-6EB2DE57BCC0}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TorrentHardLinkHelper.Test", "TorrentHardLinkHelper.Test\TorrentHardLinkHelper.Test.csproj", "{103E5401-FBB4-4DFB-9B10-4B40640B6E2D}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F60A582A-C5E4-4D17-97AB-B30E24F22D08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F60A582A-C5E4-4D17-97AB-B30E24F22D08}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F60A582A-C5E4-4D17-97AB-B30E24F22D08}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F60A582A-C5E4-4D17-97AB-B30E24F22D08}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C0E94189-B243-4492-A113-6EB2DE57BCC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C0E94189-B243-4492-A113-6EB2DE57BCC0}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C0E94189-B243-4492-A113-6EB2DE57BCC0}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C0E94189-B243-4492-A113-6EB2DE57BCC0}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {103E5401-FBB4-4DFB-9B10-4B40640B6E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {103E5401-FBB4-4DFB-9B10-4B40640B6E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {103E5401-FBB4-4DFB-9B10-4B40640B6E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {103E5401-FBB4-4DFB-9B10-4B40640B6E2D}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/BEncodingException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BEncodingException.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | 31 | using System; 32 | using System.Text; 33 | using System.Runtime.Serialization; 34 | 35 | namespace TorrentHardLinkHelper.BEncoding 36 | { 37 | [Serializable] 38 | public class BEncodingException : Exception 39 | { 40 | public BEncodingException() 41 | : base() 42 | { 43 | } 44 | 45 | public BEncodingException(string message) 46 | : base(message) 47 | { 48 | } 49 | 50 | public BEncodingException(string message, Exception innerException) 51 | : base(message, innerException) 52 | { 53 | } 54 | 55 | protected BEncodingException(SerializationInfo info, StreamingContext context) 56 | : base(info, context) 57 | { 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/TorrentFileLink.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using TorrentHardLinkHelper.Torrents; 3 | 4 | namespace TorrentHardLinkHelper.Locate 5 | { 6 | public class TorrentFileLink 7 | { 8 | private TorrentFile _torrentFile; 9 | private IList _fsFileInfos; 10 | private int _linkedFsFileIndex; 11 | private LinkState _state; 12 | 13 | public TorrentFileLink() 14 | { 15 | this._fsFileInfos = new List(); 16 | this._linkedFsFileIndex = -1; 17 | this._state = LinkState.None; 18 | } 19 | 20 | public TorrentFileLink(TorrentFile torrentFile) 21 | : this() 22 | { 23 | this._torrentFile = torrentFile; 24 | } 25 | 26 | public TorrentFile TorrentFile 27 | { 28 | get { return this._torrentFile; } 29 | internal set { this._torrentFile = value; } 30 | } 31 | 32 | public IList FsFileInfos 33 | { 34 | get { return this._fsFileInfos; } 35 | internal set { this._fsFileInfos = value; } 36 | } 37 | 38 | public int LinkedFsFileIndex 39 | { 40 | get { return this._linkedFsFileIndex; } 41 | internal set { this._linkedFsFileIndex = value; } 42 | } 43 | 44 | public FileSystemFileInfo LinkedFsFileInfo 45 | { 46 | get 47 | { 48 | if (this._linkedFsFileIndex < 0 || this._linkedFsFileIndex >= this._fsFileInfos.Count) 49 | { 50 | return null; 51 | } 52 | return this._fsFileInfos[this._linkedFsFileIndex]; 53 | } 54 | } 55 | 56 | public LinkState State 57 | { 58 | get { return this._state; } 59 | internal set { this._state = value; } 60 | } 61 | 62 | public override string ToString() 63 | { 64 | return this._torrentFile.FullPath + ", count: " + this._fsFileInfos.Count + ", state: " + this._state; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/LocateTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using TorrentHardLinkHelper.Locate; 8 | using TorrentHardLinkHelper.Torrents; 9 | 10 | namespace TorrentHardLinkHelper.Test 11 | { 12 | [TestClass] 13 | public class LocateTest 14 | { 15 | /// 16 | /// 文件名字改动 17 | /// 18 | [TestMethod] 19 | public void TestLocate1() 20 | { 21 | string sourceFolder = @"I:\[BDMV][アニメ] ココロコネクト"; 22 | string torrentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestTorrents", "[U2].13680.torrent"); 23 | 24 | IList fileInfos = GetFileSystemInfos(sourceFolder); 25 | var torrent = Torrent.Load(torrentPath); 26 | var locater = new TorrentFileLocater(torrent, fileInfos); 27 | 28 | var result = locater.Locate(); 29 | } 30 | 31 | [TestMethod] 32 | public void TestLocate2() 33 | { 34 | string sourceFolder = @"I:\[BDMV][アニメ] ココロコネクト"; 35 | string torrentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestTorrents", "[U2].13680.torrent"); 36 | 37 | IList fileInfos = GetFileSystemInfos(sourceFolder); 38 | var torrent = Torrent.Load(torrentPath); 39 | 40 | var locater = new PrivateObject(typeof(TorrentFileLocater), torrent, fileInfos); 41 | locater.Invoke("FindTorrentFileLinks"); 42 | var result = (bool)locater.Invoke("CheckPiece", 10); 43 | 44 | Assert.IsTrue(result); 45 | } 46 | 47 | private IList GetFileSystemInfos(string path) 48 | { 49 | var fileInfos = 50 | Directory.GetFiles(path, "*", SearchOption.AllDirectories) 51 | .Select(file => new FileInfo(file)) 52 | .Select(f => new FileSystemFileInfo(f)) 53 | .ToList(); 54 | return fileInfos; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("TorrentHardLinkHelper")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("TorrentHardLinkHelper")] 15 | [assembly: AssemblyCopyright("Harry Copyright © 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("0.9.0.0")] 55 | [assembly: AssemblyFileVersion("0.9.0.0")] 56 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/TorrentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using TorrentHardLinkHelper.Torrents; 8 | 9 | namespace TorrentHardLinkHelper.Test 10 | { 11 | /// 12 | /// Summary description for TorrentTest 13 | /// 14 | [TestClass] 15 | public class TorrentTest 16 | { 17 | public TorrentTest() 18 | { 19 | // 20 | // TODO: Add constructor logic here 21 | // 22 | } 23 | 24 | private TestContext testContextInstance; 25 | 26 | /// 27 | ///Gets or sets the test context which provides 28 | ///information about and functionality for the current test run. 29 | /// 30 | public TestContext TestContext 31 | { 32 | get 33 | { 34 | return testContextInstance; 35 | } 36 | set 37 | { 38 | testContextInstance = value; 39 | } 40 | } 41 | 42 | [TestMethod] 43 | public void TestLoadTorrent() 44 | { 45 | string torrentFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestTorrents", "[U2].14332.torrent"); 46 | Torrent torrent = Torrent.Load(torrentFile); 47 | 48 | Console.WriteLine("{0,15}: {1}", "Name", torrent.Name); 49 | Console.WriteLine("{0,15}: {1}", "IsPrivate", torrent.IsPrivate); 50 | Console.WriteLine("{0,15}: {1}", "Pieces", torrent.Pieces.Count); 51 | Console.WriteLine("{0,15}: {1}", "PieceLength", torrent.PieceLength); 52 | Console.WriteLine("{0,15}: {1}", "Files", torrent.Files.Count()); 53 | Console.WriteLine("----------Files------------------"); 54 | foreach (var file in torrent.Files) 55 | { 56 | Console.WriteLine("{0,15}: {1}", "Path", file.Path); 57 | Console.WriteLine("{0,15}: {1}", "Length", file.Length); 58 | Console.WriteLine("{0,15}: {1}", "StartPieceIndex", file.StartPieceIndex); 59 | Console.WriteLine("{0,15}: {1}", "EndPieceIndex", file.EndPieceIndex); 60 | Console.WriteLine("{0,15}: {1}", "EndPieceIndex", file.Priority); 61 | 62 | Console.WriteLine("-----------------------------------------"); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/RawTrackerTier.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TorrentHardLinkHelper.BEncoding; 4 | 5 | namespace TorrentHardLinkHelper.Torrents 6 | { 7 | public class RawTrackerTier : IList 8 | { 9 | public string this[int index] { 10 | get { return ((BEncodedString) Tier [index]).Text; } 11 | set { Tier [index] = new BEncodedString (value );} 12 | } 13 | 14 | internal BEncodedList Tier { 15 | get; set; 16 | } 17 | 18 | public RawTrackerTier () 19 | : this (new BEncodedList ()) 20 | { 21 | } 22 | 23 | public RawTrackerTier (BEncodedList tier) 24 | { 25 | Tier = tier; 26 | } 27 | 28 | public RawTrackerTier (IEnumerable announces) 29 | : this () 30 | { 31 | foreach (var v in announces) 32 | Add (v); 33 | } 34 | 35 | public int IndexOf (string item) 36 | { 37 | return Tier.IndexOf ((BEncodedString) item); 38 | } 39 | 40 | public void Insert (int index, string item) 41 | { 42 | Tier.Insert (index, (BEncodedString) item); 43 | } 44 | 45 | public void RemoveAt (int index) 46 | { 47 | Tier.RemoveAt (index); 48 | } 49 | 50 | public void Add (string item) 51 | { 52 | Tier.Add ((BEncodedString) item); 53 | } 54 | 55 | public void Clear () 56 | { 57 | Tier.Clear (); 58 | } 59 | 60 | public bool Contains (string item) 61 | { 62 | return Tier.Contains ((BEncodedString) item); 63 | } 64 | 65 | public void CopyTo (string[] array, int arrayIndex) 66 | { 67 | foreach (var s in this) 68 | array [arrayIndex ++] = s; 69 | } 70 | 71 | public bool Remove (string item) 72 | { 73 | return Tier.Remove ((BEncodedString) item); 74 | } 75 | 76 | public int Count { 77 | get { return Tier.Count; } 78 | } 79 | 80 | public bool IsReadOnly { 81 | get { return Tier.IsReadOnly; } 82 | } 83 | 84 | public IEnumerator GetEnumerator () 85 | { 86 | foreach (BEncodedString v in Tier) 87 | yield return v.Text; 88 | } 89 | 90 | IEnumerator IEnumerable.GetEnumerator () 91 | { 92 | return GetEnumerator (); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Views/HardLinkTool.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | This tool help create a copy of a folder, the size of file below 1M will be copyed, others will be hard-linked. 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/RawTrackerTiers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TorrentHardLinkHelper.BEncoding; 4 | 5 | namespace TorrentHardLinkHelper.Torrents 6 | { 7 | public class RawTrackerTiers : IList 8 | { 9 | BEncodedList Tiers { 10 | get; set; 11 | } 12 | 13 | public RawTrackerTiers () 14 | : this (new BEncodedList ()) 15 | { 16 | } 17 | 18 | public RawTrackerTiers (BEncodedList tiers) 19 | { 20 | Tiers = tiers; 21 | } 22 | 23 | public int IndexOf (RawTrackerTier item) 24 | { 25 | if (item != null) { 26 | for (int i = 0; i < Tiers.Count; i++) 27 | if (item.Tier == Tiers [i]) 28 | return i; 29 | } 30 | return -1; 31 | } 32 | 33 | public void Insert (int index, RawTrackerTier item) 34 | { 35 | Tiers.Insert (index, item.Tier); 36 | } 37 | 38 | public void RemoveAt (int index) 39 | { 40 | Tiers.RemoveAt (index); 41 | } 42 | 43 | public RawTrackerTier this[int index] { 44 | get { return new RawTrackerTier ((BEncodedList) Tiers [index]); } 45 | set { Tiers [index] = value.Tier; } 46 | } 47 | 48 | public void Add (RawTrackerTier item) 49 | { 50 | Tiers.Add (item.Tier); 51 | } 52 | 53 | public void AddRange (IEnumerable tiers) 54 | { 55 | foreach (var v in tiers) 56 | Add (v); 57 | } 58 | 59 | public void Clear () 60 | { 61 | Tiers.Clear (); 62 | } 63 | 64 | public bool Contains (RawTrackerTier item) 65 | { 66 | return IndexOf (item) != -1; 67 | } 68 | 69 | public void CopyTo (RawTrackerTier[] array, int arrayIndex) 70 | { 71 | foreach (var v in this) 72 | array [arrayIndex ++] = v; 73 | } 74 | 75 | public bool Remove (RawTrackerTier item) 76 | { 77 | int index = IndexOf (item); 78 | if (index != -1) 79 | RemoveAt (index); 80 | 81 | return index != -1; 82 | } 83 | 84 | public int Count { 85 | get { return Tiers.Count; } 86 | } 87 | 88 | public bool IsReadOnly { 89 | get { return Tiers.IsReadOnly; } 90 | } 91 | 92 | public IEnumerator GetEnumerator () 93 | { 94 | foreach (var v in Tiers) 95 | yield return new RawTrackerTier ((BEncodedList) v); 96 | } 97 | 98 | IEnumerator IEnumerable.GetEnumerator () 99 | { 100 | return GetEnumerator (); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TorrentHardLinkHelper.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TorrentHardLinkHelper.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/Hashes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TorrentHardLinkHelper.Torrents 4 | { 5 | public class Hashes 6 | { 7 | #region Constants 8 | /// 9 | /// Hash code length (in bytes) 10 | /// 11 | internal static readonly int HashCodeLength = 20; 12 | #endregion 13 | 14 | 15 | #region Private Fields 16 | 17 | private int count; 18 | private byte[] hashData; 19 | 20 | #endregion Private Fields 21 | 22 | 23 | #region Properties 24 | 25 | /// 26 | /// Number of Hashes (equivalent to number of Pieces) 27 | /// 28 | public int Count 29 | { 30 | get { return this.count; } 31 | } 32 | 33 | #endregion Properties 34 | 35 | 36 | #region Constructors 37 | 38 | internal Hashes(byte[] hashData, int count) 39 | { 40 | this.hashData = hashData; 41 | this.count = count; 42 | } 43 | 44 | #endregion Constructors 45 | 46 | 47 | #region Methods 48 | 49 | /// 50 | /// Determine whether a calculated hash is equal to our stored hash 51 | /// 52 | /// Hash code to check 53 | /// Index of hash/piece to verify against 54 | /// true iff hash is equal to our stored hash, false otherwise 55 | public bool IsValid(byte[] hash, int hashIndex) 56 | { 57 | if (hash == null) 58 | throw new ArgumentNullException("hash"); 59 | 60 | if (hash.Length != HashCodeLength) 61 | throw new ArgumentException(string.Format("Hash must be {0} bytes in length", HashCodeLength), "hash"); 62 | 63 | if (hashIndex < 0 || hashIndex > count) 64 | throw new ArgumentOutOfRangeException("hashIndex", string.Format("hashIndex must be between 0 and {0}", count)); 65 | 66 | int start = hashIndex * HashCodeLength; 67 | for (int i = 0; i < HashCodeLength; i++) 68 | if (hash[i] != this.hashData[i + start]) 69 | return false; 70 | 71 | return true; 72 | } 73 | 74 | /// 75 | /// Returns the hash for a specific piece 76 | /// 77 | /// Piece/hash index to return 78 | /// byte[] (length HashCodeLength) containing hashdata 79 | public byte[] ReadHash(int hashIndex) 80 | { 81 | if (hashIndex < 0 || hashIndex >= count) 82 | throw new ArgumentOutOfRangeException("hashIndex"); 83 | 84 | // Read out our specified piece's hash data 85 | byte[] hash = new byte[HashCodeLength]; 86 | Buffer.BlockCopy(this.hashData, hashIndex * HashCodeLength, hash, 0, HashCodeLength); 87 | 88 | return hash; 89 | } 90 | 91 | #endregion Methods 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # HOW TO USE THIS FILE 2 | 3 | # Installing this package works best if you do a little preparation first. 4 | # Whether you create your initial repository on GitHub, or use the Git Source Control Provider Visual Studio extension, 5 | # a default .gitignore file will already be at the root of your git repository. 6 | # Usually, this means it is directly within your main solution folder, as a sibling to your MyProject.sln file, 7 | # your .git folder, and possibly your packages directory. 8 | 9 | # In the Visual Studio Solution Explorer, right click your Solution node and select `Add > Existing Item`. 10 | # Navigate to the root .gitignore file and add it. 11 | # It will appear in your "Solution Items" folder, but only the path, not the file name. 12 | # This is because the file does not contain a name before the extension. Yay windows. 13 | # Regardless, this provides an easy way to get at your .gitignore file, which you will need to do after installing this package. 14 | 15 | # Now install this package. You will notice that a git.ignore file is added to your project directory (not your solution directory). 16 | # This is another wonderful windows *feature* that prevents nuget packages from copying nameless / extension-only files. 17 | # It doesn't matter though. All you have to do is open this git.ignore file (which you already have done), select all, copy, 18 | # then open your other .gitignore from the Solution Items folder select all, and paste. 19 | # When you are done, REMOVE THIS PACKAGE. You don't need it any more. It's pointless and it's wasting bits. 20 | 21 | # Unless you want to push all of your packages to your remote repository, enable NuGet Package Restore on your solution. 22 | # This .gitignore will make sure that NuGet.exe gets added, and your packages stay off of the remote. 23 | 24 | # Finally, you can delete all of this text to and including the #BEGIN GIT IGNORES line below. 25 | # For more information, see the README at https://github.com/danludwig/VisualStudioGitIgnore 26 | =================================================================================================================================== 27 | 28 | #BEGIN GIT IGNORES 29 | #Ignore thumbnails created by windows 30 | Thumbs.db 31 | 32 | #Ignore files built by Visual Studio 33 | *.obj 34 | *.exe 35 | *.pdb 36 | *.user 37 | *.aps 38 | *.pch 39 | *.vspscc 40 | *_i.c 41 | *_p.c 42 | *.ncb 43 | *.suo 44 | *.tlb 45 | *.tlh 46 | *.bak 47 | *.cache 48 | *.ilk 49 | *.log 50 | [Bb]in 51 | [Dd]ebug*/ 52 | *.lib 53 | *.sbr 54 | obj/ 55 | [Rr]elease*/ 56 | _ReSharper*/ 57 | [Tt]est[Rr]esult* 58 | 59 | #Ignore packages directory 60 | Packages/ 61 | 62 | #Allow NuGet.exe 63 | !NuGet.exe 64 | 65 | #Allow Net-z 66 | !netz.exe 67 | 68 | #Ignore generated nupkg files 69 | *.nupkg 70 | 71 | #Allow released nupkg files 72 | !MyProjectName.1.0.nupkg 73 | 74 | #Ignore local testsettings 75 | Local.testsettings 76 | 77 | #Allow R# .DotSettings 78 | !*.csproj.DotSettings 79 | 80 | #Ignore Azure build csdef & Pubxml files 81 | ServiceDefinition.build.csdef 82 | *.azurePubxml 83 | 84 | #Ignore WebDeploy publish profile 85 | *.Publish.xml 86 | 87 | #Ignore email files delivered to specified pickup directory 88 | *.eml 89 | 90 | #Allow chromedriver.exe 91 | !chromedriver.exe 92 | 93 | #Ignore _vti folders in ExpressionWeb 94 | desktop.ini 95 | _vti_cnf/ 96 | _vti_pvt/ 97 | 98 | #Ignore private folder 99 | /Private/ 100 | /.vs 101 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/TorrentHardLinkHelper.Library.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C0E94189-B243-4492-A113-6EB2DE57BCC0} 8 | Library 9 | Properties 10 | TorrentHardLinkHelper 11 | TorrentHardLinkHelper.Library 12 | v4.0 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 87 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/ViewModels/HardLinkToolViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using GalaSoft.MvvmLight; 4 | using GalaSoft.MvvmLight.Command; 5 | using Ookii.Dialogs.Wpf; 6 | using TorrentHardLinkHelper.HardLink; 7 | 8 | namespace TorrentHardLinkHelper.ViewModels 9 | { 10 | public class HardLinkToolViewModel : ViewModelBase 11 | { 12 | private string _sourceFolder; 13 | private string _parentFolder; 14 | private string _folderName; 15 | 16 | private RelayCommand _selectSourceFolderCommand; 17 | private RelayCommand _selectParentFolderCommand; 18 | private RelayCommand _defaultCommand; 19 | private RelayCommand _linkCommand; 20 | 21 | public HardLinkToolViewModel() 22 | { 23 | this.InitCommands(); 24 | } 25 | 26 | public void InitCommands() 27 | { 28 | this._selectSourceFolderCommand = new RelayCommand(() => 29 | { 30 | var dialog = new VistaFolderBrowserDialog(); 31 | dialog.ShowNewFolderButton = true; 32 | dialog.ShowDialog(); 33 | if (!string.IsNullOrWhiteSpace(dialog.SelectedPath)) 34 | { 35 | this.Set(() => this.SourceFolder, ref this._sourceFolder, dialog.SelectedPath); 36 | this.Set(() => this.ParentFolder, ref this._parentFolder, Directory.GetParent(dialog.SelectedPath).FullName); 37 | this.Set(() => this.FolderName, ref this._folderName, Path.GetFileName(dialog.SelectedPath) + "_Copy"); 38 | } 39 | }); 40 | 41 | this._selectParentFolderCommand = new RelayCommand(() => 42 | { 43 | var dialog = new VistaFolderBrowserDialog(); 44 | dialog.ShowNewFolderButton = true; 45 | dialog.ShowDialog(); 46 | if (dialog.SelectedPath != null) 47 | { 48 | this.Set(() => this.ParentFolder, ref this._parentFolder, dialog.SelectedPath); 49 | } 50 | }); 51 | 52 | this._defaultCommand = new RelayCommand(() => 53 | { 54 | if (string.IsNullOrWhiteSpace(this._sourceFolder)) 55 | { 56 | this.Set(() => this.FolderName, ref this._folderName, ""); 57 | } 58 | else 59 | { 60 | this.Set(() => this.FolderName, ref this._folderName, 61 | Path.GetDirectoryName(_folderName) + "_HLinked"); 62 | } 63 | }); 64 | 65 | this._linkCommand = new RelayCommand(() => 66 | { 67 | var helper = new HardLinkHelper(); 68 | helper.HardLink(this._sourceFolder, this._parentFolder, this._folderName, 1024000); 69 | Process.Start("explorer.exe", Path.Combine(this._parentFolder, this._folderName)); 70 | }, () => !string.IsNullOrWhiteSpace(this._sourceFolder) && !string.IsNullOrWhiteSpace(this._parentFolder) && !string.IsNullOrWhiteSpace(this._folderName)); 71 | } 72 | 73 | public string SourceFolder 74 | { 75 | get { return _sourceFolder; } 76 | set { _sourceFolder = value; } 77 | } 78 | 79 | public string ParentFolder 80 | { 81 | get { return _parentFolder; } 82 | set { _parentFolder = value; } 83 | } 84 | 85 | public string FolderName 86 | { 87 | get { return _folderName; } 88 | set { _folderName = value; } 89 | } 90 | 91 | public RelayCommand SelectSourceFolderCommand 92 | { 93 | get { return _selectSourceFolderCommand; } 94 | set { _selectSourceFolderCommand = value; } 95 | } 96 | 97 | public RelayCommand SelectParentFolderCommand 98 | { 99 | get { return _selectParentFolderCommand; } 100 | set { _selectParentFolderCommand = value; } 101 | } 102 | 103 | public RelayCommand DefaultCommand 104 | { 105 | get { return _defaultCommand; } 106 | set { _defaultCommand = value; } 107 | } 108 | 109 | public RelayCommand LinkCommand 110 | { 111 | get { return _linkCommand; } 112 | set { _linkCommand = value; } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Common/Toolbox.cs: -------------------------------------------------------------------------------- 1 | // 2 | // ToolBox.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Threading; 12 | 13 | namespace TorrentHardLinkHelper.Common 14 | { 15 | public delegate long Operation(T target); 16 | 17 | public static class Toolbox 18 | { 19 | private static Random r = new Random(); 20 | public static int Count(IEnumerable enumerable, Predicate predicate) 21 | { 22 | int count = 0; 23 | 24 | foreach (T t in enumerable) 25 | if (predicate(t)) 26 | count++; 27 | 28 | return count; 29 | } 30 | 31 | public static long Accumulate(IEnumerable enumerable, Operation action) 32 | { 33 | long count = 0; 34 | 35 | foreach (T t in enumerable) 36 | count += action(t); 37 | 38 | return count; 39 | } 40 | 41 | public static void RaiseAsyncEvent(EventHandler e, object o, T args) 42 | where T : EventArgs 43 | { 44 | if (e == null) 45 | return; 46 | 47 | ThreadPool.QueueUserWorkItem(delegate 48 | { 49 | if (e != null) 50 | e(o, args); 51 | }); 52 | } 53 | /// 54 | /// Randomizes the contents of the array 55 | /// 56 | /// 57 | /// 58 | public static void Randomize(List array) 59 | { 60 | List clone = new List(array); 61 | array.Clear(); 62 | 63 | while (clone.Count > 0) 64 | { 65 | int index = r.Next(0, clone.Count); 66 | array.Add(clone[index]); 67 | clone.RemoveAt(index); 68 | } 69 | } 70 | 71 | /// 72 | /// Switches the positions of two elements in an array 73 | /// 74 | /// 75 | /// 76 | /// 77 | /// 78 | public static void Switch(IList array, int first, int second) 79 | { 80 | T obj = array[first]; 81 | array[first] = array[second]; 82 | array[second] = obj; 83 | } 84 | 85 | /// 86 | /// Checks to see if the contents of two byte arrays are equal 87 | /// 88 | /// The first array 89 | /// The second array 90 | /// True if the arrays are equal, false if they aren't 91 | public static bool ByteMatch(byte[] array1, byte[] array2) 92 | { 93 | if (array1 == null) 94 | throw new ArgumentNullException("array1"); 95 | if (array2 == null) 96 | throw new ArgumentNullException("array2"); 97 | 98 | if (array1.Length != array2.Length) 99 | return false; 100 | 101 | return ByteMatch(array1, 0, array2, 0, array1.Length); 102 | } 103 | 104 | /// 105 | /// Checks to see if the contents of two byte arrays are equal 106 | /// 107 | /// The first array 108 | /// The second array 109 | /// The starting index for the first array 110 | /// The starting index for the second array 111 | /// The number of bytes to check 112 | /// 113 | public static bool ByteMatch(byte[] array1, int offset1, byte[] array2, int offset2, int count) 114 | { 115 | if (array1 == null) 116 | throw new ArgumentNullException("array1"); 117 | if (array2 == null) 118 | throw new ArgumentNullException("array2"); 119 | 120 | // If either of the arrays is too small, they're not equal 121 | if ((array1.Length - offset1) < count || (array2.Length - offset2) < count) 122 | return false; 123 | 124 | // Check if any elements are unequal 125 | for (int i = 0; i < count; i++) 126 | if (array1[offset1 + i] != array2[offset2 + i]) 127 | return false; 128 | 129 | return true; 130 | } 131 | 132 | internal static bool HasEncryption(EncryptionTypes available, EncryptionTypes check) 133 | { 134 | return (available & check) == check; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/RawReader.cs: -------------------------------------------------------------------------------- 1 | // 2 | // RawReader.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2008 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | using System; 30 | using System.Collections.Generic; 31 | using System.Text; 32 | using System.IO; 33 | 34 | namespace TorrentHardLinkHelper.BEncoding 35 | { 36 | public class RawReader : Stream 37 | { 38 | bool hasPeek; 39 | Stream input; 40 | byte[] peeked; 41 | bool strictDecoding; 42 | 43 | public bool StrictDecoding 44 | { 45 | get { return strictDecoding; } 46 | } 47 | 48 | public RawReader(Stream input) 49 | : this(input, true) 50 | { 51 | 52 | } 53 | 54 | public RawReader(Stream input, bool strictDecoding) 55 | { 56 | this.input = input; 57 | this.peeked = new byte[1]; 58 | this.strictDecoding = strictDecoding; 59 | } 60 | 61 | public override bool CanRead 62 | { 63 | get { return input.CanRead; } 64 | } 65 | 66 | public override bool CanSeek 67 | { 68 | get { return input.CanSeek; } 69 | } 70 | 71 | public override bool CanWrite 72 | { 73 | get { return false; } 74 | } 75 | 76 | public override void Flush() 77 | { 78 | throw new NotSupportedException(); 79 | } 80 | 81 | public override long Length 82 | { 83 | get { return input.Length; } 84 | } 85 | 86 | public int PeekByte() 87 | { 88 | if (!hasPeek) 89 | hasPeek = Read(peeked, 0, 1) == 1; 90 | return hasPeek ? peeked[0] : -1; 91 | } 92 | 93 | public override int ReadByte() 94 | { 95 | if (hasPeek) 96 | { 97 | hasPeek = false; 98 | return peeked[0]; 99 | } 100 | return base.ReadByte(); 101 | } 102 | 103 | public override long Position 104 | { 105 | get 106 | { 107 | if (hasPeek) 108 | return input.Position - 1; 109 | return input.Position; 110 | } 111 | set 112 | { 113 | if (value != Position) 114 | { 115 | hasPeek = false; 116 | input.Position = value; 117 | } 118 | } 119 | } 120 | 121 | public override int Read(byte[] buffer, int offset, int count) 122 | { 123 | int read = 0; 124 | if (hasPeek && count > 0) 125 | { 126 | hasPeek = false; 127 | buffer[offset] = peeked[0]; 128 | offset++; 129 | count--; 130 | read++; 131 | } 132 | read += input.Read(buffer, offset, count); 133 | return read; 134 | } 135 | 136 | public override long Seek(long offset, SeekOrigin origin) 137 | { 138 | long val; 139 | if (hasPeek && origin == SeekOrigin.Current) 140 | val = input.Seek(offset - 1, origin); 141 | else 142 | val = input.Seek(offset, origin); 143 | hasPeek = false; 144 | return val; 145 | } 146 | 147 | public override void SetLength(long value) 148 | { 149 | throw new NotSupportedException(); 150 | } 151 | 152 | public override void Write(byte[] buffer, int offset, int count) 153 | { 154 | throw new NotSupportedException(); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Test/TorrentHardLinkHelper.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {103E5401-FBB4-4DFB-9B10-4B40640B6E2D} 7 | Library 8 | Properties 9 | TorrentHardLinkHelper.Test 10 | TorrentHardLinkHelper.Test 11 | v4.0 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 3.5 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | PreserveNewest 64 | 65 | 66 | PreserveNewest 67 | 68 | 69 | 70 | 71 | {c0e94189-b243-4492-a113-6eb2de57bcc0} 72 | TorrentHardLinkHelper.Library 73 | 74 | 75 | 76 | 77 | 78 | 79 | False 80 | 81 | 82 | False 83 | 84 | 85 | False 86 | 87 | 88 | False 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Common/UriHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // System.Web.HttpUtility/HttpEncoder 3 | // 4 | // Authors: 5 | // Patrik Torstensson (Patrik.Torstensson@labs2.com) 6 | // Wictor Wilén (decode/encode functions) (wictor@ibizkit.se) 7 | // Tim Coleman (tim@timcoleman.com) 8 | // Gonzalo Paniagua Javier (gonzalo@ximian.com) 9 | // Marek Habersack 10 | // 11 | // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com) 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | using System.IO; 16 | using System.Text; 17 | 18 | namespace TorrentHardLinkHelper.Common 19 | { 20 | static class UriHelper 21 | { 22 | static readonly char [] hexChars = "0123456789abcdef".ToCharArray (); 23 | 24 | public static string UrlEncode (byte[] bytes) 25 | { 26 | if (bytes == null) 27 | throw new ArgumentNullException ("bytes"); 28 | 29 | var result = new MemoryStream (bytes.Length); 30 | for (int i = 0; i < bytes.Length; i++) 31 | UrlEncodeChar ((char)bytes [i], result, false); 32 | 33 | return Encoding.ASCII.GetString (result.ToArray()); 34 | } 35 | 36 | public static byte [] UrlDecode (string s) 37 | { 38 | if (null == s) 39 | return null; 40 | 41 | var e = Encoding.UTF8; 42 | if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1) 43 | return e.GetBytes (s); 44 | 45 | long len = s.Length; 46 | var bytes = new List (); 47 | int xchar; 48 | char ch; 49 | 50 | for (int i = 0; i < len; i++) { 51 | ch = s [i]; 52 | if (ch == '%' && i + 2 < len && s [i + 1] != '%') { 53 | if (s [i + 1] == 'u' && i + 5 < len) { 54 | // unicode hex sequence 55 | xchar = GetChar (s, i + 2, 4); 56 | if (xchar != -1) { 57 | WriteCharBytes (bytes, (char)xchar, e); 58 | i += 5; 59 | } else 60 | WriteCharBytes (bytes, '%', e); 61 | } else if ((xchar = GetChar (s, i + 1, 2)) != -1) { 62 | WriteCharBytes (bytes, (char)xchar, e); 63 | i += 2; 64 | } else { 65 | WriteCharBytes (bytes, '%', e); 66 | } 67 | continue; 68 | } 69 | 70 | if (ch == '+') 71 | WriteCharBytes (bytes, ' ', e); 72 | else 73 | WriteCharBytes (bytes, ch, e); 74 | } 75 | 76 | return bytes.ToArray (); 77 | } 78 | 79 | static void UrlEncodeChar (char c, Stream result, bool isUnicode) { 80 | if (c > ' ' && NotEncoded (c)) { 81 | result.WriteByte ((byte)c); 82 | return; 83 | } 84 | if (c==' ') { 85 | result.WriteByte ((byte)'+'); 86 | return; 87 | } 88 | if ( (c < '0') || 89 | (c < 'A' && c > '9') || 90 | (c > 'Z' && c < 'a') || 91 | (c > 'z')) { 92 | if (isUnicode && c > 127) { 93 | result.WriteByte ((byte)'%'); 94 | result.WriteByte ((byte)'u'); 95 | result.WriteByte ((byte)'0'); 96 | result.WriteByte ((byte)'0'); 97 | } 98 | else 99 | result.WriteByte ((byte)'%'); 100 | 101 | int idx = ((int) c) >> 4; 102 | result.WriteByte ((byte)hexChars [idx]); 103 | idx = ((int) c) & 0x0F; 104 | result.WriteByte ((byte)hexChars [idx]); 105 | } 106 | else { 107 | result.WriteByte ((byte)c); 108 | } 109 | } 110 | 111 | static int GetChar (string str, int offset, int length) 112 | { 113 | int val = 0; 114 | int end = length + offset; 115 | for (int i = offset; i < end; i++) { 116 | char c = str [i]; 117 | if (c > 127) 118 | return -1; 119 | 120 | int current = GetInt ((byte) c); 121 | if (current == -1) 122 | return -1; 123 | val = (val << 4) + current; 124 | } 125 | 126 | return val; 127 | } 128 | 129 | static int GetInt (byte b) 130 | { 131 | char c = (char) b; 132 | if (c >= '0' && c <= '9') 133 | return c - '0'; 134 | 135 | if (c >= 'a' && c <= 'f') 136 | return c - 'a' + 10; 137 | 138 | if (c >= 'A' && c <= 'F') 139 | return c - 'A' + 10; 140 | 141 | return -1; 142 | } 143 | 144 | static bool NotEncoded (char c) 145 | { 146 | return c == '!' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_' || c == '\''; 147 | } 148 | 149 | static void WriteCharBytes (List buf, char ch, Encoding e) 150 | { 151 | if (ch > 255) { 152 | foreach (byte b in e.GetBytes (new char[] { ch })) 153 | buf.Add (b); 154 | } else 155 | buf.Add ((byte)ch); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/HashFileLinkPieces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Threading; 7 | using TorrentHardLinkHelper.Torrents; 8 | 9 | namespace TorrentHardLinkHelper.Locate 10 | { 11 | internal class HashFileLinkPieces 12 | { 13 | private class CheckResult 14 | { 15 | public string Pattern { get; set; } 16 | public bool Matched { get; set; } 17 | } 18 | 19 | private int _result; 20 | private readonly IList _fileLinkPieces; 21 | private readonly Torrent _torrent; 22 | private readonly int _pieceIndex; 23 | private string _validPattern; 24 | private IDictionary> _cleanedFileInfos; 25 | 26 | private delegate CheckResult CheckPatternFunc(string pattern); 27 | 28 | public HashFileLinkPieces(Torrent torrent, int pieceIndex, 29 | IList fileLinkPieces) 30 | { 31 | this._pieceIndex = pieceIndex; 32 | this._fileLinkPieces = fileLinkPieces; 33 | this._result = -1; 34 | this._torrent = torrent; 35 | } 36 | 37 | public string Run() 38 | { 39 | if (_fileLinkPieces.Any(c => c.FileLink.FsFileInfos.Count == 0)) 40 | { 41 | return null; 42 | } 43 | this.CleanDuplicateFiles(); 44 | this.Run(0, ""); 45 | return this._validPattern; 46 | } 47 | 48 | private void CleanDuplicateFiles() 49 | { 50 | this._cleanedFileInfos = new Dictionary>(); 51 | for (int i = 0; i < this._fileLinkPieces.Count; i++) 52 | { 53 | var piece = this._fileLinkPieces[i]; 54 | var hashes = new List(); 55 | this._cleanedFileInfos.Add(i, new List(piece.FileLink.FsFileInfos.Count)); 56 | 57 | foreach (FileSystemFileInfo fileInfo in piece.FileLink.FsFileInfos) 58 | { 59 | if (piece.FileLink.TorrentFile.StartPieceIndex == this._pieceIndex && 60 | piece.FileLink.TorrentFile.EndPieceIndex == this._pieceIndex) 61 | { 62 | if (fileInfo.Located) 63 | { 64 | continue; 65 | } 66 | } 67 | string hash = this.HashFilePiece(fileInfo.FilePath, 68 | piece.StartPos, piece.ReadLength); 69 | if (hashes.Contains(hash)) 70 | { 71 | continue; 72 | } 73 | hashes.Add(hash); 74 | this._cleanedFileInfos[i].Add(fileInfo); 75 | } 76 | } 77 | } 78 | 79 | private void GroupFiles() 80 | { 81 | 82 | } 83 | 84 | private void Run(int index, string pattern) 85 | { 86 | if (this._result == 1) 87 | { 88 | return; 89 | } 90 | if (index < this._fileLinkPieces.Count) 91 | { 92 | for (int i = 0; i < this._cleanedFileInfos[index].Count; i++) 93 | { 94 | string nextPattern = pattern + i + ','; 95 | int nextIndex = index + 1; 96 | Run(nextIndex, nextPattern); 97 | } 98 | } 99 | else 100 | { 101 | using (var pieceStream = new MemoryStream(this._torrent.PieceLength)) 102 | { 103 | for (int i = 0; i < this._fileLinkPieces.Count; i++) 104 | { 105 | int fileIndex = int.Parse(pattern.Split(',')[i]); 106 | using (FileStream fileStream = 107 | File.OpenRead(this._cleanedFileInfos[i][fileIndex].FilePath)) 108 | { 109 | var buffer = new byte[this._fileLinkPieces[i].ReadLength]; 110 | fileStream.Position = (long)this._fileLinkPieces[i].StartPos; 111 | fileStream.Read(buffer, 0, buffer.Length); 112 | pieceStream.Write(buffer, 0, buffer.Length); 113 | } 114 | } 115 | 116 | var sha1 = HashAlgoFactory.Create(); 117 | byte[] hash = sha1.ComputeHash(pieceStream.ToArray()); 118 | if (this._torrent.Pieces.IsValid(hash, this._pieceIndex)) 119 | { 120 | this._validPattern = pattern; 121 | this._result = 1; 122 | } 123 | } 124 | } 125 | } 126 | 127 | private string HashFilePiece(string path, ulong start, ulong length) 128 | { 129 | var sha1 = HashAlgoFactory.Create(); 130 | FileStream fileStream = 131 | File.OpenRead(path); 132 | var buffer = new byte[length]; 133 | fileStream.Position = (long)start; 134 | fileStream.Read(buffer, 0, buffer.Length); 135 | byte[] hash = sha1.ComputeHash(buffer); 136 | fileStream.Close(); 137 | return Convert.ToBase64String(hash); 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Messages/Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using IMessage = TorrentHardLinkHelper.Messages.IMessage; 4 | 5 | namespace TorrentHardLinkHelper.Messages 6 | { 7 | public abstract class Message : IMessage 8 | { 9 | public abstract int ByteLength { get; } 10 | 11 | protected int CheckWritten(int written) 12 | { 13 | if (written != ByteLength) 14 | throw new MessageException("Message encoded incorrectly. Incorrect number of bytes written"); 15 | return written; 16 | } 17 | 18 | public abstract void Decode(byte[] buffer, int offset, int length); 19 | 20 | public byte[] Encode() 21 | { 22 | byte[] buffer = new byte[ByteLength]; 23 | Encode(buffer, 0); 24 | return buffer; 25 | } 26 | 27 | public abstract int Encode(byte[] buffer, int offset); 28 | 29 | static public byte ReadByte(byte[] buffer, int offset) 30 | { 31 | return buffer[offset]; 32 | } 33 | 34 | static public byte ReadByte(byte[] buffer, ref int offset) 35 | { 36 | byte b = buffer[offset]; 37 | offset++; 38 | return b; 39 | } 40 | 41 | static public byte[] ReadBytes(byte[] buffer, int offset, int count) 42 | { 43 | return ReadBytes(buffer, ref offset, count); 44 | } 45 | 46 | static public byte[] ReadBytes(byte[] buffer, ref int offset, int count) 47 | { 48 | byte[] result = new byte[count]; 49 | Buffer.BlockCopy(buffer, offset, result, 0, count); 50 | offset += count; 51 | return result; 52 | } 53 | 54 | static public short ReadShort(byte[] buffer, int offset) 55 | { 56 | return ReadShort(buffer, ref offset); 57 | } 58 | 59 | static public short ReadShort(byte[] buffer, ref int offset) 60 | { 61 | short ret = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, offset)); 62 | offset += 2; 63 | return ret; 64 | } 65 | 66 | static public string ReadString(byte[] buffer, int offset, int count) 67 | { 68 | return ReadString(buffer, ref offset, count); 69 | } 70 | 71 | static public string ReadString(byte[] buffer, ref int offset, int count) 72 | { 73 | string s = System.Text.Encoding.ASCII.GetString(buffer, offset, count); 74 | offset += count; 75 | return s; 76 | } 77 | 78 | static public int ReadInt(byte[] buffer, int offset) 79 | { 80 | return ReadInt(buffer, ref offset); 81 | } 82 | 83 | static public int ReadInt(byte[] buffer, ref int offset) 84 | { 85 | int ret = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, offset)); 86 | offset += 4; 87 | return ret; 88 | } 89 | 90 | static public long ReadLong(byte[] buffer, int offset) 91 | { 92 | return ReadLong(buffer, ref offset); 93 | } 94 | 95 | static public long ReadLong(byte[] buffer, ref int offset) 96 | { 97 | long ret = IPAddress.NetworkToHostOrder(BitConverter.ToInt64(buffer, offset)); 98 | offset += 8; 99 | return ret; 100 | } 101 | 102 | static public int Write(byte[] buffer, int offset, byte value) 103 | { 104 | buffer[offset] = value; 105 | return 1; 106 | } 107 | 108 | static public int Write(byte[] dest, int destOffset, byte[] src, int srcOffset, int count) 109 | { 110 | Buffer.BlockCopy(src, srcOffset, dest, destOffset, count); 111 | return count; 112 | } 113 | 114 | static public int Write(byte[] buffer, int offset, ushort value) 115 | { 116 | return Write(buffer, offset, (short)value); 117 | } 118 | 119 | static public int Write(byte[] buffer, int offset, short value) 120 | { 121 | offset += Write(buffer, offset, (byte)(value >> 8)); 122 | offset += Write(buffer, offset, (byte)value); 123 | return 2; 124 | } 125 | 126 | static public int Write(byte[] buffer, int offset, int value) 127 | { 128 | offset += Write(buffer, offset, (byte)(value >> 24)); 129 | offset += Write(buffer, offset, (byte)(value >> 16)); 130 | offset += Write(buffer, offset, (byte)(value >> 8)); 131 | offset += Write(buffer, offset, (byte)(value)); 132 | return 4; 133 | } 134 | 135 | static public int Write(byte[] buffer, int offset, uint value) 136 | { 137 | return Write(buffer, offset, (int)value); 138 | } 139 | 140 | static public int Write(byte[] buffer, int offset, long value) 141 | { 142 | offset += Write(buffer, offset, (int)(value >> 32)); 143 | offset += Write(buffer, offset, (int)value); 144 | return 8; 145 | } 146 | 147 | static public int Write(byte[] buffer, int offset, ulong value) 148 | { 149 | return Write(buffer, offset, (long)value); 150 | } 151 | 152 | static public int Write(byte[] buffer, int offset, byte[] value) 153 | { 154 | return Write(buffer, offset, value, 0, value.Length); 155 | } 156 | 157 | static public int WriteAscii(byte[] buffer, int offset, string text) 158 | { 159 | for (int i = 0; i < text.Length; i++) 160 | Write(buffer, offset + i, (byte)text[i]); 161 | return text.Length; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Models/EntityModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Windows.Media; 4 | using GalaSoft.MvvmLight; 5 | using TorrentHardLinkHelper.Locate; 6 | using TorrentHardLinkHelper.Torrents; 7 | 8 | namespace TorrentHardLinkHelper.Models 9 | { 10 | public class EntityModel : ObservableObject 11 | { 12 | public EntityModel() 13 | { 14 | this.Entities = new List(); 15 | } 16 | 17 | protected string _name; 18 | protected string _fullName; 19 | protected bool _locked; 20 | protected string _type; 21 | protected IList _entities; 22 | 23 | public string Name 24 | { 25 | get { return this._name; } 26 | set { this._name = value; } 27 | } 28 | 29 | public string FullName 30 | { 31 | get { return this._fullName; } 32 | set { this._fullName = value; } 33 | } 34 | 35 | public bool Located 36 | { 37 | get { return this._locked; } 38 | set { this._locked = value; } 39 | } 40 | 41 | public string Type 42 | { 43 | get { return this._type; } 44 | set { this._type = value; } 45 | } 46 | 47 | public IList Entities 48 | { 49 | get { return this._entities; } 50 | set { this._entities = value; } 51 | } 52 | 53 | public Brush TextColor 54 | { 55 | get 56 | { 57 | if (this.Type == "Folder") 58 | { 59 | return Brushes.Black; 60 | } 61 | if (this.Located) 62 | { 63 | return Brushes.Blue; 64 | } 65 | return Brushes.Red; 66 | } 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("[{0,-8}]{1}", this.Type, this.Name); 72 | } 73 | 74 | public static EntityModel Load(string path) 75 | { 76 | if (!Directory.Exists(path)) 77 | { 78 | return null; 79 | } 80 | var folderModel = new FolderModel(Path.GetFileName(path)); 81 | folderModel.FullName = path; 82 | foreach (var file in Directory.GetFiles(path)) 83 | { 84 | folderModel.Entities.Add(new FileModel(file)); 85 | } 86 | foreach (var subFolder in Directory.GetDirectories(path)) 87 | { 88 | folderModel.Entities.Add(Load(subFolder)); 89 | } 90 | return folderModel; 91 | } 92 | 93 | public static EntityModel Load(Torrent torrent) 94 | { 95 | var root = new FolderModel(torrent.Name); 96 | foreach (TorrentFile file in torrent.Files) 97 | { 98 | var folder = FindOrCreateFolder(root, file.Path); 99 | folder.Entities.Add(new FileModel(file)); 100 | } 101 | return root; 102 | } 103 | 104 | public static EntityModel Load(string title, LocateResult result) 105 | { 106 | var root = new FolderModel(title); 107 | foreach (TorrentFileLink file in result.TorrentFileLinks) 108 | { 109 | var folder = FindOrCreateFolder(root, file.TorrentFile.Path); 110 | var fileModel = new FileModel(file.TorrentFile); 111 | fileModel.Located = file.State == LinkState.Located; 112 | folder.Entities.Add(fileModel); 113 | } 114 | return root; 115 | } 116 | 117 | public static void Update(EntityModel model, IEnumerable fsFileInfos) 118 | { 119 | foreach (EntityModel entity in model.Entities) 120 | { 121 | if (entity.Type == "File") 122 | { 123 | foreach (var fsInfo in fsFileInfos) 124 | { 125 | if (fsInfo == null) 126 | { 127 | continue; 128 | } 129 | if (fsInfo.FilePath == entity.FullName) 130 | { 131 | entity.Located = true; 132 | entity.RaisePropertyChanged("TextColor"); 133 | } 134 | } 135 | } 136 | else 137 | { 138 | Update(entity, fsFileInfos); 139 | } 140 | } 141 | } 142 | 143 | private static EntityModel FindOrCreateFolder(FolderModel rootFolder, string path) 144 | { 145 | string[] pathItems = path.Split('\\'); 146 | if (pathItems.Length > 1) 147 | { 148 | EntityModel parentFolder = rootFolder; 149 | for (int i = 0; i < pathItems.Length - 1; i++) 150 | { 151 | bool found = false; 152 | foreach (EntityModel subFolder in parentFolder.Entities) 153 | { 154 | if (subFolder.Type == "Folder") 155 | { 156 | if (subFolder.Name == pathItems[i]) 157 | { 158 | found = true; 159 | parentFolder = subFolder; 160 | } 161 | } 162 | } 163 | if (!found) 164 | { 165 | var childFolder = new FolderModel(pathItems[i]); 166 | parentFolder.Entities.Add(childFolder); 167 | parentFolder = childFolder; 168 | } 169 | } 170 | return parentFolder; 171 | } 172 | return rootFolder; 173 | } 174 | } 175 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/InfoHash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Web; 5 | using TorrentHardLinkHelper.Common; 6 | 7 | namespace TorrentHardLinkHelper.Torrents 8 | { 9 | public class InfoHash : IEquatable 10 | { 11 | static Dictionary base32DecodeTable; 12 | 13 | static InfoHash() 14 | { 15 | base32DecodeTable = new Dictionary(); 16 | string table = "abcdefghijklmnopqrstuvwxyz234567"; 17 | for (int i = 0; i < table.Length; i++) 18 | base32DecodeTable[table[i]] = (byte)i; 19 | } 20 | 21 | byte[] hash; 22 | 23 | internal byte[] Hash 24 | { 25 | get { return hash; } 26 | } 27 | 28 | public InfoHash(byte[] infoHash) 29 | { 30 | Check.InfoHash(infoHash); 31 | if (infoHash.Length != 20) 32 | throw new ArgumentException("Infohash must be exactly 20 bytes long"); 33 | hash = (byte[])infoHash.Clone(); 34 | } 35 | 36 | public override bool Equals(object obj) 37 | { 38 | return Equals(obj as InfoHash); 39 | } 40 | 41 | public bool Equals(byte[] other) 42 | { 43 | return other == null || other.Length != 20 ? false : Toolbox.ByteMatch(Hash, other); 44 | } 45 | 46 | public bool Equals(InfoHash other) 47 | { 48 | return this == other; 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | // Equality is based generally on checking 20 positions, checking 4 should be enough 54 | // for the hashcode as infohashes are randomly distributed. 55 | return Hash[0] | (Hash[1] << 8) | (Hash[2] << 16) | (Hash[3] << 24); 56 | } 57 | 58 | public byte[] ToArray() 59 | { 60 | return (byte[])hash.Clone(); 61 | } 62 | 63 | public string ToHex() 64 | { 65 | StringBuilder sb = new StringBuilder(40); 66 | for (int i = 0; i < hash.Length; i++) 67 | { 68 | string hex = hash[i].ToString("X"); 69 | if (hex.Length != 2) 70 | sb.Append("0"); 71 | sb.Append(hex); 72 | } 73 | return sb.ToString(); 74 | } 75 | 76 | public override string ToString() 77 | { 78 | return BitConverter.ToString(hash); 79 | } 80 | 81 | public string UrlEncode() 82 | { 83 | return UriHelper.UrlEncode(Hash); 84 | } 85 | 86 | public static bool operator ==(InfoHash left, InfoHash right) 87 | { 88 | if ((object)left == null) 89 | return (object)right == null; 90 | if ((object)right == null) 91 | return false; 92 | return Toolbox.ByteMatch(left.Hash, right.Hash); 93 | } 94 | 95 | public static bool operator !=(InfoHash left, InfoHash right) 96 | { 97 | return !(left == right); 98 | } 99 | 100 | public static InfoHash FromBase32(string infoHash) 101 | { 102 | Check.InfoHash (infoHash); 103 | if (infoHash.Length != 32) 104 | throw new ArgumentException("Infohash must be a base32 encoded 32 character string"); 105 | 106 | infoHash = infoHash.ToLower(); 107 | int infohashOffset =0 ; 108 | byte[] hash = new byte[20]; 109 | var temp = new byte[8]; 110 | for (int i = 0; i < hash.Length; ) { 111 | for (int j=0; j < 8; j++) 112 | if (!base32DecodeTable.TryGetValue(infoHash[infohashOffset++], out temp[j])) 113 | throw new ArgumentException ("infoHash", "Value is not a valid base32 encoded string"); 114 | 115 | //8 * 5bits = 40 bits = 5 bytes 116 | hash[i++] = (byte)((temp[0] << 3) | (temp [1]>> 2)); 117 | hash[i++] = (byte)((temp[1] << 6) | (temp[2] << 1) | (temp[3] >> 4)); 118 | hash[i++] = (byte)((temp[3] << 4) | (temp [4]>> 1)); 119 | hash[i++] = (byte)((temp[4] << 7) | (temp[5] << 2) | (temp [6]>> 3)); 120 | hash[i++] = (byte)((temp[6] << 5) | temp[7]); 121 | } 122 | 123 | return new InfoHash(hash); 124 | } 125 | 126 | public static InfoHash FromHex(string infoHash) 127 | { 128 | Check.InfoHash (infoHash); 129 | if (infoHash.Length != 40) 130 | throw new ArgumentException("Infohash must be 40 characters long"); 131 | 132 | byte[] hash = new byte[20]; 133 | for (int i = 0; i < hash.Length; i++) 134 | hash[i] = byte.Parse(infoHash.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); 135 | 136 | return new InfoHash(hash); 137 | } 138 | 139 | public static InfoHash FromMagnetLink(string magnetLink) 140 | { 141 | Check.MagnetLink(magnetLink); 142 | if (!magnetLink.StartsWith("magnet:?")) 143 | throw new ArgumentException("Invalid magnet link format"); 144 | magnetLink = magnetLink.Substring("magnet:?".Length); 145 | int hashStart = magnetLink.IndexOf("xt=urn:btih:"); 146 | if (hashStart == -1) 147 | throw new ArgumentException("Magnet link does not contain an infohash"); 148 | hashStart += "xt=urn:btih:".Length; 149 | 150 | int hashEnd = magnetLink.IndexOf('&', hashStart); 151 | if (hashEnd == -1) 152 | hashEnd = magnetLink.Length; 153 | 154 | switch (hashEnd - hashStart) 155 | { 156 | case 32: 157 | return FromBase32(magnetLink.Substring(hashStart, 32)); 158 | case 40: 159 | return FromHex(magnetLink.Substring(hashStart, 40)); 160 | default: 161 | throw new ArgumentException("Infohash must be base32 or hex encoded."); 162 | } 163 | } 164 | 165 | public static InfoHash UrlDecode(string infoHash) 166 | { 167 | Check.InfoHash(infoHash); 168 | return new InfoHash(UriHelper.UrlDecode(infoHash)); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Torrents/TorrentFile.cs: -------------------------------------------------------------------------------- 1 | // 2 | // TorrentFile.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace TorrentHardLinkHelper.Torrents 14 | { 15 | /// 16 | /// This is the base class for the files available to download from within a .torrent. 17 | /// This should be inherited by both Client and Tracker "TorrentFile" classes 18 | /// 19 | public class TorrentFile : IEquatable 20 | { 21 | #region Private Fields 22 | 23 | private BitField bitfield; 24 | private BitField selector; 25 | private byte[] ed2k; 26 | private int endPiece; 27 | private string fullPath; 28 | private long length; 29 | private byte[] md5; 30 | private string path; 31 | private Priority priority; 32 | private byte[] sha1; 33 | private int startPiece; 34 | 35 | #endregion Private Fields 36 | 37 | 38 | #region Member Variables 39 | 40 | /// 41 | /// The number of pieces which have been successfully downloaded which are from this file 42 | /// 43 | public BitField BitField 44 | { 45 | get { return this.bitfield; } 46 | } 47 | 48 | public long BytesDownloaded 49 | { 50 | get { return (long)(BitField.PercentComplete * Length / 100.0); } 51 | } 52 | 53 | /// 54 | /// The ED2K hash of the file 55 | /// 56 | public byte[] ED2K 57 | { 58 | get { return ed2k; } 59 | } 60 | 61 | /// 62 | /// The index of the last piece of this file 63 | /// 64 | public int EndPieceIndex 65 | { 66 | get { return this.endPiece; } 67 | } 68 | 69 | public string FullPath 70 | { 71 | get { return fullPath; } 72 | internal set { fullPath = value; } 73 | } 74 | 75 | /// 76 | /// The length of the file in bytes 77 | /// 78 | public long Length 79 | { 80 | get { return length; } 81 | } 82 | 83 | /// 84 | /// The MD5 hash of the file 85 | /// 86 | public byte[] MD5 87 | { 88 | get { return this.md5; } 89 | internal set { md5 = value; } 90 | } 91 | 92 | /// 93 | /// In the case of a single torrent file, this is the name of the file. 94 | /// In the case of a multi-file torrent this is the relative path of the file 95 | /// (including the filename) from the base directory 96 | /// 97 | public string Path 98 | { 99 | get { return path; } 100 | } 101 | 102 | /// 103 | /// The priority of this torrent file 104 | /// 105 | public Priority Priority 106 | { 107 | get { return this.priority; } 108 | set { this.priority = value; } 109 | } 110 | 111 | /// 112 | /// The SHA1 hash of the file 113 | /// 114 | public byte[] SHA1 115 | { 116 | get { return this.sha1; } 117 | } 118 | 119 | /// 120 | /// The index of the first piece of this file 121 | /// 122 | public int StartPieceIndex 123 | { 124 | get { return this.startPiece; } 125 | } 126 | 127 | #endregion 128 | 129 | 130 | #region Constructors 131 | public TorrentFile(string path, long length) 132 | : this(path, length, path) 133 | { 134 | 135 | } 136 | 137 | public TorrentFile (string path, long length, string fullPath) 138 | : this (path, length, fullPath, 0, 0) 139 | { 140 | 141 | } 142 | 143 | public TorrentFile (string path, long length, int startIndex, int endIndex) 144 | : this (path, length, path, startIndex, endIndex) 145 | { 146 | 147 | } 148 | 149 | public TorrentFile(string path, long length, string fullPath, int startIndex, int endIndex) 150 | : this(path, length, fullPath, startIndex, endIndex, null, null, null) 151 | { 152 | 153 | } 154 | 155 | public TorrentFile(string path, long length, string fullPath, int startIndex, int endIndex, byte[] md5, byte[] ed2k, byte[] sha1) 156 | { 157 | this.bitfield = new BitField(endIndex - startIndex + 1); 158 | this.ed2k = ed2k; 159 | this.endPiece = endIndex; 160 | this.fullPath = fullPath; 161 | this.length = length; 162 | this.md5 = md5; 163 | this.path = path; 164 | this.priority = Priority.Normal; 165 | this.sha1 = sha1; 166 | this.startPiece = startIndex; 167 | } 168 | 169 | #endregion 170 | 171 | 172 | #region Methods 173 | 174 | public override bool Equals(object obj) 175 | { 176 | return Equals(obj as TorrentFile); 177 | } 178 | 179 | public bool Equals(TorrentFile other) 180 | { 181 | return other == null ? false : path == other.path && length == other.length; ; 182 | } 183 | 184 | public override int GetHashCode() 185 | { 186 | return path.GetHashCode(); 187 | } 188 | 189 | internal BitField GetSelector(int totalPieces) 190 | { 191 | if (selector != null) 192 | return selector; 193 | 194 | selector = new BitField(totalPieces); 195 | for (int i = StartPieceIndex; i <= EndPieceIndex; i++) 196 | selector[i] = true; 197 | return selector; 198 | } 199 | 200 | public override string ToString() 201 | { 202 | StringBuilder sb = new StringBuilder(32); 203 | sb.Append("File: "); 204 | sb.Append(path); 205 | sb.Append(" StartIndex: "); 206 | sb.Append(StartPieceIndex); 207 | sb.Append(" EndIndex: "); 208 | sb.Append(EndPieceIndex); 209 | return sb.ToString(); 210 | } 211 | 212 | #endregion Methods 213 | } 214 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Common/Check.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TorrentHardLinkHelper.Common 4 | { 5 | public static class Check 6 | { 7 | static void DoCheck(object toCheck, string name) 8 | { 9 | if (toCheck == null) 10 | throw new ArgumentNullException(name); 11 | } 12 | 13 | static void IsNullOrEmpty(string toCheck, string name) 14 | { 15 | DoCheck(toCheck, name); 16 | if (toCheck.Length == 0) 17 | throw new ArgumentException("Cannot be empty", name); 18 | } 19 | 20 | public static void Address(object address) 21 | { 22 | DoCheck(address, "address"); 23 | } 24 | 25 | public static void AddressRange(object addressRange) 26 | { 27 | DoCheck(addressRange, "addressRange"); 28 | } 29 | 30 | public static void AddressRanges(object addressRanges) 31 | { 32 | DoCheck(addressRanges, "addressRanges"); 33 | } 34 | 35 | public static void Announces(object announces) 36 | { 37 | DoCheck(announces, "announces"); 38 | } 39 | 40 | public static void BaseDirectory(object baseDirectory) 41 | { 42 | DoCheck(baseDirectory, "baseDirectory"); 43 | } 44 | 45 | internal static void BaseType(Type baseType) 46 | { 47 | DoCheck(baseType, "baseType"); 48 | } 49 | 50 | internal static void Buffer(object buffer) 51 | { 52 | DoCheck(buffer, "buffer"); 53 | } 54 | 55 | internal static void Cache(object cache) 56 | { 57 | DoCheck(cache, "cache"); 58 | } 59 | 60 | public static void Data(object data) 61 | { 62 | DoCheck(data, "data"); 63 | } 64 | 65 | public static void Destination(object destination) 66 | { 67 | DoCheck(destination, "destination"); 68 | } 69 | 70 | public static void Endpoint(object endpoint) 71 | { 72 | DoCheck(endpoint, "endpoint"); 73 | } 74 | 75 | public static void File(object file) 76 | { 77 | DoCheck(file, "file"); 78 | } 79 | 80 | public static void Files(object files) 81 | { 82 | DoCheck(files, "files"); 83 | } 84 | 85 | public static void FileSource(object fileSource) 86 | { 87 | DoCheck(fileSource, "fileSource"); 88 | } 89 | 90 | public static void InfoHash(object infoHash) 91 | { 92 | DoCheck(infoHash, "infoHash"); 93 | } 94 | 95 | public static void Key(object key) 96 | { 97 | DoCheck(key, "key"); 98 | } 99 | 100 | public static void Limiter(object limiter) 101 | { 102 | DoCheck(limiter, "limiter"); 103 | } 104 | 105 | public static void Listener(object listener) 106 | { 107 | DoCheck(listener, "listener"); 108 | } 109 | 110 | public static void Location(object location) 111 | { 112 | DoCheck(location, "location"); 113 | } 114 | 115 | public static void MagnetLink(object magnetLink) 116 | { 117 | DoCheck(magnetLink, "magnetLink"); 118 | } 119 | 120 | public static void Manager(object manager) 121 | { 122 | DoCheck(manager, "manager"); 123 | } 124 | 125 | public static void Mappings(object mappings) 126 | { 127 | DoCheck(mappings, "mappings"); 128 | } 129 | 130 | public static void Metadata(object metadata) 131 | { 132 | DoCheck(metadata, "metadata"); 133 | } 134 | 135 | public static void Name(object name) 136 | { 137 | DoCheck(name, "name"); 138 | } 139 | 140 | public static void Path(object path) 141 | { 142 | DoCheck(path, "path"); 143 | } 144 | 145 | public static void Paths(object paths) 146 | { 147 | DoCheck(paths, "paths"); 148 | } 149 | 150 | public static void PathNotEmpty(string path) 151 | { 152 | IsNullOrEmpty(path, "path"); 153 | } 154 | 155 | public static void Peer(object peer) 156 | { 157 | DoCheck(peer, "peer"); 158 | } 159 | 160 | public static void Peers(object peers) 161 | { 162 | DoCheck(peers, "peers"); 163 | } 164 | 165 | public static void Picker(object picker) 166 | { 167 | DoCheck(picker, "picker"); 168 | } 169 | 170 | public static void Result(object result) 171 | { 172 | DoCheck(result, "result"); 173 | } 174 | 175 | public static void SavePath(object savePath) 176 | { 177 | DoCheck(savePath, "savePath"); 178 | } 179 | 180 | public static void Settings(object settings) 181 | { 182 | DoCheck(settings, "settings"); 183 | } 184 | 185 | internal static void SpecificType(Type specificType) 186 | { 187 | DoCheck(specificType, "specificType"); 188 | } 189 | 190 | public static void Stream(object stream) 191 | { 192 | DoCheck(stream, "stream"); 193 | } 194 | 195 | public static void Torrent(object torrent) 196 | { 197 | DoCheck(torrent, "torrent"); 198 | } 199 | 200 | public static void TorrentInformation(object torrentInformation) 201 | { 202 | DoCheck(torrentInformation, "torrentInformation"); 203 | } 204 | 205 | public static void TorrentSave(object torrentSave) 206 | { 207 | DoCheck(torrentSave, "torrentSave"); 208 | } 209 | 210 | public static void Tracker(object tracker) 211 | { 212 | DoCheck(tracker, "tracker"); 213 | } 214 | 215 | public static void Url(object url) 216 | { 217 | DoCheck(url, "url"); 218 | } 219 | 220 | public static void Uri(Uri uri) 221 | { 222 | DoCheck(uri, "uri"); 223 | } 224 | 225 | public static void Value(object value) 226 | { 227 | DoCheck(value, "value"); 228 | } 229 | 230 | public static void Writer(object writer) 231 | { 232 | DoCheck(writer, "writer"); 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/HardLink/HardLinkHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Text; 6 | using TorrentHardLinkHelper.Locate; 7 | 8 | namespace TorrentHardLinkHelper.HardLink 9 | { 10 | public class HardLinkHelper 11 | { 12 | private StringBuilder _builder; 13 | private List _createdFolders; 14 | 15 | public void HardLink(string sourceFolder, string targetParentFolder, string folderName, int copyLimitSize) 16 | { 17 | 18 | this._builder = new StringBuilder(); 19 | this._builder.AppendLine("chcp 65001"); 20 | this._builder.AppendLine("::==============================================::"); 21 | this._builder.AppendLine(":: Torrent Hard-Link Helper v0.8"); 22 | this._builder.AppendLine(":: By Harry Wong(harrywong@live.com), 2013-2014"); 23 | this._builder.AppendLine("::"); 24 | this._builder.AppendLine(":: Created at " + DateTime.Now); 25 | this._builder.AppendLine("::==============================================::"); 26 | this._builder.AppendLine("::."); 27 | 28 | string rootFolder = Path.Combine(targetParentFolder, folderName); 29 | if (!Directory.Exists(rootFolder)) 30 | { 31 | CreateFolder(rootFolder); 32 | } 33 | if (!Directory.Exists(targetParentFolder)) 34 | { 35 | CreateFolder(targetParentFolder); 36 | } 37 | this.SearchFolder(sourceFolder, rootFolder, copyLimitSize); 38 | var utf8bom = new UTF8Encoding(false); 39 | File.WriteAllText(Path.Combine(rootFolder, "!hard-link.cmd"), this._builder.ToString(), utf8bom); 40 | } 41 | 42 | private void SearchFolder(string folder, string targetParentFolder, int copyLimitSize) 43 | { 44 | if (this._createdFolders == null) 45 | { 46 | this._createdFolders = new List(); 47 | } 48 | if (this._createdFolders.Contains(folder)) 49 | { 50 | return; 51 | } 52 | foreach (var file in Directory.GetFiles(folder)) 53 | { 54 | string targetFile = Path.Combine(targetParentFolder, Path.GetFileName(file)); 55 | var fileInfo = new FileInfo(file); 56 | if (fileInfo.Length >= copyLimitSize) 57 | { 58 | CreateHarkLink(file, targetFile); 59 | } 60 | else 61 | { 62 | Copy(file, targetFile); 63 | } 64 | } 65 | foreach (var subFolder in Directory.GetDirectories(folder)) 66 | { 67 | string targetSubFolder = Path.Combine(targetParentFolder, Path.GetFileName(subFolder)); 68 | if (!Directory.Exists(targetSubFolder)) 69 | { 70 | this.CreateFolder(targetSubFolder); 71 | this._createdFolders.Add(targetSubFolder); 72 | } 73 | SearchFolder(subFolder, targetSubFolder, copyLimitSize); 74 | } 75 | } 76 | 77 | public void HardLink(IList links, int copyLimitSize, string folderName, string baseFolde) 78 | { 79 | string rootFolder = Path.Combine(baseFolde, folderName); 80 | 81 | this._builder = new StringBuilder(); 82 | this._builder.AppendLine("chcp 65001"); 83 | this._builder.AppendLine("::==============================================::"); 84 | this._builder.AppendLine(":: Torrent Hard-Link Helper v0.8"); 85 | this._builder.AppendLine(":: By Harry Wong(harrywong@live.com), 2013-2014"); 86 | this._builder.AppendLine("::"); 87 | this._builder.AppendLine(":: Created at " + DateTime.Now); 88 | this._builder.AppendLine("::==============================================::"); 89 | this._builder.AppendLine("::."); 90 | if (!Directory.Exists(rootFolder)) 91 | { 92 | this.CreateFolder(rootFolder); 93 | } 94 | foreach (var link in links) 95 | { 96 | if (link.LinkedFsFileInfo == null) 97 | { 98 | continue; 99 | } 100 | string[] pathParts = link.TorrentFile.Path.Split('\\'); 101 | for (int i = 0; i < pathParts.Length - 1; i++) 102 | { 103 | var targetPathParts = new string[i + 2]; 104 | targetPathParts[0] = rootFolder; 105 | Array.Copy(pathParts, 0, targetPathParts, 1, i + 1); 106 | string targetPath = Path.Combine(targetPathParts); 107 | if (!Directory.Exists(targetPath)) 108 | { 109 | this.CreateFolder(targetPath); 110 | } 111 | } 112 | string targetFile = Path.Combine(rootFolder, link.TorrentFile.Path); 113 | 114 | if (link.TorrentFile.Length >= copyLimitSize) 115 | { 116 | CreateHarkLink(link.LinkedFsFileInfo.FilePath, targetFile); 117 | } 118 | else 119 | { 120 | Copy(link.LinkedFsFileInfo.FilePath, targetFile); 121 | } 122 | } 123 | File.WriteAllText(Path.Combine(rootFolder, "!hard-link.cmd"), this._builder.ToString(), Encoding.UTF8); 124 | } 125 | 126 | private void CreateHarkLink(string source, string target) 127 | { 128 | this._builder.AppendLine(string.Format("fsutil hardlink create \"{0}\" \"{1}\"", target, source)); 129 | var procStartInfo = 130 | new ProcessStartInfo("cmd", 131 | "/c " + string.Format("fsutil hardlink create \"{0}\" \"{1}\"", target, source)); 132 | 133 | // The following commands are needed to redirect the standard output. 134 | // This means that it will be redirected to the Process.StandardOutput StreamReader. 135 | procStartInfo.RedirectStandardOutput = true; 136 | procStartInfo.UseShellExecute = false; 137 | // Do not create the black window. 138 | procStartInfo.CreateNoWindow = true; 139 | // Now we create a process, assign its ProcessStartInfo and start it 140 | Process proc = new Process(); 141 | proc.StartInfo = procStartInfo; 142 | proc.Start(); 143 | } 144 | 145 | public void Copy(string source, string target) 146 | { 147 | this._builder.AppendLine(string.Format("copy /y \"{0}\" \"{1}\"", source, target)); 148 | var procStartInfo = 149 | new ProcessStartInfo("cmd", 150 | "/c " + string.Format("copy /y \"{0}\" \"{1}\"", source, target)); 151 | 152 | // The following commands are needed to redirect the standard output. 153 | // This means that it will be redirected to the Process.StandardOutput StreamReader. 154 | procStartInfo.RedirectStandardOutput = true; 155 | procStartInfo.UseShellExecute = false; 156 | // Do not create the black window. 157 | procStartInfo.CreateNoWindow = true; 158 | // Now we create a process, assign its ProcessStartInfo and start it 159 | Process proc = new Process(); 160 | proc.StartInfo = procStartInfo; 161 | proc.Start(); 162 | } 163 | 164 | private void CreateFolder(string path) 165 | { 166 | this._builder.AppendLine(string.Format("mkdir \"{0}\"", path)); 167 | Directory.CreateDirectory(path); 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/BEncodedList.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BEncodedList.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | 31 | using System; 32 | using System.Collections.Generic; 33 | using System.Text; 34 | using System.IO; 35 | 36 | namespace TorrentHardLinkHelper.BEncoding 37 | { 38 | /// 39 | /// Class representing a BEncoded list 40 | /// 41 | public class BEncodedList : BEncodedValue, IList 42 | { 43 | #region Member Variables 44 | 45 | private List list; 46 | 47 | #endregion 48 | 49 | 50 | #region Constructors 51 | /// 52 | /// Create a new BEncoded List with default capacity 53 | /// 54 | public BEncodedList() 55 | : this(new List()) 56 | { 57 | } 58 | 59 | /// 60 | /// Create a new BEncoded List with the supplied capacity 61 | /// 62 | /// The initial capacity 63 | public BEncodedList(int capacity) 64 | : this(new List(capacity)) 65 | { 66 | 67 | } 68 | 69 | public BEncodedList(IEnumerable list) 70 | { 71 | if (list == null) 72 | throw new ArgumentNullException("list"); 73 | 74 | this.list = new List(list); 75 | } 76 | 77 | private BEncodedList(List value) 78 | { 79 | this.list = value; 80 | } 81 | 82 | #endregion 83 | 84 | 85 | #region Encode/Decode Methods 86 | 87 | 88 | /// 89 | /// Encodes the list to a byte[] 90 | /// 91 | /// The buffer to encode the list to 92 | /// The offset to start writing the data at 93 | /// 94 | public override int Encode(byte[] buffer, int offset) 95 | { 96 | int written = 0; 97 | buffer[offset] = (byte)'l'; 98 | written++; 99 | for (int i = 0; i < this.list.Count; i++) 100 | written += this.list[i].Encode(buffer, offset + written); 101 | buffer[offset + written] = (byte)'e'; 102 | written++; 103 | return written; 104 | } 105 | 106 | /// 107 | /// Decodes a BEncodedList from the given StreamReader 108 | /// 109 | /// 110 | internal override void DecodeInternal(RawReader reader) 111 | { 112 | if (reader.ReadByte() != 'l') // Remove the leading 'l' 113 | throw new BEncodingException("Invalid data found. Aborting"); 114 | 115 | while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e')) 116 | list.Add(BEncodedValue.Decode(reader)); 117 | 118 | if (reader.ReadByte() != 'e') // Remove the trailing 'e' 119 | throw new BEncodingException("Invalid data found. Aborting"); 120 | } 121 | #endregion 122 | 123 | 124 | #region Helper Methods 125 | /// 126 | /// Returns the size of the list in bytes 127 | /// 128 | /// 129 | public override int LengthInBytes() 130 | { 131 | int length = 0; 132 | 133 | length += 1; // Lists start with 'l' 134 | for (int i=0; i < this.list.Count; i++) 135 | length += this.list[i].LengthInBytes(); 136 | 137 | length += 1; // Lists end with 'e' 138 | return length; 139 | } 140 | #endregion 141 | 142 | 143 | #region Overridden Methods 144 | public override bool Equals(object obj) 145 | { 146 | BEncodedList other = obj as BEncodedList; 147 | 148 | if (other == null) 149 | return false; 150 | 151 | for (int i = 0; i < this.list.Count; i++) 152 | if (!this.list[i].Equals(other.list[i])) 153 | return false; 154 | 155 | return true; 156 | } 157 | 158 | 159 | public override int GetHashCode() 160 | { 161 | int result = 0; 162 | for (int i = 0; i < list.Count; i++) 163 | result ^= list[i].GetHashCode(); 164 | 165 | return result; 166 | } 167 | 168 | 169 | public override string ToString() 170 | { 171 | return System.Text.Encoding.UTF8.GetString(Encode()); 172 | } 173 | #endregion 174 | 175 | 176 | #region IList methods 177 | public void Add(BEncodedValue item) 178 | { 179 | this.list.Add(item); 180 | } 181 | 182 | public void AddRange (IEnumerable collection) 183 | { 184 | list.AddRange (collection); 185 | } 186 | 187 | public void Clear() 188 | { 189 | this.list.Clear(); 190 | } 191 | 192 | public bool Contains(BEncodedValue item) 193 | { 194 | return this.list.Contains(item); 195 | } 196 | 197 | public void CopyTo(BEncodedValue[] array, int arrayIndex) 198 | { 199 | this.list.CopyTo(array, arrayIndex); 200 | } 201 | 202 | public int Count 203 | { 204 | get { return this.list.Count; } 205 | } 206 | 207 | public int IndexOf(BEncodedValue item) 208 | { 209 | return this.list.IndexOf(item); 210 | } 211 | 212 | public void Insert(int index, BEncodedValue item) 213 | { 214 | this.list.Insert(index, item); 215 | } 216 | 217 | public bool IsReadOnly 218 | { 219 | get { return false; } 220 | } 221 | 222 | public bool Remove(BEncodedValue item) 223 | { 224 | return this.list.Remove(item); 225 | } 226 | 227 | public void RemoveAt(int index) 228 | { 229 | this.list.RemoveAt(index); 230 | } 231 | 232 | public BEncodedValue this[int index] 233 | { 234 | get { return this.list[index]; } 235 | set { this.list[index] = value; } 236 | } 237 | 238 | public IEnumerator GetEnumerator() 239 | { 240 | return this.list.GetEnumerator(); 241 | } 242 | 243 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 244 | { 245 | return this.GetEnumerator(); 246 | } 247 | #endregion 248 | } 249 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/BEncodedNumber.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BEncodedNumber.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | 31 | using System; 32 | using System.IO; 33 | using System.Text; 34 | using System.Collections.Generic; 35 | 36 | namespace TorrentHardLinkHelper.BEncoding 37 | { 38 | /// 39 | /// Class representing a BEncoded number 40 | /// 41 | public class BEncodedNumber : BEncodedValue, IComparable 42 | { 43 | #region Member Variables 44 | /// 45 | /// The value of the BEncodedNumber 46 | /// 47 | public long Number 48 | { 49 | get { return number; } 50 | set { number = value; } 51 | } 52 | internal long number; 53 | #endregion 54 | 55 | 56 | #region Constructors 57 | public BEncodedNumber() 58 | : this(0) 59 | { 60 | } 61 | 62 | /// 63 | /// Create a new BEncoded number with the given value 64 | /// 65 | /// The inital value of the BEncodedNumber 66 | public BEncodedNumber(long value) 67 | { 68 | this.number = value; 69 | } 70 | 71 | public static implicit operator BEncodedNumber(long value) 72 | { 73 | return new BEncodedNumber(value); 74 | } 75 | #endregion 76 | 77 | 78 | #region Encode/Decode Methods 79 | 80 | /// 81 | /// Encodes this number to the supplied byte[] starting at the supplied offset 82 | /// 83 | /// The buffer to write the data to 84 | /// The offset to start writing the data at 85 | /// 86 | public override int Encode(byte[] buffer, int offset) 87 | { 88 | long number = this.number; 89 | 90 | int written = offset; 91 | buffer[written++] = (byte)'i'; 92 | 93 | if (number < 0) 94 | { 95 | buffer[written++] = (byte)'-'; 96 | number = -number; 97 | } 98 | // Reverse the number '12345' to get '54321' 99 | long reversed = 0; 100 | for (long i = number; i != 0; i /= 10) 101 | reversed = reversed * 10 + i % 10; 102 | 103 | // Write each digit of the reversed number to the array. We write '1' 104 | // first, then '2', etc 105 | for (long i = reversed; i != 0; i /= 10) 106 | buffer[written++] = (byte)(i % 10 + '0'); 107 | 108 | if (number == 0) 109 | buffer[written++] = (byte)'0'; 110 | 111 | // If the original number ends in one or more zeros, they are lost 112 | // when we reverse the number. We add them back in here. 113 | for (long i = number; i % 10 == 0 && number != 0; i /= 10) 114 | buffer[written++] = (byte)'0'; 115 | 116 | buffer[written++] = (byte)'e'; 117 | return written - offset; 118 | } 119 | 120 | 121 | /// 122 | /// Decodes a BEncoded number from the supplied RawReader 123 | /// 124 | /// RawReader containing a BEncoded Number 125 | internal override void DecodeInternal(RawReader reader) 126 | { 127 | int sign = 1; 128 | if (reader == null) 129 | throw new ArgumentNullException("reader"); 130 | 131 | if (reader.ReadByte() != 'i') // remove the leading 'i' 132 | throw new BEncodingException("Invalid data found. Aborting."); 133 | 134 | if (reader.PeekByte() == '-') 135 | { 136 | sign = -1; 137 | reader.ReadByte (); 138 | } 139 | 140 | int letter; 141 | while (((letter = reader.PeekByte()) != -1) && letter != 'e') 142 | { 143 | if(letter < '0' || letter > '9') 144 | throw new BEncodingException("Invalid number found."); 145 | number = number * 10 + (letter - '0'); 146 | reader.ReadByte (); 147 | } 148 | if (reader.ReadByte() != 'e') //remove the trailing 'e' 149 | throw new BEncodingException("Invalid data found. Aborting."); 150 | 151 | number *= sign; 152 | } 153 | #endregion 154 | 155 | 156 | #region Helper Methods 157 | /// 158 | /// Returns the length of the encoded string in bytes 159 | /// 160 | /// 161 | public override int LengthInBytes() 162 | { 163 | long number = this.number; 164 | int count = 2; // account for the 'i' and 'e' 165 | 166 | if (number == 0) 167 | return count + 1; 168 | 169 | if (number < 0) 170 | { 171 | number = -number; 172 | count++; 173 | } 174 | for (long i = number; i != 0; i /= 10) 175 | count++; 176 | 177 | return count; 178 | } 179 | 180 | 181 | public int CompareTo(object other) 182 | { 183 | if (other is BEncodedNumber || other is long || other is int) 184 | return CompareTo((BEncodedNumber)other); 185 | 186 | return -1; 187 | } 188 | 189 | public int CompareTo(BEncodedNumber other) 190 | { 191 | if (other == null) 192 | throw new ArgumentNullException("other"); 193 | 194 | return this.number.CompareTo(other.number); 195 | } 196 | 197 | 198 | public int CompareTo(long other) 199 | { 200 | return this.number.CompareTo(other); 201 | } 202 | #endregion 203 | 204 | 205 | #region Overridden Methods 206 | /// 207 | /// 208 | /// 209 | /// 210 | /// 211 | public override bool Equals(object obj) 212 | { 213 | BEncodedNumber obj2 = obj as BEncodedNumber; 214 | if (obj2 == null) 215 | return false; 216 | 217 | return (this.number == obj2.number); 218 | } 219 | 220 | /// 221 | /// 222 | /// 223 | /// 224 | public override int GetHashCode() 225 | { 226 | return this.number.GetHashCode(); 227 | } 228 | 229 | /// 230 | /// 231 | /// 232 | /// 233 | public override string ToString() 234 | { 235 | return (this.number.ToString()); 236 | } 237 | #endregion 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/Views/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/BEncodedString.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BEncodedString.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | 31 | using System; 32 | using System.IO; 33 | using System.Collections; 34 | using System.Text; 35 | using TorrentHardLinkHelper.Common; 36 | using TorrentHardLinkHelper.Messages; 37 | 38 | namespace TorrentHardLinkHelper.BEncoding 39 | { 40 | /// 41 | /// Class representing a BEncoded string 42 | /// 43 | public class BEncodedString : BEncodedValue, IComparable 44 | { 45 | #region Member Variables 46 | 47 | /// 48 | /// The value of the BEncodedString 49 | /// 50 | public string Text 51 | { 52 | get { return Encoding.UTF8.GetString(textBytes); } 53 | set { textBytes = Encoding.UTF8.GetBytes(value); } 54 | } 55 | 56 | /// 57 | /// The underlying byte[] associated with this BEncodedString 58 | /// 59 | public byte[] TextBytes 60 | { 61 | get { return this.textBytes; } 62 | } 63 | private byte[] textBytes; 64 | #endregion 65 | 66 | 67 | #region Constructors 68 | /// 69 | /// Create a new BEncodedString using UTF8 encoding 70 | /// 71 | public BEncodedString() 72 | : this(new byte[0]) 73 | { 74 | } 75 | 76 | /// 77 | /// Create a new BEncodedString using UTF8 encoding 78 | /// 79 | /// 80 | public BEncodedString(char[] value) 81 | : this(System.Text.Encoding.UTF8.GetBytes(value)) 82 | { 83 | } 84 | 85 | /// 86 | /// Create a new BEncodedString using UTF8 encoding 87 | /// 88 | /// Initial value for the string 89 | public BEncodedString(string value) 90 | : this(System.Text.Encoding.UTF8.GetBytes(value)) 91 | { 92 | } 93 | 94 | 95 | /// 96 | /// Create a new BEncodedString using UTF8 encoding 97 | /// 98 | /// 99 | public BEncodedString(byte[] value) 100 | { 101 | this.textBytes = value; 102 | } 103 | 104 | 105 | public static implicit operator BEncodedString(string value) 106 | { 107 | return new BEncodedString(value); 108 | } 109 | public static implicit operator BEncodedString(char[] value) 110 | { 111 | return new BEncodedString(value); 112 | } 113 | public static implicit operator BEncodedString(byte[] value) 114 | { 115 | return new BEncodedString(value); 116 | } 117 | #endregion 118 | 119 | 120 | #region Encode/Decode Methods 121 | 122 | 123 | /// 124 | /// Encodes the BEncodedString to a byte[] using the supplied Encoding 125 | /// 126 | /// The buffer to encode the string to 127 | /// The offset at which to save the data to 128 | /// The encoding to use 129 | /// The number of bytes encoded 130 | public override int Encode(byte[] buffer, int offset) 131 | { 132 | int written = offset; 133 | written += Message.WriteAscii(buffer, written, textBytes.Length.ToString ()); 134 | written += Message.WriteAscii(buffer, written, ":"); 135 | written += Message.Write(buffer, written, textBytes); 136 | return written - offset; 137 | } 138 | 139 | 140 | /// 141 | /// Decodes a BEncodedString from the supplied StreamReader 142 | /// 143 | /// The StreamReader containing the BEncodedString 144 | internal override void DecodeInternal(RawReader reader) 145 | { 146 | if (reader == null) 147 | throw new ArgumentNullException("reader"); 148 | 149 | int letterCount; 150 | string length = string.Empty; 151 | 152 | while ((reader.PeekByte() != -1) && (reader.PeekByte() != ':')) // read in how many characters 153 | length += (char)reader.ReadByte(); // the string is 154 | 155 | if (reader.ReadByte() != ':') // remove the ':' 156 | throw new BEncodingException("Invalid data found. Aborting"); 157 | 158 | if (!int.TryParse(length, out letterCount)) 159 | throw new BEncodingException(string.Format("Invalid BEncodedString. Length was '{0}' instead of a number", length)); 160 | 161 | this.textBytes = new byte[letterCount]; 162 | if (reader.Read(textBytes, 0, letterCount) != letterCount) 163 | throw new BEncodingException("Couldn't decode string"); 164 | } 165 | #endregion 166 | 167 | 168 | #region Helper Methods 169 | public string Hex 170 | { 171 | get { return BitConverter.ToString(TextBytes); } 172 | } 173 | 174 | public override int LengthInBytes() 175 | { 176 | // The length is equal to the length-prefix + ':' + length of data 177 | int prefix = 1; // Account for ':' 178 | 179 | // Count the number of characters needed for the length prefix 180 | for (int i = textBytes.Length; i != 0; i = i/10) 181 | prefix += 1; 182 | 183 | if (textBytes.Length == 0) 184 | prefix++; 185 | 186 | return prefix + textBytes.Length; 187 | } 188 | 189 | public int CompareTo(object other) 190 | { 191 | return CompareTo(other as BEncodedString); 192 | } 193 | 194 | 195 | public int CompareTo(BEncodedString other) 196 | { 197 | if (other == null) 198 | return 1; 199 | 200 | int difference=0; 201 | int length = this.textBytes.Length > other.textBytes.Length ? other.textBytes.Length : this.textBytes.Length; 202 | 203 | for (int i = 0; i < length; i++) 204 | if ((difference = this.textBytes[i].CompareTo(other.textBytes[i])) != 0) 205 | return difference; 206 | 207 | if (this.textBytes.Length == other.textBytes.Length) 208 | return 0; 209 | 210 | return this.textBytes.Length > other.textBytes.Length ? 1 : -1; 211 | } 212 | 213 | #endregion 214 | 215 | 216 | #region Overridden Methods 217 | 218 | public override bool Equals(object obj) 219 | { 220 | if (obj == null) 221 | return false; 222 | 223 | BEncodedString other; 224 | if (obj is string) 225 | other = new BEncodedString((string)obj); 226 | else if (obj is BEncodedString) 227 | other = (BEncodedString)obj; 228 | else 229 | return false; 230 | 231 | return Toolbox.ByteMatch(this.textBytes, other.textBytes); 232 | } 233 | 234 | public override int GetHashCode() 235 | { 236 | int hash = 0; 237 | for (int i = 0; i < this.textBytes.Length; i++) 238 | hash += this.textBytes[i]; 239 | 240 | return hash; 241 | } 242 | 243 | public override string ToString() 244 | { 245 | return System.Text.Encoding.UTF8.GetString(textBytes); 246 | } 247 | 248 | #endregion 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/Locate/TorrentFileLocater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using TorrentHardLinkHelper.Torrents; 7 | 8 | namespace TorrentHardLinkHelper.Locate 9 | { 10 | public class TorrentFileLocater 11 | { 12 | private readonly IList _fsfiFileInfos; 13 | private readonly IList _torrentFileLinks; 14 | private readonly Dictionary _pieceCheckedReusltsDictionary; 15 | private readonly Torrent _torrent; 16 | 17 | private readonly Action _fileLocating; 18 | 19 | public TorrentFileLocater() 20 | { 21 | this._torrentFileLinks = new List(); 22 | } 23 | 24 | public TorrentFileLocater(Torrent torrent) 25 | : this() 26 | { 27 | this._torrent = torrent; 28 | this._pieceCheckedReusltsDictionary = new Dictionary(this._torrent.Pieces.Count); 29 | } 30 | 31 | public TorrentFileLocater(Torrent torrent, IList fsfiFileInfos) 32 | : this(torrent) 33 | { 34 | this._fsfiFileInfos = fsfiFileInfos; 35 | } 36 | 37 | public TorrentFileLocater(Torrent torrent, IList fsfiFileInfos, Action fileLocating) 38 | : this(torrent, fsfiFileInfos) 39 | { 40 | this._fileLocating = fileLocating; 41 | } 42 | 43 | 44 | public LocateResult Locate() 45 | { 46 | if (this._torrent == null) 47 | { 48 | throw new ArgumentException("Torrent cannot be null."); 49 | } 50 | if (this._fsfiFileInfos == null || this._fsfiFileInfos.Count == 0) 51 | { 52 | throw new ArgumentException("FsFileInfos cannot be null or zero"); 53 | } 54 | 55 | this.FindTorrentFileLinks(); 56 | this.ConfirmFileSystemFiles(); 57 | return new LocateResult(this._torrentFileLinks); 58 | } 59 | 60 | private void FindTorrentFileLinks() 61 | { 62 | foreach (TorrentFile torrentFile in this._torrent.Files) 63 | { 64 | var fileLink = new TorrentFileLink(torrentFile); 65 | foreach (FileSystemFileInfo fsFileInfo in this._fsfiFileInfos) 66 | { 67 | if (fsFileInfo.Length == torrentFile.Length) 68 | { 69 | fileLink.FsFileInfos.Add(fsFileInfo); 70 | } 71 | } 72 | if (fileLink.FsFileInfos.Count > 1) 73 | { 74 | var torrentFilePathParts = torrentFile.Path.Split('\\').ToList(); 75 | torrentFilePathParts.Insert(0, this._torrent.Name); 76 | for (int i = 0; i < torrentFilePathParts.Count; i++) 77 | { 78 | var links = new List(); 79 | foreach (var fileInfo in fileLink.FsFileInfos) 80 | { 81 | var filePathPaths = fileInfo.FilePath.Split('\\'); 82 | if (filePathPaths.Length > i + 1 && 83 | filePathPaths[filePathPaths.Length - i - 1].ToUpperInvariant() == 84 | torrentFilePathParts[torrentFilePathParts.Count - i - 1].ToUpperInvariant()) 85 | { 86 | links.Add(fileInfo); 87 | } 88 | } 89 | if (links.Count == 0) 90 | { 91 | break; 92 | } 93 | if (links.Count >= 1) 94 | { 95 | fileLink.FsFileInfos = links; 96 | if (links.Count == 1) 97 | { 98 | break; 99 | } 100 | } 101 | } 102 | } 103 | if (fileLink.FsFileInfos.Count == 1) 104 | { 105 | fileLink.State = LinkState.Located; 106 | fileLink.LinkedFsFileIndex = 0; 107 | } 108 | else if (fileLink.FsFileInfos.Count > 1) 109 | { 110 | fileLink.State = LinkState.NeedConfirm; 111 | } 112 | else 113 | { 114 | fileLink.State = LinkState.Fail; 115 | } 116 | 117 | this._torrentFileLinks.Add(fileLink); 118 | } 119 | } 120 | 121 | private void ConfirmFileSystemFiles() 122 | { 123 | foreach (TorrentFileLink fileLink in this._torrentFileLinks) 124 | { 125 | if (this._fileLocating != null) 126 | { 127 | this._fileLocating.Invoke(); 128 | } 129 | if (fileLink.State == LinkState.Located) 130 | { 131 | continue; 132 | } 133 | if (fileLink.State == LinkState.Fail) 134 | { 135 | if (fileLink.TorrentFile.EndPieceIndex - fileLink.TorrentFile.StartPieceIndex > 2) 136 | { 137 | fileLink.State = CheckPiece(fileLink.TorrentFile.StartPieceIndex + 1) 138 | ? LinkState.Located 139 | : LinkState.Fail; 140 | } 141 | continue; 142 | } 143 | for (int i = fileLink.TorrentFile.StartPieceIndex; i <= fileLink.TorrentFile.EndPieceIndex; i++) 144 | { 145 | if (!CheckPiece(i)) 146 | { 147 | break; 148 | } 149 | if (fileLink.State == LinkState.Located) 150 | { 151 | break; 152 | } 153 | } 154 | } 155 | } 156 | 157 | private bool CheckPiece(int pieceIndex) 158 | { 159 | bool result; 160 | if (this._pieceCheckedReusltsDictionary.TryGetValue(pieceIndex, out result)) 161 | { 162 | return result; 163 | } 164 | ulong startPos = (ulong)pieceIndex * (ulong)this._torrent.PieceLength; 165 | ulong pos = 0; 166 | ulong writenLength = 0; 167 | var filePieces = new List(); 168 | foreach (TorrentFileLink fileLink in this._torrentFileLinks) 169 | { 170 | if (pos + (ulong)fileLink.TorrentFile.Length >= startPos) 171 | { 172 | ulong readPos = startPos - pos; 173 | ulong readLength = (ulong)fileLink.TorrentFile.Length - readPos; 174 | if (writenLength + readLength > (ulong)this._torrent.PieceLength) 175 | { 176 | readLength = (ulong)this._torrent.PieceLength - writenLength; 177 | } 178 | 179 | var filePiece = new FileLinkPiece 180 | { 181 | FileLink = fileLink, 182 | ReadLength = readLength, 183 | StartPos = readPos 184 | }; 185 | filePieces.Add(filePiece); 186 | 187 | writenLength += readLength; 188 | startPos += readLength; 189 | if (writenLength == (ulong)this._torrent.PieceLength) 190 | { 191 | break; 192 | } 193 | } 194 | pos += (ulong)fileLink.TorrentFile.Length; 195 | } 196 | 197 | var hash = new HashFileLinkPieces(this._torrent, pieceIndex, filePieces); 198 | string pattern = hash.Run(); 199 | if (string.IsNullOrEmpty(pattern)) 200 | { 201 | foreach (FileLinkPiece piece in filePieces) 202 | { 203 | if (piece.FileLink.State == LinkState.NeedConfirm) 204 | { 205 | piece.FileLink.State = LinkState.Fail; 206 | } 207 | } 208 | this._pieceCheckedReusltsDictionary.Add(pieceIndex, false); 209 | return false; 210 | } 211 | for (int i = 0; i < filePieces.Count; i++) 212 | { 213 | if (filePieces[i].FileLink.State == LinkState.NeedConfirm) 214 | { 215 | filePieces[i].FileLink.LinkedFsFileIndex = int.Parse(pattern.Split(',')[i]); 216 | filePieces[i].FileLink.LinkedFsFileInfo.Located = true; 217 | filePieces[i].FileLink.State = LinkState.Located; 218 | } 219 | } 220 | this._pieceCheckedReusltsDictionary.Add(pieceIndex, true); 221 | return true; 222 | } 223 | } 224 | } -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/IBEncodedValue.cs: -------------------------------------------------------------------------------- 1 | // 2 | // IBEncodedValue.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | 31 | using System; 32 | using System.IO; 33 | using System.Text; 34 | using TorrentHardLinkHelper.BEncoding; 35 | using TorrentHardLinkHelper.Common; 36 | 37 | namespace TorrentHardLinkHelper.BEncoding 38 | { 39 | /// 40 | /// Base interface for all BEncoded values. 41 | /// 42 | public abstract class BEncodedValue 43 | { 44 | internal abstract void DecodeInternal(RawReader reader); 45 | 46 | /// 47 | /// Encodes the BEncodedValue into a byte array 48 | /// 49 | /// Byte array containing the BEncoded Data 50 | public byte[] Encode() 51 | { 52 | byte[] buffer = new byte[LengthInBytes()]; 53 | if (Encode(buffer, 0) != buffer.Length) 54 | throw new BEncodingException("Error encoding the data"); 55 | 56 | return buffer; 57 | } 58 | 59 | 60 | /// 61 | /// Encodes the BEncodedValue into the supplied buffer 62 | /// 63 | /// The buffer to encode the information to 64 | /// The offset in the buffer to start writing the data 65 | /// 66 | public abstract int Encode(byte[] buffer, int offset); 67 | 68 | public static T Clone (T value) 69 | where T : BEncodedValue 70 | { 71 | Check.Value (value); 72 | return (T) BEncodedValue.Decode (value.Encode ()); 73 | } 74 | 75 | /// 76 | /// Interface for all BEncoded values 77 | /// 78 | /// The byte array containing the BEncoded data 79 | /// 80 | public static BEncodedValue Decode(byte[] data) 81 | { 82 | if (data == null) 83 | throw new ArgumentNullException("data"); 84 | 85 | using (RawReader stream = new RawReader(new MemoryStream(data))) 86 | return (Decode(stream)); 87 | } 88 | 89 | internal static BEncodedValue Decode(byte[] buffer, bool strictDecoding) 90 | { 91 | return Decode(buffer, 0, buffer.Length, strictDecoding); 92 | } 93 | 94 | /// 95 | /// Decode BEncoded data in the given byte array 96 | /// 97 | /// The byte array containing the BEncoded data 98 | /// The offset at which the data starts at 99 | /// The number of bytes to be decoded 100 | /// BEncodedValue containing the data that was in the byte[] 101 | public static BEncodedValue Decode(byte[] buffer, int offset, int length) 102 | { 103 | return Decode(buffer, offset, length, true); 104 | } 105 | 106 | public static BEncodedValue Decode(byte[] buffer, int offset, int length, bool strictDecoding) 107 | { 108 | if (buffer == null) 109 | throw new ArgumentNullException("buffer"); 110 | 111 | if (offset < 0 || length < 0) 112 | throw new IndexOutOfRangeException("Neither offset or length can be less than zero"); 113 | 114 | if (offset > buffer.Length - length) 115 | throw new ArgumentOutOfRangeException("length"); 116 | 117 | using (RawReader reader = new RawReader(new MemoryStream(buffer, offset, length), strictDecoding)) 118 | return (BEncodedValue.Decode(reader)); 119 | } 120 | 121 | 122 | /// 123 | /// Decode BEncoded data in the given stream 124 | /// 125 | /// The stream containing the BEncoded data 126 | /// BEncodedValue containing the data that was in the stream 127 | public static BEncodedValue Decode(Stream stream) 128 | { 129 | if (stream == null) 130 | throw new ArgumentNullException("stream"); 131 | 132 | return Decode(new RawReader(stream)); 133 | } 134 | 135 | 136 | /// 137 | /// Decode BEncoded data in the given RawReader 138 | /// 139 | /// The RawReader containing the BEncoded data 140 | /// BEncodedValue containing the data that was in the stream 141 | public static BEncodedValue Decode(RawReader reader) 142 | { 143 | if (reader == null) 144 | throw new ArgumentNullException("reader"); 145 | 146 | BEncodedValue data; 147 | switch (reader.PeekByte()) 148 | { 149 | case ('i'): // Integer 150 | data = new BEncodedNumber(); 151 | break; 152 | 153 | case ('d'): // Dictionary 154 | data = new BEncodedDictionary(); 155 | break; 156 | 157 | case ('l'): // List 158 | data = new BEncodedList(); 159 | break; 160 | 161 | case ('1'): // String 162 | case ('2'): 163 | case ('3'): 164 | case ('4'): 165 | case ('5'): 166 | case ('6'): 167 | case ('7'): 168 | case ('8'): 169 | case ('9'): 170 | case ('0'): 171 | data = new BEncodedString(); 172 | break; 173 | 174 | default: 175 | throw new BEncodingException("Could not find what value to decode"); 176 | } 177 | 178 | data.DecodeInternal(reader); 179 | return data; 180 | } 181 | 182 | 183 | /// 184 | /// Interface for all BEncoded values 185 | /// 186 | /// The byte array containing the BEncoded data 187 | /// 188 | public static T Decode(byte[] data) where T : BEncodedValue 189 | { 190 | return (T)BEncodedValue.Decode(data); 191 | } 192 | 193 | 194 | /// 195 | /// Decode BEncoded data in the given byte array 196 | /// 197 | /// The byte array containing the BEncoded data 198 | /// The offset at which the data starts at 199 | /// The number of bytes to be decoded 200 | /// BEncodedValue containing the data that was in the byte[] 201 | public static T Decode(byte[] buffer, int offset, int length) where T : BEncodedValue 202 | { 203 | return BEncodedValue.Decode(buffer, offset, length, true); 204 | } 205 | 206 | public static T Decode(byte[] buffer, int offset, int length, bool strictDecoding) where T : BEncodedValue 207 | { 208 | return (T)BEncodedValue.Decode(buffer, offset, length, strictDecoding); 209 | } 210 | 211 | 212 | /// 213 | /// Decode BEncoded data in the given stream 214 | /// 215 | /// The stream containing the BEncoded data 216 | /// BEncodedValue containing the data that was in the stream 217 | public static T Decode(Stream stream) where T : BEncodedValue 218 | { 219 | return (T)BEncodedValue.Decode(stream); 220 | } 221 | 222 | 223 | public static T Decode(RawReader reader) where T : BEncodedValue 224 | { 225 | return (T)BEncodedValue.Decode(reader); 226 | } 227 | 228 | 229 | /// 230 | /// Returns the size of the byte[] needed to encode this BEncodedValue 231 | /// 232 | /// 233 | public abstract int LengthInBytes(); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper/TorrentHardLinkHelper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F60A582A-C5E4-4D17-97AB-B30E24F22D08} 8 | WinExe 9 | Properties 10 | TorrentHardLinkHelper 11 | TorrentHardLinkHelper 12 | v4.0 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | false 17 | D:\hardlinktest\ 18 | true 19 | Disk 20 | false 21 | Foreground 22 | 7 23 | Days 24 | false 25 | false 26 | true 27 | 1 28 | 1.0.0.%2a 29 | false 30 | true 31 | true 32 | 33 | 34 | AnyCPU 35 | true 36 | full 37 | false 38 | bin\Debug\ 39 | DEBUG;TRACE 40 | prompt 41 | 4 42 | 43 | 44 | AnyCPU 45 | pdbonly 46 | true 47 | bin\Release\ 48 | TRACE 49 | prompt 50 | 4 51 | 52 | 53 | 6221D2BFC157999FB0D7F00DE2A9B0318A0A1495 54 | 55 | 56 | TorrentHardLinkHelper_TemporaryKey.pfx 57 | 58 | 59 | true 60 | 61 | 62 | false 63 | 64 | 65 | ico2.ico 66 | 67 | 68 | 69 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net40\GalaSoft.MvvmLight.dll 70 | True 71 | 72 | 73 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net40\GalaSoft.MvvmLight.Extras.dll 74 | True 75 | 76 | 77 | ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll 78 | 79 | 80 | ..\packages\Ookii.Dialogs.1.0\lib\net35\Ookii.Dialogs.Wpf.dll 81 | 82 | 83 | 84 | 85 | 86 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net40\System.Windows.Interactivity.dll 87 | True 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 4.0 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | MSBuild:Compile 104 | Designer 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Designer 114 | MSBuild:Compile 115 | 116 | 117 | MSBuild:Compile 118 | Designer 119 | 120 | 121 | App.xaml 122 | Code 123 | 124 | 125 | HardLinkTool.xaml 126 | 127 | 128 | MainWindow.xaml 129 | Code 130 | 131 | 132 | 133 | 134 | Code 135 | 136 | 137 | True 138 | True 139 | Resources.resx 140 | 141 | 142 | True 143 | Settings.settings 144 | True 145 | 146 | 147 | ResXFileCodeGenerator 148 | Resources.Designer.cs 149 | 150 | 151 | 152 | 153 | SettingsSingleFileGenerator 154 | Settings.Designer.cs 155 | 156 | 157 | 158 | 159 | 160 | 161 | {c0e94189-b243-4492-a113-6eb2de57bcc0} 162 | TorrentHardLinkHelper.Library 163 | 164 | 165 | 166 | 167 | False 168 | Microsoft .NET Framework 4 %28x86 and x64%29 169 | true 170 | 171 | 172 | False 173 | .NET Framework 3.5 SP1 Client Profile 174 | false 175 | 176 | 177 | False 178 | .NET Framework 3.5 SP1 179 | false 180 | 181 | 182 | False 183 | Windows Installer 4.5 184 | true 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 201 | -------------------------------------------------------------------------------- /TorrentHardLinkHelper.Library/BEncoding/BEncodedDictionary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // BEncodedDictionary.cs 3 | // 4 | // Authors: 5 | // Alan McGovern alan.mcgovern@gmail.com 6 | // 7 | // Copyright (C) 2006 Alan McGovern 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | using System; 31 | using System.Collections.Generic; 32 | using System.IO; 33 | 34 | namespace TorrentHardLinkHelper.BEncoding 35 | { 36 | /// 37 | /// Class representing a BEncoded Dictionary 38 | /// 39 | public class BEncodedDictionary : BEncodedValue, IDictionary 40 | { 41 | #region Member Variables 42 | 43 | private SortedDictionary dictionary; 44 | 45 | #endregion 46 | 47 | 48 | #region Constructors 49 | 50 | /// 51 | /// Create a new BEncodedDictionary 52 | /// 53 | public BEncodedDictionary() 54 | { 55 | this.dictionary = new SortedDictionary(); 56 | } 57 | 58 | #endregion 59 | 60 | 61 | #region Encode/Decode Methods 62 | 63 | /// 64 | /// Encodes the dictionary to a byte[] 65 | /// 66 | /// The buffer to encode the data to 67 | /// The offset to start writing the data to 68 | /// 69 | public override int Encode(byte[] buffer, int offset) 70 | { 71 | int written = 0; 72 | 73 | //Dictionaries start with 'd' 74 | buffer[offset] = (byte)'d'; 75 | written++; 76 | 77 | foreach (KeyValuePair keypair in this) 78 | { 79 | written += keypair.Key.Encode(buffer, offset + written); 80 | written += keypair.Value.Encode(buffer, offset + written); 81 | } 82 | 83 | // Dictionaries end with 'e' 84 | buffer[offset + written] = (byte)'e'; 85 | written++; 86 | return written; 87 | } 88 | 89 | 90 | /// 91 | /// 92 | /// 93 | /// 94 | internal override void DecodeInternal(RawReader reader) 95 | { 96 | DecodeInternal(reader, reader.StrictDecoding); 97 | } 98 | 99 | private void DecodeInternal(RawReader reader, bool strictDecoding) 100 | { 101 | BEncodedString key = null; 102 | BEncodedValue value = null; 103 | BEncodedString oldkey = null; 104 | 105 | if (reader.ReadByte() != 'd') 106 | throw new BEncodingException("Invalid data found. Aborting"); // Remove the leading 'd' 107 | 108 | while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e')) 109 | { 110 | key = (BEncodedString)BEncodedValue.Decode(reader); // keys have to be BEncoded strings 111 | 112 | if (oldkey != null && oldkey.CompareTo(key) > 0) 113 | if (strictDecoding) 114 | throw new BEncodingException(String.Format( 115 | "Illegal BEncodedDictionary. The attributes are not ordered correctly. Old key: {0}, New key: {1}", 116 | oldkey, key)); 117 | 118 | oldkey = key; 119 | value = BEncodedValue.Decode(reader); // the value is a BEncoded value 120 | dictionary.Add(key, value); 121 | } 122 | 123 | if (reader.ReadByte() != 'e') // remove the trailing 'e' 124 | throw new BEncodingException("Invalid data found. Aborting"); 125 | } 126 | 127 | public static BEncodedDictionary DecodeTorrent(byte[] bytes) 128 | { 129 | return DecodeTorrent(new MemoryStream(bytes)); 130 | } 131 | 132 | public static BEncodedDictionary DecodeTorrent(Stream s) 133 | { 134 | return DecodeTorrent(new RawReader(s)); 135 | } 136 | 137 | 138 | /// 139 | /// Special decoding method for torrent files - allows dictionary attributes to be out of order for the 140 | /// overall torrent file, but imposes strict rules on the info dictionary. 141 | /// 142 | /// 143 | public static BEncodedDictionary DecodeTorrent(RawReader reader) 144 | { 145 | BEncodedString key = null; 146 | BEncodedValue value = null; 147 | BEncodedDictionary torrent = new BEncodedDictionary(); 148 | if (reader.ReadByte() != 'd') 149 | throw new BEncodingException("Invalid data found. Aborting"); // Remove the leading 'd' 150 | 151 | while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e')) 152 | { 153 | key = (BEncodedString)BEncodedValue.Decode(reader); // keys have to be BEncoded strings 154 | 155 | if (reader.PeekByte() == 'd') 156 | { 157 | value = new BEncodedDictionary(); 158 | if (key.Text.ToLower().Equals("info")) 159 | ((BEncodedDictionary)value).DecodeInternal(reader, true); 160 | else 161 | ((BEncodedDictionary)value).DecodeInternal(reader, false); 162 | } 163 | else 164 | value = BEncodedValue.Decode(reader); // the value is a BEncoded value 165 | 166 | torrent.dictionary.Add(key, value); 167 | } 168 | 169 | if (reader.ReadByte() != 'e') // remove the trailing 'e' 170 | throw new BEncodingException("Invalid data found. Aborting"); 171 | 172 | return torrent; 173 | } 174 | 175 | #endregion 176 | 177 | 178 | #region Helper Methods 179 | 180 | /// 181 | /// Returns the size of the dictionary in bytes using UTF8 encoding 182 | /// 183 | /// 184 | public override int LengthInBytes() 185 | { 186 | int length = 0; 187 | length += 1; // Dictionaries start with 'd' 188 | 189 | foreach (KeyValuePair keypair in this.dictionary) 190 | { 191 | length += keypair.Key.LengthInBytes(); 192 | length += keypair.Value.LengthInBytes(); 193 | } 194 | length += 1; // Dictionaries end with 'e' 195 | return length; 196 | } 197 | 198 | #endregion 199 | 200 | 201 | #region Overridden Methods 202 | public override bool Equals(object obj) 203 | { 204 | BEncodedValue val; 205 | BEncodedDictionary other = obj as BEncodedDictionary; 206 | if (other == null) 207 | return false; 208 | 209 | if (this.dictionary.Count != other.dictionary.Count) 210 | return false; 211 | 212 | foreach (KeyValuePair keypair in this.dictionary) 213 | { 214 | if (!other.TryGetValue(keypair.Key, out val)) 215 | return false; 216 | 217 | if (!keypair.Value.Equals(val)) 218 | return false; 219 | } 220 | 221 | return true; 222 | } 223 | 224 | public override int GetHashCode() 225 | { 226 | int result = 0; 227 | foreach (KeyValuePair keypair in dictionary) 228 | { 229 | result ^= keypair.Key.GetHashCode(); 230 | result ^= keypair.Value.GetHashCode(); 231 | } 232 | 233 | return result; 234 | } 235 | 236 | public override string ToString() 237 | { 238 | return System.Text.Encoding.UTF8.GetString(Encode()); 239 | } 240 | #endregion 241 | 242 | 243 | #region IDictionary and IList methods 244 | public void Add(BEncodedString key, BEncodedValue value) 245 | { 246 | this.dictionary.Add(key, value); 247 | } 248 | 249 | public void Add(KeyValuePair item) 250 | { 251 | this.dictionary.Add(item.Key, item.Value); 252 | } 253 | public void Clear() 254 | { 255 | this.dictionary.Clear(); 256 | } 257 | 258 | public bool Contains(KeyValuePair item) 259 | { 260 | if (!this.dictionary.ContainsKey(item.Key)) 261 | return false; 262 | 263 | return this.dictionary[item.Key].Equals(item.Value); 264 | } 265 | 266 | public bool ContainsKey(BEncodedString key) 267 | { 268 | return this.dictionary.ContainsKey(key); 269 | } 270 | 271 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 272 | { 273 | this.dictionary.CopyTo(array, arrayIndex); 274 | } 275 | 276 | public int Count 277 | { 278 | get { return this.dictionary.Count; } 279 | } 280 | 281 | //public int IndexOf(KeyValuePair item) 282 | //{ 283 | // return this.dictionary.IndexOf(item); 284 | //} 285 | 286 | //public void Insert(int index, KeyValuePair item) 287 | //{ 288 | // this.dictionary.Insert(index, item); 289 | //} 290 | 291 | public bool IsReadOnly 292 | { 293 | get { return false; } 294 | } 295 | 296 | public bool Remove(BEncodedString key) 297 | { 298 | return this.dictionary.Remove(key); 299 | } 300 | 301 | public bool Remove(KeyValuePair item) 302 | { 303 | return this.dictionary.Remove(item.Key); 304 | } 305 | 306 | //public void RemoveAt(int index) 307 | //{ 308 | // this.dictionary.RemoveAt(index); 309 | //} 310 | 311 | public bool TryGetValue(BEncodedString key, out BEncodedValue value) 312 | { 313 | return this.dictionary.TryGetValue(key, out value); 314 | } 315 | 316 | public BEncodedValue this[BEncodedString key] 317 | { 318 | get { return this.dictionary[key]; } 319 | set { this.dictionary[key] = value; } 320 | } 321 | 322 | //public KeyValuePair this[int index] 323 | //{ 324 | // get { return this.dictionary[index]; } 325 | // set { this.dictionary[index] = value; } 326 | //} 327 | 328 | public ICollection Keys 329 | { 330 | get { return this.dictionary.Keys; } 331 | } 332 | 333 | public ICollection Values 334 | { 335 | get { return this.dictionary.Values; } 336 | } 337 | 338 | public IEnumerator> GetEnumerator() 339 | { 340 | return this.dictionary.GetEnumerator(); 341 | } 342 | 343 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 344 | { 345 | return this.dictionary.GetEnumerator(); 346 | } 347 | #endregion 348 | } 349 | } --------------------------------------------------------------------------------