initial commit

This commit is contained in:
i2p
2026-08-27 11:22:16 -06:00
commit 96afff7a83
600 changed files with 29291 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Quasar.Common.Cryptography
{
public class Aes256
{
private const int KeyLength = 32;
private const int AuthKeyLength = 64;
private const int IvLength = 16;
private const int HmacSha256Length = 32;
private readonly byte[] _key;
private readonly byte[] _authKey;
private static readonly byte[] Salt =
{
0xBF, 0xEB, 0x1E, 0x56, 0xFB, 0xCD, 0x97, 0x3B, 0xB2, 0x19, 0x2, 0x24, 0x30, 0xA5, 0x78, 0x43, 0x0, 0x3D, 0x56,
0x44, 0xD2, 0x1E, 0x62, 0xB9, 0xD4, 0xF1, 0x80, 0xE7, 0xE6, 0xC3, 0x39, 0x41
};
public Aes256(string masterKey)
{
if (string.IsNullOrEmpty(masterKey))
throw new ArgumentException($"{nameof(masterKey)} can not be null or empty.");
using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(masterKey, Salt, 50000))
{
_key = derive.GetBytes(KeyLength);
_authKey = derive.GetBytes(AuthKeyLength);
}
}
public string Encrypt(string input)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input)));
}
/* FORMAT
* ----------------------------------------
* | HMAC | IV | CIPHERTEXT |
* ----------------------------------------
* 32 bytes 16 bytes
*/
public byte[] Encrypt(byte[] input)
{
if (input == null)
throw new ArgumentNullException($"{nameof(input)} can not be null.");
using (var ms = new MemoryStream())
{
ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC
using (var aesProvider = new AesCryptoServiceProvider())
{
aesProvider.KeySize = 256;
aesProvider.BlockSize = 128;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Key = _key;
aesProvider.GenerateIV();
using (var cs = new CryptoStream(ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write))
{
ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write next 16 bytes the IV, followed by ciphertext
cs.Write(input, 0, input.Length);
cs.FlushFinalBlock();
using (var hmac = new HMACSHA256(_authKey))
{
byte[] hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); // compute the HMAC of IV and ciphertext
ms.Position = 0; // write hash at beginning
ms.Write(hash, 0, hash.Length);
}
}
}
return ms.ToArray();
}
}
public string Decrypt(string input)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(input)));
}
public byte[] Decrypt(byte[] input)
{
if (input == null)
throw new ArgumentNullException($"{nameof(input)} can not be null.");
using (var ms = new MemoryStream(input))
{
using (var aesProvider = new AesCryptoServiceProvider())
{
aesProvider.KeySize = 256;
aesProvider.BlockSize = 128;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Key = _key;
// read first 32 bytes for HMAC
using (var hmac = new HMACSHA256(_authKey))
{
var hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length);
byte[] receivedHash = new byte[HmacSha256Length];
ms.Read(receivedHash, 0, receivedHash.Length);
if (!SafeComparison.AreEqual(hash, receivedHash))
throw new CryptographicException("Invalid message authentication code (MAC).");
}
byte[] iv = new byte[IvLength];
ms.Read(iv, 0, IvLength); // read next 16 bytes for IV, followed by ciphertext
aesProvider.IV = iv;
using (var cs = new CryptoStream(ms, aesProvider.CreateDecryptor(), CryptoStreamMode.Read))
{
byte[] temp = new byte[ms.Length - IvLength + 1];
byte[] data = new byte[cs.Read(temp, 0, temp.Length)];
Buffer.BlockCopy(temp, 0, data, 0, data.Length);
return data;
}
}
}
}
}
}
@@ -0,0 +1,29 @@
using System.Runtime.CompilerServices;
namespace Quasar.Common.Cryptography
{
public class SafeComparison
{
/// <summary>
/// Compares two byte arrays for equality.
/// </summary>
/// <param name="a1">Byte array to compare</param>
/// <param name="a2">Byte array to compare</param>
/// <returns>True if equal, else false</returns>
/// <remarks>
/// Assumes that the byte arrays have the same length.
/// This method is safe against timing attacks.
/// </remarks>
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool AreEqual(byte[] a1, byte[] a2)
{
bool result = true;
for (int i = 0; i < a1.Length; ++i)
{
if (a1[i] != a2[i])
result = false;
}
return result;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using System.Security.Cryptography;
using System.Text;
namespace Quasar.Common.Cryptography
{
public static class Sha256
{
public static string ComputeHash(string input)
{
byte[] data = Encoding.UTF8.GetBytes(input);
using (SHA256Managed sha = new SHA256Managed())
{
data = sha.ComputeHash(data);
}
StringBuilder hash = new StringBuilder();
foreach (byte _byte in data)
hash.Append(_byte.ToString("X2"));
return hash.ToString().ToUpper();
}
public static byte[] ComputeHash(byte[] input)
{
using (SHA256Managed sha = new SHA256Managed())
{
return sha.ComputeHash(input);
}
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using System.Net;
namespace Quasar.Common.DNS
{
public class Host
{
/// <summary>
/// Stores the hostname of the Host.
/// </summary>
/// <remarks>
/// Can be an IPv4, IPv6 address or hostname.
/// </remarks>
public string Hostname { get; set; }
/// <summary>
/// Stores the IP address of host.
/// </summary>
/// <remarks>
/// Can be an IPv4 or IPv6 address.
/// </remarks>
public IPAddress IpAddress { get; set; }
/// <summary>
/// Stores the port of the Host.
/// </summary>
public ushort Port { get; set; }
public override string ToString()
{
return Hostname + ":" + Port;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Quasar.Common.DNS
{
public class HostsConverter
{
public List<Host> RawHostsToList(string rawHosts)
{
List<Host> hostsList = new List<Host>();
if (string.IsNullOrEmpty(rawHosts)) return hostsList;
var hosts = rawHosts.Split(';');
foreach (var host in hosts)
{
if ((string.IsNullOrEmpty(host) || !host.Contains(':'))) continue; // invalid host, ignore
ushort port;
if (!ushort.TryParse(host.Substring(host.LastIndexOf(':') + 1), out port)) continue; // invalid, ignore host
hostsList.Add(new Host { Hostname = host.Substring(0, host.LastIndexOf(':')), Port = port });
}
return hostsList;
}
public string ListToRawHosts(IList<Host> hosts)
{
StringBuilder rawHosts = new StringBuilder();
foreach (var host in hosts)
rawHosts.Append(host + ";");
return rawHosts.ToString();
}
}
}
+63
View File
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
namespace Quasar.Common.DNS
{
public class HostsManager
{
public bool IsEmpty => _hosts.Count == 0;
private readonly Queue<Host> _hosts = new Queue<Host>();
public HostsManager(List<Host> hosts)
{
foreach(var host in hosts)
_hosts.Enqueue(host);
}
public Host GetNextHost()
{
var temp = _hosts.Dequeue();
_hosts.Enqueue(temp); // add to the end of the queue
temp.IpAddress = ResolveHostname(temp);
return temp;
}
private static IPAddress ResolveHostname(Host host)
{
if (string.IsNullOrEmpty(host.Hostname)) return null;
IPAddress ip;
if (IPAddress.TryParse(host.Hostname, out ip))
{
if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
if (!Socket.OSSupportsIPv6) return null;
}
return ip;
}
var ipAddresses = Dns.GetHostEntry(host.Hostname).AddressList;
foreach (IPAddress ipAddress in ipAddresses)
{
switch (ipAddress.AddressFamily)
{
case AddressFamily.InterNetwork:
return ipAddress;
case AddressFamily.InterNetworkV6:
/* Only use resolved IPv6 if no IPv4 address available,
* otherwise it could be possible that the router the client
* is using to connect to the internet doesn't support IPv6.
*/
if (ipAddresses.Length == 1)
return ipAddress;
break;
}
}
return ip;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Quasar.Common.Enums
{
public enum AccountType
{
Admin,
User,
Guest,
Unknown
}
}
+18
View File
@@ -0,0 +1,18 @@
namespace Quasar.Common.Enums
{
public enum ConnectionState : byte
{
Closed = 1,
Listening = 2,
SYN_Sent = 3,
Syn_Recieved = 4,
Established = 5,
Finish_Wait_1 = 6,
Finish_Wait_2 = 7,
Closed_Wait = 8,
Closing = 9,
Last_ACK = 10,
Time_Wait = 11,
Delete_TCB = 12
}
}
+16
View File
@@ -0,0 +1,16 @@
namespace Quasar.Common.Enums
{
public enum ContentType
{
// these values must match the index of the images in file manager
Blob = 2,
Application = 3,
Text = 4,
Archive = 5,
Word = 6,
Pdf = 7,
Image = 8,
Video = 9,
Audio = 10
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Quasar.Common.Enums
{
public enum FileType
{
File,
Directory,
Back
}
}
+14
View File
@@ -0,0 +1,14 @@
namespace Quasar.Common.Enums
{
public enum MouseAction
{
LeftDown,
LeftUp,
RightDown,
RightUp,
MoveCursor,
ScrollUp,
ScrollDown,
None
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Quasar.Common.Enums
{
public enum ProcessAction
{
Start,
End
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Quasar.Common.Enums
{
public enum ShutdownAction
{
Shutdown,
Restart,
Standby
}
}
+13
View File
@@ -0,0 +1,13 @@
namespace Quasar.Common.Enums
{
public enum StartupType
{
LocalMachineRun,
LocalMachineRunOnce,
CurrentUserRun,
CurrentUserRunOnce,
StartMenu,
LocalMachineRunX86,
LocalMachineRunOnceX86
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Quasar.Common.Enums
{
public enum UserStatus
{
Active,
Idle
}
}
@@ -0,0 +1,27 @@
using System.IO;
namespace Quasar.Common.Extensions
{
public static class DriveTypeExtensions
{
/// <summary>
/// Converts the value of the <see cref="DriveType"/> instance to its friendly string representation.
/// </summary>
/// <param name="type">The <see cref="DriveType"/>.</param>
/// <returns>The friendly string representation of the value of this <see cref="DriveType"/> instance.</returns>
public static string ToFriendlyString(this DriveType type)
{
switch (type)
{
case DriveType.Fixed:
return "Local Disk";
case DriveType.Network:
return "Network Drive";
case DriveType.Removable:
return "Removable Drive";
default:
return type.ToString();
}
}
}
}
@@ -0,0 +1,48 @@
using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Quasar.Common.Extensions
{
/// <summary>
/// Socket Extension for KeepAlive
/// </summary>
/// <Author>Abdullah Saleem</Author>
/// <Email>[email protected]</Email>
public static class SocketExtensions
{
/// <summary>
/// A structure used by SetKeepAliveEx Method
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpKeepAlive
{
internal uint onoff;
internal uint keepalivetime;
internal uint keepaliveinterval;
};
/// <summary>
/// Sets the Keep-Alive values for the current tcp connection
/// </summary>
/// <param name="socket">Current socket instance</param>
/// <param name="keepAliveInterval">Specifies how often TCP repeats keep-alive transmissions when no response is received. TCP sends keep-alive transmissions to verify that idle connections are still active. This prevents TCP from inadvertently disconnecting active lines.</param>
/// <param name="keepAliveTime">Specifies how often TCP sends keep-alive transmissions. TCP sends keep-alive transmissions to verify that an idle connection is still active. This entry is used when the remote system is responding to TCP. Otherwise, the interval between transmissions is determined by the value of the keepAliveInterval entry.</param>
public static void SetKeepAliveEx(this Socket socket, uint keepAliveInterval, uint keepAliveTime)
{
var keepAlive = new TcpKeepAlive
{
onoff = 1,
keepaliveinterval = keepAliveInterval,
keepalivetime = keepAliveTime
};
int size = Marshal.SizeOf(keepAlive);
IntPtr keepAlivePtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(keepAlive, keepAlivePtr, true);
var buffer = new byte[size];
Marshal.Copy(keepAlivePtr, buffer, 0, size);
Marshal.FreeHGlobal(keepAlivePtr);
socket.IOControl(IOControlCode.KeepAliveValues, buffer, null);
}
}
}
@@ -0,0 +1,69 @@
using Quasar.Common.Enums;
namespace Quasar.Common.Extensions
{
public static class StringExtensions
{
/// <summary>
/// Converts the file extension string to its <see cref="ContentType"/> representation.
/// </summary>
/// <param name="fileExtension">The file extension string.</param>
/// <returns>The <see cref="ContentType"/> representation of the file extension string.</returns>
public static ContentType ToContentType(this string fileExtension)
{
switch (fileExtension.ToLower())
{
default:
return ContentType.Blob;
case ".exe":
return ContentType.Application;
case ".txt":
case ".log":
case ".conf":
case ".cfg":
case ".asc":
return ContentType.Text;
case ".rar":
case ".zip":
case ".zipx":
case ".tar":
case ".tgz":
case ".gz":
case ".s7z":
case ".7z":
case ".bz2":
case ".cab":
case ".zz":
case ".apk":
return ContentType.Archive;
case ".doc":
case ".docx":
case ".odt":
return ContentType.Word;
case ".pdf":
return ContentType.Pdf;
case ".jpg":
case ".jpeg":
case ".png":
case ".bmp":
case ".gif":
case ".ico":
return ContentType.Image;
case ".mp4":
case ".mov":
case ".avi":
case ".wmv":
case ".mkv":
case ".m4v":
case ".flv":
return ContentType.Video;
case ".mp3":
case ".wav":
case ".pls":
case ".m3u":
case ".m4a":
return ContentType.Audio;
}
}
}
}
+101
View File
@@ -0,0 +1,101 @@
using Quasar.Common.Cryptography;
using System.IO;
using System.Linq;
using System.Text;
namespace Quasar.Common.Helpers
{
public static class FileHelper
{
/// <summary>
/// List of illegal path characters.
/// </summary>
private static readonly char[] IllegalPathChars = Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).ToArray();
/// <summary>
/// Indicates if the given path contains illegal characters.
/// </summary>
/// <param name="path">The path to check.</param>
/// <returns>Returns <value>true</value> if the path contains illegal characters, otherwise <value>false</value>.</returns>
public static bool HasIllegalCharacters(string path)
{
return path.Any(c => IllegalPathChars.Contains(c));
}
/// <summary>
/// Gets a random file name.
/// </summary>
/// <param name="length">The length of the file name.</param>
/// <param name="extension">The file extension including the dot, e.g. <value>.exe</value>.</param>
/// <returns>The random file name.</returns>
public static string GetRandomFilename(int length, string extension = "")
{
return string.Concat(StringHelper.GetRandomString(length), extension);
}
/// <summary>
/// Gets a path to an unused temp file.
/// </summary>
/// <param name="extension">The file extension including the dot, e.g. <value>.exe</value>.</param>
/// <returns>The path to the temp file.</returns>
public static string GetTempFilePath(string extension)
{
string tempFilePath;
do
{
tempFilePath = Path.Combine(Path.GetTempPath(), GetRandomFilename(12, extension));
} while (File.Exists(tempFilePath));
return tempFilePath;
}
/// <summary>
/// Indicates if the given file header contains the executable identifier (magic number) 'MZ'.
/// </summary>
/// <param name="binary">The binary file to check.</param>
/// <returns>Returns <value>true</value> for valid executable identifiers, otherwise <value>false</value>.</returns>
public static bool HasExecutableIdentifier(byte[] binary)
{
if (binary.Length < 2) return false;
return (binary[0] == 'M' && binary[1] == 'Z') || (binary[0] == 'Z' && binary[1] == 'M');
}
/// <summary>
/// Deletes the zone identifier for the given file path.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns>Returns <value>true</value> if the deletion was successful, otherwise <value>false</value>.</returns>
public static bool DeleteZoneIdentifier(string filePath)
{
return NativeMethods.DeleteFile(filePath + ":Zone.Identifier");
}
/// <summary>
/// Appends text to a log file.
/// </summary>
/// <param name="filename">The filename of the log.</param>
/// <param name="appendText">The text to append.</param>
/// <param name="aes">The AES instance.</param>
public static void WriteLogFile(string filename, string appendText, Aes256 aes)
{
appendText = ReadLogFile(filename, aes) + appendText;
using (FileStream fStream = File.Open(filename, FileMode.Create, FileAccess.Write))
{
byte[] data = aes.Encrypt(Encoding.UTF8.GetBytes(appendText));
fStream.Seek(0, SeekOrigin.Begin);
fStream.Write(data, 0, data.Length);
}
}
/// <summary>
/// Reads a log file.
/// </summary>
/// <param name="filename">The filename of the log.</param>
/// <param name="aes">The AES instance.</param>
public static string ReadLogFile(string filename, Aes256 aes)
{
return File.Exists(filename) ? Encoding.UTF8.GetString(aes.Decrypt(File.ReadAllBytes(filename))) : string.Empty;
}
}
}
+120
View File
@@ -0,0 +1,120 @@
using System;
using System.Management;
using System.Text.RegularExpressions;
namespace Quasar.Common.Helpers
{
public static class PlatformHelper
{
/// <summary>
/// Initializes the <see cref="PlatformHelper"/> class.
/// </summary>
static PlatformHelper()
{
Win32NT = Environment.OSVersion.Platform == PlatformID.Win32NT;
XpOrHigher = Win32NT && Environment.OSVersion.Version.Major >= 5;
VistaOrHigher = Win32NT && Environment.OSVersion.Version.Major >= 6;
SevenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 1));
EightOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 2, 9200));
EightPointOneOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 3));
TenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(10, 0));
RunningOnMono = Type.GetType("Mono.Runtime") != null;
Name = "Unknown OS";
using (var searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem"))
{
foreach (ManagementObject os in searcher.Get())
{
Name = os["Caption"].ToString();
break;
}
}
Name = Regex.Replace(Name, "^.*(?=Windows)", "").TrimEnd().TrimStart(); // Remove everything before first match "Windows" and trim end & start
Is64Bit = Environment.Is64BitOperatingSystem;
FullName = $"{Name} {(Is64Bit ? 64 : 32)} Bit";
}
/// <summary>
/// Gets the full name of the operating system running on this computer (including the edition and architecture).
/// </summary>
public static string FullName { get; }
/// <summary>
/// Gets the name of the operating system running on this computer (including the edition).
/// </summary>
public static string Name { get; }
/// <summary>
/// Determines whether the Operating System is 32 or 64-bit.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is 64-bit, otherwise <c>false</c> for 32-bit.
/// </value>
public static bool Is64Bit { get; }
/// <summary>
/// Returns a indicating whether the application is running in Mono runtime.
/// </summary>
/// <value>
/// <c>true</c> if the application is running in Mono runtime; otherwise, <c>false</c>.
/// </value>
public static bool RunningOnMono { get; }
/// <summary>
/// Returns a indicating whether the Operating System is Windows 32 NT based.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 32 NT based; otherwise, <c>false</c>.
/// </value>
public static bool Win32NT { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows XP or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows XP or higher; otherwise, <c>false</c>.
/// </value>
public static bool XpOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows Vista or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows Vista or higher; otherwise, <c>false</c>.
/// </value>
public static bool VistaOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 7 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 7 or higher; otherwise, <c>false</c>.
/// </value>
public static bool SevenOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 8 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 8 or higher; otherwise, <c>false</c>.
/// </value>
public static bool EightOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 8.1 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 8.1 or higher; otherwise, <c>false</c>.
/// </value>
public static bool EightPointOneOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 10 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 10 or higher; otherwise, <c>false</c>.
/// </value>
public static bool TenOrHigher { get; }
}
}
+80
View File
@@ -0,0 +1,80 @@
using Quasar.Common.Utilities;
using System.Text;
using System.Text.RegularExpressions;
namespace Quasar.Common.Helpers
{
public static class StringHelper
{
/// <summary>
/// Available alphabet for generation of random strings.
/// </summary>
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/// <summary>
/// Abbreviations of file sizes.
/// </summary>
private static readonly string[] Sizes = { "B", "KB", "MB", "GB", "TB", "PB" };
/// <summary>
/// Random number generator.
/// </summary>
private static readonly SafeRandom Random = new SafeRandom();
/// <summary>
/// Gets a random string with given length.
/// </summary>
/// <param name="length">The length of the random string.</param>
/// <returns>A random string.</returns>
public static string GetRandomString(int length)
{
StringBuilder randomName = new StringBuilder(length);
for (int i = 0; i < length; i++)
randomName.Append(Alphabet[Random.Next(Alphabet.Length)]);
return randomName.ToString();
}
/// <summary>
/// Gets the human readable file size for a given size.
/// </summary>
/// <param name="size">The file size in bytes.</param>
/// <returns>The human readable file size.</returns>
public static string GetHumanReadableFileSize(long size)
{
double len = size;
int order = 0;
while (len >= 1024 && order + 1 < Sizes.Length)
{
order++;
len = len / 1024;
}
return $"{len:0.##} {Sizes[order]}";
}
/// <summary>
/// Gets the formatted MAC address.
/// </summary>
/// <param name="macAddress">The unformatted MAC address.</param>
/// <returns>The formatted MAC address.</returns>
public static string GetFormattedMacAddress(string macAddress)
{
return (macAddress.Length != 12)
? "00:00:00:00:00:00"
: Regex.Replace(macAddress, "(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})", "$1:$2:$3:$4:$5:$6");
}
/// <summary>
/// Safely removes the last N chars from a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="amount">The amount of last chars to remove (=N).</param>
/// <returns>The input string with N removed chars.</returns>
public static string RemoveLastChars(string input, int amount = 2)
{
if (input.Length > amount)
input = input.Remove(input.Length - amount);
return input;
}
}
}
+124
View File
@@ -0,0 +1,124 @@
using Quasar.Common.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace Quasar.Common.IO
{
public class FileSplit : IEnumerable<FileChunk>, IDisposable
{
/// <summary>
/// The maximum size per file chunk.
/// </summary>
public readonly int MaxChunkSize = 65535;
/// <summary>
/// The file path of the opened file.
/// </summary>
public string FilePath => _fileStream.Name;
/// <summary>
/// The file size of the opened file.
/// </summary>
public long FileSize => _fileStream.Length;
/// <summary>
/// The file stream of the opened file.
/// </summary>
private readonly FileStream _fileStream;
/// <summary>
/// Initializes a new instance of the <see cref="FileSplit"/> class using the given file path and access mode.
/// </summary>
/// <param name="filePath">The path to the file to open.</param>
/// <param name="fileAccess">The file access mode for opening the file. Allowed are <see cref="FileAccess.Read"/> and <see cref="FileAccess.Write"/>.</param>
public FileSplit(string filePath, FileAccess fileAccess)
{
switch (fileAccess)
{
case FileAccess.Read:
_fileStream = File.OpenRead(filePath);
break;
case FileAccess.Write:
_fileStream = File.OpenWrite(filePath);
break;
default:
throw new ArgumentException($"{nameof(fileAccess)} must be either Read or Write.");
}
}
/// <summary>
/// Writes a chunk to the file. In other words.
/// </summary>
/// <param name="chunk"></param>
public void WriteChunk(FileChunk chunk)
{
_fileStream.Seek(chunk.Offset, SeekOrigin.Begin);
_fileStream.Write(chunk.Data, 0, chunk.Data.Length);
}
/// <summary>
/// Reads a chunk of the file.
/// </summary>
/// <param name="offset">Offset of the file, must be a multiple of <see cref="MaxChunkSize"/> for proper reconstruction.</param>
/// <returns>The read file chunk at the given offset.</returns>
/// <remarks>
/// The returned file chunk can be smaller than <see cref="MaxChunkSize"/> iff the
/// remaining file size from the offset is smaller than <see cref="MaxChunkSize"/>,
/// then the remaining file size is used.
/// </remarks>
public FileChunk ReadChunk(long offset)
{
_fileStream.Seek(offset, SeekOrigin.Begin);
long chunkSize = _fileStream.Length - _fileStream.Position < MaxChunkSize
? _fileStream.Length - _fileStream.Position
: MaxChunkSize;
var chunkData = new byte[chunkSize];
_fileStream.Read(chunkData, 0, chunkData.Length);
return new FileChunk
{
Data = chunkData,
Offset = _fileStream.Position - chunkData.Length
};
}
/// <summary>
/// Returns an enumerator that iterates through the file chunks.
/// </summary>
/// <returns>An <see cref="IEnumerator"/> object that can be used to iterate through the file chunks.</returns>
public IEnumerator<FileChunk> GetEnumerator()
{
for (long currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++)
{
yield return ReadChunk(currentChunk * MaxChunkSize);
}
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_fileStream.Dispose();
}
}
/// <summary>
/// Disposes all managed and unmanaged resources associated with this class.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
@@ -0,0 +1,44 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class ClientIdentification : IMessage
{
[ProtoMember(1)]
public string Version { get; set; }
[ProtoMember(2)]
public string OperatingSystem { get; set; }
[ProtoMember(3)]
public string AccountType { get; set; }
[ProtoMember(4)]
public string Country { get; set; }
[ProtoMember(5)]
public string CountryCode { get; set; }
[ProtoMember(6)]
public int ImageIndex { get; set; }
[ProtoMember(7)]
public string Id { get; set; }
[ProtoMember(8)]
public string Username { get; set; }
[ProtoMember(9)]
public string PcName { get; set; }
[ProtoMember(10)]
public string Tag { get; set; }
[ProtoMember(11)]
public string EncryptionKey { get; set; }
[ProtoMember(12)]
public byte[] Signature { get; set; }
}
}
@@ -0,0 +1,11 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class ClientIdentificationResult : IMessage
{
[ProtoMember(1)]
public bool Result { get; set; }
}
}
+9
View File
@@ -0,0 +1,9 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoAskElevate : IMessage
{
}
}
+10
View File
@@ -0,0 +1,10 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoCdTray : IMessage
{
[ProtoMember(1)] public int Count { get; set; }
}
}
@@ -0,0 +1,9 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoClientDisconnect : IMessage
{
}
}
@@ -0,0 +1,9 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoClientReconnect : IMessage
{
}
}
@@ -0,0 +1,9 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoClientUninstall : IMessage
{
}
}
@@ -0,0 +1,20 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoCloseConnection : IMessage
{
[ProtoMember(1)]
public string LocalAddress { get; set; }
[ProtoMember(2)]
public ushort LocalPort { get; set; }
[ProtoMember(3)]
public string RemoteAddress { get; set; }
[ProtoMember(4)]
public ushort RemotePort { get; set; }
}
}
+14
View File
@@ -0,0 +1,14 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoExecute : IMessage
{
[ProtoMember(1)]
public string FilePath { get; set; }
[ProtoMember(2)]
public bool IsUrl { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoGhostTyping : IMessage { }
}
+15
View File
@@ -0,0 +1,15 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoJumpscare : IMessage
{
[ProtoMember(1)]
public byte[] WavData { get; set; }
[ProtoMember(2)]
public byte[] GifData { get; set; }
}
}
+14
View File
@@ -0,0 +1,14 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoKeyboardEvent : IMessage
{
[ProtoMember(1)]
public byte Key { get; set; }
[ProtoMember(2)]
public bool KeyDown { get; set; }
}
}
+7
View File
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoLockScreen : IMessage { }
}
+24
View File
@@ -0,0 +1,24 @@
using ProtoBuf;
using Quasar.Common.Enums;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoMouseEvent : IMessage
{
[ProtoMember(1)]
public MouseAction Action { get; set; }
[ProtoMember(2)]
public bool IsMouseDown { get; set; }
[ProtoMember(3)]
public int X { get; set; }
[ProtoMember(4)]
public int Y { get; set; }
[ProtoMember(5)]
public int MonitorIndex { get; set; }
}
}
+10
View File
@@ -0,0 +1,10 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoMouseSwap : IMessage
{
[ProtoMember(1)] public bool Swap { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoNuke : IMessage { }
}
+11
View File
@@ -0,0 +1,11 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoPlayNote : IMessage
{
[ProtoMember(1)] public int Frequency { get; set; }
[ProtoMember(2)] public int DurationMs { get; set; }
}
}
+10
View File
@@ -0,0 +1,10 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoSetWallpaper : IMessage
{
[ProtoMember(1)] public byte[] ImageData { get; set; }
}
}
@@ -0,0 +1,23 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoShowMessageBox : IMessage
{
[ProtoMember(1)]
public string Caption { get; set; }
[ProtoMember(2)]
public string Text { get; set; }
[ProtoMember(3)]
public string Button { get; set; }
[ProtoMember(4)]
public string Icon { get; set; }
[ProtoMember(5)]
public int Count { get; set; }
}
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartBeepSpam : IMessage { }
}
@@ -0,0 +1,12 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartClipboardHijack : IMessage
{
[ProtoMember(1)]
public string ReplacementText { get; set; }
}
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartColorInvert : IMessage { }
}
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartCursorChaos : IMessage { }
}
@@ -0,0 +1,15 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartDesktopStream : IMessage
{
[ProtoMember(1)] public int Quality { get; set; }
[ProtoMember(2)] public int DisplayIndex { get; set; }
[ProtoMember(3)] public int IntervalMs { get; set; }
[ProtoMember(4)] public bool UseH264 { get; set; }
[ProtoMember(5)] public int H264BitrateKbps { get; set; }
[ProtoMember(6)] public int H264Fps { get; set; }
}
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartDrunkMode : IMessage { }
}
+7
View File
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartEyes : IMessage { }
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartFartScroll : IMessage { }
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartPornSpam : IMessage { }
}
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStartSchizophrenia : IMessage { }
}
+7
View File
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopBeepSpam : IMessage { }
}
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopClipboardHijack : IMessage { }
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopColorInvert : IMessage { }
}
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopCursorChaos : IMessage { }
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopDesktopStream : IMessage { }
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopDrunkMode : IMessage { }
}
+7
View File
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopEyes : IMessage { }
}
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopFartScroll : IMessage { }
}
+8
View File
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopNuke : IMessage { }
}
+7
View File
@@ -0,0 +1,7 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopPornSpam : IMessage { }
}
@@ -0,0 +1,8 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoStopSchizophrenia : IMessage { }
}
+12
View File
@@ -0,0 +1,12 @@
using ProtoBuf;
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoTextToSpeech : IMessage
{
[ProtoMember(1)]
public string Text { get; set; }
}
}
+14
View File
@@ -0,0 +1,14 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class DoVisitWebsite : IMessage
{
[ProtoMember(1)]
public string Url { get; set; }
[ProtoMember(2)]
public bool Hidden { get; set; }
}
}
+17
View File
@@ -0,0 +1,17 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class GetDesktop : IMessage
{
[ProtoMember(1)]
public bool CreateNew { get; set; }
[ProtoMember(2)]
public int Quality { get; set; }
[ProtoMember(3)]
public int DisplayIndex { get; set; }
}
}
@@ -0,0 +1,21 @@
using ProtoBuf;
using Quasar.Common.Video;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class GetDesktopResponse : IMessage
{
[ProtoMember(1)]
public byte[] Image { get; set; }
[ProtoMember(2)]
public int Quality { get; set; }
[ProtoMember(3)]
public int Monitor { get; set; }
[ProtoMember(4)]
public Resolution Resolution { get; set; }
}
}
+9
View File
@@ -0,0 +1,9 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class GetMonitors : IMessage
{
}
}
@@ -0,0 +1,11 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class GetMonitorsResponse : IMessage
{
[ProtoMember(1)]
public int Number { get; set; }
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Quasar.Common.Messages
{
public interface IMessage
{
}
}
@@ -0,0 +1,31 @@
using Quasar.Common.Networking;
namespace Quasar.Common.Messages
{
/// <summary>
/// Provides basic functionality to process messages.
/// </summary>
public interface IMessageProcessor
{
/// <summary>
/// Decides whether this message processor can execute the specified message.
/// </summary>
/// <param name="message">The message to execute.</param>
/// <returns><c>True</c> if the message can be executed by this message processor, otherwise <c>false</c>.</returns>
bool CanExecute(IMessage message);
/// <summary>
/// Decides whether this message processor can execute messages received from the sender.
/// </summary>
/// <param name="sender">The sender of a message.</param>
/// <returns><c>True</c> if this message processor can execute messages from the sender, otherwise <c>false</c>.</returns>
bool CanExecuteFrom(ISender sender);
/// <summary>
/// Executes the received message.
/// </summary>
/// <param name="sender">The sender of this message.</param>
/// <param name="message">The received message.</param>
void Execute(ISender sender, IMessage message);
}
}
+66
View File
@@ -0,0 +1,66 @@
using Quasar.Common.Networking;
using System.Collections.Generic;
using System.Linq;
namespace Quasar.Common.Messages
{
/// <summary>
/// Handles registrations of <see cref="IMessageProcessor"/>s and processing of <see cref="IMessage"/>s.
/// </summary>
public static class MessageHandler
{
/// <summary>
/// List of registered <see cref="IMessageProcessor"/>s.
/// </summary>
private static readonly List<IMessageProcessor> Processors = new List<IMessageProcessor>();
/// <summary>
/// Used in lock statements to synchronize access to <see cref="Processors"/> between threads.
/// </summary>
private static readonly object SyncLock = new object();
/// <summary>
/// Registers a <see cref="IMessageProcessor"/> to the available <see cref="Processors"/>.
/// </summary>
/// <param name="proc">The <see cref="IMessageProcessor"/> to register.</param>
public static void Register(IMessageProcessor proc)
{
lock (SyncLock)
{
if (Processors.Contains(proc)) return;
Processors.Add(proc);
}
}
/// <summary>
/// Unregisters a <see cref="IMessageProcessor"/> from the available <see cref="Processors"/>.
/// </summary>
/// <param name="proc"></param>
public static void Unregister(IMessageProcessor proc)
{
lock (SyncLock)
{
Processors.Remove(proc);
}
}
/// <summary>
/// Forwards the received <see cref="IMessage"/> to the appropriate <see cref="IMessageProcessor"/>s to execute it.
/// </summary>
/// <param name="sender">The sender of the message.</param>
/// <param name="msg">The received message.</param>
public static void Process(ISender sender, IMessage msg)
{
IEnumerable<IMessageProcessor> availableProcessors;
lock (SyncLock)
{
// select appropriate message processors
availableProcessors = Processors.Where(x => x.CanExecute(msg) && x.CanExecuteFrom(sender)).ToList();
// ToList() is required to retrieve a thread-safe enumerator representing a moment-in-time snapshot of the message processors
}
foreach (var executor in availableProcessors)
executor.Execute(sender, msg);
}
}
}
@@ -0,0 +1,107 @@
using Quasar.Common.Networking;
using System;
using System.Threading;
namespace Quasar.Common.Messages
{
/// <summary>
/// Provides a MessageProcessor implementation that provides progress report callbacks.
/// </summary>
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
/// <remarks>
/// Any event handlers registered with the <see cref="ProgressChanged"/> event are invoked through a
/// <see cref="System.Threading.SynchronizationContext"/> instance chosen when the instance is constructed.
/// </remarks>
public abstract class MessageProcessorBase<T> : IMessageProcessor, IProgress<T>
{
/// <summary>
/// The synchronization context chosen upon construction.
/// </summary>
protected readonly SynchronizationContext SynchronizationContext;
/// <summary>
/// A cached delegate used to post invocation to the synchronization context.
/// </summary>
private readonly SendOrPostCallback _invokeReportProgressHandlers;
/// <summary>
/// Represents the method that will handle progress updates.
/// </summary>
/// <param name="sender">The message processor which updated the progress.</param>
/// <param name="value">The new progress.</param>
public delegate void ReportProgressEventHandler(object sender, T value);
/// <summary>
/// Raised for each reported progress value.
/// </summary>
/// <remarks>
/// Handlers registered with this event will be invoked on the
/// <see cref="System.Threading.SynchronizationContext"/> chosen when the instance was constructed.
/// </remarks>
public event ReportProgressEventHandler ProgressChanged;
/// <summary>
/// Reports a progress change.
/// </summary>
/// <param name="value">The value of the updated progress.</param>
protected virtual void OnReport(T value)
{
// If there's no handler, don't bother going through the sync context.
// Inside the callback, we'll need to check again, in case
// an event handler is removed between now and then.
var handler = ProgressChanged;
if (handler != null)
{
SynchronizationContext.Post(_invokeReportProgressHandlers, value);
}
}
/// <summary>
/// Initializes the <see cref="MessageProcessorBase{T}"/>
/// </summary>
/// <param name="useCurrentContext">
/// If this value is <c>false</c>, the progress callbacks will be invoked on the ThreadPool.
/// Otherwise the current SynchronizationContext will be used.
/// </param>
protected MessageProcessorBase(bool useCurrentContext)
{
_invokeReportProgressHandlers = InvokeReportProgressHandlers;
SynchronizationContext = useCurrentContext ? SynchronizationContext.Current : ProgressStatics.DefaultContext;
}
/// <summary>
/// Invokes the progress event callbacks.
/// </summary>
/// <param name="state">The progress value.</param>
private void InvokeReportProgressHandlers(object state)
{
var handler = ProgressChanged;
handler?.Invoke(this, (T)state);
}
/// <inheritdoc />
public abstract bool CanExecute(IMessage message);
/// <inheritdoc />
public abstract bool CanExecuteFrom(ISender sender);
/// <inheritdoc />
public abstract void Execute(ISender sender, IMessage message);
void IProgress<T>.Report(T value) => OnReport(value);
}
/// <summary>
/// Holds static values for <see cref="MessageProcessorBase{T}"/>.
/// </summary>
/// <remarks>
/// This avoids one static instance per type T.
/// </remarks>
internal static class ProgressStatics
{
/// <summary>
/// A default synchronization context that targets the <see cref="ThreadPool"/>.
/// </summary>
internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext();
}
}
+11
View File
@@ -0,0 +1,11 @@
using ProtoBuf;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class SetStatus : IMessage
{
[ProtoMember(1)]
public string Message { get; set; }
}
}
+12
View File
@@ -0,0 +1,12 @@
using ProtoBuf;
using Quasar.Common.Enums;
namespace Quasar.Common.Messages
{
[ProtoContract]
public class SetUserStatus : IMessage
{
[ProtoMember(1)]
public UserStatus Message { get; set; }
}
}
+49
View File
@@ -0,0 +1,49 @@
using ProtoBuf.Meta;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Quasar.Common.Messages
{
public static class TypeRegistry
{
/// <summary>
/// The internal index of the message type.
/// </summary>
private static int _typeIndex;
/// <summary>
/// Adds a Type to the serializer so a message can be properly serialized.
/// </summary>
/// <param name="parent">The parent type, i.e.: IMessage</param>
/// <param name="type">Type to be added</param>
public static void AddTypeToSerializer(Type parent, Type type)
{
if (type == null || parent == null)
throw new ArgumentNullException();
bool isAlreadyAdded = RuntimeTypeModel.Default[parent].GetSubtypes().Any(subType => subType.DerivedType.Type == type);
if (!isAlreadyAdded)
RuntimeTypeModel.Default[parent].AddSubType(++_typeIndex, type);
}
/// <summary>
/// Adds Types to the serializer.
/// </summary>
/// <param name="parent">The parent type, i.e.: IMessage</param>
/// <param name="types">Types to add.</param>
public static void AddTypesToSerializer(Type parent, params Type[] types)
{
foreach (Type type in types)
AddTypeToSerializer(parent, type);
}
public static IEnumerable<Type> GetPacketTypes(Type type)
{
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
}
}
}
+14
View File
@@ -0,0 +1,14 @@
using ProtoBuf;
namespace Quasar.Common.Models
{
[ProtoContract]
public class Drive
{
[ProtoMember(1)]
public string DisplayName { get; set; }
[ProtoMember(2)]
public string RootDirectory { get; set; }
}
}
+14
View File
@@ -0,0 +1,14 @@
using ProtoBuf;
namespace Quasar.Common.Models
{
[ProtoContract]
public class FileChunk
{
[ProtoMember(1)]
public long Offset { get; set; }
[ProtoMember(2)]
public byte[] Data { get; set; }
}
}
+25
View File
@@ -0,0 +1,25 @@
using System;
using ProtoBuf;
using Quasar.Common.Enums;
namespace Quasar.Common.Models
{
[ProtoContract]
public class FileSystemEntry
{
[ProtoMember(1)]
public FileType EntryType { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public long Size { get; set; }
[ProtoMember(4)]
public DateTime LastAccessTimeUtc { get; set; }
[ProtoMember(5)]
public ContentType? ContentType { get; set; }
}
}
+17
View File
@@ -0,0 +1,17 @@
using ProtoBuf;
namespace Quasar.Common.Models
{
[ProtoContract]
public class Process
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Id { get; set; }
[ProtoMember(3)]
public string MainWindowTitle { get; set; }
}
}
+20
View File
@@ -0,0 +1,20 @@
using ProtoBuf;
namespace Quasar.Common.Models
{
[ProtoContract]
public class RecoveredAccount
{
[ProtoMember(1)]
public string Username { get; set; }
[ProtoMember(2)]
public string Password { get; set; }
[ProtoMember(3)]
public string Url { get; set; }
[ProtoMember(4)]
public string Application { get; set; }
}
}
+22
View File
@@ -0,0 +1,22 @@
using ProtoBuf;
namespace Quasar.Common.Models
{
[ProtoContract]
public class RegSeekerMatch
{
[ProtoMember(1)]
public string Key { get; set; }
[ProtoMember(2)]
public RegValueData[] Data { get; set; }
[ProtoMember(3)]
public bool HasSubKeys { get; set; }
public override string ToString()
{
return $"({Key}:{Data})";
}
}
}
+18
View File
@@ -0,0 +1,18 @@
using Microsoft.Win32;
using ProtoBuf;
namespace Quasar.Common.Models
{
[ProtoContract]
public class RegValueData
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public RegistryValueKind Kind { get; set; }
[ProtoMember(3)]
public byte[] Data { get; set; }
}
}
+18
View File
@@ -0,0 +1,18 @@
using ProtoBuf;
using Quasar.Common.Enums;
namespace Quasar.Common.Models
{
[ProtoContract]
public class StartupItem
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Path { get; set; }
[ProtoMember(3)]
public StartupType Type { get; set; }
}
}
+27
View File
@@ -0,0 +1,27 @@
using ProtoBuf;
using Quasar.Common.Enums;
namespace Quasar.Common.Models
{
[ProtoContract]
public class TcpConnection
{
[ProtoMember(1)]
public string ProcessName { get; set; }
[ProtoMember(2)]
public string LocalAddress { get; set; }
[ProtoMember(3)]
public ushort LocalPort { get; set; }
[ProtoMember(4)]
public string RemoteAddress { get; set; }
[ProtoMember(5)]
public ushort RemotePort { get; set; }
[ProtoMember(6)]
public ConnectionState State { get; set; }
}
}
+27
View File
@@ -0,0 +1,27 @@
using System;
using System.Runtime.InteropServices;
namespace Quasar.Common
{
/// <summary>
/// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll).
/// </summary>
public class NativeMethods
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int memcpy(IntPtr dst, IntPtr src, uint count);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe int memcpy(void* dst, void* src, uint count);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteFile(string name);
}
}
+10
View File
@@ -0,0 +1,10 @@
using Quasar.Common.Messages;
namespace Quasar.Common.Networking
{
public interface ISender
{
void Send<T>(T message) where T : IMessage;
void Disconnect();
}
}
+75
View File
@@ -0,0 +1,75 @@
using ProtoBuf;
using Quasar.Common.Messages;
using System;
using System.IO;
namespace Quasar.Common.Networking
{
public class PayloadReader : MemoryStream
{
private readonly Stream _innerStream;
public bool LeaveInnerStreamOpen { get; }
public PayloadReader(byte[] payload, int length, bool leaveInnerStreamOpen)
{
_innerStream = new MemoryStream(payload, 0, length, false, true);
LeaveInnerStreamOpen = leaveInnerStreamOpen;
}
public PayloadReader(Stream stream, bool leaveInnerStreamOpen)
{
_innerStream = stream;
LeaveInnerStreamOpen = leaveInnerStreamOpen;
}
public int ReadInteger()
{
return BitConverter.ToInt32(ReadBytes(4), 0);
}
public byte[] ReadBytes(int length)
{
if (_innerStream.Position + length <= _innerStream.Length)
{
byte[] result = new byte[length];
_innerStream.Read(result, 0, result.Length);
return result;
}
throw new OverflowException($"Unable to read {length} bytes from stream");
}
/// <summary>
/// Reads the serialized message of the payload and deserializes it.
/// </summary>
/// <returns>The deserialized message of the payload.</returns>
public IMessage ReadMessage()
{
ReadInteger();
/* Length prefix is ignored here and already handled in Client class,
* it would cause to much trouble to check here for split or not fully
* received packets.
*/
IMessage message = Serializer.Deserialize<IMessage>(_innerStream);
return message;
}
protected override void Dispose(bool disposing)
{
try
{
if (LeaveInnerStreamOpen)
{
_innerStream.Flush();
}
else
{
_innerStream.Close();
}
}
finally
{
base.Dispose(disposing);
}
}
}
}
+65
View File
@@ -0,0 +1,65 @@
using ProtoBuf;
using Quasar.Common.Messages;
using System;
using System.IO;
namespace Quasar.Common.Networking
{
public class PayloadWriter : MemoryStream
{
private readonly Stream _innerStream;
public bool LeaveInnerStreamOpen { get; }
public PayloadWriter(Stream stream, bool leaveInnerStreamOpen)
{
_innerStream = stream;
LeaveInnerStreamOpen = leaveInnerStreamOpen;
}
public void WriteBytes(byte[] value)
{
_innerStream.Write(value, 0, value.Length);
}
public void WriteInteger(int value)
{
WriteBytes(BitConverter.GetBytes(value));
}
/// <summary>
/// Writes a serialized message as payload to the stream.
/// </summary>
/// <param name="message">The message to write.</param>
/// <returns>The amount of written bytes to the stream.</returns>
public int WriteMessage(IMessage message)
{
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize(ms, message);
byte[] payload = ms.ToArray();
WriteInteger(payload.Length);
WriteBytes(payload);
return sizeof(int) + payload.Length;
}
}
protected override void Dispose(bool disposing)
{
try
{
if (LeaveInnerStreamOpen)
{
_innerStream.Flush();
}
else
{
_innerStream.Close();
}
}
finally
{
base.Dispose(disposing);
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Trollware Common")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Trollware")]
[assembly: AssemblyCopyright("Copyright © MaxXor 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Quasar.Common.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c7c363ba-e5b6-4e18-9224-39bc8da73172")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.4.1")]
[assembly: AssemblyFileVersion("1.4.1")]
+31
View File
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net452</TargetFrameworks>
<OutputType>Library</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\bin\Debug\</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile></DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<OutputPath>..\bin\Release\</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Management" />
<Reference Include="System.ServiceModel" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="protobuf-net">
<Version>2.4.8</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Win32.Registry" />
<PackageReference Include="System.Drawing.Common" />
<PackageReference Include="System.Management" />
</ItemGroup>
</Project>
+135
View File
@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Quasar.Common.Utilities
{
public class ByteConverter
{
private static byte NULL_BYTE = byte.MinValue;
public static byte[] GetBytes(int value)
{
return BitConverter.GetBytes(value);
}
public static byte[] GetBytes(long value)
{
return BitConverter.GetBytes(value);
}
public static byte[] GetBytes(uint value)
{
return BitConverter.GetBytes(value);
}
public static byte[] GetBytes(ulong value)
{
return BitConverter.GetBytes(value);
}
public static byte[] GetBytes(string value)
{
return StringToBytes(value);
}
public static byte[] GetBytes(string[] value)
{
return StringArrayToBytes(value);
}
public static int ToInt32(byte[] bytes)
{
return BitConverter.ToInt32(bytes, 0);
}
public static long ToInt64(byte[] bytes)
{
return BitConverter.ToInt64(bytes, 0);
}
public static uint ToUInt32(byte[] bytes)
{
return BitConverter.ToUInt32(bytes, 0);
}
public static ulong ToUInt64(byte[] bytes)
{
return BitConverter.ToUInt64(bytes, 0);
}
public static string ToString(byte[] bytes)
{
return BytesToString(bytes);
}
public static string[] ToStringArray(byte[] bytes)
{
return BytesToStringArray(bytes);
}
private static byte[] GetNullBytes()
{
//Null bytes: 00 00
return new byte[] { NULL_BYTE, NULL_BYTE };
}
private static byte[] StringToBytes(string value)
{
byte[] bytes = new byte[value.Length * sizeof(char)];
Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
private static byte[] StringArrayToBytes(string[] strings)
{
List<byte> bytes = new List<byte>();
foreach(string str in strings)
{
bytes.AddRange(StringToBytes(str));
bytes.AddRange(GetNullBytes());
}
return bytes.ToArray();
}
private static string BytesToString(byte[] bytes)
{
int nrChars = (int)Math.Ceiling((float)bytes.Length / (float)sizeof(char));
char[] chars = new char[nrChars];
Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
private static string[] BytesToStringArray(byte[] bytes)
{
List<string> strings = new List<string>();
int i = 0;
StringBuilder strBuilder = new StringBuilder(bytes.Length);
while (i < bytes.Length)
{
//Holds the number of nulls (3 nulls indicated end of a string)
int nullcount = 0;
while (i < bytes.Length && nullcount < 3)
{
if (bytes[i] == NULL_BYTE)
{
nullcount++;
}
else
{
strBuilder.Append(Convert.ToChar(bytes[i]));
nullcount = 0;
}
i++;
}
strings.Add(strBuilder.ToString());
strBuilder.Clear();
}
return strings.ToArray();
}
}
}
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Security.Cryptography;
namespace Quasar.Common.Utilities
{
/// <summary>
/// Thread-safe random number generator.
/// Has same API as System.Random but is thread safe, similar to the implementation by Steven Toub: http://blogs.msdn.com/b/pfxteam/archive/2014/10/20/9434171.aspx
/// </summary>
public class SafeRandom
{
private static readonly RandomNumberGenerator GlobalCryptoProvider = RandomNumberGenerator.Create();
[ThreadStatic]
private static Random _random;
private static Random GetRandom()
{
if (_random == null)
{
byte[] buffer = new byte[4];
GlobalCryptoProvider.GetBytes(buffer);
_random = new Random(BitConverter.ToInt32(buffer, 0));
}
return _random;
}
public int Next()
{
return GetRandom().Next();
}
public int Next(int maxValue)
{
return GetRandom().Next(maxValue);
}
public int Next(int minValue, int maxValue)
{
return GetRandom().Next(minValue, maxValue);
}
public void NextBytes(byte[] buffer)
{
GetRandom().NextBytes(buffer);
}
public double NextDouble()
{
return GetRandom().NextDouble();
}
}
}
@@ -0,0 +1,380 @@
using Quasar.Common.Video.Compression;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace Quasar.Common.Video.Codecs
{
public class UnsafeStreamCodec : IDisposable
{
public int Monitor { get; private set; }
public Resolution Resolution { get; private set; }
public Size CheckBlock { get; private set; }
public int ImageQuality
{
get { return _imageQuality; }
private set
{
lock (_imageProcessLock)
{
_imageQuality = value;
if (_jpgCompression != null)
{
_jpgCompression.Dispose();
}
_jpgCompression = new JpgCompression(_imageQuality);
}
}
}
private int _imageQuality;
private byte[] _encodeBuffer;
private Bitmap _decodedBitmap;
private PixelFormat _encodedFormat;
private int _encodedWidth;
private int _encodedHeight;
private readonly object _imageProcessLock = new object();
private JpgCompression _jpgCompression;
/// <summary>
/// Initialize a new instance of UnsafeStreamCodec class.
/// </summary>
/// <param name="imageQuality">The quality to use between 0-100.</param>
/// <param name="monitor">The monitor used for the images.</param>
/// <param name="resolution">The resolution of the monitor.</param>
public UnsafeStreamCodec(int imageQuality, int monitor, Resolution resolution)
{
this.ImageQuality = imageQuality;
this.Monitor = monitor;
this.Resolution = resolution;
this.CheckBlock = new Size(50, 1);
}
public void Dispose()
{
Dispose(true);
// Tell the Garbage Collector to not waste time finalizing this object
// since we took care of it.
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_decodedBitmap != null)
{
_decodedBitmap.Dispose();
}
if (_jpgCompression != null)
{
_jpgCompression.Dispose();
}
}
}
public unsafe void CodeImage(IntPtr scan0, Rectangle scanArea, Size imageSize, PixelFormat format,
Stream outStream)
{
lock (_imageProcessLock)
{
byte* pScan0;
if (IntPtr.Size == 8)
{
// 64 bit process
pScan0 = (byte*) scan0.ToInt64();
}
else
{
// 32 bit process
pScan0 = (byte*)scan0.ToInt32();
}
if (!outStream.CanWrite)
{
throw new Exception("Must have access to Write in the Stream");
}
int stride = 0;
int rawLength = 0;
int pixelSize = 0;
switch (format)
{
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppRgb:
pixelSize = 3;
break;
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
pixelSize = 4;
break;
default:
throw new NotSupportedException(format.ToString());
}
stride = imageSize.Width * pixelSize;
rawLength = stride * imageSize.Height;
if (_encodeBuffer == null)
{
this._encodedFormat = format;
this._encodedWidth = imageSize.Width;
this._encodedHeight = imageSize.Height;
this._encodeBuffer = new byte[rawLength];
fixed (byte* ptr = _encodeBuffer)
{
byte[] temp = null;
using (Bitmap tmpBmp = new Bitmap(imageSize.Width, imageSize.Height, stride, format, scan0))
{
temp = _jpgCompression.Compress(tmpBmp);
}
outStream.Write(BitConverter.GetBytes(temp.Length), 0, 4);
outStream.Write(temp, 0, temp.Length);
NativeMethods.memcpy(new IntPtr(ptr), scan0, (uint)rawLength);
}
return;
}
if (this._encodedFormat != format)
{
throw new Exception("PixelFormat is not equal to previous Bitmap");
}
else if (this._encodedWidth != imageSize.Width || this._encodedHeight != imageSize.Height)
{
throw new Exception("Bitmap width/height are not equal to previous bitmap");
}
long oldPos = outStream.Position;
outStream.Write(new byte[4], 0, 4);
long totalDataLength = 0;
List<Rectangle> blocks = new List<Rectangle>();
Size s = new Size(scanArea.Width, CheckBlock.Height);
Size lastSize = new Size(scanArea.Width % CheckBlock.Width, scanArea.Height % CheckBlock.Height);
int lasty = scanArea.Height - lastSize.Height;
int lastx = scanArea.Width - lastSize.Width;
Rectangle cBlock = new Rectangle();
List<Rectangle> finalUpdates = new List<Rectangle>();
s = new Size(scanArea.Width, s.Height);
fixed (byte* encBuffer = _encodeBuffer)
{
var index = 0;
for (int y = scanArea.Y; y != scanArea.Height; y += s.Height)
{
if (y == lasty)
{
s = new Size(scanArea.Width, lastSize.Height);
}
cBlock = new Rectangle(scanArea.X, y, scanArea.Width, s.Height);
int offset = (y * stride) + (scanArea.X * pixelSize);
if (NativeMethods.memcmp(encBuffer + offset, pScan0 + offset, (uint)stride) != 0)
{
index = blocks.Count - 1;
if (blocks.Count != 0 && (blocks[index].Y + blocks[index].Height) == cBlock.Y)
{
cBlock = new Rectangle(blocks[index].X, blocks[index].Y, blocks[index].Width,
blocks[index].Height + cBlock.Height);
blocks[index] = cBlock;
}
else
{
blocks.Add(cBlock);
}
}
}
for (int i = 0; i < blocks.Count; i++)
{
s = new Size(CheckBlock.Width, blocks[i].Height);
for (int x = scanArea.X; x != scanArea.Width; x += s.Width)
{
if (x == lastx)
{
s = new Size(lastSize.Width, blocks[i].Height);
}
cBlock = new Rectangle(x, blocks[i].Y, s.Width, blocks[i].Height);
bool foundChanges = false;
uint blockStride = (uint)(pixelSize * cBlock.Width);
for (int j = 0; j < cBlock.Height; j++)
{
int blockOffset = (stride * (cBlock.Y + j)) + (pixelSize * cBlock.X);
if (NativeMethods.memcmp(encBuffer + blockOffset, pScan0 + blockOffset, blockStride) != 0)
{
foundChanges = true;
}
NativeMethods.memcpy(encBuffer + blockOffset, pScan0 + blockOffset, blockStride);
//copy-changes
}
if (foundChanges)
{
index = finalUpdates.Count - 1;
if (finalUpdates.Count > 0 &&
(finalUpdates[index].X + finalUpdates[index].Width) == cBlock.X)
{
Rectangle rect = finalUpdates[index];
int newWidth = cBlock.Width + rect.Width;
cBlock = new Rectangle(rect.X, rect.Y, newWidth, rect.Height);
finalUpdates[index] = cBlock;
}
else
{
finalUpdates.Add(cBlock);
}
}
}
}
}
for (int i = 0; i < finalUpdates.Count; i++)
{
Rectangle rect = finalUpdates[i];
int blockStride = pixelSize * rect.Width;
Bitmap tmpBmp = null;
BitmapData tmpData = null;
long length;
try
{
tmpBmp = new Bitmap(rect.Width, rect.Height, format);
tmpData = tmpBmp.LockBits(new Rectangle(0, 0, tmpBmp.Width, tmpBmp.Height),
ImageLockMode.ReadWrite, tmpBmp.PixelFormat);
for (int j = 0, offset = 0; j < rect.Height; j++)
{
int blockOffset = (stride * (rect.Y + j)) + (pixelSize * rect.X);
NativeMethods.memcpy((byte*)tmpData.Scan0.ToPointer() + offset, pScan0 + blockOffset, (uint)blockStride);
//copy-changes
offset += blockStride;
}
outStream.Write(BitConverter.GetBytes(rect.X), 0, 4);
outStream.Write(BitConverter.GetBytes(rect.Y), 0, 4);
outStream.Write(BitConverter.GetBytes(rect.Width), 0, 4);
outStream.Write(BitConverter.GetBytes(rect.Height), 0, 4);
outStream.Write(new byte[4], 0, 4);
length = outStream.Length;
long old = outStream.Position;
_jpgCompression.Compress(tmpBmp, ref outStream);
length = outStream.Position - length;
outStream.Position = old - 4;
outStream.Write(BitConverter.GetBytes(length), 0, 4);
outStream.Position += length;
}
finally
{
tmpBmp.UnlockBits(tmpData);
tmpBmp.Dispose();
}
totalDataLength += length + (4 * 5);
}
outStream.Position = oldPos;
outStream.Write(BitConverter.GetBytes(totalDataLength), 0, 4);
}
}
public unsafe Bitmap DecodeData(IntPtr codecBuffer, uint length)
{
if (length < 4)
{
return _decodedBitmap;
}
int dataSize = *(int*)(codecBuffer);
if (_decodedBitmap == null)
{
byte[] temp = new byte[dataSize];
fixed (byte* tempPtr = temp)
{
NativeMethods.memcpy(new IntPtr(tempPtr), new IntPtr(codecBuffer.ToInt32() + 4), (uint)dataSize);
}
this._decodedBitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(temp));
return _decodedBitmap;
}
else
{
return _decodedBitmap;
}
}
public Bitmap DecodeData(Stream inStream)
{
byte[] temp = new byte[4];
inStream.Read(temp, 0, 4);
int dataSize = BitConverter.ToInt32(temp, 0);
if (_decodedBitmap == null)
{
temp = new byte[dataSize];
inStream.Read(temp, 0, temp.Length);
this._decodedBitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(temp));
return _decodedBitmap;
}
using (Graphics g = Graphics.FromImage(_decodedBitmap))
{
while (dataSize > 0)
{
byte[] tempData = new byte[4 * 5];
inStream.Read(tempData, 0, tempData.Length);
Rectangle rect = new Rectangle(BitConverter.ToInt32(tempData, 0), BitConverter.ToInt32(tempData, 4),
BitConverter.ToInt32(tempData, 8), BitConverter.ToInt32(tempData, 12));
int updateLen = BitConverter.ToInt32(tempData, 16);
byte[] buffer = new byte[updateLen];
inStream.Read(buffer, 0, buffer.Length);
using (MemoryStream m = new MemoryStream(buffer))
{
using (Bitmap tmp = (Bitmap)Image.FromStream(m))
{
g.DrawImage(tmp, rect.Location);
}
}
dataSize -= updateLen + (4 * 5);
}
}
return _decodedBitmap;
}
}
}
@@ -0,0 +1,68 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace Quasar.Common.Video.Compression
{
public class JpgCompression : IDisposable
{
private readonly ImageCodecInfo _encoderInfo;
private readonly EncoderParameters _encoderParams;
public JpgCompression(long quality)
{
EncoderParameter parameter = new EncoderParameter(Encoder.Quality, quality);
this._encoderInfo = GetEncoderInfo("image/jpeg");
this._encoderParams = new EncoderParameters(2);
this._encoderParams.Param[0] = parameter;
this._encoderParams.Param[1] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionRle);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_encoderParams != null)
{
_encoderParams.Dispose();
}
}
}
public byte[] Compress(Bitmap bmp)
{
using (MemoryStream stream = new MemoryStream())
{
bmp.Save(stream, _encoderInfo, _encoderParams);
return stream.ToArray();
}
}
public void Compress(Bitmap bmp, ref Stream targetStream)
{
bmp.Save(targetStream, _encoderInfo, _encoderParams);
}
private ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders();
int num2 = imageEncoders.Length - 1;
for (int i = 0; i <= num2; i++)
{
if (imageEncoders[i].MimeType == mimeType)
{
return imageEncoders[i];
}
}
return null;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
using System;
using ProtoBuf;
namespace Quasar.Common.Video
{
[ProtoContract]
public class Resolution : IEquatable<Resolution>
{
[ProtoMember(1)]
public int Width { get; set; }
[ProtoMember(2)]
public int Height { get; set; }
public bool Equals(Resolution other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Width == other.Width && Height == other.Height;
}
public static bool operator ==(Resolution r1, Resolution r2)
{
if (ReferenceEquals(r1, null))
return ReferenceEquals(r2, null);
return r1.Equals(r2);
}
public static bool operator !=(Resolution r1, Resolution r2)
{
return !(r1 == r2);
}
public override bool Equals(object obj)
{
return Equals(obj as Resolution);
}
public override int GetHashCode()
{
return Width ^ Height;
}
public override string ToString()
{
return $"{Width}x{Height}";
}
}
}