mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-03 23:26:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef OPENMW_ESM_READER_H
 | 
						|
#define OPENMW_ESM_READER_H
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <string.h>
 | 
						|
#include <cassert>
 | 
						|
#include <vector>
 | 
						|
#include <sstream>
 | 
						|
 | 
						|
#include <OgreDataStream.h>
 | 
						|
 | 
						|
#include <components/misc/stringops.hpp>
 | 
						|
 | 
						|
#include <components/to_utf8/to_utf8.hpp>
 | 
						|
 | 
						|
#include "esmcommon.hpp"
 | 
						|
#include "loadtes3.hpp"
 | 
						|
 | 
						|
namespace ESM {
 | 
						|
 | 
						|
class ESMReader
 | 
						|
{
 | 
						|
public:
 | 
						|
 | 
						|
  ESMReader();
 | 
						|
 | 
						|
  /*************************************************************************
 | 
						|
   *
 | 
						|
   *  Information retrieval
 | 
						|
   *
 | 
						|
   *************************************************************************/
 | 
						|
 | 
						|
  int getVer() const { return mHeader.mData.version; }
 | 
						|
  int getRecordCount() const { return mHeader.mData.records; }
 | 
						|
  float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; }
 | 
						|
  const std::string getAuthor() const { return mHeader.mData.author.toString(); }
 | 
						|
  const std::string getDesc() const { return mHeader.mData.desc.toString(); }
 | 
						|
  const std::vector<Header::MasterData> &getGameFiles() const { return mHeader.mMaster; }
 | 
						|
  const Header& getHeader() const { return mHeader; }
 | 
						|
  int getFormat() const;
 | 
						|
  const NAME &retSubName() const { return mCtx.subName; }
 | 
						|
  uint32_t getSubSize() const { return mCtx.leftSub; }
 | 
						|
  std::string getName() const;
 | 
						|
 | 
						|
  /*************************************************************************
 | 
						|
   *
 | 
						|
   *  Opening and closing
 | 
						|
   *
 | 
						|
   *************************************************************************/
 | 
						|
 | 
						|
  /** Save the current file position and information in a ESM_Context
 | 
						|
      struct
 | 
						|
   */
 | 
						|
  ESM_Context getContext();
 | 
						|
 | 
						|
  /** Restore a previously saved context */
 | 
						|
  void restoreContext(const ESM_Context &rc);
 | 
						|
 | 
						|
  /** Close the file, resets all information. After calling close()
 | 
						|
      the structure may be reused to load a new file.
 | 
						|
  */
 | 
						|
  void close();
 | 
						|
 | 
						|
  /// Raw opening. Opens the file and sets everything up but doesn't
 | 
						|
  /// parse the header.
 | 
						|
  void openRaw(Ogre::DataStreamPtr _esm, const std::string &name);
 | 
						|
 | 
						|
  /// Load ES file from a new stream, parses the header. Closes the
 | 
						|
  /// currently open file first, if any.
 | 
						|
  void open(Ogre::DataStreamPtr _esm, const std::string &name);
 | 
						|
 | 
						|
  void open(const std::string &file);
 | 
						|
 | 
						|
  void openRaw(const std::string &file);
 | 
						|
 | 
						|
  /// Get the file size. Make sure that the file has been opened!
 | 
						|
  size_t getFileSize() { return mEsm->size(); }
 | 
						|
  /// Get the current position in the file. Make sure that the file has been opened!
 | 
						|
  size_t getFileOffset() { return mEsm->tell(); }
 | 
						|
 | 
						|
  // This is a quick hack for multiple esm/esp files. Each plugin introduces its own
 | 
						|
  //  terrain palette, but ESMReader does not pass a reference to the correct plugin
 | 
						|
  //  to the individual load() methods. This hack allows to pass this reference
 | 
						|
  //  indirectly to the load() method.
 | 
						|
  int mIdx;
 | 
						|
  void setIndex(const int index) {mIdx = index; mCtx.index = index;}
 | 
						|
  int getIndex() {return mIdx;}
 | 
						|
 | 
						|
  void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;}
 | 
						|
  std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;}
 | 
						|
 | 
						|
  /*************************************************************************
 | 
						|
   *
 | 
						|
   *  Medium-level reading shortcuts
 | 
						|
   *
 | 
						|
   *************************************************************************/
 | 
						|
 | 
						|
  // Read data of a given type, stored in a subrecord of a given name
 | 
						|
  template <typename X>
 | 
						|
  void getHNT(X &x, const char* name)
 | 
						|
  {
 | 
						|
    getSubNameIs(name);
 | 
						|
    getHT(x);
 | 
						|
  }
 | 
						|
 | 
						|
  // Optional version of getHNT
 | 
						|
  template <typename X>
 | 
						|
  void getHNOT(X &x, const char* name)
 | 
						|
  {
 | 
						|
      if(isNextSub(name))
 | 
						|
          getHT(x);
 | 
						|
  }
 | 
						|
 | 
						|
  // Version with extra size checking, to make sure the compiler
 | 
						|
  // doesn't mess up our struct padding.
 | 
						|
  template <typename X>
 | 
						|
  void getHNT(X &x, const char* name, int size)
 | 
						|
  {
 | 
						|
      assert(sizeof(X) == size);
 | 
						|
      getSubNameIs(name);
 | 
						|
      getHT(x);
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename X>
 | 
						|
  void getHNOT(X &x, const char* name, int size)
 | 
						|
  {
 | 
						|
      assert(sizeof(X) == size);
 | 
						|
      if(isNextSub(name))
 | 
						|
          getHT(x);
 | 
						|
  }
 | 
						|
 | 
						|
  int64_t getHNLong(const char *name);
 | 
						|
 | 
						|
  // Get data of a given type/size, including subrecord header
 | 
						|
  template <typename X>
 | 
						|
  void getHT(X &x)
 | 
						|
  {
 | 
						|
      getSubHeader();
 | 
						|
      if (mCtx.leftSub != sizeof(X))
 | 
						|
      {
 | 
						|
          std::stringstream error;
 | 
						|
          error << "getHT(): subrecord size mismatch (requested " << sizeof(X) << ", got " << mCtx.leftSub << ")";
 | 
						|
          fail(error.str());
 | 
						|
      }
 | 
						|
      getT(x);
 | 
						|
  }
 | 
						|
 | 
						|
  // Version with extra size checking, to make sure the compiler
 | 
						|
  // doesn't mess up our struct padding.
 | 
						|
  template <typename X>
 | 
						|
  void getHT(X &x, int size)
 | 
						|
  {
 | 
						|
      assert(sizeof(X) == size);
 | 
						|
      getHT(x);
 | 
						|
  }
 | 
						|
 | 
						|
  // Read a string by the given name if it is the next record.
 | 
						|
  std::string getHNOString(const char* name);
 | 
						|
 | 
						|
  // Read a string with the given sub-record name
 | 
						|
  std::string getHNString(const char* name);
 | 
						|
 | 
						|
  // Read a string, including the sub-record header (but not the name)
 | 
						|
  std::string getHString();
 | 
						|
 | 
						|
  // Read the given number of bytes from a subrecord
 | 
						|
  void getHExact(void*p, int size);
 | 
						|
 | 
						|
  // Read the given number of bytes from a named subrecord
 | 
						|
  void getHNExact(void*p, int size, const char* name);
 | 
						|
 | 
						|
  /*************************************************************************
 | 
						|
   *
 | 
						|
   *  Low level sub-record methods
 | 
						|
   *
 | 
						|
   *************************************************************************/
 | 
						|
 | 
						|
  // Get the next subrecord name and check if it matches the parameter
 | 
						|
  void getSubNameIs(const char* name);
 | 
						|
 | 
						|
  /** Checks if the next sub record name matches the parameter. If it
 | 
						|
      does, it is read into 'subName' just as if getSubName() was
 | 
						|
      called. If not, the read name will still be available for future
 | 
						|
      calls to getSubName(), isNextSub() and getSubNameIs().
 | 
						|
   */
 | 
						|
  bool isNextSub(const char* name);
 | 
						|
 | 
						|
  bool peekNextSub(const char* name);
 | 
						|
 | 
						|
  // Read subrecord name. This gets called a LOT, so I've optimized it
 | 
						|
  // slightly.
 | 
						|
  void getSubName();
 | 
						|
 | 
						|
  // This is specially optimized for LoadINFO.
 | 
						|
  bool isEmptyOrGetName();
 | 
						|
 | 
						|
  // Skip current sub record, including header (but not including
 | 
						|
  // name.)
 | 
						|
  void skipHSub();
 | 
						|
 | 
						|
  // Skip sub record and check its size
 | 
						|
  void skipHSubSize(int size);
 | 
						|
 | 
						|
  // Skip all subrecords until the given subrecord or no more subrecords remaining
 | 
						|
  void skipHSubUntil(const char* name);
 | 
						|
 | 
						|
  /* Sub-record header. This updates leftRec beyond the current
 | 
						|
     sub-record as well. leftSub contains size of current sub-record.
 | 
						|
  */
 | 
						|
  void getSubHeader();
 | 
						|
 | 
						|
  /** Get sub header and check the size
 | 
						|
   */
 | 
						|
  void getSubHeaderIs(int size);
 | 
						|
 | 
						|
  /*************************************************************************
 | 
						|
   *
 | 
						|
   *  Low level record methods
 | 
						|
   *
 | 
						|
   *************************************************************************/
 | 
						|
 | 
						|
  // Get the next record name
 | 
						|
  NAME getRecName();
 | 
						|
 | 
						|
  // Skip the rest of this record. Assumes the name and header have
 | 
						|
  // already been read
 | 
						|
  void skipRecord();
 | 
						|
 | 
						|
  /* Read record header. This updatesleftFile BEYOND the data that
 | 
						|
     follows the header, ie beyond the entire record. You should use
 | 
						|
     leftRec to orient yourself inside the record itself.
 | 
						|
  */
 | 
						|
  void getRecHeader() { getRecHeader(mRecordFlags); }
 | 
						|
  void getRecHeader(uint32_t &flags);
 | 
						|
 | 
						|
  bool hasMoreRecs() const { return mCtx.leftFile > 0; }
 | 
						|
  bool hasMoreSubs() const { return mCtx.leftRec > 0; }
 | 
						|
 | 
						|
 | 
						|
  /*************************************************************************
 | 
						|
   *
 | 
						|
   *  Lowest level data reading and misc methods
 | 
						|
   *
 | 
						|
   *************************************************************************/
 | 
						|
 | 
						|
  template <typename X>
 | 
						|
  void getT(X &x) { getExact(&x, sizeof(X)); }
 | 
						|
 | 
						|
  void getExact(void*x, int size);
 | 
						|
  void getName(NAME &name) { getT(name); }
 | 
						|
  void getUint(uint32_t &u) { getT(u); }
 | 
						|
 | 
						|
  // Read the next 'size' bytes and return them as a string. Converts
 | 
						|
  // them from native encoding to UTF8 in the process.
 | 
						|
  std::string getString(int size);
 | 
						|
 | 
						|
  void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
 | 
						|
  uint64_t getOffset() { return mEsm->tell(); }
 | 
						|
 | 
						|
  /// Used for error handling
 | 
						|
  void fail(const std::string &msg);
 | 
						|
 | 
						|
  /// Sets font encoder for ESM strings
 | 
						|
  void setEncoder(ToUTF8::Utf8Encoder* encoder);
 | 
						|
 | 
						|
  /// Get record flags of last record
 | 
						|
  unsigned int getRecordFlags() { return mRecordFlags; }
 | 
						|
 | 
						|
private:
 | 
						|
  Ogre::DataStreamPtr mEsm;
 | 
						|
 | 
						|
  ESM_Context mCtx;
 | 
						|
 | 
						|
  unsigned int mRecordFlags;
 | 
						|
 | 
						|
  // Special file signifier (see SpecialFile enum above)
 | 
						|
 | 
						|
  // Buffer for ESM strings
 | 
						|
  std::vector<char> mBuffer;
 | 
						|
 | 
						|
  Header mHeader;
 | 
						|
 | 
						|
  std::vector<ESMReader> *mGlobalReaderList;
 | 
						|
  ToUTF8::Utf8Encoder* mEncoder;
 | 
						|
};
 | 
						|
}
 | 
						|
#endif
 |