Skip to the content.

QWK.NET Architecture

This document describes the architectural boundaries, extension mechanisms, and dependency rules for the QWK.NET library.

Overview

QWK.NET is structured as a core library with extension points for archive formats. The architecture prioritises:

Module Boundaries

Core Library (QwkNet)

The core library (src/QwkNet/) contains all QWK packet parsing, validation, and generation logic. It is dependency-free except for .NET standard library types.

Responsibilities:

Built-in Archive Support:

Module Structure:

QwkNet/
├── Archive/              # Archive abstraction layer
│   ├── Extensions/       # Extension interfaces
│   └── Zip/              # Built-in ZIP implementation
├── Core/                 # Binary record parsing utilities
├── Encoding/             # CP437, line endings, text analysis
├── Models/               # Domain models
│   ├── Control/          # CONTROL.DAT structures
│   ├── Indexing/         # NDX file structures
│   ├── Messages/         # Message structures
│   └── Qwke/             # QWKE extension structures
├── Parsing/              # Format parsers
│   └── Qwke/             # QWKE parsers
└── Validation/           # Validation logic

Extension Packages (QwkNet.Archives.*)

Extension packages add support for additional archive formats beyond ZIP. They are separate NuGet packages that depend on the core QwkNet package.

Example: QwkNet.Archives.Tar provides TAR archive support.

Responsibilities:

Extension Package Structure:

QwkNet.Archives.Tar/
├── TarArchiveExtension.cs    # Implements IArchiveExtension
├── TarArchiveReader.cs       # Implements IArchiveReader
├── TarArchiveWriter.cs       # Implements IArchiveWriter
└── README.md                 # Usage documentation

Tools (QwkNet.Diagnostics, QwkNet.Benchmarking)

Command-line tools that consume the core library. They are separate projects with their own entry points.

Responsibilities:

Dependencies:

Extension Rules

Extension Interface

All archive format extensions must implement IArchiveExtension:

public interface IArchiveExtension
{
  ArchiveFormatId FormatId { get; }
  IReadOnlyList<ArchiveSignature> Signatures { get; }
  bool SupportsReading { get; }
  bool SupportsWriting { get; }
  IArchiveReader CreateReader(Stream stream, bool leaveOpen);
  IArchiveWriter CreateWriter();
}

Extension Registration

RULE: Extensions must be explicitly registered. There is no auto-discovery or assembly scanning.

using QwkNet.Archive;
using QwkNet.Archives.Tar;

// Explicit registration required
ArchiveFactory.RegisterExtension(new TarArchiveExtension());

Rationale:

Extension Detection

Extensions are detected via magic byte signatures:

  1. Signature Matching: Extensions provide ArchiveSignature instances with magic bytes, offset, and minimum length
  2. Detection Order: Registered extensions are tested first (in registration order), then built-in ZIP
  3. Precedence: Longer signatures take precedence over shorter ones
  4. Explicit Format: Users can bypass detection by specifying ArchiveFormatId directly

Extension Thread Safety

RULE: Extension implementations must be stateless and thread-safe.

Extension Licensing

RULE: Extension packages must use permissive licences compatible with MIT.

Acceptable:

Unacceptable:

Extension Versioning

RULE: Extension packages must follow semantic versioning (SemVer).

Extension Documentation

RULE: Extension packages must provide useful documentation.

Required:

Recommended:

Extension Dependencies

RULE: Extensions should minimise external dependencies.

Extension Design Principles

RULE: Extensions must not require core package modification.

RULE: Extensions should avoid global or static state.

Extension Examples

Common extension types include:

These examples demonstrate the extension model’s flexibility whilst maintaining clear boundaries with the core library.

Dependency Rules

Core Library Dependencies

RULE: The core QwkNet library must remain dependency-free.

Allowed:

Forbidden:

Extension Package Dependencies

RULE: Extension packages may depend on the core QwkNet package and format-specific libraries.

Allowed:

Forbidden:

Tool Dependencies

RULE: Tools may have their own dependencies but must not expose them to library consumers.

Allowed:

Forbidden:

Dependency Direction

RULE: Dependencies must flow in one direction only.

Tools → Core Library
Extensions → Core Library
Core Library → .NET Standard Library only

Forbidden:

Thread Safety

Read-Only Operations

RULE: All read-only operations must be thread-safe.

Mutable Operations

RULE: Mutable operations are not thread-safe.

Extension Thread Safety

RULE: Extension implementations must be stateless.

Byte Fidelity

Preservation Requirements

RULE: The library must preserve byte-level fidelity where possible.

Format Tolerance

RULE: Support real-world format variations whilst maintaining fidelity.

Validation Modes

Mode Behaviour

RULE: Validation modes control error handling, not byte preservation.

RULE: Validation mode does not affect byte preservation. All modes preserve original bytes.

Namespace Organisation

Core Namespaces

Extension Namespaces

RULE: Extension packages should use namespaces that match their package name.

Internal Namespaces

RULE: Internal implementation details use Internal suffix or are marked internal.

Summary

The QWK.NET architecture enforces clear boundaries:

  1. Core Library: Dependency-free, handles all QWK packet logic
  2. Extensions: Separate packages, explicit registration, interface-based
  3. Tools: Separate projects, consume core library
  4. Dependencies: One-way flow (Tools/Extensions → Core → .NET)
  5. Thread Safety: Read-only operations thread-safe, mutable operations single-threaded
  6. Byte Fidelity: Preserve original bytes, tolerate format variations

This architecture ensures the library remains maintainable, extensible, and compatible with modern .NET deployment scenarios whilst preserving historical QWK packet formats accurately.