Amiga RDB disk partition unpacking utility

2007/01/11

Categories: GeekStuff

I'm too lazy to publish this properly or clean it up.

There are a number of programs and utilities and things out there, even operating system mount utilities, to access or mount Amiga disk images.

That's great, but they are really utilities to access Amiga partition images. If you have an actual physical hard drive (not a floppy) containing multiple partitions, it's not immediately trivial to get the partitions extracted so that other utilities (such as adflib/unadf) can read them.

Enter "amiga\_rdb.c". This trivial program, written in 100% generic ISO C, should be able to read a dumped image of a raw Amiga hard drive, and extract from it all the named partitions on it, in ADF format (which is, in fact, just the raw bytes of those partitions). The header it's reading is called the "Rigid Disk Block" or something similar, or "RDB". NetBSD/amiga interprets them natively, which is neat, but the entire point is that I don't have any Amigas handy right now.

I have not tested this on big-endian systems, but ntohl is pretty commonplace on Unix-like systems, and should translate correctly on either sort; if you don't have it, just assume it forcibly interprets a big-endian 4-byte long in big-endian format.

Sorry for the lack of real formatting. I just want to make sure the next guy who needs to do this doesn't have to spend the thirty minutes it took me. For future reference, the IDE standard sucks badly. It took me longer to find a machine that was physically capable of booting with an early-90s IDE disk attached to it than it did to do any other part of this. SCSI disks that old, if they spin up at all, Just Work.

Special thanks to Dave, whose hard drives I needed to read.

