15 #include "pandabase.h" 19 #include "config_express.h" 20 #include "error_utils.h" 21 #include "patchfile.h" 22 #include "streamReader.h" 23 #include "streamWriter.h" 24 #include "multifile.h" 26 #include "virtualFileSystem.h" 36 istream *Patchfile::_tar_istream = NULL;
61 const int _v0_header_length = 4 + 4 + 16;
62 const int _v1_header_length = 4 + 2 + 4 + 16 + 4 + 16;
82 const PN_uint32 Patchfile::_v0_magic_number = 0xfeebfaab;
83 const PN_uint32 Patchfile::_magic_number = 0xfeebfaac;
87 const PN_uint16 Patchfile::_current_version = 2;
89 const PN_uint32 Patchfile::_HASH_BITS = 24;
90 const PN_uint32 Patchfile::_HASHTABLESIZE = PN_uint32(1) << Patchfile::_HASH_BITS;
91 const PN_uint32 Patchfile::_DEFAULT_FOOTPRINT_LENGTH = 9;
92 const PN_uint32 Patchfile::_NULL_VALUE = PN_uint32(0) - 1;
93 const PN_uint32 Patchfile::_MAX_RUN_LENGTH = (PN_uint32(1) << 16) - 1;
94 const PN_uint32 Patchfile::_HASH_MASK = (PN_uint32(1) << Patchfile::_HASH_BITS) - 1;
113 Patchfile(PT(
Buffer) buffer) {
124 _rename_output_to_orig =
false;
125 _delete_patchfile =
false;
128 nassertv(!buffer.is_null());
132 _allow_multifile =
true;
134 _patch_stream = NULL;
135 _origfile_stream = NULL;
137 reset_footprint_length();
147 if (_hash_table != (PN_uint32 *)NULL) {
148 PANDA_FREE_ARRAY(_hash_table);
155 nassertv(_patch_stream == NULL);
156 nassertv(_origfile_stream == NULL);
168 <<
"Patchfile::cleanup() - Patching has not been initiated" 175 if (_origfile_stream != NULL) {
177 _origfile_stream = NULL;
179 if (_patch_stream != NULL) {
181 _patch_stream = NULL;
183 _write_stream.close();
209 _rename_output_to_orig =
true;
210 _delete_patchfile = !keep_temporary_files;
226 <<
"Patchfile::initiate() - Patching has already been initiated" 228 return EU_error_abort;
231 nassertr(orig_file != target_file, EU_error_abort);
236 nassertr(_origfile_stream == NULL, EU_error_abort);
237 _orig_file = orig_file;
240 if (_origfile_stream == NULL) {
242 <<
"Patchfile::initiate() - Failed to open file: " << _orig_file << endl;
243 return get_write_error();
247 _output_file = target_file;
249 if (!_output_file.open_write(_write_stream)) {
251 <<
"Patchfile::initiate() - Failed to open file: " << _output_file << endl;
252 return get_write_error();
255 if (express_cat.is_debug()) {
257 <<
"Patchfile using output file " << _output_file <<
"\n";
260 int result = internal_read_header(patch_file);
261 _total_bytes_processed = 0;
276 read_header(
const Filename &patch_file) {
279 <<
"Patchfile::initiate() - Patching has already been initiated" 281 return EU_error_abort;
284 int result = internal_read_header(patch_file);
285 if (_patch_stream != NULL) {
288 _patch_stream = NULL;
310 PN_uint16 ADD_length;
311 PN_uint16 COPY_length;
312 PN_int32 COPY_offset;
314 if (_initiated ==
false) {
316 <<
"Patchfile::run() - Patching has not been initiated" 318 return EU_error_abort;
321 nassertr(_patch_stream != NULL, EU_error_abort);
322 nassertr(_origfile_stream != NULL, EU_error_abort);
325 buflen = _buffer->get_length();
328 while (bytes_read < buflen) {
331 nassertr(_buffer->get_length() >= (int)
sizeof(ADD_length),
false);
332 ADD_length = patch_reader.get_uint16();
333 if (_patch_stream->fail()) {
335 <<
"Truncated patch file.\n";
336 return EU_error_file_invalid;
339 bytes_read += (int)ADD_length;
340 _total_bytes_processed += (int)ADD_length;
341 if (_total_bytes_processed > _total_bytes_to_process) {
343 <<
"Runaway patch file.\n";
344 return EU_error_file_invalid;
348 if (express_cat.is_spam() && ADD_length != 0) {
350 <<
"ADD: " << ADD_length <<
" (to " 351 << _write_stream.tellp() <<
")" << endl;
354 PN_uint32 bytes_left = (PN_uint32)ADD_length;
355 while (bytes_left > 0) {
356 PN_uint32 bytes_this_time = (PN_uint32) min(bytes_left, (PN_uint32) buflen);
357 _patch_stream->read(_buffer->_buffer, bytes_this_time);
358 if (_patch_stream->fail()) {
360 <<
"Truncated patch file.\n";
361 return EU_error_file_invalid;
363 _write_stream.write(_buffer->_buffer, bytes_this_time);
364 bytes_left -= bytes_this_time;
369 nassertr(_buffer->get_length() >= (int)
sizeof(COPY_length),
false);
370 COPY_length = patch_reader.get_uint16();
371 if (_patch_stream->fail()) {
373 <<
"Truncated patch file.\n";
374 return EU_error_file_invalid;
377 bytes_read += (int)COPY_length;
378 _total_bytes_processed += (int)COPY_length;
379 if (_total_bytes_processed > _total_bytes_to_process) {
381 <<
"Runaway patch file.\n";
382 return EU_error_file_invalid;
386 if (0 != COPY_length) {
388 nassertr(_buffer->get_length() >= (int)
sizeof(COPY_offset),
false);
389 COPY_offset = patch_reader.get_int32();
390 if (_patch_stream->fail()) {
392 <<
"Truncated patch file.\n";
393 return EU_error_file_invalid;
397 if (_version_number < 2) {
398 _origfile_stream->seekg(COPY_offset, ios::beg);
400 _origfile_stream->seekg(COPY_offset, ios::cur);
402 if (_origfile_stream->fail()) {
404 <<
"Invalid copy offset in patch file.\n";
405 return EU_error_file_invalid;
408 if (express_cat.is_spam()) {
410 <<
"COPY: " << COPY_length <<
" bytes from offset " 411 << COPY_offset <<
" (from " << _origfile_stream->tellg()
412 <<
" to " << _write_stream.tellp() <<
")" 417 PN_uint32 bytes_left = (PN_uint32)COPY_length;
419 while (bytes_left > 0) {
420 PN_uint32 bytes_this_time = (PN_uint32) min(bytes_left, (PN_uint32) buflen);
421 _origfile_stream->read(_buffer->_buffer, bytes_this_time);
422 if (_origfile_stream->fail()) {
424 <<
"Invalid copy length in patch file.\n";
425 return EU_error_file_invalid;
427 _write_stream.write(_buffer->_buffer, bytes_this_time);
428 bytes_left -= bytes_this_time;
433 if ((0 == ADD_length) && (0 == COPY_length)) {
436 if (express_cat.is_debug()) {
439 <<
" total bytes = " << _total_bytes_processed << endl;
445 MD5_actual.hash_file(_output_file);
446 if (_MD5_ofResult != MD5_actual) {
448 if (_origfile_stream != NULL) {
451 _origfile_stream = NULL;
453 _write_stream.close();
456 <<
"Patching produced incorrect checksum. Got:\n" 459 <<
" " << _MD5_ofResult
464 if (!has_source_hash()) {
466 <<
"No source hash in patch file to verify.\n";
469 MD5_orig.hash_file(_orig_file);
470 if (MD5_orig != get_source_hash()) {
472 <<
"Started from incorrect source file. Got:\n" 475 <<
" " << get_source_hash()
479 <<
"Started from correct source file:\n" 486 if (_rename_output_to_orig) {
487 _output_file.unlink();
489 if (_delete_patchfile) {
490 _patch_file.unlink();
493 return EU_error_invalid_checksum;
498 if (_delete_patchfile) {
499 _patch_file.unlink();
503 if (_rename_output_to_orig) {
505 if (!_output_file.rename_to(_orig_file)) {
507 <<
"Patchfile::run() failed to rename temp file to: " << _orig_file
509 return EU_error_write_file_rename;
531 int ret = initiate(patch_file, file);
536 if (ret == EU_success)
554 int ret = initiate(patch_file, orig_file, target_file);
559 if (ret == EU_success)
574 internal_read_header(
const Filename &patch_file) {
577 nassertr(_patch_stream == NULL, EU_error_abort);
578 _patch_file = patch_file;
581 if (_patch_stream == NULL) {
583 <<
"Patchfile::initiate() - Failed to open file: " << _patch_file << endl;
584 return get_write_error();
593 nassertr(_buffer->get_length() >= _v0_header_length,
false);
594 PN_uint32 magic_number = patch_reader.get_uint32();
595 if (magic_number != _magic_number && magic_number != _v0_magic_number) {
597 <<
"Invalid patch file: " << _patch_file << endl;
598 return EU_error_file_invalid;
602 if (magic_number != _v0_magic_number) {
603 _version_number = patch_reader.get_uint16();
605 if (_version_number > _current_version) {
607 <<
"Can't read version " << _version_number <<
" patch files: " 608 << _patch_file << endl;
609 return EU_error_file_invalid;
612 if (_version_number >= 1) {
614 patch_reader.get_uint32();
617 _MD5_ofSource.read_stream(patch_reader);
621 _total_bytes_to_process = patch_reader.get_uint32();
624 _MD5_ofResult.read_stream(patch_reader);
627 <<
"Patchfile::initiate() - valid patchfile" << endl;
641 PN_uint32 Patchfile::
642 calc_hash(
const char *buffer) {
643 #ifdef USE_MD5_FOR_HASHTABLE_INDEX_VALUES 645 hash.hash_buffer(buffer, _footprint_length);
649 return PN_uint16(hash.get_value(0));
651 PN_uint32 hash_value = 0;
653 for(
int i = 0; i < (int)_footprint_length; i++) {
656 hash_value ^= PN_uint32(*buffer) << ((i * 2) % Patchfile::_HASH_BITS);
662 hash_value ^= (hash_value >> Patchfile::_HASH_BITS);
666 return hash_value & _HASH_MASK;
693 build_hash_link_tables(
const char *buffer_orig, PN_uint32 length_orig,
694 PN_uint32 *hash_table, PN_uint32 *link_table) {
699 for(i = 0; i < _HASHTABLESIZE; i++) {
700 hash_table[i] = _NULL_VALUE;
704 for(i = 0; i < length_orig; i++) {
705 link_table[i] = _NULL_VALUE;
708 if(length_orig < _footprint_length)
return;
711 for(i = 0; i < (length_orig - _footprint_length); i++) {
713 PN_uint32 hash_value = calc_hash(&buffer_orig[i]);
728 link_table[i] = hash_table[hash_value];
732 hash_table[hash_value] = i;
758 PN_uint32 Patchfile::
759 calc_match_length(
const char* buf1,
const char* buf2, PN_uint32 max_length,
760 PN_uint32 min_length) {
762 if (min_length > 2) {
763 if (min_length >= max_length)
765 if (buf1[min_length] != buf2[min_length] ||
766 buf1[min_length-1] != buf2[min_length-1] ||
767 buf1[min_length-2] != buf2[min_length-2]) {
772 PN_uint32 length = 0;
773 while ((length < max_length) && (*buf1 == *buf2)) {
774 buf1++, buf2++, length++;
787 find_longest_match(PN_uint32 new_pos, PN_uint32 ©_pos, PN_uint16 ©_length,
788 PN_uint32 *hash_table, PN_uint32 *link_table,
const char* buffer_orig,
789 PN_uint32 length_orig,
const char* buffer_new, PN_uint32 length_new) {
795 PN_uint32 hash_value = calc_hash(&buffer_new[new_pos]);
798 if (_NULL_VALUE == hash_table[hash_value])
801 copy_pos = hash_table[hash_value];
804 copy_length = (PN_uint16)calc_match_length(&buffer_new[new_pos],
805 &buffer_orig[copy_pos],
806 min(min((length_new - new_pos),
807 (length_orig - copy_pos)),
812 PN_uint32 match_offset;
813 PN_uint16 match_length;
814 match_offset = link_table[copy_pos];
816 while (match_offset != _NULL_VALUE) {
817 match_length = (PN_uint16)calc_match_length(&buffer_new[new_pos],
818 &buffer_orig[match_offset],
819 min(min((length_new - new_pos),
820 (length_orig - match_offset)),
825 if (match_length > copy_length) {
826 copy_pos = match_offset;
827 copy_length = match_length;
831 match_offset = link_table[match_offset];
841 emit_ADD(ostream &write_stream, PN_uint32 length,
const char* buffer) {
842 nassertv(length == (PN_uint16)length);
844 if (express_cat.is_spam()) {
846 <<
"ADD: " << length <<
" (to " << _add_pos <<
")" << endl;
851 patch_writer.add_uint16((PN_uint16)length);
855 patch_writer.append_data(buffer, (PN_uint16)length);
867 emit_COPY(ostream &write_stream, PN_uint32 length, PN_uint32 copy_pos) {
868 nassertv(length == (PN_uint16)length);
870 PN_int32 offset = (int)copy_pos - (
int)_last_copy_pos;
871 if (express_cat.is_spam()) {
873 <<
"COPY: " << length <<
" bytes from offset " << offset
874 <<
" (from " << copy_pos <<
" to " << _add_pos <<
")" << endl;
879 patch_writer.add_uint16((PN_uint16)length);
881 if ((PN_uint16)length != 0) {
883 patch_writer.add_int32(offset);
884 _last_copy_pos = copy_pos + length;
898 emit_add_and_copy(ostream &write_stream,
899 PN_uint32 add_length,
const char *add_buffer,
900 PN_uint32 copy_length, PN_uint32 copy_pos) {
901 if (add_length == 0 && copy_length == 0) {
906 static const PN_uint16 max_write = 65535;
907 while (add_length > max_write) {
910 emit_ADD(write_stream, max_write, add_buffer);
911 add_buffer += max_write;
912 add_length -= max_write;
913 emit_COPY(write_stream, 0, 0);
916 emit_ADD(write_stream, add_length, add_buffer);
918 while (copy_length > max_write) {
920 emit_COPY(write_stream, max_write, copy_pos);
921 copy_pos += max_write;
922 copy_length -= max_write;
923 emit_ADD(write_stream, 0, NULL);
926 emit_COPY(write_stream, copy_length, copy_pos);
937 cache_add_and_copy(ostream &write_stream,
938 PN_uint32 add_length,
const char *add_buffer,
939 PN_uint32 copy_length, PN_uint32 copy_pos) {
940 if (add_length != 0) {
941 if (_cache_copy_length != 0) {
943 cache_flush(write_stream);
946 _cache_add_data += string(add_buffer, add_length);
949 if (copy_length != 0) {
950 if (_cache_copy_length == 0) {
952 _cache_copy_start = copy_pos;
953 _cache_copy_length = copy_length;
955 }
else if (_cache_copy_start + _cache_copy_length == copy_pos) {
957 _cache_copy_length += copy_length;
961 cache_flush(write_stream);
962 _cache_copy_start = copy_pos;
963 _cache_copy_length = copy_length;
975 cache_flush(ostream &write_stream) {
976 emit_add_and_copy(write_stream,
977 _cache_add_data.size(), _cache_add_data.data(),
978 _cache_copy_length, _cache_copy_start);
979 _cache_add_data = string();
980 _cache_copy_length = 0;
991 write_header(ostream &write_stream,
992 istream &stream_orig, istream &stream_new) {
997 patch_writer.add_uint32(_magic_number);
998 patch_writer.add_uint16(_current_version);
1000 stream_orig.seekg(0, ios::end);
1001 streampos source_file_length = stream_orig.tellg();
1002 patch_writer.add_uint32((PN_uint32)source_file_length);
1005 _MD5_ofSource.hash_stream(stream_orig);
1007 _MD5_ofSource.write_stream(patch_writer);
1009 if (express_cat.is_debug()) {
1011 <<
"Orig: " << _MD5_ofSource <<
"\n";
1014 stream_new.seekg(0, ios::end);
1015 streampos result_file_length = stream_new.tellg();
1016 patch_writer.add_uint32((PN_uint32)result_file_length);
1019 _MD5_ofResult.hash_stream(stream_new);
1021 _MD5_ofResult.write_stream(patch_writer);
1023 if (express_cat.is_debug()) {
1025 <<
" New: " << _MD5_ofResult <<
"\n";
1035 write_terminator(ostream &write_stream) {
1036 cache_flush(write_stream);
1038 emit_ADD(write_stream, 0, NULL);
1039 emit_COPY(write_stream, 0, 0);
1051 compute_file_patches(ostream &write_stream,
1052 PN_uint32 offset_orig, PN_uint32 offset_new,
1053 istream &stream_orig, istream &stream_new) {
1055 stream_orig.seekg(0, ios::end);
1056 nassertr(stream_orig,
false);
1057 PN_uint32 source_file_length = stream_orig.tellg();
1058 if (express_cat.is_debug()) {
1060 <<
"Allocating " << source_file_length <<
" bytes to read orig\n";
1063 char *buffer_orig = (
char *)PANDA_MALLOC_ARRAY(source_file_length);
1064 stream_orig.seekg(0, ios::beg);
1065 stream_orig.read(buffer_orig, source_file_length);
1068 stream_new.seekg(0, ios::end);
1069 PN_uint32 result_file_length = stream_new.tellg();
1070 nassertr(stream_new,
false);
1071 if (express_cat.is_debug()) {
1073 <<
"Allocating " << result_file_length <<
" bytes to read new\n";
1076 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(result_file_length);
1077 stream_new.seekg(0, ios::beg);
1078 stream_new.read(buffer_new, result_file_length);
1081 if (_hash_table == (PN_uint32 *)NULL) {
1082 if (express_cat.is_debug()) {
1084 <<
"Allocating hashtable of size " << _HASHTABLESIZE <<
" * 4\n";
1086 _hash_table = (PN_uint32 *)PANDA_MALLOC_ARRAY(_HASHTABLESIZE *
sizeof(PN_uint32));
1089 if (express_cat.is_debug()) {
1091 <<
"Allocating linktable of size " << source_file_length <<
" * 4\n";
1094 PN_uint32 *link_table = (PN_uint32 *)PANDA_MALLOC_ARRAY(source_file_length *
sizeof(PN_uint32));
1097 build_hash_link_tables(buffer_orig, source_file_length, _hash_table, link_table);
1101 PN_uint32 new_pos = 0;
1102 PN_uint32 start_pos = new_pos;
1104 if(((PN_uint32) result_file_length) >= _footprint_length)
1106 while (new_pos < (result_file_length - _footprint_length)) {
1110 PN_uint16 COPY_length;
1112 find_longest_match(new_pos, COPY_pos, COPY_length, _hash_table, link_table,
1113 buffer_orig, source_file_length, buffer_new, result_file_length);
1116 if (COPY_length < _footprint_length) {
1121 int num_skipped = (int)new_pos - (
int)start_pos;
1122 if (express_cat.is_spam()) {
1124 <<
"build: num_skipped = " << num_skipped
1127 cache_add_and_copy(write_stream, num_skipped, &buffer_new[start_pos],
1128 COPY_length, COPY_pos + offset_orig);
1129 new_pos += (PN_uint32)COPY_length;
1130 start_pos = new_pos;
1135 if (express_cat.is_spam()) {
1137 <<
"build: result_file_length = " << result_file_length
1138 <<
" start_pos = " << start_pos
1143 if (start_pos != result_file_length) {
1146 PN_uint32 remaining_bytes = result_file_length - start_pos;
1147 cache_add_and_copy(write_stream, remaining_bytes, &buffer_new[start_pos],
1149 start_pos += remaining_bytes;
1152 PANDA_FREE_ARRAY(link_table);
1154 PANDA_FREE_ARRAY(buffer_orig);
1155 PANDA_FREE_ARRAY(buffer_new);
1170 compute_mf_patches(ostream &write_stream,
1171 PN_uint32 offset_orig, PN_uint32 offset_new,
1172 istream &stream_orig, istream &stream_new) {
1176 if (!mf_orig.
open_read(&stream_origw) ||
1179 <<
"Input multifiles appear to be corrupt.\n";
1185 <<
"Input multifiles need to be repacked.\n";
1194 if (!do_compute_patches(
"",
"",
1195 write_stream, offset_orig, offset_new,
1196 index_orig, index_new)) {
1199 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + mf_new.
get_index_end(),
false);
1208 for (
int ni = 0; ni < new_num_subfiles; ++ni) {
1209 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + mf_new.
get_subfile_internal_start(ni),
false);
1220 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(new_size);
1221 stream_new.seekg(new_start, ios::beg);
1222 stream_new.read(buffer_new, new_size);
1223 cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0);
1224 PANDA_FREE_ARRAY(buffer_new);
1235 if (!patch_subfile(write_stream, offset_orig, offset_new,
1237 stream_origw, orig_start, orig_start + (streampos)orig_size,
1238 stream_neww, new_start, new_start + (streampos)new_size)) {
1257 read_tar(TarDef &tar, istream &stream) {
1260 tt.openfunc = tar_openfunc;
1261 tt.closefunc = tar_closefunc;
1262 tt.readfunc = tar_readfunc;
1263 tt.writefunc = tar_writefunc;
1265 stream.seekg(0, ios::beg);
1266 nassertr(_tar_istream == NULL,
false);
1267 _tar_istream = &stream;
1268 if (tar_open(&tfile, (
char *)
"dummy", &tt, O_RDONLY, 0, 0) != 0) {
1269 _tar_istream = NULL;
1277 streampos last_pos = 0;
1278 int flag = th_read(tfile);
1281 subfile._name = th_get_pathname(tfile);
1282 subfile._header_start = last_pos;
1283 subfile._data_start = stream.tellg();
1284 subfile._data_end = subfile._data_start + (streampos)th_get_size(tfile);
1285 tar_skip_regfile(tfile);
1286 subfile._end = stream.tellg();
1287 tar.push_back(subfile);
1289 last_pos = subfile._end;
1290 flag = th_read(tfile);
1296 subfile._header_start = last_pos;
1298 stream.seekg(0, ios::end);
1299 subfile._data_start = stream.tellg();
1300 subfile._data_end = subfile._data_start;
1301 subfile._end = subfile._data_start;
1302 tar.push_back(subfile);
1305 _tar_istream = NULL;
1322 compute_tar_patches(ostream &write_stream,
1323 PN_uint32 offset_orig, PN_uint32 offset_new,
1324 istream &stream_orig, istream &stream_new,
1325 TarDef &tar_orig, TarDef &tar_new) {
1343 TarDef::const_iterator ni;
1344 streampos last_pos = 0;
1345 for (ni = tar_new.begin(); ni != tar_new.end(); ++ni) {
1346 const TarSubfile &sf_new =(*ni);
1347 nassertr(sf_new._header_start == last_pos,
false);
1349 TarDef::const_iterator oi = tar_orig.find(sf_new);
1351 if (oi == tar_orig.end()) {
1354 <<
"Adding subfile " << sf_new._name <<
"\n";
1356 streampos new_start = sf_new._header_start;
1357 size_t new_size = sf_new._end - sf_new._header_start;
1358 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(new_size);
1359 stream_new.seekg(new_start, ios::beg);
1360 stream_new.read(buffer_new, new_size);
1361 cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0);
1362 PANDA_FREE_ARRAY(buffer_new);
1367 const TarSubfile &sf_orig =(*oi);
1373 if (!patch_subfile(write_stream, offset_orig, offset_new,
"",
1374 stream_origw, sf_orig._header_start, sf_orig._data_start,
1375 stream_neww, sf_new._header_start, sf_new._data_start)) {
1379 if (!patch_subfile(write_stream, offset_orig, offset_new, sf_new._name,
1380 stream_origw, sf_orig._data_start, sf_orig._data_end,
1381 stream_neww, sf_new._data_start, sf_new._data_end)) {
1385 if (!patch_subfile(write_stream, offset_orig, offset_new,
"",
1386 stream_origw, sf_orig._data_end, sf_orig._end,
1387 stream_neww, sf_new._data_end, sf_new._end)) {
1392 last_pos = sf_new._end;
1407 tar_openfunc(
const char *,
int, ...) {
1422 tar_closefunc(
int) {
1436 tar_readfunc(
int,
void *buffer,
size_t nbytes) {
1437 nassertr(_tar_istream != NULL, 0);
1438 _tar_istream->read((
char *)buffer, nbytes);
1439 return (ssize_t)_tar_istream->gcount();
1451 tar_writefunc(
int,
const void *,
size_t) {
1454 nassertr(
false, -1);
1477 pifstream stream_orig;
1479 if (!file_orig.
open_read(stream_orig)) {
1481 <<
"Patchfile::build() - Failed to open file: " << file_orig << endl;
1486 pifstream stream_new;
1490 <<
"Patchfile::build() - Failed to open file: " << file_new << endl;
1495 pofstream write_stream;
1498 <<
"Patchfile::build() - Failed to open file: " << patch_name << endl;
1504 _cache_add_data = string();
1505 _cache_copy_start = 0;
1506 _cache_copy_length = 0;
1508 write_header(write_stream, stream_orig, stream_new);
1510 if (!do_compute_patches(file_orig, file_new,
1512 stream_orig, stream_new)) {
1516 write_terminator(write_stream);
1518 if (express_cat.is_debug()) {
1520 <<
"Patch file will generate " << _add_pos <<
"-byte file.\n";
1526 stream_new.seekg(0, ios::end);
1527 streampos result_file_length = stream_new.tellg();
1528 nassertr(_add_pos == result_file_length,
false);
1532 return (_last_copy_pos != 0);
1544 ostream &write_stream,
1545 PN_uint32 offset_orig, PN_uint32 offset_new,
1546 istream &stream_orig, istream &stream_new) {
1547 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new,
false);
1550 bool is_multifile =
false;
1552 bool is_tarfile =
false;
1553 TarDef tar_orig, tar_new;
1556 if (_allow_multifile) {
1557 if (strstr(file_orig.
get_basename().c_str(),
".mf") != NULL ||
1558 strstr(file_new.
get_basename().c_str(),
".mf") != NULL) {
1562 char *buffer = (
char *)PANDA_MALLOC_ARRAY(magic_number.size());
1563 stream_orig.seekg(0, ios::beg);
1564 stream_orig.read(buffer, magic_number.size());
1566 if (stream_orig.gcount() == (int)magic_number.size() &&
1567 memcmp(buffer, magic_number.data(), magic_number.size()) == 0) {
1568 stream_new.seekg(0, ios::beg);
1569 stream_new.read(buffer, magic_number.size());
1570 if (stream_new.gcount() == (int)magic_number.size() &&
1571 memcmp(buffer, magic_number.data(), magic_number.size()) == 0) {
1572 is_multifile =
true;
1575 PANDA_FREE_ARRAY(buffer);
1578 if (strstr(file_orig.
get_basename().c_str(),
".tar") != NULL ||
1579 strstr(file_new.
get_basename().c_str(),
".tar") != NULL) {
1580 if (read_tar(tar_orig, stream_orig) &&
1581 read_tar(tar_new, stream_new)) {
1589 if (express_cat.is_debug()) {
1591 << file_orig.
get_basename() <<
" appears to be a Panda Multifile.\n";
1593 if (!compute_mf_patches(write_stream, offset_orig, offset_new,
1594 stream_orig, stream_new)) {
1598 }
else if (is_tarfile) {
1599 if (express_cat.is_debug()) {
1601 << file_orig.
get_basename() <<
" appears to be a tar file.\n";
1603 if (!compute_tar_patches(write_stream, offset_orig, offset_new,
1604 stream_orig, stream_new, tar_orig, tar_new)) {
1609 if (express_cat.is_debug()) {
1611 << file_orig.
get_basename() <<
" is not a multifile.\n";
1613 if (!compute_file_patches(write_stream, offset_orig, offset_new,
1614 stream_orig, stream_new)) {
1629 patch_subfile(ostream &write_stream,
1630 PN_uint32 offset_orig, PN_uint32 offset_new,
1632 IStreamWrapper &stream_orig, streampos orig_start, streampos orig_end,
1633 IStreamWrapper &stream_new, streampos new_start, streampos new_end) {
1634 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + new_start,
false);
1636 size_t new_size = new_end - new_start;
1637 size_t orig_size = orig_end - orig_start;
1639 ISubStream subfile_orig(&stream_orig, orig_start, orig_end);
1640 ISubStream subfile_new(&stream_new, new_start, new_end);
1642 bool is_unchanged =
false;
1643 if (orig_size == new_size) {
1645 hash_orig.hash_stream(subfile_orig);
1646 hash_new.hash_stream(subfile_new);
1648 if (hash_orig == hash_new) {
1650 is_unchanged =
true;
1655 if (express_cat.is_debug() && !filename.empty()) {
1657 <<
"Keeping subfile " << filename <<
"\n";
1659 cache_add_and_copy(write_stream, 0, NULL,
1660 orig_size, offset_orig + orig_start);
1663 if (!filename.empty()) {
1665 <<
"Patching subfile " << filename <<
"\n";
1668 if (!do_compute_patches(filename, filename, write_stream,
1669 offset_orig + orig_start, offset_new + new_start,
1670 subfile_orig, subfile_new)) {
1678 #endif // HAVE_OPENSSL A StreamWriter object is used to write sequential binary data directly to an ostream.
string get_basename() const
Returns the basename part of the filename.
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
size_t get_subfile_internal_length(int index) const
Returns the number of bytes the indicated subfile consumes within the archive.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS's file system.
bool needs_repack() const
Returns true if the Multifile index is suboptimal and should be repacked.
istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read...
void set_binary()
Indicates that the filename represents a binary file.
int find_subfile(const string &subfile_name) const
Returns the index of the subfile with the indicated name, or -1 if the named subfile is not within th...
streampos get_index_end() const
Returns the first byte that is guaranteed to follow any index byte already written to disk in the Mul...
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer...
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
An istream object that presents a subwindow into another istream.
The name of a file, such as a texture file or an Egg file.
static string get_magic_number()
Returns a string with the first n bytes written to a Multifile, to identify it as a Multifile...
This class provides a locking wrapper around an arbitrary istream pointer.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
bool open_read(const Filename &multifile_name, const streampos &offset=0)
Opens the named Multifile on disk for reading.
A file that contains a set of files.
A class to read sequential binary data directly from an istream.
streampos get_subfile_internal_start(int index) const
Returns the starting byte position within the Multifile at which the indicated subfile begins...
const string & get_subfile_name(int index) const
Returns the name of the nth subfile.
int get_num_subfiles() const
Returns the number of subfiles within the Multifile.