- Add ability to generate .hyper files

- Add ability to open `.hyper` files
- Code optimizations
This commit is contained in:
Stone_Red
2022-02-03 19:15:42 +01:00
parent 996fc7e5a6
commit eeb1882685
9 changed files with 248 additions and 81 deletions
@@ -23,10 +23,9 @@ internal static class FileCompressor
public static IEnumerable<byte[]> ReadChunks(string path, int chunkSize) public static IEnumerable<byte[]> ReadChunks(string path, int chunkSize)
{ {
byte[] buffer = new byte[chunkSize]; byte[] buffer = new byte[chunkSize];
int bytesRead;
using FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); using FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using BufferedStream bs = new BufferedStream(fs); using BufferedStream bs = new BufferedStream(fs);
while ((bytesRead = bs.Read(buffer, 0, chunkSize)) != 0) while (bs.Read(buffer, 0, chunkSize) != 0)
{ {
yield return buffer; yield return buffer;
} }
@@ -4,9 +4,9 @@ namespace HyperbolicDownloader.FileProcessing;
internal class FilesManager internal class FilesManager
{ {
private readonly List<HyperFileInfo> files = new List<HyperFileInfo>(); private readonly List<PrivateHyperFileInfo> files = new List<PrivateHyperFileInfo>();
public bool TryAdd(string filePath, out HyperFileInfo? fileInfo, out string? errorMessage) public bool TryAdd(string filePath, out PrivateHyperFileInfo? fileInfo, out string? errorMessage)
{ {
if (string.IsNullOrWhiteSpace(filePath)) if (string.IsNullOrWhiteSpace(filePath))
{ {
@@ -33,7 +33,7 @@ internal class FilesManager
return false; return false;
} }
fileInfo = new HyperFileInfo(hash, fullPath); fileInfo = new PrivateHyperFileInfo(hash, fullPath);
files.Add(fileInfo); files.Add(fileInfo);
@@ -43,9 +43,9 @@ internal class FilesManager
return true; return true;
} }
public void AddRange(IEnumerable<HyperFileInfo> fileInfos) public void AddRange(IEnumerable<PrivateHyperFileInfo> fileInfos)
{ {
foreach (HyperFileInfo fileInfo in fileInfos) foreach (PrivateHyperFileInfo fileInfo in fileInfos)
{ {
if (File.Exists(fileInfo.FilePath) && !Contains(fileInfo.Hash)) if (File.Exists(fileInfo.FilePath) && !Contains(fileInfo.Hash))
{ {
@@ -56,7 +56,7 @@ internal class FilesManager
SaveFiles(); SaveFiles();
} }
public bool TryGet(string hash, out HyperFileInfo? fileInfo) public bool TryGet(string hash, out PrivateHyperFileInfo? fileInfo)
{ {
if (Contains(hash)) if (Contains(hash))
{ {
@@ -84,7 +84,7 @@ internal class FilesManager
return files.Any(f => f.Hash == hash); return files.Any(f => f.Hash == hash);
} }
public List<HyperFileInfo> ToList() public List<PrivateHyperFileInfo> ToList()
{ {
return files.ToList(); return files.ToList();
} }
@@ -1,13 +0,0 @@
namespace HyperbolicDownloader.FileProcessing;
internal class HyperFileInfo
{
public string Hash { get; set; } = string.Empty;
public string FilePath { get; set; } = string.Empty;
public HyperFileInfo(string hash, string filePath)
{
Hash = hash;
FilePath = filePath;
}
}
@@ -0,0 +1,13 @@
namespace HyperbolicDownloader.FileProcessing;
internal class PrivateHyperFileInfo
{
public string Hash { get; set; }
public string FilePath { get; set; }
public PrivateHyperFileInfo(string hash, string filePath)
{
Hash = hash;
FilePath = filePath;
}
}
@@ -0,0 +1,24 @@
using HyperbolicDownloader.Networking;
namespace HyperbolicDownloader.FileProcessing;
internal class PublicHyperFileInfo
{
public string Hash { get; set; } = string.Empty;
public List<NetworkSocket> Hosts { get; } = new();
public PublicHyperFileInfo()
{
}
public PublicHyperFileInfo(string hash)
{
Hash = hash;
}
public PublicHyperFileInfo(string hash, List<NetworkSocket> hosts)
{
Hash = hash;
Hosts = hosts;
}
}
@@ -88,7 +88,7 @@ namespace HyperbolicDownloader.Networking
public void StartListening(int port) public void StartListening(int port)
{ {
if (IsListening == true) if (IsListening)
{ {
throw new InvalidOperationException("Already listening!"); throw new InvalidOperationException("Already listening!");
} }
@@ -146,7 +146,7 @@ namespace HyperbolicDownloader.Networking
{ {
if (ex.SocketErrorCode != SocketError.Interrupted) if (ex.SocketErrorCode != SocketError.Interrupted)
{ {
throw ex; throw;
} }
} }
} }
@@ -162,22 +162,11 @@ namespace HyperbolicDownloader.Networking
hash = hash.Trim(); hash = hash.Trim();
NetworkStream nwStream = client.GetStream(); NetworkStream nwStream = client.GetStream();
client.SendBufferSize = 64000; client.SendBufferSize = 64000;
if (filesManager.TryGet(hash, out HyperFileInfo? hyperFileInfo) && File.Exists(hyperFileInfo?.FilePath)) if (filesManager.TryGet(hash, out PrivateHyperFileInfo? hyperFileInfo) && File.Exists(hyperFileInfo?.FilePath))
{ {
if (hash != await FileValidator.CalculateHashAsync(hyperFileInfo.FilePath)) FileInfo fileInfo = new FileInfo(hyperFileInfo.FilePath);
{
bytesToSend = Encoding.ASCII.GetBytes("Hash does not match!");
await nwStream.WriteAsync(bytesToSend);
return;
}
//string compressedFilePath = $"{hash + DateTime.Now.Millisecond}.temp"; bytesToSend = Encoding.ASCII.GetBytes($"{fileInfo.Length}/{Path.GetFileName(hyperFileInfo.FilePath)}");
//FileCompressor.CompressFile(hyperFileInfo.FilePath, compressedFilePath);
FileInfo compressedFileInfo = new FileInfo(hyperFileInfo.FilePath);
bytesToSend = Encoding.ASCII.GetBytes($"{compressedFileInfo.Length}/{Path.GetFileName(hyperFileInfo.FilePath)}");
await nwStream.WriteAsync(bytesToSend); await nwStream.WriteAsync(bytesToSend);
foreach (byte[]? chunk in FileCompressor.ReadChunks(hyperFileInfo.FilePath, 64000)) foreach (byte[]? chunk in FileCompressor.ReadChunks(hyperFileInfo.FilePath, 64000))
@@ -187,8 +176,6 @@ namespace HyperbolicDownloader.Networking
await nwStream.WriteAsync(chunk); await nwStream.WriteAsync(chunk);
} }
} }
// File.Delete(compressedFilePath);
} }
else else
{ {
@@ -4,7 +4,7 @@ using System.Net.Sockets;
namespace HyperbolicDownloader.Networking; namespace HyperbolicDownloader.Networking;
internal class NetworkUtilities internal static class NetworkUtilities
{ {
public static UnicastIPAddressInformation? GetUnicastIPAddressInformation(IPAddress address) public static UnicastIPAddressInformation? GetUnicastIPAddressInformation(IPAddress address)
{ {
@@ -12,15 +12,12 @@ internal class NetworkUtilities
{ {
foreach (UnicastIPAddressInformation unicastIPAddressInformation in adapter.GetIPProperties().UnicastAddresses) foreach (UnicastIPAddressInformation unicastIPAddressInformation in adapter.GetIPProperties().UnicastAddresses)
{ {
if (unicastIPAddressInformation.Address.AddressFamily == AddressFamily.InterNetwork) if (unicastIPAddressInformation.Address.AddressFamily == AddressFamily.InterNetwork && address.Equals(unicastIPAddressInformation.Address))
{
if (address.Equals(unicastIPAddressInformation.Address))
{ {
return unicastIPAddressInformation; return unicastIPAddressInformation;
} }
} }
} }
}
return null; return null;
} }
+26 -20
View File
@@ -1,5 +1,4 @@
using HyperbolicDownloader.FileProcessing;
using HyperbolicDownloader.FileProcessing;
using HyperbolicDownloader.Networking; using HyperbolicDownloader.Networking;
using HyperbolicDownloader.UserInterface; using HyperbolicDownloader.UserInterface;
@@ -10,30 +9,23 @@ using Stone_Red_Utilities.ConsoleExtentions;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Reflection;
using System.Text.Json; using System.Text.Json;
namespace HyperbolicDownloader; namespace HyperbolicDownloader;
internal class Program internal static class Program
{ {
public const int BroadcastPort = 2155; public const int BroadcastPort = 2155;
public const string HostsFilePath = "Hosts.json";
public const string FilesInfoPath = "Files.json"; public static string BasePath { get; } = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location) ?? string.Empty;
public static string HostsFilePath { get; } = Path.Combine(BasePath, "Hosts.json");
public static string FilesInfoPath { get; } = Path.Combine(BasePath, "Files.json");
public static int PublicPort { get; private set; } public static int PublicPort { get; private set; }
public static int PrivatePort { get; } = 3055; public static int PrivatePort { get; } = 3055;
public static IPAddress? PublicIpAddress public static IPAddress? PublicIpAddress => device?.GetExternalIPAsync().GetAwaiter().GetResult();
{
get
{
if (device is not null)
{
return device.GetExternalIPAsync().GetAwaiter().GetResult();
}
return null;
}
}
private static NatDevice? device; private static NatDevice? device;
private static Mapping? portMapping; private static Mapping? portMapping;
@@ -42,7 +34,7 @@ internal class Program
private static readonly NetworkClient networkClient = new(filesManager); private static readonly NetworkClient networkClient = new(filesManager);
private static readonly Random random = new(); private static readonly Random random = new();
private static async Task Main() private static async Task Main(string[] args)
{ {
Console.CancelKeyPress += Console_CancelKeyPress; Console.CancelKeyPress += Console_CancelKeyPress;
Console.CursorVisible = false; Console.CursorVisible = false;
@@ -56,7 +48,20 @@ internal class Program
if (File.Exists(FilesInfoPath)) if (File.Exists(FilesInfoPath))
{ {
string filesJson = await File.ReadAllTextAsync(FilesInfoPath); string filesJson = await File.ReadAllTextAsync(FilesInfoPath);
filesManager.AddRange(JsonSerializer.Deserialize<List<HyperFileInfo>>(filesJson) ?? new()); filesManager.AddRange(JsonSerializer.Deserialize<List<PrivateHyperFileInfo>>(filesJson) ?? new());
}
InputHandler inputHandler = new InputHandler(hostsManager, filesManager);
if (args.Length > 0 && File.Exists(args[0]))
{
inputHandler.GetFileFrom(args[0]);
Console.WriteLine("Do you want to continue using this instance? [y/N]");
if (char.ToLower(Console.ReadKey().KeyChar) != 'y')
{
return;
}
Console.WriteLine();
} }
Console.WriteLine("Searching for a UPnP/NAT-PMP device..."); Console.WriteLine("Searching for a UPnP/NAT-PMP device...");
@@ -77,7 +82,8 @@ internal class Program
} }
catch (SocketException ex) catch (SocketException ex)
{ {
Console.WriteLine($"An error occurred while starting the TCP listener! Error message: {ex.Message}"); ConsoleExt.WriteLine($"An error occurred while starting the TCP listener! Error message: {ex.Message}", ConsoleColor.Red); // net stop hns && net start hns
Console.ReadKey();
return; return;
} }
@@ -107,7 +113,7 @@ internal class Program
broadcastClient.OnBroadcastRecived += BroadcastClient_OnBroadcastRecived; broadcastClient.OnBroadcastRecived += BroadcastClient_OnBroadcastRecived;
ConsoleExt.WriteLine("Done", ConsoleColor.Green); ConsoleExt.WriteLine("Done", ConsoleColor.Green);
new InputHandler(hostsManager, filesManager).ReadInput(); inputHandler.ReadInput();
ClosePorts(); ClosePorts();
} }
@@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Text.Json;
namespace HyperbolicDownloader.UserInterface; namespace HyperbolicDownloader.UserInterface;
@@ -27,7 +28,12 @@ internal class InputHandler
commander.Register(Exit, "exit", "quit"); commander.Register(Exit, "exit", "quit");
commander.Register(ShowInfo, "info", "inf"); commander.Register(ShowInfo, "info", "inf");
commander.Register(Discover, "discover", "disc"); commander.Register(Discover, "discover", "disc");
commander.Register(GetFile, "get");
Command getCommand = commander.Register(GetFile, "get");
getCommand.Register(GetFileFrom, "from");
Command generateCommad = commander.Register(GenerateFileFull, "generate", "gen");
generateCommad.Register(GenerateFileSingle, "single");
Command addCommand = commander.Register(AddFile, "add"); Command addCommand = commander.Register(AddFile, "add");
addCommand.Register(AddHost, "host"); addCommand.Register(AddHost, "host");
@@ -113,9 +119,9 @@ internal class InputHandler
} }
} }
private void AddFile(string args) private void AddFile(string path)
{ {
if (filesManager.TryAdd(args, out HyperFileInfo? fileInfo, out string? message)) if (filesManager.TryAdd(path, out PrivateHyperFileInfo? fileInfo, out string? message))
{ {
ConsoleExt.WriteLine($"Added file: {fileInfo!.FilePath}", ConsoleColor.Green); ConsoleExt.WriteLine($"Added file: {fileInfo!.FilePath}", ConsoleColor.Green);
Console.WriteLine($"Hash: {fileInfo.Hash}"); Console.WriteLine($"Hash: {fileInfo.Hash}");
@@ -126,9 +132,11 @@ internal class InputHandler
} }
} }
private void RemoveFile(string args) private void RemoveFile(string hash)
{ {
if (filesManager.TryRemove(args)) hash = hash.Trim().ToLower();
if (filesManager.TryRemove(hash))
{ {
ConsoleExt.WriteLine($"Removed file successfully!", ConsoleColor.Green); ConsoleExt.WriteLine($"Removed file successfully!", ConsoleColor.Green);
} }
@@ -141,7 +149,7 @@ internal class InputHandler
private void ListFiles(string _) private void ListFiles(string _)
{ {
int index = 0; int index = 0;
foreach (HyperFileInfo fileInfo in filesManager.ToList()) foreach (PrivateHyperFileInfo fileInfo in filesManager.ToList())
{ {
index++; index++;
Console.WriteLine($"{index}) {fileInfo.FilePath}"); Console.WriteLine($"{index}) {fileInfo.FilePath}");
@@ -203,6 +211,151 @@ internal class InputHandler
exit = true; exit = true;
} }
private void GenerateFileSingle(string hash)
{
string directoryPath = Path.Combine(Program.BasePath, "GeneratedFiles");
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
hash = hash.Trim().ToLower();
if (!filesManager.TryGet(hash, out PrivateHyperFileInfo? localHyperFileInfo))
{
ConsoleExt.WriteLine("The file is not being tracked!", ConsoleColor.Red);
return;
}
string fileName = Path.GetFileName(localHyperFileInfo!.FilePath);
string filePath = Path.Combine(directoryPath, $"{fileName}.hyper");
PublicHyperFileInfo publicHyperFileInfo = new PublicHyperFileInfo(hash);
NetworkSocket? localHost = Program.GetLocalSocket();
if (localHost is null)
{
ConsoleExt.WriteLine("Network error!", ConsoleColor.Red);
return;
}
publicHyperFileInfo.Hosts.Add(localHost);
JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(publicHyperFileInfo, options);
File.WriteAllText(filePath, json);
ConsoleExt.WriteLine("Done", ConsoleColor.Green);
Console.WriteLine($"File saved at: {Path.GetFullPath(filePath)}");
}
private void GenerateFileFull(string hash)
{
string directoryPath = Path.Combine(Program.BasePath, "GeneratedFiles");
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
hash = hash.Trim().ToLower();
if (!filesManager.TryGet(hash, out PrivateHyperFileInfo? localHyperFileInfo))
{
ConsoleExt.WriteLine("The file is not being tracked!", ConsoleColor.Red);
return;
}
string fileName = Path.GetFileName(localHyperFileInfo!.FilePath);
string filePath = Path.Combine(directoryPath, $"{fileName}.hyper");
PublicHyperFileInfo publicHyperFileInfo = new PublicHyperFileInfo(hash);
NetworkSocket? localHost = Program.GetLocalSocket();
if (localHost is null)
{
ConsoleExt.WriteLine("Network error!", ConsoleColor.Red);
return;
}
foreach (NetworkSocket host in hostsManager.ToList())
{
bool validIpAdress = IPAddress.TryParse(host.IPAddress, out IPAddress? ipAddress);
if (!validIpAdress)
{
hostsManager.Remove(host, true);
continue;
}
ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow);
Console.CursorLeft = 0;
Task<bool> sendTask = NetworkClient.SendAsync<bool>(ipAddress!, host.Port, "HasFile", hash);
_ = sendTask.Wait(1000);
if (!sendTask.IsCompletedSuccessfully)
{
Console.CursorLeft = 0;
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red);
hostsManager.Remove(host);
continue;
}
else if (!sendTask.Result)
{
host.LastActive = DateTime.Now;
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", ConsoleColor.Red);
continue;
}
host.LastActive = DateTime.Now;
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green);
publicHyperFileInfo.Hosts.Add(host);
}
publicHyperFileInfo.Hosts.Add(localHost);
JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(publicHyperFileInfo, options);
File.WriteAllText(filePath, json);
ConsoleExt.WriteLine("Done", ConsoleColor.Green);
Console.WriteLine($"File saved at: {Path.GetFullPath(filePath)}");
}
public void GetFileFrom(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
ConsoleExt.WriteLine("Path is empty!", ConsoleColor.Red);
return;
}
string fullPath = Path.GetFullPath(path);
if (!File.Exists(fullPath))
{
ConsoleExt.WriteLine("Invalid file path!", ConsoleColor.Red);
}
string json = File.ReadAllText(fullPath);
PublicHyperFileInfo? publicHyperFileInfo = JsonSerializer.Deserialize<PublicHyperFileInfo>(json);
if (publicHyperFileInfo == null)
{
ConsoleExt.WriteLine("Parsing file failed!", ConsoleColor.Red);
return;
}
hostsManager.AddRange(publicHyperFileInfo.Hosts);
GetFile(publicHyperFileInfo.Hash);
}
private void GetFile(string hash) private void GetFile(string hash)
{ {
if (string.IsNullOrEmpty(hash)) if (string.IsNullOrEmpty(hash))
@@ -211,7 +364,7 @@ internal class InputHandler
return; return;
} }
hash = hash.Trim(); hash = hash.Trim().ToLower();
foreach (NetworkSocket host in hostsManager.ToList()) foreach (NetworkSocket host in hostsManager.ToList())
{ {
@@ -251,7 +404,7 @@ internal class InputHandler
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green); ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green);
Console.WriteLine("Requesting file..."); Console.WriteLine("Requesting file...");
TcpClient tcpClient = new TcpClient(); using TcpClient tcpClient = new TcpClient();
tcpClient.Connect(ipAddress!, host.Port); tcpClient.Connect(ipAddress!, host.Port);
tcpClient.ReceiveBufferSize = 64000; tcpClient.ReceiveBufferSize = 64000;
@@ -290,19 +443,20 @@ internal class InputHandler
} }
string fileName = parts[1].ToFileName(); string fileName = parts[1].ToFileName();
string filepath = $"./Downloads/{fileName}"; string directoryPath = Path.Combine(Program.BasePath, "Downloads");
string filePath = Path.Combine(directoryPath, fileName);
Console.WriteLine($"File name: {fileName}"); Console.WriteLine($"File name: {fileName}");
Console.WriteLine($"Starting download..."); Console.WriteLine($"Starting download...");
int totalBytesRead = 0; int totalBytesRead = 0;
if (!Directory.Exists("./Downloads")) if (!Directory.Exists(directoryPath))
{ {
Directory.CreateDirectory("./Downloads"); Directory.CreateDirectory(directoryPath);
} }
using FileStream? fileStream = new FileStream(filepath, FileMode.Create); using FileStream? fileStream = new FileStream(filePath, FileMode.Create);
int bytesInOneSecond = 0; int bytesInOneSecond = 0;
int unitsPerSecond = 0; int unitsPerSecond = 0;
@@ -362,16 +516,16 @@ internal class InputHandler
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Validating file..."); Console.WriteLine("Validating file...");
if (FileValidator.ValidateHash(filepath, hash)) if (FileValidator.ValidateHash(filePath, hash))
{ {
_ = filesManager.TryAdd(filepath, out _, out _); _ = filesManager.TryAdd(filePath, out _, out _);
} }
else else
{ {
ConsoleExt.WriteLine("Warning: File hash does not match! File might me corrupted or manipulated!", ConsoleColor.DarkYellow); ConsoleExt.WriteLine("Warning: File hash does not match! File might me corrupted or manipulated!", ConsoleColor.DarkYellow);
} }
Console.WriteLine($"File saved at: {Path.GetFullPath(filepath)}"); Console.WriteLine($"File saved at: {Path.GetFullPath(filePath)}");
ConsoleExt.WriteLine("Done", ConsoleColor.Green); ConsoleExt.WriteLine("Done", ConsoleColor.Green);
stopWatch.Stop(); stopWatch.Stop();
hostsManager.SaveHosts(); hostsManager.SaveHosts();