1 // license:BSD-3-Clause
2 // copyright-holders:Raphael Nabet
3 /*
4     Handlers for ti99 disk images
5 
6     We support the following types of image:
7     * floppy disks ("DSK format") in v9t9, pc99 and old mess image format
8     * hard disks ("WIN format") in MAME harddisk format (256-byte sectors only,
9       i.e. no SCSI)
10     Files are extracted in TIFILES format.  There is a compile-time option to
11     extract text files in flat format instead, but I need to re-implement it
12     properly (using filters comes to mind).
13 
14     Raphael Nabet, 2002-2004
15 
16     TODO:
17     - finish and test hd support ***test sibling FDR support***
18     - check allocation bitmap against corruption when an image is opened
19 */
20 
21 #include <climits>
22 #include <cstdio>
23 #include <cstring>
24 #include <cstdlib>
25 #include <ctime>
26 #include "imgtool.h"
27 #include "harddisk.h"
28 #include "imghd.h"
29 
30 /*
31     Concepts shared by both disk structures:
32     ----------------------------------------
33 
34     In both formats, the entire disk surface is split into physical records,
35     each 256-byte-long.  For best efficiency, the sector size is usually 256
36     bytes so that one sector is equivalent to a physical record (this is true
37     with floppies and HFDC hard disks).  If the sector size is not 256 bytes
38     (e.g. 512 bytes with SCSI hard disks, possibly 128 bytes on a TI-99
39     simulator running on TI-990), sectors are grouped or split to form 256-byte
40     physical records.  Physical records are numbered from 0, with the first
41     starting on sector 0 track 0.  To avoid confusion with physical records in
42     files, such physical records will be called "absolute physical records",
43     abbreviated as "absolute physrecs" or "aphysrecs".
44 
45     Disk space is allocated in units referred to as AUs.  Each AU represents an
46     integral number of 256-byte physical records.  The number of physrecs per
47     AU varies depending on the disk format and size.  AUs are numbered from 0,
48     with the first starting with absolute physrec 0.
49 
50     Absolute physrec 0 contains a 256-byte record with volume information,
51     which is called "Volume Information Block", abbreviated as "vib".
52 
53     To keep track of which areas on the disk are allocated and which are free,
54     disk managers maintain a bit map of allocated AUs (called "allocation
55     bitmap", abbreviated as "abm").  Each bit in the allocation bitmap
56     represents an AU: if a bit is 0, the AU is free; if a bit is 1, the AU is
57     allocated (or possibly bad if the disk manager can track bad sectors).
58     Where on the disk the abm is located depends on the disk structure (see
59     structure-specific description for details).
60 
61     Files are implemented as a succession of 256-byte physical records.  To
62     avoid confusion with absolute physical records, these physical records will
63     be called "file physical records", abbreviated as "file physrecs" or
64     "fphysrecs".
65 
66     Programs do normally not access file physical records directly.  They may
67     call high-level file routines that enable to create either fixed-length
68     logical records of any size from 1 through 255, or variable-length records
69     of any size from 0 through 254: logical records are grouped by file
70     managers into 256-byte physical records.  Some disk managers (HFDC and
71     SCSI) allow programs to create fixed-length records larger than 255 bytes,
72     too, but few programs use this possibility.  Additionally, program files
73     can be created: program files do not implement any logical record, and can
74     be seen as a flat byte stream, not unlike files under MSDOS, UNIX and the C
75     standard library.  (Unfortunately, the API for program files lacks
76     flexibility, and most programs that need to process a flat byte stream
77     will use fixed-size records of 128 bytes.)
78 
79         Fixed-length records (reclen < 256) are grouped like this:
80               fphysrec 0        fphysrec 1
81             _______________   _______________
82             |R0 |R1 |R2 |X|   |R3 |R4 |R5 |X|
83             ---------------   ---------------
84                         \_/               \_/
85                        unused            unused
86                   (size < reclen)   (size < reclen)
87 
88         Fixed-length records (reclen > 256) are grouped like this:
89               fphysrec 0         fphysrec 1         fphysrec 2          fphysrec 3      ...
90             _________________  _________________  _________________   _________________
91             |  R0 (#0-255)  |  | R0 (#256-511) |  |R0 (#512-end)|X|   |  R1 (#0-255)  |   ...
92             -----------------  -----------------  -----------------   -----------------
93                                                                 \_/
94                                                                unused
95 
96         Variable length records are grouped like this:
97                              fphysrec 0:
98             byte:
99                     ------------------------------
100                   0 |l0 = length of record 0 data|
101                     |----------------------------|
102                   1 |                            |
103                     |                            |
104                   : |       record 0 data        |
105                     |                            |
106                  l0 |                            |
107                     |----------------------------|
108                l0+1 |l1 = length of record 1 data|
109                     |----------------------------|
110                l0+2 |                            |
111                     |                            |
112                   : |       record 1 data        |
113                     |                            |
114             l0+l1+1 |                            |
115                     |----------------------------|
116                   : :                            :
117                   : :                            :
118                     |----------------------------|
119                   n |lm = length of record m data|
120                     |----------------------------|
121                 n+1 |                            |
122                     |                            |
123                   : |       record m data        |
124                     |                            |
125                n+lm |                            |
126                     |----------------------------|
127              n+lm+1 |>ff: EOR mark               |
128                     |----------------------------|
129              n+lm+2 |                            |
130                     |                            |
131                   : |           unused           |
132                   : |(we should have size < lm+1)|
133                     |                            |
134                 255 |                            |
135                     ------------------------------
136 
137                              fphysrec 1:
138             byte:
139                     ------------------------------
140                   0 |lm+1=length of record 0 data|
141                     |----------------------------|
142                   1 |                            |
143                     |                            |
144                   : |      record m+1 data       |
145                     |                            |
146                lm+1 |                            |
147                     |----------------------------|
148                   : :                            :
149                   : :                            :
150 
151             I think the EOR mark is not required if all 256 bytes of a physical
152             record are used by logical records.
153 
154 
155     All files are associated with a "file descriptor record" ("fdr") that holds
156     file information (name, format, length) and points to the data AUs. The WIN
157     disk structure also supports sibling FDRs, in case a file is so fragmented
158     that all the data pointers cannot fit in one FDR; the DSK disk structure
159     does not implement any such feature, and you may be unable to append data
160     to an existing file if it is too fragmented, even though the disk is not
161     full.
162 
163 
164     DSK disk structure:
165     -------------------
166 
167     The DSK disk structure is used for floppy disks, and a few RAMDISKs as
168     well.  It supports 1600 AUs and 16 MBytes at most (the 16 MByte value is a
169     theoretical maximum, as I doubt anyone has ever created so big a DSK volume).
170 
171     The disk sector size is always 256 bytes (the only potential exceptions are
172     the SCSI floppy disk units and the TI-99 simulator on TI-990, but I don't
173     know anything about either).
174 
175     For some reason, the number of physrecs per AU is always a power of 2.
176     Originally, AUs always were one physical record each.  However, since the
177     allocation bitmap cannot support more than 1600 AUs, disks larger than
178     400kbytes need to have several physical records per AU.  Note that
179     relatively few disk controllers implement AUs that span multiple physical
180     records (IIRC, these are Myarc's HFDC, SCSI DSR with floppy disk interface,
181     and some upgraded Myarc and Corcomp DD floppy controllers).
182 
183     The allocation bitmap is located in the VIB.  It is 200 bytes long, and
184     supports 1600 AUs at most.
185 
186     Directories are implemented with a "File Descriptor Index Record" (FDIR)
187     for each directory.  The FDIR is array of 0 through 128 words, containing
188     the aphysrec address of each fdr in the directory, sorted by ascending file
189     name, terminated with a 0.  Note that, while we should be prepared to read
190     images images with 128 entries, I think (not 100% sure) that we should
191     write no more than 127 for compatibility with some existing disk managers.
192 
193     Originally, the DSK structure only supported one directory level (i.e. the
194     root directory level).  The FDIR record for the root directory is always
195     located in aphysrec 1.  Moreover, Myarc extended the DSK structure to
196     support up to 3 subdirectories in the root directory (note that there is no
197     support for more subdirs, or for subdirs located in subdirs).  To do so,
198     they used an unused field of the VIB to hold the 10-char name of each
199     directory and the aphysrec address of the associated FDIR record.
200 
201     aphysrec 0: Volume Information Block (VIB): see below
202     aphysrec 1: FDIR for root directory
203     Remaining AUs are used for fdr and data (and subdirectory FDIR if
204     applicable).  There is one FDIR record per directory; the FDIR points to
205     the FDR for each file in the directory.  The FDR (File Descriptor Record)
206     contains the file information (name, format, length, pointers to data
207     physrecs/AUs, etc).
208 
209 
210     WIN disk structure:
211     -------------------
212 
213     The WIN disk structure is used for hard disks.  It supports 65535 AUs and
214     256 MBytes at most.
215 
216     The disk sector size is 256 bytes on HFDC, 512 bytes on SCSI.  512-byte
217     sectors are split into 256-byte physical records.
218 
219     We may have from 1 through 16 physrecs per AUs.  Since we cannot have more
220     than 65535 AUs, we need to have several physical records per AU in disks
221     larger than 16 Mbytes.
222 
223     Contrary to the DSK disk structure, the WIN disk structure supports
224     hierarchic subdirectories, with a limit of 114 subdirectories and 127 files
225     per directory.  Each directory is associated with both a "Directory
226     Descriptor Record" (DDR) and a "File Descriptor Index Record" (FDIR).  The
227     only exception is the root directory, which uses the VIB instead of a DDR
228     (the VIB includes all the important fields of the DDR).  The DDR contains
229     some directory info (name, number of files and subdirectories directly
230     enclosed in the directory, AU address of the FDIR of the parent directory,
231     etc), the AU address of the associated FDIR, and the AU address of the DDR
232     of up to 114 subdirectories, sorted by ascending directory name.  The WIN
233     FDIR is similar to, yet different from, the DSK FDIR: it contains up to 127
234     (vs. 128) AU address (vs. aphysrec address) of each fdr in the directory,
235     sorted by ascending file name.  Additionally, it includes the AU address of
236     the associated DDR.
237 
238     When a file has more than 54 data fragments, the disk manager creates
239     sibling FDRs that hold additional fragments.  (Sibling FDRs are called
240     offspring FDRs in most existing documentation, but I prefer "sibling FDRs"
241     as "offspring FDRs" wrongly suggests a directory-like hierarchy.
242     Incidentally, I cannot really figure out why they chose to duplicate the
243     FDR rather than create a file extension record, but there must be a
244     reason.)  Note that sibling FDRs usually fill unused physrecs in the AU
245     allocated for the eldest FDR, and a new AU is allocated for new sibling
246     FDRs only when the first AU is full.
247 
248     aphysrec 0: Volume Information Block (VIB): see below
249     aphysrec 1-n (where n = 1+SUP(number_of_AUs/2048)): Volume bitmap
250     Remaining AUs are used for ddr, fdir, fdr and data.
251 */
252 
253 /* Since string length is encoded with a byte, the maximum length of a string
254 is 255.  Since we need to add a device name (1 char minimum, typically 4 chars)
255 and a '.' separator, we chose a practical value of 250 (though few applications
256 will accept paths of such length). */
257 #define MAX_PATH_LEN 253
258 #define MAX_SAFE_PATH_LEN 250
259 
260 #define MAX_DIR_LEVEL 125   /* We need to put a recursion limit to avoid endless recursion hazard */
261 
262 
263 #if 0
264 #pragma mark MISCELLANEOUS UTILITIES
265 #endif
266 
267 /*
268     Miscellaneous utilities that are used to handle TI data types
269 */
270 
271 struct UINT16BE
272 {
273 	uint8_t bytes[2];
274 };
275 
276 struct UINT16LE
277 {
278 	uint8_t bytes[2];
279 };
280 
get_UINT16BE(UINT16BE word)281 static inline uint16_t get_UINT16BE(UINT16BE word)
282 {
283 	return (word.bytes[0] << 8) | word.bytes[1];
284 }
285 
set_UINT16BE(UINT16BE * word,uint16_t data)286 static inline void set_UINT16BE(UINT16BE *word, uint16_t data)
287 {
288 	word->bytes[0] = (data >> 8) & 0xff;
289 	word->bytes[1] = data & 0xff;
290 }
291 
get_UINT16LE(UINT16LE word)292 static inline uint16_t get_UINT16LE(UINT16LE word)
293 {
294 	return word.bytes[0] | (word.bytes[1] << 8);
295 }
296 
set_UINT16LE(UINT16LE * word,uint16_t data)297 static inline void set_UINT16LE(UINT16LE *word, uint16_t data)
298 {
299 	word->bytes[0] = data & 0xff;
300 	word->bytes[1] = (data >> 8) & 0xff;
301 }
302 
303 /*
304     Convert a C string to a 10-character file name (padded with spaces if necessary)
305 
306     dst (O): destination 10-character array
307     src (I): source C string
308 */
str_to_fname(char dst[10],const char * src)309 static void str_to_fname(char dst[10], const char *src)
310 {
311 	int i;
312 
313 
314 	i = 0;
315 
316 	/* copy 10 characters at most */
317 	if (src)
318 		while ((i<10) && (src[i]!='\0'))
319 		{
320 			dst[i] = src[i];
321 			i++;
322 		}
323 
324 	/* pad with spaces */
325 	while (i<10)
326 	{
327 		dst[i] = ' ';
328 		i++;
329 	}
330 }
331 
332 /*
333     Convert a 10-character file name to a C string (removing trailing spaces if necessary)
334 
335     dst (O): destination C string
336     src (I): source 10-character array
337     n (I): length of dst buffer (string may be truncated if less than 11)
338 */
fname_to_str(char * dst,const char src[10],int n)339 static void fname_to_str(char *dst, const char src[10], int n)
340 {
341 	int i;
342 	int last_nonspace;
343 
344 
345 	/* copy 10 characters at most */
346 	if (--n > 10)
347 		n = 10;
348 
349 	/* copy filename */
350 	i = 0;
351 	last_nonspace = -1;
352 
353 	while (i<n)
354 	{
355 		dst[i] = src[i];
356 		if (src[i] != ' ')
357 			last_nonspace = i;
358 		i++;
359 	}
360 
361 	/* terminate with '\0' */
362 	dst[last_nonspace+1] = '\0';
363 }
364 
365 /*
366     Check a 10-character file name.
367 
368     filename (I): 10-character array with file name
369 
370     Return non-zero if the file name is invalid.
371 */
check_fname(const char filename[10])372 static int check_fname(const char filename[10])
373 {
374 	int i;
375 	int space_found_flag;
376 
377 
378 	/* check and copy file name */
379 	space_found_flag = false;
380 	for (i=0; i<10; i++)
381 	{
382 		switch (filename[i])
383 		{
384 		case '.':
385 		case '\0':
386 			/* illegal characters */
387 			return 1;
388 		case ' ':
389 			/* illegal in a file name, but file names shorter than 10
390 			characters are padded with spaces */
391 			space_found_flag = true;
392 			break;
393 		default:
394 			/* all other characters are legal (though non-ASCII characters,
395 			control characters and even lower-case characters are not
396 			recommended) */
397 			if (space_found_flag)
398 				return 1;
399 			break;
400 		}
401 	}
402 
403 	return 0;
404 }
405 
406 /*
407     Check a file path.
408 
409     fpath (I): C string with file path
410 
411     Return non-zero if the file path is invalid.
412 */
check_fpath(const char * fpath)413 static int check_fpath(const char *fpath)
414 {
415 	const char *element_start, *element_end;
416 	int element_len;
417 
418 
419 	/* first check that the path is not too long and that there is no space
420 	character */
421 	if ((strlen(fpath) > MAX_PATH_LEN) || strchr(fpath, ' '))
422 		return 1;
423 
424 	/* check that each path element has an acceptable length */
425 	element_start = fpath;
426 	do
427 	{
428 		/* find next path element */
429 		element_end = strchr(element_start, '.');
430 		element_len = element_end ? (element_end - element_start) : strlen(element_start);
431 		if ((element_len > 10) || (element_len == 0))
432 			return 1;
433 
434 		/* iterate */
435 		if (element_end)
436 			element_start = element_end+1;
437 		else
438 			element_start = NULL;
439 	}
440 	while (element_start);
441 
442 	return 0;
443 }
444 
445 #if 0
446 #pragma mark -
447 #pragma mark LEVEL 1 DISK ROUTINES
448 #endif
449 
450 /*
451     Level 1 deals with accessing the disk hardware (in our case, the raw disk
452     image).
453 
454     Higher level routines will only know the disk as a succession of
455     256-byte-long physical records addressed by a linear address.
456 */
457 
458 /*
459     Disk geometry
460 */
461 struct ti99_geometry
462 {
463 	int secspertrack;
464 	int cylinders;
465 	int heads;
466 };
467 
468 /*
469     Physical sector address
470 */
471 struct ti99_sector_address
472 {
473 	int sector;
474 	int cylinder;
475 	int side;
476 };
477 
478 /*
479     Time stamp (used in fdr, and WIN VIB/DDR)
480 */
481 struct ti99_date_time
482 {
483 	uint8_t time_MSB, time_LSB;/* 0-4: hour, 5-10: minutes, 11-15: seconds/2 */
484 	uint8_t date_MSB, date_LSB;/* 0-6: year, 7-10: month, 11-15: day */
485 };
486 
487 /*
488     Subdirectory descriptor (HFDC only)
489 
490     The HFDC supports up to 3 subdirectories.
491 */
492 struct dsk_subdir
493 {
494 	char name[10];          /* subdirectory name (10 characters, pad with spaces) */
495 	UINT16BE fdir_aphysrec; /* aphysrec address of fdir record for this subdirectory */
496 };
497 
498 /*
499     DSK VIB record
500 
501     Most fields in this record are only relevant to level 2 routines, but level
502     1 routines need the disk geometry information extracted from the VIB.
503 */
504 struct dsk_vib
505 {
506 	char name[10];          /* disk volume name (10 characters, pad with spaces) */
507 	UINT16BE totphysrecs;   /* total number of physrecs on disk (usually 360, */
508 								/* 720, 640, 1280 or 1440).  (TI originally */
509 								/* said it should be the total number of AUs, */
510 								/* but, in practice, DSRs that supports disks */
511 								/* over 400kbytes (e.g. HFDC) write the total */
512 								/* number of physrecs.) */
513 	uint8_t secspertrack;     /* sectors per track (usually 9 (FM SD), */
514 								/* 16 or 18 (MFM DD), or 36 (MFM HD) */
515 	uint8_t id[3];            /* 'DSK' */
516 	uint8_t protection;       /* 'P' if disk is protected, ' ' otherwise. */
517 	uint8_t cylinders;        /* tracks per side (usually 40) */
518 	uint8_t heads;            /* sides (1 or 2) */
519 	uint8_t density;          /* density: 1 (FM SD), 2 (MFM DD), or 3 (MFM HD) */
520 	dsk_subdir subdir[3];   /* descriptor for up to 3 subdirectories (HFDC only) */
521 								/* reserved by TI */
522 	uint8_t abm[200];         /* allocation bitmap: there is one bit per AU. */
523 								/* A binary 1 in a bit position indicates that */
524 								/* the allocation unit associated with that */
525 								/* bit has been allocated. */
526 								/* Bits corresponding to system reserved AUs, */
527 								/* non-existent AUs, and bad AUs, are set to one, too. */
528 								/* (AU 0 is associated to LSBit of byte 0, */
529 								/* AU 7 to MSBit of byte 0, AU 8 to LSBit */
530 								/* of byte 1, etc.) */
531 };
532 
533 enum ti99_img_format
534 {
535 	if_mess,
536 	if_v9t9,
537 	if_pc99_fm,
538 	if_pc99_mfm,
539 	if_harddisk
540 };
541 
542 /*
543     level-1 disk image descriptor
544 */
545 struct ti99_lvl1_imgref
546 {
547 	ti99_img_format img_format; /* tells the image format */
548 	imgtool::stream *file_handle;        /* imgtool file handle */
549 	struct mess_hard_disk_file harddisk_handle;     /* MAME harddisk handle (harddisk format) */
550 	ti99_geometry geometry;     /* geometry */
551 	unsigned pc99_track_len;        /* unformatted track length (pc99 format) */
552 	uint32_t *pc99_data_offset_array; /* offset for each sector (pc99 format) */
553 };
554 
555 /*
556     calculate CRC for data address marks or sector data
557 
558     crc (I/O): current CRC value to be updated (initialize to 0xffff before
559         calling this function for the first time)
560     value: new byte of data to update the CRC with
561 */
calc_crc(uint16_t * crc,uint8_t value)562 static void calc_crc(uint16_t *crc, uint8_t value)
563 {
564 	uint8_t l, h;
565 
566 	l = value ^ (*crc >> 8);
567 	*crc = (*crc & 0xff) | (l << 8);
568 	l >>= 4;
569 	l ^= (*crc >> 8);
570 	*crc <<= 8;
571 	*crc = (*crc & 0xff00) | l;
572 	l = (l << 4) | (l >> 4);
573 	h = l;
574 	l = (l << 2) | (l >> 6);
575 	l &= 0x1f;
576 	*crc = *crc ^ (l << 8);
577 	l = h & 0xf0;
578 	*crc = *crc ^ (l << 8);
579 	l = (h << 1) | (h >> 7);
580 	l &= 0xe0;
581 	*crc = *crc ^ l;
582 }
583 
584 /*
585     Parse a PC99 disk image
586 
587     file_handle (I/O): imgtool file handle
588     fm_format (I): if true, the image is in FM format, otherwise it is in MFM
589         format
590     pass (I): 0 for first pass, 1 for second pass
591     vib (O): buffer where the vib should be stored (first pass)
592     geometry (O): disk image geometry (second pass)
593     data_offset_array (O): array of data offset to generate (second pass)
594     out_track_len (O): track length is returned there
595 
596     Return imgtool error code
597 */
598 #define MAX_TRACK_LEN 6872
599 #define DATA_OFFSET_NONE 0xffffffff
parse_pc99_image(imgtool::stream & file_handle,int fm_format,int pass,dsk_vib * vib,const ti99_geometry * geometry,uint32_t * data_offset_array,unsigned * out_track_len)600 static int parse_pc99_image(imgtool::stream &file_handle, int fm_format, int pass, dsk_vib *vib, const ti99_geometry *geometry, uint32_t *data_offset_array, unsigned *out_track_len)
601 {
602 	int track_len, num_tracks;  /* length of a track in bytes, and number of tracks */
603 	int phys_track;
604 	int expected_cylinder, expected_head;
605 	int track_start_pos, track_pos;
606 	uint8_t c;
607 	uint8_t cylinder, head, sector;
608 	int seclen;
609 	uint8_t crc1, crc2;
610 	uint16_t crc;
611 	long data_offset;
612 	uint8_t track_buf[MAX_TRACK_LEN];
613 	int i;
614 
615 	if (fm_format)
616 		track_len = 3253;
617 	else
618 		track_len = 6872;
619 
620 	if (out_track_len)
621 		*out_track_len = track_len;
622 
623 	if (file_handle.size() % track_len)
624 		return IMGTOOLERR_CORRUPTIMAGE;
625 
626 	num_tracks = file_handle.size() / track_len;
627 	if (num_tracks <= 0)
628 		return IMGTOOLERR_CORRUPTIMAGE;
629 
630 	/* we only support 40-track-per-side images */
631 	if ((num_tracks != 40) && (num_tracks != 80))
632 		return IMGTOOLERR_UNIMPLEMENTED;
633 
634 	if (pass == 1)
635 	{
636 		/* initialize offset map */
637 		for (head = 0; head < geometry->heads; head++)
638 			for (cylinder = 0; cylinder < geometry->cylinders; cylinder++)
639 				for (sector = 0; sector < geometry->secspertrack; sector++)
640 					data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] = DATA_OFFSET_NONE;
641 	}
642 	/* rewind to start of file */
643 	file_handle.seek(0, SEEK_SET);
644 
645 	/* pass 0 only looks for vib in track 0; pass 1 scans every track */
646 	for (phys_track=0; phys_track < ((pass == 1) ? num_tracks : 1); phys_track++)
647 	{
648 		if (file_handle.read(track_buf, track_len) != track_len)
649 			return IMGTOOLERR_READERROR;
650 
651 		/* we only support 40-track-per-side images */
652 		expected_cylinder = phys_track % 40;
653 		expected_head = phys_track / 40;
654 
655 		track_start_pos = 0;
656 
657 		while (track_start_pos < track_len)
658 		{
659 			if (fm_format)
660 			{
661 				do
662 				{
663 					c = track_buf[track_start_pos];
664 					track_start_pos++;
665 				} while ((c != 0xfe) && (track_start_pos < track_len));
666 
667 				if (c != 0xfe)
668 					break;
669 
670 				track_pos = track_start_pos;
671 
672 				crc = 0xffff;
673 				calc_crc(& crc, c);
674 			}
675 			else
676 			{
677 				do
678 				{
679 					c = track_buf[track_start_pos];
680 					track_start_pos++;
681 				} while ((c != 0xa1) && (track_start_pos < track_len));
682 
683 				if (c != 0xa1)
684 					break;
685 
686 				track_pos = track_start_pos;
687 
688 				c = track_buf[track_pos];
689 				track_pos++;
690 				if (track_pos == track_len)
691 					track_pos = 0;
692 
693 				if (c != 0xa1)
694 					continue;
695 
696 				c = track_buf[track_pos];
697 				track_pos++;
698 				if (track_pos == track_len)
699 					track_pos = 0;
700 
701 				if (c != 0xa1)
702 					continue;
703 
704 				crc = 0xffff;
705 				calc_crc(& crc, c);
706 
707 				c = track_buf[track_pos];
708 				track_pos++;
709 				if (track_pos == track_len)
710 					track_pos = 0;
711 
712 				if (c != 0xfe)
713 					continue;
714 			}
715 
716 			c = track_buf[track_pos];
717 			track_pos++;
718 			if (track_pos == track_len)
719 				track_pos = 0;
720 
721 			cylinder = c;
722 			calc_crc(& crc, c);
723 
724 			c = track_buf[track_pos];
725 			track_pos++;
726 			if (track_pos == track_len)
727 				track_pos = 0;
728 
729 			head = c;
730 			calc_crc(& crc, c);
731 
732 			c = track_buf[track_pos];
733 			track_pos++;
734 			if (track_pos == track_len)
735 				track_pos = 0;
736 
737 			sector = c;
738 			calc_crc(& crc, c);
739 
740 			c = track_buf[track_pos];
741 			track_pos++;
742 			if (track_pos == track_len)
743 				track_pos = 0;
744 
745 			seclen = 128 << c;
746 			calc_crc(& crc, c);
747 
748 			c = track_buf[track_pos];
749 			track_pos++;
750 			if (track_pos == track_len)
751 				track_pos = 0;
752 
753 			crc1 = c;
754 			calc_crc(& crc, c);
755 
756 			c = track_buf[track_pos];
757 			track_pos++;
758 			if (track_pos == track_len)
759 				track_pos = 0;
760 
761 			crc2 = c;
762 			calc_crc(& crc, c);
763 
764 #if 0
765 			/* CRC seems to be completely hosed */
766 			if (crc)
767 				printf("aargh!");
768 #endif
769 			if ((seclen != 256) || (crc1 != 0xf7) || (crc2 != 0xf7)
770 					|| (cylinder != expected_cylinder) || (head != expected_head)
771 					|| ((pass == 1) && ((cylinder >= geometry->cylinders)
772 										|| (head >= geometry->heads)
773 										|| (sector >= geometry->secspertrack))))
774 				continue;
775 
776 			c = track_buf[track_pos];
777 			track_pos++;
778 			if (track_pos == track_len)
779 				track_pos = 0;
780 
781 			while (c == (fm_format ? 0xff : 0x4e))
782 			{
783 				c = track_buf[track_pos];
784 				track_pos++;
785 				if (track_pos == track_len)
786 					track_pos = 0;
787 			}
788 
789 			while (c == 0x00)
790 			{
791 				c = track_buf[track_pos];
792 				track_pos++;
793 				if (track_pos == track_len)
794 					track_pos = 0;
795 			}
796 
797 			if (fm_format)
798 			{
799 				if (c != 0xfb)
800 					continue;
801 
802 				crc = 0xffff;
803 				calc_crc(& crc, c);
804 			}
805 			else
806 			{
807 				if (c != 0xa1)
808 					continue;
809 
810 				c = track_buf[track_pos];
811 				track_pos++;
812 				if (track_pos == track_len)
813 					track_pos = 0;
814 
815 				if (c != 0xa1)
816 					continue;
817 
818 				c = track_buf[track_pos];
819 				track_pos++;
820 				if (track_pos == track_len)
821 					track_pos = 0;
822 
823 				if (c != 0xa1)
824 					continue;
825 
826 				crc = 0xffff;
827 				calc_crc(& crc, c);
828 
829 				c = track_buf[track_pos];
830 				track_pos++;
831 				if (track_pos == track_len)
832 					track_pos = 0;
833 
834 				if (c != 0xfb)
835 					continue;
836 			}
837 			data_offset = track_pos;
838 			for (i=0; i<seclen; i++)
839 			{
840 				c = track_buf[track_pos];
841 				track_pos++;
842 				if (track_pos == track_len)
843 					track_pos = 0;
844 
845 				calc_crc(& crc, c);
846 			}
847 
848 			c = track_buf[track_pos];
849 			track_pos++;
850 			if (track_pos == track_len)
851 				track_pos = 0;
852 
853 			crc1 = c;
854 			calc_crc(& crc, c);
855 
856 			c = track_buf[track_pos];
857 			track_pos++;
858 			if (track_pos == track_len)
859 				track_pos = 0;
860 
861 			crc2 = c;
862 			calc_crc(& crc, c);
863 
864 #if 0
865 			/* CRC seems to be completely hosed */
866 			if (crc)
867 				printf("aargh!");
868 #endif
869 			if ((crc1 != 0xf7) || (crc2 != 0xf7))
870 				continue;
871 
872 			switch (pass)
873 			{
874 			case 0:
875 				if ((cylinder == 0) && (head == 0) && (sector == 0))
876 				{
877 					/* return vib */
878 					if ((data_offset + 256) <= track_len)
879 						memcpy(vib, track_buf + data_offset, 256);
880 					else
881 					{
882 						memcpy((uint8_t *)vib, track_buf + data_offset, track_len-data_offset);
883 						memcpy((uint8_t *)vib + (track_len-data_offset), track_buf, 256-(track_len-data_offset));
884 					}
885 					return 0;
886 				}
887 				break;
888 
889 			case 1:
890 				/* set up offset map */
891 				if (data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] != DATA_OFFSET_NONE)
892 					/* error: duplicate sector */
893 					return IMGTOOLERR_CORRUPTIMAGE;
894 				data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] = data_offset;
895 				break;
896 			}
897 		}
898 	}
899 
900 	if (pass == 0)
901 		return IMGTOOLERR_CORRUPTIMAGE;
902 
903 	if (pass == 1)
904 	{
905 		/* check offset map */
906 		for (head = 0; head < geometry->heads; head++)
907 			for (cylinder = 0; cylinder < geometry->cylinders; cylinder++)
908 				for (sector = 0; sector < geometry->secspertrack; sector++)
909 					if (data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] == DATA_OFFSET_NONE)
910 						/* error: missing sector */
911 						return IMGTOOLERR_CORRUPTIMAGE;
912 	}
913 
914 	return 0;
915 }
916 
917 
918 /*
919     Read the volume information block (aphysrec 0) assuming no geometry
920     information.  (Called when an image is opened to figure out the
921     geometry information.)
922 
923     file_handle (I/O): imgtool file handle
924     img_format (I): image format (MESS, V9T9 or PC99)
925     dest (O): pointer to 256-byte destination buffer
926 
927     Return imgtool error code
928 */
read_image_vib_no_geometry(imgtool::stream & file_handle,ti99_img_format img_format,dsk_vib * dest)929 static int read_image_vib_no_geometry(imgtool::stream &file_handle, ti99_img_format img_format, dsk_vib *dest)
930 {
931 	int reply;
932 
933 	switch (img_format)
934 	{
935 	case if_mess:
936 	case if_v9t9:
937 		/* seek to sector */
938 		reply = file_handle.seek(0, SEEK_SET);
939 		if (reply)
940 			return IMGTOOLERR_READERROR;
941 		/* read it */
942 		reply = file_handle.read(dest, 256);
943 		if (reply != 256)
944 			return IMGTOOLERR_READERROR;
945 		return 0;
946 
947 	case if_pc99_fm:
948 	case if_pc99_mfm:
949 		return parse_pc99_image(file_handle, img_format == if_pc99_fm, 0, dest, NULL, NULL, NULL);
950 
951 	case if_harddisk:
952 		/* not implemented, because we don't need it */
953 		break;
954 	}
955 
956 	return IMGTOOLERR_UNIMPLEMENTED;
957 }
958 
959 /*
960     Open a disk image at level 1
961 
962     file_handle (I/O): imgtool file handle
963     img_format (I): image format (MESS, V9T9, PC99, or MAME harddisk)
964     l1_img (O): level-1 image handle
965     vib (O): buffer where the vib should be stored (floppy images only)
966 
967     Return imgtool error code
968 */
open_image_lvl1(imgtool::stream::ptr && file_handle,ti99_img_format img_format,ti99_lvl1_imgref * l1_img,dsk_vib * vib)969 static imgtoolerr_t open_image_lvl1(imgtool::stream::ptr &&file_handle, ti99_img_format img_format, ti99_lvl1_imgref *l1_img, dsk_vib *vib)
970 {
971 	imgtoolerr_t err;
972 	int reply;
973 	uint16_t totphysrecs;
974 
975 	l1_img->img_format = img_format;
976 
977 	if (img_format == if_harddisk)
978 	{
979 		const hard_disk_info *info;
980 
981 		err = imghd_open(*file_handle, &l1_img->harddisk_handle);
982 		if (err)
983 			return err;
984 
985 		info = imghd_get_header(&l1_img->harddisk_handle);
986 		l1_img->geometry.cylinders = info->cylinders;
987 		l1_img->geometry.heads = info->heads;
988 		l1_img->geometry.secspertrack = info->sectors;
989 		if (info->sectorbytes != 256)
990 		{
991 			imghd_close(&l1_img->harddisk_handle);
992 			return IMGTOOLERR_CORRUPTIMAGE; /* TODO: support 512-byte sectors */
993 		}
994 
995 #if 0
996 		/* read vib */
997 		reply = read_absolute_physrec(l1_img, 0, vib);
998 		if (reply)
999 			return reply;
1000 #endif
1001 	}
1002 	else
1003 	{
1004 		/* read vib */
1005 		reply = read_image_vib_no_geometry(*file_handle, img_format, vib);
1006 		if (reply)
1007 			return (imgtoolerr_t)reply;
1008 
1009 		/* extract geometry information */
1010 		totphysrecs = get_UINT16BE(vib->totphysrecs);
1011 
1012 		l1_img->geometry.secspertrack = vib->secspertrack;
1013 		if (l1_img->geometry.secspertrack == 0)
1014 			/* Some images might be like this, because the original SSSD TI
1015 			controller always assumes 9. */
1016 			l1_img->geometry.secspertrack = 9;
1017 		l1_img->geometry.cylinders = vib->cylinders;
1018 		if (l1_img->geometry.cylinders == 0)
1019 			/* Some images are like this, because the original SSSD TI
1020 			controller always assumes 40. */
1021 			l1_img->geometry.cylinders = 40;
1022 		l1_img->geometry.heads = vib->heads;
1023 		if (l1_img->geometry.heads == 0)
1024 			/* Some images are like this, because the original SSSD TI
1025 			controller always assumes that tracks beyond 40 are on side 2. */
1026 			l1_img->geometry.heads = totphysrecs / (l1_img->geometry.secspertrack * l1_img->geometry.cylinders);
1027 
1028 		/* check information */
1029 		if ((totphysrecs != (l1_img->geometry.secspertrack * l1_img->geometry.cylinders * l1_img->geometry.heads))
1030 				|| (totphysrecs < 2)
1031 				|| memcmp(vib->id, "DSK", 3) || (! strchr(" P", vib->protection))
1032 				|| (((img_format == if_mess) || (img_format == if_v9t9))
1033 					&& (file_handle->size() != totphysrecs*256U)))
1034 			return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
1035 
1036 		if ((img_format == if_pc99_fm) || (img_format == if_pc99_mfm))
1037 		{
1038 			l1_img->pc99_data_offset_array = (uint32_t*)malloc(sizeof(*l1_img->pc99_data_offset_array)*totphysrecs);
1039 			if (! l1_img->pc99_data_offset_array)
1040 				return IMGTOOLERR_OUTOFMEMORY;
1041 			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);
1042 			if (reply)
1043 			{
1044 				free(l1_img->pc99_data_offset_array);
1045 				return (imgtoolerr_t)reply;
1046 			}
1047 		}
1048 	}
1049 
1050 	l1_img->file_handle = file_handle.release();    // we can only do this when we're sure we're successful
1051 
1052 	return (imgtoolerr_t)0;
1053 }
1054 
1055 /*
1056     Close a disk image at level 1
1057 
1058     l1_img (I/O): level-1 image handle
1059 
1060     Return imgtool error code
1061 */
close_image_lvl1(ti99_lvl1_imgref * l1_img)1062 static void close_image_lvl1(ti99_lvl1_imgref *l1_img)
1063 {
1064 	if (l1_img->img_format == if_harddisk)
1065 	{
1066 		imghd_close(&l1_img->harddisk_handle);
1067 	}
1068 
1069 	delete l1_img->file_handle;
1070 
1071 	if ((l1_img->img_format == if_pc99_fm) || (l1_img->img_format == if_pc99_mfm))
1072 		free(l1_img->pc99_data_offset_array);
1073 }
1074 
1075 /*
1076     Convert physical sector address to offset in disk image (old MESS and V9T9
1077     formats only)
1078 
1079     l1_img (I/O): level-1 image handle
1080     address (I): physical sector address
1081 
1082     Return offset in image
1083 */
sector_address_to_image_offset(const ti99_lvl1_imgref * l1_img,const ti99_sector_address * address)1084 static inline int sector_address_to_image_offset(const ti99_lvl1_imgref *l1_img, const ti99_sector_address *address)
1085 {
1086 	int offset = 0;
1087 
1088 	switch (l1_img->img_format)
1089 	{
1090 	case if_mess:
1091 		/* old MESS format */
1092 		offset = (((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector)*256;
1093 		break;
1094 
1095 	case if_v9t9:
1096 		/* V9T9 format */
1097 		if (address->side & 1)
1098 			/* on side 1, tracks are stored in the reverse order */
1099 			offset = (((address->side*l1_img->geometry.cylinders) + (l1_img->geometry.cylinders-1 - address->cylinder))*l1_img->geometry.secspertrack + address->sector)*256;
1100 		else
1101 			offset = (((address->side*l1_img->geometry.cylinders) + address->cylinder)*l1_img->geometry.secspertrack + address->sector)*256;
1102 		break;
1103 
1104 	case if_pc99_fm:
1105 	case if_pc99_mfm:
1106 		/* pc99 format */
1107 	case if_harddisk:
1108 		/* harddisk format */
1109 		assert(1);      /* not implemented */
1110 		break;
1111 	}
1112 
1113 	return offset;
1114 }
1115 
1116 /*
1117     Read one 256-byte sector from a disk image
1118 
1119     l1_img (I/O): level-1 image handle
1120     address (I): physical sector address
1121     dest (O): pointer to 256-byte destination buffer
1122 
1123     Return non-zero on error
1124 */
read_sector(ti99_lvl1_imgref * l1_img,const ti99_sector_address * address,void * dest)1125 static int read_sector(ti99_lvl1_imgref *l1_img, const ti99_sector_address *address, void *dest)
1126 {
1127 	int reply;
1128 	uint32_t track_len, track_offset, sector_offset;
1129 
1130 	switch (l1_img->img_format)
1131 	{
1132 	case if_mess:
1133 		/* old MESS format */
1134 	case if_v9t9:
1135 		/* V9T9 format */
1136 		/* seek to sector */
1137 		reply = l1_img->file_handle->seek(sector_address_to_image_offset(l1_img, address), SEEK_SET);
1138 		if (reply)
1139 			return 1;
1140 		/* read it */
1141 		reply = l1_img->file_handle->read(dest, 256);
1142 		if (reply != 256)
1143 			return 1;
1144 		break;
1145 
1146 	case if_pc99_fm:
1147 	case if_pc99_mfm:
1148 		/* pc99 format */
1149 		track_len = l1_img->pc99_track_len;
1150 		track_offset = (address->side*l1_img->geometry.cylinders + address->cylinder)*track_len;
1151 		sector_offset = l1_img->pc99_data_offset_array[(address->side*l1_img->geometry.cylinders + address->cylinder)*l1_img->geometry.secspertrack + address->sector];
1152 
1153 		if ((sector_offset + 256) <= track_len)
1154 		{
1155 			/* seek to sector */
1156 			reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET);
1157 			if (reply)
1158 				return 1;
1159 			/* read it */
1160 			reply = l1_img->file_handle->read(dest, 256);
1161 			if (reply != 256)
1162 				return 1;
1163 		}
1164 		else
1165 		{
1166 			/* seek to sector */
1167 			reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET);
1168 			if (reply)
1169 				return 1;
1170 			/* read first chunk (until end of track) */
1171 			reply = l1_img->file_handle->read((uint8_t *)dest, track_len-sector_offset);
1172 			if (reply != track_len-sector_offset)
1173 				return 1;
1174 
1175 			/* wrap to start of track */
1176 			reply = l1_img->file_handle->seek(track_offset, SEEK_SET);
1177 			if (reply)
1178 				return 1;
1179 			/* read remnant of sector */
1180 			reply = l1_img->file_handle->read((uint8_t *)dest + (track_len-sector_offset), 256-(track_len-sector_offset));
1181 			if (reply != 256-(track_len-sector_offset))
1182 				return 1;
1183 		}
1184 		break;
1185 
1186 	case if_harddisk:
1187 		/* not implemented */
1188 		assert(1);
1189 		/*return imghd_read(l1_img->harddisk_handle, ((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector, 1, dest) != 1;*/
1190 		break;
1191 	}
1192 
1193 	return 0;
1194 }
1195 
1196 /*
1197     Write one 256-byte sector to a disk image
1198 
1199     l1_img (I/O): level-1 image handle
1200     address (I): physical sector address
1201     src (I): pointer to 256-byte source buffer
1202 
1203     Return non-zero on error
1204 */
write_sector(ti99_lvl1_imgref * l1_img,const ti99_sector_address * address,const void * src)1205 static int write_sector(ti99_lvl1_imgref *l1_img, const ti99_sector_address *address, const void *src)
1206 {
1207 	int reply;
1208 	uint32_t track_len, track_offset, sector_offset;
1209 
1210 	switch (l1_img->img_format)
1211 	{
1212 	case if_mess:
1213 		/* old MESS format */
1214 	case if_v9t9:
1215 		/* V9T9 format */
1216 		/* seek to sector */
1217 		reply = l1_img->file_handle->seek(sector_address_to_image_offset(l1_img, address), SEEK_SET);
1218 		if (reply)
1219 			return 1;
1220 		/* write it */
1221 		reply = l1_img->file_handle->write(src, 256);
1222 		if (reply != 256)
1223 			return 1;
1224 		break;
1225 
1226 	case if_pc99_fm:
1227 	case if_pc99_mfm:
1228 		/* pc99 format */
1229 		track_len = l1_img->pc99_track_len;
1230 		track_offset = (address->side*l1_img->geometry.cylinders + address->cylinder)*track_len;
1231 		sector_offset = l1_img->pc99_data_offset_array[(address->side*l1_img->geometry.cylinders + address->cylinder)*l1_img->geometry.secspertrack + address->sector];
1232 
1233 		if ((sector_offset + 256) <= track_len)
1234 		{
1235 			/* seek to sector */
1236 			reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET);
1237 			if (reply)
1238 				return 1;
1239 			/* write it */
1240 			reply = l1_img->file_handle->write(src, 256);
1241 			if (reply != 256)
1242 				return 1;
1243 		}
1244 		else
1245 		{
1246 			/* seek to sector */
1247 			reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET);
1248 			if (reply)
1249 				return 1;
1250 			/* write first chunk (until end of track) */
1251 			reply = l1_img->file_handle->write((uint8_t *)src, track_len-sector_offset);
1252 			if (reply != track_len-sector_offset)
1253 				return 1;
1254 
1255 			/* wrap to start of track */
1256 			reply = l1_img->file_handle->seek(track_offset, SEEK_SET);
1257 			if (reply)
1258 				return 1;
1259 			/* write remnant of sector */
1260 			reply = l1_img->file_handle->write((uint8_t *)src + (track_len-sector_offset), 256-(track_len-sector_offset));
1261 			if (reply != 256-(track_len-sector_offset))
1262 				return 1;
1263 		}
1264 		break;
1265 
1266 	case if_harddisk:
1267 		/* not implemented */
1268 		assert(1);
1269 		/*return imghd_write(l1_img->harddisk_handle, ((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector, 1, src) != 1;*/
1270 		break;
1271 	}
1272 
1273 	return 0;
1274 }
1275 
1276 /*
1277     Convert physical record address to sector address (DSK format)
1278 
1279     aphysrec (I): absolute physrec address
1280     geometry (I): disk image geometry
1281     address (O): physical sector address
1282 */
dsk_aphysrec_to_sector_address(int aphysrec,const ti99_geometry * geometry,ti99_sector_address * address)1283 static void dsk_aphysrec_to_sector_address(int aphysrec, const ti99_geometry *geometry, ti99_sector_address *address)
1284 {
1285 	address->sector = aphysrec % geometry->secspertrack;
1286 	aphysrec /= geometry->secspertrack;
1287 	address->cylinder = aphysrec % geometry->cylinders;
1288 	address->side = aphysrec / geometry->cylinders;
1289 	if (address->side & 1)
1290 		/* on side 1, tracks are stored in the reverse order */
1291 		address->cylinder = geometry->cylinders-1 - address->cylinder;
1292 }
1293 
1294 /*
1295     Convert physical record address to sector address (WIN format for HFDC)
1296 
1297     Note that physical address translation makes sense for HFDC, but not SCSI.
1298 
1299     aphysrec (I): absolute physrec address
1300     geometry (I): disk image geometry
1301     address (O): physical sector address
1302 */
1303 #ifdef UNUSED_FUNCTION
win_aphysrec_to_sector_address(int aphysrec,const ti99_geometry * geometry,ti99_sector_address * address)1304 static void win_aphysrec_to_sector_address(int aphysrec, const ti99_geometry *geometry, ti99_sector_address *address)
1305 {
1306 	address.sector = aphysrec % l1_img->geometry.secspertrack;
1307 	aphysrec /= l1_img->geometry.secspertrack;
1308 	address.side = aphysrec % l1_img->geometry.heads;
1309 	address.cylinder = aphysrec / l1_img->geometry.heads;
1310 }
1311 #endif
1312 
1313 /*
1314     Read one 256-byte physical record from a disk image
1315 
1316     l1_img (I/O): level-1 image handle
1317     aphysrec (I): absolute physrec address
1318     dest (O): pointer to 256-byte destination buffer
1319 
1320     Return non-zero on error
1321 */
read_absolute_physrec(ti99_lvl1_imgref * l1_img,unsigned aphysrec,void * dest)1322 static int read_absolute_physrec(ti99_lvl1_imgref *l1_img, unsigned aphysrec, void *dest)
1323 {
1324 	ti99_sector_address address;
1325 
1326 
1327 	if (l1_img->img_format == if_harddisk)
1328 	{
1329 #if 0
1330 		win_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
1331 		return read_sector(l1_img, & address, dest);
1332 #endif
1333 
1334 		return imghd_read(&l1_img->harddisk_handle, aphysrec, dest) != IMGTOOLERR_SUCCESS;
1335 	}
1336 	else
1337 	{
1338 		dsk_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
1339 
1340 		return read_sector(l1_img, & address, dest);
1341 	}
1342 }
1343 
1344 /*
1345     Write one 256-byte physical record to a disk image
1346 
1347     l1_img (I/O): level-1 image handle
1348     aphysrec (I): absolute physrec address
1349     src (I): pointer to 256-byte source buffer
1350 
1351     Return non-zero on error
1352 */
write_absolute_physrec(ti99_lvl1_imgref * l1_img,unsigned aphysrec,const void * src)1353 static int write_absolute_physrec(ti99_lvl1_imgref *l1_img, unsigned aphysrec, const void *src)
1354 {
1355 	ti99_sector_address address;
1356 
1357 
1358 	if (l1_img->img_format == if_harddisk)
1359 	{
1360 #if 0
1361 		win_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
1362 		return write_sector(l1_img, & address, dest);
1363 #endif
1364 
1365 		return imghd_write(&l1_img->harddisk_handle, aphysrec, src) != IMGTOOLERR_SUCCESS;
1366 	}
1367 	else
1368 	{
1369 		dsk_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address);
1370 
1371 		return write_sector(l1_img, & address, src);
1372 	}
1373 }
1374 
1375 #if 0
1376 #pragma mark -
1377 #pragma mark LEVEL 2 DISK ROUTINES
1378 #endif
1379 
1380 /*
1381     Level 2 implements files as a succession of 256-byte-long physical records.
1382 
1383     Level 2 implements allocation bitmap (and AU), disk catalog, etc.
1384 */
1385 
1386 /*
1387     WIN VIB/DDR record
1388 */
1389 struct win_vib_ddr
1390 {
1391 	char name[10];          /* disk volume name (10 characters, pad with spaces) */
1392 	UINT16BE totAUs;        /* total number of AUs */
1393 	uint8_t secspertrack;     /* HFDC: sectors per track (typically 32) */
1394 							/* SCSI: reserved */
1395 	union
1396 	{
1397 		struct
1398 		{
1399 			uint8_t id[3];    /* V1 VIB: 'WIN' */
1400 		} vib_v1;
1401 
1402 		struct              /* V2 VIB: extra params */
1403 		{
1404 			uint8_t res_AUs;  /* # AUs reserved for vib, bitmap, ddr, fdir and fdr, divided by 64 */
1405 			uint8_t step_spd; /* HFDC: step speed (0-7) */
1406 							/* SCSI: reserved */
1407 			uint8_t red_w_cur;/* HFDC: reduced write current cylinder, divided by 8 */
1408 							/* SCSI: reserved */
1409 		} vib_v2;
1410 
1411 		struct
1412 		{
1413 			uint8_t id[3];    /* DDR: 'DIR' */
1414 		} ddr;
1415 	} u;
1416 	UINT16BE params;        /* bits 0-3: sectors/AU - 1 */
1417 							/* HFDC: */
1418 								/* bits 4-7: # heads - 1 */
1419 								/* bit 8: V1: buffered head stepping flag */
1420 								/*        V2: reserved */
1421 								/* bit 9-15: write precompensation track, divided by 16 */
1422 							/* SCSI: */
1423 								/* bits 4-15: reserved */
1424 	ti99_date_time creation;/* date and time of creation */
1425 	uint8_t num_files;        /* number of files in directory */
1426 	uint8_t num_subdirs;      /* number of subdirectories in directory */
1427 	UINT16BE fdir_AU;       /* points to root directory fdir */
1428 	union
1429 	{
1430 		struct
1431 		{
1432 			UINT16BE dsk1_AU;   /* HFDC: points to current dsk1 emulation image (0 if none) */
1433 								/* SCSI: reserved */
1434 		} vib;
1435 
1436 		struct
1437 		{
1438 			UINT16BE parent_ddr_AU; /* points to parent directory DDR */
1439 		} ddr;
1440 	} u2;
1441 	UINT16BE subdir_AU[114];/* points to all subdirectory DDRs */
1442 };
1443 
1444 /*
1445     AU format
1446 */
1447 struct ti99_AUformat
1448 {
1449 	int totAUs;             /* total number of AUs */
1450 	int physrecsperAU;      /* number of 256-byte physical records per AU */
1451 };
1452 
1453 /*
1454     DSK directory reference: 0 for root, 1 for 1st subdir, 2 for 2nd subdir, 3
1455     for 3rd subdir
1456 */
1457 /*typedef int dir_ref;*/
1458 
1459 /*
1460     catalog entry (used for in-memory catalog)
1461 */
1462 struct dir_entry
1463 {
1464 	uint16_t dir_ptr;         /* DSK: unused */
1465 							/* WIN: AU address of the DDR for this directory */
1466 	char name[10];          /* name of this directory (copied from the VIB for DSK, DDR for WIN) */
1467 };
1468 
1469 struct file_entry
1470 {
1471 	uint16_t fdr_ptr;         /* DSK: aphysrec address of the FDR for this file */
1472 							/* WIN: AU address of the FDR for this file */
1473 	char name[10];          /* name of this file (copied from FDR) */
1474 };
1475 
1476 struct ti99_catalog
1477 {
1478 	int num_subdirs;        /* number of subdirectories */
1479 	int num_files;          /* number of files */
1480 	dir_entry subdirs[114]; /* description of each subdir */
1481 	file_entry files[128];  /* description of each file */
1482 };
1483 
1484 /*
1485     level-2 disk image descriptor
1486 */
1487 struct ti99_lvl2_imgref_dsk
1488 {
1489 	uint16_t totphysrecs;             /* total number of aphysrecs (extracted from vib record in aphysrec 0) */
1490 	ti99_catalog catalogs[4];       /* catalog of root directory and up to 3 subdirectories */
1491 	uint16_t fdir_aphysrec[4];        /* fdir aphysrec address for root directory
1492 	                                    and up to 3 subdirectories */
1493 };
1494 
1495 enum win_vib_t
1496 {
1497 	win_vib_v1,
1498 	win_vib_v2
1499 };
1500 struct ti99_lvl2_imgref_win
1501 {
1502 	win_vib_t vib_version;          /* version of the vib record in aphysrec 0 (see win_vib_ddr) */
1503 };
1504 
1505 enum l2i_t
1506 {
1507 	L2I_DSK,
1508 	L2I_WIN
1509 };
1510 
1511 struct ti99_lvl2_imgref
1512 {
1513 	ti99_lvl1_imgref l1_img;/* image format, imgtool image handle, image geometry */
1514 	ti99_AUformat AUformat; /* AU format */
1515 	int data_offset;        /* In order to reduce seek times when searching the
1516 	                            disk for a given file name, fdr (and ddr, and
1517 	                            fdir) records are preferentially allocated in
1518 	                            AUs n to data_offset, whereas data records are
1519 	                            preferentially allocated in AUs starting at
1520 	                            data_offset. */
1521 							/* With the DSK disk structure, n is always 2 (if 1
1522 							    physrec per AU) or 1 (if 2 physrecs per AU or
1523 							    more), and data_offset is arbitrarily chosen as
1524 							    34. */
1525 							/* With the WIN disk structure, n depends on the
1526 							    size of the volume bitmap, which itself depends
1527 							    on the number of AUs on disk (we always have n
1528 							    <= 33), and data_offset is read from the vib
1529 							    record (except with the obsolete v1 VIB, where
1530 							    we use a default value of 64). */
1531 	char vol_name[10];      /* cached volume name (extracted from vib record in aphysrec 0) */
1532 
1533 	uint8_t abm[8192];        /* allocation bitmap */
1534 
1535 	l2i_t type;                 /* structure format */
1536 
1537 	union
1538 	{
1539 		ti99_lvl2_imgref_dsk dsk;
1540 		ti99_lvl2_imgref_win win;
1541 	};                  /* structure-specific info */
1542 };
1543 
1544 /*
1545     file flags found in fdr (and tifiles)
1546 */
1547 enum
1548 {
1549 	fdr99_f_program = 0x01, /* set for program files */
1550 	fdr99_f_int     = 0x02, /* set for binary files */
1551 	fdr99_f_wp      = 0x08, /* set if file is write-protected */
1552 	/*fdr99_f_backup    = 0x10,*/   /* set if file has been modified since last backup */
1553 	/*fdr99_f_dskimg    = 0x20,*/   /* set if file is a DSK image (HFDC HD only) */
1554 	fdr99_f_var     = 0x80  /* set if file uses variable-length records*/
1555 };
1556 
1557 /*
1558     DSK FDR record
1559 */
1560 struct dsk_fdr
1561 {
1562 	char name[10];          /* file name (10 characters, pad with spaces) */
1563 	UINT16BE xreclen;       /* extended record len: if record len is >= 256, */
1564 								/* reclen is set to 0 and the actual reclen is */
1565 								/* stored here (Myarc HFDC only).  TI reserved */
1566 								/* this field for data chain pointer extension, */
1567 								/* but this was never implemented. */
1568 	uint8_t flags;            /* file status flags (see enum above) */
1569 	uint8_t recsperphysrec;   /* logical records per physrec */
1570 								/* ignored for variable length record files and */
1571 								/* program files */
1572 	UINT16BE fphysrecs;     /* file length in physrecs */
1573 								/* Note that the HFDC defines this field as the */
1574 								/* number of allocated physrecs in the cluster */
1575 								/* chain (i.e. rounded on the next AU */
1576 								/* boundary), so level-3 routines should use */
1577 								/* the fixrecs field instead to determine the */
1578 								/* logical length of field.  IIRC, the HFDC */
1579 								/* implementation is regarded as a bug because */
1580 								/* program files do not define the fixrecs field */
1581 								/* field, so program field saved by the HFDC */
1582 								/* DSR may be larger than they should. */
1583 	uint8_t eof;              /* EOF offset in last physrec for variable length */
1584 								/* record files and program files (0->256) */
1585 	uint8_t reclen;           /* logical record size in bytes ([1,255] 0->256) */
1586 								/* Maximum allowable record size for variable */
1587 								/* length record files.  Reserved for program */
1588 								/* files (set to 0).  Set to 0 if reclen >=256 */
1589 								/* (HFDC only). */
1590 	UINT16LE fixrecs;       /* file length in logical records */
1591 								/* For variable length record files, number of */
1592 								/* 256-byte records actually used. */
1593 	ti99_date_time creation;/* date and time of creation (HFDC and BwG only; */
1594 								/* reserved in TI) */
1595 	ti99_date_time update;  /* date and time of last write to file (HFDC and */
1596 								/* BwG only; reserved in TI) */
1597 	uint8_t clusters[76][3];  /* data cluster table: 0 through 76 entries (3 */
1598 								/* bytes each), one entry for each file cluster. */
1599 								/* 12 bits: address of first AU of cluster */
1600 								/* 12 bits: offset of last 256-byte record in cluster */
1601 };
1602 
1603 /*
1604     WIN FDR record
1605 */
1606 struct win_fdr
1607 {
1608 	char name[10];          /* file name (10 characters, pad with spaces) */
1609 	UINT16BE xreclen;       /* extended record len: if record len is >= 256, */
1610 								/* reclen is set to 0 and the actual reclen is */
1611 								/* stored here (Myarc HFDC only).  TI reserved */
1612 								/* this  field for data chain pointer extension, */
1613 								/* but this was never implemented. */
1614 	uint8_t flags;            /* file status flags (see enum above) */
1615 	uint8_t recsperphysrec;   /* logical records per physrec */
1616 								/* ignored for variable length record files and */
1617 								/* program files */
1618 	UINT16BE fphysrecs_LSW; /* eldest FDR: file length in physrecs */
1619 								/* Note that the HFDC defines this field as the */
1620 								/* number of allocated physrecs in the cluster */
1621 								/* chain (i.e. rounded on the next AU */
1622 								/* boundary), so level-3 routines should use */
1623 								/* the fixrecs field instead to determine the */
1624 								/* logical length of field.  IIRC, the HFDC */
1625 								/* implementation is regarded as a bug because */
1626 								/* program files do not define the fixrecs field */
1627 								/* field, so program field saved by the HFDC */
1628 								/* DSR may be larger than they should. */
1629 							/* other sibling FDRs: index of the first file */
1630 								/* physrec in this particular sibling FDR */
1631 	uint8_t eof;              /* EOF offset in last physrec for variable length */
1632 								/* record files and program files (0->256)*/
1633 	uint8_t reclen;           /* logical record size in bytes ([1,255]) */
1634 								/* Maximum allowable record size for variable */
1635 								/* length record files.  Reserved for program */
1636 								/* files (set to 0).  Set to 0 if reclen >=256 */
1637 								/* (HFDC only). */
1638 	UINT16LE fixrecs_LSW;   /* file length in logical records */
1639 								/* For variable length record files, number of */
1640 								/* 256-byte records actually used. */
1641 	ti99_date_time creation;/* date and time of creation */
1642 	ti99_date_time update;  /* date and time of last write to file */
1643 
1644 	char id[2];             /* 'FI' */
1645 	UINT16BE prevsibFDR_AU; /* address of the AU where previous sibling FDR is */
1646 								/* (see also xinfo_LSB) */
1647 	UINT16BE nextsibFDR_AU; /* address of the AU where next sibling FDR is */
1648 								/* (see also xinfo_LSB) */
1649 	UINT16BE sibFDR_AUlen;  /* total number of data AUs allocated in this particular sibling FDR */
1650 	UINT16BE parent_FDIR_AU;/* FDIR the file is listed in */
1651 	uint8_t xinfo_MSB;        /* extended information (MSByte) */
1652 								/* bits 0-3: MSN of fphysrecs */
1653 								/* bits 4-7: MSN of fixrecs */
1654 	uint8_t xinfo_LSB;        /* extended information (LSByte) */
1655 								/* bits 8-11: physrec offset within AU for */
1656 									/* previous sibling FDR (see prevsibFDR_AU) */
1657 								/* bits 12-15: physrec offset within AU for */
1658 									/* next sibling FDR (see nextsibFDR_AU) */
1659 	UINT16BE clusters[54][2];/* data cluster table: 0 through 54 entries (4 */
1660 								/* bytes each), one entry for each file cluster. */
1661 								/* 16 bits: address of first AU of cluster */
1662 								/* 16 bits: address of last AU of cluster */
1663 };
1664 
1665 /*
1666     tifile header: stand-alone file
1667 */
1668 struct tifile_header
1669 {
1670 	char tifiles[8];        /* always '\7TIFILES' */
1671 	UINT16BE fphysrecs;     /* file length in physrecs */
1672 	uint8_t flags;            /* see enum above */
1673 	uint8_t recsperphysrec;   /* records per physrec */
1674 	uint8_t eof;              /* current position of eof in last physrec (0->255)*/
1675 	uint8_t reclen;           /* bytes per record ([1,255] 0->256) */
1676 	UINT16BE fixrecs;       /* file length in records */
1677 	uint8_t res[128-16];      /* reserved */
1678 								/* * variant a: */
1679 									/* 112 chars: 0xCA53 repeated 56 times */
1680 								/* * variant b: */
1681 									/* 4 chars: unknown */
1682 									/* 108 chars: 0xCA53 repeated 54 times */
1683 								/* * variant c: */
1684 									/* 10 chars: original TI file name filed with spaces */
1685 									/* 102 chars: spaces */
1686 								/* * variant d: */
1687 									/* 10 chars: original TI file name filed with spaces */
1688 									/* 2 chars: CR+LF */
1689 									/* 98 chars: spaces */
1690 									/* 2 chars: CR+LF */
1691 								/* * variant e: */
1692 									/* 10 chars: original TI file name */
1693 									/* 4 bytes: unknown */
1694 									/* 4 bytes: time & date of creation */
1695 									/* 4 bytes: time & date of last update */
1696 									/* 90 chars: spaces */
1697 								/* * variant f: */
1698 									/* 6 bytes: 'MYTERM' */
1699 									/* 4 bytes: time & date of creation */
1700 									/* 4 bytes: time & date of last update */
1701 									/* 2 bytes: unknown (always >0000) */
1702 									/* 96 chars: 0xCA53 repeated 56 times */
1703 };
1704 
1705 /*
1706     level-2 file descriptor
1707 */
1708 struct ti99_lvl2_fileref_dsk
1709 {
1710 	struct ti99_lvl2_imgref *l2_img;
1711 	int fdr_aphysrec;
1712 	dsk_fdr fdr;
1713 };
1714 
1715 struct ti99_lvl2_fileref_win
1716 {
1717 	struct ti99_lvl2_imgref *l2_img;
1718 	unsigned fphysrecs;             /* copy of field in the eldest FDR */
1719 	unsigned eldestfdr_aphysrec;    /* aphysrec address of the eldest FDR */
1720 	unsigned curfdr_aphysrec;       /* aphysrec address of the currently open sibling FDR */
1721 	win_fdr curfdr;                 /* buffer with currently open sibling FDR */
1722 };
1723 
1724 struct ti99_lvl2_fileref_tifiles
1725 {
1726 	imgtool::stream *file_handle;
1727 	tifile_header hdr;
1728 };
1729 
1730 enum l2f_type_t
1731 {
1732 	L2F_DSK,
1733 	L2F_WIN,
1734 	L2F_TIFILES
1735 };
1736 
1737 struct ti99_lvl2_fileref
1738 {
1739 	l2f_type_t type;
1740 	union
1741 	{
1742 		ti99_lvl2_fileref_dsk dsk;
1743 		ti99_lvl2_fileref_win win;
1744 		ti99_lvl2_fileref_tifiles tifiles;
1745 	};
1746 };
1747 
get_lvl2_imgref(imgtool::image & image)1748 static struct ti99_lvl2_imgref *get_lvl2_imgref(imgtool::image &image)
1749 {
1750 	return (struct ti99_lvl2_imgref *) image.extra_bytes();
1751 }
1752 
1753 /*
1754     Compare two (possibly empty) catalog entry for qsort
1755 */
cat_file_compare_qsort(const void * p1,const void * p2)1756 static int cat_file_compare_qsort(const void *p1, const void *p2)
1757 {
1758 	const file_entry *entry1 = (const file_entry *)p1;
1759 	const file_entry *entry2 = (const file_entry *)p2;
1760 
1761 	if ((entry1->fdr_ptr == 0) && (entry2->fdr_ptr == 0))
1762 		return 0;
1763 	else if (entry1->fdr_ptr == 0)
1764 		return +1;
1765 	else if (entry2->fdr_ptr == 0)
1766 		return -1;
1767 	else
1768 		return memcmp(entry1->name, entry2->name, 10);
1769 }
1770 
cat_dir_compare_qsort(const void * p1,const void * p2)1771 static int cat_dir_compare_qsort(const void *p1, const void *p2)
1772 {
1773 	const dir_entry *entry1 = (const dir_entry *)p1;
1774 	const dir_entry *entry2 = (const dir_entry *)p2;
1775 
1776 	if ((entry1->dir_ptr == 0) && (entry2->dir_ptr == 0))
1777 		return 0;
1778 	else if (entry1->dir_ptr == 0)
1779 		return +1;
1780 	else if (entry2->dir_ptr == 0)
1781 		return -1;
1782 	else
1783 		return memcmp(entry1->name, entry2->name, 10);
1784 }
1785 
1786 /*
1787     Read a directory catalog from disk image
1788 
1789     l2_img: image reference
1790     aphysrec: physical record address of the FDIR
1791     dest: pointer to the destination buffer where the catalog should be written
1792 
1793     Return an error code if there was an error, 0 otherwise.
1794 */
dsk_read_catalog(struct ti99_lvl2_imgref * l2_img,int aphysrec,ti99_catalog * dest)1795 static int dsk_read_catalog(struct ti99_lvl2_imgref *l2_img, int aphysrec, ti99_catalog *dest)
1796 {
1797 	int totphysrecs = l2_img->dsk.totphysrecs;
1798 	UINT16BE fdir_buf[128];
1799 	dsk_fdr fdr;
1800 	int i;
1801 	int reply;
1802 
1803 
1804 	/* Read FDIR record */
1805 	reply = read_absolute_physrec(& l2_img->l1_img, aphysrec, fdir_buf);
1806 	if (reply)
1807 		return IMGTOOLERR_READERROR;
1808 
1809 	/* Copy FDIR info to catalog structure */
1810 	for (i=0; i<128; i++)
1811 		dest->files[i].fdr_ptr = get_UINT16BE(fdir_buf[i]);
1812 
1813 	/* Check FDIR pointers and check and extract file names from DDRs */
1814 	for (i=0; i<128; i++)
1815 	{
1816 		if (dest->files[i].fdr_ptr >= totphysrecs)
1817 		{
1818 			return IMGTOOLERR_CORRUPTIMAGE;
1819 		}
1820 		else if (dest->files[i].fdr_ptr)
1821 		{
1822 			reply = read_absolute_physrec(& l2_img->l1_img, dest->files[i].fdr_ptr, &fdr);
1823 			if (reply)
1824 				return IMGTOOLERR_READERROR;
1825 
1826 			/* check and copy file name */
1827 			if (check_fname(fdr.name))
1828 				return IMGTOOLERR_CORRUPTIMAGE;
1829 			memcpy(dest->files[i].name, fdr.name, 10);
1830 		}
1831 	}
1832 
1833 	/* Check catalog */
1834 	for (i=0; i<127; i++)
1835 	{
1836 		if (((! dest->files[i].fdr_ptr) && dest->files[i+1].fdr_ptr)
1837 			|| ((dest->files[i].fdr_ptr && dest->files[i+1].fdr_ptr) && (memcmp(dest->files[i].name, dest->files[i+1].name, 10) >= 0)))
1838 		{
1839 			/* if the catalog is not sorted, we repair it */
1840 			qsort(dest->files, ARRAY_LENGTH(dest->files), sizeof(dest->files[0]),
1841 					cat_file_compare_qsort);
1842 			break;
1843 		}
1844 	}
1845 
1846 	/* Set file count */
1847 	for (i=0; (i<128) && (dest->files[i].fdr_ptr != 0); i++)
1848 		;
1849 	dest->num_files = i;
1850 
1851 	/* Set subdir count to 0 (subdirs are loaded elsewhere) */
1852 	dest->num_subdirs = 0;
1853 
1854 	return 0;
1855 }
1856 
1857 /*
1858     Read a directory catalog from disk image
1859 
1860     l2_img: image reference
1861     DDR_AU: AU address of the VIB/DDR
1862     dest: pointer to the destination buffer where the catalog should be written
1863 
1864     Return an error code if there was an error, 0 otherwise.
1865 */
win_read_catalog(struct ti99_lvl2_imgref * l2_img,int DDR_AU,ti99_catalog * dest)1866 static int win_read_catalog(struct ti99_lvl2_imgref *l2_img, int DDR_AU, ti99_catalog *dest)
1867 {
1868 	win_vib_ddr ddr_buf;
1869 	UINT16BE fdir_buf[128];
1870 	win_fdr fdr_buf;
1871 	int i;
1872 	int reply;
1873 
1874 
1875 	/* Read DDR record */
1876 	reply = read_absolute_physrec(& l2_img->l1_img, DDR_AU*l2_img->AUformat.physrecsperAU, &ddr_buf);
1877 	if (reply)
1878 		return IMGTOOLERR_READERROR;
1879 
1880 	/* sanity checks */
1881 	if ((ddr_buf.num_files > 127) || (ddr_buf.num_subdirs > 114) || (get_UINT16BE(ddr_buf.fdir_AU) > l2_img->AUformat.totAUs))
1882 		return IMGTOOLERR_CORRUPTIMAGE;
1883 
1884 	/* set file count and subdir count */
1885 	dest->num_files = ddr_buf.num_files;
1886 	dest->num_subdirs = ddr_buf.num_subdirs;
1887 
1888 	/* Copy DDR info to catalog structure */
1889 	for (i=0; i<ddr_buf.num_subdirs; i++)
1890 		dest->subdirs[i].dir_ptr = get_UINT16BE(ddr_buf.subdir_AU[i]);
1891 
1892 	/* Read FDIR record */
1893 	reply = read_absolute_physrec(& l2_img->l1_img, get_UINT16BE(ddr_buf.fdir_AU)*l2_img->AUformat.physrecsperAU, fdir_buf);
1894 	if (reply)
1895 		return IMGTOOLERR_READERROR;
1896 
1897 	/* Copy FDIR info to catalog structure */
1898 	for (i=0; i<dest->num_files; i++)
1899 		dest->files[i].fdr_ptr = get_UINT16BE(fdir_buf[i]);
1900 
1901 	/* Check DDR pointers and check and extract file names from FDRs */
1902 	for (i=0; i<dest->num_subdirs; i++)
1903 	{
1904 		if (dest->subdirs[i].dir_ptr >= l2_img->AUformat.totAUs)
1905 		{
1906 			return IMGTOOLERR_CORRUPTIMAGE;
1907 		}
1908 		else if (dest->subdirs[i].dir_ptr)
1909 		{
1910 			reply = read_absolute_physrec(& l2_img->l1_img, dest->subdirs[i].dir_ptr*l2_img->AUformat.physrecsperAU, &ddr_buf);
1911 			if (reply)
1912 				return IMGTOOLERR_READERROR;
1913 
1914 			/* check and copy file name */
1915 			if (check_fname(ddr_buf.name))
1916 				return IMGTOOLERR_CORRUPTIMAGE;
1917 			memcpy(dest->subdirs[i].name, ddr_buf.name, 10);
1918 		}
1919 	}
1920 
1921 	/* Check FDIR pointers and check and extract file names from FDRs */
1922 	for (i=0; i<dest->num_files; i++)
1923 	{
1924 		if (dest->files[i].fdr_ptr >= l2_img->AUformat.totAUs)
1925 		{
1926 			return IMGTOOLERR_CORRUPTIMAGE;
1927 		}
1928 		else if (dest->files[i].fdr_ptr)
1929 		{
1930 			reply = read_absolute_physrec(& l2_img->l1_img, dest->files[i].fdr_ptr*l2_img->AUformat.physrecsperAU, &fdr_buf);
1931 			if (reply)
1932 				return IMGTOOLERR_READERROR;
1933 
1934 			/* check and copy file name */
1935 			if (check_fname(fdr_buf.name))
1936 				return IMGTOOLERR_CORRUPTIMAGE;
1937 			memcpy(dest->files[i].name, fdr_buf.name, 10);
1938 		}
1939 	}
1940 
1941 	/* Check catalog */
1942 
1943 	/* Check subdir order */
1944 	for (i=0; i<dest->num_subdirs-1; i++)
1945 	{
1946 		if (((! dest->subdirs[i].dir_ptr) && dest->subdirs[i+1].dir_ptr)
1947 			|| ((dest->subdirs[i].dir_ptr && dest->subdirs[i+1].dir_ptr) && (memcmp(dest->subdirs[i].name, dest->subdirs[i+1].name, 10) >= 0)))
1948 		{
1949 			/* if the subdir catalog is not sorted, we repair it */
1950 			qsort(dest->subdirs, dest->num_subdirs, sizeof(dest->subdirs[0]), cat_dir_compare_qsort);
1951 			break;
1952 		}
1953 	}
1954 
1955 	/* Fix subdir count */
1956 	while (dest->num_subdirs && (dest->subdirs[dest->num_subdirs-1].dir_ptr == 0))
1957 		dest->num_subdirs--;
1958 
1959 	/* Check file order */
1960 	for (i=0; i<dest->num_files-1; i++)
1961 	{
1962 		if (((! dest->files[i].fdr_ptr) && dest->files[i+1].fdr_ptr)
1963 			|| ((dest->files[i].fdr_ptr && dest->files[i+1].fdr_ptr) && (memcmp(dest->files[i].name, dest->files[i+1].name, 10) >= 0)))
1964 		{
1965 			/* if the file catalog is not sorted, we repair it */
1966 			qsort(dest->files, dest->num_files, sizeof(dest->files[0]), cat_file_compare_qsort);
1967 			break;
1968 		}
1969 	}
1970 
1971 	/* Fix file count */
1972 	while (dest->num_files && (dest->files[dest->num_files-1].fdr_ptr == 0))
1973 		dest->num_files--;
1974 
1975 	return 0;
1976 }
1977 
1978 /*
1979     Search for a file path on a floppy image
1980 
1981     l2_img: image reference
1982     fpath: path of the file to search
1983     parent_ref_valid: set to true if either the file was found or the file was
1984         not found but its parent dir was
1985     parent_ref: reference to parent dir (0 for root)
1986     out_is_dir: true if element is a directory
1987     catalog_index: on output, index of file catalog entry (may be NULL)
1988 */
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)1989 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)
1990 {
1991 	int i;
1992 	const ti99_catalog *cur_catalog;
1993 	const char *element_start, *element_end;
1994 	int element_len;
1995 	char element[10];
1996 	int is_dir = false;
1997 
1998 
1999 	cur_catalog = & l2_img->dsk.catalogs[0];
2000 	if (parent_ref_valid)
2001 		(* parent_ref_valid) = false;
2002 	if (parent_ref)
2003 		*parent_ref = 0;
2004 
2005 	element_start = fpath;
2006 	do
2007 	{
2008 		/* find next path element */
2009 		element_end = strchr(element_start, '.');
2010 		element_len = element_end ? (element_end - element_start) : strlen(element_start);
2011 		if ((element_len > 10) || (element_len == 0))
2012 			return IMGTOOLERR_BADFILENAME;
2013 		/* last path element */
2014 		if ((!element_end) && parent_ref_valid)
2015 			(* parent_ref_valid) = true;
2016 
2017 		/* generate file name */
2018 		memcpy(element, element_start, element_len);
2019 		memset(element+element_len, ' ', 10-element_len);
2020 
2021 		/* search entry in subdirectories */
2022 		for (i = 0; i<cur_catalog->num_subdirs; i++)
2023 		{
2024 			if (! memcmp(element, cur_catalog->subdirs[i].name, 10))
2025 			{
2026 				is_dir = true;
2027 				break;
2028 			}
2029 		}
2030 
2031 		/* if it failed, search entry in files */
2032 		if (i == cur_catalog->num_subdirs)
2033 		{
2034 			for (i = 0; i<cur_catalog->num_files; i++)
2035 			{
2036 				if (! memcmp(element, cur_catalog->files[i].name, 10))
2037 				{
2038 					is_dir = false;
2039 					break;
2040 				}
2041 			}
2042 			/* exit if not found */
2043 			if (i == cur_catalog->num_files)
2044 			{
2045 				return IMGTOOLERR_FILENOTFOUND;
2046 			}
2047 		}
2048 
2049 		/* iterate */
2050 		if (element_end)
2051 		{
2052 			element_start = element_end+1;
2053 
2054 			if (! is_dir)
2055 				/* this is not a directory */
2056 				return IMGTOOLERR_BADFILENAME;
2057 
2058 			/* initialize cur_catalog */
2059 			cur_catalog = & l2_img->dsk.catalogs[i+1];
2060 			if (parent_ref)
2061 				*parent_ref = i+1;
2062 		}
2063 		else
2064 			element_start = NULL;
2065 	}
2066 	while (element_start);
2067 
2068 	if (out_is_dir)
2069 		*out_is_dir = is_dir;
2070 
2071 	if (catalog_index)
2072 		*catalog_index = i;
2073 
2074 	return 0;
2075 }
2076 
2077 /*
2078     Search for a file path on a harddisk image
2079 
2080     l2_img: image reference
2081     fpath: path of the file to search
2082     parent_ref_valid: set to true if either the file was found or the file was
2083         not found but its parent dir was
2084     parent_ddr_AU: parent DDR AU address (0 for root)
2085     parent_catalog: catalog of parent dir (cannot be NULL)
2086     out_is_dir: true if element is a directory
2087     catalog_index: on output, index of file catalog entry (may be NULL)
2088 */
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)2089 static int win_find_catalog_entry(struct ti99_lvl2_imgref *l2_img, const char *fpath,
2090 									int *parent_ref_valid, int *parent_ddr_AU, ti99_catalog *parent_catalog,
2091 									int *out_is_dir, int *catalog_index)
2092 {
2093 	int i;
2094 	const char *element_start, *element_end;
2095 	int element_len;
2096 	char element[10];
2097 	int is_dir = false;
2098 	int errorcode;
2099 
2100 	if (parent_ref_valid)
2101 		(* parent_ref_valid) = false;
2102 	if (parent_ddr_AU)
2103 		*parent_ddr_AU = 0;
2104 
2105 	errorcode = win_read_catalog(l2_img, 0, parent_catalog);
2106 	if (errorcode)
2107 		return errorcode;
2108 
2109 	element_start = fpath;
2110 	do
2111 	{
2112 		/* find next path element */
2113 		element_end = strchr(element_start, '.');
2114 		element_len = element_end ? (element_end - element_start) : strlen(element_start);
2115 		if ((element_len > 10) || (element_len == 0))
2116 			return IMGTOOLERR_BADFILENAME;
2117 		/* last path element */
2118 		if ((!element_end) && parent_ref_valid)
2119 			(* parent_ref_valid) = true;
2120 
2121 		/* generate file name */
2122 		memcpy(element, element_start, element_len);
2123 		memset(element+element_len, ' ', 10-element_len);
2124 
2125 		/* search entry in subdirectories */
2126 		for (i = 0; i<parent_catalog->num_subdirs; i++)
2127 		{
2128 			if (! memcmp(element, parent_catalog->subdirs[i].name, 10))
2129 			{
2130 				is_dir = true;
2131 				break;
2132 			}
2133 		}
2134 
2135 		/* if it failed, search entry in files */
2136 		if (i == parent_catalog->num_subdirs)
2137 		{
2138 			for (i = 0; i<parent_catalog->num_files; i++)
2139 			{
2140 				if (! memcmp(element, parent_catalog->files[i].name, 10))
2141 				{
2142 					is_dir = false;
2143 					break;
2144 				}
2145 			}
2146 			/* exit if not found */
2147 			if (i == parent_catalog->num_files)
2148 			{
2149 				return IMGTOOLERR_FILENOTFOUND;
2150 			}
2151 		}
2152 
2153 		/* iterate */
2154 		if (element_end)
2155 		{
2156 			element_start = element_end+1;
2157 
2158 			if (! is_dir)
2159 				/* this is not a directory */
2160 				return IMGTOOLERR_BADFILENAME;
2161 
2162 			if (parent_ddr_AU)
2163 				*parent_ddr_AU = parent_catalog->subdirs[i].dir_ptr;
2164 
2165 			errorcode = win_read_catalog(l2_img, parent_catalog->subdirs[i].dir_ptr, parent_catalog);
2166 			if (errorcode)
2167 				return errorcode;
2168 		}
2169 		else
2170 			element_start = NULL;
2171 	}
2172 	while (element_start);
2173 
2174 	if (out_is_dir)
2175 		*out_is_dir = is_dir;
2176 
2177 	if (catalog_index)
2178 		*catalog_index = i;
2179 
2180 	return 0;
2181 }
2182 
2183 /*
2184     Allocate one AU on disk, for use as a fdr record
2185 
2186     l2_img: image reference
2187     fdr_AU: on output, address of allocated AU
2188 */
alloc_fdr_AU(struct ti99_lvl2_imgref * l2_img,unsigned * fdr_AU)2189 static int alloc_fdr_AU(struct ti99_lvl2_imgref *l2_img, unsigned *fdr_AU)
2190 {
2191 	int totAUs = l2_img->AUformat.totAUs;
2192 	int i;
2193 
2194 	for (i=0; i<totAUs; i++)
2195 	{
2196 		if (! (l2_img->abm[i >> 3] & (1 << (i & 7))))
2197 		{
2198 			*fdr_AU = i;
2199 			l2_img->abm[i >> 3] |= 1 << (i & 7);
2200 
2201 			return 0;
2202 		}
2203 	}
2204 
2205 	return IMGTOOLERR_NOSPACE;
2206 }
2207 
get_dsk_fdr_cluster_baseAU(struct ti99_lvl2_imgref * l2_img,dsk_fdr * fdr,int cluster_index)2208 static inline int get_dsk_fdr_cluster_baseAU(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index)
2209 {
2210 	int reply;
2211 
2212 	/* read base AU/physrec for this cluster */
2213 	reply = ((fdr->clusters[cluster_index][1] & 0xf) << 8) | fdr->clusters[cluster_index][0];
2214 	/* convert to AU address */
2215 	if (l2_img->AUformat.physrecsperAU <= 2)
2216 		reply /= l2_img->AUformat.physrecsperAU;
2217 
2218 	return reply;
2219 }
2220 
get_dsk_fdr_cluster_baseaphysrec(struct ti99_lvl2_imgref * l2_img,dsk_fdr * fdr,int cluster_index)2221 static inline int get_dsk_fdr_cluster_baseaphysrec(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index)
2222 {
2223 	int reply;
2224 
2225 	/* read base AU/physrec for this cluster */
2226 	reply = ((fdr->clusters[cluster_index][1] & 0xf) << 8) | fdr->clusters[cluster_index][0];
2227 	/* convert to physrec address */
2228 	if (l2_img->AUformat.physrecsperAU > 2)
2229 		reply *= l2_img->AUformat.physrecsperAU;
2230 
2231 	return reply;
2232 }
2233 
get_dsk_fdr_cluster_lastfphysrec(dsk_fdr * fdr,int cluster_index)2234 static inline int get_dsk_fdr_cluster_lastfphysrec(dsk_fdr *fdr, int cluster_index)
2235 {
2236 	return (fdr->clusters[cluster_index][2] << 4) | (fdr->clusters[cluster_index][1] >> 4);
2237 }
2238 
set_dsk_fdr_cluster_lastfphysrec(dsk_fdr * fdr,int cluster_index,int data)2239 static inline void set_dsk_fdr_cluster_lastfphysrec(dsk_fdr *fdr, int cluster_index, int data)
2240 {
2241 	fdr->clusters[cluster_index][1] = (fdr->clusters[cluster_index][1] & 0x0f) | (data << 4);
2242 	fdr->clusters[cluster_index][2] = data >> 4;
2243 }
2244 
set_dsk_fdr_cluster(struct ti99_lvl2_imgref * l2_img,dsk_fdr * fdr,int cluster_index,int baseAU,int lastfphysrec)2245 static inline void set_dsk_fdr_cluster(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index, int baseAU, int lastfphysrec)
2246 {
2247 	/* convert AU address to FDR value */
2248 	if (l2_img->AUformat.physrecsperAU <= 2)
2249 		baseAU *= l2_img->AUformat.physrecsperAU;
2250 
2251 	/* write cluster entry */
2252 	fdr->clusters[cluster_index][0] = baseAU;
2253 	fdr->clusters[cluster_index][1] = ((baseAU >> 8) & 0x0f) | (lastfphysrec << 4);
2254 	fdr->clusters[cluster_index][2] = lastfphysrec >> 4;
2255 }
2256 
get_win_fdr_fphysrecs(win_fdr * fdr)2257 static inline unsigned get_win_fdr_fphysrecs(win_fdr *fdr)
2258 {
2259 	return (((unsigned) fdr->xinfo_MSB << 12) & 0xf0000) | get_UINT16BE(fdr->fphysrecs_LSW);
2260 }
2261 
set_win_fdr_fphysrecs(win_fdr * fdr,unsigned data)2262 static inline void set_win_fdr_fphysrecs(win_fdr *fdr, unsigned data)
2263 {
2264 	fdr->xinfo_MSB = (fdr->xinfo_MSB & 0x0f) | ((data >> 12) & 0xf0);
2265 	set_UINT16BE(&fdr->fphysrecs_LSW, data & 0xffff);
2266 }
2267 
get_win_fdr_fixrecs(win_fdr * fdr)2268 static inline unsigned get_win_fdr_fixrecs(win_fdr *fdr)
2269 {
2270 	return (((unsigned) fdr->xinfo_MSB << 16) & 0xf0000) | get_UINT16LE(fdr->fixrecs_LSW);
2271 }
2272 
set_win_fdr_fixrecs(win_fdr * fdr,unsigned data)2273 static inline void set_win_fdr_fixrecs(win_fdr *fdr, unsigned data)
2274 {
2275 	fdr->xinfo_MSB = (fdr->xinfo_MSB & 0xf0) | ((data >> 16) & 0x0f);
2276 	set_UINT16LE(&fdr->fixrecs_LSW, data & 0xffff);
2277 }
2278 
get_win_fdr_prevsibFDR_aphysrec(struct ti99_lvl2_imgref * l2_img,win_fdr * fdr)2279 static inline unsigned get_win_fdr_prevsibFDR_aphysrec(struct ti99_lvl2_imgref *l2_img, win_fdr *fdr)
2280 {
2281 	unsigned prevsibFDR_AU = get_UINT16BE(fdr->prevsibFDR_AU);
2282 
2283 	return prevsibFDR_AU
2284 			? (prevsibFDR_AU * l2_img->AUformat.physrecsperAU + ((fdr->xinfo_LSB >> 4) & 0xf))
2285 			: 0;
2286 }
2287 
get_win_fdr_nextsibFDR_aphysrec(struct ti99_lvl2_imgref * l2_img,win_fdr * fdr)2288 static inline unsigned get_win_fdr_nextsibFDR_aphysrec(struct ti99_lvl2_imgref *l2_img, win_fdr *fdr)
2289 {
2290 	unsigned nextsibFDR_AU = get_UINT16BE(fdr->nextsibFDR_AU);
2291 
2292 	return nextsibFDR_AU
2293 			? (nextsibFDR_AU * l2_img->AUformat.physrecsperAU + (fdr->xinfo_LSB & 0xf))
2294 			: 0;
2295 }
2296 
get_win_fdr_cursibFDR_basefphysrec(win_fdr * fdr)2297 static inline unsigned get_win_fdr_cursibFDR_basefphysrec(win_fdr *fdr)
2298 {
2299 	return get_UINT16BE(fdr->prevsibFDR_AU) ? get_win_fdr_fphysrecs(fdr) : 0;
2300 }
2301 
2302 /*
2303     Advance to next sibling FDR
2304 */
win_goto_next_sibFDR(ti99_lvl2_fileref_win * win_file)2305 static int win_goto_next_sibFDR(ti99_lvl2_fileref_win *win_file)
2306 {
2307 	if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) == 0)
2308 		return IMGTOOLERR_UNEXPECTED;
2309 
2310 	win_file->curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(win_file->l2_img, &win_file->curfdr);
2311 	if (read_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
2312 		return IMGTOOLERR_READERROR;
2313 
2314 	return 0;
2315 }
2316 
2317 /*
2318     Back to previous sibling FDR
2319 */
win_goto_prev_sibFDR(ti99_lvl2_fileref_win * win_file)2320 static int win_goto_prev_sibFDR(ti99_lvl2_fileref_win *win_file)
2321 {
2322 	if (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) == 0)
2323 		return IMGTOOLERR_UNEXPECTED;
2324 
2325 	win_file->curfdr_aphysrec = get_win_fdr_prevsibFDR_aphysrec(win_file->l2_img, &win_file->curfdr);
2326 	if (read_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
2327 		return IMGTOOLERR_READERROR;
2328 
2329 	return 0;
2330 }
2331 
2332 /*
2333     Append a new sibling FDR at the end of the sibling FDR list, and open it.
2334 
2335     You must have gone to the end of the list.
2336 */
win_alloc_sibFDR(ti99_lvl2_fileref_win * win_file)2337 static int win_alloc_sibFDR(ti99_lvl2_fileref_win *win_file)
2338 {
2339 	unsigned oldfdr_AU, oldfdr_physrecinAU;
2340 	unsigned newfdr_AU, newfdr_physrecinAU;
2341 	int allocated = false;
2342 	int errorcode;
2343 	unsigned cursibFDR_basefphysrec;
2344 
2345 	if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU))
2346 		return IMGTOOLERR_UNEXPECTED;
2347 
2348 	oldfdr_AU = win_file->curfdr_aphysrec / win_file->l2_img->AUformat.physrecsperAU;
2349 	oldfdr_physrecinAU = win_file->curfdr_aphysrec % win_file->l2_img->AUformat.physrecsperAU;
2350 
2351 	if (oldfdr_physrecinAU != (win_file->l2_img->AUformat.physrecsperAU - 1))
2352 	{   /* current AU is not full */
2353 		newfdr_AU = oldfdr_AU;
2354 		newfdr_physrecinAU = oldfdr_physrecinAU + 1;
2355 	}
2356 	else
2357 	{   /* current AU is full: allocate another */
2358 		errorcode = alloc_fdr_AU(win_file->l2_img, &newfdr_AU);
2359 		if (errorcode)
2360 			return errorcode;
2361 		newfdr_physrecinAU = 0;
2362 		allocated = true;
2363 	}
2364 
2365 	set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, newfdr_AU);
2366 	win_file->curfdr.xinfo_LSB = (win_file->curfdr.xinfo_LSB & 0xf0) | newfdr_physrecinAU;
2367 
2368 	/* save current sibling FDR */
2369 	if (write_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
2370 	{
2371 		/* clear pointer */
2372 		set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, 0);
2373 		win_file->curfdr.xinfo_LSB = win_file->curfdr.xinfo_LSB & 0xf0;
2374 		if (allocated)
2375 			/* free AU */
2376 			win_file->l2_img->abm[newfdr_AU >> 3] |= 1 << (newfdr_AU & 7);
2377 		return IMGTOOLERR_WRITEERROR;
2378 	}
2379 
2380 	/* now update in-memory structure to describe new sibling FDR */
2381 	cursibFDR_basefphysrec = get_win_fdr_cursibFDR_basefphysrec(&win_file->curfdr)
2382 								+ get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU;
2383 
2384 	set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, 0);
2385 	set_UINT16BE(&win_file->curfdr.prevsibFDR_AU, oldfdr_AU);
2386 	win_file->curfdr.xinfo_LSB = oldfdr_physrecinAU << 4;
2387 
2388 	win_file->curfdr_aphysrec = newfdr_AU * win_file->l2_img->AUformat.physrecsperAU + newfdr_physrecinAU;
2389 
2390 	set_win_fdr_fphysrecs(&win_file->curfdr, cursibFDR_basefphysrec);
2391 	set_UINT16BE(&win_file->curfdr.sibFDR_AUlen, 0);
2392 	memset(win_file->curfdr.clusters, 0, sizeof(win_file->curfdr.clusters));
2393 
2394 	return 0;
2395 }
2396 
2397 /*
2398     Extend a file with nb_alloc_physrecs extra physrecs
2399 
2400     dsk_file: file reference
2401     nb_alloc_physrecs: number of physical records to allocate
2402 */
dsk_alloc_file_physrecs(ti99_lvl2_fileref_dsk * dsk_file,int nb_alloc_physrecs)2403 static int dsk_alloc_file_physrecs(ti99_lvl2_fileref_dsk *dsk_file, int nb_alloc_physrecs)
2404 {
2405 	int totAUs = dsk_file->l2_img->AUformat.totAUs;
2406 	int free_physrecs;
2407 	int fphysrecs;
2408 	int i;
2409 	int cluster_index;
2410 	int last_sec, p_last_sec = 0;
2411 	int cur_block_seclen;
2412 	int cluster_baseAU, cluster_AUlen;
2413 	int first_best_block_baseAU = 0, first_best_block_seclen;
2414 	int second_best_block_baseAU = 0, second_best_block_seclen;
2415 	int search_start;
2416 
2417 	/* compute free space */
2418 	free_physrecs = 0;
2419 	for (i=0; i<totAUs; i++)
2420 	{
2421 		if (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
2422 			free_physrecs += dsk_file->l2_img->AUformat.physrecsperAU;
2423 	}
2424 
2425 	/* check we have enough free space */
2426 	if (free_physrecs < nb_alloc_physrecs)
2427 		return IMGTOOLERR_NOSPACE;
2428 
2429 	/* current number of data physrecs in file */
2430 	fphysrecs = get_UINT16BE(dsk_file->fdr.fphysrecs);
2431 
2432 	if (fphysrecs == 0)
2433 	{   /* cluster array must be empty */
2434 		cluster_index = 0;
2435 	}
2436 	else
2437 	{   /* try to extend last block */
2438 		last_sec = -1;
2439 		for (cluster_index=0; cluster_index<76; cluster_index++)
2440 		{
2441 			p_last_sec = last_sec;
2442 			last_sec = get_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index);
2443 			if (last_sec >= (fphysrecs-1))
2444 				break;
2445 		}
2446 		if (cluster_index == 76)
2447 			/* that sucks */
2448 			return IMGTOOLERR_CORRUPTIMAGE;
2449 
2450 		if (last_sec > (fphysrecs-1))
2451 		{   /* some extra space has already been allocated */
2452 			cur_block_seclen = last_sec - (fphysrecs-1);
2453 			if (cur_block_seclen > nb_alloc_physrecs)
2454 				cur_block_seclen = nb_alloc_physrecs;
2455 
2456 			fphysrecs += cur_block_seclen;
2457 			set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
2458 			nb_alloc_physrecs -= cur_block_seclen;
2459 			if (! nb_alloc_physrecs)
2460 				return 0;   /* done */
2461 		}
2462 
2463 		/* round up to next AU boundary */
2464 		last_sec = last_sec + dsk_file->l2_img->AUformat.physrecsperAU - (last_sec % dsk_file->l2_img->AUformat.physrecsperAU) - 1;
2465 
2466 		if (last_sec > (fphysrecs-1))
2467 		{   /* some extra space has already been allocated */
2468 			cur_block_seclen = last_sec - (fphysrecs-1);
2469 			if (cur_block_seclen > nb_alloc_physrecs)
2470 				cur_block_seclen = nb_alloc_physrecs;
2471 
2472 			fphysrecs += cur_block_seclen;
2473 			set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
2474 			set_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index, fphysrecs-1);
2475 			nb_alloc_physrecs -= cur_block_seclen;
2476 			if (! nb_alloc_physrecs)
2477 				return 0;   /* done */
2478 		}
2479 
2480 		/* read base AU address for this cluster */
2481 		cluster_baseAU = get_dsk_fdr_cluster_baseAU(dsk_file->l2_img, &dsk_file->fdr, cluster_index);
2482 		/* point past cluster end */
2483 		cluster_baseAU += (last_sec-p_last_sec/*+file->l2_img->AUformat.physrecsperAU-1*/) / dsk_file->l2_img->AUformat.physrecsperAU;
2484 		/* count free physrecs after last block */
2485 		cur_block_seclen = 0;
2486 		for (i=cluster_baseAU; (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7)))) && (cur_block_seclen < nb_alloc_physrecs) && (i < totAUs); i++)
2487 			cur_block_seclen += dsk_file->l2_img->AUformat.physrecsperAU;
2488 		if (cur_block_seclen)
2489 		{   /* extend last block */
2490 			if (cur_block_seclen > nb_alloc_physrecs)
2491 				cur_block_seclen = nb_alloc_physrecs;
2492 
2493 			fphysrecs += cur_block_seclen;
2494 			set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
2495 			last_sec += cur_block_seclen;
2496 			nb_alloc_physrecs -= cur_block_seclen;
2497 			set_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index, last_sec);
2498 			cluster_AUlen = (cur_block_seclen + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU;
2499 			for (i=0; i<cluster_AUlen; i++)
2500 				dsk_file->l2_img->abm[(i+cluster_baseAU) >> 3] |= 1 << ((i+cluster_baseAU) & 7);
2501 			if (! nb_alloc_physrecs)
2502 				return 0;   /* done */
2503 		}
2504 		cluster_index++;
2505 		if (cluster_index == 76)
2506 			/* that sucks */
2507 			return IMGTOOLERR_NOSPACE;
2508 	}
2509 
2510 	search_start = dsk_file->l2_img->data_offset;   /* initially, search for free space only in data space */
2511 	while (nb_alloc_physrecs)
2512 	{
2513 		/* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */
2514 		first_best_block_seclen = INT_MAX;
2515 		second_best_block_seclen = 0;
2516 		for (i=search_start; i<totAUs; i++)
2517 		{
2518 			if (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
2519 			{   /* found one free block */
2520 				/* compute its length */
2521 				cluster_baseAU = i;
2522 				cur_block_seclen = 0;
2523 				while ((i<totAUs) && (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7)))))
2524 				{
2525 					cur_block_seclen += dsk_file->l2_img->AUformat.physrecsperAU;
2526 					i++;
2527 				}
2528 				/* compare to previous best and second-best blocks */
2529 				if ((cur_block_seclen < first_best_block_seclen) && (cur_block_seclen >= nb_alloc_physrecs))
2530 				{
2531 					first_best_block_baseAU = cluster_baseAU;
2532 					first_best_block_seclen = cur_block_seclen;
2533 					if (cur_block_seclen == nb_alloc_physrecs)
2534 						/* no need to search further */
2535 						break;
2536 				}
2537 				else if ((cur_block_seclen > second_best_block_seclen) && (cur_block_seclen < nb_alloc_physrecs))
2538 				{
2539 					second_best_block_baseAU = cluster_baseAU;
2540 					second_best_block_seclen = cur_block_seclen;
2541 				}
2542 			}
2543 		}
2544 
2545 		if (first_best_block_seclen != INT_MAX)
2546 		{   /* found one contiguous block which can hold it all */
2547 			fphysrecs += nb_alloc_physrecs;
2548 			set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
2549 
2550 			set_dsk_fdr_cluster(dsk_file->l2_img, &dsk_file->fdr, cluster_index, first_best_block_baseAU, fphysrecs-1);
2551 			cluster_AUlen = (nb_alloc_physrecs + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU;
2552 			for (i=0; i<cluster_AUlen; i++)
2553 				dsk_file->l2_img->abm[(i+first_best_block_baseAU) >> 3] |= 1 << ((i+first_best_block_baseAU) & 7);
2554 
2555 			nb_alloc_physrecs = 0;
2556 		}
2557 		else if (second_best_block_seclen != 0)
2558 		{   /* jeez, we need to fragment it.  We use the largest smaller block to limit fragmentation. */
2559 			fphysrecs += second_best_block_seclen;
2560 			set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs);
2561 
2562 			set_dsk_fdr_cluster(dsk_file->l2_img, &dsk_file->fdr, cluster_index, second_best_block_baseAU, fphysrecs-1);
2563 			cluster_AUlen = (second_best_block_seclen + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU;
2564 			for (i=0; i<cluster_AUlen; i++)
2565 				dsk_file->l2_img->abm[(i+second_best_block_baseAU) >> 3] |= 1 << ((i+second_best_block_baseAU) & 7);
2566 
2567 			nb_alloc_physrecs -= second_best_block_seclen;
2568 
2569 			cluster_index++;
2570 			if (cluster_index == 76)
2571 				/* that sucks */
2572 				return IMGTOOLERR_NOSPACE;
2573 		}
2574 		else if (search_start != 0)
2575 		{   /* we did not find any free physrec in the data section of the disk */
2576 			search_start = 0;   /* time to fall back to fdr space */
2577 		}
2578 		else
2579 			return IMGTOOLERR_NOSPACE;  /* This should never happen, as we pre-check that there is enough free space */
2580 	}
2581 
2582 	return 0;
2583 }
2584 
2585 /*
2586     Extend a file with nb_alloc_physrecs extra physrecs
2587 
2588     win_file: file reference
2589     nb_alloc_physrecs: number of physical records to allocate
2590 */
win_alloc_file_physrecs(ti99_lvl2_fileref_win * win_file,int nb_alloc_physrecs)2591 static int win_alloc_file_physrecs(ti99_lvl2_fileref_win *win_file, int nb_alloc_physrecs)
2592 {
2593 	int totAUs = win_file->l2_img->AUformat.totAUs;
2594 	int free_physrecs;
2595 	int fphysrecs;
2596 	int i;
2597 	int cluster_index;
2598 	int num_fphysrec;
2599 	int cur_block_seclen;
2600 	int cluster_baseAU, cluster_AUlen;
2601 	int first_best_block_baseAU = 0, first_best_block_seclen;
2602 	int second_best_block_baseAU = 0, second_best_block_seclen;
2603 	int search_start;
2604 	int errorcode;
2605 
2606 	/* compute free space */
2607 	free_physrecs = 0;
2608 	for (i=0; i<totAUs; i++)
2609 	{
2610 		if (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
2611 			free_physrecs += win_file->l2_img->AUformat.physrecsperAU;
2612 	}
2613 
2614 	/* check we have enough free space */
2615 	if (free_physrecs < nb_alloc_physrecs)
2616 		return IMGTOOLERR_NOSPACE;
2617 
2618 	/* move to last sibling non-empty FDR */
2619 	while ((get_UINT16BE(win_file->curfdr.nextsibFDR_AU) != 0) &&
2620 				(get_UINT16BE(win_file->curfdr.clusters[53][0]) != 0))
2621 	{
2622 		errorcode = win_goto_next_sibFDR(win_file);
2623 		if (errorcode)
2624 			return errorcode;
2625 	}
2626 	if ((get_UINT16BE(win_file->curfdr.clusters[0][0]) == 0) && (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) != 0))
2627 	{   /* this is annoying: we have found a sibling FDR filled with 0s: rewind
2628 	    to last non-empty sibling if applicable */
2629 		errorcode = win_goto_prev_sibFDR(win_file);
2630 		if (errorcode)
2631 			return errorcode;
2632 	}
2633 
2634 	/* current number of data physrecs in file */
2635 	fphysrecs = win_file->fphysrecs;
2636 
2637 	/* current number of allocated physrecs */
2638 	num_fphysrec = get_win_fdr_cursibFDR_basefphysrec(&win_file->curfdr)
2639 						+ get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU;
2640 
2641 	if (num_fphysrec > fphysrecs)
2642 	{   /* some extra space has already been allocated */
2643 		cur_block_seclen = num_fphysrec - fphysrecs;
2644 		if (cur_block_seclen > nb_alloc_physrecs)
2645 			cur_block_seclen = nb_alloc_physrecs;
2646 
2647 		fphysrecs += cur_block_seclen;
2648 		win_file->fphysrecs = fphysrecs;
2649 		/* TODO: update eldest FDR fphysrecs field */
2650 		/*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
2651 		nb_alloc_physrecs -= cur_block_seclen;
2652 		if (! nb_alloc_physrecs)
2653 			return 0;   /* done */
2654 	}
2655 
2656 	/* find last non-empty cluster */
2657 	for (cluster_index=0; (cluster_index<54) && (get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) != 0); cluster_index++)
2658 		;
2659 	/* if we are dealing with an empty file, we will have (cluster_index == 0)... */
2660 	if (cluster_index != 0)
2661 	{
2662 		cluster_index--;
2663 		/* try to extend last cluster */
2664 		/* point past cluster end */
2665 		cluster_baseAU = get_UINT16BE(win_file->curfdr.clusters[cluster_index][1]) + 1;
2666 		/* count free physrecs after last block */
2667 		cur_block_seclen = 0;
2668 		for (i=cluster_baseAU; (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7)))) && (cur_block_seclen < nb_alloc_physrecs) && (i < totAUs); i++)
2669 			cur_block_seclen += win_file->l2_img->AUformat.physrecsperAU;
2670 		if (cur_block_seclen)
2671 		{   /* extend last block */
2672 			if (cur_block_seclen > nb_alloc_physrecs)
2673 				cur_block_seclen = nb_alloc_physrecs;
2674 
2675 			fphysrecs += cur_block_seclen;
2676 			cluster_AUlen = (cur_block_seclen + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU;
2677 			win_file->fphysrecs = fphysrecs;
2678 			/* TODO: update eldest FDR fphysrecs field */
2679 			/*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
2680 			set_UINT16BE(&win_file->curfdr.sibFDR_AUlen,
2681 							get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen);
2682 			set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1],
2683 							get_UINT16BE(win_file->curfdr.clusters[cluster_index][1])+cluster_AUlen);
2684 			for (i=0; i<cluster_AUlen; i++)
2685 				win_file->l2_img->abm[(i+cluster_baseAU) >> 3] |= 1 << ((i+cluster_baseAU) & 7);
2686 			nb_alloc_physrecs -= cur_block_seclen;
2687 			if (! nb_alloc_physrecs)
2688 				return 0;   /* done */
2689 		}
2690 		/* now point to first free entry in cluster table */
2691 		cluster_index++;
2692 	}
2693 
2694 	search_start = win_file->l2_img->data_offset;   /* initially, search for free space only in data space */
2695 	while (nb_alloc_physrecs)
2696 	{
2697 		/* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */
2698 		first_best_block_seclen = INT_MAX;
2699 		second_best_block_seclen = 0;
2700 		for (i=search_start; i<totAUs; i++)
2701 		{
2702 			if (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7))))
2703 			{   /* found one free block */
2704 				/* compute its length */
2705 				cluster_baseAU = i;
2706 				cur_block_seclen = 0;
2707 				while ((i<totAUs) && (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7)))))
2708 				{
2709 					cur_block_seclen += win_file->l2_img->AUformat.physrecsperAU;
2710 					i++;
2711 				}
2712 				/* compare to previous best and second-best blocks */
2713 				if ((cur_block_seclen < first_best_block_seclen) && (cur_block_seclen >= nb_alloc_physrecs))
2714 				{
2715 					first_best_block_baseAU = cluster_baseAU;
2716 					first_best_block_seclen = cur_block_seclen;
2717 					if (cur_block_seclen == nb_alloc_physrecs)
2718 						/* no need to search further */
2719 						break;
2720 				}
2721 				else if ((cur_block_seclen > second_best_block_seclen) && (cur_block_seclen < nb_alloc_physrecs))
2722 				{
2723 					second_best_block_baseAU = cluster_baseAU;
2724 					second_best_block_seclen = cur_block_seclen;
2725 				}
2726 			}
2727 		}
2728 
2729 		if ((first_best_block_seclen != INT_MAX) || (second_best_block_seclen != 0))
2730 		{
2731 			if (cluster_index == 54)
2732 			{
2733 				/* end of cluster list: go to next sibling FDR */
2734 				if (write_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr))
2735 					return IMGTOOLERR_WRITEERROR;
2736 				if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) != 0)
2737 				{   /* read next sibling FDR */
2738 					errorcode = win_goto_next_sibFDR(win_file);
2739 					if (errorcode)
2740 						return errorcode;
2741 				}
2742 				else
2743 				{   /* allocate new sibling FDR */
2744 					errorcode = win_alloc_sibFDR(win_file);
2745 					if (errorcode)
2746 						return errorcode;
2747 				}
2748 				cluster_index = 0;
2749 			}
2750 		}
2751 
2752 		if (first_best_block_seclen != INT_MAX)
2753 		{   /* found one contiguous block which can hold it all */
2754 			fphysrecs += nb_alloc_physrecs;
2755 			cluster_AUlen = (nb_alloc_physrecs + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU;
2756 			win_file->fphysrecs = fphysrecs;
2757 			/* TODO: update eldest FDR fphysrecs field */
2758 			/*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
2759 			set_UINT16BE(&win_file->curfdr.sibFDR_AUlen,
2760 								get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen);
2761 			set_UINT16BE(&win_file->curfdr.clusters[cluster_index][0], first_best_block_baseAU);
2762 			set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1],
2763 								first_best_block_baseAU+cluster_AUlen-1);
2764 
2765 			for (i=0; i<cluster_AUlen; i++)
2766 				win_file->l2_img->abm[(i+first_best_block_baseAU) >> 3] |= 1 << ((i+first_best_block_baseAU) & 7);
2767 
2768 			nb_alloc_physrecs = 0;
2769 		}
2770 		else if (second_best_block_seclen != 0)
2771 		{   /* jeez, we need to fragment it.  We use the largest smaller block to limit fragmentation. */
2772 			fphysrecs += second_best_block_seclen;
2773 			cluster_AUlen = (second_best_block_seclen + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU;
2774 			win_file->fphysrecs = fphysrecs;
2775 			/* TODO: update eldest FDR fphysrecs field */
2776 			/*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/
2777 			set_UINT16BE(&win_file->curfdr.sibFDR_AUlen,
2778 								get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen);
2779 			set_UINT16BE(&win_file->curfdr.clusters[cluster_index][0], second_best_block_baseAU);
2780 			set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1],
2781 								second_best_block_baseAU+cluster_AUlen-1);
2782 
2783 			for (i=0; i<cluster_AUlen; i++)
2784 				win_file->l2_img->abm[(i+second_best_block_baseAU) >> 3] |= 1 << ((i+second_best_block_baseAU) & 7);
2785 
2786 			nb_alloc_physrecs -= second_best_block_seclen;
2787 
2788 			/* now point to first free entry in cluster table */
2789 			cluster_index++;
2790 		}
2791 		else if (search_start != 0)
2792 		{   /* we did not find any free physrec in the data section of the disk */
2793 			search_start = 0;   /* time to fall back to fdr space */
2794 		}
2795 		else
2796 			return IMGTOOLERR_NOSPACE;  /* This should never happen, as we pre-check that there is enough free space */
2797 	}
2798 
2799 	return 0;
2800 }
2801 
2802 /*
2803     Allocate a new (empty) file
2804 */
new_file_lvl2_dsk(struct ti99_lvl2_imgref * l2_img,int parent_ref,char filename[10],struct ti99_lvl2_fileref * l2_file)2805 static int new_file_lvl2_dsk(struct ti99_lvl2_imgref *l2_img, int parent_ref, char filename[10], struct ti99_lvl2_fileref *l2_file)
2806 {
2807 	ti99_catalog *catalog = &l2_img->dsk.catalogs[parent_ref];
2808 	unsigned fdr_AU, fdr_aphysrec;
2809 	int catalog_index, i;
2810 	int reply = 0;
2811 	int errorcode;
2812 
2813 
2814 	if (catalog->num_files >= 127)
2815 		/* if num_files == 128, catalog is full */
2816 		/* if num_files == 127, catalog is not full, but we don't want to write
2817 		a 128th entry for compatibility with some existing DSRs that detect the
2818 		end of the FDIR array with a 0 entry (and do not check that the index
2819 		has not reached 128) */
2820 		return IMGTOOLERR_NOSPACE;
2821 
2822 	/* find insertion point in catalog */
2823 	for (i=0; (i < catalog->num_files) && ((reply = memcmp(catalog->files[i].name, filename, 10)) < 0); i++)
2824 		;
2825 
2826 	if ((i<catalog->num_files) && (reply == 0))
2827 		/* file already exists */
2828 		return IMGTOOLERR_BADFILENAME;
2829 	else
2830 	{
2831 		/* otherwise insert new entry */
2832 		catalog_index = i;
2833 		errorcode = alloc_fdr_AU(l2_img, &fdr_AU);
2834 		if (errorcode)
2835 			return errorcode;
2836 		fdr_aphysrec = fdr_AU * l2_img->AUformat.physrecsperAU;
2837 
2838 		/* shift catalog entries until the insertion point */
2839 		for (i=catalog->num_files; i>catalog_index; i--)
2840 			catalog->files[i] = catalog->files[i-1];
2841 
2842 		/* write new catalog entry */
2843 		catalog->files[catalog_index].fdr_ptr = fdr_aphysrec;
2844 		memcpy(catalog->files[catalog_index].name, filename, 10);
2845 
2846 		/* update catalog len */
2847 		catalog->num_files++;
2848 	}
2849 
2850 	/* set up file handle */
2851 	l2_file->type = L2F_DSK;
2852 	l2_file->dsk.l2_img = l2_img;
2853 	l2_file->dsk.fdr_aphysrec = fdr_aphysrec;
2854 	memset(&l2_file->dsk.fdr, 0, sizeof(l2_file->dsk.fdr));
2855 	memcpy(l2_file->dsk.fdr.name, filename, 10);
2856 
2857 	return 0;
2858 }
2859 
2860 /*
2861     Allocate a new (empty) file
2862 */
new_file_lvl2_win(struct ti99_lvl2_imgref * l2_img,ti99_catalog * parent_catalog,char filename[10],struct ti99_lvl2_fileref * l2_file)2863 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)
2864 {
2865 	unsigned fdr_AU;
2866 	int catalog_index, i;
2867 	int reply = 0;
2868 	int errorcode;
2869 
2870 
2871 	if (parent_catalog->num_files >= 127)
2872 		/* if num_files == 127, catalog is full */
2873 		return IMGTOOLERR_NOSPACE;
2874 
2875 	/* find insertion point in catalog */
2876 	for (i=0; (i < parent_catalog->num_files) && ((reply = memcmp(parent_catalog->files[i].name, filename, 10)) < 0); i++)
2877 		;
2878 
2879 	if ((i<parent_catalog->num_files) && (reply == 0))
2880 		/* file already exists */
2881 		return IMGTOOLERR_BADFILENAME;
2882 	else
2883 	{
2884 		/* otherwise insert new entry */
2885 		catalog_index = i;
2886 		errorcode = alloc_fdr_AU(l2_img, &fdr_AU);
2887 		if (errorcode)
2888 			return errorcode;
2889 
2890 		/* shift catalog entries until the insertion point */
2891 		for (i=parent_catalog->num_files; i>catalog_index; i--)
2892 			parent_catalog->files[i] = parent_catalog->files[i-1];
2893 
2894 		/* write new catalog entry */
2895 		parent_catalog->files[catalog_index].fdr_ptr = fdr_AU;
2896 		memcpy(parent_catalog->files[catalog_index].name, filename, 10);
2897 
2898 		/* update catalog len */
2899 		parent_catalog->num_files++;
2900 	}
2901 
2902 	/* set up file handle */
2903 	l2_file->type = L2F_WIN;
2904 	l2_file->win.l2_img = l2_img;
2905 	l2_file->win.fphysrecs = 0;
2906 	l2_file->win.eldestfdr_aphysrec = fdr_AU * l2_img->AUformat.physrecsperAU;
2907 	l2_file->win.curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
2908 	memset(&l2_file->win.curfdr, 0, sizeof(l2_file->win.curfdr));
2909 	memcpy(l2_file->win.curfdr.name, filename, 10);
2910 
2911 	return 0;
2912 }
2913 
2914 /*
2915     Allocate a new (empty) file
2916 */
new_file_lvl2_tifiles(imgtool::stream & file_handle,struct ti99_lvl2_fileref * l2_file)2917 static int new_file_lvl2_tifiles(imgtool::stream &file_handle, struct ti99_lvl2_fileref *l2_file)
2918 {
2919 	/* set up file handle */
2920 	l2_file->type = L2F_TIFILES;
2921 	l2_file->tifiles.file_handle = &file_handle;
2922 	memset(&l2_file->tifiles.hdr, 0, sizeof(l2_file->tifiles.hdr));
2923 	l2_file->tifiles.hdr.tifiles[0] = '\7';
2924 	l2_file->tifiles.hdr.tifiles[1] = 'T';
2925 	l2_file->tifiles.hdr.tifiles[2] = 'I';
2926 	l2_file->tifiles.hdr.tifiles[3] = 'F';
2927 	l2_file->tifiles.hdr.tifiles[4] = 'I';
2928 	l2_file->tifiles.hdr.tifiles[5] = 'L';
2929 	l2_file->tifiles.hdr.tifiles[6] = 'E';
2930 	l2_file->tifiles.hdr.tifiles[7] = 'S';
2931 
2932 	return 0;
2933 }
2934 
2935 /*
2936     Open an existing file on a floppy image
2937 
2938     l2_img: level 2 image the file is located on
2939     fpath: access path to the file
2940     file: set up if open is successful
2941 */
open_file_lvl2_dsk(struct ti99_lvl2_imgref * l2_img,const char * fpath,struct ti99_lvl2_fileref * l2_file)2942 static int open_file_lvl2_dsk(struct ti99_lvl2_imgref *l2_img, const char *fpath, struct ti99_lvl2_fileref *l2_file)
2943 {
2944 	int parent_ref, is_dir, catalog_index;
2945 	int reply;
2946 
2947 
2948 	reply = dsk_find_catalog_entry(l2_img, fpath, NULL, &parent_ref, &is_dir, &catalog_index);
2949 	if (reply)
2950 		return reply;
2951 
2952 	if (is_dir)
2953 		/* this is not a file */
2954 		return IMGTOOLERR_BADFILENAME;
2955 
2956 	l2_file->type = L2F_DSK;
2957 	l2_file->dsk.l2_img = l2_img;
2958 	l2_file->dsk.fdr_aphysrec = l2_img->dsk.catalogs[parent_ref].files[catalog_index].fdr_ptr;
2959 	if (read_absolute_physrec(& l2_img->l1_img, l2_file->dsk.fdr_aphysrec, &l2_file->dsk.fdr))
2960 		return IMGTOOLERR_READERROR;
2961 
2962 	return 0;
2963 }
2964 
2965 /*
2966     Open an existing file on a harddisk image
2967 
2968     l2_img: level 2 image the file is located on
2969     fpath: access path to the file
2970     file: set up if open is successful
2971 */
open_file_lvl2_win(struct ti99_lvl2_imgref * l2_img,const char * fpath,struct ti99_lvl2_fileref * l2_file)2972 static int open_file_lvl2_win(struct ti99_lvl2_imgref *l2_img, const char *fpath, struct ti99_lvl2_fileref *l2_file)
2973 {
2974 	int parent_ref, is_dir, catalog_index;
2975 	ti99_catalog catalog;
2976 	int reply;
2977 
2978 	reply = win_find_catalog_entry(l2_img, fpath, NULL, &parent_ref, &catalog, &is_dir, &catalog_index);
2979 	if (reply)
2980 		return reply;
2981 
2982 	if (is_dir)
2983 		/* this is not a file */
2984 		return IMGTOOLERR_BADFILENAME;
2985 
2986 	l2_file->type = L2F_WIN;
2987 	l2_file->win.l2_img = l2_img;
2988 	l2_file->win.eldestfdr_aphysrec = catalog.files[catalog_index].fdr_ptr * l2_img->AUformat.physrecsperAU;
2989 	l2_file->win.curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
2990 	if (read_absolute_physrec(& l2_img->l1_img, l2_file->win.curfdr_aphysrec, &l2_file->win.curfdr))
2991 		return IMGTOOLERR_READERROR;
2992 	l2_file->win.fphysrecs = get_win_fdr_fphysrecs(&l2_file->win.curfdr);
2993 
2994 	/* check integrity of FDR sibling chain */
2995 	/* note that as we check that the back chain is consistent with the forward
2996 	chain, we will also detect any cycle in the sibling chain, so we do not
2997 	need to check against them explicitely */
2998 	if (get_UINT16BE(l2_file->win.curfdr.prevsibFDR_AU) != 0)
2999 		return IMGTOOLERR_CORRUPTIMAGE;
3000 
3001 	{
3002 		int i, pastendoflist_flag;
3003 		unsigned cur_fphysrec, sibFDR_AUlen;
3004 		win_fdr *cur_fdr, fdr_buf;
3005 		unsigned curfdr_aphysrec, prevfdr_aphysrec;
3006 
3007 		cur_fphysrec = 0;
3008 		pastendoflist_flag = 0;
3009 		cur_fdr = &l2_file->win.curfdr;
3010 		curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
3011 
3012 		while (1)
3013 		{
3014 			sibFDR_AUlen = 0;
3015 			i=0;
3016 			if (! pastendoflist_flag)
3017 			{
3018 				/* compute number of allocated AUs and check number of AUs */
3019 				for (; i<54; i++)
3020 				{
3021 					if (get_UINT16BE(cur_fdr->clusters[i][0]) == 0)
3022 					{
3023 						pastendoflist_flag = true;
3024 						break;
3025 					}
3026 					sibFDR_AUlen += get_UINT16BE(cur_fdr->clusters[i][1])
3027 									- get_UINT16BE(cur_fdr->clusters[i][0])
3028 									+ 1;
3029 				}
3030 			}
3031 			/* clear remainder of cluster table */
3032 			for (; i<54; i++)
3033 			{
3034 #if 0
3035 				set_UINT16BE(&cur_fdr->clusters[i][0], 0);
3036 				set_UINT16BE(&cur_fdr->clusters[i][1], 0);
3037 #endif
3038 				if ((get_UINT16BE(cur_fdr->clusters[i][0]) != 0) || (get_UINT16BE(cur_fdr->clusters[i][1]) != 0))
3039 					return IMGTOOLERR_CORRUPTIMAGE;
3040 			}
3041 
3042 			/* check sibFDR_AUlen field */
3043 			if (get_UINT16BE(cur_fdr->sibFDR_AUlen) != sibFDR_AUlen)
3044 				return IMGTOOLERR_CORRUPTIMAGE;
3045 
3046 			/* update current file physrec position to point to end of sibling FDR */
3047 			cur_fphysrec += sibFDR_AUlen * l2_file->win.l2_img->AUformat.physrecsperAU;
3048 
3049 			/* exit loop if end of sibling chain */
3050 			if (! get_UINT16BE(cur_fdr->nextsibFDR_AU))
3051 				break;
3052 
3053 			/* otherwise read next FDR */
3054 			if (get_UINT16BE(cur_fdr->nextsibFDR_AU) >= l2_file->win.l2_img->AUformat.totAUs)
3055 				return IMGTOOLERR_CORRUPTIMAGE;
3056 
3057 			prevfdr_aphysrec = curfdr_aphysrec;
3058 			curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(l2_file->win.l2_img, cur_fdr);
3059 			if (read_absolute_physrec(& l2_file->win.l2_img->l1_img, curfdr_aphysrec, &fdr_buf))
3060 				return IMGTOOLERR_READERROR;
3061 			cur_fdr = &fdr_buf;
3062 
3063 			/* check that back chaining is consistent with forward chaining */
3064 			if (get_win_fdr_prevsibFDR_aphysrec(l2_file->win.l2_img, &fdr_buf) != prevfdr_aphysrec)
3065 				return IMGTOOLERR_CORRUPTIMAGE;
3066 			/*  check fphysrecs field */
3067 			if (get_win_fdr_fphysrecs(&fdr_buf) != cur_fphysrec)
3068 				return IMGTOOLERR_CORRUPTIMAGE;
3069 
3070 			/* check consistency of informative fields (name, record format, flags, etc) */
3071 			if (memcmp(fdr_buf.name, l2_file->win.curfdr.name, 10)
3072 					|| (get_UINT16BE(fdr_buf.xreclen) != get_UINT16BE(l2_file->win.curfdr.xreclen))
3073 					|| (fdr_buf.flags != l2_file->win.curfdr.flags)
3074 					|| (fdr_buf.recsperphysrec != l2_file->win.curfdr.recsperphysrec)
3075 					|| (fdr_buf.eof != l2_file->win.curfdr.eof)
3076 					|| (fdr_buf.reclen != l2_file->win.curfdr.reclen)
3077 					|| (get_UINT16LE(fdr_buf.fixrecs_LSW) != get_UINT16LE(l2_file->win.curfdr.fixrecs_LSW))
3078 					|| memcmp(&fdr_buf.creation, &l2_file->win.curfdr.creation, 4)
3079 					|| memcmp(&fdr_buf.update, &l2_file->win.curfdr.update, 4)
3080 					/*|| memcmp(fdr_buf.id, l2_file->win.curfdr.id, 2)*/
3081 					|| (get_UINT16BE(fdr_buf.parent_FDIR_AU) != get_UINT16BE(l2_file->win.curfdr.parent_FDIR_AU))
3082 					|| ((fdr_buf.xinfo_MSB & 0xf) != (l2_file->win.curfdr.xinfo_MSB & 0xf)))
3083 				return IMGTOOLERR_CORRUPTIMAGE;
3084 		}
3085 		if (cur_fphysrec < l2_file->win.fphysrecs)
3086 			return IMGTOOLERR_CORRUPTIMAGE;
3087 	}
3088 
3089 	return 0;
3090 }
3091 
3092 /*
3093     Open an existing file in TIFILES format
3094 */
open_file_lvl2_tifiles(imgtool::stream & file_handle,struct ti99_lvl2_fileref * l2_file)3095 static int open_file_lvl2_tifiles(imgtool::stream &file_handle, struct ti99_lvl2_fileref *l2_file)
3096 {
3097 	/* set up file handle */
3098 	l2_file->type = L2F_TIFILES;
3099 	l2_file->tifiles.file_handle = &file_handle;
3100 
3101 	/* seek to header */
3102 	if (l2_file->tifiles.file_handle->seek(0, SEEK_SET))
3103 		return IMGTOOLERR_READERROR;
3104 	/* read it */
3105 	if (l2_file->tifiles.file_handle->read(&l2_file->tifiles.hdr, sizeof(l2_file->tifiles.hdr)) != sizeof(l2_file->tifiles.hdr))
3106 		return IMGTOOLERR_READERROR;
3107 
3108 	return 0;
3109 }
3110 
3111 /*
3112     compute the aphysrec address for a given file physical record (fphysrec)
3113 
3114     l2_img: image where the file is located
3115 */
dsk_fphysrec_to_aphysrec(ti99_lvl2_fileref_dsk * dsk_file,unsigned fphysrec,unsigned * aphysrec)3116 static int dsk_fphysrec_to_aphysrec(ti99_lvl2_fileref_dsk *dsk_file, unsigned fphysrec, unsigned *aphysrec)
3117 {
3118 	int cluster_index;
3119 	int cluster_firstfphysrec, cluster_lastfphysrec;
3120 	int cluster_baseaphysrec;
3121 
3122 
3123 	/* check parameter */
3124 	if (fphysrec >= get_UINT16BE(dsk_file->fdr.fphysrecs))
3125 		return IMGTOOLERR_UNEXPECTED;
3126 
3127 
3128 	/* search for the cluster in the data chain pointers array */
3129 	cluster_firstfphysrec = 0;
3130 	for (cluster_index=0; cluster_index<76; cluster_index++)
3131 	{
3132 		/* read curent file block table entry */
3133 		cluster_lastfphysrec = get_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index);
3134 		if (cluster_lastfphysrec >= fphysrec)
3135 			break;
3136 		cluster_firstfphysrec = cluster_lastfphysrec+1;
3137 	}
3138 	if (cluster_index == 76)
3139 		/* if not found, the file is corrupt */
3140 		return IMGTOOLERR_CORRUPTIMAGE;
3141 
3142 	/* read base aphysrec address for this cluster */
3143 	cluster_baseaphysrec = get_dsk_fdr_cluster_baseaphysrec(dsk_file->l2_img, &dsk_file->fdr, cluster_index);
3144 	/* return absolute physrec address */
3145 	*aphysrec = cluster_baseaphysrec + (fphysrec - cluster_firstfphysrec);
3146 	return 0;
3147 }
3148 
3149 /*
3150     compute the aphysrec address for a given file physical record (fphysrec)
3151 
3152     l2_img: image where the file is located
3153 */
win_fphysrec_to_aphysrec(ti99_lvl2_fileref_win * win_file,unsigned fphysrec,unsigned * aphysrec)3154 static int win_fphysrec_to_aphysrec(ti99_lvl2_fileref_win *win_file, unsigned fphysrec, unsigned *aphysrec)
3155 {
3156 	int cluster_index;
3157 	int cluster_firstfphysrec, cluster_lastfphysrec;
3158 	int cluster_baseaphysrec;
3159 	int errorcode;
3160 
3161 
3162 	/* check parameter */
3163 	if (fphysrec >= win_file->fphysrecs)
3164 		return IMGTOOLERR_UNEXPECTED;
3165 
3166 	/* look for correct sibling */
3167 	if (fphysrec < get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr))
3168 	{
3169 		while (fphysrec < get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr))
3170 		{
3171 			if (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) == 0)
3172 				return IMGTOOLERR_CORRUPTIMAGE;
3173 			errorcode = win_goto_prev_sibFDR(win_file);
3174 			if (errorcode)
3175 				return errorcode;
3176 		}
3177 	}
3178 	else /*if (fphysrec >= get_win_fdr_cursibFDR_basefphysrec(& dsk_file->curfdr))*/
3179 	{
3180 		while (fphysrec >= (get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr)
3181 								+ get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU))
3182 		{
3183 			if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) == 0)
3184 				return IMGTOOLERR_CORRUPTIMAGE;
3185 			errorcode = win_goto_next_sibFDR(win_file);
3186 			if (errorcode)
3187 				return errorcode;
3188 		}
3189 	}
3190 
3191 
3192 	/* search for the cluster in the data chain pointers array */
3193 	cluster_firstfphysrec = get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr);
3194 	for (cluster_index = 0; cluster_index<54; cluster_index++)
3195 	{
3196 		cluster_lastfphysrec = cluster_firstfphysrec
3197 									+ (get_UINT16BE(win_file->curfdr.clusters[cluster_index][1])
3198 											- get_UINT16BE(win_file->curfdr.clusters[cluster_index][0])
3199 											+ 1)
3200 										* win_file->l2_img->AUformat.physrecsperAU;
3201 		if (fphysrec < cluster_lastfphysrec)
3202 			break;
3203 
3204 		cluster_firstfphysrec = cluster_lastfphysrec;
3205 	}
3206 
3207 	if (cluster_index == 54)
3208 		return IMGTOOLERR_CORRUPTIMAGE;
3209 
3210 
3211 	/* read base aphysrec address for this cluster */
3212 	cluster_baseaphysrec = get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) * win_file->l2_img->AUformat.physrecsperAU;
3213 	/* return absolute physrec address */
3214 	*aphysrec = cluster_baseaphysrec + (fphysrec - cluster_firstfphysrec);
3215 	return 0;
3216 }
3217 
3218 /*
3219     read a 256-byte physical record from a file
3220 */
read_file_physrec(struct ti99_lvl2_fileref * l2_file,unsigned fphysrec,void * dest)3221 static int read_file_physrec(struct ti99_lvl2_fileref *l2_file, unsigned fphysrec, void *dest)
3222 {
3223 	int errorcode;
3224 	unsigned aphysrec;
3225 
3226 	switch (l2_file->type)
3227 	{
3228 	case L2F_DSK:
3229 		/* compute absolute physrec address */
3230 		errorcode = dsk_fphysrec_to_aphysrec(&l2_file->dsk, fphysrec, &aphysrec);
3231 		if (errorcode)
3232 			return errorcode;
3233 		/* read physrec */
3234 		if (read_absolute_physrec(& l2_file->dsk.l2_img->l1_img, aphysrec, dest))
3235 			return IMGTOOLERR_READERROR;
3236 		break;
3237 
3238 	case L2F_WIN:
3239 		/* compute absolute physrec address */
3240 		errorcode = win_fphysrec_to_aphysrec(&l2_file->win, fphysrec, &aphysrec);
3241 		if (errorcode)
3242 			return errorcode;
3243 		/* read physrec */
3244 		if (read_absolute_physrec(& l2_file->win.l2_img->l1_img, aphysrec, dest))
3245 			return IMGTOOLERR_READERROR;
3246 		break;
3247 
3248 	case L2F_TIFILES:
3249 		/* seek to physrec */
3250 		if (l2_file->tifiles.file_handle->seek(128+256*fphysrec, SEEK_SET))
3251 			return IMGTOOLERR_READERROR;
3252 		/* read it */
3253 		if (l2_file->tifiles.file_handle->read(dest, 256) != 256)
3254 			return IMGTOOLERR_READERROR;
3255 		break;
3256 	}
3257 
3258 	return 0;
3259 }
3260 
3261 /*
3262     read a 256-byte physical record from a file
3263 */
write_file_physrec(struct ti99_lvl2_fileref * l2_file,unsigned fphysrec,const void * src)3264 static int write_file_physrec(struct ti99_lvl2_fileref *l2_file, unsigned fphysrec, const void *src)
3265 {
3266 	int errorcode;
3267 	unsigned aphysrec;
3268 
3269 	switch (l2_file->type)
3270 	{
3271 	case L2F_DSK:
3272 		/* compute absolute physrec address */
3273 		errorcode = dsk_fphysrec_to_aphysrec(&l2_file->dsk, fphysrec, &aphysrec);
3274 		if (errorcode)
3275 			return errorcode;
3276 		/* write physrec */
3277 		if (write_absolute_physrec(& l2_file->dsk.l2_img->l1_img, aphysrec, src))
3278 			return IMGTOOLERR_WRITEERROR;
3279 		break;
3280 
3281 	case L2F_WIN:
3282 		/* compute absolute physrec address */
3283 		errorcode = win_fphysrec_to_aphysrec(&l2_file->win, fphysrec, &aphysrec);
3284 		if (errorcode)
3285 			return errorcode;
3286 		/* write physrec */
3287 		if (write_absolute_physrec(& l2_file->win.l2_img->l1_img, aphysrec, src))
3288 			return IMGTOOLERR_WRITEERROR;
3289 		break;
3290 
3291 	case L2F_TIFILES:
3292 		/* seek to physrec */
3293 		if (l2_file->tifiles.file_handle->seek(128+256*fphysrec, SEEK_SET))
3294 			return IMGTOOLERR_WRITEERROR;
3295 		/* write it */
3296 		if (l2_file->tifiles.file_handle->write(src, 256) != 256)
3297 			return IMGTOOLERR_WRITEERROR;
3298 		break;
3299 	}
3300 
3301 	return 0;
3302 }
3303 
3304 /*
3305     Write a field in every fdr record associated to a file
3306 */
3307 #ifdef UNUSED_FUNCTION
set_win_fdr_field(struct ti99_lvl2_fileref * l2_file,size_t offset,size_t size,void * data)3308 static int set_win_fdr_field(struct ti99_lvl2_fileref *l2_file, size_t offset, size_t size, void *data)
3309 {
3310 	win_fdr fdr_buf;
3311 	unsigned fdr_aphysrec;
3312 	int errorcode = 0;
3313 
3314 	for (fdr_aphysrec = l2_file->win.eldestfdr_aphysrec;
3315 			fdr_aphysrec && ((errorcode = (read_absolute_physrec(&l2_file->win.l2_img->l1_img, fdr_aphysrec, &fdr_buf) ? IMGTOOLERR_READERROR : 0)) == 0);
3316 			fdr_aphysrec = get_win_fdr_nextsibFDR_physrec(l2_file->win.l2_img, &fdr_buf))
3317 	{
3318 		memcpy(((uint8_t *) &fdr_buf) + offset, data, size);
3319 		if (write_absolute_physrec(&l2_file->win.l2_img->l1_img, fdr_aphysrec, &fdr_buf))
3320 		{
3321 			errorcode = IMGTOOLERR_WRITEERROR;
3322 			break;
3323 		}
3324 	}
3325 
3326 	return errorcode;
3327 }
3328 #endif
3329 
get_file_flags(struct ti99_lvl2_fileref * l2_file)3330 static uint8_t get_file_flags(struct ti99_lvl2_fileref *l2_file)
3331 {
3332 	int reply = 0;
3333 
3334 	switch (l2_file->type)
3335 	{
3336 	case L2F_DSK:
3337 		reply = l2_file->dsk.fdr.flags;
3338 		break;
3339 
3340 	case L2F_WIN:
3341 		reply = l2_file->win.curfdr.flags;
3342 		break;
3343 
3344 	case L2F_TIFILES:
3345 		reply = l2_file->tifiles.hdr.flags;
3346 		break;
3347 	}
3348 
3349 	return reply;
3350 }
3351 
set_file_flags(struct ti99_lvl2_fileref * l2_file,uint8_t data)3352 static void set_file_flags(struct ti99_lvl2_fileref *l2_file, uint8_t data)
3353 {
3354 	switch (l2_file->type)
3355 	{
3356 	case L2F_DSK:
3357 		l2_file->dsk.fdr.flags = data;
3358 		break;
3359 
3360 	case L2F_WIN:
3361 		l2_file->win.curfdr.flags = data;
3362 		break;
3363 
3364 	case L2F_TIFILES:
3365 		l2_file->tifiles.hdr.flags = data;
3366 		break;
3367 	}
3368 }
3369 
get_file_recsperphysrec(struct ti99_lvl2_fileref * l2_file)3370 static uint8_t get_file_recsperphysrec(struct ti99_lvl2_fileref *l2_file)
3371 {
3372 	int reply = 0;
3373 
3374 	switch (l2_file->type)
3375 	{
3376 	case L2F_DSK:
3377 		reply = l2_file->dsk.fdr.recsperphysrec;
3378 		break;
3379 
3380 	case L2F_WIN:
3381 		reply = l2_file->win.curfdr.recsperphysrec;
3382 		break;
3383 
3384 	case L2F_TIFILES:
3385 		reply = l2_file->tifiles.hdr.recsperphysrec;
3386 		break;
3387 	}
3388 
3389 	return reply;
3390 }
3391 
set_file_recsperphysrec(struct ti99_lvl2_fileref * l2_file,uint8_t data)3392 static void set_file_recsperphysrec(struct ti99_lvl2_fileref *l2_file, uint8_t data)
3393 {
3394 	switch (l2_file->type)
3395 	{
3396 	case L2F_DSK:
3397 		l2_file->dsk.fdr.recsperphysrec = data;
3398 		break;
3399 
3400 	case L2F_WIN:
3401 		l2_file->win.curfdr.recsperphysrec = data;
3402 		break;
3403 
3404 	case L2F_TIFILES:
3405 		l2_file->tifiles.hdr.recsperphysrec = data;
3406 		break;
3407 	}
3408 }
3409 
get_file_fphysrecs(struct ti99_lvl2_fileref * l2_file)3410 static unsigned get_file_fphysrecs(struct ti99_lvl2_fileref *l2_file)
3411 {
3412 	int reply = 0;
3413 
3414 	switch (l2_file->type)
3415 	{
3416 	case L2F_DSK:
3417 		reply = get_UINT16BE(l2_file->dsk.fdr.fphysrecs);
3418 		break;
3419 
3420 	case L2F_WIN:
3421 		reply = l2_file->win.fphysrecs;
3422 		break;
3423 
3424 	case L2F_TIFILES:
3425 		reply = get_UINT16BE(l2_file->tifiles.hdr.fphysrecs);
3426 		break;
3427 	}
3428 
3429 	return reply;
3430 }
3431 
set_file_fphysrecs(struct ti99_lvl2_fileref * l2_file,unsigned data)3432 static int set_file_fphysrecs(struct ti99_lvl2_fileref *l2_file, unsigned data)
3433 {
3434 	switch (l2_file->type)
3435 	{
3436 	case L2F_DSK:
3437 		if (data >= 65536)
3438 			return IMGTOOLERR_UNIMPLEMENTED;
3439 		set_UINT16BE(&l2_file->dsk.fdr.fphysrecs, data);
3440 		break;
3441 
3442 	case L2F_WIN:
3443 		l2_file->win.fphysrecs = data;
3444 		break;
3445 
3446 	case L2F_TIFILES:
3447 		if (data >= 65536)
3448 			return IMGTOOLERR_UNIMPLEMENTED;
3449 		set_UINT16BE(&l2_file->tifiles.hdr.fphysrecs, data);
3450 		break;
3451 	}
3452 
3453 	return 0;
3454 }
3455 
get_file_eof(struct ti99_lvl2_fileref * l2_file)3456 static uint8_t get_file_eof(struct ti99_lvl2_fileref *l2_file)
3457 {
3458 	int reply = 0;
3459 
3460 	switch (l2_file->type)
3461 	{
3462 	case L2F_DSK:
3463 		reply = l2_file->dsk.fdr.eof;
3464 		break;
3465 
3466 	case L2F_WIN:
3467 		reply = l2_file->win.curfdr.eof;
3468 		break;
3469 
3470 	case L2F_TIFILES:
3471 		reply = l2_file->tifiles.hdr.eof;
3472 		break;
3473 	}
3474 
3475 	return reply;
3476 }
3477 
set_file_eof(struct ti99_lvl2_fileref * l2_file,uint8_t data)3478 static void set_file_eof(struct ti99_lvl2_fileref *l2_file, uint8_t data)
3479 {
3480 	switch (l2_file->type)
3481 	{
3482 	case L2F_DSK:
3483 		l2_file->dsk.fdr.eof = data;
3484 		break;
3485 
3486 	case L2F_WIN:
3487 		l2_file->win.curfdr.eof = data;
3488 		break;
3489 
3490 	case L2F_TIFILES:
3491 		l2_file->tifiles.hdr.eof = data;
3492 		break;
3493 	}
3494 }
3495 
get_file_reclen(struct ti99_lvl2_fileref * l2_file)3496 static uint16_t get_file_reclen(struct ti99_lvl2_fileref *l2_file)
3497 {
3498 	int reply = 0;
3499 
3500 	switch (l2_file->type)
3501 	{
3502 	case L2F_DSK:
3503 		reply = l2_file->dsk.fdr.reclen;
3504 		if ((reply == 0) && (! (l2_file->dsk.fdr.flags & (fdr99_f_program /*| fdr99_f_var*/))))
3505 			reply = get_UINT16BE(l2_file->dsk.fdr.xreclen);
3506 		break;
3507 
3508 	case L2F_WIN:
3509 		reply = l2_file->win.curfdr.reclen;
3510 		if ((reply == 0) && (! (l2_file->win.curfdr.flags & (fdr99_f_program /*| fdr99_f_var*/))))
3511 			reply = get_UINT16BE(l2_file->win.curfdr.xreclen);
3512 		break;
3513 
3514 	case L2F_TIFILES:
3515 		reply = l2_file->tifiles.hdr.reclen;
3516 		break;
3517 	}
3518 
3519 	return reply;
3520 }
3521 
set_file_reclen(struct ti99_lvl2_fileref * l2_file,uint16_t data)3522 static int set_file_reclen(struct ti99_lvl2_fileref *l2_file, uint16_t data)
3523 {
3524 	switch (l2_file->type)
3525 	{
3526 	case L2F_DSK:
3527 		if (data < 256)
3528 		{
3529 			l2_file->dsk.fdr.reclen = data;
3530 			set_UINT16BE(&l2_file->dsk.fdr.xreclen, 0);
3531 		}
3532 		else
3533 		{
3534 			l2_file->dsk.fdr.reclen = 0;
3535 			set_UINT16BE(&l2_file->dsk.fdr.xreclen, data);
3536 		}
3537 		break;
3538 
3539 	case L2F_WIN:
3540 		if (data < 256)
3541 		{
3542 			l2_file->win.curfdr.reclen = data;
3543 			set_UINT16BE(&l2_file->win.curfdr.xreclen, 0);
3544 		}
3545 		else
3546 		{
3547 			l2_file->win.curfdr.reclen = 0;
3548 			set_UINT16BE(&l2_file->win.curfdr.xreclen, data);
3549 		}
3550 		break;
3551 
3552 	case L2F_TIFILES:
3553 		if (data >= 256)
3554 			return IMGTOOLERR_UNIMPLEMENTED;
3555 		l2_file->tifiles.hdr.reclen = data;
3556 		break;
3557 	}
3558 
3559 	return 0;
3560 }
3561 
get_file_fixrecs(struct ti99_lvl2_fileref * l2_file)3562 static unsigned get_file_fixrecs(struct ti99_lvl2_fileref *l2_file)
3563 {
3564 	int reply = 0;
3565 
3566 	switch (l2_file->type)
3567 	{
3568 	case L2F_DSK:
3569 		reply = get_UINT16LE(l2_file->dsk.fdr.fixrecs);
3570 		break;
3571 
3572 	case L2F_WIN:
3573 		reply = get_win_fdr_fixrecs(&l2_file->win.curfdr);
3574 		break;
3575 
3576 	case L2F_TIFILES:
3577 		reply = get_UINT16BE(l2_file->tifiles.hdr.fixrecs);
3578 		break;
3579 	}
3580 
3581 	return reply;
3582 }
3583 
set_file_fixrecs(struct ti99_lvl2_fileref * l2_file,unsigned data)3584 static int set_file_fixrecs(struct ti99_lvl2_fileref *l2_file, unsigned data)
3585 {
3586 	switch (l2_file->type)
3587 	{
3588 	case L2F_DSK:
3589 		if (data >= 65536)
3590 			return IMGTOOLERR_UNIMPLEMENTED;
3591 		set_UINT16LE(&l2_file->dsk.fdr.fixrecs, data);
3592 		break;
3593 
3594 	case L2F_WIN:
3595 		set_win_fdr_fixrecs(&l2_file->win.curfdr, data);
3596 		break;
3597 
3598 	case L2F_TIFILES:
3599 		if (data >= 65536)
3600 			return IMGTOOLERR_UNIMPLEMENTED;
3601 		set_UINT16BE(&l2_file->tifiles.hdr.fixrecs, data);
3602 		break;
3603 	}
3604 
3605 	return 0;
3606 }
3607 
get_file_creation_date(struct ti99_lvl2_fileref * l2_file,ti99_date_time * reply)3608 static void get_file_creation_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time *reply)
3609 {
3610 	switch (l2_file->type)
3611 	{
3612 	case L2F_DSK:
3613 		*reply = l2_file->dsk.fdr.creation;
3614 		break;
3615 
3616 	case L2F_WIN:
3617 		*reply = l2_file->win.curfdr.creation;
3618 		break;
3619 
3620 	case L2F_TIFILES:
3621 		memset(reply, 0, sizeof(*reply));
3622 		break;
3623 	}
3624 }
3625 
set_file_creation_date(struct ti99_lvl2_fileref * l2_file,ti99_date_time data)3626 static void set_file_creation_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time data)
3627 {
3628 	switch (l2_file->type)
3629 	{
3630 	case L2F_DSK:
3631 		l2_file->dsk.fdr.creation = data;
3632 		break;
3633 
3634 	case L2F_WIN:
3635 		l2_file->win.curfdr.creation = data;
3636 		break;
3637 
3638 	case L2F_TIFILES:
3639 		break;
3640 	}
3641 }
3642 
get_file_update_date(struct ti99_lvl2_fileref * l2_file,ti99_date_time * reply)3643 static void get_file_update_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time *reply)
3644 {
3645 	switch (l2_file->type)
3646 	{
3647 	case L2F_DSK:
3648 		*reply = l2_file->dsk.fdr.update;
3649 		break;
3650 
3651 	case L2F_WIN:
3652 		*reply = l2_file->win.curfdr.update;
3653 		break;
3654 
3655 	case L2F_TIFILES:
3656 		memset(reply, 0, sizeof(*reply));
3657 		break;
3658 	}
3659 }
3660 
set_file_update_date(struct ti99_lvl2_fileref * l2_file,ti99_date_time data)3661 static void set_file_update_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time data)
3662 {
3663 	switch (l2_file->type)
3664 	{
3665 	case L2F_DSK:
3666 		l2_file->dsk.fdr.update = data;
3667 		break;
3668 
3669 	case L2F_WIN:
3670 		l2_file->win.curfdr.update = data;
3671 		break;
3672 
3673 	case L2F_TIFILES:
3674 		break;
3675 	}
3676 }
3677 
3678 #ifdef UNUSED_FUNCTION
current_date_time(ti99_date_time * reply)3679 static void current_date_time(ti99_date_time *reply)
3680 {
3681 	/* All these functions should be ANSI */
3682 	time_t cur_time = time(NULL);
3683 	struct tm expanded_time = *localtime(& cur_time);
3684 
3685 	reply->time_MSB = (expanded_time.tm_hour << 3) | (expanded_time.tm_min >> 3);
3686 	reply->time_LSB = (expanded_time.tm_min << 5) | (expanded_time.tm_sec >> 1);
3687 	reply->date_MSB = ((expanded_time.tm_year % 100) << 1) | ((expanded_time.tm_mon+1) >> 3);
3688 	reply->date_LSB = ((expanded_time.tm_mon+1) << 5) | expanded_time.tm_mday;
3689 }
3690 #endif
3691 
3692 #if 0
3693 #pragma mark -
3694 #pragma mark LEVEL 3 DISK ROUTINES
3695 #endif
3696 
3697 /*
3698     Level 3 implements files as a succession of logical records.
3699 
3700     There are three types of files:
3701     * program files that are not implemented at level 3 (no logical record)
3702     * files with fixed-size records (random-access files)
3703     * files with variable-size records (sequential-access)
3704 */
3705 
3706 struct ti99_lvl3_fileref
3707 {
3708 	ti99_lvl2_fileref l2_file;
3709 
3710 	int cur_log_rec;
3711 	int cur_phys_rec;
3712 	int cur_pos_in_phys_rec;
3713 };
3714 
3715 #ifdef UNUSED_FUNCTION
3716 /*
3717     Open a file on level 3.
3718 
3719     To open a file on level 3, you must open (or create) the file on level 2,
3720     then pass the file reference to open_file_lvl3.
3721 */
open_file_lvl3(ti99_lvl3_fileref * l3_file)3722 static int open_file_lvl3(ti99_lvl3_fileref *l3_file)
3723 {
3724 	l3_file->cur_log_rec = 0;
3725 	l3_file->cur_phys_rec = 0;
3726 	l3_file->cur_pos_in_phys_rec = 0;
3727 
3728 	/*if ()*/
3729 
3730 	return 0;
3731 }
3732 
3733 /*
3734     Test a file for EOF
3735 */
is_eof(ti99_lvl3_fileref * l3_file)3736 static int is_eof(ti99_lvl3_fileref *l3_file)
3737 {
3738 	int flags = get_file_flags(&l3_file->l2_file);
3739 	int fphysrecs = get_file_fphysrecs(&l3_file->l2_file);
3740 	int fdr_eof = get_file_eof(&l3_file->l2_file);
3741 
3742 	if (flags & fdr99_f_var)
3743 	{
3744 		return (l3_file->cur_phys_rec >= fphysrecs);
3745 	}
3746 	else
3747 	{
3748 		return ((l3_file->cur_phys_rec >= fphysrecs)
3749 				|| ((l3_file->cur_phys_rec == (fphysrecs-1))
3750 					&& (l3_file->cur_pos_in_phys_rec >= (fdr_eof ? fdr_eof : 256))));
3751 	}
3752 }
3753 
3754 /*
3755     Read next record from a file
3756 */
read_next_record(ti99_lvl3_fileref * l3_file,void * dest,int * out_reclen)3757 static int read_next_record(ti99_lvl3_fileref *l3_file, void *dest, int *out_reclen)
3758 {
3759 	int errorcode;
3760 	uint8_t physrec_buf[256];
3761 	int reclen;
3762 	int flags = get_file_flags(&l3_file->l2_file);
3763 	int fphysrecs = get_file_fphysrecs(&l3_file->l2_file);
3764 	int fdr_eof = get_file_eof(&l3_file->l2_file);
3765 
3766 	if (flags & fdr99_f_program)
3767 	{
3768 		/* program files have no level-3 record */
3769 		return IMGTOOLERR_UNEXPECTED;
3770 	}
3771 	else if (flags & fdr99_f_var)
3772 	{
3773 		/* variable-length records */
3774 		if (is_eof(l3_file))
3775 			return IMGTOOLERR_UNEXPECTED;
3776 		errorcode = read_file_physrec(&l3_file->l2_file, l3_file->cur_phys_rec, physrec_buf);
3777 		if (errorcode)
3778 			return errorcode;
3779 		/* read reclen */
3780 		reclen = physrec_buf[l3_file->cur_pos_in_phys_rec];
3781 		/* check integrity */
3782 		if ((reclen == 0xff) || (reclen > get_file_reclen(&l3_file->l2_file))
3783 				|| ((l3_file->cur_pos_in_phys_rec + 1 + reclen) > 256))
3784 			return IMGTOOLERR_CORRUPTIMAGE;
3785 		/* copy to buffer */
3786 		memcpy(dest, physrec_buf + l3_file->cur_pos_in_phys_rec + 1, reclen);
3787 		l3_file->cur_pos_in_phys_rec += reclen + 1;
3788 		/* skip to next physrec if needed */
3789 		if ((l3_file->cur_pos_in_phys_rec == 256)
3790 				|| (physrec_buf[l3_file->cur_pos_in_phys_rec] == 0xff))
3791 		{
3792 			l3_file->cur_pos_in_phys_rec = 0;
3793 			l3_file->cur_phys_rec++;
3794 		}
3795 		(* out_reclen) = reclen;
3796 	}
3797 	else
3798 	{
3799 		/* fixed len records */
3800 		reclen = get_file_reclen(&l3_file->l2_file);
3801 		if (is_eof(l3_file))
3802 			return IMGTOOLERR_UNEXPECTED;
3803 		if ((l3_file->cur_pos_in_phys_rec + reclen) > 256)
3804 		{
3805 			l3_file->cur_pos_in_phys_rec = 0;
3806 			l3_file->cur_phys_rec++;
3807 		}
3808 		if ((l3_file->cur_phys_rec >= fphysrecs)
3809 				|| ((l3_file->cur_phys_rec == (fphysrecs-1))
3810 					&& ((l3_file->cur_pos_in_phys_rec + reclen) >= (fdr_eof ? fdr_eof : 256))))
3811 			return IMGTOOLERR_CORRUPTIMAGE;
3812 		errorcode = read_file_physrec(&l3_file->l2_file, l3_file->cur_phys_rec, physrec_buf);
3813 		if (errorcode)
3814 			return errorcode;
3815 		memcpy(dest, physrec_buf + l3_file->cur_pos_in_phys_rec, reclen);
3816 		l3_file->cur_pos_in_phys_rec += reclen;
3817 		if (l3_file->cur_pos_in_phys_rec == 256)
3818 		{
3819 			l3_file->cur_pos_in_phys_rec = 0;
3820 			l3_file->cur_phys_rec++;
3821 		}
3822 		(* out_reclen) = reclen;
3823 	}
3824 
3825 	return 0;
3826 }
3827 #endif
3828 
3829 #if 0
3830 #pragma mark -
3831 #pragma mark IMGTOOL MODULE IMPLEMENTATION
3832 #endif
3833 
3834 /*
3835     ti99 catalog iterator, used when imgtool reads the catalog
3836 */
3837 struct dsk_iterator
3838 {
3839 	struct ti99_lvl2_imgref *image;
3840 	int level;
3841 	int listing_subdirs;        /* true if we are listing subdirectories at current level */
3842 	int index[2];               /* current index in the disk catalog */
3843 	ti99_catalog *cur_catalog;  /* current catalog */
3844 };
3845 
3846 struct win_iterator
3847 {
3848 	struct ti99_lvl2_imgref *image;
3849 	int level;
3850 	int listing_subdirs;        /* true if we are listing subdirectories at current level */
3851 	int index[MAX_DIR_LEVEL];   /* current index in the disk catalog */
3852 	ti99_catalog catalog[MAX_DIR_LEVEL];    /* current catalog */
3853 };
3854 
3855 
3856 static imgtoolerr_t dsk_image_init_mess(imgtool::image &image, imgtool::stream::ptr &&stream);
3857 static imgtoolerr_t dsk_image_init_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream);
3858 static imgtoolerr_t dsk_image_init_pc99_fm(imgtool::image &image, imgtool::stream::ptr &&stream);
3859 static imgtoolerr_t dsk_image_init_pc99_mfm(imgtool::image &image, imgtool::stream::ptr &&stream);
3860 static imgtoolerr_t win_image_init(imgtool::image &image, imgtool::stream::ptr &&stream);
3861 static void ti99_image_exit(imgtool::image &img);
3862 static void ti99_image_info(imgtool::image &img, std::ostream &stream);
3863 static imgtoolerr_t dsk_image_beginenum(imgtool::directory &enumeration, const char *path);
3864 static imgtoolerr_t dsk_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent);
3865 static imgtoolerr_t win_image_beginenum(imgtool::directory &enumeration, const char *path);
3866 static imgtoolerr_t win_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent);
3867 static imgtoolerr_t ti99_image_freespace(imgtool::partition &partition, uint64_t *size);
3868 static imgtoolerr_t ti99_image_readfile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &destf);
3869 static imgtoolerr_t ti99_image_writefile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions);
3870 static imgtoolerr_t dsk_image_deletefile(imgtool::partition &partition, const char *fpath);
3871 static imgtoolerr_t win_image_deletefile(imgtool::partition &partition, const char *fpath);
3872 static imgtoolerr_t dsk_image_create_mess(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions);
3873 static imgtoolerr_t dsk_image_create_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions);
3874 
3875 enum
3876 {
3877 	dsk_createopts_volname = 'A',
3878 	dsk_createopts_sides = 'B',
3879 	dsk_createopts_tracks = 'C',
3880 	dsk_createopts_sectors = 'D',
3881 	dsk_createopts_protection = 'E',
3882 	dsk_createopts_density = 'F'
3883 };
3884 
3885 OPTION_GUIDE_START( dsk_create_optionguide )
3886 	OPTION_STRING(dsk_createopts_volname, "label",  "Volume name" )
3887 	OPTION_INT(dsk_createopts_sides, "sides", "Sides" )
3888 	OPTION_INT(dsk_createopts_tracks, "tracks", "Tracks" )
3889 	OPTION_INT(dsk_createopts_sectors, "sectors",   "Sectors (1->9 for SD, 1->18 for DD, 1->36 for HD)" )
3890 	OPTION_INT(dsk_createopts_protection, "protection", "Protection (0 for normal, 1 for protected)" )
3891 	OPTION_ENUM_START(dsk_createopts_density, "density", "Density" )
3892 		OPTION_ENUM(    0,      "Auto",             "Auto" )
3893 		OPTION_ENUM(    1,      "SD",               "Single Density" )
3894 		OPTION_ENUM(    2,      "DD",               "Double Density" )
3895 		OPTION_ENUM(    3,      "HD",               "High Density" )
3896 	OPTION_ENUM_END
3897 OPTION_GUIDE_END
3898 
3899 #define dsk_create_optionspecs "B1-[2];C1-[40]-80;D1-[18]-36;E[0]-1;F[0]-3"
3900 
ti99_getinfo(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3901 static void ti99_getinfo(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3902 {
3903 	switch(state)
3904 	{
3905 		case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES:             info->i = sizeof(ti99_lvl2_imgref); break;
3906 		case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES:             info->i = sizeof(dsk_iterator); break;
3907 
3908 		case IMGTOOLINFO_STR_EOLN:                          strcpy(info->s = imgtool_temp_str(), "\r"); break;
3909 		case IMGTOOLINFO_PTR_CLOSE:                         info->close = ti99_image_exit; break;
3910 		case IMGTOOLINFO_PTR_INFO:                          info->info = ti99_image_info; break;
3911 		case IMGTOOLINFO_PTR_FREE_SPACE:                    info->free_space = ti99_image_freespace; break;
3912 		case IMGTOOLINFO_PTR_READ_FILE:                     info->read_file = ti99_image_readfile; break;
3913 		case IMGTOOLINFO_PTR_WRITE_FILE:                    info->write_file = ti99_image_writefile; break;
3914 	}
3915 }
3916 
ti99_dsk_getinfo(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3917 static void ti99_dsk_getinfo(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3918 {
3919 	switch(state)
3920 	{
3921 		case IMGTOOLINFO_STR_FILE_EXTENSIONS:               strcpy(info->s = imgtool_temp_str(), "dsk"); break;
3922 		case IMGTOOLINFO_PTR_BEGIN_ENUM:                    info->begin_enum = dsk_image_beginenum; break;
3923 		case IMGTOOLINFO_PTR_NEXT_ENUM:                     info->next_enum = dsk_image_nextenum; break;
3924 		case IMGTOOLINFO_PTR_DELETE_FILE:                   info->delete_file = dsk_image_deletefile; break;
3925 		default:                                            ti99_getinfo(imgclass, state, info);
3926 	}
3927 }
3928 
ti99_old_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3929 void ti99_old_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3930 {
3931 	switch(state)
3932 	{
3933 		case IMGTOOLINFO_STR_NAME:                  strcpy(info->s = imgtool_temp_str(), "ti99_old"); break;
3934 		case IMGTOOLINFO_STR_DESCRIPTION:           strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (old MESS format)"); break;
3935 		case IMGTOOLINFO_PTR_OPEN:                  info->open = dsk_image_init_mess; break;
3936 		case IMGTOOLINFO_PTR_CREATE:                info->create = dsk_image_create_mess; break;
3937 		case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE:  info->createimage_optguide = &dsk_create_optionguide; break;
3938 		case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC:   strcpy(info->s = imgtool_temp_str(), dsk_create_optionspecs); break;
3939 		default:                                    ti99_dsk_getinfo(imgclass, state, info); break;
3940 	}
3941 }
3942 
ti99_v9t9_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3943 void ti99_v9t9_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3944 {
3945 	switch(state)
3946 	{
3947 		case IMGTOOLINFO_STR_NAME:                  strcpy(info->s = imgtool_temp_str(), "v9t9"); break;
3948 		case IMGTOOLINFO_STR_DESCRIPTION:           strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (V9T9 format)"); break;
3949 		case IMGTOOLINFO_PTR_OPEN:                  info->open = dsk_image_init_v9t9; break;
3950 		case IMGTOOLINFO_PTR_CREATE:                info->create = dsk_image_create_v9t9; break;
3951 		case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE:  info->createimage_optguide = &dsk_create_optionguide; break;
3952 		case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC:   strcpy(info->s = imgtool_temp_str(), dsk_create_optionspecs); break;
3953 		default:                                    ti99_dsk_getinfo(imgclass, state, info); break;
3954 	}
3955 }
3956 
ti99_pc99fm_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3957 void ti99_pc99fm_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3958 {
3959 	switch(state)
3960 	{
3961 		case IMGTOOLINFO_STR_NAME:                  strcpy(info->s = imgtool_temp_str(), "pc99fm"); break;
3962 		case IMGTOOLINFO_STR_DESCRIPTION:           strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (PC99 FM format)"); break;
3963 		case IMGTOOLINFO_PTR_OPEN:                  info->open = dsk_image_init_pc99_fm; break;
3964 		case IMGTOOLINFO_PTR_CREATE:                /* info->create = dsk_image_create_pc99fm; */ break;
3965 		default:                                    ti99_dsk_getinfo(imgclass, state, info); break;
3966 	}
3967 }
3968 
ti99_pc99mfm_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3969 void ti99_pc99mfm_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3970 {
3971 	switch(state)
3972 	{
3973 		case IMGTOOLINFO_STR_NAME:                  strcpy(info->s = imgtool_temp_str(), "pc99mfm"); break;
3974 		case IMGTOOLINFO_STR_DESCRIPTION:           strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (PC99 MFM format)"); break;
3975 		case IMGTOOLINFO_PTR_OPEN:                  info->open = dsk_image_init_pc99_mfm; break;
3976 		case IMGTOOLINFO_PTR_CREATE:                /* info->create = dsk_image_create_pc99mfm; */ break;
3977 		default:                                    ti99_dsk_getinfo(imgclass, state, info); break;
3978 	}
3979 }
3980 
ti99_ti99hd_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)3981 void ti99_ti99hd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
3982 {
3983 	switch(state)
3984 	{
3985 		case IMGTOOLINFO_STR_NAME:                  strcpy(info->s = imgtool_temp_str(), "ti99hd"); break;
3986 		case IMGTOOLINFO_STR_DESCRIPTION:           strcpy(info->s = imgtool_temp_str(), "TI99 Harddisk"); break;
3987 		case IMGTOOLINFO_PTR_OPEN:                  info->open = win_image_init; break;
3988 		case IMGTOOLINFO_PTR_CREATE:                /* info->create = hd_image_create; */ break;
3989 
3990 		case IMGTOOLINFO_STR_FILE_EXTENSIONS:       strcpy(info->s = imgtool_temp_str(), "hd"); break;
3991 		case IMGTOOLINFO_PTR_BEGIN_ENUM:            info->begin_enum = win_image_beginenum; break;
3992 		case IMGTOOLINFO_PTR_NEXT_ENUM:             info->next_enum = win_image_nextenum; break;
3993 		case IMGTOOLINFO_PTR_DELETE_FILE:           info->delete_file = win_image_deletefile; break;
3994 		default:                                    ti99_getinfo(imgclass, state, info);
3995 	}
3996 }
3997 
3998 
3999 
4000 /*
4001     Open a file as a ti99_image (common code).
4002 */
dsk_image_init(imgtool::image & img,imgtool::stream::ptr && stream,ti99_img_format img_format)4003 static imgtoolerr_t dsk_image_init(imgtool::image &img, imgtool::stream::ptr &&stream, ti99_img_format img_format)
4004 {
4005 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4006 	dsk_vib vib;
4007 	imgtoolerr_t reply;
4008 	int totphysrecs;
4009 	unsigned fdir_aphysrec;
4010 	int i;
4011 
4012 	/* open disk image at level 1 */
4013 	reply = open_image_lvl1(std::move(stream), img_format, &image->l1_img, &vib);
4014 	if (reply)
4015 		return reply;
4016 
4017 	/* open disk image at level 2 */
4018 	image->type = L2I_DSK;
4019 
4020 	/* @BN@ */
4021 	/* Copy in the allocation bit map! */
4022 	memcpy(image->abm, vib.abm, 200);
4023 	/* first compute AU size and number of AUs */
4024 	totphysrecs = get_UINT16BE(vib.totphysrecs);
4025 
4026 	image->AUformat.physrecsperAU = (totphysrecs + 1599) / 1600;
4027 	/* round to next larger power of 2 */
4028 	for (i = 1; i < image->AUformat.physrecsperAU; i <<= 1)
4029 		;
4030 	image->AUformat.physrecsperAU = i;
4031 	image->AUformat.totAUs = totphysrecs / image->AUformat.physrecsperAU;
4032 
4033 	/* extract number of physrecs */
4034 	image->dsk.totphysrecs = get_UINT16BE(vib.totphysrecs);
4035 
4036 	/* read and check main volume catalog */
4037 	reply = (imgtoolerr_t)dsk_read_catalog(image, 1, &image->dsk.catalogs[0]);
4038 	if (reply)
4039 		return reply;
4040 
4041 	image->dsk.fdir_aphysrec[0] = 1;
4042 
4043 	/* read and check subdirectory catalogs */
4044 	/* Note that the reserved areas used for HFDC subdirs may be used for other
4045 	purposes by other FDRs, so, if we get any error, we will assume there is no
4046 	subdir after all... */
4047 	image->dsk.catalogs[0].num_subdirs = 0;
4048 	for (i=0; i<3; i++)
4049 	{
4050 		fdir_aphysrec = get_UINT16BE(vib.subdir[i].fdir_aphysrec);
4051 		if ((! memcmp(vib.subdir[i].name, "\0\0\0\0\0\0\0\0\0\0", 10))
4052 				|| (! memcmp(vib.subdir[i].name, "          ", 10)))
4053 		{
4054 			/* name is empty: fine with us unless there is a fdir pointer */
4055 			if (fdir_aphysrec != 0)
4056 			{
4057 				image->dsk.catalogs[0].num_subdirs = 0;
4058 				break;
4059 			}
4060 		}
4061 		else if (check_fname(vib.subdir[i].name))
4062 		{
4063 			/* name is invalid: this is not an HFDC format floppy */
4064 			image->dsk.catalogs[0].num_subdirs = 0;
4065 			break;
4066 		}
4067 		else
4068 		{
4069 			/* there is a non-empty name */
4070 			if ((fdir_aphysrec == 0) || (fdir_aphysrec >= totphysrecs))
4071 			{
4072 				/* error: fdir pointer is invalid or NULL */
4073 				image->dsk.catalogs[0].num_subdirs = 0;
4074 				break;
4075 			}
4076 			/* fill in descriptor fields */
4077 			image->dsk.fdir_aphysrec[image->dsk.catalogs[0].num_subdirs+1] = fdir_aphysrec;
4078 			/*image->dsk.catalogs[0].subdirs[image->dsk.catalogs[0].num_subdirs].dir_ptr = fdir_aphysrec;*/
4079 			memcpy(image->dsk.catalogs[0].subdirs[image->dsk.catalogs[0].num_subdirs].name, vib.subdir[i].name, 10);
4080 			reply = (imgtoolerr_t) dsk_read_catalog(image, fdir_aphysrec, &image->dsk.catalogs[image->dsk.catalogs[0].num_subdirs+1]);
4081 			if (reply)
4082 			{
4083 				/* error: invalid fdir */
4084 				image->dsk.catalogs[0].num_subdirs = 0;
4085 				break;
4086 			}
4087 			/* found valid subdirectory: increment subdir count */
4088 			image->dsk.catalogs[0].num_subdirs++;
4089 		}
4090 	}
4091 
4092 	/* extract volume name */
4093 	memcpy(image->vol_name, vib.name, 10);
4094 
4095 	/* initialize default data_offset */
4096 	image->data_offset = 32+2;
4097 
4098 	return (imgtoolerr_t)0;
4099 }
4100 
4101 /*
4102     Open a file as a ti99_image (MESS format).
4103 */
dsk_image_init_mess(imgtool::image & image,imgtool::stream::ptr && stream)4104 static imgtoolerr_t dsk_image_init_mess(imgtool::image &image, imgtool::stream::ptr &&stream)
4105 {
4106 	return dsk_image_init(image, std::move(stream), if_mess);
4107 }
4108 
4109 /*
4110     Open a file as a ti99_image (V9T9 format).
4111 */
dsk_image_init_v9t9(imgtool::image & image,imgtool::stream::ptr && stream)4112 static imgtoolerr_t dsk_image_init_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream)
4113 {
4114 	return dsk_image_init(image, std::move(stream), if_v9t9);
4115 }
4116 
4117 /*
4118     Open a file as a ti99_image (PC99 FM format).
4119 */
dsk_image_init_pc99_fm(imgtool::image & image,imgtool::stream::ptr && stream)4120 static imgtoolerr_t dsk_image_init_pc99_fm(imgtool::image &image, imgtool::stream::ptr &&stream)
4121 {
4122 	return dsk_image_init(image, std::move(stream), if_pc99_fm);
4123 }
4124 
4125 /*
4126     Open a file as a ti99_image (PC99 MFM format).
4127 */
dsk_image_init_pc99_mfm(imgtool::image & image,imgtool::stream::ptr && stream)4128 static imgtoolerr_t dsk_image_init_pc99_mfm(imgtool::image &image, imgtool::stream::ptr &&stream)
4129 {
4130 	return dsk_image_init(image, std::move(stream), if_pc99_mfm);
4131 }
4132 
4133 /*
4134     Open a file as a ti99_image (harddisk format).
4135 */
win_image_init(imgtool::image & img,imgtool::stream::ptr && stream)4136 static imgtoolerr_t win_image_init(imgtool::image &img, imgtool::stream::ptr &&stream)
4137 {
4138 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4139 	win_vib_ddr vib;
4140 	int reply;
4141 	int i;
4142 
4143 	/* open disk image at level 1 */
4144 	reply = open_image_lvl1(std::move(stream), if_harddisk, & image->l1_img, NULL);
4145 	if (reply)
4146 		return (imgtoolerr_t)reply;
4147 
4148 	/* open disk image at level 2 */
4149 	image->type = L2I_WIN;
4150 
4151 	/* read VIB */
4152 	reply = read_absolute_physrec(&image->l1_img, 0, &vib);
4153 	if (reply)
4154 		return (imgtoolerr_t)reply;
4155 
4156 	/* guess VIB version */
4157 	image->win.vib_version = memcmp(vib.u.vib_v1.id, "WIN", 3) ? win_vib_v2 : win_vib_v1;
4158 
4159 	/* extract AU size and number of AUs */
4160 	image->AUformat.physrecsperAU = ((get_UINT16BE(vib.params) >> 12) & 0xf) + 1;
4161 	image->AUformat.totAUs = get_UINT16BE(vib.totAUs);
4162 
4163 	/* extract volume name */
4164 	memcpy(image->vol_name, vib.name, 10);
4165 
4166 	/* extract data_offset */
4167 	switch (image->win.vib_version)
4168 	{
4169 	case win_vib_v1:
4170 		image->data_offset = 64;
4171 		break;
4172 
4173 	case win_vib_v2:
4174 		image->data_offset = vib.u.vib_v2.res_AUs * 64;
4175 		break;
4176 	}
4177 
4178 	/* read allocation bitmap (aphysrecs 1 through n, n<=33) */
4179 	for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
4180 	{
4181 		reply = read_absolute_physrec(&image->l1_img, i+1, image->abm+i*256);
4182 		if (reply)
4183 			return (imgtoolerr_t)reply;
4184 	}
4185 
4186 	return (imgtoolerr_t)0;
4187 }
4188 
4189 /*
4190     close a ti99_image
4191 */
ti99_image_exit(imgtool::image & img)4192 static void ti99_image_exit(imgtool::image &img)
4193 {
4194 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4195 
4196 	close_image_lvl1(&image->l1_img);
4197 }
4198 
4199 /*
4200     get basic information on a ti99_image
4201 
4202     Currently returns the volume name
4203 */
ti99_image_info(imgtool::image & img,std::ostream & stream)4204 static void ti99_image_info(imgtool::image &img, std::ostream &stream)
4205 {
4206 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4207 	char vol_name[11];
4208 
4209 	fname_to_str(vol_name, image->vol_name, 11);
4210 
4211 	stream << vol_name;
4212 }
4213 
4214 /*
4215     Open the disk catalog for enumeration
4216 */
dsk_image_beginenum(imgtool::directory & enumeration,const char * path)4217 static imgtoolerr_t dsk_image_beginenum(imgtool::directory &enumeration, const char *path)
4218 {
4219 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(enumeration.image());
4220 	dsk_iterator *iter = (dsk_iterator *) enumeration.extra_bytes();
4221 
4222 	iter->image = image;
4223 	iter->level = 0;
4224 	iter->listing_subdirs = 1;
4225 	iter->index[0] = 0;
4226 	iter->cur_catalog = &iter->image->dsk.catalogs[0];
4227 
4228 	return (imgtoolerr_t)0;
4229 }
4230 
4231 /*
4232     Enumerate disk catalog next entry
4233 */
dsk_image_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)4234 static imgtoolerr_t dsk_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
4235 {
4236 	dsk_iterator *iter = (dsk_iterator*) enumeration.extra_bytes();
4237 	dsk_fdr fdr;
4238 	int reply;
4239 	unsigned fdr_aphysrec;
4240 
4241 
4242 	ent.corrupt = 0;
4243 	ent.eof = 0;
4244 
4245 	/* iterate through catalogs to next file or dir entry */
4246 	while ((iter->level >= 0)
4247 			&& (iter->index[iter->level] >= (iter->listing_subdirs
4248 												? iter->cur_catalog->num_subdirs
4249 												: iter->cur_catalog->num_files)))
4250 	{
4251 		if (iter->listing_subdirs)
4252 		{
4253 			iter->listing_subdirs = 0;
4254 			iter->index[iter->level] = 0;
4255 		}
4256 		else
4257 		{
4258 			iter->listing_subdirs = 1;
4259 			if (! iter->level)
4260 				iter->level = -1;
4261 			else
4262 			{
4263 				iter->level = 0;
4264 				iter->index[0]++;
4265 				iter->cur_catalog = &iter->image->dsk.catalogs[0];
4266 			}
4267 		}
4268 	}
4269 
4270 	if (iter->level < 0)
4271 	{
4272 		ent.eof = 1;
4273 	}
4274 	else
4275 	{
4276 		if (iter->listing_subdirs)
4277 		{
4278 			fname_to_str(ent.filename, iter->image->dsk.catalogs[0].subdirs[iter->index[iter->level]].name, ARRAY_LENGTH(ent.filename));
4279 
4280 			/* set type of DIR */
4281 			snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "DIR");
4282 
4283 			/* len in physrecs */
4284 			/* @BN@ return length in bytes */
4285 			/* ent.filesize = 1; */
4286 			ent.filesize = 256;
4287 
4288 			/* recurse subdirectory */
4289 			iter->listing_subdirs = 0;  /* no need to list subdirs as only the
4290 			                            root dir has subdirs in DSK format */
4291 			iter->level = 1;
4292 			iter->cur_catalog = &iter->image->dsk.catalogs[iter->index[0]+1];
4293 			iter->index[iter->level] = 0;
4294 		}
4295 		else
4296 		{
4297 			fdr_aphysrec = iter->cur_catalog->files[iter->index[iter->level]].fdr_ptr;
4298 			reply = read_absolute_physrec(& iter->image->l1_img, fdr_aphysrec, & fdr);
4299 			if (reply)
4300 				return IMGTOOLERR_READERROR;
4301 #if 0
4302 			fname_to_str(ent.filename, fdr.name, ARRAY_LENGTH(ent.filename));
4303 #else
4304 			{
4305 				char buf[11];
4306 
4307 				ent.filename[0] = '\0';
4308 				if (iter->level)
4309 				{
4310 					fname_to_str(ent.filename, iter->image->dsk.catalogs[0].subdirs[iter->index[0]].name, ARRAY_LENGTH(ent.filename));
4311 					strncat(ent.filename, ".", ARRAY_LENGTH(ent.filename) - 1);
4312 				}
4313 				fname_to_str(buf, fdr.name, 11);
4314 				strncat(ent.filename, buf, ARRAY_LENGTH(ent.filename) - 1);
4315 			}
4316 #endif
4317 			/* parse flags */
4318 			if (fdr.flags & fdr99_f_program)
4319 				snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "PGM%s",
4320 							(fdr.flags & fdr99_f_wp) ? " R/O" : "");
4321 			else
4322 				snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%c/%c %d%s",
4323 							(fdr.flags & fdr99_f_int) ? 'I' : 'D',
4324 							(fdr.flags & fdr99_f_var) ? 'V' : 'F',
4325 							fdr.reclen,
4326 							(fdr.flags & fdr99_f_wp) ? " R/O" : "");
4327 			/* len in physrecs */
4328 			/* @BN@ return length in bytes */
4329 			/* ent.filesize = get_UINT16BE(fdr.fphysrecs); */
4330 			ent.filesize = (get_UINT16BE(fdr.fphysrecs)+1)*256;
4331 
4332 			iter->index[iter->level]++;
4333 		}
4334 	}
4335 
4336 	return (imgtoolerr_t)0;
4337 }
4338 
4339 /*
4340     Open the disk catalog for enumeration
4341 */
win_image_beginenum(imgtool::directory & enumeration,const char * path)4342 static imgtoolerr_t win_image_beginenum(imgtool::directory &enumeration, const char *path)
4343 {
4344 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(enumeration.image());
4345 	win_iterator *iter = (win_iterator *) enumeration.extra_bytes();
4346 	imgtoolerr_t errorcode;
4347 
4348 	iter->image = image;
4349 	iter->level = 0;
4350 	iter->listing_subdirs = 1;
4351 	iter->index[0] = 0;
4352 	errorcode = (imgtoolerr_t)win_read_catalog(image, 0, &iter->catalog[0]);
4353 	if (errorcode)
4354 		return errorcode;
4355 
4356 	return (imgtoolerr_t)0;
4357 }
4358 
4359 /*
4360     Enumerate disk catalog next entry
4361 */
win_image_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)4362 static imgtoolerr_t win_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
4363 {
4364 	win_iterator *iter = (win_iterator *) enumeration.extra_bytes();
4365 	unsigned fdr_aphysrec;
4366 	win_fdr fdr;
4367 	int reply;
4368 	int i;
4369 
4370 
4371 	ent.corrupt = 0;
4372 	ent.eof = 0;
4373 
4374 	/* iterate through catalogs to next file or dir entry */
4375 	while ((iter->level >= 0)
4376 			&& (iter->index[iter->level] >= (iter->listing_subdirs
4377 												? iter->catalog[iter->level].num_subdirs
4378 												: iter->catalog[iter->level].num_files)))
4379 	{
4380 		if (iter->listing_subdirs)
4381 		{
4382 			iter->listing_subdirs = 0;
4383 			iter->index[iter->level] = 0;
4384 		}
4385 		else
4386 		{
4387 			iter->listing_subdirs = 1;
4388 			iter->level--;
4389 			iter->index[iter->level]++;
4390 		}
4391 	}
4392 
4393 	if (iter->level < 0)
4394 	{
4395 		ent.eof = 1;
4396 	}
4397 	else
4398 	{
4399 		if (iter->listing_subdirs)
4400 		{
4401 #if 0
4402 			fname_to_str(ent.filename, iter->catalog[iter->level].subdirs[iter->index[iter->level]].name, ARRAY_LENGTH(ent.filename));
4403 #else
4404 			{
4405 				char buf[11];
4406 
4407 				ent.filename[0] = '\0';
4408 				for (i=0; i<iter->level; i++)
4409 				{
4410 					fname_to_str(buf, iter->catalog[i].subdirs[iter->index[i]].name, 11);
4411 					strncat(ent.filename, buf, ARRAY_LENGTH(ent.filename) - 1);
4412 					strncat(ent.filename, ".", ARRAY_LENGTH(ent.filename) - 1);
4413 				}
4414 				fname_to_str(buf, iter->catalog[iter->level].subdirs[iter->index[iter->level]].name, 11);
4415 				strncat(ent.filename, buf, ARRAY_LENGTH(ent.filename) - 1);
4416 			}
4417 #endif
4418 
4419 			/* set type of DIR */
4420 			snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "DIR");
4421 
4422 			/* len in physrecs */
4423 			/* @BN@ return length in bytes */
4424 			/* ent.filesize = 2; */
4425 			ent.filesize = 512;
4426 
4427 			/* recurse subdirectory */
4428 			/*iter->listing_subdirs = 1;*/
4429 			iter->level++;
4430 			iter->index[iter->level] = 0;
4431 			reply = win_read_catalog(iter->image, iter->catalog[iter->level-1].subdirs[iter->index[iter->level-1]].dir_ptr, &iter->catalog[iter->level]);
4432 			if (reply)
4433 			{
4434 				ent.corrupt = 1;
4435 				return (imgtoolerr_t)reply;
4436 			}
4437 		}
4438 		else
4439 		{
4440 			fdr_aphysrec = iter->catalog[iter->level].files[iter->index[iter->level]].fdr_ptr*iter->image->AUformat.physrecsperAU;
4441 			reply = read_absolute_physrec(& iter->image->l1_img, fdr_aphysrec, & fdr);
4442 			if (reply)
4443 				return IMGTOOLERR_READERROR;
4444 #if 0
4445 			fname_to_str(ent.filename, iter->catalog[iter->level].files[iter->index[iter->level]].name, ARRAY_LENGTH(ent.filename));
4446 #else
4447 			{
4448 				char buf[11];
4449 
4450 				ent.filename[0] = '\0';
4451 				for (i=0; i<iter->level; i++)
4452 				{
4453 					fname_to_str(buf, iter->catalog[i].subdirs[iter->index[i]].name, 11);
4454 					strncat(ent.filename, buf, ARRAY_LENGTH(ent.filename) - 1);
4455 					strncat(ent.filename, ".", ARRAY_LENGTH(ent.filename) - 1);
4456 				}
4457 				fname_to_str(buf, iter->catalog[iter->level].files[iter->index[iter->level]].name, 11);
4458 				strncat(ent.filename, buf, ARRAY_LENGTH(ent.filename) - 1);
4459 			}
4460 #endif
4461 			/* parse flags */
4462 			if (fdr.flags & fdr99_f_program)
4463 				snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "PGM%s",
4464 							(fdr.flags & fdr99_f_wp) ? " R/O" : "");
4465 			else
4466 				snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%c/%c %d%s",
4467 							(fdr.flags & fdr99_f_int) ? 'I' : 'D',
4468 							(fdr.flags & fdr99_f_var) ? 'V' : 'F',
4469 							fdr.reclen,
4470 							(fdr.flags & fdr99_f_wp) ? " R/O" : "");
4471 			/* len in physrecs */
4472 			/* @BN@ return length in bytes */
4473 			/* ent.filesize = get_win_fdr_fphysrecs(&fdr); */
4474 			ent.filesize = (get_win_fdr_fphysrecs(&fdr)+1)*256;
4475 
4476 			iter->index[iter->level]++;
4477 		}
4478 	}
4479 
4480 	return (imgtoolerr_t)0;
4481 }
4482 
4483 /*
4484     Compute free space on disk image (in AUs)
4485 */
ti99_image_freespace(imgtool::partition & partition,uint64_t * size)4486 static imgtoolerr_t ti99_image_freespace(imgtool::partition &partition, uint64_t *size)
4487 {
4488 	imgtool::image &img(partition.image());
4489 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4490 	size_t freeAUs;
4491 	int i;
4492 
4493 	freeAUs = 0;
4494 	for (i=0; i<image->AUformat.totAUs; i++)
4495 	{
4496 		if (! (image->abm[i >> 3] & (1 << (i & 7))))
4497 			freeAUs++;
4498 	}
4499 
4500 	/* @BN@ return free space in bytes */
4501 	/*    *size = freeAUs; */
4502 	*size = freeAUs*256;
4503 
4504 	return IMGTOOLERR_SUCCESS;
4505 }
4506 
4507 /*
4508     Extract a file from a ti99_image.  The file is saved in tifile format.
4509 */
ti99_image_readfile(imgtool::partition & partition,const char * fpath,const char * fork,imgtool::stream & destf)4510 static imgtoolerr_t ti99_image_readfile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &destf)
4511 {
4512 	imgtool::image &img(partition.image());
4513 #if 1
4514 
4515 	/* extract data as TIFILES */
4516 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4517 	ti99_lvl2_fileref src_file;
4518 	ti99_lvl2_fileref dst_file;
4519 	ti99_date_time date_time;
4520 	int fphysrecs;
4521 	int i;
4522 	uint8_t buf[256];
4523 	imgtoolerr_t errorcode;
4524 
4525 
4526 	if (check_fpath(fpath))
4527 		return IMGTOOLERR_BADFILENAME;
4528 
4529 	/* open file on TI image */
4530 	switch (image->type)
4531 	{
4532 	case L2I_DSK:
4533 		errorcode = (imgtoolerr_t)open_file_lvl2_dsk(image, fpath, &src_file);
4534 		break;
4535 
4536 	case L2I_WIN:
4537 		errorcode = (imgtoolerr_t)open_file_lvl2_win(image, fpath, &src_file);
4538 		break;
4539 
4540 	default:
4541 		errorcode = (imgtoolerr_t)-1;
4542 		break;
4543 	}
4544 	if (errorcode)
4545 		return errorcode;
4546 
4547 	errorcode = (imgtoolerr_t)new_file_lvl2_tifiles(destf, &dst_file);
4548 	if (errorcode)
4549 		return errorcode;
4550 
4551 	/* write TIFILE header */
4552 	/* set up parameters */
4553 	set_file_flags(&dst_file, get_file_flags(&src_file));
4554 	set_file_recsperphysrec(&dst_file, get_file_recsperphysrec(&src_file));
4555 	errorcode = (imgtoolerr_t)set_file_fphysrecs(&dst_file, get_file_fphysrecs(&src_file));
4556 	if (errorcode)
4557 		return errorcode;
4558 	set_file_eof(&dst_file, get_file_eof(&src_file));
4559 	errorcode = (imgtoolerr_t)set_file_reclen(&dst_file, get_file_reclen(&src_file));
4560 	if (errorcode)
4561 		return errorcode;
4562 	errorcode = (imgtoolerr_t)set_file_fixrecs(&dst_file, get_file_fixrecs(&src_file));
4563 	if (errorcode)
4564 		return errorcode;
4565 
4566 	get_file_creation_date(&src_file, &date_time);
4567 #if 0
4568 	if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
4569 			|| (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
4570 		current_date_time(&date_time);
4571 #endif
4572 	set_file_creation_date(&dst_file, date_time);
4573 
4574 	get_file_update_date(&src_file, &date_time);
4575 #if 0
4576 	if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
4577 			|| (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
4578 		current_date_time(&date_time);
4579 #endif
4580 	set_file_update_date(&dst_file, date_time);
4581 
4582 	if (destf.write(& dst_file.tifiles.hdr, 128) != 128)
4583 		return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4584 
4585 	/* copy data to TIFILE */
4586 	fphysrecs = get_file_fphysrecs(&src_file);
4587 
4588 	for (i=0; i<fphysrecs; i++)
4589 	{
4590 		errorcode = (imgtoolerr_t)read_file_physrec(& src_file, i, buf);
4591 		if (errorcode)
4592 			return errorcode;
4593 
4594 		errorcode = (imgtoolerr_t)write_file_physrec(& dst_file, i, buf);
4595 		if (errorcode)
4596 			return errorcode;
4597 	}
4598 
4599 	return (imgtoolerr_t)0;
4600 
4601 #else
4602 
4603 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4604 	ti99_lvl3_fileref src_file;
4605 	uint8_t buf[256];
4606 	int reclen;
4607 	char lineend = '\r';
4608 	int errorcode;
4609 
4610 
4611 	if (check_fpath(fpath))
4612 		return IMGTOOLERR_BADFILENAME;
4613 
4614 	/* open file on TI image */
4615 	switch (image->type)
4616 	{
4617 	case L2I_DSK:
4618 		errorcode = open_file_lvl2_dsk(image, fpath, &src_file.l2_file);
4619 		break;
4620 
4621 	case L2I_WIN:
4622 		errorcode = open_file_lvl2_win(image, fpath, &src_file.l2_file);
4623 		break;
4624 	}
4625 	if (errorcode)
4626 		return errorcode;
4627 
4628 	errorcode = open_file_lvl3(& src_file);
4629 	if (errorcode)
4630 		return errorcode;
4631 
4632 	/* write text data */
4633 	while (! is_eof(& src_file))
4634 	{
4635 		errorcode = read_next_record(& src_file, buf, & reclen);
4636 		if (errorcode)
4637 			return errorcode;
4638 		if (destf.write(buf, reclen) != reclen)
4639 			return IMGTOOLERR_WRITEERROR;
4640 		if (destf.write(&lineend, 1) != 1)
4641 			return IMGTOOLERR_WRITEERROR;
4642 	}
4643 
4644 	return 0;
4645 
4646 #endif
4647 }
4648 
4649 /*
4650     Add a file to a ti99_image.  The file must be in tifile format.
4651 */
ti99_image_writefile(imgtool::partition & partition,const char * fpath,const char * fork,imgtool::stream & sourcef,util::option_resolution * writeoptions)4652 static imgtoolerr_t ti99_image_writefile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions)
4653 {
4654 	imgtool::image &img(partition.image());
4655 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4656 	const char *filename;
4657 	char ti_fname[10];
4658 	ti99_lvl2_fileref src_file;
4659 	ti99_lvl2_fileref dst_file;
4660 	ti99_date_time date_time;
4661 	int i;
4662 	int fphysrecs;
4663 	uint8_t buf[256];
4664 	imgtoolerr_t errorcode;
4665 	int parent_ref_valid, parent_ref = 0;
4666 	ti99_catalog *catalog, catalog_buf = {0, };
4667 
4668 
4669 	(void) writeoptions;
4670 
4671 	if (check_fpath(fpath))
4672 		return IMGTOOLERR_BADFILENAME;
4673 
4674 	switch (image->type)
4675 	{
4676 	case L2I_DSK:
4677 		errorcode = (imgtoolerr_t)dsk_find_catalog_entry(image, fpath, &parent_ref_valid, &parent_ref, NULL, NULL);
4678 		if (errorcode == 0)
4679 			/* file already exists: causes an error for now */
4680 			return (imgtoolerr_t)IMGTOOLERR_UNEXPECTED;
4681 		else if ((! parent_ref_valid) || (errorcode != IMGTOOLERR_FILENOTFOUND))
4682 			return (imgtoolerr_t)errorcode;
4683 		break;
4684 
4685 	case L2I_WIN:
4686 		errorcode = (imgtoolerr_t)win_find_catalog_entry(image, fpath, &parent_ref_valid, &parent_ref, &catalog_buf, NULL, NULL);
4687 		if (errorcode == 0)
4688 			/* file already exists: causes an error for now */
4689 			return (imgtoolerr_t)IMGTOOLERR_UNEXPECTED;
4690 		else if ((! parent_ref_valid) || (errorcode != IMGTOOLERR_FILENOTFOUND))
4691 			return (imgtoolerr_t)errorcode;
4692 		break;
4693 	}
4694 
4695 	errorcode =(imgtoolerr_t) open_file_lvl2_tifiles(sourcef, & src_file);
4696 	if (errorcode)
4697 		return (imgtoolerr_t)errorcode;
4698 
4699 	/* create new file */
4700 	filename = strrchr(fpath, '.');
4701 	if (filename)
4702 		filename++;
4703 	else
4704 		filename = fpath;
4705 	str_to_fname(ti_fname, filename);
4706 	switch (image->type)
4707 	{
4708 	case L2I_DSK:
4709 		errorcode = (imgtoolerr_t)new_file_lvl2_dsk(image, parent_ref, ti_fname, &dst_file);
4710 		if (errorcode)
4711 			return (imgtoolerr_t)errorcode;
4712 		break;
4713 
4714 	case L2I_WIN:
4715 		errorcode = (imgtoolerr_t)new_file_lvl2_win(image, &catalog_buf, ti_fname, &dst_file);
4716 		if (errorcode)
4717 			return (imgtoolerr_t)errorcode;
4718 		break;
4719 	}
4720 
4721 	/* set up parameters */
4722 	set_file_flags(&dst_file, get_file_flags(&src_file));
4723 	set_file_recsperphysrec(&dst_file, get_file_recsperphysrec(&src_file));
4724 	errorcode = (imgtoolerr_t)set_file_fphysrecs(&dst_file, /*get_file_fphysrecs(&src_file)*/0);
4725 	if (errorcode)
4726 		return (imgtoolerr_t)errorcode;
4727 	set_file_eof(&dst_file, get_file_eof(&src_file));
4728 	errorcode = (imgtoolerr_t)set_file_reclen(&dst_file, get_file_reclen(&src_file));
4729 	if (errorcode)
4730 		return (imgtoolerr_t)errorcode;
4731 	errorcode = (imgtoolerr_t)set_file_fixrecs(&dst_file, get_file_fixrecs(&src_file));
4732 	if (errorcode)
4733 		return (imgtoolerr_t)errorcode;
4734 
4735 	get_file_creation_date(&src_file, &date_time);
4736 #if 0
4737 	if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
4738 			|| (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
4739 		current_date_time(&date_time);
4740 #endif
4741 	set_file_creation_date(&dst_file, date_time);
4742 
4743 	get_file_update_date(&src_file, &date_time);
4744 #if 0
4745 	if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0)
4746 			|| (date_time.date_MSB == 0) || (date_time.date_LSB == 0))
4747 		current_date_time(&date_time);
4748 #endif
4749 	set_file_update_date(&dst_file, date_time);
4750 
4751 	/* alloc data physrecs */
4752 	fphysrecs = get_file_fphysrecs(&src_file);
4753 	switch (dst_file.type)
4754 	{
4755 	case L2F_DSK:
4756 		errorcode = (imgtoolerr_t)dsk_alloc_file_physrecs(&dst_file.dsk, fphysrecs);
4757 		if (errorcode)
4758 			return (imgtoolerr_t)errorcode;
4759 		break;
4760 
4761 	case L2F_WIN:
4762 		errorcode = (imgtoolerr_t)win_alloc_file_physrecs(&dst_file.win, fphysrecs);
4763 		if (errorcode)
4764 			return (imgtoolerr_t)errorcode;
4765 		break;
4766 
4767 	case L2F_TIFILES:
4768 		break;
4769 	}
4770 
4771 	/* copy data */
4772 	for (i=0; i<fphysrecs; i++)
4773 	{
4774 		if (sourcef.read(buf, 256) != 256)
4775 			return (imgtoolerr_t)IMGTOOLERR_READERROR;
4776 
4777 		errorcode = (imgtoolerr_t)write_file_physrec(& dst_file, i, buf);
4778 		if (errorcode)
4779 			return (imgtoolerr_t)errorcode;
4780 	}
4781 
4782 	/* write fdr */
4783 	switch (image->type)
4784 	{
4785 	case L2I_DSK:
4786 		if (write_absolute_physrec(& image->l1_img, dst_file.dsk.fdr_aphysrec, &dst_file.dsk.fdr))
4787 			return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4788 		break;
4789 
4790 	case L2I_WIN:
4791 		/* save fphysrecs field as well */
4792 		if (dst_file.win.curfdr_aphysrec == dst_file.win.eldestfdr_aphysrec)
4793 			set_win_fdr_fphysrecs(&dst_file.win.curfdr, dst_file.win.fphysrecs);
4794 		if (write_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr))
4795 			return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4796 		if (dst_file.win.curfdr_aphysrec != dst_file.win.eldestfdr_aphysrec)
4797 		{
4798 			dst_file.win.curfdr_aphysrec = dst_file.win.eldestfdr_aphysrec;
4799 			if (read_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr))
4800 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4801 			set_win_fdr_fphysrecs(&dst_file.win.curfdr, dst_file.win.fphysrecs);
4802 			if (write_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr))
4803 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4804 		}
4805 		break;
4806 	}
4807 
4808 	/* update catalog */
4809 	switch (image->type)
4810 	{
4811 	case L2I_DSK:
4812 		catalog = &image->dsk.catalogs[parent_ref];
4813 		for (i=0; i<128; i++)
4814 		{
4815 			buf[2*i] = catalog->files[i].fdr_ptr >> 8;
4816 			buf[2*i+1] = catalog->files[i].fdr_ptr & 0xff;
4817 		}
4818 		if (write_absolute_physrec(& image->l1_img, image->dsk.fdir_aphysrec[parent_ref], buf))
4819 			return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4820 		break;
4821 
4822 	case L2I_WIN:
4823 		{
4824 			win_vib_ddr parent_ddr;
4825 			int fdir_AU;
4826 
4827 			/* update VIB/DDR and get FDIR AU address */
4828 			if (read_absolute_physrec(& image->l1_img, parent_ref * image->AUformat.physrecsperAU, &parent_ddr))
4829 				return (imgtoolerr_t)IMGTOOLERR_READERROR;
4830 			parent_ddr.num_files = catalog_buf.num_files;
4831 			if (write_absolute_physrec(& image->l1_img, parent_ref * image->AUformat.physrecsperAU, &parent_ddr))
4832 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4833 			fdir_AU = get_UINT16BE(parent_ddr.fdir_AU);
4834 
4835 			/* generate FDIR and save it */
4836 			for (i=0; i<127; i++)
4837 			{
4838 				buf[2*i] = catalog_buf.files[i].fdr_ptr >> 8;
4839 				buf[2*i+1] = catalog_buf.files[i].fdr_ptr & 0xff;
4840 			}
4841 			buf[254] = parent_ref >> 8;
4842 			buf[255] = parent_ref & 0xff;
4843 			if (write_absolute_physrec(& image->l1_img, fdir_AU * image->AUformat.physrecsperAU, buf))
4844 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4845 		}
4846 		break;
4847 	}
4848 
4849 	/* update bitmap */
4850 	switch (image->type)
4851 	{
4852 	case L2I_DSK:
4853 		{
4854 			dsk_vib vib;
4855 
4856 			if (read_absolute_physrec(& image->l1_img, 0, &vib))
4857 				return (imgtoolerr_t)IMGTOOLERR_READERROR;
4858 			memcpy(vib.abm, image->abm, 200);
4859 			if (write_absolute_physrec(& image->l1_img, 0, &vib))
4860 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4861 		}
4862 		break;
4863 
4864 	case L2I_WIN:
4865 		/* save allocation bitmap (aphysrecs 1 through n, n<=33) */
4866 		for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
4867 			if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256))
4868 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
4869 		break;
4870 	}
4871 
4872 	return (imgtoolerr_t)0;
4873 }
4874 
4875 /*
4876     Delete a file from a ti99_image.
4877 */
dsk_image_deletefile(imgtool::partition & partition,const char * fpath)4878 static imgtoolerr_t dsk_image_deletefile(imgtool::partition &partition, const char *fpath)
4879 {
4880 	imgtool::image &img(partition.image());
4881 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
4882 	dsk_fdr fdr;
4883 	int i, cluster_index;
4884 	unsigned cur_AU, cluster_lastfphysrec;
4885 //  int fphysrecs;
4886 	int parent_ref, is_dir, catalog_index;
4887 	imgtoolerr_t errorcode;
4888 	uint8_t buf[256];
4889 	ti99_catalog *catalog;
4890 
4891 
4892 	if (check_fpath(fpath))
4893 		return IMGTOOLERR_BADFILENAME;
4894 
4895 	errorcode = (imgtoolerr_t)dsk_find_catalog_entry(image, fpath, NULL, &parent_ref, &is_dir, &catalog_index);
4896 	if (errorcode)
4897 		return errorcode;
4898 
4899 	if (is_dir)
4900 	{
4901 		catalog = &image->dsk.catalogs[catalog_index+1];
4902 
4903 		if ((catalog->num_files != 0) || (catalog->num_subdirs != 0))
4904 			return IMGTOOLERR_UNIMPLEMENTED;
4905 
4906 		catalog = &image->dsk.catalogs[0];
4907 
4908 		/* free fdir AU */
4909 		cur_AU = image->dsk.fdir_aphysrec[catalog_index+1] / image->AUformat.physrecsperAU;
4910 		image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
4911 
4912 		/* delete catalog entry */
4913 		for (i=catalog_index; i<2; i++)
4914 		{
4915 			catalog->subdirs[i] = catalog->subdirs[i+1];
4916 			image->dsk.fdir_aphysrec[i+1] = image->dsk.fdir_aphysrec[i+2];
4917 		}
4918 		memset(catalog->subdirs[2].name, 0, 10);
4919 		catalog->subdirs[2].dir_ptr = 0;
4920 		image->dsk.fdir_aphysrec[3] = 0;
4921 		catalog->num_subdirs--;
4922 
4923 		/* update directory and bitmap in vib */
4924 		{
4925 			dsk_vib vib;
4926 
4927 			if (read_absolute_physrec(& image->l1_img, 0, &vib))
4928 				return IMGTOOLERR_READERROR;
4929 
4930 			for (i=0; i<3; i++)
4931 			{
4932 				memcpy(vib.subdir[i].name, catalog->subdirs[i].name, 10);
4933 				set_UINT16BE(&vib.subdir[i].fdir_aphysrec, image->dsk.fdir_aphysrec[i+1]);
4934 			}
4935 
4936 			memcpy(vib.abm, image->abm, 200);
4937 
4938 			if (write_absolute_physrec(& image->l1_img, 0, &vib))
4939 				return IMGTOOLERR_WRITEERROR;
4940 		}
4941 	}
4942 	else
4943 	{
4944 		catalog = &image->dsk.catalogs[parent_ref];
4945 
4946 		if (read_absolute_physrec(& image->l1_img, catalog->files[catalog_index].fdr_ptr, &fdr))
4947 			return IMGTOOLERR_READERROR;
4948 
4949 		/* free data AUs */
4950 //      fphysrecs =
4951 		get_UINT16BE(fdr.fphysrecs);
4952 
4953 		i = 0;
4954 		cluster_index = 0;
4955 		while (cluster_index < 76)
4956 		{
4957 			cur_AU = get_dsk_fdr_cluster_baseAU(image, & fdr, cluster_index);
4958 			if (cur_AU == 0)
4959 				/* end of cluster list */
4960 				break;
4961 			cluster_lastfphysrec = get_dsk_fdr_cluster_lastfphysrec(& fdr, cluster_index);
4962 
4963 			while (i<=cluster_lastfphysrec)
4964 			{
4965 				/* the condition below is an error, but it should not prevent
4966 				us from deleting the file */
4967 				if (cur_AU >= image->AUformat.totAUs)
4968 					/*return IMGTOOLERR_CORRUPTIMAGE;*/
4969 					break;
4970 
4971 				image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
4972 
4973 				i += image->AUformat.physrecsperAU;
4974 				cur_AU++;
4975 			}
4976 
4977 			cluster_index++;
4978 		}
4979 		/* the condition below is an error, but it should not prevent us from
4980 		deleting the file */
4981 #if 0
4982 		if (i<fphysrecs)
4983 			return IMGTOOLERR_CORRUPTIMAGE;
4984 #endif
4985 
4986 		/* free fdr AU */
4987 		cur_AU = catalog->files[catalog_index].fdr_ptr / image->AUformat.physrecsperAU;
4988 		image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
4989 
4990 		/* delete catalog entry */
4991 		for (i=catalog_index; i<127; i++)
4992 			catalog->files[i] = catalog->files[i+1];
4993 		catalog->files[127].fdr_ptr = 0;
4994 		catalog->num_files--;
4995 
4996 		/* update parent fdir */
4997 		for (i=0; i<128; i++)
4998 		{
4999 			buf[2*i] = catalog->files[i].fdr_ptr >> 8;
5000 			buf[2*i+1] = catalog->files[i].fdr_ptr & 0xff;
5001 		}
5002 		if (write_absolute_physrec(& image->l1_img, image->dsk.fdir_aphysrec[parent_ref], buf))
5003 			return IMGTOOLERR_WRITEERROR;
5004 
5005 		/* update bitmap */
5006 		{
5007 			dsk_vib vib;
5008 
5009 			if (read_absolute_physrec(& image->l1_img, 0, &vib))
5010 				return IMGTOOLERR_READERROR;
5011 			memcpy(vib.abm, image->abm, 200);
5012 			if (write_absolute_physrec(& image->l1_img, 0, &vib))
5013 				return IMGTOOLERR_WRITEERROR;
5014 		}
5015 	}
5016 
5017 	return (imgtoolerr_t)0;
5018 }
5019 
win_image_deletefile(imgtool::partition & partition,const char * fpath)5020 static imgtoolerr_t win_image_deletefile(imgtool::partition &partition, const char *fpath)
5021 {
5022 	imgtool::image &img(partition.image());
5023 	struct ti99_lvl2_imgref *image = get_lvl2_imgref(img);
5024 	int parent_ddr_AU, is_dir, catalog_index;
5025 	win_fdr fdr;
5026 	int i;
5027 	unsigned cur_AU, end_AU;
5028 	unsigned curfdr_aphysrec;
5029 	unsigned cursibFDR_index, endsibFDR_index = 0;
5030 	int errorcode;
5031 	uint8_t buf[256];
5032 	ti99_catalog catalog;
5033 
5034 
5035 	if (check_fpath(fpath))
5036 		return (imgtoolerr_t)IMGTOOLERR_BADFILENAME;
5037 
5038 	errorcode = win_find_catalog_entry(image, fpath, NULL, &parent_ddr_AU, &catalog, &is_dir, &catalog_index);
5039 	if (errorcode)
5040 		return (imgtoolerr_t)errorcode;
5041 
5042 	if (is_dir)
5043 	{
5044 		unsigned DDR_AU;
5045 		win_vib_ddr ddr_buf;
5046 
5047 		/* Read DDR record */
5048 		DDR_AU = catalog.subdirs[catalog_index].dir_ptr;
5049 		if (read_absolute_physrec(& image->l1_img, DDR_AU*image->AUformat.physrecsperAU, &ddr_buf))
5050 			return (imgtoolerr_t)IMGTOOLERR_READERROR;
5051 
5052 		/* read FDIR AU pointer */
5053 		cur_AU = get_UINT16BE(ddr_buf.fdir_AU);
5054 
5055 		/* sanity checks */
5056 		if ((ddr_buf.num_files > 127) || (ddr_buf.num_subdirs > 114) || (cur_AU > image->AUformat.totAUs))
5057 			return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
5058 
5059 		if ((ddr_buf.num_files != 0) || (ddr_buf.num_subdirs != 0))
5060 			return (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED;
5061 
5062 		/* free fdir AU */
5063 		image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
5064 
5065 		/* free ddr AU */
5066 		image->abm[DDR_AU >> 3] &= ~ (1 << (DDR_AU & 7));
5067 
5068 		/* delete parent catalog entry */
5069 		for (i=catalog_index; i<113; i++)
5070 			catalog.subdirs[i] = catalog.subdirs[i+1];
5071 		catalog.subdirs[113].dir_ptr = 0;
5072 		catalog.num_subdirs--;
5073 
5074 		/* update parent VIB/DDR */
5075 		if (read_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &ddr_buf))
5076 			return (imgtoolerr_t)IMGTOOLERR_READERROR;
5077 		ddr_buf.num_subdirs = catalog.num_subdirs;
5078 		for (i=0; i<114; i++)
5079 			set_UINT16BE(&ddr_buf.subdir_AU[i], catalog.subdirs[i].dir_ptr);
5080 		if (write_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &ddr_buf))
5081 			return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
5082 
5083 		/* save allocation bitmap (aphysrecs 1 through n, n<=33) */
5084 		for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
5085 			if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256))
5086 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
5087 	}
5088 	else
5089 	{
5090 		/* check integrity of FDR sibling chain, and go to last sibling */
5091 		/* note that as we check that the back chain is consistent with the forward
5092 		chain, we will also detect any cycle in the sibling chain, so we do not
5093 		need to check against them explicitly */
5094 		{
5095 			int pastendoflist_flag;
5096 			unsigned prevfdr_aphysrec;
5097 
5098 
5099 			pastendoflist_flag = 0;
5100 			cursibFDR_index = 0;
5101 			curfdr_aphysrec = catalog.files[catalog_index].fdr_ptr * image->AUformat.physrecsperAU;
5102 			if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr))
5103 				return (imgtoolerr_t)IMGTOOLERR_READERROR;
5104 
5105 			if (get_UINT16BE(fdr.prevsibFDR_AU) != 0)
5106 				return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
5107 
5108 			while (1)
5109 			{
5110 				if (! pastendoflist_flag)
5111 				{
5112 					/* look for end of list */
5113 					for (i=0; i<54; i++)
5114 					{
5115 						if (get_UINT16BE(fdr.clusters[i][0]) == 0)
5116 						{
5117 							endsibFDR_index = cursibFDR_index;
5118 							pastendoflist_flag = true;
5119 							break;
5120 						}
5121 					}
5122 				}
5123 
5124 				/* exit loop if end of sibling chain */
5125 				if (! get_UINT16BE(fdr.nextsibFDR_AU))
5126 					break;
5127 
5128 				/* otherwise read next FDR */
5129 				if (get_UINT16BE(fdr.nextsibFDR_AU) >= image->AUformat.totAUs)
5130 					return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
5131 
5132 				prevfdr_aphysrec = curfdr_aphysrec;
5133 				curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(image, &fdr);
5134 				if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr))
5135 					return (imgtoolerr_t)IMGTOOLERR_READERROR;
5136 				cursibFDR_index++;
5137 
5138 				/* check that back chaining is consistent with forward chaining */
5139 				if (get_win_fdr_prevsibFDR_aphysrec(image, &fdr) != prevfdr_aphysrec)
5140 					return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE;
5141 			}
5142 			if (! pastendoflist_flag)
5143 				endsibFDR_index = cursibFDR_index;
5144 		}
5145 
5146 
5147 		while (1)
5148 		{
5149 			if (cursibFDR_index <= endsibFDR_index)
5150 			{
5151 				for (i = 0; i < 54; i++)
5152 				{
5153 					cur_AU = get_UINT16BE(fdr.clusters[i][0]);
5154 					if (cur_AU == 0)
5155 						/* end of cluster list */
5156 						break;
5157 					end_AU = get_UINT16BE(fdr.clusters[i][1]);
5158 
5159 					while (cur_AU<=end_AU)
5160 					{
5161 						/* the condition below is an error, but it should not prevent
5162 						us from deleting the file */
5163 						if (cur_AU >= image->AUformat.totAUs)
5164 							/*return IMGTOOLERR_CORRUPTIMAGE;*/
5165 							break;
5166 
5167 						image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
5168 
5169 						cur_AU++;
5170 					}
5171 				}
5172 			}
5173 
5174 			/* free fdr AU if applicable */
5175 			if ((curfdr_aphysrec % image->AUformat.physrecsperAU) == 0)
5176 			{
5177 				cur_AU = curfdr_aphysrec / image->AUformat.physrecsperAU;
5178 				image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7));
5179 			}
5180 
5181 			if (cursibFDR_index == 0)
5182 				break;
5183 			curfdr_aphysrec = get_win_fdr_prevsibFDR_aphysrec(image, &fdr);
5184 			if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr))
5185 				return (imgtoolerr_t)IMGTOOLERR_READERROR;
5186 			cursibFDR_index--;
5187 		}
5188 
5189 
5190 		/* delete catalog entry */
5191 		for (i=catalog_index; i<127; i++)
5192 			catalog.files[i] = catalog.files[i+1];
5193 		catalog.files[127].fdr_ptr = 0;
5194 		catalog.num_files--;
5195 
5196 
5197 		/* update VIB/DDR and get FDIR AU address */
5198 		{
5199 			win_vib_ddr parent_ddr;
5200 
5201 			if (read_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &parent_ddr))
5202 				return (imgtoolerr_t)IMGTOOLERR_READERROR;
5203 			parent_ddr.num_files = catalog.num_files;
5204 			if (write_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &parent_ddr))
5205 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
5206 			cur_AU = get_UINT16BE(parent_ddr.fdir_AU);
5207 		}
5208 
5209 		/* generate FDIR and save it */
5210 		for (i=0; i<127; i++)
5211 		{
5212 			buf[2*i] = catalog.files[i].fdr_ptr >> 8;
5213 			buf[2*i+1] = catalog.files[i].fdr_ptr & 0xff;
5214 		}
5215 		buf[254] = parent_ddr_AU >> 8;
5216 		buf[255] = parent_ddr_AU & 0xff;
5217 		if (write_absolute_physrec(& image->l1_img, cur_AU * image->AUformat.physrecsperAU, buf))
5218 			return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
5219 
5220 		/* save allocation bitmap (aphysrecs 1 through n, n<=33) */
5221 		for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++)
5222 			if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256))
5223 				return (imgtoolerr_t)IMGTOOLERR_WRITEERROR;
5224 	}
5225 
5226 	return (imgtoolerr_t)0;
5227 }
5228 
5229 /*
5230     Create a blank ti99_image (common code).
5231 
5232     Supports MESS and V9T9 formats only
5233 */
dsk_image_create(imgtool::image & image,imgtool::stream::ptr && stream,util::option_resolution * createoptions,ti99_img_format img_format)5234 static imgtoolerr_t dsk_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions, ti99_img_format img_format)
5235 {
5236 	const char *volname;
5237 	int density;
5238 	ti99_lvl1_imgref l1_img;
5239 	int protected_var;
5240 
5241 	int totphysrecs, physrecsperAU, totAUs;
5242 
5243 	dsk_vib vib;
5244 	uint8_t empty_sec[256];
5245 
5246 	int i;
5247 
5248 	l1_img.img_format = img_format;
5249 	l1_img.file_handle = stream.get();  // can't release here
5250 
5251 	/* read options */
5252 	volname = createoptions->lookup_string(dsk_createopts_volname).c_str();
5253 	l1_img.geometry.heads = createoptions->lookup_int(dsk_createopts_sides);
5254 	l1_img.geometry.cylinders = createoptions->lookup_int(dsk_createopts_tracks);
5255 	l1_img.geometry.secspertrack = createoptions->lookup_int(dsk_createopts_sectors);
5256 	protected_var = createoptions->lookup_int(dsk_createopts_protection);
5257 	density = createoptions->lookup_int(dsk_createopts_density);
5258 
5259 	/* NPW 03-DEC-2003 - Fixes a crash if volname is NULL */
5260 	if (!volname)
5261 		volname = "";
5262 
5263 	totphysrecs = l1_img.geometry.secspertrack * l1_img.geometry.cylinders * l1_img.geometry.heads;
5264 	physrecsperAU = (totphysrecs + 1599) / 1600;
5265 	/* round to next larger power of 2 */
5266 	for (i = 1; i < physrecsperAU; i <<= 1)
5267 		;
5268 	physrecsperAU = i;
5269 	totAUs = totphysrecs / physrecsperAU;
5270 
5271 	/* auto-density */
5272 	if (density == 0)
5273 	{
5274 		if (l1_img.geometry.secspertrack <= 9)
5275 			density = 1;
5276 		else if (l1_img.geometry.secspertrack <= 18)
5277 			density = 2;
5278 		else
5279 			density = 3;
5280 	}
5281 
5282 	/* check volume name */
5283 	if (strchr(volname, '.') || strchr(volname, ' ') || (strlen(volname) > 10))
5284 		return /*IMGTOOLERR_BADFILENAME*/IMGTOOLERR_PARAMCORRUPT;
5285 
5286 	/* FM disks can hold fewer sector per track than MFM disks */
5287 	if ((density == 1) && (l1_img.geometry.secspertrack > 9))
5288 		return IMGTOOLERR_PARAMTOOLARGE;
5289 
5290 	/* DD disks can hold fewer sector per track than HD disks */
5291 	if ((density == 2) && (l1_img.geometry.secspertrack > 18))
5292 		return IMGTOOLERR_PARAMTOOLARGE;
5293 
5294 	/* check total disk size */
5295 	if (totphysrecs < 2)
5296 		return IMGTOOLERR_PARAMTOOSMALL;
5297 
5298 	/* initialize vib in aphysrec 0 */
5299 
5300 	/* set volume name */
5301 	str_to_fname(vib.name, volname);
5302 
5303 	/* set every header field */
5304 	set_UINT16BE(& vib.totphysrecs, totphysrecs);
5305 	vib.secspertrack = l1_img.geometry.secspertrack;
5306 	vib.id[0] = 'D';
5307 	vib.id[1] = 'S';
5308 	vib.id[2] = 'K';
5309 	vib.protection = protected_var ? 'P' : ' ';
5310 	vib.cylinders = l1_img.geometry.cylinders;
5311 	vib.heads = l1_img.geometry.heads;
5312 	vib.density = density;
5313 	for (i=0; i<3; i++)
5314 	{
5315 		memset(vib.subdir[i].name, '\0', 10);
5316 		set_UINT16BE(& vib.subdir[i].fdir_aphysrec, 0);
5317 	}
5318 
5319 	/* clear bitmap */
5320 	for (i=0; i < (totAUs>>3); i++)
5321 		vib.abm[i] = 0;
5322 
5323 	if (totAUs & 7)
5324 	{
5325 		vib.abm[i] = 0xff << (totAUs & 7);
5326 		i++;
5327 	}
5328 
5329 	for (; i < 200; i++)
5330 		vib.abm[i] = 0xff;
5331 
5332 	/* mark first 2 aphysrecs (vib and fdir) as used */
5333 	vib.abm[0] |= (physrecsperAU == 1) ? 3 : 1;
5334 
5335 	/* write aphysrec 0 */
5336 	if (write_absolute_physrec(&l1_img, 0, &vib))
5337 		return IMGTOOLERR_WRITEERROR;
5338 
5339 
5340 	/* now clear every other physrecs, including the FDIR record */
5341 	memset(empty_sec, 0, sizeof(empty_sec));
5342 
5343 	for (i=1; i<totphysrecs; i++)
5344 		if (write_absolute_physrec(& l1_img, i, empty_sec))
5345 			return IMGTOOLERR_WRITEERROR;
5346 
5347 
5348 	return dsk_image_init(image, std::move(stream), img_format);
5349 }
5350 
5351 /*
5352     Create a blank ti99_image (MESS format).
5353 */
dsk_image_create_mess(imgtool::image & image,imgtool::stream::ptr && stream,util::option_resolution * createoptions)5354 static imgtoolerr_t dsk_image_create_mess(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions)
5355 {
5356 	return dsk_image_create(image, std::move(stream), createoptions, if_mess);
5357 }
5358 
5359 /*
5360     Create a blank ti99_image (V9T9 format).
5361 */
dsk_image_create_v9t9(imgtool::image & image,imgtool::stream::ptr && stream,util::option_resolution * createoptions)5362 static imgtoolerr_t dsk_image_create_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions)
5363 {
5364 	return dsk_image_create(image, std::move(stream), createoptions, if_v9t9);
5365 }
5366