Since this seems to be acting up: [amiga\_rdb.c](http://www.seebs.net/log/file_download/1/amiga_rdb.c)
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

/* http://lclevy.club.fr/adflib/adf_info.html */
/* Copyright 2006 Peter Seebach.  No warranty of any sort, but good
 * luck!
 */

int
part(FILE *f) {
    off_t here = ftell(f);
    char text[32];
    int ret;

    if (fread(text, 1, 4, f) != 4) {
        fprintf(stderr, "rdsk: can't read partition header (%s)\n", strerror(errno));
        exit(1);
    }
    text[4] = '\0';
    if (!strcmp(text, "PART")) {
        ret = 1;
        unsigned long bsize, heads, secs, tracks, low, high;
        unsigned char len;

        fseek(f, here + 0x24, SEEK_SET);
        fread(&len, sizeof(char), 1, f);
        fread(text, sizeof(char), 31, f);
        text[31] = '\0';
        printf("partition: %.*s\n", len, text);
        if (len >= 0 && len < 32)
            text[len] = '\0';
        else
            text[31] = '\0';
        fseek(f, here + 0x84, SEEK_SET);
        if (fread(&bsize, sizeof(long), 1, f) != 1) {
            fprintf(stderr, "rdsk: can't read block size (%s)\n", strerror(errno));
        }
        bsize = 4 * ntohl(bsize);
        fseek(f, here + 0x8c, SEEK_SET);
        if (fread(&heads, sizeof(long), 1, f) != 1) {
            fprintf(stderr, "rdsk: can't read heads (%s)\n", strerror(errno));
        }
        heads = ntohl(heads);
        fseek(f, here + 0x90, SEEK_SET);
        if (fread(&secs, sizeof(long), 1, f) != 1) {
            fprintf(stderr, "rdsk: can't read blocks/track (%s)\n", strerror(errno));
        }
        secs = ntohl(secs);
        fseek(f, here + 0x94, SEEK_SET);
        if (fread(&tracks, sizeof(long), 1, f) != 1) {
            fprintf(stderr, "rdsk: can't read tracks/cyl (%s)\n", strerror(errno));
        }
        tracks = ntohl(tracks);
        fseek(f, here + 0xa4, SEEK_SET);
        if (fread(&low, sizeof(long), 1, f) != 1) {
            fprintf(stderr, "rdsk: can't read low cyl (%s)\n", strerror(errno));
        }
        low = ntohl(low);
        fseek(f, here + 0xa8, SEEK_SET);
        if (fread(&high, sizeof(long), 1, f) != 1) {
            fprintf(stderr, "rdsk: can't read high cyl (%s)\n", strerror(errno));
        }
        high = ntohl(high);
        printf("%ld-byte blocks/cyl: %ld\nlow: %ld\nhigh: %ld\n",
            bsize, heads * secs * tracks, low, high);
        {
            int i;
            char name[64];
            FILE *out;
            unsigned char *block = malloc(bsize * secs * heads * tracks);

            sprintf(name, "%s.adf", text);
            out = fopen(name, "wb");
            printf("saving partition %s, %d cylinders of %ld bytes\n", name,
                (int) high - low, bsize * heads * secs * tracks);
            for (i = low; i <= high; ++i) {
                printf(".");
                fseek(f, i * bsize * secs * heads * tracks, SEEK_SET);
                fread(block, bsize, secs * heads * tracks, f);
                fwrite(block, bsize, secs * heads * tracks, out);
            }
            printf("\n");
            fclose(out);
            free(block);
        }
    } else {
        printf("unknown partition type: %s\n", text);
        ret = 0;
    }
    fseek(f, here, SEEK_SET);
    return ret;
}

int
main(int argc, char **argv) {
    FILE *disk;
    long b;
    long bsize;
    long next;
    char text[5];
    text[4] = 0;

    if (argv[1]) {
        disk = fopen(argv[1], "r");
        if (!disk) {
            fprintf(stderr, "rdsk: can't open file (%s)\n", strerror(errno));
            exit(1);
        }
    } else {
        fprintf(stderr, "usage: rdsk file\n");
        exit(1);
    }
    fseek(disk, 0x0, SEEK_SET);
    if (fread(text, 1, 4, disk) != 4) {
        fprintf(stderr, "rdsk: can't read disk header (%s)\n", strerror(errno));
        exit(1);
    }
    printf("disk header: %s\n", text);
    fseek(disk, 0x10, SEEK_SET);
    if (fread(&bsize, sizeof(long), 1, disk) != 1) {
        fprintf(stderr, "rdsk: can't read blocksize (%s)\n", strerror(errno));
        exit(1);
    }
    bsize = ntohl(bsize);
    printf("block size is %ld\n", bsize);
    fseek(disk, 0x1c, SEEK_SET);
    if (fread(&b, sizeof(long), 1, disk) != 1) {
        fprintf(stderr, "rdsk: can't read partition blockno (%s)\n", strerror(errno));
        exit(1);
    }
    b = ntohl(b);
    printf("partition should be at block %ld\n", b);
    fseek(disk, b * bsize, SEEK_SET);

    while (part(disk)) {
        fseek(disk, 0x10, SEEK_CUR);
        if (fread(&next, sizeof(long), 1, disk) != 1) {
            fprintf(stderr, "rdsk: can't read next blockno (%s)\n", strerror(errno));
            exit(1);
        }
        next = ntohl(next);
        printf("next partition: %ld\n", next);
        if (next != -1)
            fseek(disk, next * bsize, SEEK_SET);
        else
            break;
    }
    return 0;
}

Comments [archived]


From: Swart
Date: 2007-01-11 16:41:18 -0600

Sweet code! I didn’t think there were many of us non-OOP people left.


What argument do you pass for disk?


From: seebs
Date: 2007-01-12 14:09:54 -0600

“disk” would be the name of a file or device. If you’ve got the disk physically attached, something like “/dev/sda1” or “/dev/sd0c”. However, DO NOT DO THAT WITH OLD DISKS. dd directly from them to a modern disk, one pass, straight through, to minimize seeking and opportunities for failure; then run on the resulting file. Thus, my actual calls were like:


dd if=/dev/wd0c of=disk1 bs=16k


(walks away, fixes washing machine which has been acting up)


./rdisk disk1


FWIW, I’ll write in OO languages, but if I’m in a hurry, and I need to deal with raw bytes, my first choice is nearly always C. Habit, really.


From: Swart
Date: 2007-01-15 04:16:18 -0600

Okay, I didn’t realise the Amiga HDDs were IDE (I should’ve read the blog more carefully).


Now, it be be really cool if you wrote the block driver for the Amiga partition and mounted it.


From: SiliconTrip
Date: 2012-03-07 22:33:56 -0600

Do you have a link for this code? The HTML formatter has made it uncompilable with it’s smart quotes.


From: seebs
Date: 2012-03-07 22:51:06 -0600

No, but I added one (to a slightly cleaned up version).


From: Stilvoid
Date: 2013-01-17 08:14:10 -0600

Thanks a load for this! I was having real trouble getting my kernel to read the partition table on my old amiga hd; this worked perfectly :)