00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "multifile.h"
00020
00021 #include "config_express.h"
00022 #include "streamWriter.h"
00023 #include "streamReader.h"
00024 #include "datagram.h"
00025 #include "zStream.h"
00026
00027 #include <algorithm>
00028
00029
00030
00031 const char Multifile::_header[] = "pmf\0\n\r";
00032 const size_t Multifile::_header_size = 6;
00033
00034
00035
00036
00037 const int Multifile::_current_major_ver = 1;
00038 const int Multifile::_current_minor_ver = 0;
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 Multifile::
00088 Multifile() {
00089 _read = (istream *)NULL;
00090 _write = (ostream *)NULL;
00091 _next_index = 0;
00092 _last_index = 0;
00093 _needs_repack = false;
00094 _scale_factor = 1;
00095 _new_scale_factor = 1;
00096 _file_major_ver = 0;
00097 _file_minor_ver = 0;
00098 }
00099
00100
00101
00102
00103
00104
00105 Multifile::
00106 ~Multifile() {
00107 close();
00108 }
00109
00110
00111
00112
00113
00114
00115 Multifile::
00116 Multifile(const Multifile ©) {
00117 nassertv(false);
00118 }
00119
00120
00121
00122
00123
00124
00125 void Multifile::
00126 operator = (const Multifile ©) {
00127 nassertv(false);
00128 }
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142 bool Multifile::
00143 open_read(const Filename &multifile_name) {
00144 close();
00145 Filename fname = multifile_name;
00146 fname.set_binary();
00147 if (!fname.open_read(_read_file)) {
00148 return false;
00149 }
00150 _read = &_read_file;
00151 _multifile_name = multifile_name;
00152 return read_index();
00153 }
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168 bool Multifile::
00169 open_write(const Filename &multifile_name) {
00170 close();
00171 Filename fname = multifile_name;
00172 fname.set_binary();
00173 if (!fname.open_write(_write_file, true)) {
00174 return false;
00175 }
00176 _write = &_write_file;
00177 _multifile_name = multifile_name;
00178 return true;
00179 }
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194 bool Multifile::
00195 open_read_write(const Filename &multifile_name) {
00196 close();
00197 Filename fname = multifile_name;
00198 fname.set_binary();
00199 bool exists = fname.exists();
00200 if (!fname.open_read_write(_read_write_file)) {
00201 return false;
00202 }
00203 _read = &_read_write_file;
00204 _write = &_read_write_file;
00205 _multifile_name = multifile_name;
00206
00207 if (exists) {
00208 return read_index();
00209 } else {
00210 return true;
00211 }
00212 }
00213
00214
00215
00216
00217
00218
00219
00220
00221 void Multifile::
00222 close() {
00223 if (_new_scale_factor != _scale_factor) {
00224
00225
00226 repack();
00227 } else {
00228 flush();
00229 }
00230
00231 _read = (istream *)NULL;
00232 _write = (ostream *)NULL;
00233 _next_index = 0;
00234 _last_index = 0;
00235 _needs_repack = false;
00236 _scale_factor = 1;
00237 _new_scale_factor = 1;
00238 _file_major_ver = 0;
00239 _file_minor_ver = 0;
00240
00241 _read_file.close();
00242 _write_file.close();
00243 _read_write_file.close();
00244 _multifile_name = Filename();
00245
00246 clear_subfiles();
00247 }
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272 void Multifile::
00273 set_scale_factor(size_t scale_factor) {
00274 nassertv(is_write_valid());
00275 nassertv(scale_factor != (size_t)0);
00276
00277 if (_next_index == (streampos)0) {
00278
00279
00280 _scale_factor = scale_factor;
00281 } else {
00282
00283
00284 nassertv(is_read_valid());
00285 }
00286
00287
00288
00289 _new_scale_factor = scale_factor;
00290 }
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302 string Multifile::
00303 add_subfile(const string &subfile_name, const Filename &filename,
00304 int compression_level) {
00305 nassertr(is_write_valid(), string());
00306
00307 if (!filename.exists()) {
00308 return string();
00309 }
00310 Subfile *subfile = new Subfile;
00311 subfile->_source_filename = filename;
00312 subfile->_source_filename.set_binary();
00313
00314 return add_new_subfile(subfile_name, subfile, compression_level);
00315 }
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338 bool Multifile::
00339 flush() {
00340 if (!is_write_valid()) {
00341 return false;
00342 }
00343
00344 if (_next_index == (streampos)0) {
00345
00346
00347 if (!write_header()) {
00348 return false;
00349 }
00350 }
00351
00352 nassertr(_write != (ostream *)NULL, false);
00353
00354
00355 PendingSubfiles::iterator pi;
00356 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
00357 Subfile *subfile = (*pi);
00358 subfile->rewrite_index_flags(*_write);
00359 delete subfile;
00360 }
00361 _removed_subfiles.clear();
00362
00363 bool wrote_ok = true;
00364
00365 if (!_new_subfiles.empty()) {
00366
00367
00368 if (_last_index != (streampos)0) {
00369 _write->seekp(0, ios::end);
00370 if (_write->fail()) {
00371 express_cat.info()
00372 << "Unable to seek Multifile " << _multifile_name << ".\n";
00373 return false;
00374 }
00375 _next_index = _write->tellp();
00376 _next_index = pad_to_streampos(_next_index);
00377
00378
00379
00380 _write->seekp(_last_index);
00381 StreamWriter writer(_write);
00382 writer.add_uint32(streampos_to_word(_next_index));
00383 }
00384
00385 _write->seekp(_next_index);
00386 nassertr(_next_index == _write->tellp(), false);
00387
00388
00389
00390 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
00391 Subfile *subfile = (*pi);
00392 _last_index = _next_index;
00393 _next_index = subfile->write_index(*_write, _next_index, this);
00394 _next_index = pad_to_streampos(_next_index);
00395 nassertr(_next_index == _write->tellp(), false);
00396 }
00397
00398
00399
00400 StreamWriter writer(_write);
00401 writer.add_uint32(0);
00402 _next_index += 4;
00403 _next_index = pad_to_streampos(_next_index);
00404
00405
00406 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
00407 Subfile *subfile = (*pi);
00408 _next_index = subfile->write_data(*_write, _read, _next_index);
00409 _next_index = pad_to_streampos(_next_index);
00410 if (subfile->is_data_invalid()) {
00411 wrote_ok = false;
00412 }
00413 nassertr(_next_index == _write->tellp(), false);
00414 }
00415
00416
00417
00418
00419
00420 for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
00421 Subfile *subfile = (*pi);
00422 subfile->rewrite_index_data_start(*_write, this);
00423 }
00424
00425 _new_subfiles.clear();
00426 }
00427
00428 _write->flush();
00429 if (!wrote_ok || _write->fail()) {
00430 express_cat.info()
00431 << "Unable to update Multifile " << _multifile_name << ".\n";
00432 close();
00433 return false;
00434 }
00435 return true;
00436 }
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456 bool Multifile::
00457 repack() {
00458 if (_next_index == (streampos)0) {
00459
00460
00461 return flush();
00462 }
00463
00464 nassertr(is_write_valid() && is_read_valid(), false);
00465 nassertr(!_multifile_name.empty(), false);
00466
00467
00468 Filename dirname = _multifile_name.get_dirname();
00469 if (dirname.empty()) {
00470 dirname = ".";
00471 }
00472 Filename temp_filename = Filename::temporary(dirname, "mftemp");
00473 temp_filename.set_binary();
00474 ofstream temp;
00475 if (!temp_filename.open_write(temp)) {
00476 express_cat.info()
00477 << "Unable to open temporary file " << temp_filename << "\n";
00478 return false;
00479 }
00480
00481
00482
00483 PendingSubfiles::iterator pi;
00484 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
00485 Subfile *subfile = (*pi);
00486 delete subfile;
00487 }
00488 _removed_subfiles.clear();
00489 _new_subfiles.clear();
00490 copy(_subfiles.begin(), _subfiles.end(), back_inserter(_new_subfiles));
00491 _next_index = 0;
00492 _last_index = 0;
00493 _scale_factor = _new_scale_factor;
00494
00495
00496 _write = &temp;
00497 if (!flush()) {
00498 temp.close();
00499 temp_filename.unlink();
00500 return false;
00501 }
00502
00503
00504
00505 Filename orig_name = _multifile_name;
00506 temp.close();
00507 close();
00508 orig_name.unlink();
00509 if (!temp_filename.rename_to(orig_name)) {
00510 express_cat.info()
00511 << "Unable to rename temporary file " << temp_filename << " to "
00512 << orig_name << ".\n";
00513 return false;
00514 }
00515
00516 if (!open_read_write(orig_name)) {
00517 express_cat.info()
00518 << "Unable to read newly repacked " << _multifile_name
00519 << ".\n";
00520 return false;
00521 }
00522
00523 return true;
00524 }
00525
00526
00527
00528
00529
00530
00531
00532
00533 int Multifile::
00534 get_num_subfiles() const {
00535 return _subfiles.size();
00536 }
00537
00538
00539
00540
00541
00542
00543
00544
00545 int Multifile::
00546 find_subfile(const string &subfile_name) const {
00547 Subfile find_subfile;
00548 find_subfile._name = subfile_name;
00549 Subfiles::const_iterator fi;
00550 fi = _subfiles.find(&find_subfile);
00551 if (fi == _subfiles.end()) {
00552
00553 return -1;
00554 }
00555 return (fi - _subfiles.begin());
00556 }
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566 bool Multifile::
00567 has_directory(const string &subfile_name) const {
00568 string prefix = subfile_name;
00569 if (!prefix.empty()) {
00570 prefix += '/';
00571 }
00572 Subfile find_subfile;
00573 find_subfile._name = prefix;
00574 Subfiles::const_iterator fi;
00575 fi = _subfiles.upper_bound(&find_subfile);
00576 if (fi == _subfiles.end()) {
00577
00578 return false;
00579 }
00580
00581
00582
00583 Subfile *subfile = (*fi);
00584 return (subfile->_name.length() > prefix.length() &&
00585 subfile->_name.substr(0, prefix.length()) == prefix);
00586 }
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603 bool Multifile::
00604 scan_directory(vector_string &contents, const string &subfile_name) const {
00605 string prefix = subfile_name;
00606 if (!prefix.empty()) {
00607 prefix += '/';
00608 }
00609 Subfile find_subfile;
00610 find_subfile._name = prefix;
00611 Subfiles::const_iterator fi;
00612 fi = _subfiles.upper_bound(&find_subfile);
00613
00614 string previous = "";
00615 while (fi != _subfiles.end()) {
00616 Subfile *subfile = (*fi);
00617 if (!(subfile->_name.length() > prefix.length() &&
00618 subfile->_name.substr(0, prefix.length()) == prefix)) {
00619
00620
00621 return true;
00622 }
00623
00624 size_t slash = subfile->_name.find('/', prefix.length());
00625 string basename = subfile->_name.substr(prefix.length(), slash);
00626 if (basename != previous) {
00627 contents.push_back(basename);
00628 previous = basename;
00629 }
00630 ++fi;
00631 }
00632
00633 return true;
00634 }
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650 void Multifile::
00651 remove_subfile(int index) {
00652 nassertv(is_write_valid());
00653 nassertv(index >= 0 && index < (int)_subfiles.size());
00654 Subfile *subfile = _subfiles[index];
00655 subfile->_flags |= SF_deleted;
00656 _removed_subfiles.push_back(subfile);
00657 _subfiles.erase(_subfiles.begin() + index);
00658
00659 _needs_repack = true;
00660 }
00661
00662
00663
00664
00665
00666
00667 const string &Multifile::
00668 get_subfile_name(int index) const {
00669 #ifndef NDEBUG
00670 static string empty_string;
00671 nassertr(index >= 0 && index < (int)_subfiles.size(), empty_string);
00672 #endif
00673 return _subfiles[index]->_name;
00674 }
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684 size_t Multifile::
00685 get_subfile_length(int index) const {
00686 nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
00687 return _subfiles[index]->_uncompressed_length;
00688 }
00689
00690
00691
00692
00693
00694
00695
00696
00697 bool Multifile::
00698 is_subfile_compressed(int index) const {
00699 nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
00700 return (_subfiles[index]->_flags & SF_compressed) != 0;
00701 }
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712 size_t Multifile::
00713 get_subfile_compressed_length(int index) const {
00714 nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
00715 return _subfiles[index]->_data_length;
00716 }
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740 istream *Multifile::
00741 open_read_subfile(int index) {
00742 nassertr(is_read_valid(), NULL);
00743 nassertr(index >= 0 && index < (int)_subfiles.size(), NULL);
00744 Subfile *subfile = _subfiles[index];
00745
00746 if (subfile->_source != (istream *)NULL ||
00747 !subfile->_source_filename.empty()) {
00748
00749
00750 flush();
00751
00752
00753
00754 nassertr(subfile == _subfiles[index], NULL);
00755 }
00756
00757
00758
00759 nassertr(subfile->_data_start != (streampos)0, NULL);
00760 istream *stream =
00761 new ISubStream(_read, subfile->_data_start,
00762 subfile->_data_start + (streampos)subfile->_data_length);
00763
00764 if ((subfile->_flags & SF_compressed) != 0) {
00765 #ifndef HAVE_ZLIB
00766 express_cat.error()
00767 << "zlib not compiled in; cannot read compressed multifiles.\n";
00768 delete stream;
00769 return NULL;
00770 #else // HAVE_ZLIB
00771
00772
00773 IDecompressStream *wrapper = new IDecompressStream(stream, true);
00774 stream = wrapper;
00775 #endif // HAVE_ZLIB
00776 }
00777
00778 if (stream->fail()) {
00779
00780 delete stream;
00781 return NULL;
00782 }
00783
00784 return stream;
00785 }
00786
00787
00788
00789
00790
00791
00792
00793 bool Multifile::
00794 extract_subfile(int index, const Filename &filename) {
00795 nassertr(is_read_valid(), false);
00796 nassertr(index >= 0 && index < (int)_subfiles.size(), false);
00797
00798 Filename fname = filename;
00799 fname.set_binary();
00800 fname.make_dir();
00801 ofstream out;
00802 if (!fname.open_write(out, true)) {
00803 express_cat.info()
00804 << "Unable to write to file " << filename << "\n";
00805 return false;
00806 }
00807
00808 return extract_subfile_to(index, out);
00809 }
00810
00811
00812
00813
00814
00815
00816 void Multifile::
00817 output(ostream &out) const {
00818 out << "Multifile " << _multifile_name << ", " << get_num_subfiles()
00819 << " subfiles.\n";
00820 }
00821
00822
00823
00824
00825
00826
00827 void Multifile::
00828 ls(ostream &out) const {
00829 int num_subfiles = get_num_subfiles();
00830 for (int i = 0; i < num_subfiles; i++) {
00831 string subfile_name = get_subfile_name(i);
00832 out << subfile_name << "\n";
00833 }
00834 }
00835
00836
00837
00838
00839
00840
00841
00842 bool Multifile::
00843 read_subfile(int index, string &result) {
00844 nassertr(is_read_valid(), false);
00845 nassertr(index >= 0 && index < (int)_subfiles.size(), false);
00846 result = string();
00847
00848 istream *in = open_read_subfile(index);
00849 if (in == (istream *)NULL) {
00850 return false;
00851 }
00852
00853 int byte = in->get();
00854 while (!in->eof() && !in->fail()) {
00855 result += (char)byte;
00856 byte = in->get();
00857 }
00858 bool failed = in->fail();
00859 delete in;
00860 nassertr(!failed, false);
00861 return true;
00862 }
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874 bool Multifile::
00875 open_read(istream *multifile_stream) {
00876 close();
00877 _read = multifile_stream;
00878 return read_index();
00879 }
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891 bool Multifile::
00892 open_write(ostream *multifile_stream) {
00893 close();
00894 _write = multifile_stream;
00895 return true;
00896 }
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909 bool Multifile::
00910 open_read_write(iostream *multifile_stream) {
00911 close();
00912 _read = multifile_stream;
00913 _write = multifile_stream;
00914
00915
00916 _read->seekg(0, ios::end);
00917 if (_read->tellg() == (streampos)0) {
00918
00919 return true;
00920 }
00921
00922
00923
00924 _read->seekg(0);
00925 return read_index();
00926 }
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938 string Multifile::
00939 add_subfile(const string &subfile_name, istream *subfile_data,
00940 int compression_level) {
00941 nassertr(is_write_valid(), string());
00942
00943 Subfile *subfile = new Subfile;
00944 subfile->_source = subfile_data;
00945
00946 return add_new_subfile(subfile_name, subfile, compression_level);
00947 }
00948
00949
00950
00951
00952
00953
00954 bool Multifile::
00955 extract_subfile_to(int index, ostream &out) {
00956 nassertr(is_read_valid(), false);
00957 nassertr(index >= 0 && index < (int)_subfiles.size(), false);
00958
00959 istream *in = open_read_subfile(index);
00960 if (in == (istream *)NULL) {
00961 return false;
00962 }
00963
00964 int byte = in->get();
00965 while (!in->fail() && !in->eof()) {
00966 out.put(byte);
00967 byte = in->get();
00968 }
00969
00970 bool failed = (in->fail() && !in->eof());
00971 delete in;
00972 nassertr(!failed, false);
00973
00974 return (!out.fail());
00975 }
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986 streampos Multifile::
00987 pad_to_streampos(streampos fpos) {
00988 nassertr(_write != (ostream *)NULL, fpos);
00989 nassertr(_write->tellp() == fpos, fpos);
00990 streampos new_fpos = normalize_streampos(fpos);
00991 while (fpos < new_fpos) {
00992 _write->put(0);
00993 fpos += 1;
00994 }
00995 return fpos;
00996 }
00997
00998
00999
01000
01001
01002
01003
01004 string Multifile::
01005 add_new_subfile(const string &subfile_name, Subfile *subfile,
01006 int compression_level) {
01007 if (compression_level != 0) {
01008 #ifndef HAVE_ZLIB
01009 express_cat.warning()
01010 << "zlib not compiled in; cannot generated compressed multifiles.\n";
01011 compression_level = 0;
01012 #else // HAVE_ZLIB
01013 subfile->_flags |= SF_compressed;
01014 subfile->_compression_level = compression_level;
01015 #endif // HAVE_ZLIB
01016 }
01017
01018 if (_next_index != (streampos)0) {
01019
01020
01021 _needs_repack = true;
01022 }
01023
01024
01025 Filename name = subfile_name;
01026 name.standardize();
01027 if (name.empty() || name == "/") {
01028
01029 return string();
01030 }
01031
01032 if (name[0] == '/') {
01033 subfile->_name = name.get_fullpath().substr(1);
01034 } else {
01035 subfile->_name = name;
01036 }
01037
01038 pair<Subfiles::iterator, bool> insert_result = _subfiles.insert(subfile);
01039 if (!insert_result.second) {
01040
01041
01042 Subfile *old_subfile = (*insert_result.first);
01043 old_subfile->_flags |= SF_deleted;
01044 _removed_subfiles.push_back(old_subfile);
01045 (*insert_result.first) = subfile;
01046 }
01047
01048 _new_subfiles.push_back(subfile);
01049 return subfile->_name;
01050 }
01051
01052
01053
01054
01055
01056
01057
01058 void Multifile::
01059 clear_subfiles() {
01060 PendingSubfiles::iterator pi;
01061 for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
01062 Subfile *subfile = (*pi);
01063 subfile->rewrite_index_flags(*_write);
01064 delete subfile;
01065 }
01066 _removed_subfiles.clear();
01067
01068
01069
01070 _new_subfiles.clear();
01071
01072 Subfiles::iterator fi;
01073 for (fi = _subfiles.begin(); fi != _subfiles.end(); ++fi) {
01074 Subfile *subfile = (*fi);
01075 delete subfile;
01076 }
01077 _subfiles.clear();
01078 }
01079
01080
01081
01082
01083
01084
01085
01086 bool Multifile::
01087 read_index() {
01088 nassertr(_read != (istream *)NULL, false);
01089
01090 char this_header[_header_size];
01091 _read->read(this_header, _header_size);
01092 if (_read->fail() || _read->gcount() != (unsigned)_header_size) {
01093 express_cat.info()
01094 << "Unable to read Multifile header " << _multifile_name << ".\n";
01095 close();
01096 return false;
01097 }
01098 if (memcmp(this_header, _header, _header_size) != 0) {
01099 express_cat.info()
01100 << _multifile_name << " is not a Multifile.\n";
01101 return false;
01102 }
01103
01104
01105 StreamReader reader(_read, false);
01106 _file_major_ver = reader.get_int16();
01107 _file_minor_ver = reader.get_int16();
01108 _scale_factor = reader.get_uint32();
01109 _new_scale_factor = _scale_factor;
01110
01111 if (_read->eof() || _read->fail()) {
01112 express_cat.info()
01113 << _multifile_name << " header is truncated.\n";
01114 return false;
01115 }
01116
01117 if (_file_major_ver != _current_major_ver ||
01118 (_file_major_ver == _current_major_ver &&
01119 _file_minor_ver > _current_minor_ver)) {
01120 express_cat.info()
01121 << _multifile_name << " has version " << _file_major_ver << "."
01122 << _file_minor_ver << ", expecting version "
01123 << _current_major_ver << "." << _current_minor_ver << ".\n";
01124 return false;
01125 }
01126
01127
01128
01129 _next_index = _read->tellg();
01130 _next_index = normalize_streampos(_next_index);
01131 _read->seekg(_next_index);
01132 _last_index = 0;
01133 streampos index_forward;
01134
01135 Subfile *subfile = new Subfile;
01136 index_forward = subfile->read_index(*_read, _next_index, this);
01137 while (index_forward != (streampos)0) {
01138 _last_index = _next_index;
01139 if (subfile->is_deleted()) {
01140
01141 _needs_repack = true;
01142 delete subfile;
01143 } else {
01144 _subfiles.push_back(subfile);
01145 }
01146 if (index_forward != normalize_streampos(_read->tellg())) {
01147
01148
01149 _needs_repack = true;
01150 }
01151 _read->seekg(index_forward);
01152 _next_index = index_forward;
01153 subfile = new Subfile;
01154 index_forward = subfile->read_index(*_read, _next_index, this);
01155 }
01156 if (subfile->is_index_invalid()) {
01157 express_cat.info()
01158 << "Error reading index for " << _multifile_name << ".\n";
01159 close();
01160 delete subfile;
01161 return false;
01162 }
01163
01164 size_t before_size = _subfiles.size();
01165 _subfiles.sort();
01166 size_t after_size = _subfiles.size();
01167
01168
01169
01170 nassertr(before_size == after_size, true);
01171
01172 delete subfile;
01173 return true;
01174 }
01175
01176
01177
01178
01179
01180
01181
01182 bool Multifile::
01183 write_header() {
01184 nassertr(_write != (ostream *)NULL, false);
01185 nassertr(_write->tellp() == (streampos)0, false);
01186 _write->write(_header, _header_size);
01187 StreamWriter writer(_write);
01188 writer.add_int16(_current_major_ver);
01189 writer.add_int16(_current_minor_ver);
01190 writer.add_uint32(_scale_factor);
01191
01192 _next_index = _write->tellp();
01193 _next_index = pad_to_streampos(_next_index);
01194 _last_index = 0;
01195
01196 if (_write->fail()) {
01197 express_cat.info()
01198 << "Unable to write header for " << _multifile_name << ".\n";
01199 close();
01200 return false;
01201 }
01202
01203 return true;
01204 }
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215 streampos Multifile::Subfile::
01216 read_index(istream &read, streampos fpos, Multifile *multifile) {
01217 nassertr(read.tellg() == fpos, fpos);
01218
01219
01220
01221 StreamReader reader(read);
01222
01223 streampos next_index = multifile->word_to_streampos(reader.get_uint32());
01224 if (read.eof() || read.fail()) {
01225 _flags |= SF_index_invalid;
01226 return 0;
01227 }
01228
01229 if (next_index == (streampos)0) {
01230 return 0;
01231 }
01232
01233
01234
01235 _index_start = fpos;
01236
01237 _data_start = multifile->word_to_streampos(reader.get_uint32());
01238 _data_length = reader.get_uint32();
01239 _flags = reader.get_uint16();
01240 if ((_flags & SF_compressed) != 0) {
01241 _uncompressed_length = reader.get_uint32();
01242 } else {
01243 _uncompressed_length = _data_length;
01244 }
01245 size_t name_length = reader.get_uint16();
01246 if (read.eof() || read.fail()) {
01247 _flags |= SF_index_invalid;
01248 return 0;
01249 }
01250
01251
01252 char *name_buffer = new char[name_length];
01253 nassertr(name_buffer != (char *)NULL, next_index);
01254 for (size_t ni = 0; ni < name_length; ni++) {
01255 name_buffer[ni] = read.get() ^ 0xff;
01256 }
01257 _name = string(name_buffer, name_length);
01258 delete[] name_buffer;
01259
01260 if (read.eof() || read.fail()) {
01261 _flags |= SF_index_invalid;
01262 return 0;
01263 }
01264
01265 return next_index;
01266 }
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280 streampos Multifile::Subfile::
01281 write_index(ostream &write, streampos fpos, Multifile *multifile) {
01282 nassertr(write.tellp() == fpos, fpos);
01283
01284 _index_start = fpos;
01285
01286
01287
01288 Datagram dg;
01289 dg.add_uint32(multifile->streampos_to_word(_data_start));
01290 dg.add_uint32(_data_length);
01291 dg.add_uint16(_flags);
01292 if ((_flags & SF_compressed) != 0) {
01293 dg.add_uint32(_uncompressed_length);
01294 }
01295 dg.add_uint16(_name.length());
01296
01297
01298
01299
01300
01301
01302 string::iterator ni;
01303 for (ni = _name.begin(); ni != _name.end(); ++ni) {
01304 dg.add_int8((*ni) ^ 0xff);
01305 }
01306
01307 size_t this_index_size = 4 + dg.get_length();
01308
01309
01310 streampos next_index = fpos + (streampos)this_index_size;
01311 Datagram idg;
01312 idg.add_uint32(multifile->streampos_to_word(next_index));
01313
01314 write.write((const char *)idg.get_data(), idg.get_length());
01315 write.write((const char *)dg.get_data(), dg.get_length());
01316
01317 return next_index;
01318 }
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340 streampos Multifile::Subfile::
01341 write_data(ostream &write, istream *read, streampos fpos) {
01342 nassertr(write.tellp() == fpos, fpos);
01343
01344 istream *source = _source;
01345 ifstream source_file;
01346 if (source == (istream *)NULL && !_source_filename.empty()) {
01347
01348 if (!_source_filename.open_read(source_file)) {
01349
01350 express_cat.info()
01351 << "Unable to read " << _source_filename << ".\n";
01352 _flags |= SF_data_invalid;
01353 _data_length = 0;
01354 _uncompressed_length = 0;
01355 } else {
01356 source = &source_file;
01357 }
01358 }
01359
01360 if (source == (istream *)NULL) {
01361
01362
01363 if (read == (istream *)NULL) {
01364
01365 express_cat.info()
01366 << "No source for subfile " << _name << ".\n";
01367 _flags |= SF_data_invalid;
01368 } else {
01369
01370 read->seekg(_data_start);
01371 for (size_t p = 0; p < _data_length; p++) {
01372 int byte = read->get();
01373 if (read->eof() || read->fail()) {
01374
01375 express_cat.info()
01376 << "Unexpected EOF for subfile " << _name << ".\n";
01377 _flags |= SF_data_invalid;
01378 break;
01379 }
01380 write.put(byte);
01381 }
01382 }
01383 } else {
01384
01385
01386 #ifndef HAVE_ZLIB
01387
01388
01389 nassertr((_flags & SF_compressed) == 0, fpos);
01390 #else // HAVE_ZLIB
01391 if ((_flags & SF_compressed) != 0) {
01392
01393 streampos write_start = write.tellp();
01394 _uncompressed_length = 0;
01395 OCompressStream zstream(&write, false, _compression_level);
01396 int byte = source->get();
01397 while (!source->eof() && !source->fail()) {
01398 _uncompressed_length++;
01399 zstream.put(byte);
01400 byte = source->get();
01401 }
01402 zstream.close();
01403 streampos write_end = write.tellp();
01404 _data_length = (size_t)(write_end - write_start);
01405 } else
01406 #endif // HAVE_ZLIB
01407 {
01408
01409 _uncompressed_length = 0;
01410 int byte = source->get();
01411 while (!source->eof() && !source->fail()) {
01412 _uncompressed_length++;
01413 write.put(byte);
01414 byte = source->get();
01415 }
01416 _data_length = _uncompressed_length;
01417 }
01418 }
01419
01420
01421
01422 _data_start = fpos;
01423
01424 _source = (istream *)NULL;
01425 _source_filename = Filename();
01426 source_file.close();
01427
01428 return fpos + (streampos)_data_length;
01429 }
01430
01431
01432
01433
01434
01435
01436
01437
01438 void Multifile::Subfile::
01439 rewrite_index_data_start(ostream &write, Multifile *multifile) {
01440 nassertv(_index_start != (streampos)0);
01441
01442 static const size_t data_start_offset = 4;
01443 size_t data_start_pos = _index_start + (streampos)data_start_offset;
01444 write.seekp(data_start_pos);
01445 nassertv(!write.fail());
01446
01447 StreamWriter writer(write);
01448 writer.add_uint32(multifile->streampos_to_word(_data_start));
01449 writer.add_uint32(_data_length);
01450 writer.add_uint16(_flags);
01451 if ((_flags & SF_compressed) != 0) {
01452 writer.add_uint32(_uncompressed_length);
01453 }
01454 }
01455
01456
01457
01458
01459
01460
01461
01462
01463 void Multifile::Subfile::
01464 rewrite_index_flags(ostream &write) {
01465
01466
01467 if (_index_start != (streampos)0) {
01468 static const size_t flags_offset = 4 + 4 + 4;
01469 size_t flags_pos = _index_start + (streampos)flags_offset;
01470 write.seekp(flags_pos);
01471 nassertv(!write.fail());
01472
01473 StreamWriter writer(write);
01474 writer.add_uint16(_flags);
01475 }
01476 }