15 #include "multifile.h" 17 #include "config_express.h" 18 #include "streamWriter.h" 19 #include "streamReader.h" 22 #include "encryptStream.h" 23 #include "virtualFileSystem.h" 24 #include "virtualFile.h" 32 const char Multifile::_header[] =
"pmf\0\n\r";
33 const size_t Multifile::_header_size = 6;
38 const int Multifile::_current_major_ver = 1;
40 const int Multifile::_current_minor_ver = 1;
50 const char Multifile::_encrypt_header[] =
"crypty";
51 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."));
119 _write = (ostream *)NULL;
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 _read_filew(_read_file),
162 _read_write_filew(_read_write_file)
200 istream *multifile_stream = vfile->open_read_file(
false);
201 if (multifile_stream == NULL) {
205 _timestamp = vfile->get_timestamp();
206 _timestamp_dirty =
true;
209 _multifile_name = multifile_name;
228 const streampos &offset) {
230 _timestamp = time(NULL);
231 _timestamp_dirty =
true;
232 _read = multifile_stream;
233 _owns_stream = owns_pointer;
259 _timestamp = time(NULL);
260 _timestamp_dirty =
true;
261 _write = &_write_file;
262 _multifile_name = multifile_name;
281 _timestamp = time(NULL);
282 _timestamp_dirty =
true;
283 _write = multifile_stream;
284 _owns_stream = owns_pointer;
285 _write->seekp(0, ios::beg);
307 bool exists = fname.
exists();
314 _timestamp = time(NULL);
316 _timestamp_dirty =
true;
317 _read = &_read_write_filew;
318 _write = &_read_write_file;
319 _multifile_name = multifile_name;
344 _timestamp = time(NULL);
345 _timestamp_dirty =
true;
352 _write = multifile_stream;
354 _write->seekp(0, ios::beg);
357 multifile_stream->seekg(0, ios::end);
358 if (multifile_stream->tellg() == (streampos)0) {
377 if (_new_scale_factor != _scale_factor) {
390 }
else if (_write != (ostream *)NULL) {
396 _write = (ostream *)NULL;
398 _owns_stream =
false;
401 _needs_repack =
false;
403 _timestamp_dirty =
false;
405 _new_scale_factor = 1;
406 _encryption_flag =
false;
412 _read_write_file.close();
444 nassertv(scale_factor != (
size_t)0);
446 if (_next_index == (streampos)0) {
449 _scale_factor = scale_factor;
458 _new_scale_factor = scale_factor;
481 int compression_level) {
485 if (multifile_always_binary) {
494 string name = standardize_subfile_name(subfile_name);
496 Subfile *subfile =
new Subfile;
497 subfile->_name = name;
498 subfile->_source_filename = fname;
500 subfile->_flags |= SF_text;
503 add_new_subfile(subfile, compression_level);
506 _timestamp = time(NULL);
507 _timestamp_dirty =
true;
535 int compression_level) {
538 string name = standardize_subfile_name(subfile_name);
540 Subfile *subfile =
new Subfile;
541 subfile->_name = name;
542 subfile->_source = subfile_data;
543 add_new_subfile(subfile, compression_level);
565 int compression_level) {
569 if (multifile_always_binary) {
578 string name = standardize_subfile_name(subfile_name);
591 Subfile *subfile =
new Subfile;
592 subfile->_name = name;
593 subfile->_source_filename = fname;
595 subfile->_flags |= SF_text;
598 add_new_subfile(subfile, compression_level);
601 _timestamp = time(NULL);
602 _timestamp_dirty =
true;
615 Multifile::CertRecord::
616 CertRecord(X509 *cert) :
626 Multifile::CertRecord::
627 CertRecord(
const Multifile::CertRecord ©) :
628 _cert(X509_dup(copy._cert))
637 Multifile::CertRecord::
647 void Multifile::CertRecord::
648 operator = (
const Multifile::CertRecord &other) {
650 _cert = X509_dup(other._cert);
652 #endif // HAVE_OPENSSL 690 const Filename &pkey,
const string &password) {
693 if (chain.empty() && pkey.empty()) {
697 return add_signature(certificate, password);
700 CertChain cert_chain;
704 string certificate_data;
705 if (!vfs->read_file(certificate, certificate_data,
true)) {
707 <<
"Could not read " << certificate <<
".\n";
712 BIO *certificate_mbio = BIO_new_mem_buf((
void *)certificate_data.data(), certificate_data.size());
713 X509 *x509 = PEM_read_bio_X509(certificate_mbio, NULL, NULL, (
void *)
"");
714 BIO_free(certificate_mbio);
717 <<
"Could not read certificate in " << certificate <<
".\n";
723 cert_chain.push_back(CertRecord(x509));
726 if (!chain.empty()) {
728 if (!vfs->read_file(chain, chain_data,
true)) {
730 <<
"Could not read " << chain <<
".\n";
734 BIO *chain_mbio = BIO_new_mem_buf((
void *)chain_data.data(), chain_data.size());
735 X509 *c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (
void *)
"");
737 cert_chain.push_back(c);
738 c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (
void *)
"");
740 BIO_free(chain_mbio);
742 if (cert_chain.size() == 1) {
744 <<
"Could not read certificate chain in " << chain <<
".\n";
752 if (!vfs->read_file(pkey, pkey_data,
true)) {
754 <<
"Could not read " << pkey <<
".\n";
758 BIO *pkey_mbio = BIO_new_mem_buf((
void *)pkey_data.data(), pkey_data.size());
759 EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
760 (
void *)password.c_str());
762 if (evp_pkey == NULL) {
764 <<
"Could not read private key in " << pkey <<
".\n";
768 bool result = add_signature(cert_chain, evp_pkey);
770 EVP_PKEY_free(evp_pkey);
774 #endif // HAVE_OPENSSL 797 add_signature(
const Filename &composite,
const string &password) {
801 string composite_data;
802 if (!vfs->read_file(composite, composite_data,
true)) {
804 <<
"Could not read " << composite <<
".\n";
809 BIO *pkey_mbio = BIO_new_mem_buf((
void *)composite_data.data(), composite_data.size());
810 EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
811 (
void *)password.c_str());
813 if (evp_pkey == NULL) {
815 <<
"Could not read private key in " << composite <<
".\n";
820 CertChain cert_chain;
822 BIO *chain_mbio = BIO_new_mem_buf((
void *)composite_data.data(), composite_data.size());
823 X509 *c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (
void *)
"");
825 cert_chain.push_back(c);
826 c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (
void *)
"");
828 BIO_free(chain_mbio);
830 if (cert_chain.empty()) {
832 <<
"Could not read certificates in " << composite <<
".\n";
839 bool found_match =
false;
840 for (i = 0; i < cert_chain.size(); ++i) {
841 X509 *c = cert_chain[i]._cert;
842 if (X509_check_private_key(c, evp_pkey)) {
846 cert_chain.insert(cert_chain.begin(), cert_chain[i]);
847 cert_chain.erase(cert_chain.begin() + i + 1);
855 <<
"No certificates in " << composite <<
" match key.\n";
859 bool result = add_signature(cert_chain, evp_pkey);
861 EVP_PKEY_free(evp_pkey);
865 #endif // HAVE_OPENSSL 895 add_signature(X509 *certificate, STACK_OF(X509) *chain, EVP_PKEY *pkey) {
898 CertChain cert_chain;
899 cert_chain.push_back(CertRecord(certificate));
901 int num = sk_X509_num(chain);
902 for (
int i = 0; i < num; ++i) {
903 cert_chain.push_back(CertRecord((X509 *)sk_X509_value(chain, i)));
907 return add_signature(cert_chain, pkey);
909 #endif // HAVE_OPENSSL 940 add_signature(
const Multifile::CertChain &cert_chain, EVP_PKEY *pkey) {
951 if (cert_chain.empty()) {
953 <<
"No certificate given.\n";
959 <<
"No private key given.\n";
963 if (!X509_check_private_key(cert_chain[0]._cert, pkey)) {
965 <<
"Private key does not match certificate.\n";
974 CertChain::const_iterator ci;
975 for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
976 X509 *cert = (*ci)._cert;
978 int der_len = i2d_X509(cert, NULL);
979 unsigned char *der_buf =
new unsigned char[der_len];
980 unsigned char *p = der_buf;
988 Subfile *subfile =
new Subfile;
989 subfile->_pkey = pkey;
990 subfile->_flags |= SF_signature;
991 subfile->_source = &der_stream;
996 nassertr(_new_subfiles.empty(),
false);
997 _new_subfiles.push_back(subfile);
998 bool result =
flush();
1004 #endif // HAVE_OPENSSL 1024 get_num_signatures()
const {
1025 ((
Multifile *)
this)->check_signatures();
1026 return _signatures.size();
1028 #endif // HAVE_OPENSSL 1037 const Multifile::CertChain &Multifile::
1038 get_signature(
int n)
const {
1039 ((
Multifile *)
this)->check_signatures();
1040 static CertChain error_chain;
1041 nassertr(n >= 0 && n < (
int)_signatures.size(), error_chain);
1042 return _signatures[n];
1044 #endif // HAVE_OPENSSL 1059 get_signature_subject_name(
int n)
const {
1060 const CertChain &cert_chain = get_signature(n);
1061 nassertr(!cert_chain.empty(), string());
1063 X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
1064 if (xname != NULL) {
1068 BIO *mbio = BIO_new(BIO_s_mem());
1069 X509_NAME_print_ex(mbio, xname, 0, XN_FLAG_RFC2253);
1072 long pp_size = BIO_get_mem_data(mbio, &pp);
1073 string name(pp, pp_size);
1080 #endif // HAVE_OPENSSL 1095 get_signature_friendly_name(
int n)
const {
1096 const CertChain &cert_chain = get_signature(n);
1097 nassertr(!cert_chain.empty(), string());
1099 static const int nid_choices[] = {
1100 NID_pkcs9_emailAddress,
1101 NID_subject_alt_name,
1107 for (
int ni = 0; nid_choices[ni] != -1; ++ni) {
1108 int nid = nid_choices[ni];
1111 X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
1112 if (xname != NULL) {
1113 int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
1117 X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
1118 if (xentry != NULL) {
1119 ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
1124 BIO *mbio = BIO_new(BIO_s_mem());
1125 ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
1128 long pp_size = BIO_get_mem_data(mbio, &pp);
1129 string name(pp, pp_size);
1140 #endif // HAVE_OPENSSL 1156 get_signature_public_key(
int n)
const {
1157 const CertChain &cert_chain = get_signature(n);
1158 nassertr(!cert_chain.empty(), string());
1160 EVP_PKEY *pkey = X509_get_pubkey(cert_chain[0]._cert);
1162 int key_len = i2d_PublicKey(pkey, NULL);
1163 unsigned char *key_buf =
new unsigned char[key_len];
1164 unsigned char *p = key_buf;
1165 i2d_PublicKey(pkey, &p);
1167 for (
int i = 0; i < key_len; ++i) {
1168 result += tohex(key_buf[i] >> 4);
1169 result += tohex(key_buf[i]);
1177 #endif // HAVE_OPENSSL 1188 print_signature_certificate(
int n, ostream &out)
const {
1189 const CertChain &cert_chain = get_signature(n);
1190 nassertv(!cert_chain.empty());
1192 BIO *mbio = BIO_new(BIO_s_mem());
1193 X509_print(mbio, cert_chain[0]._cert);
1196 long pp_size = BIO_get_mem_data(mbio, &pp);
1197 out.write(pp, pp_size);
1200 #endif // HAVE_OPENSSL 1211 write_signature_certificate(
int n, ostream &out)
const {
1212 const CertChain &cert_chain = get_signature(n);
1213 nassertv(!cert_chain.empty());
1215 BIO *mbio = BIO_new(BIO_s_mem());
1217 CertChain::const_iterator ci;
1218 for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
1219 X509 *c = (*ci)._cert;
1220 X509_print(mbio, c);
1221 PEM_write_bio_X509(mbio, c);
1225 long pp_size = BIO_get_mem_data(mbio, &pp);
1226 out.write(pp, pp_size);
1229 #endif // HAVE_OPENSSL 1243 validate_signature_certificate(
int n)
const {
1244 int verify_result = -1;
1246 const CertChain &chain = get_signature(n);
1247 nassertr(!chain.empty(),
false);
1249 OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
1253 X509 *x509 = chain[0]._cert;
1254 STACK_OF(X509) *stack = NULL;
1255 if (chain.size() > 1) {
1256 stack = sk_X509_new(NULL);
1257 for (
size_t n = 1; n < chain.size(); ++n) {
1258 sk_X509_push(stack, chain[n]._cert);
1263 X509_STORE_CTX *ctx = X509_STORE_CTX_new();
1264 X509_STORE_CTX_init(ctx, sslw->get_x509_store(), x509, stack);
1265 X509_STORE_CTX_set_cert(ctx, x509);
1267 if (X509_verify_cert(ctx)) {
1270 verify_result = X509_STORE_CTX_get_error(ctx);
1273 if (express_cat.is_debug()) {
1275 << get_signature_subject_name(n) <<
": validate " << verify_result
1279 sk_X509_free(stack);
1280 X509_STORE_CTX_cleanup(ctx);
1281 X509_STORE_CTX_free(ctx);
1283 return verify_result;
1285 #endif // HAVE_OPENSSL 1314 bool new_file = (_next_index == (streampos)0);
1318 if (!write_header()) {
1323 if (_file_minor_ver != _current_minor_ver) {
1330 nassertr(_write != (ostream *)NULL,
false);
1333 PendingSubfiles::iterator pi;
1334 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
1335 Subfile *subfile = (*pi);
1336 subfile->rewrite_index_flags(*_write);
1339 _removed_subfiles.clear();
1341 bool wrote_ok =
true;
1343 if (!_new_subfiles.empty() || new_file) {
1347 if (_last_index != (streampos)0) {
1348 _write->seekp(0, ios::end);
1349 if (_write->fail()) {
1351 <<
"Unable to seek Multifile " << _multifile_name <<
".\n";
1354 _next_index = _write->tellp();
1355 _next_index = pad_to_streampos(_next_index);
1359 _write->seekp(_last_index);
1361 writer.
add_uint32(streampos_to_word(_next_index));
1364 _write->seekp(_next_index);
1365 nassertr(_next_index == _write->tellp(),
false);
1369 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1370 Subfile *subfile = (*pi);
1371 _last_index = _next_index;
1372 _next_index = subfile->write_index(*_write, _next_index,
this);
1373 nassertr(_next_index == _write->tellp(),
false);
1374 _next_index = pad_to_streampos(_next_index);
1375 nassertr(_next_index == _write->tellp(),
false);
1381 writer.add_uint32(0);
1383 nassertr(_next_index == _write->tellp(),
false);
1384 _next_index = pad_to_streampos(_next_index);
1387 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1388 Subfile *subfile = (*pi);
1392 _next_index = subfile->write_data(*_write, _read->
get_istream(),
1397 _next_index = subfile->write_data(*_write, NULL, _next_index,
this);
1400 nassertr(_next_index == _write->tellp(),
false);
1401 _next_index = pad_to_streampos(_next_index);
1402 if (subfile->is_data_invalid()) {
1406 if (!subfile->is_cert_special()) {
1407 _last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
1409 nassertr(_next_index == _write->tellp(),
false);
1416 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1417 Subfile *subfile = (*pi);
1418 subfile->rewrite_index_data_start(*_write,
this);
1421 _new_subfiles.clear();
1425 if (_timestamp_dirty) {
1426 nassertr(!_write->fail(),
false);
1427 static const size_t timestamp_pos = _header_prefix.size() + _header_size + 2 + 2 + 4;
1428 _write->seekp(timestamp_pos);
1429 nassertr(!_write->fail(),
false);
1432 if (_record_timestamp) {
1433 writer.add_uint32(_timestamp);
1435 writer.add_uint32(0);
1437 _timestamp_dirty =
false;
1441 if (!wrote_ok || _write->fail()) {
1443 <<
"Unable to update Multifile " << _multifile_name <<
".\n";
1471 if (_next_index == (streampos)0) {
1474 _needs_repack =
false;
1479 nassertr(!_multifile_name.empty(),
false);
1483 if (dirname.empty()) {
1491 <<
"Unable to open temporary file " << temp_filename <<
"\n";
1497 PendingSubfiles::iterator pi;
1498 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
1499 Subfile *subfile = (*pi);
1502 _removed_subfiles.clear();
1503 _new_subfiles.clear();
1504 copy(_subfiles.
begin(), _subfiles.
end(), back_inserter(_new_subfiles));
1507 _last_data_byte = 0;
1508 _scale_factor = _new_scale_factor;
1520 Filename orig_name = _multifile_name;
1524 if (!temp_filename.
rename_to(orig_name)) {
1526 <<
"Unable to rename temporary file " << temp_filename <<
" to " 1527 << orig_name <<
".\n";
1533 <<
"Unable to read newly repacked " << _multifile_name
1550 return _subfiles.
size();
1563 find_subfile._name = standardize_subfile_name(subfile_name);
1564 Subfiles::const_iterator fi;
1565 fi = _subfiles.find(&find_subfile);
1566 if (fi == _subfiles.
end()) {
1570 return (fi - _subfiles.
begin());
1583 string prefix = subfile_name;
1584 if (!prefix.empty()) {
1588 find_subfile._name = prefix;
1589 Subfiles::const_iterator fi;
1590 fi = _subfiles.upper_bound(&find_subfile);
1591 if (fi == _subfiles.
end()) {
1598 Subfile *subfile = (*fi);
1599 return (subfile->_name.length() > prefix.length() &&
1600 subfile->_name.substr(0, prefix.length()) == prefix);
1620 string prefix = subfile_name;
1621 if (!prefix.empty()) {
1625 find_subfile._name = prefix;
1626 Subfiles::const_iterator fi;
1627 fi = _subfiles.upper_bound(&find_subfile);
1629 string previous =
"";
1630 while (fi != _subfiles.
end()) {
1631 Subfile *subfile = (*fi);
1632 if (!(subfile->_name.length() > prefix.length() &&
1633 subfile->_name.substr(0, prefix.length()) == prefix)) {
1639 size_t slash = subfile->_name.find(
'/', prefix.length());
1640 string basename = subfile->_name.substr(prefix.length(), slash - prefix.length());
1641 if (basename != previous) {
1642 contents.push_back(basename);
1643 previous = basename;
1668 nassertv(index >= 0 && index < (
int)_subfiles.
size());
1669 Subfile *subfile = _subfiles[index];
1670 subfile->_flags |= SF_deleted;
1671 _removed_subfiles.push_back(subfile);
1672 _subfiles.erase(_subfiles.
begin() + index);
1674 _timestamp = time(NULL);
1675 _timestamp_dirty =
true;
1677 _needs_repack =
true;
1688 static string empty_string;
1689 nassertr(index >= 0 && index < (
int)_subfiles.
size(), empty_string);
1691 return _subfiles[index]->_name;
1704 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1705 return _subfiles[index]->_uncompressed_length;
1720 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1724 return _subfiles[index]->_timestamp;
1737 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1738 return (_subfiles[index]->_flags & SF_compressed) != 0;
1750 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1751 return (_subfiles[index]->_flags & SF_encrypted) != 0;
1767 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1768 return (_subfiles[index]->_flags & SF_text) != 0;
1786 return normalize_streampos(_next_index + (streampos)4);
1802 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1803 return _subfiles[index]->_data_start;
1819 nassertr(index >= 0 && index < (
int)_subfiles.
size(), 0);
1820 return _subfiles[index]->_data_length;
1849 nassertr(index >= 0 && index < (
int)_subfiles.
size(), NULL);
1850 Subfile *subfile = _subfiles[index];
1852 if (subfile->_source != (istream *)NULL ||
1853 !subfile->_source_filename.empty()) {
1860 nassertr(subfile == _subfiles[index], NULL);
1877 if (stream != (istream *)NULL) {
1882 #if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 1884 (*global_operator_delete)(stream);
1900 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1903 if (multifile_always_binary) {
1907 if (!fname.is_binary_or_text()) {
1910 if ((_subfiles[index]->_flags & SF_text) != 0) {
1918 if (!fname.open_write(out,
true)) {
1920 <<
"Unable to write to file " << filename <<
"\n";
1935 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1938 if (in == (istream *)NULL) {
1942 static const size_t buffer_size = 4096;
1943 char buffer[buffer_size];
1945 in->read(buffer, buffer_size);
1946 size_t count = in->gcount();
1947 while (count != 0) {
1948 out.write(buffer, count);
1949 in->read(buffer, buffer_size);
1950 count = in->gcount();
1953 bool failed = (in->fail() && !in->eof());
1955 nassertr(!failed,
false);
1957 return (!out.fail());
1978 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
1980 if (!filename.
exists()) {
1982 <<
"File is missing: " << filename <<
"\n";
1990 if ((_subfiles[index]->_flags & SF_text) != 0) {
1991 if (express_cat.is_debug()) {
1993 <<
"File is not binary: " << filename <<
"\n";
2001 if ((_subfiles[index]->_flags & SF_text) == 0) {
2002 if (express_cat.is_debug()) {
2004 <<
"File is not text: " << filename <<
"\n";
2012 if ((_subfiles[index]->_flags & SF_text) != 0) {
2020 if (in1 == (istream *)NULL) {
2028 <<
"Cannot read " << filename <<
"\n";
2034 in2.seekg(0, ios::end);
2035 streampos file_size = in2.tellg();
2046 int byte1 = in1->get();
2047 int byte2 = in2.get();
2048 while (!in1->fail() && !in1->eof() &&
2049 !in2.fail() && !in2.eof()) {
2050 if (byte1 != byte2) {
2058 bool failed = (in1->fail() && !in1->eof()) || (in2.fail() && !in2.eof());
2061 nassertr(!failed,
false);
2072 output(ostream &out)
const {
2085 for (
int i = 0; i < num_subfiles; i++) {
2087 out << subfile_name <<
"\n";
2112 string new_header_prefix = header_prefix;
2114 if (!new_header_prefix.empty()) {
2116 if (new_header_prefix[0] !=
'#') {
2117 new_header_prefix = string(
"#") + new_header_prefix;
2121 if (new_header_prefix[new_header_prefix.size() - 1] !=
'\n') {
2122 new_header_prefix += string(
"\n");
2126 size_t newline = new_header_prefix.find(
'\n');
2127 while (newline < new_header_prefix.size() - 1) {
2128 if (new_header_prefix[newline + 1] !=
'#') {
2129 new_header_prefix = new_header_prefix.substr(0, newline + 1) + string(
"#") + new_header_prefix.substr(newline + 1);
2131 newline = new_header_prefix.find(
'#', newline);
2135 if (_header_prefix != new_header_prefix) {
2136 _header_prefix = new_header_prefix;
2137 _needs_repack =
true;
2161 result.append((
const char *)&pv[0], pv.size());
2176 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
2181 nassertr(index >= 0 && index < (
int)_subfiles.
size(),
false);
2182 Subfile *subfile = _subfiles[index];
2184 if (subfile->_source != (istream *)NULL ||
2185 !subfile->_source_filename.empty()) {
2192 nassertr(subfile == _subfiles[index],
false);
2195 result.reserve(subfile->_uncompressed_length);
2197 bool success =
true;
2198 if (subfile->_flags & (SF_encrypted | SF_compressed)) {
2202 if (in == (istream *)NULL) {
2213 static const size_t buffer_size = 4096;
2214 char buffer[buffer_size];
2216 streamsize pos = _offset + subfile->_data_start;
2217 size_t max_bytes = subfile->_data_length;
2218 streamsize count = 0;
2221 streamsize num_bytes = (streamsize)min(buffer_size, max_bytes);
2222 _read->
seek_read(pos, buffer, num_bytes, count, eof);
2223 while (count != 0) {
2224 thread_consider_yield();
2225 nassertr(count <= (streamsize)max_bytes,
false);
2226 result.insert(result.end(), buffer, buffer + (size_t)count);
2227 max_bytes -= (size_t)count;
2230 num_bytes = (streamsize)min(buffer_size, max_bytes);
2231 _read->
seek_read(pos, buffer, num_bytes, count, eof);
2241 nassert_raise(message.str());
2257 streampos Multifile::
2258 pad_to_streampos(streampos fpos) {
2259 nassertr(_write != (ostream *)NULL, fpos);
2260 nassertr(_write->tellp() == fpos, fpos);
2261 streampos new_fpos = normalize_streampos(fpos);
2262 while (fpos < new_fpos) {
2266 nassertr(_write->tellp() == fpos, fpos);
2277 add_new_subfile(Subfile *subfile,
int compression_level) {
2278 if (compression_level != 0) {
2280 express_cat.warning()
2281 <<
"zlib not compiled in; cannot generated compressed multifiles.\n";
2282 compression_level = 0;
2284 subfile->_flags |= SF_compressed;
2285 subfile->_compression_level = compression_level;
2290 if (_encryption_flag) {
2291 subfile->_flags |= SF_encrypted;
2293 #endif // HAVE_OPENSSL 2295 if (_next_index != (streampos)0) {
2298 _needs_repack =
true;
2301 pair<Subfiles::iterator, bool> insert_result = _subfiles.insert(subfile);
2302 if (!insert_result.second) {
2305 Subfile *old_subfile = (*insert_result.first);
2306 old_subfile->_flags |= SF_deleted;
2309 PendingSubfiles::iterator ni = find(_new_subfiles.begin(), _new_subfiles.end(), old_subfile);
2310 if (ni != _new_subfiles.end()) {
2311 _new_subfiles.erase(ni);
2315 _removed_subfiles.push_back(old_subfile);
2318 (*insert_result.first) = subfile;
2321 _new_subfiles.push_back(subfile);
2334 nassertr(subfile->_source == (istream *)NULL &&
2335 subfile->_source_filename.empty(), NULL);
2339 nassertr(subfile->_data_start != (streampos)0, NULL);
2341 new ISubStream(_read, _offset + subfile->_data_start,
2342 _offset + subfile->_data_start + (streampos)subfile->_data_length);
2344 if ((subfile->_flags & SF_encrypted) != 0) {
2345 #ifndef HAVE_OPENSSL 2347 <<
"OpenSSL not compiled in; cannot read encrypted multifiles.\n";
2350 #else // HAVE_OPENSSL 2353 IDecryptStream *wrapper =
2354 new IDecryptStream(stream,
true, _encryption_password);
2359 char this_header[_encrypt_header_size];
2360 stream->read(this_header, _encrypt_header_size);
2361 if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size ||
2362 memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) {
2364 <<
"Unable to decrypt subfile " << subfile->_name <<
".\n";
2368 #endif // HAVE_OPENSSL 2371 if ((subfile->_flags & SF_compressed) != 0) {
2374 <<
"zlib not compiled in; cannot read compressed multifiles.\n";
2380 IDecompressStream *wrapper =
new IDecompressStream(stream,
true);
2385 if (stream->fail()) {
2400 standardize_subfile_name(
const string &subfile_name)
const {
2403 if (name.empty() || name ==
"/") {
2408 if (name[0] ==
'/') {
2410 }
else if (name.length() > 2 && name[0] ==
'.' && name[1] ==
'/') {
2425 PendingSubfiles::iterator pi;
2426 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
2427 Subfile *subfile = (*pi);
2428 subfile->rewrite_index_flags(*_write);
2431 _removed_subfiles.clear();
2435 _new_subfiles.clear();
2438 for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
2439 Subfile *subfile = (*pi);
2442 _cert_special.clear();
2444 _signatures.clear();
2445 #endif // HAVE_OPENSSL 2447 Subfiles::iterator fi;
2448 for (fi = _subfiles.
begin(); fi != _subfiles.
end(); ++fi) {
2449 Subfile *subfile = (*fi);
2470 char this_header[_header_size];
2471 read->seekg(_offset);
2479 _header_prefix = string();
2480 int ch = read->get();
2483 while (ch != EOF && ch ==
'#') {
2485 while (ch != EOF && ch !=
'\n') {
2486 _header_prefix += ch;
2490 while (ch != EOF && (isspace(ch) || ch ==
'\r')) {
2491 _header_prefix += ch;
2498 this_header[0] = ch;
2499 read->read(this_header + 1, _header_size - 1);
2500 if (read->fail() || read->gcount() != (unsigned)(_header_size - 1)) {
2502 <<
"Unable to read Multifile header " << _multifile_name <<
".\n";
2508 if (memcmp(this_header, _header, _header_size) != 0) {
2510 << _multifile_name <<
" is not a Multifile.\n";
2521 _new_scale_factor = _scale_factor;
2523 if (read->eof() || read->fail()) {
2525 << _multifile_name <<
" header is truncated.\n";
2531 if (_file_major_ver != _current_major_ver ||
2532 (_file_major_ver == _current_major_ver &&
2533 _file_minor_ver > _current_minor_ver)) {
2535 << _multifile_name <<
" has version " << _file_major_ver <<
"." 2536 << _file_minor_ver <<
", expecting version " 2537 << _current_major_ver <<
"." << _current_minor_ver <<
".\n";
2543 _record_timestamp =
true;
2544 if (_file_minor_ver >= 1) {
2546 if (read_timestamp == 0) {
2549 _record_timestamp =
false;
2551 _timestamp = read_timestamp;
2553 _timestamp_dirty =
false;
2557 _next_index = read->tellg() - _offset;
2558 _next_index = normalize_streampos(_next_index);
2559 read->seekg(_next_index + _offset);
2561 _last_data_byte = 0;
2562 streampos index_forward;
2563 streamoff bytes_skipped = 0;
2564 bool read_cert_special =
false;
2566 Subfile *subfile =
new Subfile;
2567 index_forward = subfile->read_index(*read, _next_index,
this);
2568 while (index_forward != (streampos)0) {
2569 _last_index = _next_index;
2570 if (subfile->is_deleted()) {
2572 _needs_repack =
true;
2574 }
else if (subfile->is_cert_special()) {
2577 _cert_special.push_back(subfile);
2578 read_cert_special =
true;
2582 if (!subfile->is_cert_special()) {
2583 if (bytes_skipped != 0) {
2587 _needs_repack =
true;
2589 if (read_cert_special) {
2593 _needs_repack =
true;
2595 _last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
2597 streampos curr_pos = normalize_streampos(read->tellg() - _offset);
2598 bytes_skipped = index_forward - curr_pos;
2599 read->seekg(index_forward + _offset);
2600 _next_index = index_forward;
2601 subfile =
new Subfile;
2602 index_forward = subfile->read_index(*read, _next_index,
this);
2604 if (subfile->is_index_invalid()) {
2606 <<
"Error reading index for " << _multifile_name <<
".\n";
2615 for (
size_t si = 1; si < _subfiles.
size() && !_needs_repack; ++si) {
2616 if (*_subfiles[si] < *_subfiles[si - 1]) {
2617 _needs_repack =
true;
2621 if (_needs_repack) {
2623 size_t before_size = _subfiles.
size();
2625 size_t after_size = _subfiles.
size();
2629 nassertr(before_size == after_size,
true);
2645 _file_major_ver = _current_major_ver;
2646 _file_minor_ver = _current_minor_ver;
2648 nassertr(_write != (ostream *)NULL,
false);
2649 nassertr(_write->tellp() == (streampos)0,
false);
2650 _write->write(_header_prefix.data(), _header_prefix.size());
2651 _write->write(_header, _header_size);
2657 if (_record_timestamp) {
2661 _timestamp_dirty =
false;
2664 _next_index = _write->tellp();
2665 _next_index = pad_to_streampos(_next_index);
2668 if (_write->fail()) {
2670 <<
"Unable to write header for " << _multifile_name <<
".\n";
2691 check_signatures() {
2693 PendingSubfiles::iterator pi;
2695 for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
2696 Subfile *subfile = (*pi);
2697 nassertv((subfile->_flags & SF_signature) != 0);
2701 nassertv(stream != NULL);
2717 EVP_PKEY *pkey = NULL;
2718 if (!buffer.empty()) {
2719 #if OPENSSL_VERSION_NUMBER >= 0x00908000L 2721 const unsigned char *bp, *bp_end;
2724 unsigned char *bp, *bp_end;
2726 bp = (
unsigned char *)&buffer[0];
2727 bp_end = bp + buffer.size();
2728 X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
2729 while (num_certs > 0 && x509 != NULL) {
2730 chain.push_back(CertRecord(x509));
2732 x509 = d2i_X509(NULL, &bp, bp_end - bp);
2734 if (num_certs != 0 || x509 != NULL) {
2735 express_cat.warning()
2736 <<
"Extra data in signature record.\n";
2740 if (!chain.empty()) {
2741 pkey = X509_get_pubkey(chain[0]._cert);
2747 md_ctx = EVP_MD_CTX_create();
2749 md_ctx =
new EVP_MD_CTX;
2751 EVP_VerifyInit(md_ctx, EVP_sha1());
2753 nassertv(_read != NULL);
2759 read->seekg(_offset);
2760 streampos bytes_remaining = _last_data_byte;
2761 static const size_t buffer_size = 4096;
2762 char buffer[buffer_size];
2763 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2764 size_t count = read->gcount();
2765 while (count != 0) {
2766 nassertv(count <= buffer_size);
2767 EVP_VerifyUpdate(md_ctx, buffer, count);
2768 bytes_remaining -= count;
2769 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2770 count = read->gcount();
2772 nassertv(bytes_remaining == (streampos)0);
2777 EVP_VerifyFinal(md_ctx,
2778 (
unsigned char *)sig_string.data(),
2779 sig_string.size(), pkey);
2780 if (verify_result == 1) {
2782 _signatures.push_back(chain);
2785 _needs_repack =
true;
2789 #endif // HAVE_OPENSSL 2791 _cert_special.clear();
2803 streampos Multifile::Subfile::
2804 read_index(istream &read, streampos fpos,
Multifile *multifile) {
2805 nassertr(read.tellg() - multifile->_offset == fpos, fpos);
2811 streampos next_index = multifile->word_to_streampos(reader.get_uint32());
2812 if (read.eof() || read.fail()) {
2813 _flags |= SF_index_invalid;
2817 if (next_index == (streampos)0) {
2823 _index_start = fpos;
2826 _data_start = multifile->word_to_streampos(reader.get_uint32());
2827 _data_length = reader.get_uint32();
2828 _flags = reader.get_uint16();
2829 if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2830 _uncompressed_length = reader.get_uint32();
2832 _uncompressed_length = _data_length;
2834 if (multifile->_file_minor_ver < 1) {
2837 _timestamp = reader.get_uint32();
2838 if (_timestamp == 0) {
2843 size_t name_length = reader.get_uint16();
2844 if (read.eof() || read.fail()) {
2845 _flags |= SF_index_invalid;
2850 char *name_buffer = (
char *)PANDA_MALLOC_ARRAY(name_length);
2851 nassertr(name_buffer != (
char *)NULL, next_index);
2852 for (
size_t ni = 0; ni < name_length; ni++) {
2853 name_buffer[ni] = read.get() ^ 0xff;
2855 _name = string(name_buffer, name_length);
2856 PANDA_FREE_ARRAY(name_buffer);
2858 if (read.eof() || read.fail()) {
2859 _flags |= SF_index_invalid;
2863 _index_length = read.tellg() - fpos - multifile->_offset;
2879 streampos Multifile::Subfile::
2880 write_index(ostream &write, streampos fpos,
Multifile *multifile) {
2881 nassertr(write.tellp() - multifile->_offset == fpos, fpos);
2883 _index_start = fpos;
2889 dg.
add_uint32(multifile->streampos_to_word(_data_start));
2892 if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2903 string::iterator ni;
2904 for (ni = _name.begin(); ni != _name.end(); ++ni) {
2908 size_t this_index_size = 4 + dg.
get_length();
2911 streampos next_index = fpos + (streampos)this_index_size;
2914 idg.
add_uint32(multifile->streampos_to_word(next_index));
2916 write.write((
const char *)idg.get_data(), idg.get_length());
2919 _index_length = write.tellp() - fpos - multifile->_offset;
2943 streampos Multifile::Subfile::
2944 write_data(ostream &write, istream *read, streampos fpos,
2946 nassertr(write.tellp() - multifile->_offset == fpos, fpos);
2948 istream *source = _source;
2949 pifstream source_file;
2950 if (source == (istream *)NULL && !_source_filename.empty()) {
2952 if (!_source_filename.open_read(source_file)) {
2955 <<
"Unable to read " << _source_filename <<
".\n";
2956 _flags |= SF_data_invalid;
2958 _uncompressed_length = 0;
2960 source = &source_file;
2964 if (source == (istream *)NULL) {
2967 if (read == (istream *)NULL) {
2970 <<
"No source for subfile " << _name <<
".\n";
2971 _flags |= SF_data_invalid;
2974 read->seekg(_data_start + multifile->_offset);
2975 for (
size_t p = 0; p < _data_length; p++) {
2976 int byte = read->get();
2977 if (read->eof() || read->fail()) {
2980 <<
"Unexpected EOF for subfile " << _name <<
".\n";
2981 _flags |= SF_data_invalid;
2990 ostream *putter = &write;
2991 bool delete_putter =
false;
2993 #ifndef HAVE_OPENSSL 2996 nassertr((_flags & SF_encrypted) == 0, fpos);
2998 #else // HAVE_OPENSSL 2999 if ((_flags & SF_encrypted) != 0) {
3001 OEncryptStream *encrypt =
new OEncryptStream;
3002 encrypt->set_iteration_count(multifile->_encryption_iteration_count);
3003 encrypt->open(putter, delete_putter, multifile->_encryption_password);
3006 delete_putter =
true;
3011 putter->write(_encrypt_header, _encrypt_header_size);
3013 #endif // HAVE_OPENSSL 3018 nassertr((_flags & SF_compressed) == 0, fpos);
3020 if ((_flags & SF_compressed) != 0) {
3022 putter =
new OCompressStream(putter, delete_putter, _compression_level);
3023 delete_putter =
true;
3027 streampos write_start = fpos;
3028 _uncompressed_length = 0;
3030 #ifndef HAVE_OPENSSL 3032 nassertr((_flags & SF_signature) == 0, fpos);
3034 #else // HAVE_OPENSSL 3035 if ((_flags & SF_signature) != 0) {
3042 nassertr(read != NULL, fpos);
3045 nassertr(_pkey != NULL, fpos);
3049 md_ctx = EVP_MD_CTX_create();
3051 md_ctx =
new EVP_MD_CTX;
3053 EVP_SignInit(md_ctx, EVP_sha1());
3057 nassertr(multifile->_last_data_byte < fpos, fpos);
3058 read->seekg(multifile->_offset);
3059 streampos bytes_remaining = multifile->_last_data_byte;
3060 static const size_t buffer_size = 4096;
3061 char buffer[buffer_size];
3062 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
3063 size_t count = read->gcount();
3064 while (count != 0) {
3065 nassertr(count <= buffer_size, fpos);
3066 EVP_SignUpdate(md_ctx, buffer, count);
3067 bytes_remaining -= count;
3068 read->read(buffer, min((streampos)buffer_size, bytes_remaining));
3069 count = read->gcount();
3071 nassertr(bytes_remaining == (streampos)0, fpos);
3074 unsigned int max_size = EVP_PKEY_size(_pkey);
3075 unsigned char *sig_data =
new unsigned char[max_size];
3076 unsigned int sig_size;
3077 if (!EVP_SignFinal(md_ctx, sig_data, &sig_size, _pkey)) {
3078 OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
3079 sslw->notify_ssl_errors();
3081 nassertr(sig_size <= max_size, fpos);
3085 putter->write((
char *)sig_data, sig_size);
3086 _uncompressed_length += 4 + sig_size;
3091 EVP_MD_CTX_destroy(md_ctx);
3096 #endif // HAVE_OPENSSL 3099 static const size_t buffer_size = 4096;
3100 char buffer[buffer_size];
3102 source->read(buffer, buffer_size);
3103 size_t count = source->gcount();
3104 while (count != 0) {
3105 _uncompressed_length += count;
3106 putter->write(buffer, count);
3107 source->read(buffer, buffer_size);
3108 count = source->gcount();
3111 if (delete_putter) {
3115 streampos write_end = write.tellp() - multifile->_offset;
3116 _data_length = (size_t)(write_end - write_start);
3126 if (!_source_filename.empty()) {
3127 _timestamp = _source_filename.get_timestamp();
3129 if (_timestamp == 0) {
3130 _timestamp = time(NULL);
3133 _source = (istream *)NULL;
3135 source_file.close();
3137 return fpos + (streampos)_data_length;
3147 void Multifile::Subfile::
3148 rewrite_index_data_start(ostream &write,
Multifile *multifile) {
3149 nassertv(_index_start != (streampos)0);
3151 static const size_t data_start_offset = 4;
3152 size_t data_start_pos = _index_start + (streampos)data_start_offset;
3153 write.seekp(data_start_pos + multifile->_offset);
3154 nassertv(!write.fail());
3157 writer.add_uint32(multifile->streampos_to_word(_data_start));
3158 writer.add_uint32(_data_length);
3159 writer.add_uint16(_flags);
3160 if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
3161 writer.add_uint32(_uncompressed_length);
3163 if (multifile->_record_timestamp) {
3164 writer.add_uint32(_timestamp);
3166 writer.add_uint32(0);
3177 void Multifile::Subfile::
3178 rewrite_index_flags(ostream &write) {
3181 if (_index_start != (streampos)0) {
3182 static const size_t flags_offset = 4 + 4 + 4;
3183 size_t flags_pos = _index_start + (streampos)flags_offset;
3184 write.seekp(flags_pos);
3185 nassertv(!write.fail());
3188 writer.add_uint16(_flags);
A StreamWriter object is used to write sequential binary data directly to an ostream.
string get_dirname() const
Returns the directory part of the filename.
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the streamWriter.
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
size_t get_subfile_internal_length(int index) const
Returns the number of bytes the indicated subfile consumes within the archive.
time_t get_timestamp() const
Returns a time_t value that represents the time the file was last modified, to within whatever precis...
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
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.
void add_int8(PN_int8 value)
Adds a signed 8-bit integer to the datagram.
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.
string extract_bytes(size_t size)
Extracts the indicated number of bytes in the stream and returns them as a string.
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.
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
void set_text()
Indicates that the filename represents a text file.
static void close_read_subfile(istream *stream)
Closes a file opened by a previous call to open_read_subfile().
bool is_subfile_compressed(int index) const
Returns true if the indicated subfile has been compressed when stored within the archive, false otherwise.
void set_header_prefix(const string &header_prefix)
Sets the string which is written to the Multifile before the Multifile header.
int find_subfile(const string &subfile_name) const
Returns the index of the subfile with the indicated name, or -1 if the named subfile is not within th...
string read_subfile(int index)
Returns a string that contains the entire contents of the indicated subfile.
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...
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
string update_subfile(const string &subfile_name, const Filename &filename, int compression_level)
Adds a file on disk to the subfile.
void seek_read(streamsize pos, char *buffer, streamsize num_bytes, streamsize &read_bytes, bool &eof)
Atomically seeks to a particular offset from the beginning of the file, and reads a number of bytes f...
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
bool repack()
Forces a complete rewrite of the Multifile and all of its contents, so that its index will appear at ...
void release()
Releases the internal lock.
bool scan_directory(vector_string &contents, const string &subfile_name) const
Considers subfile_name to be the name of a subdirectory within the Multifile, but not a file itself; ...
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.
void add_int16(PN_int16 value)
Adds a signed 16-bit integer to the stream.
The name of a file, such as a texture file or an Egg file.
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.
bool has_directory(const string &subfile_name) const
Returns true if the indicated subfile name is the directory prefix to one or more files within the Mu...
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 open_read_write(fstream &stream, bool truncate=false) const
Opens the indicated fstream for read/write access to the file, if possible.
string add_subfile(const string &subfile_name, const Filename &filename, int compression_level)
Adds a file on disk as a subfile to the Multifile.
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...
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the stream.
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
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.
istream * get_istream() const
Returns the istream this object is wrapping.
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 extract_subfile_to(int index, ostream &out)
Extracts the nth subfile to the indicated ostream.
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
void ls(ostream &out=cout) const
Shows a list of all subfiles within the Multifile.
bool open_read(const Filename &multifile_name, const streampos &offset=0)
Opens the named Multifile on disk for reading.
PN_int16 get_int16()
Extracts a signed 16-bit integer.
void acquire()
Acquires the internal lock.
A file that contains a set of files.
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
void close()
Closes the Multifile if it is open.
This is a convenience class to specialize ConfigVariable as an integer type.
static bool simple_read_file(istream *stream, pvector< unsigned char > &result)
Fills up the indicated pvector with the contents of the just-opened file.
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
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.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
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.
const string & get_subfile_name(int index) const
Returns the name of the nth subfile.
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.
size_t get_length() const
Returns the number of bytes in the datagram.
int get_num_subfiles() const
Returns the number of subfiles within the Multifile.
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.