// license:BSD-3-Clause // copyright-holders:Raphael Nabet /* Handlers for concept floppy images Disk images are in MESS format. Raphael Nabet, 2003 */ #include #include #include #include #include "imgtool.h" struct UINT16xE { uint8_t bytes[2]; }; /* get_UINT16xE Read a 16-bit word, whether it is little-endian or big-endian little_endian (I): non-zero if word is little-endian, zero if word is big-endian word (I): pointer to word to read Returns value of word in native format */ static inline uint16_t get_UINT16xE(int little_endian, UINT16xE word) { return little_endian ? (word.bytes[0] | (word.bytes[1] << 8)) : ((word.bytes[0] << 8) | word.bytes[1]); } #if 0 /* set_UINT16xE Write a 16-bit word, whether it is little-endian or big-endian little_endian (I): non-zero if word is little-endian, zero if word is big-endian word (O): pointer to word to write data (I): value to write in word, in native format */ static inline void set_UINT16xE(int little_endian, UINT16xE *word, uint16_t data) { if (little_endian) { word->bytes[0] = data & 0xff; word->bytes[1] = (data >> 8) & 0xff; } else { word->bytes[0] = (data >> 8) & 0xff; word->bytes[1] = data & 0xff; } } #endif /* Disk structure: Track 0 Sector 0 & 1: bootstrap loader Track 0 Sector 2 through 5: disk directory Remaining sectors are used for data. */ /* device directory record (Disk sector 2-5) */ struct concept_vol_hdr_entry { UINT16xE first_block; UINT16xE next_block; UINT16xE ftype; unsigned char volname[8]; UINT16xE last_block; UINT16xE num_files; UINT16xE last_boot; UINT16xE last_access; char mem_flipped; char disk_flipped; UINT16xE unused; }; struct concept_file_dir_entry { UINT16xE first_block; UINT16xE next_block; UINT16xE ftype; unsigned char filename[16]; UINT16xE last_byte; UINT16xE last_access; }; struct concept_dev_dir { concept_vol_hdr_entry vol_hdr; concept_file_dir_entry file_dir[77]; char unused[20]; }; /* concept disk image descriptor */ struct concept_image { imgtool::stream *file_handle; /* imgtool file handle */ concept_dev_dir dev_dir; /* cached copy of device directory */ }; /* concept catalog iterator, used when imgtool reads the catalog */ struct concept_iterator { concept_image *image; int index; /* current index */ }; static imgtoolerr_t concept_image_init(imgtool::image &img, imgtool::stream::ptr &&stream); static void concept_image_exit(imgtool::image &img); static void concept_image_info(imgtool::image &img, std::ostream &stream); static imgtoolerr_t concept_image_beginenum(imgtool::directory &enumeration, const char *path); static imgtoolerr_t concept_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent); static void concept_image_closeenum(imgtool::directory &enumeration); static imgtoolerr_t concept_image_freespace(imgtool::partition &partition, uint64_t *size); static imgtoolerr_t concept_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf); #if 0 static imgtoolerr_t concept_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream *sourcef, util::option_resolution *writeoptions); static imgtoolerr_t concept_image_deletefile(imgtool::partition &partition, const char *filename); static imgtoolerr_t concept_image_create(const imgtool_module *mod, imgtool::stream *f, util::option_resolution *createoptions); #endif void concept_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_IMAGE_EXTRA_BYTES: info->i = sizeof(concept_image); break; case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(concept_iterator); break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "concept"); break; case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Concept floppy disk image"); break; case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "img"); break; case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case IMGTOOLINFO_PTR_OPEN: info->open = concept_image_init; break; case IMGTOOLINFO_PTR_CLOSE: info->close = concept_image_exit; break; case IMGTOOLINFO_PTR_INFO: info->info = concept_image_info; break; case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = concept_image_beginenum; break; case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = concept_image_nextenum; break; case IMGTOOLINFO_PTR_CLOSE_ENUM: info->close_enum = concept_image_closeenum; break; case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = concept_image_freespace; break; case IMGTOOLINFO_PTR_READ_FILE: info->read_file = concept_image_readfile; break; case IMGTOOLINFO_PTR_WRITE_FILE: /* info->write_file = concept_image_writefile */; break; case IMGTOOLINFO_PTR_DELETE_FILE: /* info->delete_file = concept_image_deletefile */; break; case IMGTOOLINFO_PTR_CREATE: /* info->create = concept_image_create */; break; } } static concept_image *get_concept_image(imgtool::image &image) { return (concept_image *)image.extra_bytes(); } /* read_physical_record Read one 512-byte physical record from a disk image file_handle: imgtool file handle secnum: physical record address dest: pointer to destination buffer Return non-zero on error */ static int read_physical_record(imgtool::stream &file_handle, int secnum, void *dest) { int reply; /* seek to sector */ reply = file_handle.seek(secnum*512, SEEK_SET); if (reply) return 1; /* read it */ reply = file_handle.read(dest, 512); if (reply != 512) return 1; return 0; } #ifdef UNUSED_FUNCTION /* write_physical_record Write one 512-byte physical record to a disk image file_handle: imgtool file handle secnum: logical sector address src: pointer to source buffer Return non-zero on error */ static int write_physical_record(imgtool::stream *file_handle, int secnum, const void *src) { int reply; /* seek to sector */ reply = file_handle->seek(secnum*512, SEEK_SET); if (reply) return 1; /* read it */ reply = file_handle->write(src, 512); if (reply != 512) return 1; return 0; } #endif /* Search for a file name on a concept_image image (I): image reference filename (I): name of the file to search entry_index (O): index of file in disk catalog Return non-zero on error */ static int get_catalog_entry(concept_image *image, const unsigned char *filename, int *entry_index) { int filename_len = filename[0]; int i; if (filename_len > 15) /* file name is bad */ return 1; for (i = 0; i < 77; i++) { if (!memcmp(filename, image->dev_dir.file_dir[i].filename, filename_len+1)) { /* file found */ *entry_index = i; return 0; } } /* file not found */ return 1; } /* Open a file as a concept_image. */ static imgtoolerr_t concept_image_init(imgtool::image &img, imgtool::stream::ptr &&stream) { concept_image *image = get_concept_image(img); int reply; int i; unsigned totphysrecs; /* read device directory */ for (i=0; i<4; i++) { reply = read_physical_record(*stream, i+2, ((char *) & image->dev_dir)+i*512); if (reply) return IMGTOOLERR_READERROR; } /* do primitive checks */ totphysrecs = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.last_block) - get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.first_block); if ((get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.first_block) != 0) || (get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.next_block) != 6) || (totphysrecs < 6) /*|| (f->size() != totphysrecs*512)*/ || (image->dev_dir.vol_hdr.volname[0] > 7)) { return IMGTOOLERR_CORRUPTIMAGE; } image->file_handle = stream.release(); return IMGTOOLERR_SUCCESS; } /* close a concept_image */ static void concept_image_exit(imgtool::image &img) { /*concept_image *image = get_concept_image(img);*/ } /* get basic information on a concept_image Currently returns the volume name */ static void concept_image_info(imgtool::image &img, std::ostream &stream) { concept_image *image = get_concept_image(img); char vol_name[8]; memcpy(vol_name, image->dev_dir.vol_hdr.volname + 1, image->dev_dir.vol_hdr.volname[0]); vol_name[image->dev_dir.vol_hdr.volname[0]] = 0; stream << vol_name; } /* Open the disk catalog for enumeration */ static imgtoolerr_t concept_image_beginenum(imgtool::directory &enumeration, const char *path) { concept_iterator *iter; iter = (concept_iterator *) enumeration.extra_bytes(); iter->image = (concept_image *) enumeration.image().extra_bytes(); iter->index = 0; return IMGTOOLERR_SUCCESS; } /* Enumerate disk catalog next entry */ static imgtoolerr_t concept_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) { concept_iterator *iter = (concept_iterator *) enumeration.extra_bytes(); ent.corrupt = 0; ent.eof = 0; if ((iter->image->dev_dir.file_dir[iter->index].filename[0] == 0) || (iter->index > 77)) { ent.eof = 1; } else if (iter->image->dev_dir.file_dir[iter->index].filename[0] > 15) { ent.corrupt = 1; } else { int len = iter->image->dev_dir.file_dir[iter->index].filename[0]; const char *type; if (len > ARRAY_LENGTH(ent.filename)) len = ARRAY_LENGTH(ent.filename); memcpy(ent.filename, iter->image->dev_dir.file_dir[iter->index].filename + 1, len); ent.filename[len] = 0; /* parse flags */ switch (get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].ftype) & 0xf) { case 0: case 8: type = "DIRHDR"; break; case 2: type = "CODE"; break; case 3: type = "TEXT"; break; case 5: type = "DATA"; break; default: type = "???"; break; } snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%s", type); /* len in physrecs */ ent.filesize = get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].next_block) - get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].first_block); iter->index++; } return (imgtoolerr_t)0; } /* Free enumerator */ static void concept_image_closeenum(imgtool::directory &enumeration) { } /* Compute free space on disk image */ static imgtoolerr_t concept_image_freespace(imgtool::partition &partition, uint64_t *size) { imgtool::image &img(partition.image()); concept_image *image = get_concept_image(img); int free_blocks; int i; /* first get number of data blocks */ free_blocks = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.last_block) - get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.next_block); /* next substract length of each file */ for (i=0; (image->dev_dir.file_dir[i].filename[0] != 0) && (i <= 77); i++) { free_blocks -= get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[i].next_block) - get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[i].first_block); } *size = free_blocks; return IMGTOOLERR_SUCCESS; } /* Extract a file from a concept_image. */ static imgtoolerr_t concept_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) { imgtool::image &img(partition.image()); concept_image *image = get_concept_image(img); size_t filename_len = strlen(filename); unsigned char concept_fname[16]; int catalog_index; int i; uint8_t buf[512]; if (filename_len > 15) return IMGTOOLERR_BADFILENAME; concept_fname[0] = filename_len; memcpy(concept_fname+1, filename, filename_len); if (get_catalog_entry(image, concept_fname, &catalog_index)) return IMGTOOLERR_FILENOTFOUND; for (i = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[catalog_index].first_block); i < get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[catalog_index].next_block); i++) { if (read_physical_record(*image->file_handle, i, buf)) return IMGTOOLERR_READERROR; if (destf.write(buf, 512) != 512) return IMGTOOLERR_WRITEERROR; } return (imgtoolerr_t)0; } #if 0 /* Add a file to a concept_image. */ static imgtoolerr_t concept_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream *sourcef, util::option_resolution *writeoptions) { /* ... */ return 0; } /* Delete a file from a concept_image. */ static imgtoolerr_t concept_image_deletefile(imgtool::partition &partition, const char *filename) { /* ... */ return 0; } /* Create a blank concept_image. */ static imgtoolerr_t concept_image_create(const imgtool_module *mod, imgtool::stream *f, util::option_resolution *createoptions) { /* ... */ return 0; } #endif