friidump/libfriidump/dumper.c
2013-11-01 12:26:04 -04:00

439 lines
12 KiB
C

/***************************************************************************
* 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <multihash.h>
#include "constants.h"
#include "disc.h"
#include "dumper.h"
#ifndef WIN32
#include <unistd.h>
#include <sys/types.h>
#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);
}