Task 123: .DAA File Format

Task 123: .DAA File Format

The Direct Access Archive (DAA) file format is a proprietary disk image format developed by PowerISO Computing. It supports compression, password protection, encryption, and multi-volume splitting, and is essentially a chunked, compressed ISO image. The specifications are not officially published, but reverse engineering from open sources reveals the structure as a header followed by a list of chunk lengths and zlib-compressed chunks (for version 1.0), with optional LZMA in other versions.

  1. List of properties intrinsic to the file format:
  • Magic sequence (typically "DAA" or "DAA..." as the signature bytes).
  • File format version (e.g., 0x00000100 for version 1.0).
  • Offset to list of compressed chunk lengths (field located at file offset 0x4C, assumed 8 bytes little-endian).
  • Offset to first compressed chunk (field located at file offset 0x5E, assumed 8 bytes little-endian).
  • List of compressed chunk lengths (each encoded as 3 bytes in MSB-LSB-middle order; e.g., for value 0x697, encoded as 0x00 0x97 0x06).
  • Compressed chunks (zlib-compressed data without header; each decompresses to 65536 bytes except the last).
  • Compression type (zlib/DEFLATE for version 1.0, potentially LZMA for others).
  • Encryption flag and type (AES for password-protected files).
  • Password protection support (hash or key in header for authentication).
  • Multi-volume splitting support (for large files split into .daa.001, .daa.002, etc.).
  • Uncompressed size (derived from chunk count and chunk size).
  • Compressed size (sum of chunk lengths).
  1. Two direct download links for .DAA files (note: .DAA files are uncommon in public repositories due to their proprietary nature; these are sample links from public sources found during search, but verify legality and safety before downloading):
  1. HTML/JavaScript for a Ghost blog embed (this is a self-contained snippet to embed in a blog post; it creates a drag-and-drop area, parses the .DAA file, and dumps the properties to the screen):
Drag and drop .DAA file here
  1. Python class:
import zlib
import struct

class DAAFile:
    def __init__(self, filepath):
        self.filepath = filepath
        self.magic = None
        self.version = None
        self.offset_to_list = None
        self.offset_to_data = None
        self.num_chunks = None
        self.chunk_lengths = []
        self.uncompressed_size = None
        self.compressed_chunks = []
        self.decompressed_data = None

    def read(self):
        with open(self.filepath, 'rb') as f:
            data = f.read()
        self.magic = data[0:4].decode('utf-8', errors='ignore')
        self.version = struct.unpack('<I', data[4:8])[0]
        self.offset_to_list = struct.unpack('<Q', data[0x4C:0x4C+8])[0]
        self.offset_to_data = struct.unpack('<Q', data[0x5E:0x5E+8])[0]
        list_size = self.offset_to_data - self.offset_to_list
        self.num_chunks = list_size // 3
        for i in range(self.num_chunks):
            pos = self.offset_to_list + i * 3
            b1, b2, b3 = data[pos:pos+3]
            length = (b1 << 16) | (b3 << 8) | b2
            self.chunk_lengths.append(length)
        pos = self.offset_to_data
        for length in self.chunk_lengths:
            chunk = data[pos:pos+length]
            self.compressed_chunks.append(chunk)
            pos += length
        self.uncompressed_size = (self.num_chunks - 1) * 65536 + (len(zlib.decompress(self.compressed_chunks[-1], wbits=-15)) if self.num_chunks > 0 else 0)

    def decode(self):
        self.decompressed_data = b''
        for chunk in self.compressed_chunks:
            decompressed = zlib.decompress(chunk, wbits=-15)
            self.decompressed_data += decompressed

    def print_properties(self):
        print(f"Magic: {self.magic}")
        print(f"Version: 0x{self.version:x}")
        print(f"Offset to chunk lengths list: 0x{self.offset_to_list:x}")
        print(f"Offset to first chunk: 0x{self.offset_to_data:x}")
        print(f"Number of chunks: {self.num_chunks}")
        print(f"Chunk lengths: {self.chunk_lengths}")
        print(f"Compression type: zlib")
        print(f"Encryption: {'Yes' if 'encrypted' in self.magic.lower() else 'No'} (basic check)")
        print(f"Multi-volume: {'Yes' if self.filepath.endswith('.001') else 'No'} (basic check)")
        print(f"Uncompressed size: {self.uncompressed_size} bytes")

    def write(self, new_filepath):
        # For write, reverse the read: compress data in chunks, update lengths, write header and data.
        # Assume self.decompressed_data is set; split into chunks, compress, etc.
        if not self.decompressed_data:
            raise ValueError("No data to write")
        chunks = [self.decompressed_data[i:i+65536] for i in range(0, len(self.decompressed_data), 65536)]
        self.chunk_lengths = []
        self.compressed_chunks = []
        for chunk in chunks:
            compressed = zlib.compress(chunk, level=9)[2:-4]  # Remove zlib header and checksum
            self.chunk_lengths.append(len(compressed))
            self.compressed_chunks.append(compressed)
        self.num_chunks = len(chunks)
        self.uncompressed_size = len(self.decompressed_data)
        # Rebuild list bytes
        list_bytes = b''
        for length in self.chunk_lengths:
            b1 = (length >> 16) & 0xFF
            b2 = length & 0xFF
            b3 = (length >> 8) & 0xFF
            list_bytes += bytes([b1, b2, b3])
        # Assume header is 0x66 bytes, update offsets
        self.offset_to_list = 0x66
        self.offset_to_data = self.offset_to_list + len(list_bytes)
        header = b'DAA\x00' + struct.pack('<I', self.version) + b'\x00' * (0x4C - 8) + struct.pack('<Q', self.offset_to_list) + b'\x00' * (0x5E - 0x4C - 8) + struct.pack('<Q', self.offset_to_data) + b'\x00' * (0x66 - 0x5E - 8)
        with open(new_filepath, 'wb') as f:
            f.write(header + list_bytes + b''.join(self.compressed_chunks))

