├── .gitignore ├── README.md ├── src ├── fileio.h ├── copy_all.h ├── file.h ├── affs.h ├── datablock.h ├── fileio.c ├── bootblock.h ├── file_ext.h ├── affs.c ├── fileheader.h ├── rootblock.h ├── find_root_block.c ├── partition.h ├── file_ext.c ├── directory.h ├── bootblock.c ├── datablock.c ├── copy_all.c ├── find_partitions.c ├── fileheader.c ├── file.c ├── rootblock.c ├── directory.c ├── parse_hunk.c ├── partition.c └── amiga_recovery.c └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | amiga_recovery 3 | *.swp 4 | find_root_block 5 | find_partitions 6 | parse_hunk 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | amiga_recovery 2 | ============== 3 | 4 | View and copy files from Amiga file system images. 5 | 6 | Website: [http://www.mikekohn.net/file_formats/amiga_recovery.php](http://www.mikekohn.net/file_formats/amiga_recovery.php) 7 | 8 | Building 9 | ======== 10 | 11 | To build amiga_recovery (and optionally the tools find_rootblock and 12 | parse_hunk): 13 | 14 | make 15 | make tools 16 | 17 | -------------------------------------------------------------------------------- /src/fileio.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk imag. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef FILEIO_H 13 | #define FILEIO_H 14 | 15 | uint32_t read_int(FILE *in); 16 | uint32_t read_short(FILE *in); 17 | int read_chars(FILE *in, uint8_t *s, int count); 18 | 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /src/copy_all.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef COPY_ALL_H 13 | #define COPY_ALL_H 14 | 15 | int copy_all( 16 | FILE *in, 17 | struct _amiga_bootblock *bootblock, 18 | struct _pwd *pwd, 19 | char *path); 20 | 21 | #endif 22 | 23 | -------------------------------------------------------------------------------- /src/file.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef FILE_H 13 | #define FILE_H 14 | 15 | #include "affs.h" 16 | 17 | void copy_file( 18 | FILE *in, 19 | struct _amiga_bootblock *bootblock, 20 | struct _pwd *pwd, 21 | char *filename, 22 | FILE *out); 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | DEBUG=-DDEBUG -g 3 | CFLAGS=-O2 -Wall $(DEBUG) 4 | CC=gcc 5 | #CC=i686-mingw32-gcc 6 | 7 | default: 8 | @+make -C build 9 | 10 | tools: 11 | $(CC) -o find_root_block src/find_root_block.c \ 12 | build/fileio.o build/rootblock.o \ 13 | $(CFLAGS) -Isrc 14 | $(CC) -o find_partitions src/find_partitions.c \ 15 | build/fileio.o build/partition.o \ 16 | $(CFLAGS) -Isrc 17 | $(CC) -o parse_hunk src/parse_hunk.c \ 18 | $(CFLAGS) -Isrc 19 | clean: 20 | @rm -f build/*.o 21 | @rm -f amiga_recovery amiga_recovery.exe 22 | @rm -f find_root_block find_partitions parse_hunk 23 | 24 | -------------------------------------------------------------------------------- /src/affs.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef AFFS_H 13 | #define AFFS_H 14 | 15 | #include "bootblock.h" 16 | #include "directory.h" 17 | #include "file.h" 18 | #include "fileheader.h" 19 | #include "partition.h" 20 | #include "rootblock.h" 21 | 22 | #define ST_FILE -3 23 | #define ST_ROOT 1 24 | #define ST_USERDIR 2 25 | #define ST_SOFTLINK 3 26 | #define ST_LINKDIR 4 27 | 28 | uint32_t hash_name(unsigned char *name); 29 | 30 | int get_sec_type( 31 | FILE *in, 32 | struct _amiga_bootblock *bootblock, 33 | struct _amiga_partition *partition, 34 | uint32_t block); 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /src/datablock.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef DATABLOCK_H 13 | #define DATABLOCK_H 14 | 15 | #include "affs.h" 16 | 17 | struct _amiga_datablock 18 | { 19 | uint32_t type; 20 | uint32_t header_key; 21 | uint32_t seq_num; 22 | uint32_t data_size; 23 | uint32_t next_data; 24 | uint32_t checksum; 25 | uint8_t data[BSIZE - 24]; 26 | }; 27 | 28 | void read_datablock( 29 | FILE *in, 30 | struct _amiga_bootblock *bootblock, 31 | struct _amiga_partition *partition, 32 | struct _amiga_datablock *datablock, 33 | uint32_t block); 34 | 35 | void print_datablock(struct _amiga_datablock *datablock); 36 | 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /src/fileio.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | uint32_t read_int(FILE *in) 17 | { 18 | uint32_t c; 19 | 20 | c = getc(in) << 24; 21 | c = c | (getc(in) << 16); 22 | c = c | (getc(in) << 8); 23 | c = c | getc(in); 24 | 25 | return c; 26 | } 27 | 28 | uint32_t read_short(FILE *in) 29 | { 30 | uint32_t c; 31 | 32 | c = getc(in) << 8; 33 | c = c | getc(in); 34 | 35 | return c; 36 | } 37 | 38 | int read_chars(FILE *in, uint8_t *s, int count) 39 | { 40 | int t; 41 | 42 | for (t = 0; t < count; t++) 43 | { 44 | s[t] = getc(in); 45 | } 46 | 47 | s[t] = 0; 48 | 49 | return 0; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/bootblock.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef BOOTBLOCK_H 13 | #define BOOTBLOCK_H 14 | 15 | #include 16 | 17 | // This really should come from the rootblock.. but I'm lazy right now 18 | #define BSIZE 512 19 | 20 | struct _amiga_bootblock 21 | { 22 | unsigned char magic[4]; 23 | uint32_t size; 24 | int checksum; 25 | uint32_t scsihost; 26 | uint32_t blksz; 27 | uint32_t flags; 28 | uint32_t badblcklst; 29 | uint32_t partitionlst; 30 | uint32_t fslst; 31 | 32 | // Offset on the disk to the bootblock 33 | long offset; 34 | }; 35 | 36 | int read_bootblock(FILE *in, struct _amiga_bootblock *bootblock); 37 | void print_bootblock(struct _amiga_bootblock *bootblock); 38 | 39 | #endif 40 | 41 | -------------------------------------------------------------------------------- /src/file_ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef FILE_EXT_H 13 | #define FILE_EXT_H 14 | 15 | #include "affs.h" 16 | 17 | struct _amiga_file_ext 18 | { 19 | uint32_t type; 20 | uint32_t header_key; 21 | uint32_t high_seq; 22 | uint32_t unused1; 23 | uint32_t unused2; 24 | uint32_t checksum; 25 | uint32_t datablocks[BSIZE/4-56]; 26 | uint32_t info[46]; 27 | uint32_t unused3; 28 | uint32_t parent; 29 | uint32_t extension; 30 | uint32_t sec_type; 31 | }; 32 | 33 | void read_file_ext( 34 | FILE * in, 35 | struct _amiga_bootblock *bootblock, 36 | struct _amiga_partition *partition, 37 | struct _amiga_file_ext *file_ext, 38 | uint32_t block); 39 | 40 | void print_file_ext(struct _amiga_file_ext *file_ext); 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /src/affs.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "affs.h" 19 | #include "fileio.h" 20 | 21 | /* hash_name function copied from http://lclevy.free.fr/adflib/adf_info.html */ 22 | uint32_t hash_name(unsigned char *name) 23 | { 24 | uint32_t hash, l; /* sizeof(int)>=2 */ 25 | int i; 26 | 27 | l = hash = strlen((char *)name); 28 | 29 | for(i = 0; i < l; i++) 30 | { 31 | hash = hash * 13; 32 | hash = hash + toupper(name[i]); /* not case sensitive */ 33 | hash = hash & 0x7ff; 34 | } 35 | 36 | /* 0 < hash < 71 in the case of 512 byte blocks */ 37 | hash = hash % ((BSIZE / 4) - 56); 38 | 39 | return(hash); 40 | } 41 | 42 | int get_sec_type( 43 | FILE *in, 44 | struct _amiga_bootblock *bootblock, 45 | struct _amiga_partition *partition, 46 | uint32_t block) 47 | { 48 | fseek(in, partition->start + (block * bootblock->blksz + (bootblock->blksz - 4)), SEEK_SET); 49 | 50 | return read_int(in); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/fileheader.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef FILEHEADER_H 13 | #define FILEHEADER_H 14 | 15 | #include 16 | 17 | #include "bootblock.h" 18 | #include "partition.h" 19 | #include "rootblock.h" 20 | 21 | struct _amiga_fileheader 22 | { 23 | uint32_t type; 24 | uint32_t header_key; 25 | uint32_t high_seq; 26 | uint32_t data_size; 27 | uint32_t first_data; 28 | uint32_t checksum; 29 | uint32_t datablocks[BSIZE/4-56]; //FIXME - wrong size? 30 | uint32_t unused1; 31 | uint16_t uid; 32 | uint16_t gid; 33 | uint32_t protect; 34 | uint32_t byte_size; 35 | unsigned char comment[80]; // first char is len 36 | unsigned char unused2[12]; 37 | uint32_t days; 38 | uint32_t mins; 39 | uint32_t ticks; 40 | unsigned char filename[32]; // first char is len, last char is unused 41 | uint32_t unused3; 42 | uint32_t read_entry; 43 | uint32_t next_link; 44 | uint32_t unused4[5]; 45 | uint32_t hash_chain; 46 | uint32_t parent; 47 | uint32_t extension; 48 | uint32_t sec_type; 49 | }; 50 | 51 | void read_fileheader(FILE * in, struct _amiga_bootblock *bootblock, struct _amiga_partition *partition, struct _amiga_fileheader *fileheader, uint32_t block); 52 | void print_fileheader(struct _amiga_fileheader *fileheader); 53 | 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /src/rootblock.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef ROOTBLOCK_H 13 | #define ROOTBLOCK_H 14 | 15 | #include "bootblock.h" 16 | #include "partition.h" 17 | 18 | #define UINT32(a,i) ((a[i+0] << 24) + (a[i+1] << 16) + (a[i+2] << 8) + a[i+3]) 19 | 20 | struct _amiga_rootblock 21 | { 22 | uint32_t type; 23 | uint32_t header_key; 24 | uint32_t high_seq; 25 | uint32_t hash_table_size; 26 | uint32_t first_size; 27 | uint32_t checksum; 28 | uint32_t hash_table[BSIZE / 4 - 56]; 29 | uint32_t bm_flag; 30 | uint32_t bm_pages[25]; 31 | uint32_t bm_ext; 32 | uint32_t r_days; 33 | uint32_t r_mins; 34 | uint32_t r_ticks; 35 | uint8_t diskname[32]; // name_len + 30 possible + 1 unused 36 | uint32_t unused1[2]; 37 | uint32_t v_days; 38 | uint32_t v_min; 39 | uint32_t v_ticks; 40 | uint32_t c_days; 41 | uint32_t c_min; 42 | uint32_t c_ticks; 43 | uint32_t next_hash; 44 | uint32_t parent_dir; 45 | uint32_t extension; 46 | uint32_t sec_type; 47 | 48 | // DEBUG STUFF 49 | uint32_t partition_offset; 50 | uint32_t disk_offset; 51 | }; 52 | 53 | int read_rootblock_data(FILE *in, struct _amiga_rootblock *rootblock); 54 | 55 | void read_rootblock( 56 | FILE *in, 57 | struct _amiga_bootblock *bootblock, 58 | struct _amiga_partition *partition, 59 | struct _amiga_rootblock *rootblock); 60 | 61 | void print_rootblock(struct _amiga_rootblock *rootblock); 62 | 63 | uint32_t find_root_block(FILE *in, struct _amiga_rootblock *rootblock); 64 | 65 | #endif 66 | 67 | -------------------------------------------------------------------------------- /src/find_root_block.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | find_root_block - Scan a disk image for a root block. 11 | 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "rootblock.h" 19 | 20 | #define UINT32(a,i) ((a[i+0] << 24) + (a[i+1] << 16) + (a[i+2] << 8) + a[i+3]) 21 | 22 | int main(int argc, char *argv[]) 23 | { 24 | FILE *in; 25 | struct _amiga_rootblock rootblock; 26 | uint8_t buffer[512]; 27 | long marker; 28 | int len; 29 | 30 | if (argc != 2) 31 | { 32 | printf("Usage: %s \n", argv[0]); 33 | exit(0); 34 | } 35 | 36 | in = fopen(argv[1], "rb"); 37 | 38 | if (in == NULL) 39 | { 40 | printf("Cannot open file %s for reading.\n", argv[1]); 41 | exit(1); 42 | } 43 | 44 | while (1) 45 | { 46 | len = fread(buffer, 512, 1, in); 47 | if (len == 0) { break; } 48 | 49 | if (UINT32(buffer, 0) == 0x02 && 50 | UINT32(buffer, 4) == 0x00 && 51 | UINT32(buffer, 8) == 0x00 && 52 | UINT32(buffer, 16) == 0x00) 53 | { 54 | 55 | marker = ftell(in); 56 | fseek(in, marker - 512, SEEK_SET); 57 | 58 | if (read_rootblock_data(in, &rootblock) == 0 && 59 | rootblock.hash_table_size != 0) 60 | { 61 | printf("Possible Rootblock at %ld block=%d\n", marker, (int)marker / 512); 62 | print_rootblock(&rootblock); 63 | } 64 | 65 | fseek(in, marker, SEEK_SET); 66 | } 67 | } 68 | 69 | fclose(in); 70 | 71 | return 0; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/partition.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef PARTITION_H 13 | #define PARTITION_H 14 | 15 | #include 16 | 17 | #include "bootblock.h" 18 | 19 | struct _amiga_partition 20 | { 21 | uint8_t magic[4]; 22 | int size; 23 | int checksum; 24 | uint32_t scsihost; 25 | uint32_t next; 26 | uint32_t flags; 27 | uint32_t unused1[2]; 28 | uint32_t devflags; 29 | unsigned char name[32]; 30 | uint32_t unused2[15]; 31 | 32 | uint32_t table_size; 33 | uint32_t size_block; 34 | uint32_t sec_org; 35 | uint32_t heads; 36 | uint32_t sectors_per_block; 37 | uint32_t blocks_per_track; 38 | uint32_t num_reserved; 39 | uint32_t pre_alloc; 40 | uint32_t interleave; 41 | uint32_t start_cyl; 42 | uint32_t end_cyl; 43 | uint32_t num_buffers; 44 | uint32_t buff_type; 45 | uint32_t max_transfer; 46 | uint32_t mask; 47 | int boot_priority; 48 | uint8_t type[4]; 49 | uint32_t baud; 50 | uint32_t control; 51 | uint32_t bootblocks; 52 | 53 | uint64_t start; 54 | uint64_t end; 55 | uint64_t size_in_bytes; 56 | }; 57 | 58 | int read_partition( 59 | FILE *in, 60 | struct _amiga_bootblock *bootblock, 61 | struct _amiga_partition *partition); 62 | 63 | void print_partition(struct _amiga_partition *partition); 64 | 65 | void print_partition_list(FILE *in, struct _amiga_bootblock *bootblock); 66 | 67 | void show_partitions(FILE *in, struct _amiga_bootblock *bootblock); 68 | 69 | int dump_partition( 70 | FILE *in, 71 | struct _amiga_bootblock *bootblock, 72 | int num, 73 | const char *name); 74 | 75 | int read_partition_num( 76 | FILE *in, 77 | struct _amiga_bootblock *bootblock, 78 | struct _amiga_partition *partition, 79 | int num); 80 | 81 | int get_partition_num(FILE *in, struct _amiga_bootblock *bootblock, char *name); 82 | 83 | #endif 84 | 85 | -------------------------------------------------------------------------------- /src/file_ext.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "file_ext.h" 18 | #include "fileio.h" 19 | 20 | void read_file_ext( 21 | FILE * in, 22 | struct _amiga_bootblock *bootblock, 23 | struct _amiga_partition *partition, 24 | struct _amiga_file_ext *file_ext, 25 | uint32_t block) 26 | { 27 | int t; 28 | 29 | fseek(in, partition->start + (block * bootblock->blksz), SEEK_SET); 30 | 31 | file_ext->type = read_int(in); 32 | file_ext->header_key = read_int(in); 33 | file_ext->high_seq = read_int(in); 34 | //uint32_t unused1; 35 | //uint32_t unused2; 36 | fseek(in, 2 * 4, SEEK_CUR); 37 | file_ext->checksum = read_int(in); 38 | //uint32_t datablocks[BSIZE/4-56] 39 | for (t = 0; t<(512 / 4 - 56); t++) 40 | { 41 | file_ext->datablocks[t] = read_int(in); 42 | } 43 | //uint32_t info[46]; 44 | fseek(in, 46 * 4, SEEK_CUR); 45 | file_ext->unused3 = read_int(in); 46 | file_ext->parent = read_int(in); 47 | file_ext->extension = read_int(in); 48 | file_ext->sec_type = read_int(in); 49 | } 50 | 51 | void print_file_ext(struct _amiga_file_ext *file_ext) 52 | { 53 | int t; 54 | 55 | printf("================== File Extension ===================\n"); 56 | printf(" type: %d\n", file_ext->type); 57 | printf(" header_key: %d\n", file_ext->header_key); 58 | printf(" high_seq: %d\n", file_ext->high_seq); 59 | printf(" checksum: %d\n", file_ext->checksum); 60 | printf(" datablocks: "); 61 | for (t = 0; t < (512 / 4 - 56); t++) 62 | { 63 | if (t != 0) { printf(" "); } 64 | printf("%d", file_ext->datablocks[t]); 65 | } 66 | printf("\n"); 67 | printf(" parent: %d\n", file_ext->parent); 68 | printf(" extension: %d\n", file_ext->extension); 69 | printf(" sec_type: %d\n", file_ext->sec_type); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/directory.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #ifndef DIRECTORY_H 13 | #define DIRECTORY_H 14 | 15 | #include 16 | 17 | #include "bootblock.h" 18 | #include "fileheader.h" 19 | #include "partition.h" 20 | #include "rootblock.h" 21 | 22 | struct _amiga_directory 23 | { 24 | uint32_t type; 25 | uint32_t header_key; 26 | uint32_t unused1[3]; 27 | uint32_t checksum; 28 | uint32_t hash_table[BSIZE / 4 - 56]; 29 | uint32_t unused2[2]; 30 | uint16_t uid; 31 | uint16_t gid; 32 | uint32_t protect; 33 | uint32_t unused3; 34 | uint8_t comment[80]; // first char is len 35 | uint8_t unused4[12]; 36 | uint32_t days; 37 | uint32_t mins; 38 | uint32_t ticks; 39 | uint8_t dirname[32]; // first char is len, last char is unused 40 | uint32_t unused5[2]; 41 | uint32_t next_link; 42 | uint32_t unused6[5]; 43 | uint32_t hash_chain; 44 | uint32_t parent; 45 | uint32_t extension; 46 | uint32_t sec_type; 47 | }; 48 | 49 | // I didn't want to put this here, but owell 50 | struct _pwd 51 | { 52 | struct _amiga_rootblock rootblock; 53 | struct _amiga_partition partition; 54 | int partition_num; 55 | char cwd[4096]; // FIXME - check bounds 56 | uint32_t dir_hash[BSIZE / 4 - 56]; 57 | uint32_t hash_table_size; 58 | uint32_t parent_dir; 59 | }; 60 | 61 | void print_hash_info( 62 | FILE *in, 63 | struct _amiga_rootblock *rootblock, 64 | struct _amiga_bootblock *bootblock, 65 | struct _amiga_partition *partition, 66 | uint32_t block); 67 | 68 | void read_directory( 69 | FILE * in, 70 | struct _amiga_bootblock *bootblock, 71 | struct _amiga_partition *partition, 72 | struct _amiga_directory *directory, 73 | uint32_t block); 74 | 75 | void list_directory( 76 | FILE *in, 77 | struct _amiga_bootblock *bootblock, 78 | struct _pwd *pwd); 79 | 80 | void print_directory(struct _amiga_directory *directory); 81 | 82 | int ch_dir( 83 | FILE *in, 84 | struct _amiga_bootblock *bootblock, 85 | struct _pwd *pwd, 86 | char *dirname); 87 | 88 | #endif 89 | 90 | -------------------------------------------------------------------------------- /src/bootblock.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "bootblock.h" 19 | #include "fileio.h" 20 | 21 | int read_bootblock(FILE *in, struct _amiga_bootblock *bootblock) 22 | { 23 | int n; 24 | 25 | for (n = 0; n < 16; n++) 26 | { 27 | fseek(in, n * 512, SEEK_SET); 28 | read_chars(in, bootblock->magic, 4); 29 | bootblock->size = read_int(in); 30 | bootblock->checksum = read_int(in); 31 | bootblock->scsihost = read_int(in); 32 | bootblock->blksz = read_int(in); 33 | bootblock->flags = read_int(in); 34 | bootblock->badblcklst = read_int(in); 35 | bootblock->partitionlst = read_int(in); 36 | bootblock->fslst = read_int(in); 37 | 38 | if (memcmp(bootblock->magic, "RDSK", 4) == 0) 39 | { 40 | // NOTE: In my Amiga image this is not needed. RDSK is always 0. 41 | // In an image someone sent me, bootblock is on block 3 and points 42 | // to the first partition on block 3, and everything is is who knows 43 | // where. I added this to help locate his partitions. 44 | bootblock->offset = 512 * n; 45 | return n; 46 | } 47 | } 48 | 49 | bootblock->offset = 0; 50 | 51 | return -1; 52 | } 53 | 54 | void print_bootblock(struct _amiga_bootblock *bootblock) 55 | { 56 | printf("================== Boot Block ===================\n"); 57 | printf(" magic: %4.4s\n", bootblock->magic); 58 | printf(" size: %d\n", bootblock->size); 59 | printf(" checksum: %d\n", bootblock->checksum); 60 | printf(" scsihost: %d\n", bootblock->scsihost); 61 | printf(" blksz: %d\n", bootblock->blksz); 62 | printf(" flags: %d\n", bootblock->flags); 63 | printf(" badblcklst: %d\n", bootblock->badblcklst); 64 | printf(" partitionlst: %d\n", bootblock->partitionlst); 65 | printf(" fslst: %d\n", bootblock->fslst); 66 | printf("\n"); 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/datablock.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "datablock.h" 18 | #include "fileio.h" 19 | 20 | void read_datablock( 21 | FILE * in, 22 | struct _amiga_bootblock *bootblock, 23 | struct _amiga_partition *partition, 24 | struct _amiga_datablock *datablock, 25 | uint32_t block) 26 | { 27 | int t; 28 | 29 | fseek(in, partition->start + (block * bootblock->blksz), SEEK_SET); 30 | 31 | datablock->type = read_int(in); 32 | datablock->header_key = read_int(in); 33 | datablock->seq_num = read_int(in); 34 | datablock->data_size = read_int(in); 35 | datablock->next_data = read_int(in); 36 | datablock->checksum = read_int(in); 37 | 38 | for (t = 0; t < 512 - 24; t++) 39 | { 40 | datablock->data[t] = getc(in); 41 | } 42 | } 43 | 44 | void print_datablock(struct _amiga_datablock *datablock) 45 | { 46 | char ascii[17]; 47 | int ptr; 48 | int t; 49 | 50 | printf("================== Datablock ===================\n"); 51 | printf(" type: %d\n", datablock->type); 52 | printf(" header_key: %d\n", datablock->header_key); 53 | printf(" seq_num: %d\n", datablock->seq_num); 54 | printf(" data_size: %d\n", datablock->data_size); 55 | printf(" next_data: %d\n", datablock->next_data); 56 | printf(" checksum: %d\n", datablock->checksum); 57 | printf(" data:\n"); 58 | 59 | if (datablock->type != 8) { return; } 60 | ptr = 0; 61 | 62 | for (t = 0; t < datablock->data_size; t++) 63 | { 64 | if ((t % 16) == 0) 65 | { 66 | ascii[ptr] = 0; 67 | if (ptr != 0) printf(" %s",ascii); 68 | printf("\n"); 69 | ptr = 0; 70 | } 71 | 72 | if ((t % 16) != 0) printf(" "); 73 | printf("%02x", datablock->data[t]); 74 | 75 | if (datablock->data[t] >= 32 && datablock->data[t] < 127) 76 | { 77 | ascii[ptr++] = datablock->data[t]; 78 | } 79 | else 80 | { 81 | ascii[ptr++] = '.'; 82 | } 83 | } 84 | 85 | //FIXME - stuff missing at bottom.. 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/copy_all.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "affs.h" 22 | #include "copy_all.h" 23 | #include "directory.h" 24 | #include "fileio.h" 25 | 26 | static int copy_from_hash( 27 | FILE *in, 28 | struct _amiga_rootblock *rootblock, 29 | struct _amiga_bootblock *bootblock, 30 | struct _pwd *pwd, 31 | uint32_t block, 32 | const char *path) 33 | { 34 | struct _amiga_directory directory; 35 | struct _amiga_fileheader fileheader; 36 | struct _amiga_partition *partition = &pwd->partition; 37 | char name[8192]; 38 | int sec_type; 39 | 40 | sec_type = get_sec_type(in, bootblock, partition, block); 41 | 42 | if (sec_type == ST_USERDIR) 43 | { 44 | struct _pwd new_pwd; 45 | 46 | read_directory(in, bootblock, partition, &directory, block); 47 | 48 | printf(" (Dir) %s\n", directory.dirname); 49 | 50 | sprintf(name, "%s/%s", path, directory.dirname); 51 | printf(" -- mkdir %s\n", name); 52 | 53 | if (mkdir(name, 0777) == 0) 54 | { 55 | memcpy(&new_pwd, pwd, sizeof(struct _pwd)); 56 | if (ch_dir(in, bootblock, &new_pwd, (char *)directory.dirname) == 0) 57 | { 58 | if (copy_all(in, bootblock, &new_pwd, name) != 0) 59 | { 60 | return -1; 61 | } 62 | } 63 | } 64 | else 65 | { 66 | if (errno != EEXIST) 67 | { 68 | printf("Could not create directory %s\n", path); 69 | return -1; 70 | } 71 | } 72 | 73 | if (directory.hash_chain != 0) 74 | { 75 | copy_from_hash(in, rootblock, bootblock, pwd, directory.hash_chain, path); 76 | } 77 | } 78 | else 79 | if (sec_type == ST_FILE) 80 | { 81 | read_fileheader(in, bootblock, partition, &fileheader, block); 82 | 83 | printf("%9d %s\n", fileheader.byte_size, fileheader.filename); 84 | 85 | sprintf(name, "%s/%s", path, fileheader.filename); 86 | printf(" -- Copy to %s\n", name); 87 | 88 | FILE *out = fopen(name, "wb"); 89 | 90 | if (out == NULL) 91 | { 92 | printf("Could not open %s for writing. Aborting.\n", name); 93 | return -1; 94 | } 95 | 96 | copy_file(in, bootblock, pwd, (char *)fileheader.filename, out); 97 | 98 | fclose(out); 99 | 100 | if (fileheader.hash_chain != 0) 101 | { 102 | copy_from_hash(in, rootblock, bootblock, pwd, fileheader.hash_chain, path); 103 | } 104 | } 105 | else 106 | { 107 | printf("Unknown sec_type %d\n", sec_type); 108 | } 109 | 110 | return 0; 111 | } 112 | 113 | int copy_all( 114 | FILE *in, 115 | struct _amiga_bootblock *bootblock, 116 | struct _pwd *pwd, 117 | char *path) 118 | { 119 | int t; 120 | 121 | for (t = 0; t < 72; t++) 122 | { 123 | if (pwd->dir_hash[t] != 0) 124 | { 125 | if (copy_from_hash(in, &pwd->rootblock, bootblock, pwd, pwd->dir_hash[t], path) != 0) 126 | { 127 | return -1; 128 | } 129 | } 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/find_partitions.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | find_partitions - Scan a disk image for partitions. 11 | 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "fileio.h" 20 | #include "partition.h" 21 | 22 | #define UINT32(a,i) ((a[i+0] << 24) + (a[i+1] << 16) + (a[i+2] << 8) + a[i+3]) 23 | 24 | int show_partition(FILE *in) 25 | { 26 | struct _amiga_partition partition; 27 | 28 | //long marker = ftell(in); 29 | 30 | read_chars(in, partition.magic, 4); 31 | 32 | if (memcmp(partition.magic, "PART", 4) != 0) 33 | { 34 | return -1; 35 | } 36 | 37 | partition.size = read_int(in); 38 | partition.checksum = read_int(in); 39 | partition.scsihost = read_int(in); 40 | partition.next = read_int(in); 41 | partition.flags = read_int(in); 42 | fseek(in, 4 * 2, SEEK_CUR); 43 | partition.devflags = read_int(in); 44 | int namelen = getc(in); 45 | 46 | read_chars(in, partition.name, 31); 47 | partition.name[namelen] = 0; 48 | 49 | fseek(in, 4 * 15, SEEK_CUR); 50 | 51 | partition.table_size = read_int(in); 52 | partition.size_block = read_int(in); 53 | partition.sec_org = read_int(in); 54 | partition.heads = read_int(in); 55 | partition.sectors_per_block = read_int(in); 56 | partition.blocks_per_track = read_int(in); 57 | partition.num_reserved = read_int(in); 58 | partition.pre_alloc = read_int(in); 59 | partition.interleave = read_int(in); 60 | partition.start_cyl = read_int(in); 61 | partition.end_cyl = read_int(in); 62 | partition.num_buffers = read_int(in); 63 | partition.buff_type = read_int(in); 64 | partition.max_transfer = read_int(in); 65 | partition.mask = read_int(in); 66 | partition.boot_priority = read_int(in); 67 | 68 | read_chars(in, partition.type, 4); 69 | 70 | if (partition.type[0] == 'D' && 71 | partition.type[1] == 'O' && 72 | partition.type[2] == 'S') 73 | { 74 | partition.type[3] += '0'; 75 | } 76 | 77 | partition.baud = read_int(in); 78 | partition.control = read_int(in); 79 | partition.bootblocks = read_int(in); 80 | 81 | int cylindar_size = 82 | //bootblock.blksz * 83 | 512 * 84 | partition.heads * 85 | partition.sectors_per_block * 86 | partition.blocks_per_track; 87 | 88 | partition.start = cylindar_size*partition.start_cyl; 89 | 90 | partition.size_in_bytes = 91 | (partition.end_cyl-partition.start_cyl + 1) * cylindar_size; 92 | partition.end = partition.start+partition.size_in_bytes; 93 | 94 | print_partition(&partition); 95 | 96 | return 0; 97 | } 98 | 99 | int main(int argc, char *argv[]) 100 | { 101 | FILE *in; 102 | char *part = "PART"; 103 | int ptr = 0, count = 0; 104 | 105 | if (argc != 2) 106 | { 107 | printf("Usage: %s \n", argv[0]); 108 | exit(0); 109 | } 110 | 111 | in = fopen(argv[1], "rb"); 112 | 113 | if (in == NULL) 114 | { 115 | printf("Cannot open file %s for reading.\n", argv[1]); 116 | exit(1); 117 | } 118 | 119 | while (1) 120 | { 121 | int ch = getc(in); 122 | 123 | if (ch == EOF) { break; } 124 | 125 | if (ch == part[ptr]) 126 | { 127 | ptr++; 128 | 129 | if (ptr == 4) 130 | { 131 | fseek(in, -4, SEEK_CUR); 132 | 133 | printf("Found at %ld (0x%04lx)\n", ftell(in), ftell(in)); 134 | 135 | show_partition(in); 136 | ptr = 0; 137 | count++; 138 | } 139 | } 140 | else 141 | { 142 | ptr = 0; 143 | } 144 | } 145 | 146 | printf("Found %d partitions\n", count); 147 | 148 | fclose(in); 149 | 150 | return 0; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /src/fileheader.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "fileheader.h" 19 | #include "fileio.h" 20 | 21 | void read_fileheader(FILE * in, struct _amiga_bootblock *bootblock, struct _amiga_partition *partition, struct _amiga_fileheader *fileheader, uint32_t block) 22 | { 23 | int namelen; 24 | int t; 25 | 26 | fseek(in, partition->start + (block * bootblock->blksz), SEEK_SET); 27 | 28 | fileheader->type = read_int(in); 29 | fileheader->header_key = read_int(in); 30 | fileheader->high_seq = read_int(in); 31 | fileheader->data_size = read_int(in); 32 | fileheader->first_data = read_int(in); 33 | fileheader->checksum = read_int(in); 34 | //uint32_t datablocks[512/4-56]; //FIXME - wrong size? 35 | for (t = 0; t < (512 / 4 - 56); t++) 36 | { 37 | fileheader->datablocks[t] = read_int(in); 38 | } 39 | fileheader->unused1 = read_int(in); 40 | fileheader->uid = read_short(in); 41 | fileheader->gid = read_short(in); 42 | fileheader->protect = read_int(in); 43 | fileheader->byte_size = read_int(in); 44 | //unsigned char comment[80]; // first char is len 45 | namelen = getc(in); 46 | for (t = 0; t < 79; t++) 47 | { 48 | fileheader->comment[t] = getc(in); 49 | } 50 | fileheader->comment[namelen] = 0; 51 | //unsigned char unused2[12]; 52 | fseek(in, 12, SEEK_CUR); 53 | fileheader->days = read_int(in); 54 | fileheader->mins = read_int(in); 55 | fileheader->ticks = read_int(in); 56 | //unsigned char filename[32]; // first char is len, last char is unused 57 | namelen = getc(in); 58 | for (t = 0; t < 31; t++) 59 | { 60 | fileheader->filename[t] = getc(in); 61 | } 62 | fileheader->filename[namelen] = 0; 63 | fileheader->unused3 = read_int(in); 64 | fileheader->read_entry = read_int(in); 65 | fileheader->next_link = read_int(in); 66 | //uint32_t unused4[5]; 67 | fseek(in, 5 * 4, SEEK_CUR); 68 | fileheader->hash_chain = read_int(in); 69 | fileheader->parent = read_int(in); 70 | fileheader->extension = read_int(in); 71 | fileheader->sec_type = read_int(in); 72 | } 73 | 74 | void print_fileheader(struct _amiga_fileheader *fileheader) 75 | { 76 | int t; 77 | 78 | printf("================== File Header ===================\n"); 79 | printf(" type: %d\n", fileheader->type); 80 | printf(" header_key: %d\n", fileheader->header_key); 81 | printf(" high_seq: %d\n", fileheader->high_seq); 82 | printf(" data_size: %d\n", fileheader->data_size); 83 | printf(" first_data: %d\n", fileheader->first_data); 84 | printf(" checksum: %d\n", fileheader->checksum); 85 | printf(" datablocks:"); 86 | for (t = 0; t < (512 / 4 - 56); t++) 87 | { 88 | if (t != 0) printf(" "); 89 | printf("%d", fileheader->datablocks[t]); 90 | } 91 | printf("\n"); 92 | printf(" uid: %d\n", fileheader->uid); 93 | printf(" gid: %d\n", fileheader->gid); 94 | printf(" protect: %d\n", fileheader->protect); 95 | printf(" byte_size: %d\n", fileheader->byte_size); 96 | printf(" comment: %s\n", fileheader->comment); 97 | printf(" days: %d\n", fileheader->days); 98 | printf(" mins: %d\n", fileheader->mins); 99 | printf(" ticks: %d\n", fileheader->ticks); 100 | printf(" filename: %s\n", fileheader->filename); 101 | printf(" read_entry: %d\n", fileheader->read_entry); 102 | printf(" next_link: %d\n", fileheader->next_link); 103 | printf(" hash_chain: %d\n", fileheader->hash_chain); 104 | printf(" parent: %d\n", fileheader->parent); 105 | printf(" extension: %d\n", fileheader->extension); 106 | printf(" sec_type: %d\n", fileheader->sec_type); 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/file.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "affs.h" 19 | #include "file_ext.h" 20 | #include "fileio.h" 21 | 22 | static void print_file_at_block( 23 | FILE *in, 24 | struct _amiga_bootblock *bootblock, 25 | struct _amiga_partition *partition, 26 | struct _amiga_fileheader *fileheader, 27 | FILE *out) 28 | { 29 | //struct _amiga_datablock datablock; 30 | struct _amiga_file_ext file_ext; 31 | uint32_t *datablocks; 32 | uint32_t bytes_left; 33 | uint8_t buffer[bootblock->blksz]; 34 | uint32_t next_block; 35 | int curr; 36 | int len; 37 | int t; 38 | 39 | datablocks = fileheader->datablocks; 40 | bytes_left = fileheader->byte_size; 41 | next_block = fileheader->extension; 42 | 43 | // print_fileheader(fileheader); 44 | 45 | while (bytes_left > 0) 46 | { 47 | for (curr = 71; curr >= 0; curr--) 48 | { 49 | if (datablocks[curr] == 0) { break; } 50 | 51 | fseek(in, partition->start + (datablocks[curr] * bootblock->blksz), SEEK_SET); 52 | if (bytes_left > bootblock->blksz) 53 | { 54 | len = fread(buffer, 1, bootblock->blksz, in); 55 | bytes_left -= bootblock->blksz; 56 | } 57 | else 58 | { 59 | len = fread(buffer, 1, bytes_left, in); 60 | bytes_left -= bytes_left; 61 | } 62 | 63 | uint32_t offset = 0; 64 | 65 | // This is OFS, so skip 24 bytes of each sector. 66 | if (partition->type[3] == '0') 67 | { 68 | offset = 24; 69 | } 70 | 71 | #if 0 72 | printf("offset=%d type=%c%c%c[%d]\n", offset, 73 | partition->type[0], 74 | partition->type[1], 75 | partition->type[2], 76 | partition->type[3] 77 | ); 78 | #endif 79 | 80 | if (out == NULL) 81 | { 82 | for (t = offset; t < len; t++) { putchar(buffer[t]); } 83 | } 84 | else 85 | { 86 | fwrite(buffer + offset, 1, len - offset, out); 87 | } 88 | } 89 | 90 | if (next_block == 0) { break; } 91 | 92 | read_file_ext(in, bootblock, partition, &file_ext, next_block); 93 | 94 | if (file_ext.type != 16) 95 | { 96 | printf("Error: File extension wasn't read right? Bug?\n"); 97 | break; 98 | } 99 | 100 | //print_file_ext(&file_ext); 101 | next_block = file_ext.extension; 102 | datablocks = file_ext.datablocks; 103 | } 104 | 105 | if (out == NULL) { printf("\n"); } 106 | } 107 | 108 | void copy_file( 109 | FILE *in, 110 | struct _amiga_bootblock *bootblock, 111 | struct _pwd *pwd, 112 | char *filename, 113 | FILE *out) 114 | { 115 | struct _amiga_directory directory; 116 | struct _amiga_fileheader fileheader; 117 | uint32_t block; 118 | int sec_type; 119 | 120 | block = pwd->dir_hash[hash_name((unsigned char*)filename)]; 121 | 122 | while (block != 0) 123 | { 124 | sec_type = get_sec_type(in, bootblock, &pwd->partition, block); 125 | 126 | if (sec_type == ST_USERDIR) 127 | { 128 | read_directory(in, bootblock, &pwd->partition, &directory, block); 129 | 130 | if (directory.hash_chain == 0) { break; } 131 | 132 | block = directory.hash_chain; 133 | } 134 | else 135 | if (sec_type == ST_FILE) 136 | { 137 | read_fileheader(in, bootblock, &pwd->partition, &fileheader, block); 138 | 139 | if (strcmp((char *)fileheader.filename, filename) == 0) 140 | { 141 | print_file_at_block(in, bootblock, &pwd->partition, &fileheader, out); 142 | return; 143 | } 144 | 145 | if (fileheader.hash_chain == 0) { break; } 146 | 147 | block = fileheader.hash_chain; 148 | } 149 | else 150 | { 151 | printf("Unknown sec_type %d\n", sec_type); 152 | break; 153 | } 154 | } 155 | 156 | printf("Error: file '%s' not found\n", filename); 157 | } 158 | 159 | -------------------------------------------------------------------------------- /src/rootblock.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "fileio.h" 18 | #include "rootblock.h" 19 | 20 | static uint32_t calc_rootblock( 21 | struct _amiga_bootblock *bootblock, 22 | struct _amiga_partition *partition) 23 | { 24 | return (((((partition->end_cyl - partition->start_cyl + 1) * 25 | partition->heads * partition->blocks_per_track) - 1) + 26 | partition->num_reserved) / 2) * bootblock->blksz; 27 | } 28 | 29 | int read_rootblock_data(FILE *in, struct _amiga_rootblock *rootblock) 30 | { 31 | int namelen; 32 | int t; 33 | 34 | rootblock->type = read_int(in); 35 | rootblock->header_key = read_int(in); 36 | rootblock->high_seq = read_int(in); 37 | rootblock->hash_table_size = read_int(in); 38 | rootblock->first_size = read_int(in); 39 | rootblock->checksum = read_int(in); 40 | 41 | if (rootblock->hash_table_size > BSIZE / 4 - 56) 42 | { 43 | printf("Error: rootblock->hash_table_size > BSIZE / 4 - 56 %s:%d\n", 44 | __FILE__, __LINE__); 45 | printf(" It's prossible this is a bug in amiga_recovery\n"); 46 | return -1; 47 | } 48 | 49 | for (t = 0; t < rootblock->hash_table_size; t++) 50 | { 51 | rootblock->hash_table[t] = read_int(in); 52 | //hash_table[512/4-56]; 53 | } 54 | 55 | rootblock->bm_flag = read_int(in); 56 | 57 | for (t = 0; t < 25; t++) 58 | { 59 | rootblock->bm_pages[t] = read_int(in); 60 | } 61 | 62 | rootblock->bm_ext = read_int(in); 63 | rootblock->r_days = read_int(in); 64 | rootblock->r_mins = read_int(in); 65 | rootblock->r_ticks = read_int(in); 66 | 67 | namelen = getc(in); 68 | 69 | read_chars(in, rootblock->diskname, 31); 70 | rootblock->diskname[namelen] = 0; 71 | 72 | //uint32_t unused1[2]; 73 | read_int(in); 74 | read_int(in); 75 | rootblock->v_days = read_int(in); 76 | rootblock->v_min = read_int(in); 77 | rootblock->v_ticks = read_int(in); 78 | rootblock->c_days = read_int(in); 79 | rootblock->c_min = read_int(in); 80 | rootblock->c_ticks = read_int(in); 81 | rootblock->next_hash = read_int(in); 82 | rootblock->parent_dir = read_int(in); 83 | rootblock->extension = read_int(in); 84 | rootblock->sec_type = read_int(in); 85 | 86 | return 0; 87 | } 88 | 89 | void read_rootblock( 90 | FILE *in, 91 | struct _amiga_bootblock *bootblock, 92 | struct _amiga_partition *partition, 93 | struct _amiga_rootblock *rootblock) 94 | { 95 | uint32_t offset; 96 | 97 | offset = calc_rootblock(bootblock, partition); // + bootblock->offset; 98 | 99 | rootblock->partition_offset = offset; 100 | rootblock->disk_offset = offset + partition->start; 101 | 102 | fseek(in, offset + partition->start, SEEK_SET); 103 | if (read_rootblock_data(in, rootblock) != 0) { return; } 104 | } 105 | 106 | void print_rootblock(struct _amiga_rootblock *rootblock) 107 | { 108 | int t; 109 | 110 | printf("===================== Rootblock ======================\n"); 111 | 112 | printf(" type: %d\n", rootblock->type); 113 | printf(" header_key: %d\n", rootblock->header_key); 114 | printf(" high_seq: %d\n", rootblock->high_seq); 115 | printf(" hash_table_size: %d\n", rootblock->hash_table_size); 116 | printf(" first_size: %d\n", rootblock->first_size); 117 | printf(" checksum: %d\n", rootblock->checksum); 118 | printf(" hash_table: "); 119 | for (t = 0; t < rootblock->hash_table_size; t++) 120 | { 121 | if (t != 0) printf(" "); 122 | printf("%d", rootblock->hash_table[t]); 123 | } 124 | printf("\n"); 125 | printf(" bm_flag: %d\n", rootblock->bm_flag); 126 | printf(" bm_pages: "); 127 | for (t = 0; t < 25; t++) 128 | { 129 | if (t != 0) printf(" "); 130 | printf("%d", rootblock->bm_pages[t]); 131 | } 132 | printf("\n"); 133 | printf(" bm_ext: %d\n", rootblock->bm_ext); 134 | printf(" r_days: %d\n", rootblock->r_days); 135 | printf(" r_mins: %d\n", rootblock->r_mins); 136 | printf(" r_ticks: %d\n", rootblock->r_ticks); 137 | printf(" diskname: %s\n", rootblock->diskname); 138 | printf(" unused1: %04x %04x\n", rootblock->unused1[0], rootblock->unused1[1]); 139 | printf(" v_days: %d\n", rootblock->v_days); 140 | printf(" v_min: %d\n", rootblock->v_min); 141 | printf(" v_ticks: %d\n", rootblock->v_ticks); 142 | printf(" c_days: %d\n", rootblock->c_days); 143 | printf(" c_min: %d\n", rootblock->c_min); 144 | printf(" c_ticks: %d\n", rootblock->c_ticks); 145 | printf(" next_hash: %d\n", rootblock->next_hash); 146 | printf(" parent_dir: %d\n", rootblock->parent_dir); 147 | printf(" extension: %d\n", rootblock->extension); 148 | printf(" sec_type: %d\n\n", rootblock->sec_type); 149 | 150 | printf(" partition_offset: 0x%08x (%d)\n", rootblock->partition_offset, rootblock->partition_offset); 151 | printf(" disk_offset: 0x%08x (%d)\n", rootblock->disk_offset, rootblock->disk_offset); 152 | } 153 | 154 | uint32_t find_root_block(FILE *in, struct _amiga_rootblock *rootblock) 155 | { 156 | //struct _amiga_rootblock rootblock; 157 | uint8_t buffer[512]; 158 | long marker; 159 | int len; 160 | uint32_t offset = 0; 161 | 162 | marker = ftell(in); 163 | fseek(in, 0, SEEK_SET); 164 | 165 | while (1) 166 | { 167 | len = fread(buffer, 512, 1, in); 168 | if (len == 0) { break; } 169 | 170 | if (UINT32(buffer, 0) == 0x02 && 171 | UINT32(buffer, 4) == 0x00 && 172 | UINT32(buffer, 8) == 0x00 && 173 | UINT32(buffer, 16) == 0x00) 174 | { 175 | long marker = ftell(in); 176 | fseek(in, marker - 512, SEEK_SET); 177 | 178 | if (read_rootblock_data(in, rootblock) == 0 && 179 | rootblock->hash_table_size != 0) 180 | { 181 | printf("Possible Rootblock at %ld block=%d\n", marker, (int)marker / 512); 182 | print_rootblock(rootblock); 183 | offset = (uint32_t)marker - 512; 184 | break; 185 | } 186 | 187 | fseek(in, marker, SEEK_SET); 188 | } 189 | } 190 | 191 | fseek(in, marker, SEEK_SET); 192 | 193 | return offset; 194 | } 195 | 196 | -------------------------------------------------------------------------------- /src/directory.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "affs.h" 19 | #include "directory.h" 20 | #include "fileio.h" 21 | 22 | void print_hash_info( 23 | FILE *in, 24 | struct _amiga_rootblock *rootblock, 25 | struct _amiga_bootblock *bootblock, 26 | struct _amiga_partition *partition, 27 | uint32_t block) 28 | { 29 | struct _amiga_directory directory; 30 | struct _amiga_fileheader fileheader; 31 | int sec_type; 32 | 33 | sec_type = get_sec_type(in, bootblock, partition, block); 34 | 35 | if (sec_type == ST_USERDIR) 36 | { 37 | read_directory(in, bootblock, partition, &directory, block); 38 | //print_directory(&directory); 39 | printf(" (Dir) %s\n", directory.dirname); 40 | 41 | if (directory.hash_chain != 0) 42 | { 43 | print_hash_info(in, rootblock, bootblock, partition, directory.hash_chain); 44 | } 45 | } 46 | else 47 | if (sec_type == ST_FILE) 48 | { 49 | read_fileheader(in, bootblock, partition, &fileheader, block); 50 | //print_fileheader(&fileheader); 51 | printf("%9d %s\n", fileheader.byte_size, fileheader.filename); 52 | 53 | if (fileheader.hash_chain != 0) 54 | { 55 | print_hash_info(in, rootblock, bootblock, partition, fileheader.hash_chain); 56 | } 57 | } 58 | else 59 | { 60 | printf("Unknown sec_type %d\n", sec_type); 61 | } 62 | } 63 | 64 | void read_directory( 65 | FILE * in, 66 | struct _amiga_bootblock *bootblock, 67 | struct _amiga_partition *partition, 68 | struct _amiga_directory *directory, 69 | uint32_t block) 70 | { 71 | int namelen; 72 | int t; 73 | 74 | fseek(in, partition->start + (block * bootblock->blksz), SEEK_SET); 75 | 76 | directory->type = read_int(in); 77 | directory->header_key = read_int(in); 78 | //uint32_t unused1[3]; 79 | fseek(in, 3 * 4, SEEK_CUR); 80 | directory->checksum = read_int(in); 81 | //uint32_t hash_table[512/4-56]; 82 | for (t = 0; t < (512 / 4 - 56); t++) 83 | { 84 | directory->hash_table[t] = read_int(in); 85 | } 86 | //uint32_t unused2[2]; 87 | fseek(in, 2 * 4, SEEK_CUR); 88 | directory->uid = read_short(in); 89 | directory->gid = read_short(in); 90 | directory->protect = read_int(in); 91 | // FIXME - WTF? 92 | //directory->unused3 = read_int(in); 93 | //unsigned char comment[80]; // first char is len 94 | namelen = getc(in); 95 | for (t = 0; t < 79; t++) 96 | { 97 | directory->comment[t] = getc(in); 98 | } 99 | directory->comment[namelen] =0; 100 | //unsigned char unused4[12]; 101 | fseek(in, 12, SEEK_CUR); 102 | directory->days= read_int(in); 103 | directory->mins = read_int(in); 104 | directory->ticks = read_int(in); 105 | //unsigned char dirname[32]; // first char is len, last char is unused 106 | namelen = getc(in); 107 | for (t = 0; t < 31; t++) 108 | { 109 | directory->dirname[t] = getc(in); 110 | } 111 | directory->dirname[namelen] = 0; 112 | //uint32_t unused5[2]; 113 | fseek(in, 2 * 4, SEEK_CUR); 114 | directory->next_link = read_int(in); 115 | //uint32_t unused6[5]; 116 | fseek(in, 5 * 4, SEEK_CUR); 117 | directory->hash_chain = read_int(in); 118 | directory->parent = read_int(in); 119 | directory->extension = read_int(in); 120 | directory->sec_type = read_int(in); 121 | } 122 | 123 | void list_directory( 124 | FILE *in, 125 | struct _amiga_bootblock *bootblock, 126 | struct _pwd *pwd) 127 | { 128 | int t; 129 | 130 | for (t = 0; t < 72; t++) 131 | { 132 | if (pwd->dir_hash[t] != 0) 133 | { 134 | print_hash_info(in, &pwd->rootblock, bootblock, &pwd->partition, pwd->dir_hash[t]); 135 | } 136 | } 137 | } 138 | 139 | void print_directory(struct _amiga_directory *directory) 140 | { 141 | int t; 142 | 143 | printf("================== Directory ===================\n"); 144 | printf(" type: %d\n", directory->type); 145 | printf(" header_key: %d\n", directory->header_key); 146 | printf(" checksum: %d\n", directory->checksum); 147 | printf(" hash_table:"); 148 | for (t = 0; t < (512 / 4 - 56); t++) 149 | { 150 | if (t != 0) { printf(" "); } 151 | printf("%d", directory->hash_table[t]); 152 | } 153 | printf("\n"); 154 | printf(" uid: %d\n", directory->uid); 155 | printf(" gid: %d\n", directory->gid); 156 | printf(" protect: %d\n", directory->protect); 157 | printf(" comment: %s\n", directory->comment); 158 | printf(" days: %d\n", directory->days); 159 | printf(" mins: %d\n", directory->mins); 160 | printf(" ticks: %d\n", directory->ticks); 161 | printf(" dirname: %s\n", directory->dirname); 162 | printf(" next_link: %d\n", directory->next_link); 163 | printf(" hash_chain: %d\n", directory->hash_chain); 164 | printf(" parent: %d\n", directory->parent); 165 | printf(" extension: %d\n", directory->extension); 166 | printf(" sec_type: %d\n", directory->sec_type); 167 | } 168 | 169 | int ch_dir( 170 | FILE *in, 171 | struct _amiga_bootblock *bootblock, 172 | struct _pwd *pwd, 173 | char *dirname) 174 | { 175 | struct _amiga_directory directory; 176 | struct _amiga_fileheader fileheader; 177 | 178 | if (strcmp(dirname, "..")==0) 179 | { 180 | if (pwd->parent_dir == 0) { return -1; } 181 | 182 | if (pwd->parent_dir == pwd->rootblock.header_key) 183 | { 184 | memcpy(pwd->dir_hash, pwd->rootblock.hash_table, (BSIZE / 4 - 56) * 4); 185 | pwd->cwd[0] = 0; 186 | pwd->parent_dir = 0; 187 | } 188 | else 189 | { 190 | read_directory(in, bootblock, &pwd->partition, &directory, pwd->parent_dir); 191 | memcpy(pwd->dir_hash, directory.hash_table, (BSIZE / 4 - 56) * 4); 192 | int l = strlen(pwd->cwd) - 2; 193 | while (l >= 0) 194 | { 195 | if (pwd->cwd[l] == '/') { break; } 196 | pwd->cwd[l--] = 0; 197 | } 198 | } 199 | } 200 | else 201 | { 202 | uint32_t block = pwd->dir_hash[hash_name((uint8_t *)dirname)]; 203 | 204 | while (block != 0) 205 | { 206 | uint32_t sec_type = get_sec_type(in, bootblock, &pwd->partition, block); 207 | 208 | if (sec_type == ST_USERDIR) 209 | { 210 | read_directory(in, bootblock, &pwd->partition, &directory, block); 211 | 212 | if (strcmp((char *)directory.dirname, dirname) == 0) 213 | { 214 | memcpy(pwd->dir_hash, directory.hash_table, (BSIZE / 4 - 56) * 4); 215 | pwd->parent_dir = directory.parent; 216 | strcat(pwd->cwd, dirname); 217 | strcat(pwd->cwd, "/"); 218 | return 0; 219 | } 220 | 221 | if (directory.hash_chain == 0) { break; } 222 | block = directory.hash_chain; 223 | } 224 | else 225 | if (sec_type == ST_FILE) 226 | { 227 | read_fileheader(in, bootblock, &pwd->partition, &fileheader, block); 228 | 229 | if (fileheader.hash_chain == 0) { break; } 230 | 231 | block = fileheader.hash_chain; 232 | } 233 | else 234 | { 235 | printf("Unknown sec_type %d\n", sec_type); 236 | break; 237 | } 238 | } 239 | 240 | printf("Error: No such directory\n"); 241 | return -1; 242 | } 243 | 244 | return 0; 245 | } 246 | 247 | -------------------------------------------------------------------------------- /src/parse_hunk.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | parse_hunk - Read in an Amiga executable and dump out all sections of 11 | the hunk file. 12 | 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #define HUNK_UNIT 0x3e7 20 | #define HUNK_NAME 0x3e8 21 | #define HUNK_CODE 0x3e9 22 | #define HUNK_DATA 0x3ea 23 | #define HUNK_BSS 0x3eb 24 | #define HUNK_RELOC32 0x3ec 25 | #define HUNK_RELOC16 0x3ed 26 | #define HUNK_RELOC8 0x3ee 27 | #define HUNK_EXT 0x3ef 28 | #define HUNK_SYMBOL 0x3f0 29 | #define HUNK_DEBUG 0x3f1 30 | #define HUNK_END 0x3f2 31 | #define HUNK_HEADER 0x3f3 32 | #define HUNK_OVERLAY 0x3f5 33 | #define HUNK_BREAK 0x3f6 34 | #define HUNK_DREL32 0x3f7 35 | #define HUNK_DREL16 0x3f8 36 | #define HUNK_DREL8 0x3f9 37 | #define HUNK_LIB 0x3fa 38 | #define HUNK_INDEX 0x3fb 39 | #define HUNK_RELOC32SHORT 0x3fc 40 | #define HUNK_RELRELOC32 0x3fd 41 | #define HUNK_ABSRELOC16 0x3fe 42 | #define HUNK_PPC_CODE 0x4e9 43 | #define HUNK_RELRELOC26 0x4ec 44 | 45 | static uint32_t read_int32(FILE *in) 46 | { 47 | return (getc(in) << 24) | 48 | (getc(in) << 16) | 49 | (getc(in) << 8) | 50 | getc(in); 51 | } 52 | 53 | const char *get_hunk_name(uint32_t hunk_type) 54 | { 55 | switch (hunk_type) 56 | { 57 | case HUNK_UNIT: 58 | return "HUNK_UNIT"; 59 | case HUNK_NAME: 60 | return "HUNK_NAME"; 61 | case HUNK_CODE: 62 | return "HUNK_CODE"; 63 | case HUNK_DATA: 64 | return "HUNK_DATA"; 65 | case HUNK_BSS: 66 | return "HUNK_BSS"; 67 | case HUNK_RELOC32: 68 | return "HUNK_RELOC32"; 69 | case HUNK_RELOC16: 70 | return "HUNK_RELOC16"; 71 | case HUNK_RELOC8: 72 | return "HUNK_RELOC8"; 73 | case HUNK_EXT: 74 | return "HUNK_EXT"; 75 | case HUNK_SYMBOL: 76 | return "HUNK_SYMBOL"; 77 | case HUNK_DEBUG: 78 | return "HUNK_DEBUG"; 79 | case HUNK_END: 80 | return "HUNK_END"; 81 | case HUNK_HEADER: 82 | return "HUNK_HEADER"; 83 | case HUNK_OVERLAY: 84 | return "HUNK_OVERLAY"; 85 | case HUNK_BREAK: 86 | return "HUNK_BREAK"; 87 | case HUNK_DREL32: 88 | return "HUNK_DREL32"; 89 | case HUNK_DREL16: 90 | return "HUNK_DREL16"; 91 | case HUNK_DREL8: 92 | return "HUNK_DREL8"; 93 | case HUNK_LIB: 94 | return "HUNK_LIB"; 95 | case HUNK_INDEX: 96 | return "HUNK_INDEX"; 97 | case HUNK_RELOC32SHORT: 98 | return "HUNK_RELOC32SHORT"; 99 | case HUNK_RELRELOC32: 100 | return "HUNK_RELRELOC32"; 101 | case HUNK_ABSRELOC16: 102 | return "HUNK_ABSRELOC16"; 103 | case HUNK_PPC_CODE: 104 | return "HUNK_PPC_CODE"; 105 | case HUNK_RELRELOC26: 106 | return "HUNK_RELRELOC26"; 107 | default: 108 | break; 109 | } 110 | 111 | return "???"; 112 | } 113 | 114 | int parse_hunk_code(FILE *in) 115 | { 116 | int n; 117 | uint32_t length = read_int32(in); 118 | 119 | for (n = 0; n < length; n++) 120 | { 121 | read_int32(in); 122 | } 123 | 124 | printf("Length In Bytes: %d\n", length * 4); 125 | 126 | return 0; 127 | } 128 | 129 | int parse_hunk_reloc32(FILE *in) 130 | { 131 | int n; 132 | 133 | while (1) 134 | { 135 | uint32_t count = read_int32(in); 136 | if (count == 0) { return 0; } 137 | 138 | uint32_t hunk_id = read_int32(in); 139 | 140 | printf(" Hunk ID: %d\n", hunk_id); 141 | 142 | for (n = 0; n < count; n++) 143 | { 144 | uint32_t hunk_offset = read_int32(in); 145 | 146 | printf(" 0x%08x\n", hunk_offset); 147 | } 148 | } 149 | 150 | return 0; 151 | } 152 | 153 | int parse_hunk_symbol(FILE *in) 154 | { 155 | int ch, n, ptr; 156 | char name[1024]; 157 | 158 | while (1) 159 | { 160 | uint32_t name_length = read_int32(in); 161 | if (name_length == 0) { break; } 162 | 163 | ptr = 0; 164 | 165 | for (n = 0; n < name_length * 4; n++) 166 | { 167 | ch = getc(in); 168 | 169 | if (ch == 0) { continue; } 170 | if (ch == EOF) { break; } 171 | if (ptr >= sizeof(name) - 1) { continue; } 172 | 173 | name[ptr++] = ch; 174 | } 175 | 176 | name[ptr] = 0; 177 | 178 | uint32_t offset = read_int32(in); 179 | 180 | printf(" 0x%08x %s\n", offset, name); 181 | } 182 | 183 | return 0; 184 | } 185 | 186 | int parse_hunk_header(FILE *in) 187 | { 188 | int ch, n; 189 | uint32_t name_length = read_int32(in); 190 | 191 | printf(" Name: "); 192 | 193 | for (n = 0; n < name_length * 4; n++) 194 | { 195 | ch = getc(in); 196 | 197 | if (ch == 0) { continue; } 198 | if (ch == EOF) { break; } 199 | 200 | printf("%c", ch); 201 | } 202 | 203 | printf("\n"); 204 | 205 | uint32_t table_length = read_int32(in); 206 | uint32_t first_hunk = read_int32(in); 207 | uint32_t last_hunk = read_int32(in); 208 | 209 | printf(" Table Length: %d\n", table_length); 210 | printf(" First Hunk: %d\n", first_hunk); 211 | printf(" Last Hunk: %d\n", last_hunk); 212 | 213 | for (n = 0; n < table_length; n++) 214 | { 215 | uint32_t size = read_int32(in); 216 | 217 | printf(" len=%d\n", size); 218 | } 219 | 220 | return 0; 221 | } 222 | 223 | int main(int argc, char *argv[]) 224 | { 225 | FILE *in; 226 | 227 | if (argc != 2) 228 | { 229 | printf("Usage: parse_hunk \n"); 230 | return 0; 231 | } 232 | 233 | in = fopen(argv[1], "rb"); 234 | 235 | if (in == NULL) 236 | { 237 | printf("Cannot open file %s\n", argv[1]); 238 | return -1; 239 | } 240 | 241 | uint32_t magic_cookie = read_int32(in); 242 | 243 | if (magic_cookie != HUNK_HEADER) 244 | { 245 | printf("Not an Amiga hunk file\n"); 246 | fclose(in); 247 | return -1; 248 | } 249 | 250 | fseek(in, 0, SEEK_SET); 251 | 252 | int running = 1; 253 | 254 | while (running == 1) 255 | { 256 | long offset = ftell(in); 257 | uint32_t hunk_type = read_int32(in); 258 | uint32_t bits = hunk_type >> 30; 259 | 260 | hunk_type &= 0x3fffffff; 261 | 262 | const char *where = ""; 263 | 264 | if (hunk_type != HUNK_HEADER) 265 | { 266 | switch (bits) 267 | { 268 | case 0: 269 | where = " (load anywhere)"; 270 | break; 271 | case 1: 272 | where = " (load in fast RAM)"; 273 | break; 274 | case 2: 275 | where = " (load in chip RAM)"; 276 | break; 277 | case 3: 278 | where = " (load .. specific flags)"; 279 | read_int32(in); 280 | break; 281 | } 282 | } 283 | 284 | printf("-- %s%s offset=%ld --\n", 285 | get_hunk_name(hunk_type), where, offset); 286 | 287 | switch (hunk_type) 288 | { 289 | case HUNK_UNIT: 290 | case HUNK_NAME: 291 | running = 0; 292 | break; 293 | case HUNK_CODE: 294 | parse_hunk_code(in); 295 | break; 296 | case HUNK_DATA: 297 | case HUNK_BSS: 298 | running = 0; 299 | break; 300 | case HUNK_RELOC32: 301 | parse_hunk_reloc32(in); 302 | break; 303 | case HUNK_RELOC16: 304 | case HUNK_RELOC8: 305 | case HUNK_EXT: 306 | running = 0; 307 | break; 308 | case HUNK_SYMBOL: 309 | parse_hunk_symbol(in); 310 | break; 311 | case HUNK_DEBUG: 312 | case HUNK_END: 313 | running = 0; 314 | break; 315 | case HUNK_HEADER: 316 | parse_hunk_header(in); 317 | break; 318 | case HUNK_OVERLAY: 319 | case HUNK_BREAK: 320 | case HUNK_DREL32: 321 | case HUNK_DREL16: 322 | case HUNK_DREL8: 323 | case HUNK_LIB: 324 | case HUNK_INDEX: 325 | case HUNK_RELOC32SHORT: 326 | case HUNK_RELRELOC32: 327 | case HUNK_ABSRELOC16: 328 | case HUNK_PPC_CODE: 329 | case HUNK_RELRELOC26: 330 | default: 331 | running = 0; 332 | break; 333 | } 334 | } 335 | 336 | fclose(in); 337 | 338 | return 0; 339 | } 340 | 341 | -------------------------------------------------------------------------------- /src/partition.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "affs.h" 20 | #include "partition.h" 21 | #include "fileio.h" 22 | 23 | int read_partition( 24 | FILE *in, 25 | struct _amiga_bootblock *bootblock, 26 | struct _amiga_partition *partition) 27 | { 28 | uint32_t cylindar_size; 29 | int namelen; 30 | 31 | // Should be "PART" 32 | read_chars(in, partition->magic, 4); 33 | 34 | if (memcmp(partition->magic, "PART", 4) != 0) 35 | { 36 | printf("Error: Partition's magic number is '%c%c%c%c' instead of'PART'\n", 37 | partition->magic[0], 38 | partition->magic[1], 39 | partition->magic[2], 40 | partition->magic[3]); 41 | } 42 | 43 | partition->size = read_int(in); 44 | partition->checksum = read_int(in); 45 | partition->scsihost = read_int(in); 46 | partition->next = read_int(in); 47 | partition->flags = read_int(in); 48 | //uint32_t partition->unused1[2]; 49 | fseek(in, 4 * 2, SEEK_CUR); 50 | //read_int(in); 51 | //read_int(in); 52 | partition->devflags = read_int(in); 53 | namelen = getc(in); 54 | 55 | read_chars(in, partition->name, 31); 56 | partition->name[namelen] = 0; 57 | 58 | //uint32_t partition->unused2[15]; 59 | //uint32_t partition->unused3[3]; 60 | fseek(in, 4 * 15, SEEK_CUR); 61 | //partition->heads = read_int(in); 62 | //partition->unused4 = read_int(in); 63 | //partition->block_per_track = read_int(in); 64 | //uint32_t partition->unused5[3]; 65 | //fseek(in, 4*3, SEEK_CUR); 66 | //partition->lowcyl = read_int(in); 67 | //partition->highcyl = read_int(in); 68 | //partition->firstcyl = read_int(in); 69 | 70 | //fseek(in, 0x3a, SEEK_CUR); 71 | 72 | partition->table_size = read_int(in); 73 | partition->size_block = read_int(in); 74 | partition->sec_org = read_int(in); 75 | partition->heads = read_int(in); 76 | partition->sectors_per_block = read_int(in); 77 | partition->blocks_per_track = read_int(in); 78 | partition->num_reserved = read_int(in); 79 | partition->pre_alloc = read_int(in); 80 | partition->interleave = read_int(in); 81 | partition->start_cyl = read_int(in); 82 | partition->end_cyl = read_int(in); 83 | partition->num_buffers = read_int(in); 84 | partition->buff_type = read_int(in); 85 | partition->max_transfer = read_int(in); 86 | partition->mask = read_int(in); 87 | partition->boot_priority = read_int(in); 88 | 89 | read_chars(in, partition->type, 4); 90 | 91 | if (partition->type[0] == 'D' && 92 | partition->type[1] == 'O' && 93 | partition->type[2] == 'S') 94 | { 95 | partition->type[3] += '0'; 96 | } 97 | 98 | partition->baud = read_int(in); 99 | partition->control = read_int(in); 100 | partition->bootblocks = read_int(in); 101 | 102 | cylindar_size = 103 | bootblock->blksz * 104 | partition->heads * 105 | partition->sectors_per_block * 106 | partition->blocks_per_track; 107 | 108 | partition->start = (uint64_t)cylindar_size * (uint64_t)partition->start_cyl; 109 | 110 | partition->size_in_bytes = 111 | (uint64_t)(partition->end_cyl-partition->start_cyl + 1) * 112 | (uint64_t)cylindar_size; 113 | partition->end = partition->start + partition->size_in_bytes; 114 | 115 | return 0; 116 | } 117 | 118 | void print_partition(struct _amiga_partition *partition) 119 | { 120 | printf("===================== Partition ======================\n"); 121 | printf(" magic: %4.4s\n", partition->magic); 122 | printf(" size: %d\n", partition->size); 123 | printf(" checksum: %d\n", partition->checksum); 124 | printf(" scsihost: %d\n", partition->scsihost); 125 | printf(" next: %d\n", partition->next); 126 | printf(" flags: %d\n", partition->flags); 127 | // partition->unused1[2]; 128 | printf(" devflags: %d\n", partition->devflags); 129 | printf(" name: %s\n", partition->name); 130 | printf(" table_size: %d\n", partition->table_size); 131 | printf(" size_block: %d\n", partition->size_block); 132 | printf(" sec_org: %d\n", partition->sec_org); 133 | printf(" heads: %d\n", partition->heads); 134 | printf("sectors_per_block: %d\n", partition->sectors_per_block); 135 | printf(" blocks_per_track: %d\n", partition->blocks_per_track); 136 | printf(" num_reserved: %d\n", partition->num_reserved); 137 | printf(" pre_alloc: %d\n", partition->pre_alloc); 138 | printf(" interleave: %d\n", partition->interleave); 139 | printf(" start_cyl: %d (addr: %" PRId64 ")\n", partition->start_cyl, partition->start); 140 | printf(" end_cyl: %d (addr: %" PRId64 ")\n", partition->end_cyl, partition->end); 141 | printf(" %" PRId64 " bytes\n", partition->size_in_bytes); 142 | printf(" num_buffers: %d\n", partition->num_buffers); 143 | printf(" buff_type: %d\n", partition->buff_type); 144 | printf(" max_transfer: %d\n", partition->max_transfer); 145 | printf(" mask: %d\n", partition->mask); 146 | printf(" boot_priority: %d\n", partition->boot_priority); 147 | 148 | printf(" type: %4.4s\n", partition->type); 149 | 150 | printf(" baud: %d\n", partition->baud); 151 | printf(" control: %d\n", partition->control); 152 | printf(" bootblocks: %d\n", partition->bootblocks); 153 | 154 | printf("\n"); 155 | } 156 | 157 | void print_partition_list(FILE *in, struct _amiga_bootblock *bootblock) 158 | { 159 | struct _amiga_partition partition; 160 | int count, t; 161 | 162 | count = 0; 163 | t = bootblock->partitionlst; 164 | 165 | while (t > 0) 166 | { 167 | fseek(in, t * 512, SEEK_SET); 168 | read_partition(in, bootblock, &partition); 169 | print_partition(&partition); 170 | 171 | t = partition.next; 172 | count++; 173 | } 174 | } 175 | 176 | void show_partitions(FILE *in, struct _amiga_bootblock *bootblock) 177 | { 178 | struct _amiga_partition partition; 179 | int count, t; 180 | 181 | count = 0; 182 | t = bootblock->partitionlst; 183 | 184 | printf("%-20s %4s %10s %10s %12s %12s\n", 185 | "Name", "Type", "Start Cyl", "End Cyl", "Offset", "Size"); 186 | 187 | while (t > 0) 188 | { 189 | fseek(in, t * 512, SEEK_SET); 190 | read_partition(in, bootblock, &partition); 191 | 192 | printf("%-20s %4.4s %10d %10d %12" PRId64 " %12" PRId64 "\n", 193 | partition.name, 194 | partition.type, 195 | partition.start_cyl, 196 | partition.end_cyl, 197 | partition.start, 198 | partition.size_in_bytes); 199 | 200 | t = partition.next; 201 | count++; 202 | } 203 | } 204 | 205 | int dump_partition( 206 | FILE *in, 207 | struct _amiga_bootblock *bootblock, 208 | int num, 209 | const char *filename) 210 | { 211 | struct _amiga_partition partition; 212 | int count,t; 213 | FILE *out; 214 | uint32_t size; 215 | uint8_t buffer[8192]; 216 | 217 | count = 0; 218 | t = bootblock->partitionlst; 219 | 220 | while (t > 0) 221 | { 222 | fseek(in, t * 512, SEEK_SET); 223 | read_partition(in, bootblock, &partition); 224 | //print_partition(&partition); 225 | 226 | if (count == num) 227 | { 228 | printf("Creating %s from partition %d (%s)\n", filename, num, partition.name); 229 | out = fopen(filename, "wb"); 230 | if (out == NULL) 231 | { 232 | printf("Could not open file %s for writing...\n", filename); 233 | return -1; 234 | } 235 | 236 | size = partition.size_in_bytes; 237 | 238 | fseek(in, partition.start, SEEK_SET); 239 | 240 | while (size > 0) 241 | { 242 | if (size > 8192) 243 | { 244 | t = fread(buffer, 1, 8192, in); 245 | if (t < 1) { printf("wtf\n"); break; } 246 | fwrite(buffer, 1, t, out); 247 | size -= t; 248 | } 249 | else 250 | { 251 | t = fread(buffer, 1, size, in); 252 | if (t < 1) { printf("wtf\n"); break; } 253 | fwrite(buffer, 1, t, out); 254 | size -= t; 255 | } 256 | } 257 | 258 | fclose(out); 259 | 260 | return 0; 261 | } 262 | 263 | t=partition.next; 264 | count++; 265 | } 266 | 267 | return -1; 268 | } 269 | 270 | int read_partition_num( 271 | FILE *in, 272 | struct _amiga_bootblock *bootblock, 273 | struct _amiga_partition *partition, 274 | int num) 275 | { 276 | int count, t; 277 | 278 | count = 0; 279 | t = bootblock->partitionlst; 280 | while (t > 0) 281 | { 282 | fseek(in, t * 512, SEEK_SET); 283 | read_partition(in, bootblock, partition); 284 | 285 | if (count == num) 286 | { 287 | return 0; 288 | } 289 | 290 | t = partition->next; 291 | count++; 292 | } 293 | 294 | return -1; 295 | } 296 | 297 | int get_partition_num(FILE *in, struct _amiga_bootblock *bootblock, char *name) 298 | { 299 | struct _amiga_partition partition; 300 | int count, t; 301 | 302 | count = 0; 303 | t = bootblock->partitionlst; 304 | while (t > 0) 305 | { 306 | fseek(in, t * 512, SEEK_SET); 307 | read_partition(in, bootblock, &partition); 308 | 309 | if (strcmp((char *)partition.name, name)==0) 310 | { 311 | return count; 312 | } 313 | 314 | t = partition.next; 315 | count++; 316 | } 317 | 318 | return -1; 319 | } 320 | 321 | -------------------------------------------------------------------------------- /src/amiga_recovery.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amiga Recovery - Recover files from an Amiga AFFS disk image. 4 | 5 | Copyright 2009-2019 - Michael Kohn (mike@mikekohn.net) 6 | http://www.mikekohn.net/ 7 | 8 | Released under GPLv3. 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "affs.h" 21 | #include "copy_all.h" 22 | #include "fileio.h" 23 | 24 | #define CHECKDIR() \ 25 | if (pwd.partition.magic[0] == 0) \ 26 | { \ 27 | printf("No current dir... cd somewhere first.\n"); \ 28 | continue; \ 29 | } 30 | 31 | static void print_help() 32 | { 33 | printf("Commands:\n"); 34 | printf(" print { bootblock, partition, rootblock } [ print debug info ]\n"); 35 | printf(" show partitions [ show partition list ]\n"); 36 | printf(" dump partition [ dump pwd partition in a new file ]\n"); 37 | printf(" cd [ change Amiga drive/dir ]\n"); 38 | printf(" lcd [ change local dir ]\n"); 39 | printf(" dir or ls [ current directory listing ]\n"); 40 | printf(" type or cat [ write a text file to screen ]\n"); 41 | printf(" get [ copy file to local disk ]\n"); 42 | printf(" copy all [ copy all files in partition to dest ]\n"); 43 | printf(" pwd [ show current directory ]\n"); 44 | printf(" help or ?\n"); 45 | printf(" exit or quit\n"); 46 | } 47 | 48 | static void print_prompt(struct _pwd *pwd) 49 | { 50 | if (pwd->partition.magic[0] != 0) 51 | { 52 | printf("%s", pwd->partition.name); 53 | } 54 | 55 | printf(":%s> ", pwd->cwd); 56 | } 57 | 58 | static void print_pwd(struct _pwd *pwd) 59 | { 60 | if (pwd->partition.magic[0] != 0) 61 | { 62 | printf("%s", pwd->partition.name); 63 | } 64 | 65 | printf(":%s\n", pwd->cwd); 66 | } 67 | 68 | static void change_dir( 69 | FILE *in, 70 | struct _amiga_bootblock *bootblock, 71 | struct _pwd *pwd, 72 | char *path) 73 | { 74 | int t; 75 | 76 | if (path[0] == 0) 77 | { 78 | printf("Illegal directory\n"); 79 | return; 80 | } 81 | 82 | t = 0; 83 | 84 | while (path[t] != 0) 85 | { 86 | if (path[t] == ':') 87 | { 88 | path[t] = 0; 89 | int p = get_partition_num(in, bootblock, path); 90 | if (p == -1) 91 | { 92 | printf("No such drive name %s\n", path); 93 | return; 94 | } 95 | 96 | read_partition_num(in, bootblock, &pwd->partition, p); 97 | 98 | if (pwd->partition_num != t) 99 | { 100 | read_rootblock(in, bootblock, &pwd->partition, &pwd->rootblock); 101 | memcpy(pwd->dir_hash, pwd->rootblock.hash_table, (BSIZE / 4 - 56) * 4); 102 | } 103 | 104 | pwd->partition_num = p; 105 | path = path + t + 1; 106 | 107 | break; 108 | } 109 | 110 | t++; 111 | } 112 | 113 | if (path[0] == 0) { pwd->cwd[0] = 0; return; } 114 | 115 | char *s = path; 116 | 117 | while (*path != 0) 118 | { 119 | while (*s != 0 && *s != '/') s++; 120 | 121 | if (*s == 0 && s != path) 122 | { 123 | ch_dir(in, bootblock, pwd, path); 124 | break; 125 | } 126 | else 127 | if (*s == '/') 128 | { 129 | *s = 0; 130 | if (ch_dir(in, bootblock, pwd, path) != 0) break; 131 | s++; 132 | path = s; 133 | } 134 | } 135 | } 136 | 137 | int main(int argc, char *argv[]) 138 | { 139 | FILE *in; 140 | struct _amiga_bootblock bootblock; 141 | struct _pwd pwd; 142 | char command[1024]; 143 | int t; 144 | 145 | printf("Amiga Recovery - 2021-April-21\n"); 146 | printf("Copyright 2009-2021 - Michael Kohn (mike@mikekohn.net)\n"); 147 | printf("http://www.mikekohn.net/\n\n"); 148 | 149 | if (argc < 2) 150 | { 151 | printf("Usage: %s \n",argv[0]); 152 | exit(0); 153 | } 154 | 155 | in = fopen(argv[1], "rb"); 156 | 157 | if (in == NULL) 158 | { 159 | printf("Could not open file %s for reading.\n", argv[1]); 160 | exit(1); 161 | } 162 | 163 | memset(&pwd, 0, sizeof(pwd)); 164 | 165 | t = read_bootblock(in, &bootblock); 166 | 167 | if (t >= 0) 168 | { 169 | printf("Found bootblock @ block %d\n\n", t); 170 | } 171 | else 172 | { 173 | printf("Error: Could not read bootblock\n\n"); 174 | 175 | uint32_t offset = find_root_block(in, &pwd.rootblock); 176 | 177 | if (offset == 0) 178 | { 179 | printf("Could not find a rootblock either.. aborting..\n\n"); 180 | exit(0); 181 | } 182 | 183 | fseek(in, offset, SEEK_SET); 184 | 185 | memset(&bootblock, 0, sizeof(bootblock)); 186 | bootblock.blksz = 512; 187 | bootblock.partitionlst = 1; 188 | 189 | read_rootblock_data(in, &pwd.rootblock); 190 | memcpy(pwd.dir_hash, pwd.rootblock.hash_table, (BSIZE / 4 - 56) * 4); 191 | 192 | strcpy((char *)pwd.partition.name, (char *)pwd.rootblock.diskname); 193 | memcpy(pwd.partition.magic, "PART", 4); 194 | 195 | fseek(in, SEEK_SET, 0); 196 | read_chars(in, pwd.partition.type, 4); 197 | 198 | if (pwd.partition.type[0] == 'D' && pwd.partition.type[1] == 'O' && 199 | pwd.partition.type[2] == 'S') 200 | { 201 | pwd.partition.type[3] += '0'; 202 | } 203 | } 204 | 205 | printf("Type help for a list of commands.\n"); 206 | command[1023] = 0; 207 | 208 | while (1) 209 | { 210 | print_prompt(&pwd); 211 | char *fu = fgets(command, 1023, stdin); 212 | if (fu == NULL) { printf("wtf gcc\n"); } 213 | 214 | t = 0; 215 | while (command[t] != 0) 216 | { 217 | if (command[t] == '\n' || command[t] == '\r') 218 | { 219 | command[t] = 0; 220 | break; 221 | } 222 | 223 | t++; 224 | } 225 | 226 | if (command[0] == 0) { continue; } 227 | 228 | if (strcmp(command, "help") == 0 || strcmp(command, "?") == 0) 229 | { 230 | print_help(); 231 | } 232 | else 233 | if (strcmp(command, "quit") == 0 || strcmp(command, "exit") == 0) 234 | { 235 | break; 236 | } 237 | else 238 | if (strcmp(command, "show partitions") == 0 || 239 | strcmp(command, "show") == 0) 240 | { 241 | show_partitions(in, &bootblock); 242 | } 243 | else 244 | if (strcmp(command, "print bootblock") == 0 || 245 | strcmp(command, "bootblock") == 0) 246 | { 247 | print_bootblock(&bootblock); 248 | } 249 | else 250 | if (strcmp(command, "print partition") == 0 || 251 | strcmp(command, "partition") == 0) 252 | { 253 | CHECKDIR(); 254 | print_partition(&pwd.partition); 255 | } 256 | else 257 | if (strcmp(command, "print rootblock") == 0 || 258 | strcmp(command, "rootblock") == 0) 259 | { 260 | CHECKDIR(); 261 | struct _amiga_rootblock rootblock; 262 | read_rootblock(in, &bootblock, &pwd.partition, &rootblock); 263 | print_rootblock(&rootblock); 264 | } 265 | else 266 | if (strcmp(command, "pwd") == 0) 267 | { 268 | print_pwd(&pwd); 269 | } 270 | else 271 | if (strncmp(command, "cd ", 3) == 0) 272 | { 273 | change_dir(in, &bootblock, &pwd, command + 3); 274 | } 275 | else 276 | if (strncmp(command, "type ", 5) == 0) 277 | { 278 | CHECKDIR(); 279 | char *filename = command + 5; 280 | if (filename[0] == 0) 281 | { 282 | printf("Error: No filename specified.\n"); 283 | } 284 | else 285 | { 286 | copy_file(in, &bootblock, &pwd, filename, NULL); 287 | } 288 | } 289 | else 290 | if (strncmp(command, "cat ", 4) == 0) 291 | { 292 | CHECKDIR(); 293 | char *filename = command + 4; 294 | if (filename[0] == 0) 295 | { 296 | printf("Error: No filename specified.\n"); 297 | } 298 | else 299 | { 300 | copy_file(in, &bootblock, &pwd, filename, NULL); 301 | } 302 | } 303 | else 304 | if (strncmp(command, "get ", 4) == 0) 305 | { 306 | CHECKDIR(); 307 | char *filename = command + 4; 308 | if (filename[0] == 0) 309 | { 310 | printf("Error: No filename specified.\n"); 311 | } 312 | else 313 | { 314 | FILE *out = fopen(filename, "rb"); 315 | if (out != NULL) 316 | { 317 | printf("Error: File exists on local disk. Will not overwrite.\n"); 318 | fclose(out); 319 | } 320 | else 321 | { 322 | out = fopen(filename, "wb"); 323 | copy_file(in, &bootblock, &pwd, filename, out); 324 | fclose(out); 325 | printf("Saved!\n"); 326 | } 327 | } 328 | } 329 | else 330 | if (strncmp(command, "lcd ", 4) == 0) 331 | { 332 | char *dir = command + 4; 333 | if (dir[0] == 0) 334 | { 335 | printf("Error: No filename specified.\n"); 336 | } 337 | else 338 | { 339 | if (chdir(dir) == 0) 340 | { 341 | printf("Local directory now: %s\n", dir); 342 | } 343 | else 344 | { 345 | printf("Error: No such directory?\n"); 346 | } 347 | } 348 | } 349 | else 350 | if (strcmp(command, "dir") == 0 || strcmp(command, "ls") == 0) 351 | { 352 | CHECKDIR(); 353 | list_directory(in, &bootblock, &pwd); 354 | } 355 | else 356 | if (strncmp(command, "copy all ", sizeof("copy all ") - 1) == 0) 357 | { 358 | char *path = command + sizeof("copy all ") - 1; 359 | int len = strlen(path); 360 | 361 | if (len != 0) { len--; } 362 | if (path[len] == '/') { path[len] = 0; } 363 | 364 | if (path[0] == 0) 365 | { 366 | printf("Illegal directory\n"); 367 | continue; 368 | } 369 | 370 | CHECKDIR(); 371 | 372 | if (mkdir(path, 0777) != 0) 373 | { 374 | if (errno != EEXIST) 375 | { 376 | printf("Could not create directory %s\n", path); 377 | continue; 378 | } 379 | } 380 | 381 | if (copy_all(in, &bootblock, &pwd, path) != 0) 382 | { 383 | printf("Errors encountered while copying all.\n"); 384 | } 385 | } 386 | else 387 | if (strncmp(command, "dump partition ", sizeof("dump partition ") - 1) == 0) 388 | { 389 | char *filename = command + sizeof("dump partition ") - 1; 390 | 391 | if (filename[0] != 0) 392 | { 393 | FILE *fp = fopen(filename, "rb"); 394 | 395 | if (fp != NULL) 396 | { 397 | printf("Error: File %s exists. Will not overwrite.\n", filename); 398 | fclose(fp); 399 | } 400 | else 401 | { 402 | printf("Dumping partition to filename '%s'\n", filename); 403 | dump_partition(in, &bootblock, pwd.partition_num, filename); 404 | } 405 | } 406 | else 407 | { 408 | printf("Illegal filename.\n"); 409 | } 410 | } 411 | else 412 | { 413 | printf("Unknown command '%s'. Type help for a list of commands\n", command); 414 | } 415 | } 416 | 417 | fclose(in); 418 | 419 | return 0; 420 | } 421 | 422 | --------------------------------------------------------------------------------