/Rainman2/file.h

http://modstudio2.googlecode.com/ · C Header · 514 lines · 228 code · 70 blank · 216 comment · 6 complexity · 341767cce9865c152356ad1e52389650 MD5 · raw file

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