# Example usage
# daa = DAAFile('example.daa')
# daa.read()
# daa.decode()
# daa.print_properties()
# daa.write('new.daa')
  1. Java class:
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.Inflater;
import java.util.ArrayList;
import java.util.List;

public class DAAFile {
    private String filepath;
    private String magic;
    private int version;
    private long offsetToList;
    private long offsetToData;
    private int numChunks;
    private List<Integer> chunkLengths = new ArrayList<>();
    private long uncompressedSize;
    private List<byte[]> compressedChunks = new ArrayList<>();
    private byte[] decompressedData;

    public DAAFile(String filepath) {
        this.filepath = filepath;
    }

    public void read() throws IOException {
        RandomAccessFile raf = new RandomAccessFile(filepath, "r");
        byte[] data = new byte[(int) raf.length()];
        raf.readFully(data);
        raf.close();
        ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
        magic = new String(data, 0, 4);
        version = bb.getInt(4);
        offsetToList = bb.getLong(0x4C);
        offsetToData = bb.getLong(0x5E);
        long listSize = offsetToData - offsetToList;
        numChunks = (int) (listSize / 3);
        for (int i = 0; i < numChunks; i++) {
            int pos = (int) offsetToList + i * 3;
            int b1 = data[pos] & 0xFF;
            int b2 = data[pos + 1] & 0xFF;
            int b3 = data[pos + 2] & 0xFF;
            int length = (b1 << 16) | (b3 << 8) | b2;
            chunkLengths.add(length);
        }
        int pos = (int) offsetToData;
        for (int length : chunkLengths) {
            byte[] chunk = new byte[length];
            System.arraycopy(data, pos, chunk, 0, length);
            compressedChunks.add(chunk);
            pos += length;
        }
        uncompressedSize = (numChunks - 1L) * 65536 + (decompressChunk(compressedChunks.get(compressedChunks.size() - 1)).length if numChunks > 0 else 0);
    }

