From 5b2c80e5f3667ff95f3a771f799dcc95fa752a30 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 21 Jun 2011 17:10:51 +0000 Subject: [PATCH] fix fmod threading issues --- panda/src/audiotraits/config_fmodAudio.cxx | 8 + panda/src/audiotraits/config_milesAudio.cxx | 5 +- panda/src/audiotraits/fmodAudioManager.cxx | 100 ------ panda/src/audiotraits/fmodAudioManager.h | 14 - panda/src/audiotraits/fmodAudioSound.cxx | 292 +++++++++++++++--- panda/src/audiotraits/fmodAudioSound.h | 24 +- panda/src/express/Sources.pp | 3 + panda/src/express/express_composite1.cxx | 1 + panda/src/express/fileSystemInfo.I | 103 ++++++ panda/src/express/fileSystemInfo.cxx | 26 ++ panda/src/express/fileSystemInfo.h | 48 +++ panda/src/express/multifile.h | 1 + panda/src/express/virtualFile.cxx | 15 + panda/src/express/virtualFile.h | 3 + panda/src/express/virtualFileMount.cxx | 15 + panda/src/express/virtualFileMount.h | 1 + .../src/express/virtualFileMountMultifile.cxx | 32 ++ panda/src/express/virtualFileMountMultifile.h | 1 + panda/src/express/virtualFileMountSystem.cxx | 17 + panda/src/express/virtualFileMountSystem.h | 1 + panda/src/express/virtualFileSimple.cxx | 15 + panda/src/express/virtualFileSimple.h | 1 + 22 files changed, 557 insertions(+), 169 deletions(-) create mode 100644 panda/src/express/fileSystemInfo.I create mode 100644 panda/src/express/fileSystemInfo.cxx create mode 100644 panda/src/express/fileSystemInfo.h diff --git a/panda/src/audiotraits/config_fmodAudio.cxx b/panda/src/audiotraits/config_fmodAudio.cxx index cfa6560aac..31297c0347 100644 --- a/panda/src/audiotraits/config_fmodAudio.cxx +++ b/panda/src/audiotraits/config_fmodAudio.cxx @@ -29,6 +29,14 @@ ConfigureFn(config_fmodAudio) { init_libFmodAudio(); } +ConfigVariableInt fmod_audio_preload_threshold +("fmod-audio-preload-threshold", 1048576, + PRC_DESC("Files that are smaller " + "than this number of bytes will be preloaded and kept " + "resident in memory, while files that are this size or larger " + "will be streamed from disk. Set this to -1 to preload " + "every file.")); + //////////////////////////////////////////////////////////////////// // Function: init_libFmodAudio // Description: Initializes the library. This must be called at diff --git a/panda/src/audiotraits/config_milesAudio.cxx b/panda/src/audiotraits/config_milesAudio.cxx index 4a0795599f..ea87b4a2a5 100644 --- a/panda/src/audiotraits/config_milesAudio.cxx +++ b/panda/src/audiotraits/config_milesAudio.cxx @@ -46,9 +46,8 @@ ConfigVariableInt miles_audio_expand_mp3_threshold ConfigVariableInt miles_audio_preload_threshold ("miles-audio-preload-threshold", -1, - PRC_DESC("This is the last Miles fallback size, and should be no smaller " - "than both miles-audio-expand-mp3-threshold and " - "miles-audio-calc-mp3-threshold. Files that are smaller " + PRC_DESC("This should be no smaller " + "than miles-audio-expand-mp3-threshold. Files that are smaller " "than this number of bytes will be preloaded and kept " "resident in memory, while files that are this size or larger " "will be streamed from disk. Set this to -1 to preload " diff --git a/panda/src/audiotraits/fmodAudioManager.cxx b/panda/src/audiotraits/fmodAudioManager.cxx index d222bda31d..50b199e231 100644 --- a/panda/src/audiotraits/fmodAudioManager.cxx +++ b/panda/src/audiotraits/fmodAudioManager.cxx @@ -142,13 +142,6 @@ FmodAudioManager() { if (_system_is_valid) { result = _system->set3DSettings( _doppler_factor, _distance_factor, _drop_off_factor); fmod_audio_errcheck("_system->set3DSettings()", result); - -#if (FMOD_VERSION >= 0x00043100) // FMod 4.31.00 changed this API - result = _system->setFileSystem(open_callback, close_callback, read_callback, seek_callback, 0, 0, -1); -#else - result = _system->setFileSystem(open_callback, close_callback, read_callback, seek_callback, -1); -#endif - fmod_audio_errcheck("_system->setFileSystem()", result); } } @@ -833,97 +826,4 @@ get_cache_limit() const { return 0; } -//////////////////////////////////////////////////////////////////// -// Function: FmodAudioManager::open_callback -// Access: Private, Static -// Description: A hook into Panda's virtual file system. -//////////////////////////////////////////////////////////////////// -FMOD_RESULT F_CALLBACK FmodAudioManager:: -open_callback(const char *name, int, unsigned int *file_size, - void **handle, void **user_data) { - if (name == (const char *)NULL || name[0] == '\0') { - // An invalid attempt to open an unnamed file. - return FMOD_ERR_FILE_NOTFOUND; - } - - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - - PT(VirtualFile) file = vfs->get_file(Filename(name)); - if (file == (VirtualFile *)NULL) { - return FMOD_ERR_FILE_NOTFOUND; - } - istream *str = file->open_read_file(true); - - (*file_size) = file->get_file_size(str); - (*handle) = (void *)str; - (*user_data) = NULL; - - return FMOD_OK; -} - -//////////////////////////////////////////////////////////////////// -// Function: FmodAudioManager::close_callback -// Access: Private, Static -// Description: A hook into Panda's virtual file system. -//////////////////////////////////////////////////////////////////// -FMOD_RESULT F_CALLBACK FmodAudioManager:: -close_callback(void *handle, void *user_data) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - - istream *str = (istream *)handle; - vfs->close_read_file(str); - - return FMOD_OK; -} - -//////////////////////////////////////////////////////////////////// -// Function: FmodAudioManager::read_callback -// Access: Private, Static -// Description: A hook into Panda's virtual file system. -//////////////////////////////////////////////////////////////////// -FMOD_RESULT F_CALLBACK FmodAudioManager:: -read_callback(void *handle, void *buffer, unsigned int size_bytes, - unsigned int *bytes_read, void *user_data) { - istream *str = (istream *)handle; - str->read((char *)buffer, size_bytes); - (*bytes_read) = str->gcount(); - - // We can't yield here, since this callback is made within a - // sub-thread--an OS-level sub-thread spawned by FMod, not a Panda - // thread. - //thread_consider_yield(); - - if (str->eof()) { - if ((*bytes_read) == 0) { - return FMOD_ERR_FILE_EOF; - } else { - // Report the EOF next time. - return FMOD_OK; - } - } if (str->fail()) { - return FMOD_ERR_FILE_BAD; - } else { - return FMOD_OK; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: FmodAudioManager::seek_callback -// Access: Private, Static -// Description: A hook into Panda's virtual file system. -//////////////////////////////////////////////////////////////////// -FMOD_RESULT F_CALLBACK FmodAudioManager:: -seek_callback(void *handle, unsigned int pos, void *user_data) { - istream *str = (istream *)handle; - str->clear(); - str->seekg(pos); - - if (str->fail() && !str->eof()) { - return FMOD_ERR_FILE_COULDNOTSEEK; - } else { - return FMOD_OK; - } -} - - #endif //] diff --git a/panda/src/audiotraits/fmodAudioManager.h b/panda/src/audiotraits/fmodAudioManager.h index 8a74bbda34..fe050bee3c 100644 --- a/panda/src/audiotraits/fmodAudioManager.h +++ b/panda/src/audiotraits/fmodAudioManager.h @@ -164,20 +164,6 @@ class EXPCL_FMOD_AUDIO FmodAudioManager : public AudioManager { //////////////////////////////////////////////////////////////////// private: - static FMOD_RESULT F_CALLBACK - open_callback(const char *name, int unicode, unsigned int *file_size, - void **handle, void **user_data); - - static FMOD_RESULT F_CALLBACK - close_callback(void *handle, void *user_data); - - static FMOD_RESULT F_CALLBACK - read_callback(void *handle, void *buffer, unsigned int size_bytes, - unsigned int *bytes_read, void *user_data); - - static FMOD_RESULT F_CALLBACK - seek_callback(void *handle, unsigned int pos, void *user_data); - FMOD::DSP *make_dsp(const FilterProperties::FilterConfig &conf); void update_dsp_chain(FMOD::DSP *head, FilterProperties *config); virtual bool configure_filters(FilterProperties *config); diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index fb0696b18e..bb6f629a9b 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -25,6 +25,7 @@ #include "config_audio.h" #include "fmodAudioSound.h" #include "string_utils.h" +#include "fileSystemInfo.h" TypeHandle FmodAudioSound::_type_handle; @@ -71,46 +72,119 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { _channel = 0; _file_name = file_name; - - FMOD_CREATESOUNDEXINFO *sound_info = NULL; + _file_name.set_binary(); //Get the Speaker Mode [Important for later on.] result = _manager->_system->getSpeakerMode( &_speakermode ); fmod_audio_errcheck("_system->getSpeakerMode()", result); - // Calculate the approximate uncompressed size of the sound. - int size = file_name.get_file_size(); - string ext = downcase(file_name.get_extension()); - if (ext != "wav") size *= 10; - - int flag = positional ? FMOD_3D : FMOD_2D; - int streamflag = (size > 250000) ? FMOD_CREATESTREAM : FMOD_CREATESAMPLE; - if (ext == "mid") { - streamflag = FMOD_CREATESTREAM; - sound_info = &_manager->_midi_info; + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + PT(VirtualFile) file = vfs->get_file(_file_name); + if (file == (VirtualFile *)NULL) { + // File not found. We will display the appropriate error message + // below. + result = FMOD_ERR_FILE_NOTFOUND; + + } else { + bool preload = (fmod_audio_preload_threshold < 0) || (file->get_file_size() < fmod_audio_preload_threshold); + int flags = FMOD_SOFTWARE; + flags |= positional ? FMOD_3D : FMOD_2D; - if (sound_info->dlsname != NULL) { - audio_debug("Using DLS file " << sound_info->dlsname); + FMOD_CREATESOUNDEXINFO sound_info; + memset(&sound_info, 0, sizeof(sound_info)); + sound_info.cbsize = sizeof(sound_info); + + string ext = downcase(_file_name.get_extension()); + if (ext == "mid") { + // Get the MIDI parameters. + memcpy(&sound_info, &_manager->_midi_info, sizeof(sound_info)); + if (sound_info.dlsname != NULL) { + audio_debug("Using DLS file " << sound_info.dlsname); + } } + + const char *name_or_data = _file_name.c_str(); + + pvector mem_buffer; + FileSystemInfo info; + if (preload) { + // Pre-read the file right now, and pass it in as a memory + // buffer. This avoids threading issues completely, because all + // of the reading happens right here. + file->read_file(mem_buffer, true); + sound_info.length = mem_buffer.size(); + if (mem_buffer.size() != 0) { + name_or_data = (const char *)&mem_buffer[0]; + } + flags |= FMOD_OPENMEMORY; + if (fmodAudio_cat.is_debug()) { + fmodAudio_cat.debug() + << "Reading " << _file_name << " into memory (" << sound_info.length + << " bytes)\n"; + } + + } else if (file->get_system_info(info)) { + // The file exists on disk (or it's part of a multifile that + // exists on disk), so we can have FMod read the file directly. + // This is also safe, because FMod uses its own I/O operations + // that don't involve Panda, so this can safely happen in an + // FMod thread. + name_or_data = info.get_os_file_name().c_str(); + sound_info.fileoffset = (unsigned int)info.get_file_start(); + sound_info.length = (unsigned int)info.get_file_size(); + flags |= FMOD_CREATESTREAM; + if (fmodAudio_cat.is_debug()) { + fmodAudio_cat.debug() + << "Streaming " << _file_name << " from disk (" << name_or_data + << ", " << sound_info.fileoffset << ", " << sound_info.length << ")\n"; + } + + } else { +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + // Otherwise, if the Panda threading system is compiled in, we + // can assign callbacks to read the file through the VFS. + name_or_data = (const char *)file.p(); + sound_info.length = (unsigned int)info.get_file_size(); + sound_info.useropen = open_callback; + sound_info.userclose = close_callback; + sound_info.userread = read_callback; + sound_info.userseek = seek_callback; + flags |= FMOD_CREATESTREAM; + if (fmodAudio_cat.is_debug()) { + fmodAudio_cat.debug() + << "Streaming " << _file_name << " from disk using callbacks\n"; + } + +#else // HAVE_THREADS && !SIMPLE_THREADS + // Without threads, we can't safely read this file. + name_or_data = ""; + + fmodAudio_cat.warning() + << "Cannot stream " << _file_name << "; file is not literally on disk.\n"; +#endif + } + + result = + _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound); } - - result = _manager->_system->createSound( file_name.c_str(), FMOD_SOFTWARE | streamflag | flag , - sound_info, &_sound); + if (result != FMOD_OK) { - audio_error("createSound(" << file_name << "): " << FMOD_ErrorString(result)); - + audio_error("createSound(" << _file_name << "): " << FMOD_ErrorString(result)); + // We couldn't load the sound file. Create a blank sound record // instead. + FMOD_CREATESOUNDEXINFO sound_info; + memset(&sound_info, 0, sizeof(sound_info)); char blank_data[100]; - FMOD_CREATESOUNDEXINFO exinfo; - memset(&exinfo, 0, sizeof(exinfo)); memset(blank_data, 0, sizeof(blank_data)); - exinfo.cbsize = sizeof(exinfo); - exinfo.length = sizeof(blank_data); - exinfo.numchannels = 1; - exinfo.defaultfrequency = 8000; - exinfo.format = FMOD_SOUND_FORMAT_PCM16; - result = _manager->_system->createSound( blank_data, FMOD_SOFTWARE | flag | FMOD_OPENMEMORY | FMOD_OPENRAW, &exinfo, &_sound); + sound_info.cbsize = sizeof(sound_info); + sound_info.length = sizeof(blank_data); + sound_info.numchannels = 1; + sound_info.defaultfrequency = 8000; + sound_info.format = FMOD_SOUND_FORMAT_PCM16; + int flags = FMOD_SOFTWARE | FMOD_OPENMEMORY | FMOD_OPENRAW; + + result = _manager->_system->createSound( blank_data, flags, &sound_info, &_sound); fmod_audio_errcheck("createSound (blank)", result); } @@ -157,27 +231,6 @@ play() { start_playing(); } -//////////////////////////////////////////////////////////////////// -// Function: sound_end_callback -// Access: Static -// Description: When fmod finishes playing a sound, decrements the -// reference count of the associated FmodAudioSound. -//////////////////////////////////////////////////////////////////// -FMOD_RESULT F_CALLBACK sound_end_callback(FMOD_CHANNEL * channel, - FMOD_CHANNEL_CALLBACKTYPE type, - void *commanddata1, - void *commanddata2) { - if (type == FMOD_CHANNEL_CALLBACKTYPE_END) { - FMOD::Channel *fc = (FMOD::Channel *)channel; - void *userdata = NULL; - FMOD_RESULT result = fc->getUserData(&userdata); - fmod_audio_errcheck("channel->getUserData()", result); - FmodAudioSound *fsound = (FmodAudioSound*)userdata; - fsound->_self_ref = fsound; - } - return FMOD_OK; -} - //////////////////////////////////////////////////////////////////// // Function: FmodAudioSound::stop // Access: public @@ -884,4 +937,147 @@ get_finished_event() const { return _finished_event; } +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioSound::sound_end_callback +// Access: Private, Static +// Description: When fmod finishes playing a sound, decrements the +// reference count of the associated FmodAudioSound. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioSound:: +sound_end_callback(FMOD_CHANNEL * channel, + FMOD_CHANNEL_CALLBACKTYPE type, + void *commanddata1, + void *commanddata2) { + // Fortunately, this callback is made synchronously rather than + // asynchronously (it is triggered during System::update()), so we + // don't have to worry about thread-related issues here. + if (type == FMOD_CHANNEL_CALLBACKTYPE_END) { + FMOD::Channel *fc = (FMOD::Channel *)channel; + void *userdata = NULL; + FMOD_RESULT result = fc->getUserData(&userdata); + fmod_audio_errcheck("channel->getUserData()", result); + FmodAudioSound *fsound = (FmodAudioSound*)userdata; + fsound->_self_ref = fsound; + } + return FMOD_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioSound::open_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioSound:: +open_callback(const char *name, int, unsigned int *file_size, + void **handle, void **user_data) { + // We actually pass in the VirtualFile pointer as the "name". + VirtualFile *file = (VirtualFile *)name; + if (file == (VirtualFile *)NULL) { + return FMOD_ERR_FILE_NOTFOUND; + } + if (fmodAudio_cat.is_spam()) { + fmodAudio_cat.spam() + << "open_callback(" << *file << ")\n"; + } + + istream *str = file->open_read_file(true); + + (*file_size) = file->get_file_size(str); + (*handle) = (void *)str; + (*user_data) = (void *)file; + + // Explicitly ref the VirtualFile since we're storing it in a void + // pointer instead of a PT(VirtualFile). + file->ref(); + + return FMOD_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioSound::close_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioSound:: +close_callback(void *handle, void *user_data) { + VirtualFile *file = (VirtualFile *)user_data; + if (fmodAudio_cat.is_spam()) { + fmodAudio_cat.spam() + << "close_callback(" << *file << ")\n"; + } + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + istream *str = (istream *)handle; + vfs->close_read_file(str); + + // Explicitly unref the VirtualFile pointer. + unref_delete(file); + + return FMOD_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioSound::read_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioSound:: +read_callback(void *handle, void *buffer, unsigned int size_bytes, + unsigned int *bytes_read, void *user_data) { + VirtualFile *file = (VirtualFile *)user_data; + if (fmodAudio_cat.is_spam()) { + fmodAudio_cat.spam() + << "read_callback(" << *file << ", " << size_bytes << ")\n"; + } + + istream *str = (istream *)handle; + str->read((char *)buffer, size_bytes); + (*bytes_read) = str->gcount(); + + // We can't yield here, since this callback is made within a + // sub-thread--an OS-level sub-thread spawned by FMod, not a Panda + // thread. But we will only execute this code in the true-threads + // case anyway. + //thread_consider_yield(); + + if (str->eof()) { + if ((*bytes_read) == 0) { + return FMOD_ERR_FILE_EOF; + } else { + // Report the EOF next time. + return FMOD_OK; + } + } if (str->fail()) { + return FMOD_ERR_FILE_BAD; + } else { + return FMOD_OK; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioSound::seek_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioSound:: +seek_callback(void *handle, unsigned int pos, void *user_data) { + VirtualFile *file = (VirtualFile *)user_data; + if (fmodAudio_cat.is_spam()) { + fmodAudio_cat.spam() + << "seek_callback(" << *file << ", " << pos << ")\n"; + } + + istream *str = (istream *)handle; + str->clear(); + str->seekg(pos); + + if (str->fail() && !str->eof()) { + return FMOD_ERR_FILE_COULDNOTSEEK; + } else { + return FMOD_OK; + } +} + + #endif //] diff --git a/panda/src/audiotraits/fmodAudioSound.h b/panda/src/audiotraits/fmodAudioSound.h index 9429c0af60..44d3417e76 100644 --- a/panda/src/audiotraits/fmodAudioSound.h +++ b/panda/src/audiotraits/fmodAudioSound.h @@ -204,10 +204,26 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound { // other mismanagement. PT(FmodAudioSound) _self_ref; - friend FMOD_RESULT F_CALLBACK sound_end_callback(FMOD_CHANNEL * channel, - FMOD_CHANNEL_CALLBACKTYPE type, - void *commanddata1, - void *commanddata2); + static FMOD_RESULT F_CALLBACK + sound_end_callback(FMOD_CHANNEL * channel, + FMOD_CHANNEL_CALLBACKTYPE type, + void *commanddata1, + void *commanddata2); + + static FMOD_RESULT F_CALLBACK + open_callback(const char *name, int unicode, unsigned int *file_size, + void **handle, void **user_data); + + static FMOD_RESULT F_CALLBACK + close_callback(void *handle, void *user_data); + + static FMOD_RESULT F_CALLBACK + read_callback(void *handle, void *buffer, unsigned int size_bytes, + unsigned int *bytes_read, void *user_data); + + static FMOD_RESULT F_CALLBACK + seek_callback(void *handle, unsigned int pos, void *user_data); + //////////////////////////////////////////////////////////// //These are needed for Panda's Pointer System. DO NOT ERASE! diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp index cdbb549c67..60e3c634dc 100644 --- a/panda/src/express/Sources.pp +++ b/panda/src/express/Sources.pp @@ -23,6 +23,7 @@ encrypt_string.h \ error_utils.h \ export_dtool.h \ + fileSystemInfo.h fileSystemInfo.I \ hashGeneratorBase.I hashGeneratorBase.h \ hashVal.I hashVal.h \ indirectLess.I indirectLess.h \ @@ -84,6 +85,7 @@ datagramSink.cxx dcast.cxx \ encrypt_string.cxx \ error_utils.cxx \ + fileSystemInfo.cxx \ hashGeneratorBase.cxx hashVal.cxx \ memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \ memoryUsagePointers.cxx multifile.cxx \ @@ -140,6 +142,7 @@ dcast.T dcast.h \ encrypt_string.h \ error_utils.h \ + fileSystemInfo.h fileSystemInfo.I \ hashGeneratorBase.I hashGeneratorBase.h \ hashVal.I hashVal.h \ indirectLess.I indirectLess.h \ diff --git a/panda/src/express/express_composite1.cxx b/panda/src/express/express_composite1.cxx index 7f070d3da9..ad8e49bcf3 100644 --- a/panda/src/express/express_composite1.cxx +++ b/panda/src/express/express_composite1.cxx @@ -10,6 +10,7 @@ #include "dcast.cxx" #include "encrypt_string.cxx" #include "error_utils.cxx" +#include "fileSystemInfo.cxx" #include "hashGeneratorBase.cxx" #include "hashVal.cxx" #include "memoryInfo.cxx" diff --git a/panda/src/express/fileSystemInfo.I b/panda/src/express/fileSystemInfo.I new file mode 100644 index 0000000000..78a9a267ac --- /dev/null +++ b/panda/src/express/fileSystemInfo.I @@ -0,0 +1,103 @@ +// Filename: fileSystemInfo.I +// Created by: drose (20Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::Default Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FileSystemInfo:: +FileSystemInfo() : + _file_start(0), + _file_size(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FileSystemInfo:: +FileSystemInfo(const string &os_file_name, streampos file_start, streamsize file_size) : + _os_file_name(os_file_name), + _file_start(file_start), + _file_size(file_size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FileSystemInfo:: +FileSystemInfo(const FileSystemInfo ©) : + _os_file_name(copy._os_file_name), + _file_start(copy._file_start), + _file_size(copy._file_size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void FileSystemInfo:: +operator = (const FileSystemInfo ©) { + _os_file_name = copy._os_file_name; + _file_start = copy._file_start; + _file_size = copy._file_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::get_os_file_name +// Access: Published +// Description: Returns the os-specific filename that may be used to +// open this file. +//////////////////////////////////////////////////////////////////// +INLINE const string &FileSystemInfo:: +get_os_file_name() const { + return _os_file_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::get_file_start +// Access: Published +// Description: Returns the offset within the file at which this file +// data begins. +//////////////////////////////////////////////////////////////////// +INLINE streampos FileSystemInfo:: +get_file_start() const { + return _file_start; +} + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::get_file_size +// Access: Published +// Description: Returns the number of consecutive bytes, beginning at +// get_file_start(), that correspond to this file data. +//////////////////////////////////////////////////////////////////// +INLINE streamsize FileSystemInfo:: +get_file_size() const { + return _file_size; +} + +INLINE ostream & +operator << (ostream &out, const FileSystemInfo &info) { + info.output(out); + return out; +} diff --git a/panda/src/express/fileSystemInfo.cxx b/panda/src/express/fileSystemInfo.cxx new file mode 100644 index 0000000000..9071408fdf --- /dev/null +++ b/panda/src/express/fileSystemInfo.cxx @@ -0,0 +1,26 @@ +// Filename: fileSystemInfo.cxx +// Created by: drose (20Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "fileSystemInfo.h" + +//////////////////////////////////////////////////////////////////// +// Function: FileSystemInfo::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void FileSystemInfo:: +output(ostream &out) const { + out << "FileSystemInfo(" << _os_file_name << ", " << _file_start + << ", " << _file_size << ")"; +} diff --git a/panda/src/express/fileSystemInfo.h b/panda/src/express/fileSystemInfo.h new file mode 100644 index 0000000000..3f874e1139 --- /dev/null +++ b/panda/src/express/fileSystemInfo.h @@ -0,0 +1,48 @@ +// Filename: fileSystemInfo.h +// Created by: drose (20Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef FILESYSTEMINFO_H +#define FILESYSTEMINFO_H + +#include "pandabase.h" + +//////////////////////////////////////////////////////////////////// +// Class : FileSystemInfo +// Description : This class is used to return data about an actual +// file on disk by VirtualFile::get_system_info(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS FileSystemInfo { +PUBLISHED: + INLINE FileSystemInfo(); + INLINE FileSystemInfo(const string &os_file_name, streampos file_start, streamsize file_size); + INLINE FileSystemInfo(const FileSystemInfo ©); + INLINE void operator = (const FileSystemInfo ©); + + INLINE const string &get_os_file_name() const; + INLINE streampos get_file_start() const; + INLINE streamsize get_file_size() const; + + void output(ostream &out) const; + +private: + string _os_file_name; + streampos _file_start; + streamsize _file_size; +}; + +INLINE ostream &operator << (ostream &out, const FileSystemInfo &info); + +#include "fileSystemInfo.I" + +#endif diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index 65a73e94b8..87a5b883ce 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -26,6 +26,7 @@ #include "referenceCount.h" #include "pvector.h" #include "openSSLWrapper.h" +#include "fileSystemInfo.h" //////////////////////////////////////////////////////////////////// // Class : Multifile diff --git a/panda/src/express/virtualFile.cxx b/panda/src/express/virtualFile.cxx index 05b34d43a0..e03755cb00 100644 --- a/panda/src/express/virtualFile.cxx +++ b/panda/src/express/virtualFile.cxx @@ -213,6 +213,21 @@ get_timestamp() const { return 0; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::get_system_info +// Access: Published, Virtual +// Description: Populates the FileSystemInfo structure with the data +// representing where the file actually resides on disk, +// if this is knowable. Returns true if the file might +// reside on disk, and the info is populated, or false +// if it does not (or it is not known where the file +// resides), in which case the info is meaningless. +//////////////////////////////////////////////////////////////////// +bool VirtualFile:: +get_system_info(FileSystemInfo &info) { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFile::close_read_file // Access: Public diff --git a/panda/src/express/virtualFile.h b/panda/src/express/virtualFile.h index 1d21175a69..5ceeb67d8c 100644 --- a/panda/src/express/virtualFile.h +++ b/panda/src/express/virtualFile.h @@ -18,6 +18,7 @@ #include "pandabase.h" #include "filename.h" +#include "fileSystemInfo.h" #include "pointerTo.h" #include "typedReferenceCount.h" #include "ordered_vector.h" @@ -60,6 +61,8 @@ PUBLISHED: BLOCKING virtual off_t get_file_size() const; BLOCKING virtual time_t get_timestamp() const; + virtual bool get_system_info(FileSystemInfo &info); + public: INLINE void set_original_filename(const Filename &filename); bool read_file(string &result, bool auto_unwrap) const; diff --git a/panda/src/express/virtualFileMount.cxx b/panda/src/express/virtualFileMount.cxx index 26f12890a8..109b42c8f9 100644 --- a/panda/src/express/virtualFileMount.cxx +++ b/panda/src/express/virtualFileMount.cxx @@ -140,6 +140,21 @@ close_read_file(istream *stream) const { } } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::get_system_info +// Access: Public, Virtual +// Description: Populates the FileSystemInfo structure with the data +// representing where the file actually resides on disk, +// if this is knowable. Returns true if the file might +// reside on disk, and the info is populated, or false +// if it does not (or it is not known where the file +// resides), in which case the info is meaningless. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMount:: +get_system_info(const Filename &file, FileSystemInfo &info) { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMount::output // Access: Public, Virtual diff --git a/panda/src/express/virtualFileMount.h b/panda/src/express/virtualFileMount.h index 6beb8bd236..67c784ca34 100644 --- a/panda/src/express/virtualFileMount.h +++ b/panda/src/express/virtualFileMount.h @@ -58,6 +58,7 @@ public: virtual off_t get_file_size(const Filename &file, istream *stream) const=0; virtual off_t get_file_size(const Filename &file) const=0; virtual time_t get_timestamp(const Filename &file) const=0; + virtual bool get_system_info(const Filename &file, FileSystemInfo &info); virtual bool scan_directory(vector_string &contents, const Filename &dir) const=0; diff --git a/panda/src/express/virtualFileMountMultifile.cxx b/panda/src/express/virtualFileMountMultifile.cxx index 7115292061..729aa2b7b7 100644 --- a/panda/src/express/virtualFileMountMultifile.cxx +++ b/panda/src/express/virtualFileMountMultifile.cxx @@ -171,6 +171,38 @@ get_timestamp(const Filename &file) const { return _multifile->get_subfile_timestamp(subfile_index); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMountMultifile::get_system_info +// Access: Public, Virtual +// Description: Populates the FileSystemInfo structure with the data +// representing where the file actually resides on disk, +// if this is knowable. Returns true if the file might +// reside on disk, and the info is populated, or false +// if it might not (or it is not known where the file +// resides), in which case the info is meaningless. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMountMultifile:: +get_system_info(const Filename &file, FileSystemInfo &info) { + Filename multifile_name = _multifile->get_multifile_name(); + if (multifile_name.empty()) { + return false; + } + int subfile_index = _multifile->find_subfile(file); + if (subfile_index < 0) { + return false; + } + if (_multifile->is_subfile_compressed(subfile_index) || + _multifile->is_subfile_encrypted(subfile_index)) { + return false; + } + + streampos start = _multifile->get_subfile_internal_start(subfile_index); + size_t length = _multifile->get_subfile_internal_length(subfile_index); + + info = FileSystemInfo(multifile_name.to_os_specific(), start, length); + return true; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountMultifile::scan_directory // Access: Public, Virtual diff --git a/panda/src/express/virtualFileMountMultifile.h b/panda/src/express/virtualFileMountMultifile.h index 6f2e28f525..1fdbaca8fd 100644 --- a/panda/src/express/virtualFileMountMultifile.h +++ b/panda/src/express/virtualFileMountMultifile.h @@ -45,6 +45,7 @@ public: virtual off_t get_file_size(const Filename &file, istream *stream) const; virtual off_t get_file_size(const Filename &file) const; virtual time_t get_timestamp(const Filename &file) const; + virtual bool get_system_info(const Filename &file, FileSystemInfo &info); virtual bool scan_directory(vector_string &contents, const Filename &dir) const; diff --git a/panda/src/express/virtualFileMountSystem.cxx b/panda/src/express/virtualFileMountSystem.cxx index 96c80d4333..9a4b474b86 100644 --- a/panda/src/express/virtualFileMountSystem.cxx +++ b/panda/src/express/virtualFileMountSystem.cxx @@ -181,6 +181,23 @@ get_timestamp(const Filename &file) const { return pathname.get_timestamp(); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMountSystem::get_system_info +// Access: Public, Virtual +// Description: Populates the FileSystemInfo structure with the data +// representing where the file actually resides on disk, +// if this is knowable. Returns true if the file might +// reside on disk, and the info is populated, or false +// if it does not (or it is not known where the file +// resides), in which case the info is meaningless. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMountSystem:: +get_system_info(const Filename &file, FileSystemInfo &info) { + Filename pathname(_physical_filename, file); + info = FileSystemInfo(pathname.to_os_specific(), 0, pathname.get_file_size()); + return true; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountSystem::scan_directory // Access: Public, Virtual diff --git a/panda/src/express/virtualFileMountSystem.h b/panda/src/express/virtualFileMountSystem.h index 63735c8e16..a4a7cb11a4 100644 --- a/panda/src/express/virtualFileMountSystem.h +++ b/panda/src/express/virtualFileMountSystem.h @@ -39,6 +39,7 @@ public: virtual off_t get_file_size(const Filename &file, istream *stream) const; virtual off_t get_file_size(const Filename &file) const; virtual time_t get_timestamp(const Filename &file) const; + virtual bool get_system_info(const Filename &file, FileSystemInfo &info); virtual bool scan_directory(vector_string &contents, const Filename &dir) const; diff --git a/panda/src/express/virtualFileSimple.cxx b/panda/src/express/virtualFileSimple.cxx index 8b9b5d9a2a..1c2bcf1f08 100644 --- a/panda/src/express/virtualFileSimple.cxx +++ b/panda/src/express/virtualFileSimple.cxx @@ -160,6 +160,21 @@ get_timestamp() const { return _mount->get_timestamp(_local_filename); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSimple::get_system_info +// Access: Published, Virtual +// Description: Populates the FileSystemInfo structure with the data +// representing where the file actually resides on disk, +// if this is knowable. Returns true if the file might +// reside on disk, and the info is populated, or false +// if it does not (or it is not known where the file +// resides), in which case the info is meaningless. +//////////////////////////////////////////////////////////////////// +bool VirtualFileSimple:: +get_system_info(FileSystemInfo &info) { + return _mount->get_system_info(_local_filename, info); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSimple::read_file // Access: Public, Virtual diff --git a/panda/src/express/virtualFileSimple.h b/panda/src/express/virtualFileSimple.h index 59a6ad9564..1259f32f0f 100644 --- a/panda/src/express/virtualFileSimple.h +++ b/panda/src/express/virtualFileSimple.h @@ -46,6 +46,7 @@ PUBLISHED: virtual off_t get_file_size(istream *stream) const; virtual off_t get_file_size() const; virtual time_t get_timestamp() const; + virtual bool get_system_info(FileSystemInfo &info); public: virtual bool read_file(pvector &result, bool auto_unwrap) const;