ungzip.cpp 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. * ungzip.cpp
  3. * ----------
  4. * Purpose: Implementation file for extracting modules from .gz archives
  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. #include "stdafx.h"
  10. #include "../common/FileReader.h"
  11. #include "ungzip.h"
  12. #if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ)
  13. #if defined(MPT_WITH_ZLIB)
  14. #include <zlib.h>
  15. #elif defined(MPT_WITH_MINIZ)
  16. #include <miniz/miniz.h>
  17. #endif
  18. #endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ
  19. OPENMPT_NAMESPACE_BEGIN
  20. #if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ)
  21. CGzipArchive::CGzipArchive(const FileReader &file) : ArchiveBase(file)
  22. {
  23. inFile.Rewind();
  24. inFile.ReadStruct(header);
  25. // Check header data + file size
  26. if(header.magic1 != GZ_HMAGIC1 || header.magic2 != GZ_HMAGIC2 || header.method != GZ_HMDEFLATE || (header.flags & GZ_FRESERVED) != 0
  27. || inFile.GetLength() <= sizeof(GZheader) + sizeof(GZtrailer))
  28. {
  29. return;
  30. }
  31. ArchiveFileInfo info;
  32. info.type = ArchiveFileType::Normal;
  33. contents.push_back(info);
  34. }
  35. CGzipArchive::~CGzipArchive()
  36. {
  37. return;
  38. }
  39. bool CGzipArchive::ExtractFile(std::size_t index)
  40. {
  41. if(index >= contents.size())
  42. {
  43. return false;
  44. }
  45. // Read trailer
  46. GZtrailer trailer;
  47. inFile.Seek(inFile.GetLength() - sizeof(GZtrailer));
  48. inFile.ReadStruct(trailer);
  49. // Continue reading header
  50. inFile.Seek(sizeof(GZheader));
  51. // Extra block present? (skip the extra data)
  52. if(header.flags & GZ_FEXTRA)
  53. {
  54. inFile.Skip(inFile.ReadUint16LE());
  55. }
  56. // Filename present? (ignore)
  57. if(header.flags & GZ_FNAME)
  58. {
  59. while(inFile.ReadUint8() != 0);
  60. }
  61. // Comment present? (ignore)
  62. if(header.flags & GZ_FCOMMENT)
  63. {
  64. while(inFile.ReadUint8() != 0);
  65. }
  66. // CRC16 present? (ignore)
  67. if(header.flags & GZ_FHCRC)
  68. {
  69. inFile.Skip(2);
  70. }
  71. // Well, this is a bit small when deflated.
  72. if(!inFile.CanRead(sizeof(GZtrailer)))
  73. {
  74. return false;
  75. }
  76. try
  77. {
  78. data.reserve(inFile.BytesLeft());
  79. } catch(...)
  80. {
  81. return false;
  82. }
  83. // Inflate!
  84. z_stream strm{};
  85. strm.zalloc = Z_NULL;
  86. strm.zfree = Z_NULL;
  87. strm.opaque = Z_NULL;
  88. strm.avail_in = 0;
  89. strm.next_in = Z_NULL;
  90. if(inflateInit2(&strm, -15) != Z_OK)
  91. return false;
  92. int retVal = Z_OK;
  93. uint32 crc = 0;
  94. auto bytesLeft = inFile.BytesLeft() - sizeof(GZtrailer);
  95. do
  96. {
  97. std::array<char, mpt::IO::BUFFERSIZE_SMALL> inBuffer, outBuffer;
  98. strm.avail_in = static_cast<uInt>(std::min(static_cast<FileReader::pos_type>(inBuffer.size()), bytesLeft));
  99. inFile.ReadStructPartial(inBuffer, strm.avail_in);
  100. strm.next_in = reinterpret_cast<Bytef *>(inBuffer.data());
  101. bytesLeft -= strm.avail_in;
  102. do
  103. {
  104. strm.avail_out = static_cast<uInt>(outBuffer.size());
  105. strm.next_out = reinterpret_cast<Bytef *>(outBuffer.data());
  106. retVal = inflate(&strm, Z_NO_FLUSH);
  107. const auto output = mpt::as_span(outBuffer.data(), outBuffer.data() + outBuffer.size() - strm.avail_out);
  108. crc = crc32(crc, reinterpret_cast<Bytef *>(output.data()), static_cast<uInt>(output.size()));
  109. data.insert(data.end(), output.begin(), output.end());
  110. } while(strm.avail_out == 0);
  111. } while(retVal == Z_OK && bytesLeft);
  112. inflateEnd(&strm);
  113. // Everything went OK? Check return code, number of written bytes and CRC32.
  114. return retVal == Z_STREAM_END && trailer.isize == static_cast<uint32>(strm.total_out) && trailer.crc32_ == crc;
  115. }
  116. #endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ
  117. OPENMPT_NAMESPACE_END