1 // license:BSD-3-Clause
2 // copyright-holders:Sergey Svishchev
3 /****************************************************************************
4 
5     rt11.cpp
6 
7     DEC RT-11 disk images
8 
9     References:
10 
11     VaFFM -- bitsavers://pdf/dec/pdp11/rt11/v5.6_Aug91/AA-PD6PA-TC_RT-11_Volume_and_File_Formats_Manual_Aug91.pdf
12     DHM -- bitsavers://pdf/dec/pdp11/rt11/v5.6_Aug91/AA-PE7VA-TC_RT-11_Device_Handlers_Manual_Aug91.pdf
13     SSM -- bitsavers://pdf/dec/pdp11/rt11/v5.0_Mar83/AA-H379B-TC_5.0_SWsuppMar83.pdf
14     TSX+ -- bitsavers://pdf/dec/pdp11/tsxPlus/manuals_6.31/TSX-Plus_UsersRef_Jan88.pdf
15     PUTR -- http://www.dbit.com/pub/putr/putr.asm
16 
17     To do:
18     - filter for text files
19     - read-write support
20     - report empty 'last modified' time if date field is all zeros
21     - report free space
22     - arbitrary sized images
23     - don't crash when strings in home block have non-ascii chars (charconverter does not apply)
24     - do something about bootblock bug in imgtool (commit aca90520)
25 
26     LBN Contents
27     --- --------
28     0   Reserved (primary bootstrap)
29     1   Reserved (home block)
30     2-5 Reserved (secondary bootstrap)
31     6-7 Directory segment 1
32     ... Directory segment 2-n
33     ... Data
34 
35     Home block
36     ----------
37     000-201 Bad block replacement table
38     202-203 ?
39     204-251 INITIALIZE/RESTORE data area
40     252-273 BUP information area
41     274-677 ?
42     700-701 (Reserved for Digital, must be zero)
43     702-703 (Reserved for Digital, must be zero)
44     704-721 ?
45     722-723 Pack cluster size (= 1)
46     724-725 Block number of first directory segment
47     726-727 System version (RAD50)
48     730-742 Volume Identification
49     744-757 Owner name
50     760-773 System Identification
51     776-777 Checksum
52 
53     Directory segment header
54     ------------------------
55     0   The total number of segments in this directory.
56     1   The segment number of the next logical directory segment. If this word is 0, there are no more segments in the list.
57     2   The number of the highest segment currently in use.  Valid only in the first directory segment.
58     3   The number of extra bytes per directory entry, always an unsigned, even octal number.
59     4   The block number on the volume where the actual stored data identified by this segment begins.
60 
61     Directory entry
62     ---------------
63     0   Status word
64     1   File name 1-3 (RAD50)
65     2   File name 4-6 (RAD50)
66     3   File type 1-3 (RAD50)
67     4   Total file length (blocks)
68     5   Job#, Channel# (RT-11 uses this information only for tentative files)
69     6   Creation date
70     7-  Optional extra words
71 
72 ****************************************************************************/
73 
74 #include "imgtool.h"
75 #include "formats/imageutl.h"
76 #include "iflopimg.h"
77 
78 #include <cstdio>
79 #include <cstdlib>
80 #include <cstring>
81 
82 
83 namespace
84 {
85 	struct rt11_diskinfo
86 	{
87 		uint16_t directory_start;
88 		uint16_t total_segments;
89 		uint16_t last_segment;
90 		uint16_t dirent_size;
91 		int dirents_per_block;
92 		// autodetected
93 		int tracks;
94 		int heads;
95 		int sectors;
96 		uint32_t sector_size;
97 		// cache
98 		int map[16];
99 		int cached_track;
100 		int cached_head;
101 	};
102 
103 	enum misc_t
104 	{
105 		HOME_BLOCK = 1,
106 		BLOCK_SIZE = 512
107 	};
108 
109 	struct rt11_direnum
110 	{
111 		uint8_t segment_data[2 * BLOCK_SIZE];
112 		uint16_t segment;
113 		uint16_t index;
114 		uint16_t data;
115 	};
116 
117 	struct rt11_dirent
118 	{
119 		uint16_t status;
120 		uint16_t filename[3];
121 		uint16_t time;
122 		// synthetic
123 		uint64_t filesize;
124 		uint16_t data;
125 	};
126 
127 	enum rt11_status
128 	{
129 		E_PRE  = 0000020,
130 		E_TENT = 0000400,
131 		E_MPTY = 0001000,
132 		E_PERM = 0002000,
133 		E_EOS  = 0004000,
134 		E_READ = 0040000,
135 		E_PROT = 0100000
136 	};
137 
138 	enum creation_policy_t
139 	{
140 		CREATE_NONE,
141 		CREATE_FILE,
142 	};
143 
144 
_rt11_crack_time(uint16_t rt11_time)145 	util::arbitrary_datetime _rt11_crack_time(uint16_t rt11_time)
146 	{
147 		util::arbitrary_datetime dt;
148 
149 		dt.second       = 0;
150 		dt.minute       = 0;
151 		dt.hour         = 0;
152 		dt.day_of_month = (rt11_time >> 5) & 31;
153 		dt.month        = (rt11_time >> 10) & 15;
154 		dt.year         = 1972 + (rt11_time & 31) + 32 * ((rt11_time >> 14) & 3);
155 
156 		return dt;
157 	}
158 
rt11_crack_time(uint16_t rt11_time)159 	imgtool::datetime rt11_crack_time(uint16_t rt11_time)
160 	{
161 		util::arbitrary_datetime dt;
162 		imgtool::datetime it;
163 
164 		if (rt11_time == 0)
165 		{
166 			return imgtool::datetime(imgtool::datetime::datetime_type::NONE, dt);
167 		}
168 
169 		dt = _rt11_crack_time(rt11_time);
170 
171 		it = imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt);
172 
173 		return it;
174 	}
175 
rt11_from_rad50(char * ascii,uint16_t * rad50,int num)176 	void rt11_from_rad50(char *ascii, uint16_t *rad50, int num)
177 	{
178 		const char rad[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789:";
179 
180 		for (int i = 0, j = 0; i < num; i++)
181 		{
182 			ascii[j++] = rad[ rad50[i] / (050 * 050)];
183 			ascii[j++] = rad[(rad50[i] / 050) % 050];
184 			ascii[j++] = rad[ rad50[i] % 050];
185 		}
186 	}
187 
rt11_filename_from_rad50(char * ascii,uint16_t * rad50)188 	void rt11_filename_from_rad50(char *ascii, uint16_t *rad50)
189 	{
190 		int i, j;
191 
192 		rt11_from_rad50(&ascii[0], &rad50[0], 2);
193 		for (i = 0; i < 6; i++)
194 		{
195 			if (ascii[i] == ' ') break;
196 		}
197 		ascii[i++] = '.';
198 		rt11_from_rad50(&ascii[i], &rad50[2], 1);
199 		for (j = i; j < i + 3; j++)
200 		{
201 			if (ascii[j] == ' ') break;
202 		}
203 		ascii[j] = '\0';
204 	}
205 
is_file_storagetype(uint16_t status)206 	int is_file_storagetype(uint16_t status)
207 	{
208 		return !(status & (E_MPTY | E_EOS | E_TENT));
209 	}
210 
get_rt11_info(imgtool::image & image)211 	rt11_diskinfo *get_rt11_info(imgtool::image &image)
212 	{
213 		return (rt11_diskinfo *)imgtool_floppy_extrabytes(image);
214 	}
215 }
216 
217 
rt11_image_get_geometry(imgtool::image & image,uint32_t * tracks,uint32_t * heads,uint32_t * sectors)218 static imgtoolerr_t rt11_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors)
219 {
220 	const rt11_diskinfo *di = get_rt11_info(image);
221 
222 	*sectors = di->sectors;
223 	*heads = di->heads;
224 	*tracks = di->tracks;
225 	return IMGTOOLERR_SUCCESS;
226 }
227 
rt11_get_sector_position(imgtool::image & image,uint32_t sector_index,uint32_t & head,uint32_t & track,uint32_t & sector)228 static imgtoolerr_t rt11_get_sector_position(imgtool::image &image, uint32_t sector_index,
229 	uint32_t &head, uint32_t &track, uint32_t &sector)
230 {
231 	imgtoolerr_t err;
232 	rt11_diskinfo *di = get_rt11_info(image);
233 	uint32_t tracks, heads, sectors;
234 
235 	err = image.get_geometry(&tracks, &heads, &sectors);
236 	if (err)
237 		return err;
238 
239 	track = sector_index / sectors / heads;
240 	head = (sector_index / sectors) % heads;
241 
242 	// map 1-based sector numbers, possibly interleaved, to sector indexes
243 	if (track != di->cached_track || head != di->cached_head)
244 	{
245 		memset(di->map, -1, sizeof(di->map));
246 		for (int i = 0; i < 256; i++)
247 		{
248 			int sector_id;
249 
250 			if (floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, i, NULL, NULL, &sector_id, NULL, NULL))
251 				continue;
252 
253 			if (sector_id > 0 && sector_id <= sectors)
254 				di->map[sector_id - 1] = i;
255 		}
256 		di->cached_track = track;
257 		di->cached_head = head;
258 	}
259 
260 	if (di->map[(sector_index % sectors)] < 0)
261 	{
262 		return IMGTOOLERR_SEEKERROR;
263 	}
264 	else
265 	{
266 		sector = di->map[(sector_index % sectors)];
267 		return IMGTOOLERR_SUCCESS;
268 	}
269 }
270 
271 
rt11_image_readblock(imgtool::image & image,void * buffer,uint64_t block)272 static imgtoolerr_t rt11_image_readblock(imgtool::image &image, void *buffer, uint64_t block)
273 {
274 	imgtoolerr_t err;
275 	floperr_t ferr;
276 	const rt11_diskinfo *di = get_rt11_info(image);
277 	uint32_t track, head, sector;
278 	unsigned long flags;
279 
280 	if (di->sector_size == 256)
281 	{
282 		err = rt11_get_sector_position(image, block * 2, head, track, sector);
283 		if (err)
284 			return err;
285 
286 		ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 256);
287 		if (ferr)
288 			return imgtool_floppy_error(ferr);
289 
290 		ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags);
291 		if (ferr)
292 			return imgtool_floppy_error(ferr);
293 
294 		if (flags)
295 			return IMGTOOLERR_READERROR;
296 
297 		err = rt11_get_sector_position(image, (block * 2) + 1, head, track, sector);
298 		if (err)
299 			return err;
300 
301 		ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, ((uint8_t *) buffer) + 256,  256);
302 		if (ferr)
303 			return imgtool_floppy_error(ferr);
304 
305 		ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags);
306 		if (ferr)
307 			return imgtool_floppy_error(ferr);
308 
309 		if (flags)
310 			return IMGTOOLERR_READERROR;
311 	}
312 	else
313 	{
314 		err = rt11_get_sector_position(image, block, head, track, sector);
315 		if (err)
316 			return err;
317 
318 		ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, buffer, BLOCK_SIZE);
319 		if (ferr)
320 			return imgtool_floppy_error(ferr);
321 
322 		ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags);
323 		if (ferr)
324 			return imgtool_floppy_error(ferr);
325 
326 		if (flags)
327 			return IMGTOOLERR_READERROR;
328 	}
329 
330 	return IMGTOOLERR_SUCCESS;
331 }
332 
rt11_probe_geometry(imgtool::image & image)333 static imgtoolerr_t rt11_probe_geometry(imgtool::image &image)
334 {
335 	floperr_t ferr;
336 	rt11_diskinfo *di = get_rt11_info(image);
337 
338 	// MX (11x256 byte sectors) or MY (10x512 byte sectors)?
339 	ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 0, 0, 0, NULL, NULL, NULL, &di->sector_size, NULL);
340 	if (ferr)
341 		return imgtool_floppy_error(ferr);
342 
343 	if (di->sector_size == 256)
344 		di->sectors = 11;
345 	else
346 		di->sectors = 10;
347 
348 	// double- or single-sided?
349 	ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 1, 0, 0, NULL, NULL, NULL, NULL, NULL);
350 	if (ferr)
351 		di->heads = 1;
352 	else
353 		di->heads = 2;
354 
355 	// 80 or 40 tracks?
356 	ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 0, 50, 0, NULL, NULL, NULL, NULL, NULL);
357 	if (ferr)
358 		di->tracks = 40;
359 	else
360 		di->tracks = 80;
361 
362 	return IMGTOOLERR_SUCCESS;
363 }
364 
rt11_image_open(imgtool::image & image,imgtool::stream::ptr && dummy)365 static imgtoolerr_t rt11_image_open(imgtool::image &image, imgtool::stream::ptr &&dummy)
366 {
367 	imgtoolerr_t err;
368 	uint8_t buffer[BLOCK_SIZE];
369 	rt11_diskinfo *di = get_rt11_info(image);
370 
371 	di->cached_head = -1;
372 	di->cached_track = -1;
373 	memset(di->map, -1, sizeof(di->map));
374 	// dummy values for rt11_image_readblock
375 	di->tracks = 40;
376 	di->heads = 1;
377 
378 	err = rt11_probe_geometry(image);
379 	if (err)
380 		return err;
381 
382 	/* load home block */
383 	err = rt11_image_readblock(image, buffer, HOME_BLOCK);
384 	if (err)
385 		return err;
386 
387 	di->directory_start = pick_integer_le(buffer, 0724, 2);
388 
389 	// real-world images seem to never have a valid checksum, but directory_start is always 6
390 #if 0
391 	uint16_t tmp, cksum;
392 
393 	tmp = 0;
394 	cksum = pick_integer_le(buffer, 0776, 2);
395 	for (int i = 0; i < 510; i+=2)
396 	{
397 		tmp += pick_integer_le(buffer, i, 2);
398 	}
399 
400 	/* sanity check these values */
401 	if (cksum != tmp)
402 	{
403 		fprintf(stderr, "cksum stored:computed %04x:%04x\n", cksum, tmp);
404 		return IMGTOOLERR_CORRUPTIMAGE;
405 	}
406 #endif
407 	if (di->directory_start != 6)
408 	{
409 		return IMGTOOLERR_CORRUPTIMAGE;
410 	}
411 
412 	/* load first directory segment */
413 	err = rt11_image_readblock(image, buffer, di->directory_start);
414 	if (err)
415 		return err;
416 
417 	di->total_segments = pick_integer_le(buffer, 0, 2);
418 	di->last_segment = pick_integer_le(buffer, 4, 2);
419 	di->dirent_size = (pick_integer_le(buffer, 6, 2) + 7) * 2;
420 	di->dirents_per_block = (2 * BLOCK_SIZE - 10) / di->dirent_size;
421 
422 	return IMGTOOLERR_SUCCESS;
423 }
424 
rt11_image_info(imgtool::image & image,std::ostream & stream)425 static void rt11_image_info(imgtool::image &image, std::ostream &stream)
426 {
427 	uint8_t buffer[BLOCK_SIZE];
428 	char system[4];
429 	char vid[13], oid[13], sid[13];
430 
431 	rt11_image_readblock(image, buffer, HOME_BLOCK);
432 	rt11_from_rad50(system, (uint16_t *)&buffer[0726], 1);
433 	system[3] = '\0';
434 
435 	memcpy(vid, &buffer[0730], 12);
436 	memcpy(oid, &buffer[0744], 12);
437 	memcpy(sid, &buffer[0760], 12);
438 	vid[12] = '\0';
439 	oid[12] = '\0';
440 	sid[12] = '\0';
441 
442 	stream << "System version: '" << system << "', System ID: '" << sid << "', Volume ID: '" << vid << "', Owner: '" << oid << "'";
443 }
444 
445 // directory operations
446 
rt11_enum_seek(imgtool::image & image,rt11_direnum * rt11enum,uint16_t segment,uint16_t index)447 static imgtoolerr_t rt11_enum_seek(imgtool::image &image,
448 	rt11_direnum *rt11enum, uint16_t segment, uint16_t index)
449 {
450 	const rt11_diskinfo *di = get_rt11_info(image);
451 	imgtoolerr_t err;
452 	uint8_t buffer[BLOCK_SIZE];
453 
454 	if (rt11enum->segment != segment)
455 	{
456 		if (segment != 0)
457 		{
458 			err = rt11_image_readblock(image, buffer, di->directory_start + (segment - 1) * 2);
459 			if (err)
460 				return err;
461 			memcpy(rt11enum->segment_data, buffer, sizeof(buffer));
462 
463 			err = rt11_image_readblock(image, buffer, di->directory_start + (segment - 1) * 2 + 1);
464 			if (err)
465 				return err;
466 			memcpy(&rt11enum->segment_data[BLOCK_SIZE], buffer, sizeof(buffer));
467 
468 			rt11enum->data = pick_integer_le(rt11enum->segment_data, 8, 2);
469 		}
470 		rt11enum->segment = segment;
471 	}
472 
473 	rt11enum->index = index;
474 	return IMGTOOLERR_SUCCESS;
475 }
476 
rt11_get_next_dirent(imgtool::image & image,rt11_direnum * rt11enum,rt11_dirent & rt_ent)477 static imgtoolerr_t rt11_get_next_dirent(imgtool::image &image,
478 	rt11_direnum *rt11enum, rt11_dirent &rt_ent)
479 {
480 	imgtoolerr_t err;
481 	const rt11_diskinfo *di = get_rt11_info(image);
482 	uint32_t next_segment, next_index;
483 	uint32_t offset;
484 
485 	memset(&rt_ent, 0, sizeof(rt_ent));
486 
487 	/* have we hit the end of the file? */
488 	if (rt11enum->segment == 0)
489 		return IMGTOOLERR_SUCCESS;
490 
491 	/* populate the resulting dirent */
492 	offset = (rt11enum->index * di->dirent_size) + 10;
493 	rt_ent.status = pick_integer_le(rt11enum->segment_data, offset, 2);
494 	memcpy(rt_ent.filename, &rt11enum->segment_data[offset + 2], 6);
495 	rt_ent.filesize = pick_integer_le(rt11enum->segment_data, offset + 8, 2) * BLOCK_SIZE;
496 	rt_ent.data = rt11enum->data;
497 	rt_ent.time = pick_integer_le(rt11enum->segment_data, offset + 12, 2);
498 	rt11enum->data += pick_integer_le(rt11enum->segment_data, offset + 8, 2);
499 
500 	/* identify next entry */
501 	next_segment = rt11enum->segment;
502 	next_index = rt11enum->index + 1;
503 	if (next_index >= di->dirents_per_block || (rt_ent.status & E_EOS))
504 	{
505 		next_segment = pick_integer_le(rt11enum->segment_data, 2, 2);
506 		next_index = 0;
507 	}
508 
509 	if (next_segment > di->total_segments || next_segment > di->last_segment)
510 		return IMGTOOLERR_CORRUPTIMAGE;
511 
512 	/* seek next segment */
513 	err = rt11_enum_seek(image, rt11enum, next_segment, next_index);
514 	if (err)
515 		return err;
516 
517 	return IMGTOOLERR_SUCCESS;
518 }
519 
rt11_image_beginenum(imgtool::directory & enumeration,const char * path)520 static imgtoolerr_t rt11_image_beginenum(imgtool::directory &enumeration, const char *path)
521 {
522 	imgtoolerr_t err;
523 	imgtool::image &image(enumeration.image());
524 
525 	/* seek initial block */
526 	err = rt11_enum_seek(image, (rt11_direnum *) enumeration.extra_bytes(), 1, 0);
527 	if (err)
528 		return err;
529 
530 	return IMGTOOLERR_SUCCESS;
531 }
532 
rt11_image_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)533 static imgtoolerr_t rt11_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
534 {
535 	imgtoolerr_t err;
536 	imgtool::image &image(enumeration.image());
537 	rt11_direnum *rt11enum = (rt11_direnum *)enumeration.extra_bytes();
538 	rt11_dirent rt_ent;
539 
540 	do
541 	{
542 		err = rt11_get_next_dirent(image, rt11enum, rt_ent);
543 		if (err)
544 			return err;
545 	}
546 	while (rt11enum->segment && !is_file_storagetype(rt_ent.status));
547 
548 	/* end of file? */
549 	if (rt11enum->segment == 0)
550 	{
551 		ent.eof = 1;
552 		return IMGTOOLERR_SUCCESS;
553 	}
554 
555 	ent.directory          = 0;
556 	ent.lastmodified_time  = rt11_crack_time(rt_ent.time);
557 	ent.filesize           = rt_ent.filesize;
558 
559 	rt11_filename_from_rad50(ent.filename, rt_ent.filename);
560 
561 	snprintf(ent.attr, sizeof(ent.attr), "%c%c%c %4d %06o",
562 		rt_ent.status & E_PROT ? 'P' : '.',
563 		rt_ent.time == 0 ? 'B' : '.',
564 		rt_ent.status & E_TENT ? 'T' : '.',
565 		rt_ent.data, rt_ent.status);
566 
567 	return IMGTOOLERR_SUCCESS;
568 }
569 
570 // file operations
571 
rt11_lookup_path(imgtool::image & image,const char * path,creation_policy_t create,rt11_direnum * direnum,rt11_dirent * rt_ent)572 static imgtoolerr_t rt11_lookup_path(imgtool::image &image, const char *path,
573 	creation_policy_t create, rt11_direnum *direnum, rt11_dirent *rt_ent)
574 {
575 	imgtoolerr_t err;
576 
577 	rt11_direnum my_direnum;
578 	if (!direnum)
579 		direnum = &my_direnum;
580 
581 	memset(direnum, 0, sizeof(*direnum));
582 	err = rt11_enum_seek(image, direnum, 1, 0);
583 	if (err)
584 		return err;
585 
586 	uint16_t this_segment;
587 	uint32_t this_index;
588 	char filename[16];
589 	do
590 	{
591 		this_segment = direnum->segment;
592 		this_index = direnum->index;
593 
594 		err = rt11_get_next_dirent(image, direnum, *rt_ent);
595 		if (err)
596 			return err;
597 		rt11_filename_from_rad50(filename, rt_ent->filename);
598 	}
599 	while(direnum->segment && (strcmp(path, filename) ||
600 		!is_file_storagetype(rt_ent->status)));
601 
602 	if (!direnum->segment)
603 	{
604 		/* did not find file; maybe we need to create it */
605 		if (create == CREATE_NONE)
606 			return IMGTOOLERR_FILENOTFOUND;
607 	}
608 	else
609 	{
610 		/* we've found the file; seek that dirent */
611 		err = rt11_enum_seek(image, direnum, this_segment, this_index);
612 		if (err)
613 			return err;
614 	}
615 
616 	return IMGTOOLERR_SUCCESS;
617 }
618 
rt11_read_bootblock(imgtool::partition & partition,imgtool::stream & stream)619 static imgtoolerr_t rt11_read_bootblock(imgtool::partition &partition, imgtool::stream &stream)
620 {
621 	imgtoolerr_t err;
622 	uint8_t block[BLOCK_SIZE];
623 
624 	err = rt11_image_readblock(partition.image(), block, 0);
625 	if (err)
626 		return err;
627 
628 	stream.write(block, sizeof(block));
629 	return IMGTOOLERR_SUCCESS;
630 }
631 
rt11_image_readfile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & destf)632 static imgtoolerr_t rt11_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
633 {
634 	imgtoolerr_t err;
635 	imgtool::image &image(partition.image());
636 	rt11_dirent rt_ent;
637 	uint8_t buffer[BLOCK_SIZE];
638 
639 	if (filename == FILENAME_BOOTBLOCK)
640 		return rt11_read_bootblock(partition, destf);
641 
642 	err = rt11_lookup_path(image, filename, CREATE_NONE, NULL, &rt_ent);
643 	if (err)
644 		return err;
645 
646 	for (uint16_t i = rt_ent.data; i < rt_ent.data + (rt_ent.filesize / BLOCK_SIZE); i++)
647 	{
648 		err = rt11_image_readblock(image, buffer, i);
649 		if (err)
650 			return err;
651 
652 		destf.write(buffer, BLOCK_SIZE);
653 	}
654 
655 	return IMGTOOLERR_SUCCESS;
656 }
657 
658 
rt11_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)659 void rt11_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
660 {
661 	switch (state)
662 	{
663 		/* --- the following bits of info are returned as 64-bit signed integers --- */
664 		case IMGTOOLINFO_INT_PREFER_UCASE:                  info->i = 1; break;
665 		case IMGTOOLINFO_INT_OPEN_IS_STRICT:                info->i = 1; break;
666 		case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME:    info->i = 1; break;
667 		case IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK:            info->i = 1; break;
668 		case IMGTOOLINFO_INT_BLOCK_SIZE:                    info->i = BLOCK_SIZE; break;
669 		case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES:             info->i = sizeof(rt11_diskinfo); break;
670 		case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES:         info->i = sizeof(rt11_direnum); break;
671 
672 		/* --- the following bits of info are returned as NULL-terminated strings --- */
673 		case IMGTOOLINFO_STR_NAME:                          strcpy(info->s = imgtool_temp_str(), "rt11"); break;
674 		case IMGTOOLINFO_STR_DESCRIPTION:                   strcpy(info->s = imgtool_temp_str(), "RT11 format"); break;
675 		case IMGTOOLINFO_STR_FILE:                          strcpy(info->s = imgtool_temp_str(), __FILE__); break;
676 		case IMGTOOLINFO_STR_EOLN:                          strcpy(info->s = imgtool_temp_str(), EOLN_CRLF); break;
677 
678 		/* --- the following bits of info are returned as pointers to data or functions --- */
679 		case IMGTOOLINFO_PTR_INFO:                          info->info = rt11_image_info; break;
680 		case IMGTOOLINFO_PTR_MAKE_CLASS:                    info->make_class = imgtool_floppy_make_class; break;
681 		case IMGTOOLINFO_PTR_GET_GEOMETRY:                  info->get_geometry = rt11_image_get_geometry; break;
682 		case IMGTOOLINFO_PTR_READ_BLOCK:                    info->read_block = rt11_image_readblock; break;
683 		case IMGTOOLINFO_PTR_BEGIN_ENUM:                    info->begin_enum = rt11_image_beginenum; break;
684 		case IMGTOOLINFO_PTR_NEXT_ENUM:                     info->next_enum = rt11_image_nextenum; break;
685 		case IMGTOOLINFO_PTR_READ_FILE:                     info->read_file = rt11_image_readfile; break;
686 
687 		case IMGTOOLINFO_PTR_FLOPPY_OPEN:                   info->open = rt11_image_open; break;
688 		case IMGTOOLINFO_PTR_FLOPPY_FORMAT:                 info->p = (void *) floppyoptions_default; break;
689 	}
690 }
691