patch.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright (c) 2017-2018 Hugh Bailey <[email protected]>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "updater.hpp"
  17. #include <stdint.h>
  18. #include <vector>
  19. #ifdef _MSC_VER
  20. #define restrict __restrict
  21. #include <lzma.h>
  22. #undef restrict
  23. #else
  24. #include <lzma.h>
  25. #endif
  26. using namespace std;
  27. #define MAX_BUF_SIZE 262144
  28. #define READ_BUF_SIZE 32768
  29. /* ------------------------------------------------------------------------ */
  30. class LZMAStream {
  31. lzma_stream strm = {};
  32. bool initialized = false;
  33. public:
  34. inline ~LZMAStream()
  35. {
  36. if (initialized) {
  37. lzma_end(&strm);
  38. }
  39. }
  40. inline bool init_decoder()
  41. {
  42. lzma_ret ret = lzma_stream_decoder(&strm, 200 * 1024 * 1024, 0);
  43. initialized = (ret == LZMA_OK);
  44. return initialized;
  45. }
  46. inline operator lzma_stream *() { return &strm; }
  47. inline bool operator!() const { return !initialized; }
  48. inline lzma_stream *get() { return &strm; }
  49. };
  50. class File {
  51. FILE *f = nullptr;
  52. public:
  53. inline ~File()
  54. {
  55. if (f)
  56. fclose(f);
  57. }
  58. inline FILE **operator&() { return &f; }
  59. inline operator FILE *() const { return f; }
  60. inline bool operator!() const { return !f; }
  61. };
  62. /* ------------------------------------------------------------------------ */
  63. struct bspatch_stream {
  64. void *opaque;
  65. int (*read)(const struct bspatch_stream *stream, void *buffer,
  66. int length);
  67. };
  68. /* ------------------------------------------------------------------------ */
  69. static int64_t offtin(const uint8_t *buf)
  70. {
  71. int64_t y;
  72. y = buf[7] & 0x7F;
  73. y = y * 256;
  74. y += buf[6];
  75. y = y * 256;
  76. y += buf[5];
  77. y = y * 256;
  78. y += buf[4];
  79. y = y * 256;
  80. y += buf[3];
  81. y = y * 256;
  82. y += buf[2];
  83. y = y * 256;
  84. y += buf[1];
  85. y = y * 256;
  86. y += buf[0];
  87. if (buf[7] & 0x80)
  88. y = -y;
  89. return y;
  90. }
  91. /* ------------------------------------------------------------------------ */
  92. static int bspatch(const uint8_t *old, int64_t oldsize, uint8_t *newp,
  93. int64_t newsize, struct bspatch_stream *stream)
  94. {
  95. uint8_t buf[8];
  96. int64_t oldpos, newpos;
  97. int64_t ctrl[3];
  98. int64_t i;
  99. oldpos = 0;
  100. newpos = 0;
  101. while (newpos < newsize) {
  102. /* Read control data */
  103. for (i = 0; i <= 2; i++) {
  104. if (stream->read(stream, buf, 8))
  105. return -1;
  106. ctrl[i] = offtin(buf);
  107. };
  108. /* Sanity-check */
  109. if (newpos + ctrl[0] > newsize)
  110. return -1;
  111. /* Read diff string */
  112. if (stream->read(stream, newp + newpos, (int)ctrl[0]))
  113. return -1;
  114. /* Add old data to diff string */
  115. for (i = 0; i < ctrl[0]; i++)
  116. if ((oldpos + i >= 0) && (oldpos + i < oldsize))
  117. newp[newpos + i] += old[oldpos + i];
  118. /* Adjust pointers */
  119. newpos += ctrl[0];
  120. oldpos += ctrl[0];
  121. /* Sanity-check */
  122. if (newpos + ctrl[1] > newsize)
  123. return -1;
  124. /* Read extra string */
  125. if (stream->read(stream, newp + newpos, (int)ctrl[1]))
  126. return -1;
  127. /* Adjust pointers */
  128. newpos += ctrl[1];
  129. oldpos += ctrl[2];
  130. };
  131. return 0;
  132. }
  133. /* ------------------------------------------------------------------------ */
  134. struct patch_data {
  135. HANDLE h;
  136. lzma_stream *strm;
  137. uint8_t buf[READ_BUF_SIZE];
  138. };
  139. static int read_lzma(const struct bspatch_stream *stream, void *buffer, int len)
  140. {
  141. if (!len)
  142. return 0;
  143. patch_data *data = (patch_data *)stream->opaque;
  144. HANDLE h = data->h;
  145. lzma_stream *strm = data->strm;
  146. strm->avail_out = (size_t)len;
  147. strm->next_out = (uint8_t *)buffer;
  148. for (;;) {
  149. if (strm->avail_in == 0) {
  150. DWORD read_size;
  151. if (!ReadFile(h, data->buf, READ_BUF_SIZE, &read_size,
  152. nullptr))
  153. return -1;
  154. if (read_size == 0)
  155. return -1;
  156. strm->avail_in = (size_t)read_size;
  157. strm->next_in = data->buf;
  158. }
  159. lzma_ret ret = lzma_code(strm, LZMA_RUN);
  160. if (ret == LZMA_STREAM_END)
  161. return 0;
  162. if (ret != LZMA_OK)
  163. return -1;
  164. if (strm->avail_out == 0)
  165. break;
  166. }
  167. return 0;
  168. }
  169. int ApplyPatch(const wchar_t *patchFile, const wchar_t *targetFile)
  170. try {
  171. uint8_t header[24];
  172. int64_t newsize;
  173. struct bspatch_stream stream;
  174. bool success;
  175. WinHandle hPatch;
  176. WinHandle hTarget;
  177. LZMAStream strm;
  178. /* --------------------------------- *
  179. * open patch and file to patch */
  180. hPatch = CreateFile(patchFile, GENERIC_READ, 0, nullptr, OPEN_EXISTING,
  181. 0, nullptr);
  182. if (!hPatch.Valid())
  183. throw int(GetLastError());
  184. hTarget = CreateFile(targetFile, GENERIC_READ, 0, nullptr,
  185. OPEN_EXISTING, 0, nullptr);
  186. if (!hTarget.Valid())
  187. throw int(GetLastError());
  188. /* --------------------------------- *
  189. * read patch header */
  190. DWORD read;
  191. success = !!ReadFile(hPatch, header, sizeof(header), &read, nullptr);
  192. if (success && read == sizeof(header)) {
  193. if (memcmp(header, "JIMSLEY/BSDIFF43", 16))
  194. throw int(-4);
  195. } else {
  196. throw int(GetLastError());
  197. }
  198. /* --------------------------------- *
  199. * allocate new file size data */
  200. newsize = offtin(header + 16);
  201. if (newsize < 0 || newsize >= 0x7ffffffff)
  202. throw int(-5);
  203. vector<uint8_t> newData;
  204. try {
  205. newData.resize((size_t)newsize);
  206. } catch (...) {
  207. throw int(-1);
  208. }
  209. /* --------------------------------- *
  210. * read old file */
  211. DWORD targetFileSize;
  212. targetFileSize = GetFileSize(hTarget, nullptr);
  213. if (targetFileSize == INVALID_FILE_SIZE)
  214. throw int(GetLastError());
  215. vector<uint8_t> oldData;
  216. try {
  217. oldData.resize(targetFileSize);
  218. } catch (...) {
  219. throw int(-1);
  220. }
  221. if (!ReadFile(hTarget, &oldData[0], targetFileSize, &read, nullptr))
  222. throw int(GetLastError());
  223. if (read != targetFileSize)
  224. throw int(-1);
  225. /* --------------------------------- *
  226. * patch to new file data */
  227. if (!strm.init_decoder())
  228. throw int(-10);
  229. patch_data data;
  230. data.h = hPatch;
  231. data.strm = strm.get();
  232. stream.read = read_lzma;
  233. stream.opaque = &data;
  234. int ret = bspatch(oldData.data(), oldData.size(), newData.data(),
  235. newData.size(), &stream);
  236. if (ret != 0)
  237. throw int(-9);
  238. /* --------------------------------- *
  239. * write new file */
  240. hTarget = nullptr;
  241. hTarget = CreateFile(targetFile, GENERIC_WRITE, 0, nullptr,
  242. CREATE_ALWAYS, 0, nullptr);
  243. if (!hTarget.Valid())
  244. throw int(GetLastError());
  245. DWORD written;
  246. success = !!WriteFile(hTarget, newData.data(), (DWORD)newsize, &written,
  247. nullptr);
  248. if (!success || written != newsize)
  249. throw int(GetLastError());
  250. return 0;
  251. } catch (int code) {
  252. return code;
  253. }