Task 016: .ADZ File Format
Task 016: .ADZ File Forma
File Format Specifications for the .ADZ File Format
The .ADZ file format is a compressed Amiga Disk File (ADF) image, where the compression is applied using the GZIP algorithm. The uncompressed content is an ADF file, which is a raw sector-by-sector dump of an Amiga floppy disk, typically 901120 bytes in size for a standard double-density (DD) floppy disk (80 cylinders, 2 heads, 11 sectors per track, 512 bytes per sector). The file system within the ADF is the AmigaDOS Old File System (OFS) or Fast File System (FFS), with optional extensions for international mode (INTL) and directory cache (DIRC).
The .ADZ format itself follows the GZIP specification: it begins with a GZIP header (magic bytes 0x1F 0x8B, compression method 0x08 for deflate, flags, modification time, extra flags, OS type, optional extra fields, filename, comment, header CRC), followed by the compressed ADF data, and ends with a CRC32 and uncompressed size.
- List of all the properties of this file format intrinsic to its file system:
Based on the logical structure of the AmigaDOS file system within the uncompressed ADF data, the intrinsic properties are derived from the key blocks (boot block, root block, bitmap block, etc.). These include:
- DOS Identifier (from boot block: string 'DOS')
- DOS Flags (from boot block: byte indicating file system type - bit 0: FFS if set, bit 1: INTL if set, bit 2: DIRC if set)
- Boot Checksum (ulong from boot block)
- Root Block Pointer (ulong from boot block)
- Boot Code Present (boolean from boot block: true if non-zero bytes after offset 12)
- Type (ulong from root block: T_HEADER = 2)
- Header Key (ulong from root block: unused, 0)
- Hash Table Size (ulong from root block)
- First Data (ulong from root block: unused, 0)
- Root Checksum (ulong from root block)
- Hash Table (array of ulongs from root block: pointers to entry blocks)
- Bitmap Flag (ulong from root block: -1 if valid)
- Bitmap Pages (array of ulongs from root block: bitmap block pointers, up to 25)
- Bitmap Extension (ulong from root block: first bitmap extension block)
- Last Root Alteration Date (days, mins, ticks from root block)
- Volume Name Length (char from root block)
- Volume Name (string from root block, max 30 chars)
- Last Volume Alteration Date (days, mins, ticks from root block)
- Creation Date (days, mins, ticks from root block)
- First Directory Cache Block (ulong from root block, for FFS)
- Secondary Type (ulong from root block: ST_ROOT = 1)
- Bitmap Checksum (long from bitmap block)
- Free Blocks (integer: count of free blocks from bitmap map)
- Allocated Blocks (integer: total blocks minus free blocks minus reserved)
- Two direct download links for files of format .ADZ:
- https://files.serverboi.org/view/Megaromserver/Roms/amiga/barb/Barba2-2.adz?share=MEGAROMSERVER&nzlS9RC3=xxNMrw
- https://chomikuj.pl/amigafan/Gry+Amiga+adf/barb/Barba2-1,6789297166.adz
- Ghost blog embedded HTML JavaScript for drag and drop .ADZ file to dump properties:
- Python class for .ADZ file:
import gzip
import struct
import datetime
class ADZFile:
def __init__(self, filename):
with open(filename, 'rb') as f:
compressed = f.read()
self.data = gzip.decompress(compressed)
self.properties = self._parse_properties()
def _parse_properties(self):
properties = {}
# Boot block
dos_id = struct.unpack('>3s', self.data[0:3])[0].decode()
properties['DOS Identifier'] = dos_id
dos_flags = self.data[3]
properties['DOS Flags'] = dos_flags
properties['File System Type'] = ('FFS' if dos_flags & 1 else 'OFS') + (' INTL' if dos_flags & 2 else '') + (' DIRC' if dos_flags & 4 else '')
properties['Boot Checksum'] = hex(struct.unpack('>I', self.data[4:8])[0])
root_block = struct.unpack('>I', self.data[8:12])[0]
properties['Root Block Pointer'] = root_block
boot_code_present = any(self.data[i] != 0 for i in range(12, 1024))
properties['Boot Code Present'] = boot_code_present
root_offset = root_block * 512
# Root block
properties['Type'] = struct.unpack('>I', self.data[root_offset:root_offset+4])[0]
properties['Header Key'] = struct.unpack('>I', self.data[root_offset+4:root_offset+8])[0]
properties['Hash Table Size'] = struct.unpack('>I', self.data[root_offset+12:root_offset+16])[0]
properties['First Data'] = struct.unpack('>I', self.data[root_offset+16:root_offset+20])[0]
properties['Root Checksum'] = hex(struct.unpack('>I', self.data[root_offset+20:root_offset+24])[0])
hash_table = []
for i in range(properties['Hash Table Size']):
ptr = struct.unpack('>I', self.data[root_offset+24 + i*4 : root_offset+28 + i*4])[0]
if ptr != 0:
hash_table.append(ptr)
properties['Hash Table'] = hash_table
properties['Bitmap Flag'] = struct.unpack('>i', self.data[root_offset+312:root_offset+316])[0]
bm_pages = []
for i in range(25):
ptr = struct.unpack('>I', self.data[root_offset+316 + i*4 : root_offset+320 + i*4])[0]
if ptr != 0:
bm_pages.append(ptr)
properties['Bitmap Pages'] = bm_pages
properties['Bitmap Extension'] = struct.unpack('>I', self.data[root_offset+416:root_offset+420])[0]
r_days, r_mins, r_ticks = struct.unpack('>III', self.data[root_offset+420:root_offset+432])
r_date = datetime.date(1978, 1, 1) + datetime.timedelta(days=r_days) + datetime.timedelta(minutes=r_mins) + datetime.timedelta(seconds=r_ticks / 50)
properties['Last Root Alteration Date'] = r_date.isoformat()
name_len = self.data[root_offset+432]
volume_name = self.data[root_offset+433:root_offset+433+name_len].decode()
properties['Volume Name Length'] = name_len
properties['Volume Name'] = volume_name
v_days, v_mins, v_ticks = struct.unpack('>III', self.data[root_offset+472:root_offset+484])
v_date = datetime.date(1978, 1, 1) + datetime.timedelta(days=v_days) + datetime.timedelta(minutes=v_mins) + datetime.timedelta(seconds=v_ticks / 50)
properties['Last Volume Alteration Date'] = v_date.isoformat()
c_days, c_mins, c_ticks = struct.unpack('>III', self.data[root_offset+484:root_offset+496])
c_date = datetime.date(1978, 1, 1) + datetime.timedelta(days=c_days) + datetime.timedelta(minutes=c_mins) + datetime.timedelta(seconds=c_ticks / 50)
properties['Creation Date'] = c_date.isoformat()
properties['First Directory Cache Block'] = struct.unpack('>I', self.data[root_offset+496:root_offset+500])[0]
properties['Secondary Type'] = struct.unpack('>I', self.data[root_offset+508:root_offset+512])[0]
# Bitmap block
bitmap_offset = bm_pages[0] * 512
properties['Bitmap Checksum'] = hex(struct.unpack('>I', self.data[bitmap_offset:bitmap_offset+4])[0])
free_blocks = 0
for i in range(127):
word = struct.unpack('>I', self.data[bitmap_offset+4 + i*4 : bitmap_offset+8 + i*4])[0]
free_blocks += bin(word).count('1')
properties['Free Blocks'] = free_blocks
properties['Allocated Blocks'] = 1760 - free_blocks - 2 # For DD floppy
return properties
def print_properties(self):
for k, v in self.properties.items():
print(f"{k}: {v}")
def write(self, filename):
with gzip.open(filename, 'wb', compresslevel=9) as f:
f.write(self.data)
# Example usage: adz = ADZFile('example.adz'); adz.print_properties(); adz.write('modified.adz')
- Java class for .ADZ file:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
public class ADZFile {
private byte[] data;
private Map<String, Object> properties;
public ADZFile(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPInputStream gis = new GZIPInputStream(fis)) {
byte[] buffer = new byte[1024];
int len;
while ((len = gis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
data = baos.toByteArray();
}
properties = parseProperties();
}
private Map<String, Object> parseProperties() {
Map<String, Object> props = new HashMap<>();
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
// Boot block
props.put("DOS Identifier", new String(data, 0, 3));
int dosFlags = data[3] & 0xFF;
props.put("DOS Flags", dosFlags);
String fsType = (dosFlags & 1) != 0 ? "FFS" : "OFS";
fsType += (dosFlags & 2) != 0 ? " INTL" : "";
fsType += (dosFlags & 4) != 0 ? " DIRC" : "";
props.put("File System Type", fsType);
props.put("Boot Checksum", Integer.toHexString(bb.getInt(4)));
int rootBlock = bb.getInt(8);
props.put("Root Block Pointer", rootBlock);
boolean bootCodePresent = false;
for (int i = 12; i < 1024; i++) {
if (data[i] != 0) {
bootCodePresent = true;
break;
}
}
props.put("Boot Code Present", bootCodePresent);
int rootOffset = rootBlock * 512;
// Root block
props.put("Type", bb.getInt(rootOffset));
props.put("Header Key", bb.getInt(rootOffset + 4));
int hashTableSize = bb.getInt(rootOffset + 12);
props.put("Hash Table Size", hashTableSize);
props.put("First Data", bb.getInt(rootOffset + 16));
props.put("Root Checksum", Integer.toHexString(bb.getInt(rootOffset + 20)));
List<Integer> hashTable = new ArrayList<>();
for (int i = 0; i < hashTableSize; i++) {
int ptr = bb.getInt(rootOffset + 24 + i * 4);
if (ptr != 0) hashTable.add(ptr);
}
props.put("Hash Table", hashTable);
props.put("Bitmap Flag", bb.getInt(rootOffset + 312));
List<Integer> bmPages = new ArrayList<>();
for (int i = 0; i < 25; i++) {
int ptr = bb.getInt(rootOffset + 316 + i * 4);
if (ptr != 0) bmPages.add(ptr);
}
props.put("Bitmap Pages", bmPages);
props.put("Bitmap Extension", bb.getInt(rootOffset + 416));
int rDays = bb.getInt(rootOffset + 420);
int rMins = bb.getInt(rootOffset + 424);
int rTicks = bb.getInt(rootOffset + 428);
Calendar rCal = Calendar.getInstance();
rCal.set(1978, 0, 1, 0, 0, 0);
rCal.add(Calendar.DATE, rDays);
rCal.add(Calendar.MINUTE, rMins);
rCal.add(Calendar.MILLISECOND, rTicks * 20);
props.put("Last Root Alteration Date", rCal.getTime());
int nameLen = data[rootOffset + 432] & 0xFF;
props.put("Volume Name Length", nameLen);
String volumeName = new String(data, rootOffset + 433, nameLen);
props.put("Volume Name", volumeName);
int vDays = bb.getInt(rootOffset + 472);
int vMins = bb.getInt(rootOffset + 476);
int vTicks = bb.getInt(rootOffset + 480);
Calendar vCal = Calendar.getInstance();
vCal.set(1978, 0, 1, 0, 0, 0);
vCal.add(Calendar.DATE, vDays);
vCal.add(Calendar.MINUTE, vMins);
vCal.add(Calendar.MILLISECOND, vTicks * 20);
props.put("Last Volume Alteration Date", vCal.getTime());
int cDays = bb.getInt(rootOffset + 484);
int cMins = bb.getInt(rootOffset + 488);
int cTicks = bb.getInt(rootOffset + 492);
Calendar cCal = Calendar.getInstance();
cCal.set(1978, 0, 1, 0, 0, 0);
cCal.add(Calendar.DATE, cDays);
cCal.add(Calendar.MINUTE, cMins);
cCal.add(Calendar.MILLISECOND, cTicks * 20);
props.put("Creation Date", cCal.getTime());
props.put("First Directory Cache Block", bb.getInt(rootOffset + 496));
props.put("Secondary Type", bb.getInt(rootOffset + 508));
// Bitmap block
int bitmapOffset = bmPages.get(0) * 512;
props.put("Bitmap Checksum", Integer.toHexString(bb.getInt(bitmapOffset)));
int freeBlocks = 0;
for (int i = 0; i < 127; i++) {
int word = bb.getInt(bitmapOffset + 4 + i * 4);
freeBlocks += Integer.bitCount(word);
}
props.put("Free Blocks", freeBlocks);
props.put("Allocated Blocks", 1760 - freeBlocks - 2);
return props;
}
public void printProperties() {
properties.forEach((k, v) -> System.out.println(k + ": " + v));
}
public void write(String filename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename);
GZIPOutputStream gos = new GZIPOutputStream(fos)) {
gos.write(data);
}
}
// Example: ADZFile adz = new ADZFile("example.adz"); adz.printProperties(); adz.write("modified.adz");
}
- JavaScript class for .ADZ file:
const pako = require('pako'); // Assume Node.js with pako installed, or include in browser
class ADZFile {
constructor(buffer) {
this.data = pako.inflate(new Uint8Array(buffer));
this.properties = this.parseProperties();
}
parseProperties() {
const properties = {};
const dv = new DataView(this.data.buffer);
// Boot block
properties['DOS Identifier'] = String.fromCharCode(dv.getUint8(0), dv.getUint8(1), dv.getUint8(2));
const dosFlags = dv.getUint8(3);
properties['DOS Flags'] = dosFlags;
properties['File System Type'] = (dosFlags & 1 ? 'FFS' : 'OFS') + (dosFlags & 2 ? ' INTL' : '') + (dosFlags & 4 ? ' DIRC' : '');
properties['Boot Checksum'] = dv.getUint32(4).toString(16);
const rootBlock = dv.getUint32(8);
properties['Root Block Pointer'] = rootBlock;
let bootCodePresent = false;
for (let i = 12; i < 1024; i++) {
if (dv.getUint8(i) !== 0) {
bootCodePresent = true;
break;
}
}
properties['Boot Code Present'] = bootCodePresent;
const rootOffset = rootBlock * 512;
// Root block
properties['Type'] = dv.getUint32(rootOffset);
properties['Header Key'] = dv.getUint32(rootOffset + 4);
const hashTableSize = dv.getUint32(rootOffset + 12);
properties['Hash Table Size'] = hashTableSize;
properties['First Data'] = dv.getUint32(rootOffset + 16);
properties['Root Checksum'] = dv.getUint32(rootOffset + 20).toString(16);
const hashTable = [];
for (let i = 0; i < hashTableSize; i++) {
const ptr = dv.getUint32(rootOffset + 24 + i * 4);
if (ptr !== 0) hashTable.push(ptr);
}
properties['Hash Table'] = hashTable;
properties['Bitmap Flag'] = dv.getInt32(rootOffset + 312);
const bmPages = [];
for (let i = 0; i < 25; i++) {
const ptr = dv.getUint32(rootOffset + 316 + i * 4);
if (ptr !== 0) bmPages.push(ptr);
}
properties['Bitmap Pages'] = bmPages;
properties['Bitmap Extension'] = dv.getUint32(rootOffset + 416);
const rDays = dv.getUint32(rootOffset + 420);
const rMins = dv.getUint32(rootOffset + 424);
const rTicks = dv.getUint32(rootOffset + 428);
const rDate = new Date(1978, 0, 1);
rDate.setDate(rDate.getDate() + rDays);
rDate.setMinutes(rDate.getMinutes() + rMins);
rDate.setMilliseconds(rDate.getMilliseconds() + rTicks * 20);
properties['Last Root Alteration Date'] = rDate.toISOString();
const nameLen = dv.getUint8(rootOffset + 432);
properties['Volume Name Length'] = nameLen;
let volumeName = '';
for (let i = 0; i < nameLen; i++) {
volumeName += String.fromCharCode(dv.getUint8(rootOffset + 433 + i));
}
properties['Volume Name'] = volumeName;
const vDays = dv.getUint32(rootOffset + 472);
const vMins = dv.getUint32(rootOffset + 476);
const vTicks = dv.getUint32(rootOffset + 480);
const vDate = new Date(1978, 0, 1);
vDate.setDate(vDate.getDate() + vDays);
vDate.setMinutes(vDate.getMinutes() + vMins);
vDate.setMilliseconds(vDate.getMilliseconds() + vTicks * 20);
properties['Last Volume Alteration Date'] = vDate.toISOString();
const cDays = dv.getUint32(rootOffset + 484);
const cMins = dv.getUint32(rootOffset + 488);
const cTicks = dv.getUint32(rootOffset + 492);
const cDate = new Date(1978, 0, 1);
cDate.setDate(cDate.getDate() + cDays);
cDate.setMinutes(cDate.getMinutes() + cMins);
cDate.setMilliseconds(cDate.getMilliseconds() + cTicks * 20);
properties['Creation Date'] = cDate.toISOString();
properties['First Directory Cache Block'] = dv.getUint32(rootOffset + 496);
properties['Secondary Type'] = dv.getUint32(rootOffset + 508);
// Bitmap block
const bitmapOffset = bmPages[0] * 512;
properties['Bitmap Checksum'] = dv.getUint32(bitmapOffset).toString(16);
let freeBlocks = 0;
for (let i = 0; i < 127; i++) {
let word = dv.getUint32(bitmapOffset + 4 + i * 4);
while (word) {
freeBlocks += word & 1;
word >>>= 1;
}
}
properties['Free Blocks'] = freeBlocks;
properties['Allocated Blocks'] = 1760 - freeBlocks - 2;
return properties;
}
printProperties() {
console.log(this.properties);
}
write() {
return pako.deflate(this.data);
}
}
// Example (Node.js): const fs = require('fs'); const buffer = fs.readFileSync('example.adz'); const adz = new ADZFile(buffer); adz.printProperties(); fs.writeFileSync('modified.adz', adz.write());
- C "class" (struct with functions) for .ADZ file:
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>
#include <string.h>
#include <time.h>
typedef struct {
unsigned char *data;
size_t size;
// Properties map simulation (array of key-value)
char *keys[50];
char *values[50];
int prop_count;
} ADZFile;
void adz_init(ADZFile *adz, const char *filename) {
FILE *f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
size_t compressed_size = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char *compressed = malloc(compressed_size);
fread(compressed, 1, compressed_size, f);
fclose(f);
uLongf dest_len = 901120; // Typical ADF size
adz->data = malloc(dest_len);
if (uncompress(adz->data, &dest_len, compressed, compressed_size) != Z_OK) {
fprintf(stderr, "Decompression failed\n");
free(compressed);
return;
}
adz->size = dest_len;
free(compressed);
adz->prop_count = 0;
// Parse properties
unsigned int *dv = (unsigned int *)adz->data;
char buf[256];
// Boot block
snprintf(buf, 256, "%c%c%c", adz->data[0], adz->data[1], adz->data[2]);
adz->keys[adz->prop_count] = strdup("DOS Identifier");
adz->values[adz->prop_count++] = strdup(buf);
unsigned char dos_flags = adz->data[3];
snprintf(buf, 256, "%u", dos_flags);
adz->keys[adz->prop_count] = strdup("DOS Flags");
adz->values[adz->prop_count++] = strdup(buf);
char fs_type[32] = "";
strcat(fs_type, (dos_flags & 1) ? "FFS" : "OFS");
if (dos_flags & 2) strcat(fs_type, " INTL");
if (dos_flags & 4) strcat(fs_type, " DIRC");
adz->keys[adz->prop_count] = strdup("File System Type");
adz->values[adz->prop_count++] = strdup(fs_type);
snprintf(buf, 256, "%08x", dv[1]);
adz->keys[adz->prop_count] = strdup("Boot Checksum");
adz->values[adz->prop_count++] = strdup(buf);
unsigned int root_block = dv[2];
snprintf(buf, 256, "%u", root_block);
adz->keys[adz->prop_count] = strdup("Root Block Pointer");
adz->values[adz->prop_count++] = strdup(buf);
int boot_code_present = 0;
for (int i = 12; i < 1024; i++) {
if (adz->data[i] != 0) {
boot_code_present = 1;
break;
}
}
snprintf(buf, 256, "%s", boot_code_present ? "true" : "false");
adz->keys[adz->prop_count] = strdup("Boot Code Present");
adz->values[adz->prop_count++] = strdup(buf);
unsigned int *root_dv = (unsigned int *)(adz->data + root_block * 512);
snprintf(buf, 256, "%u", root_dv[0]);
adz->keys[adz->prop_count] = strdup("Type");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%u", root_dv[1]);
adz->keys[adz->prop_count] = strdup("Header Key");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%u", root_dv[3]);
adz->keys[adz->prop_count] = strdup("Hash Table Size");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%u", root_dv[4]);
adz->keys[adz->prop_count] = strdup("First Data");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%08x", root_dv[5]);
adz->keys[adz->prop_count] = strdup("Root Checksum");
adz->values[adz->prop_count++] = strdup(buf);
char ht_buf[1024] = "";
for (int i = 0; i < root_dv[3]; i++) {
if (root_dv[6 + i] != 0) {
char temp[16];
snprintf(temp, 16, "%u ", root_dv[6 + i]);
strcat(ht_buf, temp);
}
}
adz->keys[adz->prop_count] = strdup("Hash Table");
adz->values[adz->prop_count++] = strdup(ht_buf);
int bitmap_flag = (int)root_dv[78 -1]; // Adjust for index
snprintf(buf, 256, "%d", bitmap_flag);
adz->keys[adz->prop_count] = strdup("Bitmap Flag");
adz->values[adz->prop_count++] = strdup(buf);
char bm_pages_buf[1024] = "";
unsigned int bm_pages[25];
for (int i = 0; i < 25; i++) {
bm_pages[i] = root_dv[78 + i];
if (bm_pages[i] != 0) {
char temp[16];
snprintf(temp, 16, "%u ", bm_pages[i]);
strcat(bm_pages_buf, temp);
}
}
adz->keys[adz->prop_count] = strdup("Bitmap Pages");
adz->values[adz->prop_count++] = strdup(bm_pages_buf);
snprintf(buf, 256, "%u", root_dv[103]);
adz->keys[adz->prop_count] = strdup("Bitmap Extension");
adz->values[adz->prop_count++] = strdup(buf);
unsigned int r_days = root_dv[105];
unsigned int r_mins = root_dv[106];
unsigned int r_ticks = root_dv[107];
struct tm tm = {0};
tm.tm_year = 78;
tm.tm_mon = 0;
tm.tm_mday = 1;
time_t r_time = mktime(&tm) + r_days * 86400 + r_mins * 60 + r_ticks / 50;
strftime(buf, 64, "%Y-%m-%d", localtime(&r_time));
adz->keys[adz->prop_count] = strdup("Last Root Alteration Date");
adz->values[adz->prop_count++] = strdup(buf);
unsigned char name_len = adz->data[root_block * 512 + 432];
snprintf(buf, 256, "%u", name_len);
adz->keys[adz->prop_count] = strdup("Volume Name Length");
adz->values[adz->prop_count++] = strdup(buf);
char volume_name[31];
memcpy(volume_name, adz->data + root_block * 512 + 433, name_len);
volume_name[name_len] = '\0';
adz->keys[adz->prop_count] = strdup("Volume Name");
adz->values[adz->prop_count++] = strdup(volume_name);
unsigned int v_days = root_dv[118];
unsigned int v_mins = root_dv[119];
unsigned int v_ticks = root_dv[120];
time_t v_time = mktime(&tm) + v_days * 86400 + v_mins * 60 + v_ticks / 50;
strftime(buf, 64, "%Y-%m-%d", localtime(&v_time));
adz->keys[adz->prop_count] = strdup("Last Volume Alteration Date");
adz->values[adz->prop_count++] = strdup(buf);
unsigned int c_days = root_dv[121];
unsigned int c_mins = root_dv[122];
unsigned int c_ticks = root_dv[123];
time_t c_time = mktime(&tm) + c_days * 86400 + c_mins * 60 + c_ticks / 50;
strftime(buf, 64, "%Y-%m-%d", localtime(&c_time));
adz->keys[adz->prop_count] = strdup("Creation Date");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%u", root_dv[124]);
adz->keys[adz->prop_count] = strdup("First Directory Cache Block");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%u", root_dv[127]);
adz->keys[adz->prop_count] = strdup("Secondary Type");
adz->values[adz->prop_count++] = strdup(buf);
// Bitmap block
unsigned int bitmap_block = bm_pages[0];
unsigned int *bitmap_dv = (unsigned int *)(adz->data + bitmap_block * 512);
snprintf(buf, 256, "%08x", bitmap_dv[0]);
adz->keys[adz->prop_count] = strdup("Bitmap Checksum");
adz->values[adz->prop_count++] = strdup(buf);
int free_blocks = 0;
for (int i = 1; i < 128; i++) { // 127 words
unsigned int word = bitmap_dv[i];
while (word) {
free_blocks += word & 1;
word >>= 1;
}
}
snprintf(buf, 256, "%d", free_blocks);
adz->keys[adz->prop_count] = strdup("Free Blocks");
adz->values[adz->prop_count++] = strdup(buf);
snprintf(buf, 256, "%d", 1760 - free_blocks - 2);
adz->keys[adz->prop_count] = strdup("Allocated Blocks");
adz->values[adz->prop_count++] = strdup(buf);
}
void adz_print_properties(ADZFile *adz) {
for (int i = 0; i < adz->prop_count; i++) {
printf("%s: %s\n", adz->keys[i], adz->values[i]);
}
}
void adz_write(ADZFile *adz, const char *filename) {
uLongf compressed_len = compressBound(adz->size);
unsigned char *compressed = malloc(compressed_len);
if (compress(compressed, &compressed_len, adz->data, adz->size) != Z_OK) {
fprintf(stderr, "Compression failed\n");
free(compressed);
return;
}
FILE *f = fopen(filename, "wb");
fwrite(compressed, 1, compressed_len, f);
fclose(f);
free(compressed);
}
void adz_free(ADZFile *adz) {
free(adz->data);
for (int i = 0; i < adz->prop_count; i++) {
free(adz->keys[i]);
free(adz->values[i]);
}
}
// Example: ADZFile adz; adz_init(&adz, "example.adz"); adz_print_properties(&adz); adz_write(&adz, "modified.adz"); adz_free(&adz);