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