35std::istream *Patchfile::_tar_istream =
nullptr;
68const int _v0_header_length = 4 + 4 + 16;
69const int _v1_header_length = 4 + 2 + 4 + 16 + 4 + 16;
86const uint32_t Patchfile::_v0_magic_number = 0xfeebfaab;
87const uint32_t Patchfile::_magic_number = 0xfeebfaac;
91const uint16_t Patchfile::_current_version = 2;
93const uint32_t Patchfile::_HASH_BITS = 24;
94const uint32_t Patchfile::_HASHTABLESIZE = uint32_t(1) << Patchfile::_HASH_BITS;
95const uint32_t Patchfile::_DEFAULT_FOOTPRINT_LENGTH = 9;
96const uint32_t Patchfile::_NULL_VALUE = uint32_t(0) - 1;
97const uint32_t Patchfile::_MAX_RUN_LENGTH = (uint32_t(1) << 16) - 1;
98const uint32_t Patchfile::_HASH_MASK = (uint32_t(1) << Patchfile::_HASH_BITS) - 1;
113Patchfile(PT(
Buffer) buffer) {
122 _rename_output_to_orig =
false;
123 _delete_patchfile =
false;
124 _hash_table =
nullptr;
126 nassertv(!buffer.is_null());
130 _allow_multifile =
true;
132 _patch_stream =
nullptr;
133 _origfile_stream =
nullptr;
135 reset_footprint_length();
143 if (_hash_table !=
nullptr) {
144 PANDA_FREE_ARRAY(_hash_table);
151 nassertv(_patch_stream ==
nullptr);
152 nassertv(_origfile_stream ==
nullptr);
162 <<
"Patchfile::cleanup() - Patching has not been initiated"
169 if (_origfile_stream !=
nullptr) {
171 _origfile_stream =
nullptr;
173 if (_patch_stream !=
nullptr) {
175 _patch_stream =
nullptr;
177 _write_stream.close();
195 int result = initiate(patch_file, file, Filename::temporary(
"",
"patch_"));
196 _rename_output_to_orig =
true;
197 _delete_patchfile = !keep_temporary_files;
210 <<
"Patchfile::initiate() - Patching has already been initiated"
212 return EU_error_abort;
215 nassertr(orig_file != target_file, EU_error_abort);
220 nassertr(_origfile_stream ==
nullptr, EU_error_abort);
221 _orig_file = orig_file;
224 if (_origfile_stream ==
nullptr) {
226 <<
"Patchfile::initiate() - Failed to open file: " << _orig_file << endl;
227 return get_write_error();
231 _output_file = target_file;
233 if (!_output_file.open_write(_write_stream)) {
235 <<
"Patchfile::initiate() - Failed to open file: " << _output_file << endl;
236 return get_write_error();
239 if (express_cat.is_debug()) {
241 <<
"Patchfile using output file " << _output_file <<
"\n";
244 int result = internal_read_header(patch_file);
245 _total_bytes_processed = 0;
257read_header(
const Filename &patch_file) {
260 <<
"Patchfile::initiate() - Patching has already been initiated"
262 return EU_error_abort;
265 int result = internal_read_header(patch_file);
266 if (_patch_stream !=
nullptr) {
269 _patch_stream =
nullptr;
290 uint16_t COPY_length;
293 if (_initiated ==
false) {
295 <<
"Patchfile::run() - Patching has not been initiated"
297 return EU_error_abort;
300 nassertr(_patch_stream !=
nullptr, EU_error_abort);
301 nassertr(_origfile_stream !=
nullptr, EU_error_abort);
304 buflen = _buffer->get_length();
307 while (bytes_read < buflen) {
309 nassertr(_buffer->get_length() >= (
int)
sizeof(ADD_length),
false);
310 ADD_length = patch_reader.get_uint16();
311 if (_patch_stream->fail()) {
313 <<
"Truncated patch file.\n";
314 return EU_error_file_invalid;
317 bytes_read += (int)ADD_length;
318 _total_bytes_processed += (int)ADD_length;
319 if (_total_bytes_processed > _total_bytes_to_process) {
321 <<
"Runaway patch file.\n";
322 return EU_error_file_invalid;
327 if (express_cat.is_spam() && ADD_length != 0) {
329 <<
"ADD: " << ADD_length <<
" (to "
330 << _write_stream.tellp() <<
")" << endl;
333 uint32_t bytes_left = (uint32_t)ADD_length;
334 while (bytes_left > 0) {
335 uint32_t bytes_this_time = (uint32_t) min(bytes_left, (uint32_t) buflen);
336 _patch_stream->read(_buffer->_buffer, bytes_this_time);
337 if (_patch_stream->fail()) {
339 <<
"Truncated patch file.\n";
340 return EU_error_file_invalid;
342 _write_stream.write(_buffer->_buffer, bytes_this_time);
343 bytes_left -= bytes_this_time;
347 nassertr(_buffer->get_length() >= (
int)
sizeof(COPY_length),
false);
348 COPY_length = patch_reader.get_uint16();
349 if (_patch_stream->fail()) {
351 <<
"Truncated patch file.\n";
352 return EU_error_file_invalid;
355 bytes_read += (int)COPY_length;
356 _total_bytes_processed += (int)COPY_length;
357 if (_total_bytes_processed > _total_bytes_to_process) {
359 <<
"Runaway patch file.\n";
360 return EU_error_file_invalid;
365 if (0 != COPY_length) {
367 nassertr(_buffer->get_length() >= (
int)
sizeof(COPY_offset),
false);
368 COPY_offset = patch_reader.get_int32();
369 if (_patch_stream->fail()) {
371 <<
"Truncated patch file.\n";
372 return EU_error_file_invalid;
376 if (_version_number < 2) {
377 _origfile_stream->seekg(COPY_offset, ios::beg);
379 _origfile_stream->seekg(COPY_offset, ios::cur);
381 if (_origfile_stream->fail()) {
383 <<
"Invalid copy offset in patch file.\n";
384 return EU_error_file_invalid;
387 if (express_cat.is_spam()) {
389 <<
"COPY: " << COPY_length <<
" bytes from offset "
390 << COPY_offset <<
" (from " << _origfile_stream->tellg()
391 <<
" to " << _write_stream.tellp() <<
")"
396 uint32_t bytes_left = (uint32_t)COPY_length;
398 while (bytes_left > 0) {
399 uint32_t bytes_this_time = (uint32_t) min(bytes_left, (uint32_t) buflen);
400 _origfile_stream->read(_buffer->_buffer, bytes_this_time);
401 if (_origfile_stream->fail()) {
403 <<
"Invalid copy length in patch file.\n";
404 return EU_error_file_invalid;
406 _write_stream.write(_buffer->_buffer, bytes_this_time);
407 bytes_left -= bytes_this_time;
412 if ((0 == ADD_length) && (0 == COPY_length)) {
415 if (express_cat.is_debug()) {
418 <<
" total bytes = " << _total_bytes_processed << endl;
424 MD5_actual.hash_file(_output_file);
425 if (_MD5_ofResult != MD5_actual) {
427 if (_origfile_stream !=
nullptr) {
430 _origfile_stream =
nullptr;
432 _write_stream.close();
435 <<
"Patching produced incorrect checksum. Got:\n"
438 <<
" " << _MD5_ofResult
442 if (!has_source_hash()) {
444 <<
"No source hash in patch file to verify.\n";
447 MD5_orig.hash_file(_orig_file);
448 if (MD5_orig != get_source_hash()) {
450 <<
"Started from incorrect source file. Got:\n"
453 <<
" " << get_source_hash()
457 <<
"Started from correct source file:\n"
464 if (_rename_output_to_orig) {
465 _output_file.unlink();
467 if (_delete_patchfile) {
468 _patch_file.unlink();
471 return EU_error_invalid_checksum;
476 if (_delete_patchfile) {
477 _patch_file.unlink();
481 if (_rename_output_to_orig) {
483 if (!_output_file.rename_to(_orig_file)) {
485 <<
"Patchfile::run() failed to rename temp file to: " << _orig_file
487 return EU_error_write_file_rename;
506 int ret = initiate(patch_file, file);
511 if (ret == EU_success)
527 int ret = initiate(patch_file, orig_file, target_file);
532 if (ret == EU_success)
545internal_read_header(
const Filename &patch_file) {
548 nassertr(_patch_stream ==
nullptr, EU_error_abort);
549 _patch_file = patch_file;
552 if (_patch_stream ==
nullptr) {
554 <<
"Patchfile::initiate() - Failed to open file: " << _patch_file << endl;
555 return get_write_error();
562 nassertr(_buffer->get_length() >= _v0_header_length,
false);
563 uint32_t magic_number = patch_reader.get_uint32();
564 if (magic_number != _magic_number && magic_number != _v0_magic_number) {
566 <<
"Invalid patch file: " << _patch_file << endl;
567 return EU_error_file_invalid;
571 if (magic_number != _v0_magic_number) {
572 _version_number = patch_reader.get_uint16();
574 if (_version_number > _current_version) {
576 <<
"Can't read version " << _version_number <<
" patch files: "
577 << _patch_file << endl;
578 return EU_error_file_invalid;
581 if (_version_number >= 1) {
583 patch_reader.get_uint32();
586 _MD5_ofSource.read_stream(patch_reader);
590 _total_bytes_to_process = patch_reader.get_uint32();
593 _MD5_ofResult.read_stream(patch_reader);
596 <<
"Patchfile::initiate() - valid patchfile" << endl;
607calc_hash(
const char *buffer) {
608#ifdef USE_MD5_FOR_HASHTABLE_INDEX_VALUES
610 hash.hash_buffer(buffer, _footprint_length);
614 return uint16_t(hash.get_value(0));
616 uint32_t hash_value = 0;
618 for(
int i = 0; i < (int)_footprint_length; i++) {
621 hash_value ^= uint32_t(*buffer) << ((i * 2) % Patchfile::_HASH_BITS);
627 hash_value ^= (hash_value >> Patchfile::_HASH_BITS);
631 return hash_value & _HASH_MASK;
652build_hash_link_tables(
const char *buffer_orig, uint32_t length_orig,
653 uint32_t *hash_table, uint32_t *link_table) {
658 for(i = 0; i < _HASHTABLESIZE; i++) {
659 hash_table[i] = _NULL_VALUE;
663 for(i = 0; i < length_orig; i++) {
664 link_table[i] = _NULL_VALUE;
667 if(length_orig < _footprint_length)
return;
670 for(i = 0; i < (length_orig - _footprint_length); i++) {
672 uint32_t hash_value = calc_hash(&buffer_orig[i]);
685 link_table[i] = hash_table[hash_value];
689 hash_table[hash_value] = i;
713calc_match_length(
const char* buf1,
const char* buf2, uint32_t max_length,
714 uint32_t min_length) {
716 if (min_length > 2) {
717 if (min_length >= max_length)
719 if (buf1[min_length] != buf2[min_length] ||
720 buf1[min_length-1] != buf2[min_length-1] ||
721 buf1[min_length-2] != buf2[min_length-2]) {
727 while ((length < max_length) && (*buf1 == *buf2)) {
728 buf1++, buf2++, length++;
739find_longest_match(uint32_t new_pos, uint32_t ©_pos, uint16_t ©_length,
740 uint32_t *hash_table, uint32_t *link_table,
const char* buffer_orig,
741 uint32_t length_orig,
const char* buffer_new, uint32_t length_new) {
747 uint32_t hash_value = calc_hash(&buffer_new[new_pos]);
750 if (_NULL_VALUE == hash_table[hash_value])
753 copy_pos = hash_table[hash_value];
756 copy_length = (uint16_t)calc_match_length(&buffer_new[new_pos],
757 &buffer_orig[copy_pos],
758 min(min((length_new - new_pos),
759 (length_orig - copy_pos)),
764 uint32_t match_offset;
765 uint16_t match_length;
766 match_offset = link_table[copy_pos];
768 while (match_offset != _NULL_VALUE) {
769 match_length = (uint16_t)calc_match_length(&buffer_new[new_pos],
770 &buffer_orig[match_offset],
771 min(min((length_new - new_pos),
772 (length_orig - match_offset)),
777 if (match_length > copy_length) {
778 copy_pos = match_offset;
779 copy_length = match_length;
783 match_offset = link_table[match_offset];
791emit_ADD(ostream &write_stream, uint32_t length,
const char* buffer) {
792 nassertv(length == (uint16_t)length);
794 if (express_cat.is_spam()) {
796 <<
"ADD: " << length <<
" (to " << _add_pos <<
")" << endl;
801 patch_writer.add_uint16((uint16_t)length);
805 patch_writer.append_data(buffer, (uint16_t)length);
815emit_COPY(ostream &write_stream, uint32_t length, uint32_t copy_pos) {
816 nassertv(length == (uint16_t)length);
818 int32_t offset = (int)copy_pos - (
int)_last_copy_pos;
819 if (express_cat.is_spam()) {
821 <<
"COPY: " << length <<
" bytes from offset " << offset
822 <<
" (from " << copy_pos <<
" to " << _add_pos <<
")" << endl;
827 patch_writer.add_uint16((uint16_t)length);
829 if ((uint16_t)length != 0) {
831 patch_writer.add_int32(offset);
832 _last_copy_pos = copy_pos + length;
843emit_add_and_copy(ostream &write_stream,
844 uint32_t add_length,
const char *add_buffer,
845 uint32_t copy_length, uint32_t copy_pos) {
846 if (add_length == 0 && copy_length == 0) {
851 static const uint16_t max_write = 65535;
852 while (add_length > max_write) {
855 emit_ADD(write_stream, max_write, add_buffer);
856 add_buffer += max_write;
857 add_length -= max_write;
858 emit_COPY(write_stream, 0, 0);
861 emit_ADD(write_stream, add_length, add_buffer);
863 while (copy_length > max_write) {
865 emit_COPY(write_stream, max_write, copy_pos);
866 copy_pos += max_write;
867 copy_length -= max_write;
868 emit_ADD(write_stream, 0,
nullptr);
871 emit_COPY(write_stream, copy_length, copy_pos);
879cache_add_and_copy(ostream &write_stream,
880 uint32_t add_length,
const char *add_buffer,
881 uint32_t copy_length, uint32_t copy_pos) {
882 if (add_length != 0) {
883 if (_cache_copy_length != 0) {
885 cache_flush(write_stream);
888 _cache_add_data += string(add_buffer, add_length);
891 if (copy_length != 0) {
892 if (_cache_copy_length == 0) {
894 _cache_copy_start = copy_pos;
895 _cache_copy_length = copy_length;
897 }
else if (_cache_copy_start + _cache_copy_length == copy_pos) {
899 _cache_copy_length += copy_length;
903 cache_flush(write_stream);
904 _cache_copy_start = copy_pos;
905 _cache_copy_length = copy_length;
915cache_flush(ostream &write_stream) {
916 emit_add_and_copy(write_stream,
917 _cache_add_data.size(), _cache_add_data.data(),
918 _cache_copy_length, _cache_copy_start);
919 _cache_add_data = string();
920 _cache_copy_length = 0;
929write_header(ostream &write_stream,
930 istream &stream_orig, istream &stream_new) {
935 patch_writer.add_uint32(_magic_number);
936 patch_writer.add_uint16(_current_version);
938 stream_orig.seekg(0, ios::end);
939 streampos source_file_length = stream_orig.tellg();
940 patch_writer.add_uint32((uint32_t)source_file_length);
943 _MD5_ofSource.hash_stream(stream_orig);
945 _MD5_ofSource.write_stream(patch_writer);
947 if (express_cat.is_debug()) {
949 <<
"Orig: " << _MD5_ofSource <<
"\n";
952 stream_new.seekg(0, ios::end);
953 streampos result_file_length = stream_new.tellg();
954 patch_writer.add_uint32((uint32_t)result_file_length);
957 _MD5_ofResult.hash_stream(stream_new);
959 _MD5_ofResult.write_stream(patch_writer);
961 if (express_cat.is_debug()) {
963 <<
" New: " << _MD5_ofResult <<
"\n";
971write_terminator(ostream &write_stream) {
972 cache_flush(write_stream);
974 emit_ADD(write_stream, 0,
nullptr);
975 emit_COPY(write_stream, 0, 0);
985compute_file_patches(ostream &write_stream,
986 uint32_t offset_orig, uint32_t offset_new,
987 istream &stream_orig, istream &stream_new) {
989 stream_orig.seekg(0, ios::end);
990 nassertr(stream_orig,
false);
991 uint32_t source_file_length = stream_orig.tellg();
992 if (express_cat.is_debug()) {
994 <<
"Allocating " << source_file_length <<
" bytes to read orig\n";
997 char *buffer_orig = (
char *)PANDA_MALLOC_ARRAY(source_file_length);
998 stream_orig.seekg(0, ios::beg);
999 stream_orig.read(buffer_orig, source_file_length);
1002 stream_new.seekg(0, ios::end);
1003 uint32_t result_file_length = stream_new.tellg();
1004 nassertr(stream_new,
false);
1005 if (express_cat.is_debug()) {
1007 <<
"Allocating " << result_file_length <<
" bytes to read new\n";
1010 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(result_file_length);
1011 stream_new.seekg(0, ios::beg);
1012 stream_new.read(buffer_new, result_file_length);
1015 if (_hash_table ==
nullptr) {
1016 if (express_cat.is_debug()) {
1018 <<
"Allocating hashtable of size " << _HASHTABLESIZE <<
" * 4\n";
1020 _hash_table = (uint32_t *)PANDA_MALLOC_ARRAY(_HASHTABLESIZE *
sizeof(uint32_t));
1023 if (express_cat.is_debug()) {
1025 <<
"Allocating linktable of size " << source_file_length <<
" * 4\n";
1028 uint32_t *link_table = (uint32_t *)PANDA_MALLOC_ARRAY(source_file_length *
sizeof(uint32_t));
1031 build_hash_link_tables(buffer_orig, source_file_length, _hash_table, link_table);
1035 uint32_t new_pos = 0;
1036 uint32_t start_pos = new_pos;
1038 if(((uint32_t) result_file_length) >= _footprint_length)
1040 while (new_pos < (result_file_length - _footprint_length)) {
1044 uint16_t COPY_length;
1046 find_longest_match(new_pos, COPY_pos, COPY_length, _hash_table, link_table,
1047 buffer_orig, source_file_length, buffer_new, result_file_length);
1051 if (COPY_length < _footprint_length) {
1056 int num_skipped = (int)new_pos - (
int)start_pos;
1057 if (express_cat.is_spam()) {
1059 <<
"build: num_skipped = " << num_skipped
1062 cache_add_and_copy(write_stream, num_skipped, &buffer_new[start_pos],
1063 COPY_length, COPY_pos + offset_orig);
1064 new_pos += (uint32_t)COPY_length;
1065 start_pos = new_pos;
1070 if (express_cat.is_spam()) {
1072 <<
"build: result_file_length = " << result_file_length
1073 <<
" start_pos = " << start_pos
1078 if (start_pos != result_file_length) {
1081 uint32_t remaining_bytes = result_file_length - start_pos;
1082 cache_add_and_copy(write_stream, remaining_bytes, &buffer_new[start_pos],
1084 start_pos += remaining_bytes;
1087 PANDA_FREE_ARRAY(link_table);
1089 PANDA_FREE_ARRAY(buffer_orig);
1090 PANDA_FREE_ARRAY(buffer_new);
1102compute_mf_patches(ostream &write_stream,
1103 uint32_t offset_orig, uint32_t offset_new,
1104 istream &stream_orig, istream &stream_new) {
1108 if (!mf_orig.open_read(&stream_origw) ||
1109 !mf_new.open_read(&stream_neww)) {
1111 <<
"Input multifiles appear to be corrupt.\n";
1117 <<
"Input multifiles need to be repacked.\n";
1126 if (!do_compute_patches(
"",
"",
1127 write_stream, offset_orig, offset_new,
1128 index_orig, index_new)) {
1131 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + (uint32_t)mf_new.
get_index_end(),
false);
1139 for (
int ni = 0; ni < new_num_subfiles; ++ni) {
1140 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + (uint32_t)mf_new.
get_subfile_internal_start(ni),
false);
1151 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(new_size);
1152 stream_new.seekg(new_start, ios::beg);
1153 stream_new.read(buffer_new, new_size);
1154 cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0);
1155 PANDA_FREE_ARRAY(buffer_new);
1166 if (!patch_subfile(write_stream, offset_orig, offset_new,
1168 stream_origw, orig_start, orig_start + (streampos)orig_size,
1169 stream_neww, new_start, new_start + (streampos)new_size)) {
1185read_tar(TarDef &tar, istream &stream) {
1188 tt.openfunc = tar_openfunc;
1189 tt.closefunc = tar_closefunc;
1190 tt.readfunc = tar_readfunc;
1191 tt.writefunc = tar_writefunc;
1193 stream.seekg(0, ios::beg);
1194 nassertr(_tar_istream ==
nullptr,
false);
1195 _tar_istream = &stream;
1196 if (tar_open(&tfile, (
char *)
"dummy", &tt, O_RDONLY, 0, 0) != 0) {
1197 _tar_istream =
nullptr;
1205 streampos last_pos = 0;
1206 int flag = th_read(tfile);
1209 subfile._name = th_get_pathname(tfile);
1210 subfile._header_start = last_pos;
1211 subfile._data_start = stream.tellg();
1212 subfile._data_end = subfile._data_start + (streampos)th_get_size(tfile);
1213 tar_skip_regfile(tfile);
1214 subfile._end = stream.tellg();
1215 tar.push_back(subfile);
1217 last_pos = subfile._end;
1218 flag = th_read(tfile);
1224 subfile._header_start = last_pos;
1226 stream.seekg(0, ios::end);
1227 subfile._data_start = stream.tellg();
1228 subfile._data_end = subfile._data_start;
1229 subfile._end = subfile._data_start;
1230 tar.push_back(subfile);
1233 _tar_istream =
nullptr;
1246compute_tar_patches(ostream &write_stream,
1247 uint32_t offset_orig, uint32_t offset_new,
1248 istream &stream_orig, istream &stream_new,
1249 TarDef &tar_orig, TarDef &tar_new) {
1266 TarDef::const_iterator ni;
1267 streampos last_pos = 0;
1268 for (ni = tar_new.begin(); ni != tar_new.end(); ++ni) {
1269 const TarSubfile &sf_new =(*ni);
1270 nassertr(sf_new._header_start == last_pos,
false);
1272 TarDef::const_iterator oi = tar_orig.find(sf_new);
1274 if (oi == tar_orig.end()) {
1277 <<
"Adding subfile " << sf_new._name <<
"\n";
1279 streampos new_start = sf_new._header_start;
1280 size_t new_size = sf_new._end - sf_new._header_start;
1281 char *buffer_new = (
char *)PANDA_MALLOC_ARRAY(new_size);
1282 stream_new.seekg(new_start, ios::beg);
1283 stream_new.read(buffer_new, new_size);
1284 cache_add_and_copy(write_stream, new_size, buffer_new, 0, 0);
1285 PANDA_FREE_ARRAY(buffer_new);
1290 const TarSubfile &sf_orig =(*oi);
1296 if (!patch_subfile(write_stream, offset_orig, offset_new,
"",
1297 stream_origw, sf_orig._header_start, sf_orig._data_start,
1298 stream_neww, sf_new._header_start, sf_new._data_start)) {
1302 if (!patch_subfile(write_stream, offset_orig, offset_new, sf_new._name,
1303 stream_origw, sf_orig._data_start, sf_orig._data_end,
1304 stream_neww, sf_new._data_start, sf_new._data_end)) {
1308 if (!patch_subfile(write_stream, offset_orig, offset_new,
"",
1309 stream_origw, sf_orig._data_end, sf_orig._end,
1310 stream_neww, sf_new._data_end, sf_new._end)) {
1315 last_pos = sf_new._end;
1328tar_openfunc(
const char *,
int, ...) {
1353tar_readfunc(
int,
void *buffer,
size_t nbytes) {
1354 nassertr(_tar_istream !=
nullptr, 0);
1355 _tar_istream->read((
char *)buffer, nbytes);
1356 return (ssize_t)_tar_istream->gcount();
1366tar_writefunc(
int,
const void *,
size_t) {
1369 nassertr(
false, -1);
1387 pifstream stream_orig;
1389 if (!file_orig.
open_read(stream_orig)) {
1391 <<
"Patchfile::build() - Failed to open file: " << file_orig << endl;
1396 pifstream stream_new;
1400 <<
"Patchfile::build() - Failed to open file: " << file_new << endl;
1405 pofstream write_stream;
1408 <<
"Patchfile::build() - Failed to open file: " << patch_name << endl;
1414 _cache_add_data = string();
1415 _cache_copy_start = 0;
1416 _cache_copy_length = 0;
1418 write_header(write_stream, stream_orig, stream_new);
1420 if (!do_compute_patches(file_orig, file_new,
1422 stream_orig, stream_new)) {
1426 write_terminator(write_stream);
1428 if (express_cat.is_debug()) {
1430 <<
"Patch file will generate " << _add_pos <<
"-byte file.\n";
1436 stream_new.seekg(0, ios::end);
1437 streampos result_file_length = stream_new.tellg();
1438 nassertr(_add_pos == result_file_length,
false);
1442 return (_last_copy_pos != 0);
1451 ostream &write_stream,
1452 uint32_t offset_orig, uint32_t offset_new,
1453 istream &stream_orig, istream &stream_new) {
1454 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new,
false);
1457 bool is_multifile =
false;
1459 bool is_tarfile =
false;
1460 TarDef tar_orig, tar_new;
1463 if (_allow_multifile) {
1464 if (strstr(file_orig.
get_basename().c_str(),
".mf") !=
nullptr ||
1465 strstr(file_new.
get_basename().c_str(),
".mf") !=
nullptr) {
1468 char *buffer = (
char *)PANDA_MALLOC_ARRAY(magic_number.size());
1469 stream_orig.seekg(0, ios::beg);
1470 stream_orig.read(buffer, magic_number.size());
1472 if (stream_orig.gcount() == (int)magic_number.size() &&
1473 memcmp(buffer, magic_number.data(), magic_number.size()) == 0) {
1474 stream_new.seekg(0, ios::beg);
1475 stream_new.read(buffer, magic_number.size());
1476 if (stream_new.gcount() == (int)magic_number.size() &&
1477 memcmp(buffer, magic_number.data(), magic_number.size()) == 0) {
1478 is_multifile =
true;
1481 PANDA_FREE_ARRAY(buffer);
1484 if (strstr(file_orig.
get_basename().c_str(),
".tar") !=
nullptr ||
1485 strstr(file_new.
get_basename().c_str(),
".tar") !=
nullptr) {
1486 if (read_tar(tar_orig, stream_orig) &&
1487 read_tar(tar_new, stream_new)) {
1495 if (express_cat.is_debug()) {
1497 << file_orig.
get_basename() <<
" appears to be a Panda Multifile.\n";
1499 if (!compute_mf_patches(write_stream, offset_orig, offset_new,
1500 stream_orig, stream_new)) {
1504 }
else if (is_tarfile) {
1505 if (express_cat.is_debug()) {
1507 << file_orig.
get_basename() <<
" appears to be a tar file.\n";
1509 if (!compute_tar_patches(write_stream, offset_orig, offset_new,
1510 stream_orig, stream_new, tar_orig, tar_new)) {
1515 if (express_cat.is_debug()) {
1517 << file_orig.
get_basename() <<
" is not a multifile.\n";
1519 if (!compute_file_patches(write_stream, offset_orig, offset_new,
1520 stream_orig, stream_new)) {
1532patch_subfile(ostream &write_stream,
1533 uint32_t offset_orig, uint32_t offset_new,
1535 IStreamWrapper &stream_orig, streampos orig_start, streampos orig_end,
1536 IStreamWrapper &stream_new, streampos new_start, streampos new_end) {
1537 nassertr(_add_pos + _cache_add_data.size() + _cache_copy_length == offset_new + (uint32_t)new_start,
false);
1539 size_t new_size = new_end - new_start;
1540 size_t orig_size = orig_end - orig_start;
1542 ISubStream subfile_orig(&stream_orig, orig_start, orig_end);
1543 ISubStream subfile_new(&stream_new, new_start, new_end);
1545 bool is_unchanged =
false;
1546 if (orig_size == new_size) {
1548 hash_orig.hash_stream(subfile_orig);
1549 hash_new.hash_stream(subfile_new);
1551 if (hash_orig == hash_new) {
1553 is_unchanged =
true;
1558 if (express_cat.is_debug() && !filename.empty()) {
1560 <<
"Keeping subfile " << filename <<
"\n";
1562 cache_add_and_copy(write_stream, 0,
nullptr,
1563 orig_size, offset_orig + orig_start);
1566 if (!filename.empty()) {
1568 <<
"Patching subfile " << filename <<
"\n";
1571 if (!do_compute_patches(filename, filename, write_stream,
1572 offset_orig + orig_start, offset_new + new_start,
1573 subfile_orig, subfile_new)) {
The name of a file, such as a texture file or an Egg file.
std::string get_basename() const
Returns the basename part of the filename.
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
void set_binary()
Indicates that the filename represents a binary file.
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer.
This class provides a locking wrapper around an arbitrary istream pointer.
An istream object that presents a subwindow into another istream.
A file that contains a set of files.
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...
get_num_subfiles
Returns the number of subfiles within the Multifile.
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...
bool needs_repack() const
Returns true if the Multifile index is suboptimal and should be repacked.
std::streampos get_subfile_internal_start(int index) const
Returns the starting byte position within the Multifile at which the indicated subfile begins.
size_t get_subfile_internal_length(int index) const
Returns the number of bytes the indicated subfile consumes within the archive.
get_magic_number
Returns a string with the first n bytes written to a Multifile, to identify it as a Multifile.
get_subfile_name
Returns the name of the nth subfile.
A class to read sequential binary data directly from an istream.
A StreamWriter object is used to write sequential binary data directly to an ostream.
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
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,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.