1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods, Raphael Nabet
3 /*
4     Code to interface the MESS image code with MAME's harddisk core.
5 
6     We do not support diff files as it will involve some changes in the MESS
7     image code.
8 
9     Raphael Nabet 2003
10 */
11 
12 #include "imgtool.h"
13 #include "harddisk.h"
14 #include "imghd.h"
15 
16 
map_chd_error(chd_error chderr)17 static imgtoolerr_t map_chd_error(chd_error chderr)
18 {
19 	imgtoolerr_t err;
20 
21 	switch(chderr)
22 	{
23 		case CHDERR_NONE:
24 			err = IMGTOOLERR_SUCCESS;
25 			break;
26 		case CHDERR_OUT_OF_MEMORY:
27 			err = IMGTOOLERR_OUTOFMEMORY;
28 			break;
29 		case CHDERR_FILE_NOT_WRITEABLE:
30 			err = IMGTOOLERR_READONLY;
31 			break;
32 		case CHDERR_NOT_SUPPORTED:
33 			err = IMGTOOLERR_UNIMPLEMENTED;
34 			break;
35 		default:
36 			err = IMGTOOLERR_UNEXPECTED;
37 			break;
38 	}
39 	return err;
40 }
41 
42 
43 
44 /*
45     imghd_create()
46 
47     Create a MAME HD image
48 */
imghd_create(imgtool::stream & stream,uint32_t hunksize,uint32_t cylinders,uint32_t heads,uint32_t sectors,uint32_t seclen)49 imgtoolerr_t imghd_create(imgtool::stream &stream, uint32_t hunksize, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t seclen)
50 {
51 	imgtoolerr_t err = IMGTOOLERR_SUCCESS;
52 	chd_file chd;
53 	chd_error rc;
54 	chd_codec_type compression[4] = { CHD_CODEC_NONE };
55 
56 	/* sanity check args -- see parse_hunk_size() in src/lib/util/chd.cpp */
57 	if (hunksize > (1024 * 1024))
58 	{
59 		err = IMGTOOLERR_PARAMCORRUPT;
60 		return err;
61 	}
62 	if (hunksize <= 0)
63 		hunksize = 4096;    /* default value */
64 
65 	/* bail if we are read only */
66 	if (stream.is_read_only())
67 	{
68 		err = IMGTOOLERR_READONLY;
69 		return err;
70 	}
71 
72 	/* calculations */
73 	const uint64_t logicalbytes = (uint64_t)cylinders * heads * sectors * seclen;
74 
75 	/* create the new hard drive */
76 	rc = chd.create(*stream.core_file(), logicalbytes, hunksize, seclen, compression);
77 	if (rc != CHDERR_NONE)
78 	{
79 		err = map_chd_error(rc);
80 		return err;
81 	}
82 
83 	/* write the metadata */
84 	const std::string metadata = string_format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, seclen);
85 	err = (imgtoolerr_t)chd.write_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
86 	if (rc != CHDERR_NONE)
87 	{
88 		err = map_chd_error(rc);
89 		return err;
90 	}
91 
92 	/* alloc and zero buffer */
93 	std::vector<uint8_t> cache;
94 	cache.resize(hunksize);
95 	memset(&cache[0], 0, hunksize);
96 
97 	/* zero out every hunk */
98 	const int totalhunks = (logicalbytes + hunksize - 1) / hunksize;
99 	for (int hunknum = 0; hunknum < totalhunks; hunknum++)
100 	{
101 		rc = chd.write_units(hunknum, &cache[0]);
102 		if (rc)
103 		{
104 			err = IMGTOOLERR_WRITEERROR;
105 			return err;
106 		}
107 	}
108 
109 	return err;
110 }
111 
112 
113 
114 /*
115     imghd_open()
116 
117     Open stream as a MAME HD image
118 */
imghd_open(imgtool::stream & stream,struct mess_hard_disk_file * hard_disk)119 imgtoolerr_t imghd_open(imgtool::stream &stream, struct mess_hard_disk_file *hard_disk)
120 {
121 	chd_error chderr;
122 	imgtoolerr_t err = IMGTOOLERR_SUCCESS;
123 
124 	hard_disk->hard_disk = nullptr;
125 
126 	chderr = hard_disk->chd.open(*stream.core_file(), !stream.is_read_only());
127 	if (chderr)
128 	{
129 		err = map_chd_error(chderr);
130 		goto done;
131 	}
132 
133 	hard_disk->hard_disk = hard_disk_open(&hard_disk->chd);
134 	if (!hard_disk->hard_disk)
135 	{
136 		err = IMGTOOLERR_UNEXPECTED;
137 		goto done;
138 	}
139 	hard_disk->stream = &stream;
140 
141 done:
142 	if (err)
143 		imghd_close(hard_disk);
144 	return err;
145 }
146 
147 
148 
149 /*
150     imghd_close()
151 
152     Close MAME HD image
153 */
imghd_close(struct mess_hard_disk_file * disk)154 void imghd_close(struct mess_hard_disk_file *disk)
155 {
156 	if (disk->hard_disk)
157 	{
158 		hard_disk_close(disk->hard_disk);
159 		disk->hard_disk = nullptr;
160 	}
161 	if (disk->stream)
162 	{
163 		delete disk->stream;
164 		disk->stream = nullptr;
165 	}
166 }
167 
168 
169 
170 /*
171     imghd_read()
172 
173     Read sector(s) from MAME HD image
174 */
imghd_read(struct mess_hard_disk_file * disk,uint32_t lbasector,void * buffer)175 imgtoolerr_t imghd_read(struct mess_hard_disk_file *disk, uint32_t lbasector, void *buffer)
176 {
177 	uint32_t reply;
178 	reply = hard_disk_read(disk->hard_disk, lbasector, buffer);
179 	return (imgtoolerr_t)(reply ? IMGTOOLERR_SUCCESS : map_chd_error((chd_error)reply));
180 }
181 
182 
183 
184 /*
185     imghd_write()
186 
187     Write sector(s) from MAME HD image
188 */
imghd_write(struct mess_hard_disk_file * disk,uint32_t lbasector,const void * buffer)189 imgtoolerr_t imghd_write(struct mess_hard_disk_file *disk, uint32_t lbasector, const void *buffer)
190 {
191 	uint32_t reply;
192 	reply = hard_disk_write(disk->hard_disk, lbasector, buffer);
193 	return (imgtoolerr_t)(reply ? IMGTOOLERR_SUCCESS : map_chd_error((chd_error)reply));
194 }
195 
196 
197 
198 /*
199     imghd_get_header()
200 
201     Return pointer to the header of MAME HD image
202 */
imghd_get_header(struct mess_hard_disk_file * disk)203 const hard_disk_info *imghd_get_header(struct mess_hard_disk_file *disk)
204 {
205 	const hard_disk_info *reply;
206 	reply = hard_disk_get_info(disk->hard_disk);
207 	return reply;
208 }
209 
210 
211 static imgtoolerr_t mess_hd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions);
212 
213 enum
214 {
215 	mess_hd_createopts_blocksize = 'B',
216 	mess_hd_createopts_cylinders = 'C',
217 	mess_hd_createopts_heads     = 'D',
218 	mess_hd_createopts_sectors   = 'E',
219 	mess_hd_createopts_seclen    = 'F'
220 };
221 
222 OPTION_GUIDE_START( mess_hd_create_optionguide )
223 	OPTION_INT(mess_hd_createopts_blocksize, "blocksize", "Sectors Per Block" )
224 	OPTION_INT(mess_hd_createopts_cylinders, "cylinders", "Cylinders" )
225 	OPTION_INT(mess_hd_createopts_heads, "heads",   "Heads" )
226 	OPTION_INT(mess_hd_createopts_sectors, "sectors", "Total Sectors" )
227 	OPTION_INT(mess_hd_createopts_seclen, "seclen", "Sector Bytes" )
228 OPTION_GUIDE_END
229 
230 #define mess_hd_create_optionspecs "B1-[4]-2048;C1-[32]-65536;D1-[8]-64;E1-[128]-4096;F128/256/[512]/1024/2048/4096/8192/16384/32768/65536"
231 
232 
hd_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)233 void hd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
234 {
235 	switch(state)
236 	{
237 		case IMGTOOLINFO_STR_NAME:                          strcpy(info->s = imgtool_temp_str(), "mess_hd"); break;
238 		case IMGTOOLINFO_STR_DESCRIPTION:                   strcpy(info->s = imgtool_temp_str(), "MESS hard disk image"); break;
239 		case IMGTOOLINFO_STR_FILE_EXTENSIONS:               strcpy(info->s = imgtool_temp_str(), "hd"); break;
240 
241 		case IMGTOOLINFO_PTR_CREATE:                        info->create = mess_hd_image_create; break;
242 
243 		case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE:          info->createimage_optguide = &mess_hd_create_optionguide; break;
244 		case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC:           strcpy(info->s = imgtool_temp_str(), mess_hd_create_optionspecs); break;
245 	}
246 }
247 
248 
249 
mess_hd_image_create(imgtool::image & image,imgtool::stream::ptr && stream,util::option_resolution * createoptions)250 static imgtoolerr_t mess_hd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions)
251 {
252 	uint32_t  blocksize, cylinders, heads, sectors, seclen;
253 
254 	/* read options */
255 	blocksize = createoptions->lookup_int(mess_hd_createopts_blocksize);
256 	cylinders = createoptions->lookup_int(mess_hd_createopts_cylinders);
257 	heads = createoptions->lookup_int(mess_hd_createopts_heads);
258 	sectors = createoptions->lookup_int(mess_hd_createopts_sectors);
259 	seclen = createoptions->lookup_int(mess_hd_createopts_seclen);
260 
261 	return imghd_create(*stream.get(), blocksize * seclen, cylinders, heads, sectors, seclen);
262 }
263