477 lines
15 KiB
C++
477 lines
15 KiB
C++
// Filename: loader.cxx
|
|
// Created by: mike (09Jan97)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "loader.h"
|
|
#include "loaderFileType.h"
|
|
#include "loaderFileTypeRegistry.h"
|
|
#include "config_pgraph.h"
|
|
|
|
#include "config_express.h"
|
|
#include "config_util.h"
|
|
#include "virtualFileSystem.h"
|
|
#include "event.h"
|
|
#include "pt_Event.h"
|
|
#include "throw_event.h"
|
|
#include "eventParameter.h"
|
|
#include "circBuffer.h"
|
|
#include "filename.h"
|
|
#include "load_dso.h"
|
|
#include "string_utils.h"
|
|
#include "plist.h"
|
|
#include "pvector.h"
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
bool Loader::_file_types_loaded = false;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Struct : LoaderToken
|
|
// Description : Holds a request for the loader (load or delete), as
|
|
// well as the return information after the request has
|
|
// completed.
|
|
////////////////////////////////////////////////////////////////////
|
|
class LoaderToken : public ReferenceCount {
|
|
public:
|
|
INLINE LoaderToken(uint id, const string &event_name, const Filename &path,
|
|
bool search, PandaNode *node=NULL) :
|
|
_id(id),
|
|
_event_name(event_name),
|
|
_path(path),
|
|
_search(search),
|
|
_node(node)
|
|
{ }
|
|
uint _id;
|
|
string _event_name;
|
|
Filename _path;
|
|
bool _search;
|
|
PT(PandaNode) _node;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
Loader::
|
|
Loader() : AsyncUtility() {
|
|
_token_board = new LoaderTokenBoard;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::Destructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
Loader::
|
|
~Loader() {
|
|
destroy_thread();
|
|
delete _token_board;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::find_all_files
|
|
// Access: Published
|
|
// Description: Searches along the given search path for the given
|
|
// file name, and fills up the results list with all
|
|
// possible matches and their associated types, in
|
|
// order.
|
|
////////////////////////////////////////////////////////////////////
|
|
int Loader::
|
|
find_all_files(const Filename &filename, const DSearchPath &search_path,
|
|
Loader::Results &results) const {
|
|
if (!_file_types_loaded) {
|
|
load_file_types();
|
|
}
|
|
string extension = filename.get_extension();
|
|
|
|
int num_added = 0;
|
|
|
|
if (!extension.empty()) {
|
|
// If the extension is not empty, it specifies a single file type.
|
|
LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
|
|
LoaderFileType *requested_type =
|
|
reg->get_type_from_extension(extension);
|
|
|
|
if (requested_type != (LoaderFileType *)NULL) {
|
|
if (!filename.is_local()) {
|
|
// Global filename, take it as it is.
|
|
results.add_file(filename, requested_type);
|
|
num_added++;
|
|
|
|
} else {
|
|
// Local filename, search along the path.
|
|
DSearchPath::Results files;
|
|
if (use_vfs) {
|
|
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
num_added = vfs->find_all_files(filename, search_path, files);
|
|
} else {
|
|
num_added = search_path.find_all_files(filename, files);
|
|
}
|
|
|
|
for (int i = 0; i < num_added; ++i) {
|
|
results.add_file(files.get_file(i), requested_type);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// If the extension *is* empty, we have to search for all possible
|
|
// file types.
|
|
LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
|
|
int num_types = reg->get_num_types();
|
|
|
|
if (!filename.is_local()) {
|
|
// Global filename, take it as it is.
|
|
for (int t = 0; t < num_types; ++t) {
|
|
LoaderFileType *type = reg->get_type(t);
|
|
Filename file(filename);
|
|
file.set_extension(type->get_extension());
|
|
|
|
if (use_vfs) {
|
|
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
if (vfs->exists(file)) {
|
|
results.add_file(file, type);
|
|
++num_added;
|
|
}
|
|
} else {
|
|
if (file.exists()) {
|
|
results.add_file(file, type);
|
|
++num_added;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Local filename, look it up on the model path.
|
|
int num_dirs = search_path.get_num_directories();
|
|
for (int i = 0; i < num_dirs; ++i) {
|
|
const Filename &directory = search_path.get_directory(i);
|
|
|
|
for (int t = 0; t < num_types; ++t) {
|
|
LoaderFileType *type = reg->get_type(t);
|
|
Filename file(directory, filename);
|
|
file.set_extension(type->get_extension());
|
|
|
|
if (use_vfs) {
|
|
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
if (vfs->exists(file)) {
|
|
results.add_file(file, type);
|
|
++num_added;
|
|
}
|
|
} else {
|
|
if (file.exists()) {
|
|
results.add_file(file, type);
|
|
++num_added;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_added;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::request_load
|
|
// Access: Published
|
|
// Description: Requests an asynchronous load of a file. The request
|
|
// will be queued and served by the asynchronous thread.
|
|
// If event_name is nonempty, it is the name of the
|
|
// event that will be thrown (with the uint id as its
|
|
// single parameter) when the loading is completed later.
|
|
//
|
|
// The return value is an integer which can be used to
|
|
// identify this particular request later to
|
|
// fetch_load(), or 0 if there has been an error.
|
|
//
|
|
// If search is true, the file is searched for along the
|
|
// model path; otherwise, only the exact filename is
|
|
// loaded.
|
|
////////////////////////////////////////////////////////////////////
|
|
uint Loader::
|
|
request_load(const string &event_name, const Filename &filename, bool search) {
|
|
if (!_file_types_loaded) {
|
|
load_file_types();
|
|
}
|
|
|
|
PT(LoaderToken) tok;
|
|
if (asynchronous_loads) {
|
|
|
|
// Make sure we actually are threaded
|
|
if (!_threaded) {
|
|
loader_cat.info()
|
|
<< "Loader::request_load() - create_thread() was "
|
|
<< "never called! Calling it now..." << endl;
|
|
create_thread();
|
|
}
|
|
|
|
// We need to grab the lock in order to signal the condition variable
|
|
#ifdef OLD_HAVE_IPC
|
|
_lock.lock();
|
|
#endif
|
|
|
|
if (_token_board->_waiting.full()) {
|
|
loader_cat.error()
|
|
<< "Loader::request_load() - Too many pending requests\n";
|
|
return 0;
|
|
}
|
|
|
|
if (loader_cat.is_debug()) {
|
|
loader_cat.debug()
|
|
<< "Load requested for file: " << filename << "\n";
|
|
}
|
|
|
|
tok = new LoaderToken(_next_token++, event_name, filename, search);
|
|
_token_board->_waiting.push_back(tok);
|
|
|
|
#ifdef OLD_HAVE_IPC
|
|
_request_cond->signal();
|
|
_lock.unlock();
|
|
#endif
|
|
|
|
} else {
|
|
// If we're not running asynchronously, process the load request
|
|
// directly now.
|
|
if (_token_board->_waiting.full()) {
|
|
loader_cat.error()
|
|
<< "Loader::request_load() - Too many pending requests\n";
|
|
return 0;
|
|
}
|
|
|
|
if (loader_cat.is_debug()) {
|
|
loader_cat.debug()
|
|
<< "Load requested for file: " << filename << "\n";
|
|
}
|
|
|
|
tok = new LoaderToken(_next_token++, event_name, filename, search);
|
|
_token_board->_waiting.push_back(tok);
|
|
process_request();
|
|
}
|
|
|
|
return tok->_id;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::check_load
|
|
// Access: Published
|
|
// Description: Returns true if the indicated load-request has
|
|
// completed and not yet been fetched, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Loader::
|
|
check_load(uint id) {
|
|
return _token_board->is_done_token(id);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::fetch_load
|
|
// Access: Published
|
|
// Description: Returns the Node associated with the indicated id
|
|
// number (returned by a previous call to request_load),
|
|
// or NULL if the request has not yet completed.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(PandaNode) Loader::
|
|
fetch_load(uint id) {
|
|
PT(LoaderToken) tok = _token_board->get_done_token(id);
|
|
if (tok.is_null()) {
|
|
loader_cat.debug()
|
|
<< "Request to fetch id " << id << " which has not yet completed.\n";
|
|
return NULL;
|
|
}
|
|
PT(PandaNode) node = tok->_node;
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::load_file_types
|
|
// Access: Private, Static
|
|
// Description: Loads up all of the dynamic libraries named in a
|
|
// load-file-type Configure variable. Presumably this
|
|
// will make the various file types available for
|
|
// runtime loading.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Loader::
|
|
load_file_types() {
|
|
if (!_file_types_loaded) {
|
|
int num_unique_values = load_file_type.get_num_unique_values();
|
|
|
|
for (int i = 0; i < num_unique_values; i++) {
|
|
string param = load_file_type.get_unique_value(i);
|
|
|
|
vector_string words;
|
|
extract_words(param, words);
|
|
|
|
if (words.size() == 1) {
|
|
// Exactly one word: load the named library immediately.
|
|
string name = words[0];
|
|
Filename dlname = Filename::dso_filename("lib" + name + ".so");
|
|
loader_cat.info()
|
|
<< "loading file type module: " << name << endl;
|
|
void *tmp = load_dso(dlname);
|
|
if (tmp == (void *)NULL) {
|
|
loader_cat.warning()
|
|
<< "Unable to load " << dlname.to_os_specific()
|
|
<< ": " << load_dso_error() << endl;
|
|
}
|
|
|
|
} else if (words.size() > 1) {
|
|
// Multiple words: the first n words are filename extensions,
|
|
// and the last word is the name of the library to load should
|
|
// any of those filename extensions be encountered.
|
|
LoaderFileTypeRegistry *registry = LoaderFileTypeRegistry::get_global_ptr();
|
|
size_t num_extensions = words.size() - 1;
|
|
string library_name = words[num_extensions];
|
|
|
|
for (size_t i = 0; i < num_extensions; i++) {
|
|
string extension = words[i];
|
|
if (extension[0] == '.') {
|
|
extension = extension.substr(1);
|
|
}
|
|
|
|
registry->register_deferred_type(extension, library_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
_file_types_loaded = true;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::process_request
|
|
// Access: Private
|
|
// Description: Serves any requests on the token board, moving them
|
|
// to the done queue.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Loader::
|
|
process_request() {
|
|
if (_shutdown) {
|
|
if (loader_cat.is_debug())
|
|
loader_cat.debug()
|
|
<< "Loader shutting down...\n";
|
|
return false;
|
|
}
|
|
|
|
// If there is actually a request token - process it
|
|
while (!_token_board->_waiting.empty()) {
|
|
PT(LoaderToken) tok = _token_board->_waiting.front();
|
|
_token_board->_waiting.pop_front();
|
|
tok->_node = load_file(tok->_path, tok->_search);
|
|
if (tok->_node == (PandaNode *)NULL) {
|
|
loader_cat.error()
|
|
<< "Loader::callback() - couldn't find file: "
|
|
<< tok->_path << "\n";
|
|
} else {
|
|
_token_board->_done.push_back(tok);
|
|
|
|
// Throw a "done" event now.
|
|
if (!tok->_event_name.empty()) {
|
|
PT_Event done = new Event(tok->_event_name);
|
|
done->add_parameter(EventParameter((int)tok->_id));
|
|
throw_event(done);
|
|
}
|
|
}
|
|
|
|
if (loader_cat.is_debug()) {
|
|
loader_cat.debug()
|
|
<< "loading complete for " << tok->_path << "\n";
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Loader::load_file
|
|
// Access: Private
|
|
// Description: Loads a single scene graph file, if possible.
|
|
// Returns the Node that is the root of the file, or
|
|
// NULL if the file cannot be loaded.
|
|
//
|
|
// If search is true, the file is searched for along the
|
|
// model path; otherwise, only the exact filename is
|
|
// loaded.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(PandaNode) Loader::
|
|
load_file(const Filename &filename, const LoaderOptions &options) const {
|
|
Results results;
|
|
int num_files;
|
|
|
|
bool search = (options.get_flags() & LoaderOptions::LF_search) != 0;
|
|
|
|
if (search) {
|
|
// Look for the file along the model path.
|
|
num_files = find_all_files(filename, get_model_path(), results);
|
|
} else {
|
|
// Look for the file only where it is.
|
|
num_files = find_all_files(filename, DSearchPath(Filename(".")), results);
|
|
}
|
|
|
|
if (num_files == 0) {
|
|
// Couldn't find the file. Either it doesn't exist, or it's an
|
|
// unknown file type. Report a useful message either way.
|
|
string extension = filename.get_extension();
|
|
if (!extension.empty()) {
|
|
LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
|
|
LoaderFileType *requested_type =
|
|
reg->get_type_from_extension(extension);
|
|
if (requested_type == (LoaderFileType *)NULL) {
|
|
loader_cat.error()
|
|
<< "Extension of file " << filename
|
|
<< " is unrecognized; cannot load.\n";
|
|
loader_cat.error(false)
|
|
<< "Currently known scene file types are:\n";
|
|
reg->write(loader_cat.error(false), 2);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (search) {
|
|
loader_cat.error()
|
|
<< "Couldn't load file " << filename << ": not found on model path "
|
|
<< "(which is currently: \"" << get_model_path() << "\")\n";
|
|
} else {
|
|
loader_cat.error()
|
|
<< "Couldn't load file ./" << filename << ": does not exist.\n";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
for (int i = 0; i < num_files; ++i) {
|
|
const Filename &path = results.get_file(i);
|
|
LoaderFileType *type = results.get_file_type(i);
|
|
PT(PandaNode) result = type->load_file(path, options);
|
|
if (result != (PandaNode *)NULL) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// None of the matching files could be loaded. Oh well.
|
|
if (search) {
|
|
loader_cat.error()
|
|
<< "Couldn't load file " << filename
|
|
<< ": all matching files on model path invalid "
|
|
<< "(the model path is currently: \"" << get_model_path() << "\")\n";
|
|
} else {
|
|
loader_cat.error()
|
|
<< "Couldn't load file " << filename
|
|
<< ": invalid.\n";
|
|
}
|
|
return NULL;
|
|
}
|
|
|