summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/tools/imgtool/modules/prodos.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/tools/imgtool/modules/prodos.c')
-rw-r--r--src/mame/tools/imgtool/modules/prodos.c2282
1 files changed, 2282 insertions, 0 deletions
diff --git a/src/mame/tools/imgtool/modules/prodos.c b/src/mame/tools/imgtool/modules/prodos.c
new file mode 100644
index 00000000000..5800e2bf9f3
--- /dev/null
+++ b/src/mame/tools/imgtool/modules/prodos.c
@@ -0,0 +1,2282 @@
+// license:BSD-3-Clause
+// copyright-holders:Raphael Nabet
+/****************************************************************************
+
+ prodos.c
+
+ Apple II ProDOS disk images
+
+*****************************************************************************
+
+ Notes:
+ - ProDOS disks are split into 512 byte blocks.
+
+ ProDOS directory structure:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 2 ???
+ 2 2 Next block (0 if end)
+ 4 39 Directory Entry
+ 43 39 Directory Entry
+ ...
+ 472 39 Directory Entry
+ 511 1 ???
+
+
+ ProDOS directory entry structure:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 1 Storage type (bits 7-4)
+ 10 - Seedling File (1 block)
+ 20 - Sapling File (2-256 blocks)
+ 30 - Tree File (257-32768 blocks)
+ 40 - Pascal Areas on ProFile HDs (???)
+ 50 - GS/OS Extended File (data and rsrc fork)
+ E0 - Subdirectory Header
+ F0 - Volume Header
+ 1 15 File name (NUL padded)
+ 16 1 File type
+ 17 2 Key pointer
+ 19 2 Blocks used
+ 21 3 File size
+ 24 4 Creation date
+ 28 1 ProDOS version that created the file
+ 29 1 Minimum ProDOS version needed to read the file
+ 30 1 Access byte
+ 31 2 Auxilary file type
+ 33 4 Last modified date
+ 37 2 Header pointer
+
+
+ In "seedling files", the key pointer points to a single block that is the
+ whole file. In "sapling files", the key pointer points to an index block
+ that contains 256 2-byte index pointers that point to the actual blocks of
+ the files. These 2-byte values are not contiguous; the low order byte is
+ in the first half of the block, and the high order byte is in the second
+ half of the block. In "tree files", the key pointer points to an index
+ block of index blocks.
+
+ ProDOS dates are 32-bit little endian values
+
+ bits 0- 4 Day
+ bits 5- 8 Month (0-11)
+ bits 9-15 Year (0-49 is 2000-2049, 50-99 is 1950-1999)
+ bits 16-21 Minute
+ bits 24-28 Hour
+
+
+ ProDOS directory and volume headers have this information:
+
+ Offset Length Description
+ ------ ------ -----------
+ 31 1 Length of the entry; generally is 39
+ 32 1 Number of entries per block; generally is 13
+ 33 2 Active entry count in directory
+ 35 2 Volume bitmap block number
+ 37 2 Total blocks on volume
+
+ GS/OS Extended Files (storage type $5) point to an extended key block that
+ contains information about the two forks. The first half of the extended
+ key block contains info about the data form, and the second half the
+ resource fork. Both sides have the following format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 1 Storage type (bits 3-0, unlike the directory entry)
+ 1 2 Key pointer
+ 3 2 Blocks used
+ 5 3 File size
+ 8 1 Size of secondary info #1 (must be 18 to be valid)
+ 9 1 Secondary info #1 type (1=FInfo 2=xFInfo)
+ 10 16 FInfo or xFInfo
+ 26 1 Size of secondary info #2 (must be 18 to be valid)
+ 27 1 Secondary info #2 type (1=FInfo 2=xFInfo)
+ 28 16 FInfo or xFInfo
+
+ FInfo format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 4 File type
+ 4 4 File creator
+ 8 2 Finder flags
+ 10 2 X Coordinate
+ 12 2 Y Coordinate
+ 14 2 Finder folder
+
+ xFInfo format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 2 Icon ID
+ 2 6 Reserved
+ 8 1 Script Code
+ 9 1 Extended flags
+ 10 2 Comment ID
+ 12 4 Put Away Directory
+
+
+ For more info, consult ProDOS technical note #25
+ (http://web.pdx.edu/~heiss/technotes/pdos/tn.pdos.25.html)
+
+*****************************************************************************/
+
+#include "imgtool.h"
+#include "formats/imageutl.h"
+#include "formats/ap2_dsk.h"
+#include "formats/ap_dsk35.h"
+#include "iflopimg.h"
+#include "macutil.h"
+
+#define ROOTDIR_BLOCK 2
+#define BLOCK_SIZE 512
+
+struct prodos_diskinfo
+{
+ imgtoolerr_t (*load_block)(imgtool_image *image, int block, void *buffer);
+ imgtoolerr_t (*save_block)(imgtool_image *image, int block, const void *buffer);
+ UINT8 dirent_size;
+ UINT8 dirents_per_block;
+ UINT16 volume_bitmap_block;
+ UINT16 total_blocks;
+};
+
+struct prodos_direnum
+{
+ UINT32 block;
+ UINT32 index;
+ UINT8 block_data[BLOCK_SIZE];
+};
+
+struct prodos_dirent
+{
+ char filename[16];
+ UINT8 storage_type;
+ UINT16 extkey_pointer;
+ UINT16 key_pointer[2];
+ UINT32 filesize[2];
+ int depth[2];
+ UINT32 lastmodified_time;
+ UINT32 creation_time;
+
+ /* FInfo */
+ UINT32 file_type;
+ UINT32 file_creator;
+ UINT16 finder_flags;
+ UINT16 coord_x;
+ UINT16 coord_y;
+ UINT16 finder_folder;
+
+ /* xFInfo */
+ UINT16 icon_id;
+ UINT8 script_code;
+ UINT8 extended_flags;
+ UINT16 comment_id;
+ UINT32 putaway_directory;
+};
+
+enum creation_policy_t
+{
+ CREATE_NONE,
+ CREATE_FILE,
+ CREATE_DIR
+};
+
+
+
+static time_t prodos_crack_time(UINT32 prodos_time)
+{
+ struct tm t;
+ time_t now;
+
+ time(&now);
+ t = *localtime(&now);
+
+ t.tm_sec = 0;
+ t.tm_min = (prodos_time >> 16) & 0x3F;
+ t.tm_hour = (prodos_time >> 24) & 0x1F;
+ t.tm_mday = (prodos_time >> 0) & 0x1F;
+ t.tm_mon = (prodos_time >> 5) & 0x0F;
+ t.tm_year = (prodos_time >> 9) & 0x7F;
+
+ if (t.tm_year <= 49)
+ t.tm_year += 100;
+ return mktime(&t);
+}
+
+
+
+static UINT32 prodos_setup_time(time_t ansi_time)
+{
+ struct tm t;
+ UINT32 result = 0;
+
+ t = *localtime(&ansi_time);
+ if ((t.tm_year >= 100) && (t.tm_year <= 149))
+ t.tm_year -= 100;
+
+ result |= (((UINT32) t.tm_min) & 0x003F) << 16;
+ result |= (((UINT32) t.tm_hour) & 0x001F) << 24;
+ result |= (((UINT32) t.tm_mday) & 0x001F) << 0;
+ result |= (((UINT32) t.tm_mon) & 0x000F) << 5;
+ result |= (((UINT32) t.tm_year) & 0x007F) << 9;
+ return result;
+}
+
+
+
+static UINT32 prodos_time_now(void)
+{
+ time_t now;
+ time(&now);
+ return prodos_setup_time(now);
+}
+
+
+
+static int is_file_storagetype(UINT8 storage_type)
+{
+ return ((storage_type >= 0x10) && (storage_type <= 0x3F))
+ || ((storage_type >= 0x50) && (storage_type <= 0x5F));
+}
+
+
+
+static int is_normalfile_storagetype(UINT8 storage_type)
+{
+ return ((storage_type >= 0x10) && (storage_type <= 0x3F));
+}
+
+
+
+static int is_extendedfile_storagetype(UINT8 storage_type)
+{
+ return ((storage_type >= 0x50) && (storage_type <= 0x5F));
+}
+
+
+
+static int is_dir_storagetype(UINT8 storage_type)
+{
+ return (storage_type >= 0xE0) && (storage_type <= 0xEF);
+}
+
+
+
+static prodos_diskinfo *get_prodos_info(imgtool_image *image)
+{
+ prodos_diskinfo *info;
+ info = (prodos_diskinfo *) imgtool_floppy_extrabytes(image);
+ return info;
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static void prodos_find_block_525(imgtool_image *image, int block,
+ UINT32 *track, UINT32 *head, UINT32 *sector1, UINT32 *sector2)
+{
+ static const UINT8 skewing[] =
+ {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
+ 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F
+ };
+
+ block *= 2;
+
+ *track = block / APPLE2_SECTOR_COUNT;
+ *head = 0;
+ *sector1 = skewing[block % APPLE2_SECTOR_COUNT + 0];
+ *sector2 = skewing[block % APPLE2_SECTOR_COUNT + 1];
+}
+
+
+
+static imgtoolerr_t prodos_load_block_525(imgtool_image *image,
+ int block, void *buffer)
+{
+ floperr_t ferr;
+ UINT32 track, head, sector1, sector2;
+
+ prodos_find_block_525(image, block, &track, &head, &sector1, &sector2);
+
+ /* read first sector */
+ ferr = floppy_read_sector(imgtool_floppy(image), head, track,
+ sector1, 0, ((UINT8 *) buffer) + 0, 256);
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ /* read second sector */
+ ferr = floppy_read_sector(imgtool_floppy(image), head, track,
+ sector2, 0, ((UINT8 *) buffer) + 256, 256);
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_save_block_525(imgtool_image *image,
+ int block, const void *buffer)
+{
+ floperr_t ferr;
+ UINT32 track, head, sector1, sector2;
+
+ prodos_find_block_525(image, block, &track, &head, &sector1, &sector2);
+
+ /* read first sector */
+ ferr = floppy_write_sector(imgtool_floppy(image), head, track,
+ sector1, 0, ((const UINT8 *) buffer) + 0, 256, 0); /* TODO: pass ddam argument from imgtool */
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ /* read second sector */
+ ferr = floppy_write_sector(imgtool_floppy(image), head, track,
+ sector2, 0, ((const UINT8 *) buffer) + 256, 256, 0); /* TODO: pass ddam argument from imgtool */
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static void prodos_setprocs_525(imgtool_image *image)
+{
+ prodos_diskinfo *info;
+ info = get_prodos_info(image);
+ info->load_block = prodos_load_block_525;
+ info->save_block = prodos_save_block_525;
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static imgtoolerr_t prodos_find_block_35(imgtool_image *image, int block,
+ UINT32 *track, UINT32 *head, UINT32 *sector)
+{
+ int sides = 2;
+
+ *track = 0;
+ while(block >= (apple35_sectors_per_track(imgtool_floppy(image), *track) * sides))
+ {
+ block -= (apple35_sectors_per_track(imgtool_floppy(image), (*track)++) * sides);
+ if (*track >= 80)
+ return IMGTOOLERR_SEEKERROR;
+ }
+
+ *head = block / apple35_sectors_per_track(imgtool_floppy(image), *track);
+ *sector = block % apple35_sectors_per_track(imgtool_floppy(image), *track);
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_load_block_35(imgtool_image *image,
+ int block, void *buffer)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ UINT32 track, head, sector;
+
+ err = prodos_find_block_35(image, block, &track, &head, &sector);
+ if (err)
+ return err;
+
+ ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 512);
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_save_block_35(imgtool_image *image,
+ int block, const void *buffer)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ UINT32 track, head, sector;
+
+ err = prodos_find_block_35(image, block, &track, &head, &sector);
+ if (err)
+ return err;
+
+ ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 512, 0); /* TODO: pass ddam argument from imgtool */
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static void prodos_setprocs_35(imgtool_image *image)
+{
+ prodos_diskinfo *info;
+ info = get_prodos_info(image);
+ info->load_block = prodos_load_block_35;
+ info->save_block = prodos_save_block_35;
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static imgtoolerr_t prodos_load_block(imgtool_image *image,
+ int block, void *buffer)
+{
+ prodos_diskinfo *diskinfo;
+ diskinfo = get_prodos_info(image);
+ return diskinfo->load_block(image, block, buffer);
+}
+
+
+
+static imgtoolerr_t prodos_save_block(imgtool_image *image,
+ int block, const void *buffer)
+{
+ prodos_diskinfo *diskinfo;
+ diskinfo = get_prodos_info(image);
+ return diskinfo->save_block(image, block, buffer);
+}
+
+
+
+static imgtoolerr_t prodos_clear_block(imgtool_image *image, int block)
+{
+ UINT8 buffer[BLOCK_SIZE];
+ memset(buffer, 0, sizeof(buffer));
+ return prodos_save_block(image, block, buffer);
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static imgtoolerr_t prodos_diskimage_open(imgtool_image *image)
+{
+ imgtoolerr_t err;
+ UINT8 buffer[BLOCK_SIZE];
+ prodos_diskinfo *di;
+ const UINT8 *ent;
+
+ di = get_prodos_info(image);
+
+ /* specify defaults */
+ di->dirent_size = 39;
+ di->dirents_per_block = 13;
+
+ /* load the first block, hoping that the volume header is first */
+ err = prodos_load_block(image, ROOTDIR_BLOCK, buffer);
+ if (err)
+ return err;
+
+ ent = &buffer[4];
+
+ /* did we find the volume header? */
+ if ((ent[0] & 0xF0) == 0xF0)
+ {
+ di->dirent_size = pick_integer_le(ent, 31, 1);
+ di->dirents_per_block = pick_integer_le(ent, 32, 1);
+ di->volume_bitmap_block = pick_integer_le(ent, 35, 2);
+ di->total_blocks = pick_integer_le(ent, 37, 2);
+ }
+
+ /* sanity check these values */
+ if (di->dirent_size < 39)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (di->dirents_per_block * di->dirent_size >= BLOCK_SIZE)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (di->volume_bitmap_block >= di->total_blocks)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_open_525(imgtool_image *image, imgtool_stream *stream)
+{
+ prodos_setprocs_525(image);
+ return prodos_diskimage_open(image);
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_open_35(imgtool_image *image, imgtool_stream *stream)
+{
+ prodos_setprocs_35(image);
+ return prodos_diskimage_open(image);
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static imgtoolerr_t prodos_load_volume_bitmap(imgtool_image *image, UINT8 **bitmap)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ UINT8 *alloc_bitmap;
+ UINT32 bitmap_blocks, i;
+
+ di = get_prodos_info(image);
+
+ bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
+ alloc_bitmap = (UINT8*)malloc(bitmap_blocks * BLOCK_SIZE);
+ if (!alloc_bitmap)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < bitmap_blocks; i++)
+ {
+ err = prodos_load_block(image, di->volume_bitmap_block + i,
+ &alloc_bitmap[i * BLOCK_SIZE]);
+ if (err)
+ goto done;
+ }
+
+ err = IMGTOOLERR_SUCCESS;
+
+done:
+ if (err && alloc_bitmap)
+ {
+ free(alloc_bitmap);
+ alloc_bitmap = NULL;
+ }
+ *bitmap = alloc_bitmap;
+ return err;
+}
+
+
+
+static imgtoolerr_t prodos_save_volume_bitmap(imgtool_image *image, const UINT8 *bitmap)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ UINT32 bitmap_blocks, i;
+
+ di = get_prodos_info(image);
+
+ bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
+
+ for (i = 0; i < bitmap_blocks; i++)
+ {
+ err = prodos_save_block(image, di->volume_bitmap_block + i,
+ &bitmap[i * BLOCK_SIZE]);
+ if (err)
+ return err;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static void prodos_set_volume_bitmap_bit(UINT8 *buffer, UINT16 block, int value)
+{
+ UINT8 mask;
+ buffer += block / 8;
+ mask = 1 << (7 - (block % 8));
+ if (value)
+ *buffer |= mask;
+ else
+ *buffer &= ~mask;
+}
+
+
+
+static int prodos_get_volume_bitmap_bit(const UINT8 *buffer, UINT16 block)
+{
+ UINT8 mask;
+ buffer += block / 8;
+ mask = 1 << (7 - (block % 8));
+ return (*buffer & mask) ? 1 : 0;
+}
+
+
+
+static imgtoolerr_t prodos_alloc_block(imgtool_image *image, UINT8 *bitmap,
+ UINT16 *block)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ prodos_diskinfo *di;
+ UINT16 bitmap_blocks, i;
+ UINT8 *alloc_bitmap = NULL;
+
+ di = get_prodos_info(image);
+ *block = 0;
+ bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
+
+ if (!bitmap)
+ {
+ err = prodos_load_volume_bitmap(image, &alloc_bitmap);
+ if (err)
+ goto done;
+ bitmap = alloc_bitmap;
+ }
+
+ for (i = (di->volume_bitmap_block + bitmap_blocks); i < di->total_blocks; i++)
+ {
+ if (!prodos_get_volume_bitmap_bit(bitmap, i))
+ {
+ prodos_set_volume_bitmap_bit(bitmap, i, 1);
+ *block = i;
+ break;
+ }
+ }
+
+ if (*block > 0)
+ {
+ if (alloc_bitmap)
+ {
+ err = prodos_save_volume_bitmap(image, bitmap);
+ if (err)
+ goto done;
+ }
+ }
+ else
+ {
+ err = IMGTOOLERR_NOSPACE;
+ }
+
+done:
+ if (err)
+ *block = 0;
+ if (alloc_bitmap)
+ free(alloc_bitmap);
+ return err;
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static imgtoolerr_t prodos_diskimage_create(imgtool_image *image, option_resolution *opts)
+{
+ imgtoolerr_t err;
+ UINT32 heads, tracks, sectors, sector_bytes;
+ UINT32 dirent_size, volume_bitmap_block, i;
+ UINT32 volume_bitmap_block_count, total_blocks;
+ UINT8 buffer[BLOCK_SIZE];
+
+ 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');
+
+ dirent_size = 39;
+ volume_bitmap_block = 6;
+ total_blocks = tracks * heads * sectors * sector_bytes / BLOCK_SIZE;
+ volume_bitmap_block_count = (total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
+
+ /* prepare initial dir block */
+ memset(buffer, 0, sizeof(buffer));
+ place_integer_le(buffer, 4 + 0, 1, 0xF0);
+ place_integer_le(buffer, 4 + 31, 1, dirent_size);
+ place_integer_le(buffer, 4 + 32, 1, BLOCK_SIZE / dirent_size);
+ place_integer_le(buffer, 4 + 35, 2, volume_bitmap_block);
+ place_integer_le(buffer, 4 + 37, 2, total_blocks);
+
+ err = prodos_save_block(image, ROOTDIR_BLOCK, buffer);
+ if (err)
+ return err;
+
+ /* setup volume bitmap */
+ memset(buffer, 0, sizeof(buffer));
+ for (i = 0; i < (volume_bitmap_block + volume_bitmap_block_count); i++)
+ prodos_set_volume_bitmap_bit(buffer, i, 1);
+ prodos_save_block(image, volume_bitmap_block, buffer);
+
+ /* and finally open the image */
+ return prodos_diskimage_open(image);
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_create_525(imgtool_image *image, imgtool_stream *stream, option_resolution *opts)
+{
+ prodos_setprocs_525(image);
+ return prodos_diskimage_create(image, opts);
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_create_35(imgtool_image *image, imgtool_stream *stream, option_resolution *opts)
+{
+ prodos_setprocs_35(image);
+ return prodos_diskimage_create(image, opts);
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static imgtoolerr_t prodos_enum_seek(imgtool_image *image,
+ prodos_direnum *appleenum, UINT32 block, UINT32 index)
+{
+ imgtoolerr_t err;
+ UINT8 buffer[BLOCK_SIZE];
+
+ if (appleenum->block != block)
+ {
+ if (block != 0)
+ {
+ err = prodos_load_block(image, block, buffer);
+ if (err)
+ return err;
+ memcpy(appleenum->block_data, buffer, sizeof(buffer));
+ }
+ appleenum->block = block;
+ }
+
+ appleenum->index = index;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static UINT8 *next_info_block(UINT8 *buffer, size_t *position)
+{
+ size_t side = *position & 0x100;
+ size_t subpos = *position & 0x0FF;
+ UINT8 *result;
+
+ if (subpos < 8)
+ {
+ subpos = 8;
+ *position = side + subpos;
+ }
+
+ while((buffer[side + subpos] == 0x00) || (subpos + buffer[side + subpos] > 0x100))
+ {
+ if (side)
+ return NULL;
+
+ side = 0x100;
+ subpos = 8;
+ }
+
+ result = &buffer[side + subpos];
+ subpos += *result;
+ *position = side + subpos;
+ return result;
+}
+
+
+
+static UINT8 *alloc_info_block(UINT8 *buffer, size_t block_size, UINT8 block_type)
+{
+ size_t position = 0;
+ size_t side;
+ size_t subpos;
+ UINT8 *result;
+
+ while(next_info_block(buffer, &position))
+ ;
+
+ side = position & 0x100;
+ subpos = position & 0x0FF;
+
+ if ((subpos + block_size) > 0x100)
+ return NULL;
+
+ result = &buffer[side + subpos];
+ *(result++) = (UINT8) block_size;
+ *(result++) = block_type;
+ memset(result, 0, block_size - 2);
+ return result;
+}
+
+
+
+static imgtoolerr_t prodos_get_next_dirent(imgtool_image *image,
+ prodos_direnum *appleenum, prodos_dirent *ent)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ size_t finfo_offset;
+ UINT32 next_block, next_index;
+ UINT32 offset;
+ UINT8 buffer[BLOCK_SIZE];
+ const UINT8 *info_ptr;
+ int fork_num;
+
+ di = get_prodos_info(image);
+ memset(ent, 0, sizeof(*ent));
+
+ /* have we hit the end of the file? */
+ if (appleenum->block == 0)
+ return IMGTOOLERR_SUCCESS;
+
+ /* populate the resulting dirent */
+ offset = appleenum->index * di->dirent_size + 4;
+ ent->storage_type = appleenum->block_data[offset + 0];
+ memcpy(ent->filename, &appleenum->block_data[offset + 1], 15);
+ ent->filename[15] = '\0';
+ ent->creation_time = pick_integer_le(appleenum->block_data, offset + 24, 4);
+ ent->lastmodified_time = pick_integer_le(appleenum->block_data, offset + 33, 4);
+ ent->file_type = 0x3F3F3F3F;
+ ent->file_creator = 0x3F3F3F3F;
+ ent->finder_flags = 0;
+ ent->coord_x = 0;
+ ent->coord_y = 0;
+ ent->finder_folder = 0;
+ ent->icon_id = 0;
+ ent->script_code = 0;
+ ent->extended_flags = 0;
+ ent->comment_id = 0;
+ ent->putaway_directory = 0;
+
+ if (is_extendedfile_storagetype(ent->storage_type))
+ {
+ /* this is a ProDOS extended file; we need to get the extended info
+ * block */
+ ent->extkey_pointer = pick_integer_le(appleenum->block_data, offset + 17, 2);
+
+ err = prodos_load_block(image, ent->extkey_pointer, buffer);
+ if (err)
+ return err;
+
+ for (fork_num = 0; fork_num <= 1; fork_num++)
+ {
+ ent->key_pointer[fork_num] = pick_integer_le(buffer, 1 + (fork_num * 256), 2);
+ ent->filesize[fork_num] = pick_integer_le(buffer, 5 + (fork_num * 256), 3);
+ ent->depth[fork_num] = buffer[fork_num * 256] & 0x0F;
+ }
+
+ finfo_offset = 0;
+ while((info_ptr = next_info_block(buffer, &finfo_offset)) != NULL)
+ {
+ if (*(info_ptr++) == 18)
+ {
+ switch(*(info_ptr++))
+ {
+ case 1: /* FInfo */
+ ent->file_type = pick_integer_be(info_ptr, 0, 4);
+ ent->file_creator = pick_integer_be(info_ptr, 4, 4);
+ ent->finder_flags = pick_integer_be(info_ptr, 8, 2);
+ ent->coord_x = pick_integer_be(info_ptr, 10, 2);
+ ent->coord_y = pick_integer_be(info_ptr, 12, 2);
+ ent->finder_folder = pick_integer_be(info_ptr, 14, 4);
+ break;
+
+ case 2: /* xFInfo */
+ ent->icon_id = pick_integer_be(info_ptr, 0, 2);
+ ent->script_code = pick_integer_be(info_ptr, 8, 1);
+ ent->extended_flags = pick_integer_be(info_ptr, 9, 1);
+ ent->comment_id = pick_integer_be(info_ptr, 10, 2);
+ ent->putaway_directory = pick_integer_be(info_ptr, 12, 4);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* normal ProDOS files have all of the info right here */
+ ent->key_pointer[0] = pick_integer_le(appleenum->block_data, offset + 17, 2);
+ ent->filesize[0] = pick_integer_le(appleenum->block_data, offset + 21, 3);
+ ent->depth[0] = ent->storage_type >> 4;
+ }
+
+ /* identify next entry */
+ next_block = appleenum->block;
+ next_index = appleenum->index + 1;
+ if (next_index >= di->dirents_per_block)
+ {
+ next_block = pick_integer_le(appleenum->block_data, 2, 2);
+ next_index = 0;
+ }
+
+ /* seek next block */
+ err = prodos_enum_seek(image, appleenum, next_block, next_index);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+/* changes a normal file to a ProDOS extended file */
+static imgtoolerr_t prodos_promote_file(imgtool_image *image, UINT8 *bitmap, prodos_dirent *ent)
+{
+ imgtoolerr_t err;
+ UINT16 new_block;
+ UINT8 buffer[BLOCK_SIZE];
+
+ assert(is_normalfile_storagetype(ent->storage_type));
+
+ err = prodos_alloc_block(image, bitmap, &new_block);
+ if (err)
+ return err;
+
+ /* create raw extended info block */
+ memset(buffer, 0, sizeof(buffer));
+ err = prodos_save_block(image, new_block, buffer);
+ if (err)
+ return err;
+
+ ent->storage_type = (ent->storage_type & 0x0F) | 0x50;
+ ent->extkey_pointer = new_block;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_put_dirent(imgtool_image *image,
+ prodos_direnum *appleenum, prodos_dirent *ent)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ UINT32 offset;
+ size_t finfo_offset;
+ UINT8 buffer[BLOCK_SIZE];
+ int fork_num;
+ int needs_finfo = FALSE;
+ int needs_xfinfo = FALSE;
+ UINT8 *info_ptr;
+ UINT8 *finfo;
+ UINT8 *xfinfo;
+
+ di = get_prodos_info(image);
+ offset = appleenum->index * di->dirent_size + 4;
+
+ /* determine whether we need FInfo and/or xFInfo */
+ if (is_normalfile_storagetype(ent->storage_type))
+ {
+ needs_finfo = (ent->file_type != 0x3F3F3F3F) ||
+ (ent->file_creator != 0x3F3F3F3F) ||
+ (ent->finder_flags != 0) ||
+ (ent->coord_x != 0) ||
+ (ent->coord_y != 0) ||
+ (ent->finder_folder != 0);
+
+ needs_xfinfo = (ent->icon_id != 0) ||
+ (ent->script_code != 0) ||
+ (ent->extended_flags != 0) ||
+ (ent->comment_id != 0) ||
+ (ent->putaway_directory != 0);
+ }
+
+ /* do we need to promote this file to an extended file? */
+ if (!is_extendedfile_storagetype(ent->storage_type)
+ && (needs_finfo || needs_xfinfo))
+ {
+ err = prodos_promote_file(image, NULL, ent);
+ if (err)
+ return err;
+ }
+
+ /* write out the storage type, filename, creation and lastmodified times */
+ appleenum->block_data[offset + 0] = ent->storage_type;
+ memcpy(&appleenum->block_data[offset + 1], ent->filename, 15);
+ place_integer_le(appleenum->block_data, offset + 24, 4, ent->creation_time);
+ place_integer_le(appleenum->block_data, offset + 33, 4, ent->lastmodified_time);
+
+ if (is_extendedfile_storagetype(ent->storage_type))
+ {
+ /* ProDOS extended file */
+ err = prodos_load_block(image, ent->extkey_pointer, buffer);
+ if (err)
+ return err;
+
+ finfo = NULL;
+ xfinfo = NULL;
+
+ for (fork_num = 0; fork_num <= 1; fork_num++)
+ {
+ place_integer_le(buffer, 1 + (fork_num * 256), 2, ent->key_pointer[fork_num]);
+ place_integer_le(buffer, 5 + (fork_num * 256), 3, ent->filesize[fork_num]);
+ buffer[fork_num * 256] = ent->depth[fork_num];
+ }
+
+ finfo_offset = 0;
+ while((info_ptr = next_info_block(buffer, &finfo_offset)) != NULL)
+ {
+ if (*(info_ptr++) == 18)
+ {
+ switch(*(info_ptr++))
+ {
+ case 1: /* FInfo */
+ finfo = info_ptr;
+ break;
+
+ case 2: /* xFInfo */
+ xfinfo = info_ptr;
+ break;
+ }
+ }
+ }
+
+ /* allocate the finfo and/or xinfo blocks, if we need them */
+ if (needs_finfo && !finfo)
+ finfo = alloc_info_block(buffer, 18, 1);
+ if (needs_xfinfo && !xfinfo)
+ xfinfo = alloc_info_block(buffer, 18, 2);
+
+ if (finfo)
+ {
+ place_integer_be(finfo, 0, 4, ent->file_type);
+ place_integer_be(finfo, 4, 4, ent->file_creator);
+ place_integer_be(finfo, 8, 2, ent->finder_flags);
+ place_integer_be(finfo, 10, 2, ent->coord_x);
+ place_integer_be(finfo, 12, 2, ent->coord_y);
+ place_integer_be(finfo, 14, 4, ent->finder_folder);
+ }
+
+ if (xfinfo)
+ {
+ place_integer_be(xfinfo, 0, 2, ent->icon_id);
+ place_integer_be(xfinfo, 8, 1, ent->script_code);
+ place_integer_be(xfinfo, 9, 1, ent->extended_flags);
+ place_integer_be(xfinfo, 10, 2, ent->comment_id);
+ place_integer_be(xfinfo, 12, 4, ent->putaway_directory);
+ }
+
+ err = prodos_save_block(image, ent->extkey_pointer, buffer);
+ if (err)
+ return err;
+
+ place_integer_le(appleenum->block_data, offset + 17, 2, ent->extkey_pointer);
+ place_integer_le(appleenum->block_data, offset + 21, 3, BLOCK_SIZE);
+ }
+ else
+ {
+ /* normal file */
+ place_integer_le(appleenum->block_data, offset + 17, 2, ent->key_pointer[0]);
+ place_integer_le(appleenum->block_data, offset + 21, 3, ent->filesize[0]);
+ }
+
+ err = prodos_save_block(image, appleenum->block, appleenum->block_data);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_lookup_path(imgtool_image *image, const char *path,
+ creation_policy_t create, prodos_direnum *direnum, prodos_dirent *ent)
+{
+ imgtoolerr_t err;
+ prodos_direnum my_direnum;
+ UINT32 block = ROOTDIR_BLOCK;
+ const char *old_path;
+ UINT16 this_block;
+ UINT32 this_index;
+ UINT16 free_block = 0;
+ UINT32 free_index = 0;
+ UINT16 new_file_block;
+ UINT8 buffer[BLOCK_SIZE];
+
+ if (!direnum)
+ direnum = &my_direnum;
+
+ while(*path)
+ {
+ memset(direnum, 0, sizeof(*direnum));
+ err = prodos_enum_seek(image, direnum, block, 0);
+ if (err)
+ goto done;
+
+ do
+ {
+ this_block = direnum->block;
+ this_index = direnum->index;
+
+ err = prodos_get_next_dirent(image, direnum, ent);
+ if (err)
+ goto done;
+
+ /* if we need to create a file entry and this is free, track it */
+ if (create && this_block && !free_block && !ent->storage_type)
+ {
+ free_block = this_block;
+ free_index = this_index;
+ }
+ }
+ while(direnum->block && (strcmp(path, ent->filename) || (
+ !is_file_storagetype(ent->storage_type) &&
+ !is_dir_storagetype(ent->storage_type))));
+
+ old_path = path;
+ path += strlen(path) + 1;
+ if (*path)
+ {
+ /* we have found part of the path; we are not finished yet */
+ if (!is_dir_storagetype(ent->storage_type))
+ {
+ err = IMGTOOLERR_FILENOTFOUND;
+ goto done;
+ }
+ block = ent->key_pointer[0];
+ }
+ else if (!direnum->block)
+ {
+ /* did not find file; maybe we need to create it */
+ if (create == CREATE_NONE)
+ {
+ err = IMGTOOLERR_FILENOTFOUND;
+ goto done;
+ }
+
+ /* do we need to expand the directory? */
+ if (!free_block)
+ {
+ if (this_block == 0)
+ {
+ err = IMGTOOLERR_CORRUPTFILE;
+ goto done;
+ }
+
+ err = prodos_load_block(image, this_block, buffer);
+ if (err)
+ goto done;
+
+ /* allocate a block */
+ err = prodos_alloc_block(image, NULL, &free_block);
+ if (err)
+ goto done;
+
+ /* clear out this new block */
+ err = prodos_clear_block(image, free_block);
+ if (err)
+ goto done;
+
+ /* save this link */
+ place_integer_le(buffer, 2, 2, free_block);
+ err = prodos_save_block(image, this_block, buffer);
+ if (err)
+ goto done;
+
+ free_index = 0;
+ }
+
+ /* seek back to the free space */
+ err = prodos_enum_seek(image, direnum, free_block, free_index);
+ if (err)
+ goto done;
+
+ new_file_block = 0;
+ if (create == CREATE_DIR)
+ {
+ /* if we are creating a directory, we need to create a new block */
+ err = prodos_alloc_block(image, NULL, &new_file_block);
+ if (err)
+ goto done;
+
+ err = prodos_clear_block(image, new_file_block);
+ if (err)
+ goto done;
+ }
+
+ /* prepare the dirent */
+ memset(ent, 0, sizeof(*ent));
+ ent->storage_type = (create == CREATE_DIR) ? 0xe0 : 0x10;
+ ent->creation_time = ent->lastmodified_time = prodos_time_now();
+ ent->key_pointer[0] = new_file_block;
+ ent->file_type = 0x3F3F3F3F;
+ ent->file_creator = 0x3F3F3F3F;
+ strncpy(ent->filename, old_path, ARRAY_LENGTH(ent->filename));
+
+ /* and place it */
+ err = prodos_put_dirent(image, direnum, ent);
+ if (err)
+ goto done;
+
+ this_block = free_block;
+ this_index = free_index;
+ }
+ else
+ {
+ /* we've found the file; seek that dirent */
+ err = prodos_enum_seek(image, direnum, this_block, this_index);
+ if (err)
+ goto done;
+ }
+ }
+
+ err = IMGTOOLERR_SUCCESS;
+done:
+ return err;
+}
+
+
+
+static imgtoolerr_t prodos_fill_file(imgtool_image *image, UINT8 *bitmap,
+ UINT16 key_block, int key_block_allocated,
+ int depth, UINT32 blockcount, UINT32 block_index)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ int dirty;
+ int sub_block_allocated;
+ UINT16 i, sub_block, new_sub_block;
+ UINT8 buffer[BLOCK_SIZE];
+
+ di = get_prodos_info(image);
+
+ if (key_block_allocated)
+ {
+ /* we are on a recently allocated key block; start fresh */
+ memset(buffer, 0, sizeof(buffer));
+ dirty = TRUE;
+ }
+ else
+ {
+ /* this is a preexisting key block */
+ err = prodos_load_block(image, key_block, buffer);
+ if (err)
+ return err;
+ dirty = FALSE;
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ sub_block_allocated = FALSE;
+
+ sub_block = buffer[i + 256];
+ sub_block <<= 8;
+ sub_block += buffer[i + 0];
+
+ new_sub_block = sub_block;
+ if ((block_index < blockcount) && (sub_block == 0))
+ {
+ err = prodos_alloc_block(image, bitmap, &new_sub_block);
+ if (err)
+ return err;
+ sub_block_allocated = TRUE;
+ }
+ else if ((block_index >= blockcount) && (sub_block != 0))
+ {
+ new_sub_block = 0;
+ if (sub_block < di->total_blocks)
+ prodos_set_volume_bitmap_bit(bitmap, sub_block, 0);
+ }
+
+ /* did we change the block? */
+ if (new_sub_block != sub_block)
+ {
+ dirty = TRUE;
+ buffer[i + 0] = new_sub_block >> 0;
+ buffer[i + 256] = new_sub_block >> 8;
+ if (sub_block == 0)
+ sub_block = new_sub_block;
+ }
+
+ /* call recursive function */
+ if (depth > 2)
+ {
+ err = prodos_fill_file(image, bitmap, sub_block, sub_block_allocated, depth - 1, blockcount, block_index);
+ if (err)
+ return err;
+ }
+
+ /* increment index */
+ block_index += 1 << ((depth - 2) * 8);
+ }
+
+ /* if we changed anything, then save the block */
+ if (dirty)
+ {
+ err = prodos_save_block(image, key_block, buffer);
+ if (err)
+ return err;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_set_file_block_count(imgtool_image *image, prodos_direnum *direnum,
+ prodos_dirent *ent, UINT8 *bitmap, int fork_num, UINT32 new_blockcount)
+{
+ imgtoolerr_t err;
+ int depth, new_depth, i;
+ UINT16 new_block, block;
+ UINT8 buffer[BLOCK_SIZE];
+ UINT16 key_pointer;
+
+ if (fork_num && (new_blockcount > 0) && !is_extendedfile_storagetype(ent->storage_type))
+ {
+ /* need to change a normal file to an extended file */
+ err = prodos_promote_file(image, bitmap, ent);
+ if (err)
+ return err;
+ }
+
+ key_pointer = ent->key_pointer[fork_num];
+ depth = ent->depth[fork_num];
+
+ /* determine the new tree depth */
+ if (new_blockcount <= 1)
+ new_depth = 1;
+ else if (new_blockcount <= 256)
+ new_depth = 2;
+ else
+ new_depth = 3;
+
+ /* are we zero length, and do we have to create a block? */
+ if ((new_blockcount >= 1) && (key_pointer == 0))
+ {
+ err = prodos_alloc_block(image, bitmap, &new_block);
+ if (err)
+ return err;
+ key_pointer = new_block;
+ }
+
+ /* do we have to grow the tree? */
+ while(new_depth > depth)
+ {
+ err = prodos_alloc_block(image, bitmap, &new_block);
+ if (err)
+ return err;
+
+ /* create this new key block, with a link to the previous one */
+ memset(buffer, 0, sizeof(buffer));
+ buffer[0] = (UINT8) (key_pointer >> 0);
+ buffer[256] = (UINT8) (key_pointer >> 8);
+ err = prodos_save_block(image, new_block, buffer);
+ if (err)
+ return err;
+
+ depth++;
+ key_pointer = new_block;
+ }
+
+ /* do we have to shrink the tree? */
+ while(new_depth < depth)
+ {
+ err = prodos_load_block(image, key_pointer, buffer);
+ if (err)
+ return err;
+
+ for (i = 1; i < 256; i++)
+ {
+ block = buffer[i + 256];
+ block <<= 8;
+ block |= buffer[i + 0];
+
+ if (block > 0)
+ {
+ if (depth > 2)
+ {
+ /* remove this block's children */
+ err = prodos_fill_file(image, bitmap, block, FALSE, depth - 1, 0, 0);
+ if (err)
+ return err;
+ }
+
+ /* and remove this block */
+ prodos_set_volume_bitmap_bit(bitmap, block, 0);
+ }
+ }
+
+ /* remove this key block */
+ prodos_set_volume_bitmap_bit(bitmap, key_pointer, 0);
+
+ /* set the new key pointer */
+ block = buffer[256];
+ block <<= 8;
+ block |= buffer[0];
+ key_pointer = block;
+
+ depth--;
+ }
+
+ if (new_blockcount > 0)
+ {
+ /* fill out the file tree */
+ err = prodos_fill_file(image, bitmap, key_pointer, FALSE, depth, new_blockcount, 0);
+ if (err)
+ return err;
+ }
+ else if (key_pointer != 0)
+ {
+ /* we are now zero length, and don't need a key pointer */
+ prodos_set_volume_bitmap_bit(bitmap, key_pointer, 0);
+ key_pointer = 0;
+ }
+
+ /* change the depth if we are not an extended file */
+ if (is_normalfile_storagetype(ent->storage_type))
+ {
+ ent->storage_type &= ~0xF0;
+ ent->storage_type |= depth * 0x10;
+ }
+
+ ent->key_pointer[fork_num] = key_pointer;
+ ent->depth[fork_num] = depth;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_set_file_size(imgtool_image *image, prodos_direnum *direnum,
+ prodos_dirent *ent, int fork_num, UINT32 new_size)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ UINT32 blockcount, new_blockcount;
+ UINT8 *bitmap = NULL;
+
+ if (ent->filesize[fork_num] != new_size)
+ {
+ blockcount = (ent->filesize[fork_num] + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ new_blockcount = (new_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ /* do we need to change the block chain? */
+ if (new_blockcount != blockcount)
+ {
+ err = prodos_load_volume_bitmap(image, &bitmap);
+ if (err)
+ goto done;
+
+ err = prodos_set_file_block_count(image, direnum, ent, bitmap, fork_num, new_blockcount);
+ if (err)
+ goto done;
+
+ err = prodos_save_volume_bitmap(image, bitmap);
+ if (err)
+ goto done;
+ }
+
+ ent->filesize[fork_num] = new_size;
+ err = prodos_put_dirent(image, direnum, ent);
+ if (err)
+ goto done;
+ }
+
+done:
+ if (bitmap)
+ free(bitmap);
+ return err;
+}
+
+
+
+static UINT32 prodos_get_storagetype_maxfilesize(UINT8 storage_type)
+{
+ UINT32 max_filesize = 0;
+ switch(storage_type & 0xF0)
+ {
+ case 0x10:
+ max_filesize = BLOCK_SIZE * 1;
+ break;
+ case 0x20:
+ max_filesize = BLOCK_SIZE * 256;
+ break;
+ case 0x30:
+ case 0x50:
+ max_filesize = BLOCK_SIZE * 32768;
+ break;
+ }
+ return max_filesize;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_beginenum(imgtool_directory *enumeration, const char *path)
+{
+ imgtoolerr_t err;
+ imgtool_image *image;
+ prodos_direnum *appleenum;
+ prodos_dirent ent;
+ UINT16 block = ROOTDIR_BLOCK;
+
+ image = imgtool_directory_image(enumeration);
+ appleenum = (prodos_direnum *) imgtool_directory_extrabytes(enumeration);
+
+ /* find subdirectory, if appropriate */
+ if (*path)
+ {
+ err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
+ if (err)
+ return err;
+
+ /* only work on directories */
+ if (!is_dir_storagetype(ent.storage_type))
+ return IMGTOOLERR_FILENOTFOUND;
+
+ block = ent.key_pointer[0];
+ }
+
+ /* seek initial block */
+ err = prodos_enum_seek(image, appleenum, block, 0);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ imgtoolerr_t err;
+ imgtool_image *image;
+ prodos_direnum *appleenum;
+ prodos_dirent pd_ent;
+ UINT32 max_filesize;
+
+ image = imgtool_directory_image(enumeration);
+ appleenum = (prodos_direnum *) imgtool_directory_extrabytes(enumeration);
+
+ do
+ {
+ err = prodos_get_next_dirent(image, appleenum, &pd_ent);
+ if (err)
+ return err;
+ }
+ while(appleenum->block
+ && !is_file_storagetype(pd_ent.storage_type)
+ && !is_dir_storagetype(pd_ent.storage_type));
+
+ /* end of file? */
+ if (pd_ent.storage_type == 0x00)
+ {
+ ent->eof = 1;
+ return IMGTOOLERR_SUCCESS;
+ }
+
+ strcpy(ent->filename, pd_ent.filename);
+ ent->directory = is_dir_storagetype(pd_ent.storage_type);
+ ent->creation_time = prodos_crack_time(pd_ent.creation_time);
+ ent->lastmodified_time = prodos_crack_time(pd_ent.lastmodified_time);
+
+ if (!ent->directory)
+ {
+ ent->filesize = pd_ent.filesize[0];
+
+ max_filesize = prodos_get_storagetype_maxfilesize(pd_ent.storage_type);
+ if (ent->filesize > max_filesize)
+ {
+ ent->corrupt = 1;
+ ent->filesize = max_filesize;
+ }
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_read_file_tree(imgtool_image *image, UINT32 *filesize,
+ UINT32 block, int nest_level, imgtool_stream *destf)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ UINT8 buffer[BLOCK_SIZE];
+ UINT16 sub_block;
+ size_t bytes_to_write;
+ int i;
+
+ /* check bounds */
+ di = get_prodos_info(image);
+ if (block >= di->total_blocks)
+ return IMGTOOLERR_CORRUPTFILE;
+
+ err = prodos_load_block(image, block, buffer);
+ if (err)
+ return err;
+
+ if (nest_level > 0)
+ {
+ /* this is an index block */
+ for (i = 0; i < 256; i++)
+ {
+ /* retrieve the block pointer; the two bytes are on either half
+ * of the block */
+ sub_block = buffer[i + 256];
+ sub_block <<= 8;
+ sub_block |= buffer[i + 0];
+
+ if (sub_block != 0)
+ {
+ err = prodos_read_file_tree(image, filesize, sub_block, nest_level - 1, destf);
+ if (err)
+ return err;
+ }
+ }
+ }
+ else
+ {
+ /* this is a leaf block */
+ bytes_to_write = MIN(*filesize, sizeof(buffer));
+ stream_write(destf, buffer, bytes_to_write);
+ *filesize -= bytes_to_write;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_write_file_tree(imgtool_image *image, UINT32 *filesize,
+ UINT32 block, int nest_level, imgtool_stream *sourcef)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ UINT8 buffer[BLOCK_SIZE];
+ UINT16 sub_block;
+ size_t bytes_to_read;
+ int i;
+
+ /* nothing more to read? bail */
+ if (*filesize == 0)
+ return IMGTOOLERR_SUCCESS;
+
+ /* check bounds */
+ di = get_prodos_info(image);
+ if (block >= di->total_blocks)
+ return IMGTOOLERR_CORRUPTFILE;
+
+ err = prodos_load_block(image, block, buffer);
+ if (err)
+ return err;
+
+ if (nest_level > 0)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ sub_block = buffer[i + 256];
+ sub_block <<= 8;
+ sub_block |= buffer[i + 0];
+
+ if (sub_block != 0)
+ {
+ err = prodos_write_file_tree(image, filesize, sub_block, nest_level - 1, sourcef);
+ if (err)
+ return err;
+ }
+ }
+ }
+ else
+ {
+ /* this is a leaf block */
+ bytes_to_read = MIN(*filesize, sizeof(buffer));
+ stream_read(sourcef, buffer, bytes_to_read);
+ *filesize -= bytes_to_read;
+
+ err = prodos_save_block(image, block, buffer);
+ if (err)
+ return err;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_freespace(imgtool_partition *partition, UINT64 *size)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_diskinfo *di;
+ UINT8 *bitmap = NULL;
+ UINT16 i;
+
+ di = get_prodos_info(image);
+ *size = 0;
+
+ err = prodos_load_volume_bitmap(image, &bitmap);
+ if (err)
+ goto done;
+
+ for (i = 0; i < di->total_blocks; i++)
+ {
+ if (!prodos_get_volume_bitmap_bit(bitmap, i))
+ *size += BLOCK_SIZE;
+ }
+
+done:
+ if (bitmap)
+ free(bitmap);
+ return err;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_readfile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ UINT16 key_pointer;
+ int nest_level;
+ mac_fork_t fork_num;
+
+ err = prodos_lookup_path(image, filename, CREATE_NONE, NULL, &ent);
+ if (err)
+ return err;
+
+ if (is_dir_storagetype(ent.storage_type))
+ return IMGTOOLERR_FILENOTFOUND;
+
+ err = mac_identify_fork(fork, &fork_num);
+ if (err)
+ return err;
+
+ key_pointer = ent.key_pointer[fork_num];
+ nest_level = ent.depth[fork_num] - 1;
+
+ if (key_pointer != 0)
+ {
+ err = prodos_read_file_tree(image, &ent.filesize[fork_num], key_pointer,
+ nest_level, destf);
+ if (err)
+ return err;
+ }
+
+ /* have we not actually received the correct amount of bytes? if not, fill in the rest */
+ if (ent.filesize[fork_num] > 0)
+ stream_fill(destf, 0, ent.filesize[fork_num]);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_writefile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *sourcef, option_resolution *opts)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ prodos_direnum direnum;
+ UINT64 file_size;
+ mac_fork_t fork_num;
+
+ file_size = stream_size(sourcef);
+
+ err = prodos_lookup_path(image, filename, CREATE_FILE, &direnum, &ent);
+ if (err)
+ return err;
+
+ /* only work on files */
+ if (is_dir_storagetype(ent.storage_type))
+ return IMGTOOLERR_FILENOTFOUND;
+
+ err = mac_identify_fork(fork, &fork_num);
+ if (err)
+ return err;
+
+ /* set the file size */
+ err = prodos_set_file_size(image, &direnum, &ent, fork_num, file_size);
+ if (err)
+ return err;
+
+ err = prodos_write_file_tree(image, &ent.filesize[fork_num], ent.key_pointer[fork_num],
+ ent.depth[fork_num] - 1, sourcef);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_deletefile(imgtool_partition *partition, const char *path)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ prodos_direnum direnum;
+
+ err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
+ if (err)
+ return err;
+
+ /* only work on files */
+ if (is_dir_storagetype(ent.storage_type))
+ return IMGTOOLERR_FILENOTFOUND;
+
+ /* empty out both forks */
+ err = prodos_set_file_size(image, &direnum, &ent, 0, 0);
+ if (err)
+ return err;
+ err = prodos_set_file_size(image, &direnum, &ent, 1, 0);
+ if (err)
+ return err;
+
+ memset(&ent, 0, sizeof(ent));
+ err = prodos_put_dirent(image, &direnum, &ent);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_listforks(imgtool_partition *partition, const char *path, imgtool_forkent *ents, size_t len)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ prodos_direnum direnum;
+ int fork_num = 0;
+
+ err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
+ if (err)
+ return err;
+
+ if (is_dir_storagetype(ent.storage_type))
+ return IMGTOOLERR_FILENOTFOUND;
+
+ /* specify data fork */
+ ents[fork_num].type = FORK_DATA;
+ ents[fork_num].forkname[0] = '\0';
+ ents[fork_num].size = ent.filesize[0];
+ fork_num++;
+
+ if (is_extendedfile_storagetype(ent.storage_type))
+ {
+ /* specify the resource fork */
+ ents[fork_num].type = FORK_RESOURCE;
+ strcpy(ents[fork_num].forkname, "RESOURCE_FORK");
+ ents[fork_num].size = ent.filesize[1];
+ fork_num++;
+ }
+
+ ents[fork_num].type = FORK_END;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_createdir(imgtool_partition *partition, const char *path)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ prodos_direnum direnum;
+
+ err = prodos_lookup_path(image, path, CREATE_DIR, &direnum, &ent);
+ if (err)
+ return err;
+
+ /* only work on directories */
+ if (!is_dir_storagetype(ent.storage_type))
+ return IMGTOOLERR_FILENOTFOUND;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_free_directory(imgtool_image *image, UINT8 *volume_bitmap, UINT16 key_pointer)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ int i;
+ UINT16 next_block;
+ UINT32 offset;
+ UINT8 buffer[BLOCK_SIZE];
+
+ di = get_prodos_info(image);
+
+ if (key_pointer != 0)
+ {
+ err = prodos_load_block(image, key_pointer, buffer);
+ if (err)
+ return err;
+
+ for (i = 0; i < di->dirents_per_block; i++)
+ {
+ offset = i * di->dirent_size + 4;
+
+ if (is_file_storagetype(buffer[offset]) || is_file_storagetype(buffer[offset]))
+ return IMGTOOLERR_DIRNOTEMPTY;
+ }
+
+ next_block = pick_integer_le(buffer, 2, 2);
+
+ err = prodos_free_directory(image, volume_bitmap, next_block);
+ if (err)
+ return err;
+
+ prodos_set_volume_bitmap_bit(volume_bitmap, key_pointer, 0);
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_deletedir(imgtool_partition *partition, const char *path)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ prodos_direnum direnum;
+ UINT8 *volume_bitmap = NULL;
+
+ err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
+ if (err)
+ goto done;
+
+ /* only work on directories */
+ if (!is_dir_storagetype(ent.storage_type))
+ {
+ err = IMGTOOLERR_FILENOTFOUND;
+ goto done;
+ }
+
+ err = prodos_load_volume_bitmap(image, &volume_bitmap);
+ if (err)
+ goto done;
+
+ err = prodos_free_directory(image, volume_bitmap, ent.key_pointer[0]);
+ if (err)
+ goto done;
+
+ err = prodos_save_volume_bitmap(image, volume_bitmap);
+ if (err)
+ goto done;
+
+ memset(&ent, 0, sizeof(ent));
+ err = prodos_put_dirent(image, &direnum, &ent);
+ if (err)
+ goto done;
+
+done:
+ if (volume_bitmap)
+ free(volume_bitmap);
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_get_file_tree(imgtool_image *image, imgtool_chainent *chain, size_t chain_size,
+ size_t *chain_pos, UINT16 block, UINT8 total_depth, UINT8 cur_depth)
+{
+ imgtoolerr_t err;
+ prodos_diskinfo *di;
+ int i;
+ UINT16 sub_block;
+ UINT8 buffer[BLOCK_SIZE];
+
+ if (block == 0)
+ return IMGTOOLERR_SUCCESS;
+ if (*chain_pos >= chain_size)
+ return IMGTOOLERR_SUCCESS;
+
+ /* check bounds */
+ di = get_prodos_info(image);
+ if (block >= di->total_blocks)
+ return IMGTOOLERR_CORRUPTFILE;
+
+ chain[*chain_pos].level = cur_depth;
+ chain[*chain_pos].block = block;
+ (*chain_pos)++;
+
+ /* must we recurse into the tree? */
+ if (cur_depth < total_depth)
+ {
+ err = prodos_load_block(image, block, buffer);
+ if (err)
+ return err;
+
+ for (i = 0; i < 256; i++)
+ {
+ sub_block = buffer[i + 256];
+ sub_block <<= 8;
+ sub_block |= buffer[i + 0];
+
+ err = prodos_get_file_tree(image, chain, chain_size, chain_pos, sub_block, total_depth, cur_depth + 1);
+ if (err)
+ return err;
+ }
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_getattrs(imgtool_partition *partition, const char *path, const UINT32 *attrs, imgtool_attribute *values)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ int i;
+
+ err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
+ if (err)
+ return err;
+
+ for (i = 0; attrs[i]; i++)
+ {
+ switch(attrs[i])
+ {
+ case IMGTOOLATTR_INT_MAC_TYPE:
+ values[i].i = ent.file_type;
+ break;
+ case IMGTOOLATTR_INT_MAC_CREATOR:
+ values[i].i = ent.file_creator;
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
+ values[i].i = ent.finder_flags;
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDX:
+ values[i].i = ent.coord_x;
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDY:
+ values[i].i = ent.coord_y;
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
+ values[i].i = ent.finder_folder;
+ break;
+ case IMGTOOLATTR_INT_MAC_ICONID:
+ values[i].i = ent.icon_id;
+ break;
+ case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
+ values[i].i = ent.script_code;
+ break;
+ case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
+ values[i].i = ent.extended_flags;
+ break;
+ case IMGTOOLATTR_INT_MAC_COMMENTID:
+ values[i].i = ent.comment_id;
+ break;
+ case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
+ values[i].i = ent.putaway_directory;
+ break;
+
+ case IMGTOOLATTR_TIME_CREATED:
+ values[i].t = prodos_crack_time(ent.creation_time);
+ break;
+ case IMGTOOLATTR_TIME_LASTMODIFIED:
+ values[i].t = prodos_crack_time(ent.lastmodified_time);
+ break;
+ }
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_setattrs(imgtool_partition *partition, const char *path, const UINT32 *attrs, const imgtool_attribute *values)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ prodos_direnum direnum;
+ int i;
+
+ err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
+ if (err)
+ return err;
+
+ for (i = 0; attrs[i]; i++)
+ {
+ switch(attrs[i])
+ {
+ case IMGTOOLATTR_INT_MAC_TYPE:
+ ent.file_type = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_CREATOR:
+ ent.file_creator = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
+ ent.finder_flags = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDX:
+ ent.coord_x = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDY:
+ ent.coord_y = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
+ ent.finder_folder = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_ICONID:
+ ent.icon_id = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
+ ent.script_code = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
+ ent.extended_flags = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_COMMENTID:
+ ent.comment_id = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
+ ent.putaway_directory = values[i].i;
+ break;
+
+ case IMGTOOLATTR_TIME_CREATED:
+ ent.creation_time = prodos_setup_time(values[i].t);
+ break;
+ case IMGTOOLATTR_TIME_LASTMODIFIED:
+ ent.lastmodified_time = prodos_setup_time(values[i].t);
+ break;
+ }
+ }
+
+ err = prodos_put_dirent(image, &direnum, &ent);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_suggesttransfer(imgtool_partition *partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ mac_filecategory_t file_category = MAC_FILECATEGORY_DATA;
+
+ if (path)
+ {
+ err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
+ if (err)
+ return err;
+
+ file_category = is_extendedfile_storagetype(ent.storage_type)
+ ? MAC_FILECATEGORY_FORKED : MAC_FILECATEGORY_DATA;
+ }
+
+ mac_suggest_transfer(file_category, suggestions, suggestions_length);
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t prodos_diskimage_getchain(imgtool_partition *partition, const char *path, imgtool_chainent *chain, size_t chain_size)
+{
+ imgtoolerr_t err;
+ imgtool_image *image = imgtool_partition_image(partition);
+ prodos_dirent ent;
+ size_t chain_pos = 0;
+ int fork_num;
+
+ err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
+ if (err)
+ return err;
+
+ switch(ent.storage_type & 0xF0)
+ {
+ case 0x10:
+ case 0x20:
+ case 0x30:
+ /* normal ProDOS file */
+ err = prodos_get_file_tree(image, chain, chain_size, &chain_pos,
+ ent.key_pointer[0], ent.depth[0] - 1, 0);
+ if (err)
+ return err;
+ break;
+
+ case 0x50:
+ /* extended ProDOS file */
+ chain[chain_pos].level = 0;
+ chain[chain_pos].block = ent.extkey_pointer;
+ chain_pos++;
+
+ for (fork_num = 0; fork_num <= 1; fork_num++)
+ {
+ if (ent.key_pointer[fork_num])
+ {
+ err = prodos_get_file_tree(image, chain, chain_size, &chain_pos,
+ ent.key_pointer[fork_num], ent.depth[fork_num] - 1, 1);
+ if (err)
+ return err;
+ }
+ }
+ break;
+
+ case 0xE0:
+ /* directory */
+ return IMGTOOLERR_UNIMPLEMENTED;
+
+ default:
+ return IMGTOOLERR_UNEXPECTED;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static void generic_prodos_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_SUPPORTS_CREATION_TIME: info->i = 1; break;
+ case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break;
+ case IMGTOOLINFO_INT_WRITING_UNTESTED: info->i = 1; break;
+ case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(prodos_diskinfo); break;
+ case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(prodos_direnum); break;
+ case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "ProDOS 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_BEGIN_ENUM: info->begin_enum = prodos_diskimage_beginenum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = prodos_diskimage_nextenum; break;
+ case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = prodos_diskimage_freespace; break;
+ case IMGTOOLINFO_PTR_READ_FILE: info->read_file = prodos_diskimage_readfile; break;
+ case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = prodos_diskimage_writefile; break;
+ case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = prodos_diskimage_deletefile; break;
+ case IMGTOOLINFO_PTR_LIST_FORKS: info->list_forks = prodos_diskimage_listforks; break;
+ case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = prodos_diskimage_createdir; break;
+ case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = prodos_diskimage_deletedir; break;
+ case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = prodos_diskimage_getattrs; break;
+ case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = prodos_diskimage_setattrs; break;
+ case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = prodos_diskimage_suggesttransfer; break;
+ case IMGTOOLINFO_PTR_GET_CHAIN: info->get_chain = prodos_diskimage_getchain; break;
+ }
+}
+
+
+
+void prodos_525_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "prodos_525"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = prodos_diskimage_create_525; break;
+ case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = prodos_diskimage_open_525; break;
+ case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple2; break;
+
+ default: generic_prodos_get_info(imgclass, state, info); break;
+ }
+}
+
+
+
+void prodos_35_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "prodos_35"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = prodos_diskimage_create_35; break;
+ case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = prodos_diskimage_open_35; break;
+ case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple35_iigs; break;
+
+ default: generic_prodos_get_info(imgclass, state, info); break;
+ }
+}