1 /* 2 * FileReader.h 3 * ------------ 4 * Purpose: A basic class for transparent reading of memory-based files. 5 * Notes : (currently none) 6 * Authors: OpenMPT Devs 7 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. 8 */ 9 10 11 #pragma once 12 13 #include "openmpt/all/BuildSettings.hpp" 14 15 #include "mpt/io_read/filecursor.hpp" 16 #include "mpt/io_read/filecursor_filename_traits.hpp" 17 #include "mpt/io_read/filecursor_traits_filedata.hpp" 18 #include "mpt/io_read/filecursor_traits_memory.hpp" 19 #include "mpt/io_read/filereader.hpp" 20 21 #include "openmpt/base/Types.hpp" 22 23 #include "mptPathString.h" 24 #include "mptStringBuffer.h" 25 26 #include <algorithm> 27 #include <array> 28 #include <limits> 29 #include <optional> 30 #include <string> 31 #include <vector> 32 33 #include <cstring> 34 35 #include "FileReaderFwd.h" 36 37 38 OPENMPT_NAMESPACE_BEGIN 39 40 41 namespace FileReaderExt 42 { 43 44 // Read a string of length srcSize into fixed-length char array destBuffer using a given read mode. 45 // The file cursor is advanced by "srcSize" bytes. 46 // Returns true if at least one byte could be read or 0 bytes were requested. 47 template<mpt::String::ReadWriteMode mode, size_t destSize, typename TFileCursor> ReadString(TFileCursor & f,char (& destBuffer)[destSize],const typename TFileCursor::pos_type srcSize)48 bool ReadString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type srcSize) 49 { 50 typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. 51 typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available 52 mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize); 53 return (realSrcSize > 0 || srcSize == 0); 54 } 55 56 // Read a string of length srcSize into a std::string dest using a given read mode. 57 // The file cursor is advanced by "srcSize" bytes. 58 // Returns true if at least one character could be read or 0 characters were requested. 59 template<mpt::String::ReadWriteMode mode, typename TFileCursor> ReadString(TFileCursor & f,std::string & dest,const typename TFileCursor::pos_type srcSize)60 bool ReadString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type srcSize) 61 { 62 dest.clear(); 63 typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. 64 typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available 65 dest = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize); 66 return (realSrcSize > 0 || srcSize == 0); 67 } 68 69 // Read a string of length srcSize into a mpt::charbuf dest using a given read mode. 70 // The file cursor is advanced by "srcSize" bytes. 71 // Returns true if at least one character could be read or 0 characters were requested. 72 template<mpt::String::ReadWriteMode mode, std::size_t len, typename TFileCursor> ReadString(TFileCursor & f,mpt::charbuf<len> & dest,const typename TFileCursor::pos_type srcSize)73 bool ReadString(TFileCursor &f, mpt::charbuf<len> &dest, const typename TFileCursor::pos_type srcSize) 74 { 75 typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. 76 typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available 77 dest = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize); 78 return (realSrcSize > 0 || srcSize == 0); 79 } 80 81 // Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode. 82 // The file cursor is advanced by "srcSize" bytes. 83 // Returns true if at least one character could be read or 0 characters were requested. 84 template<mpt::String::ReadWriteMode mode, typename TFileCursor> ReadString(TFileCursor & f,mpt::ustring & dest,mpt::Charset charset,const typename TFileCursor::pos_type srcSize)85 bool ReadString(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::pos_type srcSize) 86 { 87 dest.clear(); 88 typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly. 89 typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available 90 dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize)); 91 return (realSrcSize > 0 || srcSize == 0); 92 } 93 94 // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. 95 // The file cursor is advanced by the string length. 96 // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. 97 template<typename Tsize, mpt::String::ReadWriteMode mode, size_t destSize, typename TFileCursor> 98 bool ReadSizedString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max()) 99 { 100 mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs 101 if(!mpt::IO::FileReader::Read(f, srcSize)) 102 { 103 return false; 104 } 105 return FileReaderExt::ReadString<mode>(f, destBuffer, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength)); 106 } 107 108 // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. 109 // The file cursor is advanced by the string length. 110 // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. 111 template<typename Tsize, mpt::String::ReadWriteMode mode, typename TFileCursor> 112 bool ReadSizedString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max()) 113 { 114 mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs 115 if(!mpt::IO::FileReader::Read(f, srcSize)) 116 { 117 return false; 118 } 119 return FileReaderExt::ReadString<mode>(f, dest, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength)); 120 } 121 122 // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf dest using a given read mode. 123 // The file cursor is advanced by the string length. 124 // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. 125 template<typename Tsize, mpt::String::ReadWriteMode mode, std::size_t len, typename TFileCursor> 126 bool ReadSizedString(TFileCursor &f, mpt::charbuf<len> &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max()) 127 { 128 mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs 129 if(!mpt::IO::FileReader::Read(f, srcSize)) 130 { 131 return false; 132 } 133 return FileReaderExt::ReadString<mode>(f, dest, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength)); 134 } 135 136 } // namespace FileReaderExt 137 138 namespace detail { 139 140 template <typename Ttraits, typename Tfilenametraits> 141 using FileCursor = mpt::IO::FileCursor<Ttraits, Tfilenametraits>; 142 143 template <typename Ttraits, typename Tfilenametraits> 144 class FileReader 145 : public FileCursor<Ttraits, Tfilenametraits> 146 { 147 148 private: 149 150 using traits_type = Ttraits; 151 using filename_traits_type = Tfilenametraits; 152 153 public: 154 155 using pos_type = typename traits_type::pos_type; 156 using off_t = pos_type; 157 158 using data_type = typename traits_type::data_type; 159 using ref_data_type = typename traits_type::ref_data_type; 160 using shared_data_type = typename traits_type::shared_data_type; 161 using value_data_type = typename traits_type::value_data_type; 162 163 using shared_filename_type = typename filename_traits_type::shared_filename_type; 164 165 public: 166 167 // Initialize invalid file reader object. FileReader()168 FileReader() 169 { 170 return; 171 } 172 FileReader(const FileCursor<Ttraits,Tfilenametraits> & other)173 FileReader(const FileCursor<Ttraits, Tfilenametraits> &other) 174 : FileCursor<Ttraits, Tfilenametraits>(other) 175 { 176 return; 177 } FileReader(FileCursor<Ttraits,Tfilenametraits> && other)178 FileReader(FileCursor<Ttraits, Tfilenametraits> &&other) 179 : FileCursor<Ttraits, Tfilenametraits>(std::move(other)) 180 { 181 return; 182 } 183 184 // Initialize file reader object with pointer to data and data length. 185 template <typename Tbyte> 186 explicit FileReader(mpt::span<Tbyte> bytedata, shared_filename_type filename = shared_filename_type{}) 187 : FileCursor<Ttraits, Tfilenametraits>(bytedata, std::move(filename)) 188 { 189 return; 190 } 191 192 // Initialize file reader object based on an existing file reader object window. 193 explicit FileReader(value_data_type other, shared_filename_type filename = shared_filename_type{}) 194 : FileCursor<Ttraits, Tfilenametraits>(std::move(other), std::move(filename)) 195 { 196 return; 197 } 198 199 public: 200 201 template <typename T> Read(T & target)202 bool Read(T &target) 203 { 204 return mpt::IO::FileReader::Read(*this, target); 205 } 206 207 template <typename T> ReadIntLE()208 T ReadIntLE() 209 { 210 return mpt::IO::FileReader::ReadIntLE<T>(*this); 211 } 212 213 template <typename T> ReadIntBE()214 T ReadIntBE() 215 { 216 return mpt::IO::FileReader::ReadIntLE<T>(*this); 217 } 218 219 template <typename T> ReadTruncatedIntLE(pos_type size)220 T ReadTruncatedIntLE(pos_type size) 221 { 222 return mpt::IO::FileReader::ReadTruncatedIntLE<T>(*this, size); 223 } 224 225 template <typename T> ReadSizedIntLE(pos_type size)226 T ReadSizedIntLE(pos_type size) 227 { 228 return mpt::IO::FileReader::ReadSizedIntLE<T>(*this, size); 229 } 230 ReadUint32LE()231 uint32 ReadUint32LE() 232 { 233 return mpt::IO::FileReader::ReadUint32LE(*this); 234 } 235 ReadUint32BE()236 uint32 ReadUint32BE() 237 { 238 return mpt::IO::FileReader::ReadUint32BE(*this); 239 } 240 ReadInt32LE()241 int32 ReadInt32LE() 242 { 243 return mpt::IO::FileReader::ReadInt32LE(*this); 244 } 245 ReadInt32BE()246 int32 ReadInt32BE() 247 { 248 return mpt::IO::FileReader::ReadInt32BE(*this); 249 } 250 ReadUint24LE()251 uint32 ReadUint24LE() 252 { 253 return mpt::IO::FileReader::ReadUint24LE(*this); 254 } 255 ReadUint24BE()256 uint32 ReadUint24BE() 257 { 258 return mpt::IO::FileReader::ReadUint24BE(*this); 259 } 260 ReadUint16LE()261 uint16 ReadUint16LE() 262 { 263 return mpt::IO::FileReader::ReadUint16LE(*this); 264 } 265 ReadUint16BE()266 uint16 ReadUint16BE() 267 { 268 return mpt::IO::FileReader::ReadUint16BE(*this); 269 } 270 ReadInt16LE()271 int16 ReadInt16LE() 272 { 273 return mpt::IO::FileReader::ReadInt16LE(*this); 274 } 275 ReadInt16BE()276 int16 ReadInt16BE() 277 { 278 return mpt::IO::FileReader::ReadInt16BE(*this); 279 } 280 ReadChar()281 char ReadChar() 282 { 283 return mpt::IO::FileReader::ReadChar(*this); 284 } 285 ReadUint8()286 uint8 ReadUint8() 287 { 288 return mpt::IO::FileReader::ReadUint8(*this); 289 } 290 ReadInt8()291 int8 ReadInt8() 292 { 293 return mpt::IO::FileReader::ReadInt8(*this); 294 } 295 ReadFloatLE()296 float ReadFloatLE() 297 { 298 return mpt::IO::FileReader::ReadFloatLE(*this); 299 } 300 ReadFloatBE()301 float ReadFloatBE() 302 { 303 return mpt::IO::FileReader::ReadFloatBE(*this); 304 } 305 ReadDoubleLE()306 double ReadDoubleLE() 307 { 308 return mpt::IO::FileReader::ReadDoubleLE(*this); 309 } 310 ReadDoubleBE()311 double ReadDoubleBE() 312 { 313 return mpt::IO::FileReader::ReadDoubleBE(*this); 314 } 315 316 template <typename T> ReadStruct(T & target)317 bool ReadStruct(T &target) 318 { 319 return mpt::IO::FileReader::ReadStruct(*this, target); 320 } 321 322 template <typename T> 323 size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T)) 324 { 325 return mpt::IO::FileReader::ReadStructPartial(*this, target, partialSize); 326 } 327 328 bool ReadNullString(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max()) 329 { 330 return mpt::IO::FileReader::ReadNullString(*this, dest, maxLength); 331 } 332 333 bool ReadLine(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max()) 334 { 335 return mpt::IO::FileReader::ReadLine(*this, dest, maxLength); 336 } 337 338 template<typename T, std::size_t destSize> ReadArray(T (& destArray)[destSize])339 bool ReadArray(T (&destArray)[destSize]) 340 { 341 return mpt::IO::FileReader::ReadArray(*this, destArray); 342 } 343 344 template<typename T, std::size_t destSize> ReadArray(std::array<T,destSize> & destArray)345 bool ReadArray(std::array<T, destSize> &destArray) 346 { 347 return mpt::IO::FileReader::ReadArray(*this, destArray); 348 } 349 350 template <typename T, std::size_t destSize> ReadArray()351 std::array<T, destSize> ReadArray() 352 { 353 return mpt::IO::FileReader::ReadArray<T, destSize>(*this); 354 } 355 356 template<typename T> ReadVector(std::vector<T> & destVector,size_t destSize)357 bool ReadVector(std::vector<T> &destVector, size_t destSize) 358 { 359 return mpt::IO::FileReader::ReadVector(*this, destVector, destSize); 360 } 361 362 template<size_t N> ReadMagic(const char (& magic)[N])363 bool ReadMagic(const char (&magic)[N]) 364 { 365 return mpt::IO::FileReader::ReadMagic(*this, magic); 366 } 367 368 template<typename T> ReadVarInt(T & target)369 bool ReadVarInt(T &target) 370 { 371 return mpt::IO::FileReader::ReadVarInt(*this, target); 372 } 373 374 template <typename T> 375 using Item = mpt::IO::FileReader::Chunk<T, FileReader>; 376 377 template <typename T> 378 using ChunkList = mpt::IO::FileReader::ChunkList<T, FileReader>; 379 380 template<typename T> ReadNextChunk(off_t alignment)381 Item<T> ReadNextChunk(off_t alignment) 382 { 383 return mpt::IO::FileReader::ReadNextChunk<T, FileReader>(*this, alignment); 384 } 385 386 template<typename T> ReadChunks(off_t alignment)387 ChunkList<T> ReadChunks(off_t alignment) 388 { 389 return mpt::IO::FileReader::ReadChunks<T, FileReader>(*this, alignment); 390 } 391 392 template<typename T> 393 ChunkList<T> ReadChunksUntil(off_t alignment, decltype(T().GetID()) stopAtID) 394 { 395 return mpt::IO::FileReader::ReadChunksUntil<T, FileReader>(*this, alignment, stopAtID); 396 } 397 398 template<mpt::String::ReadWriteMode mode, size_t destSize> ReadString(char (& destBuffer)[destSize],const pos_type srcSize)399 bool ReadString(char (&destBuffer)[destSize], const pos_type srcSize) 400 { 401 return FileReaderExt::ReadString<mode>(*this, destBuffer, srcSize); 402 } 403 404 template<mpt::String::ReadWriteMode mode> ReadString(std::string & dest,const pos_type srcSize)405 bool ReadString(std::string &dest, const pos_type srcSize) 406 { 407 return FileReaderExt::ReadString<mode>(*this, dest, srcSize); 408 } 409 410 template<mpt::String::ReadWriteMode mode, std::size_t len> ReadString(mpt::charbuf<len> & dest,const pos_type srcSize)411 bool ReadString(mpt::charbuf<len> &dest, const pos_type srcSize) 412 { 413 return FileReaderExt::ReadString<mode>(*this, dest, srcSize); 414 } 415 416 template<mpt::String::ReadWriteMode mode> ReadString(mpt::ustring & dest,mpt::Charset charset,const pos_type srcSize)417 bool ReadString(mpt::ustring &dest, mpt::Charset charset, const pos_type srcSize) 418 { 419 return FileReaderExt::ReadString<mode>(*this, dest, charset, srcSize); 420 } 421 422 template<typename Tsize, mpt::String::ReadWriteMode mode, size_t destSize> 423 bool ReadSizedString(char (&destBuffer)[destSize], const pos_type maxLength = std::numeric_limits<pos_type>::max()) 424 { 425 return FileReaderExt::ReadSizedString<Tsize, mode>(*this, destBuffer, maxLength); 426 } 427 428 template<typename Tsize, mpt::String::ReadWriteMode mode> 429 bool ReadSizedString(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max()) 430 { 431 return FileReaderExt::ReadSizedString<Tsize, mode>(*this, dest, maxLength); 432 } 433 434 template<typename Tsize, mpt::String::ReadWriteMode mode, std::size_t len> 435 bool ReadSizedString(mpt::charbuf<len> &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max()) 436 { 437 return FileReaderExt::ReadSizedString<Tsize, mode, len>(*this, dest, maxLength); 438 } 439 440 }; 441 442 } // namespace detail 443 444 using FileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>; 445 using FileReader = detail::FileReader<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>; 446 447 using ChunkReader = FileReader; 448 449 using MemoryFileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>; 450 using MemoryFileReader = detail::FileReader<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>; 451 452 453 OPENMPT_NAMESPACE_END 454