OpenMW
|
00001 #ifndef OPENMW_ESM_READER_H 00002 #define OPENMW_ESM_READER_H 00003 00004 #include <libs/platform/stdint.h> 00005 #include <libs/platform/string.h> 00006 #include <cassert> 00007 #include <vector> 00008 #include <sstream> 00009 00010 #include <OgreDataStream.h> 00011 00012 #include <components/misc/stringops.hpp> 00013 00014 #include <components/to_utf8/to_utf8.hpp> 00015 00016 #include "esmcommon.hpp" 00017 #include "loadtes3.hpp" 00018 00019 namespace ESM { 00020 00021 class ESMReader 00022 { 00023 public: 00024 00025 ESMReader(); 00026 00027 /************************************************************************* 00028 * 00029 * Information retrieval 00030 * 00031 *************************************************************************/ 00032 00033 int getVer() const { return mHeader.mData.version; } 00034 float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } 00035 const std::string getAuthor() const { return mHeader.mData.author.toString(); } 00036 const std::string getDesc() const { return mHeader.mData.desc.toString(); } 00037 const std::vector<Header::MasterData> &getGameFiles() const { return mHeader.mMaster; } 00038 int getFormat() const; 00039 const NAME &retSubName() const { return mCtx.subName; } 00040 uint32_t getSubSize() const { return mCtx.leftSub; } 00041 00042 /************************************************************************* 00043 * 00044 * Opening and closing 00045 * 00046 *************************************************************************/ 00047 00051 ESM_Context getContext(); 00052 00054 void restoreContext(const ESM_Context &rc); 00055 00059 void close(); 00060 00063 void openRaw(Ogre::DataStreamPtr _esm, const std::string &name); 00064 00067 void open(Ogre::DataStreamPtr _esm, const std::string &name); 00068 00069 void open(const std::string &file); 00070 00071 void openRaw(const std::string &file); 00072 00074 size_t getFileSize() { return mEsm->size(); } 00076 size_t getFileOffset() { return mEsm->tell(); } 00077 00078 // This is a quick hack for multiple esm/esp files. Each plugin introduces its own 00079 // terrain palette, but ESMReader does not pass a reference to the correct plugin 00080 // to the individual load() methods. This hack allows to pass this reference 00081 // indirectly to the load() method. 00082 int mIdx; 00083 void setIndex(const int index) {mIdx = index; mCtx.index = index;} 00084 const int getIndex() {return mIdx;} 00085 00086 void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;} 00087 std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;} 00088 00089 /************************************************************************* 00090 * 00091 * Medium-level reading shortcuts 00092 * 00093 *************************************************************************/ 00094 00095 // Read data of a given type, stored in a subrecord of a given name 00096 template <typename X> 00097 void getHNT(X &x, const char* name) 00098 { 00099 getSubNameIs(name); 00100 getHT(x); 00101 } 00102 00103 // Optional version of getHNT 00104 template <typename X> 00105 void getHNOT(X &x, const char* name) 00106 { 00107 if(isNextSub(name)) 00108 getHT(x); 00109 } 00110 00111 // Version with extra size checking, to make sure the compiler 00112 // doesn't mess up our struct padding. 00113 template <typename X> 00114 void getHNT(X &x, const char* name, int size) 00115 { 00116 assert(sizeof(X) == size); 00117 getSubNameIs(name); 00118 getHT(x); 00119 } 00120 00121 template <typename X> 00122 void getHNOT(X &x, const char* name, int size) 00123 { 00124 assert(sizeof(X) == size); 00125 if(isNextSub(name)) 00126 getHT(x); 00127 } 00128 00129 int64_t getHNLong(const char *name); 00130 00131 // Get data of a given type/size, including subrecord header 00132 template <typename X> 00133 void getHT(X &x) 00134 { 00135 getSubHeader(); 00136 if (mCtx.leftSub != sizeof(X)) 00137 fail("getHT(): subrecord size mismatch"); 00138 getT(x); 00139 } 00140 00141 // Version with extra size checking, to make sure the compiler 00142 // doesn't mess up our struct padding. 00143 template <typename X> 00144 void getHT(X &x, int size) 00145 { 00146 assert(sizeof(X) == size); 00147 getHT(x); 00148 } 00149 00150 // Read a string by the given name if it is the next record. 00151 std::string getHNOString(const char* name); 00152 00153 // Read a string with the given sub-record name 00154 std::string getHNString(const char* name); 00155 00156 // Read a string, including the sub-record header (but not the name) 00157 std::string getHString(); 00158 00159 // Read the given number of bytes from a subrecord 00160 void getHExact(void*p, int size); 00161 00162 // Read the given number of bytes from a named subrecord 00163 void getHNExact(void*p, int size, const char* name); 00164 00165 /************************************************************************* 00166 * 00167 * Low level sub-record methods 00168 * 00169 *************************************************************************/ 00170 00171 // Get the next subrecord name and check if it matches the parameter 00172 void getSubNameIs(const char* name); 00173 00179 bool isNextSub(const char* name); 00180 00181 // Read subrecord name. This gets called a LOT, so I've optimized it 00182 // slightly. 00183 void getSubName(); 00184 00185 // This is specially optimized for LoadINFO. 00186 bool isEmptyOrGetName(); 00187 00188 // Skip current sub record, including header (but not including 00189 // name.) 00190 void skipHSub(); 00191 00192 // Skip sub record and check its size 00193 void skipHSubSize(int size); 00194 00195 /* Sub-record header. This updates leftRec beyond the current 00196 sub-record as well. leftSub contains size of current sub-record. 00197 */ 00198 void getSubHeader(); 00199 00202 void getSubHeaderIs(int size); 00203 00204 /************************************************************************* 00205 * 00206 * Low level record methods 00207 * 00208 *************************************************************************/ 00209 00210 // Get the next record name 00211 NAME getRecName(); 00212 00213 // Skip the rest of this record. Assumes the name and header have 00214 // already been read 00215 void skipRecord(); 00216 00217 // Skip an entire record, including the header (but not the name) 00218 void skipHRecord(); 00219 00220 /* Read record header. This updatesleftFile BEYOND the data that 00221 follows the header, ie beyond the entire record. You should use 00222 leftRec to orient yourself inside the record itself. 00223 */ 00224 void getRecHeader() { getRecHeader(mRecordFlags); } 00225 void getRecHeader(uint32_t &flags); 00226 00227 bool hasMoreRecs() const { return mCtx.leftFile > 0; } 00228 bool hasMoreSubs() const { return mCtx.leftRec > 0; } 00229 00230 00231 /************************************************************************* 00232 * 00233 * Lowest level data reading and misc methods 00234 * 00235 *************************************************************************/ 00236 00237 template <typename X> 00238 void getT(X &x) { getExact(&x, sizeof(X)); } 00239 00240 void getExact(void*x, int size); 00241 void getName(NAME &name) { getT(name); } 00242 void getUint(uint32_t &u) { getT(u); } 00243 00244 // Read the next 'size' bytes and return them as a string. Converts 00245 // them from native encoding to UTF8 in the process. 00246 std::string getString(int size); 00247 00248 void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); } 00249 uint64_t getOffset() { return mEsm->tell(); } 00250 00252 void fail(const std::string &msg); 00253 00255 void setEncoder(ToUTF8::Utf8Encoder* encoder); 00256 00258 unsigned int getRecordFlags() { return mRecordFlags; } 00259 00260 private: 00261 Ogre::DataStreamPtr mEsm; 00262 00263 ESM_Context mCtx; 00264 00265 unsigned int mRecordFlags; 00266 00267 // Special file signifier (see SpecialFile enum above) 00268 00269 // Buffer for ESM strings 00270 std::vector<char> mBuffer; 00271 00272 Header mHeader; 00273 00274 std::vector<ESMReader> *mGlobalReaderList; 00275 ToUTF8::Utf8Encoder* mEncoder; 00276 }; 00277 } 00278 #endif