    private byte[] decompressChunk(byte[] chunk) throws IOException {
        Inflater inflater = new Inflater(true); // No header
        byte[] output = new byte[65536];
        inflater.setInput(chunk);
        int len = inflater.inflate(output);
        inflater.end();
        byte[] result = new byte[len];
        System.arraycopy(output, 0, result, 0, len);
        return result;
    }

    public void decode() throws IOException {
        List<byte[]> decompressed = new ArrayList<>();
        for (byte[] chunk : compressedChunks) {
            decompressed.add(decompressChunk(chunk));
        }
        int total = decompressed.stream().mapToInt(b -> b.length).sum();
        decompressedData = new byte[total];
        int pos = 0;
        for (byte[] d : decompressed) {
            System.arraycopy(d, 0, decompressedData, pos, d.length);
            pos += d.length;
        }
    }

    public void printProperties() {
        System.out.println("Magic: " + magic);
        System.out.println("Version: 0x" + Integer.toHexString(version));
        System.out.println("Offset to chunk lengths list: 0x" + Long.toHexString(offsetToList));
        System.out.println("Offset to first chunk: 0x" + Long.toHexString(offsetToData));
        System.out.println("Number of chunks: " + numChunks);
        System.out.println("Chunk lengths: " + chunkLengths);
        System.out.println("Compression type: zlib");
        System.out.println("Encryption: " + (magic.toLowerCase().contains("encrypted") ? "Yes" : "No") + " (basic check)");
        System.out.println("Multi-volume: " + (filepath.endsWith(".001") ? "Yes" : "No") + " (basic check)");
        System.out.println("Uncompressed size: " + uncompressedSize + " bytes");
    }

    public void write(String newFilepath) throws IOException {
        // Similar to Python: compress decompressedData, update lengths, write.
        if (decompressedData == null) {
            throw new IOException("No data to write");
        }
        List<byte[]> chunks = new ArrayList<>();
        for (int i = 0; i < decompressedData.length; i += 65536) {
            int end = Math.min(i + 65536, decompressedData.length);
            byte[] chunk = new byte[end - i];
            System.arraycopy(decompressedData, i, chunk, 0, chunk.length);
            chunks.add(chunk);
        }
        chunkLengths.clear();
        compressedChunks.clear();
        for (byte[] chunk : chunks) {
            byte[] compressed = compressChunk(chunk);
            chunkLengths.add(compressed.length);
            compressedChunks.add(compressed);
        }
        numChunks = chunks.size();
        uncompressedSize = decompressedData.length;
        // Build list bytes
        ByteBuffer listBb = ByteBuffer.allocate(numChunks * 3);
        for (int length : chunkLengths) {
            int b1 = (length >> 16) & 0xFF;
            int b2 = length & 0xFF;
            int b3 = (length >> 8) & 0xFF;
            listBb.put((byte) b1);
            listBb.put((byte) b2);
            listBb.put((byte) b3);
        }
        // Header
        offsetToList = 0x66;
        offsetToData = offsetToList + numChunks * 3;
        ByteBuffer header = ByteBuffer.allocate(0x66).order(ByteOrder.LITTLE_ENDIAN);
        header.position(0);
        header.put("DAA\0".getBytes());
        header.putInt(version);
        header.position(0x4C);
        header.putLong(offsetToList);
        header.position(0x5E);
        header.putLong(offsetToData);
        // Write
        RandomAccessFile raf = new RandomAccessFile(newFilepath, "rw");
        raf.write(header.array());
        raf.write(listBb.array());
        for (byte[] chunk : compressedChunks) {
            raf.write(chunk);
        }
        raf.close();
    }

    private byte[] compressChunk(byte[] chunk) throws IOException {
        // Use Deflater with no header
        java.util.zip.Deflater deflater = new java.util.zip.Deflater(9, true);
        deflater.setInput(chunk);
        deflater.finish();
        byte[] output = new byte[65536];
        int len = deflater.deflate(output);
        deflater.end();
        byte[] result = new byte[len];
        System.arraycopy(output, 0, result, 0, len);
        return result;
    }

