patch.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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(
  43. &strm,
  44. 200 * 1024 * 1024,
  45. 0);
  46. initialized = (ret == LZMA_OK);
  47. return initialized;
  48. }
  49. inline operator lzma_stream *() { return &strm; }
  50. inline bool operator!() const { return !initialized; }
  51. inline lzma_stream *get() { return &strm; }
  52. };
  53. class File {
  54. FILE *f = nullptr;
  55. public:
  56. inline ~File()
  57. {
  58. if (f)
  59. fclose(f);
  60. }
  61. inline FILE **operator&() { return &f; }
  62. inline operator FILE *() const { return f; }
  63. inline bool operator!() const { return !f; }
  64. };
  65. /* ------------------------------------------------------------------------ */
  66. struct bspatch_stream {
  67. void *opaque;
  68. int (*read)(const struct bspatch_stream *stream, void *buffer,
  69. int length);
  70. };
  71. /* ------------------------------------------------------------------------ */
  72. static int64_t offtin(const uint8_t *buf)
  73. {
  74. int64_t y;
  75. y = buf[7] & 0x7F;
  76. y = y * 256;
  77. y += buf[6];
  78. y = y * 256;
  79. y += buf[5];
  80. y = y * 256;
  81. y += buf[4];
  82. y = y * 256;
  83. y += buf[3];
  84. y = y * 256;
  85. y += buf[2];
  86. y = y * 256;
  87. y += buf[1];
  88. y = y * 256;
  89. y += buf[0];
  90. if (buf[7] & 0x80)
  91. y = -y;
  92. return y;
  93. }
  94. /* ------------------------------------------------------------------------ */
  95. static int bspatch(const uint8_t *old, int64_t oldsize, uint8_t *newp,
  96. int64_t newsize, struct bspatch_stream *stream)
  97. {
  98. uint8_t buf[8];
  99. int64_t oldpos, newpos;
  100. int64_t ctrl[3];
  101. int64_t i;
  102. oldpos = 0;
  103. newpos = 0;
  104. while (newpos < newsize) {
  105. /* Read control data */
  106. for (i = 0; i <= 2; i++) {
  107. if (stream->read(stream, buf, 8))
  108. return -1;
  109. ctrl[i] = offtin(buf);
  110. };
  111. /* Sanity-check */
  112. if (newpos + ctrl[0] > newsize)
  113. return -1;
  114. /* Read diff string */
  115. if (stream->read(stream, newp + newpos, (int)ctrl[0]))
  116. return -1;
  117. /* Add old data to diff string */
  118. for (i = 0; i < ctrl[0]; i++)
  119. if ((oldpos + i >= 0) && (oldpos + i < oldsize))
  120. newp[newpos + i] += old[oldpos + i];
  121. /* Adjust pointers */
  122. newpos += ctrl[0];
  123. oldpos += ctrl[0];
  124. /* Sanity-check */
  125. if (newpos + ctrl[1] > newsize)
  126. return -1;
  127. /* Read extra string */
  128. if (stream->read(stream, newp + newpos, (int)ctrl[1]))
  129. return -1;
  130. /* Adjust pointers */
  131. newpos += ctrl[1];
  132. oldpos += ctrl[2];
  133. };
  134. return 0;
  135. }
  136. /* ------------------------------------------------------------------------ */
  137. struct patch_data {
  138. HANDLE h;
  139. lzma_stream *strm;
  140. uint8_t buf[READ_BUF_SIZE];
  141. };
  142. static int read_lzma(const struct bspatch_stream *stream, void *buffer, int len)
  143. {
  144. if (!len)
  145. return 0;
  146. patch_data *data = (patch_data*)stream->opaque;
  147. HANDLE h = data->h;
  148. lzma_stream *strm = data->strm;
  149. strm->avail_out = (size_t)len;
  150. strm->next_out = (uint8_t *)buffer;
  151. for (;;) {
  152. if (strm->avail_in == 0) {
  153. DWORD read_size;
  154. if (!ReadFile(h, data->buf, READ_BUF_SIZE, &read_size,
  155. nullptr))
  156. return -1;
  157. if (read_size == 0)
  158. return -1;
  159. strm->avail_in = (size_t)read_size;
  160. strm->next_in = data->buf;
  161. }
  162. lzma_ret ret = lzma_code(strm, LZMA_RUN);
  163. if (ret == LZMA_STREAM_END)
  164. return 0;
  165. if (ret != LZMA_OK)
  166. return -1;
  167. if (strm->avail_out == 0)
  168. break;
  169. }
  170. return 0;
  171. }
  172. int ApplyPatch(const wchar_t *patchFile, const wchar_t *targetFile)
  173. try {
  174. uint8_t header[24];
  175. int64_t newsize;
  176. struct bspatch_stream stream;
  177. bool success;
  178. WinHandle hPatch;
  179. WinHandle hTarget;
  180. LZMAStream strm;
  181. /* --------------------------------- *
  182. * open patch and file to patch */
  183. hPatch = CreateFile(patchFile, GENERIC_READ, 0, nullptr,
  184. OPEN_EXISTING, 0, nullptr);
  185. if (!hPatch.Valid())
  186. throw int(GetLastError());
  187. hTarget = CreateFile(targetFile, GENERIC_READ, 0, nullptr,
  188. OPEN_EXISTING, 0, nullptr);
  189. if (!hTarget.Valid())
  190. throw int(GetLastError());
  191. /* --------------------------------- *
  192. * read patch header */
  193. DWORD read;
  194. success = !!ReadFile(hPatch, header, sizeof(header), &read, nullptr);
  195. if (success && read == sizeof(header)) {
  196. if (memcmp(header, "JIMSLEY/BSDIFF43", 16))
  197. throw int(-4);
  198. } else {
  199. throw int(GetLastError());
  200. }
  201. /* --------------------------------- *
  202. * allocate new file size data */
  203. newsize = offtin(header + 16);
  204. if (newsize < 0 || newsize >= 0x7ffffffff)
  205. throw int(-5);
  206. vector<uint8_t> newData;
  207. try {
  208. newData.resize(newsize);
  209. } catch (...) {
  210. throw int(-1);
  211. }
  212. /* --------------------------------- *
  213. * read old file */
  214. DWORD targetFileSize;
  215. targetFileSize = GetFileSize(hTarget, nullptr);
  216. if (targetFileSize == INVALID_FILE_SIZE)
  217. throw int(GetLastError());
  218. vector<uint8_t> oldData;
  219. try {
  220. oldData.resize(targetFileSize);
  221. } catch (...) {
  222. throw int(-1);
  223. }
  224. if (!ReadFile(hTarget, &oldData[0], targetFileSize, &read, nullptr))
  225. throw int(GetLastError());
  226. if (read != targetFileSize)
  227. throw int(-1);
  228. /* --------------------------------- *
  229. * patch to new file data */
  230. if (!strm.init_decoder())
  231. throw int(-10);
  232. patch_data data;
  233. data.h = hPatch;
  234. data.strm = strm.get();
  235. stream.read = read_lzma;
  236. stream.opaque = &data;
  237. int ret = bspatch(oldData.data(), oldData.size(), newData.data(),
  238. newData.size(), &stream);
  239. if (ret != 0)
  240. throw int(-9);
  241. /* --------------------------------- *
  242. * write new file */
  243. hTarget = nullptr;
  244. hTarget = CreateFile(targetFile, GENERIC_WRITE, 0, nullptr,
  245. CREATE_ALWAYS, 0, nullptr);
  246. if (!hTarget.Valid())
  247. throw int(GetLastError());
  248. DWORD written;
  249. success = !!WriteFile(hTarget, newData.data(), (DWORD)newsize,
  250. &written, nullptr);
  251. if (!success || written != newsize)
  252. throw int(GetLastError());
  253. return 0;
  254. } catch (int code) {
  255. return code;
  256. }