37 using std::ostringstream;
40 using std::streamsize;
41 using std::stringstream;
45 const char Multifile::_header[] =
"pmf\0\n\r";
46 const size_t Multifile::_header_size = 6;
51 const int Multifile::_current_major_ver = 1;
53 const int Multifile::_current_minor_ver = 1;
62 const char Multifile::_encrypt_header[] =
"crypty";
63 const size_t Multifile::_encrypt_header_size = 6;
107 _read_filew(_read_file),
108 _read_write_filew(_read_write_file)
111 (
"multifile-encryption-iteration-count", 0,
112 PRC_DESC(
"This is a special value of encryption-iteration-count used to encrypt " 113 "subfiles within a multifile. It has a default value of 0 (just one " 114 "application), on the assumption that the files from a multifile must " 115 "be loaded quickly, without paying the cost of an expensive hash on " 116 "each subfile in order to decrypt it."));
121 _owns_stream =
false;
125 _needs_repack =
false;
127 _timestamp_dirty =
false;
128 _record_timestamp =
true;
130 _new_scale_factor = 1;
131 _encryption_flag =
false;
132 _encryption_iteration_count = multifile_encryption_iteration_count;
138 EncryptStreamBuf tbuf;
139 _encryption_algorithm = tbuf.get_algorithm();
140 _encryption_key_length = tbuf.get_key_length();
161 open_read(
const Filename &multifile_name,
const streampos &offset) {
168 if (vfile ==
nullptr) {
171 istream *multifile_stream = vfile->open_read_file(
false);
172 if (multifile_stream ==
nullptr) {
176 _timestamp = vfile->get_timestamp();
177 _timestamp_dirty =
true;
180 _multifile_name = multifile_name;
195 const streampos &offset) {
197 _timestamp = time(
nullptr);
198 _timestamp_dirty =
true;
199 _read = multifile_stream;
200 _owns_stream = owns_pointer;
222 _timestamp = time(
nullptr);
223 _timestamp_dirty =
true;
224 _write = &_write_file;
225 _multifile_name = multifile_name;
240 _timestamp = time(
nullptr);
241 _timestamp_dirty =
true;
242 _write = multifile_stream;
243 _owns_stream = owns_pointer;
244 _write->seekp(0, ios::beg);
262 bool exists = fname.
exists();
269 _timestamp = time(
nullptr);
271 _timestamp_dirty =
true;
272 _read = &_read_write_filew;
273 _write = &_read_write_file;
274 _multifile_name = multifile_name;
295 _timestamp = time(
nullptr);
296 _timestamp_dirty =
true;
303 _write = multifile_stream;
305 _write->seekp(0, ios::beg);
308 multifile_stream->seekg(0, ios::end);
309 if (multifile_stream->tellg() == (streampos)0) {
325 if (_new_scale_factor != _scale_factor) {
335 if (_read !=
nullptr) {
337 if (!_read->
unref()) {
340 }
else if (_write !=
nullptr) {
348 _owns_stream =
false;
351 _needs_repack =
false;
353 _timestamp_dirty =
false;
355 _new_scale_factor = 1;
356 _encryption_flag =
false;
362 _read_write_file.close();
388 nassertv(scale_factor != (
size_t)0);
390 if (_next_index == (streampos)0) {
392 _scale_factor = scale_factor;
400 _new_scale_factor = scale_factor;
418 int compression_level) {
422 if (multifile_always_binary) {
431 string name = standardize_subfile_name(subfile_name);
433 Subfile *subfile =
new Subfile;
434 subfile->_name = name;
435 subfile->_source_filename = fname;
437 subfile->_flags |= SF_text;
440 add_new_subfile(subfile, compression_level);
443 _timestamp = time(
nullptr);
444 _timestamp_dirty =
true;
466 int compression_level) {
469 string name = standardize_subfile_name(subfile_name);
471 Subfile *subfile =
new Subfile;
472 subfile->_name = name;
473 subfile->_source = subfile_data;
474 add_new_subfile(subfile, compression_level);
492 int compression_level) {
496 if (multifile_always_binary) {
505 string name = standardize_subfile_name(subfile_name);
518 Subfile *subfile =
new Subfile;
519 subfile->_name = name;
520 subfile->_source_filename = fname;
522 subfile->_flags |= SF_text;
525 add_new_subfile(subfile, compression_level);
528 _timestamp = time(
nullptr);
529 _timestamp_dirty =
true;
539 Multifile::CertRecord::
540 CertRecord(X509 *cert) :
548 Multifile::CertRecord::
549 CertRecord(
const Multifile::CertRecord ©) :
550 _cert(X509_dup(copy._cert))
557 Multifile::CertRecord::
565 void Multifile::CertRecord::
566 operator = (
const Multifile::CertRecord &other) {
568 _cert = X509_dup(other._cert);
570 #endif // HAVE_OPENSSL 600 const Filename &pkey,
const string &password) {
603 if (chain.empty() && pkey.empty()) {
606 return add_signature(certificate, password);
609 CertChain cert_chain;
613 string certificate_data;
614 if (!vfs->read_file(certificate, certificate_data,
true)) {
616 <<
"Could not read " << certificate <<
".\n";
621 BIO *certificate_mbio = BIO_new_mem_buf((
void *)certificate_data.data(), certificate_data.size());
622 X509 *x509 = PEM_read_bio_X509(certificate_mbio,
nullptr,
nullptr, (
void *)
"");
623 BIO_free(certificate_mbio);
624 if (x509 ==
nullptr) {
626 <<
"Could not read certificate in " << certificate <<
".\n";
632 cert_chain.push_back(CertRecord(x509));
635 if (!chain.empty()) {
637 if (!vfs->read_file(chain, chain_data,
true)) {
639 <<
"Could not read " << chain <<
".\n";
643 BIO *chain_mbio = BIO_new_mem_buf((
void *)chain_data.data(), chain_data.size());
644 X509 *c = PEM_read_bio_X509(chain_mbio,
nullptr,
nullptr, (
void *)
"");
645 while (c !=
nullptr) {
646 cert_chain.push_back(c);
647 c = PEM_read_bio_X509(chain_mbio,
nullptr,
nullptr, (
void *)
"");
649 BIO_free(chain_mbio);
651 if (cert_chain.size() == 1) {
653 <<
"Could not read certificate chain in " << chain <<
".\n";
661 if (!vfs->read_file(pkey, pkey_data,
true)) {
663 <<
"Could not read " << pkey <<
".\n";
667 BIO *pkey_mbio = BIO_new_mem_buf((
void *)pkey_data.data(), pkey_data.size());
668 EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio,
nullptr,
nullptr,
669 (
void *)password.c_str());
671 if (evp_pkey ==
nullptr) {
673 <<
"Could not read private key in " << pkey <<
".\n";
677 bool result = add_signature(cert_chain, evp_pkey);
679 EVP_PKEY_free(evp_pkey);
683 #endif // HAVE_OPENSSL 700 add_signature(
const Filename &composite,
const string &password) {
704 string composite_data;
705 if (!vfs->read_file(composite, composite_data,
true)) {
707 <<
"Could not read " << composite <<
".\n";
712 BIO *pkey_mbio = BIO_new_mem_buf((
void *)composite_data.data(), composite_data.size());
713 EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio,
nullptr,
nullptr,
714 (
void *)password.c_str());
716 if (evp_pkey ==
nullptr) {
718 <<
"Could not read private key in " << composite <<
".\n";
723 CertChain cert_chain;
725 BIO *chain_mbio = BIO_new_mem_buf((
void *)composite_data.data(), composite_data.size());
726 X509 *c = PEM_read_bio_X509(chain_mbio,
nullptr,
nullptr, (
void *)
"");
727 while (c !=
nullptr) {
728 cert_chain.push_back(c);
729 c = PEM_read_bio_X509(chain_mbio,
nullptr,
nullptr, (
void *)
"");
731 BIO_free(chain_mbio);
733 if (cert_chain.empty()) {
735 <<
"Could not read certificates in " << composite <<
".\n";
742 bool found_match =
false;
743 for (i = 0; i < cert_chain.size(); ++i) {
744 X509 *c = cert_chain[i]._cert;
745 if (X509_check_private_key(c, evp_pkey)) {
749 cert_chain.insert(cert_chain.begin(), cert_chain[i]);
750 cert_chain.erase(cert_chain.begin() + i + 1);
758 <<
"No certificates in " << composite <<
" match key.\n";
762 bool result = add_signature(cert_chain, evp_pkey);
764 EVP_PKEY_free(evp_pkey);
768 #endif // HAVE_OPENSSL 793 add_signature(
const Multifile::CertChain &cert_chain, EVP_PKEY *pkey) {
804 if (cert_chain.empty()) {
806 <<
"No certificate given.\n";
810 if (pkey ==
nullptr) {
812 <<
"No private key given.\n";
816 if (!X509_check_private_key(cert_chain[0]._cert, pkey)) {
818 <<
"Private key does not match certificate.\n";
823 stringstream der_stream;
825 der_writer.add_uint32((uint32_t)cert_chain.size());
827 CertChain::const_iterator ci;
828 for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
829 X509 *cert = (*ci)._cert;
831 int der_len = i2d_X509(cert,
nullptr);
832 unsigned char *der_buf =
new unsigned char[der_len];
833 unsigned char *p = der_buf;
835 der_writer.append_data(der_buf, der_len);
841 Subfile *subfile =
new Subfile;
842 subfile->_pkey = pkey;
843 subfile->_flags |= SF_signature;
844 subfile->_source = &der_stream;
848 nassertr(_new_subfiles.empty(),
false);
849 _new_subfiles.push_back(subfile);
850 bool result =
flush();
856 #endif // HAVE_OPENSSL 871 get_num_signatures()
const {
873 return _signatures.size();
875 #endif // HAVE_OPENSSL 882 const Multifile::CertChain &Multifile::
883 get_signature(
int n)
const {
885 static CertChain error_chain;
886 nassertr(n >= 0 && n < (
int)_signatures.size(), error_chain);
887 return _signatures[n];
889 #endif // HAVE_OPENSSL 900 get_signature_subject_name(
int n)
const {
901 const CertChain &cert_chain = get_signature(n);
902 nassertr(!cert_chain.empty(), string());
904 X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
905 if (xname !=
nullptr) {
908 BIO *mbio = BIO_new(BIO_s_mem());
909 X509_NAME_print_ex(mbio, xname, 0, XN_FLAG_RFC2253);
912 long pp_size = BIO_get_mem_data(mbio, &pp);
913 string name(pp, pp_size);
920 #endif // HAVE_OPENSSL 932 get_signature_friendly_name(
int n)
const {
933 const CertChain &cert_chain = get_signature(n);
934 nassertr(!cert_chain.empty(), string());
936 static const int nid_choices[] = {
937 NID_pkcs9_emailAddress,
938 NID_subject_alt_name,
944 for (
int ni = 0; nid_choices[ni] != -1; ++ni) {
945 int nid = nid_choices[ni];
948 X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
949 if (xname !=
nullptr) {
950 int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
954 X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
955 if (xentry !=
nullptr) {
956 ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
957 if (data !=
nullptr) {
961 BIO *mbio = BIO_new(BIO_s_mem());
962 ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
965 long pp_size = BIO_get_mem_data(mbio, &pp);
966 string name(pp, pp_size);
977 #endif // HAVE_OPENSSL 990 get_signature_public_key(
int n)
const {
991 const CertChain &cert_chain = get_signature(n);
992 nassertr(!cert_chain.empty(), string());
994 EVP_PKEY *pkey = X509_get_pubkey(cert_chain[0]._cert);
995 if (pkey !=
nullptr) {
996 int key_len = i2d_PublicKey(pkey,
nullptr);
997 unsigned char *key_buf =
new unsigned char[key_len];
998 unsigned char *p = key_buf;
999 i2d_PublicKey(pkey, &p);
1001 for (
int i = 0; i < key_len; ++i) {
1002 result += tohex(key_buf[i] >> 4);
1003 result += tohex(key_buf[i]);
1011 #endif // HAVE_OPENSSL 1019 print_signature_certificate(
int n, ostream &out)
const {
1020 const CertChain &cert_chain = get_signature(n);
1021 nassertv(!cert_chain.empty());
1023 BIO *mbio = BIO_new(BIO_s_mem());
1024 X509_print(mbio, cert_chain[0]._cert);
1027 long pp_size = BIO_get_mem_data(mbio, &pp);
1028 out.write(pp, pp_size);
1031 #endif // HAVE_OPENSSL 1039 write_signature_certificate(
int n, ostream &out)
const {
1040 const CertChain &cert_chain = get_signature(n);
1041 nassertv(!cert_chain.empty());
1043 BIO *mbio = BIO_new(BIO_s_mem());
1045 CertChain::const_iterator ci;
1046 for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
1047 X509 *c = (*ci)._cert;
1048 X509_print(mbio, c);
1049 PEM_write_bio_X509(mbio, c);
1053 long pp_size = BIO_get_mem_data(mbio, &pp);
1054 out.write(pp, pp_size);
1057 #endif // HAVE_OPENSSL 1067 validate_signature_certificate(
int n)
const {
1068 int verify_result = -1;
1070 const CertChain &chain = get_signature(n);
1071 nassertr(!chain.empty(),
false);
1073 OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
1077 X509 *x509 = chain[0]._cert;
1078 STACK_OF(X509) *stack =
nullptr;
1079 if (chain.size() > 1) {
1080 stack = sk_X509_new(
nullptr);
1081 for (
size_t n = 1; n < chain.size(); ++n) {
1082 sk_X509_push(stack, chain[n]._cert);
1087 X509_STORE_CTX *ctx = X509_STORE_CTX_new();
1088 X509_STORE_CTX_init(ctx, sslw->get_x509_store(), x509, stack);
1089 X509_STORE_CTX_set_cert(ctx, x509);
1091 if (X509_verify_cert(ctx)) {
1094 verify_result = X509_STORE_CTX_get_error(ctx);
1097 if (express_cat.is_debug()) {
1099 << get_signature_subject_name(n) <<
": validate " << verify_result
1103 sk_X509_free(stack);
1104 X509_STORE_CTX_cleanup(ctx);
1105 X509_STORE_CTX_free(ctx);
1107 return verify_result;
1109 #endif // HAVE_OPENSSL 1132 bool new_file = (_next_index == (streampos)0);
1136 if (!write_header()) {
1141 if (_file_minor_ver != _current_minor_ver) {
1148 nassertr(_write !=
nullptr,
false);
1151 PendingSubfiles::iterator pi;
1152 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
1153 Subfile *subfile = (*pi);
1154 subfile->rewrite_index_flags(*_write);
1157 _removed_subfiles.clear();
1159 bool wrote_ok =
true;
1161 if (!_new_subfiles.empty() || new_file) {
1165 if (_last_index != (streampos)0) {
1166 _write->seekp(0, ios::end);
1167 if (_write->fail()) {
1169 <<
"Unable to seek Multifile " << _multifile_name <<
".\n";
1172 _next_index = _write->tellp();
1173 _next_index = pad_to_streampos(_next_index);
1177 _write->seekp(_last_index);
1179 writer.
add_uint32(streampos_to_word(_next_index));
1182 _write->seekp(_next_index);
1183 nassertr(_next_index == _write->tellp(),
false);
1187 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1188 Subfile *subfile = (*pi);
1189 _last_index = _next_index;
1190 _next_index = subfile->write_index(*_write, _next_index,
this);
1191 nassertr(_next_index == _write->tellp(),
false);
1192 _next_index = pad_to_streampos(_next_index);
1193 nassertr(_next_index == _write->tellp(),
false);
1200 nassertr(_next_index == _write->tellp(),
false);
1201 _next_index = pad_to_streampos(_next_index);
1204 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1205 Subfile *subfile = (*pi);
1207 if (_read !=
nullptr) {
1209 _next_index = subfile->write_data(*_write, _read->
get_istream(),
1214 _next_index = subfile->write_data(*_write,
nullptr, _next_index,
this);
1217 nassertr(_next_index == _write->tellp(),
false);
1218 _next_index = pad_to_streampos(_next_index);
1219 if (subfile->is_data_invalid()) {
1223 if (!subfile->is_cert_special()) {
1224 _last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
1226 nassertr(_next_index == _write->tellp(),
false);
1233 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1234 Subfile *subfile = (*pi);
1235 subfile->rewrite_index_data_start(*_write,
this);
1238 _new_subfiles.clear();
1242 if (_timestamp_dirty) {
1243 nassertr(!_write->fail(),
false);
1244 static const size_t timestamp_pos = _header_prefix.size() + _header_size + 2 + 2 + 4;
1245 _write->seekp(timestamp_pos);
1246 nassertr(!_write->fail(),
false);
1249 if (_record_timestamp) {
1250 writer.add_uint32(_timestamp);
1252 writer.add_uint32(0);
1254 _timestamp_dirty =
false;
1258 if (!wrote_ok || _write->fail()) {
1260 <<
"Unable to update Multifile " << _multifile_name <<
".\n";
1282 if (_next_index == (streampos)0) {
1285 _needs_repack =
false;
1290 nassertr(!_multifile_name.empty(),
false);
1294 if (dirname.empty()) {
1302 <<
"Unable to open temporary file " << temp_filename <<
"\n";
1308 PendingSubfiles::iterator pi;
1309 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
1310 Subfile *subfile = (*pi);
1313 _removed_subfiles.clear();
1314 _new_subfiles.clear();
1315 std::copy(_subfiles.
begin(), _subfiles.
end(), std::back_inserter(_new_subfiles));
1318 _last_data_byte = 0;
1319 _scale_factor = _new_scale_factor;
1331 Filename orig_name = _multifile_name;
1335 if (!temp_filename.
rename_to(orig_name)) {
1337 <<
"Unable to rename temporary file " << temp_filename <<
" to " 1338 << orig_name <<
".\n";
1344 <<
"Unable to read newly repacked " << _multifile_name
1358 get_num_subfiles()
const {
1359 return _subfiles.
size();
1369 find_subfile._name = standardize_subfile_name(subfile_name);
1370 Subfiles::const_iterator fi;
1372 if (fi == _subfiles.
end()) {
1376 return (fi - _subfiles.
begin());
1386 string prefix = subfile_name;
1387 if (!prefix.empty()) {
1392 Subfiles::const_iterator fi;
1394 if (fi == _subfiles.
end()) {
1401 Subfile *subfile = (*fi);
1402 return (subfile->_name.length() > prefix.length() &&
1403 subfile->_name.substr(0, prefix.length()) == prefix);
1418 string prefix = subfile_name;
1419 if (!prefix.empty()) {
1424 Subfiles::const_iterator fi;
1427 string previous =
"";
1428 while (fi != _subfiles.
end()) {
1429 Subfile *subfile = (*fi);
1430 if (!(subfile->_name.length() > prefix.length() &&
1431 subfile->_name.substr(0, prefix.length()) == prefix)) {
1437 size_t slash = subfile->_name.find(
'/', prefix.length());
1438 string basename = subfile->_name.substr(prefix.length(), slash - prefix.length());
1439 if (basename != previous) {
1440 contents.push_back(basename);
1441 previous = basename;
1461 nassertv(index >= 0 && index < (
int)_subfiles.
size());
1462 Subfile *subfile = _subfiles[index];
1463 subfile->_flags |= SF_deleted;
1464 _removed_subfiles.push_back(subfile);
1465 _subfiles.erase(_subfiles.
begin() + index);
1467 _timestamp = time(
nullptr);
1468 _timestamp_dirty =
true;
1470 _needs_repack =
true;
1479 static string empty_string;
1480 nassertr(index >= 0 && index < (
int)_subfiles.
size(), empty_string);
1482 return _subfiles[index]->_name;
1492 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1493 return _subfiles[index]->_uncompressed_length;
1504 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1508 return _subfiles[index]->_timestamp;
1518 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1519 return (_subfiles[index]->_flags & SF_compressed) != 0;
1528 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1529 return (_subfiles[index]->_flags & SF_encrypted) != 0;
1541 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1542 return (_subfiles[index]->_flags & SF_text) != 0;
1556 return normalize_streampos(_next_index + (streampos)4);
1568 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1569 return _subfiles[index]->_data_start;
1581 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1582 return _subfiles[index]->_data_length;
1605 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
nullptr);
1606 Subfile *subfile = _subfiles[index];
1608 if (subfile->_source !=
nullptr ||
1609 !subfile->_source_filename.empty()) {
1615 nassertr(subfile == _subfiles[index],
nullptr);
1629 if (stream !=
nullptr) {
1634 #if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 1636 (*global_operator_delete)(stream);
1649 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1652 if (multifile_always_binary) {
1656 if (!fname.is_binary_or_text()) {
1659 if ((_subfiles[index]->_flags & SF_text) != 0) {
1667 if (!fname.open_write(out,
true)) {
1669 <<
"Unable to write to file " << filename <<
"\n";
1682 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1685 if (in ==
nullptr) {
1689 static const size_t buffer_size = 4096;
1690 char buffer[buffer_size];
1692 in->read(buffer, buffer_size);
1693 size_t count = in->gcount();
1694 while (count != 0) {
1695 out.write(buffer, count);
1696 in->read(buffer, buffer_size);
1697 count = in->gcount();
1700 bool failed = (in->fail() && !in->eof());
1702 nassertr(!failed,
false);
1704 return (!out.fail());
1720 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1722 if (!filename.
exists()) {
1724 <<
"File is missing: " << filename <<
"\n";
1731 if ((_subfiles[index]->_flags & SF_text) != 0) {
1732 if (express_cat.is_debug()) {
1734 <<
"File is not binary: " << filename <<
"\n";
1741 if ((_subfiles[index]->_flags & SF_text) == 0) {
1742 if (express_cat.is_debug()) {
1744 <<
"File is not text: " << filename <<
"\n";
1752 if ((_subfiles[index]->_flags & SF_text) != 0) {
1760 if (in1 ==
nullptr) {
1768 <<
"Cannot read " << filename <<
"\n";
1774 in2.seekg(0, ios::end);
1775 streampos file_size = in2.tellg();
1786 int byte1 = in1->get();
1787 int byte2 = in2.get();
1788 while (!in1->fail() && !in1->eof() &&
1789 !in2.fail() && !in2.eof()) {
1790 if (byte1 != byte2) {
1798 bool failed = (in1->fail() && !in1->eof()) || (in2.fail() && !in2.eof());
1801 nassertr(!failed,
false);
1810 output(ostream &out)
const {
1811 out <<
"Multifile " << _multifile_name <<
", " << get_num_subfiles()
1821 for (
int i = 0; i < num_subfiles; i++) {
1823 out << subfile_name <<
"\n";
1842 string new_header_prefix = header_prefix;
1844 if (!new_header_prefix.empty()) {
1846 if (new_header_prefix[0] !=
'#') {
1847 new_header_prefix = string(
"#") + new_header_prefix;
1851 if (new_header_prefix[new_header_prefix.size() - 1] !=
'\n') {
1852 new_header_prefix += string(
"\n");
1856 size_t newline = new_header_prefix.find(
'\n');
1857 while (newline < new_header_prefix.size() - 1) {
1858 if (new_header_prefix[newline + 1] !=
'#') {
1859 new_header_prefix = new_header_prefix.substr(0, newline + 1) + string(
"#") + new_header_prefix.substr(newline + 1);
1861 newline = new_header_prefix.find(
'#', newline);
1865 if (_header_prefix != new_header_prefix) {
1866 _header_prefix = new_header_prefix;
1867 _needs_repack =
true;
1888 result.append((
const char *)&pv[0], pv.size());
1900 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1905 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1906 Subfile *subfile = _subfiles[index];
1908 if (subfile->_source !=
nullptr ||
1909 !subfile->_source_filename.empty()) {
1915 nassertr(subfile == _subfiles[index],
false);
1918 result.reserve(subfile->_uncompressed_length);
1920 bool success =
true;
1921 if (subfile->_flags & (SF_encrypted | SF_compressed)) {
1925 if (in ==
nullptr) {
1935 static const size_t buffer_size = 4096;
1936 char buffer[buffer_size];
1938 streamsize pos = _offset + subfile->_data_start;
1939 size_t max_bytes = subfile->_data_length;
1940 streamsize count = 0;
1943 streamsize num_bytes = (streamsize)min(buffer_size, max_bytes);
1944 _read->
seek_read(pos, buffer, num_bytes, count, eof);
1945 while (count != 0) {
1946 thread_consider_yield();
1947 nassertr(count <= (streamsize)max_bytes,
false);
1948 result.insert(result.end(), buffer, buffer + (size_t)count);
1949 max_bytes -= (size_t)count;
1952 num_bytes = (streamsize)min(buffer_size, max_bytes);
1953 _read->
seek_read(pos, buffer, num_bytes, count, eof);
1960 ostringstream message;
1963 nassert_raise(message.str());
1975 streampos Multifile::
1976 pad_to_streampos(streampos fpos) {
1977 nassertr(_write !=
nullptr, fpos);
1978 nassertr(_write->tellp() == fpos, fpos);
1979 streampos new_fpos = normalize_streampos(fpos);
1980 while (fpos < new_fpos) {
1984 nassertr(_write->tellp() == fpos, fpos);
1992 add_new_subfile(Subfile *subfile,
int compression_level) {
1993 if (compression_level != 0) {
1995 express_cat.warning()
1996 <<
"zlib not compiled in; cannot generated compressed multifiles.\n";
1997 compression_level = 0;
1999 subfile->_flags |= SF_compressed;
2000 subfile->_compression_level = compression_level;
2005 if (_encryption_flag) {
2006 subfile->_flags |= SF_encrypted;
2008 #endif // HAVE_OPENSSL 2010 if (_next_index != (streampos)0) {
2013 _needs_repack =
true;
2016 std::pair<Subfiles::iterator, bool> insert_result = _subfiles.insert(subfile);
2017 if (!insert_result.second) {
2020 Subfile *old_subfile = (*insert_result.first);
2021 old_subfile->_flags |= SF_deleted;
2025 PendingSubfiles::iterator ni = find(_new_subfiles.begin(), _new_subfiles.end(), old_subfile);
2026 if (ni != _new_subfiles.end()) {
2027 _new_subfiles.erase(ni);
2032 _removed_subfiles.push_back(old_subfile);
2035 (*insert_result.first) = subfile;
2038 _new_subfiles.push_back(subfile);
2048 nassertr(subfile->_source ==
nullptr &&
2049 subfile->_source_filename.empty(),
nullptr);
2053 nassertr(subfile->_data_start != (streampos)0,
nullptr);
2055 new ISubStream(_read, _offset + subfile->_data_start,
2056 _offset + subfile->_data_start + (streampos)subfile->_data_length);
2058 if ((subfile->_flags & SF_encrypted) != 0) {
2059 #ifndef HAVE_OPENSSL 2061 <<
"OpenSSL not compiled in; cannot read encrypted multifiles.\n";
2064 #else // HAVE_OPENSSL 2067 IDecryptStream *wrapper =
2068 new IDecryptStream(stream,
true, _encryption_password);
2072 char this_header[_encrypt_header_size];
2073 stream->read(this_header, _encrypt_header_size);
2074 if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size ||
2075 memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) {
2077 <<
"Unable to decrypt subfile " << subfile->_name <<
".\n";
2081 #endif // HAVE_OPENSSL 2084 if ((subfile->_flags & SF_compressed) != 0) {
2087 <<
"zlib not compiled in; cannot read compressed multifiles.\n";
2093 IDecompressStream *wrapper =
new IDecompressStream(stream,
true);
2098 if (stream->fail()) {
2111 standardize_subfile_name(
const string &subfile_name)
const {
2114 if (name.empty() || name ==
"/") {
2119 if (name[0] ==
'/') {
2121 }
else if (name.length() > 2 && name[0] ==
'.' && name[1] ==
'/') {
2134 PendingSubfiles::iterator pi;
2135 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
2136 Subfile *subfile = (*pi);
2137 subfile->rewrite_index_flags(*_write);
2140 _removed_subfiles.clear();
2144 _new_subfiles.clear();
2147 for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
2148 Subfile *subfile = (*pi);
2151 _cert_special.clear();
2153 _signatures.clear();
2154 #endif // HAVE_OPENSSL 2156 Subfiles::iterator fi;
2157 for (fi = _subfiles.
begin(); fi != _subfiles.
end(); ++fi) {
2158 Subfile *subfile = (*fi);
2170 nassertr(_read !=
nullptr,
false);
2176 char this_header[_header_size];
2177 read->seekg(_offset);
2184 _header_prefix = string();
2185 int ch = read->get();
2188 while (ch != EOF && ch ==
'#') {
2190 while (ch != EOF && ch !=
'\n') {
2191 _header_prefix += ch;
2195 while (ch != EOF && (isspace(ch) || ch ==
'\r')) {
2196 _header_prefix += ch;
2203 this_header[0] = ch;
2204 read->read(this_header + 1, _header_size - 1);
2205 if (read->fail() || read->gcount() != (unsigned)(_header_size - 1)) {
2207 <<
"Unable to read Multifile header " << _multifile_name <<
".\n";
2213 if (memcmp(this_header, _header, _header_size) != 0) {
2215 << _multifile_name <<
" is not a Multifile.\n";
2223 _file_major_ver = reader.get_int16();
2224 _file_minor_ver = reader.get_int16();
2225 _scale_factor = reader.get_uint32();
2226 _new_scale_factor = _scale_factor;
2228 if (read->eof() || read->fail()) {
2230 << _multifile_name <<
" header is truncated.\n";
2236 if (_file_major_ver != _current_major_ver ||
2237 (_file_major_ver == _current_major_ver &&
2238 _file_minor_ver > _current_minor_ver)) {
2240 << _multifile_name <<
" has version " << _file_major_ver <<
"." 2241 << _file_minor_ver <<
", expecting version " 2242 << _current_major_ver <<
"." << _current_minor_ver <<
".\n";
2248 _record_timestamp =
true;
2249 if (_file_minor_ver >= 1) {
2250 time_t read_timestamp = reader.get_uint32();
2251 if (read_timestamp == 0) {
2254 _record_timestamp =
false;
2256 _timestamp = read_timestamp;
2258 _timestamp_dirty =
false;
2262 streampos curr_pos = read->tellg() - _offset;
2263 _next_index = normalize_streampos(curr_pos);
2264 if (_next_index > curr_pos) {
2265 read->ignore(_next_index - curr_pos);
2268 _last_data_byte = 0;
2269 streampos index_forward;
2270 streamoff bytes_skipped = 0;
2271 bool read_cert_special =
false;
2273 Subfile *subfile =
new Subfile;
2274 index_forward = subfile->read_index(*read, _next_index,
this);
2275 while (index_forward != (streampos)0) {
2276 _last_index = _next_index;
2277 if (subfile->is_deleted()) {
2279 _needs_repack =
true;
2281 }
else if (subfile->is_cert_special()) {
2283 _cert_special.push_back(subfile);
2284 read_cert_special =
true;
2288 if (!subfile->is_cert_special()) {
2289 if (bytes_skipped != 0) {
2292 _needs_repack =
true;
2294 if (read_cert_special) {
2297 _needs_repack =
true;
2299 _last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
2301 streampos curr_pos = read->tellg() - _offset;
2302 bytes_skipped = index_forward - normalize_streampos(curr_pos);
2303 _next_index = index_forward;
2304 if (_next_index > curr_pos) {
2305 read->ignore(_next_index - curr_pos);
2307 subfile =
new Subfile;
2308 index_forward = subfile->read_index(*read, _next_index,
this);
2310 if (subfile->is_index_invalid()) {
2312 <<
"Error reading index for " << _multifile_name <<
".\n";
2320 for (
size_t si = 1; si < _subfiles.
size() && !_needs_repack; ++si) {
2321 if (*_subfiles[si] < *_subfiles[si - 1]) {
2322 _needs_repack =
true;
2326 if (_needs_repack) {
2328 size_t before_size = _subfiles.
size();
2330 size_t after_size = _subfiles.
size();
2334 nassertr(before_size == after_size,
true);
2347 _file_major_ver = _current_major_ver;
2348 _file_minor_ver = _current_minor_ver;
2350 nassertr(_write !=
nullptr,
false);
2351 nassertr(_write->tellp() == (streampos)0,
false);
2352 _write->write(_header_prefix.data(), _header_prefix.size());
2353 _write->write(_header, _header_size);
2355 writer.add_int16(_current_major_ver);
2356 writer.add_int16(_current_minor_ver);
2357 writer.add_uint32(_scale_factor);
2359 if (_record_timestamp) {
2360 writer.add_uint32(_timestamp);
2362 writer.add_uint32(0);
2363 _timestamp_dirty =
false;
2366 _next_index = _write->tellp();
2367 _next_index = pad_to_streampos(_next_index);
2370 if (_write->fail()) {
2372 <<
"Unable to write header for " << _multifile_name <<
".\n";
2389 check_signatures() {
2391 PendingSubfiles::iterator pi;
2393 for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
2394 Subfile *subfile = (*pi);
2395 nassertv((subfile->_flags & SF_signature) != 0);
2399 nassertv(stream !=
nullptr);
2401 size_t sig_size = reader.get_uint32();
2402 vector_uchar sig_data = reader.extract_bytes(sig_size);
2404 size_t num_certs = reader.get_uint32();
2407 vector_uchar buffer;
2415 EVP_PKEY *pkey =
nullptr;
2416 if (!buffer.empty()) {
2417 #if OPENSSL_VERSION_NUMBER >= 0x00908000L 2419 const unsigned char *bp, *bp_end;
2422 unsigned char *bp, *bp_end;
2424 bp = (
unsigned char *)&buffer[0];
2425 bp_end = bp + buffer.size();
2426 X509 *x509 = d2i_X509(
nullptr, &bp, bp_end - bp);
2427 while (num_certs > 0 && x509 !=
nullptr) {
2428 chain.push_back(CertRecord(x509));
2430 x509 = d2i_X509(
nullptr, &bp, bp_end - bp);
2432 if (num_certs != 0 || x509 !=
nullptr) {
2433 express_cat.warning()
2434 <<
"Extra data in signature record.\n";
2438 if (!chain.empty()) {
2439 pkey = X509_get_pubkey(chain[0]._cert);
2442 if (pkey !=
nullptr) {
2443 EVP_MD_CTX *md_ctx = EVP_MD_CTX_create();
2444 EVP_VerifyInit(md_ctx, EVP_sha1());
2446 nassertv(_read !=
nullptr);
2452 read->seekg(_offset);
2453 streampos bytes_remaining = _last_data_byte;
2454 static const size_t buffer_size = 4096;
2455 char buffer[buffer_size];
2456 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2457 size_t count = read->gcount();
2458 while (count != 0) {
2459 nassertv(count <= buffer_size);
2460 EVP_VerifyUpdate(md_ctx, buffer, count);
2461 bytes_remaining -= count;
2462 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2463 count = read->gcount();
2465 nassertv(bytes_remaining == (streampos)0);
2470 EVP_VerifyFinal(md_ctx, sig_data.data(), sig_data.size(), pkey);
2471 if (verify_result == 1) {
2473 _signatures.push_back(chain);
2476 _needs_repack =
true;
2480 #endif // HAVE_OPENSSL 2482 _cert_special.clear();
2491 streampos Multifile::Subfile::
2492 read_index(istream &read, streampos fpos,
Multifile *multifile) {
2493 nassertr(read.tellg() - multifile->_offset == fpos, fpos);
2499 streampos next_index = multifile->word_to_streampos(reader.get_uint32());
2500 if (read.eof() || read.fail()) {
2501 _flags |= SF_index_invalid;
2505 if (next_index == (streampos)0) {
2511 _index_start = fpos;
2514 _data_start = multifile->word_to_streampos(reader.get_uint32());
2515 _data_length = reader.get_uint32();
2516 _flags = reader.get_uint16();
2517 if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2518 _uncompressed_length = reader.get_uint32();
2520 _uncompressed_length = _data_length;
2522 if (multifile->_file_minor_ver < 1) {
2525 _timestamp = reader.get_uint32();
2526 if (_timestamp == 0) {
2531 size_t name_length = reader.get_uint16();
2532 if (read.eof() || read.fail()) {
2533 _flags |= SF_index_invalid;
2538 char *name_buffer = (
char *)PANDA_MALLOC_ARRAY(name_length);
2539 nassertr(name_buffer !=
nullptr, next_index);
2540 for (
size_t ni = 0; ni < name_length; ni++) {
2541 name_buffer[ni] = read.get() ^ 0xff;
2543 _name = string(name_buffer, name_length);
2544 PANDA_FREE_ARRAY(name_buffer);
2546 if (read.eof() || read.fail()) {
2547 _flags |= SF_index_invalid;
2551 _index_length = read.tellg() - fpos - multifile->_offset;
2563 streampos Multifile::Subfile::
2564 write_index(ostream &write, streampos fpos,
Multifile *multifile) {
2565 nassertr(write.tellp() - multifile->_offset == fpos, fpos);
2567 _index_start = fpos;
2573 dg.
add_uint32(multifile->streampos_to_word(_data_start));
2576 if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2586 string::iterator ni;
2587 for (ni = _name.begin(); ni != _name.end(); ++ni) {
2591 size_t this_index_size = 4 + dg.
get_length();
2594 streampos next_index = fpos + (streampos)this_index_size;
2597 idg.
add_uint32(multifile->streampos_to_word(next_index));
2599 write.write((
const char *)idg.get_data(), idg.get_length());
2602 _index_length = write.tellp() - fpos - multifile->_offset;
2620 streampos Multifile::Subfile::
2621 write_data(ostream &write, istream *read, streampos fpos,
2623 nassertr(write.tellp() - multifile->_offset == fpos, fpos);
2625 istream *source = _source;
2626 pifstream source_file;
2627 if (source ==
nullptr && !_source_filename.empty()) {
2629 if (!_source_filename.open_read(source_file)) {
2632 <<
"Unable to read " << _source_filename <<
".\n";
2633 _flags |= SF_data_invalid;
2635 _uncompressed_length = 0;
2637 source = &source_file;
2641 if (source ==
nullptr) {
2644 if (read ==
nullptr) {
2647 <<
"No source for subfile " << _name <<
".\n";
2648 _flags |= SF_data_invalid;
2651 read->seekg(_data_start + multifile->_offset);
2652 for (
size_t p = 0; p < _data_length; p++) {
2653 int byte = read->get();
2654 if (read->eof() || read->fail()) {
2657 <<
"Unexpected EOF for subfile " << _name <<
".\n";
2658 _flags |= SF_data_invalid;
2666 ostream *putter = &write;
2667 bool delete_putter =
false;
2669 #ifndef HAVE_OPENSSL 2672 nassertr((_flags & SF_encrypted) == 0, fpos);
2674 #else // HAVE_OPENSSL 2675 if ((_flags & SF_encrypted) != 0) {
2677 OEncryptStream *encrypt =
new OEncryptStream;
2678 encrypt->set_iteration_count(multifile->_encryption_iteration_count);
2679 encrypt->open(putter, delete_putter, multifile->_encryption_password);
2682 delete_putter =
true;
2686 putter->write(_encrypt_header, _encrypt_header_size);
2688 #endif // HAVE_OPENSSL 2693 nassertr((_flags & SF_compressed) == 0, fpos);
2695 if ((_flags & SF_compressed) != 0) {
2697 putter =
new OCompressStream(putter, delete_putter, _compression_level);
2698 delete_putter =
true;
2702 streampos write_start = fpos;
2703 _uncompressed_length = 0;
2705 #ifndef HAVE_OPENSSL 2707 nassertr((_flags & SF_signature) == 0, fpos);
2709 #else // HAVE_OPENSSL 2710 if ((_flags & SF_signature) != 0) {
2717 nassertr(read !=
nullptr, fpos);
2720 nassertr(_pkey !=
nullptr, fpos);
2722 EVP_MD_CTX *md_ctx = EVP_MD_CTX_create();
2723 EVP_SignInit(md_ctx, EVP_sha1());
2727 nassertr(multifile->_last_data_byte < fpos, fpos);
2728 read->seekg(multifile->_offset);
2729 streampos bytes_remaining = multifile->_last_data_byte;
2730 static const size_t buffer_size = 4096;
2731 char buffer[buffer_size];
2732 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2733 size_t count = read->gcount();
2734 while (count != 0) {
2735 nassertr(count <= buffer_size, fpos);
2736 EVP_SignUpdate(md_ctx, buffer, count);
2737 bytes_remaining -= count;
2738 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2739 count = read->gcount();
2741 nassertr(bytes_remaining == (streampos)0, fpos);
2744 unsigned int max_size = EVP_PKEY_size(_pkey);
2745 unsigned char *sig_data =
new unsigned char[max_size];
2746 unsigned int sig_size;
2747 if (!EVP_SignFinal(md_ctx, sig_data, &sig_size, _pkey)) {
2748 OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
2749 sslw->notify_ssl_errors();
2751 nassertr(sig_size <= max_size, fpos);
2754 writer.add_uint32(sig_size);
2755 putter->write((
char *)sig_data, sig_size);
2756 _uncompressed_length += 4 + sig_size;
2760 EVP_MD_CTX_destroy(md_ctx);
2762 #endif // HAVE_OPENSSL 2765 static const size_t buffer_size = 4096;
2766 char buffer[buffer_size];
2768 source->read(buffer, buffer_size);
2769 size_t count = source->gcount();
2770 while (count != 0) {
2771 _uncompressed_length += count;
2772 putter->write(buffer, count);
2773 source->read(buffer, buffer_size);
2774 count = source->gcount();
2777 if (delete_putter) {
2781 streampos write_end = write.tellp() - multifile->_offset;
2782 _data_length = (size_t)(write_end - write_start);
2791 if (!_source_filename.empty()) {
2792 _timestamp = _source_filename.get_timestamp();
2794 if (_timestamp == 0) {
2795 _timestamp = time(
nullptr);
2800 source_file.close();
2802 return fpos + (streampos)_data_length;
2809 void Multifile::Subfile::
2810 rewrite_index_data_start(ostream &write,
Multifile *multifile) {
2811 nassertv(_index_start != (streampos)0);
2813 static const size_t data_start_offset = 4;
2814 size_t data_start_pos = _index_start + (streampos)data_start_offset;
2815 write.seekp(data_start_pos + multifile->_offset);
2816 nassertv(!write.fail());
2819 writer.add_uint32(multifile->streampos_to_word(_data_start));
2820 writer.add_uint32(_data_length);
2821 writer.add_uint16(_flags);
2822 if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2823 writer.add_uint32(_uncompressed_length);
2825 if (multifile->_record_timestamp) {
2826 writer.add_uint32(_timestamp);
2828 writer.add_uint32(0);
2836 void Multifile::Subfile::
2837 rewrite_index_flags(ostream &write) {
2840 if (_index_start != (streampos)0) {
2841 static const size_t flags_offset = 4 + 4 + 4;
2842 size_t flags_pos = _index_start + (streampos)flags_offset;
2843 write.seekp(flags_pos);
2844 nassertv(!write.fail());
2847 writer.add_uint16(_flags);
A StreamWriter object is used to write sequential binary data directly to an ostream.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
std::string get_dirname() const
Returns the directory part of the filename.
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.
time_t get_timestamp() const
Returns a time_t value that represents the time the file was last modified, to within whatever precis...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::istream * open_read_subfile(int index)
Returns an istream that may be used to read the indicated subfile.
bool open_write(const Filename &multifile_name)
Opens the named Multifile on disk for writing.
size_type_0 size() const
Returns the number of elements in the ordered vector.
void clear()
Removes all elements from the ordered vector.
time_t get_timestamp() const
Returns the modification timestamp of the overall Multifile.
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.
void set_binary()
Indicates that the filename represents a binary file.
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
void set_text()
Indicates that the filename represents a text file.
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
bool is_subfile_compressed(int index) const
Returns true if the indicated subfile has been compressed when stored within the archive, false otherwise.
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...
This class provides a locking wrapper around a combination ostream/istream pointer.
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
The abstract base class for a file or directory within the VirtualFileSystem.
bool is_write_valid() const
Returns true if the Multifile has been opened for write mode and there have been no errors...
bool is_subfile_encrypted(int index) const
Returns true if the indicated subfile has been encrypted when stored within the archive, false otherwise.
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash...
bool is_binary() const
Returns true if the Filename has been indicated to represent a binary file via a previous call to set...
static void close_read_subfile(std::istream *stream)
Closes a file opened by a previous call to open_read_subfile().
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
bool repack()
Forces a complete rewrite of the Multifile and all of its contents, so that its index will appear at ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void release()
Releases the internal lock.
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
void add_int8(int8_t value)
Adds a signed 8-bit integer to the datagram.
size_t get_subfile_length(int index) const
Returns the uncompressed data length of the nth subfile.
An istream object that presents a subwindow into another istream.
bool scan_directory(vector_string &contents, const std::string &subfile_name) const
Considers subfile_name to be the name of a subdirectory within the Multifile, but not a file itself; ...
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 has_directory(const std::string &subfile_name) const
Returns true if the indicated subfile name is the directory prefix to one or more files within the Mu...
The name of a file, such as a texture file or an Egg file.
get_subfile_name
Returns the name of the nth subfile.
std::string update_subfile(const std::string &subfile_name, const Filename &filename, int compression_level)
Adds a file on disk to the subfile.
This class provides a locking wrapper around an arbitrary istream pointer.
time_t get_subfile_timestamp(int index) const
Returns the modification time of the nth subfile.
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
void set_scale_factor(size_t scale_factor)
Changes the internal scale factor for this Multifile.
bool open_read_write(const Filename &multifile_name)
Opens the named Multifile on disk for reading and writing.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void sort()
Maps to sort_unique().
bool is_read_valid() const
Returns true if the Multifile has been opened for read mode and there have been no errors...
bool extract_subfile_to(int index, std::ostream &out)
Extracts the nth subfile to the indicated ostream.
bool flush()
Writes all contents of the Multifile to disk.
bool get_record_timestamp() const
Returns the flag indicating whether timestamps should be recorded within the Multifile or not...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
void ls(std::ostream &out=std::cout) const
Shows a list of all subfiles within the Multifile.
bool compare_subfile(int index, const Filename &filename)
Performs a byte-for-byte comparison of the indicated file on disk with the nth subfile.
bool extract_subfile(int index, const Filename &filename)
Extracts the nth subfile into a file with the given name.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
void remove_subfile(int index)
Removes the nth subfile from the Multifile.
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
get_num_subfiles
Returns the number of subfiles within the Multifile.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void acquire()
Acquires the internal lock.
A file that contains a set of files.
void set_header_prefix(const std::string &header_prefix)
Sets the string which is written to the Multifile before the Multifile header.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void close()
Closes the Multifile if it is open.
This is a convenience class to specialize ConfigVariable as an integer type.
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...
bool open_read_write(std::fstream &stream, bool truncate=false) const
Opens the indicated fstream for read/write access to the file, if possible.
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
static bool simple_read_file(std::istream *stream, vector_uchar &result)
Fills up the indicated pvector with the contents of the just-opened file.
get_istream
Returns the istream this object is wrapping.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the stream.
bool is_subfile_text(int index) const
Returns true if the indicated subfile represents text data, or false if it represents binary data...
A class to read sequential binary data directly from an istream.
void seek_read(std::streamsize pos, char *buffer, std::streamsize num_bytes, std::streamsize &read_bytes, bool &eof)
Atomically seeks to a particular offset from the beginning of the file, and reads a number of bytes f...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
std::string add_subfile(const std::string &subfile_name, const Filename &filename, int compression_level)
Adds a file on disk as a subfile to the Multifile.
vector_uchar read_subfile(int index)
Returns a vector_uchar that contains the entire contents of the indicated subfile.
std::streampos get_subfile_internal_start(int index) const
Returns the starting byte position within the Multifile at which the indicated subfile begins...
bool rename_to(const Filename &other) const
Renames the file to the indicated new filename.
bool is_binary_or_text() const
Returns true either is_binary() or is_text() is true; that is, that the filename has been specified a...
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
bool unref() const
Decrements the reference count.
size_t get_length() const
Returns the number of bytes in the datagram.
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.