1 // license:BSD-3-Clause
2 // copyright-holders:Raphael Nabet
3 /****************************************************************************
4
5 prodos.cpp
6
7 Apple II ProDOS disk images
8
9 *****************************************************************************
10
11 Notes:
12 - ProDOS disks are split into 512 byte blocks.
13
14 ProDOS directory structure:
15
16 Offset Length Description
17 ------ ------ -----------
18 0 2 ???
19 2 2 Next block (0 if end)
20 4 39 Directory Entry
21 43 39 Directory Entry
22 ...
23 472 39 Directory Entry
24 511 1 ???
25
26
27 ProDOS directory entry structure:
28
29 Offset Length Description
30 ------ ------ -----------
31 0 1 Storage type (bits 7-4)
32 10 - Seedling File (1 block)
33 20 - Sapling File (2-256 blocks)
34 30 - Tree File (257-32768 blocks)
35 40 - Pascal Areas on ProFile HDs (???)
36 50 - GS/OS Extended File (data and rsrc fork)
37 E0 - Subdirectory Header
38 F0 - Volume Header
39 1 15 File name (NUL padded)
40 16 1 File type
41 17 2 Key pointer
42 19 2 Blocks used
43 21 3 File size
44 24 4 Creation date
45 28 1 ProDOS version that created the file
46 29 1 Minimum ProDOS version needed to read the file
47 30 1 Access byte
48 31 2 Auxilary file type
49 33 4 Last modified date
50 37 2 Header pointer
51
52
53 In "seedling files", the key pointer points to a single block that is the
54 whole file. In "sapling files", the key pointer points to an index block
55 that contains 256 2-byte index pointers that point to the actual blocks of
56 the files. These 2-byte values are not contiguous; the low order byte is
57 in the first half of the block, and the high order byte is in the second
58 half of the block. In "tree files", the key pointer points to an index
59 block of index blocks.
60
61 ProDOS dates are 32-bit little endian values
62
63 bits 0- 4 Day
64 bits 5- 8 Month (0-11)
65 bits 9-15 Year (0-49 is 2000-2049, 50-99 is 1950-1999)
66 bits 16-21 Minute
67 bits 24-28 Hour
68
69
70 ProDOS directory and volume headers have this information:
71
72 Offset Length Description
73 ------ ------ -----------
74 31 1 Length of the entry; generally is 39
75 32 1 Number of entries per block; generally is 13
76 33 2 Active entry count in directory
77 35 2 Volume bitmap block number
78 37 2 Total blocks on volume
79
80 GS/OS Extended Files (storage type $5) point to an extended key block that
81 contains information about the two forks. The first half of the extended
82 key block contains info about the data form, and the second half the
83 resource fork. Both sides have the following format:
84
85 Offset Length Description
86 ------ ------ -----------
87 0 1 Storage type (bits 3-0, unlike the directory entry)
88 1 2 Key pointer
89 3 2 Blocks used
90 5 3 File size
91 8 1 Size of secondary info #1 (must be 18 to be valid)
92 9 1 Secondary info #1 type (1=FInfo 2=xFInfo)
93 10 16 FInfo or xFInfo
94 26 1 Size of secondary info #2 (must be 18 to be valid)
95 27 1 Secondary info #2 type (1=FInfo 2=xFInfo)
96 28 16 FInfo or xFInfo
97
98 FInfo format:
99
100 Offset Length Description
101 ------ ------ -----------
102 0 4 File type
103 4 4 File creator
104 8 2 Finder flags
105 10 2 X Coordinate
106 12 2 Y Coordinate
107 14 2 Finder folder
108
109 xFInfo format:
110
111 Offset Length Description
112 ------ ------ -----------
113 0 2 Icon ID
114 2 6 Reserved
115 8 1 Script Code
116 9 1 Extended flags
117 10 2 Comment ID
118 12 4 Put Away Directory
119
120
121 For more info, consult ProDOS technical note #25
122 (http://web.pdx.edu/~heiss/technotes/pdos/tn.pdos.25.html)
123
124 *****************************************************************************/
125
126 #include "imgtool.h"
127 #include "formats/imageutl.h"
128 #include "formats/ap2_dsk.h"
129 #include "formats/ap_dsk35.h"
130 #include "iflopimg.h"
131 #include "macutil.h"
132
133 #define ROOTDIR_BLOCK 2
134 #define BLOCK_SIZE 512
135
136 struct prodos_diskinfo
137 {
138 imgtoolerr_t (*load_block)(imgtool::image &image, int block, void *buffer);
139 imgtoolerr_t (*save_block)(imgtool::image &image, int block, const void *buffer);
140 uint8_t dirent_size;
141 uint8_t dirents_per_block;
142 uint16_t volume_bitmap_block;
143 uint16_t total_blocks;
144 };
145
146 struct prodos_direnum
147 {
148 uint32_t block;
149 uint32_t index;
150 uint8_t block_data[BLOCK_SIZE];
151 };
152
153 struct prodos_dirent
154 {
155 char filename[16];
156 uint8_t storage_type;
157 uint16_t extkey_pointer;
158 uint16_t key_pointer[2];
159 uint32_t filesize[2];
160 int depth[2];
161 uint32_t lastmodified_time;
162 uint32_t creation_time;
163
164 /* FInfo */
165 uint32_t file_type;
166 uint32_t file_creator;
167 uint16_t finder_flags;
168 uint16_t coord_x;
169 uint16_t coord_y;
170 uint16_t finder_folder;
171
172 /* xFInfo */
173 uint16_t icon_id;
174 uint8_t script_code;
175 uint8_t extended_flags;
176 uint16_t comment_id;
177 uint32_t putaway_directory;
178 };
179
180 enum creation_policy_t
181 {
182 CREATE_NONE,
183 CREATE_FILE,
184 CREATE_DIR
185 };
186
187
188
prodos_crack_time(uint32_t prodos_time)189 static imgtool::datetime prodos_crack_time(uint32_t prodos_time)
190 {
191 util::arbitrary_datetime dt;
192 dt.second = 0;
193 dt.minute = ((prodos_time >> 16) & 0x3F);
194 dt.hour = ((prodos_time >> 24) & 0x1F);
195 dt.day_of_month = ((prodos_time >> 0) & 0x1F);
196 dt.month = ((prodos_time >> 5) & 0x0F) + 1;
197 dt.year = ((prodos_time >> 9) & 0x7F) + 1900;
198 if (dt.year <= 1949)
199 dt.year += 100;
200
201 return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt);
202 }
203
204
205
prodos_setup_time(time_t ansi_time)206 static uint32_t prodos_setup_time(time_t ansi_time)
207 {
208 struct tm t;
209 uint32_t result = 0;
210
211 t = *localtime(&ansi_time);
212 if ((t.tm_year >= 100) && (t.tm_year <= 149))
213 t.tm_year -= 100;
214
215 result |= (((uint32_t) t.tm_min) & 0x003F) << 16;
216 result |= (((uint32_t) t.tm_hour) & 0x001F) << 24;
217 result |= (((uint32_t) t.tm_mday) & 0x001F) << 0;
218 result |= (((uint32_t) t.tm_mon) & 0x000F) << 5;
219 result |= (((uint32_t) t.tm_year) & 0x007F) << 9;
220 return result;
221 }
222
223
224
prodos_time_now(void)225 static uint32_t prodos_time_now(void)
226 {
227 time_t now;
228 time(&now);
229 return prodos_setup_time(now);
230 }
231
232
233
is_file_storagetype(uint8_t storage_type)234 static int is_file_storagetype(uint8_t storage_type)
235 {
236 return ((storage_type >= 0x10) && (storage_type <= 0x3F))
237 || ((storage_type >= 0x50) && (storage_type <= 0x5F));
238 }
239
240
241
is_normalfile_storagetype(uint8_t storage_type)242 static int is_normalfile_storagetype(uint8_t storage_type)
243 {
244 return ((storage_type >= 0x10) && (storage_type <= 0x3F));
245 }
246
247
248
is_extendedfile_storagetype(uint8_t storage_type)249 static int is_extendedfile_storagetype(uint8_t storage_type)
250 {
251 return ((storage_type >= 0x50) && (storage_type <= 0x5F));
252 }
253
254
255
is_dir_storagetype(uint8_t storage_type)256 static int is_dir_storagetype(uint8_t storage_type)
257 {
258 return (storage_type >= 0xE0) && (storage_type <= 0xEF);
259 }
260
261
262
get_prodos_info(imgtool::image & image)263 static prodos_diskinfo *get_prodos_info(imgtool::image &image)
264 {
265 prodos_diskinfo *info;
266 info = (prodos_diskinfo *) imgtool_floppy_extrabytes(image);
267 return info;
268 }
269
270
271
272 /* ----------------------------------------------------------------------- */
273
prodos_find_block_525(imgtool::image & image,int block,uint32_t * track,uint32_t * head,uint32_t * sector1,uint32_t * sector2)274 static void prodos_find_block_525(imgtool::image &image, int block,
275 uint32_t *track, uint32_t *head, uint32_t *sector1, uint32_t *sector2)
276 {
277 static const uint8_t skewing[] =
278 {
279 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
280 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F
281 };
282
283 block *= 2;
284
285 *track = block / APPLE2_SECTOR_COUNT;
286 *head = 0;
287 *sector1 = skewing[block % APPLE2_SECTOR_COUNT + 0];
288 *sector2 = skewing[block % APPLE2_SECTOR_COUNT + 1];
289 }
290
291
292
prodos_load_block_525(imgtool::image & image,int block,void * buffer)293 static imgtoolerr_t prodos_load_block_525(imgtool::image &image,
294 int block, void *buffer)
295 {
296 floperr_t ferr;
297 uint32_t track, head, sector1, sector2;
298
299 prodos_find_block_525(image, block, &track, &head, §or1, §or2);
300
301 /* read first sector */
302 ferr = floppy_read_sector(imgtool_floppy(image), head, track,
303 sector1, 0, ((uint8_t *) buffer) + 0, 256);
304 if (ferr)
305 return imgtool_floppy_error(ferr);
306
307 /* read second sector */
308 ferr = floppy_read_sector(imgtool_floppy(image), head, track,
309 sector2, 0, ((uint8_t *) buffer) + 256, 256);
310 if (ferr)
311 return imgtool_floppy_error(ferr);
312
313 return IMGTOOLERR_SUCCESS;
314 }
315
316
317
prodos_save_block_525(imgtool::image & image,int block,const void * buffer)318 static imgtoolerr_t prodos_save_block_525(imgtool::image &image,
319 int block, const void *buffer)
320 {
321 floperr_t ferr;
322 uint32_t track, head, sector1, sector2;
323
324 prodos_find_block_525(image, block, &track, &head, §or1, §or2);
325
326 /* read first sector */
327 ferr = floppy_write_sector(imgtool_floppy(image), head, track,
328 sector1, 0, ((const uint8_t *) buffer) + 0, 256, 0); /* TODO: pass ddam argument from imgtool */
329 if (ferr)
330 return imgtool_floppy_error(ferr);
331
332 /* read second sector */
333 ferr = floppy_write_sector(imgtool_floppy(image), head, track,
334 sector2, 0, ((const uint8_t *) buffer) + 256, 256, 0); /* TODO: pass ddam argument from imgtool */
335 if (ferr)
336 return imgtool_floppy_error(ferr);
337
338 return IMGTOOLERR_SUCCESS;
339 }
340
341
342
prodos_setprocs_525(imgtool::image & image)343 static void prodos_setprocs_525(imgtool::image &image)
344 {
345 prodos_diskinfo *info;
346 info = get_prodos_info(image);
347 info->load_block = prodos_load_block_525;
348 info->save_block = prodos_save_block_525;
349 }
350
351
352
353 /* ----------------------------------------------------------------------- */
354
prodos_find_block_35(imgtool::image & image,int block,uint32_t * track,uint32_t * head,uint32_t * sector)355 static imgtoolerr_t prodos_find_block_35(imgtool::image &image, int block,
356 uint32_t *track, uint32_t *head, uint32_t *sector)
357 {
358 int sides = 2;
359
360 *track = 0;
361 while(block >= (apple35_sectors_per_track(imgtool_floppy(image), *track) * sides))
362 {
363 block -= (apple35_sectors_per_track(imgtool_floppy(image), (*track)++) * sides);
364 if (*track >= 80)
365 return IMGTOOLERR_SEEKERROR;
366 }
367
368 *head = block / apple35_sectors_per_track(imgtool_floppy(image), *track);
369 *sector = block % apple35_sectors_per_track(imgtool_floppy(image), *track);
370 return IMGTOOLERR_SUCCESS;
371 }
372
373
374
prodos_load_block_35(imgtool::image & image,int block,void * buffer)375 static imgtoolerr_t prodos_load_block_35(imgtool::image &image,
376 int block, void *buffer)
377 {
378 imgtoolerr_t err;
379 floperr_t ferr;
380 uint32_t track, head, sector;
381
382 err = prodos_find_block_35(image, block, &track, &head, §or);
383 if (err)
384 return err;
385
386 ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 512);
387 if (ferr)
388 return imgtool_floppy_error(ferr);
389
390 return IMGTOOLERR_SUCCESS;
391 }
392
393
394
prodos_save_block_35(imgtool::image & image,int block,const void * buffer)395 static imgtoolerr_t prodos_save_block_35(imgtool::image &image,
396 int block, const void *buffer)
397 {
398 imgtoolerr_t err;
399 floperr_t ferr;
400 uint32_t track, head, sector;
401
402 err = prodos_find_block_35(image, block, &track, &head, §or);
403 if (err)
404 return err;
405
406 ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 512, 0); /* TODO: pass ddam argument from imgtool */
407 if (ferr)
408 return imgtool_floppy_error(ferr);
409
410 return IMGTOOLERR_SUCCESS;
411 }
412
413
414
prodos_setprocs_35(imgtool::image & image)415 static void prodos_setprocs_35(imgtool::image &image)
416 {
417 prodos_diskinfo *info;
418 info = get_prodos_info(image);
419 info->load_block = prodos_load_block_35;
420 info->save_block = prodos_save_block_35;
421 }
422
423
424
425 /* ----------------------------------------------------------------------- */
426
prodos_load_block(imgtool::image & image,int block,void * buffer)427 static imgtoolerr_t prodos_load_block(imgtool::image &image,
428 int block, void *buffer)
429 {
430 prodos_diskinfo *diskinfo;
431 diskinfo = get_prodos_info(image);
432 return diskinfo->load_block(image, block, buffer);
433 }
434
435
436
prodos_save_block(imgtool::image & image,int block,const void * buffer)437 static imgtoolerr_t prodos_save_block(imgtool::image &image,
438 int block, const void *buffer)
439 {
440 prodos_diskinfo *diskinfo;
441 diskinfo = get_prodos_info(image);
442 return diskinfo->save_block(image, block, buffer);
443 }
444
445
446
prodos_clear_block(imgtool::image & image,int block)447 static imgtoolerr_t prodos_clear_block(imgtool::image &image, int block)
448 {
449 uint8_t buffer[BLOCK_SIZE];
450 memset(buffer, 0, sizeof(buffer));
451 return prodos_save_block(image, block, buffer);
452 }
453
454
455
456 /* ----------------------------------------------------------------------- */
457
prodos_diskimage_open(imgtool::image & image)458 static imgtoolerr_t prodos_diskimage_open(imgtool::image &image)
459 {
460 imgtoolerr_t err;
461 uint8_t buffer[BLOCK_SIZE];
462 prodos_diskinfo *di;
463 const uint8_t *ent;
464
465 di = get_prodos_info(image);
466
467 /* specify defaults */
468 di->dirent_size = 39;
469 di->dirents_per_block = 13;
470
471 /* load the first block, hoping that the volume header is first */
472 err = prodos_load_block(image, ROOTDIR_BLOCK, buffer);
473 if (err)
474 return err;
475
476 ent = &buffer[4];
477
478 /* did we find the volume header? */
479 if ((ent[0] & 0xF0) == 0xF0)
480 {
481 di->dirent_size = pick_integer_le(ent, 31, 1);
482 di->dirents_per_block = pick_integer_le(ent, 32, 1);
483 di->volume_bitmap_block = pick_integer_le(ent, 35, 2);
484 di->total_blocks = pick_integer_le(ent, 37, 2);
485 }
486
487 /* sanity check these values */
488 if (di->dirent_size < 39)
489 return IMGTOOLERR_CORRUPTIMAGE;
490 if (di->dirents_per_block * di->dirent_size >= BLOCK_SIZE)
491 return IMGTOOLERR_CORRUPTIMAGE;
492 if (di->volume_bitmap_block >= di->total_blocks)
493 return IMGTOOLERR_CORRUPTIMAGE;
494
495 return IMGTOOLERR_SUCCESS;
496 }
497
498
499
prodos_diskimage_open_525(imgtool::image & image,imgtool::stream::ptr && dummy)500 static imgtoolerr_t prodos_diskimage_open_525(imgtool::image &image, imgtool::stream::ptr &&dummy)
501 {
502 prodos_setprocs_525(image);
503 return prodos_diskimage_open(image);
504 }
505
506
507
prodos_diskimage_open_35(imgtool::image & image,imgtool::stream::ptr && dummy)508 static imgtoolerr_t prodos_diskimage_open_35(imgtool::image &image, imgtool::stream::ptr &&dummy)
509 {
510 prodos_setprocs_35(image);
511 return prodos_diskimage_open(image);
512 }
513
514
515
516 /* ----------------------------------------------------------------------- */
517
prodos_load_volume_bitmap(imgtool::image & image,uint8_t ** bitmap)518 static imgtoolerr_t prodos_load_volume_bitmap(imgtool::image &image, uint8_t **bitmap)
519 {
520 imgtoolerr_t err;
521 prodos_diskinfo *di;
522 uint8_t *alloc_bitmap;
523 uint32_t bitmap_blocks, i;
524
525 di = get_prodos_info(image);
526
527 bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
528 alloc_bitmap = (uint8_t*)malloc(bitmap_blocks * BLOCK_SIZE);
529 if (!alloc_bitmap)
530 {
531 err = IMGTOOLERR_OUTOFMEMORY;
532 goto done;
533 }
534
535 for (i = 0; i < bitmap_blocks; i++)
536 {
537 err = prodos_load_block(image, di->volume_bitmap_block + i,
538 &alloc_bitmap[i * BLOCK_SIZE]);
539 if (err)
540 goto done;
541 }
542
543 err = IMGTOOLERR_SUCCESS;
544
545 done:
546 if (err && alloc_bitmap)
547 {
548 free(alloc_bitmap);
549 alloc_bitmap = NULL;
550 }
551 *bitmap = alloc_bitmap;
552 return err;
553 }
554
555
556
prodos_save_volume_bitmap(imgtool::image & image,const uint8_t * bitmap)557 static imgtoolerr_t prodos_save_volume_bitmap(imgtool::image &image, const uint8_t *bitmap)
558 {
559 imgtoolerr_t err;
560 prodos_diskinfo *di;
561 uint32_t bitmap_blocks, i;
562
563 di = get_prodos_info(image);
564
565 bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
566
567 for (i = 0; i < bitmap_blocks; i++)
568 {
569 err = prodos_save_block(image, di->volume_bitmap_block + i,
570 &bitmap[i * BLOCK_SIZE]);
571 if (err)
572 return err;
573 }
574 return IMGTOOLERR_SUCCESS;
575 }
576
577
578
prodos_set_volume_bitmap_bit(uint8_t * buffer,uint16_t block,int value)579 static void prodos_set_volume_bitmap_bit(uint8_t *buffer, uint16_t block, int value)
580 {
581 uint8_t mask;
582 buffer += block / 8;
583 mask = 1 << (7 - (block % 8));
584 if (value)
585 *buffer |= mask;
586 else
587 *buffer &= ~mask;
588 }
589
590
591
prodos_get_volume_bitmap_bit(const uint8_t * buffer,uint16_t block)592 static int prodos_get_volume_bitmap_bit(const uint8_t *buffer, uint16_t block)
593 {
594 uint8_t mask;
595 buffer += block / 8;
596 mask = 1 << (7 - (block % 8));
597 return (*buffer & mask) ? 1 : 0;
598 }
599
600
601
prodos_alloc_block(imgtool::image & image,uint8_t * bitmap,uint16_t * block)602 static imgtoolerr_t prodos_alloc_block(imgtool::image &image, uint8_t *bitmap,
603 uint16_t *block)
604 {
605 imgtoolerr_t err = IMGTOOLERR_SUCCESS;
606 prodos_diskinfo *di;
607 uint16_t bitmap_blocks, i;
608 uint8_t *alloc_bitmap = NULL;
609
610 di = get_prodos_info(image);
611 *block = 0;
612 bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
613
614 if (!bitmap)
615 {
616 err = prodos_load_volume_bitmap(image, &alloc_bitmap);
617 if (err)
618 goto done;
619 bitmap = alloc_bitmap;
620 }
621
622 for (i = (di->volume_bitmap_block + bitmap_blocks); i < di->total_blocks; i++)
623 {
624 if (!prodos_get_volume_bitmap_bit(bitmap, i))
625 {
626 prodos_set_volume_bitmap_bit(bitmap, i, 1);
627 *block = i;
628 break;
629 }
630 }
631
632 if (*block > 0)
633 {
634 if (alloc_bitmap)
635 {
636 err = prodos_save_volume_bitmap(image, bitmap);
637 if (err)
638 goto done;
639 }
640 }
641 else
642 {
643 err = IMGTOOLERR_NOSPACE;
644 }
645
646 done:
647 if (err)
648 *block = 0;
649 if (alloc_bitmap)
650 free(alloc_bitmap);
651 return err;
652 }
653
654
655
656 /* ----------------------------------------------------------------------- */
657
prodos_diskimage_create(imgtool::image & image,util::option_resolution * opts)658 static imgtoolerr_t prodos_diskimage_create(imgtool::image &image, util::option_resolution *opts)
659 {
660 imgtoolerr_t err;
661 uint32_t heads, tracks, sectors, sector_bytes;
662 uint32_t dirent_size, volume_bitmap_block, i;
663 uint32_t volume_bitmap_block_count, total_blocks;
664 uint8_t buffer[BLOCK_SIZE];
665
666 heads = opts->lookup_int('H');
667 tracks = opts->lookup_int('T');
668 sectors = opts->lookup_int('S');
669 sector_bytes = opts->lookup_int('L');
670
671 dirent_size = 39;
672 volume_bitmap_block = 6;
673 total_blocks = tracks * heads * sectors * sector_bytes / BLOCK_SIZE;
674 volume_bitmap_block_count = (total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8);
675
676 /* prepare initial dir block */
677 memset(buffer, 0, sizeof(buffer));
678 place_integer_le(buffer, 4 + 0, 1, 0xF0);
679 place_integer_le(buffer, 4 + 31, 1, dirent_size);
680 place_integer_le(buffer, 4 + 32, 1, BLOCK_SIZE / dirent_size);
681 place_integer_le(buffer, 4 + 35, 2, volume_bitmap_block);
682 place_integer_le(buffer, 4 + 37, 2, total_blocks);
683
684 err = prodos_save_block(image, ROOTDIR_BLOCK, buffer);
685 if (err)
686 return err;
687
688 /* setup volume bitmap */
689 memset(buffer, 0, sizeof(buffer));
690 for (i = 0; i < (volume_bitmap_block + volume_bitmap_block_count); i++)
691 prodos_set_volume_bitmap_bit(buffer, i, 1);
692 prodos_save_block(image, volume_bitmap_block, buffer);
693
694 /* and finally open the image */
695 return prodos_diskimage_open(image);
696 }
697
698
699
prodos_diskimage_create_525(imgtool::image & image,imgtool::stream::ptr && dummy,util::option_resolution * opts)700 static imgtoolerr_t prodos_diskimage_create_525(imgtool::image &image, imgtool::stream::ptr &&dummy, util::option_resolution *opts)
701 {
702 prodos_setprocs_525(image);
703 return prodos_diskimage_create(image, opts);
704 }
705
706
707
prodos_diskimage_create_35(imgtool::image & image,imgtool::stream::ptr && dummy,util::option_resolution * opts)708 static imgtoolerr_t prodos_diskimage_create_35(imgtool::image &image, imgtool::stream::ptr &&dummy, util::option_resolution *opts)
709 {
710 prodos_setprocs_35(image);
711 return prodos_diskimage_create(image, opts);
712 }
713
714
715
716 /* ----------------------------------------------------------------------- */
717
prodos_enum_seek(imgtool::image & image,prodos_direnum * appleenum,uint32_t block,uint32_t index)718 static imgtoolerr_t prodos_enum_seek(imgtool::image &image,
719 prodos_direnum *appleenum, uint32_t block, uint32_t index)
720 {
721 imgtoolerr_t err;
722 uint8_t buffer[BLOCK_SIZE];
723
724 if (appleenum->block != block)
725 {
726 if (block != 0)
727 {
728 err = prodos_load_block(image, block, buffer);
729 if (err)
730 return err;
731 memcpy(appleenum->block_data, buffer, sizeof(buffer));
732 }
733 appleenum->block = block;
734 }
735
736 appleenum->index = index;
737 return IMGTOOLERR_SUCCESS;
738 }
739
740
741
next_info_block(uint8_t * buffer,size_t * position)742 static uint8_t *next_info_block(uint8_t *buffer, size_t *position)
743 {
744 size_t side = *position & 0x100;
745 size_t subpos = *position & 0x0FF;
746 uint8_t *result;
747
748 if (subpos < 8)
749 {
750 subpos = 8;
751 *position = side + subpos;
752 }
753
754 while((buffer[side + subpos] == 0x00) || (subpos + buffer[side + subpos] > 0x100))
755 {
756 if (side)
757 return NULL;
758
759 side = 0x100;
760 subpos = 8;
761 }
762
763 result = &buffer[side + subpos];
764 subpos += *result;
765 *position = side + subpos;
766 return result;
767 }
768
769
770
alloc_info_block(uint8_t * buffer,size_t block_size,uint8_t block_type)771 static uint8_t *alloc_info_block(uint8_t *buffer, size_t block_size, uint8_t block_type)
772 {
773 size_t position = 0;
774 size_t side;
775 size_t subpos;
776 uint8_t *result;
777
778 while(next_info_block(buffer, &position))
779 ;
780
781 side = position & 0x100;
782 subpos = position & 0x0FF;
783
784 if ((subpos + block_size) > 0x100)
785 return NULL;
786
787 result = &buffer[side + subpos];
788 *(result++) = (uint8_t) block_size;
789 *(result++) = block_type;
790 memset(result, 0, block_size - 2);
791 return result;
792 }
793
794
795
prodos_get_next_dirent(imgtool::image & image,prodos_direnum * appleenum,prodos_dirent & ent)796 static imgtoolerr_t prodos_get_next_dirent(imgtool::image &image,
797 prodos_direnum *appleenum, prodos_dirent &ent)
798 {
799 imgtoolerr_t err;
800 prodos_diskinfo *di;
801 size_t finfo_offset;
802 uint32_t next_block, next_index;
803 uint32_t offset;
804 uint8_t buffer[BLOCK_SIZE];
805 const uint8_t *info_ptr;
806 int fork_num;
807
808 di = get_prodos_info(image);
809 memset(&ent, 0, sizeof(ent));
810
811 /* have we hit the end of the file? */
812 if (appleenum->block == 0)
813 return IMGTOOLERR_SUCCESS;
814
815 /* populate the resulting dirent */
816 offset = appleenum->index * di->dirent_size + 4;
817 ent.storage_type = appleenum->block_data[offset + 0];
818 memcpy(ent.filename, &appleenum->block_data[offset + 1], 15);
819 ent.filename[15] = '\0';
820 ent.creation_time = pick_integer_le(appleenum->block_data, offset + 24, 4);
821 ent.lastmodified_time = pick_integer_le(appleenum->block_data, offset + 33, 4);
822 ent.file_type = 0x3F3F3F3F;
823 ent.file_creator = 0x3F3F3F3F;
824 ent.finder_flags = 0;
825 ent.coord_x = 0;
826 ent.coord_y = 0;
827 ent.finder_folder = 0;
828 ent.icon_id = 0;
829 ent.script_code = 0;
830 ent.extended_flags = 0;
831 ent.comment_id = 0;
832 ent.putaway_directory = 0;
833
834 if (is_extendedfile_storagetype(ent.storage_type))
835 {
836 /* this is a ProDOS extended file; we need to get the extended info
837 * block */
838 ent.extkey_pointer = pick_integer_le(appleenum->block_data, offset + 17, 2);
839
840 err = prodos_load_block(image, ent.extkey_pointer, buffer);
841 if (err)
842 return err;
843
844 for (fork_num = 0; fork_num <= 1; fork_num++)
845 {
846 ent.key_pointer[fork_num] = pick_integer_le(buffer, 1 + (fork_num * 256), 2);
847 ent.filesize[fork_num] = pick_integer_le(buffer, 5 + (fork_num * 256), 3);
848 ent.depth[fork_num] = buffer[fork_num * 256] & 0x0F;
849 }
850
851 finfo_offset = 0;
852 while((info_ptr = next_info_block(buffer, &finfo_offset)) != NULL)
853 {
854 if (*(info_ptr++) == 18)
855 {
856 switch(*(info_ptr++))
857 {
858 case 1: /* FInfo */
859 ent.file_type = pick_integer_be(info_ptr, 0, 4);
860 ent.file_creator = pick_integer_be(info_ptr, 4, 4);
861 ent.finder_flags = pick_integer_be(info_ptr, 8, 2);
862 ent.coord_x = pick_integer_be(info_ptr, 10, 2);
863 ent.coord_y = pick_integer_be(info_ptr, 12, 2);
864 ent.finder_folder = pick_integer_be(info_ptr, 14, 4);
865 break;
866
867 case 2: /* xFInfo */
868 ent.icon_id = pick_integer_be(info_ptr, 0, 2);
869 ent.script_code = pick_integer_be(info_ptr, 8, 1);
870 ent.extended_flags = pick_integer_be(info_ptr, 9, 1);
871 ent.comment_id = pick_integer_be(info_ptr, 10, 2);
872 ent.putaway_directory = pick_integer_be(info_ptr, 12, 4);
873 break;
874 }
875 }
876 }
877 }
878 else
879 {
880 /* normal ProDOS files have all of the info right here */
881 ent.key_pointer[0] = pick_integer_le(appleenum->block_data, offset + 17, 2);
882 ent.filesize[0] = pick_integer_le(appleenum->block_data, offset + 21, 3);
883 ent.depth[0] = ent.storage_type >> 4;
884 }
885
886 /* identify next entry */
887 next_block = appleenum->block;
888 next_index = appleenum->index + 1;
889 if (next_index >= di->dirents_per_block)
890 {
891 next_block = pick_integer_le(appleenum->block_data, 2, 2);
892 next_index = 0;
893 }
894
895 /* seek next block */
896 err = prodos_enum_seek(image, appleenum, next_block, next_index);
897 if (err)
898 return err;
899
900 return IMGTOOLERR_SUCCESS;
901 }
902
903
904
905 /* changes a normal file to a ProDOS extended file */
prodos_promote_file(imgtool::image & image,uint8_t * bitmap,prodos_dirent * ent)906 static imgtoolerr_t prodos_promote_file(imgtool::image &image, uint8_t *bitmap, prodos_dirent *ent)
907 {
908 imgtoolerr_t err;
909 uint16_t new_block;
910 uint8_t buffer[BLOCK_SIZE];
911
912 assert(is_normalfile_storagetype(ent->storage_type));
913
914 err = prodos_alloc_block(image, bitmap, &new_block);
915 if (err)
916 return err;
917
918 /* create raw extended info block */
919 memset(buffer, 0, sizeof(buffer));
920 err = prodos_save_block(image, new_block, buffer);
921 if (err)
922 return err;
923
924 ent->storage_type = (ent->storage_type & 0x0F) | 0x50;
925 ent->extkey_pointer = new_block;
926 return IMGTOOLERR_SUCCESS;
927 }
928
929
930
prodos_put_dirent(imgtool::image & image,prodos_direnum * appleenum,prodos_dirent * ent)931 static imgtoolerr_t prodos_put_dirent(imgtool::image &image,
932 prodos_direnum *appleenum, prodos_dirent *ent)
933 {
934 imgtoolerr_t err;
935 prodos_diskinfo *di;
936 uint32_t offset;
937 size_t finfo_offset;
938 uint8_t buffer[BLOCK_SIZE];
939 int fork_num;
940 int needs_finfo = false;
941 int needs_xfinfo = false;
942 uint8_t *info_ptr;
943 uint8_t *finfo;
944 uint8_t *xfinfo;
945
946 di = get_prodos_info(image);
947 offset = appleenum->index * di->dirent_size + 4;
948
949 /* determine whether we need FInfo and/or xFInfo */
950 if (is_normalfile_storagetype(ent->storage_type))
951 {
952 needs_finfo = (ent->file_type != 0x3F3F3F3F) ||
953 (ent->file_creator != 0x3F3F3F3F) ||
954 (ent->finder_flags != 0) ||
955 (ent->coord_x != 0) ||
956 (ent->coord_y != 0) ||
957 (ent->finder_folder != 0);
958
959 needs_xfinfo = (ent->icon_id != 0) ||
960 (ent->script_code != 0) ||
961 (ent->extended_flags != 0) ||
962 (ent->comment_id != 0) ||
963 (ent->putaway_directory != 0);
964 }
965
966 /* do we need to promote this file to an extended file? */
967 if (!is_extendedfile_storagetype(ent->storage_type)
968 && (needs_finfo || needs_xfinfo))
969 {
970 err = prodos_promote_file(image, NULL, ent);
971 if (err)
972 return err;
973 }
974
975 /* write out the storage type, filename, creation and lastmodified times */
976 appleenum->block_data[offset + 0] = ent->storage_type;
977 memcpy(&appleenum->block_data[offset + 1], ent->filename, 15);
978 place_integer_le(appleenum->block_data, offset + 24, 4, ent->creation_time);
979 place_integer_le(appleenum->block_data, offset + 33, 4, ent->lastmodified_time);
980
981 if (is_extendedfile_storagetype(ent->storage_type))
982 {
983 /* ProDOS extended file */
984 err = prodos_load_block(image, ent->extkey_pointer, buffer);
985 if (err)
986 return err;
987
988 finfo = NULL;
989 xfinfo = NULL;
990
991 for (fork_num = 0; fork_num <= 1; fork_num++)
992 {
993 place_integer_le(buffer, 1 + (fork_num * 256), 2, ent->key_pointer[fork_num]);
994 place_integer_le(buffer, 5 + (fork_num * 256), 3, ent->filesize[fork_num]);
995 buffer[fork_num * 256] = ent->depth[fork_num];
996 }
997
998 finfo_offset = 0;
999 while((info_ptr = next_info_block(buffer, &finfo_offset)) != NULL)
1000 {
1001 if (*(info_ptr++) == 18)
1002 {
1003 switch(*(info_ptr++))
1004 {
1005 case 1: /* FInfo */
1006 finfo = info_ptr;
1007 break;
1008
1009 case 2: /* xFInfo */
1010 xfinfo = info_ptr;
1011 break;
1012 }
1013 }
1014 }
1015
1016 /* allocate the finfo and/or xinfo blocks, if we need them */
1017 if (needs_finfo && !finfo)
1018 finfo = alloc_info_block(buffer, 18, 1);
1019 if (needs_xfinfo && !xfinfo)
1020 xfinfo = alloc_info_block(buffer, 18, 2);
1021
1022 if (finfo)
1023 {
1024 place_integer_be(finfo, 0, 4, ent->file_type);
1025 place_integer_be(finfo, 4, 4, ent->file_creator);
1026 place_integer_be(finfo, 8, 2, ent->finder_flags);
1027 place_integer_be(finfo, 10, 2, ent->coord_x);
1028 place_integer_be(finfo, 12, 2, ent->coord_y);
1029 place_integer_be(finfo, 14, 4, ent->finder_folder);
1030 }
1031
1032 if (xfinfo)
1033 {
1034 place_integer_be(xfinfo, 0, 2, ent->icon_id);
1035 place_integer_be(xfinfo, 8, 1, ent->script_code);
1036 place_integer_be(xfinfo, 9, 1, ent->extended_flags);
1037 place_integer_be(xfinfo, 10, 2, ent->comment_id);
1038 place_integer_be(xfinfo, 12, 4, ent->putaway_directory);
1039 }
1040
1041 err = prodos_save_block(image, ent->extkey_pointer, buffer);
1042 if (err)
1043 return err;
1044
1045 place_integer_le(appleenum->block_data, offset + 17, 2, ent->extkey_pointer);
1046 place_integer_le(appleenum->block_data, offset + 21, 3, BLOCK_SIZE);
1047 }
1048 else
1049 {
1050 /* normal file */
1051 place_integer_le(appleenum->block_data, offset + 17, 2, ent->key_pointer[0]);
1052 place_integer_le(appleenum->block_data, offset + 21, 3, ent->filesize[0]);
1053 }
1054
1055 err = prodos_save_block(image, appleenum->block, appleenum->block_data);
1056 if (err)
1057 return err;
1058
1059 return IMGTOOLERR_SUCCESS;
1060 }
1061
1062
1063
prodos_lookup_path(imgtool::image & image,const char * path,creation_policy_t create,prodos_direnum * direnum,prodos_dirent * ent)1064 static imgtoolerr_t prodos_lookup_path(imgtool::image &image, const char *path,
1065 creation_policy_t create, prodos_direnum *direnum, prodos_dirent *ent)
1066 {
1067 imgtoolerr_t err;
1068 prodos_direnum my_direnum;
1069 uint32_t block = ROOTDIR_BLOCK;
1070 const char *old_path;
1071 uint16_t this_block;
1072 uint32_t this_index;
1073 uint16_t free_block = 0;
1074 uint32_t free_index = 0;
1075 uint16_t new_file_block;
1076 uint8_t buffer[BLOCK_SIZE];
1077
1078 if (!direnum)
1079 direnum = &my_direnum;
1080
1081 while(*path)
1082 {
1083 memset(direnum, 0, sizeof(*direnum));
1084 err = prodos_enum_seek(image, direnum, block, 0);
1085 if (err)
1086 goto done;
1087
1088 do
1089 {
1090 this_block = direnum->block;
1091 this_index = direnum->index;
1092
1093 err = prodos_get_next_dirent(image, direnum, *ent);
1094 if (err)
1095 goto done;
1096
1097 /* if we need to create a file entry and this is free, track it */
1098 if (create && this_block && !free_block && !ent->storage_type)
1099 {
1100 free_block = this_block;
1101 free_index = this_index;
1102 }
1103 }
1104 while(direnum->block && (strcmp(path, ent->filename) || (
1105 !is_file_storagetype(ent->storage_type) &&
1106 !is_dir_storagetype(ent->storage_type))));
1107
1108 old_path = path;
1109 path += strlen(path) + 1;
1110 if (*path)
1111 {
1112 /* we have found part of the path; we are not finished yet */
1113 if (!is_dir_storagetype(ent->storage_type))
1114 {
1115 err = IMGTOOLERR_FILENOTFOUND;
1116 goto done;
1117 }
1118 block = ent->key_pointer[0];
1119 }
1120 else if (!direnum->block)
1121 {
1122 /* did not find file; maybe we need to create it */
1123 if (create == CREATE_NONE)
1124 {
1125 err = IMGTOOLERR_FILENOTFOUND;
1126 goto done;
1127 }
1128
1129 /* do we need to expand the directory? */
1130 if (!free_block)
1131 {
1132 if (this_block == 0)
1133 {
1134 err = IMGTOOLERR_CORRUPTFILE;
1135 goto done;
1136 }
1137
1138 err = prodos_load_block(image, this_block, buffer);
1139 if (err)
1140 goto done;
1141
1142 /* allocate a block */
1143 err = prodos_alloc_block(image, NULL, &free_block);
1144 if (err)
1145 goto done;
1146
1147 /* clear out this new block */
1148 err = prodos_clear_block(image, free_block);
1149 if (err)
1150 goto done;
1151
1152 /* save this link */
1153 place_integer_le(buffer, 2, 2, free_block);
1154 err = prodos_save_block(image, this_block, buffer);
1155 if (err)
1156 goto done;
1157
1158 free_index = 0;
1159 }
1160
1161 /* seek back to the free space */
1162 err = prodos_enum_seek(image, direnum, free_block, free_index);
1163 if (err)
1164 goto done;
1165
1166 new_file_block = 0;
1167 if (create == CREATE_DIR)
1168 {
1169 /* if we are creating a directory, we need to create a new block */
1170 err = prodos_alloc_block(image, NULL, &new_file_block);
1171 if (err)
1172 goto done;
1173
1174 err = prodos_clear_block(image, new_file_block);
1175 if (err)
1176 goto done;
1177 }
1178
1179 /* prepare the dirent */
1180 memset(ent, 0, sizeof(*ent));
1181 ent->storage_type = (create == CREATE_DIR) ? 0xe0 : 0x10;
1182 ent->creation_time = ent->lastmodified_time = prodos_time_now();
1183 ent->key_pointer[0] = new_file_block;
1184 ent->file_type = 0x3F3F3F3F;
1185 ent->file_creator = 0x3F3F3F3F;
1186 strncpy(ent->filename, old_path, ARRAY_LENGTH(ent->filename));
1187
1188 /* and place it */
1189 err = prodos_put_dirent(image, direnum, ent);
1190 if (err)
1191 goto done;
1192
1193 this_block = free_block;
1194 this_index = free_index;
1195 }
1196 else
1197 {
1198 /* we've found the file; seek that dirent */
1199 err = prodos_enum_seek(image, direnum, this_block, this_index);
1200 if (err)
1201 goto done;
1202 }
1203 }
1204
1205 err = IMGTOOLERR_SUCCESS;
1206 done:
1207 return err;
1208 }
1209
1210
1211
prodos_fill_file(imgtool::image & image,uint8_t * bitmap,uint16_t key_block,int key_block_allocated,int depth,uint32_t blockcount,uint32_t block_index)1212 static imgtoolerr_t prodos_fill_file(imgtool::image &image, uint8_t *bitmap,
1213 uint16_t key_block, int key_block_allocated,
1214 int depth, uint32_t blockcount, uint32_t block_index)
1215 {
1216 imgtoolerr_t err;
1217 prodos_diskinfo *di;
1218 int dirty;
1219 int sub_block_allocated;
1220 uint16_t i, sub_block, new_sub_block;
1221 uint8_t buffer[BLOCK_SIZE];
1222
1223 di = get_prodos_info(image);
1224
1225 if (key_block_allocated)
1226 {
1227 /* we are on a recently allocated key block; start fresh */
1228 memset(buffer, 0, sizeof(buffer));
1229 dirty = true;
1230 }
1231 else
1232 {
1233 /* this is a preexisting key block */
1234 err = prodos_load_block(image, key_block, buffer);
1235 if (err)
1236 return err;
1237 dirty = false;
1238 }
1239
1240 for (i = 0; i < 256; i++)
1241 {
1242 sub_block_allocated = false;
1243
1244 sub_block = buffer[i + 256];
1245 sub_block <<= 8;
1246 sub_block += buffer[i + 0];
1247
1248 new_sub_block = sub_block;
1249 if ((block_index < blockcount) && (sub_block == 0))
1250 {
1251 err = prodos_alloc_block(image, bitmap, &new_sub_block);
1252 if (err)
1253 return err;
1254 sub_block_allocated = true;
1255 }
1256 else if ((block_index >= blockcount) && (sub_block != 0))
1257 {
1258 new_sub_block = 0;
1259 if (sub_block < di->total_blocks)
1260 prodos_set_volume_bitmap_bit(bitmap, sub_block, 0);
1261 }
1262
1263 /* did we change the block? */
1264 if (new_sub_block != sub_block)
1265 {
1266 dirty = true;
1267 buffer[i + 0] = new_sub_block >> 0;
1268 buffer[i + 256] = new_sub_block >> 8;
1269 if (sub_block == 0)
1270 sub_block = new_sub_block;
1271 }
1272
1273 /* call recursive function */
1274 if (depth > 2)
1275 {
1276 err = prodos_fill_file(image, bitmap, sub_block, sub_block_allocated, depth - 1, blockcount, block_index);
1277 if (err)
1278 return err;
1279 }
1280
1281 /* increment index */
1282 block_index += 1 << ((depth - 2) * 8);
1283 }
1284
1285 /* if we changed anything, then save the block */
1286 if (dirty)
1287 {
1288 err = prodos_save_block(image, key_block, buffer);
1289 if (err)
1290 return err;
1291 }
1292 return IMGTOOLERR_SUCCESS;
1293 }
1294
1295
1296
prodos_set_file_block_count(imgtool::image & image,prodos_direnum * direnum,prodos_dirent * ent,uint8_t * bitmap,int fork_num,uint32_t new_blockcount)1297 static imgtoolerr_t prodos_set_file_block_count(imgtool::image &image, prodos_direnum *direnum,
1298 prodos_dirent *ent, uint8_t *bitmap, int fork_num, uint32_t new_blockcount)
1299 {
1300 imgtoolerr_t err;
1301 int depth, new_depth, i;
1302 uint16_t new_block, block;
1303 uint8_t buffer[BLOCK_SIZE];
1304 uint16_t key_pointer;
1305
1306 if (fork_num && (new_blockcount > 0) && !is_extendedfile_storagetype(ent->storage_type))
1307 {
1308 /* need to change a normal file to an extended file */
1309 err = prodos_promote_file(image, bitmap, ent);
1310 if (err)
1311 return err;
1312 }
1313
1314 key_pointer = ent->key_pointer[fork_num];
1315 depth = ent->depth[fork_num];
1316
1317 /* determine the new tree depth */
1318 if (new_blockcount <= 1)
1319 new_depth = 1;
1320 else if (new_blockcount <= 256)
1321 new_depth = 2;
1322 else
1323 new_depth = 3;
1324
1325 /* are we zero length, and do we have to create a block? */
1326 if ((new_blockcount >= 1) && (key_pointer == 0))
1327 {
1328 err = prodos_alloc_block(image, bitmap, &new_block);
1329 if (err)
1330 return err;
1331 key_pointer = new_block;
1332 }
1333
1334 /* do we have to grow the tree? */
1335 while(new_depth > depth)
1336 {
1337 err = prodos_alloc_block(image, bitmap, &new_block);
1338 if (err)
1339 return err;
1340
1341 /* create this new key block, with a link to the previous one */
1342 memset(buffer, 0, sizeof(buffer));
1343 buffer[0] = (uint8_t) (key_pointer >> 0);
1344 buffer[256] = (uint8_t) (key_pointer >> 8);
1345 err = prodos_save_block(image, new_block, buffer);
1346 if (err)
1347 return err;
1348
1349 depth++;
1350 key_pointer = new_block;
1351 }
1352
1353 /* do we have to shrink the tree? */
1354 while(new_depth < depth)
1355 {
1356 err = prodos_load_block(image, key_pointer, buffer);
1357 if (err)
1358 return err;
1359
1360 for (i = 1; i < 256; i++)
1361 {
1362 block = buffer[i + 256];
1363 block <<= 8;
1364 block |= buffer[i + 0];
1365
1366 if (block > 0)
1367 {
1368 if (depth > 2)
1369 {
1370 /* remove this block's children */
1371 err = prodos_fill_file(image, bitmap, block, false, depth - 1, 0, 0);
1372 if (err)
1373 return err;
1374 }
1375
1376 /* and remove this block */
1377 prodos_set_volume_bitmap_bit(bitmap, block, 0);
1378 }
1379 }
1380
1381 /* remove this key block */
1382 prodos_set_volume_bitmap_bit(bitmap, key_pointer, 0);
1383
1384 /* set the new key pointer */
1385 block = buffer[256];
1386 block <<= 8;
1387 block |= buffer[0];
1388 key_pointer = block;
1389
1390 depth--;
1391 }
1392
1393 if (new_blockcount > 0)
1394 {
1395 /* fill out the file tree */
1396 err = prodos_fill_file(image, bitmap, key_pointer, false, depth, new_blockcount, 0);
1397 if (err)
1398 return err;
1399 }
1400 else if (key_pointer != 0)
1401 {
1402 /* we are now zero length, and don't need a key pointer */
1403 prodos_set_volume_bitmap_bit(bitmap, key_pointer, 0);
1404 key_pointer = 0;
1405 }
1406
1407 /* change the depth if we are not an extended file */
1408 if (is_normalfile_storagetype(ent->storage_type))
1409 {
1410 ent->storage_type &= ~0xF0;
1411 ent->storage_type |= depth * 0x10;
1412 }
1413
1414 ent->key_pointer[fork_num] = key_pointer;
1415 ent->depth[fork_num] = depth;
1416 return IMGTOOLERR_SUCCESS;
1417 }
1418
1419
1420
prodos_set_file_size(imgtool::image & image,prodos_direnum * direnum,prodos_dirent * ent,int fork_num,uint32_t new_size)1421 static imgtoolerr_t prodos_set_file_size(imgtool::image &image, prodos_direnum *direnum,
1422 prodos_dirent *ent, int fork_num, uint32_t new_size)
1423 {
1424 imgtoolerr_t err = IMGTOOLERR_SUCCESS;
1425 uint32_t blockcount, new_blockcount;
1426 uint8_t *bitmap = NULL;
1427
1428 if (ent->filesize[fork_num] != new_size)
1429 {
1430 blockcount = (ent->filesize[fork_num] + BLOCK_SIZE - 1) / BLOCK_SIZE;
1431 new_blockcount = (new_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
1432
1433 /* do we need to change the block chain? */
1434 if (new_blockcount != blockcount)
1435 {
1436 err = prodos_load_volume_bitmap(image, &bitmap);
1437 if (err)
1438 goto done;
1439
1440 err = prodos_set_file_block_count(image, direnum, ent, bitmap, fork_num, new_blockcount);
1441 if (err)
1442 goto done;
1443
1444 err = prodos_save_volume_bitmap(image, bitmap);
1445 if (err)
1446 goto done;
1447 }
1448
1449 ent->filesize[fork_num] = new_size;
1450 err = prodos_put_dirent(image, direnum, ent);
1451 if (err)
1452 goto done;
1453 }
1454
1455 done:
1456 if (bitmap)
1457 free(bitmap);
1458 return err;
1459 }
1460
1461
1462
prodos_get_storagetype_maxfilesize(uint8_t storage_type)1463 static uint32_t prodos_get_storagetype_maxfilesize(uint8_t storage_type)
1464 {
1465 uint32_t max_filesize = 0;
1466 switch(storage_type & 0xF0)
1467 {
1468 case 0x10:
1469 max_filesize = BLOCK_SIZE * 1;
1470 break;
1471 case 0x20:
1472 max_filesize = BLOCK_SIZE * 256;
1473 break;
1474 case 0x30:
1475 case 0x50:
1476 max_filesize = BLOCK_SIZE * 32768;
1477 break;
1478 }
1479 return max_filesize;
1480 }
1481
1482
1483
prodos_diskimage_beginenum(imgtool::directory & enumeration,const char * path)1484 static imgtoolerr_t prodos_diskimage_beginenum(imgtool::directory &enumeration, const char *path)
1485 {
1486 imgtoolerr_t err;
1487 imgtool::image &image(enumeration.image());
1488 prodos_direnum *appleenum;
1489 prodos_dirent ent;
1490 uint16_t block = ROOTDIR_BLOCK;
1491
1492 appleenum = (prodos_direnum *) enumeration.extra_bytes();
1493
1494 /* find subdirectory, if appropriate */
1495 if (*path)
1496 {
1497 err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
1498 if (err)
1499 return err;
1500
1501 /* only work on directories */
1502 if (!is_dir_storagetype(ent.storage_type))
1503 return IMGTOOLERR_FILENOTFOUND;
1504
1505 block = ent.key_pointer[0];
1506 }
1507
1508 /* seek initial block */
1509 err = prodos_enum_seek(image, appleenum, block, 0);
1510 if (err)
1511 return err;
1512
1513 return IMGTOOLERR_SUCCESS;
1514 }
1515
1516
1517
prodos_diskimage_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)1518 static imgtoolerr_t prodos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
1519 {
1520 imgtoolerr_t err;
1521 imgtool::image &image(enumeration.image());
1522 prodos_direnum *appleenum;
1523 prodos_dirent pd_ent;
1524 uint32_t max_filesize;
1525
1526 appleenum = (prodos_direnum *) enumeration.extra_bytes();
1527
1528 do
1529 {
1530 err = prodos_get_next_dirent(image, appleenum, pd_ent);
1531 if (err)
1532 return err;
1533 }
1534 while(appleenum->block
1535 && !is_file_storagetype(pd_ent.storage_type)
1536 && !is_dir_storagetype(pd_ent.storage_type));
1537
1538 /* end of file? */
1539 if (pd_ent.storage_type == 0x00)
1540 {
1541 ent.eof = 1;
1542 return IMGTOOLERR_SUCCESS;
1543 }
1544
1545 strcpy(ent.filename, pd_ent.filename);
1546 ent.directory = is_dir_storagetype(pd_ent.storage_type);
1547 ent.creation_time = prodos_crack_time(pd_ent.creation_time);
1548 ent.lastmodified_time = prodos_crack_time(pd_ent.lastmodified_time);
1549
1550 if (!ent.directory)
1551 {
1552 ent.filesize = pd_ent.filesize[0];
1553
1554 max_filesize = prodos_get_storagetype_maxfilesize(pd_ent.storage_type);
1555 if (ent.filesize > max_filesize)
1556 {
1557 ent.corrupt = 1;
1558 ent.filesize = max_filesize;
1559 }
1560 }
1561 return IMGTOOLERR_SUCCESS;
1562 }
1563
1564
1565
prodos_read_file_tree(imgtool::image & image,uint32_t * filesize,uint32_t block,int nest_level,imgtool::stream & destf)1566 static imgtoolerr_t prodos_read_file_tree(imgtool::image &image, uint32_t *filesize,
1567 uint32_t block, int nest_level, imgtool::stream &destf)
1568 {
1569 imgtoolerr_t err;
1570 prodos_diskinfo *di;
1571 uint8_t buffer[BLOCK_SIZE];
1572 uint16_t sub_block;
1573 size_t bytes_to_write;
1574 int i;
1575
1576 /* check bounds */
1577 di = get_prodos_info(image);
1578 if (block >= di->total_blocks)
1579 return IMGTOOLERR_CORRUPTFILE;
1580
1581 err = prodos_load_block(image, block, buffer);
1582 if (err)
1583 return err;
1584
1585 if (nest_level > 0)
1586 {
1587 /* this is an index block */
1588 for (i = 0; i < 256; i++)
1589 {
1590 /* retrieve the block pointer; the two bytes are on either half
1591 * of the block */
1592 sub_block = buffer[i + 256];
1593 sub_block <<= 8;
1594 sub_block |= buffer[i + 0];
1595
1596 if (sub_block != 0)
1597 {
1598 err = prodos_read_file_tree(image, filesize, sub_block, nest_level - 1, destf);
1599 if (err)
1600 return err;
1601 }
1602 }
1603 }
1604 else
1605 {
1606 /* this is a leaf block */
1607 bytes_to_write = std::min(size_t(*filesize), sizeof(buffer));
1608 destf.write(buffer, bytes_to_write);
1609 *filesize -= bytes_to_write;
1610 }
1611 return IMGTOOLERR_SUCCESS;
1612 }
1613
1614
1615
prodos_write_file_tree(imgtool::image & image,uint32_t * filesize,uint32_t block,int nest_level,imgtool::stream & sourcef)1616 static imgtoolerr_t prodos_write_file_tree(imgtool::image &image, uint32_t *filesize,
1617 uint32_t block, int nest_level, imgtool::stream &sourcef)
1618 {
1619 imgtoolerr_t err;
1620 prodos_diskinfo *di;
1621 uint8_t buffer[BLOCK_SIZE];
1622 uint16_t sub_block;
1623 size_t bytes_to_read;
1624 int i;
1625
1626 /* nothing more to read? bail */
1627 if (*filesize == 0)
1628 return IMGTOOLERR_SUCCESS;
1629
1630 /* check bounds */
1631 di = get_prodos_info(image);
1632 if (block >= di->total_blocks)
1633 return IMGTOOLERR_CORRUPTFILE;
1634
1635 err = prodos_load_block(image, block, buffer);
1636 if (err)
1637 return err;
1638
1639 if (nest_level > 0)
1640 {
1641 for (i = 0; i < 256; i++)
1642 {
1643 sub_block = buffer[i + 256];
1644 sub_block <<= 8;
1645 sub_block |= buffer[i + 0];
1646
1647 if (sub_block != 0)
1648 {
1649 err = prodos_write_file_tree(image, filesize, sub_block, nest_level - 1, sourcef);
1650 if (err)
1651 return err;
1652 }
1653 }
1654 }
1655 else
1656 {
1657 /* this is a leaf block */
1658 bytes_to_read = std::min(size_t(*filesize), sizeof(buffer));
1659 sourcef.read(buffer, bytes_to_read);
1660 *filesize -= bytes_to_read;
1661
1662 err = prodos_save_block(image, block, buffer);
1663 if (err)
1664 return err;
1665 }
1666 return IMGTOOLERR_SUCCESS;
1667 }
1668
1669
1670
prodos_diskimage_freespace(imgtool::partition & partition,uint64_t * size)1671 static imgtoolerr_t prodos_diskimage_freespace(imgtool::partition &partition, uint64_t *size)
1672 {
1673 imgtoolerr_t err;
1674 imgtool::image &image(partition.image());
1675 prodos_diskinfo *di;
1676 uint8_t *bitmap = NULL;
1677 uint16_t i;
1678
1679 di = get_prodos_info(image);
1680 *size = 0;
1681
1682 err = prodos_load_volume_bitmap(image, &bitmap);
1683 if (err)
1684 goto done;
1685
1686 for (i = 0; i < di->total_blocks; i++)
1687 {
1688 if (!prodos_get_volume_bitmap_bit(bitmap, i))
1689 *size += BLOCK_SIZE;
1690 }
1691
1692 done:
1693 if (bitmap)
1694 free(bitmap);
1695 return err;
1696 }
1697
1698
1699
prodos_diskimage_readfile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & destf)1700 static imgtoolerr_t prodos_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
1701 {
1702 imgtoolerr_t err;
1703 imgtool::image &image(partition.image());
1704 prodos_dirent ent;
1705 uint16_t key_pointer;
1706 int nest_level;
1707 mac_fork_t fork_num;
1708
1709 err = prodos_lookup_path(image, filename, CREATE_NONE, NULL, &ent);
1710 if (err)
1711 return err;
1712
1713 if (is_dir_storagetype(ent.storage_type))
1714 return IMGTOOLERR_FILENOTFOUND;
1715
1716 err = mac_identify_fork(fork, &fork_num);
1717 if (err)
1718 return err;
1719
1720 key_pointer = ent.key_pointer[fork_num];
1721 nest_level = ent.depth[fork_num] - 1;
1722
1723 if (key_pointer != 0)
1724 {
1725 err = prodos_read_file_tree(image, &ent.filesize[fork_num], key_pointer,
1726 nest_level, destf);
1727 if (err)
1728 return err;
1729 }
1730
1731 /* have we not actually received the correct amount of bytes? if not, fill in the rest */
1732 if (ent.filesize[fork_num] > 0)
1733 destf.fill(0, ent.filesize[fork_num]);
1734
1735 return IMGTOOLERR_SUCCESS;
1736 }
1737
1738
1739
prodos_diskimage_writefile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & sourcef,util::option_resolution * opts)1740 static imgtoolerr_t prodos_diskimage_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
1741 {
1742 imgtoolerr_t err;
1743 imgtool::image &image(partition.image());
1744 prodos_dirent ent;
1745 prodos_direnum direnum;
1746 uint64_t file_size;
1747 mac_fork_t fork_num;
1748
1749 file_size = sourcef.size();
1750
1751 err = prodos_lookup_path(image, filename, CREATE_FILE, &direnum, &ent);
1752 if (err)
1753 return err;
1754
1755 /* only work on files */
1756 if (is_dir_storagetype(ent.storage_type))
1757 return IMGTOOLERR_FILENOTFOUND;
1758
1759 err = mac_identify_fork(fork, &fork_num);
1760 if (err)
1761 return err;
1762
1763 /* set the file size */
1764 err = prodos_set_file_size(image, &direnum, &ent, fork_num, file_size);
1765 if (err)
1766 return err;
1767
1768 err = prodos_write_file_tree(image, &ent.filesize[fork_num], ent.key_pointer[fork_num],
1769 ent.depth[fork_num] - 1, sourcef);
1770 if (err)
1771 return err;
1772
1773 return IMGTOOLERR_SUCCESS;
1774 }
1775
1776
1777
prodos_diskimage_deletefile(imgtool::partition & partition,const char * path)1778 static imgtoolerr_t prodos_diskimage_deletefile(imgtool::partition &partition, const char *path)
1779 {
1780 imgtoolerr_t err;
1781 imgtool::image &image(partition.image());
1782 prodos_dirent ent;
1783 prodos_direnum direnum;
1784
1785 err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
1786 if (err)
1787 return err;
1788
1789 /* only work on files */
1790 if (is_dir_storagetype(ent.storage_type))
1791 return IMGTOOLERR_FILENOTFOUND;
1792
1793 /* empty out both forks */
1794 err = prodos_set_file_size(image, &direnum, &ent, 0, 0);
1795 if (err)
1796 return err;
1797 err = prodos_set_file_size(image, &direnum, &ent, 1, 0);
1798 if (err)
1799 return err;
1800
1801 memset(&ent, 0, sizeof(ent));
1802 err = prodos_put_dirent(image, &direnum, &ent);
1803 if (err)
1804 return err;
1805
1806 return IMGTOOLERR_SUCCESS;
1807 }
1808
1809
1810
prodos_diskimage_listforks(imgtool::partition & partition,const char * path,std::vector<imgtool::fork_entry> & forks)1811 static imgtoolerr_t prodos_diskimage_listforks(imgtool::partition &partition, const char *path, std::vector<imgtool::fork_entry> &forks)
1812 {
1813 imgtoolerr_t err;
1814 imgtool::image &image(partition.image());
1815 prodos_dirent ent;
1816 prodos_direnum direnum;
1817
1818 err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
1819 if (err)
1820 return err;
1821
1822 if (is_dir_storagetype(ent.storage_type))
1823 return IMGTOOLERR_FILENOTFOUND;
1824
1825 // specify data fork
1826 forks.emplace_back(ent.filesize[0], imgtool::fork_entry::type_t::DATA);
1827
1828 if (is_extendedfile_storagetype(ent.storage_type))
1829 {
1830 // specify the resource fork
1831 forks.emplace_back(ent.filesize[1], imgtool::fork_entry::type_t::RESOURCE);
1832 }
1833
1834 return IMGTOOLERR_SUCCESS;
1835 }
1836
1837
1838
prodos_diskimage_createdir(imgtool::partition & partition,const char * path)1839 static imgtoolerr_t prodos_diskimage_createdir(imgtool::partition &partition, const char *path)
1840 {
1841 imgtoolerr_t err;
1842 imgtool::image &image(partition.image());
1843 prodos_dirent ent;
1844 prodos_direnum direnum;
1845
1846 err = prodos_lookup_path(image, path, CREATE_DIR, &direnum, &ent);
1847 if (err)
1848 return err;
1849
1850 /* only work on directories */
1851 if (!is_dir_storagetype(ent.storage_type))
1852 return IMGTOOLERR_FILENOTFOUND;
1853
1854 return IMGTOOLERR_SUCCESS;
1855 }
1856
1857
1858
prodos_free_directory(imgtool::image & image,uint8_t * volume_bitmap,uint16_t key_pointer)1859 static imgtoolerr_t prodos_free_directory(imgtool::image &image, uint8_t *volume_bitmap, uint16_t key_pointer)
1860 {
1861 imgtoolerr_t err;
1862 prodos_diskinfo *di;
1863 int i;
1864 uint16_t next_block;
1865 uint32_t offset;
1866 uint8_t buffer[BLOCK_SIZE];
1867
1868 di = get_prodos_info(image);
1869
1870 if (key_pointer != 0)
1871 {
1872 err = prodos_load_block(image, key_pointer, buffer);
1873 if (err)
1874 return err;
1875
1876 for (i = 0; i < di->dirents_per_block; i++)
1877 {
1878 offset = i * di->dirent_size + 4;
1879
1880 if (is_file_storagetype(buffer[offset]) || is_file_storagetype(buffer[offset]))
1881 return IMGTOOLERR_DIRNOTEMPTY;
1882 }
1883
1884 next_block = pick_integer_le(buffer, 2, 2);
1885
1886 err = prodos_free_directory(image, volume_bitmap, next_block);
1887 if (err)
1888 return err;
1889
1890 prodos_set_volume_bitmap_bit(volume_bitmap, key_pointer, 0);
1891 }
1892 return IMGTOOLERR_SUCCESS;
1893 }
1894
1895
1896
prodos_diskimage_deletedir(imgtool::partition & partition,const char * path)1897 static imgtoolerr_t prodos_diskimage_deletedir(imgtool::partition &partition, const char *path)
1898 {
1899 imgtoolerr_t err;
1900 imgtool::image &image(partition.image());
1901 prodos_dirent ent;
1902 prodos_direnum direnum;
1903 uint8_t *volume_bitmap = NULL;
1904
1905 err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
1906 if (err)
1907 goto done;
1908
1909 /* only work on directories */
1910 if (!is_dir_storagetype(ent.storage_type))
1911 {
1912 err = IMGTOOLERR_FILENOTFOUND;
1913 goto done;
1914 }
1915
1916 err = prodos_load_volume_bitmap(image, &volume_bitmap);
1917 if (err)
1918 goto done;
1919
1920 err = prodos_free_directory(image, volume_bitmap, ent.key_pointer[0]);
1921 if (err)
1922 goto done;
1923
1924 err = prodos_save_volume_bitmap(image, volume_bitmap);
1925 if (err)
1926 goto done;
1927
1928 memset(&ent, 0, sizeof(ent));
1929 err = prodos_put_dirent(image, &direnum, &ent);
1930 if (err)
1931 goto done;
1932
1933 done:
1934 if (volume_bitmap)
1935 free(volume_bitmap);
1936 return IMGTOOLERR_SUCCESS;
1937 }
1938
1939
1940
prodos_get_file_tree(imgtool::image & image,imgtool_chainent * chain,size_t chain_size,size_t * chain_pos,uint16_t block,uint8_t total_depth,uint8_t cur_depth)1941 static imgtoolerr_t prodos_get_file_tree(imgtool::image &image, imgtool_chainent *chain, size_t chain_size,
1942 size_t *chain_pos, uint16_t block, uint8_t total_depth, uint8_t cur_depth)
1943 {
1944 imgtoolerr_t err;
1945 prodos_diskinfo *di;
1946 int i;
1947 uint16_t sub_block;
1948 uint8_t buffer[BLOCK_SIZE];
1949
1950 if (block == 0)
1951 return IMGTOOLERR_SUCCESS;
1952 if (*chain_pos >= chain_size)
1953 return IMGTOOLERR_SUCCESS;
1954
1955 /* check bounds */
1956 di = get_prodos_info(image);
1957 if (block >= di->total_blocks)
1958 return IMGTOOLERR_CORRUPTFILE;
1959
1960 chain[*chain_pos].level = cur_depth;
1961 chain[*chain_pos].block = block;
1962 (*chain_pos)++;
1963
1964 /* must we recurse into the tree? */
1965 if (cur_depth < total_depth)
1966 {
1967 err = prodos_load_block(image, block, buffer);
1968 if (err)
1969 return err;
1970
1971 for (i = 0; i < 256; i++)
1972 {
1973 sub_block = buffer[i + 256];
1974 sub_block <<= 8;
1975 sub_block |= buffer[i + 0];
1976
1977 err = prodos_get_file_tree(image, chain, chain_size, chain_pos, sub_block, total_depth, cur_depth + 1);
1978 if (err)
1979 return err;
1980 }
1981 }
1982 return IMGTOOLERR_SUCCESS;
1983 }
1984
1985
1986
prodos_diskimage_getattrs(imgtool::partition & partition,const char * path,const uint32_t * attrs,imgtool_attribute * values)1987 static imgtoolerr_t prodos_diskimage_getattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values)
1988 {
1989 imgtoolerr_t err;
1990 imgtool::image &image(partition.image());
1991 prodos_dirent ent;
1992 int i;
1993
1994 err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
1995 if (err)
1996 return err;
1997
1998 for (i = 0; attrs[i]; i++)
1999 {
2000 switch(attrs[i])
2001 {
2002 case IMGTOOLATTR_INT_MAC_TYPE:
2003 values[i].i = ent.file_type;
2004 break;
2005 case IMGTOOLATTR_INT_MAC_CREATOR:
2006 values[i].i = ent.file_creator;
2007 break;
2008 case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
2009 values[i].i = ent.finder_flags;
2010 break;
2011 case IMGTOOLATTR_INT_MAC_COORDX:
2012 values[i].i = ent.coord_x;
2013 break;
2014 case IMGTOOLATTR_INT_MAC_COORDY:
2015 values[i].i = ent.coord_y;
2016 break;
2017 case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
2018 values[i].i = ent.finder_folder;
2019 break;
2020 case IMGTOOLATTR_INT_MAC_ICONID:
2021 values[i].i = ent.icon_id;
2022 break;
2023 case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
2024 values[i].i = ent.script_code;
2025 break;
2026 case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
2027 values[i].i = ent.extended_flags;
2028 break;
2029 case IMGTOOLATTR_INT_MAC_COMMENTID:
2030 values[i].i = ent.comment_id;
2031 break;
2032 case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
2033 values[i].i = ent.putaway_directory;
2034 break;
2035
2036 case IMGTOOLATTR_TIME_CREATED:
2037 values[i].t = prodos_crack_time(ent.creation_time).to_time_t();
2038 break;
2039 case IMGTOOLATTR_TIME_LASTMODIFIED:
2040 values[i].t = prodos_crack_time(ent.lastmodified_time).to_time_t();
2041 break;
2042 }
2043 }
2044
2045 return IMGTOOLERR_SUCCESS;
2046 }
2047
2048
2049
prodos_diskimage_setattrs(imgtool::partition & partition,const char * path,const uint32_t * attrs,const imgtool_attribute * values)2050 static imgtoolerr_t prodos_diskimage_setattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values)
2051 {
2052 imgtoolerr_t err;
2053 imgtool::image &image(partition.image());
2054 prodos_dirent ent;
2055 prodos_direnum direnum;
2056 int i;
2057
2058 err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent);
2059 if (err)
2060 return err;
2061
2062 for (i = 0; attrs[i]; i++)
2063 {
2064 switch(attrs[i])
2065 {
2066 case IMGTOOLATTR_INT_MAC_TYPE:
2067 ent.file_type = values[i].i;
2068 break;
2069 case IMGTOOLATTR_INT_MAC_CREATOR:
2070 ent.file_creator = values[i].i;
2071 break;
2072 case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
2073 ent.finder_flags = values[i].i;
2074 break;
2075 case IMGTOOLATTR_INT_MAC_COORDX:
2076 ent.coord_x = values[i].i;
2077 break;
2078 case IMGTOOLATTR_INT_MAC_COORDY:
2079 ent.coord_y = values[i].i;
2080 break;
2081 case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
2082 ent.finder_folder = values[i].i;
2083 break;
2084 case IMGTOOLATTR_INT_MAC_ICONID:
2085 ent.icon_id = values[i].i;
2086 break;
2087 case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
2088 ent.script_code = values[i].i;
2089 break;
2090 case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
2091 ent.extended_flags = values[i].i;
2092 break;
2093 case IMGTOOLATTR_INT_MAC_COMMENTID:
2094 ent.comment_id = values[i].i;
2095 break;
2096 case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
2097 ent.putaway_directory = values[i].i;
2098 break;
2099
2100 case IMGTOOLATTR_TIME_CREATED:
2101 ent.creation_time = prodos_setup_time(values[i].t);
2102 break;
2103 case IMGTOOLATTR_TIME_LASTMODIFIED:
2104 ent.lastmodified_time = prodos_setup_time(values[i].t);
2105 break;
2106 }
2107 }
2108
2109 err = prodos_put_dirent(image, &direnum, &ent);
2110 if (err)
2111 return err;
2112
2113 return IMGTOOLERR_SUCCESS;
2114 }
2115
2116
2117
prodos_diskimage_suggesttransfer(imgtool::partition & partition,const char * path,imgtool_transfer_suggestion * suggestions,size_t suggestions_length)2118 static imgtoolerr_t prodos_diskimage_suggesttransfer(imgtool::partition &partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
2119 {
2120 imgtoolerr_t err;
2121 imgtool::image &image(partition.image());
2122 prodos_dirent ent;
2123 mac_filecategory_t file_category = MAC_FILECATEGORY_DATA;
2124
2125 if (path)
2126 {
2127 err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
2128 if (err)
2129 return err;
2130
2131 file_category = is_extendedfile_storagetype(ent.storage_type)
2132 ? MAC_FILECATEGORY_FORKED : MAC_FILECATEGORY_DATA;
2133 }
2134
2135 mac_suggest_transfer(file_category, suggestions, suggestions_length);
2136 return IMGTOOLERR_SUCCESS;
2137 }
2138
2139
2140
prodos_diskimage_getchain(imgtool::partition & partition,const char * path,imgtool_chainent * chain,size_t chain_size)2141 static imgtoolerr_t prodos_diskimage_getchain(imgtool::partition &partition, const char *path, imgtool_chainent *chain, size_t chain_size)
2142 {
2143 imgtoolerr_t err;
2144 imgtool::image &image(partition.image());
2145 prodos_dirent ent;
2146 size_t chain_pos = 0;
2147 int fork_num;
2148
2149 err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent);
2150 if (err)
2151 return err;
2152
2153 switch(ent.storage_type & 0xF0)
2154 {
2155 case 0x10:
2156 case 0x20:
2157 case 0x30:
2158 /* normal ProDOS file */
2159 err = prodos_get_file_tree(image, chain, chain_size, &chain_pos,
2160 ent.key_pointer[0], ent.depth[0] - 1, 0);
2161 if (err)
2162 return err;
2163 break;
2164
2165 case 0x50:
2166 /* extended ProDOS file */
2167 chain[chain_pos].level = 0;
2168 chain[chain_pos].block = ent.extkey_pointer;
2169 chain_pos++;
2170
2171 for (fork_num = 0; fork_num <= 1; fork_num++)
2172 {
2173 if (ent.key_pointer[fork_num])
2174 {
2175 err = prodos_get_file_tree(image, chain, chain_size, &chain_pos,
2176 ent.key_pointer[fork_num], ent.depth[fork_num] - 1, 1);
2177 if (err)
2178 return err;
2179 }
2180 }
2181 break;
2182
2183 case 0xE0:
2184 /* directory */
2185 return IMGTOOLERR_UNIMPLEMENTED;
2186
2187 default:
2188 return IMGTOOLERR_UNEXPECTED;
2189 }
2190
2191 return IMGTOOLERR_SUCCESS;
2192 }
2193
2194
2195
generic_prodos_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)2196 static void generic_prodos_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
2197 {
2198 switch(state)
2199 {
2200 /* --- the following bits of info are returned as 64-bit signed integers --- */
2201 case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break;
2202 case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break;
2203 case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME: info->i = 1; break;
2204 case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break;
2205 case IMGTOOLINFO_INT_WRITING_UNTESTED: info->i = 1; break;
2206 case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(prodos_diskinfo); break;
2207 case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(prodos_direnum); break;
2208 case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break;
2209
2210 /* --- the following bits of info are returned as NULL-terminated strings --- */
2211 case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "ProDOS format"); break;
2212 case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
2213 case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
2214
2215 /* --- the following bits of info are returned as pointers to data or functions --- */
2216 case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
2217 case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = prodos_diskimage_beginenum; break;
2218 case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = prodos_diskimage_nextenum; break;
2219 case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = prodos_diskimage_freespace; break;
2220 case IMGTOOLINFO_PTR_READ_FILE: info->read_file = prodos_diskimage_readfile; break;
2221 case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = prodos_diskimage_writefile; break;
2222 case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = prodos_diskimage_deletefile; break;
2223 case IMGTOOLINFO_PTR_LIST_FORKS: info->list_forks = prodos_diskimage_listforks; break;
2224 case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = prodos_diskimage_createdir; break;
2225 case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = prodos_diskimage_deletedir; break;
2226 case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = prodos_diskimage_getattrs; break;
2227 case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = prodos_diskimage_setattrs; break;
2228 case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = prodos_diskimage_suggesttransfer; break;
2229 case IMGTOOLINFO_PTR_GET_CHAIN: info->get_chain = prodos_diskimage_getchain; break;
2230 }
2231 }
2232
2233
2234
prodos_525_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)2235 void prodos_525_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
2236 {
2237 switch(state)
2238 {
2239 /* --- the following bits of info are returned as NULL-terminated strings --- */
2240 case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "prodos_525"); break;
2241
2242 /* --- the following bits of info are returned as pointers to data or functions --- */
2243 case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = prodos_diskimage_create_525; break;
2244 case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = prodos_diskimage_open_525; break;
2245 case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple2; break;
2246
2247 default: generic_prodos_get_info(imgclass, state, info); break;
2248 }
2249 }
2250
2251
2252
prodos_35_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)2253 void prodos_35_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
2254 {
2255 switch(state)
2256 {
2257 /* --- the following bits of info are returned as NULL-terminated strings --- */
2258 case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "prodos_35"); break;
2259
2260 /* --- the following bits of info are returned as pointers to data or functions --- */
2261 case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = prodos_diskimage_create_35; break;
2262 case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = prodos_diskimage_open_35; break;
2263 case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple35_iigs; break;
2264
2265 default: generic_prodos_get_info(imgclass, state, info); break;
2266 }
2267 }
2268