summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/tools/imgtool/modules/pc_hard.c
blob: bab9b8e9bc2bc74e3268ee46c8da97c23307c9fe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
/****************************************************************************

    pc_hard.c

    PC hard drives

*****************************************************************************

  Master boot record format:

  Offset  Length  Description
  ------  ------  -----------
       0     446  Boot machine code
     446      16  Partion #1 info
     462      16  Partion #2 info
     478      16  Partion #3 info
     494      16  Partion #4 info
     510       2  Magic bytes (0x55 0xAA)


  Partition info format:

  Offset  Length  Description
  ------  ------  -----------
       0       1  Bootable (0x80=bootable 0x00=not bootable)
       1       1  Starting head
       2       1  Starting sector (bits 5-0) and high bits of starting track (bits 6-5)
       3       1  Low bits of starting track
       4       1  Partition type:
                       0x00     Unused
                       0x?1     FAT12   (0-15 MB)
                       0x?2     XENIX
                       0x?4     FAT16   (16-32 MB)
                       0x?6     FAT16`  (32 MB-2 GB)
                       0x?7     HPFS or NTFS
                       0x?A     Boot Manager
                       0x?B     FAT32   (512 MB-2 TB)
                       0x?C     FAT32   (512 MB-2 TB LBA)
                       0x1?     OS/2 Boot manager/Win95 hidden
                       0xC?     DR-DOS secured partition
                       0xD?     Multiuser DOS secured partition
                       0xE?     SpeedStor extended partition
       5       1  Ending head
       6       1  Ending sector (bits 5-0) and high bits of ending track (bits 6-5)
       7       1  Low bits of ending track
       8       4  Sector index of beginning of partition
      12       4  Total sectors in partition

****************************************************************************/
#include "imgtool.h"
#include "formats/imageutl.h"
#include "imghd.h"

#define FAT_SECLEN  512

static OPTION_GUIDE_START( pc_chd_create_optionguide )
	OPTION_INT('T', "cylinders",    "Cylinders" )
	OPTION_INT('H', "heads",        "Heads" )
	OPTION_INT('S', "sectors",      "Sectors" )
OPTION_GUIDE_END

static const char pc_chd_create_optionspec[] = "H1-[16]S1-[32]-63T10/20/30/40/50/60/70/80/90/[100]/110/120/130/140/150/160/170/180/190/200";

static const char fat8_string[8]  = { 'F', 'A', 'T', ' ', ' ', ' ', ' ', ' ' };
static const char fat12_string[8] = { 'F', 'A', 'T', '1', '2', ' ', ' ', ' ' };
static const char fat16_string[8] = { 'F', 'A', 'T', '1', '6', ' ', ' ', ' ' };
//static const char fat32_string[8] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' };

/* imports from fat.c */
extern void fat_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info);


struct pc_chd_image_info
{
	struct mess_hard_disk_file hard_disk;

	struct
	{
		unsigned int corrupt : 1;
		UINT8 partition_type;
		UINT32 fat_bits;
		UINT32 starting_track;
		UINT32 starting_head;
		UINT32 starting_sector;
		UINT32 ending_track;
		UINT32 ending_head;
		UINT32 ending_sector;
		UINT32 sector_index;
		UINT32 total_sectors;
	} partitions[4];
};



static pc_chd_image_info *pc_chd_get_image_info(imgtool_image *image)
{
	return (pc_chd_image_info *) imgtool_image_extra_bytes(image);
}



static void pc_chd_locate_block(imgtool_image *image, UINT64 block, UINT32 *cylinder, UINT32 *head, UINT32 *sector)
{
	pc_chd_image_info *info;
	const hard_disk_info *hd_info;

	info = pc_chd_get_image_info(image);
	hd_info = imghd_get_header(&info->hard_disk);

	*sector = block % hd_info->sectors;
	*head = (block / hd_info->sectors) % hd_info->heads;
	*cylinder = block / hd_info->sectors / hd_info->heads;
}



