new multifile format for upcoming support of direct loading from multifiles
This commit is contained in:
parent
2919377449
commit
b1dc8b2fa6
|
|
@ -1,347 +0,0 @@
|
|||
// Filename: asyncDecompressor.cxx
|
||||
// Created by: mike (09Jan97)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is compiled only if we have zlib installed.
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "config_downloader.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <pt_Event.h>
|
||||
#include <throw_event.h>
|
||||
#include <eventParameter.h>
|
||||
#include <filename.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "asyncDecompressor.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Defines
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : DecompressorToken
|
||||
// Description : Holds a request for the decompressor.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class DecompressorToken : public ReferenceCount {
|
||||
public:
|
||||
INLINE DecompressorToken(uint id, const Filename &source_file,
|
||||
const Filename &dest_file, const string &event_name) {
|
||||
_id = id;
|
||||
_source_file = source_file;
|
||||
_dest_file = dest_file;
|
||||
_event_name = event_name;
|
||||
}
|
||||
int _id;
|
||||
Filename _source_file;
|
||||
Filename _dest_file;
|
||||
string _event_name;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Decompressor::
|
||||
Decompressor(void) : AsyncUtility() {
|
||||
PT(Buffer) buffer = new Buffer(decompressor_buffer_size);
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Decompressor::
|
||||
Decompressor(PT(Buffer) buffer) : AsyncUtility() {
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::Constructor
|
||||
// Access: Private
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Decompressor::
|
||||
init(PT(Buffer) buffer) {
|
||||
nassertv(!buffer.is_null());
|
||||
_frequency = decompressor_frequency;
|
||||
_token_board = new DecompressorTokenBoard;
|
||||
_half_buffer_length = buffer->get_length()/2;
|
||||
_buffer = buffer;
|
||||
char *temp_name = tempnam(NULL, "dc");
|
||||
_temp_file_name = temp_name;
|
||||
_temp_file_name.set_binary();
|
||||
delete temp_name;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::Destructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Decompressor::
|
||||
~Decompressor(void) {
|
||||
destroy_thread();
|
||||
|
||||
delete _token_board;
|
||||
_temp_file_name.unlink();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::request_decompress
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Decompressor::
|
||||
request_decompress(const Filename &source_file, const string &event_name) {
|
||||
Filename dest_file = source_file;
|
||||
string extension = source_file.get_extension();
|
||||
if (extension == "pz")
|
||||
dest_file = source_file.get_fullpath_wo_extension();
|
||||
else {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Decompressor::request_decompress() - Unknown file extension: ."
|
||||
<< extension << endl;
|
||||
}
|
||||
return request_decompress(source_file, dest_file, event_name);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::request_decompress
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Decompressor::
|
||||
request_decompress(const Filename &source_file, const Filename &dest_file,
|
||||
const string &event_name) {
|
||||
|
||||
PT(DecompressorToken) tok;
|
||||
if (_threads_enabled) {
|
||||
|
||||
// Make sure we actually are threaded
|
||||
if (!_threaded) {
|
||||
downloader_cat.info()
|
||||
<< "Decompressor::request_decompress() - 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 HAVE_IPC
|
||||
_lock.lock();
|
||||
#endif
|
||||
|
||||
if (_token_board->_waiting.is_full()) {
|
||||
downloader_cat.error()
|
||||
<< "Downloader::request_download() - Too many pending requests\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Decompress requested for file: " << source_file << endl;
|
||||
}
|
||||
|
||||
tok = new DecompressorToken(_next_token++, source_file, dest_file,
|
||||
event_name);
|
||||
_token_board->_waiting.insert(tok);
|
||||
|
||||
#ifdef 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.is_full()) {
|
||||
downloader_cat.error()
|
||||
<< "Downloader::request_download() - Too many pending requests\n";
|
||||
return 0;
|
||||
}
|
||||
if (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Decompress requested for file: " << source_file << endl;
|
||||
}
|
||||
|
||||
tok = new DecompressorToken(_next_token++, source_file, dest_file,
|
||||
event_name);
|
||||
_token_board->_waiting.insert(tok);
|
||||
process_request();
|
||||
}
|
||||
|
||||
return tok->_id;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::process_request
|
||||
// Access: Private
|
||||
// Description: Serves any requests on the token board, moving them
|
||||
// to the done queue.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Decompressor::
|
||||
process_request() {
|
||||
if (_shutdown) {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Decompressor shutting down...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is actually a request token - process it
|
||||
while (!_token_board->_waiting.is_empty()) {
|
||||
PT(DecompressorToken) tok = _token_board->_waiting.extract();
|
||||
if (decompress(tok->_source_file, tok->_dest_file)) {
|
||||
_token_board->_done.insert(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 (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Decompressor::process_request() - decompress complete for "
|
||||
<< tok->_source_file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::decompress
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Decompressor::
|
||||
decompress(Filename &source_file) {
|
||||
Filename dest_file = source_file;
|
||||
string extension = source_file.get_extension();
|
||||
if (extension == "pz")
|
||||
dest_file = source_file.get_fullpath_wo_extension();
|
||||
else {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Decompressor::request_decompress() - Unknown file extension: ."
|
||||
<< extension << endl;
|
||||
return false;
|
||||
}
|
||||
return decompress(source_file, dest_file);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Decompressor::decompress
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Decompressor::
|
||||
decompress(Filename &source_file, Filename &dest_file) {
|
||||
|
||||
// Open source file
|
||||
ifstream read_stream;
|
||||
source_file.set_binary();
|
||||
if (!source_file.open_read(read_stream)) {
|
||||
downloader_cat.error()
|
||||
<< "Decompressor::decompress() - Error opening source file: "
|
||||
<< source_file << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine source file length
|
||||
read_stream.seekg(0, ios::end);
|
||||
int source_file_length = read_stream.tellg();
|
||||
if (source_file_length == 0) {
|
||||
downloader_cat.warning()
|
||||
<< "Decompressor::decompress() - Zero length file: "
|
||||
<< source_file << endl;
|
||||
return true;
|
||||
}
|
||||
read_stream.seekg(0, ios::beg);
|
||||
|
||||
// Open destination file
|
||||
ofstream write_stream;
|
||||
dest_file.set_binary();
|
||||
if (!dest_file.open_write(write_stream)) {
|
||||
downloader_cat.error()
|
||||
<< "Decompressor::decompress() - Error opening dest file: "
|
||||
<< source_file << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read from the source file into the first half of the buffer,
|
||||
// decompress into the second half of the buffer, write the second
|
||||
// half of the buffer to disk, and repeat.
|
||||
int total_bytes_read = 0;
|
||||
bool read_all_input = false;
|
||||
bool handled_all_input = false;
|
||||
int source_buffer_length;
|
||||
ZDecompressor decompressor;
|
||||
while (handled_all_input == false) {
|
||||
|
||||
// See if there is anything left in the source file
|
||||
if (read_all_input == false) {
|
||||
read_stream.read(_buffer->_buffer, _half_buffer_length);
|
||||
source_buffer_length = read_stream.gcount();
|
||||
total_bytes_read += source_buffer_length;
|
||||
if (read_stream.eof()) {
|
||||
nassertr(total_bytes_read == source_file_length, false);
|
||||
read_all_input = true;
|
||||
}
|
||||
}
|
||||
|
||||
char *next_in = _buffer->_buffer;
|
||||
int avail_in = source_buffer_length;
|
||||
char *dest_buffer = _buffer->_buffer + source_buffer_length;
|
||||
char *next_out = dest_buffer;
|
||||
int dest_buffer_length = _buffer->get_length() - source_buffer_length;
|
||||
int avail_out = dest_buffer_length;
|
||||
nassertr(avail_out > 0 && avail_in > 0, false);
|
||||
|
||||
while (avail_in > 0) {
|
||||
int ret = decompressor.decompress_to_stream(next_in, avail_in,
|
||||
next_out, avail_out, dest_buffer,
|
||||
dest_buffer_length, write_stream);
|
||||
if (ret == ZCompressorBase::S_error)
|
||||
return false;
|
||||
if ((int)decompressor.get_total_in() == source_file_length &&
|
||||
avail_out == dest_buffer_length)
|
||||
handled_all_input = true;
|
||||
}
|
||||
|
||||
nap();
|
||||
|
||||
}
|
||||
|
||||
read_stream.close();
|
||||
write_stream.close();
|
||||
|
||||
source_file.unlink();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// Filename: asyncDecompressor.h
|
||||
// Created by: mike (09Jan97)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#ifndef ASYNCDECOMPRESSOR_H
|
||||
#define ASYNCDECOMPRESSOR_H
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#include <pandabase.h>
|
||||
#include <filename.h>
|
||||
#include <tokenBoard.h>
|
||||
#include <buffer.h>
|
||||
#include "zcompressor.h"
|
||||
#include "asyncUtility.h"
|
||||
|
||||
class DecompressorToken;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : Decompressor
|
||||
// Description :
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS Decompressor : public AsyncUtility {
|
||||
PUBLISHED:
|
||||
Decompressor(void);
|
||||
Decompressor(PT(Buffer) buffer);
|
||||
virtual ~Decompressor(void);
|
||||
|
||||
int request_decompress(const Filename &source_file,
|
||||
const string &event_name);
|
||||
int request_decompress(const Filename &source_file,
|
||||
const Filename &dest_file,
|
||||
const string &event_name);
|
||||
|
||||
bool decompress(Filename &source_file);
|
||||
bool decompress(Filename &source_file, Filename &dest_file);
|
||||
|
||||
private:
|
||||
void init(PT(Buffer) buffer);
|
||||
virtual bool process_request(void);
|
||||
|
||||
typedef TokenBoard<DecompressorToken> DecompressorTokenBoard;
|
||||
DecompressorTokenBoard *_token_board;
|
||||
|
||||
PT(Buffer) _buffer;
|
||||
int _half_buffer_length;
|
||||
Filename _temp_file_name;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
// Filename: asyncDownloader.I
|
||||
// Created by: mike (09Jan97)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "config_downloader.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::set_byte_rate
|
||||
// Access: Public
|
||||
// Description: Note: modem speeds are reported in bits, so you
|
||||
// need to convert!
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void Downloader::
|
||||
set_byte_rate(float bytes) {
|
||||
nassertv(bytes > 0.0);
|
||||
if (bytes == _byte_rate)
|
||||
return;
|
||||
#ifdef HAVE_IPC
|
||||
_buffer_lock.lock();
|
||||
#endif
|
||||
|
||||
_new_byte_rate = bytes;
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
_buffer_lock.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::get_byte_rate
|
||||
// Access: Public
|
||||
// Description: Returns byte rate in bytes.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float Downloader::
|
||||
get_byte_rate(void) const {
|
||||
return _byte_rate;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::enable_download
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void Downloader::
|
||||
enable_download(bool val) {
|
||||
_download_enabled = val;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::is_download_enabled
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Downloader::
|
||||
is_download_enabled(void) const {
|
||||
return _download_enabled;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::set_disk_write_frequency
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void Downloader::
|
||||
set_disk_write_frequency(int frequency) {
|
||||
nassertv(frequency > 0);
|
||||
#ifdef HAVE_IPC
|
||||
_buffer_lock.lock();
|
||||
#endif
|
||||
|
||||
_new_disk_write_frequency = frequency;
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
_buffer_lock.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::get_disk_write_frequency
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int Downloader::
|
||||
get_disk_write_frequency(void) const {
|
||||
return _disk_write_frequency;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Downloader::get_last_attempt_stalled
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Downloader::
|
||||
get_last_attempt_stalled(void) const {
|
||||
return _last_attempt_stalled;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,150 +0,0 @@
|
|||
// Filename: asyncDownloader.h
|
||||
// Created by: mike (09Jan97)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#ifndef ASYNCDOWNLOADER_H
|
||||
#define ASYNCDOWNLOADER_H
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#include <pandabase.h>
|
||||
#include <notify.h>
|
||||
#include <filename.h>
|
||||
#include <tokenBoard.h>
|
||||
#include <buffer.h>
|
||||
#include "asyncUtility.h"
|
||||
|
||||
#if defined(WIN32_VC)
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <netinet/in.h> // Irix seems to require this one for resolv.h.
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <resolv.h>
|
||||
#endif
|
||||
|
||||
class DownloaderToken;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : Downloader
|
||||
// Description :
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS Downloader : public AsyncUtility {
|
||||
PUBLISHED:
|
||||
enum DownloadCode {
|
||||
DS_write = 2,
|
||||
DS_success = 1,
|
||||
DS_abort = -1,
|
||||
DS_timeout = -2
|
||||
};
|
||||
Downloader(void);
|
||||
//Downloader(PT(Buffer) buffer);
|
||||
virtual ~Downloader(void);
|
||||
|
||||
bool connect_to_server(const string &name, uint port=80);
|
||||
void disconnect_from_server(void);
|
||||
|
||||
int request_sync_download(const string &file_name, const Filename &file_dest,
|
||||
const string &event_name);
|
||||
int request_sync_download(const string &file_name, const Filename &file_dest,
|
||||
const string &event_name, int first_byte,
|
||||
int last_byte, int total_bytes,
|
||||
bool partial_content = true);
|
||||
int request_download(const string &file_name, const Filename &file_dest,
|
||||
const string &event_name, bool sync = false);
|
||||
int request_download(const string &file_name, const Filename &file_dest,
|
||||
const string &event_name, int first_byte,
|
||||
int last_byte, int total_bytes,
|
||||
bool partial_content = true, bool sync = false);
|
||||
|
||||
INLINE void set_byte_rate(float bytes);
|
||||
INLINE float get_byte_rate(void) const;
|
||||
INLINE void set_disk_write_frequency(int frequency);
|
||||
INLINE int get_disk_write_frequency(void) const;
|
||||
INLINE void enable_download(bool val);
|
||||
INLINE bool is_download_enabled(void) const;
|
||||
INLINE bool get_last_attempt_stalled(void) const;
|
||||
|
||||
private:
|
||||
class DownloadStatus {
|
||||
public:
|
||||
DownloadStatus(char *buffer, const string &event_name, int first_byte,
|
||||
int last_byte, int total_bytes, bool partial_content,
|
||||
uint id);
|
||||
void reset(void);
|
||||
|
||||
public:
|
||||
bool _first_line_complete;
|
||||
bool _header_is_complete;
|
||||
bool _header_is_valid;
|
||||
char *_start;
|
||||
char *_next_in;
|
||||
int _bytes_in_buffer;
|
||||
string _event_name;
|
||||
int _total_bytes_written;
|
||||
int _first_byte;
|
||||
int _last_byte;
|
||||
int _total_bytes;
|
||||
bool _partial_content;
|
||||
uint _id;
|
||||
char *_buffer;
|
||||
};
|
||||
|
||||
void init();
|
||||
int download(const string &file_name, Filename file_dest,
|
||||
const string &event_name, int first_byte,
|
||||
int last_byte, int total_bytes, bool partial_content,
|
||||
bool sync, uint id);
|
||||
virtual bool process_request(void);
|
||||
bool parse_header(DownloadStatus &status);
|
||||
bool write_to_disk(DownloadStatus &status);
|
||||
bool connect_to_server(void);
|
||||
int safe_send(int socket, const char *data, int length, long timeout);
|
||||
int safe_receive(int socket, DownloadStatus &status, int length,
|
||||
long timeout, int &bytes);
|
||||
bool parse_http_response(const string &resp);
|
||||
int attempt_read(int length, DownloadStatus &status, int &bytes_read);
|
||||
|
||||
typedef TokenBoard<DownloaderToken> DownloaderTokenBoard;
|
||||
DownloaderTokenBoard *_token_board;
|
||||
|
||||
bool _connected;
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
mutex _buffer_lock;
|
||||
#endif
|
||||
|
||||
int _socket;
|
||||
PT(Buffer) _buffer;
|
||||
int _disk_write_frequency;
|
||||
int _new_disk_write_frequency;
|
||||
float _byte_rate;
|
||||
float _new_byte_rate;
|
||||
int _read_size;
|
||||
bool _download_enabled;
|
||||
ofstream _dest_stream;
|
||||
int _disk_buffer_size;
|
||||
bool _last_attempt_stalled;
|
||||
bool _current_attempt_stalled;
|
||||
|
||||
string _server_name;
|
||||
struct sockaddr_in _sin;
|
||||
};
|
||||
|
||||
#include "asyncDownloader.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
// Filename: asyncExtractor.cxx
|
||||
// Created by: mike (09Jan97)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "config_downloader.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <pt_Event.h>
|
||||
#include <throw_event.h>
|
||||
#include <eventParameter.h>
|
||||
#include <filename.h>
|
||||
|
||||
#include "asyncExtractor.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Defines
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ExtractorToken
|
||||
// Description : Holds a request for the extractor.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class ExtractorToken : public ReferenceCount {
|
||||
public:
|
||||
INLINE ExtractorToken(uint id, const Filename &source_file,
|
||||
const string &event_name,
|
||||
const Filename &rel_path) {
|
||||
_id = id;
|
||||
_source_file = source_file;
|
||||
_event_name = event_name;
|
||||
_rel_path = rel_path;
|
||||
}
|
||||
int _id;
|
||||
Filename _source_file;
|
||||
string _event_name;
|
||||
Filename _rel_path;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Extractor::
|
||||
Extractor(void) : AsyncUtility() {
|
||||
PT(Buffer) buffer = new Buffer(extractor_buffer_size);
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Extractor::
|
||||
Extractor(PT(Buffer) buffer) : AsyncUtility() {
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Constructor
|
||||
// Access: Private
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Extractor::
|
||||
init(PT(Buffer) buffer) {
|
||||
nassertv(!buffer.is_null());
|
||||
_frequency = extractor_frequency;
|
||||
_token_board = new ExtractorTokenBoard;
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Destructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Extractor::
|
||||
~Extractor(void) {
|
||||
destroy_thread();
|
||||
|
||||
delete _token_board;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::request_extract
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Extractor::
|
||||
request_extract(const Filename &source_file, const string &event_name,
|
||||
const Filename &rel_path) {
|
||||
|
||||
PT(ExtractorToken) tok;
|
||||
if (_threads_enabled) {
|
||||
|
||||
// Make sure we actually are threaded
|
||||
if (!_threaded) {
|
||||
downloader_cat.info()
|
||||
<< "Extractor::request_extract() - 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 HAVE_IPC
|
||||
_lock.lock();
|
||||
#endif
|
||||
|
||||
if (_token_board->_waiting.is_full()) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::request_extract() - Too many pending requests\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Extract requested for file: " << source_file << endl;
|
||||
}
|
||||
|
||||
tok = new ExtractorToken(_next_token++, source_file, event_name,
|
||||
rel_path);
|
||||
_token_board->_waiting.insert(tok);
|
||||
|
||||
#ifdef 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.is_full()) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::request_extract() - Too many pending requests\n";
|
||||
return 0;
|
||||
}
|
||||
if (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Extract requested for file: " << source_file << endl;
|
||||
}
|
||||
|
||||
tok = new ExtractorToken(_next_token++, source_file, event_name,
|
||||
rel_path);
|
||||
_token_board->_waiting.insert(tok);
|
||||
process_request();
|
||||
}
|
||||
|
||||
return tok->_id;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::process_request
|
||||
// Access: Private
|
||||
// Description: Serves any requests on the token board, moving them
|
||||
// to the done queue.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Extractor::
|
||||
process_request() {
|
||||
if (_shutdown) {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Extractor shutting down...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is actually a request token - process it
|
||||
while (!_token_board->_waiting.is_empty()) {
|
||||
PT(ExtractorToken) tok = _token_board->_waiting.extract();
|
||||
if (extract(tok->_source_file, tok->_rel_path)) {
|
||||
_token_board->_done.insert(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 (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Extractor::process_request() - extract complete for "
|
||||
<< tok->_source_file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::extract
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Extractor::
|
||||
extract(Filename &source_file, const Filename &rel_path) {
|
||||
|
||||
// Open source file
|
||||
ifstream read_stream;
|
||||
source_file.set_binary();
|
||||
if (!source_file.open_read(read_stream)) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::extract() - Error opening source file: "
|
||||
<< source_file << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine source file length
|
||||
read_stream.seekg(0, ios::end);
|
||||
int source_file_length = read_stream.tellg();
|
||||
read_stream.seekg(0, ios::beg);
|
||||
|
||||
// Read the multifile header
|
||||
Multifile mfile;
|
||||
|
||||
// Read from the source file and write to the appropriate extracted file
|
||||
int total_bytes_read = 0;
|
||||
bool read_all_input = false;
|
||||
bool handled_all_input = false;
|
||||
int source_buffer_length;
|
||||
while (handled_all_input == false) {
|
||||
|
||||
// See if there is anything left in the source file
|
||||
if (read_all_input == false) {
|
||||
read_stream.read(_buffer->_buffer, _buffer->get_length());
|
||||
source_buffer_length = read_stream.gcount();
|
||||
total_bytes_read += source_buffer_length;
|
||||
if (read_stream.eof()) {
|
||||
nassertr(total_bytes_read == source_file_length, false);
|
||||
read_all_input = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Write to the out file
|
||||
char *start = _buffer->_buffer;
|
||||
int size = source_buffer_length;
|
||||
if (mfile.write_extract(start, size, rel_path) == true)
|
||||
handled_all_input = true;
|
||||
|
||||
nap();
|
||||
}
|
||||
|
||||
read_stream.close();
|
||||
source_file.unlink();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// Filename: asyncExtractor.h
|
||||
// Created by: mike (09Jan97)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#ifndef ASYNCEXTRACTOR_H
|
||||
#define ASYNCEXTRACTOR_H
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#include <pandabase.h>
|
||||
#include <filename.h>
|
||||
#include <tokenBoard.h>
|
||||
#include <buffer.h>
|
||||
#include <multifile.h>
|
||||
#include "asyncUtility.h"
|
||||
|
||||
class ExtractorToken;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : Extractor
|
||||
// Description :
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS Extractor : public AsyncUtility {
|
||||
PUBLISHED:
|
||||
Extractor(void);
|
||||
Extractor(PT(Buffer) buffer);
|
||||
virtual ~Extractor(void);
|
||||
|
||||
int request_extract(const Filename &source_file,
|
||||
const string &event_name, const Filename &rel_path = "");
|
||||
|
||||
bool extract(Filename &source_file, const Filename &rel_path);
|
||||
|
||||
private:
|
||||
void init(PT(Buffer) buffer);
|
||||
virtual bool process_request(void);
|
||||
|
||||
typedef TokenBoard<ExtractorToken> ExtractorTokenBoard;
|
||||
ExtractorTokenBoard *_token_board;
|
||||
|
||||
PT(Buffer) _buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -21,16 +21,11 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::get_progress
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Description: Returns the fraction of the Multifile extracted so
|
||||
// far.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float Extractor::
|
||||
get_progress(void) const {
|
||||
if (_initiated == false) {
|
||||
downloader_cat.warning()
|
||||
<< "Extractor::get_progress() - Extraction has not been initiated"
|
||||
<< endl;
|
||||
return 0.0;
|
||||
}
|
||||
nassertr(_source_file_length > 0, 0.0);
|
||||
return ((float)_total_bytes_read / (float)_source_file_length);
|
||||
get_progress() const {
|
||||
nassertr(_initiated, 0.0f);
|
||||
return ((float)_subfile_index / (float)(_multifile.get_num_subfiles() + 1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,12 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "extractor.h"
|
||||
#include "config_downloader.h"
|
||||
|
||||
#include <filename.h>
|
||||
#include <error_utils.h>
|
||||
#include "filename.h"
|
||||
#include "error_utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include "extractor.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Defines
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Constructor
|
||||
|
|
@ -34,35 +29,8 @@
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Extractor::
|
||||
Extractor(void) {
|
||||
PT(Buffer) buffer = new Buffer(extractor_buffer_size);
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Extractor::
|
||||
Extractor(PT(Buffer) buffer) {
|
||||
init(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::Constructor
|
||||
// Access: Private
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Extractor::
|
||||
init(PT(Buffer) buffer) {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Extractor constructor called" << endl;
|
||||
Extractor() {
|
||||
_initiated = false;
|
||||
nassertv(!buffer.is_null());
|
||||
_buffer = buffer;
|
||||
_mfile = NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -71,136 +39,163 @@ init(PT(Buffer) buffer) {
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Extractor::
|
||||
~Extractor(void) {
|
||||
~Extractor() {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Extractor destructor called" << endl;
|
||||
if (_initiated == true)
|
||||
if (_initiated) {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::initiate
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Description: Begins the extraction process. Returns EU_success if
|
||||
// successful, EU_error_abort otherwise.
|
||||
//
|
||||
// After calling initiate(), you should repeatedly call
|
||||
// run() as a background task until the file is
|
||||
// completely extracted.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Extractor::
|
||||
initiate(Filename &source_file, const Filename &rel_path) {
|
||||
|
||||
if (_initiated == true) {
|
||||
initiate(const Filename &multifile_name, const Filename &extract_to) {
|
||||
if (_initiated) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::initiate() - Extraction has already been initiated"
|
||||
<< endl;
|
||||
return EU_error_abort;
|
||||
}
|
||||
|
||||
// Open source file
|
||||
_source_file = source_file;
|
||||
_source_file.set_binary();
|
||||
if (!_source_file.open_read(_read_stream)) {
|
||||
_multifile_name = multifile_name;
|
||||
_extract_to = extract_to;
|
||||
|
||||
if (!_multifile.open_read(_multifile_name)) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::extract() - Error opening source file: "
|
||||
<< _source_file << " : " << strerror(errno) << endl;
|
||||
return get_write_error();
|
||||
<< "Error opening Multifile " << _multifile_name << ".\n";
|
||||
return EU_error_abort;
|
||||
}
|
||||
|
||||
_rel_path = rel_path;
|
||||
|
||||
// Determine source file length
|
||||
_read_stream.seekg(0, ios::end);
|
||||
_source_file_length = _read_stream.tellg();
|
||||
_read_stream.seekg(0, ios::beg);
|
||||
|
||||
_total_bytes_read = 0;
|
||||
_read_all_input = false;
|
||||
_handled_all_input = false;
|
||||
_mfile = new Multifile();
|
||||
_subfile_index = 0;
|
||||
_subfile_pos = 0;
|
||||
_subfile_length = 0;
|
||||
_read = (istream *)NULL;
|
||||
_initiated = true;
|
||||
|
||||
return EU_success;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::run
|
||||
// Access: Public
|
||||
// Description: Extracts the next small unit of data from the
|
||||
// Multifile. Returns EU_ok if progress is continuing,
|
||||
// EU_error_abort if there is a problem, or EU_success
|
||||
// when the last piece has been extracted.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Extractor::
|
||||
run() {
|
||||
if (!_initiated) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::run() - Extraction has not been initiated"
|
||||
<< endl;
|
||||
return EU_error_abort;
|
||||
}
|
||||
|
||||
if (_read == (istream *)NULL) {
|
||||
// Time to open the next subfile.
|
||||
if (_subfile_index >= _multifile.get_num_subfiles()) {
|
||||
// All done!
|
||||
cleanup();
|
||||
return EU_success;
|
||||
}
|
||||
|
||||
Filename subfile_filename(_extract_to,
|
||||
_multifile.get_subfile_name(_subfile_index));
|
||||
subfile_filename.set_binary();
|
||||
subfile_filename.make_dir();
|
||||
if (!subfile_filename.open_write(_write)) {
|
||||
downloader_cat.error()
|
||||
<< "Unable to write to " << subfile_filename << ".\n";
|
||||
cleanup();
|
||||
return EU_error_abort;
|
||||
}
|
||||
|
||||
_subfile_length = _multifile.get_subfile_length(_subfile_index);
|
||||
_subfile_pos = 0;
|
||||
_read = &_multifile.open_read_subfile(_subfile_index);
|
||||
|
||||
} else if (_subfile_pos >= _subfile_length) {
|
||||
// Time to close this subfile.
|
||||
_multifile.close_subfile();
|
||||
_write.close();
|
||||
_read = (istream *)NULL;
|
||||
_subfile_index++;
|
||||
|
||||
} else {
|
||||
// Read a number of bytes from the subfile and write them to the
|
||||
// output.
|
||||
size_t max_bytes = min((size_t)extractor_buffer_size,
|
||||
_subfile_length - _subfile_pos);
|
||||
for (size_t p = 0; p < max_bytes; p++) {
|
||||
int byte = _read->get();
|
||||
if (_read->eof() || _read->fail()) {
|
||||
downloader_cat.error()
|
||||
<< "Unexpected EOF on multifile " << _multifile_name << ".\n";
|
||||
cleanup();
|
||||
return EU_error_abort;
|
||||
}
|
||||
_write.put(byte);
|
||||
}
|
||||
_subfile_pos += max_bytes;
|
||||
}
|
||||
|
||||
return EU_ok;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::extract
|
||||
// Access: Public
|
||||
// Description: A convenience function to extract the Multifile all
|
||||
// at once, when you don't care about doing it in the
|
||||
// background.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Extractor::
|
||||
extract(const Filename &source_file, const Filename &rel_path) {
|
||||
int ret = initiate(source_file, rel_path);
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
for (;;) {
|
||||
ret = run();
|
||||
if (ret == EU_success) {
|
||||
return true;
|
||||
}
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::cleanup
|
||||
// Access: Private
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void Extractor::
|
||||
cleanup(void) {
|
||||
cleanup() {
|
||||
if (downloader_cat.is_debug())
|
||||
downloader_cat.debug()
|
||||
<< "Extractor cleanup called" << endl;
|
||||
if (_initiated == false) {
|
||||
if (!_initiated) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::cleanup() - Extraction has not been initiated"
|
||||
<< endl;
|
||||
return;
|
||||
}
|
||||
|
||||
delete _mfile;
|
||||
_mfile = NULL;
|
||||
_read_stream.close();
|
||||
// _source_file.unlink();
|
||||
_initiated = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::run
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int Extractor::
|
||||
run(void) {
|
||||
if (_initiated == false) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::run() - Extraction has not been initiated"
|
||||
<< endl;
|
||||
return EU_error_abort;
|
||||
}
|
||||
|
||||
// See if there is anything left in the source file
|
||||
if (_read_all_input == false) {
|
||||
_read_stream.read(_buffer->_buffer, _buffer->get_length());
|
||||
_source_buffer_length = _read_stream.gcount();
|
||||
_total_bytes_read += _source_buffer_length;
|
||||
if (_read_stream.eof()) {
|
||||
nassertr(_total_bytes_read == _source_file_length, false);
|
||||
_read_all_input = true;
|
||||
}
|
||||
}
|
||||
|
||||
char *buffer_start = _buffer->_buffer;
|
||||
int buffer_size = _source_buffer_length;
|
||||
|
||||
// Write to the out file
|
||||
int write_ret = _mfile->write(buffer_start, buffer_size, _rel_path);
|
||||
if (write_ret == EU_success) {
|
||||
cleanup();
|
||||
return EU_success;
|
||||
} else if (write_ret < 0) {
|
||||
downloader_cat.error()
|
||||
<< "Extractor::run() - got error from Multifile: " << write_ret
|
||||
<< endl;
|
||||
return write_ret;
|
||||
}
|
||||
return EU_ok;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Extractor::extract
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Extractor::
|
||||
extract(Filename &source_file, const Filename &rel_path) {
|
||||
int ret = initiate(source_file, rel_path);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
for (;;) {
|
||||
ret = run();
|
||||
if (ret == EU_success)
|
||||
return true;
|
||||
if (ret < 0)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
_multifile.close();
|
||||
_write.close();
|
||||
_read = (istream *)NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,49 +17,51 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
#ifndef EXTRACTOR_H
|
||||
#define EXTRACTOR_H
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#include <pandabase.h>
|
||||
#include <filename.h>
|
||||
#include <buffer.h>
|
||||
#include <multifile.h>
|
||||
#include <pointerTo.h>
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "filename.h"
|
||||
#include "buffer.h"
|
||||
#include "multifile.h"
|
||||
#include "pointerTo.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : Extractor
|
||||
// Description :
|
||||
// Description : This class automatically extracts the contents of a
|
||||
// Multifile to the current directory (or to a specified
|
||||
// directory) in the background.
|
||||
//
|
||||
// It is designed to limit its use of system resources
|
||||
// and run unobtrusively in the background. After
|
||||
// initiate(), each call to run() extracts another small
|
||||
// portion of the Multifile. Call run() repeatedly
|
||||
// whenever you have spare cycles until run() returns
|
||||
// EU_success.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS Extractor {
|
||||
PUBLISHED:
|
||||
Extractor(void);
|
||||
Extractor(PT(Buffer) buffer);
|
||||
virtual ~Extractor(void);
|
||||
Extractor();
|
||||
~Extractor();
|
||||
|
||||
int initiate(Filename &source_file, const Filename &rel_path = "");
|
||||
int run(void);
|
||||
int initiate(const Filename &multifile_name, const Filename &extract_to = "");
|
||||
int run();
|
||||
|
||||
bool extract(Filename &source_file, const Filename &rel_path = "");
|
||||
bool extract(const Filename &multifile_name, const Filename &extract_to = "");
|
||||
|
||||
INLINE float get_progress(void) const;
|
||||
|
||||
private:
|
||||
void init(PT(Buffer) buffer);
|
||||
void cleanup(void);
|
||||
void cleanup();
|
||||
|
||||
bool _initiated;
|
||||
PT(Buffer) _buffer;
|
||||
Filename _multifile_name;
|
||||
Filename _extract_to;
|
||||
Multifile _multifile;
|
||||
|
||||
ifstream _read_stream;
|
||||
int _source_file_length;
|
||||
Multifile *_mfile;
|
||||
int _total_bytes_read;
|
||||
bool _read_all_input;
|
||||
bool _handled_all_input;
|
||||
int _source_buffer_length;
|
||||
Filename _source_file;
|
||||
Filename _rel_path;
|
||||
int _subfile_index;
|
||||
size_t _subfile_pos;
|
||||
size_t _subfile_length;
|
||||
istream *_read;
|
||||
ofstream _write;
|
||||
};
|
||||
|
||||
#include "extractor.I"
|
||||
|
|
|
|||
|
|
@ -16,89 +16,354 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <pandabase.h>
|
||||
#include "pandabase.h"
|
||||
#ifndef HAVE_GETOPT
|
||||
#include <gnu_getopt.h>
|
||||
#include "gnu_getopt.h"
|
||||
#else
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <multifile.h>
|
||||
#include <filename.h>
|
||||
#ifndef OLD_WAY
|
||||
#include <extractor.h>
|
||||
#endif
|
||||
#include "multifile.h"
|
||||
#include "filename.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
bool create = false; // -c
|
||||
bool append = false; // -r
|
||||
bool list = false; // -t
|
||||
bool extract = false; // -x
|
||||
bool verbose = false; // -v
|
||||
Filename multifile_name; // -f
|
||||
bool got_multifile_name = false;
|
||||
bool to_stdout = false; // -O
|
||||
Filename chdir_to; // -C
|
||||
bool got_chdir_to = false;
|
||||
size_t scale_factor = 0; // -F
|
||||
|
||||
void
|
||||
usage() {
|
||||
cerr << "Usage: multify -[c|r|t|x] -f <multifile_name> [options] <subfile_name> ...\n";
|
||||
}
|
||||
|
||||
void
|
||||
help() {
|
||||
cerr << "multify is used to store and extract files from a Panda Multifile.\n"
|
||||
<< "This is similar to a tar or zip file in that it is an archive file that\n"
|
||||
<< "contains a number of subfiles that may later be extracted.\n\n"
|
||||
|
||||
<< "The command-line options for multify are designed to be similar to those\n"
|
||||
<< "for tar, the traditional Unix archiver utility.\n\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
bool
|
||||
is_named(const string &subfile_name, int argc, char *argv[]) {
|
||||
// Returns true if the indicated subfile appears on the list of
|
||||
// files named on the command line.
|
||||
if (argc < 2) {
|
||||
// No named files; everything is listed.
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (subfile_name == argv[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
add_directory(Multifile &multifile, const Filename &directory_name) {
|
||||
vector_string files;
|
||||
if (!directory_name.scan_directory(files)) {
|
||||
cerr << "Unable to scan directory " << directory_name << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool okflag = true;
|
||||
|
||||
vector_string::const_iterator fi;
|
||||
for (fi = files.begin(); fi != files.end(); ++fi) {
|
||||
Filename subfile_name(directory_name, (*fi));
|
||||
if (subfile_name.is_directory()) {
|
||||
okflag = add_directory(multifile, subfile_name);
|
||||
|
||||
} else if (!subfile_name.exists()) {
|
||||
cerr << "Not found: " << subfile_name << "\n";
|
||||
okflag = false;
|
||||
|
||||
} else {
|
||||
if (verbose) {
|
||||
cout << subfile_name << "\n";
|
||||
}
|
||||
if (!multifile.add_subfile(subfile_name, subfile_name)) {
|
||||
cerr << "Unable to add " << subfile_name << ".\n";
|
||||
okflag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return okflag;
|
||||
}
|
||||
|
||||
bool
|
||||
add_files(int argc, char *argv[]) {
|
||||
Multifile multifile;
|
||||
if (append) {
|
||||
if (!multifile.open_read_write(multifile_name)) {
|
||||
cerr << "Unable to open " << multifile_name << " for updating.\n";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!multifile.open_write(multifile_name)) {
|
||||
cerr << "Unable to open " << multifile_name << " for writing.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (scale_factor != 0 && scale_factor != multifile.get_scale_factor()) {
|
||||
cerr << "Setting scale factor to " << scale_factor << "\n";
|
||||
multifile.set_scale_factor(scale_factor);
|
||||
}
|
||||
|
||||
bool okflag = true;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
Filename subfile_name = Filename::from_os_specific(argv[i]);
|
||||
if (subfile_name.is_directory()) {
|
||||
if (!add_directory(multifile, subfile_name)) {
|
||||
okflag = false;
|
||||
}
|
||||
|
||||
} else if (!subfile_name.exists()) {
|
||||
cerr << "Not found: " << subfile_name << "\n";
|
||||
okflag = false;
|
||||
|
||||
} else {
|
||||
if (verbose) {
|
||||
cout << subfile_name << "\n";
|
||||
}
|
||||
if (!multifile.add_subfile(subfile_name, subfile_name)) {
|
||||
cerr << "Unable to add " << subfile_name << ".\n";
|
||||
okflag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (multifile.needs_repack()) {
|
||||
if (!multifile.repack()) {
|
||||
cerr << "Failed to write " << multifile_name << ".\n";
|
||||
okflag = false;
|
||||
}
|
||||
} else {
|
||||
if (!multifile.flush()) {
|
||||
cerr << "Failed to write " << multifile_name << ".\n";
|
||||
okflag = false;
|
||||
}
|
||||
}
|
||||
|
||||
return okflag;
|
||||
}
|
||||
|
||||
bool
|
||||
extract_files(int argc, char *argv[]) {
|
||||
if (!multifile_name.exists()) {
|
||||
cerr << multifile_name << " not found.\n";
|
||||
return false;
|
||||
}
|
||||
Multifile multifile;
|
||||
if (!multifile.open_read(multifile_name)) {
|
||||
cerr << "Unable to open " << multifile_name << " for reading.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_subfiles = multifile.get_num_subfiles();
|
||||
|
||||
for (int i = 0; i < num_subfiles; i++) {
|
||||
string subfile_name = multifile.get_subfile_name(i);
|
||||
if (is_named(subfile_name, argc, argv)) {
|
||||
Filename filename = subfile_name;
|
||||
if (got_chdir_to) {
|
||||
filename = Filename(chdir_to, subfile_name);
|
||||
}
|
||||
if (to_stdout) {
|
||||
if (verbose) {
|
||||
cerr << filename << "\n";
|
||||
}
|
||||
multifile.extract_subfile_to(i, cout);
|
||||
} else {
|
||||
if (verbose) {
|
||||
cout << filename << "\n";
|
||||
}
|
||||
multifile.extract_subfile(i, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
list_files(int argc, char *argv[]) {
|
||||
if (!multifile_name.exists()) {
|
||||
cerr << multifile_name << " not found.\n";
|
||||
return false;
|
||||
}
|
||||
Multifile multifile;
|
||||
if (!multifile.open_read(multifile_name)) {
|
||||
cerr << "Unable to open " << multifile_name << " for reading.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_subfiles = multifile.get_num_subfiles();
|
||||
|
||||
if (verbose) {
|
||||
cout << num_subfiles << " subfiles:\n" << flush;
|
||||
for (int i = 0; i < num_subfiles; i++) {
|
||||
string subfile_name = multifile.get_subfile_name(i);
|
||||
if (is_named(subfile_name, argc, argv)) {
|
||||
printf("%12d %s\n",
|
||||
multifile.get_subfile_length(i),
|
||||
subfile_name.c_str());
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
if (multifile.get_scale_factor() != 1) {
|
||||
cout << "Scale factor is " << multifile.get_scale_factor() << "\n";
|
||||
}
|
||||
if (multifile.needs_repack()) {
|
||||
cout << "Multifile needs to be repacked.\n";
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < num_subfiles; i++) {
|
||||
string subfile_name = multifile.get_subfile_name(i);
|
||||
if (is_named(subfile_name, argc, argv)) {
|
||||
cout << subfile_name << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
cerr << "Usage: multify -[x,c|vr] <dest_file> <src_file> ..." << endl;
|
||||
return 0;
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool verbose = true;
|
||||
bool extract = false;
|
||||
// To emulate tar, we assume an implicit hyphen in front of the
|
||||
// first argument if there is not one already.
|
||||
if (argc >= 2) {
|
||||
if (*argv[1] != '-' && *argv[1] != '\0') {
|
||||
char *new_arg = new char[strlen(argv[1]) + 2];
|
||||
new_arg[0] = '-';
|
||||
strcpy(new_arg + 1, argv[1]);
|
||||
argv[1] = new_arg;
|
||||
}
|
||||
}
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
int flag = getopt(argc, argv, "xcvr:");
|
||||
static const char *optflags = "crtxvf:OC:F:h";
|
||||
int flag = getopt(argc, argv, optflags);
|
||||
Filename rel_path;
|
||||
while (flag != EOF) {
|
||||
switch (flag) {
|
||||
case 'x':
|
||||
extract = true;
|
||||
break;
|
||||
case 'c':
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'r':
|
||||
rel_path = optarg;
|
||||
break;
|
||||
default:
|
||||
cerr << "Unhandled switch: " << flag << endl;
|
||||
break;
|
||||
case 'c':
|
||||
create = true;
|
||||
break;
|
||||
case 'r':
|
||||
append = true;
|
||||
break;
|
||||
case 't':
|
||||
list = true;
|
||||
break;
|
||||
case 'x':
|
||||
extract = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'f':
|
||||
multifile_name = Filename::from_os_specific(optarg);
|
||||
got_multifile_name = true;
|
||||
break;
|
||||
case 'C':
|
||||
chdir_to = Filename::from_os_specific(optarg);
|
||||
got_chdir_to = true;
|
||||
break;
|
||||
case 'O':
|
||||
to_stdout = true;
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
char *endptr;
|
||||
scale_factor = strtol(optarg, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
cerr << "Invalid integer: " << optarg << "\n";
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (scale_factor == 0) {
|
||||
cerr << "Scale factor must be nonzero.\n";
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
help();
|
||||
return 1;
|
||||
case '?':
|
||||
usage();
|
||||
return 1;
|
||||
default:
|
||||
cerr << "Unhandled switch: " << flag << endl;
|
||||
break;
|
||||
}
|
||||
flag = getopt(argc, argv, "xcvr:");
|
||||
flag = getopt(argc, argv, optflags);
|
||||
}
|
||||
argc -= (optind - 1);
|
||||
argv += (optind - 1);
|
||||
|
||||
if (argc <= 1) {
|
||||
cerr << "Usage: multify -[x,c|v] <dest_file> <src_file> ..." << endl;
|
||||
return 0;
|
||||
// We should have exactly one of these options.
|
||||
if ((create?1:0) + (append?1:0) + (list?1:0) + (extract?1:0) != 1) {
|
||||
cerr << "Exactly one of -c, -r, -t, -x must be specified.\n";
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
Filename dest_file = argv[1];
|
||||
dest_file.set_binary();
|
||||
|
||||
if (verbose == true) {
|
||||
if (extract == true)
|
||||
cerr << "Extracting from: " << dest_file << endl;
|
||||
else
|
||||
cerr << "Appending to: " << dest_file << endl;
|
||||
}
|
||||
|
||||
Multifile mfile;
|
||||
|
||||
if (extract == false) {
|
||||
for (int i = 2; i < argc; i++) {
|
||||
Filename in_file = argv[i];
|
||||
in_file.set_binary();
|
||||
mfile.add(in_file);
|
||||
if (!got_multifile_name) {
|
||||
if (argc <= 1) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mfile.write(dest_file) == false)
|
||||
cerr << "Failed to write: " << dest_file << endl;
|
||||
} else {
|
||||
#ifdef OLD_WAY
|
||||
mfile.read(dest_file);
|
||||
mfile.extract_all(rel_path);
|
||||
#else
|
||||
Extractor extor;
|
||||
extor.extract(dest_file, rel_path);
|
||||
#endif
|
||||
// For now, we allow -f to be omitted, and use the first argument
|
||||
// as the archive name, for backward compatibility. Later we will
|
||||
// remove this.
|
||||
multifile_name = Filename::from_os_specific(argv[1]);
|
||||
cerr << "Warning: using " << multifile_name
|
||||
<< " as archive name. Use -f in the future to specify this.\n";
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
bool okflag = true;
|
||||
if (create || append) {
|
||||
okflag = add_files(argc, argv);
|
||||
} else if (extract) {
|
||||
okflag = extract_files(argc, argv);
|
||||
} else { // list
|
||||
okflag = list_files(argc, argv);
|
||||
}
|
||||
|
||||
if (okflag) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,55 +9,61 @@
|
|||
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
|
||||
|
||||
#define SOURCES \
|
||||
bigEndian.h buffer.I buffer.h \
|
||||
checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
|
||||
circBuffer.h clockObject.I clockObject.h config_express.h \
|
||||
datagram.I datagram.h datagramGenerator.I \
|
||||
datagramGenerator.h datagramInputFile.I datagramInputFile.h \
|
||||
datagramIterator.I datagramIterator.h datagramOutputFile.I \
|
||||
datagramOutputFile.h datagramSink.I datagramSink.h \
|
||||
dcast.T dcast.h \
|
||||
error_utils.h \
|
||||
get_config_path.h hashGeneratorBase.I hashGeneratorBase.h \
|
||||
hashVal.I hashVal.h indent.I indent.h littleEndian.h \
|
||||
memoryInfo.I memoryInfo.h \
|
||||
memoryUsage.I memoryUsage.h \
|
||||
memoryUsagePointerCounts.I memoryUsagePointerCounts.h \
|
||||
memoryUsagePointers.I memoryUsagePointers.h \
|
||||
multifile.I multifile.h namable.I \
|
||||
namable.h nativeNumericData.I nativeNumericData.h \
|
||||
numeric_types.h pointerTo.I pointerTo.h \
|
||||
pointerToArray.I pointerToArray.h \
|
||||
profileTimer.I profileTimer.h \
|
||||
pta_uchar.h referenceCount.I referenceCount.h \
|
||||
register_type.I register_type.h \
|
||||
reversedNumericData.I reversedNumericData.h tokenBoard.I \
|
||||
tokenBoard.h trueClock.I trueClock.h typeHandle.I \
|
||||
typeHandle.h typedObject.I typedObject.h \
|
||||
typedReferenceCount.I typedReferenceCount.h typedef.h \
|
||||
typeRegistry.I typeRegistry.h \
|
||||
typeRegistryNode.I typeRegistryNode.h \
|
||||
vector_uchar.h \
|
||||
$[if $[HAVE_CRYPTO], \
|
||||
crypto_utils.cxx crypto_utils.h patchfile.I \
|
||||
patchfile.cxx patchfile.h ]
|
||||
bigEndian.h buffer.I buffer.h \
|
||||
checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
|
||||
circBuffer.h clockObject.I clockObject.h config_express.h \
|
||||
datagram.I datagram.h datagramGenerator.I \
|
||||
datagramGenerator.h datagramInputFile.I datagramInputFile.h \
|
||||
datagramIterator.I datagramIterator.h datagramOutputFile.I \
|
||||
datagramOutputFile.h datagramSink.I datagramSink.h \
|
||||
dcast.T dcast.h \
|
||||
error_utils.h \
|
||||
get_config_path.h hashGeneratorBase.I hashGeneratorBase.h \
|
||||
hashVal.I hashVal.h indent.I indent.h \
|
||||
indirectLess.I indirectLess.h \
|
||||
littleEndian.h \
|
||||
memoryInfo.I memoryInfo.h \
|
||||
memoryUsage.I memoryUsage.h \
|
||||
memoryUsagePointerCounts.I memoryUsagePointerCounts.h \
|
||||
memoryUsagePointers.I memoryUsagePointers.h \
|
||||
multifile.I multifile.h namable.I \
|
||||
namable.h nativeNumericData.I nativeNumericData.h \
|
||||
numeric_types.h \
|
||||
ordered_vector.h ordered_vector.I ordered_vector.T \
|
||||
pointerTo.I pointerTo.h \
|
||||
pointerToArray.I pointerToArray.h \
|
||||
profileTimer.I profileTimer.h \
|
||||
pta_uchar.h referenceCount.I referenceCount.h \
|
||||
register_type.I register_type.h \
|
||||
reversedNumericData.I reversedNumericData.h tokenBoard.I \
|
||||
tokenBoard.h trueClock.I trueClock.h typeHandle.I \
|
||||
typeHandle.h typedObject.I typedObject.h \
|
||||
typedReferenceCount.I typedReferenceCount.h typedef.h \
|
||||
typeRegistry.I typeRegistry.h \
|
||||
typeRegistryNode.I typeRegistryNode.h \
|
||||
vector_uchar.h \
|
||||
$[if $[HAVE_CRYPTO], \
|
||||
crypto_utils.cxx crypto_utils.h patchfile.I \
|
||||
patchfile.cxx patchfile.h ]
|
||||
|
||||
#define INCLUDED_SOURCES \
|
||||
buffer.cxx checksumHashGenerator.cxx clockObject.cxx \
|
||||
config_express.cxx datagram.cxx datagramGenerator.cxx \
|
||||
datagramInputFile.cxx datagramIterator.cxx \
|
||||
datagramOutputFile.cxx datagramSink.cxx dcast.cxx error_utils.cxx \
|
||||
get_config_path.cxx \
|
||||
hashGeneratorBase.cxx hashVal.cxx indent.cxx \
|
||||
memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
|
||||
memoryUsagePointers.cxx multifile.cxx namable.cxx \
|
||||
nativeNumericData.cxx profileTimer.cxx \
|
||||
pta_uchar.cxx referenceCount.cxx register_type.cxx \
|
||||
reversedNumericData.cxx trueClock.cxx typeHandle.cxx \
|
||||
typedObject.cxx typedReferenceCount.cxx \
|
||||
typeRegistry.cxx typeRegistryNode.cxx vector_uchar.cxx
|
||||
buffer.cxx checksumHashGenerator.cxx clockObject.cxx \
|
||||
config_express.cxx datagram.cxx datagramGenerator.cxx \
|
||||
datagramInputFile.cxx datagramIterator.cxx \
|
||||
datagramOutputFile.cxx datagramSink.cxx dcast.cxx error_utils.cxx \
|
||||
get_config_path.cxx \
|
||||
hashGeneratorBase.cxx hashVal.cxx indent.cxx \
|
||||
memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
|
||||
memoryUsagePointers.cxx multifile.cxx namable.cxx \
|
||||
nativeNumericData.cxx \
|
||||
ordered_vector.cxx \
|
||||
profileTimer.cxx \
|
||||
pta_uchar.cxx referenceCount.cxx register_type.cxx \
|
||||
reversedNumericData.cxx trueClock.cxx typeHandle.cxx \
|
||||
typedObject.cxx typedReferenceCount.cxx \
|
||||
typeRegistry.cxx typeRegistryNode.cxx vector_uchar.cxx
|
||||
|
||||
#define INSTALL_HEADERS \
|
||||
#define INSTALL_HEADERS \
|
||||
bigEndian.h buffer.I buffer.h checksumHashGenerator.I \
|
||||
checksumHashGenerator.h circBuffer.I circBuffer.h clockObject.I \
|
||||
clockObject.h config_express.h datagram.I datagram.h \
|
||||
|
|
@ -66,12 +72,15 @@
|
|||
datagramOutputFile.I datagramOutputFile.h datagramSink.I \
|
||||
datagramSink.h dcast.T dcast.h \
|
||||
error_utils.h get_config_path.h hashGeneratorBase.I \
|
||||
hashGeneratorBase.h hashVal.I hashVal.h indent.I indent.h \
|
||||
hashGeneratorBase.h hashVal.I hashVal.h \
|
||||
indent.I indent.h \
|
||||
indirectLess.I indirectLess.h \
|
||||
littleEndian.h memoryInfo.I memoryInfo.h memoryUsage.I \
|
||||
memoryUsage.h memoryUsagePointerCounts.I \
|
||||
memoryUsagePointerCounts.h memoryUsagePointers.I \
|
||||
memoryUsagePointers.h multifile.I multifile.h namable.I namable.h \
|
||||
nativeNumericData.I nativeNumericData.h numeric_types.h \
|
||||
ordered_vector.h ordered_vector.I ordered_vector.T \
|
||||
patchfile.I patchfile.h pointerTo.I pointerTo.h \
|
||||
pointerToArray.I pointerToArray.h profileTimer.I \
|
||||
profileTimer.h pta_uchar.h referenceCount.I referenceCount.h \
|
||||
|
|
@ -98,3 +107,14 @@
|
|||
|
||||
#end test_bin_target
|
||||
|
||||
|
||||
#begin test_bin_target
|
||||
#define TARGET test_ordered_vector
|
||||
|
||||
#define SOURCES \
|
||||
test_ordered_vector.cxx
|
||||
|
||||
#define LOCAL_LIBS $[LOCAL_LIBS] putil
|
||||
#define OTHER_LIBS $[OTHER_LIBS] pystub
|
||||
|
||||
#end test_bin_target
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "multifile.cxx"
|
||||
#include "namable.cxx"
|
||||
#include "nativeNumericData.cxx"
|
||||
#include "ordered_vector.cxx"
|
||||
#include "profileTimer.cxx"
|
||||
#include "pta_uchar.cxx"
|
||||
#include "referenceCount.cxx"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#ifndef INDIRECTLESS_H
|
||||
#define INDIRECTLESS_H
|
||||
|
||||
#include <pandabase.h>
|
||||
#include "pandabase.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : IndirectLess
|
||||
|
|
@ -16,27 +16,180 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::write_header
|
||||
// Access: Private
|
||||
// Description: Writes the header uncompressed with platform-
|
||||
// independent byte ordering
|
||||
// Function: Multifile::is_read_valid
|
||||
// Access: Published
|
||||
// Description: Returns true if the Multifile has been opened for
|
||||
// read mode and there have been no errors, and
|
||||
// individual Subfile contents may be extracted.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void Multifile::
|
||||
write_header(ofstream &write_stream) {
|
||||
_datagram.clear();
|
||||
_datagram.add_uint32(_magic_number);
|
||||
_datagram.add_int32(_num_mfiles);
|
||||
string msg = _datagram.get_message();
|
||||
write_stream.write((char *)msg.data(), msg.length());
|
||||
INLINE bool Multifile::
|
||||
is_read_valid() const {
|
||||
return (_read != (istream *)NULL && !_read->fail() &&
|
||||
_open_subfile == (Subfile *)NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::get_header_length
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Function: Multifile::is_write_valid
|
||||
// Access: Published
|
||||
// Description: Returns true if the Multifile has been opened for
|
||||
// write mode and there have been no errors, and
|
||||
// Subfiles may be added or removed from the Multifile.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int Multifile::
|
||||
get_header_length(void) const {
|
||||
return _header_length;
|
||||
INLINE bool Multifile::
|
||||
is_write_valid() const {
|
||||
return (_write != (ostream *)NULL && !_write->fail() &&
|
||||
_open_subfile == (Subfile *)NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::needs_repack
|
||||
// Access: Published
|
||||
// Description: Returns true if the Multifile index is suboptimal and
|
||||
// should be repacked. Call repack() to achieve this.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Multifile::
|
||||
needs_repack() const {
|
||||
return _needs_repack;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::set_scale_factor
|
||||
// Access: Published
|
||||
// Description: Changes the internal scale factor for this Multifile.
|
||||
//
|
||||
// This is normally 1, but it may be set to any
|
||||
// arbitrary value (greater than zero) to support
|
||||
// Multifile archives that exceed 4GB, if necessary.
|
||||
// (Individual subfiles may still not exceed 4GB.)
|
||||
//
|
||||
// All addresses within the file are rounded up to the
|
||||
// next multiple of _scale_factor, and zeros are written
|
||||
// to the file to fill the resulting gaps. Then the
|
||||
// address is divided by _scale_factor and written out
|
||||
// as a 32-bit integer. Thus, setting a scale factor of
|
||||
// 2 supports up to 8GB files, 3 supports 12GB files,
|
||||
// etc.
|
||||
//
|
||||
// Calling this function on an already-existing
|
||||
// Multifile forces an immediate repack() operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void Multifile::
|
||||
set_scale_factor(size_t scale_factor) {
|
||||
nassertv(scale_factor != 0);
|
||||
if (_scale_factor != scale_factor) {
|
||||
_scale_factor = scale_factor;
|
||||
if (_next_index != 0) {
|
||||
repack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::get_scale_factor
|
||||
// Access: Published
|
||||
// Description: Returns the internal scale factor for this Multifile.
|
||||
// See set_scale_factor().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE size_t Multifile::
|
||||
get_scale_factor() const {
|
||||
return _scale_factor;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::word_to_streampos
|
||||
// Access: Private
|
||||
// Description: Converts a size_t address read from the file to
|
||||
// a streampos byte address within the file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE streampos Multifile::
|
||||
word_to_streampos(size_t word) const {
|
||||
return (streampos)word * (streampos)_scale_factor;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::streampos_to_word
|
||||
// Access: Private
|
||||
// Description: Converts a streampos byte address within the file to
|
||||
// a size_t value suitable for writing to the file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE size_t Multifile::
|
||||
streampos_to_word(streampos fpos) const {
|
||||
return (size_t)((fpos + _scale_factor - 1) / _scale_factor);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::normalize_streampos
|
||||
// Access: Private
|
||||
// Description: Rounds the streampos byte address up to the next
|
||||
// multiple of _scale_factor. Only multiples of
|
||||
// _scale_factor may be written to the file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE streampos Multifile::
|
||||
normalize_streampos(streampos fpos) const {
|
||||
return word_to_streampos(streampos_to_word(fpos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::Subfile::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE Multifile::Subfile::
|
||||
Subfile(const string &name) :
|
||||
_name(name)
|
||||
{
|
||||
_index_start = 0;
|
||||
_data_start = 0;
|
||||
_data_length = 0;
|
||||
_source = (istream *)NULL;
|
||||
_flags = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::Subfile::operator <
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Multifile::Subfile::
|
||||
operator < (const Multifile::Subfile &other) const {
|
||||
return _name < other._name;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::Subfile::is_deleted
|
||||
// Access: Public
|
||||
// Description: Returns true if the Subfile indicates it has been
|
||||
// deleted (removed from the index), false otherwise.
|
||||
// This should never be true of Subfiles that currently
|
||||
// appear in either the _subfiles or _new_subfiles
|
||||
// lists.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Multifile::Subfile::
|
||||
is_deleted() const {
|
||||
return (_flags & SF_deleted) != 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::Subfile::is_index_invalid
|
||||
// Access: Public
|
||||
// Description: Returns true if there was some problem reading the
|
||||
// index record for this Subfile from the Multifile.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Multifile::Subfile::
|
||||
is_index_invalid() const {
|
||||
return (_flags & SF_index_invalid) != 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Multifile::Subfile::is_data_invalid
|
||||
// Access: Public
|
||||
// Description: Returns true if there was some problem reading the
|
||||
// data contents of this Subfile, particularly when
|
||||
// copying into the Multifile.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool Multifile::Subfile::
|
||||
is_data_invalid() const {
|
||||
return (_flags & SF_data_invalid) != 0;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,21 +15,19 @@
|
|||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MULTIFILE_H
|
||||
#define MULTIFILE_H
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#include <pandabase.h>
|
||||
|
||||
#include "typedef.h"
|
||||
#include "pandabase.h"
|
||||
|
||||
#include "datagram.h"
|
||||
#include "datagramIterator.h"
|
||||
|
||||
#include <notify.h>
|
||||
#include <filename.h>
|
||||
#include "plist.h"
|
||||
#include "filename.h"
|
||||
#include "ordered_vector.h"
|
||||
#include "indirectLess.h"
|
||||
#include "pvector.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : Multifile
|
||||
|
|
@ -37,95 +35,120 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS Multifile {
|
||||
PUBLISHED:
|
||||
enum Type {
|
||||
T_unknown,
|
||||
T_valid,
|
||||
T_invalid,
|
||||
};
|
||||
Multifile();
|
||||
~Multifile();
|
||||
|
||||
Multifile(void);
|
||||
~Multifile(void);
|
||||
bool open_read(const Filename &multifile_name);
|
||||
bool open_write(const Filename &multifile_name);
|
||||
bool open_read_write(const Filename &multifile_name);
|
||||
void close();
|
||||
|
||||
INLINE int get_header_length(void) const;
|
||||
INLINE bool is_read_valid() const;
|
||||
INLINE bool is_write_valid() const;
|
||||
INLINE bool needs_repack() const;
|
||||
|
||||
static int evaluate(const char *start, int size);
|
||||
INLINE void set_scale_factor(size_t scale_factor);
|
||||
INLINE size_t get_scale_factor() const;
|
||||
|
||||
bool add(const Filename &name);
|
||||
bool remove(const Filename &name);
|
||||
bool has_file(const Filename &name);
|
||||
bool add_subfile(const string &subfile_name, const Filename &filename);
|
||||
bool flush();
|
||||
bool repack();
|
||||
|
||||
bool read(Filename &name);
|
||||
bool write(Filename name);
|
||||
int write(char *&start, int &size, const Filename &rel_path = "");
|
||||
bool write_extract(char *&start, int &size, const Filename &rel_path = "");
|
||||
bool extract(const Filename &name, const Filename &rel_path = "");
|
||||
void extract_all(const Filename &rel_path = "");
|
||||
int get_num_subfiles() const;
|
||||
int find_subfile(const string &subfile_name) const;
|
||||
void remove_subfile(int index);
|
||||
const string &get_subfile_name(int index) const;
|
||||
size_t get_subfile_length(int index) const;
|
||||
|
||||
void reset(void);
|
||||
int parse_header(char *&start, int &size);
|
||||
|
||||
private:
|
||||
|
||||
INLINE void write_header(ofstream &write_stream);
|
||||
void read_subfile(int index, Datagram &datagram);
|
||||
bool extract_subfile(int index, const Filename &filename);
|
||||
|
||||
public:
|
||||
// This nested class must be public so we can make a list of its
|
||||
// pointers. Weird.
|
||||
class Memfile {
|
||||
public:
|
||||
Memfile(void);
|
||||
~Memfile(void);
|
||||
bool parse_header_length(char *&start, int &size);
|
||||
bool parse_header(char *&start, int &size);
|
||||
// Special interfaces to work with iostreams, not necessarily files.
|
||||
bool open_read(istream *multifile_stream);
|
||||
bool open_write(ostream *multifile_stream);
|
||||
bool open_read_write(iostream *multifile_stream);
|
||||
bool add_subfile(const string &subfile_name, istream *subfile_data);
|
||||
|
||||
bool read(const Filename &name);
|
||||
bool read_from_multifile(ifstream &read_stream);
|
||||
bool write(const Filename &rel_path);
|
||||
void write_to_multifile(ofstream &write_stream);
|
||||
int write(char *&start, int &size, const Filename &rel_path = "");
|
||||
void reset(void);
|
||||
|
||||
public:
|
||||
bool _header_length_parsed;
|
||||
bool _header_parsed;
|
||||
Datagram _datagram;
|
||||
char *_header_length_buf;
|
||||
int _header_length_buf_length;
|
||||
|
||||
PN_int32 _header_length;
|
||||
Filename _name;
|
||||
PN_int32 _buffer_length;
|
||||
char* _buffer;
|
||||
|
||||
bool _file_open;
|
||||
ofstream _write_stream;
|
||||
int _bytes_written;
|
||||
};
|
||||
bool extract_subfile_to(int index, ostream &out);
|
||||
istream &open_read_subfile(int index);
|
||||
void close_subfile();
|
||||
|
||||
private:
|
||||
typedef plist<Memfile *> MemfileList;
|
||||
|
||||
class MemfileMatch {
|
||||
public:
|
||||
MemfileMatch(const Filename &name) {
|
||||
_want_name = name;
|
||||
}
|
||||
bool operator()(Memfile *mfile) const {
|
||||
return mfile->_name == _want_name;
|
||||
}
|
||||
Filename _want_name;
|
||||
enum SubfileFlags {
|
||||
SF_deleted = 0x0001,
|
||||
SF_index_invalid = 0x0002,
|
||||
SF_data_invalid = 0x0004,
|
||||
};
|
||||
|
||||
private:
|
||||
MemfileList _files;
|
||||
PN_int32 _num_mfiles;
|
||||
bool _header_parsed;
|
||||
Memfile *_current_mfile;
|
||||
Datagram _datagram;
|
||||
int _header_length;
|
||||
int _mfiles_written;
|
||||
class Subfile {
|
||||
public:
|
||||
INLINE Subfile(const string &name);
|
||||
INLINE bool operator < (const Subfile &other) const;
|
||||
streampos read_index(istream &read, streampos fpos,
|
||||
Multifile *multfile);
|
||||
streampos write_index(ostream &write, streampos fpos,
|
||||
Multifile *multifile);
|
||||
streampos write_data(ostream &write, istream *read, streampos fpos);
|
||||
void rewrite_index_data_start(ostream &write, Multifile *multifile);
|
||||
void rewrite_index_flags(ostream &write);
|
||||
INLINE bool is_deleted() const;
|
||||
INLINE bool is_index_invalid() const;
|
||||
INLINE bool is_data_invalid() const;
|
||||
|
||||
static PN_uint32 _magic_number;
|
||||
string _name;
|
||||
streampos _index_start;
|
||||
streampos _data_start;
|
||||
size_t _data_length;
|
||||
istream *_source;
|
||||
Filename _source_filename;
|
||||
int _flags;
|
||||
};
|
||||
|
||||
INLINE streampos word_to_streampos(size_t word) const;
|
||||
INLINE size_t streampos_to_word(streampos fpos) const;
|
||||
INLINE streampos normalize_streampos(streampos fpos) const;
|
||||
streampos pad_to_streampos(streampos fpos);
|
||||
|
||||
bool add_new_subfile(Subfile *subfile);
|
||||
void clear_subfiles();
|
||||
bool read_index();
|
||||
bool write_header();
|
||||
|
||||
|
||||
typedef ov_set<Subfile *, IndirectLess<Subfile> > Subfiles;
|
||||
Subfiles _subfiles;
|
||||
typedef pvector<Subfile *> PendingSubfiles;
|
||||
PendingSubfiles _new_subfiles;
|
||||
PendingSubfiles _removed_subfiles;
|
||||
|
||||
istream *_read;
|
||||
ostream *_write;
|
||||
streampos _next_index;
|
||||
streampos _last_index;
|
||||
|
||||
bool _needs_repack;
|
||||
size_t _scale_factor;
|
||||
|
||||
ifstream _read_file;
|
||||
ofstream _write_file;
|
||||
fstream _read_write_file;
|
||||
Filename _multifile_name;
|
||||
|
||||
int _file_major_ver;
|
||||
int _file_minor_ver;
|
||||
|
||||
Subfile *_open_subfile;
|
||||
// This is just to support open_read_subfile() for those subfiles
|
||||
// that have recently been added but not flushed.
|
||||
ifstream _subfile_read;
|
||||
|
||||
static const char _header[];
|
||||
static const size_t _header_size;
|
||||
static const int _current_major_ver;
|
||||
static const int _current_minor_ver;
|
||||
|
||||
friend class Subfile;
|
||||
};
|
||||
|
||||
#include "multifile.I"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
#define ORDERED_VECTOR_H
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "config_util.h"
|
||||
|
||||
#include "pvector.h"
|
||||
#include "pset.h"
|
||||
|
|
@ -30,13 +30,11 @@
|
|||
globalPointerRegistry.I globalPointerRegistry.h \
|
||||
indirectCompareNames.I indirectCompareNames.h \
|
||||
indirectCompareTo.I indirectCompareTo.h \
|
||||
indirectLess.I indirectLess.h \
|
||||
ioPtaDatagramFloat.h ioPtaDatagramInt.h \
|
||||
ioPtaDatagramShort.h keyboardButton.h lineStream.I \
|
||||
lineStream.h lineStreamBuf.I lineStreamBuf.h \
|
||||
modifierButtons.I modifierButtons.h mouseButton.h \
|
||||
mouseData.I mouseData.h nameUniquifier.I nameUniquifier.h \
|
||||
ordered_vector.h ordered_vector.I ordered_vector.T \
|
||||
pipeline.h pipeline.I \
|
||||
pipelineCycler.h pipelineCycler.I \
|
||||
pipelineCyclerBase.h pipelineCyclerBase.I \
|
||||
|
|
@ -67,7 +65,6 @@
|
|||
keyboardButton.cxx lineStream.cxx lineStreamBuf.cxx \
|
||||
modifierButtons.cxx mouseButton.cxx mouseData.cxx \
|
||||
nameUniquifier.cxx \
|
||||
ordered_vector.cxx \
|
||||
pipeline.cxx \
|
||||
pipelineCycler.cxx \
|
||||
pipelineCyclerBase.cxx \
|
||||
|
|
@ -100,13 +97,12 @@
|
|||
globPattern.I globPattern.h \
|
||||
globalPointerRegistry.I globalPointerRegistry.h \
|
||||
indirectCompareNames.I indirectCompareNames.h indirectCompareTo.I \
|
||||
indirectCompareTo.h indirectLess.I indirectLess.h \
|
||||
indirectCompareTo.h \
|
||||
ioPtaDatagramFloat.h ioPtaDatagramInt.h \
|
||||
ioPtaDatagramShort.h iterator_types.h keyboardButton.h lineStream.I \
|
||||
lineStream.h lineStreamBuf.I lineStreamBuf.h modifierButtons.I \
|
||||
modifierButtons.h mouseButton.h mouseData.I mouseData.h \
|
||||
nameUniquifier.I nameUniquifier.h \
|
||||
ordered_vector.h ordered_vector.I ordered_vector.T \
|
||||
pipeline.h pipeline.I \
|
||||
pipelineCycler.h pipelineCycler.I \
|
||||
pipelineCyclerBase.h pipelineCyclerBase.I \
|
||||
|
|
@ -170,14 +166,3 @@
|
|||
test_linestream.cxx
|
||||
|
||||
#end test_bin_target
|
||||
|
||||
#begin test_bin_target
|
||||
#define TARGET test_ordered_vector
|
||||
|
||||
#define SOURCES \
|
||||
test_ordered_vector.cxx
|
||||
|
||||
#define LOCAL_LIBS $[LOCAL_LIBS] putil
|
||||
#define OTHER_LIBS $[OTHER_LIBS] pystub
|
||||
|
||||
#end test_bin_target
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "nameUniquifier.cxx"
|
||||
#include "ordered_vector.cxx"
|
||||
#include "pipeline.cxx"
|
||||
#include "pipelineCycler.cxx"
|
||||
#include "pipelineCyclerBase.cxx"
|
||||
|
|
|
|||
Loading…
Reference in New Issue