fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/drivers/block/blkchd.c *
7 * Created: 2017-12-09 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2017-2019 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <drivers/block/block.h>
24#include <drivers/block/blkchd.h>
25
26#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29
30
31#define CHD_MAGIC1 0x4d436f6d
32#define CHD_MAGIC2 0x70724844
33
34
35static
36void chd_cache_init (disk_chd_t *img)
37{
38 unsigned i;
39
40 for (i = 0; i < CHD_CACHE_SIZE; i++) {
41 img->cache_valid[i] = 0;
42 img->cache_src[i] = 0;
43 img->cache_dst[i] = 0;
44 }
45}
46
47static
48int chd_cache_map (disk_chd_t *img, uint64_t *ofs, uint64_t *max, int alloc)
49{
50 unsigned idx;
51 uint32_t hunk, hofs;
52 uint64_t next;
53 unsigned char buf[4];
54
55 hunk = *ofs / img->hunk_size;
56 hofs = *ofs % img->hunk_size;
57
58 if (hunk >= img->hunks) {
59 return (1);
60 }
61
62 idx = hunk % CHD_CACHE_SIZE;
63
64 if ((img->cache_valid[idx] == 0) || (img->cache_src[idx] != hunk)) {
65 if (dsk_read (img->fp, buf, img->map_offset + 4 * hunk, 4)) {
66 return (1);
67 }
68
69 img->cache_src[idx] = hunk;
70 img->cache_dst[idx] = dsk_get_uint32_be (buf, 0);
71 img->cache_valid[idx] = 1;
72 }
73
74 if ((img->cache_dst[idx] == 0) && alloc) {
75 next = img->hunk_size * (uint64_t) img->next_hunk;
76
77 if (dsk_set_filesize (img->fp, next + img->hunk_size)) {
78 return (1);
79 }
80
81 img->cache_src[idx] = hunk;
82 img->cache_dst[idx] = img->next_hunk;
83 img->cache_valid[idx] = 1;
84
85 img->next_hunk += 1;
86
87 dsk_set_uint32_be (buf, 0, img->cache_dst[idx]);
88
89 if (dsk_write (img->fp, buf, img->map_offset + 4 * hunk, 4)) {
90 return (1);
91 }
92 }
93
94 if (img->cache_dst[idx] == 0) {
95 *ofs = 0;
96 }
97 else {
98 *ofs = img->hunk_size * (uint64_t) img->cache_dst[idx] + hofs;
99 }
100
101 *max = img->hunk_size - hofs;
102
103 return (0);
104}
105
106static
107int chd_read (disk_t *dsk, void *buf, uint32_t i, uint32_t n)
108{
109 disk_chd_t *img;
110 unsigned char *p;
111 uint32_t blk;
112 uint64_t ofs, max;
113
114 img = dsk->ext;
115
116 p = buf;
117
118 while (n > 0) {
119 ofs = 512ULL * i;
120
121 if (chd_cache_map (img, &ofs, &max, 0)) {
122 return (1);
123 }
124
125 blk = max / 512;
126
127 if (blk == 0) {
128 return (1);
129 }
130
131 if (blk > n) {
132 blk = n;
133 }
134
135 if (ofs == 0) {
136 memset (p, 0, 512 * blk);
137 }
138 else {
139 if (dsk_read (img->fp, p, ofs, 512 * blk)) {
140 return (1);
141 }
142 }
143
144 i += blk;
145 n -= blk;
146 p += 512 * blk;
147 }
148
149 return (0);
150}
151
152static
153int chd_write (disk_t *dsk, const void *buf, uint32_t i, uint32_t n)
154{
155 disk_chd_t *img;
156 const unsigned char *p;
157 uint32_t blk;
158 uint64_t ofs, max;
159
160 if (dsk->readonly) {
161 return (1);
162 }
163
164 img = dsk->ext;
165
166 p = buf;
167
168 while (n > 0) {
169 ofs = 512ULL * i;
170
171 if (chd_cache_map (img, &ofs, &max, 1)) {
172 return (1);
173 }
174
175 blk = max / 512;
176
177 if ((blk == 0) || (ofs == 0)) {
178 return (1);
179 }
180
181 if (blk > n) {
182 blk = n;
183 }
184
185 if (dsk_write (img->fp, p, ofs, 512 * blk)) {
186 return (1);
187 }
188
189 i += blk;
190 n -= blk;
191 p += 512 * blk;
192 }
193
194 return (0);
195}
196
197
198static
199int chd_get_msg (disk_t *dsk, const char *msg, char *val, unsigned max)
200{
201 return (1);
202}
203
204static
205int chd_set_msg (disk_t *dsk, const char *msg, const char *val)
206{
207 if (strcmp (msg, "commit") == 0) {
208 return (0);
209 }
210
211 return (1);
212}
213
214static
215void chd_del (disk_t *dsk)
216{
217 disk_chd_t *img;
218
219 img = dsk->ext;
220
221 if (img->fp != NULL) {
222 fclose (img->fp);
223 }
224
225 free (img->map);
226 free (img);
227}
228
229disk_t *dsk_chd_open_fp (FILE *fp, int ro)
230{
231 disk_chd_t *img;
232 uint32_t hunk_size, hunks;
233 uint64_t image_size, file_size;
234 unsigned char buf[128];
235
236 if (dsk_read (fp, buf, 0, 124)) {
237 return (NULL);
238 }
239
240 if (dsk_get_uint32_be (buf, 0) != CHD_MAGIC1) {
241 return (NULL);
242 }
243
244 if (dsk_get_uint32_be (buf, 4) != CHD_MAGIC2) {
245 return (NULL);
246 }
247
248 /* header size */
249 if (dsk_get_uint32_be (buf, 8) < 124) {
250 return (NULL);
251 }
252
253 /* version */
254 if (dsk_get_uint32_be (buf, 12) != 5) {
255 fprintf (stderr, "chd: unsupported version\n");
256 return (NULL);
257 }
258
259 /* compressor */
260 if (dsk_get_uint32_be (buf, 16) != 0) {
261 fprintf (stderr, "chd: unsupported compressed image\n");
262 return (NULL);
263 }
264
265 image_size = dsk_get_uint64_be (buf, 32);
266 hunk_size = dsk_get_uint32_be (buf, 56);
267
268 if ((image_size & 511) || (hunk_size & 511) || (hunk_size < 512)) {
269 return (NULL);
270 }
271
272 hunks = (image_size + hunk_size - 1) / hunk_size;
273
274 if ((img = malloc (sizeof (disk_chd_t))) == NULL) {
275 return (NULL);
276 }
277
278 dsk_init (&img->dsk, img, image_size / 512, 0, 0, 0);
279 dsk_set_type (&img->dsk, PCE_DISK_CHD);
280 dsk_set_readonly (&img->dsk, ro);
281
282 dsk_get_filesize (fp, &file_size);
283
284 img->next_hunk = (file_size + hunk_size - 1) / hunk_size;
285
286 img->dsk.del = chd_del;
287 img->dsk.read = chd_read;
288 img->dsk.write = chd_write;
289 img->dsk.get_msg = chd_get_msg;
290 img->dsk.set_msg = chd_set_msg;
291
292 img->fp = fp;
293
294 memcpy (img->header, buf, 124);
295
296 img->map_offset = dsk_get_uint64_be (buf, 40);
297 img->image_size = image_size;
298 img->hunk_size = dsk_get_uint32_be (buf, 56);
299 img->hunks = hunks;
300
301 chd_cache_init (img);
302
303 return (&img->dsk);
304}
305
306disk_t *dsk_chd_open (const char *fname, int ro)
307{
308 disk_t *dsk;
309 FILE *fp;
310
311 if (ro) {
312 fp = fopen (fname, "rb");
313 }
314 else {
315 if ((fp = fopen (fname, "r+b")) == NULL) {
316 fp = fopen (fname, "rb");
317 ro = 1;
318 }
319 }
320
321 if (fp == NULL) {
322 return (NULL);
323 }
324
325 dsk = dsk_chd_open_fp (fp, ro);
326
327 if (dsk == NULL) {
328 fclose (fp);
329 return (NULL);
330 }
331
332 dsk_guess_geometry (dsk);
333
334 dsk_set_fname (dsk, fname);
335
336 return (dsk);
337}
338
339int dsk_chd_create_fp (FILE *fp, uint32_t n, uint32_t c, uint32_t h, uint32_t s)
340{
341 unsigned char buf[256];
342 uint32_t k, cnt;
343 uint32_t hunk_size;
344 uint64_t image_size;
345 uint64_t ofs;
346
347 memset (buf, 0, 256);
348
349 image_size = 512 * (uint64_t) n;
350 hunk_size = 4096;
351
352 dsk_set_uint32_be (buf, 0, CHD_MAGIC1);
353 dsk_set_uint32_be (buf, 4, CHD_MAGIC2);
354 dsk_set_uint32_be (buf, 8, 124);
355 dsk_set_uint32_be (buf, 12, 5);
356 dsk_set_uint64_be (buf, 32, image_size);
357 dsk_set_uint64_be (buf, 40, 128);
358 dsk_set_uint32_be (buf, 56, hunk_size);
359 dsk_set_uint32_be (buf, 60, 512);
360
361 if (dsk_write (fp, buf, 0, 124)) {
362 return (1);
363 }
364
365 memset (buf, 0, 256);
366
367 ofs = 128;
368 cnt = 4 * ((image_size + hunk_size - 1) / hunk_size);
369
370 while (cnt > 0) {
371 k = (cnt < 256) ? cnt : 256;
372
373 if (dsk_write (fp, buf, ofs, k)) {
374 return (1);
375 }
376
377 cnt -= k;
378 ofs += k;
379 }
380
381 return (0);
382}
383
384int dsk_chd_create (const char *fname, uint32_t n, uint32_t c, uint32_t h, uint32_t s)
385{
386 int r;
387 FILE *fp;
388
389 if ((fp = fopen (fname, "wb")) == NULL) {
390 return (1);
391 }
392
393 r = dsk_chd_create_fp (fp, n, c, h, s);
394
395 fclose (fp);
396
397 return (r);
398}
399
400int dsk_chd_probe_fp (FILE *fp)
401{
402 unsigned char buf[16];
403
404 if (dsk_read (fp, buf, 0, 16)) {
405 return (0);
406 }
407
408 if (dsk_get_uint32_be (buf, 0) != CHD_MAGIC1) {
409 return (0);
410 }
411
412 if (dsk_get_uint32_be (buf, 4) != CHD_MAGIC2) {
413 return (0);
414 }
415
416 return (1);
417}
418
419int dsk_chd_probe (const char *fname)
420{
421 int r;
422 FILE *fp;
423
424 if ((fp = fopen (fname, "rb")) == NULL) {
425 return (0);
426 }
427
428 r = dsk_chd_probe_fp (fp);
429
430 fclose (fp);
431
432 return (r);
433}