35 std::istream *Patchfile::_tar_istream =
nullptr;
61 const int _v0_header_length = 4 + 4 + 16;
62 const int _v1_header_length = 4 + 2 + 4 + 16 + 4 + 16;
72 const uint32_t Patchfile::_v0_magic_number = 0xfeebfaab;
73 const uint32_t Patchfile::_magic_number = 0xfeebfaac;
77 const uint16_t Patchfile::_current_version = 2;
79 const uint32_t Patchfile::_HASH_BITS = 24;
80 const uint32_t Patchfile::_HASHTABLESIZE = uint32_t(1) << Patchfile::_HASH_BITS;
81 const uint32_t Patchfile::_DEFAULT_FOOTPRINT_LENGTH = 9;
82 const uint32_t Patchfile::_NULL_VALUE = uint32_t(0) - 1;
83 const uint32_t Patchfile::_MAX_RUN_LENGTH = (uint32_t(1) << 16) - 1;
84 const uint32_t Patchfile::_HASH_MASK = (uint32_t(1) << Patchfile::_HASH_BITS) - 1;
99 Patchfile(PT(
Buffer) buffer) {
108 _rename_output_to_orig =
false;
109 _delete_patchfile =
false;
110 _hash_table =
nullptr;
112 nassertv(!buffer.is_null());
116 _allow_multifile =
true;
118 _patch_stream =
nullptr;
119 _origfile_stream =
nullptr;
121 reset_footprint_length();
129 if (_hash_table !=
nullptr) {
130 PANDA_FREE_ARRAY(_hash_table);
137 nassertv(_patch_stream ==
nullptr);
138 nassertv(_origfile_stream ==
nullptr);
148 <<
"Patchfile::cleanup() - Patching has not been initiated" 155 if (_origfile_stream !=
nullptr) {
157 _origfile_stream =
nullptr;
159 if (_patch_stream !=
nullptr) {
161 _patch_stream =
nullptr;
163 _write_stream.close();
182 _rename_output_to_orig =
true;
183 _delete_patchfile = !keep_temporary_files;
196 <<
"Patchfile::initiate() - Patching has already been initiated" 198 return EU_error_abort;
201 nassertr(orig_file != target_file, EU_error_abort);
206 nassertr(_origfile_stream ==
nullptr, EU_error_abort);
207 _orig_file = orig_file;
210 if (_origfile_stream ==
nullptr) {
212 <<
"Patchfile::initiate() - Failed to open file: " << _orig_file << endl;
213 return get_write_error();
217 _output_file = target_file;
219 if (!_output_file.open_write(_write_stream)) {
221 <<
"Patchfile::initiate() - Failed to open file: " << _output_file << endl;
222 return get_write_error();
225 if (express_cat.is_debug()) {
227 <<
"Patchfile using output file " << _output_file <<
"\n";
230 int result = internal_read_header(patch_file);
231 _total_bytes_processed = 0;
243 read_header(
const Filename &patch_file) {
246 <<
"Patchfile::initiate() - Patching has already been initiated" 248 return EU_error_abort;
251 int result = internal_read_header(patch_file);
252 if (_patch_stream !=
nullptr) {
255 _patch_stream =
nullptr;
273 uint16_t COPY_length;
276 if (_initiated ==
false) {
278 <<
"Patchfile::run() - Patching has not been initiated" 280 return EU_error_abort;
283 nassertr(_patch_stream !=
nullptr, EU_error_abort);
284 nassertr(_origfile_stream !=
nullptr, EU_error_abort);
287 buflen = _buffer->get_length();
290 while (bytes_read < buflen) {
292 nassertr(_buffer->get_length() >= (int)
sizeof(ADD_length),
false);
293 ADD_length = patch_reader.get_uint16();
294 if (_patch_stream->fail()) {
296 <<
"Truncated patch file.\n";
297 return EU_error_file_invalid;
300 bytes_read += (int)ADD_length;
301 _total_bytes_processed += (int)ADD_length;
302 if (_total_bytes_processed > _total_bytes_to_process) {
304 <<
"Runaway patch file.\n";
305 return EU_error_file_invalid;
310 if (express_cat.is_spam() && ADD_length != 0) {
312 <<
"ADD: " << ADD_length <<
" (to " 313 << _write_stream.tellp() <<
")" << endl;
316 uint32_t bytes_left = (uint32_t)ADD_length;
317 while (bytes_left > 0) {
318 uint32_t bytes_this_time = (uint32_t) min(bytes_left, (uint32_t) buflen);
319 _patch_stream->read(_buffer->_buffer, bytes_this_time);
320 if (_patch_stream->fail()) {
322 <<
"Truncated patch file.\n";
323 return EU_error_file_invalid;
325 _write_stream.write(_buffer->_buffer, bytes_this_time);
326 bytes_left -= bytes_this_time;
330 nassertr(_buffer->get_length() >= (int)
sizeof(COPY_length),
false);
331 COPY_length = patch_reader.get_uint16();
332 if (_patch_stream->fail()) {
334 <<
"Truncated patch file.\n";
335 return EU_error_file_invalid;
338 bytes_read += (int)COPY_length;
339 _total_bytes_processed += (int)COPY_length;
340 if (_total_bytes_processed > _total_bytes_to_process) {
342 <<
"Runaway patch file.\n";
343 return EU_error_file_invalid;
348 if (0 != COPY_length) {
350 nassertr(_buffer->get_length() >= (int)
sizeof(COPY_offset),
false);
351 COPY_offset = patch_reader.get_int32();
352 if (_patch_stream->fail()) {
354 <<
"Truncated patch file.\n";
355 return EU_error_file_invalid;
359 if (_version_number < 2) {
360 _origfile_stream->seekg(COPY_offset, ios::beg);
362 _origfile_stream->seekg(COPY_offset, ios::cur);
364 if (_origfile_stream->fail()) {
366 <<
"Invalid copy offset in patch file.\n";
367 return EU_error_file_invalid;
370 if (express_cat.is_spam()) {
372 <<
"COPY: " << COPY_length <<
" bytes from offset " 373 << COPY_offset <<
" (from " << _origfile_stream->tellg()
374 <<
" to " << _write_stream.tellp() <<
")" 379 uint32_t bytes_left = (uint32_t)COPY_length;
381 while (bytes_left > 0) {
382 uint32_t bytes_this_time = (uint32_t) min(bytes_left, (uint32_t) buflen);
383 _origfile_stream->read(_buffer->_buffer, bytes_this_time);
384 if (_origfile_stream->fail()) {
386 <<
"Invalid copy length in patch file.\n";
387 return EU_error_file_invalid;
389 _write_stream.write(_buffer->_buffer, bytes_this_time);
390 bytes_left -= bytes_this_time;
395 if ((0 == ADD_length) && (0 == COPY_length)) {
398 if (express_cat.is_debug()) {
401 <<
" total bytes = " << _total_bytes_processed << endl;
407 MD5_actual.hash_file(_output_file);
408 if (_MD5_ofResult != MD5_actual) {
410 if (_origfile_stream !=
nullptr) {
413 _origfile_stream =
nullptr;
415 _write_stream.close();
418 <<
"Patching produced incorrect checksum. Got:\n" 421 <<
" " << _MD5_ofResult
425 if (!has_source_hash()) {
427 <<
"No source hash in patch file to verify.\n";
430 MD5_orig.hash_file(_orig_file);
431 if (MD5_orig != get_source_hash()) {
433 <<
"Started from incorrect source file. Got:\n" 436 <<
" " << get_source_hash()
440 <<
"Started from correct source file:\n" 447 if (_rename_output_to_orig) {
448 _output_file.unlink();
450 if (_delete_patchfile) {
451 _patch_file.unlink();
454 return EU_error_invalid_checksum;
459 if (_delete_patchfile) {
460 _patch_file.unlink();
464 if (_rename_output_to_orig) {
466 if (!_output_file.rename_to(_orig_file)) {
468 <<
"Patchfile::run() failed to rename temp file to: " << _orig_file
470 return EU_error_write_file_rename;
489 int ret = initiate(patch_file, file);
494 if (ret == EU_success)
510 int ret = initiate(patch_file, orig_file, target_file);
515 if (ret == EU_success)
528 internal_read_header(
const Filename &patch_file) {
531 nassertr(_patch_stream ==
nullptr, EU_error_abort);
532 _patch_file = patch_file;
535 if (_patch_stream ==
nullptr) {
537 <<
"Patchfile::initiate() - Failed to open file: " << _patch_file << endl;
538 return get_write_error();
545 nassertr(_buffer->get_length() >= _v0_header_length,
false);
546 uint32_t magic_number = patch_reader.get_uint32();
547 if (magic_number != _magic_number && magic_number != _v0_magic_number) {
549 <<
"Invalid patch file: " << _patch_file << endl;
550 return EU_error_file_invalid;
554 if (magic_number != _v0_magic_number) {
555 _version_number = patch_reader.get_uint16();
557 if (_version_number > _current_version) {
559 <<
"Can't read version " << _version_number <<
" patch files: " 560 << _patch_file << endl;
561 return EU_error_file_invalid;
564 if (_version_number >= 1) {
566 patch_reader.get_uint32();
569 _MD5_ofSource.read_stream(patch_reader);
573 _total_bytes_to_process = patch_reader.get_uint32();
576 _MD5_ofResult.read_stream(patch_reader);
579 <<
"Patchfile::initiate() - valid patchfile" << endl;
590 calc_hash(
const char *buffer) {
591 #ifdef USE_MD5_FOR_HASHTABLE_INDEX_VALUES 593 hash.hash_buffer(buffer, _footprint_length);
597 return uint16_t(hash.get_value(0));
599 uint32_t hash_value = 0;
601 for(
int i = 0; i < (int)_footprint_length; i++) {
604 hash_value ^= uint32_t(*buffer) << ((i * 2) % Patchfile::_HASH_BITS);
610 hash_value ^= (hash_value >> Patchfile::_HASH_BITS);
614 return hash_value & _HASH_MASK;
635 build_hash_link_tables(
const char *buffer_orig, uint32_t length_orig,
636 uint32_t *hash_table, uint32_t *link_table) {
641 for(i = 0; i < _HASHTABLESIZE; i++) {
642 hash_table[i] = _NULL_VALUE;
646 for(i = 0; i < length_orig; i++) {
647 link_table[i] = _NULL_VALUE;
650 if(length_orig < _footprint_length)
return;
653 for(i = 0; i < (length_orig - _footprint_length); i++) {
655 uint32_t hash_value = calc_hash(&buffer_orig[i]);
668 link_table[i] = hash_table[hash_value];
672 hash_table[hash_value] = i;
696 calc_match_length(
const char* buf1,
const char* buf2, uint32_t max_length,
697 uint32_t min_length) {
699 if (min_length > 2) {
700 if (min_length >= max_length)
702 if (buf1[min_length] != buf2[min_length] ||
703 buf1[min_length-1] != buf2[min_length-1] ||
704 buf1[min_length-2] != buf2[min_length-2]) {
710 while ((length < max_length) && (*buf1 == *buf2)) {
711 buf1++, buf2++, length++;
722 find_longest_match(uint32_t new_pos, uint32_t ©_pos, uint16_t ©_length,
723 uint32_t *hash_table, uint32_t *link_table,
const char* buffer_orig,
724 uint32_t length_orig,
const char* buffer_new, uint32_t length_new) {
730 uint32_t hash_value = calc_hash(&buffer_new[new_pos]);
733 if (_NULL_VALUE == hash_table[hash_value])
736 copy_pos = hash_table[hash_value];
739 copy_length = (uint16_t)calc_match_length(&buffer_new[new_pos],
740 &buffer_orig[copy_pos],
741 min(min((length_new - new_pos),
742 (length_orig - copy_pos)),
747 uint32_t match_offset;
748 uint16_t match_length;
749 match_offset = link_table[copy_pos];
751 while (match_offset != _NULL_VALUE) {
752 match_length = (uint16_t)calc_match_length(&buffer_new[new_pos],
753 &buffer_orig[match_offset],
754 min(min((length_new - new_pos),
755 (length_orig - match_offset)),
760 if (match_length > copy_length) {
761 copy_pos = match_offset;
762 copy_length = match_length;
766 match_offset = link_table[match_offset];
774 emit_ADD(ostream &write_stream, uint32_t length,
const char* buffer) {
775 nassertv(length == (uint16_t)length);
777 if (express_cat.is_spam()) {
779 <<
"ADD: " << length <<
" (to " << _add_pos <<
")" << endl;
784 patch_writer.add_uint16((uint16_t)length);
788 patch_writer.append_data(buffer, (uint16_t)length);
798 emit_COPY(ostream &write_stream, uint32_t length, uint32_t copy_pos) {
799 nassertv(length == (uint16_t)length);
801 int32_t offset = (int)copy_pos - (
int)_last_copy_pos;
802 if (express_cat.is_spam()) {
804 <<
"COPY: " << length <<
" bytes from offset " << offset
805 <<
" (from " << copy_pos <<
" to " << _add_pos <<
")" << endl;
810 patch_writer.add_uint16((uint16_t)length);
812 if ((uint16_t)length != 0) {
814 patch_writer.add_int32(offset);
815 _last_copy_pos = copy_pos + length;
826 emit_add_and_copy(ostream &write_stream,
827 uint32_t add_length,
const char *add_buffer,
828 uint32_t copy_length, uint32_t copy_pos) {
829 if (add_length == 0 && copy_length == 0) {
834 static const uint16_t max_write = 65535;
835 while (add_length > max_write) {
838 emit_ADD(write_stream, max_write, add_buffer);
839 add_buffer += max_write;
840 add_length -= max_write;
841 emit_COPY(write_stream, 0, 0);
844 emit_ADD(write_stream, add_length, add_buffer);
846 while (copy_length > max_write) {
848 emit_COPY(write_stream, max_write, copy_pos);
849 copy_pos += max_write;
850 copy_length -= max_write;
851 emit_ADD(write_stream, 0,
nullptr);
854 emit_COPY(write_stream, copy_length, copy_pos);
862 cache_add_and_copy(ostream &write_stream,
863 uint32_t add_length,
const char *add_buffer,
864 uint32_t copy_length, uint32_t copy_pos) {
865 if (add_length != 0) {
866 if (_cache_copy_length != 0) {
868 cache_flush(write_stream);
871 _cache_add_data += string(add_buffer, add_length);
874 if (copy_length != 0) {
875 if (_cache_copy_length == 0) {
877 _cache_copy_start = copy_pos;
878 _cache_copy_length = copy_length;
880 }
else if (_cache_copy_start + _cache_copy_length == copy_pos) {
882 _cache_copy_length += copy_length;
886 cache_flush(write_stream);
887 _cache_copy_start = copy_pos;
888 _cache_copy_length = copy_length;
898 cache_flush(ostream &write_stream) {
899 emit_add_and_copy(write_stream,
900 _cache_add_data.size(), _cache_add_data.data(),
901 _cache_copy_length, _cache_copy_start);
902 _cache_add_data = string();
903 _cache_copy_length = 0;
912 write_header(ostream &write_stream,
913 istream &stream_orig, istream &stream_new) {
918 patch_writer.add_uint32(_magic_number);
919 patch_writer.add_uint16(_current_version);
921 stream_orig.seekg(0, ios::end);
922 streampos source_file_length = stream_orig.tellg();
923 patch_writer.add_uint32((uint32_t)source_file_length);
926 _MD5_ofSource.hash_stream(stream_orig);
928 _MD5_ofSource.write_stream(patch_writer);
930 if (express_cat.is_debug()) {
932 <<
"Orig: " << _MD5_ofSource <<
"\n";
935 stream_new.seekg(0, ios::end);
936 streampos result_file_length = stream_new.tellg();
937 patch_writer.add_uint32((uint32_t)result_file_length);
940 _MD5_ofResult.hash_stream(stream_new);
942 _MD5_ofResult.write_stream(patch_writer);
944 if (express_cat.is_debug()) {
946 <<
" New: " << _MD5_ofResult <<
"\n";
954 write_terminator(ostream &write_stream) {
955 cache_flush(write_stream);
957 emit_ADD(write_stream, 0,
nullptr);
958 emit_COPY(write_stream, 0, 0);
968 compute_file_patches(ostream &write_stream,
969 uint32_t offset_orig, uint32_t offset_new,
970 istream &stream_orig, istream &stream_new) {
972 stream_orig.seekg(0, ios::end);
973 nassertr(stream_orig,
false);
974 uint32_t source_file_length = stream_orig.tellg();
975 if (express_cat.is_debug()) {
977 <<
"Allocating " << source_file_length <<
" bytes to read orig\n";
980 char *buffer_orig = (
char *)PANDA_MALLOC_ARRAY(source_file_length);
981 stream_orig.seekg(0, ios::beg);
982 stream_orig.read(buffer_orig, source_file_length);
985 stream_new.seekg(0, ios::end);
986 uint32_t result_file_length = stream_new.tellg();
987 nassertr(stream_new,
false);
988 if (express_cat.is_debug()) {
990 <<
"Allocating " << result_file_length <<
" bytes to read new\n";
993 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(result_file_length);
994 stream_new.seekg(0, ios::beg);
995 stream_new.read(buffer_new, result_file_length);
998 if (_hash_table ==
nullptr) {
999 if (express_cat.is_debug()) {
1001 <<
"Allocating hashtable of size " << _HASHTABLESIZE <<
" * 4\n";
1003 _hash_table = (uint32_t *)PANDA_MALLOC_ARRAY(_HASHTABLESIZE *
sizeof(uint32_t));
1006 if (express_cat.is_debug()) {
1008 <<
"Allocating linktable of size " << source_file_length <<
" * 4\n";
1011 uint32_t *link_table = (uint32_t *)PANDA_MALLOC_ARRAY(source_file_length *
sizeof(uint32_t));
1014 build_hash_link_tables(buffer_orig, source_file_length, _hash_table, link_table);
1018 uint32_t new_pos = 0;
1019 uint32_t start_pos = new_pos;
1021 if(((uint32_t) result_file_length) >= _footprint_length)
1023 while (new_pos < (result_file_length - _footprint_length)) {
1027 uint16_t COPY_length;
1029 find_longest_match(new_pos, COPY_pos, COPY_length, _hash_table, link_table,
1030 buffer_orig, source_file_length, buffer_new, result_file_length);
1034 if (COPY_length < _footprint_length) {
1039 int num_skipped = (int)new_pos - (
int)start_pos;
1040 if (express_cat.is_spam()) {
1042 <<
"build: num_skipped = " << num_skipped
1045 cache_add_and_copy(write_stream, num_skipped, &buffer_new[start_pos],
1046 COPY_length, COPY_pos + offset_orig);
1047 new_pos += (uint32_t)COPY_length;
1048 start_pos = new_pos;
1053 if (express_cat.is_spam()) {
1055 <<
"build: result_file_length = " << result_file_length
1056 <<
" start_pos = " << start_pos
1061 if (start_pos != result_file_length) {
1064 uint32_t remaining_bytes = result_file_length - start_pos;
1065 cache_add_and_copy(write_stream, remaining_bytes, &buffer_new[start_pos],
1067 start_pos += remaining_bytes;
1070 PANDA_FREE_ARRAY(link_table);
1072 PANDA_FREE_ARRAY(buffer_orig);
1073 PANDA_FREE_ARRAY(buffer_new);
1085 compute_mf_patches(ostream &write_stream,
1086 uint32_t offset_orig, uint32_t offset_new,
1087 istream &stream_orig, istream &stream_new) {
1091 if (!mf_orig.open_read(&stream_origw) ||
1092 !mf_new.open_read(&stream_neww)) {
1094 <<
"Input multifiles appear to be corrupt.\n";
1100 <<
"Input multifiles need to be repacked.\n";
1109 if (!do_compute_patches(
"",
"",
1110 write_stream, offset_orig, offset_new,
1111 index_orig, index_new)) {
1114 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + (uint32_t)mf_new.
get_index_end(),
false);
1122 for (
int ni = 0; ni < new_num_subfiles; ++ni) {
1123 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + (uint32_t)mf_new.
get_subfile_internal_start(ni),
false);
1134 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(new_size);
1135 stream_new.seekg(new_start, ios::beg);
1136 stream_new.read(buffer_new, new_size);
1137 cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0);
1138 PANDA_FREE_ARRAY(buffer_new);
1149 if (!patch_subfile(write_stream, offset_orig, offset_new,
1151 stream_origw, orig_start, orig_start + (streampos)orig_size,
1152 stream_neww, new_start, new_start + (streampos)new_size)) {
1168 read_tar(TarDef &tar, istream &stream) {
1171 tt.openfunc = tar_openfunc;
1172 tt.closefunc = tar_closefunc;
1173 tt.readfunc = tar_readfunc;
1174 tt.writefunc = tar_writefunc;
1176 stream.seekg(0, ios::beg);
1177 nassertr(_tar_istream ==
nullptr,
false);
1178 _tar_istream = &stream;
1179 if (tar_open(&tfile, (
char *)
"dummy", &tt, O_RDONLY, 0, 0) != 0) {
1180 _tar_istream =
nullptr;
1188 streampos last_pos = 0;
1189 int flag = th_read(tfile);
1192 subfile._name = th_get_pathname(tfile);
1193 subfile._header_start = last_pos;
1194 subfile._data_start = stream.tellg();
1195 subfile._data_end = subfile._data_start + (streampos)th_get_size(tfile);
1196 tar_skip_regfile(tfile);
1197 subfile._end = stream.tellg();
1198 tar.push_back(subfile);
1200 last_pos = subfile._end;
1201 flag = th_read(tfile);
1207 subfile._header_start = last_pos;
1209 stream.seekg(0, ios::end);
1210 subfile._data_start = stream.tellg();
1211 subfile._data_end = subfile._data_start;
1212 subfile._end = subfile._data_start;
1213 tar.push_back(subfile);
1216 _tar_istream =
nullptr;
1229 compute_tar_patches(ostream &write_stream,
1230 uint32_t offset_orig, uint32_t offset_new,
1231 istream &stream_orig, istream &stream_new,
1232 TarDef &tar_orig, TarDef &tar_new) {
1249 TarDef::const_iterator ni;
1250 streampos last_pos = 0;
1251 for (ni = tar_new.begin(); ni != tar_new.end(); ++ni) {
1252 const TarSubfile &sf_new =(*ni);
1253 nassertr(sf_new._header_start == last_pos,
false);
1255 TarDef::const_iterator oi = tar_orig.find(sf_new);
1257 if (oi == tar_orig.end()) {
1260 <<
"Adding subfile " << sf_new._name <<
"\n";
1262 streampos new_start = sf_new._header_start;
1263 size_t new_size = sf_new._end - sf_new._header_start;
1264 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(new_size);
1265 stream_new.seekg(new_start, ios::beg);
1266 stream_new.read(buffer_new, new_size);
1267 cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0);
1268 PANDA_FREE_ARRAY(buffer_new);
1273 const TarSubfile &sf_orig =(*oi);
1279 if (!patch_subfile(write_stream, offset_orig, offset_new,
"",
1280 stream_origw, sf_orig._header_start, sf_orig._data_start,
1281 stream_neww, sf_new._header_start, sf_new._data_start)) {
1285 if (!patch_subfile(write_stream, offset_orig, offset_new, sf_new._name,
1286 stream_origw, sf_orig._data_start, sf_orig._data_end,
1287 stream_neww, sf_new._data_start, sf_new._data_end)) {
1291 if (!patch_subfile(write_stream, offset_orig, offset_new,
"",
1292 stream_origw, sf_orig._data_end, sf_orig._end,
1293 stream_neww, sf_new._data_end, sf_new._end)) {
1298 last_pos = sf_new._end;
1311 tar_openfunc(
const char *,
int, ...) {
1324 tar_closefunc(
int) {
1336 tar_readfunc(
int,
void *buffer,
size_t nbytes) {
1337 nassertr(_tar_istream !=
nullptr, 0);
1338 _tar_istream->read((
char *)buffer, nbytes);
1339 return (ssize_t)_tar_istream->gcount();
1349 tar_writefunc(
int,
const void *,
size_t) {
1352 nassertr(
false, -1);
1370 pifstream stream_orig;
1372 if (!file_orig.
open_read(stream_orig)) {
1374 <<
"Patchfile::build() - Failed to open file: " << file_orig << endl;
1379 pifstream stream_new;
1383 <<
"Patchfile::build() - Failed to open file: " << file_new << endl;
1388 pofstream write_stream;
1391 <<
"Patchfile::build() - Failed to open file: " << patch_name << endl;
1397 _cache_add_data = string();
1398 _cache_copy_start = 0;
1399 _cache_copy_length = 0;
1401 write_header(write_stream, stream_orig, stream_new);
1403 if (!do_compute_patches(file_orig, file_new,
1405 stream_orig, stream_new)) {
1409 write_terminator(write_stream);
1411 if (express_cat.is_debug()) {
1413 <<
"Patch file will generate " << _add_pos <<
"-byte file.\n";
1419 stream_new.seekg(0, ios::end);
1420 streampos result_file_length = stream_new.tellg();
1421 nassertr(_add_pos == result_file_length,
false);
1425 return (_last_copy_pos != 0);
1434 ostream &write_stream,
1435 uint32_t offset_orig, uint32_t offset_new,
1436 istream &stream_orig, istream &stream_new) {
1437 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new,
false);
1440 bool is_multifile =
false;
1442 bool is_tarfile =
false;
1443 TarDef tar_orig, tar_new;
1446 if (_allow_multifile) {
1447 if (strstr(file_orig.
get_basename().c_str(),
".mf") !=
nullptr ||
1448 strstr(file_new.
get_basename().c_str(),
".mf") !=
nullptr) {
1451 char *buffer = (
char *)PANDA_MALLOC_ARRAY(magic_number.size());
1452 stream_orig.seekg(0, ios::beg);
1453 stream_orig.read(buffer, magic_number.size());
1455 if (stream_orig.gcount() == (int)magic_number.size() &&
1456 memcmp(buffer, magic_number.data(), magic_number.size()) == 0) {
1457 stream_new.seekg(0, ios::beg);
1458 stream_new.read(buffer, magic_number.size());
1459 if (stream_new.gcount() == (int)magic_number.size() &&
1460 memcmp(buffer, magic_number.data(), magic_number.size()) == 0) {
1461 is_multifile =
true;
1464 PANDA_FREE_ARRAY(buffer);
1467 if (strstr(file_orig.
get_basename().c_str(),
".tar") !=
nullptr ||
1468 strstr(file_new.
get_basename().c_str(),
".tar") !=
nullptr) {
1469 if (read_tar(tar_orig, stream_orig) &&
1470 read_tar(tar_new, stream_new)) {
1478 if (express_cat.is_debug()) {
1480 << file_orig.
get_basename() <<
" appears to be a Panda Multifile.\n";
1482 if (!compute_mf_patches(write_stream, offset_orig, offset_new,
1483 stream_orig, stream_new)) {
1487 }
else if (is_tarfile) {
1488 if (express_cat.is_debug()) {
1490 << file_orig.
get_basename() <<
" appears to be a tar file.\n";
1492 if (!compute_tar_patches(write_stream, offset_orig, offset_new,
1493 stream_orig, stream_new, tar_orig, tar_new)) {
1498 if (express_cat.is_debug()) {
1500 << file_orig.
get_basename() <<
" is not a multifile.\n";
1502 if (!compute_file_patches(write_stream, offset_orig, offset_new,
1503 stream_orig, stream_new)) {
1515 patch_subfile(ostream &write_stream,
1516 uint32_t offset_orig, uint32_t offset_new,
1518 IStreamWrapper &stream_orig, streampos orig_start, streampos orig_end,
1519 IStreamWrapper &stream_new, streampos new_start, streampos new_end) {
1520 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + (uint32_t)new_start,
false);
1522 size_t new_size = new_end - new_start;
1523 size_t orig_size = orig_end - orig_start;
1525 ISubStream subfile_orig(&stream_orig, orig_start, orig_end);
1526 ISubStream subfile_new(&stream_new, new_start, new_end);
1528 bool is_unchanged =
false;
1529 if (orig_size == new_size) {
1531 hash_orig.hash_stream(subfile_orig);
1532 hash_new.hash_stream(subfile_new);
1534 if (hash_orig == hash_new) {
1536 is_unchanged =
true;
1541 if (express_cat.is_debug() && !filename.empty()) {
1543 <<
"Keeping subfile " << filename <<
"\n";
1545 cache_add_and_copy(write_stream, 0,
nullptr,
1546 orig_size, offset_orig + orig_start);
1549 if (!filename.empty()) {
1551 <<
"Patching subfile " << filename <<
"\n";
1554 if (!do_compute_patches(filename, filename, write_stream,
1555 offset_orig + orig_start, offset_new + new_start,
1556 subfile_orig, subfile_new)) {
1564 #endif // HAVE_OPENSSL A StreamWriter object is used to write sequential binary data directly to an ostream.
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
size_t get_subfile_internal_length(int index) const
Returns the number of bytes the indicated subfile consumes within the archive.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A hierarchy of directories and files that appears to be one continuous file system,...
bool needs_repack() const
Returns true if the Multifile index is suboptimal and should be repacked.
std::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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
std::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(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An istream object that presents a subwindow into another istream.
int find_subfile(const std::string &subfile_name) const
Returns the index of the subfile with the indicated name, or -1 if the named subfile is not within th...
The name of a file, such as a texture file or an Egg file.
get_subfile_name
Returns the name of the nth subfile.
This class provides a locking wrapper around an arbitrary istream pointer.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
get_magic_number
Returns a string with the first n bytes written to a Multifile, to identify it as a Multifile.
std::string get_basename() const
Returns the basename part of the filename.
get_num_subfiles
Returns the number of subfiles within the Multifile.
A file that contains a set of files.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Filename temporary(const std::string &dirname, const std::string &prefix, const std::string &suffix=std::string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A class to read sequential binary data directly from an istream.
std::streampos get_subfile_internal_start(int index) const
Returns the starting byte position within the Multifile at which the indicated subfile begins.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.