static imgtoolerr_t pc_chd_partition_create(imgtool_image *image, int partition_index, UINT64 first_block, UINT64 block_count)
{
	imgtoolerr_t err;
	UINT8 header_block[FAT_SECLEN];
	UINT8 partition_block[FAT_SECLEN];
	UINT8 partition_type;
	UINT8 *fat_type;
	UINT8 *partition_entry;
	UINT32 first_cylinder, first_head, first_sector;
	UINT32 last_cylinder, last_head, last_sector;
	imgtool_class imgclass = { fat_get_info };
	imgtoolerr_t (*fat_partition_create)(imgtool_image *image, UINT64 first_block, UINT64 block_count);

	/* sanity checks */
	assert((partition_index >= 0) && (partition_index <= 3));

	/* compute geometry */
	pc_chd_locate_block(image, first_block, &first_cylinder, &first_head, &first_sector);
	pc_chd_locate_block(image, first_block + block_count - 1, &last_cylinder, &last_head, &last_sector);

	/* load fat_partition_create */
	fat_partition_create = (imgtoolerr_t (*)(imgtool_image *, UINT64, UINT64))
		imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_PARTITION);

	/* first create the actual partition */
	err = fat_partition_create(image, first_block, block_count);
	if (err)
		goto done;

	/* read the first block of the partition, to determine the type of FAT */
	err = imgtool_image_read_block(image, first_block, partition_block);
	if (err)
		goto done;
	fat_type = &partition_block[54];
	if (!memcmp(fat_type, fat8_string, sizeof(fat8_string)))
		partition_type = 0x01;
	else if (!memcmp(fat_type, fat12_string, sizeof(fat12_string)))
		partition_type = 0x01;
	else if ((!memcmp(fat_type, fat16_string, sizeof(fat16_string))) && (block_count < 32*1024*1024/FAT_SECLEN))
		partition_type = 0x04;
	else if ((!memcmp(fat_type, fat16_string, sizeof(fat16_string))) && (block_count >= 32*1024*1024/FAT_SECLEN))
		partition_type = 0x06;
	else
		partition_type = 0x0B;

	/* read the partition header */
	err = imgtool_image_read_block(image, 0, header_block);
	if (err)
		goto done;

	/* fill out the partition entry */
	partition_entry = &header_block[446 + (partition_index * 16)];
	place_integer_le(partition_entry,  0, 1, 0x80);
	place_integer_le(partition_entry,  1, 1, first_head);
	place_integer_le(partition_entry,  2, 1, ((first_sector & 0x3F) | (first_cylinder >> 8 << 2)));
	place_integer_le(partition_entry,  3, 1, first_cylinder);
	place_integer_le(partition_entry,  4, 1, partition_type);
	place_integer_le(partition_entry,  5, 1, last_head);
	place_integer_le(partition_entry,  6, 1, ((last_sector & 0x3F) | (last_cylinder >> 8 << 2)));
	place_integer_le(partition_entry,  7, 1, last_cylinder);
	place_integer_le(partition_entry,  8, 4, first_block);
	place_integer_le(partition_entry, 12, 4, block_count);

	/* write the partition header */
	err = imgtool_image_write_block(image, 0, header_block);
	if (err)
		goto done;

done:
	return err;
}



static imgtoolerr_t pc_chd_read_partition_header(imgtool_image *image)
{
	imgtoolerr_t err;
	int i;
	const UINT8 *partition_info;
	pc_chd_image_info *info;
	UINT8 buffer[FAT_SECLEN];

	info = pc_chd_get_image_info(image);

	/* read the initial block */
	err = imgtool_image_read_block(image, 0, buffer);
	if (err)
		return err;

	/* magic bytes present? */
	if ((buffer[510] != 0x55) || (buffer[511] != 0xAA))
		return IMGTOOLERR_CORRUPTIMAGE;

	for (i = 0; i < ARRAY_LENGTH(info->partitions); i++)
	{
		partition_info = &buffer[446 + i * 16];

		info->partitions[i].partition_type  = partition_info[4];
		info->partitions[i].starting_head   = partition_info[1];
		info->partitions[i].starting_track  = ((partition_info[2] << 2) & 0xFF00) | partition_info[3];
		info->partitions[i].starting_sector = partition_info[2] & 0x3F;
		info->partitions[i].ending_head     = partition_info[5];
		info->partitions[i].ending_track    = ((partition_info[6] << 2) & 0xFF00) | partition_info[7];
		info->partitions[i].ending_sector   = partition_info[6] & 0x3F;

		info->partitions[i].sector_index    = pick_integer_le(partition_info,  8, 4);
		info->partitions[i].total_sectors   = pick_integer_le(partition_info, 12, 4);

		if (info->partitions[i].starting_track > info->partitions[i].ending_track)
			return IMGTOOLERR_CORRUPTIMAGE;
	}
	return IMGTOOLERR_SUCCESS;
}



