summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/tools/imgtool/modules/fat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/tools/imgtool/modules/fat.c')
-rw-r--r--src/mess/tools/imgtool/modules/fat.c2114
1 files changed, 2114 insertions, 0 deletions
diff --git a/src/mess/tools/imgtool/modules/fat.c b/src/mess/tools/imgtool/modules/fat.c
new file mode 100644
index 00000000000..a60ac7a0603
--- /dev/null
+++ b/src/mess/tools/imgtool/modules/fat.c
@@ -0,0 +1,2114 @@
+/****************************************************************************
+
+ fat.c
+
+ PC FAT disk images
+
+*****************************************************************************
+
+ Master boot record format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 446 Boot machine code
+ 446 16 Partion #1 info
+ 462 16 Partion #2 info
+ 478 16 Partion #3 info
+ 494 16 Partion #4 info
+ 510 2 Magic bytes (0x55 0xAA)
+
+
+ Partition info format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 1 Active byte (0x80=active 0x00=inactive)
+ 1 1 Starting head
+ 2 1 Starting sector (bits 5-0) and high bits of starting track (bits 6-5)
+ 3 1 Low bits of starting track
+ 4 1 Partition type:
+ 0x00 Unused
+ 0x?1 FAT12 (0-15 MB)
+ 0x?2 XENIX
+ 0x?4 FAT16 (16-32 MB)
+ 0x?6 FAT16` (32 MB-2 GB)
+ 0x?7 HPFS or NTFS
+ 0x?A Boot Manager
+ 0x?B FAT32 (512 MB-2 TB)
+ 0x?C FAT32 (512 MB-2 TB LBA)
+ 0x1? OS/2 Boot manager/Win95 hidden
+ 0xC? DR-DOS secured partition
+ 0xD? Multiuser DOS secured partition
+ 0xE? SpeedStor extended partition
+ 5 1 Ending head
+ 6 1 Ending sector (bits 5-0) and high bits of ending track (bits 6-5)
+ 7 1 Low bits of ending track
+ 8 4 Sector index of beginning of partition
+ 12 4 Total sectors in partition
+
+
+ Boot sector format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 3 Jump instruction (to skip over header on boot)
+ 3 8 OEM Name
+ 11 2 Bytes per sector
+ 13 1 Sectors per cluster
+ 14 2 Reserved sector count (including boot sector)
+ 16 1 Number of FATs (file allocation tables)
+ 17 2 Number of root directory entries
+ 19 2 Total sectors (bits 0-15)
+ 21 1 Media descriptor
+ 22 2 Sectors per FAT
+ 24 2 Sectors per track
+ 26 2 Number of heads
+ 28 4 Hidden sectors
+ 32 4 Total sectors (bits 16-47)
+ 36 1 Physical drive number
+ 37 1 Current head
+ 38 1 Signature
+ 39 4 ID
+ 43 11 Volume Label
+ 54 8 FAT file system type
+ 62 448 Boot machine code
+ 510 2 Magic bytes (0x55 0xAA)
+
+ For more information:
+ http://support.microsoft.com/kb/q140418/
+
+
+ Directory Entry Format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 8 DOS File Name (padded with spaces)
+ 8 3 DOS File Extension (padded with spaces)
+ 11 1 File Attributes
+ 12 2 Unknown
+ 14 4 Time of Creation
+ 18 2 Last Access Time
+ 20 2 EA-Index (OS/2 stuff)
+ 22 4 Last Modified Time
+ 26 2 First Cluster
+ 28 4 File Size
+
+
+ Dates and times are stored in separate words; when together, the time is
+ first and the date is second.
+
+ Time:
+ bits 15-11 Hour
+ bits 10- 5 Minute
+ bits 4- 0 Second / 2
+
+ Date:
+ bits 15- 9 Year - 1980
+ bits 8- 5 Month
+ bits 4- 0 Day
+
+ LFN Entry Format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 1 Sequence Number (bit 6 is set on highest sequence)
+ 1 10 Name characters (five UTF-16LE chars)
+ 11 1 Attributes (always 0x0F)
+ 12 1 Reserved (always 0x00)
+ 13 1 Checksum of short filename entry
+ 14 12 Name characters (six UTF-16LE chars)
+ 26 2 Entry Cluster (always 0x00)
+ 28 4 Name characters (two UTF-16LE chars)
+
+ Valid characters in DOS file names:
+ - Upper case letters A-Z
+ - Numbers 0-9
+ - Space (though there is no way to identify a trailing space)
+ - ! # $ % & ( ) - @ ^ _ ` { } ~
+ - Characters 128-255 (though the code page is indeterminate)
+
+ For more information:
+ http://en.wikipedia.org/wiki/File_Allocation_Table
+
+****************************************************************************/
+
+#include <time.h>
+#include <ctype.h>
+#include "imgtool.h"
+#include "formats/imageutl.h"
+#include "unicode.h"
+#include "fat.h"
+
+#define FAT_DIRENT_SIZE 32
+#define FAT_SECLEN 512
+
+#define LOG(x)
+
+typedef struct _fat_partition_info fat_partition_info;
+struct _fat_partition_info
+{
+ UINT32 fat_bits;
+ UINT32 sectors_per_cluster;
+ UINT32 cluster_size;
+ UINT32 reserved_sectors;
+ UINT32 fat_count;
+ UINT32 root_entries;
+ UINT32 sectors_per_fat;
+ UINT64 total_sectors;
+ UINT32 total_clusters;
+};
+
+typedef struct _fat_file fat_file;
+struct _fat_file
+{
+ unsigned int root : 1;
+ unsigned int directory : 1;
+ unsigned int eof : 1;
+ UINT32 index;
+ UINT32 filesize;
+ UINT32 first_cluster;
+ UINT32 parent_first_cluster;
+ UINT32 cluster;
+ UINT32 cluster_index;
+ UINT32 dirent_sector_index;
+ UINT32 dirent_sector_offset;
+};
+
+typedef struct _fat_dirent fat_dirent;
+struct _fat_dirent
+{
+ char long_filename[512];
+ char short_filename[13];
+ unsigned int directory : 1;
+ unsigned int eof : 1;
+ UINT32 filesize;
+ UINT32 first_cluster;
+ UINT32 dirent_sector_index;
+ UINT32 dirent_sector_offset;
+ time_t creation_time;
+ time_t lastmodified_time;
+};
+
+typedef struct _fat_freeentry_info fat_freeentry_info;
+struct _fat_freeentry_info
+{
+ UINT32 required_size;
+ UINT32 candidate_position;
+ UINT32 position;
+};
+
+typedef struct _fat_mediatype fat_mediatype;
+struct _fat_mediatype
+{
+ UINT8 media_descriptor;
+ UINT8 heads;
+ UINT8 tracks;
+ UINT8 sectors;
+};
+
+typedef enum
+{
+ CREATE_NONE,
+ CREATE_FILE,
+ CREATE_DIR
+} creation_policy_t;
+
+
+
+static const fat_mediatype known_media[] =
+{
+ { 0xF0, 2, 80, 36 },
+ { 0xF0, 2, 80, 18 },
+ { 0xF9, 2, 80, 9 },
+ { 0xF9, 2, 80, 15 },
+ { 0xFD, 2, 40, 9 },
+ { 0xFF, 2, 40, 8 },
+ { 0xFC, 1, 40, 9 },
+ { 0xFE, 1, 40, 8 },
+ { 0xF8, 0, 0, 0 }
+};
+
+/* boot sector code taken from FreeDOS */
+static const UINT8 boot_sector_code[] =
+{
+ 0xfa, 0xfc, 0x31, 0xc0, 0x8e, 0xd8, 0xbd, 0x00, 0x7c, 0xb8, 0xe0, 0x1f,
+ 0x8e, 0xc0, 0x89, 0xee, 0x89, 0xef, 0xb9, 0x00, 0x01, 0xf3, 0xa5, 0xea,
+ 0x5e, 0x7c, 0xe0, 0x1f, 0x00, 0x00, 0x60, 0x00, 0x8e, 0xd8, 0x8e, 0xd0,
+ 0x8d, 0x66, 0xa0, 0xfb, 0x80, 0x7e, 0x24, 0xff, 0x75, 0x03, 0x88, 0x56,
+ 0x24, 0xc7, 0x46, 0xc0, 0x10, 0x00, 0xc7, 0x46, 0xc2, 0x01, 0x00, 0x8b,
+ 0x76, 0x1c, 0x8b, 0x7e, 0x1e, 0x03, 0x76, 0x0e, 0x83, 0xd7, 0x00, 0x89,
+ 0x76, 0xd2, 0x89, 0x7e, 0xd4, 0x8a, 0x46, 0x10, 0x98, 0xf7, 0x66, 0x16,
+ 0x01, 0xc6, 0x11, 0xd7, 0x89, 0x76, 0xd6, 0x89, 0x7e, 0xd8, 0x8b, 0x5e,
+ 0x0b, 0xb1, 0x05, 0xd3, 0xeb, 0x8b, 0x46, 0x11, 0x31, 0xd2, 0xf7, 0xf3,
+ 0x89, 0x46, 0xd0, 0x01, 0xc6, 0x83, 0xd7, 0x00, 0x89, 0x76, 0xda, 0x89,
+ 0x7e, 0xdc, 0x8b, 0x46, 0xd6, 0x8b, 0x56, 0xd8, 0x8b, 0x7e, 0xd0, 0xc4,
+ 0x5e, 0x5a, 0xe8, 0xac, 0x00, 0xc4, 0x7e, 0x5a, 0xb9, 0x0b, 0x00, 0xbe,
+ 0xf1, 0x7d, 0x57, 0xf3, 0xa6, 0x5f, 0x26, 0x8b, 0x45, 0x1a, 0x74, 0x0b,
+ 0x83, 0xc7, 0x20, 0x26, 0x80, 0x3d, 0x00, 0x75, 0xe7, 0x72, 0x68, 0x50,
+ 0xc4, 0x5e, 0x5a, 0x8b, 0x7e, 0x16, 0x8b, 0x46, 0xd2, 0x8b, 0x56, 0xd4,
+ 0xe8, 0x7e, 0x00, 0x58, 0x1e, 0x07, 0x8e, 0x5e, 0x5c, 0xbf, 0x00, 0x20,
+ 0xab, 0x89, 0xc6, 0x01, 0xf6, 0x01, 0xc6, 0xd1, 0xee, 0xad, 0x73, 0x04,
+ 0xb1, 0x04, 0xd3, 0xe8, 0x80, 0xe4, 0x0f, 0x3d, 0xf8, 0x0f, 0x72, 0xe8,
+ 0x31, 0xc0, 0xab, 0x0e, 0x1f, 0xc4, 0x5e, 0x5a, 0xbe, 0x00, 0x20, 0xad,
+ 0x09, 0xc0, 0x75, 0x05, 0x88, 0xd3, 0xff, 0x6e, 0x5a, 0x48, 0x48, 0x8b,
+ 0x7e, 0x0d, 0x81, 0xe7, 0xff, 0x00, 0xf7, 0xe7, 0x03, 0x46, 0xda, 0x13,
+ 0x56, 0xdc, 0xe8, 0x34, 0x00, 0xeb, 0xe0, 0x5e, 0xac, 0x56, 0xb4, 0x0e,
+ 0xcd, 0x10, 0x3c, 0x2e, 0x75, 0xf5, 0xc3, 0xe8, 0xf1, 0xff, 0x45, 0x72,
+ 0x72, 0x6f, 0x72, 0x21, 0x20, 0x48, 0x69, 0x74, 0x20, 0x61, 0x20, 0x6b,
+ 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x30, 0xe4, 0xcd, 0x13, 0xcd, 0x16, 0xcd, 0x19, 0x56, 0x89, 0x46,
+ 0xc8, 0x89, 0x56, 0xca, 0x8c, 0x46, 0xc6, 0x89, 0x5e, 0xc4, 0xe8, 0xbe,
+ 0xff, 0x2e, 0xb4, 0x41, 0xbb, 0xaa, 0x55, 0x8a, 0x56, 0x24, 0x84, 0xd2,
+ 0x74, 0x19, 0xcd, 0x13, 0x72, 0x15, 0xd1, 0xe9, 0x81, 0xdb, 0x54, 0xaa,
+ 0x75, 0x0d, 0x8d, 0x76, 0xc0, 0x89, 0x5e, 0xcc, 0x89, 0x5e, 0xce, 0xb4,
+ 0x42, 0xeb, 0x26, 0x8b, 0x4e, 0xc8, 0x8b, 0x56, 0xca, 0x8a, 0x46, 0x18,
+ 0xf6, 0x66, 0x1a, 0x91, 0xf7, 0xf1, 0x92, 0xf6, 0x76, 0x18, 0x89, 0xd1,
+ 0x88, 0xc6, 0x86, 0xe9, 0xd0, 0xc9, 0xd0, 0xc9, 0x08, 0xe1, 0x41, 0xc4,
+ 0x5e, 0xc4, 0xb8, 0x01, 0x02, 0x8a, 0x56, 0x24, 0xcd, 0x13, 0x0f, 0x82,
+ 0x75, 0xff, 0x8b, 0x46, 0x0b, 0xf6, 0x76, 0xc0, 0x01, 0x46, 0xc6, 0x83,
+ 0x46, 0xc8, 0x01, 0x83, 0x56, 0xca, 0x00, 0x4f, 0x75, 0x98, 0x8e, 0x46,
+ 0xc6, 0x5e, 0xc3, 0x4d, 0x45, 0x54, 0x41, 0x4b, 0x45, 0x52, 0x4e, 0x53,
+ 0x59, 0x53, 0x00, 0x00, 0x55, 0xaa
+};
+
+static const char fat8_string[8] = { 'F', 'A', 'T', ' ', ' ', ' ', ' ', ' ' };
+static const char fat12_string[8] = { 'F', 'A', 'T', '1', '2', ' ', ' ', ' ' };
+static const char fat16_string[8] = { 'F', 'A', 'T', '1', '6', ' ', ' ', ' ' };
+static const char fat32_string[8] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' };
+
+
+
+static fat_partition_info *fat_get_partition_info(imgtool_partition *partition)
+{
+ return (fat_partition_info *) imgtool_partition_extra_bytes(partition);
+}
+
+
+
+static imgtoolerr_t fat_read_sector(imgtool_partition *partition, UINT32 sector_index,
+ int offset, void *buffer, size_t buffer_len)
+{
+ //const fat_partition_info *disk_info;
+ imgtoolerr_t err;
+ UINT8 data[FAT_SECLEN];
+ UINT32 block_size;
+ size_t len;
+
+ //disk_info = fat_get_partition_info(partition);
+
+ /* sanity check */
+ err = imgtool_partition_get_block_size(partition, &block_size);
+ if (err)
+ return err;
+ assert(block_size == sizeof(data));
+
+ while(buffer_len > 0)
+ {
+ err = imgtool_partition_read_block(partition, sector_index++, data);
+ if (err)
+ return err;
+
+ len = MIN(buffer_len, sizeof(data) - offset);
+ memcpy(buffer, data + offset, len);
+
+ buffer = ((UINT8 *) buffer) + len;
+ buffer_len -= len;
+ offset = 0;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_write_sector(imgtool_partition *partition, UINT32 sector_index,
+ int offset, const void *buffer, size_t buffer_len)
+{
+ //const fat_partition_info *disk_info;
+ imgtoolerr_t err;
+ UINT8 data[FAT_SECLEN];
+ const void *write_data;
+ UINT32 block_size;
+ size_t len;
+
+ //disk_info = fat_get_partition_info(partition);
+
+ /* sanity check */
+ err = imgtool_partition_get_block_size(partition, &block_size);
+ if (err)
+ return err;
+ assert(block_size == sizeof(data));
+
+ while(buffer_len > 0)
+ {
+ len = MIN(buffer_len, sizeof(data) - offset);
+
+ if ((offset != 0) || (buffer_len < sizeof(data)))
+ {
+ err = imgtool_partition_read_block(partition, sector_index, data);
+ if (err)
+ return err;
+ memcpy(data + offset, buffer, len);
+ write_data = data;
+ }
+ else
+ {
+ write_data = buffer;
+ }
+
+ err = imgtool_partition_write_block(partition, sector_index++, write_data);
+ if (err)
+ return err;
+
+ buffer = ((UINT8 *) buffer) + len;
+ buffer_len -= len;
+ offset = 0;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+#ifdef UNUSED_FUNCTION
+static imgtoolerr_t fat_clear_sector(imgtool_partition *partition, UINT32 sector_index, UINT8 data)
+{
+ char buf[FAT_SECLEN];
+ memset(buf, data, sizeof(buf));
+ return fat_write_sector(partition, sector_index, 0, buf, sizeof(buf));
+}
+#endif
+
+
+static imgtoolerr_t fat_partition_open(imgtool_partition *partition, UINT64 first_block, UINT64 block_count)
+{
+ UINT8 header[FAT_SECLEN];
+ imgtoolerr_t err;
+ fat_partition_info *info;
+ UINT32 fat_bits, total_sectors_l, total_sectors_h, sector_size;
+ UINT64 available_sectors;
+ //int has_extended_bios_param_block = TRUE;
+
+ info = fat_get_partition_info(partition);
+
+ /* read the boot/root sector */
+ err = fat_read_sector(partition, 0, 0, header, sizeof(header));
+ if (err)
+ return err;
+
+ /* magic bytes present? */
+ if ((header[510] != 0x55) || (header[511] != 0xAA))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* determine which type of FAT is on this disk */
+ if (!memcmp(&header[54], fat8_string, sizeof(fat8_string)))
+ fat_bits = 8;
+ else if (!memcmp(&header[54], fat12_string, sizeof(fat12_string)))
+ fat_bits = 12;
+ else if (!memcmp(&header[54], fat16_string, sizeof(fat16_string)))
+ fat_bits = 16;
+ else if (!memcmp(&header[54], fat32_string, sizeof(fat32_string)))
+ fat_bits = 32;
+ else
+ {
+ fat_bits = 8;
+ //has_extended_bios_param_block = FALSE;
+ }
+
+ info->fat_bits = fat_bits;
+ sector_size = pick_integer_le(header, 11, 2);
+ info->sectors_per_cluster = pick_integer_le(header, 13, 1);
+ info->reserved_sectors = pick_integer_le(header, 14, 2);
+ info->fat_count = pick_integer_le(header, 16, 1);
+ info->root_entries = pick_integer_le(header, 17, 2);
+ total_sectors_l = pick_integer_le(header, 19, 2);
+ info->sectors_per_fat = pick_integer_le(header, 22, 2);
+ total_sectors_h = pick_integer_le(header, 32, 4);
+
+ if (info->sectors_per_cluster == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ info->total_sectors = total_sectors_l + (((UINT64) total_sectors_h) << 16);
+ available_sectors = info->total_sectors - info->reserved_sectors
+ - (info->sectors_per_fat * info->fat_count)
+ - (info->root_entries * FAT_DIRENT_SIZE + FAT_SECLEN - 1) / FAT_SECLEN;
+ info->total_clusters = (available_sectors + info->sectors_per_cluster - 1) / info->sectors_per_cluster;
+ info->cluster_size = FAT_SECLEN * info->sectors_per_cluster;
+
+ if (info->fat_count == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (sector_size != FAT_SECLEN)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (info->sectors_per_fat == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (info->sectors_per_cluster == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (info->reserved_sectors == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (info->total_clusters * info->fat_bits > info->sectors_per_fat * FAT_SECLEN * 8)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_create(imgtool_image *image, UINT64 first_block, UINT64 block_count)
+{
+ imgtoolerr_t err;
+ UINT32 heads, tracks, sectors_per_track;
+ UINT32 fat_bits, sectors_per_cluster, reserved_sectors, hidden_sectors;
+ UINT32 root_dir_count, root_dir_sectors;
+ UINT32 sectors_per_fat, fat_count, i;
+ UINT32 boot_sector_offset;
+ UINT64 total_clusters;
+ UINT8 media_descriptor;
+ //const char *title;
+ const char *fat_bits_string;
+ UINT8 header[FAT_SECLEN];
+#if 0
+ UINT64 first_fat_entries;
+#endif
+
+ /* check for limits */
+ if (block_count > U64(0xFFFFFFFFFFFF))
+ return IMGTOOLERR_PARAMTOOLARGE;
+
+ /* get the geometry */
+ err = imgtool_image_get_geometry(image, &tracks, &heads, &sectors_per_track);
+ if (err)
+ return err;
+
+ /* cap our sector count so that we only use FAT12/16 */
+ sectors_per_cluster = (block_count + 65524 - 1) / 65524;
+
+ /* compute the FAT file system type */
+ if ((block_count / sectors_per_cluster) <= 4084)
+ {
+ fat_bits = 12;
+ fat_bits_string = fat12_string;
+ }
+ else if ((block_count / sectors_per_cluster) <= 65524)
+ {
+ fat_bits = 16;
+ fat_bits_string = fat16_string;
+ }
+ else
+ {
+ fat_bits = 32;
+ fat_bits_string = fat32_string;
+ }
+
+ /* figure out media type */
+ i = 0;
+ while((known_media[i].heads > 0) && ((known_media[i].heads != heads)
+ || (known_media[i].tracks != tracks)
+ || (known_media[i].sectors != sectors_per_track)))
+ {
+ i++;
+ }
+ media_descriptor = known_media[i].media_descriptor;
+
+ /* other miscellaneous settings */
+ //title = "";
+ fat_count = 2;
+ root_dir_count = 512;
+ hidden_sectors = 0;
+ reserved_sectors = 1;
+
+ /* calculated settings */
+ root_dir_sectors = (root_dir_count * FAT_DIRENT_SIZE + FAT_SECLEN - 1) / FAT_SECLEN;
+ total_clusters = (block_count - reserved_sectors - hidden_sectors - root_dir_sectors)
+ / sectors_per_cluster;
+ sectors_per_fat = (total_clusters * fat_bits + (FAT_SECLEN * 8) - 1)
+ / (FAT_SECLEN * 8);
+
+ /* prepare the header */
+ memset(header, 0, sizeof(header));
+ memcpy(&header[3], "IMGTOOL ", 8);
+ place_integer_le(header, 11, 2, FAT_SECLEN);
+ place_integer_le(header, 13, 1, sectors_per_cluster);
+ place_integer_le(header, 14, 1, reserved_sectors);
+ place_integer_le(header, 16, 1, fat_count);
+ place_integer_le(header, 17, 2, root_dir_count);
+ place_integer_le(header, 19, 2, (UINT16) (block_count >> 0));
+ place_integer_le(header, 21, 1, media_descriptor);
+ place_integer_le(header, 22, 2, sectors_per_fat);
+ place_integer_le(header, 24, 2, sectors_per_track);
+ place_integer_le(header, 26, 2, heads);
+ place_integer_le(header, 28, 4, hidden_sectors);
+ place_integer_le(header, 32, 4, (UINT32) (block_count >> 16));
+ place_integer_le(header, 36, 1, 0xFF);
+ place_integer_le(header, 38, 1, 0x28);
+ place_integer_le(header, 39, 4, imgtool_image_rand(image));
+ memcpy(&header[43], " ", 11);
+ memcpy(&header[54], fat_bits_string, 8);
+
+ /* store boot code */
+ boot_sector_offset = sizeof(header) - sizeof(boot_sector_code);
+ if (boot_sector_offset < 62)
+ return IMGTOOLERR_UNEXPECTED; /* sanity check */
+ if (boot_sector_offset > 510)
+ return IMGTOOLERR_UNEXPECTED; /* sanity check */
+ memcpy(&header[boot_sector_offset], boot_sector_code, sizeof(boot_sector_code));
+
+ /* specify jump instruction */
+ if (boot_sector_offset <= 129)
+ {
+ header[0] = 0xEB; /* JMP rel8 */
+ header[1] = (UINT8) (boot_sector_offset - 2); /* (offset) */
+ header[2] = 0x90; /* NOP */
+ }
+ else
+ {
+ header[0] = 0xE9; /* JMP rel16 */
+ header[1] = (UINT8) ((boot_sector_offset - 2) >> 0); /* (offset) */
+ header[2] = (UINT8) ((boot_sector_offset - 2) >> 8); /* (offset) */
+ }
+
+ err = imgtool_image_write_block(image, first_block, header);
+ if (err)
+ return err;
+
+ /* clear out file allocation table */
+ for (i = reserved_sectors; i < (reserved_sectors + sectors_per_fat * fat_count + root_dir_sectors); i++)
+ {
+ err = imgtool_image_clear_block(image, first_block + i, 0);
+ if (err)
+ return err;
+ }
+
+ // FIXME: this causes a corrupt PC floppy image since it doubles the FAT partition header - works without it though
+#if 0
+ /* set first two FAT entries */
+ first_fat_entries = ((UINT64) media_descriptor) | 0xFFFFFF00;
+ first_fat_entries &= (((UINT64) 1) << fat_bits) - 1;
+ first_fat_entries |= ((((UINT64) 1) << fat_bits) - 1) << fat_bits;
+ first_fat_entries = LITTLE_ENDIANIZE_INT64(first_fat_entries);
+
+ for (i = 0; i < fat_count; i++)
+ {
+ err = imgtool_image_write_block(image, first_block + 1 + (i * sectors_per_fat), &first_fat_entries);
+ if (err)
+ return err;
+ }
+#endif
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_load_fat(imgtool_partition *partition, UINT8 **fat_table)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ const fat_partition_info *disk_info;
+ UINT8 *table;
+ UINT32 table_size;
+ UINT32 pos, len;
+ UINT32 sector_index;
+
+ disk_info = fat_get_partition_info(partition);
+
+ table_size = disk_info->sectors_per_fat * disk_info->fat_count * FAT_SECLEN;
+
+ /* allocate the table with extra bytes, in case we "overextend" our reads */
+ table = (UINT8*)malloc(table_size + sizeof(UINT64));
+ if (!table)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(table, 0, table_size + sizeof(UINT64));
+
+ pos = 0;
+ sector_index = disk_info->reserved_sectors;
+
+ while(pos < table_size)
+ {
+ len = MIN(table_size - pos, FAT_SECLEN);
+
+ err = fat_read_sector(partition, sector_index++, 0, &table[pos], len);
+ if (err)
+ goto done;
+
+ pos += FAT_SECLEN;
+ }
+
+done:
+ if (err && table)
+ {
+ free(table);
+ table = NULL;
+ }
+ *fat_table = table;
+ return err;
+}
+
+
+
+static imgtoolerr_t fat_save_fat(imgtool_partition *partition, const UINT8 *fat_table)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ const fat_partition_info *disk_info;
+ UINT32 table_size;
+ UINT32 pos, len;
+ UINT32 sector_index;
+
+ disk_info = fat_get_partition_info(partition);
+
+ table_size = disk_info->sectors_per_fat * disk_info->fat_count * FAT_SECLEN;
+
+ pos = 0;
+ sector_index = disk_info->reserved_sectors;
+
+ while(pos < table_size)
+ {
+ len = MIN(table_size - pos, FAT_SECLEN);
+
+ err = fat_write_sector(partition, sector_index++, 0, &fat_table[pos], len);
+ if (err)
+ goto done;
+
+ pos += FAT_SECLEN;
+ }
+
+done:
+ return err;
+}
+
+
+
+static UINT32 fat_get_fat_entry(imgtool_partition *partition, const UINT8 *fat_table, UINT32 fat_entry)
+{
+ const fat_partition_info *disk_info;
+ UINT64 entry;
+ UINT32 bit_index, i;
+ UINT32 last_entry = 0;
+ UINT32 bit_mask;
+
+ disk_info = fat_get_partition_info(partition);
+ bit_index = fat_entry * disk_info->fat_bits;
+ bit_mask = 0xFFFFFFFF >> (32 - disk_info->fat_bits);
+
+ if (fat_entry >= disk_info->total_clusters)
+ {
+ assert(0);
+ return 1;
+ }
+
+ /* make sure that the cluster is free in all fats */
+ for (i = 0; i < disk_info->fat_count; i++)
+ {
+ memcpy(&entry, fat_table + (i * FAT_SECLEN
+ * disk_info->sectors_per_fat) + (bit_index / 8), sizeof(entry));
+
+ /* we've extracted the bytes; we now need to normalize it */
+ entry = LITTLE_ENDIANIZE_INT64(entry);
+ entry >>= bit_index % 8;
+ entry &= bit_mask;
+
+ if (i == 0)
+ last_entry = (UINT32) entry;
+ else if (last_entry != (UINT32) entry)
+ return 1; /* if the FATs disagree; mark this as reserved */
+ }
+
+ /* normalize special clusters */
+ if (last_entry >= (0xFFFFFFF0 & bit_mask))
+ {
+ last_entry |= 0xFFFFFFF0;
+ if (last_entry >= 0xFFFFFFF8)
+ last_entry = 0xFFFFFFFF;
+ }
+ return last_entry;
+}
+
+
+
+static void fat_set_fat_entry(imgtool_partition *partition, UINT8 *fat_table, UINT32 fat_entry, UINT32 value)
+{
+ const fat_partition_info *disk_info;
+ UINT64 entry;
+ UINT32 bit_index, i;
+
+ disk_info = fat_get_partition_info(partition);
+ bit_index = fat_entry * disk_info->fat_bits;
+ value &= 0xFFFFFFFF >> (32 - disk_info->fat_bits);
+
+ for (i = 0; i < disk_info->fat_count; i++)
+ {
+ memcpy(&entry, fat_table + (i * FAT_SECLEN
+ * disk_info->sectors_per_fat) + (bit_index / 8), sizeof(entry));
+
+ entry = LITTLE_ENDIANIZE_INT64(entry);
+ entry &= (~((UINT64) 0xFFFFFFFF >> (32 - disk_info->fat_bits)) << (bit_index % 8)) | ((1 << (bit_index % 8)) - 1);
+ entry |= ((UINT64) value) << (bit_index % 8);
+ entry = LITTLE_ENDIANIZE_INT64(entry);
+
+ memcpy(fat_table + (i * FAT_SECLEN
+ * disk_info->sectors_per_fat) + (bit_index / 8), &entry, sizeof(entry));
+ }
+}
+
+
+
+static void fat_debug_integrity_check(imgtool_partition *partition, const UINT8 *fat_table, const fat_file *file)
+{
+#ifdef MAME_DEBUG
+ /* debug function to test the integrity of a file */
+ UINT32 cluster;
+ const fat_partition_info *disk_info;
+
+ disk_info = fat_get_partition_info(partition);
+ cluster = file->first_cluster ? file->first_cluster : 0xFFFFFFFF;
+
+ if (!file->root)
+ {
+ while(cluster != 0xFFFFFFFF)
+ {
+ assert((cluster >= 2) && (cluster < disk_info->total_clusters));
+ cluster = fat_get_fat_entry(partition, fat_table, cluster);
+ }
+ }
+#endif
+}
+
+
+
+static imgtoolerr_t fat_seek_file(imgtool_partition *partition, fat_file *file, UINT32 pos)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ const fat_partition_info *disk_info;
+ UINT32 new_cluster;
+ UINT8 *fat_table = NULL;
+
+ disk_info = fat_get_partition_info(partition);
+
+ /* can't seek past end of file */
+ if (!file->directory && (pos > file->filesize))
+ pos = file->filesize;
+
+ if (file->first_cluster == 0)
+ {
+ /* special case; the root directory */
+ file->index = pos;
+ }
+ else
+ {
+ /* first, we need to check to see if we have to go back to the beginning */
+ if (pos < file->index)
+ {
+ file->cluster = file->first_cluster;
+ file->cluster_index = 0;
+ file->eof = 0;
+ }
+
+ /* skip ahead clusters */
+ while((file->cluster_index + disk_info->cluster_size) <= pos)
+ {
+ if (!fat_table)
+ {
+ err = fat_load_fat(partition, &fat_table);
+ if (err)
+ goto done;
+ }
+
+ new_cluster = fat_get_fat_entry(partition, fat_table, file->cluster);
+
+ file->cluster = new_cluster;
+ file->cluster_index += disk_info->cluster_size;
+
+ /* are we at the end of the file? */
+ if (new_cluster == 0xFFFFFFFF)
+ {
+ pos = file->cluster_index;
+ file->eof = 1;
+ }
+ }
+ file->index = pos;
+ }
+
+done:
+ if (fat_table)
+ free(fat_table);
+ return err;
+}
+
+
+
+static UINT32 fat_get_filepos_sector_index(imgtool_partition *partition, fat_file *file)
+{
+ UINT32 sector_index;
+ const fat_partition_info *disk_info;
+
+ disk_info = fat_get_partition_info(partition);
+
+ sector_index = disk_info->reserved_sectors + (disk_info->sectors_per_fat * disk_info->fat_count);
+ if (file->root)
+ {
+ /* special case for the root file */
+ sector_index += file->index / FAT_SECLEN;
+ }
+ else
+ {
+ /* cluster out of range? */
+ if ((file->cluster < 2) || (file->cluster >= disk_info->total_clusters))
+ return 0;
+
+ sector_index += (disk_info->root_entries * FAT_DIRENT_SIZE + FAT_SECLEN - 1) / FAT_SECLEN;
+ sector_index += (file->cluster - 2) * disk_info->sectors_per_cluster;
+ sector_index += (file->index / FAT_SECLEN) % disk_info->sectors_per_cluster;
+ }
+ return sector_index;
+}
+
+
+
+static imgtoolerr_t fat_corrupt_file_error(const fat_file *file)
+{
+ imgtoolerr_t err;
+ if (file->root)
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ else if (file->directory)
+ err = IMGTOOLERR_CORRUPTDIR;
+ else
+ err = IMGTOOLERR_CORRUPTFILE;
+ return err;
+}
+
+
+
+static imgtoolerr_t fat_readwrite_file(imgtool_partition *partition, fat_file *file,
+ void *buffer, size_t buffer_len, size_t *bytes_read, int read_or_write)
+{
+ imgtoolerr_t err;
+// const fat_partition_info *disk_info;
+ UINT32 sector_index;
+ int offset;
+ size_t len;
+
+// disk_info =
+ fat_get_partition_info(partition);
+ if (bytes_read)
+ *bytes_read = 0;
+ if (!file->directory)
+ buffer_len = MIN(buffer_len, file->filesize - file->index);
+
+ while(!file->eof && (buffer_len > 0))
+ {
+ sector_index = fat_get_filepos_sector_index(partition, file);
+ if (sector_index == 0)
+ return fat_corrupt_file_error(file);
+
+ offset = file->index % FAT_SECLEN;
+ len = MIN(buffer_len, FAT_SECLEN - offset);
+
+ /* read or write the data from the disk */
+ if (read_or_write)
+ err = fat_write_sector(partition, sector_index, offset, buffer, len);
+ else
+ err = fat_read_sector(partition, sector_index, offset, buffer, len);
+ if (err)
+ return err;
+
+ /* and move the file pointer ahead */
+ err = fat_seek_file(partition, file, file->index + len);
+ if (err)
+ return err;
+
+ buffer = ((UINT8 *) buffer) + len;
+ buffer_len -= len;
+ if (bytes_read)
+ *bytes_read += len;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_read_file(imgtool_partition *partition, fat_file *file,
+ void *buffer, size_t buffer_len, size_t *bytes_read)
+{
+ return fat_readwrite_file(partition, file, buffer, buffer_len, bytes_read, 0);
+}
+
+
+
+static imgtoolerr_t fat_write_file(imgtool_partition *partition, fat_file *file,
+ const void *buffer, size_t buffer_len, size_t *bytes_read)
+{
+ return fat_readwrite_file(partition, file, (void *) buffer, buffer_len, bytes_read, 1);
+}
+
+
+
+static UINT32 fat_allocate_cluster(imgtool_partition *partition, UINT8 *fat_table)
+{
+ const fat_partition_info *disk_info;
+ UINT32 i, val;
+
+ disk_info = fat_get_partition_info(partition);
+
+ for (i = 2; i < disk_info->total_clusters; i++)
+ {
+ val = fat_get_fat_entry(partition, fat_table, i);
+ if (val == 0)
+ {
+ fat_set_fat_entry(partition, fat_table, i, 1);
+ return i;
+ }
+ }
+ return 0;
+}
+
+
+
+/* sets the size of a file; ~0 means 'delete' */
+static imgtoolerr_t fat_set_file_size(imgtool_partition *partition, fat_file *file,
+ UINT32 new_size)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ const fat_partition_info *disk_info;
+ UINT32 new_cluster_count;
+ UINT32 old_cluster_count;
+ UINT32 cluster, write_cluster, last_cluster, new_pos, i;
+ UINT8 *fat_table = NULL;
+ UINT8 dirent[32];
+ size_t clear_size;
+ void *clear_buffer = NULL;
+ int delete_file = FALSE;
+ int rest_free = FALSE;
+
+ disk_info = fat_get_partition_info(partition);
+
+ LOG(("fat_set_file_size(): file->first_cluster=%d new_size=0x%08x\n", file->first_cluster, new_size));
+
+ /* special case */
+ if (new_size == ~0)
+ {
+ delete_file = TRUE;
+ new_size = 0;
+ }
+
+ /* if this is the trivial case (not changing the size), succeed */
+ if (!delete_file && (file->filesize == new_size))
+ {
+ err = IMGTOOLERR_SUCCESS;
+ goto done;
+ }
+
+ /* what is the new position? */
+ new_pos = MIN(file->index, new_size);
+
+ if (file->root)
+ {
+ /* this is the root directory; this is a special case */
+ if (new_size > (disk_info->root_entries * FAT_DIRENT_SIZE))
+ {
+ err = IMGTOOLERR_NOSPACE;
+ goto done;
+ }
+ }
+ else
+ {
+ old_cluster_count = (file->filesize + disk_info->cluster_size - 1) / disk_info->cluster_size;
+ new_cluster_count = (new_size + disk_info->cluster_size - 1) / disk_info->cluster_size;
+ cluster = 0;
+
+ /* load the dirent */
+ err = fat_read_sector(partition, file->dirent_sector_index, file->dirent_sector_offset, dirent, sizeof(dirent));
+ if (err)
+ goto done;
+
+ /* need to load the FAT whether we are growing or shrinking the file */
+ if (old_cluster_count != new_cluster_count)
+ {
+ err = fat_load_fat(partition, &fat_table);
+ if (err)
+ goto done;
+
+ cluster = 0;
+ i = 0;
+ do
+ {
+ last_cluster = cluster;
+ write_cluster = 0;
+
+ /* identify the next cluster */
+ if (cluster != 0)
+ cluster = fat_get_fat_entry(partition, fat_table, cluster);
+ else
+ cluster = file->first_cluster ? file->first_cluster : 0xFFFFFFFF;
+
+ /* do we need to grow the file by a cluster? */
+ if (i < new_cluster_count && ((cluster < 2) || (cluster >= disk_info->total_clusters)))
+ {
+ /* grow this file by a cluster */
+ cluster = fat_allocate_cluster(partition, fat_table);
+ if (cluster == 0)
+ {
+ err = IMGTOOLERR_NOSPACE;
+ goto done;
+ }
+
+ write_cluster = cluster;
+ }
+ else if (i >= new_cluster_count)
+ {
+ /* we are shrinking the file; we need to unlink this node */
+ if ((cluster < 2) || (cluster >= disk_info->total_clusters))
+ cluster = 0xFFFFFFFF; /* ack file is corrupt! recover */
+ write_cluster = 0xFFFFFFFF;
+ }
+
+ /* write out the entry, if appropriate */
+ if (write_cluster != 0)
+ {
+ /* are we tieing up loose ends? */
+ if (rest_free && (write_cluster == 0xFFFFFFFF))
+ write_cluster = 0;
+
+ if (last_cluster == 0)
+ file->first_cluster = (write_cluster != 0xFFFFFFFF) ? write_cluster : 0;
+ else
+ fat_set_fat_entry(partition, fat_table, last_cluster, write_cluster);
+
+ /* did we write the last cluster? if so, the rest (if any) are free */
+ if (write_cluster == 0xFFFFFFFF)
+ rest_free = TRUE;
+ }
+ }
+ while((++i < new_cluster_count) || (cluster != 0xFFFFFFFF));
+ }
+
+ /* record the new file size */
+ place_integer_le(dirent, 26, 2, file->first_cluster);
+ place_integer_le(dirent, 28, 4, new_size);
+
+ /* delete the file, if appropriate */
+ if (delete_file)
+ dirent[0] = 0xE5;
+
+ /* save the dirent */
+ err = fat_write_sector(partition, file->dirent_sector_index, file->dirent_sector_offset, dirent, sizeof(dirent));
+ if (err)
+ goto done;
+
+ /* if we've modified the FAT, save it out */
+ if (fat_table)
+ {
+ err = fat_save_fat(partition, fat_table);
+ if (err)
+ goto done;
+ }
+
+ /* update the file structure */
+ if (!file->directory)
+ file->filesize = new_size;
+ file->cluster = file->first_cluster;
+ file->index = 0;
+ file->cluster_index = 0;
+ file->eof = (new_cluster_count == 0);
+ }
+
+ /* special case; clear out stale bytes on non-root directories */
+ if (file->directory && !delete_file)
+ {
+ if (file->root)
+ clear_size = MIN(file->filesize - new_size, FAT_DIRENT_SIZE);
+ else
+ clear_size = (disk_info->cluster_size - (new_size % disk_info->cluster_size)) % disk_info->cluster_size;
+
+ if (clear_size > 0)
+ {
+ err = fat_seek_file(partition, file, new_size);
+ if (err)
+ goto done;
+
+ clear_buffer = malloc(clear_size);
+ if (!clear_buffer)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(clear_buffer, '\0', clear_size);
+
+ err = fat_write_file(partition, file, clear_buffer, clear_size, NULL);
+ if (err)
+ goto done;
+ }
+ }
+
+ /* seek back to original pos */
+ err = fat_seek_file(partition, file, new_pos);
+ if (err)
+ goto done;
+
+ if (fat_table)
+ fat_debug_integrity_check(partition, fat_table, file);
+
+done:
+ if (fat_table)
+ free(fat_table);
+ if (clear_buffer)
+ free(clear_buffer);
+ return err;
+}
+
+
+
+static void prepend_lfn_bytes(utf16_char *lfn_buf, size_t lfn_buflen, size_t *lfn_len,
+ const UINT8 *entry, int offset, int chars)
+{
+ UINT16 w;
+ int i;
+ size_t move_len;
+
+ move_len = MIN(*lfn_len + 1, lfn_buflen - chars - 1);
+ memmove(&lfn_buf[chars], &lfn_buf[0], move_len * sizeof(*lfn_buf));
+
+ for (i = 0; i < chars; i++)
+ {
+ /* read the character */
+ memcpy(&w, &entry[offset + i * 2], 2);
+ w = LITTLE_ENDIANIZE_INT16(w);
+
+ /* append to buffer */
+ lfn_buf[i] = (w != 0xFFFF) ? w : 0;
+ }
+ *lfn_len += chars;
+}
+
+
+
+static UINT8 fat_calc_filename_checksum(const UINT8 *short_filename)
+{
+ UINT8 checksum;
+ int i, j;
+
+ checksum = 0;
+ for (i = 0; i < 11; i++)
+ {
+ j = checksum & 1;
+ checksum >>= 1;
+ if (j)
+ checksum |= 0x80;
+ checksum += short_filename[i];
+ }
+ return checksum;
+}
+
+
+
+static void fat_calc_dirent_lfnchecksum(UINT8 *entry, size_t entry_len)
+{
+ UINT8 checksum;
+ int i;
+
+ checksum = fat_calc_filename_checksum(entry + entry_len - FAT_DIRENT_SIZE);
+
+ for (i = 0; i < (entry_len / FAT_DIRENT_SIZE - 1); i++)
+ entry[i * FAT_DIRENT_SIZE + 13] = checksum;
+}
+
+
+
+static char fat_cannonicalize_sfn_char(char ch)
+{
+ /* return the display version of this short file name character */
+ return tolower(ch);
+}
+
+
+
+static void fat_cannonicalize_sfn(char *sfn, const UINT8 *sfn_bytes)
+{
+ /* return the display version of this short file name */
+ int i;
+
+ memset(sfn, '\0', 13);
+ memcpy(sfn, sfn_bytes, 8);
+ rtrim(sfn);
+ if (sfn[0] == 0x05)
+ sfn[0] = (char) 0xE5;
+ if ((sfn_bytes[8] != ' ') || (sfn_bytes[9] != ' ') || (sfn_bytes[10] != ' '))
+ {
+ strcat(sfn, ".");
+ memcpy(sfn + strlen(sfn), &sfn_bytes[8], 3);
+ rtrim(sfn);
+ }
+ for (i = 0; sfn[i]; i++)
+ sfn[i] = fat_cannonicalize_sfn_char(sfn[i]);
+}
+
+
+
+static time_t fat_crack_time(UINT32 fat_time)
+{
+ struct tm t;
+ time_t now;
+
+ time(&now);
+ t = *localtime(&now);
+
+ t.tm_sec = ((fat_time >> 0) & 0x001F) * 2;
+ t.tm_min = ((fat_time >> 5) & 0x003F);
+ t.tm_hour = ((fat_time >> 11) & 0x001F);
+ t.tm_mday = ((fat_time >> 16) & 0x001F);
+ t.tm_mon = ((fat_time >> 21) & 0x000F);
+ t.tm_year = ((fat_time >> 25) & 0x007F) + 1980 - 1900;
+
+ return mktime(&t);
+}
+
+
+
+static UINT32 fat_setup_time(time_t ansi_time)
+{
+ struct tm t;
+ UINT32 result = 0;
+
+ t = *localtime(&ansi_time);
+
+ result |= (((UINT32) (t.tm_sec / 2)) & 0x001F) << 0;
+ result |= (((UINT32) t.tm_min) & 0x003F) << 5;
+ result |= (((UINT32) t.tm_hour) & 0x001F) << 11;
+ result |= (((UINT32) t.tm_mday) & 0x001F) << 16;
+ result |= (((UINT32) t.tm_mon) & 0x000F) << 21;
+ result |= (((UINT32) (t.tm_year + 1900 - 1980)) & 0x007F) << 25;
+
+ return result;
+}
+
+
+
+static imgtoolerr_t fat_read_dirent(imgtool_partition *partition, fat_file *file,
+ fat_dirent *ent, fat_freeentry_info *freeent)
+{
+ imgtoolerr_t err;
+ //const fat_partition_info *disk_info;
+ UINT8 entry[FAT_DIRENT_SIZE];
+ size_t bytes_read;
+ int i, j;
+ unicode_char ch;
+ utf16_char lfn_buf[512];
+ size_t lfn_len = 0;
+ int lfn_lastentry = 0;
+ UINT8 lfn_checksum = 0;
+ UINT32 entry_index, entry_sector_index, entry_sector_offset;
+
+ assert(file->directory);
+ lfn_buf[0] = '\0';
+ memset(ent, 0, sizeof(*ent));
+ //disk_info = fat_get_partition_info(partition);
+
+ /* The first eight bytes of a FAT directory entry is a blank padded name
+ *
+ * The first byte can be special:
+ * 0x00 - entry is available and no further entry is used
+ * 0x05 - first character is actually 0xe5
+ * 0x2E - dot entry; either '.' or '..'
+ * 0xE5 - entry has been erased and is available
+ *
+ * Byte 11 is the attributes; and 0x0F denotes a LFN entry
+ */
+ do
+ {
+ entry_index = file->index;
+ entry_sector_index = fat_get_filepos_sector_index(partition, file);
+ entry_sector_offset = file->index % FAT_SECLEN;
+
+ err = fat_read_file(partition, file, entry, sizeof(entry), &bytes_read);
+ if (err)
+ return err;
+ if (bytes_read < sizeof(entry))
+ memset(entry, 0, sizeof(entry));
+
+ if (entry[11] == 0x0F)
+ {
+ /* this is an LFN entry */
+ if ((lfn_lastentry == 0)
+ || ((entry[0] & 0x3F) != (lfn_lastentry - 1))
+ || (lfn_checksum != entry[13]))
+ {
+ lfn_buf[0] = 0;
+ lfn_len = 0;
+ lfn_checksum = entry[13];
+ }
+ lfn_lastentry = entry[0] & 0x3F;
+ prepend_lfn_bytes(lfn_buf, ARRAY_LENGTH(lfn_buf),
+ &lfn_len, entry, 28, 2);
+ prepend_lfn_bytes(lfn_buf, ARRAY_LENGTH(lfn_buf),
+ &lfn_len, entry, 14, 6);
+ prepend_lfn_bytes(lfn_buf, ARRAY_LENGTH(lfn_buf),
+ &lfn_len, entry, 1, 5);
+ }
+ else if (freeent && (freeent->position == ~0))
+ {
+ /* do a quick check to find out if we found space */
+ if ((entry[0] == '\0') || (entry[0] == 0xE5))
+ {
+ if (freeent->candidate_position > entry_index)
+ freeent->candidate_position = entry_index;
+
+ if ((entry[0] == '\0') || (freeent->candidate_position + freeent->required_size < file->index))
+ freeent->position = freeent->candidate_position;
+ }
+ else
+ {
+ freeent->candidate_position = ~0;
+ }
+ }
+ }
+ while((entry[0] == 0x2E) || (entry[0] == 0xE5) || (entry[11] == 0x0F));
+
+ /* no more directory entries? */
+ if (entry[0] == '\0')
+ {
+ ent->eof = 1;
+ return IMGTOOLERR_SUCCESS;
+ }
+
+ /* pick apart short filename */
+ fat_cannonicalize_sfn(ent->short_filename, entry);
+
+ /* and the long filename */
+ if (lfn_lastentry == 1)
+ {
+ /* only use the LFN if the checksum passes */
+ if (lfn_checksum == fat_calc_filename_checksum(entry))
+ {
+ i = 0;
+ j = 0;
+ do
+ {
+ i += uchar_from_utf16(&ch, &lfn_buf[i], ARRAY_LENGTH(lfn_buf) - i);
+ j += utf8_from_uchar(&ent->long_filename[j], ARRAY_LENGTH(ent->long_filename) - j, ch);
+ }
+ while(ch != 0);
+ }
+ }
+
+ /* other attributes */
+ ent->filesize = pick_integer_le(entry, 28, 4);
+ ent->directory = (entry[11] & 0x10) ? 1 : 0;
+ ent->first_cluster = pick_integer_le(entry, 26, 2);
+ ent->dirent_sector_index = entry_sector_index;
+ ent->dirent_sector_offset = entry_sector_offset;
+ ent->creation_time = fat_crack_time(pick_integer_le(entry, 14, 4));
+ ent->lastmodified_time = fat_crack_time(pick_integer_le(entry, 22, 4));
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+typedef enum
+{
+ SFN_SUFFICIENT, /* name fully representable in short file name */
+ SFN_DERIVATIVE, /* name not fully representable in short file name, but no need to tildize */
+ SFN_MANGLED /* name not representable in short file name; must tildize */
+} sfn_disposition_t;
+
+static imgtoolerr_t fat_construct_dirent(const char *filename, creation_policy_t create,
+ UINT8 **entry, size_t *entry_len)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ UINT8 *created_entry = NULL;
+ UINT8 *new_created_entry;
+ UINT32 now;
+ size_t created_entry_len = FAT_DIRENT_SIZE;
+ size_t created_entry_pos = 0;
+ unicode_char ch;
+ char last_short_char = ' ';
+ char short_char = '\0';
+ char cannonical_short_char;
+ utf16_char buf[UTF16_CHAR_MAX];
+ int i, len;
+ int sfn_pos = 0;
+ sfn_disposition_t sfn_disposition = SFN_SUFFICIENT;
+ int sfn_in_extension = 0;
+
+ /* sanity check */
+ if (*filename == '\0')
+ {
+ err = IMGTOOLERR_BADFILENAME;
+ goto done;
+ }
+
+ /* construct intial entry */
+ created_entry = (UINT8 *) malloc(FAT_DIRENT_SIZE);
+ if (!created_entry)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* set up the basics for the new dirent */
+ memset(created_entry + 0, ' ', 11);
+ memset(created_entry + 12, '\0', FAT_DIRENT_SIZE - 12);
+ created_entry[11] = (create == CREATE_DIR) ? 0x10 : 0x00;
+
+ /* set up file dates in the new dirent */
+ now = fat_setup_time(time(NULL));
+ place_integer_le(created_entry, 14, 4, now);
+ place_integer_le(created_entry, 18, 2, now);
+ place_integer_le(created_entry, 22, 4, now);
+
+ while(*filename)
+ {
+ filename += uchar_from_utf8(&ch, filename, UTF8_CHAR_MAX);
+
+ /* append to short filename, if possible */
+ if ((ch < 32) || (ch > 128))
+ short_char = '\0';
+ else if (isalnum((char) ch))
+ short_char = toupper((char) ch);
+ else if (strchr(".!#$%^()-@^_`{}~", (char) ch))
+ short_char = (char) ch;
+ else
+ short_char = '\0'; /* illegal SFN char */
+ cannonical_short_char = fat_cannonicalize_sfn_char((char) ch);
+ if (!short_char || (short_char != cannonical_short_char))
+ {
+ if (toupper(short_char) == toupper(cannonical_short_char))
+ sfn_disposition = MAX(sfn_disposition, SFN_DERIVATIVE);
+ else
+ sfn_disposition = SFN_MANGLED;
+ }
+
+ /* append the short filename char */
+ if (short_char == '.')
+ {
+ /* multiple extensions or trailing spaces? */
+ if (sfn_in_extension)
+ sfn_disposition = SFN_MANGLED;
+ else if (last_short_char == ' ')
+ sfn_disposition = MAX(sfn_disposition, SFN_DERIVATIVE);
+
+ sfn_in_extension = 1;
+ sfn_pos = 8;
+ created_entry[created_entry_len - FAT_DIRENT_SIZE + 8] = ' ';
+ created_entry[created_entry_len - FAT_DIRENT_SIZE + 9] = ' ';
+ created_entry[created_entry_len - FAT_DIRENT_SIZE + 10] = ' ';
+ }
+ else if (sfn_pos == (sfn_in_extension ? 11 : 8))
+ {
+ /* ran out of characters for short filename */
+ sfn_disposition = SFN_MANGLED;
+ }
+ else if (short_char != '\0')
+ {
+ created_entry[created_entry_len - FAT_DIRENT_SIZE + sfn_pos++] = short_char;
+ }
+ last_short_char = short_char;
+
+
+ /* convert to UTF-16 and add a long filename entry */
+ len = utf16le_from_uchar(buf, UTF16_CHAR_MAX, ch);
+ for (i = 0; i < len; i++)
+ {
+ switch(created_entry_pos)
+ {
+ case 0:
+ case 32:
+ /* need to grow */
+ new_created_entry = (UINT8 *) malloc(created_entry_len + FAT_DIRENT_SIZE);
+ if (!new_created_entry)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* move existing entries forward */
+ memmove(new_created_entry + 32, created_entry, created_entry_len);
+
+ if (created_entry) free(created_entry);
+ created_entry = new_created_entry;
+ created_entry_len += 32;
+
+ /* set up this LFN */
+ memset(created_entry, '\0', 32);
+ memset(&created_entry[1], '\xFF', 10);
+ memset(&created_entry[14], '\xFF', 12);
+ memset(&created_entry[28], '\xFF', 4);
+ created_entry[11] = 0x0F;
+
+ /* specify entry index */
+ created_entry[0] = (created_entry_len / 32) - 1;
+ if (created_entry[0] >= 0x40)
+ {
+ err = IMGTOOLERR_BADFILENAME;
+ goto done;
+ }
+ created_entry_pos = 1;
+ break;
+
+ case 11:
+ created_entry_pos = 14;
+ break;
+
+ case 26:
+ created_entry_pos = 28;
+ break;
+ }
+
+ memcpy(&created_entry[created_entry_pos], &buf[i], 2);
+ created_entry_pos += 2;
+ }
+ }
+
+ /* trailing spaces? */
+ if (short_char == ' ')
+ sfn_disposition = MAX(sfn_disposition, SFN_DERIVATIVE);
+
+ if (sfn_disposition == SFN_SUFFICIENT)
+ {
+ /* the short filename suffices; remove the LFN stuff */
+ memcpy(created_entry, created_entry + created_entry_len - FAT_DIRENT_SIZE, FAT_DIRENT_SIZE);
+ created_entry_len = FAT_DIRENT_SIZE;
+ if (created_entry) free(created_entry);
+ new_created_entry = (UINT8 *) malloc(created_entry_len);
+ if (!new_created_entry)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto done;
+ }
+ created_entry = new_created_entry;
+ }
+ else
+ {
+ /* need to do finishing touches on the LFN */
+ created_entry[0] |= 0x40;
+
+ /* if necessary, mangle the name */
+ if (sfn_disposition == SFN_MANGLED)
+ {
+ i = 6;
+ while((i > 0) && isspace(created_entry[created_entry_len - FAT_DIRENT_SIZE + i - 1]))
+ i--;
+ created_entry[created_entry_len - FAT_DIRENT_SIZE + i + 0] = '~';
+ created_entry[created_entry_len - FAT_DIRENT_SIZE + i + 1] = '1';
+ }
+ fat_calc_dirent_lfnchecksum(created_entry, created_entry_len);
+ }
+
+done:
+ if (err && created_entry)
+ {
+ free(created_entry);
+ created_entry = NULL;
+ }
+ *entry = created_entry;
+ *entry_len = created_entry_len;
+ return err;
+}
+
+
+
+static void fat_bump_dirent(imgtool_partition *partition, UINT8 *entry, size_t entry_len)
+{
+ UINT8 *sfn_entry;
+ int pos, digit_count, i;
+ UINT32 digit_place, val = 0;
+
+ sfn_entry = &entry[entry_len - FAT_DIRENT_SIZE];
+
+ digit_place = 1;
+ for (pos = 7; (pos >= 0) && isdigit((char) sfn_entry[pos]); pos--)
+ {
+ val += (sfn_entry[pos] - '0') * digit_place;
+ digit_place *= 10;
+ }
+ val++;
+ pos++;
+
+ /* count the digits */
+ digit_place = 1;
+ digit_count = 1;
+ while(val >= digit_place * 10)
+ {
+ digit_count++;
+ digit_place *= 10;
+ }
+
+ /* give us some more space, if necessary */
+ while ((pos > 0) && ((pos + digit_count) > 8))
+ pos--;
+
+ /* have we actually ran out of digits */
+ if ((pos + digit_count) > 8)
+ {
+ /* extreme degenerate case; simply randomize the filename */
+ for (i = 0; i < 6; i++)
+ sfn_entry[i] = 'A' + (imgtool_partition_rand(partition) % 26);
+ sfn_entry[6] = '~';
+ sfn_entry[7] = '0';
+ }
+ else
+ {
+ /* write the tilde, if possible */
+ if (pos > 0)
+ sfn_entry[pos - 1] = '~';
+
+ /* write out the number */
+ while(digit_place > 0)
+ {
+ sfn_entry[pos++] = (val / digit_place) + '0';
+ val %= digit_place;
+ digit_place /= 10;
+ }
+ }
+
+ /* since we changed the short file name, we need to recalc the checksums
+ * in the LFN entries */
+ fat_calc_dirent_lfnchecksum(entry, entry_len);
+}
+
+
+
+static imgtoolerr_t fat_lookup_path(imgtool_partition *partition, const char *path,
+ creation_policy_t create, fat_file *file)
+{
+ imgtoolerr_t err;
+ const fat_partition_info *disk_info;
+ fat_dirent ent;
+ fat_freeentry_info freeent = { 0, };
+ const char *next_path_part;
+ UINT8 *created_entry = NULL;
+ size_t created_entry_len = 0;
+ UINT32 entry_sector_index, entry_sector_offset;
+ UINT32 parent_first_cluster;
+ int bumped_sfn;
+ char sfn[13];
+
+ disk_info = fat_get_partition_info(partition);
+
+ memset(file, 0, sizeof(*file));
+ file->root = 1;
+ file->directory = 1;
+ file->filesize = disk_info->root_entries * FAT_DIRENT_SIZE;
+
+ while(*path)
+ {
+ if (!file->directory)
+ {
+ err = IMGTOOLERR_PATHNOTFOUND;
+ goto done;
+ }
+
+ next_path_part = path + strlen(path) + 1;
+ if (create && (*next_path_part == '\0'))
+ {
+ /* this is the last entry, and we are creating a file */
+ err = fat_construct_dirent(path, create, &created_entry, &created_entry_len);
+ if (err)
+ goto done;
+
+ freeent.required_size = created_entry_len;
+ freeent.candidate_position = ~0;
+ freeent.position = ~0;
+ }
+
+ do
+ {
+ err = fat_read_dirent(partition, file, &ent, created_entry ? &freeent : NULL);
+ if (err)
+ goto done;
+
+ LOG(("fat_lookup_path(): %s/%s: %d\n", ent.short_filename, ent.long_filename, ent.dirent_sector_offset));
+ }
+ while(!ent.eof && core_stricmp(path, ent.short_filename) && core_stricmp(path, ent.long_filename));
+
+ parent_first_cluster = file->first_cluster;
+
+ if (ent.eof)
+ {
+ /* it seems that we have reached the end of this directory */
+ if (!created_entry)
+ {
+ err = IMGTOOLERR_FILENOTFOUND;
+ goto done;
+ }
+
+ if (created_entry_len > FAT_DIRENT_SIZE)
+ {
+ /* must ensure uniqueness of the short filename */
+ do
+ {
+ /* rewind to the beginning of the directory */
+ err = fat_seek_file(partition, file, 0);
+ if (err)
+ goto done;
+
+ bumped_sfn = FALSE;
+ fat_cannonicalize_sfn(sfn, &created_entry[created_entry_len - FAT_DIRENT_SIZE]);
+
+ do
+ {
+ err = fat_read_dirent(partition, file, &ent, NULL);
+ if (err)
+ goto done;
+
+ if (!core_stricmp(sfn, ent.short_filename))
+ {
+ bumped_sfn = TRUE;
+ fat_bump_dirent(partition, created_entry, created_entry_len);
+ fat_cannonicalize_sfn(sfn, &created_entry[created_entry_len - FAT_DIRENT_SIZE]);
+ }
+ }
+ while(!ent.eof);
+ }
+ while(bumped_sfn);
+ }
+
+ LOG(("fat_lookup_path(): creating entry; pos=%u length=%u\n", freeent.position, freeent.required_size));
+
+ err = fat_set_file_size(partition, file, MAX(file->filesize, freeent.position + created_entry_len));
+ if (err)
+ goto done;
+
+ err = fat_seek_file(partition, file, freeent.position);
+ if (err)
+ goto done;
+
+ err = fat_write_file(partition, file, created_entry, created_entry_len, NULL);
+ if (err)
+ goto done;
+
+ /* we have to do a special seek operation to get the main dirent */
+ err = fat_seek_file(partition, file, freeent.position + created_entry_len - FAT_DIRENT_SIZE);
+ if (err)
+ goto done;
+ entry_sector_index = fat_get_filepos_sector_index(partition, file);
+ entry_sector_offset = file->index % FAT_SECLEN;
+
+ /* build the file struct for the newly created file/directory */
+ memset(file, 0, sizeof(*file));
+ file->directory = (created_entry[created_entry_len - FAT_DIRENT_SIZE + 11] & 0x10) ? 1 : 0;
+ file->dirent_sector_index = entry_sector_index;
+ file->dirent_sector_offset = entry_sector_offset;
+ }
+ else
+ {
+ /* update the current file */
+ memset(file, 0, sizeof(*file));
+ file->directory = ent.directory;
+ file->filesize = ent.filesize;
+ file->cluster = ent.first_cluster;
+ file->first_cluster = ent.first_cluster;
+ file->dirent_sector_index = ent.dirent_sector_index;
+ file->dirent_sector_offset = ent.dirent_sector_offset;
+ }
+
+ path = next_path_part;
+ file->parent_first_cluster = parent_first_cluster;
+ }
+
+ err = IMGTOOLERR_SUCCESS;
+
+done:
+ if (created_entry)
+ free(created_entry);
+ return err;
+}
+
+
+
+static imgtoolerr_t fat_partition_beginenum(imgtool_directory *enumeration, const char *path)
+{
+ imgtoolerr_t err;
+ fat_file *file;
+
+ file = (fat_file *) imgtool_directory_extrabytes(enumeration);
+
+ err = fat_lookup_path(imgtool_directory_partition(enumeration), path, CREATE_NONE, file);
+ if (err)
+ return err;
+ if (!file->directory)
+ return IMGTOOLERR_PATHNOTFOUND;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ imgtoolerr_t err;
+ fat_file *file;
+ fat_dirent fatent;
+
+ file = (fat_file *) imgtool_directory_extrabytes(enumeration);
+ err = fat_read_dirent(imgtool_directory_partition(enumeration), file, &fatent, NULL);
+ if (err)
+ return err;
+
+ /* copy stuff from the FAT dirent to the Imgtool dirent */
+ snprintf(ent->filename, ARRAY_LENGTH(ent->filename), "%s", fatent.long_filename[0]
+ ? fatent.long_filename : fatent.short_filename);
+ ent->filesize = fatent.filesize;
+ ent->directory = fatent.directory;
+ ent->eof = fatent.eof;
+ ent->creation_time = fatent.creation_time;
+ ent->lastmodified_time = fatent.lastmodified_time;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_read_bootblock(imgtool_partition *partition, imgtool_stream *stream)
+{
+ imgtoolerr_t err;
+ UINT8 block[FAT_SECLEN];
+
+ err = fat_read_sector(partition, 0, 0, block, sizeof(block));
+ if (err)
+ return err;
+
+ stream_write(stream, block, sizeof(block));
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_write_bootblock(imgtool_partition *partition, imgtool_stream *stream)
+{
+ imgtoolerr_t err;
+ UINT8 block[FAT_SECLEN];
+ UINT8 new_block[FAT_SECLEN];
+
+ if (stream_size(stream) != sizeof(new_block))
+ return IMGTOOLERR_UNEXPECTED;
+ stream_read(stream, new_block, sizeof(new_block));
+
+ if (new_block[510] != 0x55)
+ return IMGTOOLERR_UNEXPECTED;
+ if (new_block[511] != 0xAA)
+ return IMGTOOLERR_UNEXPECTED;
+
+ /* read current boot sector */
+ err = fat_read_sector(partition, 0, 0, block, sizeof(block));
+ if (err)
+ return err;
+
+ /* merge in the new stuff */
+ memcpy(&block[ 0], &new_block[ 0], 3);
+ memcpy(&block[62], &new_block[62], 448);
+
+ /* and write it out */
+ err = fat_write_sector(partition, 0, 0, block, sizeof(block));
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_readfile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf)
+{
+ imgtoolerr_t err;
+ fat_file file;
+ size_t bytes_read;
+ char buffer[1024];
+
+ /* special case for bootblock */
+ if (filename == FILENAME_BOOTBLOCK)
+ return fat_read_bootblock(partition, destf);
+
+ err = fat_lookup_path(partition, filename, CREATE_NONE, &file);
+ if (err)
+ return err;
+
+ if (file.directory)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ do
+ {
+ err = fat_read_file(partition, &file, buffer, sizeof(buffer), &bytes_read);
+ if (err)
+ return err;
+
+ stream_write(destf, buffer, bytes_read);
+ }
+ while(bytes_read > 0);
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_writefile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *sourcef, option_resolution *opts)
+{
+ imgtoolerr_t err;
+ fat_file file;
+ UINT32 bytes_left, len;
+ char buffer[1024];
+
+ /* special case for bootblock */
+ if (filename == FILENAME_BOOTBLOCK)
+ return fat_write_bootblock(partition, sourcef);
+
+ err = fat_lookup_path(partition, filename, CREATE_FILE, &file);
+ if (err)
+ return err;
+
+ if (file.directory)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ bytes_left = (UINT32) stream_size(sourcef);
+
+ err = fat_set_file_size(partition, &file, bytes_left);
+ if (err)
+ return err;
+
+ while(bytes_left > 0)
+ {
+ len = MIN(bytes_left, sizeof(buffer));
+ stream_read(sourcef, buffer, len);
+
+ err = fat_write_file(partition, &file, buffer, len, NULL);
+ if (err)
+ return err;
+
+ bytes_left -= len;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_delete(imgtool_partition *partition, const char *filename, unsigned int dir)
+{
+ imgtoolerr_t err;
+ fat_file file;
+ fat_dirent ent;
+
+ err = fat_lookup_path(partition, filename, CREATE_NONE, &file);
+ if (err)
+ return err;
+ if (file.directory != dir)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ if (dir)
+ {
+ err = fat_read_dirent(partition, &file, &ent, NULL);
+ if (err)
+ return err;
+ if (!ent.eof)
+ return IMGTOOLERR_DIRNOTEMPTY;
+ }
+
+ err = fat_set_file_size(partition, &file, ~0);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_deletefile(imgtool_partition *partition, const char *filename)
+{
+ return fat_partition_delete(partition, filename, 0);
+}
+
+
+
+static imgtoolerr_t fat_partition_freespace(imgtool_partition *partition, UINT64 *size)
+{
+ imgtoolerr_t err;
+ const fat_partition_info *disk_info;
+ UINT8 *fat_table;
+ UINT32 i;
+
+ disk_info = fat_get_partition_info(partition);
+
+ err = fat_load_fat(partition, &fat_table);
+ if (err)
+ goto done;
+
+ *size = 0;
+ for (i = 2; i < disk_info->total_clusters; i++)
+ {
+ if (fat_get_fat_entry(partition, fat_table, i) == 0)
+ *size += disk_info->cluster_size;
+ }
+
+done:
+ if (fat_table)
+ free(fat_table);
+ return err;
+}
+
+
+
+static imgtoolerr_t fat_partition_createdir(imgtool_partition *partition, const char *path)
+{
+ imgtoolerr_t err;
+ fat_file file;
+ UINT8 initial_data[64];
+
+ err = fat_lookup_path(partition, path, CREATE_DIR, &file);
+ if (err)
+ return err;
+ if (!file.directory)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ err = fat_set_file_size(partition, &file, sizeof(initial_data));
+ if (err)
+ return err;
+
+ /* set up the two directory entries in all directories */
+ memset(initial_data, 0, sizeof(initial_data));
+ memcpy(&initial_data[0], ". ", 11);
+ place_integer_le(initial_data, 11, 1, 0x10);
+ place_integer_le(initial_data, 26, 2, file.first_cluster);
+ memcpy(&initial_data[32], ". ", 11);
+ place_integer_le(initial_data, 43, 1, 0x10);
+ place_integer_le(initial_data, 58, 2, file.parent_first_cluster);
+
+ err = fat_write_file(partition, &file, initial_data, sizeof(initial_data), NULL);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t fat_partition_deletedir(imgtool_partition *partition, const char *path)
+{
+ return fat_partition_delete(partition, path, 1);
+}
+
+
+
+void fat_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_SUPPORTS_BOOTBLOCK: info->i = 1; break;
+ case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '\\'; break;
+ case IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR: info->i = '/'; break;
+ case IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES: info->i = sizeof(fat_partition_info); break;
+ case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(fat_file); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
+ case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r\n"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case IMGTOOLINFO_PTR_CREATE_PARTITION: info->create_partition = fat_partition_create; break;
+ case IMGTOOLINFO_PTR_OPEN_PARTITION: info->open_partition = fat_partition_open; break;
+ case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = fat_partition_beginenum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = fat_partition_nextenum; break;
+ case IMGTOOLINFO_PTR_READ_FILE: info->read_file = fat_partition_readfile; break;
+ case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = fat_partition_writefile; break;
+ case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = fat_partition_deletefile; break;
+ case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = fat_partition_freespace; break;
+ case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = fat_partition_createdir; break;
+ case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = fat_partition_deletedir; break;
+ }
+}