fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
at master 433 lines 8.2 kB view raw
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}