/*************************************************************************** * Copyright (C) 2007 by Arep * * Support is provided through the forums at * * http://www.console-tribe.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "misc.h" #include #include #include #include #include "constants.h" #include "disc.h" #include "dumper.h" #ifndef WIN32 #include #include #endif struct dumper_s { disc *dsk; char *outfile_raw; u_int32_t start_sector_raw; FILE *fp_raw; char *outfile_iso; u_int32_t start_sector_iso; FILE *fp_iso; u_int32_t start_sector; bool hashing; bool flushing; multihash hash_raw; multihash hash_iso; progress_func progress; void *progress_data; }; /** * Tries to open the output file for writing and to find out if it contains valid data so that the dump can continue. * @param dvd * @param outfile * @param[out] fp The file pointer to write to. * @param[out] start_sector The sector to start reading from. * @return true if the dumping can start/continue, false otherwise (for instance if outfile cannot be written to). */ bool dumper_set_raw_output_file (dumper *dmp, char *outfile_raw, bool resume) { bool out; my_off_t filesize; FILE *fp; if (!outfile_raw) { /* Raw output disabled */ out = true; dmp -> start_sector_raw = -1; my_free (dmp -> outfile_raw); dmp -> outfile_raw = NULL; } else if (dmp -> outfile_raw) { error ("Raw output file already defined"); out = false; } else if (!(fp = fopen (outfile_raw, "rb"))) { /** @todo Maybe we could not open file for permission problems */ /* Raw output file does not exist, start from scratch */ out = true; dmp -> start_sector_raw = 0; my_strdup (dmp -> outfile_raw, outfile_raw); } else if (resume) { /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */ my_fseek (fp, 0, SEEK_END); filesize = my_ftell (fp); fclose (fp); out = true; dmp -> start_sector_raw = (u_int32_t) (filesize / RAW_SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK; debug ("Raw output can restart from sector %u", dmp -> start_sector_raw); my_strdup (dmp -> outfile_raw, outfile_raw); } else { /* Raw output file exists but resume was not requested, error */ fclose (fp); error ("Raw output file exists, but resume was not requested."); out = false; dmp -> start_sector_raw = -1; my_free (dmp -> outfile_raw); dmp -> outfile_raw = NULL; } return (out); } bool dumper_set_iso_output_file (dumper *dmp, char *outfile_iso, bool resume) { bool out; my_off_t filesize; FILE *fp; if (!outfile_iso) { /* Raw output disabled */ out = true; dmp -> start_sector_iso = -1; my_free (dmp -> outfile_iso); dmp -> outfile_iso = NULL; } else if (dmp -> outfile_iso) { error ("ISO output file already defined"); out = false; } else if (!(fp = fopen (outfile_iso, "rb"))) { /** @todo Maybe we could not open file for permission problems */ /* Raw output file does not exist, start from scratch */ out = true; dmp -> start_sector_iso = 0; my_strdup (dmp -> outfile_iso, outfile_iso); } else if (resume) { /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */ my_fseek (fp, 0, SEEK_END); filesize = my_ftell (fp); fclose (fp); out = true; dmp -> start_sector_iso = (u_int32_t) (filesize / SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK; debug ("ISO output can restart from sector %u", dmp -> start_sector_iso); my_strdup (dmp -> outfile_iso, outfile_iso); } else { /* Raw output file exists but resume was not requested, error */ fclose (fp); error ("ISO output file exists, but resume was not requested."); out = false; dmp -> start_sector_iso = -1; my_free (dmp -> outfile_iso); dmp -> outfile_iso = NULL; } return (out); } bool dumper_prepare (dumper *dmp) { bool out; u_int8_t buf[RAW_SECTOR_SIZE]; size_t r; u_int32_t i; /* Outputting to both files, resume must start from the file with the least sectors. Hopefully they will have the same number of sectors, anyway... */ if (dmp -> outfile_raw && dmp -> outfile_iso && dmp -> start_sector_raw != dmp -> start_sector_iso) { if (dmp -> start_sector_raw < dmp -> start_sector_iso) dmp -> start_sector = dmp -> start_sector_raw; else dmp -> start_sector = dmp -> start_sector_iso; } else if (dmp -> outfile_raw) { dmp -> start_sector = dmp -> start_sector_raw; } else if (dmp -> outfile_iso) { dmp -> start_sector = dmp -> start_sector_iso; } else { MY_ASSERT (0); } /* Prepare hashes */ if (dmp -> hashing) { multihash_init (&(dmp -> hash_raw)); multihash_init (&(dmp -> hash_iso)); } /* Setup raw output file */ if (dmp -> outfile_raw) { dmp -> fp_raw = fopen (dmp -> outfile_raw, "a+b"); if (dmp -> hashing) { debug ("Calculating hashes for pre-existing raw dump data"); if (dmp -> start_sector > 0) { for (i = 0; i < dmp -> start_sector && (r = fread (buf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw)) > 0; i++) multihash_update (&(dmp -> hash_raw), buf, RAW_SECTOR_SIZE); MY_ASSERT (r > 0); } } /* Now call fseek as file will only be written, from now on */ if (my_fseek (dmp -> fp_raw, dmp -> start_sector * RAW_SECTOR_SIZE, SEEK_SET) == 0 && ftruncate (fileno (dmp -> fp_raw), (int64_t) dmp -> start_sector * RAW_SECTOR_SIZE) == 0) { out = true; debug ("Writing to file \"%s\" in raw format (fseeked() to %lld)", dmp -> outfile_raw, my_ftell (dmp -> fp_raw)); } else { out = false; fclose (dmp -> fp_raw); dmp -> fp_raw = NULL; } } else { dmp -> fp_raw = NULL; } /* Setup ISO output file */ if (dmp -> outfile_iso) { dmp -> fp_iso = fopen (dmp -> outfile_iso, "a+b"); if (dmp -> hashing) { debug ("Calculating hashes for pre-existing ISO dump data"); if (dmp -> start_sector > 0) { for (i = 0; i < dmp -> start_sector && (r = fread (buf, SECTOR_SIZE, 1, dmp -> fp_iso)) > 0; i++) multihash_update (&(dmp -> hash_iso), buf, SECTOR_SIZE); MY_ASSERT (r > 0); } } if (my_fseek (dmp -> fp_iso, dmp -> start_sector * SECTOR_SIZE, SEEK_SET) == 0 && ftruncate (fileno (dmp -> fp_iso), (int64_t) dmp -> start_sector * SECTOR_SIZE) == 0) { out = true; debug ("Writing to file \"%s\" in ISO format (fseeked() to %lld)", dmp -> outfile_iso, my_ftell (dmp -> fp_iso)); } else { out = false; fclose (dmp -> fp_iso); dmp -> fp_iso = NULL; } } else { dmp -> fp_iso = NULL; } return (out); } int dumper_dump (dumper *dmp, u_int32_t *current_sector) { bool out; u_int8_t *rawbuf, *isobuf; u_int32_t i, sectors_no, last_sector; #ifdef DEBUGaa bool no_unscrambling; #endif #ifdef DEBUGaa no_unscrambling = dd -> no_unscrambling; if (fp_iso && no_unscrambling) { warning ("Output to ISO format requested, ignoring no_unscrambling!"); no_unscrambling = false; } #endif sectors_no = disc_get_sectors_no (dmp -> dsk); #if 0 if (dd -> start_sector != -1) { if (dd -> start_sector >= sectors_no) { error ("Cannot start dumping from sector %u as the inserted disc only has %u sectors\n", dd -> start_sector, sectors_no); out = false; } else { warning ("Start sector forced to %u\n", dd -> start_sector); ss = dd -> start_sector; if (fp_iso) fseek (fp_iso, ss * 2048, SEEK_SET); if (fp_raw) fseek (fp_raw, ss * 2064, SEEK_SET); } } #endif if (true) { debug ("Starting dump process from sector %u...\n", dmp -> start_sector); /* First call to progress function */ if (dmp -> progress) dmp -> progress (true, dmp -> start_sector, sectors_no, dmp -> progress_data); last_sector=sectors_no-1; for (i = dmp -> start_sector, out = true; i < sectors_no && out; i++) { disc_read_sector (dmp -> dsk, i, &isobuf, &rawbuf); if (dmp -> fp_raw) { clearerr (dmp -> fp_raw); if (!rawbuf) { error ("NULL buffer"); out = false; *(current_sector) = i; } else fwrite (rawbuf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw); if (ferror (dmp -> fp_raw)) { error ("fwrite() to raw output file failed"); out = false; *(current_sector) = i; } if (dmp -> flushing) fflush (dmp -> fp_raw); if (dmp -> hashing && out) multihash_update (&(dmp -> hash_raw), rawbuf, RAW_SECTOR_SIZE); } if (dmp -> fp_iso) { clearerr (dmp -> fp_iso); if (!isobuf) { error ("NULL buffer"); out = false; *(current_sector) = i; } else fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_iso); if (ferror (dmp -> fp_iso)) { error ("fwrite() to ISO output file failed"); out = false; *(current_sector) = i; } if (dmp -> flushing) fflush (dmp -> fp_iso); if (dmp -> hashing && out) multihash_update (&(dmp -> hash_iso), isobuf, SECTOR_SIZE); } if ((i % 320 == 0) || (i == last_sector)) { //speedhack if (dmp -> progress) dmp -> progress (false, i + 1, sectors_no, dmp -> progress_data); /* i + 1 'cause sectors range from 0 to N */ } } if (dmp -> hashing) { multihash_finish (&(dmp -> hash_raw)); multihash_finish (&(dmp -> hash_iso)); } if (dmp -> fp_raw) fclose (dmp -> fp_raw); if (dmp -> fp_iso) fclose (dmp -> fp_iso); if (out) { } } return (out); } dumper *dumper_new (disc *d) { dumper *dmp; dmp = (dumper *) malloc (sizeof (dumper)); memset (dmp, 0, sizeof (dumper)); dmp -> dsk = d; dumper_set_hashing (dmp, true); dumper_set_flushing (dmp, true); return (dmp); } void dumper_set_progress_callback (dumper *dmp, progress_func progress, void *progress_data) { dmp -> progress = progress; dmp -> progress_data = progress_data; return; } void dumper_set_hashing (dumper *dmp, bool h) { dmp -> hashing = h; debug ("Hashing %s", h ? "enabled" : "disabled"); return; } void dumper_set_flushing (dumper *dmp, bool f) { dmp -> flushing = f; debug ("Flushing %s", f ? "enabled" : "disabled"); return; } void *dumper_destroy (dumper *dmp) { my_free (dmp -> outfile_raw); my_free (dmp -> outfile_iso); my_free (dmp); return (NULL); } char *dumper_get_iso_crc32 (dumper *dmp) { return ((dmp -> hash_iso).crc32_s); } char *dumper_get_raw_crc32 (dumper *dmp) { return ((dmp -> hash_raw).crc32_s); } char *dumper_get_iso_md4 (dumper *dmp) { return ((dmp -> hash_iso).md4_s); } char *dumper_get_raw_md4 (dumper *dmp) { return ((dmp -> hash_raw).md4_s); } char *dumper_get_iso_md5 (dumper *dmp) { return ((dmp -> hash_iso).md5_s); } char *dumper_get_raw_md5 (dumper *dmp) { return ((dmp -> hash_raw).md5_s); } char *dumper_get_iso_ed2k (dumper *dmp) { return ((dmp -> hash_iso).ed2k_s); } char *dumper_get_raw_ed2k (dumper *dmp) { return ((dmp -> hash_raw).ed2k_s); } char *dumper_get_iso_sha1 (dumper *dmp) { return ((dmp -> hash_iso).sha1_s); } char *dumper_get_raw_sha1 (dumper *dmp) { return ((dmp -> hash_raw).sha1_s); }