Add p3dembed and add support for offsetted p3d payload
This commit is contained in:
parent
b7f659bd0a
commit
51afeb20ac
|
|
@ -23,3 +23,14 @@ inline const string &P3DFileParams::
|
|||
get_p3d_filename() const {
|
||||
return _p3d_filename;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DFileParams::get_p3d_offset
|
||||
// Access: Public
|
||||
// Description: Returns the p3d file offset, the location
|
||||
// in the file where the p3d data starts.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline const int &P3DFileParams::
|
||||
get_p3d_offset() const {
|
||||
return _p3d_offset;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DFileParams::
|
||||
P3DFileParams() {
|
||||
P3DFileParams() : _p3d_offset(0) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -32,6 +32,7 @@ P3DFileParams() {
|
|||
P3DFileParams::
|
||||
P3DFileParams(const P3DFileParams ©) :
|
||||
_p3d_filename(copy._p3d_filename),
|
||||
_p3d_offset(copy._p3d_offset),
|
||||
_tokens(copy._tokens),
|
||||
_args(copy._args)
|
||||
{
|
||||
|
|
@ -45,6 +46,7 @@ P3DFileParams(const P3DFileParams ©) :
|
|||
void P3DFileParams::
|
||||
operator = (const P3DFileParams &other) {
|
||||
_p3d_filename = other._p3d_filename;
|
||||
_p3d_offset = other._p3d_offset;
|
||||
_tokens = other._tokens;
|
||||
_args = other._args;
|
||||
}
|
||||
|
|
@ -59,6 +61,17 @@ set_p3d_filename(const string &p3d_filename) {
|
|||
_p3d_filename = p3d_filename;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DFileParams::set_p3d_filename
|
||||
// Access: Public
|
||||
// Description: Specifies the location in the file where
|
||||
// the p3d file data starts.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DFileParams::
|
||||
set_p3d_offset(const int &p3d_offset) {
|
||||
_p3d_offset = p3d_offset;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DFileParams::set_tokens
|
||||
// Access: Public
|
||||
|
|
@ -165,6 +178,7 @@ make_xml() {
|
|||
TiXmlElement *xfparams = new TiXmlElement("fparams");
|
||||
|
||||
xfparams->SetAttribute("p3d_filename", _p3d_filename);
|
||||
xfparams->SetAttribute("p3d_offset", _p3d_offset);
|
||||
|
||||
Tokens::const_iterator ti;
|
||||
for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
|
||||
|
|
|
|||
|
|
@ -31,10 +31,12 @@ public:
|
|||
void operator = (const P3DFileParams &other);
|
||||
|
||||
void set_p3d_filename(const string &p3d_filename);
|
||||
void set_p3d_offset(const int &p3d_offset);
|
||||
void set_tokens(const P3D_token tokens[], size_t num_tokens);
|
||||
void set_args(int argc, const char *argv[]);
|
||||
|
||||
inline const string &get_p3d_filename() const;
|
||||
inline const int &get_p3d_offset() const;
|
||||
string lookup_token(const string &keyword) const;
|
||||
int lookup_token_int(const string &keyword) const;
|
||||
bool has_token(const string &keyword) const;
|
||||
|
|
@ -51,6 +53,7 @@ private:
|
|||
typedef vector<string> Args;
|
||||
|
||||
string _p3d_filename;
|
||||
int _p3d_offset;
|
||||
Tokens _tokens;
|
||||
Args _args;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ P3DInstance::
|
|||
|
||||
if (_auth_session != NULL) {
|
||||
_auth_session->shutdown(false);
|
||||
unref_delete(_auth_session);
|
||||
p3d_unref_delete(_auth_session);
|
||||
_auth_session = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -389,9 +389,9 @@ make_p3d_stream(const string &p3d_url) {
|
|||
// Normally this is only called once.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
set_p3d_filename(const string &p3d_filename) {
|
||||
set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
|
||||
determine_p3d_basename(p3d_filename);
|
||||
priv_set_p3d_filename(p3d_filename);
|
||||
priv_set_p3d_filename(p3d_filename, p3d_offset);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -789,7 +789,7 @@ feed_url_stream(int unique_id,
|
|||
if (!download_ok || download->get_download_finished()) {
|
||||
// All done.
|
||||
_downloads.erase(di);
|
||||
unref_delete(download);
|
||||
p3d_unref_delete(download);
|
||||
}
|
||||
|
||||
return download_ok;
|
||||
|
|
@ -1239,7 +1239,7 @@ auth_button_clicked() {
|
|||
// Delete the previous session and create a new one.
|
||||
if (_auth_session != NULL) {
|
||||
_auth_session->shutdown(false);
|
||||
unref_delete(_auth_session);
|
||||
p3d_unref_delete(_auth_session);
|
||||
_auth_session = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1311,7 +1311,7 @@ auth_finished_main_thread() {
|
|||
// internally, and might be passed a temporary filename.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
priv_set_p3d_filename(const string &p3d_filename) {
|
||||
priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
|
||||
if (!_fparams.get_p3d_filename().empty()) {
|
||||
nout << "p3d_filename already set to: " << _fparams.get_p3d_filename()
|
||||
<< ", trying to set to " << p3d_filename << "\n";
|
||||
|
|
@ -1319,6 +1319,10 @@ priv_set_p3d_filename(const string &p3d_filename) {
|
|||
}
|
||||
|
||||
_fparams.set_p3d_filename(p3d_filename);
|
||||
// The default for p3d_offset is -1, which means not to change it.
|
||||
if (p3d_offset >= 0) {
|
||||
_fparams.set_p3d_offset(p3d_offset);
|
||||
}
|
||||
_got_fparams = true;
|
||||
|
||||
_panda_script_object->set_float_property("instanceDownloadProgress", 1.0);
|
||||
|
|
@ -1328,8 +1332,13 @@ priv_set_p3d_filename(const string &p3d_filename) {
|
|||
// if Python has not yet started).
|
||||
send_notify("onpluginload");
|
||||
|
||||
if (!_mf_reader.open_read(_fparams.get_p3d_filename())) {
|
||||
nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
|
||||
if (!_mf_reader.open_read(_fparams.get_p3d_filename(), _fparams.get_p3d_offset())) {
|
||||
if (_fparams.get_p3d_offset() == 0) {
|
||||
nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
|
||||
} else {
|
||||
nout << "Couldn't read " << _fparams.get_p3d_filename()
|
||||
<< " at offset " << _fparams.get_p3d_offset() << "\n";
|
||||
}
|
||||
set_failed();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public:
|
|||
~P3DInstance();
|
||||
|
||||
void set_p3d_url(const string &p3d_url);
|
||||
void set_p3d_filename(const string &p3d_filename);
|
||||
void set_p3d_filename(const string &p3d_filename, const int &p3d_offset = 0);
|
||||
int make_p3d_stream(const string &p3d_url);
|
||||
inline const P3DFileParams &get_fparams() const;
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ private:
|
|||
IT_num_image_types, // Not a real value
|
||||
};
|
||||
|
||||
void priv_set_p3d_filename(const string &p3d_filename);
|
||||
void priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset = -1);
|
||||
void determine_p3d_basename(const string &p3d_url);
|
||||
|
||||
bool check_matches_origin(const string &origin_match);
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ P3DInstanceManager::
|
|||
assert(_sessions.empty());
|
||||
|
||||
if (_auth_session != NULL) {
|
||||
unref_delete(_auth_session);
|
||||
p3d_unref_delete(_auth_session);
|
||||
_auth_session = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -424,13 +424,13 @@ create_instance(P3D_request_ready_func *func,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DInstanceManager::
|
||||
set_p3d_filename(P3DInstance *inst, bool is_local,
|
||||
const string &p3d_filename) {
|
||||
const string &p3d_filename, const int &p3d_offset) {
|
||||
if (inst->is_started()) {
|
||||
nout << "Instance started twice: " << inst << "\n";
|
||||
return false;
|
||||
}
|
||||
if (is_local) {
|
||||
inst->set_p3d_filename(p3d_filename);
|
||||
inst->set_p3d_filename(p3d_filename, p3d_offset);
|
||||
} else {
|
||||
inst->set_p3d_url(p3d_filename);
|
||||
}
|
||||
|
|
@ -513,11 +513,11 @@ finish_instance(P3DInstance *inst) {
|
|||
if (session->get_num_instances() == 0) {
|
||||
_sessions.erase(session->get_session_key());
|
||||
session->shutdown();
|
||||
unref_delete(session);
|
||||
p3d_unref_delete(session);
|
||||
}
|
||||
}
|
||||
|
||||
unref_delete(inst);
|
||||
p3d_unref_delete(inst);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -534,7 +534,7 @@ authorize_instance(P3DInstance *inst) {
|
|||
// We only want one auth_session window open at a time, to
|
||||
// minimize user confusion, so close any previous window.
|
||||
_auth_session->shutdown(true);
|
||||
unref_delete(_auth_session);
|
||||
p3d_unref_delete(_auth_session);
|
||||
_auth_session = NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public:
|
|||
int argc, const char *argv[], void *user_data);
|
||||
|
||||
bool set_p3d_filename(P3DInstance *inst, bool is_local,
|
||||
const string &p3d_filename);
|
||||
const string &p3d_filename, const int &p3d_offset);
|
||||
int make_p3d_stream(P3DInstance *inst, const string &p3d_url);
|
||||
bool start_instance(P3DInstance *inst);
|
||||
void finish_instance(P3DInstance *inst);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const int P3DMultifileReader::_current_minor_ver = 1;
|
|||
P3DMultifileReader::
|
||||
P3DMultifileReader() {
|
||||
_is_open = false;
|
||||
_read_offset = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -48,10 +49,12 @@ P3DMultifileReader() {
|
|||
// on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
open_read(const string &pathname) {
|
||||
open_read(const string &pathname, const int &offset) {
|
||||
if (_is_open) {
|
||||
close();
|
||||
}
|
||||
|
||||
_read_offset = offset;
|
||||
if (!read_header(pathname)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -220,7 +223,7 @@ read_header(const string &pathname) {
|
|||
}
|
||||
|
||||
char this_header[_header_size];
|
||||
_in.seekg(0);
|
||||
_in.seekg(_read_offset);
|
||||
|
||||
// Here's a special case: if the multifile begins with a hash
|
||||
// character, then we continue reading and discarding lines of ASCII
|
||||
|
|
@ -296,7 +299,7 @@ read_index() {
|
|||
}
|
||||
while (next_entry != 0) {
|
||||
Subfile s;
|
||||
s._index_start = (size_t)_in.tellg();
|
||||
s._index_start = (size_t)_in.tellg() - _read_offset;
|
||||
s._index_length = 0;
|
||||
s._data_start = read_uint32();
|
||||
s._data_length = read_uint32();
|
||||
|
|
@ -319,7 +322,7 @@ read_index() {
|
|||
s._filename = string(buffer, name_length);
|
||||
delete[] buffer;
|
||||
|
||||
s._index_length = (size_t)_in.tellg() - s._index_start;
|
||||
s._index_length = (size_t)_in.tellg() - s._index_start - _read_offset;
|
||||
|
||||
if (flags & SF_signature) {
|
||||
// A subfile with this bit set is a signature.
|
||||
|
|
@ -334,7 +337,7 @@ read_index() {
|
|||
}
|
||||
}
|
||||
|
||||
_in.seekg(next_entry);
|
||||
_in.seekg(next_entry + _read_offset);
|
||||
next_entry = read_uint32();
|
||||
if (!_in) {
|
||||
return false;
|
||||
|
|
@ -353,7 +356,7 @@ read_index() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
extract_subfile(ostream &out, const Subfile &s) {
|
||||
_in.seekg(s._data_start);
|
||||
_in.seekg(s._data_start + _read_offset);
|
||||
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
|
@ -397,7 +400,7 @@ check_signatures() {
|
|||
Subfile *subfile = &(*pi);
|
||||
|
||||
// Extract the signature data and certificate separately.
|
||||
_in.seekg(subfile->_data_start);
|
||||
_in.seekg(subfile->_data_start + _read_offset);
|
||||
size_t sig_size = read_uint32();
|
||||
char *sig = new char[sig_size];
|
||||
_in.read(sig, sig_size);
|
||||
|
|
@ -410,7 +413,7 @@ check_signatures() {
|
|||
size_t num_certs = read_uint32();
|
||||
|
||||
// Read the remaining buffer of certificate data.
|
||||
size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start;
|
||||
size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start - _read_offset;
|
||||
size_t buffer_size = subfile->_data_length - bytes_read;
|
||||
char *buffer = new char[buffer_size];
|
||||
_in.read(buffer, buffer_size);
|
||||
|
|
@ -463,7 +466,7 @@ check_signatures() {
|
|||
|
||||
// Read and hash the multifile contents, but only up till
|
||||
// _last_data_byte.
|
||||
_in.seekg(0);
|
||||
_in.seekg(_read_offset);
|
||||
streampos bytes_remaining = (streampos)_last_data_byte;
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
class P3DMultifileReader {
|
||||
public:
|
||||
P3DMultifileReader();
|
||||
bool open_read(const string &pathname);
|
||||
bool open_read(const string &pathname, const int &offset = 0);
|
||||
inline bool is_open() const;
|
||||
void close();
|
||||
|
||||
|
|
@ -82,6 +82,7 @@ private:
|
|||
|
||||
ifstream _in;
|
||||
bool _is_open;
|
||||
int _read_offset;
|
||||
|
||||
typedef vector<Subfile> Subfiles;
|
||||
Subfiles _subfiles;
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ contents_file_redownload_finished(bool success) {
|
|||
|
||||
} else {
|
||||
download->resume_download_finished(false);
|
||||
unref_delete(download);
|
||||
p3d_unref_delete(download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1108,7 +1108,7 @@ void P3DPackage::
|
|||
set_active_download(Download *download) {
|
||||
if (_active_download != download) {
|
||||
if (_active_download != NULL) {
|
||||
unref_delete(_active_download);
|
||||
p3d_unref_delete(_active_download);
|
||||
}
|
||||
_active_download = download;
|
||||
if (_active_download != NULL) {
|
||||
|
|
@ -1127,7 +1127,7 @@ void P3DPackage::
|
|||
set_saved_download(Download *download) {
|
||||
if (_saved_download != download) {
|
||||
if (_saved_download != NULL) {
|
||||
unref_delete(_saved_download);
|
||||
p3d_unref_delete(_saved_download);
|
||||
}
|
||||
_saved_download = download;
|
||||
if (_saved_download != NULL) {
|
||||
|
|
@ -1350,7 +1350,7 @@ InstallStepDownloadFile(P3DPackage *package, const FileSpec &file) :
|
|||
P3DPackage::InstallStepDownloadFile::
|
||||
~InstallStepDownloadFile() {
|
||||
if (_download != NULL) {
|
||||
unref_delete(_download);
|
||||
p3d_unref_delete(_download);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1577,6 +1577,7 @@ P3DPackage::InstallToken P3DPackage::InstallStepUnpackArchive::
|
|||
do_step(bool download_finished) {
|
||||
string source_pathname = _package->get_archive_file_pathname();
|
||||
P3DMultifileReader reader;
|
||||
|
||||
if (!reader.open_read(source_pathname)) {
|
||||
nout << "Couldn't read " << source_pathname << "\n";
|
||||
return IT_step_failed;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ P3DPythonObject::
|
|||
// child process that we no longer need the corresponding PyObject
|
||||
// to be kept around.
|
||||
_session->drop_pyobj(_object_id);
|
||||
unref_delete(_session);
|
||||
p3d_unref_delete(_session);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -1262,10 +1262,15 @@ add_package_info(P3DCInstance *inst, TiXmlElement *xpackage) {
|
|||
void P3DPythonRun::
|
||||
set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
|
||||
string p3d_filename;
|
||||
int p3d_offset;
|
||||
const char *p3d_filename_c = xfparams->Attribute("p3d_filename");
|
||||
if (p3d_filename_c != NULL) {
|
||||
p3d_filename = p3d_filename_c;
|
||||
}
|
||||
const char *p3d_offset_c = xfparams->Attribute("p3d_offset");
|
||||
if (p3d_offset_c != NULL) {
|
||||
p3d_offset = atoi(p3d_offset_c);
|
||||
}
|
||||
|
||||
PyObject *token_list = PyList_New(0);
|
||||
TiXmlElement *xtoken = xfparams->FirstChildElement("token");
|
||||
|
|
@ -1306,8 +1311,8 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
|
|||
}
|
||||
|
||||
PyObject *result = PyObject_CallMethod
|
||||
(_runner, (char *)"setP3DFilename", (char *)"sOOii", p3d_filename.c_str(),
|
||||
token_list, arg_list, inst->get_instance_id(), _interactive_console);
|
||||
(_runner, (char *)"setP3DFilename", (char *)"sOOiii", p3d_filename.c_str(),
|
||||
token_list, arg_list, inst->get_instance_id(), _interactive_console, p3d_offset);
|
||||
Py_DECREF(token_list);
|
||||
Py_DECREF(arg_list);
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ ref() const {
|
|||
// Function: P3DReferenceCount::unref
|
||||
// Access: Public
|
||||
// Description: Explicitly decrements the reference count. Usually,
|
||||
// you should call unref_delete() instead.
|
||||
// you should call p3d_unref_delete() instead.
|
||||
//
|
||||
// The return value is true if the new reference count
|
||||
// is nonzero, false if it is zero.
|
||||
|
|
@ -68,14 +68,14 @@ get_ref_count() const {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: unref_delete
|
||||
// Function: p3d_unref_delete
|
||||
// Description: This global helper function will unref the given
|
||||
// P3DReferenceCount object, and if the reference count
|
||||
// reaches zero, automatically delete it.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class RefCountType>
|
||||
inline void
|
||||
unref_delete(RefCountType *ptr) {
|
||||
p3d_unref_delete(RefCountType *ptr) {
|
||||
if (!ptr->unref()) {
|
||||
delete ptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ private:
|
|||
};
|
||||
|
||||
template<class RefCountType>
|
||||
inline void unref_delete(RefCountType *ptr);
|
||||
inline void p3d_unref_delete(RefCountType *ptr);
|
||||
|
||||
#include "p3dReferenceCount.I"
|
||||
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ terminate_instance(P3DInstance *inst) {
|
|||
_instances.erase(inst->get_instance_id());
|
||||
}
|
||||
RELEASE_LOCK(_instances_lock);
|
||||
unref_delete(inst);
|
||||
p3d_unref_delete(inst);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ P3D_new_instance(P3D_request_ready_func *func,
|
|||
|
||||
bool
|
||||
P3D_instance_start(P3D_instance *instance, bool is_local,
|
||||
const char *p3d_filename) {
|
||||
const char *p3d_filename, const int p3d_offset) {
|
||||
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
|
||||
if (p3d_filename == NULL) {
|
||||
p3d_filename = "";
|
||||
|
|
@ -145,7 +145,8 @@ P3D_instance_start(P3D_instance *instance, bool is_local,
|
|||
// We don't actually start it immediately; the instance will have
|
||||
// to download the p3d url and read it, reading the python
|
||||
// version, before it can start.
|
||||
result = inst_mgr->set_p3d_filename(inst, is_local, p3d_filename);
|
||||
result = inst_mgr->set_p3d_filename(inst, is_local,
|
||||
p3d_filename, p3d_offset);
|
||||
}
|
||||
RELEASE_LOCK(_api_lock);
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ P3D_new_instance_func(P3D_request_ready_func *func,
|
|||
The return value is true on success, false on failure. */
|
||||
typedef bool
|
||||
P3D_instance_start_func(P3D_instance *instance, bool is_local,
|
||||
const char *p3d_filename);
|
||||
const char *p3d_filename, const int p3d_offset = 0);
|
||||
|
||||
/* This function is an alternative to P3D_instance_start(); it
|
||||
indicates an intention to feed the p3d file data to the instance as
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
// Filename: p3dEmbed.cxx
|
||||
// Created by: rdb (07Dec09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "p3d_plugin_composite1.cxx"
|
||||
#include "panda3dBase.cxx"
|
||||
#include <stdint.h>
|
||||
|
||||
const uint32_t p3d_offset = 0xFF3D3D00;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
Panda3DBase program(true);
|
||||
return program.run_embedded(p3d_offset, argc, argv);
|
||||
}
|
||||
|
|
@ -11,27 +11,3 @@
|
|||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::time_to_exit
|
||||
// Access: Public
|
||||
// Description: Returns true if it is time to exit because the last
|
||||
// instance has exited, or false if we should continue
|
||||
// running.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::
|
||||
time_to_exit() {
|
||||
return _instances.empty() && _exit_with_last_instance;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::URLGetter::get_instance
|
||||
// Access: Public
|
||||
// Description: Returns the P3D_instance associated with this
|
||||
// URLGetter.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3D_instance *Panda3D::URLGetter::
|
||||
get_instance() {
|
||||
return _instance;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "panda3d.h"
|
||||
#include "httpClient.h"
|
||||
#include "load_plugin.h"
|
||||
#include "find_root_dir.h"
|
||||
#include "p3d_plugin_config.h"
|
||||
|
||||
#include "panda3dBase.cxx"
|
||||
|
||||
// We can include this header file to get the DTOOL_PLATFORM
|
||||
// definition, even though we don't link with dtool.
|
||||
#include "dtool_platform.h"
|
||||
|
|
@ -40,38 +40,13 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// The amount of time in seconds to wait for new messages.
|
||||
static const double wait_cycle = 0.2;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Panda3D::
|
||||
Panda3D(bool console_environment) {
|
||||
_console_environment = console_environment;
|
||||
|
||||
_root_dir = find_root_dir();
|
||||
_reporting_download = false;
|
||||
_enable_security = false;
|
||||
|
||||
_window_type = P3D_WT_toplevel;
|
||||
|
||||
_win_x = 0;
|
||||
_win_y = 0;
|
||||
_win_width = 640;
|
||||
_win_height = 480;
|
||||
|
||||
_exit_with_last_instance = true;
|
||||
_host_url = PANDA_PACKAGE_HOST_URL;
|
||||
_this_platform = DTOOL_PLATFORM;
|
||||
_verify_contents = false;
|
||||
|
||||
// Seed the lame random number generator in rand(); we use it to
|
||||
// select a mirror for downloading.
|
||||
srand((unsigned int)time(NULL));
|
||||
Panda3D(bool console_environment) : Panda3DBase(console_environment) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -298,106 +273,6 @@ run_command_line(int argc, char *argv[]) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::run_main_loop
|
||||
// Access: Public
|
||||
// Description: Gets lost in the application main loop, waiting for
|
||||
// system events and notifications from the open
|
||||
// instance(s).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
run_main_loop() {
|
||||
#ifdef _WIN32
|
||||
if (_window_type == P3D_WT_embedded) {
|
||||
// Wait for new messages from Windows, and new requests from the
|
||||
// plugin.
|
||||
MSG msg;
|
||||
int retval;
|
||||
retval = GetMessage(&msg, NULL, 0, 0);
|
||||
while (retval != 0 && !time_to_exit()) {
|
||||
if (retval == -1) {
|
||||
cerr << "Error processing message queue.\n";
|
||||
return;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
|
||||
// Check for new requests from the Panda3D plugin.
|
||||
P3D_instance *inst = P3D_check_request(wait_cycle);
|
||||
while (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
inst = P3D_check_request(wait_cycle);
|
||||
}
|
||||
|
||||
while (!_url_getters.empty() &&
|
||||
!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
||||
// If there are no Windows messages, check the download tasks.
|
||||
run_getters();
|
||||
}
|
||||
retval = GetMessage(&msg, NULL, 0, 0);
|
||||
}
|
||||
|
||||
// WM_QUIT has been received. Terminate all instances, and fall
|
||||
// through.
|
||||
while (!_instances.empty()) {
|
||||
P3D_instance *inst = *(_instances.begin());
|
||||
delete_instance(inst);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Not an embedded window, so we don't have our own window to
|
||||
// generate Windows events. Instead, just wait for requests.
|
||||
while (!time_to_exit()) {
|
||||
P3D_instance *inst = P3D_check_request(wait_cycle);
|
||||
if (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
}
|
||||
run_getters();
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
// OSX really prefers to own the main loop, so we install a timer to
|
||||
// call out to our instances and getters, rather than polling within
|
||||
// the event loop as we do in the Windows case, above.
|
||||
EventLoopRef main_loop = GetMainEventLoop();
|
||||
EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback);
|
||||
EventLoopTimerRef timer;
|
||||
EventTimerInterval interval = wait_cycle * kEventDurationSecond;
|
||||
InstallEventLoopTimer(main_loop, interval, interval,
|
||||
timer_upp, this, &timer);
|
||||
RunApplicationEventLoop();
|
||||
RemoveEventLoopTimer(timer);
|
||||
|
||||
// Terminate all instances, and fall through.
|
||||
while (!_instances.empty()) {
|
||||
P3D_instance *inst = *(_instances.begin());
|
||||
delete_instance(inst);
|
||||
}
|
||||
|
||||
#else // _WIN32, __APPLE__
|
||||
|
||||
// Now wait while we process pending requests.
|
||||
while (!time_to_exit()) {
|
||||
P3D_instance *inst = P3D_check_request(wait_cycle);
|
||||
if (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
}
|
||||
run_getters();
|
||||
}
|
||||
|
||||
#endif // _WIN32, __APPLE__
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::post_arg_processing
|
||||
// Access: Protected
|
||||
|
|
@ -817,260 +692,6 @@ get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
|
|||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::run_getters
|
||||
// Access: Protected
|
||||
// Description: Polls all of the active URL requests.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
run_getters() {
|
||||
URLGetters::iterator gi;
|
||||
gi = _url_getters.begin();
|
||||
while (gi != _url_getters.end()) {
|
||||
URLGetter *getter = (*gi);
|
||||
if (getter->run()) {
|
||||
// This URLGetter is still working. Leave it.
|
||||
++gi;
|
||||
} else {
|
||||
// This URLGetter is done. Remove it and delete it.
|
||||
URLGetters::iterator dgi = gi;
|
||||
++gi;
|
||||
_url_getters.erase(dgi);
|
||||
delete getter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::handle_request
|
||||
// Access: Protected
|
||||
// Description: Handles a single request received via the plugin API
|
||||
// from a p3d instance.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
handle_request(P3D_request *request) {
|
||||
bool handled = false;
|
||||
switch (request->_request_type) {
|
||||
case P3D_RT_stop:
|
||||
delete_instance(request->_instance);
|
||||
#ifdef _WIN32
|
||||
// Post a silly message to spin the event loop.
|
||||
PostMessage(NULL, WM_USER, 0, 0);
|
||||
#endif
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case P3D_RT_get_url:
|
||||
{
|
||||
int unique_id = request->_request._get_url._unique_id;
|
||||
const string &url = request->_request._get_url._url;
|
||||
URLGetter *getter = new URLGetter
|
||||
(request->_instance, unique_id, URLSpec(url), "");
|
||||
_url_getters.insert(getter);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case P3D_RT_notify:
|
||||
{
|
||||
if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) {
|
||||
// Tell the user we're downloading a package.
|
||||
report_downloading_package(request->_instance);
|
||||
} else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) {
|
||||
// Tell the user we're done downloading.
|
||||
report_download_complete(request->_instance);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
P3D_request_finish(request, handled);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
LONG WINAPI
|
||||
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
switch (msg) {
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
};
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::make_parent_window
|
||||
// Access: Protected
|
||||
// Description: Creates a toplevel window to contain the embedded
|
||||
// instances. Windows implementation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
make_parent_window() {
|
||||
WNDCLASS wc;
|
||||
|
||||
HINSTANCE application = GetModuleHandle(NULL);
|
||||
ZeroMemory(&wc, sizeof(WNDCLASS));
|
||||
wc.lpfnWndProc = window_proc;
|
||||
wc.hInstance = application;
|
||||
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
||||
wc.lpszClassName = "panda3d";
|
||||
|
||||
if (!RegisterClass(&wc)) {
|
||||
cerr << "Could not register window class!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD window_style =
|
||||
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
|
||||
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
|
||||
WS_SIZEBOX | WS_MAXIMIZEBOX;
|
||||
|
||||
HWND toplevel_window =
|
||||
CreateWindow("panda3d", "Panda3D", window_style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height,
|
||||
NULL, NULL, application, 0);
|
||||
if (!toplevel_window) {
|
||||
cerr << "Could not create toplevel window!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ShowWindow(toplevel_window, SW_SHOWNORMAL);
|
||||
|
||||
memset(&_parent_window, 0, sizeof(_parent_window));
|
||||
_parent_window._window_handle_type = P3D_WHT_win_hwnd;
|
||||
_parent_window._handle._win_hwnd._hwnd = toplevel_window;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::make_parent_window
|
||||
// Access: Protected
|
||||
// Description: Creates a toplevel window to contain the embedded
|
||||
// instances.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
make_parent_window() {
|
||||
// TODO.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::create_instance
|
||||
// Access: Protected
|
||||
// Description: Uses the plugin API to create a new P3D instance to
|
||||
// play a particular .p3d file. This instance is also
|
||||
// started if start_instance is true (which requires
|
||||
// that the named p3d file exists).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3D_instance *Panda3D::
|
||||
create_instance(const string &p3d, bool start_instance,
|
||||
int win_x, int win_y, int win_width, int win_height,
|
||||
char **args, int num_args) {
|
||||
// Check to see if the p3d filename we were given is a URL, or a
|
||||
// local file.
|
||||
Filename p3d_filename = Filename::from_os_specific(p3d);
|
||||
string os_p3d_filename = p3d;
|
||||
bool is_local = !is_url(p3d);
|
||||
if (is_local && start_instance) {
|
||||
if (!p3d_filename.exists()) {
|
||||
cerr << "No such file: " << p3d_filename << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
p3d_filename.make_absolute();
|
||||
os_p3d_filename = p3d_filename.to_os_specific();
|
||||
}
|
||||
|
||||
// Build up the token list.
|
||||
Tokens tokens = _tokens;
|
||||
P3D_token token;
|
||||
|
||||
string log_basename;
|
||||
if (!_log_dirname.empty()) {
|
||||
// Generate output to a logfile.
|
||||
log_basename = p3d_filename.get_basename_wo_extension();
|
||||
token._keyword = "log_basename";
|
||||
token._value = log_basename.c_str();
|
||||
tokens.push_back(token);
|
||||
} else {
|
||||
// Send output to the console.
|
||||
token._keyword = "console_output";
|
||||
if (_console_environment) {
|
||||
token._value = "1";
|
||||
} else {
|
||||
token._value = "0";
|
||||
}
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
token._keyword = "auto_start";
|
||||
token._value = "1";
|
||||
tokens.push_back(token);
|
||||
|
||||
P3D_token *tokens_p;
|
||||
size_t num_tokens = tokens.size();
|
||||
if (!tokens.empty()) {
|
||||
tokens_p = &tokens[0];
|
||||
}
|
||||
|
||||
// Build up the argument list, beginning with the p3d_filename.
|
||||
pvector<const char *> argv;
|
||||
argv.push_back(os_p3d_filename.c_str());
|
||||
for (int i = 0; i < num_args; ++i) {
|
||||
argv.push_back(args[i]);
|
||||
}
|
||||
|
||||
P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens,
|
||||
argv.size(), &argv[0], NULL);
|
||||
|
||||
if (inst != NULL) {
|
||||
if (start_instance) {
|
||||
// We call start() first, to give the core API a chance to
|
||||
// notice the "hidden" attrib before we set the window
|
||||
// parameters.
|
||||
P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
|
||||
}
|
||||
|
||||
P3D_instance_setup_window
|
||||
(inst, _window_type, _win_x, _win_y, _win_width, _win_height, &_parent_window);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::delete_instance
|
||||
// Access: Protected
|
||||
// Description: Deletes the indicated instance and removes it from
|
||||
// the internal structures.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
delete_instance(P3D_instance *inst) {
|
||||
P3D_instance_finish(inst);
|
||||
_instances.erase(inst);
|
||||
|
||||
// Make sure we also terminate any pending URLGetters associated
|
||||
// with this instance.
|
||||
URLGetters::iterator gi;
|
||||
gi = _url_getters.begin();
|
||||
while (gi != _url_getters.end()) {
|
||||
URLGetter *getter = (*gi);
|
||||
if (getter->get_instance() == inst) {
|
||||
URLGetters::iterator dgi = gi;
|
||||
++gi;
|
||||
_url_getters.erase(dgi);
|
||||
delete getter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::usage
|
||||
// Access: Protected
|
||||
|
|
@ -1163,244 +784,3 @@ usage() {
|
|||
<< " Specify the platform to masquerade as. The default is \""
|
||||
<< DTOOL_PLATFORM << "\" .\n\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::parse_token
|
||||
// Access: Protected
|
||||
// Description: Parses a web token of the form token=value, and
|
||||
// stores it in _tokens. Returns true on success, false
|
||||
// on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::
|
||||
parse_token(char *arg) {
|
||||
char *equals = strchr(arg, '=');
|
||||
if (equals == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Directly munge the C string to truncate it at the equals sign.
|
||||
// Classic C tricks.
|
||||
*equals = '\0';
|
||||
P3D_token token;
|
||||
token._keyword = strdup(arg);
|
||||
token._value = strdup(equals + 1);
|
||||
*equals = '=';
|
||||
|
||||
_tokens.push_back(token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::parse_int_pair
|
||||
// Access: Protected
|
||||
// Description: Parses a string into an x,y pair of integers.
|
||||
// Returns true on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::
|
||||
parse_int_pair(char *arg, int &x, int &y) {
|
||||
char *endptr;
|
||||
x = strtol(arg, &endptr, 10);
|
||||
if (*endptr == ',') {
|
||||
y = strtol(endptr + 1, &endptr, 10);
|
||||
if (*endptr == '\0') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Some parse error on the string.
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::is_url
|
||||
// Access: Protected, Static
|
||||
// Description: Returns true if the indicated string appears to be a
|
||||
// URL, with a leading http:// or file:// or whatever,
|
||||
// or false if it must be a local filename instead.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::
|
||||
is_url(const string ¶m) {
|
||||
// We define a URL prefix as a sequence of at least two letters,
|
||||
// followed by a colon, followed by at least one slash.
|
||||
size_t p = 0;
|
||||
while (p < param.size() && isalpha(param[p])) {
|
||||
++p;
|
||||
}
|
||||
if (p < 2) {
|
||||
// Not enough letters.
|
||||
return false;
|
||||
}
|
||||
if (p >= param.size() || param[p] != ':') {
|
||||
// No colon.
|
||||
return false;
|
||||
}
|
||||
++p;
|
||||
if (p >= param.size() || param[p] != '/') {
|
||||
// No slash.
|
||||
return false;
|
||||
}
|
||||
|
||||
// It matches the rules.
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::report_downloading_package
|
||||
// Access: Protected
|
||||
// Description: Tells the user we have to download a package.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
report_downloading_package(P3D_instance *instance) {
|
||||
P3D_object *obj = P3D_instance_get_panda_script_object(instance);
|
||||
|
||||
P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName");
|
||||
if (display_name == NULL) {
|
||||
cerr << "Installing package.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
int name_length = P3D_object_get_string(display_name, NULL, 0);
|
||||
char *name = new char[name_length + 1];
|
||||
P3D_object_get_string(display_name, name, name_length + 1);
|
||||
|
||||
cerr << "Installing " << name << "\n";
|
||||
|
||||
delete[] name;
|
||||
P3D_object_decref(display_name);
|
||||
_reporting_download = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::report_download_complete
|
||||
// Access: Protected
|
||||
// Description: Tells the user we're done downloading packages
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
report_download_complete(P3D_instance *instance) {
|
||||
if (_reporting_download) {
|
||||
cerr << "Install complete.\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::st_timer_callback
|
||||
// Access: Protected, Static
|
||||
// Description: Installed as a timer on the event loop, so we can
|
||||
// process local events, in the Apple implementation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
pascal void Panda3D::
|
||||
st_timer_callback(EventLoopTimerRef timer, void *user_data) {
|
||||
((Panda3D *)user_data)->timer_callback(timer);
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
#ifdef __APPLE__
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::timer_callback
|
||||
// Access: Protected
|
||||
// Description: Installed as a timer on the event loop, so we can
|
||||
// process local events, in the Apple implementation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3D::
|
||||
timer_callback(EventLoopTimerRef timer) {
|
||||
// Check for new requests from the Panda3D plugin.
|
||||
P3D_instance *inst = P3D_check_request(0.0);
|
||||
while (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
inst = P3D_check_request(0.0);
|
||||
}
|
||||
|
||||
// Check the download tasks.
|
||||
run_getters();
|
||||
|
||||
// If we're out of instances, exit the application.
|
||||
if (time_to_exit()) {
|
||||
QuitApplicationEventLoop();
|
||||
}
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::URLGetter::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Panda3D::URLGetter::
|
||||
URLGetter(P3D_instance *instance, int unique_id,
|
||||
const URLSpec &url, const string &post_data) :
|
||||
_instance(instance),
|
||||
_unique_id(unique_id),
|
||||
_url(url),
|
||||
_post_data(post_data)
|
||||
{
|
||||
HTTPClient *http = HTTPClient::get_global_ptr();
|
||||
|
||||
_channel = http->make_channel(false);
|
||||
// _channel->set_download_throttle(true);
|
||||
if (_post_data.empty()) {
|
||||
_channel->begin_get_document(_url);
|
||||
} else {
|
||||
_channel->begin_post_form(_url, _post_data);
|
||||
}
|
||||
|
||||
_channel->download_to_ram(&_rf);
|
||||
_bytes_sent = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::URLGetter::run
|
||||
// Access: Public
|
||||
// Description: Polls the URLGetter for new results. Returns true if
|
||||
// the URL request is still in progress and run() should
|
||||
// be called again later, or false if the URL request
|
||||
// has been completed and run() should not be called
|
||||
// again.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::URLGetter::
|
||||
run() {
|
||||
if (_channel->run() || _rf.get_data_size() != 0) {
|
||||
if (_rf.get_data_size() != 0) {
|
||||
// Got some new data.
|
||||
bool download_ok = P3D_instance_feed_url_stream
|
||||
(_instance, _unique_id, P3D_RC_in_progress,
|
||||
_channel->get_status_code(),
|
||||
_channel->get_file_size(),
|
||||
(const unsigned char *)_rf.get_data().data(), _rf.get_data_size());
|
||||
_bytes_sent += _rf.get_data_size();
|
||||
_rf.clear();
|
||||
|
||||
if (!download_ok) {
|
||||
// The plugin doesn't care any more. Interrupt the download.
|
||||
cerr << "Download interrupted: " << _url
|
||||
<< ", after " << _bytes_sent << " of " << _channel->get_file_size()
|
||||
<< " bytes.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Still more to come; call this method again later.
|
||||
return true;
|
||||
}
|
||||
|
||||
// All done.
|
||||
P3D_result_code status = P3D_RC_done;
|
||||
if (!_channel->is_valid()) {
|
||||
if (_channel->get_status_code() != 0) {
|
||||
status = P3D_RC_http_error;
|
||||
} else {
|
||||
status = P3D_RC_generic_error;
|
||||
}
|
||||
cerr << "Error getting URL " << _url << "\n";
|
||||
}
|
||||
|
||||
P3D_instance_feed_url_stream
|
||||
(_instance, _unique_id, status,
|
||||
_channel->get_status_code(),
|
||||
_bytes_sent, NULL, 0);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,7 @@
|
|||
#ifndef PANDA3D_H
|
||||
#define PANDA3D_H
|
||||
|
||||
// This program must link with Panda for HTTPClient support. This
|
||||
// means it probably should be built with LINK_ALL_STATIC defined, so
|
||||
// we won't have to deal with confusing .dll or .so files that might
|
||||
// compete on the disk with the dynamically-loaded versions. There's
|
||||
// no competition in memory address space, though, because
|
||||
// p3d_plugin--the only file we dynamically link in--doesn't itself
|
||||
// link with Panda.
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "panda3dBase.h"
|
||||
#include "p3d_plugin.h"
|
||||
#include "httpChannel.h"
|
||||
#include "ramfile.h"
|
||||
|
|
@ -36,12 +28,11 @@
|
|||
// Description : A standalone program that invokes the Panda3D plugin
|
||||
// to launch .p3d files.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class Panda3D {
|
||||
class Panda3D : private Panda3DBase {
|
||||
public:
|
||||
Panda3D(bool console_environment);
|
||||
|
||||
int run_command_line(int argc, char *argv[]);
|
||||
void run_main_loop();
|
||||
|
||||
protected:
|
||||
bool post_arg_processing();
|
||||
|
|
@ -52,44 +43,10 @@ protected:
|
|||
void add_mirror(string mirror_url);
|
||||
void choose_random_mirrors(vector_string &result, int num_mirrors);
|
||||
bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin);
|
||||
void run_getters();
|
||||
void handle_request(P3D_request *request);
|
||||
void make_parent_window();
|
||||
|
||||
P3D_instance *
|
||||
create_instance(const string &p3d, bool start_instance,
|
||||
int win_x, int win_y, int win_width, int win_height,
|
||||
char **args, int num_args);
|
||||
void delete_instance(P3D_instance *instance);
|
||||
|
||||
void usage();
|
||||
bool parse_token(char *arg);
|
||||
bool parse_int_pair(char *arg, int &x, int &y);
|
||||
static bool is_url(const string ¶m);
|
||||
|
||||
void report_downloading_package(P3D_instance *instance);
|
||||
void report_download_complete(P3D_instance *instance);
|
||||
|
||||
inline bool time_to_exit();
|
||||
|
||||
#ifdef __APPLE__
|
||||
static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data);
|
||||
void timer_callback(EventLoopTimerRef timer);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
string _root_dir;
|
||||
string _log_dirname;
|
||||
string _log_basename;
|
||||
string _this_platform;
|
||||
bool _verify_contents;
|
||||
P3D_window_type _window_type;
|
||||
P3D_window_handle _parent_window;
|
||||
int _win_x, _win_y;
|
||||
int _win_width, _win_height;
|
||||
bool _exit_with_last_instance;
|
||||
|
||||
string _host_url;
|
||||
string _super_mirror_url;
|
||||
string _host_url_prefix;
|
||||
string _download_url_prefix;
|
||||
|
|
@ -98,38 +55,6 @@ protected:
|
|||
Mirrors _mirrors;
|
||||
|
||||
FileSpec _core_api_dll;
|
||||
bool _reporting_download;
|
||||
bool _enable_security;
|
||||
bool _console_environment;
|
||||
|
||||
typedef pset<P3D_instance *> Instances;
|
||||
Instances _instances;
|
||||
|
||||
typedef pvector<P3D_token> Tokens;
|
||||
Tokens _tokens;
|
||||
|
||||
// This nested class keeps track of active URL requests.
|
||||
class URLGetter {
|
||||
public:
|
||||
URLGetter(P3D_instance *instance, int unique_id,
|
||||
const URLSpec &url, const string &post_data);
|
||||
|
||||
bool run();
|
||||
inline P3D_instance *get_instance();
|
||||
|
||||
private:
|
||||
P3D_instance *_instance;
|
||||
int _unique_id;
|
||||
URLSpec _url;
|
||||
string _post_data;
|
||||
|
||||
PT(HTTPChannel) _channel;
|
||||
Ramfile _rf;
|
||||
size_t _bytes_sent;
|
||||
};
|
||||
|
||||
typedef pset<URLGetter *> URLGetters;
|
||||
URLGetters _url_getters;
|
||||
};
|
||||
|
||||
#include "panda3d.I"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// Filename: panda3dBase.I
|
||||
// Created by: pro-rsoft (07Dec09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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: Panda3DBase::time_to_exit
|
||||
// Access: Public
|
||||
// Description: Returns true if it is time to exit because the last
|
||||
// instance has exited, or false if we should continue
|
||||
// running.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3DBase::
|
||||
time_to_exit() {
|
||||
return _instances.empty() && _exit_with_last_instance;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::URLGetter::get_instance
|
||||
// Access: Public
|
||||
// Description: Returns the P3D_instance associated with this
|
||||
// URLGetter.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3D_instance *Panda3DBase::URLGetter::
|
||||
get_instance() {
|
||||
return _instance;
|
||||
}
|
||||
|
|
@ -0,0 +1,691 @@
|
|||
// Filename: panda3dBase.cxx
|
||||
// Created by: pro-rsoft (07Dec09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "panda3dBase.h"
|
||||
#include "httpClient.h"
|
||||
#include "find_root_dir.h"
|
||||
#include "p3d_plugin_config.h"
|
||||
#include "executionEnvironment.h"
|
||||
|
||||
// We can include this header file to get the DTOOL_PLATFORM
|
||||
// definition, even though we don't link with dtool.
|
||||
#include "dtool_platform.h"
|
||||
#include "pandaVersion.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
// The amount of time in seconds to wait for new messages.
|
||||
static const double wait_cycle = 0.2;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Panda3DBase::
|
||||
Panda3DBase(bool console_environment) {
|
||||
_console_environment = console_environment;
|
||||
|
||||
_root_dir = find_root_dir();
|
||||
_reporting_download = false;
|
||||
_enable_security = false;
|
||||
|
||||
_window_type = P3D_WT_toplevel;
|
||||
|
||||
_win_x = 0;
|
||||
_win_y = 0;
|
||||
_win_width = 640;
|
||||
_win_height = 480;
|
||||
|
||||
_exit_with_last_instance = true;
|
||||
_host_url = PANDA_PACKAGE_HOST_URL;
|
||||
_this_platform = DTOOL_PLATFORM;
|
||||
_verify_contents = false;
|
||||
|
||||
// Seed the lame random number generator in rand(); we use it to
|
||||
// select a mirror for downloading.
|
||||
srand((unsigned int)time(NULL));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::run_embedded
|
||||
// Access: Public
|
||||
// Description: Gets lost in the application main loop, waiting for
|
||||
// system events and notifications from the open
|
||||
// instance(s).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Panda3DBase::
|
||||
run_embedded(int read_offset, int argc, char *argv[]) {
|
||||
if (!P3D_initialize(P3D_API_VERSION, NULL,
|
||||
_host_url.c_str(), _verify_contents, _this_platform.c_str(),
|
||||
_log_dirname.c_str(), _log_basename.c_str(),
|
||||
!_enable_security, _console_environment)) {
|
||||
// Oops, failure to initialize.
|
||||
cerr << "Failed to initialize plugin (wrong API version?)\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Invoke the Core API and run the program
|
||||
P3D_instance *inst = create_instance
|
||||
(ExecutionEnvironment::get_binary_name(), true,
|
||||
_win_x, _win_y, _win_width, _win_height,
|
||||
argv, argc, read_offset);
|
||||
_instances.insert(inst);
|
||||
|
||||
run_main_loop();
|
||||
P3D_finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::run_main_loop
|
||||
// Access: Public
|
||||
// Description: Gets lost in the application main loop, waiting for
|
||||
// system events and notifications from the open
|
||||
// instance(s).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
run_main_loop() {
|
||||
#ifdef _WIN32
|
||||
if (_window_type == P3D_WT_embedded) {
|
||||
// Wait for new messages from Windows, and new requests from the
|
||||
// plugin.
|
||||
MSG msg;
|
||||
int retval;
|
||||
retval = GetMessage(&msg, NULL, 0, 0);
|
||||
while (retval != 0 && !time_to_exit()) {
|
||||
if (retval == -1) {
|
||||
cerr << "Error processing message queue.\n";
|
||||
return;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
|
||||
// Check for new requests from the Panda3D plugin.
|
||||
P3D_instance *inst = P3D_check_request(wait_cycle);
|
||||
while (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
inst = P3D_check_request(wait_cycle);
|
||||
}
|
||||
|
||||
while (!_url_getters.empty() &&
|
||||
!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
||||
// If there are no Windows messages, check the download tasks.
|
||||
run_getters();
|
||||
}
|
||||
retval = GetMessage(&msg, NULL, 0, 0);
|
||||
}
|
||||
|
||||
// WM_QUIT has been received. Terminate all instances, and fall
|
||||
// through.
|
||||
while (!_instances.empty()) {
|
||||
P3D_instance *inst = *(_instances.begin());
|
||||
delete_instance(inst);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Not an embedded window, so we don't have our own window to
|
||||
// generate Windows events. Instead, just wait for requests.
|
||||
while (!time_to_exit()) {
|
||||
P3D_instance *inst = P3D_check_request(wait_cycle);
|
||||
if (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
}
|
||||
run_getters();
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
// OSX really prefers to own the main loop, so we install a timer to
|
||||
// call out to our instances and getters, rather than polling within
|
||||
// the event loop as we do in the Windows case, above.
|
||||
EventLoopRef main_loop = GetMainEventLoop();
|
||||
EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback);
|
||||
EventLoopTimerRef timer;
|
||||
EventTimerInterval interval = wait_cycle * kEventDurationSecond;
|
||||
InstallEventLoopTimer(main_loop, interval, interval,
|
||||
timer_upp, this, &timer);
|
||||
RunApplicationEventLoop();
|
||||
RemoveEventLoopTimer(timer);
|
||||
|
||||
// Terminate all instances, and fall through.
|
||||
while (!_instances.empty()) {
|
||||
P3D_instance *inst = *(_instances.begin());
|
||||
delete_instance(inst);
|
||||
}
|
||||
|
||||
#else // _WIN32, __APPLE__
|
||||
|
||||
// Now wait while we process pending requests.
|
||||
while (!time_to_exit()) {
|
||||
P3D_instance *inst = P3D_check_request(wait_cycle);
|
||||
if (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
}
|
||||
run_getters();
|
||||
}
|
||||
|
||||
#endif // _WIN32, __APPLE__
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::run_getters
|
||||
// Access: Protected
|
||||
// Description: Polls all of the active URL requests.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
run_getters() {
|
||||
URLGetters::iterator gi;
|
||||
gi = _url_getters.begin();
|
||||
while (gi != _url_getters.end()) {
|
||||
URLGetter *getter = (*gi);
|
||||
if (getter->run()) {
|
||||
// This URLGetter is still working. Leave it.
|
||||
++gi;
|
||||
} else {
|
||||
// This URLGetter is done. Remove it and delete it.
|
||||
URLGetters::iterator dgi = gi;
|
||||
++gi;
|
||||
_url_getters.erase(dgi);
|
||||
delete getter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::handle_request
|
||||
// Access: Protected
|
||||
// Description: Handles a single request received via the plugin API
|
||||
// from a p3d instance.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
handle_request(P3D_request *request) {
|
||||
bool handled = false;
|
||||
switch (request->_request_type) {
|
||||
case P3D_RT_stop:
|
||||
delete_instance(request->_instance);
|
||||
#ifdef _WIN32
|
||||
// Post a silly message to spin the event loop.
|
||||
PostMessage(NULL, WM_USER, 0, 0);
|
||||
#endif
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case P3D_RT_get_url:
|
||||
{
|
||||
int unique_id = request->_request._get_url._unique_id;
|
||||
const string &url = request->_request._get_url._url;
|
||||
URLGetter *getter = new URLGetter
|
||||
(request->_instance, unique_id, URLSpec(url), "");
|
||||
_url_getters.insert(getter);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case P3D_RT_notify:
|
||||
{
|
||||
if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) {
|
||||
// Tell the user we're downloading a package.
|
||||
report_downloading_package(request->_instance);
|
||||
} else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) {
|
||||
// Tell the user we're done downloading.
|
||||
report_download_complete(request->_instance);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
P3D_request_finish(request, handled);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
LONG WINAPI
|
||||
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
switch (msg) {
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
};
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::make_parent_window
|
||||
// Access: Protected
|
||||
// Description: Creates a toplevel window to contain the embedded
|
||||
// instances. Windows implementation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
make_parent_window() {
|
||||
WNDCLASS wc;
|
||||
|
||||
HINSTANCE application = GetModuleHandle(NULL);
|
||||
ZeroMemory(&wc, sizeof(WNDCLASS));
|
||||
wc.lpfnWndProc = window_proc;
|
||||
wc.hInstance = application;
|
||||
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
||||
wc.lpszClassName = "panda3d";
|
||||
|
||||
if (!RegisterClass(&wc)) {
|
||||
cerr << "Could not register window class!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD window_style =
|
||||
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
|
||||
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
|
||||
WS_SIZEBOX | WS_MAXIMIZEBOX;
|
||||
|
||||
HWND toplevel_window =
|
||||
CreateWindow("panda3d", "Panda3D", window_style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height,
|
||||
NULL, NULL, application, 0);
|
||||
if (!toplevel_window) {
|
||||
cerr << "Could not create toplevel window!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ShowWindow(toplevel_window, SW_SHOWNORMAL);
|
||||
|
||||
memset(&_parent_window, 0, sizeof(_parent_window));
|
||||
_parent_window._window_handle_type = P3D_WHT_win_hwnd;
|
||||
_parent_window._handle._win_hwnd._hwnd = toplevel_window;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::make_parent_window
|
||||
// Access: Protected
|
||||
// Description: Creates a toplevel window to contain the embedded
|
||||
// instances.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
make_parent_window() {
|
||||
// TODO.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::create_instance
|
||||
// Access: Protected
|
||||
// Description: Uses the plugin API to create a new P3D instance to
|
||||
// play a particular .p3d file. This instance is also
|
||||
// started if start_instance is true (which requires
|
||||
// that the named p3d file exists).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3D_instance *Panda3DBase::
|
||||
create_instance(const string &p3d, bool start_instance,
|
||||
int win_x, int win_y, int win_width, int win_height,
|
||||
char **args, int num_args, const int &p3d_offset) {
|
||||
// Check to see if the p3d filename we were given is a URL, or a
|
||||
// local file.
|
||||
Filename p3d_filename = Filename::from_os_specific(p3d);
|
||||
string os_p3d_filename = p3d;
|
||||
bool is_local = !is_url(p3d);
|
||||
if (is_local && start_instance) {
|
||||
if (!p3d_filename.exists()) {
|
||||
cerr << "No such file: " << p3d_filename << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
p3d_filename.make_absolute();
|
||||
os_p3d_filename = p3d_filename.to_os_specific();
|
||||
}
|
||||
|
||||
// Build up the token list.
|
||||
Tokens tokens = _tokens;
|
||||
P3D_token token;
|
||||
|
||||
string log_basename;
|
||||
if (!_log_dirname.empty()) {
|
||||
// Generate output to a logfile.
|
||||
log_basename = p3d_filename.get_basename_wo_extension();
|
||||
token._keyword = "log_basename";
|
||||
token._value = log_basename.c_str();
|
||||
tokens.push_back(token);
|
||||
} else {
|
||||
// Send output to the console.
|
||||
token._keyword = "console_output";
|
||||
if (_console_environment) {
|
||||
token._value = "1";
|
||||
} else {
|
||||
token._value = "0";
|
||||
}
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
token._keyword = "auto_start";
|
||||
token._value = "1";
|
||||
tokens.push_back(token);
|
||||
|
||||
P3D_token *tokens_p;
|
||||
size_t num_tokens = tokens.size();
|
||||
if (!tokens.empty()) {
|
||||
tokens_p = &tokens[0];
|
||||
}
|
||||
|
||||
// Build up the argument list, beginning with the p3d_filename.
|
||||
pvector<const char *> argv;
|
||||
argv.push_back(os_p3d_filename.c_str());
|
||||
for (int i = 0; i < num_args; ++i) {
|
||||
argv.push_back(args[i]);
|
||||
}
|
||||
|
||||
P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens,
|
||||
argv.size(), &argv[0], NULL);
|
||||
|
||||
if (inst != NULL) {
|
||||
if (start_instance) {
|
||||
// We call start() first, to give the core API a chance to
|
||||
// notice the "hidden" attrib before we set the window
|
||||
// parameters.
|
||||
P3D_instance_start(inst, is_local, os_p3d_filename.c_str(), p3d_offset);
|
||||
}
|
||||
|
||||
P3D_instance_setup_window
|
||||
(inst, _window_type, _win_x, _win_y, _win_width, _win_height, &_parent_window);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::delete_instance
|
||||
// Access: Protected
|
||||
// Description: Deletes the indicated instance and removes it from
|
||||
// the internal structures.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
delete_instance(P3D_instance *inst) {
|
||||
P3D_instance_finish(inst);
|
||||
_instances.erase(inst);
|
||||
|
||||
// Make sure we also terminate any pending URLGetters associated
|
||||
// with this instance.
|
||||
URLGetters::iterator gi;
|
||||
gi = _url_getters.begin();
|
||||
while (gi != _url_getters.end()) {
|
||||
URLGetter *getter = (*gi);
|
||||
if (getter->get_instance() == inst) {
|
||||
URLGetters::iterator dgi = gi;
|
||||
++gi;
|
||||
_url_getters.erase(dgi);
|
||||
delete getter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::parse_token
|
||||
// Access: Protected
|
||||
// Description: Parses a web token of the form token=value, and
|
||||
// stores it in _tokens. Returns true on success, false
|
||||
// on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3DBase::
|
||||
parse_token(char *arg) {
|
||||
char *equals = strchr(arg, '=');
|
||||
if (equals == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Directly munge the C string to truncate it at the equals sign.
|
||||
// Classic C tricks.
|
||||
*equals = '\0';
|
||||
P3D_token token;
|
||||
token._keyword = strdup(arg);
|
||||
token._value = strdup(equals + 1);
|
||||
*equals = '=';
|
||||
|
||||
_tokens.push_back(token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::parse_int_pair
|
||||
// Access: Protected
|
||||
// Description: Parses a string into an x,y pair of integers.
|
||||
// Returns true on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3DBase::
|
||||
parse_int_pair(char *arg, int &x, int &y) {
|
||||
char *endptr;
|
||||
x = strtol(arg, &endptr, 10);
|
||||
if (*endptr == ',') {
|
||||
y = strtol(endptr + 1, &endptr, 10);
|
||||
if (*endptr == '\0') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Some parse error on the string.
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::is_url
|
||||
// Access: Protected, Static
|
||||
// Description: Returns true if the indicated string appears to be a
|
||||
// URL, with a leading http:// or file:// or whatever,
|
||||
// or false if it must be a local filename instead.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3DBase::
|
||||
is_url(const string ¶m) {
|
||||
// We define a URL prefix as a sequence of at least two letters,
|
||||
// followed by a colon, followed by at least one slash.
|
||||
size_t p = 0;
|
||||
while (p < param.size() && isalpha(param[p])) {
|
||||
++p;
|
||||
}
|
||||
if (p < 2) {
|
||||
// Not enough letters.
|
||||
return false;
|
||||
}
|
||||
if (p >= param.size() || param[p] != ':') {
|
||||
// No colon.
|
||||
return false;
|
||||
}
|
||||
++p;
|
||||
if (p >= param.size() || param[p] != '/') {
|
||||
// No slash.
|
||||
return false;
|
||||
}
|
||||
|
||||
// It matches the rules.
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::report_downloading_package
|
||||
// Access: Protected
|
||||
// Description: Tells the user we have to download a package.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
report_downloading_package(P3D_instance *instance) {
|
||||
P3D_object *obj = P3D_instance_get_panda_script_object(instance);
|
||||
|
||||
P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName");
|
||||
if (display_name == NULL) {
|
||||
cerr << "Installing package.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
int name_length = P3D_object_get_string(display_name, NULL, 0);
|
||||
char *name = new char[name_length + 1];
|
||||
P3D_object_get_string(display_name, name, name_length + 1);
|
||||
|
||||
cerr << "Installing " << name << "\n";
|
||||
|
||||
delete[] name;
|
||||
P3D_object_decref(display_name);
|
||||
_reporting_download = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::report_download_complete
|
||||
// Access: Protected
|
||||
// Description: Tells the user we're done downloading packages
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
report_download_complete(P3D_instance *instance) {
|
||||
if (_reporting_download) {
|
||||
cerr << "Install complete.\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::st_timer_callback
|
||||
// Access: Protected, Static
|
||||
// Description: Installed as a timer on the event loop, so we can
|
||||
// process local events, in the Apple implementation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
pascal void Panda3DBase::
|
||||
st_timer_callback(EventLoopTimerRef timer, void *user_data) {
|
||||
((Panda3D *)user_data)->timer_callback(timer);
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
#ifdef __APPLE__
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::timer_callback
|
||||
// Access: Protected
|
||||
// Description: Installed as a timer on the event loop, so we can
|
||||
// process local events, in the Apple implementation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Panda3DBase::
|
||||
timer_callback(EventLoopTimerRef timer) {
|
||||
// Check for new requests from the Panda3D plugin.
|
||||
P3D_instance *inst = P3D_check_request(0.0);
|
||||
while (inst != (P3D_instance *)NULL) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
handle_request(request);
|
||||
}
|
||||
inst = P3D_check_request(0.0);
|
||||
}
|
||||
|
||||
// Check the download tasks.
|
||||
run_getters();
|
||||
|
||||
// If we're out of instances, exit the application.
|
||||
if (time_to_exit()) {
|
||||
QuitApplicationEventLoop();
|
||||
}
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::URLGetter::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Panda3DBase::URLGetter::
|
||||
URLGetter(P3D_instance *instance, int unique_id,
|
||||
const URLSpec &url, const string &post_data) :
|
||||
_instance(instance),
|
||||
_unique_id(unique_id),
|
||||
_url(url),
|
||||
_post_data(post_data)
|
||||
{
|
||||
HTTPClient *http = HTTPClient::get_global_ptr();
|
||||
|
||||
_channel = http->make_channel(false);
|
||||
// _channel->set_download_throttle(true);
|
||||
if (_post_data.empty()) {
|
||||
_channel->begin_get_document(_url);
|
||||
} else {
|
||||
_channel->begin_post_form(_url, _post_data);
|
||||
}
|
||||
|
||||
_channel->download_to_ram(&_rf);
|
||||
_bytes_sent = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3DBase::URLGetter::run
|
||||
// Access: Public
|
||||
// Description: Polls the URLGetter for new results. Returns true if
|
||||
// the URL request is still in progress and run() should
|
||||
// be called again later, or false if the URL request
|
||||
// has been completed and run() should not be called
|
||||
// again.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3DBase::URLGetter::
|
||||
run() {
|
||||
if (_channel->run() || _rf.get_data_size() != 0) {
|
||||
if (_rf.get_data_size() != 0) {
|
||||
// Got some new data.
|
||||
bool download_ok = P3D_instance_feed_url_stream
|
||||
(_instance, _unique_id, P3D_RC_in_progress,
|
||||
_channel->get_status_code(),
|
||||
_channel->get_file_size(),
|
||||
(const unsigned char *)_rf.get_data().data(), _rf.get_data_size());
|
||||
_bytes_sent += _rf.get_data_size();
|
||||
_rf.clear();
|
||||
|
||||
if (!download_ok) {
|
||||
// The plugin doesn't care any more. Interrupt the download.
|
||||
cerr << "Download interrupted: " << _url
|
||||
<< ", after " << _bytes_sent << " of " << _channel->get_file_size()
|
||||
<< " bytes.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Still more to come; call this method again later.
|
||||
return true;
|
||||
}
|
||||
|
||||
// All done.
|
||||
P3D_result_code status = P3D_RC_done;
|
||||
if (!_channel->is_valid()) {
|
||||
if (_channel->get_status_code() != 0) {
|
||||
status = P3D_RC_http_error;
|
||||
} else {
|
||||
status = P3D_RC_generic_error;
|
||||
}
|
||||
cerr << "Error getting URL " << _url << "\n";
|
||||
}
|
||||
|
||||
P3D_instance_feed_url_stream
|
||||
(_instance, _unique_id, status,
|
||||
_channel->get_status_code(),
|
||||
_bytes_sent, NULL, 0);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// Filename: panda3dBase.h
|
||||
// Created by: pro-rsoft (07Dec09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 PANDA3DBASE_H
|
||||
#define PANDA3DBASE_H
|
||||
|
||||
// This program must link with Panda for HTTPClient support. This
|
||||
// means it probably should be built with LINK_ALL_STATIC defined, so
|
||||
// we won't have to deal with confusing .dll or .so files that might
|
||||
// compete on the disk with the dynamically-loaded versions. There's
|
||||
// no competition in memory address space, though, because
|
||||
// p3d_plugin--the only file we dynamically link in--doesn't itself
|
||||
// link with Panda.
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "p3d_plugin.h"
|
||||
#include "httpChannel.h"
|
||||
#include "ramfile.h"
|
||||
#include "fileSpec.h"
|
||||
#include "pset.h"
|
||||
#include "vector_string.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : Panda3DBase
|
||||
// Description : Base for creating a standalone application that
|
||||
// invokes the panda3d plugin to launch .p3d files.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class Panda3DBase {
|
||||
public:
|
||||
Panda3DBase(bool console_environment);
|
||||
|
||||
int run_embedded(int read_offset, int argc, char *argv[]);
|
||||
void run_main_loop();
|
||||
|
||||
protected:
|
||||
void run_getters();
|
||||
void handle_request(P3D_request *request);
|
||||
void make_parent_window();
|
||||
|
||||
P3D_instance *
|
||||
create_instance(const string &p3d, bool start_instance,
|
||||
int win_x, int win_y, int win_width, int win_height,
|
||||
char **args, int num_args, const int &p3d_offset = 0);
|
||||
void delete_instance(P3D_instance *instance);
|
||||
|
||||
bool parse_token(char *arg);
|
||||
bool parse_int_pair(char *arg, int &x, int &y);
|
||||
static bool is_url(const string ¶m);
|
||||
|
||||
void report_downloading_package(P3D_instance *instance);
|
||||
void report_download_complete(P3D_instance *instance);
|
||||
|
||||
inline bool time_to_exit();
|
||||
|
||||
#ifdef __APPLE__
|
||||
static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data);
|
||||
void timer_callback(EventLoopTimerRef timer);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
string _host_url;
|
||||
string _root_dir;
|
||||
string _log_dirname;
|
||||
string _log_basename;
|
||||
string _this_platform;
|
||||
bool _verify_contents;
|
||||
P3D_window_type _window_type;
|
||||
P3D_window_handle _parent_window;
|
||||
int _win_x, _win_y;
|
||||
int _win_width, _win_height;
|
||||
bool _exit_with_last_instance;
|
||||
|
||||
bool _reporting_download;
|
||||
bool _enable_security;
|
||||
bool _console_environment;
|
||||
|
||||
typedef pset<P3D_instance *> Instances;
|
||||
Instances _instances;
|
||||
|
||||
typedef pvector<P3D_token> Tokens;
|
||||
Tokens _tokens;
|
||||
|
||||
// This nested class keeps track of active URL requests.
|
||||
class URLGetter {
|
||||
public:
|
||||
URLGetter(P3D_instance *instance, int unique_id,
|
||||
const URLSpec &url, const string &post_data);
|
||||
|
||||
bool run();
|
||||
inline P3D_instance *get_instance();
|
||||
|
||||
private:
|
||||
P3D_instance *_instance;
|
||||
int _unique_id;
|
||||
URLSpec _url;
|
||||
string _post_data;
|
||||
|
||||
PT(HTTPChannel) _channel;
|
||||
Ramfile _rf;
|
||||
size_t _bytes_sent;
|
||||
};
|
||||
|
||||
typedef pset<URLGetter *> URLGetters;
|
||||
URLGetters _url_getters;
|
||||
};
|
||||
|
||||
#include "panda3dBase.I"
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue