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, 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.
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.