// license:GPL-2.0+ // copyright-holders:Jonathan Edwards /**************************************************************************** bml3.c Hitachi bml3 disk images By Jonathan Edwards, based on rsdos.c (both use Microsoft BASIC) ****************************************************************************/ /* Supported Hitachi floppy formats are: - 3" or 5"1/4 single density, single-sided: 40 tracks, 16 sectors/track, 128 bytes/sector - (used with MP-1805 floppy disk controller card) - 5"1/4 double density, double-sided: 40 tracks, 16 sectors/track, 256 bytes/sector - (first track on first head may be single density) - (used with MP-1802 floppy disk controller card) */ #include #include #include #include "imgtool.h" #include "iflopimg.h" #define MAX_SECTOR_SIZE 256 struct bml3_diskinfo { uint16_t sector_size; /* 128 or 256 */ uint8_t heads; /* 1 or 2 */ uint8_t fat_start_sector; /* in cylinder 20, start sector of FAT */ uint8_t fat_start_offset; /* start byte of FAT in sector */ uint8_t fat_sectors; /* the number of sectors in the FAT */ uint8_t dirent_start_sector; /* in cylinder 20, start sector of directory entries */ uint8_t granule_sectors; /* how many sectors per granule */ uint8_t first_granule_cylinder; /* the number of the first cylinder with granule numbers assigned */ uint8_t variant; /* 0 - older version, uses EOF to terminate files, 1 - newer version, stores file length */ }; /* this structure mirrors the structure of a directory entry on disk */ struct bml3_dirent { char fname[8]; char fext[3]; uint8_t ftype; uint8_t asciiflag; uint8_t first_granule; uint16_t lastsectorbytes; // TODO there are some 'unused' bytes here that are sometimes used to store a timestamp, maybe support this? }; struct bml3_direnum { int index; int eof; }; #define MAX_GRANULEMAP_SIZE 256 struct granule_list_t { uint8_t granules[MAX_GRANULEMAP_SIZE]; uint8_t granule_count; uint8_t last_granule_sectors; }; #define BML3_OPTIONS_FTYPE 'T' #define BML3_OPTIONS_ASCII 'M' static imgtoolerr_t bml3_diskimage_deletefile(imgtool::partition &partition, const char *fname); /********************************************************************* Imgtool module code *********************************************************************/ static bml3_diskinfo *bml3_get_diskinfo(imgtool::image &image) { return (bml3_diskinfo *) imgtool_floppy_extrabytes(image); } static int max_dirents(imgtool::image &image) { bml3_diskinfo *info = bml3_get_diskinfo(image); return (16 * info->heads + 1 - info->dirent_start_sector)*(info->sector_size/32); } static void dirent_location(imgtool::image &image, int index_loc, uint8_t *head, uint8_t *track, uint8_t *sector, uint8_t *offset) { bml3_diskinfo *info = bml3_get_diskinfo(image); *track = 20; *sector = info->dirent_start_sector + index_loc / (info->sector_size / 32); *head = 0; if (*sector > 16) { // wrap to second head *sector -= 16; (*head)++; } *offset = index_loc % (info->sector_size/32) * 32; } static floperr_t get_bml3_dirent(imgtool::image &f, int index_loc, struct bml3_dirent *ent) { floperr_t err; uint8_t head, track, sector, offset; uint8_t buf[32]; bml3_diskinfo *info = bml3_get_diskinfo(f); dirent_location(f, index_loc, &head, &track, §or, &offset); err = floppy_read_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf)); memset(ent, 0, sizeof(*ent)); switch (info->variant) { case 0: memcpy(&ent->fname, &buf[0], 8); ent->ftype = buf[11]; ent->asciiflag = buf[12]; ent->first_granule = buf[14]; break; case 1: memcpy(&ent->fname, &buf[0], 8); memcpy(&ent->fext, &buf[8], 3); ent->ftype = buf[11]; ent->asciiflag = buf[12]; ent->first_granule = buf[13]; ent->lastsectorbytes = (buf[14] << 8) | buf[15]; break; default: return FLOPPY_ERROR_INVALIDIMAGE; } return err; } static floperr_t put_bml3_dirent(imgtool::image &f, int index_loc, const struct bml3_dirent *ent) { floperr_t err; uint8_t head, track, sector, offset; uint8_t buf[32]; bml3_diskinfo *info = bml3_get_diskinfo(f); if (index_loc >= max_dirents(f)) return (floperr_t)IMGTOOLERR_FILENOTFOUND; dirent_location(f, index_loc, &head, &track, §or, &offset); memset(buf, 0, sizeof(buf)); switch (info->variant) { case 0: memcpy(&buf[0], &ent->fname, 8); buf[11] = ent->ftype; buf[12] = ent->asciiflag; buf[14] = ent->first_granule; break; case 1: memcpy(&buf[0], &ent->fname, 8); memcpy(&buf[8], &ent->fext, 3); buf[11] = ent->ftype; buf[12] = ent->asciiflag; buf[13] = ent->first_granule; buf[14] = ent->lastsectorbytes >> 8; buf[15] = ent->lastsectorbytes & 0xff; break; default: return FLOPPY_ERROR_INVALIDIMAGE; } err = floppy_write_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf), 0); /* TODO: pass ddam argument from imgtool */ return err; } /* fnamebuf must have at least 13 bytes */ static void get_dirent_fname(char *fnamebuf, const struct bml3_dirent *ent) { char *s; memset(fnamebuf, 0, 13); memcpy(fnamebuf, ent->fname, sizeof(ent->fname)); rtrim(fnamebuf); s = fnamebuf + strlen(fnamebuf); *(s++) = '.'; memcpy(s, ent->fext, sizeof(ent->fext)); rtrim(s); /* If no extension, remove period */ if (*s == '\0') s[-1] = '\0'; } static imgtoolerr_t lookup_bml3_file(imgtool::image &f, const char *fname, struct bml3_dirent *ent, int *position) { int i; floperr_t ferr; char fnamebuf[13]; i = 0; fnamebuf[0] = '\0'; do { do { ferr = get_bml3_dirent(f, i++, ent); if (ferr) return imgtool_floppy_error(ferr); } while(ent->fname[0] == '\0'); if (ent->fname[0] != -1) get_dirent_fname(fnamebuf, ent); } while((ent->fname[0] != -1) && core_stricmp(fnamebuf, fname)); if (ent->fname[0] == -1) return IMGTOOLERR_FILENOTFOUND; if (position) *position = i - 1; return (imgtoolerr_t)0; } static uint8_t get_granule_count(imgtool::image &img) { // uint16_t tracks; uint16_t disk_granules; bml3_diskinfo *info = bml3_get_diskinfo(img); // This always returns 82 for D88, so not quite right // tracks = floppy_get_tracks_per_disk(imgtool_floppy(img)); // The number of granules is primarily constrained by the disk capacity. disk_granules = (40 - 1 - info->first_granule_cylinder) * info->heads * (16 / info->granule_sectors); // Also, granule numbers from 0xC0 upwards are reserved for terminating a granule chain return (uint8_t)((disk_granules < 0xC0) ? disk_granules : 0xC0); } /* granule_map must be an array of MAX_GRANULEMAP_SIZE bytes */ static floperr_t get_granule_map(imgtool::image &img, uint8_t *granule_map, uint8_t *granule_count) { bml3_diskinfo *info = bml3_get_diskinfo(img); uint8_t count; count = get_granule_count(img); if (granule_count) *granule_count = count; // The first byte of the granule map sector is ignored (and expected to be 0) return floppy_read_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, count); } static floperr_t put_granule_map(imgtool::image &img, const uint8_t *granule_map, uint8_t granule_count) { bml3_diskinfo *info = bml3_get_diskinfo(img); return floppy_write_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, granule_count, 0); /* TODO: pass ddam argument from imgtool */ } static void granule_location(imgtool::image &image, uint8_t granule, uint8_t *head, uint8_t *track, uint8_t *sector) { bml3_diskinfo *info = bml3_get_diskinfo(image); uint16_t abs_track = granule * info->granule_sectors / 16; *head = abs_track % info->heads; *track = abs_track / info->heads + info->first_granule_cylinder; // skip filesystem cylinder if (*track >= 20) (*track)++; *sector = granule * info->granule_sectors % 16 + 1; } static imgtoolerr_t transfer_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &f, imgtoolerr_t (*proc)(imgtool::image &, int, int, int, int, size_t, imgtool::stream &)) { imgtoolerr_t err = IMGTOOLERR_SUCCESS; uint8_t head, track, sector; granule_location(img, granule, &head, &track, §or); if (length > 0) err = proc(img, head, track, sector, 0, length, f); return err; } static imgtoolerr_t transfer_from_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &destf) { return transfer_granule(img, granule, length, destf, imgtool_floppy_read_sector_to_stream); } static imgtoolerr_t transfer_to_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &sourcef) { return transfer_granule(img, granule, length, sourcef, imgtool_floppy_write_sector_from_stream); } static floperr_t read_granule(imgtool::image &img, uint8_t granule, int offset, int length, uint8_t *buf) { uint8_t head, track, sector; granule_location(img, granule, &head, &track, §or); return floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buf, length); } static floperr_t write_granule(imgtool::image &img, uint8_t granule, int offset, int length, const uint8_t *buf) { uint8_t head, track, sector; granule_location(img, granule, &head, &track, §or); return floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buf, length, 0); /* TODO: pass ddam argument from imgtool */ } static imgtoolerr_t list_granules(struct bml3_dirent *ent, imgtool::image &img, struct granule_list_t *granule_list) { floperr_t ferr; uint8_t max_granules; uint8_t granule; uint8_t usedmap[MAX_GRANULEMAP_SIZE]; /* Used to detect infinite loops */ uint8_t granule_map[MAX_GRANULEMAP_SIZE]; bml3_diskinfo *info = bml3_get_diskinfo(img); ferr = get_granule_map(img, granule_map, &max_granules); if (ferr) return imgtool_floppy_error(ferr); memset(usedmap, 0, max_granules); granule = ent->first_granule; granule_list->granule_count = 0; while(!usedmap[granule] && granule < max_granules) { usedmap[granule] = 1; granule_list->granules[granule_list->granule_count++] = granule; granule = granule_map[granule]; } granule_list->last_granule_sectors = granule - 0xc0; if (info->variant == 0) { // add final incomplete sector granule_list->last_granule_sectors++; } // A value of zero (variant 1) and max (variant 0) seem to indicate a file open for writing. // Strictly speaking this means the image is corrupt, although a real system will happily read // garbage from the file. if (granule_list->last_granule_sectors > info->granule_sectors) return IMGTOOLERR_CORRUPTIMAGE; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t get_file_size(struct bml3_dirent *ent, imgtool::image &img, const struct granule_list_t *granule_list, size_t *size) { floperr_t ferr; size_t last_sector_bytes = 0; bml3_diskinfo *info = bml3_get_diskinfo(img); // TODO are these special cases valid, or maybe indicate a corrupt image? if (granule_list->granule_count == 0) { *size = 0; return IMGTOOLERR_SUCCESS; } else if (granule_list->last_granule_sectors == 0) { *size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors); return IMGTOOLERR_SUCCESS; } // determine size excluding final sector *size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors + granule_list->last_granule_sectors - 1); // determine bytes used in final sector switch (info->variant) { case 0: // look for EOF (ASCII SUB) and trailing NULs in final sector { uint8_t buf[MAX_SECTOR_SIZE]; ferr = read_granule(img, granule_list->granules[granule_list->granule_count-1], info->sector_size * (granule_list->last_granule_sectors - 1), info->sector_size, buf); if (ferr) return imgtool_floppy_error(ferr); for (last_sector_bytes = info->sector_size - 1; ; last_sector_bytes--) { if (buf[last_sector_bytes] != 0) break; if (last_sector_bytes == 0) break; } if (buf[last_sector_bytes] != 0x1a) { last_sector_bytes++; } } break; case 1: last_sector_bytes = ent->lastsectorbytes; break; } // TODO is it valid for last_sector_bytes == 0? if (last_sector_bytes > info->sector_size) { return IMGTOOLERR_CORRUPTIMAGE; } *size += last_sector_bytes; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t process_bml3_file(struct bml3_dirent *ent, imgtool::image &img, imgtool::stream *destf, size_t *size) { imgtoolerr_t err; size_t remaining_size, granule_size; bml3_diskinfo *info = bml3_get_diskinfo(img); struct granule_list_t granule_list; granule_list.granule_count = 0; err = list_granules(ent, img, &granule_list); if (err) return err; err = get_file_size(ent, img, &granule_list, size); if (err) return err; if (destf) { remaining_size = *size; granule_size = info->granule_sectors * info->sector_size; for (int c = 0; c < granule_list.granule_count; c++) { if (granule_size >= remaining_size) granule_size = remaining_size; transfer_from_granule(img, granule_list.granules[c], granule_size, *destf); remaining_size -= granule_size; } } return IMGTOOLERR_SUCCESS; } /* create a new directory entry with a specified name */ static imgtoolerr_t prepare_dirent(uint8_t variant, struct bml3_dirent *ent, const char *fname) { const char *fname_end; const char *fname_ext; int fname_ext_len; memset(ent, '\0', sizeof(*ent)); memset(ent->fname, ' ', sizeof(ent->fname)); memset(ent->fext, ' ', sizeof(ent->fext)); fname_end = strchr(fname, '.'); if (fname_end) fname_ext = fname_end + 1; else fname_end = fname_ext = fname + strlen(fname); fname_ext_len = strlen(fname_ext); switch (variant) { case 0: /* 8-character max filename */ if (((fname_end - fname) > 8) || (fname_ext_len > 0)) return IMGTOOLERR_BADFILENAME; break; case 1: /*8.3 filename */ if (((fname_end - fname) > 8) || (fname_ext_len > 3)) return IMGTOOLERR_BADFILENAME; break; default: return IMGTOOLERR_CORRUPTIMAGE; } memcpy(ent->fname, fname, fname_end - fname); memcpy(ent->fext, fname_ext, fname_ext_len); /* By default, set as a type 2 binary file */ ent->ftype = 2; ent->asciiflag = 0; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t bml3_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy) { // imgtoolerr_t err; floperr_t ferr; bml3_diskinfo *info = bml3_get_diskinfo(image); floppy_image_legacy *floppy = imgtool_floppy(image); const struct FloppyCallbacks *callbacks = floppy_callbacks(floppy); // probe disk geometry to guess format int heads_per_disk = callbacks->get_heads_per_disk(floppy); uint32_t sector_length; ferr = callbacks->get_sector_length(floppy, 0, 20, 1, §or_length); if (ferr) return imgtool_floppy_error(ferr); int sectors_per_track = callbacks->get_sectors_per_track(floppy, 0, 20); if (heads_per_disk == 2 && sector_length == 128 && sectors_per_track == 16) { // single-sided, single-density info->sector_size = 128; info->heads = 1; info->fat_start_sector = 1; info->fat_start_offset = 5; info->fat_sectors = 2; info->dirent_start_sector = 7; info->granule_sectors = 4; info->first_granule_cylinder = 0; info->variant = 0; } else if (heads_per_disk == 2 && sector_length == 256 && sectors_per_track == 16) { // double-sided, double-density info->sector_size = 256; info->heads = 2; info->fat_start_sector = 2; info->fat_start_offset = 1; info->fat_sectors = 1; info->dirent_start_sector = 5; info->granule_sectors = 8; info->first_granule_cylinder = 1; info->variant = 1; } else { // invalid or unsupported format return IMGTOOLERR_CORRUPTIMAGE; } return IMGTOOLERR_SUCCESS; } static imgtoolerr_t bml3_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) { floperr_t ferr; imgtoolerr_t err; size_t filesize; struct bml3_direnum *rsenum; struct bml3_dirent rsent; char fname[13]; imgtool::image &image(enumeration.image()); rsenum = (struct bml3_direnum *) enumeration.extra_bytes(); /* Did we hit the end of file before? */ if (rsenum->eof) goto eof; do { if (rsenum->index >= max_dirents(image)) goto eof; ferr = get_bml3_dirent(image, rsenum->index++, &rsent); if (ferr) return imgtool_floppy_error(ferr); } while(rsent.fname[0] == '\0'); /* Now are we at the eof point? */ if (rsent.fname[0] == -1) { rsenum->eof = 1; eof: ent.eof = 1; } else { /* Not the end of file */ err = process_bml3_file(&rsent, image, nullptr, &filesize); if (err) return err; if (filesize == ((size_t) -1)) { /* corrupt! */ ent.filesize = 0; ent.corrupt = 1; } else { ent.filesize = filesize; ent.corrupt = 0; } ent.eof = 0; get_dirent_fname(fname, &rsent); snprintf(ent.filename, ARRAY_LENGTH(ent.filename), "%s", fname); snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%d %c", (int) rsent.ftype, (char) (rsent.asciiflag + 'B')); } return IMGTOOLERR_SUCCESS; } static imgtoolerr_t bml3_diskimage_freespace(imgtool::partition &partition, uint64_t *size) { floperr_t ferr; uint8_t i; size_t s = 0; uint8_t granule_count; uint8_t granule_map[MAX_GRANULEMAP_SIZE]; imgtool::image &image(partition.image()); bml3_diskinfo *info = bml3_get_diskinfo(image); ferr = get_granule_map(image, granule_map, &granule_count); if (ferr) return imgtool_floppy_error(ferr); for (i = 0; i < granule_count; i++) { if (granule_map[i] == 0xff) s += (info->granule_sectors * info->sector_size); } *size = s; return (imgtoolerr_t)FLOPPY_ERROR_SUCCESS; } static imgtoolerr_t delete_entry(imgtool::image &img, struct bml3_dirent *ent, int pos) { floperr_t ferr; unsigned char g, i; uint8_t granule_count; uint8_t granule_map[MAX_GRANULEMAP_SIZE]; /* Write a NUL in the filename, marking it deleted */ ent->fname[0] = 0; ferr = put_bml3_dirent(img, pos, ent); if (ferr) return imgtool_floppy_error(ferr); ferr = get_granule_map(img, granule_map, &granule_count); if (ferr) return imgtool_floppy_error(ferr); /* Now free up the granules */ g = ent->first_granule; while (g < granule_count) { i = granule_map[g]; granule_map[g] = 0xff; g = i; } ferr = put_granule_map(img, granule_map, granule_count); if (ferr) return imgtool_floppy_error(ferr); return IMGTOOLERR_SUCCESS; } static imgtoolerr_t bml3_diskimage_readfile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &destf) { imgtoolerr_t err; struct bml3_dirent ent; size_t size; imgtool::image &img(partition.image()); err = lookup_bml3_file(img, fname, &ent, nullptr); if (err) return err; err = process_bml3_file(&ent, img, &destf, &size); if (err) return err; if (size == (size_t) -1) return IMGTOOLERR_CORRUPTIMAGE; return (imgtoolerr_t)0; } static imgtoolerr_t bml3_diskimage_writefile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions) { floperr_t ferr; imgtoolerr_t err; imgtool::image &img(partition.image()); bml3_diskinfo *info = bml3_get_diskinfo(img); struct bml3_dirent ent, ent2; size_t i; uint64_t sz, read_sz; uint64_t freespace = 0; unsigned char *gptr; uint8_t granule_count; uint8_t granule_map[MAX_GRANULEMAP_SIZE]; uint8_t eof_buf[MAX_SECTOR_SIZE]; // one-time setup of eof_buf memset(eof_buf, 0, sizeof(eof_buf)); eof_buf[0] = 0x1A; /* can we write to this image? */ if (floppy_is_read_only(imgtool_floppy(img))) return IMGTOOLERR_READONLY; err = bml3_diskimage_freespace(partition, &freespace); if (err) return err; /* is there enough space? */ sz = read_sz = sourcef.size(); if (info->variant == 0) { // also need to write EOF sz++; } if (sz > freespace) return IMGTOOLERR_NOSPACE; /* setup our directory entry */ err = prepare_dirent(info->variant, &ent, fname); if (err) return err; ent.ftype = writeoptions->lookup_int(BML3_OPTIONS_FTYPE); ent.asciiflag = uint8_t(writeoptions->lookup_int(BML3_OPTIONS_ASCII)) - 1; gptr = &ent.first_granule; ferr = get_granule_map(img, granule_map, &granule_count); if (ferr) return imgtool_floppy_error(ferr); unsigned char g = 0x00; uint32_t granule_bytes = info->granule_sectors * info->sector_size; do { while (granule_map[g] != 0xff) { g++; if ((g >= granule_count) || (g == 0)) return IMGTOOLERR_UNEXPECTED; /* We should have already verified that there is enough space */ } *gptr = g; gptr = &granule_map[g]; i = std::min(read_sz, uint64_t(granule_bytes)); if (i > 0) { err = transfer_to_granule(img, g, i, sourcef); if (err) return err; read_sz -= i; sz -= i; } if (i < info->granule_sectors * info->sector_size && sz > 0) { // write EOF and trailing NULs in the final sector ferr = write_granule(img, g, i, (info->granule_sectors * info->sector_size - i - 1) % info->sector_size + 1, eof_buf); if (ferr) return imgtool_floppy_error(ferr); sz--; i++; } /* Go to next granule */ g++; } while(sz > 0); /* Now that we are done with the file, we need to specify the final entry * in the file allocation table */ *gptr = 0xc0 + ((i + info->sector_size-1) / info->sector_size) - (info->variant == 0 ? 1 : 0); ent.lastsectorbytes = (i - 1) % info->sector_size + 1; /* delete file if it already exists */ err = bml3_diskimage_deletefile(partition, fname); if (err && err != IMGTOOLERR_FILENOTFOUND) return err; /* Now we need to find an empty directory entry */ i = -1; do { ferr = get_bml3_dirent(img, ++i, &ent2); if (ferr) return imgtool_floppy_error(ferr); } while(ent2.fname[0] != '\0' && ent2.fname[0] != -1); ferr = put_bml3_dirent(img, i, &ent); if (ferr) return imgtool_floppy_error(ferr); /* write the granule map back out */ ferr = put_granule_map(img, granule_map, granule_count); if (ferr) return imgtool_floppy_error(ferr); return IMGTOOLERR_SUCCESS; } static imgtoolerr_t bml3_diskimage_deletefile(imgtool::partition &partition, const char *fname) { imgtoolerr_t err; imgtool::image &image(partition.image()); int pos = 0; struct bml3_dirent ent; err = lookup_bml3_file(image, fname, &ent, &pos); if (err) return err; return delete_entry(image, &ent, pos); } static imgtoolerr_t bml3_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) { imgtoolerr_t err; imgtool::image &image(partition.image()); struct bml3_dirent ent; int pos; if (fname) { err = lookup_bml3_file(image, fname, &ent, &pos); if (err) return err; if (ent.asciiflag == 0xFF) { /* ASCII file */ suggestions[0].viability = SUGGESTION_RECOMMENDED; suggestions[0].filter = filter_eoln_getinfo; suggestions[1].viability = SUGGESTION_POSSIBLE; suggestions[1].filter = NULL; } else if (ent.ftype == 0) { /* tokenized BASIC file */ suggestions[0].viability = SUGGESTION_RECOMMENDED; suggestions[0].filter = NULL; suggestions[1].viability = SUGGESTION_POSSIBLE; suggestions[1].filter = filter_bml3bas_getinfo; } } else { suggestions[0].viability = SUGGESTION_RECOMMENDED; suggestions[0].filter = NULL; suggestions[1].viability = SUGGESTION_POSSIBLE; suggestions[1].filter = filter_eoln_getinfo; suggestions[2].viability = SUGGESTION_POSSIBLE; suggestions[2].filter = filter_bml3bas_getinfo; } return IMGTOOLERR_SUCCESS; } /********************************************************************* Imgtool module declaration *********************************************************************/ OPTION_GUIDE_START( bml3_writefile_optionguide ) OPTION_ENUM_START( BML3_OPTIONS_FTYPE, "ftype", "File type" ) OPTION_ENUM( 0, "basic", "Basic" ) OPTION_ENUM( 1, "data", "Data" ) OPTION_ENUM( 2, "binary", "Binary" ) OPTION_ENUM( 3, "assembler", "Assembler Source" ) OPTION_ENUM_END OPTION_ENUM_START( BML3_OPTIONS_ASCII, "ascii", "Ascii flag" ) OPTION_ENUM( 0, "ascii", "Ascii" ) OPTION_ENUM( 1, "binary", "Binary" ) OPTION_ENUM_END OPTION_GUIDE_END void bml3_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) { switch(state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break; case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(bml3_diskinfo); break; case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct bml3_direnum); break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "bml3"); break; case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Basic Master Level 3 format"); break; case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T0-[2]-3;M0-[1]"); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = bml3_diskimage_open; break; case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = bml3_diskimage_nextenum; break; case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = bml3_diskimage_freespace; break; case IMGTOOLINFO_PTR_READ_FILE: info->read_file = bml3_diskimage_readfile; break; case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = bml3_diskimage_writefile; break; case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = bml3_diskimage_deletefile; break; case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = bml3_diskimage_suggesttransfer; break; case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &bml3_writefile_optionguide; break; case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_default; break; } }