PageRenderTime 42ms CodeModel.GetById 11ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Rainman2/file.h

http://modstudio2.googlecode.com/
C++ Header | 514 lines | 228 code | 70 blank | 216 comment | 5 complexity | 341767cce9865c152356ad1e52389650 MD5 | raw file
  1/*
  2Copyright (c) 2008 Peter "Corsix" Cawley
  3
  4Permission is hereby granted, free of charge, to any person
  5obtaining a copy of this software and associated documentation
  6files (the "Software"), to deal in the Software without
  7restriction, including without limitation the rights to use,
  8copy, modify, merge, publish, distribute, sublicense, and/or sell
  9copies of the Software, and to permit persons to whom the
 10Software is furnished to do so, subject to the following
 11conditions:
 12
 13The above copyright notice and this permission notice shall be
 14included in all copies or substantial portions of the Software.
 15
 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 18OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 20HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 21WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 22FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 23OTHER DEALINGS IN THE SOFTWARE.
 24*/
 25#pragma once
 26#include "string.h"
 27#include <stdio.h>
 28#include <vector>
 29#include <iterator>
 30
 31//! Type used when specifying the offset in file seek / tell operations
 32typedef long seek_offset_t;
 33
 34//! Values passed to IFIle::seek to specify where to seek from
 35/*!
 36  These values map to their C standard library equivalents for ease of convertion
 37*/
 38enum seek_relative_t
 39{
 40  SR_Start   = SEEK_SET, //!< Seek relative to the start of the file
 41  SR_Current = SEEK_CUR, //!< Seek relative to the current position in the file
 42  SR_End     = SEEK_END, //!< Seek relative to the end of the file
 43};
 44
 45//! Different ways of opening a file
 46enum eFileOpenMode
 47{
 48  FM_Read,  //!< Open a file in read-only binary mode
 49  FM_Write, //!< Open a file in read & write binary mode
 50};
 51
 52//! A generic interface for files and other file-like things
 53/*!
 54  RainOpenFile() can be used to open a traditional file and obtain an IFile pointer
 55*/
 56class RAINMAN2_API IFile
 57{
 58public:
 59  virtual ~IFile() throw(); //!< virtual destructor
 60
 61  //! Read bytes from the file
 62  /*!
 63    Reads a number of items from a file and copies them to a user-supplied buffer.
 64    If it cannot read all of the items, then an exception will be thrown, and the file
 65    position may or may not have been incremented.
 66    \param pDestination Buffer at least iItemSize*iItemCount bytes big to copy the
 67           read bytes into.
 68    \param iItemSize Size, in bytes, of one item
 69    \param iItemCount Number of items to read from the file
 70    \sa readNoThrow() readOne() readArray()
 71  */
 72  virtual void read(void* pDestination, size_t iItemSize, size_t iItemCount) throw(...) = 0;
 73
 74  //! Read bytes from the file, without throwing an exception
 75  /*!
 76    Reads a number of items from a file and copies them to a user-supplied buffer.
 77    If it cannot read all of the items, then it will read as many as it can, incrementing
 78    the file position by that number of items.
 79    \param pDestination Buffer at least iItemSize*iItemCount bytes big to copy the
 80           read bytes into.
 81    \param iItemSize Size, in bytes, of one item
 82    \param iItemCount Number of items to read from the file
 83    \return The number of items read
 84    \sa read() readOneNoThrow() readArrayNoThrow()
 85  */
 86  virtual size_t readNoThrow(void* pDestination, size_t iItemSize, size_t iItemCount) throw() = 0;
 87
 88  //! Read a single item from the file
 89  /*!
 90    Reads one item of a user-specified type.
 91    If the item cannot be read, then an exception will be thrown and the file position
 92    incremented by an unspecified amount.
 93    \param T The type of item to read (usually determined automatically by the compiler)
 94    \param oDestination Variable to store the read item in
 95    \sa read() readArray() readOneNoThrow()
 96  */
 97  template <class T>
 98  void readOne(T& oDestination) throw(...)
 99  { read(&oDestination, sizeof(T), 1); }
100
101  //! Read a single item from the file, without throwing an exception
102  /*!
103    Reads one item of a user-specified type.
104    If the item cannot be read, then 0 is returned and the file position is unchanged.
105    \param T The type of item to read (usually determined automatically by the compiler)
106    \param oDestination Variable to store the read item in
107    \return The number of items read (0 or 1)
108    \sa readNoThrow() readOne() readArrayNoThrow()
109  */
110  template <class T>
111  size_t readOneNoThrow(T& oDestination) throw()
112  { return readNoThrow(&oDestination, sizeof(T), 1); }
113
114  //! Read an array of items from the file
115  /*!
116    Reads a specified number of iterms of a user-specified type.
117    If the requested number of items cannot be read, then an exception will be thrown
118    and the file position incremented by an unspecified amount.
119    \param T The type of item to read (usually determined automatically by the compiler)
120    \param pDestination Pointer to an array of at-lest size iCount
121    \param iCount Number of items to read
122    \sa read() readOne() readOneNoThrow()
123  */
124  template <class T>
125  void readArray(T* pDestination, size_t iCount) throw(...)
126  { read(pDestination, sizeof(T), iCount); }
127
128#ifdef RAINMAN2_USE_LUA
129  //! Load the file as a Lua chunk
130  /*!
131    Reads the remainder of the file (i.e. the whole file if the file pointer is at the
132    start) into a Lua state as a chunk.
133    \param L The Lua state to load into
134    \param sChunkName The name to give the loaded chunk
135    \return Same values as ::lua_load(), with an error or chunk placed on the Lua stack
136  */
137  int lua_load(lua_State *L, const char* sChunkName) throw();
138#endif
139
140  //! Read an array of items from the file, without throwing an exception
141  /*!
142    Reads a specified number of iterms of a user-specified type.
143    If the requested number of items cannot be read, then it will read as many
144    as it can, incrementing the file position by that number of items.
145    \param T The type of item to read (usually determined automatically by the compiler)
146    \param pDestination Pointer to an array of at-lest size iCount
147    \param iCount Number of items to read
148    \return The number of items read (which may be less than the requested number)
149    \sa readNoThrow() readOneNoThrow() readArray()
150  */
151  template <class T>
152  size_t readArrayNoThrow(T* pDestination, size_t iCount) throw()
153  { return readNoThrow(pDestination, sizeof(T), iCount); }
154
155  virtual void write(const void* pSource, size_t iItemSize, size_t iItemCount) throw(...) = 0;
156  virtual size_t writeNoThrow(const void* pSource, size_t iItemSize, size_t iItemCount) throw() = 0;
157
158  template <class T>
159  void writeOne(const T& oSource) throw(...)
160  { write(&oSource, sizeof(T), 1); }
161
162  template <class T>
163  size_t writeOneThrow(const T& oSource) throw()
164  { return writeNoThrow(&oSource, sizeof(T), 1); }
165
166  template <class T>
167  void writeArray(const T* pSource, size_t iCount) throw(...)
168  { write(pSource, sizeof(T), iCount); }
169
170  template <class T>
171  size_t writeArrayNoThrow(const T* pSource, size_t iCount) throw()
172  { return writeNoThrow(pSource, sizeof(T), iCount); }
173
174  virtual void seek(seek_offset_t iOffset, seek_relative_t eRelativeTo) throw(...) = 0;
175  virtual bool seekNoThrow(seek_offset_t iOffset, seek_relative_t eRelativeTo) throw() = 0;
176
177  virtual seek_offset_t tell() throw() = 0;
178
179  template <class T>
180  RainString readString(T cDelimiter)
181  {
182    RainString r;
183    T v;
184    readOne(v);
185    while(v != cDelimiter)
186    {
187      r += v;
188      readOne(v);
189    }
190    return r;
191  }
192};
193
194struct filesize_t
195{
196  unsigned long iUpper, iLower;
197};
198
199typedef time_t filetime_t; //!< Unix time stamp; seconds elapsed since Jan 1, 1970
200
201struct RAINMAN2_API directory_item_t
202{
203  struct field_list_t
204  {
205    bool name : 1;
206    bool dir : 1;
207    bool size : 1;
208    bool time : 1;
209
210    field_list_t& operator= (bool value) throw();
211  } oFields;
212
213  RainString sName;
214  bool bIsDirectory;
215  filesize_t iSize;
216  filetime_t iTimestamp;
217};
218
219class RAINMAN2_API IDirectory;
220
221class RAINMAN2_API auto_directory_item
222{
223public:
224  auto_directory_item() throw();
225  auto_directory_item(const auto_directory_item&) throw();
226  auto_directory_item(IDirectory*, size_t) throw();
227
228  auto_directory_item& operator =  (const auto_directory_item&) throw();
229                  bool operator == (const auto_directory_item&) const throw();
230                  bool operator != (const auto_directory_item&) const throw();
231
232  void setItem(IDirectory*, size_t) throw();
233
234  bool isValid() const throw();
235  IDirectory* getDirectory() const throw();
236  size_t getIndex() const throw();
237
238  RainString name() const throw(...);
239  bool isDirectory() const throw(...);
240  filesize_t size() const throw(...);
241  filetime_t timestamp() const throw(...);
242
243  IDirectory* open       ()                    const throw(...);
244  IFile*      open       (eFileOpenMode eMode) const throw(...);
245  void        pump       (IFile *pSink)        const throw(...);
246  IDirectory* openNoThrow()                    const throw();
247  IFile*      openNoThrow(eFileOpenMode eMode) const throw();
248
249protected:
250  IDirectory* m_pDirectory;
251  size_t m_iIndex;
252  mutable directory_item_t m_oItem;
253  mutable directory_item_t::field_list_t m_oFieldsPresent;
254};
255
256class RAINMAN2_API IFileStore;
257
258class RAINMAN2_API IDirectory
259{
260public:
261  //! virtual destructor
262  virtual ~IDirectory() throw();
263  
264  //! Get the total number of items (files and sub-directories) within the directory
265  virtual size_t getItemCount() throw() = 0;
266
267  //! Get certain details about an item in the directory
268  /*!
269    \param iIndex A value in range [0, getItemCount), specifying the item to get details on
270    \param oDetails The details marked as true in the oFields member will be updated to
271      reflect the value of that detail for the requested item; other details will be left
272      unchanged.
273    Will throw an exception only if the index is out of range. Should file-specific details
274    be requested for a directory, then an exception shall NOT be thrown, but the value
275    assigned to those details is undefined.
276  */
277  virtual void getItemDetails(size_t iIndex, directory_item_t& oDetails) throw(...) = 0;
278
279  virtual const RainString& getPath() throw() = 0;
280  virtual IFileStore* getStore() throw() = 0;
281
282  virtual IFile* openFile(size_t iIndex, eFileOpenMode eMode) throw(...);
283  virtual void   pumpFile(size_t iIndex, IFile *pSink) throw(...);
284  virtual IFile* openFileNoThrow(size_t iIndex, eFileOpenMode eMode) throw();
285  virtual IDirectory* openDirectory(size_t iIndex) throw(...);
286  virtual IDirectory* openDirectoryNoThrow(size_t iIndex) throw();
287
288  //! Input iterator for easily traversing the contents of a directory
289  /*!
290    Use IDirectory::begin() and IDirectory::end() to get iterators for a
291    particular directory. These iterators should be able to be used with
292    any STL functions which expect input iterators, for example, the following
293    construction will print a list of items in pDirectory to stdout:
294    std::transform(pDirectory->begin(), pDirectory->end(),
295      std::ostream_iterator<RainString, wchar_t>(std::wcout, L"\n"),
296      std::mem_fun_ref(&auto_directory_item::name)
297    );
298    The following has a similar effect, but without much of the STL:
299    for(IDirectory::iterator itr = pDirectory->begin(); itr != pDirectory->end(); ++itr)
300      std::wcout << itr->name() << std::endl;
301  */
302  class RAINMAN2_API iterator
303  {
304  public:
305    iterator() throw();
306    iterator(const iterator&) throw();
307    iterator(IDirectory*, size_t) throw();
308
309    typedef std::input_iterator_tag iterator_category;
310    typedef const auto_directory_item value_type;
311    typedef ptrdiff_t difference_type;
312    typedef value_type* pointer;
313    typedef value_type& reference;
314
315    iterator& operator =  (const iterator&) throw();
316    iterator& operator ++ ()                throw();
317    iterator& operator -- ()                throw();
318    bool      operator == (const iterator&) const throw();
319    bool      operator != (const iterator&) const throw();
320    reference operator *  ()                const throw();
321    pointer   operator -> ()                const throw();
322
323  protected:
324    auto_directory_item m_item;
325  };
326
327  iterator begin();
328  iterator end();
329};
330
331//! Definition of the capabilities of a file store
332/*!
333  Call IFileStore::getCaps() to fill this structure.
334  The general rule for capabilities is to set them to false if that operation is
335  never possible, and set it to true if the operation may be possible, depending
336  on the file it is applied to. For example, the file system store reports that
337  it has the delete file capability, even though it cannot delete certain files.
338  The SGA store reports that it doesn't have the delete file capability, because
339  it cannot delete any files.
340*/
341struct RAINMAN2_API file_store_caps_t
342{
343  //! Default constructor; sets all capabilities flags to false
344  file_store_caps_t() throw();
345
346  //! Set all capabilities to the same value
347  /*!
348    A common pattern is to set all capabilities to false using this,
349    then flag as true the capabilities which are supported. This is
350    future-proof, as if any new capabilities are added later, then
351    this method will cause them to be set to false.
352  */
353  file_store_caps_t& operator= (bool bValue) throw();
354
355  bool bCanReadFiles : 1;         //!< true if files can be opened for reading
356  bool bCanWriteFiles : 1;        //!< true if files can be opened for writing
357  bool bCanDeleteFiles : 1;       //!< true if files can be deleted
358  bool bCanOpenDirectories : 1;   //!< true if directories can be opened
359  bool bCanCreateDirectories : 1; //!< true if directories can be created
360  bool bCanDeleteDirectories : 1; //!< true if directories can be deleted
361};
362
363//! Interface for entities which contain files
364/*!
365  Any entity containing files (e.g. file archive, hard disks, etc.) must implement this
366  interface to make the files within it available to the application.
367*/
368class RAINMAN2_API IFileStore
369{
370public:
371  virtual ~IFileStore() throw(); //!< virtual destructor
372  
373  //! Get details of the capabilities of the file store
374  /*!
375  	Not all file stores support all operations, so it may be useful to query the file
376  	store to determine what it can do.
377  	\param oCaps A capabilites structure to be filled
378  */
379  virtual void getCaps(file_store_caps_t& oCaps) const throw() = 0;
380  
381  //! Open a file within the store for reading for writing
382  /*!
383  	\param sPath The full path to the file in question
384  	\param eMode The mode (i.e. read or write) in which to open the file
385  	\return A valid file interface pointer, which the caller must later delete
386  */
387  virtual IFile* openFile(const RainString& sPath, eFileOpenMode eMode) throw(...) = 0;
388
389  //! Open a file within the store for reading for writing, never throwing an exception
390  /*!
391  	Same as openFile(), except it will never throw an exception, instead returning a null
392  	pointer.
393  	\param sPath The full path to the file in question
394  	\param eMode The mode (i.e. read or write) in which to open the file
395  	\return A valid file interface pointer, which the caller must later delete, or in the
396  	  case of an error, a null pointer
397  */
398  virtual IFile* openFileNoThrow(const RainString& sPath, eFileOpenMode eMode) throw() = 0;
399
400  //! Open a file and copy its contents to another file
401  virtual void pumpFile(const RainString& sPath, IFile* pSink) throw(...);
402
403  virtual bool doesFileExist(const RainString& sPath) throw() = 0;
404  
405  //! Delete a file from the store
406  /*!
407  	\param sPath The full path to the file in question
408  */
409  virtual void deleteFile(const RainString& sPath) throw(...) = 0;
410  
411  //! Delete a file from the store, never throwing an exception
412  /*!
413    Same as deleteFile(), except it will never throw an exception, instead returning
414    false
415    \return true if the file was deleted, false in case of error
416  */
417  virtual bool deleteFileNoThrow(const RainString& sPath) throw() = 0;
418  
419  //! Get the number of entry points into the file store
420  /*!
421  	The root level folders in the file store are called entry points, which may corespond
422  	to drive letters (win32 file system), tables of contents (SGA archives), etc.
423  */
424  virtual size_t getEntryPointCount() throw() = 0;
425  
426  //! Get the name of one of the entry points
427  /*!
428    Only throws an exception when the index is out of bounds.
429  	\param iIndex A value between 0 and getEntryPointCount() - 1 (inclusive)
430  	\return Name of the entry point, which can be passed to openDirectory()
431  */
432  virtual const RainString& getEntryPointName(size_t iIndex) throw(...) = 0;
433  
434  //! Open a directory within the store for enumeration
435  /*!
436  	\param sPath The full path to the directory in question (trailing slash optional)
437  	\return A valid directory interface pointer, which the caller must later delete
438  */
439  virtual IDirectory* openDirectory(const RainString& sPath) throw(...) = 0;
440  
441  //! Open a directory within the store for enumeration, never throwing an exception
442  /*!
443  	Same as openDirectory(), except it will never throw an exception, instead returning a
444  	null pointer.
445  	\param sPath The full path to the directory in question (trailing slash optional)
446  	\return A valid directory interface pointer, which the caller must later delete, or
447  	  in the case of an error, a null pointer
448  */
449  virtual IDirectory* openDirectoryNoThrow(const RainString& sPath) throw() = 0;
450
451  virtual bool doesDirectoryExist(const RainString& sPath) throw() = 0;
452
453  virtual void createDirectory(const RainString& sPath) throw(...) = 0;
454  virtual bool createDirectoryNoThrow(const RainString& sPath) throw() = 0;
455  virtual void deleteDirectory(const RainString& sPath) throw(...) = 0;
456  virtual bool deleteDirectoryNoThrow(const RainString& sPath) throw() = 0;
457};
458
459//! Implementation of the IFileStore interface for the standard physical filesystem
460/*!
461  RainGetFileSystemStore() can be used to get a instance of this class. Functions
462  like RainOpenFile(), RainDeleteFile(), RainOpenDirectory() and their NoThrow
463  equivalents can also be used instead of this class when an IFileStore interface
464  is not required.
465*/
466class RAINMAN2_API FileSystemStore : public IFileStore
467{
468public:
469  FileSystemStore() throw();
470  ~FileSystemStore() throw();
471
472  virtual void getCaps(file_store_caps_t& oCaps) const throw();
473
474  virtual IFile* openFile         (const RainString& sPath, eFileOpenMode eMode) throw(...);
475  virtual IFile* openFileNoThrow  (const RainString& sPath, eFileOpenMode eMode) throw();
476  virtual bool   doesFileExist    (const RainString& sPath) throw();
477  virtual void   deleteFile       (const RainString& sPath) throw(...);
478  virtual bool   deleteFileNoThrow(const RainString& sPath) throw();
479
480  virtual size_t            getEntryPointCount() throw();
481  virtual const RainString& getEntryPointName(size_t iIndex) throw(...);
482
483  virtual IDirectory* openDirectory         (const RainString& sPath) throw(...);
484  virtual IDirectory* openDirectoryNoThrow  (const RainString& sPath) throw();
485  virtual bool        doesDirectoryExist    (const RainString& sPath) throw();
486  virtual void        createDirectory       (const RainString& sPath) throw(...);
487  virtual bool        createDirectoryNoThrow(const RainString& sPath) throw();
488  virtual void        deleteDirectory       (const RainString& sPath) throw(...);
489  virtual bool        deleteDirectoryNoThrow(const RainString& sPath) throw();
490
491public:
492  void _ensureGotEntryPoints() throw();
493
494  bool m_bKnowEntryPoints;
495  std::vector<RainString> m_vEntryPoints;
496};
497
498RAINMAN2_API FileSystemStore* RainGetFileSystemStore();
499
500RAINMAN2_API IFile* RainOpenFile(const RainString& sPath, eFileOpenMode eMode) throw(...);
501RAINMAN2_API IFile* RainOpenFileNoThrow(const RainString& sPath, eFileOpenMode eMode) throw();
502RAINMAN2_API IFile* RainOpenFilePtr(FILE* pFile, bool bCloseWhenDone = true) throw(...);
503RAINMAN2_API bool RainDoesFileExist(const RainString& sPath) throw();
504RAINMAN2_API void RainDeleteFile(const RainString& sPath) throw(...);
505RAINMAN2_API bool RainDeleteFileNoThrow(const RainString& sPath) throw();
506RAINMAN2_API IDirectory* RainOpenDirectory(const RainString& sPath) throw(...);
507RAINMAN2_API IDirectory* RainOpenDirectoryNoThrow(const RainString& sPath) throw();
508RAINMAN2_API bool        RainDoesDirectoryExist    (const RainString& sPath) throw();
509RAINMAN2_API void        RainCreateDirectory       (const RainString& sPath) throw(...);
510RAINMAN2_API bool        RainCreateDirectoryNoThrow(const RainString& sPath) throw();
511RAINMAN2_API void        RainDeleteDirectory       (const RainString& sPath) throw(...);
512RAINMAN2_API bool        RainDeleteDirectoryNoThrow(const RainString& sPath) throw();
513
514RAINMAN2_API filesize_t operator+ (const filesize_t& a, const filesize_t& b);