static imgtoolerr_t pc_chd_image_create(imgtool_image *image, imgtool_stream *f, option_resolution *opts)
{
	imgtoolerr_t err;
	UINT32 cylinders, heads, sectors;
	pc_chd_image_info *info;
	UINT8 header_block[FAT_SECLEN];

	cylinders = option_resolution_lookup_int(opts, 'T');
	heads = option_resolution_lookup_int(opts, 'H');
	sectors = option_resolution_lookup_int(opts, 'S');

	info = pc_chd_get_image_info(image);

	/* create the hard disk image */
	err = imghd_create(f, 0, cylinders, heads, sectors, FAT_SECLEN);
	if (err)
		goto done;

	err = imghd_open(f, &info->hard_disk);
	if (err)
		goto done;

	/* set up partition header block */
	memset(header_block, 0, sizeof(header_block));
	header_block[510] = 0x55;
	header_block[511] = 0xAA;
	err = imgtool_image_write_block(image, 0, header_block);
	if (err)
		goto done;

	err = pc_chd_partition_create(image, 0, 1, cylinders * heads * sectors - 1);
	if (err)
		goto done;

	err = pc_chd_read_partition_header(image);
	if (err)
		goto done;

done:
	if (err)
		imghd_close(&info->hard_disk);
	return err;
}



static imgtoolerr_t pc_chd_image_open(imgtool_image *image, imgtool_stream *stream)
{
	imgtoolerr_t err;
	pc_chd_image_info *info;

	info = pc_chd_get_image_info(image);

	/* open the hard drive */
	err = imghd_open(stream, &info->hard_disk);
	if (err)
		return err;

	err = pc_chd_read_partition_header(image);
	if (err)
		return err;

	return IMGTOOLERR_SUCCESS;
}



static void pc_chd_image_close(imgtool_image *image)
{
	pc_chd_image_info *info;
	info = pc_chd_get_image_info(image);
	imghd_close(&info->hard_disk);
}



static imgtoolerr_t pc_chd_image_get_geometry(imgtool_image *image, UINT32 *tracks, UINT32 *heads, UINT32 *sectors)
{
	pc_chd_image_info *info;
	const hard_disk_info *hd_info;

	info = pc_chd_get_image_info(image);
	hd_info = imghd_get_header(&info->hard_disk);

	*tracks = hd_info->cylinders;
	*heads = hd_info->heads;
	*sectors = hd_info->sectors;
	return IMGTOOLERR_SUCCESS;
}



static imgtoolerr_t pc_chd_image_getsectorsize(imgtool_image *image, UINT32 track, UINT32 head, UINT32 sector, UINT32 *sector_size)
{
	pc_chd_image_info *info;
	info = pc_chd_get_image_info(image);
	*sector_size = imghd_get_header(&info->hard_disk)->sectorbytes;
	return IMGTOOLERR_SUCCESS;
}



static UINT32 pc_chd_calc_lbasector(pc_chd_image_info *info, UINT32 track, UINT32 head, UINT32 sector)
{
	UINT32 lbasector;
	const hard_disk_info *hd_info;

	hd_info = imghd_get_header(&info->hard_disk);
	lbasector = track;
	lbasector *= hd_info->heads;
	lbasector += head;
	lbasector *= hd_info->sectors;
	lbasector += sector;
	return lbasector;
}



static imgtoolerr_t pc_chd_image_readsector(imgtool_image *image, UINT32 track, UINT32 head, UINT32 sector, void *buffer, size_t len)
{
	pc_chd_image_info *info;
	info = pc_chd_get_image_info(image);
	return imghd_read(&info->hard_disk,
		pc_chd_calc_lbasector(info, track, head, sector),
		buffer);
}



static imgtoolerr_t pc_chd_image_writesector(imgtool_image *image, UINT32 track, UINT32 head, UINT32 sector, const void *buffer, size_t len, int ddam)
{
	pc_chd_image_info *info;
	info = pc_chd_get_image_info(image);
	return imghd_write(&info->hard_disk,
		pc_chd_calc_lbasector(info, track, head, sector),
		buffer);
}



static imgtoolerr_t pc_chd_image_readblock(imgtool_image *image, void *buffer, UINT64 block)
{
	pc_chd_image_info *info;
	info = pc_chd_get_image_info(image);
	return imghd_read(&info->hard_disk, block, buffer);
}



