- 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)
{
byte[] buffer = new byte[chunkSize];
int bytesRead;
using FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using BufferedStream bs = new BufferedStream(fs);
while ((bytesRead = bs.Read(buffer, 0, chunkSize)) != 0)
while (bs.Read(buffer, 0, chunkSize) != 0)
{
yield return buffer;
}
@@ -4,9 +4,9 @@ namespace HyperbolicDownloader.FileProcessing;
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))
{
@@ -33,7 +33,7 @@ internal class FilesManager
return false;
}
fileInfo = new HyperFileInfo(hash, fullPath);
fileInfo = new PrivateHyperFileInfo(hash, fullPath);
files.Add(fileInfo);
@@ -43,9 +43,9 @@ internal class FilesManager
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))
{
@@ -56,7 +56,7 @@ internal class FilesManager
SaveFiles();
}
public bool TryGet(string hash, out HyperFileInfo? fileInfo)
public bool TryGet(string hash, out PrivateHyperFileInfo? fileInfo)
{
if (Contains(hash))
{
@@ -84,7 +84,7 @@ internal class FilesManager
return files.Any(f => f.Hash == hash);
}
public List<HyperFileInfo> ToList()
public List<PrivateHyperFileInfo> 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)
{
if (IsListening == true)
if (IsListening)
{
throw new InvalidOperationException("Already listening!");
}
@@ -146,7 +146,7 @@ namespace HyperbolicDownloader.Networking
{
if (ex.SocketErrorCode != SocketError.Interrupted)
{
throw ex;
throw;
}
}
}
@@ -162,22 +162,11 @@ namespace HyperbolicDownloader.Networking
hash = hash.Trim();
NetworkStream nwStream = client.GetStream();
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))
{
bytesToSend = Encoding.ASCII.GetBytes("Hash does not match!");
await nwStream.WriteAsync(bytesToSend);
return;
}
FileInfo fileInfo = new FileInfo(hyperFileInfo.FilePath);
//string compressedFilePath = $"{hash + DateTime.Now.Millisecond}.temp";
//FileCompressor.CompressFile(hyperFileInfo.FilePath, compressedFilePath);
FileInfo compressedFileInfo = new FileInfo(hyperFileInfo.FilePath);
bytesToSend = Encoding.ASCII.GetBytes($"{compressedFileInfo.Length}/{Path.GetFileName(hyperFileInfo.FilePath)}");
bytesToSend = Encoding.ASCII.GetBytes($"{fileInfo.Length}/{Path.GetFileName(hyperFileInfo.FilePath)}");
await nwStream.WriteAsync(bytesToSend);
foreach (byte[]? chunk in FileCompressor.ReadChunks(hyperFileInfo.FilePath, 64000))
@@ -187,8 +176,6 @@ namespace HyperbolicDownloader.Networking
await nwStream.WriteAsync(chunk);
}
}
// File.Delete(compressedFilePath);
}
else
{
@@ -4,7 +4,7 @@ using System.Net.Sockets;
namespace HyperbolicDownloader.Networking;
internal class NetworkUtilities
internal static class NetworkUtilities
{
public static UnicastIPAddressInformation? GetUnicastIPAddressInformation(IPAddress address)
{
@@ -12,12 +12,9 @@ internal class NetworkUtilities
{
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;
}
}
}
+26 -20
View File
@@ -1,5 +1,4 @@
using HyperbolicDownloader.FileProcessing;
using HyperbolicDownloader.FileProcessing;
using HyperbolicDownloader.Networking;
using HyperbolicDownloader.UserInterface;
@@ -10,30 +9,23 @@ using Stone_Red_Utilities.ConsoleExtentions;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text.Json;
namespace HyperbolicDownloader;
internal class Program
internal static class Program
{
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 PrivatePort { get; } = 3055;
public static IPAddress? PublicIpAddress
{
get
{
if (device is not null)
{
return device.GetExternalIPAsync().GetAwaiter().GetResult();
}
return null;
}
}
public static IPAddress? PublicIpAddress => device?.GetExternalIPAsync().GetAwaiter().GetResult();
private static NatDevice? device;
private static Mapping? portMapping;
@@ -42,7 +34,7 @@ internal class Program
private static readonly NetworkClient networkClient = new(filesManager);
private static readonly Random random = new();
private static async Task Main()
private static async Task Main(string[] args)
{
Console.CancelKeyPress += Console_CancelKeyPress;
Console.CursorVisible = false;
@@ -56,7 +48,20 @@ internal class Program
if (File.Exists(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...");
@@ -77,7 +82,8 @@ internal class Program
}
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;
}
@@ -107,7 +113,7 @@ internal class Program
broadcastClient.OnBroadcastRecived += BroadcastClient_OnBroadcastRecived;
ConsoleExt.WriteLine("Done", ConsoleColor.Green);
new InputHandler(hostsManager, filesManager).ReadInput();
inputHandler.ReadInput();
ClosePorts();
}
@@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
namespace HyperbolicDownloader.UserInterface;
@@ -27,7 +28,12 @@ internal class InputHandler
commander.Register(Exit, "exit", "quit");
commander.Register(ShowInfo, "info", "inf");
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");
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);
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);
}
@@ -141,7 +149,7 @@ internal class InputHandler
private void ListFiles(string _)
{
int index = 0;
foreach (HyperFileInfo fileInfo in filesManager.ToList())
foreach (PrivateHyperFileInfo fileInfo in filesManager.ToList())
{
index++;
Console.WriteLine($"{index}) {fileInfo.FilePath}");
@@ -203,6 +211,151 @@ internal class InputHandler
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)
{
if (string.IsNullOrEmpty(hash))
@@ -211,7 +364,7 @@ internal class InputHandler
return;
}
hash = hash.Trim();
hash = hash.Trim().ToLower();
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);
Console.WriteLine("Requesting file...");
TcpClient tcpClient = new TcpClient();
using TcpClient tcpClient = new TcpClient();
tcpClient.Connect(ipAddress!, host.Port);
tcpClient.ReceiveBufferSize = 64000;
@@ -290,19 +443,20 @@ internal class InputHandler
}
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($"Starting download...");
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 unitsPerSecond = 0;
@@ -362,16 +516,16 @@ internal class InputHandler
Console.WriteLine();
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
{
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);
stopWatch.Stop();
hostsManager.SaveHosts();