summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/tools/imgtool/modules/ti99.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/tools/imgtool/modules/ti99.c')
-rw-r--r--src/mame/tools/imgtool/modules/ti99.c5360
1 files changed, 5360 insertions, 0 deletions
diff --git a/src/mame/tools/imgtool/modules/ti99.c b/src/mame/tools/imgtool/modules/ti99.c
new file mode 100644
index 00000000000..220cbebdb27
--- /dev/null
+++ b/src/mame/tools/imgtool/modules/ti99.c
@@ -0,0 +1,5360 @@
+// license:BSD-3-Clause
+// copyright-holders:Raphael Nabet
+/*
+ Handlers for ti99 disk images
+
+ We support the following types of image:
+ * floppy disks ("DSK format") in v9t9, pc99 and old mess image format
+ * hard disks ("WIN format") in MAME harddisk format (256-byte sectors only,
+ i.e. no SCSI)
+ Files are extracted in TIFILES format. There is a compile-time option to
+ extract text files in flat format instead, but I need to re-implement it
+ properly (using filters comes to mind).
+
+ Raphael Nabet, 2002-2004
+
+ TODO:
+ - finish and test hd support ***test sibling FDR support***
+ - check allocation bitmap against corruption when an image is opened
+*/
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "imgtool.h"
+#include "harddisk.h"
+#include "imghd.h"
+
+/*
+ Concepts shared by both disk structures:
+ ----------------------------------------
+
+ In both formats, the entire disk surface is split into physical records,
+ each 256-byte-long. For best efficiency, the sector size is usually 256
+ bytes so that one sector is equivalent to a physical record (this is true
+ with floppies and HFDC hard disks). If the sector size is not 256 bytes
+ (e.g. 512 bytes with SCSI hard disks, possibly 128 bytes on a TI-99
+ simulator running on TI-990), sectors are grouped or split to form 256-byte
+ physical records. Physical records are numbered from 0, with the first
+ starting on sector 0 track 0. To avoid confusion with physical records in
+ files, such physical records will be called "absolute physical records",
+ abbreviated as "absolute physrecs" or "aphysrecs".
+
+ Disk space is allocated in units referred to as AUs. Each AU represents an
+ integral number of 256-byte physical records. The number of physrecs per
+ AU varies depending on the disk format and size. AUs are numbered from 0,
+ with the first starting with absolute physrec 0.
+
+ Absolute physrec 0 contains a 256-byte record with volume information,
+ which is called "Volume Information Block", abbreviated as "vib".
+
+ To keep track of which areas on the disk are allocated and which are free,
+ disk managers maintain a bit map of allocated AUs (called "allocation
+ bitmap", abbreviated as "abm"). Each bit in the allocation bitmap
+ represents an AU: if a bit is 0, the AU is free; if a bit is 1, the AU is
+ allocated (or possibly bad if the disk manager can track bad sectors).
+ Where on the disk the abm is located depends on the disk structure (see
+ structure-specific description for details).
+
+ Files are implemented as a succession of 256-byte physical records. To
+ avoid confusion with absolute physical records, these physical records will
+ be called "file physical records", abbreviated as "file physrecs" or
+ "fphysrecs".
+
+ Programs do normally not access file physical records directly. They may
+ call high-level file routines that enable to create either fixed-length
+ logical records of any size from 1 through 255, or variable-length records
+ of any size from 0 through 254: logical records are grouped by file
+ managers into 256-byte physical records. Some disk managers (HFDC and
+ SCSI) allow programs to create fixed-length records larger than 255 bytes,
+ too, but few programs use this possibility. Additionally, program files
+ can be created: program files do not implement any logical record, and can
+ be seen as a flat byte stream, not unlike files under MSDOS, UNIX and the C
+ standard library. (Unfortunately, the API for program files lacks
+ flexibility, and most programs that need to process a flat byte stream
+ will use fixed-size records of 128 bytes.)
+
+ Fixed-length records (reclen < 256) are grouped like this:
+ fphysrec 0 fphysrec 1
+ _______________ _______________
+ |R0 |R1 |R2 |X| |R3 |R4 |R5 |X|
+ --------------- ---------------
+ \_/ \_/
+ unused unused
+ (size < reclen) (size < reclen)
+
+ Fixed-length records (reclen > 256) are grouped like this:
+ fphysrec 0 fphysrec 1 fphysrec 2 fphysrec 3 ...
+ _________________ _________________ _________________ _________________
+ | R0 (#0-255) | | R0 (#256-511) | |R0 (#512-end)|X| | R1 (#0-255) | ...
+ ----------------- ----------------- ----------------- -----------------
+ \_/
+ unused
+
+ Variable length records are grouped like this:
+ fphysrec 0:
+ byte:
+ ------------------------------
+ 0 |l0 = length of record 0 data|
+ |----------------------------|
+ 1 | |
+ | |
+ : | record 0 data |
+ | |
+ l0 | |
+ |----------------------------|
+ l0+1 |l1 = length of record 1 data|
+ |----------------------------|
+ l0+2 | |
+ | |
+ : | record 1 data |
+ | |
+ l0+l1+1 | |
+ |----------------------------|
+ : : :
+ : : :
+ |----------------------------|
+ n |lm = length of record m data|
+ |----------------------------|
+ n+1 | |
+ | |
+ : | record m data |
+ | |
+ n+lm | |
+ |----------------------------|
+ n+lm+1 |>ff: EOR mark |
+ |----------------------------|
+ n+lm+2 | |
+ | |
+ : | unused |
+ : |(we should have size < lm+1)|
+ | |
+ 255 | |
+ ------------------------------
+
+ fphysrec 1:
+ byte:
+ ------------------------------
+ 0 |lm+1=length of record 0 data|
+ |----------------------------|
+ 1 | |
+ | |
+ : | record m+1 data |
+ | |
+ lm+1 | |
+ |----------------------------|
+ : : :
+ : : :
+
+ I think the EOR mark is not required if all 256 bytes of a physical
+ record are used by logical records.
+
+
+ All files are associated with a "file descriptor record" ("fdr") that holds
+ file information (name, format, length) and points to the data AUs. The WIN
+ disk structure also supports sibling FDRs, in case a file is so fragmented
+ that all the data pointers cannot fit in one FDR; the DSK disk structure
+ does not implement any such feature, and you may be unable to append data
+ to an existing file if it is too fragmented, even though the disk is not
+ full.
+
+
+ DSK disk structure:
+ -------------------
+
+ The DSK disk structure is used for floppy disks, and a few RAMDISKs as
+ well. It supports 1600 AUs and 16 MBytes at most (the 16 MByte value is a
+ theorical maximum, as I doubt anyone has ever created so big a DSK volume).
+
+ The disk sector size is always 256 bytes (the only potential exceptions are
+ the SCSI floppy disk units and the TI-99 simulator on TI-990, but I don't
+ know anything about either).
+
+ For some reason, the number of physrecs per AU is always a power of 2.
+ Originally, AUs always were one physical record each. However, since the
+ allocation bitmap cannot support more than 1600 AUs, disks larger than
+ 400kbytes need to have several physical records per AU. Note that
+ relatively few disk controllers implement AUs that span multiple physical
+ records (IIRC, these are Myarc's HFDC, SCSI DSR with floppy disk interface,
+ and some upgraded Myarc and Corcomp DD floppy controllers).
+
+ The allocation bitmap is located in the VIB. It is 200 bytes long, and
+ supports 1600 AUs at most.
+
+ Directories are implemented with a "File Descriptor Index Record" (FDIR)
+ for each directory. The FDIR is array of 0 through 128 words, containing
+ the aphysrec address of each fdr in the directory, sorted by ascending file
+ name, terminated with a 0. Note that, while we should be prepared to read
+ images images with 128 entries, I think (not 100% sure) that we should
+ write no more than 127 for compatibility with some existing disk managers.
+
+ Originally, the DSK structure only supported one directory level (i.e. the
+ root directory level). The FDIR record for the root directory is always
+ located in aphysrec 1. Moreover, Myarc extended the DSK structure to
+ support up to 3 subdirectories in the root directory (note that there is no
+ support for more subdirs, or for subdirs located in subdirs). To do so,
+ they used an unused field of the VIB to hold the 10-char name of each
+ directory and the aphysrec address of the associated FDIR record.
+
+ aphysrec 0: Volume Information Block (VIB): see below
+ aphysrec 1: FDIR for root directory
+ Remaining AUs are used for fdr and data (and subdirectory FDIR if
+ applicable). There is one FDIR record per directory; the FDIR points to
+ the FDR for each file in the directory. The FDR (File Descriptor Record)
+ contains the file information (name, format, length, pointers to data
+ physrecs/AUs, etc).
+
+
+ WIN disk structure:
+ -------------------
+
+ The WIN disk structure is used for hard disks. It supports 65535 AUs and
+ 256 MBytes at most.
+
+ The disk sector size is 256 bytes on HFDC, 512 bytes on SCSI. 512-byte
+ sectors are split into 256-byte physical records.
+
+ We may have from 1 through 16 physrecs per AUs. Since we cannot have more
+ than 65535 AUs, we need to have several physical records per AU in disks
+ larger than 16 Mbytes.
+
+ Contrary to the DSK disk structure, the WIN disk structure supports
+ hierarchic subdirectories, with a limit of 114 subdirectories and 127 files
+ per directory. Each directory is associated with both a "Directory
+ Descriptor Record" (DDR) and a "File Descriptor Index Record" (FDIR). The
+ only exception is the root directory, which uses the VIB instead of a DDR
+ (the VIB includes all the important fields of the DDR). The DDR contains
+ some directory info (name, number of files and subdirectories directly
+ enclosed in the directory, AU address of the FDIR of the parent directory,
+ etc), the AU address of the associated FDIR, and the AU address of the DDR
+ of up to 114 subdirectories, sorted by ascending directory name. The WIN
+ FDIR is similar to, yet different from, the DSK FDIR: it contains up to 127
+ (vs. 128) AU address (vs. aphysrec address) of each fdr in the directory,
+ sorted by ascending file name. Additionally, it includes the AU address of
+ the associated DDR.
+
+ When a file has more than 54 data fragments, the disk manager creates
+ sibling FDRs that hold additional fragments. (Sibling FDRs are called
+ offspring FDRs in most existing documentation, but I prefer "sibling FDRs"
+ as "offspring FDRs" wrongly suggests a directory-like hierarchy.
+ Incidentally, I cannot really figure out why they chose to duplicate the
+ FDR rather than create a file extension record, but there must be a
+ reason.) Note that sibling FDRs usually fill unused physrecs in the AU
+ allocated for the eldest FDR, and a new AU is allocated for new sibling
+ FDRs only when the first AU is full.
+
+ aphysrec 0: Volume Information Block (VIB): see below
+ aphysrec 1-n (where n = 1+SUP(number_of_AUs/2048)): Volume bitmap
+ Remaining AUs are used for ddr, fdir, fdr and data.
+*/
+
+/* Since string length is encoded with a byte, the maximum length of a string
+is 255. Since we need to add a device name (1 char minimum, typically 4 chars)
+and a '.' separator, we chose a practical value of 250 (though few applications
+will accept paths of such length). */
+#define MAX_PATH_LEN 253
+#define MAX_SAFE_PATH_LEN 250
+
+#define MAX_DIR_LEVEL 125 /* We need to put a recursion limit to avoid endless recursion hazard */
+
+
+#if 0
+#pragma mark MISCELLANEOUS UTILITIES
+#endif
+
+/*
+ Miscellaneous utilities that are used to handle TI data types
+*/
+
+struct UINT16BE
+{
+ UINT8 bytes[2];
+};
+
+struct UINT16LE
+{
+ UINT8 bytes[2];
+};
+
+INLINE UINT16 get_UINT16BE(UINT16BE word)
+{
+ return (word.bytes[0] << 8) | word.bytes[1];
+}
+
+INLINE void set_UINT16BE(UINT16BE *word, UINT16 data)
+{
+ word->bytes[0] = (data >> 8) & 0xff;
+ word->bytes[1] = data & 0xff;
+}
+
+INLINE UINT16 get_UINT16LE(UINT16LE word)
+{
+ return word.bytes[0] | (word.bytes[1] << 8);
+}
+
+INLINE void set_UINT16LE(UINT16LE *word, UINT16 data)
+{
+ word->bytes[0] = data & 0xff;
+ word->bytes[1] = (data >> 8) & 0xff;
+}
+
+/*
+ Convert a C string to a 10-character file name (padded with spaces if necessary)
+
+ dst (O): destination 10-character array
+ src (I): source C string
+*/
+static void str_to_fname(char dst[10], const char *src)
+{
+ int i;
+
+
+ i = 0;
+
+ /* copy 10 characters at most */
+ if (src)
+ while ((i<10) && (src[i]!='\0'))
+ {
+ dst[i] = src[i];
+ i++;
+ }
+
+ /* pad with spaces */
+ while (i<10)
+ {
+ dst[i] = ' ';
+ i++;
+ }
+}
+
+/*
+ Convert a 10-character file name to a C string (removing trailing spaces if necessary)
+
+ dst (O): destination C string
+ src (I): source 10-character array
+ n (I): length of dst buffer (string may be truncated if less than 11)
+*/
+static void fname_to_str(char *dst, const char src[10], int n)
+{
+ int i;
+ int last_nonspace;
+
+
+ /* copy 10 characters at most */
+ if (--n > 10)
+ n = 10;
+
+ /* copy filename */
+ i = 0;
+ last_nonspace = -1;
+
+ while (i<n)
+ {
+ dst[i] = src[i];
+ if (src[i] != ' ')
+ last_nonspace = i;
+ i++;
+ }
+
+ /* terminate with '\0' */
+ dst[last_nonspace+1] = '\0';
+}
+
+/*
+ Check a 10-character file name.
+
+ filename (I): 10-character array with file name
+
+ Return non-zero if the file name is invalid.
+*/
+static int check_fname(const char filename[10])
+{
+ int i;
+ int space_found_flag;
+
+
+ /* check and copy file name */
+ space_found_flag = FALSE;
+ for (i=0; i<10; i++)
+ {
+ switch (filename[i])
+ {
+ case '.':
+ case '\0':
+ /* illegal characters */
+ return 1;
+ case ' ':
+ /* illegal in a file name, but file names shorter than 10
+ characters are padded with spaces */
+ space_found_flag = TRUE;
+ break;
+ default:
+ /* all other characters are legal (though non-ASCII characters,
+ control characters and even lower-case characters are not
+ recommended) */
+ if (space_found_flag)
+ return 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ Check a file path.
+
+ fpath (I): C string with file path
+
+ Return non-zero if the file path is invalid.
+*/
+static int check_fpath(const char *fpath)
+{
+ const char *element_start, *element_end;
+ int element_len;
+
+
+ /* first check that the path is not too long and that there is no space
+ character */
+ if ((strlen(fpath) > MAX_PATH_LEN) || strchr(fpath, ' '))
+ return 1;
+
+ /* check that each path element has an acceptable length */
+ element_start = fpath;
+ do
+ {
+ /* find next path element */
+ element_end = strchr(element_start, '.');
+ element_len = element_end ? (element_end - element_start) : strlen(element_start);
+ if ((element_len > 10) || (element_len == 0))
+ return 1;
+
+ /* iterate */
+ if (element_end)
+ element_start = element_end+1;
+ else
+ element_start = NULL;
+ }
+ while (element_start);
+
+ return 0;
+}
+
+#if 0
+#pragma mark -
+#pragma mark LEVEL 1 DISK ROUTINES
+#endif
+
+/*
+ Level 1 deals with accessing the disk hardware (in our case, the raw disk
+ image).
+
+ Higher level routines will only know the disk as a succession of
+ 256-byte-long physical records addressed by a linear address.
+*/
+
+/*
+ Disk geometry
+*/
+struct ti99_geometry
+{
+ int secspertrack;
+ int cylinders;
+ int heads;
+};
+
+/*
+ Physical sector address
+*/
+struct ti99_sector_address
+{
+ int sector;
+ int cylinder;
+ int side;
+};
+
+/*
+ Time stamp (used in fdr, and WIN VIB/DDR)
+*/
+struct ti99_date_time
+{
+ UINT8 time_MSB, time_LSB;/* 0-4: hour, 5-10: minutes, 11-15: seconds/2 */
+ UINT8 date_MSB, date_LSB;/* 0-6: year, 7-10: month, 11-15: day */
+};
+
+/*
+ Subdirectory descriptor (HFDC only)
+
+ The HFDC supports up to 3 subdirectories.
+*/
+struct dsk_subdir
+{
+ char name[10]; /* subdirectory name (10 characters, pad with spaces) */
+ UINT16BE fdir_aphysrec; /* aphysrec address of fdir record for this subdirectory */
+};
+
+/*
+ DSK VIB record
+
+ Most fields in this record are only relevant to level 2 routines, but level
+ 1 routines need the disk geometry information extracted from the VIB.
+*/
+struct dsk_vib
+{
+ char name[10]; /* disk volume name (10 characters, pad with spaces) */
+ UINT16BE totphysrecs; /* total number of physrecs on disk (usually 360, */
+ /* 720, 640, 1280 or 1440). (TI originally */
+ /* said it should be the total number of AUs, */
+ /* but, in practice, DSRs that supports disks */
+ /* over 400kbytes (e.g. HFDC) write the total */
+ /* number of physrecs.) */
+ UINT8 secspertrack; /* sectors per track (usually 9 (FM SD), */
+ /* 16 or 18 (MFM DD), or 36 (MFM HD) */
+ UINT8 id[3]; /* 'DSK' */
+ UINT8 protection; /* 'P' if disk is protected, ' ' otherwise. */
+ UINT8 cylinders; /* tracks per side (usually 40) */
+ UINT8 heads; /* sides (1 or 2) */
+ UINT8 density; /* density: 1 (FM SD), 2 (MFM DD), or 3 (MFM HD) */
+ dsk_subdir subdir[3]; /* descriptor for up to 3 subdirectories (HFDC only) */
+ /* reserved by TI */
+ UINT8 abm[200]; /* allocation bitmap: there is one bit per AU. */
+ /* A binary 1 in a bit position indicates that */
+ /* the allocation unit associated with that */
+ /* bit has been allocated. */
+ /* Bits corresponding to system reserved AUs, */
+ /* non-existant AUs, and bad AUs, are set to one, too. */
+ /* (AU 0 is associated to LSBit of byte 0, */
+ /* AU 7 to MSBit of byte 0, AU 8 to LSBit */
+ /* of byte 1, etc.) */
+};
+
+enum ti99_img_format
+{
+ if_mess,
+ if_v9t9,
+ if_pc99_fm,
+ if_pc99_mfm,
+ if_harddisk
+};
+
+/*
+ level-1 disk image descriptor
+*/
+struct ti99_lvl1_imgref
+{
+ ti99_img_format img_format; /* tells the image format */
+ imgtool_stream *file_handle; /* imgtool file handle */
+ struct mess_hard_disk_file harddisk_handle; /* MAME harddisk handle (harddisk format) */
+ ti99_geometry geometry; /* geometry */
+ unsigned pc99_track_len; /* unformatted track length (pc99 format) */
+ UINT32 *pc99_data_offset_array; /* offset for each sector (pc99 format) */
+};
+
+/*
+ calculate CRC for data address marks or sector data
+
+ crc (I/O): current CRC value to be updated (initialize to 0xffff before
+ calling this function for the first time)
+ value: new byte of data to update the CRC with
+*/
+static void calc_crc(UINT16 *crc, UINT8 value)
+{
+ UINT8 l, h;
+
+ l = value ^ (*crc >> 8);
+ *crc = (*crc & 0xff) | (l << 8);
+ l >>= 4;
+ l ^= (*crc >> 8);
+ *crc <<= 8;
+ *crc = (*crc & 0xff00) | l;
+ l = (l << 4) | (l >> 4);
+ h = l;
+ l = (l << 2) | (l >> 6);
+ l &= 0x1f;
+ *crc = *crc ^ (l << 8);
+ l = h & 0xf0;
+ *crc = *crc ^ (l << 8);
+ l = (h << 1) | (h >> 7);
+ l &= 0xe0;
+ *crc = *crc ^ l;
+}
+
+/*
+ Parse a PC99 disk image
+
+ file_handle (I/O): imgtool file handle
+ fm_format (I): if true, the image is in FM format, otherwise it is in MFM
+ format
+ pass (I): 0 for first pass, 1 for second pass
+ vib (O): buffer where the vib should be stored (first pass)
+ geometry (O): disk image geometry (second pass)
+ data_offset_array (O): array of data offset to generate (second pass)
+ out_track_len (O): track length is returned there
+
+ Return imgtool error code
+*/
+#define MAX_TRACK_LEN 6872
+#define DATA_OFFSET_NONE 0xffffffff
+static int parse_pc99_image(imgtool_stream *file_handle, int fm_format, int pass, dsk_vib *vib, const ti99_geometry *geometry, UINT32 *data_offset_array, unsigned *out_track_len)
+{
+ int track_len, num_tracks; /* length of a track in bytes, and number of tracks */
+ int phys_track;
+ int expected_cylinder, expected_head;
+ int track_start_pos, track_pos;
+ UINT8 c;
+ UINT8 cylinder, head, sector;
+ int seclen;
+ UINT8 crc1, crc2;
+ UINT16 crc;
+ long data_offset;
+ UINT8 track_buf[MAX_TRACK_LEN];
+ int i;
+
+ if (fm_format)
+ track_len = 3253;
+ else
+ track_len = 6872;
+
+ if (out_track_len)
+ *out_track_len = track_len;
+
+ if (stream_size(file_handle) % track_len)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ num_tracks = stream_size(file_handle) / track_len;
+ if (num_tracks <= 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* we only support 40-track-per-side images */
+ if ((num_tracks != 40) && (num_tracks != 80))
+ return IMGTOOLERR_UNIMPLEMENTED;
+
+ if (pass == 1)
+ {
+ /* initialize offset map */
+ for (head = 0; head < geometry->heads; head++)
+ for (cylinder = 0; cylinder < geometry->cylinders; cylinder++)
+ for (sector = 0; sector < geometry->secspertrack; sector++)
+ data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] = DATA_OFFSET_NONE;
+ }
+ /* rewind to start of file */
+ stream_seek(file_handle, 0, SEEK_SET);
+
+ /* pass 0 only looks for vib in track 0; pass 1 scans every track */
+ for (phys_track=0; phys_track < ((pass == 1) ? num_tracks : 1); phys_track++)
+ {
+ if (stream_read(file_handle, track_buf, track_len) != track_len)
+ return IMGTOOLERR_READERROR;
+
+ /* we only support 40-track-per-side images */
+ expected_cylinder = phys_track % 40;
+ expected_head = phys_track / 40;
+
+ track_start_pos = 0;
+
+ while (track_start_pos < track_len)
+ {
+ if (fm_format)
+ {
+ do
+ {
+ c = track_buf[track_start_pos];
+ track_start_pos++;
+ } while ((c != 0xfe) && (track_start_pos < track_len));
+
+ if (c != 0xfe)
+ break;
+
+ track_pos = track_start_pos;
+
+ crc = 0xffff;
+ calc_crc(& crc, c);
+ }
+ else
+ {
+ do
+ {
+ c = track_buf[track_start_pos];
+ track_start_pos++;
+ } while ((c != 0xa1) && (track_start_pos < track_len));
+
+ if (c != 0xa1)
+ break;
+
+ track_pos = track_start_pos;
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ if (c != 0xa1)
+ continue;
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ if (c != 0xa1)
+ continue;
+
+ crc = 0xffff;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ if (c != 0xfe)
+ continue;
+ }
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ cylinder = c;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ head = c;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ sector = c;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ seclen = 128 << c;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ crc1 = c;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ crc2 = c;
+ calc_crc(& crc, c);
+
+#if 0
+ /* CRC seems to be completely hosed */
+ if (crc)
+ printf("aargh!");
+#endif
+ if ((seclen != 256) || (crc1 != 0xf7) || (crc2 != 0xf7)
+ || (cylinder != expected_cylinder) || (head != expected_head)
+ || ((pass == 1) && ((cylinder >= geometry->cylinders)
+ || (head >= geometry->heads)
+ || (sector >= geometry->secspertrack))))
+ continue;
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ while (c == (fm_format ? 0xff : 0x4e))
+ {
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+ }
+
+ while (c == 0x00)
+ {
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+ }
+
+ if (fm_format)
+ {
+ if (c != 0xfb)
+ continue;
+
+ crc = 0xffff;
+ calc_crc(& crc, c);
+ }
+ else
+ {
+ if (c != 0xa1)
+ continue;
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ if (c != 0xa1)
+ continue;
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ if (c != 0xa1)
+ continue;
+
+ crc = 0xffff;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ if (c != 0xfb)
+ continue;
+ }
+ data_offset = track_pos;
+ for (i=0; i<seclen; i++)
+ {
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ calc_crc(& crc, c);
+ }
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ crc1 = c;
+ calc_crc(& crc, c);
+
+ c = track_buf[track_pos];
+ track_pos++;
+ if (track_pos == track_len)
+ track_pos = 0;
+
+ crc2 = c;
+ calc_crc(& crc, c);
+
+#if 0
+ /* CRC seems to be completely hosed */
+ if (crc)
+ printf("aargh!");
+#endif
+ if ((crc1 != 0xf7) || (crc2 != 0xf7))
+ continue;
+
+ switch (pass)
+ {
+ case 0:
+ if ((cylinder == 0) && (head == 0) && (sector == 0))
+ {
+ /* return vib */
+ if ((data_offset + 256) <= track_len)
+ memcpy(vib, track_buf + data_offset, 256);
+ else
+ {
+ memcpy((UINT8 *)vib, track_buf + data_offset, track_len-data_offset);
+ memcpy((UINT8 *)vib + (track_len-data_offset), track_buf, 256-(track_len-data_offset));
+ }
+ return 0;
+ }
+ break;
+
+ case 1:
+ /* set up offset map */
+ if (data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] != DATA_OFFSET_NONE)
+ /* error: duplicate sector */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] = data_offset;
+ break;
+ }
+ }
+ }
+
+ if (pass == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (pass == 1)
+ {
+ /* check offset map */
+ for (head = 0; head < geometry->heads; head++)
+ for (cylinder = 0; cylinder < geometry->cylinders; cylinder++)
+ for (sector = 0; sector < geometry->secspertrack; sector++)
+ if (data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] == DATA_OFFSET_NONE)
+ /* error: missing sector */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ return 0;
+}
+
+
+/*
+ Read the volume information block (aphysrec 0) assuming no geometry
+ information. (Called when an image is opened to figure out the
+ geometry information.)
+
+ file_handle (I/O): imgtool file handle
+ img_format (I): image format (MESS, V9T9 or PC99)
+ dest (O): pointer to 256-byte destination buffer
+
+ Return imgtool error code
+*/
+static int read_image_vib_no_geometry(imgtool_stream *file_handle, ti99_img_format img_format, dsk_vib *dest)
+{
+ int reply;
+
+ switch (img_format)
+ {
+ case if_mess:
+ case if_v9t9:
+ /* seek to sector */
+ reply = stream_seek(file_handle, 0, SEEK_SET);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+ /* read it */
+ reply = stream_read(file_handle, dest, 256);
+ if (reply != 256)
+ return IMGTOOLERR_READERROR;
+ return 0;
+
+ case if_pc99_fm:
+ case if_pc99_mfm:
+ return parse_pc99_image(file_handle, img_format == if_pc99_fm, 0, dest, NULL, NULL, NULL);
+
+ case if_harddisk:
+ /* not implemented, because we don't need it */
+ break;
+ }
+
+ return IMGTOOLERR_UNIMPLEMENTED;
+}
+
+/*
+ Open a disk image at level 1
+
+ file_handle (I/O): imgtool file handle
+ img_format (I): image format (MESS, V9T9, PC99, or MAME harddisk)
+ l1_img (O): level-1 image handle
+ vib (O): buffer where the vib should be stored (floppy images only)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t open_image_lvl1(imgtool_stream *file_handle, ti99_img_format img_format, ti99_lvl1_imgref *l1_img, dsk_vib *vib)
+{
+ imgtoolerr_t err;
+ int reply;
+ UINT16 totphysrecs;
+
+
+ l1_img->img_format = img_format;
+ l1_img->file_handle = file_handle;
+
+ if (img_format == if_harddisk)
+ {
+ const hard_disk_info *info;
+
+ err = imghd_open(file_handle, &l1_img->harddisk_handle);
+ if (err)
+ return err;
+
+ info = imghd_get_header(&l1_img->harddisk_handle);
+ l1_img->geometry.cylinders = info->cylinders;
+ l1_img->geometry.heads = info->heads;
+ l1_img->geometry.secspertrack = info->sectors;
+ if (info->sectorbytes != 256)
+ {
+ imghd_close(&l1_img->harddisk_handle);
+ return IMGTOOLERR_CORRUPTIMAGE; /* TODO: support 512-byte sectors */
+ }
+
+#if 0
+ /* read vib */
+ reply = read_absolute_physrec(l1_img, 0, vib);
+ if (reply)
+ return reply;
+#endif
+ }
+ else
+ {
+ /* read vib */
+ reply = read_image_vib_no_geometry(file_handle, img_format, vib);
+ if (reply)
+ return (imgtoolerr_t)reply;
+
+ /* extract geometry information */
+ totphysrecs = get_UINT16BE(vib->totphysrecs);
+
+ l1_img->geometry.secspertrack = vib->secspertrack;
+ if (l1_img->geometry.secspertrack == 0)
+ /* Some images might be like this, because the original SSSD TI
+ controller always assumes 9. */
+ l1_img->geometry.secspertrack = 9;
+ l1_img->geometry.cylinders = vib->cylinders;
+ if (l1_img->geometry.cylinders == 0)
+ /* Some images are like this, because the original SSSD TI
+ controller always assumes 40. */
+ l1_img->geometry.cylinders = 40;
+ l1_img->geometry.heads = vib->heads;
+ if (l1_img->geometry.heads == 0)
+ /* Some images are like this, because the original SSSD TI
+ controller always assumes that tracks beyond 40 are on side 2. */
+ l1_img->geometry.heads = totphysrecs / (l1_img->geometry.secspertrack * l1_img->geometry.cylinders);
+
+ /* check information */
+ if ((totphysrecs != (l1_img->geometry.secspertrack * l1_img->geometry.cylinders * l1_img->geometry.heads))
+ || (totphysrecs < 2)
+ || memcmp(vib->id, "DSK", 3) || (! strchr(" P", vib->protection))
+ || (((img_format == if_mess) || (img_format == if_v9t9))
+ && (stream_size(file_handle) != totphysrecs*256U)))
+ return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
+
+ if ((img_format == if_pc99_fm) || (img_format == if_pc99_mfm))
+ {
+ l1_img->pc99_data_offset_array = (UINT32*)malloc(sizeof(*l1_img->pc99_data_offset_array)*totphysrecs);
+ if (! l1_img->pc99_data_offset_array)
+ return IMGTOOLERR_OUTOFMEMORY;
+ reply = parse_pc99_image(file_handle, img_format == if_pc99_fm, 1, NULL, & l1_img->geometry, l1_img->pc99_data_offset_array, &l1_img->pc99_track_len);
+ if (reply)
+ {
+ free(l1_img->pc99_data_offset_array);
+ return (imgtoolerr_t)reply;
+ }
+ }
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Close a disk image at level 1
+
+ l1_img (I/O): level-1 image handle
+
+ Return imgtool error code
+*/
+static void close_image_lvl1(ti99_lvl1_imgref *l1_img)
+{
+ if (l1_img->img_format == if_harddisk)
+ {
+ imghd_close(&l1_img->harddisk_handle);
+ }
+
+ stream_close(l1_img->file_handle);
+
+ if ((l1_img->img_format == if_pc99_fm) || (l1_img->img_format == if_pc99_mfm))
+ free(l1_img->pc99_data_offset_array);
+}
+
+/*
+ Convert physical sector address to offset in disk image (old MESS and V9T9
+ formats only)
+
+ l1_img (I/O): level-1 image handle
+ address (I): physical sector address
+
+ Return offset in image
+*/
+INLINE int sector_address_to_image_offset(const ti99_lvl1_imgref *l1_img, const ti99_sector_address *address)
+{
+ int offset = 0;
+
+ switch (l1_img->img_format)
+ {
+ case if_mess:
+ /* old MESS format */
+ offset = (((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector)*256;
+ break;
+
+ case if_v9t9:
+ /* V9T9 format */
+ if (address->side & 1)
+ /* on side 1, tracks are stored in the reverse order */
+ offset = (((address->side*l1_img->geometry.cylinders) + (l1_img->geometry.cylinders-1 - address->cylinder))*l1_img->geometry.secspertrack + address->sector)*256;
+ else
+ offset = (((address->side*l1_img->geometry.cylinders) + address->cylinder)*l1_img->geometry.secspertrack + address->sector)*256;
+ break;
+
+ case if_pc99_fm:
+ case if_pc99_mfm:
+ /* pc99 format */
+ case if_harddisk:
+ /* harddisk format */
+ assert(1); /* not implemented */
+ break;
+ }
+
+ return offset;
+}
+
+/*
+ Read one 256-byte sector from a disk image
+
+ l1_img (I/O): level-1 image handle
+ address (I): physical sector address
+ dest (O): pointer to 256-byte destination buffer
+
+ Return non-zero on error
+*/
+static int read_sector(ti99_lvl1_imgref *l1_img, const ti99_sector_address *address, void *dest)
+{
+ int reply;
+ UINT32 track_len, track_offset, sector_offset;
+
+ switch (l1_img->img_format)
+ {
+ case if_mess:
+ /* old MESS format */
+ case if_v9t9:
+ /* V9T9 format */
+ /* seek to sector */
+ reply = stream_seek(l1_img->file_handle, sector_address_to_image_offset(l1_img, address), SEEK_SET);
+ if (reply)
+ return 1;
+ /* read it */
+ reply = stream_read(l1_img->file_handle, dest, 256);
+ if (reply != 256)
+ return 1;
+ break;
+
+ case if_pc99_fm:
+ case if_pc99_mfm:
+ /* pc99 format */
+ track_len = l1_img->pc99_track_len;
+ track_offset = (address->side*l1_img->geometry.cylinders + address->cylinder)*track_len;
+ sector_offset = l1_img->pc99_data_offset_array[(address->side*l1_img->geometry.cylinders + address->cylinder)*l1_img->geometry.secspertrack + address->sector];
+
+ if ((sector_offset + 256) <= track_len)
+ {
+ /* seek to sector */
+ reply = stream_seek(l1_img->file_handle, track_offset+sector_offset, SEEK_SET);
+ if (reply)
+ return 1;
+ /* read it */
+ reply = stream_read(l1_img->file_handle, dest, 256);
+ if (reply != 256)
+ return 1;
+ }
+ else
+ {
+ /* seek to sector */
+ reply = stream_seek(l1_img->file_handle, track_offset+sector_offset, SEEK_SET);
+ if (reply)
+ return 1;
+ /* read first chunk (until end of track) */
+ reply = stream_read(l1_img->file_handle, (UINT8 *)dest, track_len-sector_offset);
+ if (reply != track_len-sector_offset)
+ return 1;
+
+ /* wrap to start of track */
+ reply = stream_seek(l1_img->file_handle, track_offset, SEEK_SET);
+ if (reply)
+ return 1;
+ /* read remnant of sector */
+ reply = stream_read(l1_img->file_handle, (UINT8 *)dest + (track_len-sector_offset), 256-(track_len-sector_offset));
+ if (reply != 256-(track_len-sector_offset))
+ return 1;
+ }
+ break;
+
+ case if_harddisk:
+ /* not implemented */
+ assert(1);
+ /*return imghd_read(l1_img->harddisk_handle, ((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector, 1, dest) != 1;*/
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Write one 256-byte sector to a disk image
+
+ l1_img (I/O): level-1 image handle
+ address (I): physical sector address
+ src (I): pointer to 256-byte source buffer
+
+ Return non-zero on error
+*/
+static int write_sector(ti99_lvl1_imgref *l1_img, const ti99_sector_address *address, const void *src)
+{
+ int reply;
+ UINT32 track_len, track_offset, sector_offset;
+
+ switch (l1_img->img_format)
+ {
+ case if_mess:
+ /* old MESS format */
+ case if_v9t9:
+ /* V9T9 format */
+ /* seek to sector */
+ reply = stream_seek(l1_img->file_handle, sector_address_to_image_offset(l1_img, address), SEEK_SET);
+ if (reply)
+ return 1;
+ /* write it */
+ reply = stream_write(l1_img->file_handle, src, 256);
+ if (reply != 256)
+ return 1;
+ break;
+
+ case if_pc99_fm:
+ case if_pc99_mfm:
+ /* pc99 format */
+ track_len = l1_img->pc99_track_len;
+ track_offset = (address->side*l1_img->geometry.cylinders + address->cylinder)*track_len;
+ sector_offset = l1_img->pc99_data_offset_array[(address->side*l1_img->geometry.cylinders + address->cylinder)*l1_img->geometry.secspertrack + address->sector];
+
+ if ((sector_offset + 256) <= track_len)
+ {
+ /* seek to sector */
+ reply = stream_seek(l1_img->file_handle, track_offset+sector_offset, SEEK_SET);
+ if (reply)
+ return 1;
+ /* write it */
+ reply = stream_write(l1_img->file_handle, src, 256);
+ if (reply != 256)
+ return 1;
+ }
+ else
+ {
+ /* seek to sector */
+ reply = stream_seek(l1_img->file_handle, track_offset+sector_offset, SEEK_SET);
+ if (reply)
+ return 1;
+ /* write first chunk (until end of track) */
+ reply = stream_write(l1_img->file_handle, (UINT8 *)src, track_len-sector_offset);
+ if (reply != track_len-sector_offset)
+ return 1;
+
+ /* wrap to start of track */
+ reply = stream_seek(l1_img->file_handle, track_offset, SEEK_SET);
+ if (reply)
+ return 1;
+ /* write remnant of sector */
+ reply = stream_write(l1_img->file_handle, (UINT8 *)src + (track_len-sector_offset), 256-(track_len-sector_offset));
+ if (reply != 256-(track_len-sector_offset))
+ return 1;
+ }
+ break;
+
+ case if_harddisk:
+ /* not implemented */
+ assert(1);
+ /*return imghd_write(l1_img->harddisk_handle, ((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector, 1, src) != 1;*/
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Convert physical record address to sector address (DSK format)
+
+ aphysrec (I): absolute physrec address
+ geometry (I): disk image geometry
+ address (O): physical sector address
+*/
+static void dsk_aphysrec_to_sector_address(int aphysrec, const ti99_geometry *geometry, ti99_sector_address *address)
+{
+ address->sector = aphysrec % geometry->secspertrack;
+ aphysrec /= geometry->secspertrack;
+ address->cylinder = aphysrec % geometry->cylinders;
+ address->side = aphysrec / geometry->cylinders;
+ if (address->side & 1)
+ /* on side 1, tracks are stored in the reverse order */
+ address->cylinder = geometry->cylinders-1 - address->cylinder;
+}
+
+/*
+ Convert physical record address to sector address (WIN format for HFDC)
+
+ Note that physical address translation makes sense for HFDC, but not SCSI.
+
+ aphysrec (I): absolute physrec address
+ geometry (I): disk image geometry
+ address (O): physical sector address
+*/
+#ifdef UNUSED_FUNCTION
+static void win_aphysrec_to_sector_address(int aphysrec, const ti99_geometry *geometry, ti99_sector_address *address)
+{
+ address.sector = aphysrec % l1_img->geometry.secspertrack;
+ aphysrec /= l1_img->geometry.secspertrack;
+ address.side = aphysrec % l1_img->geometry.heads;
+ address.cylinder = aphysrec / l1_img->geometry.heads;
+}
+#endif
+
+/*
+ Read one 256-byte physical record from a disk image
+
+ l1_img (I/O): level-1 image handle
+ aphysrec (I): absolute physrec address
+ dest (O): pointer to 256-byte destination buffer
+
+ Return non-zero on error
+*/
+static int read_absolute_physrec(ti99_lvl1_imgref *l1_img, unsigned aphysrec, void *dest)
+{
+ ti99_sector_address address;
+
+
+ if (l1_img->img_format == if_harddisk)
+ {
+#if 0
+ win_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
+ return read_sector(l1_img, & address, dest);
+#endif
+
+ return imghd_read(&l1_img->harddisk_handle, aphysrec, dest) != IMGTOOLERR_SUCCESS;
+ }
+ else
+ {
+ dsk_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
+
+ return read_sector(l1_img, & address, dest);
+ }
+}
+
+/*
+ Write one 256-byte physical record to a disk image
+
+ l1_img (I/O): level-1 image handle
+ aphysrec (I): absolute physrec address
+ src (I): pointer to 256-byte source buffer
+
+ Return non-zero on error
+*/
+static int write_absolute_physrec(ti99_lvl1_imgref *l1_img, unsigned aphysrec, const void *src)
+{
+ ti99_sector_address address;
+
+
+ if (l1_img->img_format == if_harddisk)
+ {
+#if 0
+ win_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
+ return write_sector(l1_img, & address, dest);
+#endif
+
+ return imghd_write(&l1_img->harddisk_handle, aphysrec, src) != IMGTOOLERR_SUCCESS;
+ }
+ else
+ {
+ dsk_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
+
+ return write_sector(l1_img, & address, src);
+ }
+}
+
+#if 0
+#pragma mark -
+#pragma mark LEVEL 2 DISK ROUTINES
+#endif
+
+/*
+ Level 2 implements files as a succession of 256-byte-long physical records.
+
+ Level 2 implements allocation bitmap (and AU), disk catalog, etc.
+*/
+
+/*
+ WIN VIB/DDR record
+*/
+struct win_vib_ddr
+{
+ char name[10]; /* disk volume name (10 characters, pad with spaces) */
+ UINT16BE totAUs; /* total number of AUs */
+ UINT8 secspertrack; /* HFDC: sectors per track (typically 32) */
+ /* SCSI: reserved */
+ union
+ {
+ struct
+ {
+ UINT8 id[3]; /* V1 VIB: 'WIN' */
+ } vib_v1;
+
+ struct /* V2 VIB: extra params */
+ {
+ UINT8 res_AUs; /* # AUs reserved for vib, bitmap, ddr, fdir and fdr, divided by 64 */
+ UINT8 step_spd; /* HFDC: step speed (0-7) */
+ /* SCSI: reserved */
+ UINT8 red_w_cur;/* HFDC: reduced write current cylinder, divided by 8 */
+ /* SCSI: reserved */
+ } vib_v2;
+
+ struct
+ {
+ UINT8 id[3]; /* DDR: 'DIR' */
+ } ddr;
+ } u;
+ UINT16BE params; /* bits 0-3: sectors/AU - 1 */
+ /* HFDC: */
+ /* bits 4-7: # heads - 1 */
+ /* bit 8: V1: buffered head stepping flag */
+ /* V2: reserved */
+ /* bit 9-15: write precompensation track, divided by 16 */
+ /* SCSI: */
+ /* bits 4-15: reserved */
+ ti99_date_time creation;/* date and time of creation */
+ UINT8 num_files; /* number of files in directory */
+ UINT8 num_subdirs; /* number of subdirectories in directory */
+ UINT16BE fdir_AU; /* points to root directory fdir */
+ union
+ {
+ struct
+ {
+ UINT16BE dsk1_AU; /* HFDC: points to current dsk1 emulation image (0 if none) */
+ /* SCSI: reserved */
+ } vib;
+
+ struct
+ {
+ UINT16BE parent_ddr_AU; /* points to parent directory DDR */
+ } ddr;
+ } u2;
+ UINT16BE subdir_AU[114];/* points to all subdirectory DDRs */
+};
+
+/*
+ AU format
+*/
+struct ti99_AUformat
+{
+ int totAUs; /* total number of AUs */
+ int physrecsperAU; /* number of 256-byte physical records per AU */
+};
+
+/*
+ DSK directory reference: 0 for root, 1 for 1st subdir, 2 for 2nd subdir, 3
+ for 3rd subdir
+*/
+/*typedef int dir_ref;*/
+
+/*
+ catalog entry (used for in-memory catalog)
+*/
+struct dir_entry
+{
+ UINT16 dir_ptr; /* DSK: unused */
+ /* WIN: AU address of the DDR for this directory */
+ char name[10]; /* name of this directory (copied from the VIB for DSK, DDR for WIN) */
+};
+
+struct file_entry
+{
+ UINT16 fdr_ptr; /* DSK: aphysrec address of the FDR for this file */
+ /* WIN: AU address of the FDR for this file */
+ char name[10]; /* name of this file (copied from FDR) */
+};
+
+struct ti99_catalog
+{
+ int num_subdirs; /* number of subdirectories */
+ int num_files; /* number of files */
+ dir_entry subdirs[114]; /* description of each subdir */
+ file_entry files[128]; /* description of each file */
+};
+
+/*
+ level-2 disk image descriptor
+*/
+struct ti99_lvl2_imgref_dsk
+{
+ UINT16 totphysrecs; /* total number of aphysrecs (extracted from vib record in aphysrec 0) */
+ ti99_catalog catalogs[4]; /* catalog of root directory and up to 3 subdirectories */
+ UINT16 fdir_aphysrec[4]; /* fdir aphysrec address for root directory
+ and up to 3 subdirectories */
+};
+
+enum win_vib_t
+{
+ win_vib_v1,
+ win_vib_v2
+};
+struct ti99_lvl2_imgref_win
+{
+ win_vib_t vib_version; /* version of the vib record in aphysrec 0 (see win_vib_ddr) */
+};
+
+enum l2i_t
+{
+ L2I_DSK,
+ L2I_WIN
+};
+
+struct ti99_lvl2_imgref
+{
+ ti99_lvl1_imgref l1_img;/* image format, imgtool image handle, image geometry */
+ ti99_AUformat AUformat; /* AU format */
+ int data_offset; /* In order to reduce seek times when searching the
+ disk for a given file name, fdr (and ddr, and
+ fdir) records are preferentially allocated in
+ AUs n to data_offset, whereas data records are
+ preferentially allocated in AUs starting at
+ data_offset. */
+ /* With the DSK disk structure, n is always 2 (if 1
+ physrec per AU) or 1 (if 2 physrecs per AU or
+ more), and data_offset is arbitrarily chosen as
+ 34. */
+ /* With the WIN disk structure, n depends on the
+ size of the volume bitmap, which itself depends
+ on the number of AUs on disk (we always have n
+ <= 33), and data_offset is read from the vib
+ record (except with the obsolete v1 VIB, where
+ we use a default value of 64). */
+ char vol_name[10]; /* cached volume name (extracted from vib record in aphysrec 0) */
+
+ UINT8 abm[8192]; /* allocation bitmap */
+
+ l2i_t type; /* structure format */
+
+ union
+ {
+ ti99_lvl2_imgref_dsk dsk;
+ ti99_lvl2_imgref_win win;
+ }; /* structure-specific info */
+};
+
+/*
+ file flags found in fdr (and tifiles)
+*/
+enum
+{
+ fdr99_f_program = 0x01, /* set for program files */
+ fdr99_f_int = 0x02, /* set for binary files */
+ fdr99_f_wp = 0x08, /* set if file is write-protected */
+ /*fdr99_f_backup = 0x10,*/ /* set if file has been modified since last backup */
+ /*fdr99_f_dskimg = 0x20,*/ /* set if file is a DSK image (HFDC HD only) */
+ fdr99_f_var = 0x80 /* set if file uses variable-length records*/
+};
+
+/*
+ DSK FDR record
+*/
+struct dsk_fdr
+{
+ char name[10]; /* file name (10 characters, pad with spaces) */
+ UINT16BE xreclen; /* extended record len: if record len is >= 256, */
+ /* reclen is set to 0 and the actual reclen is */
+ /* stored here (Myarc HFDC only). TI reserved */
+ /* this field for data chain pointer extension, */
+ /* but this was never implemented. */
+ UINT8 flags; /* file status flags (see enum above) */
+ UINT8 recsperphysrec; /* logical records per physrec */
+ /* ignored for variable length record files and */
+ /* program files */
+ UINT16BE fphysrecs; /* file length in physrecs */
+ /* Note that the HFDC defines this field as the */
+ /* number of allocated physrecs in the cluster */
+ /* chain (i.e. rounded on the next AU */
+ /* boundary), so level-3 routines should use */
+ /* the fixrecs field instead to determine the */
+ /* logical length of field. IIRC, the HFDC */
+ /* implementation is regarded as a bug because */
+ /* program files do not define the fixrecs field */
+ /* field, so program field saved by the HFDC */
+ /* DSR may be larger whan they should. */
+ UINT8 eof; /* EOF offset in last physrec for variable length */
+ /* record files and program files (0->256)*/
+ UINT8 reclen; /* logical record size in bytes ([1,255] 0->256) */
+ /* Maximum allowable record size for variable */
+ /* length record files. Reserved for program */
+ /* files (set to 0). Set to 0 if reclen >=256 */
+ /* (HFDC only). */
+ UINT16LE fixrecs; /* file length in logical records */
+ /* For variable length record files, number of */
+ /* 256-byte records actually used. */
+ ti99_date_time creation;/* date and time of creation (HFDC and BwG only; */
+ /* reserved in TI) */
+ ti99_date_time update; /* date and time of last write to file (HFDC and */
+ /* BwG only; reserved in TI) */
+ UINT8 clusters[76][3]; /* data cluster table: 0 through 76 entries (3 */
+ /* bytes each), one entry for each file cluster. */
+ /* 12 bits: address of first AU of cluster */
+ /* 12 bits: offset of last 256-byte record in cluster */
+};
+
+/*
+ WIN FDR record
+*/
+struct win_fdr
+{
+ char name[10]; /* file name (10 characters, pad with spaces) */
+ UINT16BE xreclen; /* extended record len: if record len is >= 256, */
+ /* reclen is set to 0 and the actual reclen is */
+ /* stored here (Myarc HFDC only). TI reserved */
+ /* this field for data chain pointer extension, */
+ /* but this was never implemented. */
+ UINT8 flags; /* file status flags (see enum above) */
+ UINT8 recsperphysrec; /* logical records per physrec */
+ /* ignored for variable length record files and */
+ /* program files */
+ UINT16BE fphysrecs_LSW; /* eldest FDR: file length in physrecs */
+ /* Note that the HFDC defines this field as the */
+ /* number of allocated physrecs in the cluster */
+ /* chain (i.e. rounded on the next AU */
+ /* boundary), so level-3 routines should use */
+ /* the fixrecs field instead to determine the */
+ /* logical length of field. IIRC, the HFDC */
+ /* implementation is regarded as a bug because */
+ /* program files do not define the fixrecs field */
+ /* field, so program field saved by the HFDC */
+ /* DSR may be larger whan they should. */
+ /* other sibling FDRs: index of the first file */
+ /* physrec in this particular sibling FDR */
+ UINT8 eof; /* EOF offset in last physrec for variable length */
+ /* record files and program files (0->256)*/
+ UINT8 reclen; /* logical record size in bytes ([1,255]) */
+ /* Maximum allowable record size for variable */
+ /* length record files. Reserved for program */
+ /* files (set to 0). Set to 0 if reclen >=256 */
+ /* (HFDC only). */
+ UINT16LE fixrecs_LSW; /* file length in logical records */
+ /* For variable length record files, number of */
+ /* 256-byte records actually used. */
+ ti99_date_time creation;/* date and time of creation */
+ ti99_date_time update; /* date and time of last write to file */
+
+ char id[2]; /* 'FI' */
+ UINT16BE prevsibFDR_AU; /* address of the AU where previous sibling FDR is */
+ /* (see also xinfo_LSB) */
+ UINT16BE nextsibFDR_AU; /* address of the AU where next sibling FDR is */
+ /* (see also xinfo_LSB) */
+ UINT16BE sibFDR_AUlen; /* total number of data AUs allocated in this particular sibling FDR */
+ UINT16BE parent_FDIR_AU;/* FDIR the file is listed in */
+ UINT8 xinfo_MSB; /* extended information (MSByte) */
+ /* bits 0-3: MSN of fphysrecs */
+ /* bits 4-7: MSN of fixrecs */
+ UINT8 xinfo_LSB; /* extended information (LSByte) */
+ /* bits 8-11: physrec offset within AU for */
+ /* previous sibling FDR (see prevsibFDR_AU) */
+ /* bits 12-15: physrec offset within AU for */
+ /* next sibling FDR (see nextsibFDR_AU) */
+ UINT16BE clusters[54][2];/* data cluster table: 0 through 54 entries (4 */
+ /* bytes each), one entry for each file cluster. */
+ /* 16 bits: address of first AU of cluster */
+ /* 16 bits: address of last AU of cluster */
+};
+
+/*
+ tifile header: stand-alone file
+*/
+struct tifile_header
+{
+ char tifiles[8]; /* always '\7TIFILES' */
+ UINT16BE fphysrecs; /* file length in physrecs */
+ UINT8 flags; /* see enum above */
+ UINT8 recsperphysrec; /* records per physrec */
+ UINT8 eof; /* current position of eof in last physrec (0->255)*/
+ UINT8 reclen; /* bytes per record ([1,255] 0->256) */
+ UINT16BE fixrecs; /* file length in records */
+ UINT8 res[128-16]; /* reserved */
+ /* * variant a: */
+ /* 112 chars: 0xCA53 repeated 56 times */
+ /* * variant b: */
+ /* 4 chars: unknown */
+ /* 108 chars: 0xCA53 repeated 54 times */
+ /* * variant c: */
+ /* 10 chars: original TI file name filed with spaces */
+ /* 102 chars: spaces */
+ /* * variant d: */
+ /* 10 chars: original TI file name filed with spaces */
+ /* 2 chars: CR+LF */
+ /* 98 chars: spaces */
+ /* 2 chars: CR+LF */
+ /* * variant e: */
+ /* 10 chars: original TI file name */
+ /* 4 bytes: unknown */
+ /* 4 bytes: time & date of creation */
+ /* 4 bytes: time & date of last update */
+ /* 90 chars: spaces */
+ /* * variant f: */
+ /* 6 bytes: 'MYTERM' */
+ /* 4 bytes: time & date of creation */
+ /* 4 bytes: time & date of last update */
+ /* 2 bytes: unknown (always >0000) */
+ /* 96 chars: 0xCA53 repeated 56 times */
+};
+
+/*
+ level-2 file descriptor
+*/
+struct ti99_lvl2_fileref_dsk
+{
+ struct ti99_lvl2_imgref *l2_img;
+ int fdr_aphysrec;
+ dsk_fdr fdr;
+};
+
+struct ti99_lvl2_fileref_win
+{
+ struct ti99_lvl2_imgref *l2_img;
+ unsigned fphysrecs; /* copy of field in the eldest FDR */
+ unsigned eldestfdr_aphysrec; /* aphysrec address of the eldest FDR */
+ unsigned curfdr_aphysrec; /* aphysrec address of the currently open sibling FDR */
+ win_fdr curfdr; /* buffer with currently open sibling FDR */
+};
+
+struct ti99_lvl2_fileref_tifiles
+{
+ imgtool_stream *file_handle;
+ tifile_header hdr;
+};
+
+enum l2f_type_t
+{
+ L2F_DSK,
+ L2F_WIN,
+ L2F_TIFILES
+};
+
+struct ti99_lvl2_fileref
+{
+ l2f_type_t type;
+ union
+ {
+ ti99_lvl2_fileref_dsk dsk;
+ ti99_lvl2_fileref_win win;
+ ti99_lvl2_fileref_tifiles tifiles;
+ };
+};
+
+/*
+ Compare two (possibly empty) catalog entry for qsort
+*/
+static int cat_file_compare_qsort(const void *p1, const void *p2)
+{
+ const file_entry *entry1 = (const file_entry *)p1;
+ const file_entry *entry2 = (const file_entry *)p2;
+
+ if ((entry1->fdr_ptr == 0) && (entry2->fdr_ptr == 0))
+ return 0;
+ else if (entry1->fdr_ptr == 0)
+ return +1;
+ else if (entry2->fdr_ptr == 0)
+ return -1;
+ else
+ return memcmp(entry1->name, entry2->name, 10);
+}
+
+static int cat_dir_compare_qsort(const void *p1, const void *p2)
+{
+ const dir_entry *entry1 = (const dir_entry *)p1;
+ const dir_entry *entry2 = (const dir_entry *)p2;
+
+ if ((entry1->dir_ptr == 0) && (entry2->dir_ptr == 0))
+ return 0;
+ else if (entry1->dir_ptr == 0)
+ return +1;
+ else if (entry2->dir_ptr == 0)
+ return -1;
+ else
+ return memcmp(entry1->name, entry2->name, 10);
+}
+
+/*
+ Read a directory catalog from disk image
+
+ l2_img: image reference
+ aphysrec: physical record address of the FDIR
+ dest: pointer to the destination buffer where the catalog should be written
+
+ Return an error code if there was an error, 0 otherwise.
+*/
+static int dsk_read_catalog(struct ti99_lvl2_imgref *l2_img, int aphysrec, ti99_catalog *dest)
+{
+ int totphysrecs = l2_img->dsk.totphysrecs;
+ UINT16BE fdir_buf[128];
+ dsk_fdr fdr;
+ int i;
+ int reply;
+
+
+ /* Read FDIR record */
+ reply = read_absolute_physrec(& l2_img->l1_img, aphysrec, fdir_buf);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+
+ /* Copy FDIR info to catalog structure */
+ for (i=0; i<128; i++)
+ dest->files[i].fdr_ptr = get_UINT16BE(fdir_buf[i]);
+
+ /* Check FDIR pointers and check and extract file names from DDRs */
+ for (i=0; i<128; i++)
+ {
+ if (dest->files[i].fdr_ptr >= totphysrecs)
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ else if (dest->files[i].fdr_ptr)
+ {
+ reply = read_absolute_physrec(& l2_img->l1_img, dest->files[i].fdr_ptr, &fdr);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+
+ /* check and copy file name */
+ if (check_fname(fdr.name))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ memcpy(dest->files[i].name, fdr.name, 10);
+ }
+ }
+
+ /* Check catalog */
+ for (i=0; i<127; i++)
+ {
+ if (((! dest->files[i].fdr_ptr) && dest->files[i+1].fdr_ptr)
+ || ((dest->files[i].fdr_ptr && dest->files[i+1].fdr_ptr) && (memcmp(dest->files[i].name, dest->files[i+1].name, 10) >= 0)))
+ {
+ /* if the catalog is not sorted, we repair it */
+ qsort(dest->files, ARRAY_LENGTH(dest->files), sizeof(dest->files[0]),
+ cat_file_compare_qsort);
+ break;
+ }
+ }
+
+ /* Set file count */
+ for (i=0; (i<128) && (dest->files[i].fdr_ptr != 0); i++)
+ ;
+ dest->num_files = i;
+
+ /* Set subdir count to 0 (subdirs are loaded elsewhere) */
+ dest->num_subdirs = 0;
+
+ return 0;
+}
+
+/*
+ Read a directory catalog from disk image
+
+ l2_img: image reference
+ DDR_AU: AU address of the VIB/DDR
+ dest: pointer to the destination buffer where the catalog should be written
+
+ Return an error code if there was an error, 0 otherwise.
+*/
+static int win_read_catalog(struct ti99_lvl2_imgref *l2_img, int DDR_AU, ti99_catalog *dest)
+{
+ win_vib_ddr ddr_buf;
+ UINT16BE fdir_buf[128];
+ win_fdr fdr_buf;
+ int i;
+ int reply;
+
+
+ /* Read DDR record */
+ reply = read_absolute_physrec(& l2_img->l1_img, DDR_AU*l2_img->AUformat.physrecsperAU, &ddr_buf);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+
+ /* sanity checks */
+ if ((ddr_buf.num_files > 127) || (ddr_buf.num_subdirs > 114) || (get_UINT16BE(ddr_buf.fdir_AU) > l2_img->AUformat.totAUs))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* set file count and subdir count */
+ dest->num_files = ddr_buf.num_files;
+ dest->num_subdirs = ddr_buf.num_subdirs;
+
+ /* Copy DDR info to catalog structure */
+ for (i=0; i<ddr_buf.num_subdirs; i++)
+ dest->subdirs[i].dir_ptr = get_UINT16BE(ddr_buf.subdir_AU[i]);
+
+ /* Read FDIR record */
+ reply = read_absolute_physrec(& l2_img->l1_img, get_UINT16BE(ddr_buf.fdir_AU)*l2_img->AUformat.physrecsperAU, fdir_buf);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+
+ /* Copy FDIR info to catalog structure */
+ for (i=0; i<dest->num_files; i++)
+ dest->files[i].fdr_ptr = get_UINT16BE(fdir_buf[i]);
+
+ /* Check DDR pointers and check and extract file names from FDRs */
+ for (i=0; i<dest->num_subdirs; i++)
+ {
+ if (dest->subdirs[i].dir_ptr >= l2_img->AUformat.totAUs)
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ else if (dest->subdirs[i].dir_ptr)
+ {
+ reply = read_absolute_physrec(& l2_img->l1_img, dest->subdirs[i].dir_ptr*l2_img->AUformat.physrecsperAU, &ddr_buf);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+
+ /* check and copy file name */
+ if (check_fname(ddr_buf.name))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ memcpy(dest->subdirs[i].name, ddr_buf.name, 10);
+ }
+ }
+
+ /* Check FDIR pointers and check and extract file names from FDRs */
+ for (i=0; i<dest->num_files; i++)
+ {
+ if (dest->files[i].fdr_ptr >= l2_img->AUformat.totAUs)
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ else if (dest->files[i].fdr_ptr)
+ {
+ reply = read_absolute_physrec(& l2_img->l1_img, dest->files[i].fdr_ptr*l2_img->AUformat.physrecsperAU, &fdr_buf);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+
+ /* check and copy file name */
+ if (check_fname(fdr_buf.name))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ memcpy(dest->files[i].name, fdr_buf.name, 10);
+ }
+ }
+
+ /* Check catalog */
+
+ /* Check subdir order */
+ for (i=0; i<dest->num_subdirs-1; i++)
+ {
+ if (((! dest->subdirs[i].dir_ptr) && dest->subdirs[i+1].dir_ptr)
+ || ((dest->subdirs[i].dir_ptr && dest->subdirs[i+1].dir_ptr) && (memcmp(dest->subdirs[i].name, dest->subdirs[i+1].name, 10) >= 0)))
+ {
+ /* if the subdir catalog is not sorted, we repair it */
+ qsort(dest->subdirs, dest->num_subdirs, sizeof(dest->subdirs[0]), cat_dir_compare_qsort);
+ break;
+ }
+ }
+
+ /* Fix subdir count */
+ while (dest->num_subdirs && (dest->subdirs[dest->num_subdirs-1].dir_ptr == 0))
+ dest->num_subdirs--;
+
+ /* Check file order */
+ for (i=0; i<dest->num_files-1; i++)
+ {
+ if (((! dest->files[i].fdr_ptr) && dest->files[i+1].fdr_ptr)
+ || ((dest->files[i].fdr_ptr && dest->files[i+1].fdr_ptr) && (memcmp(dest->files[i].name, dest->files[i+1].name, 10) >= 0)))
+ {
+ /* if the file catalog is not sorted, we repair it */
+ qsort(dest->files, dest->num_files, sizeof(dest->files[0]), cat_file_compare_qsort);
+ break;
+ }
+ }
+
+ /* Fix file count */
+ while (dest->num_files && (dest->files[dest->num_files-1].fdr_ptr == 0))
+ dest->num_files--;
+
+ return 0;
+}
+
+/*
+ Search for a file path on a floppy image
+
+ l2_img: image reference
+ fpath: path of the file to search
+ parent_ref_valid: set to TRUE if either the file was found or the file was
+ not found but its parent dir was
+ parent_ref: reference to parent dir (0 for root)
+ out_is_dir: TRUE if element is a directory
+ catalog_index: on output, index of file catalog entry (may be NULL)
+*/
+static int dsk_find_catalog_entry(struct ti99_lvl2_imgref *l2_img, const char *fpath, int *parent_ref_valid, int *parent_ref, int *out_is_dir, int *catalog_index)
+{
+ int i;
+ const ti99_catalog *cur_catalog;
+ const char *element_start, *element_end;
+ int element_len;
+ char element[10];
+ int is_dir = FALSE;
+
+
+ cur_catalog = & l2_img->dsk.catalogs[0];
+ if (parent_ref_valid)
+ (* parent_ref_valid) = FALSE;
+ if (parent_ref)
+ *parent_ref = 0;
+
+ element_start = fpath;
+ do
+ {
+ /* find next path element */
+ element_end = strchr(element_start, '.');
+ element_len = element_end ? (element_end - element_start) : strlen(element_start);
+ if ((element_len > 10) || (element_len == 0))
+ return IMGTOOLERR_BADFILENAME;
+ /* last path element */
+ if ((!element_end) && parent_ref_valid)
+ (* parent_ref_valid) = TRUE;
+
+ /* generate file name */
+ memcpy(element, element_start, element_len);
+ memset(element+element_len, ' ', 10-element_len);
+
+ /* search entry in subdirectories */
+ for (i = 0; i<cur_catalog->num_subdirs; i++)
+ {
+ if (! memcmp(element, cur_catalog->subdirs[i].name, 10))
+ {
+ is_dir = TRUE;
+ break;
+ }
+ }
+
+ /* if it failed, search entry in files */
+ if (i == cur_catalog->num_subdirs)
+ {
+ for (i = 0; i<cur_catalog->num_files; i++)
+ {
+ if (! memcmp(element, cur_catalog->files[i].name, 10))
+ {
+ is_dir = FALSE;
+ break;
+ }
+ }
+ /* exit if not found */
+ if (i == cur_catalog->num_files)
+ {
+ return IMGTOOLERR_FILENOTFOUND;
+ }
+ }
+
+ /* iterate */
+ if (element_end)
+ {
+ element_start = element_end+1;
+
+ if (! is_dir)
+ /* this is not a directory */
+ return IMGTOOLERR_BADFILENAME;
+
+ /* initialize cur_catalog */
+ cur_catalog = & l2_img->dsk.catalogs[i+1];
+ if (parent_ref)
+ *parent_ref = i+1;
+ }
+ else
+ element_start = NULL;
+ }
+ while (element_start);
+
+ if (out_is_dir)
+ *out_is_dir = is_dir;
+
+ if (catalog_index)
+ *catalog_index = i;
+
+ return 0;
+}
+
+/*
+ Search for a file path on a harddisk image
+
+ l2_img: image reference
+ fpath: path of the file to search
+ parent_ref_valid: set to TRUE if either the file was found or the file was
+ not found but its parent dir was
+ parent_ddr_AU: parent DDR AU address (0 for root)
+ parent_catalog: catalog of parent dir (cannot be NULL)
+ out_is_dir: TRUE if element is a directory
+ catalog_index: on output, index of file catalog entry (may be NULL)
+*/
+static int win_find_catalog_entry(struct ti99_lvl2_imgref *l2_img, const char *fpath,
+ int *parent_ref_valid, int *parent_ddr_AU, ti99_catalog *parent_catalog,
+ int *out_is_dir, int *catalog_index)
+{
+ int i;
+ const char *element_start, *element_end;
+ int element_len;
+ char element[10];
+ int is_dir = FALSE;
+ int errorcode;
+
+ if (parent_ref_valid)
+ (* parent_ref_valid) = FALSE;
+ if (parent_ddr_AU)
+ *parent_ddr_AU = 0;
+
+ errorcode = win_read_catalog(l2_img, 0, parent_catalog);
+ if (errorcode)
+ return errorcode;
+
+ element_start = fpath;
+ do
+ {
+ /* find next path element */
+ element_end = strchr(element_start, '.');
+ element_len = element_end ? (element_end - element_start) : strlen(element_start);
+ if ((element_len > 10) || (element_len == 0))
+ return IMGTOOLERR_BADFILENAME;
+ /* last path element */
+ if ((!element_end) && parent_ref_valid)
+ (* parent_ref_valid) = TRUE;
+
+ /* generate file name */
+ memcpy(element, element_start, element_len);
+ memset(element+element_len, ' ', 10-element_len);
+
+ /* search entry in subdirectories */
+ for (i = 0; i<parent_catalog->num_subdirs; i++)
+ {
+ if (! memcmp(element, parent_catalog->subdirs[i].name, 10))
+ {
+ is_dir = TRUE;
+ break;
+ }
+ }
+
+ /* if it failed, search entry in files */
+ if (i == parent_catalog->num_subdirs)
+ {
+ for (i = 0; i<parent_catalog->num_files; i++)
+ {
+ if (! memcmp(element, parent_catalog->files[i].name, 10))
+ {
+ is_dir = FALSE;
+ break;
+ }
+ }
+ /* exit if not found */
+ if (i == parent_catalog->num_files)
+ {
+ return IMGTOOLERR_FILENOTFOUND;
+ }
+ }
+
+ /* iterate */
+ if (element_end)
+ {
+ element_start = element_end+1;
+
+ if (! is_dir)
+ /* this is not a directory */
+ return IMGTOOLERR_BADFILENAME;
+
+ if (parent_ddr_AU)
+ *parent_ddr_AU = parent_catalog->subdirs[i].dir_ptr;
+
+ errorcode = win_read_catalog(l2_img, parent_catalog->subdirs[i].dir_ptr, parent_catalog);
+ if (errorcode)
+ return errorcode;
+ }
+ else
+ element_start = NULL;
+ }
+ while (element_start);
+
+ if (out_is_dir)
+ *out_is_dir = is_dir;
+
+ if (catalog_index)
+ *catalog_index = i;
+
+ return 0;
+}
+
+/*
+ Allocate one AU on disk, for use as a fdr record
+
+ l2_img: image reference
+ fdr_AU: on output, address of allocated AU
+*/
+static int alloc_fdr_AU(struct ti99_lvl2_imgref *l2_img, unsigned *fdr_AU)
+{
+ int totAUs = l2_img->AUformat.totAUs;
+ int i;
+
+ for (i=0; i<totAUs; i++)
+ {
+ if (! (l2_img->abm[i >> 3] & (1 << (i & 7))))
+ {
+ *fdr_AU = i;
+ l2_img->abm[i >> 3] |= 1 << (i & 7);
+
+ return 0;
+ }
+ }
+
+ return IMGTOOLERR_NOSPACE;
+}
+
+INLINE int get_dsk_fdr_cluster_baseAU(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index)
+{
+ int reply;
+
+ /* read base AU/physrec for this cluster */
+ reply = ((fdr->clusters[cluster_index][1] & 0xf) << 8) | fdr->clusters[cluster_index][0];
+ /* convert to AU address */
+ if (l2_img->AUformat.physrecsperAU <= 2)
+ reply /= l2_img->AUformat.physrecsperAU;
+
+ return reply;
+}
+
+INLINE int get_dsk_fdr_cluster_baseaphysrec(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index)
+{
+ int reply;
+
+ /* read base AU/physrec for this cluster */
+ reply = ((fdr->clusters[cluster_index][1] & 0xf) << 8) | fdr->clusters[cluster_index][0];
+ /* convert to physrec address */
+ if (l2_img->AUformat.physrecsperAU > 2)
+ reply *= l2_img->AUformat.physrecsperAU;
+
+ return reply;
+}
+
+INLINE int get_dsk_fdr_cluster_lastfphysrec(dsk_fdr *fdr, int cluster_index)
+{
+ return (fdr->clusters[cluster_index][2] << 4) | (fdr->clusters[cluster_index][1] >> 4);
+}
+
+INLINE void set_dsk_fdr_cluster_lastfphysrec(dsk_fdr *fdr, int cluster_index, int data)
+{
+ fdr->clusters[cluster_index][1] = (fdr->clusters[cluster_index][1] & 0x0f) | (data << 4);
+ fdr->clusters[cluster_index][2] = data >> 4;
+}
+
+INLINE void set_dsk_fdr_cluster(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index, int baseAU, int lastfphysrec)
+{
+ /* convert AU address to FDR value */
+ if (l2_img->AUformat.physrecsperAU <= 2)
+ baseAU *= l2_img->AUformat.physrecsperAU;
+
+ /* write cluster entry */
+ fdr->clusters[cluster_index][0] = baseAU;
+ fdr->clusters[cluster_index][1] = ((baseAU >> 8) & 0x0f) | (lastfphysrec << 4);
+ fdr->clusters[cluster_index][2] = lastfphysrec >> 4;
+}
+
+INLINE unsigned get_win_fdr_fphysrecs(win_fdr *fdr)
+{
+ return (((unsigned) fdr->xinfo_MSB << 12) & 0xf0000) | get_UINT16BE(fdr->fphysrecs_LSW);
+}
+
+INLINE void set_win_fdr_fphysrecs(win_fdr *fdr, unsigned data)
+{
+ fdr->xinfo_MSB = (fdr->xinfo_MSB & 0x0f) | ((data >> 12) & 0xf0);
+ set_UINT16BE(&fdr->fphysrecs_LSW, data & 0xffff);
+}
+
+INLINE unsigned get_win_fdr_fixrecs(win_fdr *fdr)
+{
+ return (((unsigned) fdr->xinfo_MSB << 16) & 0xf0000) | get_UINT16LE(fdr->fixrecs_LSW);
+}
+
+INLINE void set_win_fdr_fixrecs(win_fdr *fdr, unsigned data)
+{
+ fdr->xinfo_MSB = (fdr->xinfo_MSB & 0xf0) | ((data >> 16) & 0x0f);
+ set_UINT16LE(&fdr->fixrecs_LSW, data & 0xffff);
+}
+
+INLINE unsigned get_win_fdr_prevsibFDR_aphysrec(struct ti99_lvl2_imgref *l2_img, win_fdr *fdr)
+{
+ unsigned prevsibFDR_AU = get_UINT16BE(fdr->prevsibFDR_AU);
+
+ return prevsibFDR_AU
+ ? (prevsibFDR_AU * l2_img->AUformat.physrecsperAU + ((fdr->xinfo_LSB >> 4) & 0xf))
+ : 0;
+}
+
+INLINE unsigned get_win_fdr_nextsibFDR_aphysrec(struct ti99_lvl2_imgref *l2_img, win_fdr *fdr)
+{
+ unsigned nextsibFDR_AU = get_UINT16BE(fdr->nextsibFDR_AU);
+
+ return nextsibFDR_AU
+ ? (nextsibFDR_AU * l2_img->AUformat.physrecsperAU + (fdr->xinfo_LSB & 0xf))
+ : 0;
+}
+
+INLINE unsigned get_win_fdr_cursibFDR_basefphysrec(win_fdr *fdr)
+{
+ return get_UINT16BE(fdr->prevsibFDR_AU) ? get_win_fdr_fphysrecs(fdr) : 0;
+}
+
+/*
+ Advance to next sibling FDR
+*/
+static int win_goto_next_sibFDR(ti99_lvl2_fileref_win *win_file)
+{
+ if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) == 0)
+ return IMGTOOLERR_UNEXPECTED;
+
+ win_file->curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(win_file->l2_img, &win_file->curfdr);
+ if (read_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
+ return IMGTOOLERR_READERROR;
+
+ return 0;
+}
+
+/*
+ Back to previous sibling FDR
+*/
+static int win_goto_prev_sibFDR(ti99_lvl2_fileref_win *win_file)
+{
+ if (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) == 0)
+ return IMGTOOLERR_UNEXPECTED;
+
+ win_file->curfdr_aphysrec = get_win_fdr_prevsibFDR_aphysrec(win_file->l2_img, &win_file->curfdr);
+ if (read_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
+ return IMGTOOLERR_READERROR;
+
+ return 0;
+}
+
+/*
+ Append a new sibling FDR at the end of the sibling FDR list, and open it.
+
+ You must have gone to the end of the list.
+*/
+static int win_alloc_sibFDR(ti99_lvl2_fileref_win *win_file)
+{
+ unsigned oldfdr_AU, oldfdr_physrecinAU;
+ unsigned newfdr_AU, newfdr_physrecinAU;
+ int allocated = FALSE;
+ int errorcode;
+ unsigned cursibFDR_basefphysrec;
+
+ if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU))
+ return IMGTOOLERR_UNEXPECTED;
+
+ oldfdr_AU = win_file->curfdr_aphysrec / win_file->l2_img->AUformat.physrecsperAU;
+ oldfdr_physrecinAU = win_file->curfdr_aphysrec % win_file->l2_img->AUformat.physrecsperAU;
+
+ if (oldfdr_physrecinAU != (win_file->l2_img->AUformat.physrecsperAU - 1))
+ { /* current AU is not full */
+ newfdr_AU = oldfdr_AU;
+ newfdr_physrecinAU = oldfdr_physrecinAU + 1;
+ }
+ else
+ { /* current AU is full: allocate another */
+ errorcode = alloc_fdr_AU(win_file->l2_img, &newfdr_AU);
+ if (errorcode)
+ return errorcode;
+ newfdr_physrecinAU = 0;
+ allocated = TRUE;
+ }
+
+ set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, newfdr_AU);
+ win_file->curfdr.xinfo_LSB = (win_file->curfdr.xinfo_LSB & 0xf0) | newfdr_physrecinAU;
+
+ /* save current sibling FDR */
+ if (write_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
+ {
+ /* clear pointer */
+ set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, 0);
+ win_file->curfdr.xinfo_LSB = win_file->curfdr.xinfo_LSB & 0xf0;
+ if (allocated)
+ /* free AU */
+ win_file->l2_img->abm[newfdr_AU >> 3] |= 1 << (newfdr_AU & 7);
+ return IMGTOOLERR_WRITEERROR;
+ }
+
+ /* now update in-memory structure to describe new sibling FDR */
+ cursibFDR_basefphysrec = get_win_fdr_cursibFDR_basefphysrec(&win_file->curfdr)
+ + get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU;
+
+ set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, 0);
+ set_UINT16BE(&win_file->curfdr.prevsibFDR_AU, oldfdr_AU);
+ win_file->curfdr.xinfo_LSB = oldfdr_physrecinAU << 4;
+
+ win_file->curfdr_aphysrec = newfdr_AU * win_file->l2_img->AUformat.physrecsperAU + newfdr_physrecinAU;
+
+ set_win_fdr_fphysrecs(&win_file->curfdr, cursibFDR_basefphysrec);
+ set_UINT16BE(&win_file->curfdr.sibFDR_AUlen, 0);
+ memset(win_file->curfdr.clusters, 0, sizeof(win_file->curfdr.clusters));
+
+ return 0;
+}
+
+/*
+ Extend a file with nb_alloc_physrecs extra physrecs
+
+ dsk_file: file reference
+ nb_alloc_physrecs: number of physical records to allocate
+*/
+static int dsk_alloc_file_physrecs(ti99_lvl2_fileref_dsk *dsk_file, int nb_alloc_physrecs)
+{
+ int totAUs = dsk_file->l2_img->AUformat.totAUs;
+ int free_physrecs;
+ int fphysrecs;
+ int i;
+ int cluster_index;
+ int last_sec, p_last_sec = 0;
+ int cur_block_seclen;
+ int cluster_baseAU, cluster_AUlen;
+ int first_best_block_baseAU = 0, first_best_block_seclen;
+ int second_best_block_baseAU = 0, second_best_block_seclen;
+ int search_start;
+
+ /* compute free space */
+ free_physrecs = 0;
+ for (i=0; i<totAUs; i++)
+ {
+ if (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
+ free_physrecs += dsk_file->l2_img->AUformat.physrecsperAU;
+ }
+
+ /* check we have enough free space */
+ if (free_physrecs < nb_alloc_physrecs)
+ return IMGTOOLERR_NOSPACE;
+
+ /* current number of data physrecs in file */
+ fphysrecs = get_UINT16BE(dsk_file->fdr.fphysrecs);
+
+ if (fphysrecs == 0)
+ { /* cluster array must be empty */
+ cluster_index = 0;
+ }
+ else
+ { /* try to extend last block */
+ last_sec = -1;
+ for (cluster_index=0; cluster_index<76; cluster_index++)
+ {
+ p_last_sec = last_sec;
+ last_sec = get_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index);
+ if (last_sec >= (fphysrecs-1))
+ break;
+ }
+ if (cluster_index == 76)
+ /* that sucks */
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (last_sec > (fphysrecs-1))
+ { /* some extra space has already been allocated */
+ cur_block_seclen = last_sec - (fphysrecs-1);
+ if (cur_block_seclen > nb_alloc_physrecs)
+ cur_block_seclen = nb_alloc_physrecs;
+
+ fphysrecs += cur_block_seclen;
+ set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
+ nb_alloc_physrecs -= cur_block_seclen;
+ if (! nb_alloc_physrecs)
+ return 0; /* done */
+ }
+
+ /* round up to next AU boundary */
+ last_sec = last_sec + dsk_file->l2_img->AUformat.physrecsperAU - (last_sec % dsk_file->l2_img->AUformat.physrecsperAU) - 1;
+
+ if (last_sec > (fphysrecs-1))
+ { /* some extra space has already been allocated */
+ cur_block_seclen = last_sec - (fphysrecs-1);
+ if (cur_block_seclen > nb_alloc_physrecs)
+ cur_block_seclen = nb_alloc_physrecs;
+
+ fphysrecs += cur_block_seclen;
+ set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
+ set_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index, fphysrecs-1);
+ nb_alloc_physrecs -= cur_block_seclen;
+ if (! nb_alloc_physrecs)
+ return 0; /* done */
+ }
+
+ /* read base AU address for this cluster */
+ cluster_baseAU = get_dsk_fdr_cluster_baseAU(dsk_file->l2_img, &dsk_file->fdr, cluster_index);
+ /* point past cluster end */
+ cluster_baseAU += (last_sec-p_last_sec/*+file->l2_img->AUformat.physrecsperAU-1*/) / dsk_file->l2_img->AUformat.physrecsperAU;
+ /* count free physrecs after last block */
+ cur_block_seclen = 0;
+ for (i=cluster_baseAU; (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7)))) && (cur_block_seclen < nb_alloc_physrecs) && (i < totAUs); i++)
+ cur_block_seclen += dsk_file->l2_img->AUformat.physrecsperAU;
+ if (cur_block_seclen)
+ { /* extend last block */
+ if (cur_block_seclen > nb_alloc_physrecs)
+ cur_block_seclen = nb_alloc_physrecs;
+
+ fphysrecs += cur_block_seclen;
+ set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
+ last_sec += cur_block_seclen;
+ nb_alloc_physrecs -= cur_block_seclen;
+ set_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index, last_sec);
+ cluster_AUlen = (cur_block_seclen + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU;
+ for (i=0; i<cluster_AUlen; i++)
+ dsk_file->l2_img->abm[(i+cluster_baseAU) >> 3] |= 1 << ((i+cluster_baseAU) & 7);
+ if (! nb_alloc_physrecs)
+ return 0; /* done */
+ }
+ cluster_index++;
+ if (cluster_index == 76)
+ /* that sucks */
+ return IMGTOOLERR_NOSPACE;
+ }
+
+ search_start = dsk_file->l2_img->data_offset; /* initially, search for free space only in data space */
+ while (nb_alloc_physrecs)
+ {
+ /* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */
+ first_best_block_seclen = INT_MAX;
+ second_best_block_seclen = 0;
+ for (i=search_start; i<totAUs; i++)
+ {
+ if (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
+ { /* found one free block */
+ /* compute its length */
+ cluster_baseAU = i;
+ cur_block_seclen = 0;
+ while ((i<totAUs) && (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7)))))
+ {
+ cur_block_seclen += dsk_file->l2_img->AUformat.physrecsperAU;
+ i++;
+ }
+ /* compare to previous best and second-best blocks */
+ if ((cur_block_seclen < first_best_block_seclen) && (cur_block_seclen >= nb_alloc_physrecs))
+ {
+ first_best_block_baseAU = cluster_baseAU;
+ first_best_block_seclen = cur_block_seclen;
+ if (cur_block_seclen == nb_alloc_physrecs)
+ /* no need to search further */
+ break;
+ }
+ else if ((cur_block_seclen > second_best_block_seclen) && (cur_block_seclen < nb_alloc_physrecs))
+ {
+ second_best_block_baseAU = cluster_baseAU;
+ second_best_block_seclen = cur_block_seclen;
+ }
+ }
+ }
+
+ if (first_best_block_seclen != INT_MAX)
+ { /* found one contiguous block which can hold it all */
+ fphysrecs += nb_alloc_physrecs;
+ set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
+
+ set_dsk_fdr_cluster(dsk_file->l2_img, &dsk_file->fdr, cluster_index, first_best_block_baseAU, fphysrecs-1);
+ cluster_AUlen = (nb_alloc_physrecs + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU;
+ for (i=0; i<cluster_AUlen; i++)
+ dsk_file->l2_img->abm[(i+first_best_block_baseAU) >> 3] |= 1 << ((i+first_best_block_baseAU) & 7);
+
+ nb_alloc_physrecs = 0;
+ }
+ else if (second_best_block_seclen != 0)
+ { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */
+ fphysrecs += second_best_block_seclen;
+ set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
+
+ set_dsk_fdr_cluster(dsk_file->l2_img, &dsk_file->fdr, cluster_index, second_best_block_baseAU, fphysrecs-1);
+ cluster_AUlen = (second_best_block_seclen + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU;
+ for (i=0; i<cluster_AUlen; i++)
+ dsk_file->l2_img->abm[(i+second_best_block_baseAU) >> 3] |= 1 << ((i+second_best_block_baseAU) & 7);
+
+ nb_alloc_physrecs -= second_best_block_seclen;
+
+ cluster_index++;
+ if (cluster_index == 76)
+ /* that sucks */
+ return IMGTOOLERR_NOSPACE;
+ }
+ else if (search_start != 0)
+ { /* we did not find any free physrec in the data section of the disk */
+ search_start = 0; /* time to fall back to fdr space */
+ }
+ else
+ return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */
+ }
+
+ return 0;
+}
+
+/*
+ Extend a file with nb_alloc_physrecs extra physrecs
+
+ win_file: file reference
+ nb_alloc_physrecs: number of physical records to allocate
+*/
+static int win_alloc_file_physrecs(ti99_lvl2_fileref_win *win_file, int nb_alloc_physrecs)
+{
+ int totAUs = win_file->l2_img->AUformat.totAUs;
+ int free_physrecs;
+ int fphysrecs;
+ int i;
+ int cluster_index;
+ int num_fphysrec;
+ int cur_block_seclen;
+ int cluster_baseAU, cluster_AUlen;
+ int first_best_block_baseAU = 0, first_best_block_seclen;
+ int second_best_block_baseAU = 0, second_best_block_seclen;
+ int search_start;
+ int errorcode;
+
+ /* compute free space */
+ free_physrecs = 0;
+ for (i=0; i<totAUs; i++)
+ {
+ if (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
+ free_physrecs += win_file->l2_img->AUformat.physrecsperAU;
+ }
+
+ /* check we have enough free space */
+ if (free_physrecs < nb_alloc_physrecs)
+ return IMGTOOLERR_NOSPACE;
+
+ /* move to last sibling non-empty FDR */
+ while ((get_UINT16BE(win_file->curfdr.nextsibFDR_AU) != 0) &&
+ (get_UINT16BE(win_file->curfdr.clusters[53][0]) != 0))
+ {
+ errorcode = win_goto_next_sibFDR(win_file);
+ if (errorcode)
+ return errorcode;
+ }
+ if ((get_UINT16BE(win_file->curfdr.clusters[0][0]) == 0) && (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) != 0))
+ { /* this is annoying: we have found a sibling FDR filled with 0s: rewind
+ to last non-empty sibling if applicable */
+ errorcode = win_goto_prev_sibFDR(win_file);
+ if (errorcode)
+ return errorcode;
+ }
+
+ /* current number of data physrecs in file */
+ fphysrecs = win_file->fphysrecs;
+
+ /* current number of allocated physrecs */
+ num_fphysrec = get_win_fdr_cursibFDR_basefphysrec(&win_file->curfdr)
+ + get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU;
+
+ if (num_fphysrec > fphysrecs)
+ { /* some extra space has already been allocated */
+ cur_block_seclen = num_fphysrec - fphysrecs;
+ if (cur_block_seclen > nb_alloc_physrecs)
+ cur_block_seclen = nb_alloc_physrecs;
+
+ fphysrecs += cur_block_seclen;
+ win_file->fphysrecs = fphysrecs;
+ /* TODO: update eldest FDR fphysrecs field */
+ /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
+ nb_alloc_physrecs -= cur_block_seclen;
+ if (! nb_alloc_physrecs)
+ return 0; /* done */
+ }
+
+ /* find last non-empty cluster */
+ for (cluster_index=0; (cluster_index<54) && (get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) != 0); cluster_index++)
+ ;
+ /* if we are dealing with an empty file, we will have (cluster_index == 0)... */
+ if (cluster_index != 0)
+ {
+ cluster_index--;
+ /* try to extend last cluster */
+ /* point past cluster end */
+ cluster_baseAU = get_UINT16BE(win_file->curfdr.clusters[cluster_index][1]) + 1;
+ /* count free physrecs after last block */
+ cur_block_seclen = 0;
+ for (i=cluster_baseAU; (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7)))) && (cur_block_seclen < nb_alloc_physrecs) && (i < totAUs); i++)
+ cur_block_seclen += win_file->l2_img->AUformat.physrecsperAU;
+ if (cur_block_seclen)
+ { /* extend last block */
+ if (cur_block_seclen > nb_alloc_physrecs)
+ cur_block_seclen = nb_alloc_physrecs;
+
+ fphysrecs += cur_block_seclen;
+ cluster_AUlen = (cur_block_seclen + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU;
+ win_file->fphysrecs = fphysrecs;
+ /* TODO: update eldest FDR fphysrecs field */
+ /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
+ set_UINT16BE(&win_file->curfdr.sibFDR_AUlen,
+ get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen);
+ set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1],
+ get_UINT16BE(win_file->curfdr.clusters[cluster_index][1])+cluster_AUlen);
+ for (i=0; i<cluster_AUlen; i++)
+ win_file->l2_img->abm[(i+cluster_baseAU) >> 3] |= 1 << ((i+cluster_baseAU) & 7);
+ nb_alloc_physrecs -= cur_block_seclen;
+ if (! nb_alloc_physrecs)
+ return 0; /* done */
+ }
+ /* now point to first free entry in cluster table */
+ cluster_index++;
+ }
+
+ search_start = win_file->l2_img->data_offset; /* initially, search for free space only in data space */
+ while (nb_alloc_physrecs)
+ {
+ /* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */
+ first_best_block_seclen = INT_MAX;
+ second_best_block_seclen = 0;
+ for (i=search_start; i<totAUs; i++)
+ {
+ if (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
+ { /* found one free block */
+ /* compute its length */
+ cluster_baseAU = i;
+ cur_block_seclen = 0;
+ while ((i<totAUs) && (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7)))))
+ {
+ cur_block_seclen += win_file->l2_img->AUformat.physrecsperAU;
+ i++;
+ }
+ /* compare to previous best and second-best blocks */
+ if ((cur_block_seclen < first_best_block_seclen) && (cur_block_seclen >= nb_alloc_physrecs))
+ {
+ first_best_block_baseAU = cluster_baseAU;
+ first_best_block_seclen = cur_block_seclen;
+ if (cur_block_seclen == nb_alloc_physrecs)
+ /* no need to search further */
+ break;
+ }
+ else if ((cur_block_seclen > second_best_block_seclen) && (cur_block_seclen < nb_alloc_physrecs))
+ {
+ second_best_block_baseAU = cluster_baseAU;
+ second_best_block_seclen = cur_block_seclen;
+ }
+ }
+ }
+
+ if ((first_best_block_seclen != INT_MAX) || (second_best_block_seclen != 0))
+ {
+ if (cluster_index == 54)
+ {
+ /* end of cluster list: go to next sibling FDR */
+ if (write_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
+ return IMGTOOLERR_WRITEERROR;
+ if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) != 0)
+ { /* read next sibling FDR */
+ errorcode = win_goto_next_sibFDR(win_file);
+ if (errorcode)
+ return errorcode;
+ }
+ else
+ { /* allocate new sibling FDR */
+ errorcode = win_alloc_sibFDR(win_file);
+ if (errorcode)
+ return errorcode;
+ }
+ cluster_index = 0;
+ }
+ }
+
+ if (first_best_block_seclen != INT_MAX)
+ { /* found one contiguous block which can hold it all */
+ fphysrecs += nb_alloc_physrecs;
+ cluster_AUlen = (nb_alloc_physrecs + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU;
+ win_file->fphysrecs = fphysrecs;
+ /* TODO: update eldest FDR fphysrecs field */
+ /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
+ set_UINT16BE(&win_file->curfdr.sibFDR_AUlen,
+ get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen);
+ set_UINT16BE(&win_file->curfdr.clusters[cluster_index][0], first_best_block_baseAU);
+ set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1],
+ first_best_block_baseAU+cluster_AUlen-1);
+
+ for (i=0; i<cluster_AUlen; i++)
+ win_file->l2_img->abm[(i+first_best_block_baseAU) >> 3] |= 1 << ((i+first_best_block_baseAU) & 7);
+
+ nb_alloc_physrecs = 0;
+ }
+ else if (second_best_block_seclen != 0)
+ { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */
+ fphysrecs += second_best_block_seclen;
+ cluster_AUlen = (second_best_block_seclen + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU;
+ win_file->fphysrecs = fphysrecs;
+ /* TODO: update eldest FDR fphysrecs field */
+ /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
+ set_UINT16BE(&win_file->curfdr.sibFDR_AUlen,
+ get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen);
+ set_UINT16BE(&win_file->curfdr.clusters[cluster_index][0], second_best_block_baseAU);
+ set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1],
+ second_best_block_baseAU+cluster_AUlen-1);
+
+ for (i=0; i<cluster_AUlen; i++)
+ win_file->l2_img->abm[(i+second_best_block_baseAU) >> 3] |= 1 << ((i+second_best_block_baseAU) & 7);
+
+ nb_alloc_physrecs -= second_best_block_seclen;
+
+ /* now point to first free entry in cluster table */
+ cluster_index++;
+ }
+ else if (search_start != 0)
+ { /* we did not find any free physrec in the data section of the disk */
+ search_start = 0; /* time to fall back to fdr space */
+ }
+ else
+ return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */
+ }
+
+ return 0;
+}
+
+/*
+ Allocate a new (empty) file
+*/
+static int new_file_lvl2_dsk(struct ti99_lvl2_imgref *l2_img, int parent_ref, char filename[10], struct ti99_lvl2_fileref *l2_file)
+{
+ ti99_catalog *catalog = &l2_img->dsk.catalogs[parent_ref];
+ unsigned fdr_AU, fdr_aphysrec;
+ int catalog_index, i;
+ int reply = 0;
+ int errorcode;
+
+
+ if (catalog->num_files >= 127)
+ /* if num_files == 128, catalog is full */
+ /* if num_files == 127, catalog is not full, but we don't want to write
+ a 128th entry for compatibility with some existing DSRs that detect the
+ end of the FDIR array with a 0 entry (and do not check that the index
+ has not reached 128) */
+ return IMGTOOLERR_NOSPACE;
+
+ /* find insertion point in catalog */
+ for (i=0; (i < catalog->num_files) && ((reply = memcmp(catalog->files[i].name, filename, 10)) < 0); i++)
+ ;
+
+ if ((i<catalog->num_files) && (reply == 0))
+ /* file already exists */
+ return IMGTOOLERR_BADFILENAME;
+ else
+ {
+ /* otherwise insert new entry */
+ catalog_index = i;
+ errorcode = alloc_fdr_AU(l2_img, &fdr_AU);
+ if (errorcode)
+ return errorcode;
+ fdr_aphysrec = fdr_AU * l2_img->AUformat.physrecsperAU;
+
+ /* shift catalog entries until the insertion point */
+ for (i=catalog->num_files; i>catalog_index; i--)
+ catalog->files[i] = catalog->files[i-1];
+
+ /* write new catalog entry */
+ catalog->files[catalog_index].fdr_ptr = fdr_aphysrec;
+ memcpy(catalog->files[catalog_index].name, filename, 10);
+
+ /* update catalog len */
+ catalog->num_files++;
+ }
+
+ /* set up file handle */
+ l2_file->type = L2F_DSK;
+ l2_file->dsk.l2_img = l2_img;
+ l2_file->dsk.fdr_aphysrec = fdr_aphysrec;
+ memset(&l2_file->dsk.fdr, 0, sizeof(l2_file->dsk.fdr));
+ memcpy(l2_file->dsk.fdr.name, filename, 10);
+
+ return 0;
+}
+
+/*
+ Allocate a new (empty) file
+*/
+static int new_file_lvl2_win(struct ti99_lvl2_imgref *l2_img, ti99_catalog *parent_catalog, char filename[10], struct ti99_lvl2_fileref *l2_file)
+{
+ unsigned fdr_AU;
+ int catalog_index, i;
+ int reply = 0;
+ int errorcode;
+
+
+ if (parent_catalog->num_files >= 127)
+ /* if num_files == 127, catalog is full */
+ return IMGTOOLERR_NOSPACE;
+
+ /* find insertion point in catalog */
+ for (i=0; (i < parent_catalog->num_files) && ((reply = memcmp(parent_catalog->files[i].name, filename, 10)) < 0); i++)
+ ;
+
+ if ((i<parent_catalog->num_files) && (reply == 0))
+ /* file already exists */
+ return IMGTOOLERR_BADFILENAME;
+ else
+ {
+ /* otherwise insert new entry */
+ catalog_index = i;
+ errorcode = alloc_fdr_AU(l2_img, &fdr_AU);
+ if (errorcode)
+ return errorcode;
+
+ /* shift catalog entries until the insertion point */
+ for (i=parent_catalog->num_files; i>catalog_index; i--)
+ parent_catalog->files[i] = parent_catalog->files[i-1];
+
+ /* write new catalog entry */
+ parent_catalog->files[catalog_index].fdr_ptr = fdr_AU;
+ memcpy(parent_catalog->files[catalog_index].name, filename, 10);
+
+ /* update catalog len */
+ parent_catalog->num_files++;
+ }
+
+ /* set up file handle */
+ l2_file->type = L2F_WIN;
+ l2_file->win.l2_img = l2_img;
+ l2_file->win.fphysrecs = 0;
+ l2_file->win.eldestfdr_aphysrec = fdr_AU * l2_img->AUformat.physrecsperAU;
+ l2_file->win.curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
+ memset(&l2_file->win.curfdr, 0, sizeof(l2_file->win.curfdr));
+ memcpy(l2_file->win.curfdr.name, filename, 10);
+
+ return 0;
+}
+
+/*
+ Allocate a new (empty) file
+*/
+static int new_file_lvl2_tifiles(imgtool_stream *file_handle, struct ti99_lvl2_fileref *l2_file)
+{
+ /* set up file handle */
+ l2_file->type = L2F_TIFILES;
+ l2_file->tifiles.file_handle = file_handle;
+ memset(&l2_file->tifiles.hdr, 0, sizeof(l2_file->tifiles.hdr));
+ l2_file->tifiles.hdr.tifiles[0] = '\7';
+ l2_file->tifiles.hdr.tifiles[1] = 'T';
+ l2_file->tifiles.hdr.tifiles[2] = 'I';
+ l2_file->tifiles.hdr.tifiles[3] = 'F';
+ l2_file->tifiles.hdr.tifiles[4] = 'I';
+ l2_file->tifiles.hdr.tifiles[5] = 'L';
+ l2_file->tifiles.hdr.tifiles[6] = 'E';
+ l2_file->tifiles.hdr.tifiles[7] = 'S';
+
+ return 0;
+}
+
+/*
+ Open an existing file on a floppy image
+
+ l2_img: level 2 image the file is located on
+ fpath: access path to the file
+ file: set up if open is successful
+*/
+static int open_file_lvl2_dsk(struct ti99_lvl2_imgref *l2_img, const char *fpath, struct ti99_lvl2_fileref *l2_file)
+{
+ int parent_ref, is_dir, catalog_index;
+ int reply;
+
+
+ reply = dsk_find_catalog_entry(l2_img, fpath, NULL, &parent_ref, &is_dir, &catalog_index);
+ if (reply)
+ return reply;
+
+ if (is_dir)
+ /* this is not a file */
+ return IMGTOOLERR_BADFILENAME;
+
+ l2_file->type = L2F_DSK;
+ l2_file->dsk.l2_img = l2_img;
+ l2_file->dsk.fdr_aphysrec = l2_img->dsk.catalogs[parent_ref].files[catalog_index].fdr_ptr;
+ if (read_absolute_physrec(& l2_img->l1_img, l2_file->dsk.fdr_aphysrec, &l2_file->dsk.fdr))
+ return IMGTOOLERR_READERROR;
+
+ return 0;
+}
+
+/*
+ Open an existing file on a harddisk image
+
+ l2_img: level 2 image the file is located on
+ fpath: access path to the file
+ file: set up if open is successful
+*/
+static int open_file_lvl2_win(struct ti99_lvl2_imgref *l2_img, const char *fpath, struct ti99_lvl2_fileref *l2_file)
+{
+ int parent_ref, is_dir, catalog_index;
+ ti99_catalog catalog;
+ int reply;
+
+ reply = win_find_catalog_entry(l2_img, fpath, NULL, &parent_ref, &catalog, &is_dir, &catalog_index);
+ if (reply)
+ return reply;
+
+ if (is_dir)
+ /* this is not a file */
+ return IMGTOOLERR_BADFILENAME;
+
+ l2_file->type = L2F_WIN;
+ l2_file->win.l2_img = l2_img;
+ l2_file->win.eldestfdr_aphysrec = catalog.files[catalog_index].fdr_ptr * l2_img->AUformat.physrecsperAU;
+ l2_file->win.curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
+ if (read_absolute_physrec(& l2_img->l1_img, l2_file->win.curfdr_aphysrec, &l2_file->win.curfdr))
+ return IMGTOOLERR_READERROR;
+ l2_file->win.fphysrecs = get_win_fdr_fphysrecs(&l2_file->win.curfdr);
+
+ /* check integrity of FDR sibling chain */
+ /* note that as we check that the back chain is consistent with the forward
+ chain, we will also detect any cycle in the sibling chain, so we do not
+ need to check against them explicitely */
+ if (get_UINT16BE(l2_file->win.curfdr.prevsibFDR_AU) != 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ {
+ int i, pastendoflist_flag;
+ unsigned cur_fphysrec, sibFDR_AUlen;
+ win_fdr *cur_fdr, fdr_buf;
+ unsigned curfdr_aphysrec, prevfdr_aphysrec;
+
+ cur_fphysrec = 0;
+ pastendoflist_flag = 0;
+ cur_fdr = &l2_file->win.curfdr;
+ curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
+
+ while (1)
+ {
+ sibFDR_AUlen = 0;
+ i=0;
+ if (! pastendoflist_flag)
+ {
+ /* compute number of allocated AUs and check number of AUs */
+ for (; i<54; i++)
+ {
+ if (get_UINT16BE(cur_fdr->clusters[i][0]) == 0)
+ {
+ pastendoflist_flag = TRUE;
+ break;
+ }
+ sibFDR_AUlen += get_UINT16BE(cur_fdr->clusters[i][1])
+ - get_UINT16BE(cur_fdr->clusters[i][0])
+ + 1;
+ }
+ }
+ /* clear remainder of cluster table */
+ for (; i<54; i++)
+ {
+#if 0
+ set_UINT16BE(&cur_fdr->clusters[i][0], 0);
+ set_UINT16BE(&cur_fdr->clusters[i][1], 0);
+#endif
+ if ((get_UINT16BE(cur_fdr->clusters[i][0]) != 0) || (get_UINT16BE(cur_fdr->clusters[i][1]) != 0))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ /* check sibFDR_AUlen field */
+ if (get_UINT16BE(cur_fdr->sibFDR_AUlen) != sibFDR_AUlen)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* update current file physrec position to point to end of sibling FDR */
+ cur_fphysrec += sibFDR_AUlen * l2_file->win.l2_img->AUformat.physrecsperAU;
+
+ /* exit loop if end of sibling chain */
+ if (! get_UINT16BE(cur_fdr->nextsibFDR_AU))
+ break;
+
+ /* otherwise read next FDR */
+ if (get_UINT16BE(cur_fdr->nextsibFDR_AU) >= l2_file->win.l2_img->AUformat.totAUs)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ prevfdr_aphysrec = curfdr_aphysrec;
+ curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(l2_file->win.l2_img, cur_fdr);
+ if (read_absolute_physrec(& l2_file->win.l2_img->l1_img, curfdr_aphysrec, &fdr_buf))
+ return IMGTOOLERR_READERROR;
+ cur_fdr = &fdr_buf;
+
+ /* check that back chaining is consistent with forward chaining */
+ if (get_win_fdr_prevsibFDR_aphysrec(l2_file->win.l2_img, &fdr_buf) != prevfdr_aphysrec)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ /* check fphysrecs field */
+ if (get_win_fdr_fphysrecs(&fdr_buf) != cur_fphysrec)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* check consistency of informative fields (name, record format, flags, etc) */
+ if (memcmp(fdr_buf.name, l2_file->win.curfdr.name, 10)
+ || (get_UINT16BE(fdr_buf.xreclen) != get_UINT16BE(l2_file->win.curfdr.xreclen))
+ || (fdr_buf.flags != l2_file->win.curfdr.flags)
+ || (fdr_buf.recsperphysrec != l2_file->win.curfdr.recsperphysrec)
+ || (fdr_buf.eof != l2_file->win.curfdr.eof)
+ || (fdr_buf.reclen != l2_file->win.curfdr.reclen)
+ || (get_UINT16LE(fdr_buf.fixrecs_LSW) != get_UINT16LE(l2_file->win.curfdr.fixrecs_LSW))
+ || memcmp(&fdr_buf.creation, &l2_file->win.curfdr.creation, 4)
+ || memcmp(&fdr_buf.update, &l2_file->win.curfdr.update, 4)
+ /*|| memcmp(fdr_buf.id, l2_file->win.curfdr.id, 2)*/
+ || (get_UINT16BE(fdr_buf.parent_FDIR_AU) != get_UINT16BE(l2_file->win.curfdr.parent_FDIR_AU))
+ || ((fdr_buf.xinfo_MSB & 0xf) != (l2_file->win.curfdr.xinfo_MSB & 0xf)))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ if (cur_fphysrec < l2_file->win.fphysrecs)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ return 0;
+}
+
+/*
+ Open an existing file in TIFILES format
+*/
+static int open_file_lvl2_tifiles(imgtool_stream *file_handle, struct ti99_lvl2_fileref *l2_file)
+{
+ /* set up file handle */
+ l2_file->type = L2F_TIFILES;
+ l2_file->tifiles.file_handle = file_handle;
+
+ /* seek to header */
+ if (stream_seek(l2_file->tifiles.file_handle, 0, SEEK_SET))
+ return IMGTOOLERR_READERROR;
+ /* read it */
+ if (stream_read(l2_file->tifiles.file_handle, &l2_file->tifiles.hdr, sizeof(l2_file->tifiles.hdr)) != sizeof(l2_file->tifiles.hdr))
+ return IMGTOOLERR_READERROR;
+
+ return 0;
+}
+
+/*
+ compute the aphysrec address for a given file physical record (fphysrec)
+
+ l2_img: image where the file is located
+*/
+static int dsk_fphysrec_to_aphysrec(ti99_lvl2_fileref_dsk *dsk_file, unsigned fphysrec, unsigned *aphysrec)
+{
+ int cluster_index;
+ int cluster_firstfphysrec, cluster_lastfphysrec;
+ int cluster_baseaphysrec;
+
+
+ /* check parameter */
+ if (fphysrec >= get_UINT16BE(dsk_file->fdr.fphysrecs))
+ return IMGTOOLERR_UNEXPECTED;
+
+
+ /* search for the cluster in the data chain pointers array */
+ cluster_firstfphysrec = 0;
+ for (cluster_index=0; cluster_index<76; cluster_index++)
+ {
+ /* read curent file block table entry */
+ cluster_lastfphysrec = get_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index);
+ if (cluster_lastfphysrec >= fphysrec)
+ break;
+ cluster_firstfphysrec = cluster_lastfphysrec+1;
+ }
+ if (cluster_index == 76)
+ /* if not found, the file is corrupt */
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* read base aphysrec address for this cluster */
+ cluster_baseaphysrec = get_dsk_fdr_cluster_baseaphysrec(dsk_file->l2_img, &dsk_file->fdr, cluster_index);
+ /* return absolute physrec address */
+ *aphysrec = cluster_baseaphysrec + (fphysrec - cluster_firstfphysrec);
+ return 0;
+}
+
+/*
+ compute the aphysrec address for a given file physical record (fphysrec)
+
+ l2_img: image where the file is located
+*/
+static int win_fphysrec_to_aphysrec(ti99_lvl2_fileref_win *win_file, unsigned fphysrec, unsigned *aphysrec)
+{
+ int cluster_index;
+ int cluster_firstfphysrec, cluster_lastfphysrec;
+ int cluster_baseaphysrec;
+ int errorcode;
+
+
+ /* check parameter */
+ if (fphysrec >= win_file->fphysrecs)
+ return IMGTOOLERR_UNEXPECTED;
+
+ /* look for correct sibling */
+ if (fphysrec < get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr))
+ {
+ while (fphysrec < get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr))
+ {
+ if (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ errorcode = win_goto_prev_sibFDR(win_file);
+ if (errorcode)
+ return errorcode;
+ }
+ }
+ else /*if (fphysrec >= get_win_fdr_cursibFDR_basefphysrec(& dsk_file->curfdr))*/
+ {
+ while (fphysrec >= (get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr)
+ + get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU))
+ {
+ if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) == 0)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ errorcode = win_goto_next_sibFDR(win_file);
+ if (errorcode)
+ return errorcode;
+ }
+ }
+
+
+ /* search for the cluster in the data chain pointers array */
+ cluster_firstfphysrec = get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr);
+ for (cluster_index = 0; cluster_index<54; cluster_index++)
+ {
+ cluster_lastfphysrec = cluster_firstfphysrec
+ + (get_UINT16BE(win_file->curfdr.clusters[cluster_index][1])
+ - get_UINT16BE(win_file->curfdr.clusters[cluster_index][0])
+ + 1)
+ * win_file->l2_img->AUformat.physrecsperAU;
+ if (fphysrec < cluster_lastfphysrec)
+ break;
+
+ cluster_firstfphysrec = cluster_lastfphysrec;
+ }
+
+ if (cluster_index == 54)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+
+ /* read base aphysrec address for this cluster */
+ cluster_baseaphysrec = get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) * win_file->l2_img->AUformat.physrecsperAU;
+ /* return absolute physrec address */
+ *aphysrec = cluster_baseaphysrec + (fphysrec - cluster_firstfphysrec);
+ return 0;
+}
+
+/*
+ read a 256-byte physical record from a file
+*/
+static int read_file_physrec(struct ti99_lvl2_fileref *l2_file, unsigned fphysrec, void *dest)
+{
+ int errorcode;
+ unsigned aphysrec;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ /* compute absolute physrec address */
+ errorcode = dsk_fphysrec_to_aphysrec(&l2_file->dsk, fphysrec, &aphysrec);
+ if (errorcode)
+ return errorcode;
+ /* read physrec */
+ if (read_absolute_physrec(& l2_file->dsk.l2_img->l1_img, aphysrec, dest))
+ return IMGTOOLERR_READERROR;
+ break;
+
+ case L2F_WIN:
+ /* compute absolute physrec address */
+ errorcode = win_fphysrec_to_aphysrec(&l2_file->win, fphysrec, &aphysrec);
+ if (errorcode)
+ return errorcode;
+ /* read physrec */
+ if (read_absolute_physrec(& l2_file->win.l2_img->l1_img, aphysrec, dest))
+ return IMGTOOLERR_READERROR;
+ break;
+
+ case L2F_TIFILES:
+ /* seek to physrec */
+ if (stream_seek(l2_file->tifiles.file_handle, 128+256*fphysrec, SEEK_SET))
+ return IMGTOOLERR_READERROR;
+ /* read it */
+ if (stream_read(l2_file->tifiles.file_handle, dest, 256) != 256)
+ return IMGTOOLERR_READERROR;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ read a 256-byte physical record from a file
+*/
+static int write_file_physrec(struct ti99_lvl2_fileref *l2_file, unsigned fphysrec, const void *src)
+{
+ int errorcode;
+ unsigned aphysrec;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ /* compute absolute physrec address */
+ errorcode = dsk_fphysrec_to_aphysrec(&l2_file->dsk, fphysrec, &aphysrec);
+ if (errorcode)
+ return errorcode;
+ /* write physrec */
+ if (write_absolute_physrec(& l2_file->dsk.l2_img->l1_img, aphysrec, src))
+ return IMGTOOLERR_WRITEERROR;
+ break;
+
+ case L2F_WIN:
+ /* compute absolute physrec address */
+ errorcode = win_fphysrec_to_aphysrec(&l2_file->win, fphysrec, &aphysrec);
+ if (errorcode)
+ return errorcode;
+ /* write physrec */
+ if (write_absolute_physrec(& l2_file->win.l2_img->l1_img, aphysrec, src))
+ return IMGTOOLERR_WRITEERROR;
+ break;
+
+ case L2F_TIFILES:
+ /* seek to physrec */
+ if (stream_seek(l2_file->tifiles.file_handle, 128+256*fphysrec, SEEK_SET))
+ return IMGTOOLERR_WRITEERROR;
+ /* write it */
+ if (stream_write(l2_file->tifiles.file_handle, src, 256) != 256)
+ return IMGTOOLERR_WRITEERROR;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Write a field in every fdr record associated to a file
+*/
+#ifdef UNUSED_FUNCTION
+static int set_win_fdr_field(struct ti99_lvl2_fileref *l2_file, size_t offset, size_t size, void *data)
+{
+ win_fdr fdr_buf;
+ unsigned fdr_aphysrec;
+ int errorcode = 0;
+
+ for (fdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
+ fdr_aphysrec && ((errorcode = (read_absolute_physrec(&l2_file->win.l2_img->l1_img, fdr_aphysrec, &fdr_buf) ? IMGTOOLERR_READERROR : 0)) == 0);
+ fdr_aphysrec = get_win_fdr_nextsibFDR_physrec(l2_file->win.l2_img, &fdr_buf))
+ {
+ memcpy(((UINT8 *) &fdr_buf) + offset, data, size);
+ if (write_absolute_physrec(&l2_file->win.l2_img->l1_img, fdr_aphysrec, &fdr_buf))
+ {
+ errorcode = IMGTOOLERR_WRITEERROR;
+ break;
+ }
+ }
+
+ return errorcode;
+}
+#endif
+
+static UINT8 get_file_flags(struct ti99_lvl2_fileref *l2_file)
+{
+ int reply = 0;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ reply = l2_file->dsk.fdr.flags;
+ break;
+
+ case L2F_WIN:
+ reply = l2_file->win.curfdr.flags;
+ break;
+
+ case L2F_TIFILES:
+ reply = l2_file->tifiles.hdr.flags;
+ break;
+ }
+
+ return reply;
+}
+
+static void set_file_flags(struct ti99_lvl2_fileref *l2_file, UINT8 data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ l2_file->dsk.fdr.flags = data;
+ break;
+
+ case L2F_WIN:
+ l2_file->win.curfdr.flags = data;
+ break;
+
+ case L2F_TIFILES:
+ l2_file->tifiles.hdr.flags = data;
+ break;
+ }
+}
+
+static UINT8 get_file_recsperphysrec(struct ti99_lvl2_fileref *l2_file)
+{
+ int reply = 0;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ reply = l2_file->dsk.fdr.recsperphysrec;
+ break;
+
+ case L2F_WIN:
+ reply = l2_file->win.curfdr.recsperphysrec;
+ break;
+
+ case L2F_TIFILES:
+ reply = l2_file->tifiles.hdr.recsperphysrec;
+ break;
+ }
+
+ return reply;
+}
+
+static void set_file_recsperphysrec(struct ti99_lvl2_fileref *l2_file, UINT8 data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ l2_file->dsk.fdr.recsperphysrec = data;
+ break;
+
+ case L2F_WIN:
+ l2_file->win.curfdr.recsperphysrec = data;
+ break;
+
+ case L2F_TIFILES:
+ l2_file->tifiles.hdr.recsperphysrec = data;
+ break;
+ }
+}
+
+static unsigned get_file_fphysrecs(struct ti99_lvl2_fileref *l2_file)
+{
+ int reply = 0;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ reply = get_UINT16BE(l2_file->dsk.fdr.fphysrecs);
+ break;
+
+ case L2F_WIN:
+ reply = l2_file->win.fphysrecs;
+ break;
+
+ case L2F_TIFILES:
+ reply = get_UINT16BE(l2_file->tifiles.hdr.fphysrecs);
+ break;
+ }
+
+ return reply;
+}
+
+static int set_file_fphysrecs(struct ti99_lvl2_fileref *l2_file, unsigned data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ if (data >= 65536)
+ return IMGTOOLERR_UNIMPLEMENTED;
+ set_UINT16BE(&l2_file->dsk.fdr.fphysrecs, data);
+ break;
+
+ case L2F_WIN:
+ l2_file->win.fphysrecs = data;
+ break;
+
+ case L2F_TIFILES:
+ if (data >= 65536)
+ return IMGTOOLERR_UNIMPLEMENTED;
+ set_UINT16BE(&l2_file->tifiles.hdr.fphysrecs, data);
+ break;
+ }
+
+ return 0;
+}
+
+static UINT8 get_file_eof(struct ti99_lvl2_fileref *l2_file)
+{
+ int reply = 0;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ reply = l2_file->dsk.fdr.eof;
+ break;
+
+ case L2F_WIN:
+ reply = l2_file->win.curfdr.eof;
+ break;
+
+ case L2F_TIFILES:
+ reply = l2_file->tifiles.hdr.eof;
+ break;
+ }
+
+ return reply;
+}
+
+static void set_file_eof(struct ti99_lvl2_fileref *l2_file, UINT8 data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ l2_file->dsk.fdr.eof = data;
+ break;
+
+ case L2F_WIN:
+ l2_file->win.curfdr.eof = data;
+ break;
+
+ case L2F_TIFILES:
+ l2_file->tifiles.hdr.eof = data;
+ break;
+ }
+}
+
+static UINT16 get_file_reclen(struct ti99_lvl2_fileref *l2_file)
+{
+ int reply = 0;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ reply = l2_file->dsk.fdr.reclen;
+ if ((reply == 0) && (! (l2_file->dsk.fdr.flags & (fdr99_f_program /*| fdr99_f_var*/))))
+ reply = get_UINT16BE(l2_file->dsk.fdr.xreclen);
+ break;
+
+ case L2F_WIN:
+ reply = l2_file->win.curfdr.reclen;
+ if ((reply == 0) && (! (l2_file->win.curfdr.flags & (fdr99_f_program /*| fdr99_f_var*/))))
+ reply = get_UINT16BE(l2_file->win.curfdr.xreclen);
+ break;
+
+ case L2F_TIFILES:
+ reply = l2_file->tifiles.hdr.reclen;
+ break;
+ }
+
+ return reply;
+}
+
+static int set_file_reclen(struct ti99_lvl2_fileref *l2_file, UINT16 data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ if (data < 256)
+ {
+ l2_file->dsk.fdr.reclen = data;
+ set_UINT16BE(&l2_file->dsk.fdr.xreclen, 0);
+ }
+ else
+ {
+ l2_file->dsk.fdr.reclen = 0;
+ set_UINT16BE(&l2_file->dsk.fdr.xreclen, data);
+ }
+ break;
+
+ case L2F_WIN:
+ if (data < 256)
+ {
+ l2_file->win.curfdr.reclen = data;
+ set_UINT16BE(&l2_file->win.curfdr.xreclen, 0);
+ }
+ else
+ {
+ l2_file->win.curfdr.reclen = 0;
+ set_UINT16BE(&l2_file->win.curfdr.xreclen, data);
+ }
+ break;
+
+ case L2F_TIFILES:
+ if (data >= 256)
+ return IMGTOOLERR_UNIMPLEMENTED;
+ l2_file->tifiles.hdr.reclen = data;
+ break;
+ }
+
+ return 0;
+}
+
+static unsigned get_file_fixrecs(struct ti99_lvl2_fileref *l2_file)
+{
+ int reply = 0;
+
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ reply = get_UINT16LE(l2_file->dsk.fdr.fixrecs);
+ break;
+
+ case L2F_WIN:
+ reply = get_win_fdr_fixrecs(&l2_file->win.curfdr);
+ break;
+
+ case L2F_TIFILES:
+ reply = get_UINT16BE(l2_file->tifiles.hdr.fixrecs);
+ break;
+ }
+
+ return reply;
+}
+
+static int set_file_fixrecs(struct ti99_lvl2_fileref *l2_file, unsigned data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ if (data >= 65536)
+ return IMGTOOLERR_UNIMPLEMENTED;
+ set_UINT16LE(&l2_file->dsk.fdr.fixrecs, data);
+ break;
+
+ case L2F_WIN:
+ set_win_fdr_fixrecs(&l2_file->win.curfdr, data);
+ break;
+
+ case L2F_TIFILES:
+ if (data >= 65536)
+ return IMGTOOLERR_UNIMPLEMENTED;
+ set_UINT16BE(&l2_file->tifiles.hdr.fixrecs, data);
+ break;
+ }
+
+ return 0;
+}
+
+static void get_file_creation_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time *reply)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ *reply = l2_file->dsk.fdr.creation;
+ break;
+
+ case L2F_WIN:
+ *reply = l2_file->win.curfdr.creation;
+ break;
+
+ case L2F_TIFILES:
+ memset(reply, 0, sizeof(*reply));
+ break;
+ }
+}
+
+static void set_file_creation_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ l2_file->dsk.fdr.creation = data;
+ break;
+
+ case L2F_WIN:
+ l2_file->win.curfdr.creation = data;
+ break;
+
+ case L2F_TIFILES:
+ break;
+ }
+}
+
+static void get_file_update_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time *reply)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ *reply = l2_file->dsk.fdr.update;
+ break;
+
+ case L2F_WIN:
+ *reply = l2_file->win.curfdr.update;
+ break;
+
+ case L2F_TIFILES:
+ memset(reply, 0, sizeof(*reply));
+ break;
+ }
+}
+
+static void set_file_update_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time data)
+{
+ switch (l2_file->type)
+ {
+ case L2F_DSK:
+ l2_file->dsk.fdr.update = data;
+ break;
+
+ case L2F_WIN:
+ l2_file->win.curfdr.update = data;
+ break;
+
+ case L2F_TIFILES:
+ break;
+ }
+}
+
+#ifdef UNUSED_FUNCTION
+static void current_date_time(ti99_date_time *reply)
+{
+ /* All these functions should be ANSI */
+ time_t cur_time = time(NULL);
+ struct tm expanded_time = *localtime(& cur_time);
+
+ reply->time_MSB = (expanded_time.tm_hour << 3) | (expanded_time.tm_min >> 3);
+ reply->time_LSB = (expanded_time.tm_min << 5) | (expanded_time.tm_sec >> 1);
+ reply->date_MSB = ((expanded_time.tm_year % 100) << 1) | ((expanded_time.tm_mon+1) >> 3);
+ reply->date_LSB = ((expanded_time.tm_mon+1) << 5) | expanded_time.tm_mday;
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark LEVEL 3 DISK ROUTINES
+#endif
+
+/*
+ Level 3 implements files as a succession of logical records.
+
+ There are three types of files:
+ * program files that are not implemented at level 3 (no logical record)
+ * files with fixed-size records (random-access files)
+ * files with variable-size records (sequential-access)
+*/
+
+struct ti99_lvl3_fileref
+{
+ ti99_lvl2_fileref l2_file;
+
+ int cur_log_rec;
+ int cur_phys_rec;
+ int cur_pos_in_phys_rec;
+};
+
+#ifdef UNUSED_FUNCTION
+/*
+ Open a file on level 3.
+
+ To open a file on level 3, you must open (or create) the file on level 2,
+ then pass the file reference to open_file_lvl3.
+*/
+static int open_file_lvl3(ti99_lvl3_fileref *l3_file)
+{
+ l3_file->cur_log_rec = 0;
+ l3_file->cur_phys_rec = 0;
+ l3_file->cur_pos_in_phys_rec = 0;
+
+ /*if ()*/
+
+ return 0;
+}
+
+/*
+ Test a file for EOF
+*/
+static int is_eof(ti99_lvl3_fileref *l3_file)
+{
+ int flags = get_file_flags(&l3_file->l2_file);
+ int fphysrecs = get_file_fphysrecs(&l3_file->l2_file);
+ int fdr_eof = get_file_eof(&l3_file->l2_file);
+
+ if (flags & fdr99_f_var)
+ {
+ return (l3_file->cur_phys_rec >= fphysrecs);
+ }
+ else
+ {
+ return ((l3_file->cur_phys_rec >= fphysrecs)
+ || ((l3_file->cur_phys_rec == (fphysrecs-1))
+ && (l3_file->cur_pos_in_phys_rec >= (fdr_eof ? fdr_eof : 256))));
+ }
+}
+
+/*
+ Read next record from a file
+*/
+static int read_next_record(ti99_lvl3_fileref *l3_file, void *dest, int *out_reclen)
+{
+ int errorcode;
+ UINT8 physrec_buf[256];
+ int reclen;
+ int flags = get_file_flags(&l3_file->l2_file);
+ int fphysrecs = get_file_fphysrecs(&l3_file->l2_file);
+ int fdr_eof = get_file_eof(&l3_file->l2_file);
+
+ if (flags & fdr99_f_program)
+ {
+ /* program files have no level-3 record */
+ return IMGTOOLERR_UNEXPECTED;
+ }
+ else if (flags & fdr99_f_var)
+ {
+ /* variable-length records */
+ if (is_eof(l3_file))
+ return IMGTOOLERR_UNEXPECTED;
+ errorcode = read_file_physrec(&l3_file->l2_file, l3_file->cur_phys_rec, physrec_buf);
+ if (errorcode)
+ return errorcode;
+ /* read reclen */
+ reclen = physrec_buf[l3_file->cur_pos_in_phys_rec];
+ /* check integrity */
+ if ((reclen == 0xff) || (reclen > get_file_reclen(&l3_file->l2_file))
+ || ((l3_file->cur_pos_in_phys_rec + 1 + reclen) > 256))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ /* copy to buffer */
+ memcpy(dest, physrec_buf + l3_file->cur_pos_in_phys_rec + 1, reclen);
+ l3_file->cur_pos_in_phys_rec += reclen + 1;
+ /* skip to next physrec if needed */
+ if ((l3_file->cur_pos_in_phys_rec == 256)
+ || (physrec_buf[l3_file->cur_pos_in_phys_rec] == 0xff))
+ {
+ l3_file->cur_pos_in_phys_rec = 0;
+ l3_file->cur_phys_rec++;
+ }
+ (* out_reclen) = reclen;
+ }
+ else
+ {
+ /* fixed len records */
+ reclen = get_file_reclen(&l3_file->l2_file);
+ if (is_eof(l3_file))
+ return IMGTOOLERR_UNEXPECTED;
+ if ((l3_file->cur_pos_in_phys_rec + reclen) > 256)
+ {
+ l3_file->cur_pos_in_phys_rec = 0;
+ l3_file->cur_phys_rec++;
+ }
+ if ((l3_file->cur_phys_rec >= fphysrecs)
+ || ((l3_file->cur_phys_rec == (fphysrecs-1))
+ && ((l3_file->cur_pos_in_phys_rec + reclen) >= (fdr_eof ? fdr_eof : 256))))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ errorcode = read_file_physrec(&l3_file->l2_file, l3_file->cur_phys_rec, physrec_buf);
+ if (errorcode)
+ return errorcode;
+ memcpy(dest, physrec_buf + l3_file->cur_pos_in_phys_rec, reclen);
+ l3_file->cur_pos_in_phys_rec += reclen;
+ if (l3_file->cur_pos_in_phys_rec == 256)
+ {
+ l3_file->cur_pos_in_phys_rec = 0;
+ l3_file->cur_phys_rec++;
+ }
+ (* out_reclen) = reclen;
+ }
+
+ return 0;
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark IMGTOOL MODULE IMPLEMENTATION
+#endif
+
+/*
+ ti99 catalog iterator, used when imgtool reads the catalog
+*/
+struct dsk_iterator
+{
+ struct ti99_lvl2_imgref *image;
+ int level;
+ int listing_subdirs; /* true if we are listing subdirectories at current level */
+ int index[2]; /* current index in the disk catalog */
+ ti99_catalog *cur_catalog; /* current catalog */
+};
+
+struct win_iterator
+{
+ struct ti99_lvl2_imgref *image;
+ int level;
+ int listing_subdirs; /* true if we are listing subdirectories at current level */
+ int index[MAX_DIR_LEVEL]; /* current index in the disk catalog */
+ ti99_catalog catalog[MAX_DIR_LEVEL]; /* current catalog */
+};
+
+
+static imgtoolerr_t dsk_image_init_mess(imgtool_image *image, imgtool_stream *f);
+static imgtoolerr_t dsk_image_init_v9t9(imgtool_image *image, imgtool_stream *f);
+static imgtoolerr_t dsk_image_init_pc99_fm(imgtool_image *image, imgtool_stream *f);
+static imgtoolerr_t dsk_image_init_pc99_mfm(imgtool_image *image, imgtool_stream *f);
+static imgtoolerr_t win_image_init(imgtool_image *image, imgtool_stream *f);
+static void ti99_image_exit(imgtool_image *img);
+static void ti99_image_info(imgtool_image *img, char *string, size_t len);
+static imgtoolerr_t dsk_image_beginenum(imgtool_directory *enumeration, const char *path);
+static imgtoolerr_t dsk_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent);
+static imgtoolerr_t win_image_beginenum(imgtool_directory *enumeration, const char *path);
+static imgtoolerr_t win_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent);
+static imgtoolerr_t ti99_image_freespace(imgtool_partition *partition, UINT64 *size);
+static imgtoolerr_t ti99_image_readfile(imgtool_partition *partition, const char *fpath, const char *fork, imgtool_stream *destf);
+static imgtoolerr_t ti99_image_writefile(imgtool_partition *partition, const char *fpath, const char *fork, imgtool_stream *sourcef, option_resolution *writeoptions);
+static imgtoolerr_t dsk_image_deletefile(imgtool_partition *partition, const char *fpath);
+static imgtoolerr_t win_image_deletefile(imgtool_partition *partition, const char *fpath);
+static imgtoolerr_t dsk_image_create_mess(imgtool_image *image, imgtool_stream *f, option_resolution *createoptions);
+static imgtoolerr_t dsk_image_create_v9t9(imgtool_image *image, imgtool_stream *f, option_resolution *createoptions);
+
+enum
+{
+ dsk_createopts_volname = 'A',
+ dsk_createopts_sides = 'B',
+ dsk_createopts_tracks = 'C',
+ dsk_createopts_sectors = 'D',
+ dsk_createopts_protection = 'E',
+ dsk_createopts_density = 'F'
+};
+
+static OPTION_GUIDE_START( dsk_create_optionguide )
+ OPTION_STRING(dsk_createopts_volname, "label", "Volume name" )
+ OPTION_INT(dsk_createopts_sides, "sides", "Sides" )
+ OPTION_INT(dsk_createopts_tracks, "tracks", "Tracks" )
+ OPTION_INT(dsk_createopts_sectors, "sectors", "Sectors (1->9 for SD, 1->18 for DD, 1->36 for HD)" )
+ OPTION_INT(dsk_createopts_protection, "protection", "Protection (0 for normal, 1 for protected)" )
+ OPTION_ENUM_START(dsk_createopts_density, "density", "Density" )
+ OPTION_ENUM( 0, "Auto", "Auto" )
+ OPTION_ENUM( 1, "SD", "Single Density" )
+ OPTION_ENUM( 2, "DD", "Double Density" )
+ OPTION_ENUM( 3, "HD", "High Density" )
+ OPTION_ENUM_END
+OPTION_GUIDE_END
+
+#define dsk_create_optionspecs "B1-[2];C1-[40]-80;D1-[18]-36;E[0]-1;F[0]-3"
+
+static void ti99_getinfo(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(ti99_lvl2_imgref); break;
+ case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(dsk_iterator); break;
+
+ case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
+ case IMGTOOLINFO_PTR_CLOSE: info->close = ti99_image_exit; break;
+ case IMGTOOLINFO_PTR_INFO: info->info = ti99_image_info; break;
+ case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = ti99_image_freespace; break;
+ case IMGTOOLINFO_PTR_READ_FILE: info->read_file = ti99_image_readfile; break;
+ case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = ti99_image_writefile; break;
+ }
+}
+
+static void ti99_dsk_getinfo(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "dsk"); break;
+ case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = dsk_image_beginenum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = dsk_image_nextenum; break;
+ case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = dsk_image_deletefile; break;
+ default: ti99_getinfo(imgclass, state, info);
+ }
+}
+
+void ti99_old_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "ti99_old"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (old MESS format)"); break;
+ case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_mess; break;
+ case IMGTOOLINFO_PTR_CREATE: info->create = dsk_image_create_mess; break;
+ case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = dsk_create_optionguide; break;
+ case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), dsk_create_optionspecs); break;
+ default: ti99_dsk_getinfo(imgclass, state, info); break;
+ }
+}
+
+void ti99_v9t9_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "v9t9"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (V9T9 format)"); break;
+ case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_v9t9; break;
+ case IMGTOOLINFO_PTR_CREATE: info->create = dsk_image_create_v9t9; break;
+ case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = dsk_create_optionguide; break;
+ case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), dsk_create_optionspecs); break;
+ default: ti99_dsk_getinfo(imgclass, state, info); break;
+ }
+}
+
+void ti99_pc99fm_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "pc99fm"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (PC99 FM format)"); break;
+ case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_pc99_fm; break;
+ case IMGTOOLINFO_PTR_CREATE: /* info->create = dsk_image_create_pc99fm; */ break;
+ default: ti99_dsk_getinfo(imgclass, state, info); break;
+ }
+}
+
+void ti99_pc99mfm_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "pc99mfm"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (PC99 MFM format)"); break;
+ case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_pc99_mfm; break;
+ case IMGTOOLINFO_PTR_CREATE: /* info->create = dsk_image_create_pc99mfm; */ break;
+ default: ti99_dsk_getinfo(imgclass, state, info); break;
+ }
+}
+
+void ti99_ti99hd_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "ti99hd"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Harddisk"); break;
+ case IMGTOOLINFO_PTR_OPEN: info->open = win_image_init; break;
+ case IMGTOOLINFO_PTR_CREATE: /* info->create = hd_image_create; */ break;
+
+ case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "hd"); break;
+ case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = win_image_beginenum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = win_image_nextenum; break;
+ case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = win_image_deletefile; break;
+ default: ti99_getinfo(imgclass, state, info);
+ }
+}
+
+
+
+/*
+ Open a file as a ti99_image (common code).
+*/
+static int dsk_image_init(imgtool_image *img, imgtool_stream *f, ti99_img_format img_format)
+{
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ dsk_vib vib;
+ int reply;
+ int totphysrecs;
+ unsigned fdir_aphysrec;
+ int i;
+
+ /* open disk image at level 1 */
+ reply = open_image_lvl1(f, img_format, &image->l1_img, &vib);
+ if (reply)
+ return reply;
+
+ /* open disk image at level 2 */
+ image->type = L2I_DSK;
+
+ /* @BN@ */
+ /* Copy in the allocation bit map! */
+ memcpy(image->abm, vib.abm, 200);
+ /* first compute AU size and number of AUs */
+ totphysrecs = get_UINT16BE(vib.totphysrecs);
+
+ image->AUformat.physrecsperAU = (totphysrecs + 1599) / 1600;
+ /* round to next larger power of 2 */
+ for (i = 1; i < image->AUformat.physrecsperAU; i <<= 1)
+ ;
+ image->AUformat.physrecsperAU = i;
+ image->AUformat.totAUs = totphysrecs / image->AUformat.physrecsperAU;
+
+ /* extract number of physrecs */
+ image->dsk.totphysrecs = get_UINT16BE(vib.totphysrecs);
+
+ /* read and check main volume catalog */
+ reply = dsk_read_catalog(image, 1, &image->dsk.catalogs[0]);
+ if (reply)
+ return reply;
+
+ image->dsk.fdir_aphysrec[0] = 1;
+
+ /* read and check subdirectory catalogs */
+ /* Note that the reserved areas used for HFDC subdirs may be used for other
+ purposes by other FDRs, so, if we get any error, we will assume there is no
+ subdir after all... */
+ image->dsk.catalogs[0].num_subdirs = 0;
+ for (i=0; i<3; i++)
+ {
+ fdir_aphysrec = get_UINT16BE(vib.subdir[i].fdir_aphysrec);
+ if ((! memcmp(vib.subdir[i].name, "\0\0\0\0\0\0\0\0\0\0", 10))
+ || (! memcmp(vib.subdir[i].name, " ", 10)))
+ {
+ /* name is empty: fine with us unless there is a fdir pointer */
+ if (fdir_aphysrec != 0)
+ {
+ image->dsk.catalogs[0].num_subdirs = 0;
+ break;
+ }
+ }
+ else if (check_fname(vib.subdir[i].name))
+ {
+ /* name is invalid: this is not an HFDC format floppy */
+ image->dsk.catalogs[0].num_subdirs = 0;
+ break;
+ }
+ else
+ {
+ /* there is a non-empty name */
+ if ((fdir_aphysrec == 0) || (fdir_aphysrec >= totphysrecs))
+ {
+ /* error: fdir pointer is invalid or NULL */
+ image->dsk.catalogs[0].num_subdirs = 0;
+ break;
+ }
+ /* fill in descriptor fields */
+ image->dsk.fdir_aphysrec[image->dsk.catalogs[0].num_subdirs+1] = fdir_aphysrec;
+ /*image->dsk.catalogs[0].subdirs[image->dsk.catalogs[0].num_subdirs].dir_ptr = fdir_aphysrec;*/
+ memcpy(image->dsk.catalogs[0].subdirs[image->dsk.catalogs[0].num_subdirs].name, vib.subdir[i].name, 10);
+ reply = dsk_read_catalog(image, fdir_aphysrec, &image->dsk.catalogs[image->dsk.catalogs[0].num_subdirs+1]);
+ if (reply)
+ {
+ /* error: invalid fdir */
+ image->dsk.catalogs[0].num_subdirs = 0;
+ break;
+ }
+ /* found valid subdirectory: increment subdir count */
+ image->dsk.catalogs[0].num_subdirs++;
+ }
+ }
+
+ /* extract volume name */
+ memcpy(image->vol_name, vib.name, 10);
+
+ /* initialize default data_offset */
+ image->data_offset = 32+2;
+
+ return 0;
+}
+
+/*
+ Open a file as a ti99_image (MESS format).
+*/
+static imgtoolerr_t dsk_image_init_mess(imgtool_image *image, imgtool_stream *f)
+{
+ return (imgtoolerr_t)dsk_image_init(image, f, if_mess);
+}
+
+/*
+ Open a file as a ti99_image (V9T9 format).
+*/
+static imgtoolerr_t dsk_image_init_v9t9(imgtool_image *image, imgtool_stream *f)
+{
+ return (imgtoolerr_t)dsk_image_init(image, f, if_v9t9);
+}
+
+/*
+ Open a file as a ti99_image (PC99 FM format).
+*/
+static imgtoolerr_t dsk_image_init_pc99_fm(imgtool_image *image, imgtool_stream *f)
+{
+ return (imgtoolerr_t)dsk_image_init(image, f, if_pc99_fm);
+}
+
+/*
+ Open a file as a ti99_image (PC99 MFM format).
+*/
+static imgtoolerr_t dsk_image_init_pc99_mfm(imgtool_image *image, imgtool_stream *f)
+{
+ return (imgtoolerr_t)dsk_image_init(image, f, if_pc99_mfm);
+}
+
+/*
+ Open a file as a ti99_image (harddisk format).
+*/
+static imgtoolerr_t win_image_init(imgtool_image *img, imgtool_stream *f)
+{
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ win_vib_ddr vib;
+ int reply;
+ int i;
+
+ /* open disk image at level 1 */
+ reply = open_image_lvl1(f, if_harddisk, & image->l1_img, NULL);
+ if (reply)
+ return (imgtoolerr_t)reply;
+
+ /* open disk image at level 2 */
+ image->type = L2I_WIN;
+
+ /* read VIB */
+ reply = read_absolute_physrec(&image->l1_img, 0, &vib);
+ if (reply)
+ return (imgtoolerr_t)reply;
+
+ /* guess VIB version */
+ image->win.vib_version = memcmp(vib.u.vib_v1.id, "WIN", 3) ? win_vib_v2 : win_vib_v1;
+
+ /* extract AU size and number of AUs */
+ image->AUformat.physrecsperAU = ((get_UINT16BE(vib.params) >> 12) & 0xf) + 1;
+ image->AUformat.totAUs = get_UINT16BE(vib.totAUs);
+
+ /* extract volume name */
+ memcpy(image->vol_name, vib.name, 10);
+
+ /* extract data_offset */
+ switch (image->win.vib_version)
+ {
+ case win_vib_v1:
+ image->data_offset = 64;
+ break;
+
+ case win_vib_v2:
+ image->data_offset = vib.u.vib_v2.res_AUs * 64;
+ break;
+ }
+
+ /* read allocation bitmap (aphysrecs 1 through n, n<=33) */
+ for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
+ {
+ reply = read_absolute_physrec(&image->l1_img, i+1, image->abm+i*256);
+ if (reply)
+ return (imgtoolerr_t)reply;
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ close a ti99_image
+*/
+static void ti99_image_exit(imgtool_image *img)
+{
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+
+ close_image_lvl1(&image->l1_img);
+}
+
+/*
+ get basic information on a ti99_image
+
+ Currently returns the volume name
+*/
+static void ti99_image_info(imgtool_image *img, char *string, size_t len)
+{
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ char vol_name[11];
+
+ fname_to_str(vol_name, image->vol_name, 11);
+
+ snprintf(string, len, "%s", vol_name);
+}
+
+/*
+ Open the disk catalog for enumeration
+*/
+static imgtoolerr_t dsk_image_beginenum(imgtool_directory *enumeration, const char *path)
+{
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(imgtool_directory_image(enumeration));
+ dsk_iterator *iter = (dsk_iterator *) imgtool_directory_extrabytes(enumeration);
+
+ iter->image = image;
+ iter->level = 0;
+ iter->listing_subdirs = 1;
+ iter->index[0] = 0;
+ iter->cur_catalog = &iter->image->dsk.catalogs[0];
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Enumerate disk catalog next entry
+*/
+static imgtoolerr_t dsk_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ dsk_iterator *iter = (dsk_iterator*) imgtool_directory_extrabytes(enumeration);
+ dsk_fdr fdr;
+ int reply;
+ unsigned fdr_aphysrec;
+
+
+ ent->corrupt = 0;
+ ent->eof = 0;
+
+ /* iterate through catalogs to next file or dir entry */
+ while ((iter->level >= 0)
+ && (iter->index[iter->level] >= (iter->listing_subdirs
+ ? iter->cur_catalog->num_subdirs
+ : iter->cur_catalog->num_files)))
+ {
+ if (iter->listing_subdirs)
+ {
+ iter->listing_subdirs = 0;
+ iter->index[iter->level] = 0;
+ }
+ else
+ {
+ iter->listing_subdirs = 1;
+ if (! iter->level)
+ iter->level = -1;
+ else
+ {
+ iter->level = 0;
+ iter->index[0]++;
+ iter->cur_catalog = &iter->image->dsk.catalogs[0];
+ }
+ }
+ }
+
+ if (iter->level < 0)
+ {
+ ent->eof = 1;
+ }
+ else
+ {
+ if (iter->listing_subdirs)
+ {
+ fname_to_str(ent->filename, iter->image->dsk.catalogs[0].subdirs[iter->index[iter->level]].name, ARRAY_LENGTH(ent->filename));
+
+ /* set type of DIR */
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "DIR");
+
+ /* len in physrecs */
+ /* @BN@ return length in bytes */
+ /* ent->filesize = 1; */
+ ent->filesize = 256;
+
+ /* recurse subdirectory */
+ iter->listing_subdirs = 0; /* no need to list subdirs as only the
+ root dir has subdirs in DSK format */
+ iter->level = 1;
+ iter->cur_catalog = &iter->image->dsk.catalogs[iter->index[0]+1];
+ iter->index[iter->level] = 0;
+ }
+ else
+ {
+ fdr_aphysrec = iter->cur_catalog->files[iter->index[iter->level]].fdr_ptr;
+ reply = read_absolute_physrec(& iter->image->l1_img, fdr_aphysrec, & fdr);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+#if 0
+ fname_to_str(ent->filename, fdr.name, ARRAY_LENGTH(ent->filename));
+#else
+ {
+ char buf[11];
+
+ ent->filename[0] = '\0';
+ if (iter->level)
+ {
+ fname_to_str(ent->filename, iter->image->dsk.catalogs[0].subdirs[iter->index[0]].name, ARRAY_LENGTH(ent->filename));
+ strncat(ent->filename, ".", ARRAY_LENGTH(ent->filename) - 1);
+ }
+ fname_to_str(buf, fdr.name, 11);
+ strncat(ent->filename, buf, ARRAY_LENGTH(ent->filename) - 1);
+ }
+#endif
+ /* parse flags */
+ if (fdr.flags & fdr99_f_program)
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "PGM%s",
+ (fdr.flags & fdr99_f_wp) ? " R/O" : "");
+ else
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "%c/%c %d%s",
+ (fdr.flags & fdr99_f_int) ? 'I' : 'D',
+ (fdr.flags & fdr99_f_var) ? 'V' : 'F',
+ fdr.reclen,
+ (fdr.flags & fdr99_f_wp) ? " R/O" : "");
+ /* len in physrecs */
+ /* @BN@ return length in bytes */
+ /* ent->filesize = get_UINT16BE(fdr.fphysrecs); */
+ ent->filesize = (get_UINT16BE(fdr.fphysrecs)+1)*256;
+
+ iter->index[iter->level]++;
+ }
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Open the disk catalog for enumeration
+*/
+static imgtoolerr_t win_image_beginenum(imgtool_directory *enumeration, const char *path)
+{
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(imgtool_directory_image(enumeration));
+ win_iterator *iter = (win_iterator *) imgtool_directory_extrabytes(enumeration);
+ imgtoolerr_t errorcode;
+
+ iter->image = image;
+ iter->level = 0;
+ iter->listing_subdirs = 1;
+ iter->index[0] = 0;
+ errorcode = (imgtoolerr_t)win_read_catalog(image, 0, &iter->catalog[0]);
+ if (errorcode)
+ return errorcode;
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Enumerate disk catalog next entry
+*/
+static imgtoolerr_t win_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ win_iterator *iter = (win_iterator *) imgtool_directory_extrabytes(enumeration);
+ unsigned fdr_aphysrec;
+ win_fdr fdr;
+ int reply;
+ int i;
+
+
+ ent->corrupt = 0;
+ ent->eof = 0;
+
+ /* iterate through catalogs to next file or dir entry */
+ while ((iter->level >= 0)
+ && (iter->index[iter->level] >= (iter->listing_subdirs
+ ? iter->catalog[iter->level].num_subdirs
+ : iter->catalog[iter->level].num_files)))
+ {
+ if (iter->listing_subdirs)
+ {
+ iter->listing_subdirs = 0;
+ iter->index[iter->level] = 0;
+ }
+ else
+ {
+ iter->listing_subdirs = 1;
+ iter->level--;
+ iter->index[iter->level]++;
+ }
+ }
+
+ if (iter->level < 0)
+ {
+ ent->eof = 1;
+ }
+ else
+ {
+ if (iter->listing_subdirs)
+ {
+#if 0
+ fname_to_str(ent->filename, iter->catalog[iter->level].subdirs[iter->index[iter->level]].name, ARRAY_LENGTH(ent->filename));
+#else
+ {
+ char buf[11];
+
+ ent->filename[0] = '\0';
+ for (i=0; i<iter->level; i++)
+ {
+ fname_to_str(buf, iter->catalog[i].subdirs[iter->index[i]].name, 11);
+ strncat(ent->filename, buf, ARRAY_LENGTH(ent->filename) - 1);
+ strncat(ent->filename, ".", ARRAY_LENGTH(ent->filename) - 1);
+ }
+ fname_to_str(buf, iter->catalog[iter->level].subdirs[iter->index[iter->level]].name, 11);
+ strncat(ent->filename, buf, ARRAY_LENGTH(ent->filename) - 1);
+ }
+#endif
+
+ /* set type of DIR */
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "DIR");
+
+ /* len in physrecs */
+ /* @BN@ return length in bytes */
+ /* ent->filesize = 2; */
+ ent->filesize = 512;
+
+ /* recurse subdirectory */
+ /*iter->listing_subdirs = 1;*/
+ iter->level++;
+ iter->index[iter->level] = 0;
+ reply = win_read_catalog(iter->image, iter->catalog[iter->level-1].subdirs[iter->index[iter->level-1]].dir_ptr, &iter->catalog[iter->level]);
+ if (reply)
+ {
+ ent->corrupt = 1;
+ return (imgtoolerr_t)reply;
+ }
+ }
+ else
+ {
+ fdr_aphysrec = iter->catalog[iter->level].files[iter->index[iter->level]].fdr_ptr*iter->image->AUformat.physrecsperAU;
+ reply = read_absolute_physrec(& iter->image->l1_img, fdr_aphysrec, & fdr);
+ if (reply)
+ return IMGTOOLERR_READERROR;
+#if 0
+ fname_to_str(ent->filename, iter->catalog[iter->level].files[iter->index[iter->level]].name, ARRAY_LENGTH(ent->filename));
+#else
+ {
+ char buf[11];
+
+ ent->filename[0] = '\0';
+ for (i=0; i<iter->level; i++)
+ {
+ fname_to_str(buf, iter->catalog[i].subdirs[iter->index[i]].name, 11);
+ strncat(ent->filename, buf, ARRAY_LENGTH(ent->filename) - 1);
+ strncat(ent->filename, ".", ARRAY_LENGTH(ent->filename) - 1);
+ }
+ fname_to_str(buf, iter->catalog[iter->level].files[iter->index[iter->level]].name, 11);
+ strncat(ent->filename, buf, ARRAY_LENGTH(ent->filename) - 1);
+ }
+#endif
+ /* parse flags */
+ if (fdr.flags & fdr99_f_program)
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "PGM%s",
+ (fdr.flags & fdr99_f_wp) ? " R/O" : "");
+ else
+ snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "%c/%c %d%s",
+ (fdr.flags & fdr99_f_int) ? 'I' : 'D',
+ (fdr.flags & fdr99_f_var) ? 'V' : 'F',
+ fdr.reclen,
+ (fdr.flags & fdr99_f_wp) ? " R/O" : "");
+ /* len in physrecs */
+ /* @BN@ return length in bytes */
+ /* ent->filesize = get_win_fdr_fphysrecs(&fdr); */
+ ent->filesize = (get_win_fdr_fphysrecs(&fdr)+1)*256;
+
+ iter->index[iter->level]++;
+ }
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Compute free space on disk image (in AUs)
+*/
+static imgtoolerr_t ti99_image_freespace(imgtool_partition *partition, UINT64 *size)
+{
+ imgtool_image *img = imgtool_partition_image(partition);
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ size_t freeAUs;
+ int i;
+
+ freeAUs = 0;
+ for (i=0; i<image->AUformat.totAUs; i++)
+ {
+ if (! (image->abm[i >> 3] & (1 << (i & 7))))
+ freeAUs++;
+ }
+
+ /* @BN@ return free space in bytes */
+ /* *size = freeAUs; */
+ *size = freeAUs*256;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ Extract a file from a ti99_image. The file is saved in tifile format.
+*/
+static imgtoolerr_t ti99_image_readfile(imgtool_partition *partition, const char *fpath, const char *fork, imgtool_stream *destf)
+{
+ imgtool_image *img = imgtool_partition_image(partition);
+#if 1
+
+ /* extract data as TIFILES */
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ ti99_lvl2_fileref src_file;
+ ti99_lvl2_fileref dst_file;
+ ti99_date_time date_time;
+ int fphysrecs;
+ int i;
+ UINT8 buf[256];
+ imgtoolerr_t errorcode;
+
+
+ if (check_fpath(fpath))
+ return IMGTOOLERR_BADFILENAME;
+
+ /* open file on TI image */
+ switch (image->type)
+ {
+ case L2I_DSK:
+ errorcode = (imgtoolerr_t)open_file_lvl2_dsk(image, fpath, &src_file);
+ break;
+
+ case L2I_WIN:
+ errorcode = (imgtoolerr_t)open_file_lvl2_win(image, fpath, &src_file);
+ break;
+
+ default:
+ errorcode = (imgtoolerr_t)-1;
+ break;
+ }
+ if (errorcode)
+ return errorcode;
+
+ errorcode = (imgtoolerr_t)new_file_lvl2_tifiles(destf, &dst_file);
+ if (errorcode)
+ return errorcode;
+
+ /* write TIFILE header */
+ /* set up parameters */
+ set_file_flags(&dst_file, get_file_flags(&src_file));
+ set_file_recsperphysrec(&dst_file, get_file_recsperphysrec(&src_file));
+ errorcode = (imgtoolerr_t)set_file_fphysrecs(&dst_file, get_file_fphysrecs(&src_file));
+ if (errorcode)
+ return errorcode;
+ set_file_eof(&dst_file, get_file_eof(&src_file));
+ errorcode = (imgtoolerr_t)set_file_reclen(&dst_file, get_file_reclen(&src_file));
+ if (errorcode)
+ return errorcode;
+ errorcode = (imgtoolerr_t)set_file_fixrecs(&dst_file, get_file_fixrecs(&src_file));
+ if (errorcode)
+ return errorcode;
+
+ get_file_creation_date(&src_file, &date_time);
+#if 0
+ if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
+ || (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
+ current_date_time(&date_time);
+#endif
+ set_file_creation_date(&dst_file, date_time);
+
+ get_file_update_date(&src_file, &date_time);
+#if 0
+ if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
+ || (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
+ current_date_time(&date_time);
+#endif
+ set_file_update_date(&dst_file, date_time);
+
+ if (stream_write(destf, & dst_file.tifiles.hdr, 128) != 128)
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+
+ /* copy data to TIFILE */
+ fphysrecs = get_file_fphysrecs(&src_file);
+
+ for (i=0; i<fphysrecs; i++)
+ {
+ errorcode = (imgtoolerr_t)read_file_physrec(& src_file, i, buf);
+ if (errorcode)
+ return errorcode;
+
+ errorcode = (imgtoolerr_t)write_file_physrec(& dst_file, i, buf);
+ if (errorcode)
+ return errorcode;
+ }
+
+ return (imgtoolerr_t)0;
+
+#else
+
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ ti99_lvl3_fileref src_file;
+ UINT8 buf[256];
+ int reclen;
+ char lineend = '\r';
+ int errorcode;
+
+
+ if (check_fpath(fpath))
+ return IMGTOOLERR_BADFILENAME;
+
+ /* open file on TI image */
+ switch (image->type)
+ {
+ case L2I_DSK:
+ errorcode = open_file_lvl2_dsk(image, fpath, &src_file.l2_file);
+ break;
+
+ case L2I_WIN:
+ errorcode = open_file_lvl2_win(image, fpath, &src_file.l2_file);
+ break;
+ }
+ if (errorcode)
+ return errorcode;
+
+ errorcode = open_file_lvl3(& src_file);
+ if (errorcode)
+ return errorcode;
+
+ /* write text data */
+ while (! is_eof(& src_file))
+ {
+ errorcode = read_next_record(& src_file, buf, & reclen);
+ if (errorcode)
+ return errorcode;
+ if (stream_write(destf, buf, reclen) != reclen)
+ return IMGTOOLERR_WRITEERROR;
+ if (stream_write(destf, &lineend, 1) != 1)
+ return IMGTOOLERR_WRITEERROR;
+ }
+
+ return 0;
+
+#endif
+}
+
+/*
+ Add a file to a ti99_image. The file must be in tifile format.
+*/
+static imgtoolerr_t ti99_image_writefile(imgtool_partition *partition, const char *fpath, const char *fork, imgtool_stream *sourcef, option_resolution *writeoptions)
+{
+ imgtool_image *img = imgtool_partition_image(partition);
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ const char *filename;
+ char ti_fname[10];
+ ti99_lvl2_fileref src_file;
+ ti99_lvl2_fileref dst_file;
+ ti99_date_time date_time;
+ int i;
+ int fphysrecs;
+ UINT8 buf[256];
+ imgtoolerr_t errorcode;
+ int parent_ref_valid, parent_ref = 0;
+ ti99_catalog *catalog, catalog_buf = {0, };
+
+
+ (void) writeoptions;
+
+ if (check_fpath(fpath))
+ return IMGTOOLERR_BADFILENAME;
+
+ switch (image->type)
+ {
+ case L2I_DSK:
+ errorcode = (imgtoolerr_t)dsk_find_catalog_entry(image, fpath, &parent_ref_valid, &parent_ref, NULL, NULL);
+ if (errorcode == 0)
+ /* file already exists: causes an error for now */
+ return (imgtoolerr_t)IMGTOOLERR_UNEXPECTED;
+ else if ((! parent_ref_valid) || (errorcode != IMGTOOLERR_FILENOTFOUND))
+ return (imgtoolerr_t)errorcode;
+ break;
+
+ case L2I_WIN:
+ errorcode = (imgtoolerr_t)win_find_catalog_entry(image, fpath, &parent_ref_valid, &parent_ref, &catalog_buf, NULL, NULL);
+ if (errorcode == 0)
+ /* file already exists: causes an error for now */
+ return (imgtoolerr_t)IMGTOOLERR_UNEXPECTED;
+ else if ((! parent_ref_valid) || (errorcode != IMGTOOLERR_FILENOTFOUND))
+ return (imgtoolerr_t)errorcode;
+ break;
+ }
+
+ errorcode =(imgtoolerr_t) open_file_lvl2_tifiles(sourcef, & src_file);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+
+ /* create new file */
+ filename = strrchr(fpath, '.');
+ if (filename)
+ filename++;
+ else
+ filename = fpath;
+ str_to_fname(ti_fname, filename);
+ switch (image->type)
+ {
+ case L2I_DSK:
+ errorcode = (imgtoolerr_t)new_file_lvl2_dsk(image, parent_ref, ti_fname, &dst_file);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ break;
+
+ case L2I_WIN:
+ errorcode = (imgtoolerr_t)new_file_lvl2_win(image, &catalog_buf, ti_fname, &dst_file);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ break;
+ }
+
+ /* set up parameters */
+ set_file_flags(&dst_file, get_file_flags(&src_file));
+ set_file_recsperphysrec(&dst_file, get_file_recsperphysrec(&src_file));
+ errorcode = (imgtoolerr_t)set_file_fphysrecs(&dst_file, /*get_file_fphysrecs(&src_file)*/0);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ set_file_eof(&dst_file, get_file_eof(&src_file));
+ errorcode = (imgtoolerr_t)set_file_reclen(&dst_file, get_file_reclen(&src_file));
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ errorcode = (imgtoolerr_t)set_file_fixrecs(&dst_file, get_file_fixrecs(&src_file));
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+
+ get_file_creation_date(&src_file, &date_time);
+#if 0
+ if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
+ || (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
+ current_date_time(&date_time);
+#endif
+ set_file_creation_date(&dst_file, date_time);
+
+ get_file_update_date(&src_file, &date_time);
+#if 0
+ if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
+ || (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
+ current_date_time(&date_time);
+#endif
+ set_file_update_date(&dst_file, date_time);
+
+ /* alloc data physrecs */
+ fphysrecs = get_file_fphysrecs(&src_file);
+ switch (dst_file.type)
+ {
+ case L2F_DSK:
+ errorcode = (imgtoolerr_t)dsk_alloc_file_physrecs(&dst_file.dsk, fphysrecs);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ break;
+
+ case L2F_WIN:
+ errorcode = (imgtoolerr_t)win_alloc_file_physrecs(&dst_file.win, fphysrecs);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ break;
+
+ case L2F_TIFILES:
+ break;
+ }
+
+ /* copy data */
+ for (i=0; i<fphysrecs; i++)
+ {
+ if (stream_read(sourcef, buf, 256) != 256)
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+
+ errorcode = (imgtoolerr_t)write_file_physrec(& dst_file, i, buf);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+ }
+
+ /* write fdr */
+ switch (image->type)
+ {
+ case L2I_DSK:
+ if (write_absolute_physrec(& image->l1_img, dst_file.dsk.fdr_aphysrec, &dst_file.dsk.fdr))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ break;
+
+ case L2I_WIN:
+ /* save fphysrecs field as well */
+ if (dst_file.win.curfdr_aphysrec == dst_file.win.eldestfdr_aphysrec)
+ set_win_fdr_fphysrecs(&dst_file.win.curfdr, dst_file.win.fphysrecs);
+ if (write_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ if (dst_file.win.curfdr_aphysrec != dst_file.win.eldestfdr_aphysrec)
+ {
+ dst_file.win.curfdr_aphysrec = dst_file.win.eldestfdr_aphysrec;
+ if (read_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ set_win_fdr_fphysrecs(&dst_file.win.curfdr, dst_file.win.fphysrecs);
+ if (write_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ }
+ break;
+ }
+
+ /* update catalog */
+ switch (image->type)
+ {
+ case L2I_DSK:
+ catalog = &image->dsk.catalogs[parent_ref];
+ for (i=0; i<128; i++)
+ {
+ buf[2*i] = catalog->files[i].fdr_ptr >> 8;
+ buf[2*i+1] = catalog->files[i].fdr_ptr & 0xff;
+ }
+ if (write_absolute_physrec(& image->l1_img, image->dsk.fdir_aphysrec[parent_ref], buf))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ break;
+
+ case L2I_WIN:
+ {
+ win_vib_ddr parent_ddr;
+ int fdir_AU;
+
+ /* update VIB/DDR and get FDIR AU address */
+ if (read_absolute_physrec(& image->l1_img, parent_ref * image->AUformat.physrecsperAU, &parent_ddr))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+ parent_ddr.num_files = catalog_buf.num_files;
+ if (write_absolute_physrec(& image->l1_img, parent_ref * image->AUformat.physrecsperAU, &parent_ddr))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ fdir_AU = get_UINT16BE(parent_ddr.fdir_AU);
+
+ /* generate FDIR and save it */
+ for (i=0; i<127; i++)
+ {
+ buf[2*i] = catalog_buf.files[i].fdr_ptr >> 8;
+ buf[2*i+1] = catalog_buf.files[i].fdr_ptr & 0xff;
+ }
+ buf[254] = parent_ref >> 8;
+ buf[255] = parent_ref & 0xff;
+ if (write_absolute_physrec(& image->l1_img, fdir_AU * image->AUformat.physrecsperAU, buf))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ }
+ break;
+ }
+
+ /* update bitmap */
+ switch (image->type)
+ {
+ case L2I_DSK:
+ {
+ dsk_vib vib;
+
+ if (read_absolute_physrec(& image->l1_img, 0, &vib))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+ memcpy(vib.abm, image->abm, 200);
+ if (write_absolute_physrec(& image->l1_img, 0, &vib))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ }
+ break;
+
+ case L2I_WIN:
+ /* save allocation bitmap (aphysrecs 1 through n, n<=33) */
+ for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
+ if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ break;
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Delete a file from a ti99_image.
+*/
+static imgtoolerr_t dsk_image_deletefile(imgtool_partition *partition, const char *fpath)
+{
+ imgtool_image *img = imgtool_partition_image(partition);
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ dsk_fdr fdr;
+ int i, cluster_index;
+ unsigned cur_AU, cluster_lastfphysrec;
+// int fphysrecs;
+ int parent_ref, is_dir, catalog_index;
+ imgtoolerr_t errorcode;
+ UINT8 buf[256];
+ ti99_catalog *catalog;
+
+
+ if (check_fpath(fpath))
+ return IMGTOOLERR_BADFILENAME;
+
+ errorcode = (imgtoolerr_t)dsk_find_catalog_entry(image, fpath, NULL, &parent_ref, &is_dir, &catalog_index);
+ if (errorcode)
+ return errorcode;
+
+ if (is_dir)
+ {
+ catalog = &image->dsk.catalogs[catalog_index+1];
+
+ if ((catalog->num_files != 0) || (catalog->num_subdirs != 0))
+ return IMGTOOLERR_UNIMPLEMENTED;
+
+ catalog = &image->dsk.catalogs[0];
+
+ /* free fdir AU */
+ cur_AU = image->dsk.fdir_aphysrec[catalog_index+1] / image->AUformat.physrecsperAU;
+ image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
+
+ /* delete catalog entry */
+ for (i=catalog_index; i<2; i++)
+ {
+ catalog->subdirs[i] = catalog->subdirs[i+1];
+ image->dsk.fdir_aphysrec[i+1] = image->dsk.fdir_aphysrec[i+2];
+ }
+ memset(catalog->subdirs[2].name, 0, 10);
+ catalog->subdirs[2].dir_ptr = 0;
+ image->dsk.fdir_aphysrec[3] = 0;
+ catalog->num_subdirs--;
+
+ /* update directory and bitmap in vib */
+ {
+ dsk_vib vib;
+
+ if (read_absolute_physrec(& image->l1_img, 0, &vib))
+ return IMGTOOLERR_READERROR;
+
+ for (i=0; i<3; i++)
+ {
+ memcpy(vib.subdir[i].name, catalog->subdirs[i].name, 10);
+ set_UINT16BE(&vib.subdir[i].fdir_aphysrec, image->dsk.fdir_aphysrec[i+1]);
+ }
+
+ memcpy(vib.abm, image->abm, 200);
+
+ if (write_absolute_physrec(& image->l1_img, 0, &vib))
+ return IMGTOOLERR_WRITEERROR;
+ }
+ }
+ else
+ {
+ catalog = &image->dsk.catalogs[parent_ref];
+
+ if (read_absolute_physrec(& image->l1_img, catalog->files[catalog_index].fdr_ptr, &fdr))
+ return IMGTOOLERR_READERROR;
+
+ /* free data AUs */
+// fphysrecs =
+ get_UINT16BE(fdr.fphysrecs);
+
+ i = 0;
+ cluster_index = 0;
+ while (cluster_index < 76)
+ {
+ cur_AU = get_dsk_fdr_cluster_baseAU(image, & fdr, cluster_index);
+ if (cur_AU == 0)
+ /* end of cluster list */
+ break;
+ cluster_lastfphysrec = get_dsk_fdr_cluster_lastfphysrec(& fdr, cluster_index);
+
+ while (i<=cluster_lastfphysrec)
+ {
+ /* the condition below is an error, but it should not prevent
+ us from deleting the file */
+ if (cur_AU >= image->AUformat.totAUs)
+ /*return IMGTOOLERR_CORRUPTIMAGE;*/
+ break;
+
+ image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
+
+ i += image->AUformat.physrecsperAU;
+ cur_AU++;
+ }
+
+ cluster_index++;
+ }
+ /* the condition below is an error, but it should not prevent us from
+ deleting the file */
+#if 0
+ if (i<fphysrecs)
+ return IMGTOOLERR_CORRUPTIMAGE;
+#endif
+
+ /* free fdr AU */
+ cur_AU = catalog->files[catalog_index].fdr_ptr / image->AUformat.physrecsperAU;
+ image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
+
+ /* delete catalog entry */
+ for (i=catalog_index; i<127; i++)
+ catalog->files[i] = catalog->files[i+1];
+ catalog->files[127].fdr_ptr = 0;
+ catalog->num_files--;
+
+ /* update parent fdir */
+ for (i=0; i<128; i++)
+ {
+ buf[2*i] = catalog->files[i].fdr_ptr >> 8;
+ buf[2*i+1] = catalog->files[i].fdr_ptr & 0xff;
+ }
+ if (write_absolute_physrec(& image->l1_img, image->dsk.fdir_aphysrec[parent_ref], buf))
+ return IMGTOOLERR_WRITEERROR;
+
+ /* update bitmap */
+ {
+ dsk_vib vib;
+
+ if (read_absolute_physrec(& image->l1_img, 0, &vib))
+ return IMGTOOLERR_READERROR;
+ memcpy(vib.abm, image->abm, 200);
+ if (write_absolute_physrec(& image->l1_img, 0, &vib))
+ return IMGTOOLERR_WRITEERROR;
+ }
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+static imgtoolerr_t win_image_deletefile(imgtool_partition *partition, const char *fpath)
+{
+ imgtool_image *img = imgtool_partition_image(partition);
+ struct ti99_lvl2_imgref *image = (struct ti99_lvl2_imgref *) imgtool_image_extra_bytes(img);
+ int parent_ddr_AU, is_dir, catalog_index;
+ win_fdr fdr;
+ int i;
+ unsigned cur_AU, end_AU;
+ unsigned curfdr_aphysrec;
+ unsigned cursibFDR_index, endsibFDR_index = 0;
+ int errorcode;
+ UINT8 buf[256];
+ ti99_catalog catalog;
+
+
+ if (check_fpath(fpath))
+ return (imgtoolerr_t)IMGTOOLERR_BADFILENAME;
+
+ errorcode = win_find_catalog_entry(image, fpath, NULL, &parent_ddr_AU, &catalog, &is_dir, &catalog_index);
+ if (errorcode)
+ return (imgtoolerr_t)errorcode;
+
+ if (is_dir)
+ {
+ unsigned DDR_AU;
+ win_vib_ddr ddr_buf;
+
+ /* Read DDR record */
+ DDR_AU = catalog.subdirs[catalog_index].dir_ptr;
+ if (read_absolute_physrec(& image->l1_img, DDR_AU*image->AUformat.physrecsperAU, &ddr_buf))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+
+ /* read FDIR AU pointer */
+ cur_AU = get_UINT16BE(ddr_buf.fdir_AU);
+
+ /* sanity checks */
+ if ((ddr_buf.num_files > 127) || (ddr_buf.num_subdirs > 114) || (cur_AU > image->AUformat.totAUs))
+ return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
+
+ if ((ddr_buf.num_files != 0) || (ddr_buf.num_subdirs != 0))
+ return (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED;
+
+ /* free fdir AU */
+ image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
+
+ /* free ddr AU */
+ image->abm[DDR_AU >> 3] &= ~ (1 << (DDR_AU & 7));
+
+ /* delete parent catalog entry */
+ for (i=catalog_index; i<113; i++)
+ catalog.subdirs[i] = catalog.subdirs[i+1];
+ catalog.subdirs[113].dir_ptr = 0;
+ catalog.num_subdirs--;
+
+ /* update parent VIB/DDR */
+ if (read_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &ddr_buf))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+ ddr_buf.num_subdirs = catalog.num_subdirs;
+ for (i=0; i<114; i++)
+ set_UINT16BE(&ddr_buf.subdir_AU[i], catalog.subdirs[i].dir_ptr);
+ if (write_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &ddr_buf))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+
+ /* save allocation bitmap (aphysrecs 1 through n, n<=33) */
+ for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
+ if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ }
+ else
+ {
+ /* check integrity of FDR sibling chain, and go to last sibling */
+ /* note that as we check that the back chain is consistent with the forward
+ chain, we will also detect any cycle in the sibling chain, so we do not
+ need to check against them explicitely */
+ {
+ int pastendoflist_flag;
+ unsigned prevfdr_aphysrec;
+
+
+ pastendoflist_flag = 0;
+ cursibFDR_index = 0;
+ curfdr_aphysrec = catalog.files[catalog_index].fdr_ptr * image->AUformat.physrecsperAU;
+ if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+
+ if (get_UINT16BE(fdr.prevsibFDR_AU) != 0)
+ return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
+
+ while (1)
+ {
+ if (! pastendoflist_flag)
+ {
+ /* look for end of list */
+ for (i=0; i<54; i++)
+ {
+ if (get_UINT16BE(fdr.clusters[i][0]) == 0)
+ {
+ endsibFDR_index = cursibFDR_index;
+ pastendoflist_flag = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* exit loop if end of sibling chain */
+ if (! get_UINT16BE(fdr.nextsibFDR_AU))
+ break;
+
+ /* otherwise read next FDR */
+ if (get_UINT16BE(fdr.nextsibFDR_AU) >= image->AUformat.totAUs)
+ return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
+
+ prevfdr_aphysrec = curfdr_aphysrec;
+ curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(image, &fdr);
+ if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+ cursibFDR_index++;
+
+ /* check that back chaining is consistent with forward chaining */
+ if (get_win_fdr_prevsibFDR_aphysrec(image, &fdr) != prevfdr_aphysrec)
+ return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
+ }
+ if (! pastendoflist_flag)
+ endsibFDR_index = cursibFDR_index;
+ }
+
+
+ while (1)
+ {
+ if (cursibFDR_index <= endsibFDR_index)
+ {
+ for (i = 0; i < 54; i++)
+ {
+ cur_AU = get_UINT16BE(fdr.clusters[i][0]);
+ if (cur_AU == 0)
+ /* end of cluster list */
+ break;
+ end_AU = get_UINT16BE(fdr.clusters[i][1]);
+
+ while (cur_AU<=end_AU)
+ {
+ /* the condition below is an error, but it should not prevent
+ us from deleting the file */
+ if (cur_AU >= image->AUformat.totAUs)
+ /*return IMGTOOLERR_CORRUPTIMAGE;*/
+ break;
+
+ image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
+
+ cur_AU++;
+ }
+ }
+ }
+
+ /* free fdr AU if applicable */
+ if ((curfdr_aphysrec % image->AUformat.physrecsperAU) == 0)
+ {
+ cur_AU = curfdr_aphysrec / image->AUformat.physrecsperAU;
+ image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
+ }
+
+ if (cursibFDR_index == 0)
+ break;
+ curfdr_aphysrec = get_win_fdr_prevsibFDR_aphysrec(image, &fdr);
+ if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+ cursibFDR_index--;
+ }
+
+
+ /* delete catalog entry */
+ for (i=catalog_index; i<127; i++)
+ catalog.files[i] = catalog.files[i+1];
+ catalog.files[127].fdr_ptr = 0;
+ catalog.num_files--;
+
+
+ /* update VIB/DDR and get FDIR AU address */
+ {
+ win_vib_ddr parent_ddr;
+
+ if (read_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &parent_ddr))
+ return (imgtoolerr_t)IMGTOOLERR_READERROR;
+ parent_ddr.num_files = catalog.num_files;
+ if (write_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &parent_ddr))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ cur_AU = get_UINT16BE(parent_ddr.fdir_AU);
+ }
+
+ /* generate FDIR and save it */
+ for (i=0; i<127; i++)
+ {
+ buf[2*i] = catalog.files[i].fdr_ptr >> 8;
+ buf[2*i+1] = catalog.files[i].fdr_ptr & 0xff;
+ }
+ buf[254] = parent_ddr_AU >> 8;
+ buf[255] = parent_ddr_AU & 0xff;
+ if (write_absolute_physrec(& image->l1_img, cur_AU * image->AUformat.physrecsperAU, buf))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+
+ /* save allocation bitmap (aphysrecs 1 through n, n<=33) */
+ for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
+ if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256))
+ return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
+ }
+
+ return (imgtoolerr_t)0;
+}
+
+/*
+ Create a blank ti99_image (common code).
+
+ Supports MESS and V9T9 formats only
+*/
+static imgtoolerr_t dsk_image_create(imgtool_image *image, imgtool_stream *f, option_resolution *createoptions, ti99_img_format img_format)
+{
+ const char *volname;
+ int density;
+ ti99_lvl1_imgref l1_img;
+ int protected_var;
+
+ int totphysrecs, physrecsperAU, totAUs;
+
+ dsk_vib vib;
+ UINT8 empty_sec[256];
+
+ int i;
+
+ l1_img.img_format = img_format;
+ l1_img.file_handle = f;
+
+ /* read options */
+ volname = option_resolution_lookup_string(createoptions, dsk_createopts_volname);
+ l1_img.geometry.heads = option_resolution_lookup_int(createoptions, dsk_createopts_sides);
+ l1_img.geometry.cylinders = option_resolution_lookup_int(createoptions, dsk_createopts_tracks);
+ l1_img.geometry.secspertrack = option_resolution_lookup_int(createoptions, dsk_createopts_sectors);
+ protected_var = option_resolution_lookup_int(createoptions, dsk_createopts_protection);
+ density = option_resolution_lookup_int(createoptions, dsk_createopts_density);
+
+ /* NPW 03-DEC-2003 - Fixes a crash if volname is NULL */
+ if (!volname)
+ volname = "";
+
+ totphysrecs = l1_img.geometry.secspertrack * l1_img.geometry.cylinders * l1_img.geometry.heads;
+ physrecsperAU = (totphysrecs + 1599) / 1600;
+ /* round to next larger power of 2 */
+ for (i = 1; i < physrecsperAU; i <<= 1)
+ ;
+ physrecsperAU = i;
+ totAUs = totphysrecs / physrecsperAU;
+
+ /* auto-density */
+ if (density == 0)
+ {
+ if (l1_img.geometry.secspertrack <= 9)
+ density = 1;
+ else if (l1_img.geometry.secspertrack <= 18)
+ density = 2;
+ else
+ density = 3;
+ }
+
+ /* check volume name */
+ if (strchr(volname, '.') || strchr(volname, ' ') || (strlen(volname) > 10))
+ return /*IMGTOOLERR_BADFILENAME*/IMGTOOLERR_PARAMCORRUPT;
+
+ /* FM disks can hold fewer sector per track than MFM disks */
+ if ((density == 1) && (l1_img.geometry.secspertrack > 9))
+ return IMGTOOLERR_PARAMTOOLARGE;
+
+ /* DD disks can hold fewer sector per track than HD disks */
+ if ((density == 2) && (l1_img.geometry.secspertrack > 18))
+ return IMGTOOLERR_PARAMTOOLARGE;
+
+ /* check total disk size */
+ if (totphysrecs < 2)
+ return IMGTOOLERR_PARAMTOOSMALL;
+
+ /* initialize vib in aphysrec 0 */
+
+ /* set volume name */
+ str_to_fname(vib.name, volname);
+
+ /* set every header field */
+ set_UINT16BE(& vib.totphysrecs, totphysrecs);
+ vib.secspertrack = l1_img.geometry.secspertrack;
+ vib.id[0] = 'D';
+ vib.id[1] = 'S';
+ vib.id[2] = 'K';
+ vib.protection = protected_var ? 'P' : ' ';
+ vib.cylinders = l1_img.geometry.cylinders;
+ vib.heads = l1_img.geometry.heads;
+ vib.density = density;
+ for (i=0; i<3; i++)
+ {
+ memset(vib.subdir[i].name, '\0', 10);
+ set_UINT16BE(& vib.subdir[i].fdir_aphysrec, 0);
+ }
+
+ /* clear bitmap */
+ for (i=0; i < (totAUs>>3); i++)
+ vib.abm[i] = 0;
+
+ if (totAUs & 7)
+ {
+ vib.abm[i] = 0xff << (totAUs & 7);
+ i++;
+ }
+
+ for (; i < 200; i++)
+ vib.abm[i] = 0xff;
+
+ /* mark first 2 aphysrecs (vib and fdir) as used */
+ vib.abm[0] |= (physrecsperAU == 1) ? 3 : 1;
+
+ /* write aphysrec 0 */
+ if (write_absolute_physrec(& l1_img, 0, &vib))
+ return IMGTOOLERR_WRITEERROR;
+
+
+ /* now clear every other physrecs, including the FDIR record */
+ memset(empty_sec, 0, sizeof(empty_sec));
+
+ for (i=1; i<totphysrecs; i++)
+ if (write_absolute_physrec(& l1_img, i, empty_sec))
+ return IMGTOOLERR_WRITEERROR;
+
+
+ return (imgtoolerr_t)dsk_image_init(image, f, img_format);
+}
+
+/*
+ Create a blank ti99_image (MESS format).
+*/
+static imgtoolerr_t dsk_image_create_mess(imgtool_image *image, imgtool_stream *f, option_resolution *createoptions)
+{
+ return dsk_image_create(image, f, createoptions, if_mess);
+}
+
+/*
+ Create a blank ti99_image (V9T9 format).
+*/
+static imgtoolerr_t dsk_image_create_v9t9(imgtool_image *image, imgtool_stream *f, option_resolution *createoptions)
+{
+ return dsk_image_create(image, f, createoptions, if_v9t9);
+}