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 §or)
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, §ors);
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, §or_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