Develop and Download Open Source Software

Browse CVS Repository

Contents of /mame32jp/mame32jp/src/harddisk.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.5 - (show annotations) (download) (as text)
Wed Apr 24 03:53:20 2002 UTC (21 years, 11 months ago) by zero
Branch: MAIN
CVS Tags: ver_0_60_1, ver0_59_13, ver0_59_14, ver0_60_2, ver0_60_3, ver0_60_4, ver0_60_5, HEAD
Changes since 1.4: +0 -0 lines
File MIME type: text/x-csrc
*** empty log message ***

1 /***************************************************************************
2
3 Generic MAME hard disk implementation, with differencing files
4
5 ***************************************************************************/
6
7 #include "harddisk.h"
8 #include "md5.h"
9 #include "zlib.h"
10 #include <time.h>
11
12
13
14 /*************************************
15 *
16 * Debugging
17 *
18 *************************************/
19
20 #define LOG_DISK_ACCESSES 0
21
22
23
24 /*************************************
25 *
26 * Constants
27 *
28 *************************************/
29
30 #define STACK_ENTRIES 1024 /* 16-bit array size on stack */
31 #define COOKIE_VALUE 0xbaadf00d
32
33 #define END_OF_LIST_COOKIE "EndOfListCookie"
34
35
36
37 /*************************************
38 *
39 * Macros
40 *
41 *************************************/
42
43 #define SET_ERROR_AND_CLEANUP(err) do { last_error = (err); goto cleanup; } while (0)
44
45
46
47 /*************************************
48 *
49 * Type definitions
50 *
51 *************************************/
52
53 typedef UINT64 mapentry_t;
54
55 struct hard_disk_info
56 {
57 UINT32 cookie; /* cookie, should equal COOKIE_VALUE */
58 struct hard_disk_info * next; /* pointer to next drive in the global list */
59
60 void * file; /* handle to the open file */
61 struct hard_disk_header header; /* header, extracted from drive */
62
63 struct hard_disk_info * parent; /* pointer to parent drive, or NULL */
64
65 mapentry_t * map; /* array of map entries */
66 UINT64 eof; /* end of file, computed from the blocks */
67
68 UINT8 * cache; /* block cache pointer */
69 UINT32 cacheblock; /* index of currently cached block */
70
71 UINT8 * compressed; /* pointer to buffer for compressed data */
72 void * codecdata; /* opaque pointer to codec data */
73 };
74
75 struct zlib_codec_data
76 {
77 z_stream inflater;
78 z_stream deflater;
79 };
80
81
82
83 /*************************************
84 *
85 * Local variables
86 *
87 *************************************/
88
89 static struct hard_disk_interface a_interface;
90 static struct hard_disk_info *first_disk;
91 static int last_error;
92
93
94
95 /*************************************
96 *
97 * Prototypes
98 *
99 *************************************/
100
101 static int read_block_into_cache(struct hard_disk_info *info, UINT32 block);
102 static int write_block_from_cache(struct hard_disk_info *info, UINT32 block);
103 static int read_header(void *file, struct hard_disk_header *header);
104 static int write_header(void *file, const struct hard_disk_header *header);
105 static int read_sector_map(struct hard_disk_info *info);
106 static int init_codec(struct hard_disk_info *info);
107 static void free_codec(struct hard_disk_info *info);
108
109
110
111 /*************************************
112 *
113 * Inline helpers
114 *
115 *************************************/
116
117 INLINE UINT64 offset_from_mapentry(mapentry_t *entry)
118 {
119 return (*entry << 20) >> 20;
120 }
121
122 INLINE UINT32 length_from_mapentry(mapentry_t *entry)
123 {
124 return *entry >> 44;
125 }
126
127 INLINE void encode_mapentry(mapentry_t *entry, UINT64 offset, UINT32 length)
128 {
129 *entry = ((UINT64)length << 44) | ((offset << 20) >> 20);
130 }
131
132 INLINE void byteswap_mapentry(mapentry_t *entry)
133 {
134 #ifdef LSB_FIRST
135 mapentry_t temp = *entry;
136 UINT8 *s = (UINT8 *)&temp;
137 UINT8 *d = (UINT8 *)entry;
138 int i;
139
140 for (i = 0; i < sizeof(mapentry_t); i++)
141 d[i] = s[sizeof(mapentry_t) - 1 - i];
142 #endif
143 }
144
145 INLINE UINT32 get_bigendian_uint32(UINT8 *base)
146 {
147 return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3];
148 }
149
150 INLINE void put_bigendian_uint32(UINT8 *base, UINT32 value)
151 {
152 base[0] = value >> 24;
153 base[1] = value >> 16;
154 base[2] = value >> 8;
155 base[3] = value;
156 }
157
158
159
160 /*************************************
161 *
162 * Interface setup
163 *
164 *************************************/
165
166 void hard_disk_set_interface(struct hard_disk_interface *_interface)
167 {
168 if (_interface)
169 a_interface = *_interface;
170 else
171 memset(&a_interface, 0, sizeof(a_interface));
172 }
173
174
175
176 /*************************************
177 *
178 * Creating a new hard disk
179 *
180 *************************************/
181
182 int hard_disk_create(const char *filename, const struct hard_disk_header *_header)
183 {
184 static const UINT8 nullmd5[16] = { 0 };
185 struct hard_disk_header header = *_header;
186 mapentry_t blank_entries[STACK_ENTRIES];
187 int fullchunks, remainder, count;
188 UINT64 fileoffset;
189 void *file = NULL;
190 int i, err;
191
192 last_error = HDERR_NONE;
193
194 /* punt if no interface */
195 if (!a_interface.open)
196 SET_ERROR_AND_CLEANUP(HDERR_NO_INTERFACE);
197
198 /* verify parameters */
199 if (!filename)
200 SET_ERROR_AND_CLEANUP(HDERR_FILE_NOT_FOUND);
201
202 /* sanity check the header, filling in missing info */
203 header.length = HARD_DISK_HEADER_SIZE;
204 header.version = HARD_DISK_HEADER_VERSION;
205
206 /* require a valid MD5 if we're using a parent */
207 if ((header.flags & HDFLAGS_HAS_PARENT) && !memcmp(header.parentmd5, nullmd5, sizeof(nullmd5)))
208 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
209
210 /* require a valid compression mechanism */
211 if (header.compression >= HDCOMPRESSION_MAX)
212 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
213
214 /* require a valid blocksize */
215 if (header.blocksize == 0 || header.blocksize >= 2048)
216 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
217
218 /* require either totalsectors or CHS values */
219 if (!header.cylinders || !header.sectors || !header.heads)
220 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
221
222 /* derive the remaining data */
223 header.totalblocks = ((header.cylinders * header.sectors * header.heads) + header.blocksize - 1) / header.blocksize;
224
225 /* attempt to create the file */
226 file = (*a_interface.open)(filename, "wb");
227 if (!file)
228 SET_ERROR_AND_CLEANUP(HDERR_CANT_CREATE_FILE);
229
230 /* write the resulting header */
231 err = write_header(file, &header);
232 if (err != HDERR_NONE)
233 SET_ERROR_AND_CLEANUP(err);
234
235 /* prepare to write a blank sector map immediately following */
236 memset(blank_entries, 0, sizeof(blank_entries));
237 fileoffset = header.length;
238 fullchunks = header.totalblocks / STACK_ENTRIES;
239 remainder = header.totalblocks % STACK_ENTRIES;
240
241 /* first write full chunks of blank entries */
242 for (i = 0; i < fullchunks; i++)
243 {
244 count = (*a_interface.write)(file, fileoffset, sizeof(blank_entries), blank_entries);
245 if (count != sizeof(blank_entries))
246 SET_ERROR_AND_CLEANUP(HDERR_WRITE_ERROR);
247 fileoffset += sizeof(blank_entries);
248 }
249
250 /* then write the remainder */
251 if (remainder)
252 {
253 count = (*a_interface.write)(file, fileoffset, remainder * sizeof(blank_entries[0]), blank_entries);
254 if (count != remainder * sizeof(blank_entries[0]))
255 SET_ERROR_AND_CLEANUP(HDERR_WRITE_ERROR);
256 fileoffset += remainder * sizeof(blank_entries[0]);
257 }
258
259 /* then write a special end-of-list cookie */
260 memcpy(&blank_entries[0], END_OF_LIST_COOKIE, sizeof(blank_entries[0]));
261 count = (*a_interface.write)(file, fileoffset, sizeof(blank_entries[0]), blank_entries);
262 if (count != sizeof(blank_entries[0]))
263 SET_ERROR_AND_CLEANUP(HDERR_WRITE_ERROR);
264
265 /* all done */
266 (*a_interface.close)(file);
267 return HDERR_NONE;
268
269 cleanup:
270 if (file)
271 (*a_interface.close)(file);
272 return last_error;
273 }
274
275
276
277 /*************************************
278 *
279 * Opening a hard disk
280 *
281 *************************************/
282
283 void *hard_disk_open(const char *filename, int writeable, void *parent)
284 {
285 struct hard_disk_info *finalinfo;
286 struct hard_disk_info info = { 0 };
287 int err;
288
289 last_error = HDERR_NONE;
290
291 /* punt if no interface */
292 if (!a_interface.open)
293 SET_ERROR_AND_CLEANUP(HDERR_NO_INTERFACE);
294
295 /* verify parameters */
296 if (!filename)
297 SET_ERROR_AND_CLEANUP(HDERR_FILE_NOT_FOUND);
298
299 /* punt if invalid parent */
300 info.parent = parent;
301 if (info.parent && info.parent->cookie != COOKIE_VALUE)
302 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
303
304 /* first attempt to open the file */
305 info.file = (*a_interface.open)(filename, writeable ? "rb+" : "rb");
306 if (!info.file)
307 SET_ERROR_AND_CLEANUP(HDERR_FILE_NOT_FOUND);
308
309 /* now attempt to read the header */
310 err = read_header(info.file, &info.header);
311 if (err != HDERR_NONE)
312 SET_ERROR_AND_CLEANUP(err);
313
314 /* make sure we don't open a read-only file writeable */
315 if (writeable && !(info.header.flags & HDFLAGS_IS_WRITEABLE))
316 SET_ERROR_AND_CLEANUP(HDERR_FILE_NOT_WRITEABLE);
317
318 /* make sure we have a valid parent */
319 if (parent && (memcmp(info.parent->header.md5, info.header.parentmd5, 16) || !(info.header.flags & HDFLAGS_HAS_PARENT)))
320 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARENT);
321
322 /* now read the sector map */
323 err = read_sector_map(&info);
324 if (err != HDERR_NONE)
325 SET_ERROR_AND_CLEANUP(err);
326
327 /* allocate and init the block cache */
328 info.cache = malloc(info.header.blocksize * HARD_DISK_SECTOR_SIZE);
329 if (!info.cache)
330 SET_ERROR_AND_CLEANUP(HDERR_OUT_OF_MEMORY);
331 info.cacheblock = -1;
332
333 /* allocate the temporary compressed buffer */
334 info.compressed = malloc(info.header.blocksize * HARD_DISK_SECTOR_SIZE);
335 if (!info.compressed)
336 SET_ERROR_AND_CLEANUP(HDERR_OUT_OF_MEMORY);
337
338 /* now init the codec */
339 err = init_codec(&info);
340 if (err != HDERR_NONE)
341 SET_ERROR_AND_CLEANUP(err);
342
343 /* okay, now allocate our entry and copy it */
344 finalinfo = malloc(sizeof(info));
345 if (!finalinfo)
346 SET_ERROR_AND_CLEANUP(HDERR_OUT_OF_MEMORY);
347 *finalinfo = info;
348
349 /* hook us into the global list */
350 finalinfo->cookie = COOKIE_VALUE;
351 finalinfo->next = first_disk;
352 first_disk = finalinfo;
353
354 /* all done */
355 return finalinfo;
356
357 cleanup:
358 if (info.codecdata)
359 free_codec(&info);
360 if (info.compressed)
361 free(info.compressed);
362 if (info.cache)
363 free(info.cache);
364 if (info.map)
365 free(info.map);
366 if (info.file)
367 (*a_interface.close)(info.file);
368 return NULL;
369 }
370
371
372
373 /*************************************
374 *
375 * Closing a hard disk
376 *
377 *************************************/
378
379 void hard_disk_close(void *disk)
380 {
381 struct hard_disk_info *info = disk;
382 struct hard_disk_info *curr, *prev;
383
384 /* punt if NULL or invalid */
385 if (!info || info->cookie != COOKIE_VALUE)
386 return;
387
388 /* deinit the codec */
389 if (info->codecdata)
390 free_codec(info);
391
392 /* free the compressed data buffer */
393 if (info->compressed)
394 free(info->compressed);
395
396 /* free the block cache */
397 if (info->cache)
398 free(info->cache);
399
400 /* free the sector map */
401 if (info->map)
402 free(info->map);
403
404 /* close the file */
405 if (info->file)
406 (*a_interface.close)(info->file);
407
408 /* unlink ourselves */
409 for (prev = NULL, curr = first_disk; curr; prev = curr, curr = curr->next)
410 if (curr == info)
411 {
412 if (prev)
413 prev->next = curr->next;
414 else
415 first_disk = curr->next;
416 break;
417 }
418
419 /* free our memory */
420 free(info);
421 }
422
423
424
425 /*************************************
426 *
427 * Closing all open hard disks
428 *
429 *************************************/
430
431 void hard_disk_close_all(void)
432 {
433 while (first_disk)
434 hard_disk_close(first_disk);
435 }
436
437
438
439 /*************************************
440 *
441 * Reading from a hard disk
442 *
443 *************************************/
444
445 UINT32 hard_disk_read(void *disk, UINT32 lbasector, UINT32 numsectors, void *buffer)
446 {
447 struct hard_disk_info *info = disk;
448 UINT32 block, offset;
449 int err;
450
451 last_error = HDERR_NONE;
452
453 /* for now, just break down multisector reads into single sectors */
454 if (numsectors > 1)
455 {
456 UINT32 total = 0;
457 while (numsectors-- && last_error == HDERR_NONE)
458 total += hard_disk_read(disk, lbasector++, 1, (UINT8 *)buffer + total * HARD_DISK_SECTOR_SIZE);
459 return total;
460 }
461
462 /* punt if NULL or invalid */
463 if (!info || info->cookie != COOKIE_VALUE)
464 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
465
466 /* first determine the block number */
467 block = lbasector / info->header.blocksize;
468 offset = lbasector % info->header.blocksize;
469
470 /* if we're past the end, fail */
471 if (block >= info->header.totalblocks)
472 SET_ERROR_AND_CLEANUP(HDERR_SECTOR_OUT_OF_RANGE);
473
474 /* if we don't own the block, ask the parent to handle it */
475 if (info->map[block] == 0)
476 return hard_disk_read(info->parent, lbasector, numsectors, buffer);
477
478 /* if the block is not cached, load and decompress it */
479 if (info->cacheblock != block)
480 {
481 err = read_block_into_cache(info, block);
482 if (err != HDERR_NONE)
483 SET_ERROR_AND_CLEANUP(err);
484 }
485
486 /* now copy the data */
487 memcpy(buffer, &info->cache[offset * HARD_DISK_SECTOR_SIZE], HARD_DISK_SECTOR_SIZE);
488 return 1;
489
490 cleanup:
491 return 0;
492 }
493
494
495
496 /*************************************
497 *
498 * Writing to a hard disk
499 *
500 *************************************/
501
502 UINT32 hard_disk_write(void *disk, UINT32 lbasector, UINT32 numsectors, const void *buffer)
503 {
504 struct hard_disk_info *info = disk;
505 UINT32 block, offset, count;
506 UINT64 fileoffset;
507 int err;
508
509 last_error = HDERR_NONE;
510
511 /* for now, just break down multisector writes into single sectors */
512 if (numsectors > 1)
513 {
514 UINT32 total = 0;
515 while (numsectors-- && last_error == HDERR_NONE)
516 total += hard_disk_write(disk, lbasector++, 1, (const UINT8 *)buffer + total * HARD_DISK_SECTOR_SIZE);
517 return total;
518 }
519
520 /* punt if NULL or invalid */
521 if (!info || info->cookie != COOKIE_VALUE)
522 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
523
524 /* first determine the block number */
525 block = lbasector / info->header.blocksize;
526 offset = lbasector % info->header.blocksize;
527
528 /* if we're past the end, fail */
529 if (block >= info->header.totalblocks)
530 SET_ERROR_AND_CLEANUP(HDERR_SECTOR_OUT_OF_RANGE);
531
532 /* determine the file offset of this block */
533 fileoffset = offset_from_mapentry(&info->map[block]);
534
535 /* if we already own the block, and we're not compressing things, just write through */
536 if (fileoffset != 0 && info->header.compression == HDCOMPRESSION_NONE)
537 {
538 /* do the write */
539 count = (*a_interface.write)(info->file, fileoffset + offset * HARD_DISK_SECTOR_SIZE, HARD_DISK_SECTOR_SIZE, buffer);
540 if (count != HARD_DISK_SECTOR_SIZE)
541 SET_ERROR_AND_CLEANUP(HDERR_WRITE_ERROR);
542
543 /* if this is going to the currently cached block, update the cache as well */
544 if (info->cacheblock == block)
545 memcpy(&info->cache[offset * HARD_DISK_SECTOR_SIZE], buffer, HARD_DISK_SECTOR_SIZE);
546 return 1;
547 }
548
549 /* we don't own the block yet -- read it from the parent into the cache */
550 count = hard_disk_read(disk, block * info->header.blocksize, info->header.blocksize, info->cache);
551 if (count != info->header.blocksize)
552 {
553 info->cacheblock = -1;
554 SET_ERROR_AND_CLEANUP(HDERR_READ_ERROR);
555 }
556 info->cacheblock = block;
557
558 /* now copy in the modified data */
559 memcpy(&info->cache[offset * HARD_DISK_SECTOR_SIZE], buffer, HARD_DISK_SECTOR_SIZE);
560
561 /* then write out the block */
562 err = write_block_from_cache(info, block);
563 if (err != HDERR_NONE)
564 SET_ERROR_AND_CLEANUP(err);
565
566 return 1;
567
568 cleanup:
569 return 0;
570 }
571
572
573
574 /*************************************
575 *
576 * Return last error
577 *
578 *************************************/
579
580 int hard_disk_get_last_error(void)
581 {
582 return last_error;
583 }
584
585
586
587 /*************************************
588 *
589 * Return pointer to header
590 *
591 *************************************/
592
593 const struct hard_disk_header *hard_disk_get_header(void *disk)
594 {
595 const struct hard_disk_info *info = disk;
596
597 /* punt if NULL or invalid */
598 if (!info || info->cookie != COOKIE_VALUE)
599 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
600
601 return &info->header;
602
603 cleanup:
604 return NULL;
605 }
606
607
608
609 /*************************************
610 *
611 * All-in-one disk compressor
612 *
613 *************************************/
614
615 int hard_disk_compress(const char *rawfile, UINT32 offset, const char *newfile, const struct hard_disk_header *header, const char *difffile, void (*progress)(const char *, ...))
616 {
617 const struct hard_disk_header *readbackheader;
618 struct hard_disk_header tempheader = *header;
619 struct hard_disk_info *comparefile = NULL;
620 struct hard_disk_info *destfile = NULL;
621 UINT64 sourceoffset = offset;
622 void *sourcefile = NULL;
623 struct MD5Context md5;
624 UINT32 blocksizebytes;
625 int err, block = 0;
626 clock_t lastupdate;
627
628 /* punt if no interface */
629 if (!a_interface.open)
630 SET_ERROR_AND_CLEANUP(HDERR_NO_INTERFACE);
631
632 /* verify parameters */
633 if (!rawfile || !newfile || !header)
634 SET_ERROR_AND_CLEANUP(HDERR_INVALID_PARAMETER);
635
636 /* open the raw file */
637 sourcefile = (*a_interface.open)(rawfile, "rb");
638 if (!sourcefile)
639 SET_ERROR_AND_CLEANUP(HDERR_FILE_NOT_FOUND);
640
641 /* open the diff file */
642 if (difffile)
643 {
644 comparefile = hard_disk_open(difffile, 0, NULL);
645 if (!comparefile)
646 SET_ERROR_AND_CLEANUP(HDERR_FILE_NOT_FOUND);
647 }
648
649 /* create a new writeable disk with the header */
650 tempheader = *header;
651 tempheader.flags |= HDFLAGS_IS_WRITEABLE;
652 err = hard_disk_create(newfile, &tempheader);
653 if (err != HDERR_NONE)
654 SET_ERROR_AND_CLEANUP(err);
655
656 /* now open it writeable */
657 destfile = hard_disk_open(newfile, 1, comparefile);
658 if (!destfile)
659 SET_ERROR_AND_CLEANUP(HDERR_CANT_CREATE_FILE);
660 readbackheader = hard_disk_get_header(destfile);
661
662 /* init the MD5 computation */
663 MD5Init(&md5);
664
665 /* loop over source blocks until we run out */
666 blocksizebytes = destfile->header.blocksize * HARD_DISK_SECTOR_SIZE;
667 memset(destfile->cache, 0, blocksizebytes);
668 lastupdate = 0;
669 while (block < readbackheader->totalblocks && (*a_interface.read)(sourcefile, sourceoffset, blocksizebytes, destfile->cache) != 0)
670 {
671 int write_this_block = 1;
672 clock_t curtime = clock();
673
674 /* progress */
675 if (curtime - lastupdate > CLOCKS_PER_SEC / 2)
676 {
677 UINT64 sourcepos = (UINT64)block * blocksizebytes;
678 if (progress && sourcepos)
679 (*progress)("Compressing block %d... (ratio=%d%%) \r", block, 100 - destfile->eof * 100 / sourcepos);
680 lastupdate = curtime;
681 }
682
683 /* update the MD5 */
684 MD5Update(&md5, destfile->cache, blocksizebytes);
685
686 /* see if we have an exact match */
687 if (comparefile)
688 {
689 if (read_block_into_cache(comparefile, block) == HDERR_NONE && memcmp(comparefile->cache, destfile->cache, blocksizebytes) == 0)
690 write_this_block = 0;
691 }
692
693 /* write out the block */
694 if (write_this_block)
695 {
696 err = write_block_from_cache(destfile, block);
697 if (err != HDERR_NONE)
698 SET_ERROR_AND_CLEANUP(err);
699 }
700
701 /* prepare for the next block */
702 block++;
703 sourceoffset += blocksizebytes;
704 memset(destfile->cache, 0, blocksizebytes);
705 }
706
707 /* compute the final MD5 and update to the final header */
708 tempheader = destfile->header;
709 if (!(header->flags & HDFLAGS_IS_WRITEABLE))
710 tempheader.flags &= ~HDFLAGS_IS_WRITEABLE;
711 MD5Final(tempheader.md5, &md5);
712 err = write_header(destfile->file, &tempheader);
713 if (err != HDERR_NONE)
714 SET_ERROR_AND_CLEANUP(err);
715
716 /* close the drives */
717 hard_disk_close(destfile);
718 if (comparefile)
719 hard_disk_close(comparefile);
720 (*a_interface.close)(sourcefile);
721
722 return HDERR_NONE;
723
724 cleanup:
725 if (destfile)
726 hard_disk_close(destfile);
727 if (comparefile)
728 hard_disk_close(comparefile);
729 if (sourcefile)
730 (*a_interface.close)(sourcefile);
731 return last_error;
732 }
733
734
735
736 /*************************************
737 *
738 * Block read/decompress
739 *
740 *************************************/
741
742 static int read_block_into_cache(struct hard_disk_info *info, UINT32 block)
743 {
744 UINT64 fileoffset = offset_from_mapentry(&info->map[block]);
745 UINT32 length = length_from_mapentry(&info->map[block]);
746 UINT32 bytes;
747
748 /* if the data is uncompressed, just read it directly into the cache */
749 if (length == info->header.blocksize * HARD_DISK_SECTOR_SIZE)
750 {
751 bytes = (*a_interface.read)(info->file, fileoffset, length, info->cache);
752 if (bytes != length)
753 return HDERR_READ_ERROR;
754 info->cacheblock = block;
755 return HDERR_NONE;
756 }
757
758 /* otherwise, read it into the decompression buffer */
759 bytes = (*a_interface.read)(info->file, fileoffset, length, info->compressed);
760 if (bytes != length)
761 return HDERR_READ_ERROR;
762
763 /* now decompress based on the compression method */
764 switch (info->header.compression)
765 {
766 case HDCOMPRESSION_ZLIB:
767 {
768 struct zlib_codec_data *codec = info->codecdata;
769 int err;
770
771 /* reset the decompressor */
772 codec->inflater.next_in = info->compressed;
773 codec->inflater.avail_in = length;
774 codec->inflater.total_in = 0;
775 codec->inflater.next_out = info->cache;
776 codec->inflater.avail_out = info->header.blocksize * HARD_DISK_SECTOR_SIZE;
777 codec->inflater.total_out = 0;
778 err = inflateReset(&codec->inflater);
779 if (err != Z_OK)
780 return HDERR_DECOMPRESSION_ERROR;
781
782 /* do it */
783 err = inflate(&codec->inflater, Z_FINISH);
784 if (codec->inflater.total_out != info->header.blocksize * HARD_DISK_SECTOR_SIZE)
785 return HDERR_DECOMPRESSION_ERROR;
786
787 /* mark the block successfully cached in */
788 info->cacheblock = block;
789 break;
790 }
791 }
792 return HDERR_NONE;
793 }
794
795
796
797 /*************************************
798 *
799 * Block write/compress
800 *
801 *************************************/
802
803 static int write_block_from_cache(struct hard_disk_info *info, UINT32 block)
804 {
805 UINT64 fileoffset = offset_from_mapentry(&info->map[block]);
806 UINT32 length = length_from_mapentry(&info->map[block]);
807 UINT32 datalength = info->header.blocksize * HARD_DISK_SECTOR_SIZE;
808 void *data = info->cache;
809 mapentry_t entry;
810 UINT32 bytes;
811
812 /* first compress the data */
813 switch (info->header.compression)
814 {
815 case HDCOMPRESSION_ZLIB:
816 {
817 struct zlib_codec_data *codec = info->codecdata;
818 int err;
819
820 /* reset the decompressor */
821 codec->deflater.next_in = info->cache;
822 codec->deflater.avail_in = info->header.blocksize * HARD_DISK_SECTOR_SIZE;
823 codec->deflater.total_in = 0;
824 codec->deflater.next_out = info->compressed;
825 codec->deflater.avail_out = info->header.blocksize * HARD_DISK_SECTOR_SIZE;
826 codec->deflater.total_out = 0;
827 err = deflateReset(&codec->deflater);
828 if (err != Z_OK)
829 return HDERR_COMPRESSION_ERROR;
830
831 /* do it */
832 err = deflate(&codec->deflater, Z_FINISH);
833
834 /* if we didn't run out of space, override the raw data with compressed */
835 if (err == Z_STREAM_END)
836 {
837 data = info->compressed;
838 datalength = codec->deflater.total_out;
839 }
840 break;
841 }
842 }
843
844 /* if the data doesn't fit into the previous entry, make a new one at the eof */
845 if (fileoffset == 0 || length < datalength)
846 {
847 fileoffset = info->eof;
848 info->eof += datalength;
849 }
850
851 /* write the data */
852 bytes = (*a_interface.write)(info->file, fileoffset, datalength, data);
853 if (bytes != datalength)
854 return HDERR_WRITE_ERROR;
855
856 /* update the entry */
857 encode_mapentry(&info->map[block], fileoffset, datalength);
858
859 /* update the map on disk */
860 entry = info->map[block];
861 byteswap_mapentry(&entry);
862 bytes = (*a_interface.write)(info->file, info->header.length + block * sizeof(info->map[0]), sizeof(entry), &entry);
863 if (bytes != sizeof(entry))
864 return HDERR_WRITE_ERROR;
865
866 return HDERR_NONE;
867 }
868
869
870
871 /*************************************
872 *
873 * Header read
874 *
875 *************************************/
876
877 static int read_header(void *file, struct hard_disk_header *header)
878 {
879 UINT8 rawheader[HARD_DISK_HEADER_SIZE];
880 UINT32 count;
881
882 /* punt if NULL */
883 if (!header)
884 return HDERR_INVALID_PARAMETER;
885
886 /* punt if invalid file */
887 if (!file)
888 return HDERR_INVALID_FILE;
889
890 /* punt if no interface */
891 if (!a_interface.read)
892 return HDERR_NO_INTERFACE;
893
894 /* seek and read */
895 count = (*a_interface.read)(file, 0, sizeof(rawheader), rawheader);
896 if (count != sizeof(rawheader))
897 return HDERR_READ_ERROR;
898
899 /* verify the tag */
900 if (strncmp((char *)rawheader, "MComprHD", 8) != 0)
901 return HDERR_INVALID_DATA;
902
903 /* extract the direct data */
904 memset(header, 0, sizeof(*header));
905 header->length = get_bigendian_uint32(&rawheader[8]);
906 header->version = get_bigendian_uint32(&rawheader[12]);
907 header->flags = get_bigendian_uint32(&rawheader[16]);
908 header->compression = get_bigendian_uint32(&rawheader[20]);
909 header->blocksize = get_bigendian_uint32(&rawheader[24]);
910 header->totalblocks = get_bigendian_uint32(&rawheader[28]);
911 header->cylinders = get_bigendian_uint32(&rawheader[32]);
912 header->heads = get_bigendian_uint32(&rawheader[36]);
913 header->sectors = get_bigendian_uint32(&rawheader[40]);
914 memcpy(header->md5, &rawheader[44], 16);
915 memcpy(header->parentmd5, &rawheader[60], 16);
916 return HDERR_NONE;
917 }
918
919
920
921 /*************************************
922 *
923 * Header write
924 *
925 *************************************/
926
927 static int write_header(void *file, const struct hard_disk_header *header)
928 {
929 UINT8 rawheader[HARD_DISK_HEADER_SIZE];
930 UINT32 count;
931
932 /* punt if NULL */
933 if (!header)
934 return HDERR_INVALID_PARAMETER;
935
936 /* punt if invalid file */
937 if (!file)
938 return HDERR_INVALID_FILE;
939
940 /* punt if no interface */
941 if (!a_interface.write)
942 return HDERR_NO_INTERFACE;
943
944 /* assemble the data */
945 memset(rawheader, 0, sizeof(rawheader));
946 memcpy(rawheader, "MComprHD", 8);
947 put_bigendian_uint32(&rawheader[8], HARD_DISK_HEADER_SIZE);
948 put_bigendian_uint32(&rawheader[12], header->version);
949 put_bigendian_uint32(&rawheader[16], header->flags);
950 put_bigendian_uint32(&rawheader[20], header->compression);
951 put_bigendian_uint32(&rawheader[24], header->blocksize);
952 put_bigendian_uint32(&rawheader[28], header->totalblocks);
953 put_bigendian_uint32(&rawheader[32], header->cylinders);
954 put_bigendian_uint32(&rawheader[36], header->heads);
955 put_bigendian_uint32(&rawheader[40], header->sectors);
956 memcpy(&rawheader[44], header->md5, 16);
957 memcpy(&rawheader[60], header->parentmd5, 16);
958
959 /* seek and write */
960 count = (*a_interface.write)(file, 0, sizeof(rawheader), rawheader);
961 if (count != sizeof(rawheader))
962 return HDERR_WRITE_ERROR;
963
964 return HDERR_NONE;
965 }
966
967
968
969 /*************************************
970 *
971 * Read the sector map
972 *
973 *************************************/
974
975 static int read_sector_map(struct hard_disk_info *info)
976 {
977 mapentry_t cookie;
978 int i, err;
979 UINT32 count;
980
981 /* first allocate memory */
982 info->map = malloc(sizeof(info->map[0]) * info->header.totalblocks);
983 if (!info->map)
984 return HDERR_OUT_OF_MEMORY;
985
986 /* read the entire sector map */
987 count = (*a_interface.read)(info->file, info->header.length, sizeof(info->map[0]) * info->header.totalblocks, info->map);
988 if (count != sizeof(info->map[0]) * info->header.totalblocks)
989 {
990 err = HDERR_READ_ERROR;
991 goto cleanup;
992 }
993
994 /* verify the cookie */
995 count = (*a_interface.read)(info->file, info->header.length + sizeof(info->map[0]) * info->header.totalblocks, sizeof(cookie), &cookie);
996 if (count != sizeof(cookie) || memcmp(&cookie, END_OF_LIST_COOKIE, sizeof(cookie)))
997 {
998 err = HDERR_INVALID_FILE;
999 goto cleanup;
1000 }
1001
1002 /* compute the EOF and byteswap as necessary */
1003 info->eof = info->header.length + sizeof(info->map[0]) * (info->header.totalblocks + 1);
1004 for (i = 0; i < info->header.totalblocks; i++)
1005 {
1006 UINT64 end;
1007
1008 /* first byteswap properly */
1009 byteswap_mapentry(&info->map[i]);
1010
1011 /* compute the end of this block; if past EOF, make it the EOF */
1012 end = offset_from_mapentry(&info->map[i]) + length_from_mapentry(&info->map[i]);
1013 if (end > info->eof)
1014 info->eof = end;
1015 }
1016
1017 /* all done */
1018 return HDERR_NONE;
1019
1020 cleanup:
1021 if (info->map)
1022 free(info->map);
1023 info->map = NULL;
1024 return err;
1025 }
1026
1027
1028
1029 /*************************************
1030 *
1031 * Compression init
1032 *
1033 *************************************/
1034
1035 static int init_codec(struct hard_disk_info *info)
1036 {
1037 int err = HDERR_NONE;
1038
1039 /* now decompress based on the compression method */
1040 switch (info->header.compression)
1041 {
1042 case HDCOMPRESSION_NONE:
1043 /* nothing to do */
1044 break;
1045
1046 case HDCOMPRESSION_ZLIB:
1047 {
1048 struct zlib_codec_data *data;
1049
1050 /* allocate memory for the 2 stream buffers */
1051 info->codecdata = malloc(sizeof(struct zlib_codec_data));
1052 if (!info->codecdata)
1053 return HDERR_OUT_OF_MEMORY;
1054
1055 /* clear the buffers */
1056 data = info->codecdata;
1057 memset(data, 0, sizeof(struct zlib_codec_data));
1058
1059 /* init the first for decompression and the second for compression */
1060 data->inflater.next_in = info->compressed;
1061 data->inflater.avail_in = 0;
1062 err = inflateInit2(&data->inflater, -MAX_WBITS);
1063 if (err == Z_OK)
1064 {
1065 data->deflater.next_in = info->compressed;
1066 data->deflater.avail_in = 0;
1067 err = deflateInit2(&data->deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
1068 }
1069
1070 /* convert errors */
1071 if (err == Z_MEM_ERROR)
1072 err = HDERR_OUT_OF_MEMORY;
1073 else if (err != Z_OK)
1074 err = HDERR_CODEC_ERROR;
1075 else
1076 err = HDERR_NONE;
1077
1078 /* handle an error */
1079 if (err != HDERR_NONE)
1080 free(info->codecdata);
1081 break;
1082 }
1083 }
1084
1085 /* return the error */
1086 return err;
1087 }
1088
1089
1090
1091 /*************************************
1092 *
1093 * Compression de-init
1094 *
1095 *************************************/
1096
1097 static void free_codec(struct hard_disk_info *info)
1098 {
1099 /* now decompress based on the compression method */
1100 switch (info->header.compression)
1101 {
1102 case HDCOMPRESSION_NONE:
1103 /* nothing to do */
1104 break;
1105
1106 case HDCOMPRESSION_ZLIB:
1107 {
1108 struct zlib_codec_data *data = info->codecdata;
1109
1110 /* deinit the streams */
1111 if (data)
1112 {
1113 inflateEnd(&data->inflater);
1114 deflateEnd(&data->deflater);
1115 free(data);
1116 }
1117 break;
1118 }
1119 }
1120 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26