    // Example usage
    // DAAFile daa = new DAAFile("example.daa");
    // daa.read();
    // daa.decode();
    // daa.printProperties();
    // daa.write("new.daa");
}
  1. JavaScript class:
class DAAFile {
  constructor(filepath) {
    this.filepath = filepath;
    this.magic = null;
    this.version = null;
    this.offsetToList = null;
    this.offsetToData = null;
    this.numChunks = null;
    this.chunkLengths = [];
    this.uncompressedSize = null;
    this.compressedChunks = [];
    this.decompressedData = null;
  }

  async read() {
    // Assume fetch for node or browser with file access; for simplicity, assume arrayBuffer is provided
    // In practice, use fs in Node or FileReader in browser
    const fs = require('fs'); // For Node
    const data = fs.readFileSync(this.filepath);
    const view = new DataView(data.buffer);
    const uint8 = new Uint8Array(data.buffer);
    this.magic = String.fromCharCode(...uint8.slice(0, 4));
    this.version = view.getUint32(4, true);
    this.offsetToList = view.getBigUint64(0x4C, true);
    this.offsetToData = view.getBigUint64(0x5E, true);
    const listSize = Number(this.offsetToData - this.offsetToList);
    this.numChunks = listSize / 3;
    for (let i = 0; i < this.numChunks; i++) {
      const pos = Number(this.offsetToList) + i * 3;
      const b1 = uint8[pos];
      const b2 = uint8[pos + 1];
      const b3 = uint8[pos + 2];
      const length = (b1 << 16) | (b3 << 8) | b2;
      this.chunkLengths.push(length);
    }
    let pos = Number(this.offsetToData);
    for (let length of this.chunkLengths) {
      const chunk = uint8.slice(pos, pos + length);
      this.compressedChunks.push(chunk);
      pos += length;
    }
    this.uncompressedSize = (this.numChunks - 1) * 65536 + (this.decompressChunk(this.compressedChunks[this.compressedChunks.length - 1]).length || 0);
  }

  decompressChunk(chunk) {
    // JS zlib library needed, e.g., pako
    const pako = require('pako');
    return pako.inflateRaw(chunk);
  }

  async decode() {
    this.decompressedData = new Uint8Array(0);
    for (let chunk of this.compressedChunks) {
      const decompressed = this.decompressChunk(chunk);
      const newData = new Uint8Array(this.decompressedData.length + decompressed.length);
      newData.set(this.decompressedData);
      newData.set(decompressed, this.decompressedData.length);
      this.decompressedData = newData;
    }
  }

  printProperties() {
    console.log(`Magic: ${this.magic}`);
    console.log(`Version: 0x${this.version.toString(16)}`);
    console.log(`Offset to chunk lengths list: 0x${this.offsetToList.toString(16)}`);
    console.log(`Offset to first chunk: 0x${this.offsetToData.toString(16)}`);
    console.log(`Number of chunks: ${this.numChunks}`);
    console.log(`Chunk lengths: ${this.chunkLengths.join(', ')}`);
    console.log(`Compression type: zlib`);
    console.log(`Encryption: ${this.magic.toLowerCase().includes('encrypted') ? 'Yes' : 'No'} (basic check)`);
    console.log(`Multi-volume: ${this.filepath.endsWith('.001') ? 'Yes' : 'No'} (basic check)`);
    console.log(`Uncompressed size: ${this.uncompressedSize} bytes`);
  }

