add file timestamps

This commit is contained in:
David Rose 2006-06-08 22:33:08 +00:00
parent cb8e25f859
commit 87a912818b
15 changed files with 294 additions and 11 deletions

View File

@ -1166,6 +1166,41 @@ compare_timestamps(const Filename &other,
return other_missing_is_old ? 1 : -1;
}
////////////////////////////////////////////////////////////////////
// Function: Filename::get_timestamp
// Access: Published
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t Filename::
get_timestamp() const {
string os_specific = get_filename_index(0).to_os_specific();
#ifdef WIN32_VC
struct _stat this_buf;
if (_stat(os_specific.c_str(), &this_buf) == 0) {
return this_buf.st_mtime;
}
#else // WIN32_VC
struct stat this_buf;
if (stat(os_specific.c_str(), &this_buf) == 0) {
return this_buf.st_mtime;
}
#endif
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: Filename::resolve_filename
// Access: Published

View File

@ -159,6 +159,8 @@ PUBLISHED:
int compare_timestamps(const Filename &other,
bool this_missing_is_old = true,
bool other_missing_is_old = true) const;
time_t get_timestamp() const;
bool resolve_filename(const DSearchPath &searchpath,
const string &default_extension = string());
bool make_relative_to(Filename directory, bool allow_backups = true);

View File

@ -388,6 +388,31 @@ extract_files(int argc, char *argv[]) {
return true;
}
const char *
format_timestamp(time_t timestamp) {
static const size_t buffer_size = 512;
static char buffer[buffer_size];
if (timestamp == 0) {
// A zero timestamp is a special case.
return " (no date) ";
}
time_t now = time(NULL);
struct tm *tm_p = localtime(&timestamp);
if (timestamp > now || (now - timestamp > 86400 * 365)) {
// A timestamp in the future, or more than a year in the past,
// gets a year appended.
strftime(buffer, buffer_size, "%b %d %Y", tm_p);
} else {
// Otherwise, within the past year, show the date and time.
strftime(buffer, buffer_size, "%b %d %H:%M", tm_p);
}
return buffer;
}
bool
list_files(int argc, char *argv[]) {
if (!multifile_name.exists()) {
@ -419,23 +444,28 @@ list_files(int argc, char *argv[]) {
ratio = (double)internal_length / (double)orig_length;
}
if (ratio > 1.0) {
printf("%12d worse %c %s\n",
printf("%12d worse %c %s %s\n",
multifile->get_subfile_length(i),
encrypted_symbol,
format_timestamp(multifile->get_subfile_timestamp(i)),
subfile_name.c_str());
} else {
printf("%12d %3.0f%% %c %s\n",
printf("%12d %3.0f%% %c %s %s\n",
multifile->get_subfile_length(i),
100.0 - ratio * 100.0, encrypted_symbol,
format_timestamp(multifile->get_subfile_timestamp(i)),
subfile_name.c_str());
}
} else {
printf("%12d %c %s\n",
printf("%12d %c %s %s\n",
multifile->get_subfile_length(i),
encrypted_symbol, subfile_name.c_str());
encrypted_symbol,
format_timestamp(multifile->get_subfile_timestamp(i)),
subfile_name.c_str());
}
}
}
cout << "Last modification " << format_timestamp(multifile->get_timestamp()) << "\n";
fflush(stdout);
if (multifile->get_scale_factor() != 1) {
cout << "Scale factor is " << multifile->get_scale_factor() << "\n";

View File

@ -63,6 +63,21 @@ needs_repack() const {
return _needs_repack || (_scale_factor != _new_scale_factor);
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_timestamp
// Access: Published
// Description: Returns the modification timestamp of the overall
// Multifile. This indicates the most recent date at
// which subfiles were added or removed from the
// Multifile. Note that it is logically possible for an
// individual subfile to have a more recent timestamp
// than the overall timestamp.
////////////////////////////////////////////////////////////////////
INLINE time_t Multifile::
get_timestamp() const {
return _timestamp;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_scale_factor
// Access: Published
@ -197,6 +212,7 @@ Subfile() {
_index_start = 0;
_data_start = 0;
_data_length = 0;
_timestamp = 0;
_source = (istream *)NULL;
_flags = 0;
_compression_level = 0;

View File

@ -36,7 +36,9 @@ const size_t Multifile::_header_size = 6;
// change in the major version is intolerable; while a Multifile with
// an older minor version may still be read.
const int Multifile::_current_major_ver = 1;
const int Multifile::_current_minor_ver = 0;
const int Multifile::_current_minor_ver = 1;
// Bumped to version 1.1 on 6/8/06 to add timestamps.
// To confirm that the supplied password matches, we write the
// Mutifile magic header at the beginning of the encrypted stream.
@ -63,6 +65,7 @@ const size_t Multifile::_encrypt_header_size = 6;
// uint32 Scale factor. This scales all address references within
// the file. Normally 1, this may be set larger to
// support Multifiles larger than 4GB.
// uint32 An overall modification timestamp for the entire multifile.
//
// (2) Zero or more index entries, one for each subfile within the
@ -82,6 +85,7 @@ const size_t Multifile::_encrypt_header_size = 6;
// subfile, if it is compressed or encrypted. This field
// is only present if one or both of the SF_compressed
// or SF_encrypted bits are set in _flags.
// uint32 A modification timestamp for the subfile.
// uint16 The length in bytes of the subfile's name.
// char[n] The subfile's name.
//
@ -105,6 +109,8 @@ Multifile() {
_next_index = 0;
_last_index = 0;
_needs_repack = false;
_timestamp = 0;
_timestamp_dirty = false;
_scale_factor = 1;
_new_scale_factor = 1;
_encryption_flag = false;
@ -162,6 +168,8 @@ open_read(const Filename &multifile_name) {
if (!fname.open_read(_read_file)) {
return false;
}
_timestamp = fname.get_timestamp();
_timestamp_dirty = true;
_read = &_read_file;
_multifile_name = multifile_name;
return read_index();
@ -188,6 +196,8 @@ open_write(const Filename &multifile_name) {
if (!fname.open_write(_write_file, true)) {
return false;
}
_timestamp = time(NULL);
_timestamp_dirty = true;
_write = &_write_file;
_multifile_name = multifile_name;
return true;
@ -215,6 +225,12 @@ open_read_write(const Filename &multifile_name) {
if (!fname.open_read_write(_read_write_file)) {
return false;
}
if (exists) {
_timestamp = fname.get_timestamp();
} else {
_timestamp = time(NULL);
}
_timestamp_dirty = true;
_read = &_read_write_file;
_write = &_read_write_file;
_multifile_name = multifile_name;
@ -248,8 +264,11 @@ close() {
_next_index = 0;
_last_index = 0;
_needs_repack = false;
_timestamp = 0;
_timestamp_dirty = false;
_scale_factor = 1;
_new_scale_factor = 1;
_encryption_flag = false;
_file_major_ver = 0;
_file_minor_ver = 0;
@ -335,6 +354,9 @@ add_subfile(const string &subfile_name, const Filename &filename,
add_new_subfile(subfile, compression_level);
}
_timestamp = time(NULL);
_timestamp_dirty = true;
return name;
}
@ -343,9 +365,9 @@ add_subfile(const string &subfile_name, const Filename &filename,
// Access: Published
// Description: Adds a file on disk to the subfile. If a subfile
// already exists with the same name, its contents are
// compared to the disk file, and it is replaced only if
// it is different; otherwise, the multifile is left
// unchanged.
// compared byte-for-byte to the disk file, and it is
// replaced only if it is different; otherwise, the
// multifile is left unchanged.
////////////////////////////////////////////////////////////////////
string Multifile::
update_subfile(const string &subfile_name, const Filename &filename,
@ -376,6 +398,9 @@ update_subfile(const string &subfile_name, const Filename &filename,
add_new_subfile(subfile, compression_level);
}
_timestamp = time(NULL);
_timestamp_dirty = true;
return name;
}
@ -412,6 +437,13 @@ flush() {
if (!write_header()) {
return false;
}
} else {
if (_file_minor_ver != _current_minor_ver) {
// If we *do* have an index already, but this is an old version
// multifile, we have to completely rewrite it anyway.
return repack();
}
}
nassertr(_write != (ostream *)NULL, false);
@ -484,8 +516,8 @@ flush() {
// Now go back and fill in the proper addresses for the data start.
// We didn't do it in the first pass, because we don't really want
// to keep all those pile handles open, and so we didn't have to
// determine each pile's length ahead of time.
// to keep all those file handles open, and so we didn't have to
// determine each file's length ahead of time.
for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
Subfile *subfile = (*pi);
subfile->rewrite_index_data_start(*_write, this);
@ -494,6 +526,18 @@ flush() {
_new_subfiles.clear();
}
// Also update the overall timestamp.
if (_timestamp_dirty) {
nassertr(!_write->fail(), false);
static const size_t timestamp_pos = _header_size + 2 + 2 + 4;
_write->seekp(timestamp_pos);
nassertr(!_write->fail(), false);
StreamWriter writer(*_write);
writer.add_uint32(_timestamp);
_timestamp_dirty = false;
}
_write->flush();
if (!wrote_ok || _write->fail()) {
express_cat.info()
@ -725,6 +769,9 @@ remove_subfile(int index) {
_removed_subfiles.push_back(subfile);
_subfiles.erase(_subfiles.begin() + index);
_timestamp = time(NULL);
_timestamp_dirty = true;
_needs_repack = true;
}
@ -756,6 +803,21 @@ get_subfile_length(int index) const {
return _subfiles[index]->_uncompressed_length;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_subfile_timestamp
// Access: Published
// Description: Returns the modification time of the nth
// subfile. If this is called on an older .mf file,
// which did not store individual timestamps in the
// file, this will return the modification time of the
// overall multifile.
////////////////////////////////////////////////////////////////////
time_t Multifile::
get_subfile_timestamp(int index) const {
nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
return _subfiles[index]->_timestamp;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::is_subfile_compressed
// Access: Published
@ -951,6 +1013,18 @@ compare_subfile(int index, const Filename &filename) {
return false;
}
// Check the file size.
in2.seekg(0, ios::end);
streampos file_size = in2.tellg();
if (file_size != (streampos)get_subfile_length(index)) {
// The files have different sizes.
delete in1;
return false;
}
// Check the file data, byte-for-byte.
in2.seekg(0);
int byte1 = in1->get();
int byte2 = in2.get();
while (!in1->fail() && !in1->eof() &&
@ -967,6 +1041,7 @@ compare_subfile(int index, const Filename &filename) {
delete in1;
nassertr(!failed, false);
return true;
}
@ -1036,6 +1111,8 @@ read_subfile(int index, string &result) {
bool Multifile::
open_read(istream *multifile_stream) {
close();
_timestamp = time(NULL);
_timestamp_dirty = true;
_read = multifile_stream;
return read_index();
}
@ -1053,6 +1130,8 @@ open_read(istream *multifile_stream) {
bool Multifile::
open_write(ostream *multifile_stream) {
close();
_timestamp = time(NULL);
_timestamp_dirty = true;
_write = multifile_stream;
return true;
}
@ -1071,6 +1150,8 @@ open_write(ostream *multifile_stream) {
bool Multifile::
open_read_write(iostream *multifile_stream) {
close();
_timestamp = time(NULL);
_timestamp_dirty = true;
_read = multifile_stream;
_write = multifile_stream;
@ -1303,6 +1384,10 @@ read_index() {
return false;
}
if (_file_minor_ver >= 1) {
_timestamp = reader.get_uint32();
_timestamp_dirty = false;
}
// Now read the index out.
_next_index = _read->tellg();
@ -1360,6 +1445,9 @@ read_index() {
////////////////////////////////////////////////////////////////////
bool Multifile::
write_header() {
_file_major_ver = _current_major_ver;
_file_minor_ver = _current_minor_ver;
nassertr(_write != (ostream *)NULL, false);
nassertr(_write->tellp() == (streampos)0, false);
_write->write(_header, _header_size);
@ -1368,6 +1456,8 @@ write_header() {
writer.add_int16(_current_minor_ver);
writer.add_uint32(_scale_factor);
writer.add_uint32(_timestamp);
_next_index = _write->tellp();
_next_index = pad_to_streampos(_next_index);
_last_index = 0;
@ -1421,6 +1511,12 @@ read_index(istream &read, streampos fpos, Multifile *multifile) {
} else {
_uncompressed_length = _data_length;
}
if (multifile->_file_minor_ver < 1) {
_timestamp = multifile->get_timestamp();
} else {
_timestamp = reader.get_uint32();
}
size_t name_length = reader.get_uint16();
if (read.eof() || read.fail()) {
_flags |= SF_index_invalid;
@ -1471,6 +1567,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
dg.add_uint32(_uncompressed_length);
}
dg.add_uint32(_timestamp);
dg.add_uint16(_name.length());
// For no real good reason, we'll invert all the bits in the name.
@ -1497,7 +1594,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::Subfile::write_index
// Function: Multifile::Subfile::write_data
// Access: Public
// Description: Writes the data record for the Subfile to the
// indicated ostream: the actual contents of the
@ -1621,6 +1718,16 @@ write_data(ostream &write, istream *read, streampos fpos,
// Subfile. (In case we are running during repack()).
_data_start = fpos;
// Get the modification timestamp for this subfile. This is read
// from the source file, if we have a filename; otherwise, it's the
// current time.
if (!_source_filename.empty()) {
_timestamp = _source_filename.get_timestamp();
}
if (_timestamp == 0) {
_timestamp = time(NULL);
}
_source = (istream *)NULL;
_source_filename = Filename();
source_file.close();
@ -1651,6 +1758,7 @@ rewrite_index_data_start(ostream &write, Multifile *multifile) {
if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
writer.add_uint32(_uncompressed_length);
}
writer.add_uint32(_timestamp);
}
////////////////////////////////////////////////////////////////////

View File

@ -54,6 +54,8 @@ PUBLISHED:
INLINE bool is_write_valid() const;
INLINE bool needs_repack() const;
INLINE time_t get_timestamp() const;
void set_scale_factor(size_t scale_factor);
INLINE size_t get_scale_factor() const;
@ -77,6 +79,7 @@ PUBLISHED:
void remove_subfile(int index);
const string &get_subfile_name(int index) const;
size_t get_subfile_length(int index) const;
time_t get_subfile_timestamp(int index) const;
bool is_subfile_compressed(int index) const;
bool is_subfile_encrypted(int index) const;
size_t get_subfile_internal_length(int index) const;
@ -131,6 +134,7 @@ private:
streampos _data_start;
size_t _data_length;
size_t _uncompressed_length;
time_t _timestamp;
istream *_source;
Filename _source_filename;
int _flags;
@ -162,6 +166,8 @@ private:
streampos _last_index;
bool _needs_repack;
time_t _timestamp;
bool _timestamp_dirty;
size_t _scale_factor;
size_t _new_scale_factor;

View File

@ -177,6 +177,25 @@ get_file_size(istream *stream) const {
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::get_timestamp
// Access: Published, Virtual
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t VirtualFile::
get_timestamp() const {
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFile::close_read_file
// Access: Public

View File

@ -58,6 +58,7 @@ PUBLISHED:
virtual istream *open_read_file(bool auto_unwrap) const;
void close_read_file(istream *stream) const;
virtual streampos get_file_size(istream *stream) const;
virtual time_t get_timestamp() const;
public:
INLINE void set_original_filename(const Filename &filename);

View File

@ -53,6 +53,7 @@ public:
virtual istream *open_read_file(const Filename &file) const=0;
void close_read_file(istream *stream) const;
virtual streampos get_file_size(const Filename &file, istream *stream) const=0;
virtual time_t get_timestamp(const Filename &file) const=0;
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const=0;

View File

@ -102,6 +102,29 @@ get_file_size(const Filename &file, istream *) const {
return _multifile->get_subfile_length(subfile_index);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountMultifile::get_timestamp
// Access: Published, Virtual
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t VirtualFileMountMultifile::
get_timestamp(const Filename &file) const {
int subfile_index = _multifile->find_subfile(file);
if (subfile_index < 0) {
return 0;
}
return _multifile->get_subfile_timestamp(subfile_index);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountMultifile::scan_directory
// Access: Public, Virtual

View File

@ -46,6 +46,7 @@ public:
virtual istream *open_read_file(const Filename &file) const;
virtual streampos get_file_size(const Filename &file, istream *stream) const;
virtual time_t get_timestamp(const Filename &file) const;
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const;

View File

@ -141,6 +141,26 @@ get_file_size(const Filename &, istream *stream) const {
return size;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::get_timestamp
// Access: Published, Virtual
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t VirtualFileMountSystem::
get_timestamp(const Filename &file) const {
Filename pathname(_physical_filename, file);
return pathname.get_timestamp();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountSystem::scan_directory
// Access: Public, Virtual

View File

@ -42,6 +42,7 @@ public:
virtual istream *open_read_file(const Filename &file) const;
virtual streampos get_file_size(const Filename &file, istream *stream) const;
virtual time_t get_timestamp(const Filename &file) const;
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const;

View File

@ -124,6 +124,25 @@ get_file_size(istream *stream) const {
return _mount->get_file_size(_local_filename, stream);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::get_timestamp
// Access: Published, Virtual
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t VirtualFileSimple::
get_timestamp() const {
return _mount->get_timestamp(_local_filename);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSimple::scan_local_directory
// Access: Protected, Virtual

View File

@ -45,6 +45,7 @@ public:
virtual istream *open_read_file(bool auto_unwrap) const;
virtual streampos get_file_size(istream *stream) const;
virtual time_t get_timestamp() const;
protected:
virtual bool scan_local_directory(VirtualFileList *file_list,