/source/vba/common/Patch.cpp

https://github.com/dborth/vbagx · C++ · 445 lines · 366 code · 69 blank · 10 comment · 149 complexity · f73114ce1c91c2b154a761dd225c52e1 MD5 · raw file

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <zlib.h>
  5. #include "Patch.h"
  6. #ifndef _MSC_VER
  7. #define _stricmp strcasecmp
  8. #endif // ! _MSC_VER
  9. static int readInt2(MFILE *f)
  10. {
  11. int res = 0;
  12. int c = memfgetc(f);
  13. if(c == MEOF)
  14. return -1;
  15. res = c;
  16. c = memfgetc(f);
  17. if(c == MEOF)
  18. return -1;
  19. return c + (res<<8);
  20. }
  21. static int readInt3(MFILE *f)
  22. {
  23. int res = 0;
  24. int c = memfgetc(f);
  25. if(c == MEOF)
  26. return -1;
  27. res = c;
  28. c = memfgetc(f);
  29. if(c == MEOF)
  30. return -1;
  31. res = c + (res<<8);
  32. c = memfgetc(f);
  33. if(c == MEOF)
  34. return -1;
  35. return c + (res<<8);
  36. }
  37. static s64 readInt4(MFILE *f)
  38. {
  39. s64 tmp, res = 0;
  40. int c;
  41. for (int i = 0; i < 4; i++) {
  42. c = memfgetc(f);
  43. if (c == MEOF)
  44. return -1;
  45. tmp = c;
  46. res = res + (tmp << (i*8));
  47. }
  48. return res;
  49. }
  50. static s64 readInt8(MFILE *f)
  51. {
  52. s64 tmp, res = 0;
  53. int c;
  54. for (int i = 0; i < 8; i++) {
  55. c = memfgetc(f);
  56. if (c == MEOF)
  57. return -1;
  58. tmp = c;
  59. res = res + (tmp << (i*8));
  60. }
  61. return res;
  62. }
  63. static s64 readVarPtr(MFILE *f)
  64. {
  65. s64 offset = 0, shift = 1;
  66. for (;;) {
  67. int c = memfgetc(f);
  68. if (c == MEOF) return 0;
  69. offset += (c & 0x7F) * shift;
  70. if (c & 0x80) break;
  71. shift <<= 7;
  72. offset += shift;
  73. }
  74. return offset;
  75. }
  76. #ifndef MIN
  77. #define MIN(a,b) (((a)<(b))?(a):(b))
  78. #endif
  79. static uLong computePatchCRC(MFILE *f, unsigned int size)
  80. {
  81. Bytef buf[4096];
  82. long readed;
  83. uLong crc = crc32(0L, Z_NULL, 0);
  84. do {
  85. readed = memfread(buf, 1, MIN(size, sizeof(buf)), f);
  86. crc = crc32(crc, buf, readed);
  87. size -= readed;
  88. } while (readed > 0);
  89. return crc;
  90. }
  91. bool patchApplyIPS(MFILE *f, u8 **r, int *s)
  92. {
  93. // from the IPS spec at http://zerosoft.zophar.net/ips.htm
  94. bool result = false;
  95. u8 *rom = *r;
  96. int size = *s;
  97. if(memfgetc(f) == 'P' &&
  98. memfgetc(f) == 'A' &&
  99. memfgetc(f) == 'T' &&
  100. memfgetc(f) == 'C' &&
  101. memfgetc(f) == 'H') {
  102. int b;
  103. int offset;
  104. int len;
  105. result = true;
  106. for(;;) {
  107. // read offset
  108. offset = readInt3(f);
  109. // if offset == MEOF, end of patch
  110. if(offset == 0x454f46 || offset == -1)
  111. break;
  112. // read length
  113. len = readInt2(f);
  114. if(!len) {
  115. // len == 0, RLE block
  116. len = readInt2(f);
  117. // byte to fill
  118. int c = memfgetc(f);
  119. if(c == -1)
  120. break;
  121. b = (u8)c;
  122. } else
  123. b= -1;
  124. // check if we need to reallocate our ROM
  125. if((offset + len) >= size) {
  126. #ifdef GEKKO
  127. size = offset + len;
  128. #else
  129. size *= 2;
  130. rom = (u8 *)realloc(rom, size);
  131. #endif
  132. *r = rom;
  133. *s = size;
  134. }
  135. if(b == -1) {
  136. // normal block, just read the data
  137. if(memfread(&rom[offset], 1, len, f) != (size_t)len)
  138. break;
  139. } else {
  140. // fill the region with the given byte
  141. while(len--) {
  142. rom[offset++] = b;
  143. }
  144. }
  145. }
  146. }
  147. return result;
  148. }
  149. bool patchApplyUPS(MFILE *f, u8 **rom, int *size)
  150. {
  151. s64 srcCRC, dstCRC, patchCRC;
  152. memfseek(f, 0, MSEEK_END);
  153. long int patchSize = memftell(f);
  154. if (patchSize < 20) {
  155. return false;
  156. }
  157. memfseek(f, 0, MSEEK_SET);
  158. if(memfgetc(f) != 'U' || memfgetc(f) != 'P' || memfgetc(f) != 'S' || memfgetc(f) != '1') {
  159. return false;
  160. }
  161. memfseek(f, -12, MSEEK_END);
  162. srcCRC = readInt4(f);
  163. dstCRC = readInt4(f);
  164. patchCRC = readInt4(f);
  165. if (srcCRC == -1 || dstCRC == -1 || patchCRC == -1) {
  166. return false;
  167. }
  168. memfseek(f, 0, MSEEK_SET);
  169. u32 crc = computePatchCRC(f, patchSize-4);
  170. if (crc != patchCRC) {
  171. return false;
  172. }
  173. crc = crc32(0L, Z_NULL, 0);
  174. crc = crc32(crc, *rom, *size);
  175. memfseek(f, 4, MSEEK_SET);
  176. s64 dataSize;
  177. s64 srcSize = readVarPtr(f);
  178. s64 dstSize = readVarPtr(f);
  179. if (crc == srcCRC) {
  180. if (srcSize != *size) {
  181. return false;
  182. }
  183. dataSize = dstSize;
  184. } else if (crc == dstCRC) {
  185. if (dstSize != *size) {
  186. return false;
  187. }
  188. dataSize = srcSize;
  189. } else {
  190. return false;
  191. }
  192. if (dataSize > *size) {
  193. *rom = (u8*)realloc(*rom, dataSize);
  194. memset(*rom + *size, 0, dataSize - *size);
  195. *size = dataSize;
  196. }
  197. s64 relative = 0;
  198. u8 *mem;
  199. while(memftell(f) < patchSize - 12) {
  200. relative += readVarPtr(f);
  201. if (relative > dataSize) continue;
  202. mem = *rom + relative;
  203. for(s64 i = relative; i < dataSize; i++) {
  204. int x = memfgetc(f);
  205. relative++;
  206. if (!x) break;
  207. if (i < dataSize) {
  208. *mem++ ^= x;
  209. }
  210. }
  211. }
  212. return true;
  213. }
  214. static int ppfVersion(MFILE *f)
  215. {
  216. memfseek(f, 0, MSEEK_SET);
  217. if (memfgetc(f) != 'P' || memfgetc(f) != 'P' || memfgetc(f) != 'F') //-V501
  218. return 0;
  219. switch(memfgetc(f)){
  220. case '1': return 1;
  221. case '2': return 2;
  222. case '3': return 3;
  223. default: return 0;
  224. }
  225. }
  226. static int ppfFileIdLen(MFILE *f, int version)
  227. {
  228. if (version == 2) {
  229. memfseek(f, -8, MSEEK_END);
  230. } else {
  231. memfseek(f, -6, MSEEK_END);
  232. }
  233. if (memfgetc(f) != '.' || memfgetc(f) != 'D' || memfgetc(f) != 'I' || memfgetc(f) != 'Z')
  234. return 0;
  235. return (version == 2) ? readInt4(f) : readInt2(f);
  236. }
  237. static bool patchApplyPPF1(MFILE *f, u8 **rom, int *size)
  238. {
  239. memfseek(f, 0, MSEEK_END);
  240. int count = memftell(f);
  241. if (count < 56)
  242. return false;
  243. count -= 56;
  244. memfseek(f, 56, MSEEK_SET);
  245. u8 *mem = *rom;
  246. while (count > 0) {
  247. int offset = readInt4(f);
  248. if (offset == -1)
  249. break;
  250. int len = memfgetc(f);
  251. if (len == MEOF)
  252. break;
  253. if (offset+len > *size)
  254. break;
  255. if (memfread(&mem[offset], 1, len, f) != (size_t)len)
  256. break;
  257. count -= 4 + 1 + len;
  258. }
  259. return (count == 0);
  260. }
  261. static bool patchApplyPPF2(MFILE *f, u8 **rom, int *size)
  262. {
  263. memfseek(f, 0, MSEEK_END);
  264. int count = memftell(f);
  265. if (count < 56+4+1024)
  266. return false;
  267. count -= 56+4+1024;
  268. memfseek(f, 56, MSEEK_SET);
  269. int datalen = readInt4(f);
  270. if (datalen != *size)
  271. return false;
  272. u8 *mem = *rom;
  273. u8 block[1024];
  274. memfread(&block, 1, 1024, f);
  275. if (memcmp(&mem[0x9320], &block, 1024) != 0)
  276. return false;
  277. int idlen = ppfFileIdLen(f, 2);
  278. if (idlen > 0)
  279. count -= 16 + 16 + idlen;
  280. memfseek(f, 56+4+1024, MSEEK_SET);
  281. while (count > 0) {
  282. int offset = readInt4(f);
  283. if (offset == -1)
  284. break;
  285. int len = memfgetc(f);
  286. if (len == MEOF)
  287. break;
  288. if (offset+len > *size)
  289. break;
  290. if (memfread(&mem[offset], 1, len, f) != (size_t)len)
  291. break;
  292. count -= 4 + 1 + len;
  293. }
  294. return (count == 0);
  295. }
  296. static bool patchApplyPPF3(MFILE *f, u8 **rom, int *size)
  297. {
  298. memfseek(f, 0, MSEEK_END);
  299. int count = memftell(f);
  300. if (count < 56+4+1024)
  301. return false;
  302. count -= 56+4;
  303. memfseek(f, 56, MSEEK_SET);
  304. int imagetype = memfgetc(f);
  305. int blockcheck = memfgetc(f);
  306. int undo = memfgetc(f);
  307. memfgetc(f);
  308. u8 *mem = *rom;
  309. if (blockcheck) {
  310. u8 block[1024];
  311. memfread(&block, 1, 1024, f);
  312. if (memcmp(&mem[(imagetype == 0) ? 0x9320 : 0x80A0], &block, 1024) != 0)
  313. return false;
  314. count -= 1024;
  315. }
  316. int idlen = ppfFileIdLen(f, 2);
  317. if (idlen > 0)
  318. count -= 16 + 16 + idlen;
  319. memfseek(f, 56+4+(blockcheck ? 1024 : 0), MSEEK_SET);
  320. while (count > 0) {
  321. s64 offset = readInt8(f);
  322. if (offset == -1)
  323. break;
  324. int len = memfgetc(f);
  325. if (len == MEOF)
  326. break;
  327. if (offset+len > *size)
  328. break;
  329. if (memfread(&mem[offset], 1, len, f) != (size_t)len)
  330. break;
  331. if (undo) memfseek(f, len, MSEEK_CUR);
  332. count -= 8 + 1 + len;
  333. if (undo) count -= len;
  334. }
  335. return (count == 0);
  336. }
  337. bool patchApplyPPF(MFILE *f, u8 **rom, int *size)
  338. {
  339. bool res = false;
  340. int version = ppfVersion(f);
  341. switch (version) {
  342. case 1: res = patchApplyPPF1(f, rom, size); break;
  343. case 2: res = patchApplyPPF2(f, rom, size); break;
  344. case 3: res = patchApplyPPF3(f, rom, size); break;
  345. }
  346. return res;
  347. }
  348. bool applyPatch(const char *patchname, u8 **rom, int *size)
  349. {
  350. bool result = false;
  351. if (strlen(patchname) < 5)
  352. return false;
  353. const char * p = strrchr(patchname, '.');
  354. if (p == NULL)
  355. return false;
  356. FILE *f = fopen(patchname, "rb");
  357. if (!f)
  358. return false;
  359. // read in file
  360. fseek(f, 0, SEEK_END); // go to end of file
  361. int filesize = ftell(f); // get filesize
  362. fseek(f, 0, SEEK_SET); // go to start of file
  363. char * pbuffer = (char *) malloc(filesize);
  364. fread (pbuffer, 1, filesize, f);
  365. fclose(f);
  366. MFILE * mf = memfopen(pbuffer, filesize); // create memory file
  367. if (_stricmp(p, ".ips") == 0)
  368. result = patchApplyIPS(mf, rom, size);
  369. else if (_stricmp(p, ".ups") == 0)
  370. result = patchApplyUPS(mf, rom, size);
  371. else if (_stricmp(p, ".ppf") == 0)
  372. result = patchApplyPPF(mf, rom, size);
  373. memfclose(mf); // close memory file
  374. free(pbuffer); // free buffer
  375. return result;
  376. }