summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/tools/imgtool/modules/os9.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/tools/imgtool/modules/os9.c')
-rw-r--r--src/mess/tools/imgtool/modules/os9.c1257
1 files changed, 1257 insertions, 0 deletions
diff --git a/src/mess/tools/imgtool/modules/os9.c b/src/mess/tools/imgtool/modules/os9.c
new file mode 100644
index 00000000000..e1d174a843b
--- /dev/null
+++ b/src/mess/tools/imgtool/modules/os9.c
@@ -0,0 +1,1257 @@
+/****************************************************************************
+
+ os9.c
+
+ CoCo OS-9 disk images
+
+****************************************************************************/
+
+#include <time.h>
+
+#include "imgtool.h"
+#include "formats/imageutl.h"
+#include "formats/coco_dsk.h"
+#include "iflopimg.h"
+
+typedef enum
+{
+ CREATE_NONE,
+ CREATE_FILE,
+ CREATE_DIR
+} creation_policy_t;
+
+typedef struct _os9_diskinfo os9_diskinfo;
+struct _os9_diskinfo
+{
+ UINT32 total_sectors;
+ UINT32 sectors_per_track;
+ UINT32 allocation_bitmap_bytes;
+ UINT32 cluster_size;
+ UINT32 root_dir_lsn;
+ UINT32 owner_id;
+ UINT32 attributes;
+ UINT32 disk_id;
+ UINT32 format_flags;
+ UINT32 bootstrap_lsn;
+ UINT32 bootstrap_size;
+ UINT32 sector_size;
+
+ unsigned int sides : 2;
+ unsigned int double_density : 1;
+ unsigned int double_track : 1;
+ unsigned int quad_track_density : 1;
+ unsigned int octal_track_density : 1;
+
+ char name[33];
+ UINT8 *allocation_bitmap;
+};
+
+
+struct os9_fileinfo
+{
+ unsigned int directory : 1;
+ unsigned int non_sharable : 1;
+ unsigned int public_execute : 1;
+ unsigned int public_write : 1;
+ unsigned int public_read : 1;
+ unsigned int user_execute : 1;
+ unsigned int user_write : 1;
+ unsigned int user_read : 1;
+
+ UINT32 lsn;
+ UINT32 owner_id;
+ UINT32 link_count;
+ UINT32 file_size;
+
+ struct
+ {
+ UINT32 lsn;
+ UINT32 count;
+ } sector_map[48];
+};
+
+
+struct os9_direnum
+{
+ struct os9_fileinfo dir_info;
+ UINT32 index;
+};
+
+
+
+static void pick_string(const void *ptr, size_t offset, size_t length, char *dest)
+{
+ UINT8 b;
+
+ while(length--)
+ {
+ b = ((UINT8 *) ptr)[offset++];
+ *(dest++) = b & 0x7F;
+ if (b & 0x80)
+ length = 0;
+ }
+ *dest = '\0';
+}
+
+
+
+static void place_string(void *ptr, size_t offset, size_t length, const char *s)
+{
+ size_t i;
+ UINT8 b;
+ UINT8 *bptr;
+
+ bptr = (UINT8 *) ptr;
+ bptr += offset;
+
+ bptr[0] = 0x80;
+
+ for (i = 0; s[i] && (i < length); i++)
+ {
+ b = ((UINT8) s[i]) & 0x7F;
+ if (s[i+1] == '\0')
+ b |= 0x80;
+ bptr[i] = b;
+ }
+}
+
+
+
+static os9_diskinfo *os9_get_diskinfo(imgtool_image *image)
+{
+ return (os9_diskinfo *) imgtool_floppy_extrabytes(image);
+}
+
+
+
+static struct os9_direnum *os9_get_dirinfo(imgtool_directory *directory)
+{
+ return (struct os9_direnum *) imgtool_directory_extrabytes(directory);
+}
+
+
+
+static imgtoolerr_t os9_locate_lsn(imgtool_image *image, UINT32 lsn, UINT32 *head, UINT32 *track, UINT32 *sector)
+{
+ const os9_diskinfo *disk_info;
+
+ disk_info = os9_get_diskinfo(image);
+ if (lsn > disk_info->total_sectors)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ *track = lsn / (disk_info->sectors_per_track * disk_info->sides);
+ *head = (lsn / disk_info->sectors_per_track) % disk_info->sides;
+ *sector = (lsn % disk_info->sectors_per_track) + 1;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_read_lsn(imgtool_image *img, UINT32 lsn, int offset, void *buffer, size_t buffer_len)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ UINT32 head, track, sector;
+
+ err = os9_locate_lsn(img, lsn, &head, &track, &sector);
+ if (err)
+ return err;
+
+ ferr = floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len);
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_write_lsn(imgtool_image *img, UINT32 lsn, int offset, const void *buffer, size_t buffer_len)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ UINT32 head, track, sector;
+
+ err = os9_locate_lsn(img, lsn, &head, &track, &sector);
+ if (err)
+ return err;
+
+ ferr = floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len, 0); /* TODO: pass ddam argument from imgtool */
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static UINT32 os9_lookup_lsn(imgtool_image *img,
+ const struct os9_fileinfo *file_info, UINT32 *index)
+{
+ int i;
+ UINT32 lsn;
+ const os9_diskinfo *disk_info;
+
+ disk_info = os9_get_diskinfo(img);
+ lsn = *index / disk_info->sector_size;
+
+ i = 0;
+ while(lsn >= file_info->sector_map[i].count)
+ lsn -= file_info->sector_map[i++].count;
+
+ lsn = file_info->sector_map[i].lsn + lsn;
+ *index %= disk_info->sector_size;
+ return lsn;
+}
+
+
+
+static int os9_interpret_dirent(void *entry, char **filename, UINT32 *lsn, int *corrupt)
+{
+ int i;
+ char *entry_b = (char *) entry;
+
+ *filename = NULL;
+ *lsn = 0;
+ if (corrupt)
+ *corrupt = FALSE;
+
+ if (entry_b[28] != '\0')
+ {
+ if (corrupt)
+ *corrupt = TRUE;
+ }
+
+ for (i = 0; (i < 28) && !(entry_b[i] & 0x80); i++)
+ ;
+ entry_b[i] &= 0x7F;
+ entry_b[i+1] = '\0';
+
+ *lsn = pick_integer_be(entry, 29, 3);
+ if (strcmp(entry_b, ".") && strcmp(entry_b, ".."))
+ *filename = entry_b;
+ return *filename != NULL;
+}
+
+
+
+/*-------------------------------------------------
+ os9_decode_file_header - reads a file/directory
+ entry from an LSN, and decodes it
+-------------------------------------------------*/
+
+static imgtoolerr_t os9_decode_file_header(imgtool_image *image,
+ int lsn, struct os9_fileinfo *info)
+{
+ imgtoolerr_t err;
+ UINT32 attributes, count;
+ int max_entries, i;
+ const os9_diskinfo *disk_info;
+ UINT8 header[256];
+
+ disk_info = os9_get_diskinfo(image);
+
+ err = os9_read_lsn(image, lsn, 0, header, sizeof(header));
+ if (err)
+ return err;
+
+ info->lsn = lsn;
+ attributes = pick_integer_be(header, 0, 1);
+ info->owner_id = pick_integer_be(header, 1, 2);
+ info->link_count = pick_integer_be(header, 8, 1);
+ info->file_size = pick_integer_be(header, 9, 4);
+ info->directory = (attributes & 0x80) ? 1 : 0;
+ info->non_sharable = (attributes & 0x40) ? 1 : 0;
+ info->public_execute = (attributes & 0x20) ? 1 : 0;
+ info->public_write = (attributes & 0x10) ? 1 : 0;
+ info->public_read = (attributes & 0x08) ? 1 : 0;
+ info->user_execute = (attributes & 0x04) ? 1 : 0;
+ info->user_write = (attributes & 0x02) ? 1 : 0;
+ info->user_read = (attributes & 0x01) ? 1 : 0;
+
+ if (info->directory && (info->file_size % 32 != 0))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* read all sector map entries */
+ max_entries = (disk_info->sector_size - 16) / 5;
+ max_entries = MIN(max_entries, ARRAY_LENGTH(info->sector_map) - 1);
+ for (i = 0; i < max_entries; i++)
+ {
+ lsn = pick_integer_be(header, 16 + (i * 5) + 0, 3);
+ count = pick_integer_be(header, 16 + (i * 5) + 3, 2);
+ if (count <= 0)
+ break;
+
+ info->sector_map[i].lsn = lsn;
+ info->sector_map[i].count = count;
+ }
+ info->sector_map[i].lsn = 0;
+ info->sector_map[i].count = 0;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_allocate_lsn(imgtool_image *image, UINT32 *lsn)
+{
+ UINT32 i;
+ os9_diskinfo *disk_info;
+ UINT8 b, mask;
+
+ disk_info = os9_get_diskinfo(image);
+
+ for (i = 0; i < disk_info->total_sectors; i++)
+ {
+ b = disk_info->allocation_bitmap[i / 8];
+ mask = 1 << (7 - (i % 8));
+
+ if ((b & mask) == 0)
+ {
+ disk_info->allocation_bitmap[i / 8] |= mask;
+ *lsn = i;
+ return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes);
+ }
+ }
+ return IMGTOOLERR_NOSPACE;
+}
+
+
+
+static imgtoolerr_t os9_deallocate_lsn(imgtool_image *image, UINT32 lsn)
+{
+ UINT8 mask;
+ os9_diskinfo *disk_info;
+
+ disk_info = os9_get_diskinfo(image);
+ mask = 1 << (7 - (lsn % 8));
+ disk_info->allocation_bitmap[lsn / 8] &= ~mask;
+ return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes);
+}
+
+
+
+static UINT32 os9_get_free_lsns(imgtool_image *image)
+{
+ const os9_diskinfo *disk_info;
+ UINT32 i, free_lsns;
+ UINT8 b;
+
+ disk_info = os9_get_diskinfo(image);
+ free_lsns = 0;
+
+ for (i = 0; i < disk_info->total_sectors; i++)
+ {
+ b = disk_info->allocation_bitmap[i / 8];
+ b >>= (7 - (i % 8));
+ free_lsns += ~b & 1;
+ }
+
+ return free_lsns;
+}
+
+
+
+static imgtoolerr_t os9_corrupt_file_error(const struct os9_fileinfo *file_info)
+{
+ imgtoolerr_t err;
+ if (file_info->directory)
+ err = IMGTOOLERR_CORRUPTDIR;
+ else
+ err = IMGTOOLERR_CORRUPTFILE;
+ return err;
+}
+
+
+
+static imgtoolerr_t os9_set_file_size(imgtool_image *image,
+ struct os9_fileinfo *file_info, UINT32 new_size)
+{
+ imgtoolerr_t err;
+ const os9_diskinfo *disk_info;
+ UINT32 new_lsn_count, current_lsn_count;
+ UINT32 free_lsns, lsn, i;
+ int sector_map_length = -1;
+ UINT8 header[256];
+
+ /* easy way out? */
+ if (file_info->file_size == new_size)
+ return IMGTOOLERR_SUCCESS;
+
+ disk_info = os9_get_diskinfo(image);
+
+ free_lsns = os9_get_free_lsns(image);
+ current_lsn_count = (file_info->file_size + disk_info->sector_size - 1) / disk_info->sector_size;
+ new_lsn_count = (new_size + disk_info->sector_size - 1) / disk_info->sector_size;
+
+ /* check to see if the file is growing and we do not have enough space */
+ if ((new_lsn_count > current_lsn_count) && (new_lsn_count - current_lsn_count > free_lsns))
+ return IMGTOOLERR_NOSPACE;
+
+ if (current_lsn_count != new_lsn_count)
+ {
+ /* first find out the size of our sector map */
+ sector_map_length = 0;
+ lsn = 0;
+ while((lsn < current_lsn_count) && (sector_map_length < ARRAY_LENGTH(file_info->sector_map)))
+ {
+ if (file_info->sector_map[sector_map_length].count == 0)
+ return os9_corrupt_file_error(file_info);
+
+ lsn += file_info->sector_map[sector_map_length].count;
+ sector_map_length++;
+ }
+
+ /* keep in mind that the sector_map might not parallel our expected
+ * current LSN count; we should tolerate this abnormality */
+ current_lsn_count = lsn;
+
+ while(current_lsn_count > new_lsn_count)
+ {
+ /* shrink this file */
+ lsn = file_info->sector_map[sector_map_length - 1].lsn +
+ file_info->sector_map[sector_map_length - 1].count - 1;
+
+ err = os9_deallocate_lsn(image, lsn);
+ if (err)
+ return err;
+
+ file_info->sector_map[sector_map_length - 1].count--;
+ while(sector_map_length > 0 && (file_info->sector_map[sector_map_length - 1].count == 0))
+ sector_map_length--;
+ current_lsn_count--;
+ }
+
+ while(current_lsn_count < new_lsn_count)
+ {
+ /* grow this file */
+ err = os9_allocate_lsn(image, &lsn);
+ if (err)
+ return err;
+
+ if ((sector_map_length > 0) && ((file_info->sector_map[sector_map_length - 1].lsn +
+ file_info->sector_map[sector_map_length - 1].count) == lsn))
+ {
+ file_info->sector_map[sector_map_length - 1].count++;
+ }
+ else if (sector_map_length >= ARRAY_LENGTH(file_info->sector_map))
+ {
+ return IMGTOOLERR_NOSPACE;
+ }
+ else
+ {
+ file_info->sector_map[sector_map_length].lsn = lsn;
+ file_info->sector_map[sector_map_length].count = 1;
+ sector_map_length++;
+ file_info->sector_map[sector_map_length].lsn = 0;
+ file_info->sector_map[sector_map_length].count = 0;
+ }
+ current_lsn_count++;
+ }
+ }
+
+ /* now lets write back the sector */
+ err = os9_read_lsn(image, file_info->lsn, 0, header, sizeof(header));
+ if (err)
+ return err;
+
+ file_info->file_size = new_size;
+ place_integer_be(header, 9, 4, file_info->file_size);
+
+ /* do we have to write the sector map? */
+ if (sector_map_length >= 0)
+ {
+ for (i = 0; i < MIN(sector_map_length + 1, ARRAY_LENGTH(file_info->sector_map)); i++)
+ {
+ place_integer_be(header, 16 + (i * 5) + 0, 3, file_info->sector_map[i].lsn);
+ place_integer_be(header, 16 + (i * 5) + 3, 2, file_info->sector_map[i].count);
+ }
+ }
+
+ err = os9_write_lsn(image, file_info->lsn, 0, header, disk_info->sector_size);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+/*-------------------------------------------------
+ os9_lookup_path - walks an OS-9 directory
+ structure to find a file, or optionally, create
+ a file
+-------------------------------------------------*/
+
+static imgtoolerr_t os9_lookup_path(imgtool_image *img, const char *path,
+ creation_policy_t create, struct os9_fileinfo *file_info,
+ UINT32 *parent_lsn, UINT32 *dirent_lsn, UINT32 *dirent_index)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ struct os9_fileinfo dir_info;
+ UINT32 index, current_lsn, dir_size;
+ UINT32 entry_index = 0;
+ UINT32 free_entry_index = 0xffffffff;
+ UINT32 entry_lsn = 0;
+ UINT32 allocated_lsn = 0;
+ UINT8 entry[32];
+ UINT8 block[64];
+ char *filename;
+ const os9_diskinfo *disk_info;
+
+ disk_info = os9_get_diskinfo(img);
+ current_lsn = disk_info->root_dir_lsn;
+
+ if (parent_lsn)
+ *parent_lsn = 0;
+
+ /* we have to transverse each path element */
+ while(*path)
+ {
+ if (parent_lsn)
+ *parent_lsn = current_lsn;
+
+ /* decode the directory header of this directory */
+ err = os9_decode_file_header(img, current_lsn, &dir_info);
+ if (err)
+ goto done;
+ dir_size = dir_info.file_size;
+
+ /* sanity check directory */
+ if (!dir_info.directory)
+ {
+ err = (current_lsn == disk_info->root_dir_lsn) ? IMGTOOLERR_CORRUPTIMAGE : IMGTOOLERR_INVALIDPATH;
+ goto done;
+ }
+
+ /* walk the directory */
+ for (index = 0; index < dir_size; index += 32)
+ {
+ entry_index = index;
+ entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index);
+
+ err = os9_read_lsn(img, entry_lsn, entry_index, entry, sizeof(entry));
+ if (err)
+ goto done;
+
+ /* remember first free entry found */
+ if( free_entry_index == 0xffffffff )
+ {
+ if( entry[0] == 0 )
+ free_entry_index = index;
+ }
+
+ if (os9_interpret_dirent(entry, &filename, &current_lsn, NULL))
+ {
+ if (!strcmp(path, filename))
+ break;
+ }
+
+ }
+
+ /* at the end of the file? */
+ if (index >= dir_size)
+ {
+ /* if we're not creating, or we are creating but we have not fully
+ * transversed the directory, error out */
+ if (!create || path[strlen(path) + 1])
+ {
+ err = IMGTOOLERR_PATHNOTFOUND;
+ goto done;
+ }
+
+ /* allocate a new LSN */
+ err = os9_allocate_lsn(img, &allocated_lsn);
+ if (err)
+ goto done;
+
+ /* write the file */
+ memset(block, 0, sizeof(block));
+ place_integer_be(block, 0, 1, 0x1B | ((create == CREATE_DIR) ? 0x80 : 0x00));
+ err = os9_write_lsn(img, allocated_lsn, 0, block, sizeof(block));
+ if (err)
+ goto done;
+
+ if( free_entry_index == 0xffffffff )
+ {
+ /* expand the directory to hold the new entry */
+ err = os9_set_file_size(img, &dir_info, dir_size + 32);
+ if (err)
+ goto done;
+ }
+ else
+ /* use first unused entry in directory */
+ dir_size = free_entry_index;
+
+ /* now prepare the directory entry */
+ memset(entry, 0, sizeof(entry));
+ place_string(entry, 0, 28, path);
+ place_integer_be(entry, 29, 3, allocated_lsn);
+
+ /* now write the directory entry */
+ entry_index = dir_size;
+ entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index);
+ err = os9_write_lsn(img, entry_lsn, entry_index, entry, 32);
+ if (err)
+ goto done;
+
+ /* directory entry append complete; no need to hold this lsn */
+ current_lsn = allocated_lsn;
+ allocated_lsn = 0;
+ }
+ path += strlen(path) + 1;
+ }
+
+ if (file_info)
+ {
+ err = os9_decode_file_header(img, current_lsn, file_info);
+ if (err)
+ goto done;
+ }
+
+ if (dirent_lsn)
+ *dirent_lsn = entry_lsn;
+ if (dirent_index)
+ *dirent_index = entry_index;
+
+done:
+ if (allocated_lsn != 0)
+ os9_deallocate_lsn(img, allocated_lsn);
+ return err;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_open(imgtool_image *image, imgtool_stream *stream)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ os9_diskinfo *info;
+ UINT32 track_size_in_sectors, i; //, attributes;
+ UINT8 header[256];
+ UINT32 allocation_bitmap_lsns;
+ UINT8 b, mask;
+
+ info = os9_get_diskinfo(image);
+
+ ferr = floppy_read_sector(imgtool_floppy(image), 0, 0, 1, 0, header, sizeof(header));
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ info->total_sectors = pick_integer_be(header, 0, 3);
+ track_size_in_sectors = pick_integer_be(header, 3, 1);
+ info->allocation_bitmap_bytes = pick_integer_be(header, 4, 2);
+ info->cluster_size = pick_integer_be(header, 6, 2);
+ info->root_dir_lsn = pick_integer_be(header, 8, 3);
+ info->owner_id = pick_integer_be(header, 11, 2);
+// attributes =
+ pick_integer_be(header, 13, 1);
+ info->disk_id = pick_integer_be(header, 14, 2);
+ info->format_flags = pick_integer_be(header, 16, 1);
+ info->sectors_per_track = pick_integer_be(header, 17, 2);
+ info->bootstrap_lsn = pick_integer_be(header, 21, 3);
+ info->bootstrap_size = pick_integer_be(header, 24, 2);
+ info->sector_size = pick_integer_be(header, 104, 2);
+
+ info->sides = (info->format_flags & 0x01) ? 2 : 1;
+ info->double_density = (info->format_flags & 0x02) ? 1 : 0;
+ info->double_track = (info->format_flags & 0x04) ? 1 : 0;
+ info->quad_track_density = (info->format_flags & 0x08) ? 1 : 0;
+ info->octal_track_density = (info->format_flags & 0x10) ? 1 : 0;
+
+ pick_string(header, 31, 32, info->name);
+
+ if (info->sector_size == 0)
+ info->sector_size = 256;
+
+ /* does the root directory and allocation bitmap collide? */
+ allocation_bitmap_lsns = (info->allocation_bitmap_bytes + info->sector_size - 1) / info->sector_size;
+ if (1 + allocation_bitmap_lsns > info->root_dir_lsn)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* is the allocation bitmap too big? */
+ info->allocation_bitmap = (UINT8*)imgtool_image_malloc(image, info->allocation_bitmap_bytes);
+ if (!info->allocation_bitmap)
+ return IMGTOOLERR_OUTOFMEMORY;
+ memset(info->allocation_bitmap, 0, info->allocation_bitmap_bytes);
+
+ /* sectors per track and track size dont jive? */
+ if (info->sectors_per_track != track_size_in_sectors)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* zero sectors per track? */
+ if (info->sectors_per_track == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* do we have an odd number of sectors? */
+ if (info->total_sectors % info->sectors_per_track)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* read the allocation bitmap */
+ for (i = 0; i < allocation_bitmap_lsns; i++)
+ {
+ err = os9_read_lsn(image, 1 + i, 0, &info->allocation_bitmap[i * info->sector_size],
+ MIN(info->allocation_bitmap_bytes - (i * info->sector_size), info->sector_size));
+ if (err)
+ return err;
+ }
+
+ /* check to make sure that the allocation bitmap and root sector are reserved */
+ for (i = 0; i <= allocation_bitmap_lsns; i++)
+ {
+ b = info->allocation_bitmap[i / 8];
+ mask = 1 << (7 - (i % 8));
+ if ((b & mask) == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_create(imgtool_image *img, imgtool_stream *stream, option_resolution *opts)
+{
+ imgtoolerr_t err;
+ UINT8 *header;
+ UINT32 heads, tracks, sectors, sector_bytes, first_sector_id;
+ UINT32 cluster_size, owner_id;
+ UINT32 allocation_bitmap_bits, allocation_bitmap_lsns;
+ UINT32 attributes, format_flags, disk_id;
+ UINT32 i;
+ INT32 total_allocated_sectors;
+ const char *title;
+ time_t t;
+ struct tm *ltime;
+
+ time(&t);
+ ltime = localtime(&t);
+
+ heads = option_resolution_lookup_int(opts, 'H');
+ tracks = option_resolution_lookup_int(opts, 'T');
+ sectors = option_resolution_lookup_int(opts, 'S');
+ sector_bytes = option_resolution_lookup_int(opts, 'L');
+ first_sector_id = option_resolution_lookup_int(opts, 'F');
+ title = "";
+
+ header = (UINT8*)malloc(sector_bytes);
+ if (!header)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+
+ if (sector_bytes > 256)
+ sector_bytes = 256;
+ cluster_size = 1;
+ owner_id = 1;
+ disk_id = 1;
+ attributes = 0;
+ allocation_bitmap_bits = heads * tracks * sectors / cluster_size;
+ allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes;
+ format_flags = ((heads > 1) ? 0x01 : 0x00) | ((tracks > 40) ? 0x02 : 0x00);
+
+ memset(header, 0, sector_bytes);
+ place_integer_be(header, 0, 3, heads * tracks * sectors);
+ place_integer_be(header, 3, 1, sectors);
+ place_integer_be(header, 4, 2, (allocation_bitmap_bits + 7) / 8);
+ place_integer_be(header, 6, 2, cluster_size);
+ place_integer_be(header, 8, 3, 1 + allocation_bitmap_lsns);
+ place_integer_be(header, 11, 2, owner_id);
+ place_integer_be(header, 13, 1, attributes);
+ place_integer_be(header, 14, 2, disk_id);
+ place_integer_be(header, 16, 1, format_flags);
+ place_integer_be(header, 17, 2, sectors);
+ place_string(header, 31, 32, title);
+ place_integer_be(header, 103, 2, sector_bytes / 256);
+
+ /* path descriptor options */
+ place_integer_be(header, 0x3f+0x00, 1, 1); /* device class */
+ place_integer_be(header, 0x3f+0x01, 1, 1); /* drive number */
+ place_integer_be(header, 0x3f+0x03, 1, 0x20); /* device type */
+ place_integer_be(header, 0x3f+0x04, 1, 1); /* density capability */
+ place_integer_be(header, 0x3f+0x05, 2, tracks); /* number of tracks */
+ place_integer_be(header, 0x3f+0x07, 1, heads); /* number of sides */
+ place_integer_be(header, 0x3f+0x09, 2, sectors); /* sectors per track */
+ place_integer_be(header, 0x3f+0x0b, 2, sectors); /* sectors on track zero */
+ place_integer_be(header, 0x3f+0x0d, 1, 3); /* sector interleave factor */
+ place_integer_be(header, 0x3f+0x0e, 1, 8); /* default sectors per allocation */
+
+ err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id, 0, header, sector_bytes, 0); /* TODO: pass ddam argument from imgtool */
+ if (err)
+ goto done;
+
+ total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 7;
+
+ for (i = 0; i < allocation_bitmap_lsns; i++)
+ {
+ memset(header, 0x00, sector_bytes);
+
+ if (total_allocated_sectors > (8 * 256))
+ {
+ memset(header, 0xff, sector_bytes);
+ total_allocated_sectors -= (8 * 256);
+ }
+ else if (total_allocated_sectors > 1 )
+ {
+ int offset;
+ UINT8 mask;
+
+ while( total_allocated_sectors >= 0 )
+ {
+ offset = total_allocated_sectors / 8;
+ mask = 1 << (7 - ( total_allocated_sectors % 8 ) );
+
+ header[offset] |= mask;
+ total_allocated_sectors--;
+ }
+ }
+
+ err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + i, 0, header, sector_bytes, 0); /* TODO: pass ddam argument from imgtool */
+ if (err)
+ goto done;
+ }
+
+ memset(header, 0, sector_bytes);
+ header[0x00] = 0xBF;
+ header[0x01] = 0x00;
+ header[0x02] = 0x00;
+ header[0x03] = (UINT8) ltime->tm_year;
+ header[0x04] = (UINT8) ltime->tm_mon + 1;
+ header[0x05] = (UINT8) ltime->tm_mday;
+ header[0x06] = (UINT8) ltime->tm_hour;
+ header[0x07] = (UINT8) ltime->tm_min;
+ header[0x08] = 0x02;
+ header[0x09] = 0x00;
+ header[0x0A] = 0x00;
+ header[0x0B] = 0x00;
+ header[0x0C] = 0x40;
+ header[0x0D] = (UINT8) (ltime->tm_year % 100);
+ header[0x0E] = (UINT8) ltime->tm_mon;
+ header[0x0F] = (UINT8) ltime->tm_mday;
+ place_integer_be(header, 0x10, 3, 1 + allocation_bitmap_lsns + 1);
+ place_integer_be(header, 0x13, 2, 8);
+
+ err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + allocation_bitmap_lsns, 0, header, sector_bytes, 0); /* TODO: pass ddam argument from imgtool */
+ if (err)
+ goto done;
+
+ memset(header, 0, sector_bytes);
+ header[0x00] = 0x2E;
+ header[0x01] = 0xAE;
+ header[0x1F] = 1 + allocation_bitmap_lsns;
+ header[0x20] = 0xAE;
+ header[0x3F] = 1 + allocation_bitmap_lsns;
+ err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 2 + allocation_bitmap_lsns, 0, header, sector_bytes, 0); /* TOOD: pass ddam argument from imgtool */
+ if (err)
+ goto done;
+
+done:
+ if (header)
+ free(header);
+ return err;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_beginenum(imgtool_directory *enumeration, const char *path)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ struct os9_direnum *os9enum;
+ imgtool_image *image;
+
+ image = imgtool_directory_image(enumeration);
+ os9enum = os9_get_dirinfo(enumeration);
+
+ err = os9_lookup_path(image, path, CREATE_NONE, &os9enum->dir_info, NULL, NULL, NULL);
+ if (err)
+ goto done;
+
+ /* this had better be a directory */
+ if (!os9enum->dir_info.directory)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto done;
+ }
+
+done:
+ return err;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ struct os9_direnum *os9enum;
+ UINT32 lsn, index;
+ imgtoolerr_t err;
+ UINT8 dir_entry[32];
+ char filename[29];
+ struct os9_fileinfo file_info;
+ imgtool_image *image;
+
+ image = imgtool_directory_image(enumeration);
+ os9enum = os9_get_dirinfo(enumeration);
+
+ do
+ {
+ /* check for EOF */
+ if (os9enum->index >= os9enum->dir_info.file_size)
+ {
+ ent->eof = 1;
+ return IMGTOOLERR_SUCCESS;
+ }
+
+ /* locate the LSN and offset for this directory entry */
+ index = os9enum->index;
+ lsn = os9_lookup_lsn(image, &os9enum->dir_info, &index);
+
+ /* read the directory entry out of the lSN */
+ err = os9_read_lsn(image, lsn, index, dir_entry, sizeof(dir_entry));
+ if (err)
+ return err;
+
+ if (dir_entry[0])
+ {
+ /* read the file or directory name */
+ pick_string(dir_entry, 0, 28, filename);
+
+ /* we have certain expectations of the directory contents; the
+ * first directory entry should be "..", the second ".", and
+ * subsequent entries should never be "." or ".." */
+ switch(os9enum->index)
+ {
+ case 0:
+ if (strcmp(filename, ".."))
+ imgtool_warn("First entry in directory should be \"..\" and not \"%s\"", filename);
+ break;
+
+ case 32:
+ if (strcmp(filename, "."))
+ imgtool_warn("Second entry in directory should be \".\" and not \"%s\"", filename);
+ break;
+
+ default:
+ if (!strcmp(filename, ".") || !strcmp(filename, ".."))
+ imgtool_warn("Directory entry %d should not be \"%s\"", index / 32, filename);
+ break;
+ }
+
+ /* if the filename is ".", the file should point to the current directory */
+ if (!strcmp(filename, ".") && (pick_integer_be(dir_entry, 29, 3) != os9enum->dir_info.lsn))
+ {
+ imgtool_warn("Directory \".\" does not point back to same directory");
+ }
+ }
+ else
+ {
+ /* no more directory entries */
+ filename[0] = '\0';
+ }
+
+ /* move on to the next directory entry */
+ os9enum->index += 32;
+ }
+ while(!filename[0] || !strcmp(filename, ".") || !strcmp(filename, ".."));
+
+ /* read file attributes */
+ lsn = pick_integer_be(dir_entry, 29, 3);
+ err = os9_decode_file_header(imgtool_directory_image(enumeration), lsn, &file_info);
+ if (err)
+ return err;
+
+ /* fill out imgtool_dirent structure */
+ snprintf(ent->filename, ARRAY_LENGTH(ent->filename), "%s", filename);
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "%c%c%c%c%c%c%c%c",
+ file_info.directory ? 'd' : '-',
+ file_info.non_sharable ? 's' : '-',
+ file_info.public_execute ? 'x' : '-',
+ file_info.public_write ? 'w' : '-',
+ file_info.public_read ? 'r' : '-',
+ file_info.user_execute ? 'x' : '-',
+ file_info.user_write ? 'w' : '-',
+ file_info.user_read ? 'r' : '-');
+
+ ent->directory = file_info.directory;
+ ent->corrupt = (dir_entry[28] != 0);
+ ent->filesize = file_info.file_size;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_freespace(imgtool_partition *partition, UINT64 *size)
+{
+ imgtool_image *image = imgtool_partition_image(partition);
+ const os9_diskinfo *disk_info;
+ UINT32 free_lsns;
+
+ disk_info = os9_get_diskinfo(image);
+ free_lsns = os9_get_free_lsns(image);
+
+ *size = free_lsns * disk_info->sector_size;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_readfile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf)
+{
+ imgtoolerr_t err;
+ imgtool_image *img = imgtool_partition_image(partition);
+ const os9_diskinfo *disk_info;
+ struct os9_fileinfo file_info;
+ UINT8 buffer[256];
+ int i, j;
+ UINT32 file_size;
+ UINT32 used_size;
+
+ disk_info = os9_get_diskinfo(img);
+
+ err = os9_lookup_path(img, filename, CREATE_NONE, &file_info, NULL, NULL, NULL);
+ if (err)
+ return err;
+ if (file_info.directory)
+ return IMGTOOLERR_FILENOTFOUND;
+ file_size = file_info.file_size;
+
+ for (i = 0; file_info.sector_map[i].count > 0; i++)
+ {
+ for (j = 0; j < file_info.sector_map[i].count; j++)
+ {
+ used_size = MIN(file_size, disk_info->sector_size);
+ err = os9_read_lsn(img, file_info.sector_map[i].lsn + j, 0,
+ buffer, used_size);
+ if (err)
+ return err;
+ stream_write(destf, buffer, used_size);
+ file_size -= used_size;
+ }
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_writefile(imgtool_partition *partition, const char *path, const char *fork, imgtool_stream *sourcef, option_resolution *opts)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ struct os9_fileinfo file_info;
+ size_t write_size;
+ void *buf = NULL;
+ int i = -1;
+ UINT32 lsn = 0;
+ UINT32 count = 0;
+ UINT32 sz;
+ const os9_diskinfo *disk_info;
+
+ disk_info = os9_get_diskinfo(image);
+
+ buf = malloc(disk_info->sector_size);
+ if (!buf)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+
+ err = os9_lookup_path(image, path, CREATE_FILE, &file_info, NULL, NULL, NULL);
+ if (err)
+ goto done;
+
+ sz = (UINT32) stream_size(sourcef);
+
+ err = os9_set_file_size(image, &file_info, sz);
+ if (err)
+ goto done;
+
+ while(sz > 0)
+ {
+ write_size = (size_t) MIN(sz, (UINT64) disk_info->sector_size);
+
+ stream_read(sourcef, buf, write_size);
+
+ while(count == 0)
+ {
+ i++;
+ lsn = file_info.sector_map[i].lsn;
+ count = file_info.sector_map[i].count;
+ }
+
+ err = os9_write_lsn(image, lsn, 0, buf, write_size);
+ if (err)
+ goto done;
+
+ lsn++;
+ count--;
+ sz -= write_size;
+ }
+
+done:
+ if (buf)
+ free(buf);
+ return err;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_delete(imgtool_partition *partition, const char *path,
+ unsigned int delete_directory)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ //const os9_diskinfo *disk_info;
+ struct os9_fileinfo file_info;
+ UINT32 dirent_lsn, dirent_index;
+ UINT32 entry_lsn, entry_index;
+ UINT32 i, j, lsn;
+ UINT8 b;
+
+ //disk_info = os9_get_diskinfo(image);
+
+ err = os9_lookup_path(image, path, CREATE_NONE, &file_info, NULL, &dirent_lsn, &dirent_index);
+ if (err)
+ return err;
+ if (file_info.directory != delete_directory)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ /* make sure that if we are deleting a directory, it is empty */
+ if (delete_directory)
+ {
+ for (i = 64; i < file_info.file_size; i += 32)
+ {
+ entry_index = i;
+ entry_lsn = os9_lookup_lsn(image, &file_info, &entry_index);
+
+ err = os9_read_lsn(image, entry_lsn, entry_index, &b, 1);
+ if (err)
+ return err;
+
+ /* this had better be a deleted file, if not we can't delete */
+ if (b != 0)
+ return IMGTOOLERR_DIRNOTEMPTY;
+ }
+ }
+
+ /* zero out the file entry */
+ b = '\0';
+ err = os9_write_lsn(image, dirent_lsn, dirent_index, &b, 1);
+ if (err)
+ return err;
+
+ /* get the link count */
+ err = os9_read_lsn(image, file_info.lsn, 8, &b, 1);
+ if (err)
+ return err;
+
+ if (b > 0)
+ b--;
+ if (b > 0)
+ {
+ /* link count is greater than zero */
+ err = os9_write_lsn(image, file_info.lsn, 8, &b, 1);
+ if (err)
+ return err;
+ }
+ else
+ {
+ /* no more links; outright delete the file */
+ err = os9_deallocate_lsn(image, file_info.lsn);
+ if (err)
+ return err;
+
+ for (i = 0; (i < ARRAY_LENGTH(file_info.sector_map)) && file_info.sector_map[i].count; i++)
+ {
+ lsn = file_info.sector_map[i].lsn;
+ for (j = 0; j < file_info.sector_map[i].count; j++)
+ {
+ err = os9_deallocate_lsn(image, lsn + j);
+ if (err)
+ return err;
+ }
+ }
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_deletefile(imgtool_partition *partition, const char *path)
+{
+ return os9_diskimage_delete(partition, path, 0);
+}
+
+
+
+static imgtoolerr_t os9_diskimage_createdir(imgtool_partition *partition, const char *path)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ struct os9_fileinfo file_info;
+ UINT8 dir_data[64];
+ UINT32 parent_lsn;
+
+ err = os9_lookup_path(image, path, CREATE_DIR, &file_info, &parent_lsn, NULL, NULL);
+ if (err)
+ goto done;
+
+ err = os9_set_file_size(image, &file_info, 64);
+ if (err)
+ goto done;
+
+ /* create intial directories */
+ memset(dir_data, 0, sizeof(dir_data));
+ place_string(dir_data, 0, 32, "..");
+ place_integer_be(dir_data, 29, 3, parent_lsn);
+ place_string(dir_data, 32, 32, ".");
+ place_integer_be(dir_data, 61, 3, file_info.lsn);
+
+ err = os9_write_lsn(image, file_info.sector_map[0].lsn, 0, dir_data, sizeof(dir_data));
+ if (err)
+ goto done;
+
+done:
+ return err;
+}
+
+
+
+static imgtoolerr_t os9_diskimage_deletedir(imgtool_partition *partition, const char *path)
+{
+ return os9_diskimage_delete(partition, path, 1);
+}
+
+
+
+void os9_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break;
+ case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break;
+ case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(os9_diskinfo); break;
+ case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct os9_direnum); break;
+ case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "os9"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "OS-9 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;
+
+ /* --- 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_CREATE: info->create = os9_diskimage_create; break;
+ case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = os9_diskimage_open; break;
+ case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = os9_diskimage_beginenum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = os9_diskimage_nextenum; break;
+ case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = os9_diskimage_freespace; break;
+ case IMGTOOLINFO_PTR_READ_FILE: info->read_file = os9_diskimage_readfile; break;
+ case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = os9_diskimage_writefile; break;
+ case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = os9_diskimage_deletefile; break;
+ case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = os9_diskimage_createdir; break;
+ case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = os9_diskimage_deletedir; break;
+ case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break;
+ }
+}