diff --git a/README.md b/README.md index dbad085..796fdd4 100644 --- a/README.md +++ b/README.md @@ -7,85 +7,93 @@ FILO -## FILO v1.1 Highlights +--- -**Added:** +## FILO v1.2.0 Highlights -* βœ” Chunk integrity hashing (SHA256) -* βœ” Password-based encryption (PBKDF2) +### 🧱 Stability & Format Fixes +- βœ” Fixed footer structure (v1.2 deterministic format) +- βœ” Standardized chunk format (encrypted + plain) +- βœ” Fixed index offset validation issues +- βœ” Added footer magic validation (`FLOF`) +- βœ” Stronger container corruption detection -**Deprecated:** +### πŸ” Security Improvements +- βœ” Password-based encryption (PBKDF2 + AES) +- βœ” Encryption contract stabilized (AES-CBC defined) +- βœ” Password verification via SHA256 check +- βœ” Safer chunk validation during streaming -* ⚠ `WithEncryption(byte[] key)` β€” use `WithPassword(string password)` instead. +### βš™οΈ Reliability Improvements +- βœ” Safe offset & length validation +- βœ” Stronger reader error handling +- βœ” Deterministic file structure --- ## Overview -**FILO** (Files In, Layered & Organized) is a modern **multi-file container format** for .NET designed for **large files**. -It stores multiple files (video, audio, text, binaries, etc.) in a **single container**, supporting: +**FILO** (Files In, Layered & Organized) is a modern multi-file container format for .NET designed for large-scale file storage and streaming. + +It supports: -* **Large files** (GB-sized videos, audio, binaries) -* **Multiple files per container** -* **Chunked streaming** for memory-efficient reads -* **AES256 optional encryption per chunk** -* **Embedded metadata** -* **File checksums** for integrity -* Fully **async APIs** +- Large files (GB-sized video/audio/binaries) +- Multi-file containers +- Chunked streaming (memory efficient) +- Optional AES256 encryption per chunk +- Embedded metadata & integrity checks +- Fully async APIs -> FILO = **Files In, Layered & Organized** β€” ideal for **video/audio streaming, backups, and custom file packaging**. +> FILO is designed for **streaming-first storage systems**, not just archive compression. --- ## Why FILO? -Traditional ZIP or JSON-based storage has limitations: +Traditional formats like ZIP have limitations: -- Limited streaming support for large files -- No native chunked encryption -- Metadata is often scattered or external +- ❌ Poor streaming support +- ❌ Weak chunk-level control +- ❌ No streaming encryption model +- ❌ Limited metadata structure -**FILO solves this** by: +FILO solves this by: -- Storing files in **chunks** for streaming or partial reads -- Supporting **encryption at chunk level** -- Embedding **metadata and checksums** -- Providing **simple async APIs** in fluent style +- Streaming files in **chunks** +- Encrypting **per chunk** +- Embedding **structured metadata** +- Supporting **direct streaming without extraction** --- -## FILO Container Layout +## FILO Container Layout (v1.2) ```text +------------------------------------------------+ -| Header (JSON) | -| - Format: "FILO" | -| - Version | -| - Created (UTC) | -| - ChunkSize | -| - FileCount | -| - Compression | -| - Encryption | -| - Description | +| HEADER (JSON) | +|------------------------------------------------| +| - Format: FILO | +| - Version: 1.2 | +| - ChunkSize | +| - FileCount | +| - Encryption Mode (AES-CBC) | +| - KDF (PBKDF2) | +------------------------------------------------+ -| File Chunks | -| [chunk1] [chunk2] ... | -| (Encrypted if AES256) | +| FILE CHUNKS | +| [IV][LEN][DATA] (encrypted) | +| [LEN][DATA] (plain) | +------------------------------------------------+ -| Index (JSON) | -| - File names | -| - Offsets | -| - Chunk sizes | +| INDEX (JSON) | +------------------------------------------------+ -| Metadata (JSON) | -| - File metadata (MIME type, tags, etc.) | +| METADATA (JSON) | +------------------------------------------------+ -| Checksum (JSON) | -| - SHA256 hashes | +| CHECKSUM (JSON) | +------------------------------------------------+ -| Footer | -| - Index offset | -| - Metadata offset | +| FOOTER | +| - IndexOffset | +| - MetadataOffset | +| - ChecksumOffset | +| - "FLOF" magic | +------------------------------------------------+ ``` > This design allows **streaming large files directly**, without full extraction. @@ -108,85 +116,101 @@ Traditional ZIP or JSON-based storage has limitations: --- - ## Installation Install via NuGet: ```bash -dotnet add package Filo --version 1.1.0 +dotnet add package Filo --version 1.2.0 ```` --- ## Basic Usage -### Writing a container +### πŸ“¦ Create Container ```csharp using Filo; -var pwd = "mypassword"; - var writer = new FiloWriter("backup.filo") .AddFile("video.mp4", new FileMetadata { MimeType = "video/mp4" }) - .AddFile("subtitle.srt", new FileMetadata { MimeType = "text/plain" }) + .AddFile("audio.mp3", new FileMetadata { MimeType = "audio/mpeg" }) .WithChunkSize(5_000_000) - .WithPassword(pwd); + .WithPassword("1234567890"); await writer.WriteAsync(); + Console.WriteLine("FILO container created!"); ``` -### Reading files +### πŸ“– Read Container ```csharp var reader = new FiloReader("backup.filo"); await reader.InitializeAsync(); -var key = reader.DeriveKey("mypassword"); +var key = reader.DeriveKey("1234567890"); -// List files foreach (var file in reader.ListFiles()) - Console.WriteLine(file); - -// Stream chunks -await foreach (var chunk in reader.StreamFileAsync("video.mp4", key)) { - await chunk.WriteAsync(chunk); + Console.WriteLine($"{file.Name} ({file.FileSize} bytes)"); } ``` --- - -### Direct Streaming +### πŸ“‘ Stream File (Recommended) ```csharp -using var stream = reader.OpenStream("video.mp4", key); -await stream.CopyToAsync(outputFile); +await using var stream = reader.OpenStream("video.mp4", key); +await using var output = File.Create("restored.mp4"); + +await stream.CopyToAsync(output); ``` -### Extract Files Using `FiloStream` +--- -```csharp -using var filoStream = new FiloStream(reader, "video.mp4", key); -using var output = File.Create("video_restored.mp4"); +### πŸ” Chunk-by-chunk processing -await filoStream.CopyToAsync(output); -Console.WriteLine("File extracted!"); +```csharp +await foreach (var chunk in reader.StreamFileAsync("video.mp4", key)) +{ + // Process streaming data +} ``` -### Load Image +--- -```csharp -using var stream = new FiloStream(reader, "photo.jpg"); -using var image = System.Drawing.Image.FromStream(stream); +### πŸ” Encryption Model (v1.2) -Console.WriteLine($"Loaded image: {image.Width}x{image.Height}"); +- Encrypted chunk format: + ``` + [16-byte IV][4-byte length][encrypted data] + ``` +- Plain chunk format: + ``` + [4-byte length][plain data] + ``` -``` +* Key derived using PBKDF2 (100k iterations) +* AES-CBC encryption per chunk +* Password verification via SHA256 key hash + +--- + +### 🧠 Integrity System + +- Each chunk is validated using SHA256: + ```csharp + var checksum = await FiloChecksum.ComputeFileSHA256Async("video.mp4"); + ``` + +* Prevents corrupted chunk playback +* Ensures file integrity during streaming + +--- -## Streaming Video/Audio in ASP.NET Core / Blazor +### 🌐 ASP.NET Streaming Example ```csharp public async Task GetVideo() @@ -195,6 +219,7 @@ public async Task GetVideo() await reader.InitializeAsync(); var key = reader.DeriveKey("password"); + var stream = new FiloStream(reader, "movie.mp4", key); return File(stream, "video/mp4"); @@ -238,17 +263,29 @@ await foreach (var chunk in reader.StreamFileAsync("largevideo.mp4", key)) --- -## ⚑ When to Use FiloStream vs StreamFileAsync +## ⚑ When to Use What + +| Method | Use Case | +| ------------------- | ----------------------- | +| `OpenStream()` | Direct file streaming | +| `FiloStream` | ASP.NET / UI streaming | +| `StreamFileAsync()` | Custom chunk processing | +| `CopyToAsync()` | Extraction | -| Method | Best For | -| ------------------| ---------------- | -| StreamFileAsync() | chunk processing | -| FiloStream normal | file streaming | -| CopyToAsync() | extraction | -| HTTP streaming | media servers | --- +### πŸ“¦ File Metadata + +```csharp +new FileMetadata +{ + MimeType = "video/mp4", + Description = "Main movie file" +} +``` +--- + > Always verify checksum for **large file integrity**. @@ -275,11 +312,26 @@ Console.WriteLine(checksum); --- -## Notes +## πŸ”§ Core Classes + +| Class | Responsibility | +| -------------- | ---------------------- | +| FiloWriter | Builds container | +| FiloReader | Reads container | +| FiloStream | Streaming abstraction | +| FiloChecksum | Integrity verification | +| FiloEncryption | AES operations | + +--- + -* FILO supports **any file type**: video, audio, images, text, binaries -* For **large containers (GBs)**, keep them **server-side** and stream with `FiloStream`. -* Fully **async and memory-efficient** +## Notes (v1.2 Rules) + +* Footer size is fixed (28 bytes) +* Chunk offsets always point to chunk start +* AES-CBC is the defined encryption mode +* Index must always validate against file length +* Footer magic must be "FLOF" --- @@ -288,4 +340,3 @@ Console.WriteLine(checksum); MIT License - diff --git a/app/FiloExplorer/FiloExplorer.csproj b/app/FiloExplorer/FiloExplorer.csproj index 1acf605..3f0b557 100644 --- a/app/FiloExplorer/FiloExplorer.csproj +++ b/app/FiloExplorer/FiloExplorer.csproj @@ -45,10 +45,12 @@ - + + + MSBuild:Compile diff --git a/src/Filo/Core/FiloReader.cs b/src/Filo/Core/FiloReader.cs index 75caaf1..be3aa28 100644 --- a/src/Filo/Core/FiloReader.cs +++ b/src/Filo/Core/FiloReader.cs @@ -1,4 +1,5 @@ -ο»Ώusing ManuHub.Filo.Utils; +ο»Ώusing ManuHub.Filo.Shared; +using ManuHub.Filo.Utils; using System.Security.Cryptography; using System.Text; using System.Text.Json; @@ -67,19 +68,37 @@ public async Task InitializeAsync() ?? throw new InvalidDataException("Failed to deserialize header."); // FOOTER - fs.Seek(-16, SeekOrigin.End); + fs.Seek(-FiloFooter.Size, SeekOrigin.End); var longBuffer = new byte[8]; + // INDEX OFFSET await fs.ReadExactlyAsync(longBuffer); long indexOffset = BitConverter.ToInt64(longBuffer); + // METADATA OFFSET await fs.ReadExactlyAsync(longBuffer); long metadataOffset = BitConverter.ToInt64(longBuffer); - if (indexOffset >= fs.Length) + // CHECKSUM OFFSET + await fs.ReadExactlyAsync(longBuffer); + long checksumOffset = BitConverter.ToInt64(longBuffer); + + // FOOTER MAGIC + var magicBuf = new byte[4]; + await fs.ReadExactlyAsync(magicBuf); + + var footerMagic = Encoding.ASCII.GetString(magicBuf); + + if (footerMagic != "FLOF") + throw new InvalidDataException("Invalid FILO footer."); + + if (indexOffset <= 0 || indexOffset >= fs.Length) throw new InvalidDataException("Invalid index offset."); + if (metadataOffset < 0 || metadataOffset >= fs.Length) + throw new InvalidDataException("Invalid metadata offset."); + // READ INDEX fs.Position = indexOffset; @@ -210,6 +229,12 @@ public async IAsyncEnumerable StreamFileAsync(string fileName, byte[]? k { fs.Position = chunk.Offset; + if (chunk.Offset < 0 || chunk.Offset >= fs.Length) + { + throw new InvalidDataException( + $"Chunk {chunk.Id} has invalid offset: {chunk.Offset}"); + } + byte[] dataChunk; try @@ -223,6 +248,12 @@ public async IAsyncEnumerable StreamFileAsync(string fileName, byte[]? k await fs.ReadExactlyAsync(lenBuf); int len = BitConverter.ToInt32(lenBuf); + if (len <= 0 || len > fs.Length) + { + throw new InvalidDataException( + $"Invalid encrypted chunk length: {len}"); + } + var enc = new byte[len]; await fs.ReadExactlyAsync(enc); @@ -232,9 +263,14 @@ public async IAsyncEnumerable StreamFileAsync(string fileName, byte[]? k { var lenBuf = new byte[4]; await fs.ReadExactlyAsync(lenBuf); - int len = BitConverter.ToInt32(lenBuf); + if (len <= 0 || len > fs.Length) + { + throw new InvalidDataException( + $"Invalid chunk length: {len}"); + } + dataChunk = new byte[len]; await fs.ReadExactlyAsync(dataChunk); } diff --git a/src/Filo/Filo.csproj b/src/Filo/Filo.csproj index c842049..ac059e6 100644 --- a/src/Filo/Filo.csproj +++ b/src/Filo/Filo.csproj @@ -6,10 +6,10 @@ enable latest Filo - 1.1.0 + 1.2.0 ManuHub.Filo Filo - 1.1.0 + 1.2.0 Manojbabu ManuHub FILO – Fast, flexible multi-file container for .NET with streaming, encryption, and metadata support. diff --git a/src/Filo/Models/FiloChunkIndex.cs b/src/Filo/Models/FiloChunkIndex.cs index 51239d1..aaab65f 100644 --- a/src/Filo/Models/FiloChunkIndex.cs +++ b/src/Filo/Models/FiloChunkIndex.cs @@ -2,8 +2,32 @@ public class FiloChunkIndex { + /// + /// Chunk identifier (sequential per file) + /// public int Id { get; set; } + + /// + /// File offset where this chunk starts. + /// + /// IMPORTANT (v1.2 RULE): + /// Offset always points to the beginning of the chunk header: + /// + /// If encrypted: + /// [IV (16 bytes)][Length (4 bytes)][Data] + /// + /// If not encrypted: + /// [Length (4 bytes)][Data] + /// public long Offset { get; set; } + + /// + /// Total stored chunk size in container (header + data) + /// public int Length { get; set; } + + /// + /// SHA256 hash of ORIGINAL (decrypted) chunk data + /// public string? Hash { get; set; } // v1.1 } \ No newline at end of file diff --git a/src/Filo/README.md b/src/Filo/README.md index 3279500..2069769 100644 --- a/src/Filo/README.md +++ b/src/Filo/README.md @@ -6,85 +6,91 @@ --- -## FILO v1.1 Highlights - -**Added:** - -* βœ” Chunk integrity hashing (SHA256) -* βœ” Password-based encryption (PBKDF2) - -**Deprecated:** - -* ⚠ `WithEncryption(byte[] key)` β€” use `WithPassword(string password)` instead. +## FILO v1.2.0 Highlights + +### 🧱 Stability & Format Fixes +- βœ” Fixed footer structure (v1.2 deterministic format) +- βœ” Standardized chunk format (encrypted + plain) +- βœ” Fixed index offset validation issues +- βœ” Added footer magic validation (`FLOF`) +- βœ” Stronger container corruption detection + +### πŸ” Security Improvements +- βœ” Password-based encryption (PBKDF2 + AES) +- βœ” Encryption contract stabilized (AES-CBC defined) +- βœ” Password verification via SHA256 check +- βœ” Safer chunk validation during streaming + +### βš™οΈ Reliability Improvements +- βœ” Safe offset & length validation +- βœ” Stronger reader error handling +- βœ” Deterministic file structure --- ## Overview -**FILO** (Files In, Layered & Organized) is a modern **multi-file container format** for .NET designed for **large files**. -It stores multiple files (video, audio, text, binaries, etc.) in a **single container**, supporting: +**FILO** (Files In, Layered & Organized) is a modern multi-file container format for .NET designed for large-scale file storage and streaming. + +It supports: -* **Large files** (GB-sized videos, audio, binaries) -* **Multiple files per container** -* **Chunked streaming** for memory-efficient reads -* **AES256 optional encryption per chunk** -* **Embedded metadata** -* **File checksums** for integrity -* Fully **async APIs** +- Large files (GB-sized video/audio/binaries) +- Multi-file containers +- Chunked streaming (memory efficient) +- Optional AES256 encryption per chunk +- Embedded metadata & integrity checks +- Fully async APIs -> FILO = **Files In, Layered & Organized** β€” ideal for **video/audio streaming, backups, and custom file packaging**. +> FILO is designed for **streaming-first storage systems**, not just archive compression. --- ## Why FILO? -Traditional ZIP or JSON-based storage has limitations: +Traditional formats like ZIP have limitations: -- Limited streaming support for large files -- No native chunked encryption -- Metadata is often scattered or external +- ❌ Poor streaming support +- ❌ Weak chunk-level control +- ❌ No streaming encryption model +- ❌ Limited metadata structure -**FILO solves this** by: +FILO solves this by: -- Storing files in **chunks** for streaming or partial reads -- Supporting **encryption at chunk level** -- Embedding **metadata and checksums** -- Providing **simple async APIs** in fluent style +- Streaming files in **chunks** +- Encrypting **per chunk** +- Embedding **structured metadata** +- Supporting **direct streaming without extraction** --- -## FILO Container Layout +## FILO Container Layout (v1.2) ```text +------------------------------------------------+ -| Header (JSON) | -| - Format: "FILO" | -| - Version | -| - Created (UTC) | -| - ChunkSize | -| - FileCount | -| - Compression | -| - Encryption | -| - Description | +| HEADER (JSON) | +|------------------------------------------------| +| - Format: FILO | +| - Version: 1.2 | +| - ChunkSize | +| - FileCount | +| - Encryption Mode (AES-CBC) | +| - KDF (PBKDF2) | +------------------------------------------------+ -| File Chunks | -| [chunk1] [chunk2] ... | -| (Encrypted if AES256) | +| FILE CHUNKS | +| [IV][LEN][DATA] (encrypted) | +| [LEN][DATA] (plain) | +------------------------------------------------+ -| Index (JSON) | -| - File names | -| - Offsets | -| - Chunk sizes | +| INDEX (JSON) | +------------------------------------------------+ -| Metadata (JSON) | -| - File metadata (MIME type, tags, etc.) | +| METADATA (JSON) | +------------------------------------------------+ -| Checksum (JSON) | -| - SHA256 hashes | +| CHECKSUM (JSON) | +------------------------------------------------+ -| Footer | -| - Index offset | -| - Metadata offset | +| FOOTER | +| - IndexOffset | +| - MetadataOffset | +| - ChecksumOffset | +| - "FLOF" magic | +------------------------------------------------+ ``` > This design allows **streaming large files directly**, without full extraction. @@ -107,85 +113,101 @@ Traditional ZIP or JSON-based storage has limitations: --- - ## Installation Install via NuGet: ```bash -dotnet add package Filo --version 1.1.0 +dotnet add package Filo --version 1.2.0 ```` --- ## Basic Usage -### Writing a container +### πŸ“¦ Create Container ```csharp using Filo; -var pwd = "mypassword"; - var writer = new FiloWriter("backup.filo") .AddFile("video.mp4", new FileMetadata { MimeType = "video/mp4" }) - .AddFile("subtitle.srt", new FileMetadata { MimeType = "text/plain" }) + .AddFile("audio.mp3", new FileMetadata { MimeType = "audio/mpeg" }) .WithChunkSize(5_000_000) - .WithPassword(pwd); + .WithPassword("1234567890"); await writer.WriteAsync(); + Console.WriteLine("FILO container created!"); ``` -### Reading files +### πŸ“– Read Container ```csharp var reader = new FiloReader("backup.filo"); await reader.InitializeAsync(); -var key = reader.DeriveKey("mypassword"); +var key = reader.DeriveKey("1234567890"); -// List files foreach (var file in reader.ListFiles()) - Console.WriteLine(file); - -// Stream chunks -await foreach (var chunk in reader.StreamFileAsync("video.mp4", key)) { - await chunk.WriteAsync(chunk); + Console.WriteLine($"{file.Name} ({file.FileSize} bytes)"); } ``` --- - -### Direct Streaming +### πŸ“‘ Stream File (Recommended) ```csharp -using var stream = reader.OpenStream("video.mp4", key); -await stream.CopyToAsync(outputFile); +await using var stream = reader.OpenStream("video.mp4", key); +await using var output = File.Create("restored.mp4"); + +await stream.CopyToAsync(output); ``` -### Extract Files Using `FiloStream` +--- -```csharp -using var filoStream = new FiloStream(reader, "video.mp4", key); -using var output = File.Create("video_restored.mp4"); +### πŸ” Chunk-by-chunk processing -await filoStream.CopyToAsync(output); -Console.WriteLine("File extracted!"); +```csharp +await foreach (var chunk in reader.StreamFileAsync("video.mp4", key)) +{ + // Process streaming data +} ``` -### Load Image +--- -```csharp -using var stream = new FiloStream(reader, "photo.jpg"); -using var image = System.Drawing.Image.FromStream(stream); +### πŸ” Encryption Model (v1.2) -Console.WriteLine($"Loaded image: {image.Width}x{image.Height}"); +- Encrypted chunk format: + ``` + [16-byte IV][4-byte length][encrypted data] + ``` +- Plain chunk format: + ``` + [4-byte length][plain data] + ``` -``` +* Key derived using PBKDF2 (100k iterations) +* AES-CBC encryption per chunk +* Password verification via SHA256 key hash + +--- -## Streaming Video/Audio in ASP.NET Core / Blazor +### 🧠 Integrity System + +- Each chunk is validated using SHA256: + ```csharp + var checksum = await FiloChecksum.ComputeFileSHA256Async("video.mp4"); + ``` + +* Prevents corrupted chunk playback +* Ensures file integrity during streaming + +--- + +### 🌐 ASP.NET Streaming Example ```csharp public async Task GetVideo() @@ -194,6 +216,7 @@ public async Task GetVideo() await reader.InitializeAsync(); var key = reader.DeriveKey("password"); + var stream = new FiloStream(reader, "movie.mp4", key); return File(stream, "video/mp4"); @@ -237,15 +260,27 @@ await foreach (var chunk in reader.StreamFileAsync("largevideo.mp4", key)) --- -## ⚑ When to Use FiloStream vs StreamFileAsync +## ⚑ When to Use What + +| Method | Use Case | +| ------------------- | ----------------------- | +| `OpenStream()` | Direct file streaming | +| `FiloStream` | ASP.NET / UI streaming | +| `StreamFileAsync()` | Custom chunk processing | +| `CopyToAsync()` | Extraction | + -| Method | Best For | -| ------------------| ---------------- | -| StreamFileAsync() | chunk processing | -| FiloStream normal | file streaming | -| CopyToAsync() | extraction | -| HTTP streaming | media servers | +--- + +### πŸ“¦ File Metadata +```csharp +new FileMetadata +{ + MimeType = "video/mp4", + Description = "Main movie file" +} +``` --- > Always verify checksum for **large file integrity**. @@ -274,11 +309,26 @@ Console.WriteLine(checksum); --- -## Notes +## πŸ”§ Core Classes + +| Class | Responsibility | +| -------------- | ---------------------- | +| FiloWriter | Builds container | +| FiloReader | Reads container | +| FiloStream | Streaming abstraction | +| FiloChecksum | Integrity verification | +| FiloEncryption | AES operations | + +--- + + +## Notes (v1.2 Rules) -* FILO supports **any file type**: video, audio, images, text, binaries -* For **large containers (GBs)**, keep them **server-side** and stream with `FiloStream`. -* Fully **async and memory-efficient** +* Footer size is fixed (28 bytes) +* Chunk offsets always point to chunk start +* AES-CBC is the defined encryption mode +* Index must always validate against file length +* Footer magic must be "FLOF" --- diff --git a/src/Filo/Shared/FiloConstants.cs b/src/Filo/Shared/FiloConstants.cs index 09ae3e6..f6624d2 100644 --- a/src/Filo/Shared/FiloConstants.cs +++ b/src/Filo/Shared/FiloConstants.cs @@ -6,4 +6,10 @@ public static class FiloConstants public const int IvSize = 16; public const int LengthSize = 4; +} + +public static class FiloFooter +{ + public const int Size = + sizeof(long) + sizeof(long) + sizeof(long) + 4; } \ No newline at end of file diff --git a/test/Filo.Console/Program.cs b/test/Filo.Console/Program.cs index 2134ade..1939d0c 100644 --- a/test/Filo.Console/Program.cs +++ b/test/Filo.Console/Program.cs @@ -1,7 +1,7 @@ ο»Ώusing ManuHub.Filo; using System.Security.Cryptography; -string filoPath = "backupv1.1.filo"; +string filoPath = "backupv1.2.filo"; try { @@ -30,7 +30,7 @@ try { Console.WriteLine("Read file header:"); - var header = await FiloReader.ReadHeaderAsync("backupv1.1.filo"); + var header = await FiloReader.ReadHeaderAsync("backupv1.2.filo"); Console.WriteLine($"Files: {header.FileCount}"); Console.WriteLine($"Created: {header.Created}");