  async write(newFilepath) {
    if (!this.decompressedData) throw new Error('No data to write');
    const chunks = [];
    for (let i = 0; i < this.decompressedData.length; i += 65536) {
      const end = Math.min(i + 65536, this.decompressedData.length);
      chunks.push(this.decompressedData.subarray(i, end));
    }
    this.chunkLengths = [];
    this.compressedChunks = [];
    const pako = require('pako');
    for (let chunk of chunks) {
      const compressed = pako.deflateRaw(chunk);
      this.chunkLengths.push(compressed.length);
      this.compressedChunks.push(compressed);
    }
    this.numChunks = chunks.length;
    this.uncompressedSize = this.decompressedData.length;
    // Build list
    const listBytes = new Uint8Array(this.numChunks * 3);
    for (let j = 0; j < this.numChunks; j++) {
      const length = this.chunkLengths[j];
      const b1 = (length >> 16) & 0xFF;
      const b2 = length & 0xFF;
      const b3 = (length >> 8) & 0xFF;
      listBytes[j * 3] = b1;
      listBytes[j * 3 + 1] = b2;
      listBytes[j * 3 + 2] = b3;
    }
    // Header
    this.offsetToList = BigInt(0x66);
    this.offsetToData = this.offsetToList + BigInt(this.numChunks * 3);
    const header = new Uint8Array(0x66);
    const headerView = new DataView(header.buffer);
    header.set(new Uint8Array([68, 65, 65, 0])); // DAA\0
    headerView.setUint32(4, this.version, true);
    headerView.setBigUint64(0x4C, this.offsetToList, true);
    headerView.setBigUint64(0x5E, this.offsetToData, true);
    // Write
    const fs = require('fs');
    const fullData = new Uint8Array(header.length + listBytes.length + this.compressedChunks.reduce((a, c) => a + c.length, 0));
    fullData.set(header);
    fullData.set(listBytes, header.length);
    let pos = header.length + listBytes.length;
    for (let chunk of this.compressedChunks) {
      fullData.set(chunk, pos);
      pos += chunk.length;
    }
    fs.writeFileSync(newFilepath, fullData);
  }
}

// Example usage in Node
// const daa = new DAAFile('example.daa');
// await daa.read();
// await daa.decode();
// daa.printProperties();
// await daa.write('new.daa');
  1. C class (using C++ for class support):
#include <fstream>
#include <iostream>
#include <vector>
#include <zlib.h>
#include <cstring>

class DAAFile {
private:
    std::string filepath;
    std::string magic;
    uint32_t version;
    uint64_t offsetToList;
    uint64_t offsetToData;
    uint32_t numChunks;
    std::vector<uint32_t> chunkLengths;
    uint64_t uncompressedSize;
    std::vector<std::vector<uint8_t>> compressedChunks;
    std::vector<uint8_t> decompressedData;

public:
    DAAFile(const std::string& filepath) : filepath(filepath), version(0), offsetToList(0), offsetToData(0), numChunks(0), uncompressedSize(0) {}

    void read() {
        std::ifstream f(filepath, std::ios::binary);
        f.seekg(0, std::ios::end);
        size_t size = f.tellg();
        f.seekg(0, std::ios::beg);
        std::vector<uint8_t> data(size);
        f.read((char*)data.data(), size);
        f.close();
        magic = std::string(data.begin(), data.begin() + 4);
        memcpy(&version, data.data() + 4, 4);
        memcpy(&offsetToList, data.data() + 0x4C, 8);
        memcpy(&offsetToData, data.data() + 0x5E, 8);
        uint64_t listSize = offsetToData - offsetToList;
        numChunks = listSize / 3;
        chunkLengths.resize(numChunks);
        for (uint32_t i = 0; i < numChunks; i++) {
            uint32_t pos = offsetToList + i * 3;
            uint8_t b1 = data[pos];
            uint8_t b2 = data[pos + 1];
            uint8_t b3 = data[pos + 2];
            chunkLengths[i] = (b1 << 16) | (b3 << 8) | b2;
        }
        uint32_t pos = offsetToData;
        compressedChunks.resize(numChunks);
        for (uint32_t i = 0; i < numChunks; i++) {
            uint32_t length = chunkLengths[i];
            compressedChunks[i].resize(length);
            memcpy(compressedChunks[i].data(), data.data() + pos, length);
            pos += length;
        }
        uncompressedSize = (numChunks - 1) * 65536ULL + (decompressChunk(compressedChunks.back()).size() if numChunks > 0 else 0);
    }

