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, &sector1, &sector2);
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, &sector1, &sector2);
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, &sector);
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, &sector);
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