1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods
3 /****************************************************************************
4 
5     os9.cpp
6 
7     CoCo OS-9 disk images
8 
9 ****************************************************************************/
10 
11 #include <ctime>
12 
13 #include "imgtool.h"
14 #include "formats/imageutl.h"
15 #include "formats/coco_dsk.h"
16 #include "iflopimg.h"
17 
18 enum creation_policy_t
19 {
20 	CREATE_NONE,
21 	CREATE_FILE,
22 	CREATE_DIR
23 };
24 
25 struct os9_diskinfo
26 {
27 	uint32_t total_sectors;
28 	uint32_t sectors_per_track;
29 	uint32_t allocation_bitmap_bytes;
30 	uint32_t cluster_size;
31 	uint32_t root_dir_lsn;
32 	uint32_t owner_id;
33 	uint32_t attributes;
34 	uint32_t disk_id;
35 	uint32_t format_flags;
36 	uint32_t bootstrap_lsn;
37 	uint32_t bootstrap_size;
38 	uint32_t sector_size;
39 
40 	unsigned int sides : 2;
41 	unsigned int double_density : 1;
42 	unsigned int double_track : 1;
43 	unsigned int quad_track_density : 1;
44 	unsigned int octal_track_density : 1;
45 
46 	char name[33];
47 	uint8_t *allocation_bitmap;
48 };
49 
50 
51 struct os9_fileinfo
52 {
53 	unsigned int directory : 1;
54 	unsigned int non_sharable : 1;
55 	unsigned int public_execute : 1;
56 	unsigned int public_write : 1;
57 	unsigned int public_read : 1;
58 	unsigned int user_execute : 1;
59 	unsigned int user_write : 1;
60 	unsigned int user_read : 1;
61 
62 	uint32_t lsn;
63 	uint32_t owner_id;
64 	uint32_t link_count;
65 	uint32_t file_size;
66 
67 	struct
68 	{
69 		uint32_t lsn;
70 		uint32_t count;
71 	} sector_map[48];
72 };
73 
74 
75 struct os9_direnum
76 {
77 	struct os9_fileinfo dir_info;
78 	uint32_t index;
79 };
80 
81 
82 
pick_string(const void * ptr,size_t offset,size_t length,char * dest)83 static void pick_string(const void *ptr, size_t offset, size_t length, char *dest)
84 {
85 	uint8_t b;
86 
87 	while(length--)
88 	{
89 		b = ((uint8_t *) ptr)[offset++];
90 		*(dest++) = b & 0x7F;
91 		if (b & 0x80)
92 			length = 0;
93 	}
94 	*dest = '\0';
95 }
96 
97 
98 
place_string(void * ptr,size_t offset,size_t length,const char * s)99 static void place_string(void *ptr, size_t offset, size_t length, const char *s)
100 {
101 	size_t i;
102 	uint8_t b;
103 	uint8_t *bptr;
104 
105 	bptr = (uint8_t *) ptr;
106 	bptr += offset;
107 
108 	bptr[0] = 0x80;
109 
110 	for (i = 0; s[i] && (i < length); i++)
111 	{
112 		b = ((uint8_t) s[i]) & 0x7F;
113 		if (s[i+1] == '\0')
114 			b |= 0x80;
115 		bptr[i] = b;
116 	}
117 }
118 
119 
120 
os9_get_diskinfo(imgtool::image & image)121 static os9_diskinfo *os9_get_diskinfo(imgtool::image &image)
122 {
123 	return (os9_diskinfo *) imgtool_floppy_extrabytes(image);
124 }
125 
126 
127 
os9_get_dirinfo(imgtool::directory & directory)128 static struct os9_direnum *os9_get_dirinfo(imgtool::directory &directory)
129 {
130 	return (struct os9_direnum *) directory.extra_bytes();
131 }
132 
133 
134 
os9_locate_lsn(imgtool::image & image,uint32_t lsn,uint32_t * head,uint32_t * track,uint32_t * sector)135 static imgtoolerr_t os9_locate_lsn(imgtool::image &image, uint32_t lsn, uint32_t *head, uint32_t *track, uint32_t *sector)
136 {
137 	const os9_diskinfo *disk_info;
138 
139 	disk_info = os9_get_diskinfo(image);
140 	if (lsn > disk_info->total_sectors)
141 		return IMGTOOLERR_CORRUPTIMAGE;
142 
143 	*track = lsn / (disk_info->sectors_per_track * disk_info->sides);
144 	*head = (lsn / disk_info->sectors_per_track) % disk_info->sides;
145 	*sector = (lsn % disk_info->sectors_per_track) + 1;
146 	return IMGTOOLERR_SUCCESS;
147 }
148 
149 
150 
os9_read_lsn(imgtool::image & img,uint32_t lsn,int offset,void * buffer,size_t buffer_len)151 static imgtoolerr_t os9_read_lsn(imgtool::image &img, uint32_t lsn, int offset, void *buffer, size_t buffer_len)
152 {
153 	imgtoolerr_t err;
154 	floperr_t ferr;
155 	uint32_t head, track, sector;
156 
157 	err = os9_locate_lsn(img, lsn, &head, &track, &sector);
158 	if (err)
159 		return err;
160 
161 	ferr = floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len);
162 	if (ferr)
163 		return imgtool_floppy_error(ferr);
164 
165 	return IMGTOOLERR_SUCCESS;
166 }
167 
168 
169 
os9_write_lsn(imgtool::image & img,uint32_t lsn,int offset,const void * buffer,size_t buffer_len)170 static imgtoolerr_t os9_write_lsn(imgtool::image &img, uint32_t lsn, int offset, const void *buffer, size_t buffer_len)
171 {
172 	imgtoolerr_t err;
173 	floperr_t ferr;
174 	uint32_t head, track, sector;
175 
176 	err = os9_locate_lsn(img, lsn, &head, &track, &sector);
177 	if (err)
178 		return err;
179 
180 	ferr = floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len, 0);    /* TODO: pass ddam argument from imgtool */
181 	if (ferr)
182 		return imgtool_floppy_error(ferr);
183 
184 	return IMGTOOLERR_SUCCESS;
185 }
186 
187 
188 
os9_lookup_lsn(imgtool::image & img,const struct os9_fileinfo * file_info,uint32_t * index)189 static uint32_t os9_lookup_lsn(imgtool::image &img,
190 	const struct os9_fileinfo *file_info, uint32_t *index)
191 {
192 	int i;
193 	uint32_t lsn;
194 	const os9_diskinfo *disk_info;
195 
196 	disk_info = os9_get_diskinfo(img);
197 	lsn = *index / disk_info->sector_size;
198 
199 	i = 0;
200 	while(lsn >= file_info->sector_map[i].count)
201 		lsn -= file_info->sector_map[i++].count;
202 
203 	lsn = file_info->sector_map[i].lsn + lsn;
204 	*index %= disk_info->sector_size;
205 	return lsn;
206 }
207 
208 
209 
os9_interpret_dirent(void * entry,char ** filename,uint32_t * lsn,int * corrupt)210 static int os9_interpret_dirent(void *entry, char **filename, uint32_t *lsn, int *corrupt)
211 {
212 	int i;
213 	char *entry_b = (char *) entry;
214 
215 	*filename = NULL;
216 	*lsn = 0;
217 	if (corrupt)
218 		*corrupt = false;
219 
220 	if (entry_b[28] != '\0')
221 	{
222 		if (corrupt)
223 			*corrupt = true;
224 	}
225 
226 	for (i = 0; (i < 28) && !(entry_b[i] & 0x80); i++)
227 		;
228 	entry_b[i] &= 0x7F;
229 	entry_b[i+1] = '\0';
230 
231 	*lsn = pick_integer_be(entry, 29, 3);
232 	if (strcmp(entry_b, ".") && strcmp(entry_b, ".."))
233 		*filename = entry_b;
234 	return *filename != NULL;
235 }
236 
237 
238 
239 /*-------------------------------------------------
240     os9_decode_file_header - reads a file/directory
241     entry from an LSN, and decodes it
242 -------------------------------------------------*/
243 
os9_decode_file_header(imgtool::image & image,int lsn,struct os9_fileinfo * info)244 static imgtoolerr_t os9_decode_file_header(imgtool::image &image,
245 	int lsn, struct os9_fileinfo *info)
246 {
247 	imgtoolerr_t err;
248 	uint32_t attributes, count;
249 	int max_entries, i;
250 	const os9_diskinfo *disk_info;
251 	uint8_t header[256];
252 
253 	disk_info = os9_get_diskinfo(image);
254 
255 	err = os9_read_lsn(image, lsn, 0, header, sizeof(header));
256 	if (err)
257 		return err;
258 
259 	info->lsn               = lsn;
260 	attributes              = pick_integer_be(header, 0, 1);
261 	info->owner_id          = pick_integer_be(header, 1, 2);
262 	info->link_count        = pick_integer_be(header, 8, 1);
263 	info->file_size         = pick_integer_be(header, 9, 4);
264 	info->directory         = (attributes & 0x80) ? 1 : 0;
265 	info->non_sharable      = (attributes & 0x40) ? 1 : 0;
266 	info->public_execute    = (attributes & 0x20) ? 1 : 0;
267 	info->public_write      = (attributes & 0x10) ? 1 : 0;
268 	info->public_read       = (attributes & 0x08) ? 1 : 0;
269 	info->user_execute      = (attributes & 0x04) ? 1 : 0;
270 	info->user_write        = (attributes & 0x02) ? 1 : 0;
271 	info->user_read         = (attributes & 0x01) ? 1 : 0;
272 
273 	if (info->directory && (info->file_size % 32 != 0))
274 		return IMGTOOLERR_CORRUPTIMAGE;
275 
276 	/* read all sector map entries */
277 	max_entries = (disk_info->sector_size - 16) / 5;
278 	max_entries = (std::min<std::size_t>)(max_entries, ARRAY_LENGTH(info->sector_map) - 1);
279 	for (i = 0; i < max_entries; i++)
280 	{
281 		lsn = pick_integer_be(header, 16 + (i * 5) + 0, 3);
282 		count = pick_integer_be(header, 16 + (i * 5) + 3, 2);
283 		if (count <= 0)
284 			break;
285 
286 		info->sector_map[i].lsn = lsn;
287 		info->sector_map[i].count = count;
288 	}
289 	info->sector_map[i].lsn = 0;
290 	info->sector_map[i].count = 0;
291 	return IMGTOOLERR_SUCCESS;
292 }
293 
294 
295 
os9_allocate_lsn(imgtool::image & image,uint32_t * lsn)296 static imgtoolerr_t os9_allocate_lsn(imgtool::image &image, uint32_t *lsn)
297 {
298 	uint32_t i;
299 	os9_diskinfo *disk_info;
300 	uint8_t b, mask;
301 
302 	disk_info = os9_get_diskinfo(image);
303 
304 	for (i = 0; i < disk_info->total_sectors; i++)
305 	{
306 		b = disk_info->allocation_bitmap[i / 8];
307 		mask = 1 << (7 - (i % 8));
308 
309 		if ((b & mask) == 0)
310 		{
311 			disk_info->allocation_bitmap[i / 8] |= mask;
312 			*lsn = i;
313 			return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes);
314 		}
315 	}
316 	return IMGTOOLERR_NOSPACE;
317 }
318 
319 
320 
os9_deallocate_lsn(imgtool::image & image,uint32_t lsn)321 static imgtoolerr_t os9_deallocate_lsn(imgtool::image &image, uint32_t lsn)
322 {
323 	uint8_t mask;
324 	os9_diskinfo *disk_info;
325 
326 	disk_info = os9_get_diskinfo(image);
327 	mask = 1 << (7 - (lsn % 8));
328 	disk_info->allocation_bitmap[lsn / 8] &= ~mask;
329 	return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes);
330 }
331 
332 
333 
os9_get_free_lsns(imgtool::image & image)334 static uint32_t os9_get_free_lsns(imgtool::image &image)
335 {
336 	const os9_diskinfo *disk_info;
337 	uint32_t i, free_lsns;
338 	uint8_t b;
339 
340 	disk_info = os9_get_diskinfo(image);
341 	free_lsns = 0;
342 
343 	for (i = 0; i < disk_info->total_sectors; i++)
344 	{
345 		b = disk_info->allocation_bitmap[i / 8];
346 		b >>= (7 - (i % 8));
347 		free_lsns += ~b & 1;
348 	}
349 
350 	return free_lsns;
351 }
352 
353 
354 
os9_corrupt_file_error(const struct os9_fileinfo * file_info)355 static imgtoolerr_t os9_corrupt_file_error(const struct os9_fileinfo *file_info)
356 {
357 	imgtoolerr_t err;
358 	if (file_info->directory)
359 		err = IMGTOOLERR_CORRUPTDIR;
360 	else
361 		err = IMGTOOLERR_CORRUPTFILE;
362 	return err;
363 }
364 
365 
366 
os9_set_file_size(imgtool::image & image,struct os9_fileinfo * file_info,uint32_t new_size)367 static imgtoolerr_t os9_set_file_size(imgtool::image &image,
368 	struct os9_fileinfo *file_info, uint32_t new_size)
369 {
370 	imgtoolerr_t err;
371 	const os9_diskinfo *disk_info;
372 	uint32_t new_lsn_count, current_lsn_count;
373 	uint32_t free_lsns, lsn, i;
374 	int sector_map_length = -1;
375 	uint8_t header[256];
376 
377 	/* easy way out? */
378 	if (file_info->file_size == new_size)
379 		return IMGTOOLERR_SUCCESS;
380 
381 	disk_info = os9_get_diskinfo(image);
382 
383 	free_lsns = os9_get_free_lsns(image);
384 	current_lsn_count = (file_info->file_size + disk_info->sector_size - 1) / disk_info->sector_size;
385 	new_lsn_count = (new_size + disk_info->sector_size - 1) / disk_info->sector_size;
386 
387 	/* check to see if the file is growing and we do not have enough space */
388 	if ((new_lsn_count > current_lsn_count) && (new_lsn_count - current_lsn_count > free_lsns))
389 		return IMGTOOLERR_NOSPACE;
390 
391 	if (current_lsn_count != new_lsn_count)
392 	{
393 		/* first find out the size of our sector map */
394 		sector_map_length = 0;
395 		lsn = 0;
396 		while((lsn < current_lsn_count) && (sector_map_length < ARRAY_LENGTH(file_info->sector_map)))
397 		{
398 			if (file_info->sector_map[sector_map_length].count == 0)
399 				return os9_corrupt_file_error(file_info);
400 
401 			lsn += file_info->sector_map[sector_map_length].count;
402 			sector_map_length++;
403 		}
404 
405 		/* keep in mind that the sector_map might not parallel our expected
406 		 * current LSN count; we should tolerate this abnormality */
407 		current_lsn_count = lsn;
408 
409 		while(current_lsn_count > new_lsn_count)
410 		{
411 			/* shrink this file */
412 			lsn = file_info->sector_map[sector_map_length - 1].lsn +
413 				file_info->sector_map[sector_map_length - 1].count - 1;
414 
415 			err = os9_deallocate_lsn(image, lsn);
416 			if (err)
417 				return err;
418 
419 			file_info->sector_map[sector_map_length - 1].count--;
420 			while(sector_map_length > 0 && (file_info->sector_map[sector_map_length - 1].count == 0))
421 				sector_map_length--;
422 			current_lsn_count--;
423 		}
424 
425 		while(current_lsn_count < new_lsn_count)
426 		{
427 			/* grow this file */
428 			err = os9_allocate_lsn(image, &lsn);
429 			if (err)
430 				return err;
431 
432 			if ((sector_map_length > 0) && ((file_info->sector_map[sector_map_length - 1].lsn +
433 				file_info->sector_map[sector_map_length - 1].count) == lsn))
434 			{
435 				file_info->sector_map[sector_map_length - 1].count++;
436 			}
437 			else if (sector_map_length >= ARRAY_LENGTH(file_info->sector_map))
438 			{
439 				return IMGTOOLERR_NOSPACE;
440 			}
441 			else
442 			{
443 				file_info->sector_map[sector_map_length].lsn = lsn;
444 				file_info->sector_map[sector_map_length].count = 1;
445 				sector_map_length++;
446 				file_info->sector_map[sector_map_length].lsn = 0;
447 				file_info->sector_map[sector_map_length].count = 0;
448 			}
449 			current_lsn_count++;
450 		}
451 	}
452 
453 	/* now lets write back the sector */
454 	err = os9_read_lsn(image, file_info->lsn, 0, header, sizeof(header));
455 	if (err)
456 		return err;
457 
458 	file_info->file_size = new_size;
459 	place_integer_be(header, 9, 4, file_info->file_size);
460 
461 	/* do we have to write the sector map? */
462 	if (sector_map_length >= 0)
463 	{
464 		for (i = 0; i < (std::min<std::size_t>)(sector_map_length + 1, ARRAY_LENGTH(file_info->sector_map)); i++)
465 		{
466 			place_integer_be(header, 16 + (i * 5) + 0, 3, file_info->sector_map[i].lsn);
467 			place_integer_be(header, 16 + (i * 5) + 3, 2, file_info->sector_map[i].count);
468 		}
469 	}
470 
471 	err = os9_write_lsn(image, file_info->lsn, 0, header, disk_info->sector_size);
472 	if (err)
473 		return err;
474 
475 	return IMGTOOLERR_SUCCESS;
476 }
477 
478 
479 
480 /*-------------------------------------------------
481     os9_lookup_path - walks an OS-9 directory
482     structure to find a file, or optionally, create
483     a file
484 -------------------------------------------------*/
485 
os9_lookup_path(imgtool::image & img,const char * path,creation_policy_t create,struct os9_fileinfo * file_info,uint32_t * parent_lsn,uint32_t * dirent_lsn,uint32_t * dirent_index)486 static imgtoolerr_t os9_lookup_path(imgtool::image &img, const char *path,
487 	creation_policy_t create, struct os9_fileinfo *file_info,
488 	uint32_t *parent_lsn, uint32_t *dirent_lsn, uint32_t *dirent_index)
489 {
490 	imgtoolerr_t err = IMGTOOLERR_SUCCESS;
491 	struct os9_fileinfo dir_info;
492 	uint32_t index, current_lsn, dir_size;
493 	uint32_t entry_index = 0;
494 	uint32_t free_entry_index = 0xffffffff;
495 	uint32_t entry_lsn = 0;
496 	uint32_t allocated_lsn = 0;
497 	uint8_t entry[32];
498 	uint8_t block[64];
499 	char *filename;
500 	const os9_diskinfo *disk_info;
501 
502 	disk_info = os9_get_diskinfo(img);
503 	current_lsn = disk_info->root_dir_lsn;
504 
505 	if (parent_lsn)
506 		*parent_lsn = 0;
507 
508 	/* we have to transverse each path element */
509 	while(*path)
510 	{
511 		if (parent_lsn)
512 			*parent_lsn = current_lsn;
513 
514 		/* decode the directory header of this directory */
515 		err = os9_decode_file_header(img, current_lsn, &dir_info);
516 		if (err)
517 			goto done;
518 		dir_size = dir_info.file_size;
519 
520 		/* sanity check directory */
521 		if (!dir_info.directory)
522 		{
523 			err = (current_lsn == disk_info->root_dir_lsn) ? IMGTOOLERR_CORRUPTIMAGE : IMGTOOLERR_INVALIDPATH;
524 			goto done;
525 		}
526 
527 		/* walk the directory */
528 		for (index = 0; index < dir_size; index += 32)
529 		{
530 			entry_index = index;
531 			entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index);
532 
533 			err = os9_read_lsn(img, entry_lsn, entry_index, entry, sizeof(entry));
534 			if (err)
535 				goto done;
536 
537 			/* remember first free entry found */
538 			if( free_entry_index == 0xffffffff )
539 			{
540 				if( entry[0] == 0 )
541 					free_entry_index = index;
542 			}
543 
544 			if (os9_interpret_dirent(entry, &filename, &current_lsn, NULL))
545 			{
546 				if (!strcmp(path, filename))
547 					break;
548 			}
549 
550 		}
551 
552 		/* at the end of the file? */
553 		if (index >= dir_size)
554 		{
555 			/* if we're not creating, or we are creating but we have not fully
556 			 * transversed the directory, error out */
557 			if (!create || path[strlen(path) + 1])
558 			{
559 				err = IMGTOOLERR_PATHNOTFOUND;
560 				goto done;
561 			}
562 
563 			/* allocate a new LSN */
564 			err = os9_allocate_lsn(img, &allocated_lsn);
565 			if (err)
566 				goto done;
567 
568 			/* write the file */
569 			memset(block, 0, sizeof(block));
570 			place_integer_be(block, 0, 1, 0x1B | ((create == CREATE_DIR) ? 0x80 : 0x00));
571 			err = os9_write_lsn(img, allocated_lsn, 0, block, sizeof(block));
572 			if (err)
573 				goto done;
574 
575 			if( free_entry_index == 0xffffffff )
576 			{
577 				/* expand the directory to hold the new entry */
578 				err = os9_set_file_size(img, &dir_info, dir_size + 32);
579 				if (err)
580 					goto done;
581 			}
582 			else
583 				/* use first unused entry in directory */
584 				dir_size = free_entry_index;
585 
586 			/* now prepare the directory entry */
587 			memset(entry, 0, sizeof(entry));
588 			place_string(entry, 0, 28, path);
589 			place_integer_be(entry, 29, 3, allocated_lsn);
590 
591 			/* now write the directory entry */
592 			entry_index = dir_size;
593 			entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index);
594 			err = os9_write_lsn(img, entry_lsn, entry_index, entry, 32);
595 			if (err)
596 				goto done;
597 
598 			/* directory entry append complete; no need to hold this lsn */
599 			current_lsn = allocated_lsn;
600 			allocated_lsn = 0;
601 		}
602 		path += strlen(path) + 1;
603 	}
604 
605 	if (file_info)
606 	{
607 		err = os9_decode_file_header(img, current_lsn, file_info);
608 		if (err)
609 			goto done;
610 	}
611 
612 	if (dirent_lsn)
613 		*dirent_lsn = entry_lsn;
614 	if (dirent_index)
615 		*dirent_index = entry_index;
616 
617 done:
618 	if (allocated_lsn != 0)
619 		os9_deallocate_lsn(img, allocated_lsn);
620 	return err;
621 }
622 
623 
624 
os9_diskimage_open(imgtool::image & image,imgtool::stream::ptr && dummy)625 static imgtoolerr_t os9_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy)
626 {
627 	imgtoolerr_t err;
628 	floperr_t ferr;
629 	os9_diskinfo *info;
630 	uint32_t track_size_in_sectors, i; //, attributes;
631 	uint8_t header[256];
632 	uint32_t allocation_bitmap_lsns;
633 	uint8_t b, mask;
634 
635 	info = os9_get_diskinfo(image);
636 
637 	ferr = floppy_read_sector(imgtool_floppy(image), 0, 0, 1, 0, header, sizeof(header));
638 	if (ferr)
639 		return imgtool_floppy_error(ferr);
640 
641 	info->total_sectors             = pick_integer_be(header,   0, 3);
642 	track_size_in_sectors           = pick_integer_be(header,   3, 1);
643 	info->allocation_bitmap_bytes   = pick_integer_be(header,   4, 2);
644 	info->cluster_size              = pick_integer_be(header,   6, 2);
645 	info->root_dir_lsn              = pick_integer_be(header,   8, 3);
646 	info->owner_id                  = pick_integer_be(header,  11, 2);
647 //  attributes                      =
648 	pick_integer_be(header,  13, 1);
649 	info->disk_id                   = pick_integer_be(header,  14, 2);
650 	info->format_flags              = pick_integer_be(header,  16, 1);
651 	info->sectors_per_track         = pick_integer_be(header,  17, 2);
652 	info->bootstrap_lsn             = pick_integer_be(header,  21, 3);
653 	info->bootstrap_size            = pick_integer_be(header,  24, 2);
654 	info->sector_size               = pick_integer_be(header, 104, 2);
655 
656 	info->sides                 = (info->format_flags & 0x01) ? 2 : 1;
657 	info->double_density        = (info->format_flags & 0x02) ? 1 : 0;
658 	info->double_track          = (info->format_flags & 0x04) ? 1 : 0;
659 	info->quad_track_density    = (info->format_flags & 0x08) ? 1 : 0;
660 	info->octal_track_density   = (info->format_flags & 0x10) ? 1 : 0;
661 
662 	pick_string(header, 31, 32, info->name);
663 
664 	if (info->sector_size == 0)
665 		info->sector_size = 256;
666 
667 	/* does the root directory and allocation bitmap collide? */
668 	allocation_bitmap_lsns = (info->allocation_bitmap_bytes + info->sector_size - 1) / info->sector_size;
669 	if (1 + allocation_bitmap_lsns > info->root_dir_lsn)
670 		return IMGTOOLERR_CORRUPTIMAGE;
671 
672 	/* is the allocation bitmap too big? */
673 	info->allocation_bitmap = (uint8_t*)image.malloc(info->allocation_bitmap_bytes);
674 	if (!info->allocation_bitmap)
675 		return IMGTOOLERR_OUTOFMEMORY;
676 	memset(info->allocation_bitmap, 0, info->allocation_bitmap_bytes);
677 
678 	/* sectors per track and track size don't jive? */
679 	if (info->sectors_per_track != track_size_in_sectors)
680 		return IMGTOOLERR_CORRUPTIMAGE;
681 
682 	/* zero sectors per track? */
683 	if (info->sectors_per_track == 0)
684 		return IMGTOOLERR_CORRUPTIMAGE;
685 
686 	/* do we have an odd number of sectors? */
687 	if (info->total_sectors % info->sectors_per_track)
688 		return IMGTOOLERR_CORRUPTIMAGE;
689 
690 	/* read the allocation bitmap */
691 	for (i = 0; i < allocation_bitmap_lsns; i++)
692 	{
693 		err = os9_read_lsn(image, 1 + i, 0, &info->allocation_bitmap[i * info->sector_size],
694 			std::min(info->allocation_bitmap_bytes - (i * info->sector_size), info->sector_size));
695 		if (err)
696 			return err;
697 	}
698 
699 	/* check to make sure that the allocation bitmap and root sector are reserved */
700 	for (i = 0; i <= allocation_bitmap_lsns; i++)
701 	{
702 		b = info->allocation_bitmap[i / 8];
703 		mask = 1 << (7 - (i % 8));
704 		if ((b & mask) == 0)
705 			return IMGTOOLERR_CORRUPTIMAGE;
706 	}
707 
708 	return IMGTOOLERR_SUCCESS;
709 }
710 
711 
712 
os9_diskimage_create(imgtool::image & img,imgtool::stream::ptr && stream,util::option_resolution * opts)713 static imgtoolerr_t os9_diskimage_create(imgtool::image &img, imgtool::stream::ptr &&stream, util::option_resolution *opts)
714 {
715 	imgtoolerr_t err;
716 	std::vector<uint8_t> header;
717 	uint32_t heads, tracks, sectors, sector_bytes, first_sector_id;
718 	uint32_t cluster_size, owner_id;
719 	uint32_t allocation_bitmap_bits, allocation_bitmap_lsns;
720 	uint32_t attributes, format_flags, disk_id;
721 	uint32_t i;
722 	int32_t total_allocated_sectors;
723 	const char *title;
724 	time_t t;
725 	struct tm *ltime;
726 
727 	time(&t);
728 	ltime = localtime(&t);
729 
730 	heads = opts->lookup_int('H');
731 	tracks = opts->lookup_int('T');
732 	sectors = opts->lookup_int('S');
733 	sector_bytes = opts->lookup_int('L');
734 	first_sector_id = opts->lookup_int('F');
735 	title = "";
736 
737 	header.resize(sector_bytes);
738 
739 	if (sector_bytes > 256)
740 		sector_bytes = 256;
741 	cluster_size = 1;
742 	owner_id = 1;
743 	disk_id = 1;
744 	attributes = 0;
745 	allocation_bitmap_bits = heads * tracks * sectors / cluster_size;
746 	allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes;
747 	format_flags = ((heads > 1) ? 0x01 : 0x00) | ((tracks > 40) ? 0x02 : 0x00);
748 
749 	memset(&header[0], 0, sector_bytes);
750 	place_integer_be(&header[0],   0,  3, heads * tracks * sectors);
751 	place_integer_be(&header[0],   3,  1, sectors);
752 	place_integer_be(&header[0],   4,  2, (allocation_bitmap_bits + 7) / 8);
753 	place_integer_be(&header[0],   6,  2, cluster_size);
754 	place_integer_be(&header[0],   8,  3, 1 + allocation_bitmap_lsns);
755 	place_integer_be(&header[0],  11,  2, owner_id);
756 	place_integer_be(&header[0],  13,  1, attributes);
757 	place_integer_be(&header[0],  14,  2, disk_id);
758 	place_integer_be(&header[0],  16,  1, format_flags);
759 	place_integer_be(&header[0],  17,  2, sectors);
760 	place_string(&header[0],   31, 32, title);
761 	place_integer_be(&header[0], 103, 2, sector_bytes / 256);
762 
763 	/* path descriptor options */
764 	place_integer_be(&header[0], 0x3f+0x00, 1, 1); /* device class */
765 	place_integer_be(&header[0], 0x3f+0x01, 1, 1); /* drive number */
766 	place_integer_be(&header[0], 0x3f+0x03, 1, 0x20); /* device type */
767 	place_integer_be(&header[0], 0x3f+0x04, 1, 1); /* density capability */
768 	place_integer_be(&header[0], 0x3f+0x05, 2, tracks); /* number of tracks */
769 	place_integer_be(&header[0], 0x3f+0x07, 1, heads); /* number of sides */
770 	place_integer_be(&header[0], 0x3f+0x09, 2, sectors); /* sectors per track */
771 	place_integer_be(&header[0], 0x3f+0x0b, 2, sectors); /* sectors on track zero */
772 	place_integer_be(&header[0], 0x3f+0x0d, 1, 3); /* sector interleave factor */
773 	place_integer_be(&header[0], 0x3f+0x0e, 1, 8); /* default sectors per allocation */
774 
775 	err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id, 0, &header[0], sector_bytes, 0);    /* TODO: pass ddam argument from imgtool */
776 	if (err)
777 		goto done;
778 
779 	total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 7;
780 
781 	for (i = 0; i < allocation_bitmap_lsns; i++)
782 	{
783 		memset(&header[0], 0x00, sector_bytes);
784 
785 		if (total_allocated_sectors > (8 * 256))
786 		{
787 			memset(&header[0], 0xff, sector_bytes);
788 			total_allocated_sectors -= (8 * 256);
789 		}
790 		else if (total_allocated_sectors > 1 )
791 		{
792 			int offset;
793 			uint8_t mask;
794 
795 			while( total_allocated_sectors >= 0 )
796 			{
797 				offset = total_allocated_sectors / 8;
798 				mask = 1 << (7 - ( total_allocated_sectors % 8 ) );
799 
800 				header[offset] |= mask;
801 				total_allocated_sectors--;
802 			}
803 		}
804 
805 		err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + i, 0, &header[0], sector_bytes, 0);    /* TODO: pass ddam argument from imgtool */
806 		if (err)
807 			goto done;
808 	}
809 
810 	memset(&header[0], 0, sector_bytes);
811 	header[0x00] = 0xBF;
812 	header[0x01] = 0x00;
813 	header[0x02] = 0x00;
814 	header[0x03] = (uint8_t) ltime->tm_year;
815 	header[0x04] = (uint8_t) ltime->tm_mon + 1;
816 	header[0x05] = (uint8_t) ltime->tm_mday;
817 	header[0x06] = (uint8_t) ltime->tm_hour;
818 	header[0x07] = (uint8_t) ltime->tm_min;
819 	header[0x08] = 0x02;
820 	header[0x09] = 0x00;
821 	header[0x0A] = 0x00;
822 	header[0x0B] = 0x00;
823 	header[0x0C] = 0x40;
824 	header[0x0D] = (uint8_t) (ltime->tm_year % 100);
825 	header[0x0E] = (uint8_t) ltime->tm_mon;
826 	header[0x0F] = (uint8_t) ltime->tm_mday;
827 	place_integer_be(&header[0], 0x10, 3, 1 + allocation_bitmap_lsns + 1);
828 	place_integer_be(&header[0], 0x13, 2, 8);
829 
830 	err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + allocation_bitmap_lsns, 0, &header[0], sector_bytes, 0);   /* TODO: pass ddam argument from imgtool */
831 	if (err)
832 		goto done;
833 
834 	memset(&header[0], 0, sector_bytes);
835 	header[0x00] = 0x2E;
836 	header[0x01] = 0xAE;
837 	header[0x1F] = 1 + allocation_bitmap_lsns;
838 	header[0x20] = 0xAE;
839 	header[0x3F] = 1 + allocation_bitmap_lsns;
840 	err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 2 + allocation_bitmap_lsns, 0, &header[0], sector_bytes, 0);   /* TOOD: pass ddam argument from imgtool */
841 	if (err)
842 		goto done;
843 
844 done:
845 	return err;
846 }
847 
848 
849 
os9_diskimage_beginenum(imgtool::directory & enumeration,const char * path)850 static imgtoolerr_t os9_diskimage_beginenum(imgtool::directory &enumeration, const char *path)
851 {
852 	imgtoolerr_t err = IMGTOOLERR_SUCCESS;
853 	struct os9_direnum *os9enum;
854 	imgtool::image &image(enumeration.image());
855 
856 	os9enum = os9_get_dirinfo(enumeration);
857 
858 	err = os9_lookup_path(image, path, CREATE_NONE, &os9enum->dir_info, NULL, NULL, NULL);
859 	if (err)
860 		goto done;
861 
862 	/* this had better be a directory */
863 	if (!os9enum->dir_info.directory)
864 	{
865 		err = IMGTOOLERR_CORRUPTIMAGE;
866 		goto done;
867 	}
868 
869 done:
870 	return err;
871 }
872 
873 
874 
os9_diskimage_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)875 static imgtoolerr_t os9_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
876 {
877 	struct os9_direnum *os9enum;
878 	uint32_t lsn, index;
879 	imgtoolerr_t err;
880 	uint8_t dir_entry[32];
881 	char filename[29];
882 	struct os9_fileinfo file_info;
883 	imgtool::image &image(enumeration.image());
884 
885 	os9enum = os9_get_dirinfo(enumeration);
886 
887 	do
888 	{
889 		/* check for EOF */
890 		if (os9enum->index >= os9enum->dir_info.file_size)
891 		{
892 			ent.eof = 1;
893 			return IMGTOOLERR_SUCCESS;
894 		}
895 
896 		/* locate the LSN and offset for this directory entry */
897 		index = os9enum->index;
898 		lsn = os9_lookup_lsn(image, &os9enum->dir_info, &index);
899 
900 		/* read the directory entry out of the lSN */
901 		err = os9_read_lsn(image, lsn, index, dir_entry, sizeof(dir_entry));
902 		if (err)
903 			return err;
904 
905 		if (dir_entry[0])
906 		{
907 			/* read the file or directory name */
908 			pick_string(dir_entry, 0, 28, filename);
909 
910 			/* we have certain expectations of the directory contents; the
911 			 * first directory entry should be "..", the second ".", and
912 			 * subsequent entries should never be "." or ".." */
913 			switch(os9enum->index)
914 			{
915 				case 0:
916 					if (strcmp(filename, ".."))
917 						imgtool_warn("First entry in directory should be \"..\" and not \"%s\"", filename);
918 					break;
919 
920 				case 32:
921 					if (strcmp(filename, "."))
922 						imgtool_warn("Second entry in directory should be \".\" and not \"%s\"", filename);
923 					break;
924 
925 				default:
926 					if (!strcmp(filename, ".") || !strcmp(filename, ".."))
927 						imgtool_warn("Directory entry %d should not be \"%s\"", index / 32, filename);
928 					break;
929 			}
930 
931 			/* if the filename is ".", the file should point to the current directory */
932 			if (!strcmp(filename, ".") && (pick_integer_be(dir_entry, 29, 3) != os9enum->dir_info.lsn))
933 			{
934 				imgtool_warn("Directory \".\" does not point back to same directory");
935 			}
936 		}
937 		else
938 		{
939 			/* no more directory entries */
940 			filename[0] = '\0';
941 		}
942 
943 		/* move on to the next directory entry */
944 		os9enum->index += 32;
945 	}
946 	while(!filename[0] || !strcmp(filename, ".") || !strcmp(filename, ".."));
947 
948 	/* read file attributes */
949 	lsn = pick_integer_be(dir_entry, 29, 3);
950 	err = os9_decode_file_header(enumeration.image(), lsn, &file_info);
951 	if (err)
952 		return err;
953 
954 	/* fill out imgtool_dirent structure */
955 	snprintf(ent.filename, ARRAY_LENGTH(ent.filename), "%s", filename);
956 	snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%c%c%c%c%c%c%c%c",
957 		file_info.directory      ? 'd' : '-',
958 		file_info.non_sharable   ? 's' : '-',
959 		file_info.public_execute ? 'x' : '-',
960 		file_info.public_write   ? 'w' : '-',
961 		file_info.public_read    ? 'r' : '-',
962 		file_info.user_execute   ? 'x' : '-',
963 		file_info.user_write     ? 'w' : '-',
964 		file_info.user_read      ? 'r' : '-');
965 
966 	ent.directory = file_info.directory;
967 	ent.corrupt = (dir_entry[28] != 0);
968 	ent.filesize = file_info.file_size;
969 	return IMGTOOLERR_SUCCESS;
970 }
971 
972 
973 
os9_diskimage_freespace(imgtool::partition & partition,uint64_t * size)974 static imgtoolerr_t os9_diskimage_freespace(imgtool::partition &partition, uint64_t *size)
975 {
976 	imgtool::image &image(partition.image());
977 	const os9_diskinfo *disk_info;
978 	uint32_t free_lsns;
979 
980 	disk_info = os9_get_diskinfo(image);
981 	free_lsns = os9_get_free_lsns(image);
982 
983 	*size = free_lsns * disk_info->sector_size;
984 	return IMGTOOLERR_SUCCESS;
985 }
986 
987 
988 
os9_diskimage_readfile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & destf)989 static imgtoolerr_t os9_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
990 {
991 	imgtoolerr_t err;
992 	imgtool::image &img(partition.image());
993 	const os9_diskinfo *disk_info;
994 	struct os9_fileinfo file_info;
995 	uint8_t buffer[256];
996 	int i, j;
997 	uint32_t file_size;
998 	uint32_t used_size;
999 
1000 	disk_info = os9_get_diskinfo(img);
1001 
1002 	err = os9_lookup_path(img, filename, CREATE_NONE, &file_info, NULL, NULL, NULL);
1003 	if (err)
1004 		return err;
1005 	if (file_info.directory)
1006 		return IMGTOOLERR_FILENOTFOUND;
1007 	file_size = file_info.file_size;
1008 
1009 	for (i = 0; file_info.sector_map[i].count > 0; i++)
1010 	{
1011 		for (j = 0; j < file_info.sector_map[i].count; j++)
1012 		{
1013 			used_size = std::min(file_size, disk_info->sector_size);
1014 			err = os9_read_lsn(img, file_info.sector_map[i].lsn + j, 0,
1015 				buffer, used_size);
1016 			if (err)
1017 				return err;
1018 			destf.write(buffer, used_size);
1019 			file_size -= used_size;
1020 		}
1021 	}
1022 	return IMGTOOLERR_SUCCESS;
1023 }
1024 
1025 
1026 
os9_diskimage_writefile(imgtool::partition & partition,const char * path,const char * fork,imgtool::stream & sourcef,util::option_resolution * opts)1027 static imgtoolerr_t os9_diskimage_writefile(imgtool::partition &partition, const char *path, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
1028 {
1029 	imgtoolerr_t err;
1030 	imgtool::image &image(partition.image());
1031 	struct os9_fileinfo file_info;
1032 	size_t write_size;
1033 	std::vector<uint8_t> buf;
1034 	int i = -1;
1035 	uint32_t lsn = 0;
1036 	uint32_t count = 0;
1037 	uint32_t sz;
1038 	const os9_diskinfo *disk_info;
1039 
1040 	disk_info = os9_get_diskinfo(image);
1041 
1042 	buf.resize(disk_info->sector_size);
1043 
1044 	err = os9_lookup_path(image, path, CREATE_FILE, &file_info, NULL, NULL, NULL);
1045 	if (err)
1046 		goto done;
1047 
1048 	sz = (uint32_t) sourcef.size();
1049 
1050 	err = os9_set_file_size(image, &file_info, sz);
1051 	if (err)
1052 		goto done;
1053 
1054 	while(sz > 0)
1055 	{
1056 		write_size = (std::min<uint64_t>)(sz, disk_info->sector_size);
1057 
1058 		sourcef.read(&buf[0], write_size);
1059 
1060 		while(count == 0)
1061 		{
1062 			i++;
1063 			lsn = file_info.sector_map[i].lsn;
1064 			count = file_info.sector_map[i].count;
1065 		}
1066 
1067 		err = os9_write_lsn(image, lsn, 0, &buf[0], write_size);
1068 		if (err)
1069 			goto done;
1070 
1071 		lsn++;
1072 		count--;
1073 		sz -= write_size;
1074 	}
1075 
1076 done:
1077 	return err;
1078 }
1079 
1080 
1081 
os9_diskimage_delete(imgtool::partition & partition,const char * path,unsigned int delete_directory)1082 static imgtoolerr_t os9_diskimage_delete(imgtool::partition &partition, const char *path,
1083 	unsigned int delete_directory)
1084 {
1085 	imgtoolerr_t err;
1086 	imgtool::image &image(partition.image());
1087 	struct os9_fileinfo file_info;
1088 	uint32_t dirent_lsn, dirent_index;
1089 	uint32_t entry_lsn, entry_index;
1090 	uint32_t i, j, lsn;
1091 	uint8_t b;
1092 
1093 	//disk_info = os9_get_diskinfo(image);
1094 
1095 	err = os9_lookup_path(image, path, CREATE_NONE, &file_info, NULL, &dirent_lsn, &dirent_index);
1096 	if (err)
1097 		return err;
1098 	if (file_info.directory != delete_directory)
1099 		return IMGTOOLERR_FILENOTFOUND;
1100 
1101 	/* make sure that if we are deleting a directory, it is empty */
1102 	if (delete_directory)
1103 	{
1104 		for (i = 64; i < file_info.file_size; i += 32)
1105 		{
1106 			entry_index = i;
1107 			entry_lsn = os9_lookup_lsn(image, &file_info, &entry_index);
1108 
1109 			err = os9_read_lsn(image, entry_lsn, entry_index, &b, 1);
1110 			if (err)
1111 				return err;
1112 
1113 			/* this had better be a deleted file, if not we can't delete */
1114 			if (b != 0)
1115 				return IMGTOOLERR_DIRNOTEMPTY;
1116 		}
1117 	}
1118 
1119 	/* zero out the file entry */
1120 	b = '\0';
1121 	err = os9_write_lsn(image, dirent_lsn, dirent_index, &b, 1);
1122 	if (err)
1123 		return err;
1124 
1125 	/* get the link count */
1126 	err = os9_read_lsn(image, file_info.lsn, 8, &b, 1);
1127 	if (err)
1128 		return err;
1129 
1130 	if (b > 0)
1131 		b--;
1132 	if (b > 0)
1133 	{
1134 		/* link count is greater than zero */
1135 		err = os9_write_lsn(image, file_info.lsn, 8, &b, 1);
1136 		if (err)
1137 			return err;
1138 	}
1139 	else
1140 	{
1141 		/* no more links; outright delete the file */
1142 		err = os9_deallocate_lsn(image, file_info.lsn);
1143 		if (err)
1144 			return err;
1145 
1146 		for (i = 0; (i < ARRAY_LENGTH(file_info.sector_map)) && file_info.sector_map[i].count; i++)
1147 		{
1148 			lsn = file_info.sector_map[i].lsn;
1149 			for (j = 0;  j < file_info.sector_map[i].count; j++)
1150 			{
1151 				err = os9_deallocate_lsn(image, lsn + j);
1152 				if (err)
1153 					return err;
1154 			}
1155 		}
1156 	}
1157 
1158 	return IMGTOOLERR_SUCCESS;
1159 }
1160 
1161 
1162 
os9_diskimage_deletefile(imgtool::partition & partition,const char * path)1163 static imgtoolerr_t os9_diskimage_deletefile(imgtool::partition &partition, const char *path)
1164 {
1165 	return os9_diskimage_delete(partition, path, 0);
1166 }
1167 
1168 
1169 
os9_diskimage_createdir(imgtool::partition & partition,const char * path)1170 static imgtoolerr_t os9_diskimage_createdir(imgtool::partition &partition, const char *path)
1171 {
1172 	imgtoolerr_t err;
1173 	imgtool::image &image(partition.image());
1174 	struct os9_fileinfo file_info;
1175 	uint8_t dir_data[64];
1176 	uint32_t parent_lsn;
1177 
1178 	err = os9_lookup_path(image, path, CREATE_DIR, &file_info, &parent_lsn, NULL, NULL);
1179 	if (err)
1180 		goto done;
1181 
1182 	err = os9_set_file_size(image, &file_info, 64);
1183 	if (err)
1184 		goto done;
1185 
1186 	/* create intial directories */
1187 	memset(dir_data, 0, sizeof(dir_data));
1188 	place_string(dir_data,   0, 32, "..");
1189 	place_integer_be(dir_data, 29,  3, parent_lsn);
1190 	place_string(dir_data,  32, 32, ".");
1191 	place_integer_be(dir_data, 61,  3, file_info.lsn);
1192 
1193 	err = os9_write_lsn(image, file_info.sector_map[0].lsn, 0, dir_data, sizeof(dir_data));
1194 	if (err)
1195 		goto done;
1196 
1197 done:
1198 	return err;
1199 }
1200 
1201 
1202 
os9_diskimage_deletedir(imgtool::partition & partition,const char * path)1203 static imgtoolerr_t os9_diskimage_deletedir(imgtool::partition &partition, const char *path)
1204 {
1205 	return os9_diskimage_delete(partition, path, 1);
1206 }
1207 
1208 
1209 
os9_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)1210 void os9_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
1211 {
1212 	switch(state)
1213 	{
1214 		/* --- the following bits of info are returned as 64-bit signed integers --- */
1215 		case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR:        info->i = 1; break;
1216 		case IMGTOOLINFO_INT_OPEN_IS_STRICT:                info->i = 1; break;
1217 		case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES:             info->i = sizeof(os9_diskinfo); break;
1218 		case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES:             info->i = sizeof(struct os9_direnum); break;
1219 		case IMGTOOLINFO_INT_PATH_SEPARATOR:                info->i = '/'; break;
1220 
1221 		/* --- the following bits of info are returned as NULL-terminated strings --- */
1222 		case IMGTOOLINFO_STR_NAME:                          strcpy(info->s = imgtool_temp_str(), "os9"); break;
1223 		case IMGTOOLINFO_STR_DESCRIPTION:                   strcpy(info->s = imgtool_temp_str(), "OS-9 format"); break;
1224 		case IMGTOOLINFO_STR_FILE:                          strcpy(info->s = imgtool_temp_str(), __FILE__); break;
1225 		case IMGTOOLINFO_STR_EOLN:                          strcpy(info->s = imgtool_temp_str(), "\r"); break;
1226 
1227 		/* --- the following bits of info are returned as pointers to data or functions --- */
1228 		case IMGTOOLINFO_PTR_MAKE_CLASS:                    info->make_class = imgtool_floppy_make_class; break;
1229 		case IMGTOOLINFO_PTR_FLOPPY_CREATE:                 info->create = os9_diskimage_create; break;
1230 		case IMGTOOLINFO_PTR_FLOPPY_OPEN:                   info->open = os9_diskimage_open; break;
1231 		case IMGTOOLINFO_PTR_BEGIN_ENUM:                    info->begin_enum = os9_diskimage_beginenum; break;
1232 		case IMGTOOLINFO_PTR_NEXT_ENUM:                     info->next_enum = os9_diskimage_nextenum; break;
1233 		case IMGTOOLINFO_PTR_FREE_SPACE:                    info->free_space = os9_diskimage_freespace; break;
1234 		case IMGTOOLINFO_PTR_READ_FILE:                     info->read_file = os9_diskimage_readfile; break;
1235 		case IMGTOOLINFO_PTR_WRITE_FILE:                    info->write_file = os9_diskimage_writefile; break;
1236 		case IMGTOOLINFO_PTR_DELETE_FILE:                   info->delete_file = os9_diskimage_deletefile; break;
1237 		case IMGTOOLINFO_PTR_CREATE_DIR:                    info->create_dir = os9_diskimage_createdir; break;
1238 		case IMGTOOLINFO_PTR_DELETE_DIR:                    info->delete_dir = os9_diskimage_deletedir; break;
1239 		case IMGTOOLINFO_PTR_FLOPPY_FORMAT:                 info->p = (void *) floppyoptions_coco; break;
1240 	}
1241 }
1242