PhysfsIo.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /**
  2. * Copyright (c) 2006-2023 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include <cassert>
  21. #include <algorithm>
  22. #include "PhysfsIo.h"
  23. namespace love
  24. {
  25. namespace filesystem
  26. {
  27. namespace physfs
  28. {
  29. bool StripSuffixIo::determineStrippedLength()
  30. {
  31. if (!file) {
  32. return false;
  33. }
  34. const int64_t fullSize = fullLength();
  35. int64_t chunkSize = std::min(fullSize, (int64_t)8192);
  36. std::string buffer;
  37. buffer.reserve(chunkSize);
  38. int64_t i = fullSize - chunkSize;
  39. // I don't think we really need to go through the whole file. The main known use
  40. // case for this functionality is to skip windows codesign signatures, which are
  41. // from what I have seen ~12KB or so, but trying is better than just failing.
  42. while (i >= 0)
  43. {
  44. buffer.resize(chunkSize);
  45. if (seek(i) == 0)
  46. return false;
  47. const auto n = read(&buffer[0], chunkSize);
  48. if (n <= 0)
  49. return false;
  50. buffer.resize(n);
  51. // We are looking for the magic bytes that indicate the start
  52. // of the "End of cental directory record (EOCD)".
  53. // As this is most likely not a multi-disk zip, we could include 4 bytes of 0x00,
  54. // but I'd rather make fewer assumptions.
  55. const auto endOfCentralDirectory = buffer.rfind("\x50\x4B\x05\x06");
  56. if (endOfCentralDirectory != std::string::npos)
  57. {
  58. i = i + endOfCentralDirectory;
  59. break;
  60. }
  61. if (i == 0)
  62. break;
  63. i = std::max((int64_t)0, i - chunkSize);
  64. }
  65. if (i > 0)
  66. {
  67. // The EOCD record is at least 22 bytes but may include a comment
  68. if (i + 22 > fullSize)
  69. return false; // Incomplete central directory
  70. // The comment length (u16) is located 20 bytes from the start of the EOCD record
  71. if (seek(i + 20) == 0)
  72. return false;
  73. uint8_t buffer[2];
  74. const auto n = read(buffer, 2);
  75. if (n <= 0)
  76. return false;
  77. const auto commentSize = (buffer[1] << 8) | buffer[0];
  78. if (i + 22 + commentSize > fullSize) // Comment incomplete
  79. return false;
  80. // We pretend the file ends just after the comment
  81. // (which should be the end of the embedded zip file)
  82. strippedLength_ = i + 22 + commentSize;
  83. }
  84. else
  85. {
  86. strippedLength_ = fullSize;
  87. }
  88. if (seek(0) == 0)
  89. return false;
  90. return true;
  91. }
  92. int64_t StripSuffixIo::read(void* buf, uint64_t len)
  93. {
  94. if (!file)
  95. {
  96. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  97. return -1;
  98. }
  99. const auto ret = std::fread(buf, 1, len, file);
  100. if (ret == 0)
  101. {
  102. if (std::feof(file))
  103. {
  104. PHYSFS_setErrorCode(PHYSFS_ERR_OK);
  105. return 0;
  106. }
  107. else
  108. {
  109. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  110. return -1;
  111. }
  112. }
  113. else if (ret < len && std::ferror(file))
  114. {
  115. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  116. return -1;
  117. }
  118. PHYSFS_setErrorCode(PHYSFS_ERR_OK);
  119. return ret;
  120. }
  121. int64_t StripSuffixIo::write(const void* /*buf*/, uint64_t /*len*/)
  122. {
  123. PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
  124. return -1;
  125. }
  126. int64_t StripSuffixIo::seek(uint64_t offset)
  127. {
  128. if (!file)
  129. {
  130. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  131. return 0;
  132. }
  133. const auto ret = std::fseek(file, offset, SEEK_SET);
  134. PHYSFS_setErrorCode(ret != 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK);
  135. return ret == 0 ? 1 : 0;
  136. }
  137. int64_t StripSuffixIo::tell()
  138. {
  139. if (!file)
  140. {
  141. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  142. return -1;
  143. }
  144. return std::ftell(file);
  145. }
  146. int64_t StripSuffixIo::length()
  147. {
  148. return strippedLength_;
  149. }
  150. int64_t StripSuffixIo::flush()
  151. {
  152. if (!file)
  153. {
  154. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  155. return 0;
  156. }
  157. return std::fflush(file) == 0 ? 1 : 0;
  158. }
  159. int64_t StripSuffixIo::fullLength()
  160. {
  161. assert(file);
  162. const auto cur = std::ftell(file);
  163. if (cur == -1)
  164. {
  165. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  166. return -1;
  167. }
  168. if (std::fseek(file, 0, SEEK_END) != 0)
  169. {
  170. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  171. return -1;
  172. }
  173. const auto len = std::ftell(file);
  174. if (len == -1)
  175. {
  176. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  177. return -1;
  178. }
  179. if (std::fseek(file, cur, SEEK_SET) != 0)
  180. {
  181. // We do have the length now, but something is wrong, so we return an error anyways
  182. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  183. return -1;
  184. }
  185. return len;
  186. }
  187. } // physfs
  188. } // filesystem
  189. } // love