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