|
@@ -18,17 +18,47 @@
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
#include "patchfile.h"
|
|
#include "patchfile.h"
|
|
|
#include "config_express.h"
|
|
#include "config_express.h"
|
|
|
|
|
+#include "error_utils.h"
|
|
|
|
|
|
|
|
#include <stdio.h> // for tempnam
|
|
#include <stdio.h> // for tempnam
|
|
|
|
|
|
|
|
|
|
+// this actually slows things down...
|
|
|
|
|
+//#define USE_MD5_HASH
|
|
|
|
|
+
|
|
|
|
|
+#ifdef USE_MD5_HASH
|
|
|
|
|
+#include "crypto_utils.h"
|
|
|
|
|
+#include "hashVal.h"
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+// Patch File Format ///////////////////////////////////////////////
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// [ HEADER ]
|
|
|
|
|
+// 4 bytes 0xfeebfaab ("magic number")
|
|
|
|
|
+// 4 bytes length of resulting patched file
|
|
|
|
|
+// 4 bytes LFN = length of target file's name
|
|
|
|
|
+// LFN bytes file name string
|
|
|
|
|
+//
|
|
|
|
|
+// [ ADD/COPY pairs; repeated N times ]
|
|
|
|
|
+// 2 bytes AL = ADD length
|
|
|
|
|
+// AL bytes bytes to add
|
|
|
|
|
+// 2 bytes CL = COPY length
|
|
|
|
|
+// 4 bytes offset of data to copy from original file, if CL != 0
|
|
|
|
|
+//
|
|
|
|
|
+// [ TERMINATOR ]
|
|
|
|
|
+// 2 bytes zero-length ADD
|
|
|
|
|
+// 2 bytes zero-length COPY
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Defines
|
|
// Defines
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
const PN_uint32 Patchfile::_magic_number = 0xfeebfaab;
|
|
const PN_uint32 Patchfile::_magic_number = 0xfeebfaab;
|
|
|
|
|
|
|
|
const PN_uint32 Patchfile::_HASHTABLESIZE = PN_uint32(1) << 16;
|
|
const PN_uint32 Patchfile::_HASHTABLESIZE = PN_uint32(1) << 16;
|
|
|
-const PN_uint32 Patchfile::_footprint_length = 16;
|
|
|
|
|
|
|
+const PN_uint32 Patchfile::_DEFAULT_FOOTPRINT_LENGTH = 9; // this gave the smallest patch file for libpanda.dll when tested, 12/20/2000
|
|
|
const PN_uint32 Patchfile::_NULL_VALUE = PN_uint32(0) - 1;
|
|
const PN_uint32 Patchfile::_NULL_VALUE = PN_uint32(0) - 1;
|
|
|
|
|
+const PN_uint32 Patchfile::_MAX_RUN_LENGTH = (PN_uint32(1) << 16) - 1;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Patchfile::Constructor
|
|
// Function: Patchfile::Constructor
|
|
@@ -58,38 +88,292 @@ Patchfile(PT(Buffer) buffer) {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Patchfile::
|
|
void Patchfile::
|
|
|
init(PT(Buffer) buffer) {
|
|
init(PT(Buffer) buffer) {
|
|
|
|
|
+ _initiated = false;
|
|
|
nassertv(!buffer.is_null());
|
|
nassertv(!buffer.is_null());
|
|
|
_buffer = buffer;
|
|
_buffer = buffer;
|
|
|
|
|
|
|
|
- // get all set up with a temp file
|
|
|
|
|
- char *temp_name = tempnam(NULL, "pf");
|
|
|
|
|
- _temp_file_name = temp_name;
|
|
|
|
|
- _temp_file_name.set_binary();
|
|
|
|
|
- delete temp_name;
|
|
|
|
|
|
|
+ reset_footprint_length();
|
|
|
|
|
|
|
|
- reset();
|
|
|
|
|
|
|
+ _datagram.clear();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: Patchfile::reset
|
|
|
|
|
|
|
+// Function: Patchfile::Destructor
|
|
|
// Access: Public
|
|
// Access: Public
|
|
|
// Description:
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
+Patchfile::
|
|
|
|
|
+~Patchfile(void) {
|
|
|
|
|
+ if (true == _initiated)
|
|
|
|
|
+ cleanup();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Patchfile::cleanup
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description:
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
void Patchfile::
|
|
void Patchfile::
|
|
|
-reset(void) {
|
|
|
|
|
|
|
+cleanup(void) {
|
|
|
|
|
+ if (false == _initiated) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::cleanup() - Patching has not been initiated"
|
|
|
|
|
+ << endl;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // close files
|
|
|
|
|
+ _origfile_stream.close();
|
|
|
|
|
+ _patch_stream.close();
|
|
|
|
|
+ _write_stream.close();
|
|
|
|
|
+
|
|
|
|
|
+ _initiated = false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+///// PATCH FILE APPLY MEMBER FUNCTIONS
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Patchfile::initiate
|
|
|
|
|
+// Access: Public
|
|
|
|
|
+// Description: Set up to apply the patch to the file (original
|
|
|
|
|
+// file and patch are destroyed in the process).
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+int Patchfile::
|
|
|
|
|
+initiate(Filename &patch_file, Filename &file) {
|
|
|
|
|
+ const int _header_length = sizeof(PN_uint32) + sizeof(PN_uint32) + sizeof(PN_int32);
|
|
|
|
|
+
|
|
|
|
|
+ if (true == _initiated) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::initiate_apply() - Patching has already been initiated"
|
|
|
|
|
+ << endl;
|
|
|
|
|
+ return EU_error_abort;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Open the patch file for read
|
|
|
|
|
+ _patch_file = patch_file;
|
|
|
|
|
+ _patch_file.set_binary();
|
|
|
|
|
+ if (!_patch_file.open_read(_patch_stream)) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() - Failed to open file: " << _patch_file << endl;
|
|
|
|
|
+ return get_write_error();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Open the original file for read
|
|
|
|
|
+ _orig_file = file;
|
|
|
|
|
+ _orig_file.set_binary();
|
|
|
|
|
+ if (!file.open_read(_origfile_stream)) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() - Failed to open file: " << file << endl;
|
|
|
|
|
+ return get_write_error();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Open the temp file for write
|
|
|
|
|
+ {
|
|
|
|
|
+ char *tempfilename = _tempnam(".", "pf");
|
|
|
|
|
+ if (NULL == tempfilename) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() - Failed to create temp file name, using default" << endl;
|
|
|
|
|
+ _temp_file = "patcher_temp_file";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //cout << tempfilename << endl;
|
|
|
|
|
+ _temp_file = tempfilename;
|
|
|
|
|
+ free(tempfilename);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ _temp_file.set_binary();
|
|
|
|
|
+ if (!_temp_file.open_write(_write_stream)) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() - Failed to open file: " << _temp_file << endl;
|
|
|
|
|
+ return get_write_error();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /////////////
|
|
|
|
|
+ // read header, make sure the patch file is valid
|
|
|
|
|
+
|
|
|
|
|
+ // check the magic number
|
|
|
|
|
+ nassertr(_buffer->get_length() >= _header_length, false);
|
|
|
|
|
+ _patch_stream.read(_buffer->_buffer, _header_length);
|
|
|
_datagram.clear();
|
|
_datagram.clear();
|
|
|
|
|
+ _datagram.append_data(_buffer->_buffer, _header_length);
|
|
|
|
|
+ DatagramIterator di(_datagram);
|
|
|
|
|
+ PN_uint32 magic_number = di.get_uint32();
|
|
|
|
|
+ if (magic_number != _magic_number) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() - invalid patch file: " << _patch_file << endl;
|
|
|
|
|
+ //return PF_error_invalidpatchfile;
|
|
|
|
|
+ return EU_error_abort;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // get the length of the patched result file
|
|
|
|
|
+ _result_file_length = di.get_uint32();
|
|
|
|
|
+
|
|
|
|
|
+ // check the filename
|
|
|
|
|
+ PN_int32 name_length = di.get_int32();
|
|
|
|
|
+ nassertr(_buffer->get_length() >= name_length, false);
|
|
|
|
|
+ _patch_stream.read(_buffer->_buffer, name_length);
|
|
|
|
|
+ _datagram.clear();
|
|
|
|
|
+ _datagram.append_data(_buffer->_buffer, name_length);
|
|
|
|
|
+ DatagramIterator di2(_datagram);
|
|
|
|
|
+ string name = di2.extract_bytes(name_length);
|
|
|
|
|
+ if (name != file.get_basename()) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() - patch intended for file: " << name
|
|
|
|
|
+ << ", not file: " << file << endl;
|
|
|
|
|
+ //return PF_error_wrongpatchfile;
|
|
|
|
|
+ return EU_error_abort;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ express_cat.debug()
|
|
|
|
|
+ << "Patchfile::apply() - valid patchfile for file: " << name << endl;
|
|
|
|
|
+
|
|
|
|
|
+ _total_bytes_processed = 0;
|
|
|
|
|
+
|
|
|
|
|
+ _initiated = true;
|
|
|
|
|
+
|
|
|
|
|
+ return EU_success;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: Patchfile::Destructor
|
|
|
|
|
|
|
+// Function: Patchfile::run
|
|
|
|
|
+// Access: Public
|
|
|
|
|
+// Description: Perform one buffer's worth of patching
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+int Patchfile::
|
|
|
|
|
+run(void) {
|
|
|
|
|
+ // Now patch the file using the given buffer
|
|
|
|
|
+ int buflen;
|
|
|
|
|
+ int bytes_read;
|
|
|
|
|
+ PN_uint16 ADD_length;
|
|
|
|
|
+ PN_uint16 COPY_length;
|
|
|
|
|
+ PN_uint32 COPY_offset;
|
|
|
|
|
+
|
|
|
|
|
+ if (_initiated == false) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::run_apply() - Patching has not been initiated"
|
|
|
|
|
+ << endl;
|
|
|
|
|
+ return EU_error_abort;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ buflen = _buffer->get_length();
|
|
|
|
|
+ bytes_read = 0;
|
|
|
|
|
+
|
|
|
|
|
+ while (bytes_read < buflen) {
|
|
|
|
|
+ ///////////
|
|
|
|
|
+ // read # of ADD bytes
|
|
|
|
|
+ nassertr(_buffer->get_length() >= (int)sizeof(ADD_length), false);
|
|
|
|
|
+ _patch_stream.read(_buffer->_buffer, sizeof(ADD_length));
|
|
|
|
|
+ _datagram.clear();
|
|
|
|
|
+ _datagram.append_data(_buffer->_buffer, sizeof(ADD_length));
|
|
|
|
|
+ DatagramIterator di(_datagram);
|
|
|
|
|
+ ADD_length = di.get_uint16();
|
|
|
|
|
+
|
|
|
|
|
+ bytes_read += (int)ADD_length;
|
|
|
|
|
+ _total_bytes_processed += (int)ADD_length;
|
|
|
|
|
+
|
|
|
|
|
+ // if there are bytes to add, read them from patch file and write them to output
|
|
|
|
|
+ if (0 != ADD_length) {
|
|
|
|
|
+ PN_uint32 bytes_left = (PN_uint32)ADD_length;
|
|
|
|
|
+
|
|
|
|
|
+ //cout << "ADD: " << ADD_length << endl;
|
|
|
|
|
+
|
|
|
|
|
+ while (bytes_left > 0) {
|
|
|
|
|
+ PN_uint32 bytes_this_time = (int)min(bytes_left, buflen);
|
|
|
|
|
+ _patch_stream.read(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
+ _write_stream.write(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
+ bytes_left -= bytes_this_time;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ///////////
|
|
|
|
|
+ // read # of COPY bytes
|
|
|
|
|
+ nassertr(_buffer->get_length() >= (int)sizeof(COPY_length), false);
|
|
|
|
|
+ _patch_stream.read(_buffer->_buffer, sizeof(COPY_length));
|
|
|
|
|
+ _datagram.clear();
|
|
|
|
|
+ _datagram.append_data(_buffer->_buffer, sizeof(COPY_length));
|
|
|
|
|
+ DatagramIterator di2(_datagram);
|
|
|
|
|
+ COPY_length = di2.get_uint16();
|
|
|
|
|
+
|
|
|
|
|
+ bytes_read += (int)COPY_length;
|
|
|
|
|
+ _total_bytes_processed += (int)COPY_length;
|
|
|
|
|
+
|
|
|
|
|
+ // if there are bytes to copy, read them from original file and write them to output
|
|
|
|
|
+ if (0 != COPY_length) {
|
|
|
|
|
+ // read copy offset
|
|
|
|
|
+ nassertr(_buffer->get_length() >= (int)sizeof(COPY_offset), false);
|
|
|
|
|
+ _patch_stream.read(_buffer->_buffer, sizeof(COPY_offset));
|
|
|
|
|
+ _datagram.clear();
|
|
|
|
|
+ _datagram.append_data(_buffer->_buffer, sizeof(COPY_offset));
|
|
|
|
|
+ DatagramIterator di(_datagram);
|
|
|
|
|
+ COPY_offset = di.get_uint32();
|
|
|
|
|
+
|
|
|
|
|
+ //cout << "COPY: " << COPY_length << " bytes at offset " << COPY_offset << endl;
|
|
|
|
|
+
|
|
|
|
|
+ // seek to the offset
|
|
|
|
|
+ _origfile_stream.seekg(COPY_offset, ios::beg);
|
|
|
|
|
+
|
|
|
|
|
+ // read the copy bytes from original file and write them to output
|
|
|
|
|
+ PN_uint32 bytes_left = (PN_uint32)COPY_length;
|
|
|
|
|
+
|
|
|
|
|
+ while (bytes_left > 0) {
|
|
|
|
|
+ PN_uint32 bytes_this_time = ((int)bytes_left < buflen) ? bytes_left : buflen;
|
|
|
|
|
+ _origfile_stream.read(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
+ _write_stream.write(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
+ bytes_left -= bytes_this_time;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // if we got a pair of zero-length ADD and COPY blocks, we're done
|
|
|
|
|
+ if ((0 == ADD_length) && (0 == COPY_length)) {
|
|
|
|
|
+ cleanup();
|
|
|
|
|
+
|
|
|
|
|
+ //cout << _result_file_length << " " << _total_bytes_processed << endl;
|
|
|
|
|
+
|
|
|
|
|
+ // delete the patch file and the original file
|
|
|
|
|
+ _patch_file.unlink();
|
|
|
|
|
+ _orig_file.unlink();
|
|
|
|
|
+
|
|
|
|
|
+ // rename the temp file
|
|
|
|
|
+ if (!_temp_file.rename_to(_orig_file)) {
|
|
|
|
|
+ express_cat.error()
|
|
|
|
|
+ << "Patchfile::apply() failed to rename temp file to: " << _orig_file
|
|
|
|
|
+ << endl;
|
|
|
|
|
+ //return PF_error_renamingtempfile;
|
|
|
|
|
+ return EU_error_abort;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return EU_success;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return EU_ok;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Patchfile::apply
|
|
|
// Access: Public
|
|
// Access: Public
|
|
|
// Description:
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-Patchfile::
|
|
|
|
|
-~Patchfile(void) {
|
|
|
|
|
- _temp_file_name.unlink();
|
|
|
|
|
|
|
+bool Patchfile::
|
|
|
|
|
+apply(Filename &patch_file, Filename &file) {
|
|
|
|
|
+ int ret = initiate(patch_file, file);
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return false;
|
|
|
|
|
+ for (;;) {
|
|
|
|
|
+ ret = run();
|
|
|
|
|
+ if (ret == EU_success)
|
|
|
|
|
+ return true;
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+///// PATCH FILE BUILDING MEMBER FUNCTIONS
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Patchfile::calc_hash
|
|
// Function: Patchfile::calc_hash
|
|
|
// Access: Private
|
|
// Access: Private
|
|
@@ -97,6 +381,15 @@ Patchfile::
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
PN_uint16 Patchfile::
|
|
PN_uint16 Patchfile::
|
|
|
calc_hash(const char *buffer) {
|
|
calc_hash(const char *buffer) {
|
|
|
|
|
+#ifdef USE_MD5_HASH
|
|
|
|
|
+ HashVal hash;
|
|
|
|
|
+
|
|
|
|
|
+ md5_a_buffer((const unsigned char*)buffer, (int)_footprint_length, hash);
|
|
|
|
|
+
|
|
|
|
|
+ //cout << PN_uint16(hash.get_value(0)) << " ";
|
|
|
|
|
+
|
|
|
|
|
+ return PN_uint16(hash.get_value(0));
|
|
|
|
|
+#else
|
|
|
PN_uint16 hash_value = 0;
|
|
PN_uint16 hash_value = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < (int)_footprint_length; i++) {
|
|
for(int i = 0; i < (int)_footprint_length; i++) {
|
|
@@ -105,13 +398,33 @@ calc_hash(const char *buffer) {
|
|
|
buffer++;
|
|
buffer++;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ //cout << hash_value << " ";
|
|
|
|
|
+
|
|
|
return hash_value;
|
|
return hash_value;
|
|
|
|
|
+#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Patchfile::build_hash_link_tables
|
|
// Function: Patchfile::build_hash_link_tables
|
|
|
// Access: Private
|
|
// Access: Private
|
|
|
// Description:
|
|
// Description:
|
|
|
|
|
+// The hash and link tables allow for a quick, linear
|
|
|
|
|
+// search of all locations in the file that begin with
|
|
|
|
|
+// a particular sequence of bytes, or "footprint."
|
|
|
|
|
+//
|
|
|
|
|
+// The hash table is a table of offsets into the file,
|
|
|
|
|
+// with one entry for every possible footprint hash
|
|
|
|
|
+// value. For a hash of a footprint, the entry at the
|
|
|
|
|
+// offset of the hash value provides an initial location
|
|
|
|
|
+// in the file that has a matching footprint.
|
|
|
|
|
+//
|
|
|
|
|
+// The link table is a large linked list of file offsets,
|
|
|
|
|
+// with one entry for every byte in the file. Each offset
|
|
|
|
|
+// in the link table will point to another offset that
|
|
|
|
|
+// has the same footprint at the corresponding offset in the
|
|
|
|
|
+// actual file. Starting with an offset taken from the hash
|
|
|
|
|
+// table, one can rapidly produce a list of offsets that
|
|
|
|
|
+// all have the same footprint.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Patchfile::
|
|
void Patchfile::
|
|
|
build_hash_link_tables(const char *buffer_orig, PN_uint32 length_orig,
|
|
build_hash_link_tables(const char *buffer_orig, PN_uint32 length_orig,
|
|
@@ -129,6 +442,8 @@ build_hash_link_tables(const char *buffer_orig, PN_uint32 length_orig,
|
|
|
link_table[i] = _NULL_VALUE;
|
|
link_table[i] = _NULL_VALUE;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if(length_orig < _footprint_length) return;
|
|
|
|
|
+
|
|
|
// run through original file and hash each footprint
|
|
// run through original file and hash each footprint
|
|
|
for(i = 0; i < (length_orig - _footprint_length); i++) {
|
|
for(i = 0; i < (length_orig - _footprint_length); i++) {
|
|
|
PN_uint16 hash_value = calc_hash(&buffer_orig[i]);
|
|
PN_uint16 hash_value = calc_hash(&buffer_orig[i]);
|
|
@@ -170,7 +485,7 @@ calc_match_length(const char* buf1, const char* buf2, PN_uint32 max_length) {
|
|
|
// original file that matches a string in the new file.
|
|
// original file that matches a string in the new file.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Patchfile::
|
|
void Patchfile::
|
|
|
-find_longest_match(PN_uint32 new_pos, PN_uint32 ©_offset, PN_uint32 ©_length,
|
|
|
|
|
|
|
+find_longest_match(PN_uint32 new_pos, PN_uint32 ©_offset, PN_uint16 ©_length,
|
|
|
PN_uint32 *hash_table, PN_uint32 *link_table, const char* buffer_orig,
|
|
PN_uint32 *hash_table, PN_uint32 *link_table, const char* buffer_orig,
|
|
|
PN_uint32 length_orig, const char* buffer_new, PN_uint32 length_new) {
|
|
PN_uint32 length_orig, const char* buffer_new, PN_uint32 length_new) {
|
|
|
|
|
|
|
@@ -187,16 +502,17 @@ find_longest_match(PN_uint32 new_pos, PN_uint32 ©_offset, PN_uint32 ©_le
|
|
|
copy_offset = hash_table[hash_value];
|
|
copy_offset = hash_table[hash_value];
|
|
|
|
|
|
|
|
// calc match length
|
|
// calc match length
|
|
|
- copy_length = calc_match_length(&buffer_new[new_pos], &buffer_orig[copy_offset],
|
|
|
|
|
- min((length_new - new_pos),(length_orig - copy_offset)));
|
|
|
|
|
|
|
+ copy_length = (PN_uint16)calc_match_length(&buffer_new[new_pos], &buffer_orig[copy_offset],
|
|
|
|
|
+ min(min((length_new - new_pos),(length_orig - copy_offset)), _MAX_RUN_LENGTH));
|
|
|
|
|
|
|
|
// run through link table, see if we find any longer matches
|
|
// run through link table, see if we find any longer matches
|
|
|
- PN_uint32 match_offset, match_length;
|
|
|
|
|
|
|
+ PN_uint32 match_offset;
|
|
|
|
|
+ PN_uint16 match_length;
|
|
|
match_offset = link_table[copy_offset];
|
|
match_offset = link_table[copy_offset];
|
|
|
|
|
|
|
|
while (match_offset != _NULL_VALUE) {
|
|
while (match_offset != _NULL_VALUE) {
|
|
|
- match_length = calc_match_length(&buffer_new[new_pos], &buffer_orig[match_offset],
|
|
|
|
|
- min((length_new - new_pos),(length_orig - match_offset)));
|
|
|
|
|
|
|
+ match_length = (PN_uint16)calc_match_length(&buffer_new[new_pos], &buffer_orig[match_offset],
|
|
|
|
|
+ min(min((length_new - new_pos),(length_orig - match_offset)), _MAX_RUN_LENGTH));
|
|
|
|
|
|
|
|
// have we found a longer match?
|
|
// have we found a longer match?
|
|
|
if (match_length > copy_length) {
|
|
if (match_length > copy_length) {
|
|
@@ -215,12 +531,12 @@ find_longest_match(PN_uint32 new_pos, PN_uint32 ©_offset, PN_uint32 ©_le
|
|
|
// Description:
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Patchfile::
|
|
void Patchfile::
|
|
|
-emit_ADD(ofstream &write_stream, PN_uint32 length, const char* buffer) {
|
|
|
|
|
-// cout << "ADD: " << length << " bytes" << endl;
|
|
|
|
|
|
|
+emit_ADD(ofstream &write_stream, PN_uint16 length, const char* buffer) {
|
|
|
|
|
+ //cout << "ADD: " << length << " bytes" << endl;
|
|
|
|
|
|
|
|
// write ADD length
|
|
// write ADD length
|
|
|
_datagram.clear();
|
|
_datagram.clear();
|
|
|
- _datagram.add_uint32(length);
|
|
|
|
|
|
|
+ _datagram.add_uint16(length);
|
|
|
string msg = _datagram.get_message();
|
|
string msg = _datagram.get_message();
|
|
|
write_stream.write((char *)msg.data(), msg.length());
|
|
write_stream.write((char *)msg.data(), msg.length());
|
|
|
|
|
|
|
@@ -239,12 +555,12 @@ emit_ADD(ofstream &write_stream, PN_uint32 length, const char* buffer) {
|
|
|
// Description:
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Patchfile::
|
|
void Patchfile::
|
|
|
-emit_COPY(ofstream &write_stream, PN_uint32 length, PN_uint32 offset) {
|
|
|
|
|
-// cout << "COPY: " << length << " bytes at offset " << offset << endl;
|
|
|
|
|
|
|
+emit_COPY(ofstream &write_stream, PN_uint16 length, PN_uint32 offset) {
|
|
|
|
|
+ //cout << "COPY: " << length << " bytes at offset " << offset << endl;
|
|
|
|
|
|
|
|
// write COPY length
|
|
// write COPY length
|
|
|
_datagram.clear();
|
|
_datagram.clear();
|
|
|
- _datagram.add_uint32(length);
|
|
|
|
|
|
|
+ _datagram.add_uint16(length);
|
|
|
string msg = _datagram.get_message();
|
|
string msg = _datagram.get_message();
|
|
|
write_stream.write((char *)msg.data(), msg.length());
|
|
write_stream.write((char *)msg.data(), msg.length());
|
|
|
|
|
|
|
@@ -326,7 +642,7 @@ build(Filename &file_orig, Filename &file_new) {
|
|
|
// build hash and link tables for original file
|
|
// build hash and link tables for original file
|
|
|
build_hash_link_tables(buffer_orig, length_orig, hash_table, link_table);
|
|
build_hash_link_tables(buffer_orig, length_orig, hash_table, link_table);
|
|
|
|
|
|
|
|
- // write the patch file header
|
|
|
|
|
|
|
+ // prepare to write the patch file header
|
|
|
// Strip the v# out of the filename
|
|
// Strip the v# out of the filename
|
|
|
// Save the original extension
|
|
// Save the original extension
|
|
|
string ext = file_orig.get_extension();
|
|
string ext = file_orig.get_extension();
|
|
@@ -334,36 +650,50 @@ build(Filename &file_orig, Filename &file_new) {
|
|
|
Filename tfile = file_orig.get_basename_wo_extension();
|
|
Filename tfile = file_orig.get_basename_wo_extension();
|
|
|
// Now strip out the .v#
|
|
// Now strip out the .v#
|
|
|
string fname = tfile.get_basename_wo_extension();
|
|
string fname = tfile.get_basename_wo_extension();
|
|
|
- fname += ".";
|
|
|
|
|
- fname += ext;
|
|
|
|
|
- write_header(write_stream, fname);
|
|
|
|
|
|
|
+ if(ext != "") {
|
|
|
|
|
+ fname += ".";
|
|
|
|
|
+ fname += ext;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // write the patch file header
|
|
|
|
|
+ _datagram.clear();
|
|
|
|
|
+ _datagram.add_uint32(_magic_number);
|
|
|
|
|
+ _datagram.add_uint32(length_new);
|
|
|
|
|
+ _datagram.add_int32(fname.length());
|
|
|
|
|
+ _datagram.append_data(fname.c_str(), fname.length());
|
|
|
|
|
+ string msg = _datagram.get_message();
|
|
|
|
|
+ write_stream.write((char *)msg.data(), msg.length());
|
|
|
|
|
|
|
|
// run through new file
|
|
// run through new file
|
|
|
PN_uint32 new_pos = 0;
|
|
PN_uint32 new_pos = 0;
|
|
|
PN_uint32 ADD_offset = new_pos; // this is the offset for the start of ADD operations
|
|
PN_uint32 ADD_offset = new_pos; // this is the offset for the start of ADD operations
|
|
|
|
|
|
|
|
- while (new_pos < (length_new - _footprint_length)) {
|
|
|
|
|
|
|
+ if(length_new >= _footprint_length)
|
|
|
|
|
+ {
|
|
|
|
|
+ while (new_pos < (length_new - _footprint_length)) {
|
|
|
|
|
|
|
|
- // find best match for current position
|
|
|
|
|
- PN_uint32 COPY_offset, COPY_length;
|
|
|
|
|
|
|
+ // find best match for current position
|
|
|
|
|
+ PN_uint32 COPY_offset;
|
|
|
|
|
+ PN_uint16 COPY_length;
|
|
|
|
|
|
|
|
- find_longest_match(new_pos, COPY_offset, COPY_length, hash_table, link_table,
|
|
|
|
|
- buffer_orig, length_orig, buffer_new, length_new);
|
|
|
|
|
|
|
+ find_longest_match(new_pos, COPY_offset, COPY_length, hash_table, link_table,
|
|
|
|
|
+ buffer_orig, length_orig, buffer_new, length_new);
|
|
|
|
|
|
|
|
- // if no match or match not longer than footprint length, skip to next byte
|
|
|
|
|
- if (COPY_length < _footprint_length) {
|
|
|
|
|
- // go to next byte
|
|
|
|
|
- new_pos++;
|
|
|
|
|
- } else {
|
|
|
|
|
- // emit ADD for all skipped bytes
|
|
|
|
|
- emit_ADD(write_stream, new_pos - ADD_offset, &buffer_new[ADD_offset]);
|
|
|
|
|
|
|
+ // if no match or match not longer than footprint length, skip to next byte
|
|
|
|
|
+ if (COPY_length < _footprint_length) {
|
|
|
|
|
+ // go to next byte
|
|
|
|
|
+ new_pos++;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // emit ADD for all skipped bytes
|
|
|
|
|
+ emit_ADD(write_stream, new_pos - ADD_offset, &buffer_new[ADD_offset]);
|
|
|
|
|
|
|
|
- // emit COPY for matching string
|
|
|
|
|
- emit_COPY(write_stream, COPY_length, COPY_offset);
|
|
|
|
|
|
|
+ // emit COPY for matching string
|
|
|
|
|
+ emit_COPY(write_stream, COPY_length, COPY_offset);
|
|
|
|
|
|
|
|
- // skip past match in new_file
|
|
|
|
|
- new_pos += COPY_length;
|
|
|
|
|
- ADD_offset = new_pos;
|
|
|
|
|
|
|
+ // skip past match in new_file
|
|
|
|
|
+ new_pos += (PN_uint32)COPY_length;
|
|
|
|
|
+ ADD_offset = new_pos;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -383,209 +713,3 @@ build(Filename &file_orig, Filename &file_new) {
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: Patchfile::apply
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description: Apply the patch to the file (original file and
|
|
|
|
|
-// patch are destroyed in the process).
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-bool Patchfile::
|
|
|
|
|
-apply(Filename &patch, Filename &file) {
|
|
|
|
|
- const int _header_length = sizeof(PN_uint32) + sizeof(PN_int32);
|
|
|
|
|
-
|
|
|
|
|
- // Open the patch file for read
|
|
|
|
|
- ifstream patch_stream;
|
|
|
|
|
- patch.set_binary();
|
|
|
|
|
- if (!patch.open_read(patch_stream)) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::apply() - Failed to open file: " << patch << endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Open the original file for read
|
|
|
|
|
- ifstream origfile_stream;
|
|
|
|
|
- file.set_binary();
|
|
|
|
|
- if (!file.open_read(origfile_stream)) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::apply() - Failed to open file: " << file << endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Open the temp file for write
|
|
|
|
|
- ofstream write_stream;
|
|
|
|
|
- Filename mofile = "patcher_temp_file";
|
|
|
|
|
- mofile.set_binary();
|
|
|
|
|
- if (!mofile.open_write(write_stream)) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::apply() - Failed to open file: " << mofile << endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /////////////
|
|
|
|
|
- // read header, make sure the patch file is valid
|
|
|
|
|
-
|
|
|
|
|
- // check the magic number
|
|
|
|
|
- nassertr(_buffer->get_length() >= _header_length, false);
|
|
|
|
|
- patch_stream.read(_buffer->_buffer, _header_length);
|
|
|
|
|
- _datagram.clear();
|
|
|
|
|
- _datagram.append_data(_buffer->_buffer, _header_length);
|
|
|
|
|
- DatagramIterator di(_datagram);
|
|
|
|
|
- PN_uint32 magic_number = di.get_uint32();
|
|
|
|
|
- if (magic_number != _magic_number) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::apply() - invalid patch file: " << patch << endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // check the filename
|
|
|
|
|
- PN_int32 name_length = di.get_int32();
|
|
|
|
|
- nassertr(_buffer->get_length() >= name_length, false);
|
|
|
|
|
- patch_stream.read(_buffer->_buffer, name_length);
|
|
|
|
|
- _datagram.clear();
|
|
|
|
|
- _datagram.append_data(_buffer->_buffer, name_length);
|
|
|
|
|
- DatagramIterator di2(_datagram);
|
|
|
|
|
- string name = di2.extract_bytes(name_length);
|
|
|
|
|
- if (name != file.get_basename()) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::apply() - patch intended for file: " << name
|
|
|
|
|
- << ", not file: " << file << endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- express_cat.debug()
|
|
|
|
|
- << "Patchfile::apply() - valid patchfile for file: " << name << endl;
|
|
|
|
|
-
|
|
|
|
|
- // Now patch the file using the given buffer
|
|
|
|
|
- int buflen = _buffer->get_length();
|
|
|
|
|
- int done = 0;
|
|
|
|
|
- PN_uint32 ADD_length;
|
|
|
|
|
- PN_uint32 COPY_length;
|
|
|
|
|
- PN_uint32 COPY_offset;
|
|
|
|
|
-
|
|
|
|
|
- while (!done)
|
|
|
|
|
- {
|
|
|
|
|
- ///////////
|
|
|
|
|
- // read # of ADD bytes
|
|
|
|
|
- nassertr(_buffer->get_length() >= (int)sizeof(ADD_length), false);
|
|
|
|
|
- patch_stream.read(_buffer->_buffer, sizeof(ADD_length));
|
|
|
|
|
- _datagram.clear();
|
|
|
|
|
- _datagram.append_data(_buffer->_buffer, sizeof(ADD_length));
|
|
|
|
|
- DatagramIterator di(_datagram);
|
|
|
|
|
- ADD_length = di.get_uint32();
|
|
|
|
|
-
|
|
|
|
|
- // if there are bytes to add, read them from patch file and write them to output
|
|
|
|
|
- if (0 != ADD_length) {
|
|
|
|
|
- PN_uint32 bytes_left = ADD_length;
|
|
|
|
|
-
|
|
|
|
|
- while (bytes_left > 0) {
|
|
|
|
|
- PN_uint32 bytes_this_time = ((int)bytes_left < buflen) ? bytes_left : buflen;
|
|
|
|
|
- patch_stream.read(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
- write_stream.write(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
- bytes_left -= bytes_this_time;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ///////////
|
|
|
|
|
- // read # of COPY bytes
|
|
|
|
|
- nassertr(_buffer->get_length() >= (int)sizeof(COPY_length), false);
|
|
|
|
|
- patch_stream.read(_buffer->_buffer, sizeof(COPY_length));
|
|
|
|
|
- _datagram.clear();
|
|
|
|
|
- _datagram.append_data(_buffer->_buffer, sizeof(COPY_length));
|
|
|
|
|
- DatagramIterator di2(_datagram);
|
|
|
|
|
- COPY_length = di2.get_uint32();
|
|
|
|
|
-
|
|
|
|
|
- // if there are bytes to copy, read them from original file and write them to output
|
|
|
|
|
- if (0 != COPY_length) {
|
|
|
|
|
- // read copy offset
|
|
|
|
|
- nassertr(_buffer->get_length() >= (int)sizeof(COPY_offset), false);
|
|
|
|
|
- patch_stream.read(_buffer->_buffer, sizeof(COPY_offset));
|
|
|
|
|
- _datagram.clear();
|
|
|
|
|
- _datagram.append_data(_buffer->_buffer, sizeof(COPY_offset));
|
|
|
|
|
- DatagramIterator di(_datagram);
|
|
|
|
|
- COPY_offset = di.get_uint32();
|
|
|
|
|
-
|
|
|
|
|
- // seek to the offset
|
|
|
|
|
- origfile_stream.seekg(COPY_offset, ios::beg);
|
|
|
|
|
-
|
|
|
|
|
- // read the copy bytes from original file and write them to output
|
|
|
|
|
- PN_uint32 bytes_left = COPY_length;
|
|
|
|
|
-
|
|
|
|
|
- while (bytes_left > 0) {
|
|
|
|
|
- PN_uint32 bytes_this_time = ((int)bytes_left < buflen) ? bytes_left : buflen;
|
|
|
|
|
- origfile_stream.read(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
- write_stream.write(_buffer->_buffer, bytes_this_time);
|
|
|
|
|
- bytes_left -= bytes_this_time;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // if we got a pair of zero-length ADD and COPY blocks, we're done
|
|
|
|
|
- if ((0 == ADD_length) && (0 == COPY_length)) {
|
|
|
|
|
- done = 1;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // close files
|
|
|
|
|
- patch_stream.close();
|
|
|
|
|
- origfile_stream.close();
|
|
|
|
|
- write_stream.close();
|
|
|
|
|
-
|
|
|
|
|
- // delete the patch file and the original file
|
|
|
|
|
- patch.unlink();
|
|
|
|
|
- file.unlink();
|
|
|
|
|
-
|
|
|
|
|
- // rename the temp file
|
|
|
|
|
- if (!mofile.rename_to(file)) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::apply() failed to rename temp file to: " << file
|
|
|
|
|
- << endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: Patchfile::find_longest_sequence
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description:
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-int Patchfile::
|
|
|
|
|
-find_longest_sequence(Filename &infile, int &pos, int &len) const {
|
|
|
|
|
- // Open the file for read
|
|
|
|
|
- ifstream read_stream;
|
|
|
|
|
- infile.set_binary();
|
|
|
|
|
- if (!infile.open_read(read_stream)) {
|
|
|
|
|
- express_cat.error()
|
|
|
|
|
- << "Patchfile::find_longest_sequence() - Failed to open file: "
|
|
|
|
|
- << infile << endl;
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Determine file length
|
|
|
|
|
- read_stream.seekg(0, ios::end);
|
|
|
|
|
- len = read_stream.tellg();
|
|
|
|
|
- char *buffer = new char[len];
|
|
|
|
|
- read_stream.seekg(0, ios::beg);
|
|
|
|
|
- read_stream.read(buffer, len);
|
|
|
|
|
-
|
|
|
|
|
- pos = 0;
|
|
|
|
|
- char holder = 0;
|
|
|
|
|
- int seq_len;
|
|
|
|
|
- int longest_seq_len = 0;
|
|
|
|
|
- for (int i = 0; i < len; i++) {
|
|
|
|
|
- if (buffer[i] != holder) {
|
|
|
|
|
- holder = buffer[i];
|
|
|
|
|
- seq_len = 0;
|
|
|
|
|
- } else {
|
|
|
|
|
- if (++seq_len > longest_seq_len) {
|
|
|
|
|
- longest_seq_len = seq_len;
|
|
|
|
|
- pos = i;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- read_stream.close();
|
|
|
|
|
-
|
|
|
|
|
- return longest_seq_len;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|