Utility static class providing transport-agnostic download plumbing. It copies bytes from a source Stream to a target Stream with progress callbacks, cancellation support, and streaming SHA-256 calculation, and verifies a finished file against an optional pinned SHA-256 hex.
namespace AutoRig.Dl;
/// <summary>
/// Transport-agnostic download plumbing (testable without HTTP): copy with progress
/// + cancellation + streaming SHA-256, and resume-offset bookkeeping. The editor
/// layer supplies the HTTP streams and file IO.
/// </summary>
public static class DownloadCore
{
/// <summary>
/// Copies <paramref name="source"/> into <paramref name="target"/>, reporting
/// (doneBytes, totalBytes) and hashing what passes through. Returns the hex
/// SHA-256 of THE COPIED PORTION (callers hash-verify only full downloads).
/// </summary>
public static string Copy(
Stream source, Stream target, long alreadyDone, long totalBytes,
Action<long, long> progress, Func<bool> cancelled )
{
ArgumentNullException.ThrowIfNull( source );
ArgumentNullException.ThrowIfNull( target );
var hasher = new Sha256Pure.Hasher();
var buffer = new byte[81920];
var done = alreadyDone;
while ( true )
{
if ( cancelled?.Invoke() == true )
throw new OperationCanceledException();
var read = source.Read( buffer, 0, buffer.Length );
if ( read <= 0 )
break;
target.Write( buffer, 0, read );
hasher.Update( buffer, 0, read );
done += read;
progress?.Invoke( done, totalBytes );
}
return hasher.FinishHex();
}
/// <summary>
/// Verifies a completed file's hash against a pin. Empty pin = unpinned: accepted
/// with <paramref name="warning"/> set so the UI can surface it.
/// </summary>
public static bool Verify( string actualSha256Hex, string pinnedSha256Hex, out string warning )
{
warning = "";
if ( string.IsNullOrEmpty( pinnedSha256Hex ) )
{
warning = "No pinned checksum for this file - downloaded content was not verified.";
return true;
}
return string.Equals( actualSha256Hex, pinnedSha256Hex, StringComparison.OrdinalIgnoreCase );
}
}