/* * ZIDRAV, file corruption repairer * Copyright (C) 1999 Ben Wilhelm * * 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 "core.h" //#define CPU_BIGENDIAN #define strncpyn( dest, source, len ) strncpy( dest, source, len ); dest[len] = '\0'; #define IDS_UPT_INVCDT 17 #define IDS_UPT_INVCDP 18 #define SwapBO32(x) ((x>>24&0xFF)|((x&0xFF0000)>>8)|((x&0xFF00)<<8)|((x&0xFF)<<24)) #ifdef CPU_BIGENDIAN #define write32le(fs, x) be_write32le(fs, x) #define read32le(fs, x) be_read32le(fs, x) #define int32tole(x) SwapBO32(x) #else // LITTLEENDIAN #define write32le(fs, x) fs.write(x, 4) #define read32le(fs, x) fs.read(x, 4) #define int32tole(x) x #endif void be_write32le(iostream &output, char *buf) { int data; data = *(int*)buf; data = SwapBO32(data); output.write((char *)&data, 4); } void be_read32le(istream &input, char *buf) { int data; input.read((char *)&data, 4); data = SwapBO32(data); *(int*)buf = data; } void MakeChecksumCore( istream &input, iostream &output, int blocksize, int datalen ) { char *buffer; int i; long crc; short oldprc = 0; output.write( ZC, 8 ); output.write( CVER, 2 ); write32le(output, (char *)&datalen); write32le(output, (char *)&blocksize); buffer = new char[blocksize]; for( datalen % blocksize ? i = -1 : i = 0; i < datalen / blocksize; i++ ) { if (i >= 0) { short newprc = (short) (((float) (i * blocksize) / datalen) * 100); if (newprc != oldprc) { cout << (char) 13 << newprc << "%" << flush; oldprc = newprc; } } input.read( buffer, blocksize ); if( i == datalen / blocksize - 1 && (datalen % blocksize) ) // If it's the end of the file, only make the checksum for what's left CreateChecksum( buffer, datalen % blocksize, &crc ); else CreateChecksum( buffer, blocksize, &crc ); write32le(output, (char *)&crc); } delete [] buffer; cout << (char) 13 << "Creating internal file checksum...\n"; if (datalen % blocksize) MakeOverallChecksum( output, 18 + ( datalen / blocksize + 1 ) * 4 ); else MakeOverallChecksum( output, 18 + ( datalen / blocksize ) * 4 ); } void MakePatchCore( istream &cdti, istream &vstr, iostream &output, int cdtlen, int vstrlen, int * efound) { long filesize; long blocksize; long crc; long cdtcrc; long curbs; int i; long curolen; long temp; short oldprc = 0; char *buffer; *efound = 0; if( cdtlen < 22 ) { // 22 == minimum size - 8 for signature, 2 for version, 4 for blocksize, 4 for filesize, // 4 for comparison checksum - who says it can't be a .cdt of a 0-size file? :) cout << "Error - .CDT invalid\n"; return; } if( VerifyStream( cdti, cdtlen, IDS_UPT_INVCDT, CSV ) ) return; cdti.seekg( 10 ); read32le(cdti, (char *)&filesize); read32le(cdti, (char *)&blocksize); if( filesize != vstrlen ) { cout << "Error - verified file size is " << vstrlen << " instead of " << filesize << "\n"; return; } output.write( ZP, 8 ); output.write( PVER, 2 ); write32le(output, (char *)&filesize); curolen = 14; buffer = new char[ blocksize ]; for( vstrlen % blocksize ? i = -1 : i = 0; i < vstrlen / blocksize; i++ ) { if (i >= 0) { short newprc = (short) (((float) (i * blocksize) / vstrlen) * 100); if (newprc != oldprc) { cout << (char) 13 << newprc << "%" << flush; oldprc = newprc; } } if( i == vstrlen / blocksize - 1 && (vstrlen % blocksize)) curbs = vstrlen % blocksize; else curbs = blocksize; vstr.read( buffer, curbs ); read32le(cdti, (char *)&cdtcrc); CreateChecksum( buffer, curbs, &crc ); if( crc != cdtcrc ) { if (vstrlen % blocksize) temp = i * blocksize + blocksize; else temp = i * blocksize; cout << (char) 13 << "Corruption found at " << temp << "\n"; write32le(output, (char *)&temp); // write the current offset write32le(output, (char *)&curbs); output.write( buffer, curbs ); curolen += 8 + curbs; *efound = 1; } } delete [] buffer; if( *efound ) { temp = -1; write32le(output, (char *)&temp); // end flag cout << (char) 13 << "Creating internal file checksum...\n"; curolen += 4; MakeOverallChecksum( output, curolen ); } } void ApplyPatchCore( istream &cdpi, iostream &pstr, int cdplen, int pstrlen) { long filesize; long blocksize; long curbs = 0; long offset; char *buffer = NULL; if( cdplen < 22 ) { // 22 == minimum size - 8 for signature, 2 for version, 4 for blocksize, 4 for filesize, // 4 for comparison checksum cout << "Error - .CDP invalid\n"; return; } if( VerifyStream( cdpi, cdplen, IDS_UPT_INVCDP, PSV) ) return; cdpi.seekg( 10 ); read32le(cdpi, (char *)&filesize); if( filesize != pstrlen ) { cout << "Error - patch target file size is " << pstrlen << " instead of " << filesize << "\n"; return; } read32le(cdpi, (char *)&offset); read32le(cdpi, (char *)&blocksize); while( offset != -1 ) { if( blocksize > curbs ) { if( curbs != 0 ) delete [] buffer; buffer = new char[ blocksize ]; curbs = blocksize; } cdpi.read( buffer, blocksize ); pstr.seekp( offset ); pstr.write( buffer, blocksize ); read32le(cdpi, (char *)&offset); read32le(cdpi, (char *)&blocksize); } delete [] buffer; } int VerifyStream( istream &input, int datalen, int emsg, KSigver sigver ) { char *buffer; char minibuff[9]; long crc; buffer = new char[10]; input.read( buffer, 10 ); // Get the header, don't want to accidentally load a 100 meg file unless we have to :) strncpyn( minibuff, buffer, 8 ); // Copy the signature in if( strcmp( minibuff, sigver.signature ) ) { delete [] buffer; switch(emsg) { case IDS_UPT_INVCDT: cout << "Error - .CDT invalid\n"; break; case IDS_UPT_INVCDP: cout << "Error - .CDP invalid\n"; } return 1; } strncpyn( minibuff, &buffer[8], 2 ); // Version number comparison if( strcmp( minibuff, sigver.version ) ) { delete [] buffer; cout << "Error - file is v" << minibuff[0] << "." << minibuff[1] << ", program needs v" << sigver.version[0] << "." << sigver.version[1] << "\n"; return 1; } delete [] buffer; // FIXME: we shouldn't load whole patch/checksum file into memory! // 150M patches aren't common, but they happen sometime buffer = new char[ datalen ]; // want to load the whole thing if (!buffer) { switch(emsg) { case IDS_UPT_INVCDT: cout << "Error - not enough memory to load .CDT file :(\n"; break; case IDS_UPT_INVCDP: cout << "Error - not enough memory to load .CDP file :(\n"; } return 1; } input.seekg( 0 ); // go to the beginning input.read( buffer, datalen ); // and pull it all CreateChecksum( buffer, datalen - 4, &crc ); if( crc != int32tole(*(int *)&buffer[ datalen - 4 ]) ) { delete [] buffer; switch(emsg) { case IDS_UPT_INVCDT: cout << "Error - bad CRC for .CDT file (probably damaged?)\n"; break; case IDS_UPT_INVCDP: cout << "Error - bad CRC for .CDP file (probably damaged?)\n"; } return 1; } delete [] buffer; return 0; } void MakeOverallChecksum( iostream &st, long size ) { char *buffer; long crc; // FIXME: trying to load whole file... again... :( buffer = new char[size]; st.flush(); st.seekg(0); st.read( buffer, size ); CreateChecksum( buffer, size, &crc ); st.tellg(); write32le(st, (char *)&crc); delete [] buffer; } #define DO1( buffer ) *crc = crc_table[ ( *crc ^ *(buffer++) ) & 0xff ] ^ (*crc >> 8); #define DO2( buffer ) DO1( buffer ); DO1( buffer ); #define DO4( buffer ) DO2( buffer ); DO2( buffer ); #define DO8( buffer ) DO4( buffer ); DO4( buffer ); #define DO16( buffer ) DO8( buffer ); DO8( buffer ); void CreateChecksum( char *buffer, long size, long *crc ) { *crc = 0; *crc ^= 0xffffffffL; while ( size >= 16 ) { DO16( buffer ); size -= 16; } while ( size ) { DO1( buffer ); size--; } *crc ^= 0xffffffffL; }