    std::vector<uint8_t> decompressChunk(const std::vector<uint8_t>& chunk) {
        uLongf destLen = 65536;
        std::vector<uint8_t> output(destLen);
        int ret = uncompress2(output.data(), &destLen, chunk.data(), (uLongf*)&chunk.size());
        if (ret != Z_OK) {
            std::cerr << "Decompression error" << std::endl;
            return {};
        }
        output.resize(destLen);
        return output;
    }

    void decode() {
        decompressedData.clear();
        for (const auto& chunk : compressedChunks) {
            auto d = decompressChunk(chunk);
            decompressedData.insert(decompressedData.end(), d.begin(), d.end());
        }
    }

    void printProperties() {
        std::cout << "Magic: " << magic << std::endl;
        std::cout << "Version: 0x" << std::hex << version << std::endl;
        std::cout << "Offset to chunk lengths list: 0x" << std::hex << offsetToList << std::endl;
        std::cout << "Offset to first chunk: 0x" << std::hex << offsetToData << std::endl;
        std::cout << "Number of chunks: " << numChunks << std::endl;
        std::cout << "Chunk lengths: ";
        for (auto l : chunkLengths) std::cout << l << " ";
        std::cout << std::endl;
        std::cout << "Compression type: zlib" << std::endl;
        std::cout << "Encryption: " << (magic.find("encrypted") != std::string::npos ? "Yes" : "No") << " (basic check)" << std::endl;
        std::cout << "Multi-volume: " << (filepath.find(".001") != std::string::npos ? "Yes" : "No") << " (basic check)" << std::endl;
        std::cout << "Uncompressed size: " << uncompressedSize << " bytes" << std::endl;
    }

    void write(const std::string& newFilepath) {
        if (decompressedData.empty()) {
            std::cerr << "No data to write" << std::endl;
            return;
        }
        std::vector<std::vector<uint8_t>> chunks;
        for (size_t i = 0; i < decompressedData.size(); i += 65536) {
            size_t end = std::min(i + 65536, decompressedData.size());
            std::vector<uint8_t> chunk(decompressedData.begin() + i, decompressedData.begin() + end);
            chunks.push_back(chunk);
        }
        chunkLengths.clear();
        compressedChunks.clear();
        for (const auto& chunk : chunks) {
            uLongf destLen = compressBound(chunk.size());
            std::vector<uint8_t> compressed(destLen);
            int ret = compress2(compressed.data(), &destLen, chunk.data(), chunk.size(), 9);
            if (ret != Z_OK) {
                std::cerr << "Compression error" << std::endl;
                return;
            }
            compressed.resize(destLen);
            chunkLengths.push_back(destLen);
            compressedChunks.push_back(compressed);
        }
        numChunks = chunks.size();
        uncompressedSize = decompressedData.size();
        // Build list
        std::vector<uint8_t> listBytes(numChunks * 3);
        for (uint32_t j = 0; j < numChunks; j++) {
            uint32_t length = chunkLengths[j];
            uint8_t b1 = (length >> 16) & 0xFF;
            uint8_t b2 = length & 0xFF;
            uint8_t b3 = (length >> 8) & 0xFF;
            listBytes[j * 3] = b1;
            listBytes[j * 3 + 1] = b2;
            listBytes[j * 3 + 2] = b3;
        }
        // Header
        offsetToList = 0x66;
        offsetToData = offsetToList + numChunks * 3;
        std::vector<uint8_t> header(0x66, 0);
        memcpy(header.data(), "DAA\0", 4);
        memcpy(header.data() + 4, &version, 4);
        memcpy(header.data() + 0x4C, &offsetToList, 8);
        memcpy(header.data() + 0x5E, &offsetToData, 8);
        // Write
        std::ofstream f(newFilepath, std::ios::binary);
        f.write((char*)header.data(), header.size());
        f.write((char*)listBytes.data(), listBytes.size());
        for (const auto& chunk : compressedChunks) {
            f.write((char*)chunk.data(), chunk.size());
        }
        f.close();
    }
};

// Example usage
// DAAFile daa("example.daa");
// daa.read();
// daa.decode();
// daa.printProperties();
// daa.write("new.daa");