CARDS.DLL loader for SDL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cards.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /* Copyright (C) 2019 C. McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <errno.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include "cards.h"
  20. static SDL_Surface *
  21. dibSurface(SDL_RWops *rw, Uint32 offset, Uint32 length) {
  22. Uint32 bitmapLength = 0x0E + length;
  23. Uint8 *buffer = malloc(bitmapLength);
  24. if (!buffer) {
  25. SDL_SetError("malloc error: %s", strerror(errno));
  26. return NULL;
  27. }
  28. SDL_RWops *bitmap = SDL_RWFromMem(buffer, bitmapLength);
  29. if (!bitmap) {
  30. free(buffer);
  31. return NULL;
  32. }
  33. if (SDL_RWseek(rw, offset, RW_SEEK_SET) < 0) goto fail;
  34. Uint32 dibHeaderLength = SDL_ReadLE32(rw);
  35. Uint32 bitmapDataOffset = 0x0E + dibHeaderLength;
  36. if (dibHeaderLength == 0x0C) {
  37. if (SDL_RWseek(rw, 0x06, RW_SEEK_CUR) < 0) goto fail;
  38. Uint16 bitsPerPixel = SDL_ReadLE16(rw);
  39. bitmapDataOffset += 3 * (1 << bitsPerPixel);
  40. } else if (dibHeaderLength == 0x28) {
  41. if (SDL_RWseek(rw, 0x0A, RW_SEEK_CUR) < 0) goto fail;
  42. Uint16 bitsPerPixel = SDL_ReadLE16(rw);
  43. if (SDL_RWseek(rw, 0x10, RW_SEEK_CUR) < 0) goto fail;
  44. Uint32 paletteLength = SDL_ReadLE32(rw);
  45. if (!paletteLength && bitsPerPixel < 16) {
  46. paletteLength = 1 << bitsPerPixel;
  47. }
  48. bitmapDataOffset += 4 * paletteLength;
  49. } else {
  50. SDL_SetError("unrecognized DIB header length %u", dibHeaderLength);
  51. goto fail;
  52. }
  53. SDL_WriteU8(bitmap, 'B');
  54. SDL_WriteU8(bitmap, 'M');
  55. SDL_WriteLE32(bitmap, bitmapLength);
  56. SDL_WriteLE16(bitmap, 0); // reserved
  57. SDL_WriteLE16(bitmap, 0); // reserved
  58. SDL_WriteLE32(bitmap, bitmapDataOffset);
  59. if (SDL_RWseek(rw, offset, RW_SEEK_SET) < 0) goto fail;
  60. if (SDL_RWread(rw, &buffer[SDL_RWtell(bitmap)], length, 1) < 1) goto fail;
  61. SDL_RWseek(bitmap, 0, RW_SEEK_SET);
  62. SDL_Surface *surface = SDL_LoadBMP_RW(bitmap, 1);
  63. free(buffer);
  64. return surface;
  65. fail:
  66. SDL_RWclose(bitmap);
  67. free(buffer);
  68. return NULL;
  69. }
  70. // exefmt.txt
  71. static int
  72. loadNE(
  73. SDL_Surface **surfaces, size_t count,
  74. SDL_RWops *rw, Uint16 neOffset, Uint32 idSkip
  75. ) {
  76. if (SDL_RWseek(rw, neOffset + 0x24, RW_SEEK_SET) < 0) return -1;
  77. Uint16 resourceTableOffset = neOffset + SDL_ReadLE16(rw);
  78. if (SDL_RWseek(rw, resourceTableOffset, RW_SEEK_SET) < 0) return -1;
  79. Uint16 alignmentShift = SDL_ReadLE16(rw);
  80. Uint16 resourceCount;
  81. for (;;) {
  82. Uint16 typeID = SDL_ReadLE16(rw);
  83. resourceCount = SDL_ReadLE16(rw);
  84. SDL_ReadLE32(rw); // reserved
  85. if (!typeID) {
  86. SDL_SetError("no bitmap resources");
  87. return -1;
  88. }
  89. if (typeID == 0x8002) break;
  90. if (SDL_RWseek(rw, 0x0C * resourceCount, RW_SEEK_CUR) < 0) return -1;
  91. }
  92. for (Uint16 i = 0; i < resourceCount; ++i) {
  93. Uint16 dataOffset = SDL_ReadLE16(rw);
  94. Uint16 dataLength = SDL_ReadLE16(rw);
  95. /* Uint16 flags = */ SDL_ReadLE16(rw);
  96. Uint16 resourceID = SDL_ReadLE16(rw);
  97. SDL_ReadLE32(rw); // reserved
  98. resourceID &= 0x7FFF;
  99. if (resourceID >= idSkip) resourceID -= idSkip;
  100. if (resourceID >= count) continue;
  101. Sint64 nextResource = SDL_RWtell(rw);
  102. if (nextResource < 0) return -1;
  103. surfaces[resourceID] = dibSurface(
  104. rw,
  105. (Uint32)dataOffset << alignmentShift,
  106. (Uint32)dataLength << alignmentShift
  107. );
  108. if (!surfaces[resourceID]) return -1;
  109. if (SDL_RWseek(rw, nextResource, RW_SEEK_SET) < 0) return -1;
  110. }
  111. return 0;
  112. }
  113. // <https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format>
  114. static int
  115. loadPE(
  116. SDL_Surface **surfaces, size_t count,
  117. SDL_RWops *rw, Uint16 peOffset, Uint32 idSkip
  118. ) {
  119. if (SDL_RWseek(rw, peOffset + 0x04 + 0x02, RW_SEEK_SET) < 0) return -1;
  120. Uint16 sectionCount = SDL_ReadLE16(rw);
  121. if (SDL_RWseek(rw, peOffset + 0x04 + 0x10, RW_SEEK_SET) < 0) return -1;
  122. Uint16 optionalHeaderLength = SDL_ReadLE16(rw);
  123. Uint16 sectionTableOffset = peOffset + 0x04 + 0x14 + optionalHeaderLength;
  124. Uint32 resourceTableOffset = 0;
  125. Uint32 resourceTableVirtual = 0;
  126. for (Uint16 i = 0; i < sectionCount; ++i) {
  127. Uint16 headerOffset = sectionTableOffset + 0x28 * i;
  128. if (SDL_RWseek(rw, headerOffset, RW_SEEK_SET) < 0) return -1;
  129. char name[8];
  130. if (!SDL_RWread(rw, name, sizeof(name), 1)) return -1;
  131. if (strncmp(".rsrc", name, sizeof(name))) continue;
  132. if (SDL_RWseek(rw, headerOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
  133. resourceTableVirtual = SDL_ReadLE32(rw);
  134. if (SDL_RWseek(rw, headerOffset + 0x14, RW_SEEK_SET) < 0) return -1;
  135. resourceTableOffset = SDL_ReadLE32(rw);
  136. break;
  137. }
  138. if (!resourceTableOffset) {
  139. SDL_SetError("no resource table");
  140. return -1;
  141. }
  142. if (SDL_RWseek(rw, resourceTableOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
  143. Uint16 typeNameCount = SDL_ReadLE16(rw);
  144. Uint16 typeIDCount = SDL_ReadLE16(rw);
  145. if (SDL_RWseek(rw, 0x08 * typeNameCount, RW_SEEK_CUR) < 0) return -1;
  146. Uint32 bitmapTableOffset = 0;
  147. for (Uint16 i = 0; i < typeIDCount; ++i) {
  148. Uint32 typeID = SDL_ReadLE32(rw);
  149. Uint32 subdirOffset = SDL_ReadLE32(rw);
  150. if (typeID != 0x02) continue;
  151. if (!(subdirOffset & (1 << 31))) {
  152. SDL_SetError("bitmap type entry does not point to table");
  153. return -1;
  154. }
  155. bitmapTableOffset = resourceTableOffset + (subdirOffset & ~(1 << 31));
  156. break;
  157. }
  158. if (SDL_RWseek(rw, bitmapTableOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
  159. Uint16 nameNameCount = SDL_ReadLE16(rw);
  160. Uint16 nameIDCount = SDL_ReadLE16(rw);
  161. if (SDL_RWseek(rw, 0x08 * nameNameCount, RW_SEEK_CUR) < 0) return -1;
  162. for (Uint16 i = 0; i < nameIDCount; ++i) {
  163. Uint32 nameID = SDL_ReadLE32(rw);
  164. if (nameID >= idSkip) nameID -= idSkip;
  165. if (nameID >= count) continue;
  166. Uint32 subdirOffset = SDL_ReadLE32(rw);
  167. if (!(subdirOffset & (1 << 31))) {
  168. SDL_SetError("bitmap name entry does not point to table");
  169. return -1;
  170. }
  171. Sint64 nextName = SDL_RWtell(rw);
  172. if (nextName < 0) return -1;
  173. Uint32 langTableOffset =
  174. resourceTableOffset + (subdirOffset & ~(1 << 31));
  175. if (SDL_RWseek(rw, langTableOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
  176. Uint16 langNameCount = SDL_ReadLE16(rw);
  177. Uint16 langIDCount = SDL_ReadLE16(rw);
  178. if (langNameCount != 0 || langIDCount != 1) {
  179. SDL_SetError("language table contains more than one entry");
  180. return -1;
  181. }
  182. /* Uint32 langID = */ SDL_ReadLE32(rw);
  183. Uint32 dataEntryOffset = SDL_ReadLE32(rw);
  184. if (dataEntryOffset & (1 << 31)) {
  185. SDL_SetError("language entry does not point to data");
  186. return -1;
  187. }
  188. dataEntryOffset += resourceTableOffset;
  189. if (SDL_RWseek(rw, dataEntryOffset, RW_SEEK_SET) < 0) return -1;
  190. Uint32 dataVirtual = SDL_ReadLE32(rw);
  191. Uint32 dataLength = SDL_ReadLE32(rw);
  192. Uint32 dataOffset =
  193. dataVirtual - (resourceTableVirtual - resourceTableOffset);
  194. surfaces[nameID] = dibSurface(rw, dataOffset, dataLength);
  195. if (!surfaces[nameID]) return -1;
  196. if (SDL_RWseek(rw, nextName, RW_SEEK_SET) < 0) return -1;
  197. }
  198. return 0;
  199. }
  200. static int
  201. loadEXE(SDL_Surface **surfaces, size_t count, SDL_RWops *rw, Uint32 idSkip) {
  202. if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0) return -1;
  203. if (SDL_ReadU8(rw) != 'M' || SDL_ReadU8(rw) != 'Z') {
  204. SDL_SetError("invalid MZ signature");
  205. return -1;
  206. }
  207. if (SDL_RWseek(rw, 0x3C, RW_SEEK_SET) < 0) return -1;
  208. Uint16 offset = SDL_ReadLE16(rw);
  209. if (SDL_RWseek(rw, offset, RW_SEEK_SET) < 0) return -1;
  210. Uint8 sig[2] = { SDL_ReadU8(rw), SDL_ReadU8(rw) };
  211. if (sig[0] == 'N' && sig[1] == 'E') {
  212. return loadNE(surfaces, count, rw, offset, idSkip);
  213. } else if (sig[0] == 'P' && sig[1] == 'E') {
  214. return loadPE(surfaces, count, rw, offset, idSkip);
  215. } else {
  216. SDL_SetError("invalid NE/PE signature");
  217. return -1;
  218. }
  219. }
  220. static int setColorKey(SDL_Surface **surfaces, size_t count) {
  221. size_t i = Cards_Empty;
  222. if (i < count) {
  223. if (SDL_SetColorKey(surfaces[i], SDL_TRUE, 1) < 0) return -1;
  224. }
  225. for (i = Cards_X; i <= Cards_O; ++i) {
  226. if (i >= count) break;
  227. if (SDL_SetColorKey(surfaces[i], SDL_TRUE, 12) < 0) return -1;
  228. }
  229. return 0;
  230. }
  231. static int setAlphaCorners(SDL_Surface **surfaces, size_t count) {
  232. SDL_Surface *alpha = NULL;
  233. for (size_t i = 0; i < count; ++i) {
  234. if (!surfaces[i]) continue;
  235. alpha = SDL_ConvertSurfaceFormat(surfaces[i], SDL_PIXELFORMAT_RGBA32, 0);
  236. if (!alpha) return -1;
  237. if (SDL_SetSurfaceBlendMode(alpha, SDL_BLENDMODE_BLEND) < 0) goto fail;
  238. SDL_Rect rects[8] = {
  239. { 0, 0, 2, 1 },
  240. { 0, 1, 1, 1 },
  241. { Cards_CardWidth - 2, 0, 2, 1 },
  242. { Cards_CardWidth - 1, 1, 1, 1 },
  243. { 0, Cards_CardHeight - 1, 2, 1 },
  244. { 0, Cards_CardHeight - 2, 1, 1 },
  245. { Cards_CardWidth - 2, Cards_CardHeight - 1, 2, 1 },
  246. { Cards_CardWidth - 1, Cards_CardHeight - 2, 1, 1 },
  247. };
  248. Uint32 trans = SDL_MapRGBA(alpha->format, 0x00, 0x00, 0x00, 0x00);
  249. if (SDL_FillRects(alpha, rects, 8, trans) < 0) goto fail;
  250. SDL_FreeSurface(surfaces[i]);
  251. surfaces[i] = alpha;
  252. }
  253. return 0;
  254. fail:
  255. SDL_FreeSurface(alpha);
  256. return -1;
  257. }
  258. static int setBlackBorders(SDL_Surface **surfaces, size_t count) {
  259. for (size_t i = Cards_Diamond + Cards_A; i <= Cards_Heart + Cards_K; ++i) {
  260. if (i >= count) break;
  261. if (!surfaces[i]) continue;
  262. SDL_Rect rects[8] = {
  263. { 2, 0, Cards_CardWidth - 4, 1 },
  264. { 2, Cards_CardHeight - 1, Cards_CardWidth - 4, 1 },
  265. { 0, 2, 1, Cards_CardHeight - 4 },
  266. { Cards_CardWidth - 1, 2, 1, Cards_CardHeight - 4 },
  267. { 1, 1, 1, 1 },
  268. { Cards_CardWidth - 2, 1, 1, 1 },
  269. { 1, Cards_CardHeight - 2, 1, 1 },
  270. { Cards_CardWidth - 2, Cards_CardHeight - 2, 1, 1 },
  271. };
  272. Uint32 black = SDL_MapRGB(surfaces[i]->format, 0x00, 0x00, 0x00);
  273. if (SDL_FillRects(surfaces[i], rects, 8, black) < 0) return -1;
  274. }
  275. return 0;
  276. }
  277. static int
  278. checkRange(SDL_Surface **surfaces, size_t count, size_t a, size_t b) {
  279. for (size_t i = a; i <= b; ++i) {
  280. if (i >= count) break;
  281. if (surfaces[i]) continue;
  282. SDL_SetError("missing resource %zu", i);
  283. return -1;
  284. }
  285. return 0;
  286. }
  287. int
  288. Cards_LoadCards(
  289. SDL_Surface *surfaces[], size_t count,
  290. SDL_RWops *rw, enum Cards_Flag flags
  291. ) {
  292. memset(surfaces, 0, sizeof(*surfaces) * count);
  293. if (loadEXE(surfaces, count, rw, 0) < 0) return -1;
  294. if (checkRange(surfaces, count, Cards_A, Cards_Back12) < 0) return -1;
  295. if (checkRange(surfaces, count, Cards_X, Cards_O) < 0) return -1;
  296. if (flags & Cards_ColorKey) {
  297. if (setColorKey(surfaces, count) < 0) return -1;
  298. }
  299. if (flags & Cards_AlphaCorners) {
  300. if (setAlphaCorners(surfaces, count) < 0) return -1;
  301. }
  302. if (flags & Cards_BlackBorders) {
  303. if (setBlackBorders(surfaces, count) < 0) return -1;
  304. }
  305. return 0;
  306. }
  307. int
  308. Cards_LoadFreeCell(
  309. SDL_Surface *surfaces[], size_t count,
  310. SDL_RWops *rw, enum Cards_Flag flags
  311. ) {
  312. memset(surfaces, 0, sizeof(*surfaces) * count);
  313. if (loadEXE(surfaces, count, rw, 402) < 0) return -1;
  314. if (checkRange(surfaces, count, Cards_KingRight, Cards_KingWin) < 0) {
  315. return -1;
  316. }
  317. if (flags & Cards_ColorKey) {
  318. for (size_t i = Cards_KingRight; i <= Cards_KingWin; ++i) {
  319. if (i >= count) break;
  320. if (SDL_SetColorKey(surfaces[i], SDL_TRUE, 2) < 0) return -1;
  321. }
  322. }
  323. return 0;
  324. }
  325. static int invertPalette(SDL_Surface *surface) {
  326. const SDL_Palette *palette = surface->format->palette;
  327. SDL_Palette *invert = SDL_AllocPalette(palette->ncolors);
  328. if (!invert) return -1;
  329. for (int i = 0; i < invert->ncolors; ++i) {
  330. invert->colors[i].r = ~palette->colors[i].r;
  331. invert->colors[i].g = ~palette->colors[i].g;
  332. invert->colors[i].b = ~palette->colors[i].b;
  333. invert->colors[i].a = palette->colors[i].a;
  334. }
  335. if (SDL_SetSurfacePalette(surface, invert) < 0) return -1;
  336. SDL_FreePalette(invert);
  337. return 0;
  338. }
  339. static int invertPixels(SDL_Surface *surface) {
  340. if (SDL_LockSurface(surface) < 0) return -1;
  341. Uint8 *pixels = surface->pixels;
  342. for (int y = 0; y < surface->h; ++y) {
  343. Uint32 *row = (Uint32 *)&pixels[y * surface->pitch];
  344. for (int x = 0; x < surface->w; ++x) {
  345. Uint32 color = ~row[x] & ~surface->format->Amask;
  346. Uint32 alpha = row[x] & surface->format->Amask;
  347. row[x] = color | alpha;
  348. }
  349. }
  350. SDL_UnlockSurface(surface);
  351. return 0;
  352. }
  353. int Cards_InvertSurface(SDL_Surface *surface) {
  354. if (surface->format->palette) {
  355. if (invertPalette(surface) < 0) return -1;
  356. } else if (surface->format->BytesPerPixel == 4) {
  357. if (invertPixels(surface) < 0) return -1;
  358. } else {
  359. SDL_SetError("cannot invert surface format");
  360. return -1;
  361. }
  362. return 0;
  363. }