static imgtoolerr_t pc_chd_image_writeblock(imgtool_image *image, const void *buffer, UINT64 block)
{
	pc_chd_image_info *info;
	info = pc_chd_get_image_info(image);
	return imghd_write(&info->hard_disk, block, buffer);
}



static imgtoolerr_t pc_chd_list_partitions(imgtool_image *image, imgtool_partition_info *partitions, size_t len)
{
	pc_chd_image_info *info;
	size_t i;

	info = pc_chd_get_image_info(image);

	for (i = 0; i < MIN(4, len); i++)
	{
		partitions[i].base_block    = info->partitions[i].sector_index;
		partitions[i].block_count   = info->partitions[i].total_sectors;

		switch(info->partitions[i].partition_type)
		{
			case 0x00:  /* Empty Partition */
				partitions[i].get_info = NULL;
				break;

			case 0x01:  /* FAT12 */
			case 0x04:  /* FAT16 (-32 MB) */
			case 0x06:  /* FAT16 (32+ MB) */
			case 0x0B:  /* FAT32 */
			case 0x0C:  /* FAT32 (LBA Mapped) */
			case 0x0E:  /* FAT16 (LBA Mapped) */
			case 0x11:  /* OS/2 FAT12 */
			case 0x14:  /* OS/2 FAT16 (-32 MB) */
			case 0x16:  /* OS/2 FAT16 (32+ MB) */
			case 0x1B:  /* Hidden Win95 FAT32 */
			case 0x1C:  /* Hidden Win95 FAT32 (LBA Mapped) */
			case 0x1D:  /* Hidden Win95 FAT16 (LBA Mapped) */
			case 0xC1:  /* DR-DOS FAT12 */
			case 0xC4:  /* DR-DOS FAT16 (-32 MB) */
			case 0xC6:  /* DR-DOS FAT16 (32+ MB) */
			case 0xD1:  /* Old Multiuser DOS FAT12 */
			case 0xD4:  /* Old Multiuser DOS FAT16 (-32 MB) */
			case 0xD6:  /* Old Multiuser DOS FAT16 (32+ MB) */
				partitions[i].get_info = fat_get_info;
				break;

			default:
				partitions[i].get_info = unknown_partition_get_info;
				break;
		}
	}
	return IMGTOOLERR_SUCCESS;
}



void pc_chd_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
{
	switch(state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case IMGTOOLINFO_INT_BLOCK_SIZE:                    info->i = FAT_SECLEN; break;
		case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES:             info->i = sizeof(pc_chd_image_info); break;
		case IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS:   info->i = 1; break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case IMGTOOLINFO_STR_NAME:                          strcpy(info->s = imgtool_temp_str(), "pc_chd"); break;
		case IMGTOOLINFO_STR_DESCRIPTION:                   strcpy(info->s = imgtool_temp_str(), "PC CHD disk image"); break;
		case IMGTOOLINFO_STR_FILE_EXTENSIONS:               strcpy(info->s = imgtool_temp_str(), "chd"); break;
		case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC:           strcpy(info->s = imgtool_temp_str(), pc_chd_create_optionspec); break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case IMGTOOLINFO_PTR_CREATE:                        info->create = pc_chd_image_create; break;
		case IMGTOOLINFO_PTR_OPEN:                          info->open = pc_chd_image_open; break;
		case IMGTOOLINFO_PTR_CLOSE:                         info->close = pc_chd_image_close; break;
		case IMGTOOLINFO_PTR_READ_SECTOR:                   info->read_sector = pc_chd_image_readsector; break;
		case IMGTOOLINFO_PTR_WRITE_SECTOR:                  info->write_sector = pc_chd_image_writesector; break;
		case IMGTOOLINFO_PTR_READ_BLOCK:                    info->read_block = pc_chd_image_readblock; break;
		case IMGTOOLINFO_PTR_WRITE_BLOCK:                   info->write_block = pc_chd_image_writeblock; break;
		case IMGTOOLINFO_PTR_GET_SECTOR_SIZE:               info->get_sector_size = pc_chd_image_getsectorsize; break;
		case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE:          info->createimage_optguide = pc_chd_create_optionguide; break;
		case IMGTOOLINFO_PTR_GET_GEOMETRY:                  info->get_geometry = pc_chd_image_get_geometry; break;
		case IMGTOOLINFO_PTR_LIST_PARTITIONS:               info->list_partitions = pc_chd_list_partitions; break;
	}
}