CBinaryReader.cpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /*
  2. * CBinaryReader.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CBinaryReader.h"
  12. //FIXME:library file depends on SDL - make cause troubles
  13. #include <SDL_endian.h>
  14. #include "CInputStream.h"
  15. #include "../CGeneralTextHandler.h"
  16. #if SDL_BYTEORDER == SDL_BIG_ENDIAN
  17. template <typename CData>
  18. CData readLE(CData data)
  19. {
  20. auto dataPtr = (char*)&data;
  21. std::reverse(dataPtr, dataPtr + sizeof(data));
  22. return data;
  23. }
  24. #else
  25. template <typename CData>
  26. CData readLE(CData data)
  27. {
  28. return data;
  29. }
  30. #endif
  31. CBinaryReader::CBinaryReader() : stream(nullptr)
  32. {
  33. }
  34. CBinaryReader::CBinaryReader(CInputStream * stream) : stream(stream)
  35. {
  36. }
  37. CInputStream * CBinaryReader::getStream()
  38. {
  39. return stream;
  40. }
  41. void CBinaryReader::setStream(CInputStream * stream)
  42. {
  43. this->stream = stream;
  44. }
  45. si64 CBinaryReader::read(ui8 * data, si64 size)
  46. {
  47. si64 bytesRead = stream->read(data, size);
  48. if(bytesRead != size)
  49. {
  50. throw std::runtime_error(getEndOfStreamExceptionMsg((long)size));
  51. }
  52. return bytesRead;
  53. }
  54. template <typename CData>
  55. CData CBinaryReader::readInteger()
  56. {
  57. CData val;
  58. stream->read(reinterpret_cast<unsigned char *>(&val), sizeof(val));
  59. return readLE(val);
  60. }
  61. //FIXME: any way to do this without macro?
  62. #define INSTANTIATE(datatype, methodname) \
  63. datatype CBinaryReader::methodname() \
  64. { return readInteger<datatype>(); }
  65. // While it is certanly possible to leave only template method
  66. // but typing template parameter every time can be annoying
  67. // and templates parameters can't be resolved by return type
  68. INSTANTIATE(ui8, readUInt8)
  69. INSTANTIATE(si8, readInt8)
  70. INSTANTIATE(ui16, readUInt16)
  71. INSTANTIATE(si16, readInt16)
  72. INSTANTIATE(ui32, readUInt32)
  73. INSTANTIATE(si32, readInt32)
  74. INSTANTIATE(ui64, readUInt64)
  75. INSTANTIATE(si64, readInt64)
  76. #undef INSTANTIATE
  77. std::string CBinaryReader::readString()
  78. {
  79. unsigned int len = readUInt32();
  80. assert(len <= 500000); //not too long
  81. if (len > 0)
  82. {
  83. std::string ret;
  84. ret.resize(len);
  85. read(reinterpret_cast<ui8*>(&ret[0]), len);
  86. //FIXME: any need to move this into separate "read localized string" method?
  87. if (Unicode::isValidASCII(ret))
  88. return ret;
  89. return Unicode::toUnicode(ret);
  90. }
  91. return "";
  92. }
  93. void CBinaryReader::skip(int count)
  94. {
  95. stream->skip(count);
  96. }
  97. std::string CBinaryReader::getEndOfStreamExceptionMsg(long bytesToRead) const
  98. {
  99. std::stringstream ss;
  100. ss << "The end of the stream was reached unexpectedly. The stream has a length of " << stream->getSize() << " and the current reading position is "
  101. << stream->tell() << ". The client wanted to read " << bytesToRead << " bytes.";
  102. return ss.str();
  103. }