1 // license:BSD-3-Clause
2 // copyright-holders:Dirk Best
3 /****************************************************************************
4
5 amiga.cpp
6
7 Amiga floppies
8
9 ****************************************************************************/
10
11
12 /*****************************************************************************
13 Includes
14 *****************************************************************************/
15
16
17 #include <ctime>
18 #include <cstring>
19 #include <cctype>
20
21 #include "imgtool.h"
22 #include "iflopimg.h"
23 #include "formats/imageutl.h"
24
25
26
27 /*****************************************************************************
28 Data structures
29 *****************************************************************************/
30
31
32 #define BSIZE (512) /* Block size is always 512 bytes for floppies */
33 #define TSIZE ((BSIZE/4) - 56) /* Size of data tables */
34 #define MSIZE ((BSIZE/4) - 1) /* Size of bitmaps */
35
36
37 enum disk_type
38 {
39 DT_UNKNOWN = -1,
40 DT_OFS = 0,
41 DT_FFS = 1,
42 DT_OFS_INTL = 2,
43 DT_FFS_INTL = 3,
44 DT_OFS_INTL_DIRC = 4,
45 DT_FFS_INTL_DIRC = 5
46 };
47
48
49 enum
50 {
51 T_INVALID = 0,
52 T_HEADER = 2,
53 T_DATA = 8,
54 T_LIST = 16,
55 T_DIRCACHE = 33
56 };
57
58
59 enum sec_type
60 {
61 ST_INVALID = 0,
62 ST_ROOT = 1,
63 ST_USERDIR = 2,
64 ST_FILE = -3,
65 ST_LINKFILE = -4,
66 ST_LINKDIR = 4,
67 ST_SOFTLINK = 3
68 };
69
70
71 struct amiga_date
72 {
73 uint32_t days; /* days since 1 jan 78 */
74 uint32_t mins; /* minutes past midnight */
75 uint32_t ticks; /* ticks (1/50 sec) past last minute */
76 };
77
78
79 struct root_block
80 {
81 uint32_t ht_size; /* Hash table size in long */
82 uint32_t chksum; /* Rootblock checksum */
83 uint32_t ht[TSIZE]; /* Hash table (entry block number) */
84 uint32_t bm_flag; /* bitmap flag, -1 means VALID */
85 uint32_t bm_pages[25]; /* bitmap blocks pointers (first one at bm_pages[0]) */
86 amiga_date r; /* last root alteration date */
87 uint8_t name_len; /* volume name length */
88 uint8_t diskname[30]; /* volume name */
89 amiga_date v; /* last disk alteration date */
90 amiga_date c; /* filesystem creation date */
91 uint32_t extension; /* FFS: first directory cache block, 0 otherwise */
92 };
93
94
95 struct bitmap_block
96 {
97 uint32_t chksum; /* checksum, normal algorithm */
98 uint32_t map[MSIZE]; /* bitmap */
99 };
100
101
102 struct bitmap_ext_block
103 {
104 uint32_t map[MSIZE]; /* bitmap */
105 uint32_t next; /* next extension block */
106 };
107
108
109 struct file_block
110 {
111 uint32_t header_key; /* self pointer (to this block) */
112 uint32_t high_seq; /* number of data block ptr stored here */
113 uint32_t first_data; /* first data block ptr */
114 uint32_t chksum; /* same algorithm as rootblock */
115 uint32_t data_blocks[TSIZE]; /* data blk ptr */
116 uint16_t uid; /* UserID */
117 uint16_t gid; /* GroupID */
118 uint32_t protect; /* protection flags (0 by default) */
119 uint32_t byte_size; /* file size in bytes */
120 uint8_t comm_len; /* file comment length */
121 uint8_t comment[79]; /* comment (max. 79 chars permitted) */
122 amiga_date date; /* last change date */
123 uint8_t name_len; /* filename length */
124 uint8_t filename[30]; /* filename (max. 30 chars permitted) */
125 uint32_t real_entry; /* FFS: unused, set to 0 */
126 uint32_t next_link; /* FFS: hardlinks chained list (first == newest */
127 uint32_t hash_chain; /* next entry ptr with same hash */
128 uint32_t parent; /* parent directory */
129 uint32_t extension; /* pointer to 1st file extension block */
130 };
131
132
133 struct file_ext_block
134 {
135 uint32_t header_key; /* self pointer (to this block) */
136 uint32_t high_seq; /* number of data block ptr stored here */
137 uint32_t chksum; /* same algorithm as rootblock */
138 uint32_t data_blocks[TSIZE]; /* data blk ptr */
139 uint32_t parent; /* file header block */
140 uint32_t extension; /* pointer to next file extension block */
141 };
142
143
144 struct data_block
145 {
146 uint32_t header_key; /* self pointer (to this block) */
147 uint32_t seq_num; /* file data block number */
148 uint32_t data_size; /* data size */
149 uint32_t next_data; /* next data block ptr */
150 uint32_t chksum; /* checksum, rootblock algorithm */
151 uint8_t data[BSIZE-24]; /* file data */
152 };
153
154
155 struct dir_block
156 {
157 uint32_t header_key; /* self pointer (to this block) */
158 uint32_t chksum; /* same algorithm as rootblock */
159 uint32_t ht[TSIZE]; /* hash table (entry block number) */
160 uint8_t uid; /* UserID */
161 uint8_t gid; /* GroupID */
162 uint32_t protect; /* protection flags (0 by default) */
163 uint8_t comm_len; /* file comment length */
164 uint8_t comment[79]; /* comment (max. 79 chars permitted) */
165 amiga_date date; /* last access date */
166 uint8_t name_len; /* directory name length */
167 uint8_t dirname[30]; /* directory name (max. 30 chars permitted) */
168 uint32_t next_link; /* FFS: hardlinks chained list (first == newest */
169 uint32_t hash_chain; /* next entry ptr with same hash */
170 uint32_t parent; /* parent directory */
171 uint32_t extension; /* FFS: first directory cache block */
172 };
173
174
175 struct hardlink_block
176 {
177 uint32_t header_key; /* self pointer (to this block) */
178 uint32_t chksum; /* same algorithm as rootblock */
179 uint32_t protect; /* protection flags (0 by default) */
180 uint8_t comm_len; /* file comment length */
181 uint8_t comment[79]; /* comment (max. 79 chars permitted) */
182 amiga_date date; /* last access date */
183 uint8_t name_len; /* hard link name length */
184 uint8_t hlname[30]; /* hard link name (max. 30 chars permitted) */
185 uint32_t real_entry; /* FFS: pointer to "real" file or directory */
186 uint32_t next_link; /* FFS: hardlinks chained list (first == newest */
187 uint32_t hash_chain; /* next entry ptr with same hash */
188 uint32_t parent; /* parent directory */
189 uint32_t sec_type; /* secondary type, ST_LINKFILE/ST_LINKDIR */
190 };
191
192
193 struct softlink_block
194 {
195 uint32_t header_key; /* self pointer (to this block) */
196 uint32_t chksum; /* same algorithm as rootblock */
197 uint8_t symbolic_name[BSIZE-224]; /* path name to referenced object */
198 uint32_t protect; /* protection flags (0 by default) */
199 uint8_t comm_len; /* file comment length */
200 uint8_t comment[79]; /* comment (max. 79 chars permitted) */
201 amiga_date date; /* last access date */
202 uint8_t name_len; /* soft link name length */
203 uint8_t slname[30]; /* soft link name (max. 30 chars permitted) */
204 uint32_t hash_chain; /* next entry ptr with same hash */
205 uint32_t parent; /* parent directory */
206 };
207
208
209 /* Basic Amiga floppy disk image info */
210 struct amiga_floppy
211 {
212 imgtool::stream *stream;
213 uint8_t sectors;
214 };
215
216
217 /* iterator used to walk through directory entries */
218 struct amiga_iterator
219 {
220 unsigned int index; /* current file index */
221 int block; /* block number we are iterating */
222 uint32_t next_block; /* next block in hash chain */
223 int ht_index; /* current index in the hash table */
224 unsigned int eof : 1; /* end of file listing reached? */
225 };
226
227
228 /*****************************************************************************
229 Prototypes
230 *****************************************************************************/
231
232
233 static imgtoolerr_t amiga_image_read_sector(imgtool::image &img,
234 uint32_t track, uint32_t head, uint32_t sector, void *buf, size_t len);
235 static imgtoolerr_t amiga_image_read_sector(imgtool::image &img,
236 uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer);
237 static imgtoolerr_t amiga_image_write_sector(imgtool::image &img,
238 uint32_t track, uint32_t head, uint32_t sector, const void *buf, size_t len, int ddam);
239
240
241
242 /*****************************************************************************
243 Utility functions
244 *****************************************************************************/
245
246
247 /* Amiga version of the toupper function */
intl_toupper(int c)248 static int intl_toupper(int c)
249 {
250 return (c>='a' && c<='z') || (c>=224 && c<=254 && c!=247) ? c - ('a'-'A') : c ;
251 }
252
253
254 /* Amiga filename case insensitive string compare */
intl_stricmp(const char * s1,const char * s2)255 static int intl_stricmp(const char *s1, const char *s2)
256 {
257 for (;;)
258 {
259 int c1 = intl_toupper(*s1++);
260 int c2 = intl_toupper(*s2++);
261
262 if (c1 == 0 || c1 != c2)
263 return c1 - c2;
264 }
265 }
266
267
268 /* Calculate the hash value for a filename */
hash_name(const char * name,int intl)269 static int hash_name(const char *name, int intl)
270 {
271 int i, l = strlen(name);
272 uint32_t hash = l;
273
274 for(i = 0; i < l; i++)
275 {
276 hash *= 13;
277 hash += (uint8_t) (intl ? intl_toupper(name[i]) : toupper(name[i]));
278 hash &= 0x7ff;
279 }
280
281 return hash % TSIZE; /* 0 < hash < 71 in case of BSIZE=512 */
282 }
283
284
285 /* Returns true if year is a leap year */
is_leap(int year)286 static int is_leap(int year)
287 {
288 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
289 }
290
291
292 /* Convert amiga time to standard time */
amiga_crack_time(amiga_date * date)293 static imgtool::datetime amiga_crack_time(amiga_date *date)
294 {
295 int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
296 int year = 1978, month = 1, year_days = 365; /* base date */
297 int day = date->days;
298
299 /* first calculate the year */
300 while (day >= year_days)
301 {
302 day -= year_days;
303 year_days = is_leap(++year) ? 366 : 365;
304 }
305
306 /* then the month */
307 while(day >= month_days[month-1])
308 {
309 day -= month_days[month-1];
310 if (month == 2 && is_leap(year))
311 day -= 1;
312 month++;
313 }
314
315 // fill the struct with our calculated values
316 util::arbitrary_datetime dt;
317 dt.year = year;
318 dt.month = month;
319 dt.day_of_month = day;
320 dt.hour = date->mins / 60;
321 dt.minute = date->mins % 60;
322 dt.second = date->ticks / 50;
323
324 return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt);
325 }
326
327
328 /* convert standard time to amiga time */
amiga_setup_time(time_t time,amiga_date * dest)329 static void amiga_setup_time(time_t time, amiga_date *dest)
330 {
331 struct tm t = *localtime(&time);
332 int year;
333
334 dest->days = 0;
335
336 for (year = 1978; year < t.tm_year + 1900; year++)
337 {
338 dest->days += is_leap(year) ? 366 : 365;
339 }
340
341 dest->days += t.tm_yday;
342 dest->mins = t.tm_hour * 60 + t.tm_min;
343 dest->ticks = t.tm_sec * 50;
344 }
345
346
347 /* convert flags to human readable form */
amiga_decode_flags(uint32_t flags,char * dest)348 static void amiga_decode_flags(uint32_t flags, char *dest)
349 {
350 /* test for flags */
351 dest[0] = (flags & 0x80) ? 'h' : '-';
352 dest[1] = (flags & 0x40) ? 's' : '-';
353 dest[2] = (flags & 0x20) ? 'p' : '-';
354 dest[3] = (flags & 0x10) ? 'a' : '-';
355 dest[4] = (flags & 0x08) ? '-' : 'r';
356 dest[5] = (flags & 0x04) ? '-' : 'w';
357 dest[6] = (flags & 0x02) ? '-' : 'e';
358 dest[7] = (flags & 0x01) ? '-' : 'd';
359 dest[8] = '\0';
360 }
361
362
copy_integer_array_be(uint32_t * dest,const uint32_t * source,int size)363 static void copy_integer_array_be(uint32_t *dest, const uint32_t *source, int size)
364 {
365 int i;
366
367 for (i = 0; i < size; i++)
368 {
369 dest[i] = big_endianize_int32(source[i]);
370 }
371 }
372
373
374 /* This function converts an array of UINT32s to an amiga_date */
copy_date_be(amiga_date * dest,const uint32_t * source)375 static void copy_date_be(amiga_date *dest, const uint32_t *source)
376 {
377 dest->days = big_endianize_int32(source[0]);
378 dest->mins = big_endianize_int32(source[1]);
379 dest->ticks = big_endianize_int32(source[2]);
380 }
381
382
383 /* Calculate the block checksum of a byte array */
block_checksum(uint8_t * buffer,int length)384 static uint32_t block_checksum(uint8_t *buffer, int length)
385 {
386 uint32_t chksum = 0;
387 int i;
388
389 for (i = 0; i < length/4; i++)
390 {
391 chksum += pick_integer_be(buffer, i*4, 4);
392 }
393
394 return -chksum;
395 }
396
397
398 /* Get Amiga floppy data */
get_amiga_floppy(imgtool::image & image)399 static amiga_floppy *get_amiga_floppy(imgtool::image &image)
400 {
401 return (amiga_floppy *)image.extra_bytes();
402 }
403
404
405 /* Returns the total number of blocks in the image */
get_total_blocks(imgtool::image & img)406 static int get_total_blocks(imgtool::image &img)
407 {
408 amiga_floppy *f = get_amiga_floppy(img);
409
410 return 2 * 80 * f->sectors;
411 }
412
413
414 /* Returns track, head and sector for a block */
find_block(amiga_floppy * f,int block,int * track,int * head,int * sector)415 static void find_block(amiga_floppy *f, int block, int *track,
416 int *head, int *sector)
417 {
418 *track = block / f->sectors;
419 *head = (block - *track * f->sectors) / f->sectors;
420 *sector = (block - *track * f->sectors) % f->sectors;
421 }
422
423
424 /* Generic read block */
read_block(imgtool::image & img,int block,uint8_t * buffer)425 static imgtoolerr_t read_block(imgtool::image &img, int block, uint8_t *buffer)
426 {
427 imgtoolerr_t ret;
428 int track, head, sector;
429
430 find_block(get_amiga_floppy(img), block, &track, &head, §or);
431
432 /* get block from image */
433 ret = amiga_image_read_sector(img, track, head, sector, buffer, BSIZE);
434 if (ret) return ret;
435
436 return IMGTOOLERR_SUCCESS;
437 }
438
439
440 /* Generic write block */
write_block(imgtool::image & img,int block,const uint8_t * buffer)441 static imgtoolerr_t write_block(imgtool::image &img, int block, const uint8_t *buffer)
442 {
443 imgtoolerr_t ret;
444 int track, head, sector;
445
446 find_block(get_amiga_floppy(img), block, &track, &head, §or);
447
448 /* write block to image */
449 ret = amiga_image_write_sector(img, track, head, sector, buffer, BSIZE, 0);
450 if (ret) return ret;
451
452 return IMGTOOLERR_SUCCESS;
453 }
454
455
456 /* Return the type a block */
get_block_type(imgtool::image & img,int block)457 static sec_type get_block_type(imgtool::image &img, int block)
458 {
459 imgtoolerr_t ret;
460 uint8_t buffer[BSIZE];
461
462 /* get data */
463 ret = read_block(img, block, buffer);
464 if (ret) return ST_INVALID;
465
466 /* return type */
467 switch ((int32_t) pick_integer_be(buffer, BSIZE-4, 4))
468 {
469 case 1: return ST_ROOT;
470 case 2: return ST_USERDIR;
471 case -3: return ST_FILE;
472 case -4: return ST_LINKFILE;
473 case 4: return ST_LINKDIR;
474 case 3: return ST_SOFTLINK;
475 default: return ST_INVALID;
476 }
477 }
478
479
480 /* Read a bitmap block */
read_bitmap_block(imgtool::image & img,int block,bitmap_block * bm)481 static imgtoolerr_t read_bitmap_block(imgtool::image &img, int block, bitmap_block *bm)
482 {
483 imgtoolerr_t ret;
484 uint8_t buffer[BSIZE];
485
486 /* read block */
487 ret = read_block(img, block, buffer);
488 if (ret) return ret;
489
490 /* fill in data */
491 bm->chksum = pick_integer_be(buffer, 0, 4);
492 copy_integer_array_be(bm->map, (uint32_t *) &buffer[4], MSIZE);
493
494 return IMGTOOLERR_SUCCESS;
495 }
496
497
498 /* Write a bitmap block */
write_bitmap_block(imgtool::image & img,int block,const bitmap_block * bm)499 static imgtoolerr_t write_bitmap_block(imgtool::image &img, int block, const bitmap_block *bm)
500 {
501 imgtoolerr_t ret;
502 uint8_t buffer[BSIZE];
503
504 /* Setup buffer */
505 place_integer_be(buffer, 0, 4, bm->chksum);
506 copy_integer_array_be((uint32_t *) &buffer[4], bm->map, MSIZE);
507
508 /* write block */
509 ret = write_block(img, block, buffer);
510 if (ret) return ret;
511
512 return IMGTOOLERR_SUCCESS;
513 }
514
515
516 #ifdef UNUSED_FUNCTION
517 /* Read a bitmap extended block */
read_bitmap_ext_block(imgtool::image * img,int block,bitmap_ext_block * bm)518 static imgtoolerr_t read_bitmap_ext_block(imgtool::image *img, int block, bitmap_ext_block *bm)
519 {
520 imgtoolerr_t ret;
521 uint8_t buffer[BSIZE];
522
523 /* read block */
524 ret = read_block(img, block, buffer);
525 if (ret) return ret;
526
527 /* fill in data */
528 copy_integer_array_be(bm->map, (uint32_t *) &buffer, MSIZE);
529 bm->next = pick_integer_be(buffer, BSIZE-4, 4);
530
531 return IMGTOOLERR_SUCCESS;
532 }
533 #endif
534
535
536 /* Read the root block */
read_root_block(imgtool::image & img,root_block * root)537 static imgtoolerr_t read_root_block(imgtool::image &img, root_block *root)
538 {
539 imgtoolerr_t ret;
540 uint8_t buffer[BSIZE];
541
542 /* get raw root block from image */
543 ret = read_block(img, get_total_blocks(img)/2, buffer);
544 if (ret) return ret;
545
546 /* copy data to root_block */
547 memset(root, 0, sizeof(root_block));
548
549 root->ht_size = pick_integer_be(buffer, 12, 4);
550 root->chksum = pick_integer_be(buffer, 20, 4);
551 copy_integer_array_be(root->ht, (uint32_t *) &buffer[24], TSIZE);
552 root->bm_flag = pick_integer_be(buffer, BSIZE-200, 4);
553 copy_integer_array_be(root->bm_pages, (uint32_t *) &buffer[BSIZE-196], 25);
554 copy_date_be(&root->r, (uint32_t *) &buffer[BSIZE-92]);
555 root->name_len = pick_integer_be(buffer, BSIZE-80, 1);
556 memcpy(root->diskname, &buffer[BSIZE-79], 30);
557 copy_date_be(&root->v, (uint32_t *) &buffer[BSIZE-40]);
558 copy_date_be(&root->c, (uint32_t *) &buffer[BSIZE-28]);
559 root->extension = pick_integer_be(buffer, BSIZE-8, 4);
560
561 return IMGTOOLERR_SUCCESS;
562 }
563
564
write_root_block(imgtool::image & img,const root_block * root)565 static imgtoolerr_t write_root_block(imgtool::image &img, const root_block *root)
566 {
567 imgtoolerr_t ret;
568 uint8_t buffer[BSIZE];
569
570 /* Setup buffer */
571 memset(buffer, 0, BSIZE);
572
573 place_integer_be(buffer, 0, 4, T_HEADER);
574 place_integer_be(buffer, 12, 4, root->ht_size);
575 place_integer_be(buffer, 20, 4, root->chksum);
576 copy_integer_array_be((uint32_t *) &buffer[24], root->ht, TSIZE);
577 place_integer_be(buffer, BSIZE-200, 4, root->bm_flag);
578 copy_integer_array_be((uint32_t *) &buffer[BSIZE-196], root->bm_pages, 25);
579 place_integer_be(buffer, BSIZE-92, 4, root->r.days);
580 place_integer_be(buffer, BSIZE-88, 4, root->r.mins);
581 place_integer_be(buffer, BSIZE-84, 4, root->r.ticks);
582 place_integer_be(buffer, BSIZE-80, 1, root->name_len);
583 memcpy(&buffer[BSIZE-79], root->diskname, root->name_len);
584 place_integer_be(buffer, BSIZE-40, 4, root->v.days);
585 place_integer_be(buffer, BSIZE-36, 4, root->v.mins);
586 place_integer_be(buffer, BSIZE-32, 4, root->v.ticks);
587 place_integer_be(buffer, BSIZE-28, 4, root->c.days);
588 place_integer_be(buffer, BSIZE-24, 4, root->c.mins);
589 place_integer_be(buffer, BSIZE-20, 4, root->c.ticks);
590 place_integer_be(buffer, BSIZE-8, 4, root->extension);
591 place_integer_be(buffer, BSIZE-4, 4, ST_ROOT);
592
593 /* write root block to image */
594 ret = write_block(img, get_total_blocks(img)/2, buffer);
595 if (ret) return ret;
596
597 return IMGTOOLERR_SUCCESS;
598 }
599
600
601 /* Read a file block */
read_file_block(imgtool::image & img,int block,file_block * fb)602 static imgtoolerr_t read_file_block(imgtool::image &img, int block, file_block *fb)
603 {
604 imgtoolerr_t ret;
605 uint8_t buffer[BSIZE];
606
607 /* read block */
608 ret = read_block(img, block, buffer);
609 if (ret) return ret;
610
611 /* fill in data */
612 fb->header_key = pick_integer_be(buffer, 4, 4);
613 fb->high_seq = pick_integer_be(buffer, 8, 4);
614 fb->first_data = pick_integer_be(buffer, 16, 4);
615 fb->chksum = pick_integer_be(buffer, 20, 4);
616 copy_integer_array_be(fb->data_blocks, (uint32_t *) &buffer[24], TSIZE);
617 fb->uid = pick_integer_be(buffer, BSIZE-196, 2);
618 fb->gid = pick_integer_be(buffer, BSIZE-194, 2);
619 fb->protect = pick_integer_be(buffer, BSIZE-192, 4);
620 fb->byte_size = pick_integer_be(buffer, BSIZE-188, 4);
621 fb->comm_len = pick_integer_be(buffer, BSIZE-184, 1);
622 memcpy(fb->comment, &buffer[BSIZE-183], 79);
623 copy_date_be(&fb->date, (uint32_t *) &buffer[BSIZE-92]);
624 fb->name_len = pick_integer_be(buffer, BSIZE-80, 1);
625 memcpy(fb->filename, (uint32_t *) &buffer[BSIZE-79], 30);
626 fb->real_entry = pick_integer_be(buffer, BSIZE-44, 4);
627 fb->next_link = pick_integer_be(buffer, BSIZE-40, 4);
628 fb->hash_chain = pick_integer_be(buffer, BSIZE-16, 4);
629 fb->parent = pick_integer_be(buffer, BSIZE-12, 4);
630 fb->extension = pick_integer_be(buffer, BSIZE-8, 4);
631
632 return IMGTOOLERR_SUCCESS;
633 }
634
635
read_file_ext_block(imgtool::image & img,int block,file_ext_block * fe)636 static imgtoolerr_t read_file_ext_block(imgtool::image &img, int block, file_ext_block *fe)
637 {
638 imgtoolerr_t ret;
639 uint8_t buffer[BSIZE];
640
641 /* read block */
642 ret = read_block(img, block, buffer);
643 if (ret) return ret;
644
645 /* fill in data */
646 fe->header_key = pick_integer_be(buffer, 4, 4);
647 fe->high_seq = pick_integer_be(buffer, 8, 4);
648 fe->chksum = pick_integer_be(buffer, 20, 4);
649 copy_integer_array_be(fe->data_blocks, (uint32_t *) &buffer[24], TSIZE);
650 fe->parent = pick_integer_be(buffer, BSIZE-12, 4);
651 fe->extension = pick_integer_be(buffer, BSIZE-8, 4);
652
653 return IMGTOOLERR_SUCCESS;
654 }
655
656
read_data_block(imgtool::image & img,int block,data_block * d)657 static imgtoolerr_t read_data_block(imgtool::image &img, int block, data_block *d)
658 {
659 imgtoolerr_t ret;
660 uint8_t buffer[BSIZE];
661
662 /* read block */
663 ret = read_block(img, block, buffer);
664 if (ret) return ret;
665
666 /* fill in data */
667 d->header_key = pick_integer_be(buffer, 4, 4);
668 d->seq_num = pick_integer_be(buffer, 8, 4);
669 d->data_size = pick_integer_be(buffer, 12, 4);
670 d->next_data = pick_integer_be(buffer, 16, 4);
671 d->chksum = pick_integer_be(buffer, 20, 4);
672 memcpy(d->data, &buffer[24], BSIZE-24);
673
674 return IMGTOOLERR_SUCCESS;
675 }
676
677
678 /* Read a directory block */
read_dir_block(imgtool::image & img,int block,dir_block * db)679 static imgtoolerr_t read_dir_block(imgtool::image &img, int block, dir_block *db)
680 {
681 imgtoolerr_t ret;
682 uint8_t buffer[BSIZE];
683
684 /* read block */
685 ret = read_block(img, block, buffer);
686 if (ret) return ret;
687
688 /* fill in data */
689 db->header_key = pick_integer_be(buffer, 4, 4);
690 db->chksum = pick_integer_be(buffer, 20, 4);
691 copy_integer_array_be(db->ht, (uint32_t *) &buffer[24], TSIZE);
692 db->uid = pick_integer_be(buffer, BSIZE-196, 2);
693 db->gid = pick_integer_be(buffer, BSIZE-194, 2);
694 db->protect = pick_integer_be(buffer, BSIZE-192, 4);
695 db->comm_len = pick_integer_be(buffer, BSIZE-184, 1);
696 memcpy(db->comment, &buffer[BSIZE-183], 79);
697 copy_date_be(&db->date, (uint32_t *) &buffer[BSIZE-92]);
698 db->name_len = pick_integer_be(buffer, BSIZE-80, 1);
699 memcpy(db->dirname, (uint32_t *) &buffer[BSIZE-79], 30);
700 db->next_link = pick_integer_be(buffer, BSIZE-40, 4);
701 db->hash_chain = pick_integer_be(buffer, BSIZE-16, 4);
702 db->parent = pick_integer_be(buffer, BSIZE-12, 4);
703 db->extension = pick_integer_be(buffer, BSIZE-8, 4);
704
705 return IMGTOOLERR_SUCCESS;
706 }
707
708
write_dir_block(imgtool::image & img,int block,const dir_block * db)709 static imgtoolerr_t write_dir_block(imgtool::image &img, int block, const dir_block *db)
710 {
711 uint8_t buffer[BSIZE];
712
713 /* Setup buffer */
714 memset(buffer, 0, BSIZE);
715
716 /* Copy data */
717 place_integer_be(buffer, 0, 4, T_HEADER);
718 place_integer_be(buffer, 4, 4, db->header_key);
719 place_integer_be(buffer, 20, 4, db->chksum);
720 copy_integer_array_be((uint32_t *) &buffer[24], db->ht, TSIZE);
721 place_integer_be(buffer, BSIZE-196, 2, db->uid);
722 place_integer_be(buffer, BSIZE-194, 2, db->gid);
723 place_integer_be(buffer, BSIZE-192, 4, db->protect);
724 place_integer_be(buffer, BSIZE-184, 1, db->comm_len);
725 memcpy((uint32_t *) &buffer[BSIZE-183], db->comment, db->comm_len);
726 place_integer_be(buffer, BSIZE-92, 4, db->date.days);
727 place_integer_be(buffer, BSIZE-88, 4, db->date.mins);
728 place_integer_be(buffer, BSIZE-84, 4, db->date.ticks);
729 place_integer_be(buffer, BSIZE-80, 1, db->name_len);
730 memcpy((uint32_t *) &buffer[BSIZE-79], db->dirname, db->name_len);
731 place_integer_be(buffer, BSIZE-40, 4, db->next_link);
732 place_integer_be(buffer, BSIZE-16, 4, db->hash_chain);
733 place_integer_be(buffer, BSIZE-12, 4, db->parent);
734 place_integer_be(buffer, BSIZE-8, 4, db->extension);
735 place_integer_be(buffer, BSIZE-4, 4, ST_USERDIR);
736
737 /* Write block to disk */
738 return write_block(img, block, buffer);
739 }
740
741
read_hardlink_block(imgtool::image & img,int block,hardlink_block * hl)742 static imgtoolerr_t read_hardlink_block(imgtool::image &img, int block, hardlink_block *hl)
743 {
744 imgtoolerr_t ret;
745 uint8_t buffer[BSIZE];
746
747 /* read block */
748 ret = read_block(img, block, buffer);
749 if (ret) return ret;
750
751 /* fill in data */
752 hl->header_key = pick_integer_be(buffer, 4, 4);
753 hl->chksum = pick_integer_be(buffer, 20, 4);
754 hl->protect = pick_integer_be(buffer, BSIZE-192, 4);
755 hl->comm_len = pick_integer_be(buffer, BSIZE-184, 1);
756 memcpy(hl->comment, &buffer[BSIZE-183], 79);
757 copy_date_be(&hl->date, (uint32_t *) &buffer[BSIZE-92]);
758 hl->name_len = pick_integer_be(buffer, BSIZE-80, 1);
759 memcpy(hl->hlname, (uint32_t *) &buffer[BSIZE-79], 30);
760 hl->real_entry = pick_integer_be(buffer, BSIZE-44, 4);
761 hl->next_link = pick_integer_be(buffer, BSIZE-40, 4);
762 hl->hash_chain = pick_integer_be(buffer, BSIZE-16, 4);
763 hl->parent = pick_integer_be(buffer, BSIZE-12, 4);
764 hl->sec_type = pick_integer_be(buffer, BSIZE-4, 4);
765
766 return IMGTOOLERR_SUCCESS;
767 }
768
769
read_softlink_block(imgtool::image & img,int block,softlink_block * sl)770 static imgtoolerr_t read_softlink_block(imgtool::image &img, int block, softlink_block *sl)
771 {
772 imgtoolerr_t ret;
773 uint8_t buffer[BSIZE];
774
775 /* read block */
776 ret = read_block(img, block, buffer);
777 if (ret) return ret;
778
779 /* fill in data */
780 sl->header_key = pick_integer_be(buffer, 4, 4);
781 sl->chksum = pick_integer_be(buffer, 20, 4);
782 memcpy(sl->symbolic_name, &buffer[24], BSIZE-224);
783 sl->protect = pick_integer_be(buffer, BSIZE-192, 4);
784 sl->comm_len = pick_integer_be(buffer, BSIZE-184, 1);
785 memcpy(sl->comment, &buffer[BSIZE-183], 79);
786 copy_date_be(&sl->date, (uint32_t *) &buffer[BSIZE-92]);
787 sl->name_len = pick_integer_be(buffer, BSIZE-80, 1);
788 memcpy(sl->slname, (uint32_t *) &buffer[BSIZE-79], 30);
789 sl->hash_chain = pick_integer_be(buffer, BSIZE-16, 4);
790 sl->parent = pick_integer_be(buffer, BSIZE-12, 4);
791
792 return IMGTOOLERR_SUCCESS;
793 }
794
795
796 /* Returns the disk type */
get_disk_type(imgtool::image & img)797 static disk_type get_disk_type(imgtool::image &img)
798 {
799 imgtoolerr_t ret;
800 uint8_t buffer[BSIZE];
801
802 ret = read_block(img, 0, buffer);
803 if (ret) return DT_UNKNOWN;
804
805 switch(buffer[3])
806 {
807 case 0: return DT_OFS;
808 case 1: return DT_FFS;
809 case 2: return DT_OFS_INTL;
810 case 3: return DT_FFS_INTL;
811 case 4: return DT_OFS_INTL_DIRC;
812 case 5: return DT_FFS_INTL_DIRC;
813 default: return DT_UNKNOWN;
814 }
815 }
816
817
818 /* Returns true if the disk is formatted with the FastFileSystem */
is_ffs(imgtool::image & img)819 static int is_ffs(imgtool::image &img)
820 {
821 disk_type t = get_disk_type(img);
822
823 return ((t == DT_FFS ||
824 t == DT_FFS_INTL ||
825 t == DT_FFS_INTL_DIRC) ? true : false);
826 }
827
828
829 /* Returns true if the disk uses the international mode */
is_intl(imgtool::image & img)830 static int is_intl(imgtool::image &img)
831 {
832 disk_type t = get_disk_type(img);
833
834 return ((t == DT_OFS_INTL ||
835 t == DT_FFS_INTL ||
836 t == DT_OFS_INTL_DIRC ||
837 t == DT_FFS_INTL_DIRC) ? true : false);
838 }
839
840 #ifdef UNUSED_FUNCTION
841 /* Returns true if the disk uses the directory cache mode */
is_dirc(imgtool::image * img)842 static int is_dirc(imgtool::image *img)
843 {
844 disk_type t = get_disk_type(img);
845
846 return ((t == DT_OFS_INTL_DIRC ||
847 t == DT_FFS_INTL_DIRC) ? true : false);
848 }
849 #endif
850
get_hash_table(imgtool::image & img,int block,uint32_t * ht)851 static imgtoolerr_t get_hash_table(imgtool::image &img, int block, uint32_t *ht)
852 {
853 imgtoolerr_t ret;
854
855 switch (get_block_type(img, block))
856 {
857 case ST_USERDIR:
858 {
859 dir_block dir;
860
861 /* get the directory block */
862 ret = read_dir_block(img, block, &dir);
863 if (ret) return ret;
864
865 /* copy data */
866 memcpy(ht, &dir.ht, TSIZE*4);
867
868 return IMGTOOLERR_SUCCESS;
869 }
870
871 case ST_ROOT:
872 {
873 root_block root;
874
875 /* get the root block */
876 ret = read_root_block(img, &root);
877 if (ret) return ret;
878
879 /* copy data */
880 memcpy(ht, &root.ht, TSIZE*4);
881
882 return IMGTOOLERR_SUCCESS;
883 }
884
885 default:
886 return IMGTOOLERR_UNEXPECTED;
887 }
888 }
889
890
set_hash_table(imgtool::image & img,int block,const uint32_t * ht)891 static imgtoolerr_t set_hash_table(imgtool::image &img, int block, const uint32_t *ht)
892 {
893 uint8_t buffer[BSIZE];
894 imgtoolerr_t ret;
895
896 /* Read block */
897 ret = read_block(img, block, buffer);
898 if (ret) return ret;
899
900 /* Copy new hash table into it */
901 copy_integer_array_be((uint32_t *) &buffer[24], ht, TSIZE);
902
903 /* Write it back again */
904 ret = write_block(img, block, buffer);
905 if (ret) return ret;
906
907 return IMGTOOLERR_SUCCESS;
908 }
909
910 #ifdef UNUSED_FUNCTION
get_root_hash_table(imgtool::image * img,uint32_t * ht)911 static imgtoolerr_t get_root_hash_table(imgtool::image *img, uint32_t *ht)
912 {
913 return get_hash_table(img, get_total_blocks(img)/2, ht);
914 }
915 #endif
916
get_blockname(imgtool::image & img,int block,char * dest)917 static imgtoolerr_t get_blockname(imgtool::image &img, int block, char *dest)
918 {
919 uint8_t buffer[BSIZE];
920 imgtoolerr_t ret;
921
922 /* Read the block */
923 ret = read_block(img, block, buffer);
924 if (ret) return ret;
925
926 /* Copy filename out of the buffer */
927 memset(dest, 0, 31);
928 memcpy(dest, &buffer[BSIZE-79], buffer[BSIZE-80]);
929
930 return IMGTOOLERR_SUCCESS;
931 }
932
933
get_hash_chain(imgtool::image & img,int block,uint32_t * chain)934 static imgtoolerr_t get_hash_chain(imgtool::image &img, int block, uint32_t *chain)
935 {
936 uint8_t buffer[BSIZE];
937 imgtoolerr_t ret;
938
939 /* Read block */
940 ret = read_block(img, block, buffer);
941 if (ret) return ret;
942
943 /* Get chain value */
944 *chain = pick_integer_be(buffer, BSIZE-16, 4);
945
946 return IMGTOOLERR_SUCCESS;
947 }
948
949
set_hash_chain(imgtool::image & img,int block,uint32_t chain)950 static imgtoolerr_t set_hash_chain(imgtool::image &img, int block, uint32_t chain)
951 {
952 uint8_t buffer[BSIZE];
953 imgtoolerr_t ret;
954
955 /* Read block */
956 ret = read_block(img, block, buffer);
957 if (ret) return ret;
958
959 /* Copy new hash chain value into it */
960 place_integer_be(buffer, BSIZE-16, 4, chain);
961
962 /* Write it back again */
963 ret = write_block(img, block, buffer);
964 if (ret) return ret;
965
966 return IMGTOOLERR_SUCCESS;
967 }
968
969
walk_hash_chain(imgtool::image & img,const char * path,int start_block,int * prev_block,int * block)970 static imgtoolerr_t walk_hash_chain(imgtool::image &img, const char *path, int start_block, int *prev_block, int *block)
971 {
972 imgtoolerr_t err;
973 uint32_t hash_chain;
974 char name[31];
975
976 /* choose compare function depending on intl mode */
977 int (*cmp)(const char *, const char *) = is_intl(img) ? &intl_stricmp : &core_stricmp;
978
979 /* initialize filenames */
980 memset(name, 0, sizeof(name));
981
982 switch (get_block_type(img, start_block))
983 {
984 case ST_USERDIR:
985 {
986 dir_block dir;
987
988 /* read block */
989 err = read_dir_block(img, start_block, &dir);
990 if (err) return err;
991
992 /* copy filename string and next hash */
993 memcpy(name, dir.dirname, dir.name_len);
994 hash_chain = dir.hash_chain;
995
996 break;
997 }
998
999 case ST_FILE:
1000 {
1001 file_block file;
1002
1003 /* read block */
1004 err = read_file_block(img, start_block, &file);
1005 if (err) return err;
1006
1007 /* copy filename string and next hash */
1008 memcpy(name, file.filename, file.name_len);
1009 hash_chain = file.hash_chain;
1010
1011 break;
1012 }
1013
1014 case ST_SOFTLINK:
1015 {
1016 softlink_block sl;
1017
1018 /* read block */
1019 err = read_softlink_block(img, start_block, &sl);
1020 if (err) return err;
1021
1022 /* copy filename string and next hash */
1023 memcpy(name, sl.slname, sl.name_len);
1024 hash_chain = sl.hash_chain;
1025
1026 break;
1027 }
1028
1029 case ST_LINKDIR:
1030 case ST_LINKFILE:
1031 {
1032 hardlink_block hl;
1033
1034 /* read block */
1035 err = read_hardlink_block(img, start_block, &hl);
1036 if (err) return err;
1037
1038 /* copy filename string and next hash */
1039 memcpy(name, hl.hlname, hl.name_len);
1040 hash_chain = hl.hash_chain;
1041 break;
1042 }
1043
1044 default:
1045 return IMGTOOLERR_UNEXPECTED;
1046
1047 }
1048
1049 /* if we haven't found the right filename but there are linked entries,
1050 * walk up the chain */
1051 if ((*cmp)(name, path) != 0 && hash_chain != 0)
1052 {
1053 *prev_block = start_block;
1054 return walk_hash_chain(img, path, hash_chain, prev_block, block);
1055 }
1056
1057 /* found the correct block, return */
1058 if ((*cmp)(name, path) == 0)
1059 {
1060 *block = start_block;
1061 return IMGTOOLERR_SUCCESS;
1062 }
1063
1064 /* we should never get here */
1065 return IMGTOOLERR_UNEXPECTED;
1066 }
1067
1068
1069 /* Returns the block number for a dir/file/link entry given as NUL delimited
1070 * list of path parts, for example "dir1\0dir2\0dir3" returns the block number
1071 * for directory "dir3" */
find_entry(imgtool::image & img,const char * path,int start_block,int * block)1072 static imgtoolerr_t find_entry(imgtool::image &img, const char *path, int start_block, int *block)
1073 {
1074 imgtoolerr_t ret;
1075 const char *next_path;
1076 int current_block, prev;
1077 uint32_t ht[TSIZE];
1078
1079 /* get the hash table */
1080 ret = get_hash_table(img, start_block, ht);
1081 if (ret) return ret;
1082
1083 /* calculate hash and get block for initial entry */
1084 current_block = ht[hash_name(path, is_intl(img))];
1085
1086 /* check if there was a match in the hash table */
1087 if (current_block == 0)
1088 {
1089 return IMGTOOLERR_PATHNOTFOUND;
1090 }
1091
1092 /* walk the linked hash list */
1093 ret = walk_hash_chain(img, path, current_block, &prev, block);
1094 if (ret) return ret;
1095
1096 /* follow links */
1097 switch (get_block_type(img, *block))
1098 {
1099 case ST_SOFTLINK:
1100
1101 /* TODO: Softlink support */
1102 return IMGTOOLERR_UNIMPLEMENTED;
1103
1104 case ST_LINKDIR:
1105 case ST_LINKFILE:
1106 {
1107 hardlink_block hl;
1108
1109 ret = read_hardlink_block(img, *block, &hl);
1110 if (ret) return ret;
1111
1112 *block = hl.real_entry;
1113
1114 break;
1115 }
1116
1117 default:
1118 break;
1119 }
1120
1121 /* get next path part */
1122 next_path = path + strlen(path) + 1;
1123
1124 /* if there are more path parts, search the next block */
1125 if (next_path[0])
1126 {
1127 return find_entry(img, next_path, *block, block);
1128 }
1129
1130 return IMGTOOLERR_SUCCESS;
1131 }
1132
1133
get_block_chksum(imgtool::image & img,int block,uint32_t * chksum,int bitmap)1134 static imgtoolerr_t get_block_chksum(imgtool::image &img, int block, uint32_t *chksum, int bitmap)
1135 {
1136 imgtoolerr_t ret;
1137 uint8_t buffer[BSIZE];
1138
1139 /* get block data */
1140 ret = read_block(img, block, buffer);
1141 if (ret) return ret;
1142
1143 /* clear old checksum */
1144 if (bitmap)
1145 {
1146 memset(buffer, 0, 4);
1147 }
1148 else
1149 {
1150 memset(&buffer[20], 0, 4);
1151 }
1152
1153 /* calulate checksum */
1154 *chksum = block_checksum(buffer, BSIZE);
1155
1156 return IMGTOOLERR_SUCCESS;
1157 }
1158
1159
fix_chksum(imgtool::image & img,int block,int bitmap)1160 static imgtoolerr_t fix_chksum(imgtool::image &img, int block, int bitmap)
1161 {
1162 imgtoolerr_t ret;
1163 uint8_t buffer[BSIZE];
1164 uint32_t chksum;
1165
1166 /* calculate block checksum */
1167 ret = get_block_chksum(img, block, &chksum, bitmap);
1168 if (ret) return ret;
1169
1170 /* read block data */
1171 ret = read_block(img, block, buffer);
1172 if (ret) return ret;
1173
1174 /* update checksum */
1175 if (bitmap)
1176 {
1177 place_integer_be(buffer, 0, 4, chksum);
1178 }
1179 else
1180 {
1181 place_integer_be(buffer, 20, 4, chksum);
1182 }
1183
1184 /* write back new block data */
1185 ret = write_block(img, block, buffer);
1186 if (ret) return ret;
1187
1188 return IMGTOOLERR_SUCCESS;
1189 }
1190
1191
fix_block_chksum(imgtool::image & img,int block)1192 static imgtoolerr_t fix_block_chksum(imgtool::image &img, int block)
1193 {
1194 return fix_chksum(img, block, false);
1195 }
1196
1197
fix_bitmap_chksum(imgtool::image & img,int block)1198 static imgtoolerr_t fix_bitmap_chksum(imgtool::image &img, int block)
1199 {
1200 return fix_chksum(img, block, true);
1201 }
1202
1203
1204 /* Set a block as used */
bitmap_mark(imgtool::image & img,int block,int used)1205 static imgtoolerr_t bitmap_mark(imgtool::image &img, int block, int used)
1206 {
1207 imgtoolerr_t ret;
1208 bitmap_block bm;
1209 root_block root;
1210 int page;
1211
1212 block -= 2; /* subtract boot block sectors, 2 only for floppies! */
1213
1214 ret = read_root_block(img, &root);
1215 if (ret) return ret;
1216
1217 /* figure out bitmap block location */
1218 page = root.bm_pages[block / (MSIZE * 32)];
1219
1220 /* get bitmap */
1221 ret = read_bitmap_block(img, page, &bm);
1222 if (ret) return ret;
1223
1224 /* subtract pages we skip */
1225 block -= MSIZE * 32 * (block / (MSIZE * 32));
1226
1227 /* mark as used or free */
1228 if (used)
1229 {
1230 bm.map[block/32] &= ~(1 << block % 32);
1231 }
1232 else
1233 {
1234 bm.map[block/32] |= (1 << block % 32);
1235 }
1236
1237 /* write changed bitmap block back to disk */
1238 ret = write_bitmap_block(img, page, &bm);
1239 if (ret) return ret;
1240
1241 /* update checksum */
1242 ret = fix_bitmap_chksum(img, page);
1243 if (ret) return ret;
1244
1245 return IMGTOOLERR_SUCCESS;
1246 }
1247
1248
bitmap_mark_used(imgtool::image & img,int block)1249 static imgtoolerr_t bitmap_mark_used(imgtool::image &img, int block)
1250 {
1251 return bitmap_mark(img, block, true);
1252 }
1253
1254
bitmap_mark_free(imgtool::image & img,int block)1255 static imgtoolerr_t bitmap_mark_free(imgtool::image &img, int block)
1256 {
1257 return bitmap_mark(img, block, false);
1258 }
1259
1260
update_block_modified_date(imgtool::image & img,int block)1261 static imgtoolerr_t update_block_modified_date(imgtool::image &img, int block)
1262 {
1263 uint8_t buffer[BSIZE];
1264 imgtoolerr_t ret;
1265 amiga_date date;
1266 time_t now;
1267
1268 ret = read_block(img, block, buffer);
1269 if (ret) return ret;
1270
1271 /* Set new time */
1272 time(&now);
1273 amiga_setup_time(now, &date);
1274
1275 /* Write new time into block */
1276 place_integer_be(buffer, BSIZE-92, 4, date.days);
1277 place_integer_be(buffer, BSIZE-88, 4, date.mins);
1278 place_integer_be(buffer, BSIZE-84, 4, date.ticks);
1279
1280 /* Write block back to disk */
1281 ret = write_block(img, block, buffer);
1282 if (ret) return ret;
1283
1284 return IMGTOOLERR_SUCCESS;
1285 }
1286
1287
clear_hash_table_entry(imgtool::image & img,int parent,char * filename)1288 static imgtoolerr_t clear_hash_table_entry(imgtool::image &img, int parent, char *filename)
1289 {
1290 imgtoolerr_t ret;
1291 uint32_t ht[TSIZE], chain;
1292 int index, entry, prev, block;
1293
1294 ret = get_hash_table(img, parent, ht);
1295 if (ret) return ret;
1296
1297 /* Calculate hash and get block for initial entry */
1298 index = hash_name(filename, is_intl(img));
1299 entry = ht[index];
1300
1301 /* Walk the hash chain to get the real entry */
1302 ret = walk_hash_chain(img, filename, entry, &prev, &block);
1303 if (ret) return ret;
1304
1305 /* Get chained value from block */
1306 ret = get_hash_chain(img, block, &chain);
1307 if (ret) return ret;
1308
1309 /* Check if we need to change the hash table */
1310 if (entry == block)
1311 {
1312 /* Set new value (might be 0 if there were no linked entries) */
1313 ht[index] = chain;
1314
1315 /* Save changed hash table */
1316 ret = set_hash_table(img, parent, ht);
1317 if (ret) return ret;
1318
1319 /* Update last modified date */
1320 ret = update_block_modified_date(img, parent);
1321 if (ret) return ret;
1322
1323 /* Calculate new checksum */
1324 ret = fix_block_chksum(img, parent);
1325 if (ret) return ret;
1326 }
1327 else
1328 {
1329 /* Save chained value to previous chain element */
1330 ret = set_hash_chain(img, prev, chain);
1331 if (ret) return ret;
1332
1333 /* Calculate new checksum */
1334 ret = fix_block_chksum(img, prev);
1335 if (ret) return ret;
1336 }
1337
1338 /* Mark our block as free */
1339 ret = bitmap_mark_free(img, block);
1340 if (ret) return ret;
1341
1342 return IMGTOOLERR_SUCCESS;
1343 }
1344
1345
1346 /* Returns the number of the first bit that is set in the array */
get_first_bit(uint32_t * array,int size)1347 static int get_first_bit(uint32_t *array, int size)
1348 {
1349 int i;
1350
1351 for (i = 0; i < size; i++)
1352 {
1353 if (array[i] != 0)
1354 {
1355 uint32_t v = array[i];
1356 int c;
1357
1358 /* get first bit that is set */
1359 for (c = 0; (v & 1) == 0; c++) v >>= 1;
1360
1361 /* return bit number */
1362 return i * 32 + c;
1363 }
1364 }
1365
1366 return -1;
1367 }
1368
1369
1370 #ifdef UNUSED_FUNCTION
walk_bitmap_ext_blocks(imgtool::image * img,int start,int * block)1371 static imgtoolerr_t walk_bitmap_ext_blocks(imgtool::image *img, int start, int *block)
1372 {
1373 imgtoolerr_t ret;
1374 bitmap_ext_block bm_ext;
1375 int bit;
1376
1377 /* if we don't have a valid block, bail out */
1378 if (start == 0)
1379 {
1380 return IMGTOOLERR_NOSPACE;
1381 }
1382
1383 /* get the extended bitmap block */
1384 ret = read_bitmap_ext_block(img, start, &bm_ext);
1385 if (ret) return ret;
1386
1387 /* get the first bit that is set in the map */
1388 bit = get_first_bit(bm_ext.map, MSIZE);
1389
1390 /* if we found one, return */
1391 if (bit != -1)
1392 {
1393 *block += bit;
1394 return IMGTOOLERR_SUCCESS;
1395 }
1396
1397 /* increase on each call */
1398 *block += MSIZE * 32;
1399
1400 /* else continue walking the list */
1401 return walk_bitmap_ext_blocks(img, bm_ext.next, block);
1402 }
1403 #endif
1404
1405
1406 /* Searches for a block marked as free
1407 * TODO: bm_ext support for HDs */
find_free_block(imgtool::image & img,int * block)1408 static imgtoolerr_t find_free_block(imgtool::image &img, int *block)
1409 {
1410 imgtoolerr_t ret;
1411 root_block root;
1412 int i;
1413
1414 /* get root block */
1415 ret = read_root_block(img, &root);
1416 if (ret) return ret;
1417
1418 /* iterate bitmap pointers */
1419 for (i = 0; i < 25; i++)
1420 {
1421 bitmap_block bm;
1422
1423 /* get bitmap block pointed to */
1424 ret = read_bitmap_block(img, root.bm_pages[i], &bm);
1425 if (ret) return ret;
1426
1427 *block = i * 32 * MSIZE + get_first_bit(bm.map, MSIZE);
1428
1429 if (*block != -1)
1430 {
1431 *block += 2;
1432 return IMGTOOLERR_SUCCESS;
1433 }
1434 }
1435
1436 /* if we get here we haven't found a free block */
1437 return IMGTOOLERR_NOSPACE;
1438 }
1439
1440
add_entry(imgtool::image & img,int parent,int block)1441 static imgtoolerr_t add_entry(imgtool::image &img, int parent, int block)
1442 {
1443 imgtoolerr_t ret;
1444 uint32_t ht[TSIZE];
1445 char name[31];
1446 int hash;
1447
1448 ret = get_blockname(img, block, name);
1449 if (ret) return ret;
1450
1451 ret = get_hash_table(img, parent, ht);
1452 if (ret) return ret;
1453
1454 hash = hash_name(name, is_intl(img));
1455
1456 /* Check if there is already an entry with that name */
1457 if (ht[hash] != 0)
1458 {
1459 /* Save the old value to our hash chain */
1460 ret = set_hash_chain(img, block, ht[hash]);
1461 if (ret) return ret;
1462 }
1463
1464 /* Write our block number into the table */
1465 ht[hash] = block;
1466
1467 /* Write table back to disk */
1468 ret = set_hash_table(img, parent, ht);
1469 if (ret) return ret;
1470
1471 return IMGTOOLERR_SUCCESS;
1472 }
1473
1474
1475 /* Recursively create new directory entries */
makedir(imgtool::image & img,const char * path,int parent)1476 static imgtoolerr_t makedir(imgtool::image &img, const char *path, int parent)
1477 {
1478 imgtoolerr_t ret;
1479 dir_block dir;
1480 time_t now;
1481 int block;
1482
1483 if (!path)
1484 {
1485 return IMGTOOLERR_PARAMNEEDED;
1486 }
1487
1488 if (!path[0])
1489 {
1490 return IMGTOOLERR_SUCCESS;
1491 }
1492
1493 /* Get a free block */
1494 ret = find_free_block(img, &block);
1495 if (ret) return ret;
1496
1497 /* Initialize entry */
1498 memset(&dir, 0, sizeof(dir_block));
1499
1500 /* Copy data */
1501 time(&now);
1502 amiga_setup_time(now, &dir.date);
1503 dir.name_len = strlen(path);
1504 memcpy(dir.dirname, path, dir.name_len);
1505 dir.header_key = block;
1506 dir.parent = parent;
1507
1508 /* Write block */
1509 ret = write_dir_block(img, block, &dir);
1510 if (ret) return ret;
1511
1512 /* Fix checksum */
1513 ret = fix_block_chksum(img, block);
1514 if (ret) return ret;
1515
1516 /* Link the new entry in the parent */
1517 ret = add_entry(img, parent, block);
1518 if (ret) return ret;
1519
1520 /* Mark it as used */
1521 ret = bitmap_mark_used(img, block);
1522 if (ret) return ret;
1523
1524 /* Create the next entry */
1525 return makedir(img, path + strlen(path) + 1, block);
1526 }
1527
1528
1529 /* Recursively checks the path parts and creates directories for them */
checkdir(imgtool::image & img,const char * path,int parent)1530 static imgtoolerr_t checkdir(imgtool::image &img, const char *path, int parent)
1531 {
1532 imgtoolerr_t ret;
1533 int block;
1534 char first_part[31];
1535
1536 memset(first_part, 0, sizeof(first_part));
1537 strcpy(first_part, path);
1538
1539 /* Directories all the way down, bail out */
1540 if (!path[0])
1541 {
1542 return IMGTOOLERR_CANNOTUSEPATH;
1543 }
1544
1545 /* Search for the entry */
1546 ret = find_entry(img, first_part, parent, &block);
1547
1548 switch (ret)
1549 {
1550 case IMGTOOLERR_PATHNOTFOUND:
1551
1552 /* There is no entry with this name yet, so we can just create them */
1553 return makedir(img, path, parent);
1554
1555 case IMGTOOLERR_SUCCESS:
1556
1557 if (get_block_type(img, block) == ST_USERDIR)
1558 {
1559 /* Go down one level */
1560 return checkdir(img, path + strlen(path) + 1, block);
1561 }
1562 else
1563 {
1564 /* There is an entry but it's not a directory, create it */
1565 return makedir(img, path, parent);
1566 }
1567
1568 default: return ret;
1569 }
1570 }
1571
1572
1573 /* Writes the file data from the specified block into the stream */
write_file_block_data(imgtool::image & img,int block,int size,imgtool::stream & destf)1574 static imgtoolerr_t write_file_block_data(imgtool::image &img, int block, int size, imgtool::stream &destf)
1575 {
1576 imgtoolerr_t ret;
1577 uint8_t buffer[BSIZE];
1578
1579 /* Check if we even need to write something */
1580 if (size == 0)
1581 {
1582 return IMGTOOLERR_SUCCESS;
1583 }
1584
1585 if (is_ffs(img))
1586 {
1587 /* Get block and read directly into buffer */
1588 ret = read_block(img, block, buffer);
1589 if (ret) return ret;
1590 }
1591 else
1592 {
1593 data_block db;
1594 uint32_t chksum;
1595
1596 ret = read_data_block(img, block, &db);
1597 if (ret) return ret;
1598
1599 /* Verify data checksum */
1600 ret = get_block_chksum(img, block, &chksum, false);
1601 if (ret) return ret;
1602
1603 if (db.chksum != chksum)
1604 {
1605 return IMGTOOLERR_CORRUPTFILE;
1606 }
1607
1608 /* Copy data to buffer */
1609 memcpy(buffer, db.data, size);
1610 }
1611
1612 /* Write data to stream */
1613 if (destf.write(buffer, size) != size)
1614 {
1615 return IMGTOOLERR_WRITEERROR;
1616 }
1617
1618 return IMGTOOLERR_SUCCESS;
1619 }
1620
1621
walk_data_block_ptr(imgtool::image & img,uint32_t * ptr,int * filesize,imgtool::stream * destf,bool write)1622 static imgtoolerr_t walk_data_block_ptr(imgtool::image &img, uint32_t *ptr, int *filesize, imgtool::stream *destf, bool write)
1623 {
1624 int i, blocksize = is_ffs(img) ? BSIZE : BSIZE-24;
1625 imgtoolerr_t ret;
1626
1627 for (i = TSIZE-1; i >= 0; i--)
1628 {
1629 /* We write either blocksize bytes or whats remaining */
1630 int bytes_left = (*filesize >= blocksize) ? blocksize : *filesize;
1631
1632 if (write)
1633 {
1634 ret = write_file_block_data(img, ptr[i], bytes_left, *destf);
1635 if (ret) return ret;
1636 }
1637 else
1638 {
1639 ret = bitmap_mark_free(img, ptr[i]);
1640 if (ret) return ret;
1641 }
1642
1643 *filesize -= bytes_left;
1644
1645 /* Check if we are finished early */
1646 if (*filesize == 0) break;
1647 }
1648
1649 return IMGTOOLERR_SUCCESS;
1650 }
1651
1652
write_data_block_ptr(imgtool::image & img,uint32_t * ptr,int * filesize,imgtool::stream & destf)1653 static imgtoolerr_t write_data_block_ptr(imgtool::image &img, uint32_t *ptr, int *filesize, imgtool::stream &destf)
1654 {
1655 return walk_data_block_ptr(img, ptr, filesize, &destf, true);
1656 }
1657
1658
1659 /* Marks all blocks pointed to by the data block pointers as free */
clear_data_block_ptr(imgtool::image & img,uint32_t * ptr,int * filesize)1660 static imgtoolerr_t clear_data_block_ptr(imgtool::image &img, uint32_t *ptr, int *filesize)
1661 {
1662 return walk_data_block_ptr(img, ptr, filesize, nullptr, false);
1663 }
1664
1665
walk_file_ext_data(imgtool::image & img,int block,int * filesize,imgtool::stream * destf,int write)1666 static imgtoolerr_t walk_file_ext_data(imgtool::image &img, int block, int *filesize, imgtool::stream *destf, int write)
1667 {
1668 file_ext_block file_ext;
1669 imgtoolerr_t ret;
1670
1671 /* Get file extension block */
1672 ret = read_file_ext_block(img, block, &file_ext);
1673 if (ret) return ret;
1674
1675 /* Write all data pointers in the table */
1676 ret = walk_data_block_ptr(img, file_ext.data_blocks, filesize, destf, write);
1677 if (ret) return ret;
1678
1679 /* Check if we are finished */
1680 if (*filesize != 0)
1681 {
1682 if (file_ext.extension == 0)
1683 {
1684 /* We are not finished, but there are no more extension blocks */
1685 return IMGTOOLERR_CORRUPTFILE;
1686 }
1687 else
1688 {
1689 /* Write the next file extension block */
1690 return walk_file_ext_data(img, file_ext.extension, filesize, destf, write);
1691 }
1692 }
1693
1694 /* Mark ourself as free if we not writing */
1695 if (!write)
1696 {
1697 ret = bitmap_mark_free(img, block);
1698 if (ret) return ret;
1699 }
1700
1701 return IMGTOOLERR_SUCCESS;
1702 }
1703
1704
write_file_ext_data(imgtool::image & img,int block,int * filesize,imgtool::stream * destf)1705 static imgtoolerr_t write_file_ext_data(imgtool::image &img, int block, int *filesize, imgtool::stream *destf)
1706 {
1707 return walk_file_ext_data(img, block, filesize, destf, true);
1708 }
1709
1710
clear_file_ext_data(imgtool::image & img,int block,int * filesize)1711 static imgtoolerr_t clear_file_ext_data(imgtool::image &img, int block, int *filesize)
1712 {
1713 return walk_file_ext_data(img, block, filesize, NULL, false);
1714 }
1715
1716
1717 /* Updates the disk alteration date stored in the root block */
update_disk_alteration_date(imgtool::image & img)1718 static imgtoolerr_t update_disk_alteration_date(imgtool::image &img)
1719 {
1720 imgtoolerr_t ret;
1721 root_block root;
1722 time_t now;
1723
1724 /* Get root block */
1725 ret = read_root_block(img, &root);
1726 if (ret) return ret;
1727
1728 /* Get current time */
1729 time(&now);
1730 amiga_setup_time(now, &root.v);
1731
1732 /* Write back new root block */
1733 ret = write_root_block(img, &root);
1734 if (ret) return ret;
1735
1736 /* And update its checksum */
1737 ret = fix_block_chksum(img, get_total_blocks(img)/2);
1738 if (ret) return ret;
1739
1740 return IMGTOOLERR_SUCCESS;
1741 }
1742
1743
1744 /*****************************************************************************
1745 Imgtool functions
1746 *****************************************************************************/
1747
1748
amiga_image_open(imgtool::image & img,imgtool::stream::ptr && stream)1749 static imgtoolerr_t amiga_image_open(imgtool::image &img, imgtool::stream::ptr &&stream)
1750 {
1751 amiga_floppy *f = get_amiga_floppy(img);
1752 uint64_t size = stream->size();
1753
1754 f->sectors = size/BSIZE/80/2;
1755
1756 if (f->sectors != 11 && f->sectors != 22)
1757 {
1758 return IMGTOOLERR_CORRUPTIMAGE;
1759 }
1760
1761 f->stream = stream.release();
1762 return IMGTOOLERR_SUCCESS;
1763 }
1764
1765
amiga_image_exit(imgtool::image & img)1766 static void amiga_image_exit(imgtool::image &img)
1767 {
1768 amiga_floppy *f = get_amiga_floppy(img);
1769 if (f->stream)
1770 delete f->stream;
1771 }
1772
1773
amiga_image_info(imgtool::image & img,std::ostream & stream)1774 static void amiga_image_info(imgtool::image &img, std::ostream &stream)
1775 {
1776 imgtoolerr_t ret;
1777 root_block root;
1778 char info[255];
1779 time_t t_c, t_v, t_r;
1780 char c[19], v[19], r[19];
1781
1782 ret = read_root_block(img, &root);
1783 if (ret) return;
1784
1785 t_c = amiga_crack_time(&root.c).to_time_t();
1786 t_v = amiga_crack_time(&root.v).to_time_t();
1787 t_r = amiga_crack_time(&root.r).to_time_t();
1788
1789 strftime(c, sizeof(c), "%d-%b-%y %H:%M:%S", localtime(&t_c));
1790 strftime(v, sizeof(v), "%d-%b-%y %H:%M:%S", localtime(&t_v));
1791 strftime(r, sizeof(r), "%d-%b-%y %H:%M:%S", localtime(&t_r));
1792
1793 strcpy(info, "Volume name: ");
1794 strncat(info, (char *)root.diskname, root.name_len);
1795 strcat(info, "\nVolume created: ");
1796 strcat(info, c);
1797 strcat(info, "\nVolume modified: ");
1798 strcat(info, v);
1799 strcat(info, "\n Root modified: ");
1800 strcat(info, r);
1801
1802 stream << info;
1803 }
1804
1805
amiga_image_read_sector(imgtool::image & img,uint32_t track,uint32_t head,uint32_t sector,void * buf,size_t len)1806 static imgtoolerr_t amiga_image_read_sector(imgtool::image &img, uint32_t track, uint32_t head, uint32_t sector, void *buf, size_t len)
1807 {
1808 amiga_floppy *f = get_amiga_floppy(img);
1809
1810 /* skip ahead to the area we want to read */
1811 f->stream->seek(track * (head+1) * f->sectors * BSIZE + sector * BSIZE, SEEK_CUR);
1812
1813 if (f->stream->read(buf, len) != len)
1814 {
1815 return IMGTOOLERR_READERROR;
1816 }
1817
1818 /* reset stream */
1819 f->stream->seek(0, 0);
1820
1821 return IMGTOOLERR_SUCCESS;
1822 }
1823
1824
amiga_image_read_sector(imgtool::image & img,uint32_t track,uint32_t head,uint32_t sector,std::vector<uint8_t> & buffer)1825 static imgtoolerr_t amiga_image_read_sector(imgtool::image &img,
1826 uint32_t track, uint32_t head, uint32_t sector, std::vector<uint8_t> &buffer)
1827 {
1828 try { buffer.resize(BSIZE); }
1829 catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; }
1830
1831 return amiga_image_read_sector(img, track, head, sector, &buffer[0], buffer.size());
1832 }
1833
1834
amiga_image_write_sector(imgtool::image & img,uint32_t track,uint32_t head,uint32_t sector,const void * buf,size_t len,int ddam)1835 static imgtoolerr_t amiga_image_write_sector(imgtool::image &img, uint32_t track, uint32_t head, uint32_t sector, const void *buf, size_t len, int ddam)
1836 {
1837 amiga_floppy *f = get_amiga_floppy(img);
1838
1839 /* skip ahead to the area we want to write */
1840 f->stream->seek(track * (head+1) * f->sectors * BSIZE + sector * BSIZE, SEEK_CUR);
1841
1842 /* write data */
1843 if (f->stream->write(buf, len) != len)
1844 {
1845 return IMGTOOLERR_WRITEERROR;
1846 }
1847
1848 /* reset stream */
1849 f->stream->seek(0, 0);
1850
1851 return IMGTOOLERR_SUCCESS;
1852 }
1853
1854
amiga_image_beginenum(imgtool::directory & enumeration,const char * path)1855 static imgtoolerr_t amiga_image_beginenum(imgtool::directory &enumeration, const char *path)
1856 {
1857 int blocks = get_total_blocks(enumeration.image());
1858 imgtoolerr_t ret;
1859 amiga_iterator *iter;
1860
1861 iter = (amiga_iterator *) enumeration.extra_bytes();
1862 if (!iter) return IMGTOOLERR_OUTOFMEMORY;
1863
1864 iter->index = 1;
1865 iter->ht_index = 0;
1866 iter->eof = 0;
1867
1868 if (path[0])
1869 {
1870 /* search for the directory block, start with the root block */
1871 ret = find_entry(enumeration.image(), path, blocks/2, &iter->block);
1872 if (ret) return ret;
1873 }
1874 else
1875 {
1876 /* we didn't get a path, use the root directory */
1877 iter->block = blocks / 2;
1878 }
1879
1880 return IMGTOOLERR_SUCCESS;
1881 }
1882
1883
amiga_image_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)1884 static imgtoolerr_t amiga_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
1885 {
1886 amiga_iterator *iter = (amiga_iterator *) enumeration.extra_bytes();
1887 imgtoolerr_t ret;
1888 uint32_t ht[TSIZE];
1889 int block;
1890
1891 /* finished listing all entries? */
1892 if (iter->eof == 1 || iter->ht_index == TSIZE)
1893 {
1894 ent.eof = 1;
1895 return IMGTOOLERR_SUCCESS;
1896 }
1897
1898 /* get hash table */
1899 ret = get_hash_table(enumeration.image(), iter->block, ht);
1900 if (ret) return ret;
1901
1902 /* skip empty hash table entries */
1903 while (ht[iter->ht_index] == 0)
1904 {
1905 iter->ht_index++;
1906 /* check if we are already at the end */
1907 if (iter->ht_index == TSIZE)
1908 {
1909 ent.eof = 1;
1910 return IMGTOOLERR_SUCCESS;
1911 }
1912 }
1913
1914 /* get block number */
1915 block = (iter->next_block == 0) ? ht[iter->ht_index] : iter->next_block;
1916
1917 switch (get_block_type(enumeration.image(), block))
1918 {
1919 case ST_FILE:
1920 {
1921 file_block file;
1922
1923 /* get block */
1924 ret = read_file_block(enumeration.image(), block, &file);
1925 if (ret) return ret;
1926
1927 /* fill directory entry */
1928 strncpyz(ent.filename, (char *)file.filename, file.name_len + 1);
1929 ent.filesize = file.byte_size;
1930 ent.lastmodified_time = amiga_crack_time(&file.date);
1931 amiga_decode_flags(file.protect, ent.attr);
1932 strncpyz(ent.comment, (char *)file.comment, file.comm_len + 1);
1933
1934 iter->next_block = file.hash_chain;
1935
1936 break;
1937 }
1938
1939 case ST_USERDIR:
1940 {
1941 dir_block dir;
1942
1943 /* get block */
1944 ret = read_dir_block(enumeration.image(), block, &dir);
1945 if (ret) return ret;
1946
1947 /* fill directory entry */
1948 strncpyz(ent.filename, (char *)dir.dirname, dir.name_len + 1);
1949 ent.lastmodified_time = amiga_crack_time(&dir.date);
1950 amiga_decode_flags(dir.protect, ent.attr);
1951 strncpyz(ent.comment, (char *)dir.comment, dir.comm_len + 1);
1952 ent.directory = 1;
1953
1954 iter->next_block = dir.hash_chain;
1955
1956 break;
1957 }
1958
1959 case ST_SOFTLINK:
1960 {
1961 softlink_block sl;
1962
1963 /* get block */
1964 ret = read_softlink_block(enumeration.image(), block, &sl);
1965 if (ret) return ret;
1966
1967 /* fill directory entry */
1968 strncpyz(ent.filename, (char *)sl.slname, sl.name_len + 1);
1969 ent.lastmodified_time = amiga_crack_time(&sl.date);
1970 amiga_decode_flags(sl.protect, ent.attr);
1971 strncpyz(ent.comment, (char *)sl.comment, sl.comm_len + 1);
1972 strcpy(ent.softlink, (char *)sl.symbolic_name);
1973
1974 iter->next_block = sl.hash_chain;
1975
1976 break;
1977 }
1978
1979 case ST_LINKDIR:
1980
1981 ent.directory = 1;
1982
1983 case ST_LINKFILE:
1984 {
1985 hardlink_block hl;
1986
1987 /* get block */
1988 ret = read_hardlink_block(enumeration.image(), block, &hl);
1989 if (ret) return ret;
1990
1991 /* get filesize from linked file */
1992 if (!ent.directory)
1993 {
1994 file_block file;
1995 ret = read_file_block(enumeration.image(), hl.real_entry, &file);
1996 if (ret) return ret;
1997 ent.filesize = file.byte_size;
1998 }
1999
2000 /* fill directory entry */
2001 strncpyz(ent.filename, (char *)hl.hlname, hl.name_len + 1);
2002 ent.lastmodified_time = amiga_crack_time(&hl.date);
2003 amiga_decode_flags(hl.protect, ent.attr);
2004 strncpyz(ent.comment, (char *)hl.comment, hl.comm_len + 1);
2005 ent.hardlink = 1;
2006
2007 iter->next_block = hl.hash_chain;
2008
2009 break;
2010 }
2011
2012 default:
2013 return IMGTOOLERR_UNIMPLEMENTED;
2014 }
2015
2016 /* if there are no linked entries, go to the next hash table entry */
2017 if (iter->next_block == 0)
2018 {
2019 iter->ht_index++;
2020 }
2021
2022 /* jump to next index */
2023 iter->index++;
2024
2025 return IMGTOOLERR_SUCCESS;
2026 }
2027
2028
amiga_image_freespace(imgtool::partition & partition,uint64_t * size)2029 static imgtoolerr_t amiga_image_freespace(imgtool::partition &partition, uint64_t *size)
2030 {
2031 imgtoolerr_t ret;
2032 imgtool::image &image(partition.image());
2033 const int data_size = is_ffs(image) ? BSIZE : BSIZE-24;
2034 root_block root;
2035 bitmap_block bm;
2036 int blocks, blocks_processed = 0, pages, i, c;
2037 uint32_t v;
2038
2039 /* initialize size */
2040 *size = 0;
2041
2042 /* get root block */
2043 ret = read_root_block(image, &root);
2044 if (ret) return ret;
2045
2046 /* get total number of blocks in the image */
2047 blocks = get_total_blocks(image);
2048
2049 /* subtract the two bootblock blocks (only for floppies!) */
2050 blocks -= 2;
2051
2052 /* iterate all bitmap pages */
2053 for (pages = 0; pages < 25; pages++)
2054 {
2055 ret = read_bitmap_block(image, root.bm_pages[pages], &bm);
2056 if (ret) return ret;
2057
2058 for (i = 0; i < MSIZE; i++)
2059 {
2060 v = bm.map[i];
2061
2062 /* clear half used value */
2063 if ((blocks_processed + 32) > blocks)
2064 v &= ~(~0 << (blocks - blocks_processed));
2065
2066 /* count bits */
2067 for (c = 0; v; c++)
2068 v &= v - 1;
2069
2070 *size += c * data_size;
2071
2072 blocks_processed += 32;
2073
2074 if (blocks_processed >= blocks)
2075 return IMGTOOLERR_SUCCESS;
2076 }
2077 }
2078
2079 return IMGTOOLERR_SUCCESS;
2080 }
2081
2082
amiga_image_readfile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & destf)2083 static imgtoolerr_t amiga_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
2084 {
2085 imgtool::image &img(partition.image());
2086 imgtoolerr_t ret;
2087 file_block file;
2088 int filesize, block;
2089
2090 /* Search for the block number */
2091 ret = find_entry(img, filename, get_total_blocks(img)/2, &block);
2092 if (ret == IMGTOOLERR_PATHNOTFOUND) return IMGTOOLERR_FILENOTFOUND;
2093 if (ret) return ret; /* Other errors */
2094
2095 /* Phase 1: Follow data pointers */
2096 ret = read_file_block(img, block, &file);
2097 if (ret) return ret;
2098
2099 filesize = file.byte_size;
2100
2101 /* Write out file data pointed to by data block pointers */
2102 ret = write_data_block_ptr(img, file.data_blocks, &filesize, destf);
2103 if (ret) return ret;
2104
2105 /* Check if we are done already */
2106 if (filesize == 0) return IMGTOOLERR_SUCCESS;
2107
2108 /* Phase 2: Follow file extension blocks */
2109 ret = write_file_ext_data(img, file.extension, &filesize, &destf);
2110 if (ret) return ret;
2111
2112 return IMGTOOLERR_SUCCESS;
2113 }
2114
2115
2116 /* When a file is deleted, only its File header block number is cleared from
2117 * the Directory block (or from the same-hash-value list) and the bitmap is
2118 * updated. File header block, Data blocks and File extension blocks are not
2119 * cleared, but the bitmap blocks are updated. */
amiga_image_deletefile(imgtool::partition & partition,const char * fname)2120 static imgtoolerr_t amiga_image_deletefile(imgtool::partition &partition, const char *fname)
2121 {
2122 imgtool::image &img(partition.image());
2123 imgtoolerr_t ret;
2124 int parent, block;
2125 char filename[31];
2126
2127 /* Initialize filename */
2128 memset(filename, 0, sizeof(filename));
2129
2130 /* Search for the block number */
2131 ret = find_entry(img, fname, get_total_blocks(img)/2, &block);
2132 if (ret == IMGTOOLERR_PATHNOTFOUND) return IMGTOOLERR_FILENOTFOUND;
2133 if (ret) return ret;
2134
2135 /* Get the parent block, where we need to clear the hash */
2136 switch (get_block_type(img, block))
2137 {
2138 case ST_FILE:
2139 {
2140 file_block file;
2141 int filesize;
2142
2143 ret = read_file_block(img, block, &file);
2144 if (ret) return ret;
2145
2146 filesize = file.byte_size;
2147 parent = file.parent;
2148 memcpy(filename, file.filename, file.name_len);
2149
2150 /* Clear all linked data sectors */
2151 ret = clear_data_block_ptr(img, file.data_blocks, &filesize);
2152 if (ret) return ret;
2153
2154 /* Clear extended file data sectors */
2155 if (filesize != 0)
2156 {
2157 ret = clear_file_ext_data(img, file.extension, &filesize);
2158 if (ret) return ret;
2159 }
2160
2161 break;
2162 }
2163
2164 case ST_LINKFILE:
2165 {
2166 softlink_block link;
2167
2168 ret = read_softlink_block(img, block, &link);
2169 if (ret) return ret;
2170
2171 parent = link.parent;
2172 memcpy(filename, link.slname, link.name_len);
2173
2174 break;
2175 }
2176
2177 default:
2178 return IMGTOOLERR_UNEXPECTED;
2179 }
2180
2181 /* Clear hash table entry */
2182 ret = clear_hash_table_entry(img, parent, filename);
2183 if (ret) return ret;
2184
2185 /* Update disk alteration date */
2186 ret = update_disk_alteration_date(img);
2187 if (ret) return ret;
2188
2189 return IMGTOOLERR_SUCCESS;
2190 }
2191
2192
amiga_image_writefile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & sourcef,util::option_resolution * opts)2193 static imgtoolerr_t amiga_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
2194 {
2195 return IMGTOOLERR_UNIMPLEMENTED;
2196 }
2197
2198
amiga_image_create(imgtool::image & img,imgtool::stream::ptr && stream,util::option_resolution * opts)2199 static imgtoolerr_t amiga_image_create(imgtool::image &img, imgtool::stream::ptr &&stream, util::option_resolution *opts)
2200 {
2201 amiga_floppy *f = get_amiga_floppy(img);
2202 const std::string &dskname = opts->lookup_string('N');
2203 imgtoolerr_t ret;
2204 uint8_t buffer[BSIZE];
2205 root_block root;
2206 bitmap_block bm;
2207 time_t now;
2208 int blocks;
2209
2210 f->stream = stream.get();
2211
2212 switch (opts->lookup_int('S'))
2213 {
2214 case 0: f->sectors = 11; break;
2215 case 1: f->sectors = 22; break;
2216 default: return IMGTOOLERR_PARAMCORRUPT;
2217 }
2218
2219 /* initialize with zeros */
2220 memset(buffer, 0, BSIZE);
2221
2222 /* add DOS magic string and flags */
2223 buffer[0] = 'D';
2224 buffer[1] = 'O';
2225 buffer[2] = 'S';
2226 buffer[3] = 0;
2227
2228 /* File system */
2229 buffer[3] += (opts->lookup_int('F'));
2230
2231 /* File system mode */
2232 buffer[3] += (opts->lookup_int('M'));
2233
2234 /* write first bootblock sector */
2235 ret = write_block(img, 0, buffer);
2236 if (ret) return ret;
2237
2238 /* reset with zeros */
2239 memset(buffer, 0, BSIZE);
2240
2241 /* write second bootblock sector */
2242 ret = write_block(img, 1, buffer);
2243 if (ret) return ret;
2244
2245 /* rootblock */
2246 memset(&root, 0, sizeof(root_block));
2247
2248 blocks = get_total_blocks(img);
2249
2250 root.chksum = 0;
2251 root.ht_size = TSIZE;
2252 root.bm_flag = -1;
2253 root.bm_pages[0] = blocks/2 + 1; /* generally it's located here */
2254
2255 time(&now);
2256 amiga_setup_time(now, &root.r);
2257 amiga_setup_time(now, &root.v);
2258 amiga_setup_time(now, &root.c);
2259
2260 /* volume name */
2261 if (!dskname.empty())
2262 {
2263 root.name_len = dskname.length();
2264 memcpy(&root.diskname, dskname.c_str(), root.name_len);
2265 }
2266 else
2267 {
2268 root.name_len = strlen("Empty");
2269 memcpy(&root.diskname, "Empty", root.name_len);
2270 }
2271
2272 /* write root block to disk */
2273 ret = write_root_block(img, &root);
2274 if (ret) return ret;
2275
2276 /* calculate block checksum */
2277 ret = fix_block_chksum(img, blocks/2);
2278 if (ret) return ret;
2279
2280 /* bitmap block */
2281 memset(&bm, 0xff, sizeof(bitmap_block));
2282
2283 /* write bitmap block to disk */
2284 ret = write_bitmap_block(img, root.bm_pages[0], &bm);
2285 if (ret) return ret;
2286
2287 /* set root and bitmap block as used */
2288 ret = bitmap_mark_used(img, blocks/2);
2289 if (ret) return ret;
2290
2291 ret = bitmap_mark_used(img, root.bm_pages[0]);
2292 if (ret) return ret;
2293
2294 /* write empty last block so that we don't get a truncated image */
2295 memset(buffer, 0, BSIZE);
2296
2297 ret = write_block(img, blocks - 1, buffer);
2298 if (ret) return ret;
2299
2300 f->stream = stream.release();
2301 return IMGTOOLERR_SUCCESS;
2302 }
2303
2304
amiga_image_createdir(imgtool::partition & partition,const char * path)2305 static imgtoolerr_t amiga_image_createdir(imgtool::partition &partition, const char *path)
2306 {
2307 imgtool::image &img(partition.image());
2308 imgtoolerr_t ret;
2309
2310 /* Create directories */
2311 ret = checkdir(img, path, get_total_blocks(img)/2);
2312 if (ret) return ret;
2313
2314 /* Update disk alteration date */
2315 ret = update_disk_alteration_date(img);
2316 if (ret) return ret;
2317
2318 return IMGTOOLERR_SUCCESS;
2319 }
2320
2321
amiga_image_getattrs(imgtool::partition & partition,const char * path,const uint32_t * attrs,imgtool_attribute * values)2322 static imgtoolerr_t amiga_image_getattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values)
2323 {
2324 return IMGTOOLERR_UNIMPLEMENTED;
2325 }
2326
2327
amiga_image_setattrs(imgtool::partition & partition,const char * path,const uint32_t * attrs,const imgtool_attribute * values)2328 static imgtoolerr_t amiga_image_setattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values)
2329 {
2330 return IMGTOOLERR_UNIMPLEMENTED;
2331 }
2332
2333
amiga_image_geticoninfo(imgtool::partition & partition,const char * path,imgtool_iconinfo * iconinfo)2334 static imgtoolerr_t amiga_image_geticoninfo(imgtool::partition &partition, const char *path, imgtool_iconinfo *iconinfo)
2335 {
2336 return IMGTOOLERR_UNIMPLEMENTED;
2337 }
2338
2339
amiga_image_suggesttransfer(imgtool::partition & partition,const char * fname,imgtool_transfer_suggestion * suggestions,size_t suggestions_length)2340 static imgtoolerr_t amiga_image_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
2341 {
2342 return IMGTOOLERR_UNIMPLEMENTED;
2343 }
2344
2345
2346
2347 /*****************************************************************************
2348 Create image options
2349 *****************************************************************************/
2350
2351
2352 OPTION_GUIDE_START(amiga_createimage_optionguide)
2353 OPTION_STRING( 'N', "name", "Volume name" )
2354 OPTION_ENUM_START( 'S', "density", "Density" )
2355 OPTION_ENUM( 0, "dd", "Double Density" )
2356 OPTION_ENUM( 1, "hd", "High Density" )
2357 OPTION_ENUM_END
2358 OPTION_ENUM_START( 'F', "filesystem", "File system" )
2359 OPTION_ENUM( 0, "ofs", "OldFileSystem" )
2360 OPTION_ENUM( 1, "ffs", "FastFileSystem" )
2361 OPTION_ENUM_END
2362 OPTION_ENUM_START( 'M', "mode", "File system options" )
2363 OPTION_ENUM( 0, "none", "None" )
2364 OPTION_ENUM( 2, "intl", "International Mode" )
2365 OPTION_ENUM( 4, "dirc", "International Mode with Directory Cache" )
2366 OPTION_ENUM_END
2367 OPTION_GUIDE_END
2368
2369
2370
2371 /*****************************************************************************
2372 Imgtool module declaration
2373 *****************************************************************************/
2374
2375
2376 /* Amiga floppy disk attributes */
amiga_floppy_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)2377 void amiga_floppy_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
2378 {
2379 switch(state)
2380 {
2381 /* --- the following bits of info are returned as 64-bit signed integers --- */
2382 case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(amiga_floppy); break;
2383 case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(amiga_iterator); break;
2384 case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break;
2385 case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break;
2386
2387 /* --- the following bits of info are returned as NULL-terminated strings --- */
2388 case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "amiga_floppy"); break;
2389 case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Amiga floppy disk image (OFS/FFS format)"); break;
2390 case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "adf"); break;
2391 case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
2392 case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), EOLN_LF); break;
2393 case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "S[0]-1;F[0]-1;M[0]/2/4"); break;
2394
2395 /* --- the following bits of info are returned as pointers to data or functions --- */
2396 case IMGTOOLINFO_PTR_OPEN: info->open = amiga_image_open; break;
2397 case IMGTOOLINFO_PTR_CLOSE: info->close = amiga_image_exit; break;
2398 case IMGTOOLINFO_PTR_READ_SECTOR: info->read_sector = amiga_image_read_sector; break;
2399 case IMGTOOLINFO_PTR_WRITE_SECTOR: info->write_sector = amiga_image_write_sector; break;
2400 case IMGTOOLINFO_PTR_CREATE: info->create = amiga_image_create; break;
2401 case IMGTOOLINFO_PTR_INFO: info->info = amiga_image_info; break;
2402 case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = amiga_image_beginenum; break;
2403 case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = amiga_image_nextenum; break;
2404 case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = amiga_image_freespace; break;
2405 case IMGTOOLINFO_PTR_READ_FILE: info->read_file = amiga_image_readfile; break;
2406 case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = amiga_image_writefile; break;
2407 case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = amiga_image_deletefile; break;
2408 case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = amiga_image_createdir; break;
2409 case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = amiga_image_getattrs; break;
2410 case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = amiga_image_setattrs; break;
2411 case IMGTOOLINFO_PTR_GET_ICON_INFO: info->get_iconinfo = amiga_image_geticoninfo; break;
2412 case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = amiga_image_suggesttransfer; break;
2413 case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &amiga_createimage_optionguide; break;
2414 case IMGTOOLINFO_PTR_CHARCONVERTER: info->charconverter = &imgtool::charconverter_iso_8859_1; break;
2415 }
2416 }
2417