Panda3D
datagramOutputFile.cxx
1 // Filename: datagramOutputFile.cxx
2 // Created by: drose (30Oct00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "datagramOutputFile.h"
16 #include "streamWriter.h"
17 #include "zStream.h"
18 #include <algorithm>
19 
20 ////////////////////////////////////////////////////////////////////
21 // Function: DatagramOutputFile::open
22 // Access: Public
23 // Description: Opens the indicated filename for writing. Returns
24 // true if successful, false on failure.
25 ////////////////////////////////////////////////////////////////////
27 open(const FileReference *file) {
28  close();
29 
30  _file = file;
31  _filename = _file->get_filename();
32 
33  // DatagramOutputFiles are always binary.
34  _filename.set_binary();
35 
37  _vfile = vfs->create_file(_filename);
38  if (_vfile == (VirtualFile *)NULL) {
39  // No such file.
40  return false;
41  }
42  _out = _vfile->open_write_file(true, true);
43  _owns_out = (_out != (ostream *)NULL);
44  return _owns_out && !_out->fail();
45 }
46 
47 ////////////////////////////////////////////////////////////////////
48 // Function: DatagramOutputFile::open
49 // Access: Public
50 // Description: Starts writing to the indicated stream. Returns
51 // true on success, false on failure. The
52 // DatagramOutputFile does not take ownership of the
53 // stream; you are responsible for closing or deleting
54 // it when you are done.
55 ////////////////////////////////////////////////////////////////////
57 open(ostream &out, const Filename &filename) {
58  close();
59 
60  _out = &out;
61  _owns_out = false;
62  _filename = filename;
63 
64  if (!filename.empty()) {
65  _file = new FileReference(filename);
66  }
67 
68  return !_out->fail();
69 }
70 
71 ////////////////////////////////////////////////////////////////////
72 // Function: DatagramOutputFile::close
73 // Access: Public
74 // Description: Closes the file. This is also implicitly done when
75 // the DatagramOutputFile destructs.
76 ////////////////////////////////////////////////////////////////////
78 close() {
79  _vfile.clear();
80  if (_owns_out) {
82  vfs->close_write_file(_out);
83  }
84  _out = (ostream *)NULL;
85  _owns_out = false;
86 
87  _file.clear();
88  _filename = Filename();
89 
90  _wrote_first_datagram = false;
91  _error = false;
92 }
93 
94 ////////////////////////////////////////////////////////////////////
95 // Function: DatagramOutputFile::write_header
96 // Access: Public
97 // Description: Writes a sequence of bytes to the beginning of the
98 // datagram file. This may be called any number of
99 // times after the file has been opened and before the
100 // first datagram is written. It may not be called once
101 // the first datagram is written.
102 ////////////////////////////////////////////////////////////////////
104 write_header(const string &header) {
105  nassertr(_out != (ostream *)NULL, false);
106  nassertr(!_wrote_first_datagram, false);
107 
108  _out->write(header.data(), header.size());
109  thread_consider_yield();
110  return !_out->fail();
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: DatagramOutputFile::put_datagram
115 // Access: Public, Virtual
116 // Description: Writes the given datagram to the file. Returns true
117 // on success, false if there is an error.
118 ////////////////////////////////////////////////////////////////////
120 put_datagram(const Datagram &data) {
121  nassertr(_out != (ostream *)NULL, false);
122  _wrote_first_datagram = true;
123 
124  // First, write the size of the upcoming datagram.
125  StreamWriter writer(_out, false);
126  size_t num_bytes = data.get_length();
127  if (num_bytes == (PN_uint32)-1 || num_bytes != (PN_uint32)num_bytes) {
128  // Write a large value as a 64-bit size.
129  writer.add_uint32((PN_uint32)-1);
130  writer.add_uint64(num_bytes);
131  } else {
132  // Write a value that fits in 32 bits.
133  writer.add_uint32((PN_uint32)num_bytes);
134  }
135 
136  // Now, write the datagram itself.
137  _out->write((const char *)data.get_data(), data.get_length());
138  thread_consider_yield();
139 
140  return !_out->fail();
141 }
142 
143 ////////////////////////////////////////////////////////////////////
144 // Function: DatagramOutputFile::copy_datagram
145 // Access: Published, Virtual
146 // Description: Copies the file data from the entire indicated
147 // file (via the vfs) as the next datagram. This is
148 // intended to support potentially very large datagrams.
149 //
150 // Returns true on success, false on failure or if this
151 // method is unimplemented. On true, fills "result"
152 // with the information that references the copied file,
153 // if possible.
154 ////////////////////////////////////////////////////////////////////
156 copy_datagram(SubfileInfo &result, const Filename &filename) {
157  nassertr(_out != (ostream *)NULL, false);
158  _wrote_first_datagram = true;
159 
161  PT(VirtualFile) vfile = vfs->get_file(filename);
162  if (vfile == NULL) {
163  return false;
164  }
165  istream *in = vfile->open_read_file(true);
166  if (in == NULL) {
167  return false;
168  }
169 
170  streamsize size = vfile->get_file_size(in);
171  streamsize num_remaining = size;
172 
173  StreamWriter writer(_out, false);
174  if (num_remaining == (PN_uint32)-1 || num_remaining != (PN_uint32)num_remaining) {
175  // Write a large value as a 64-bit size.
176  writer.add_uint32((PN_uint32)-1);
177  writer.add_uint64(num_remaining);
178  } else {
179  // Write a value that fits in 32 bits.
180  writer.add_uint32((PN_uint32)num_remaining);
181  }
182 
183  static const size_t buffer_size = 4096;
184  char buffer[buffer_size];
185 
186  streampos start = _out->tellp();
187  in->read(buffer, min((streamsize)buffer_size, num_remaining));
188  streamsize count = in->gcount();
189  while (count != 0) {
190  _out->write(buffer, count);
191  if (_out->fail()) {
192  vfile->close_read_file(in);
193  return false;
194  }
195  num_remaining -= count;
196  if (num_remaining == 0) {
197  break;
198  }
199  in->read(buffer, min((streamsize)buffer_size, num_remaining));
200  count = in->gcount();
201  }
202 
203  vfile->close_read_file(in);
204 
205  if (num_remaining != 0) {
206  util_cat.error()
207  << "Truncated input stream.\n";
208  return false;
209  }
210 
211  result = SubfileInfo(_file, start, size);
212  return true;
213 }
214 
215 ////////////////////////////////////////////////////////////////////
216 // Function: DatagramOutputFile::copy_datagram
217 // Access: Published, Virtual
218 // Description: Copies the file data from the range of the indicated
219 // file (outside of the vfs) as the next datagram. This
220 // is intended to support potentially very large
221 // datagrams.
222 //
223 // Returns true on success, false on failure or if this
224 // method is unimplemented. On true, fills "result"
225 // with the information that references the copied file,
226 // if possible.
227 ////////////////////////////////////////////////////////////////////
229 copy_datagram(SubfileInfo &result, const SubfileInfo &source) {
230  nassertr(_out != (ostream *)NULL, false);
231  _wrote_first_datagram = true;
232 
233  pifstream in;
234  if (!source.get_filename().open_read(in)) {
235  return false;
236  }
237 
238  streamsize num_remaining = source.get_size();
239 
240  StreamWriter writer(_out, false);
241  if (num_remaining == (PN_uint32)-1 || num_remaining != (PN_uint32)num_remaining) {
242  // Write a large value as a 64-bit size.
243  writer.add_uint32((PN_uint32)-1);
244  writer.add_uint64(num_remaining);
245  } else {
246  // Write a value that fits in 32 bits.
247  writer.add_uint32((PN_uint32)num_remaining);
248  }
249 
250  static const size_t buffer_size = 4096;
251  char buffer[buffer_size];
252 
253  streampos start = _out->tellp();
254  in.seekg(source.get_start());
255  in.read(buffer, min((streamsize)buffer_size, num_remaining));
256  streamsize count = in.gcount();
257  while (count != 0) {
258  _out->write(buffer, count);
259  if (_out->fail()) {
260  return false;
261  }
262  num_remaining -= count;
263  if (num_remaining == 0) {
264  break;
265  }
266  in.read(buffer, min((streamsize)buffer_size, num_remaining));
267  count = in.gcount();
268  }
269 
270  if (num_remaining != 0) {
271  util_cat.error()
272  << "Truncated input stream.\n";
273  return false;
274  }
275 
276  result = SubfileInfo(_file, start, source.get_size());
277  return true;
278 }
279 
280 ////////////////////////////////////////////////////////////////////
281 // Function: DatagramOutputFile::is_error
282 // Access: Public, Virtual
283 // Description: Returns true if the file has reached an error
284 // condition.
285 ////////////////////////////////////////////////////////////////////
288  if (_out == (ostream *)NULL) {
289  return true;
290  }
291 
292  if (_out->fail()) {
293  _error = true;
294  }
295  return _error;
296 }
297 
298 ////////////////////////////////////////////////////////////////////
299 // Function: DatagramOutputFile::flush
300 // Access: Public, Virtual
301 // Description: Ensures that all datagrams previously written will be
302 // visible in the output file.
303 ////////////////////////////////////////////////////////////////////
305 flush() {
306  if (_out != (ostream *)NULL) {
307  _out->flush();
308  }
309 }
310 
311 
312 ////////////////////////////////////////////////////////////////////
313 // Function: DatagramOutputFile::get_filename
314 // Access: Published, Virtual
315 // Description: Returns the filename that provides the target for
316 // these datagrams, if any, or empty string if the
317 // datagrams do not get written to a file on disk.
318 ////////////////////////////////////////////////////////////////////
321  return _filename;
322 }
323 
324 ////////////////////////////////////////////////////////////////////
325 // Function: DatagramOutputFile::get_file
326 // Access: Published, Virtual
327 // Description: Returns the FileReference that provides the target for
328 // these datagrams, if any, or NULL if the datagrams do
329 // not written to a file on disk.
330 ////////////////////////////////////////////////////////////////////
333  return _file;
334 }
335 
336 ////////////////////////////////////////////////////////////////////
337 // Function: DatagramOutputFile::get_file_pos
338 // Access: Published, Virtual
339 // Description: Returns the current file position within the data
340 // stream, if any, or 0 if the file position is not
341 // meaningful or cannot be determined.
342 //
343 // For DatagramOutputFiles that return a meaningful file
344 // position, this will be pointing to the first byte
345 // following the datagram returned after a call to
346 // put_datagram().
347 ////////////////////////////////////////////////////////////////////
348 streampos DatagramOutputFile::
350  if (_out == (ostream *)NULL) {
351  return 0;
352  }
353  return _out->tellp();
354 }
A StreamWriter object is used to write sequential binary data directly to an ostream.
Definition: streamWriter.h:33
virtual streampos get_file_pos()
Returns the current file position within the data stream, if any, or 0 if the file position is not me...
Keeps a reference-counted pointer to a file on disk.
Definition: fileReference.h:29
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
bool write_header(const string &header)
Writes a sequence of bytes to the beginning of the datagram file.
virtual const Filename & get_filename()
Returns the filename that provides the target for these datagrams, if any, or empty string if the dat...
virtual bool is_error()
Returns true if the file has reached an error condition.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:37
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it...
virtual bool copy_datagram(SubfileInfo &result, const Filename &filename)
Copies the file data from the entire indicated file (via the vfs) as the next datagram.
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:2003
bool open(const FileReference *file)
Opens the indicated filename for writing.
virtual bool put_datagram(const Datagram &data)
Writes the given datagram to the file.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the stream.
Definition: streamWriter.I:170
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
virtual void flush()
Ensures that all datagrams previously written will be visible in the output file. ...
const Filename & get_filename() const
A shortcut to the filename.
Definition: subfileInfo.I:106
virtual const FileReference * get_file()
Returns the FileReference that provides the target for these datagrams, if any, or NULL if the datagr...
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:29
streampos get_start() const
Returns the offset within the file at which this file data begins.
Definition: subfileInfo.I:121
static void close_write_file(ostream *stream)
Closes a file opened by a previous call to open_write_file().
void close()
Closes the file.
void add_uint64(PN_uint64 value)
Adds an unsigned 64-bit integer to the stream.
Definition: streamWriter.I:181
streamsize get_size() const
Returns the number of consecutive bytes, beginning at get_start(), that correspond to this file data...
Definition: subfileInfo.I:132
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
size_t get_length() const
Returns the number of bytes in the datagram.
Definition: datagram.I:457
const void * get_data() const
Returns a pointer to the beginning of the datagram&#39;s data.
Definition: datagram.I:447