1
// This file is part of par2cmdline (a PAR 2.0 compatible file verification and
2
// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
4
// Copyright (c) 2003 Peter Brian Clements
6
// par2cmdline is free software; you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation; either version 2 of the License, or
9
// (at your option) any later version.
11
// par2cmdline is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program; if not, write to the Free Software
18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
20
#include "par2cmdline.h"
25
static char THIS_FILE[]=__FILE__;
30
Par2CreatorSourceFile::Par2CreatorSourceFile(void)
32
descriptionpacket = 0;
33
verificationpacket = 0;
41
Par2CreatorSourceFile::~Par2CreatorSourceFile(void)
43
delete descriptionpacket;
44
delete verificationpacket;
49
// Open the source file, compute the MD5 Hash of the whole file and the first
50
// 16k of the file, and then compute the FileId and store the results
51
// in a file description packet and a file verification packet.
53
bool Par2CreatorSourceFile::Open(CommandLine::NoiseLevel noiselevel, const CommandLine::ExtraFile &extrafile, u64 blocksize, bool deferhashcomputation)
55
// Get the filename and filesize
56
diskfilename = extrafile.FileName();
57
filesize = extrafile.FileSize();
59
// Work out how many blocks the file will be sliced into
60
blockcount = (u32)((filesize + blocksize-1) / blocksize);
62
// Determine what filename to record in the PAR2 files
63
string::size_type where;
64
if (string::npos != (where = diskfilename.find_last_of('\\')) ||
65
string::npos != (where = diskfilename.find_last_of('/')))
67
parfilename = diskfilename.substr(where+1);
71
parfilename = diskfilename;
74
// Create the Description and Verification packets
75
descriptionpacket = new DescriptionPacket;
76
descriptionpacket->Create(parfilename, filesize);
78
verificationpacket = new VerificationPacket;
79
verificationpacket->Create(blockcount);
81
// Create the diskfile object
82
diskfile = new DiskFile;
84
// Open the source file
85
if (!diskfile->Open(diskfilename, filesize))
88
// Do we want to defer the computation of the full file hash, and
89
// the block crc and hashes. This is only permitted if there
90
// is sufficient memory available to create all recovery blocks
91
// in one pass of the source files (i.e. chunksize == blocksize)
92
if (deferhashcomputation)
94
// Initialise a buffer to read the first 16k of the source file
95
size_t buffersize = 16 * 1024;
96
if (buffersize > filesize)
97
buffersize = (size_t)filesize;
98
char *buffer = new char[buffersize];
100
// Read the data from the file
101
if (!diskfile->Read(0, buffer, buffersize))
108
// Compute the hash of the data read from the file
110
context.Update(buffer, buffersize);
115
// Store the hash in the descriptionpacket and compute the file id
116
descriptionpacket->Hash16k(hash);
118
// Compute the fileid and store it in the verification packet.
119
descriptionpacket->ComputeFileId();
120
verificationpacket->FileId(descriptionpacket->FileId());
122
// Allocate an MD5 context for computing the file hash
123
// during the recovery data generation phase
124
contextfull = new MD5Context;
128
// Initialise a buffer to read the source file
129
size_t buffersize = 1024*1024;
130
if (buffersize > min(blocksize,filesize))
131
buffersize = (size_t)min(blocksize,filesize);
132
char *buffer = new char[buffersize];
134
// Get ready to start reading source file to compute the hashes and crcs
137
u64 need = blocksize;
139
MD5Context filecontext;
140
MD5Context blockcontext;
143
// Whilst we have not reached the end of the file
144
while (offset < filesize)
146
// Work out how much we can read
147
size_t want = (size_t)min(filesize-offset, (u64)buffersize);
149
// Read some data from the file into the buffer
150
if (!diskfile->Read(offset, buffer, want))
157
// If the new data passes the 16k boundary, compute the 16k hash for the file
158
if (offset < 16384 && offset + want >= 16384)
160
filecontext.Update(buffer, (size_t)(16384-offset));
162
MD5Context temp = filecontext;
166
// Store the 16k hash in the file description packet
167
descriptionpacket->Hash16k(hash);
169
if (offset + want > 16384)
171
filecontext.Update(&buffer[16384-offset], (size_t)(offset+want)-16384);
176
filecontext.Update(buffer, want);
179
// Get ready to update block hashes and crcs
182
// Whilst we have not used all of the data we just read
185
// How much of it can we use for the current block
186
u32 use = (u32)min(need, (u64)(want-used));
188
blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, use, &buffer[used]);
189
blockcontext.Update(&buffer[used], use);
194
// Have we finished the current block
198
blockcontext.Final(blockhash);
200
// Store the block hash and block crc in the file verification packet.
201
verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
206
if (blocknumber < blockcount)
210
blockcontext.Reset();
216
if (noiselevel > CommandLine::nlQuiet)
219
u32 oldfraction = (u32)(1000 * offset / filesize);
221
u32 newfraction = (u32)(1000 * offset / filesize);
222
if (oldfraction != newfraction)
224
cout << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
229
// Did we finish the last block
232
blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, (size_t)need);
233
blockcontext.Update((size_t)need);
236
blockcontext.Final(blockhash);
238
// Store the block hash and block crc in the file verification packet.
239
verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
246
// Finish computing the file hash.
248
filecontext.Final(filehash);
250
// Store the file hash in the file description packet.
251
descriptionpacket->HashFull(filehash);
253
// Did we compute the 16k hash.
256
// Store the 16k hash in the file description packet.
257
descriptionpacket->Hash16k(filehash);
262
// Compute the fileid and store it in the verification packet.
263
descriptionpacket->ComputeFileId();
264
verificationpacket->FileId(descriptionpacket->FileId());
270
void Par2CreatorSourceFile::Close(void)
276
void Par2CreatorSourceFile::RecordCriticalPackets(list<CriticalPacket*> &criticalpackets)
278
// Add the file description packet and file verification packet to
279
// the critical packet list.
280
criticalpackets.push_back(descriptionpacket);
281
criticalpackets.push_back(verificationpacket);
284
bool Par2CreatorSourceFile::CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right)
286
// Sort source files based on fileid
287
return left->descriptionpacket->FileId() < right->descriptionpacket->FileId();
290
const MD5Hash& Par2CreatorSourceFile::FileId(void) const
292
// Get the file id hash
293
return descriptionpacket->FileId();
296
void Par2CreatorSourceFile::InitialiseSourceBlocks(vector<DataBlock>::iterator &sourceblock, u64 blocksize)
298
for (u32 blocknum=0; blocknum<blockcount; blocknum++)
300
// Configure each source block to an appropriate offset and length within the source file.
301
sourceblock->SetLocation(diskfile, // file
302
blocknum * blocksize); // offset
303
sourceblock->SetLength(min(blocksize, filesize - (u64)blocknum * blocksize)); // length
308
void Par2CreatorSourceFile::UpdateHashes(u32 blocknumber, const void *buffer, size_t length)
310
// Compute the crc and hash of the data
311
u32 blockcrc = ~0 ^ CRCUpdateBlock(~0, length, buffer);
312
MD5Context blockcontext;
313
blockcontext.Update(buffer, length);
315
blockcontext.Final(blockhash);
317
// Store the results in the verification packet
318
verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
321
// Update the full file hash, but don't go beyond the end of the file
322
if (length > filesize - blocknumber * length)
324
length = (size_t)(filesize - blocknumber * (u64)length);
327
assert(contextfull != 0);
329
contextfull->Update(buffer, length);
332
void Par2CreatorSourceFile::FinishHashes(void)
334
assert(contextfull != 0);
336
// Finish computation of the full file hash
338
contextfull->Final(hash);
340
// Store it in the description packet
341
descriptionpacket->HashFull(hash);