logo

xcursorgen-nox

xcursorgen(1) without libX11/libXcursor dependency

file.c (24649B)


  1. /*
  2. * Copyright © 2002 Keith Packard
  3. *
  4. * SPDX-License-Identifier: MIT
  5. *
  6. * Permission to use, copy, modify, distribute, and sell this software and its
  7. * documentation for any purpose is hereby granted without fee, provided that
  8. * the above copyright notice appear in all copies and that both that
  9. * copyright notice and this permission notice appear in supporting
  10. * documentation, and that the name of Keith Packard not be used in
  11. * advertising or publicity pertaining to distribution of the software without
  12. * specific, written prior permission. Keith Packard makes no
  13. * representations about the suitability of this software for any purpose. It
  14. * is provided "as is" without express or implied warranty.
  15. *
  16. * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17. * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  18. * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  20. * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  21. * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  22. * PERFORMANCE OF THIS SOFTWARE.
  23. */
  24. #define _POSIX_C_SOURCE 200809L
  25. #include "Xcursor.h"
  26. #include <stdlib.h>
  27. #include <string.h>
  28. XcursorImage *
  29. XcursorImageCreate(int width, int height)
  30. {
  31. XcursorImage *image;
  32. if(width < 0 || height < 0) return NULL;
  33. if(width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) return NULL;
  34. image = malloc(sizeof(XcursorImage) + (size_t)(width * height) * sizeof(XcursorPixel));
  35. if(!image) return NULL;
  36. image->version = XCURSOR_IMAGE_VERSION;
  37. image->pixels = (XcursorPixel *)(image + 1);
  38. image->size = (XcursorDim)(width > height ? width : height);
  39. image->width = (XcursorDim)width;
  40. image->height = (XcursorDim)height;
  41. image->delay = 0;
  42. return image;
  43. }
  44. void
  45. XcursorImageDestroy(XcursorImage *image)
  46. {
  47. free(image);
  48. }
  49. XcursorImages *
  50. XcursorImagesCreate(int size)
  51. {
  52. XcursorImages *images;
  53. images = malloc(sizeof(XcursorImages) + (size_t)size * sizeof(XcursorImage *));
  54. if(!images) return NULL;
  55. images->nimage = 0;
  56. images->images = (XcursorImage **)(images + 1);
  57. images->name = NULL;
  58. return images;
  59. }
  60. void
  61. XcursorImagesDestroy(XcursorImages *images)
  62. {
  63. int n;
  64. if(!images) return;
  65. for(n = 0; n < images->nimage; n++)
  66. XcursorImageDestroy(images->images[n]);
  67. if(images->name) free(images->name);
  68. free(images);
  69. }
  70. void
  71. XcursorImagesSetName(XcursorImages *images, const char *name)
  72. {
  73. char *new;
  74. if(!images || !name) return;
  75. new = strdup(name);
  76. if(!new) return;
  77. if(images->name) free(images->name);
  78. images->name = new;
  79. }
  80. XcursorComment *
  81. XcursorCommentCreate(XcursorUInt comment_type, int length)
  82. {
  83. XcursorComment *comment;
  84. if(length < 0 || length > XCURSOR_COMMENT_MAX_LEN) return NULL;
  85. comment = malloc(sizeof(XcursorComment) + (size_t)length + 1);
  86. if(!comment) return NULL;
  87. comment->version = XCURSOR_COMMENT_VERSION;
  88. comment->comment_type = comment_type;
  89. comment->comment = (char *)(comment + 1);
  90. comment->comment[0] = '\0';
  91. return comment;
  92. }
  93. void
  94. XcursorCommentDestroy(XcursorComment *comment)
  95. {
  96. free(comment);
  97. }
  98. XcursorComments *
  99. XcursorCommentsCreate(int size)
  100. {
  101. XcursorComments *comments;
  102. comments = malloc(sizeof(XcursorComments) + (size_t)size * sizeof(XcursorComment *));
  103. if(!comments) return NULL;
  104. comments->ncomment = 0;
  105. comments->comments = (XcursorComment **)(comments + 1);
  106. return comments;
  107. }
  108. void
  109. XcursorCommentsDestroy(XcursorComments *comments)
  110. {
  111. int n;
  112. if(!comments) return;
  113. for(n = 0; n < comments->ncomment; n++)
  114. XcursorCommentDestroy(comments->comments[n]);
  115. free(comments);
  116. }
  117. static XcursorBool
  118. _XcursorReadUInt(XcursorFile *file, XcursorUInt *u)
  119. {
  120. unsigned char bytes[4];
  121. if(!file || !u) return XcursorFalse;
  122. if((*file->read)(file, bytes, 4) != 4) return XcursorFalse;
  123. *u = ((XcursorUInt)(bytes[0]) << 0) | ((XcursorUInt)(bytes[1]) << 8) |
  124. ((XcursorUInt)(bytes[2]) << 16) | ((XcursorUInt)(bytes[3]) << 24);
  125. return XcursorTrue;
  126. }
  127. static XcursorBool
  128. _XcursorReadBytes(XcursorFile *file, char *bytes, int length)
  129. {
  130. if(!file || !bytes || (*file->read)(file, (unsigned char *)bytes, length) != length)
  131. return XcursorFalse;
  132. return XcursorTrue;
  133. }
  134. static XcursorBool
  135. _XcursorWriteUInt(XcursorFile *file, XcursorUInt u)
  136. {
  137. unsigned char bytes[4];
  138. if(!file) return XcursorFalse;
  139. bytes[0] = (unsigned char)(u);
  140. bytes[1] = (unsigned char)(u >> 8);
  141. bytes[2] = (unsigned char)(u >> 16);
  142. bytes[3] = (unsigned char)(u >> 24);
  143. if((*file->write)(file, bytes, 4) != 4) return XcursorFalse;
  144. return XcursorTrue;
  145. }
  146. static XcursorBool
  147. _XcursorWriteBytes(XcursorFile *file, char *bytes, int length)
  148. {
  149. if(!file || !bytes || (*file->write)(file, (unsigned char *)bytes, length) != length)
  150. return XcursorFalse;
  151. return XcursorTrue;
  152. }
  153. static void
  154. _XcursorFileHeaderDestroy(XcursorFileHeader *fileHeader)
  155. {
  156. free(fileHeader);
  157. }
  158. static XcursorFileHeader *
  159. _XcursorFileHeaderCreate(XcursorUInt ntoc)
  160. {
  161. XcursorFileHeader *fileHeader;
  162. if(ntoc > 0x10000) return NULL;
  163. fileHeader = malloc(sizeof(XcursorFileHeader) + ntoc * sizeof(XcursorFileToc));
  164. if(!fileHeader) return NULL;
  165. fileHeader->magic = XCURSOR_MAGIC;
  166. fileHeader->header = XCURSOR_FILE_HEADER_LEN;
  167. fileHeader->version = XCURSOR_FILE_VERSION;
  168. fileHeader->ntoc = ntoc;
  169. fileHeader->tocs = (XcursorFileToc *)(fileHeader + 1);
  170. return fileHeader;
  171. }
  172. static XcursorFileHeader *
  173. _XcursorReadFileHeader(XcursorFile *file)
  174. {
  175. XcursorFileHeader head, *fileHeader;
  176. XcursorUInt skip;
  177. XcursorUInt n;
  178. if(!file) return NULL;
  179. if(!_XcursorReadUInt(file, &head.magic)) return NULL;
  180. if(head.magic != XCURSOR_MAGIC) return NULL;
  181. if(!_XcursorReadUInt(file, &head.header)) return NULL;
  182. if(!_XcursorReadUInt(file, &head.version)) return NULL;
  183. if(!_XcursorReadUInt(file, &head.ntoc)) return NULL;
  184. skip = head.header - XCURSOR_FILE_HEADER_LEN;
  185. if(skip)
  186. if((*file->seek)(file, skip, SEEK_CUR) == EOF) return NULL;
  187. fileHeader = _XcursorFileHeaderCreate(head.ntoc);
  188. if(!fileHeader) return NULL;
  189. fileHeader->magic = head.magic;
  190. fileHeader->header = head.header;
  191. fileHeader->version = head.version;
  192. fileHeader->ntoc = head.ntoc;
  193. for(n = 0; n < fileHeader->ntoc; n++)
  194. {
  195. if(!_XcursorReadUInt(file, &fileHeader->tocs[n].type)) break;
  196. if(!_XcursorReadUInt(file, &fileHeader->tocs[n].subtype)) break;
  197. if(!_XcursorReadUInt(file, &fileHeader->tocs[n].position)) break;
  198. }
  199. if(n != fileHeader->ntoc)
  200. {
  201. _XcursorFileHeaderDestroy(fileHeader);
  202. return NULL;
  203. }
  204. return fileHeader;
  205. }
  206. static XcursorUInt
  207. _XcursorFileHeaderLength(XcursorFileHeader *fileHeader)
  208. {
  209. return (XCURSOR_FILE_HEADER_LEN + fileHeader->ntoc * XCURSOR_FILE_TOC_LEN);
  210. }
  211. static XcursorBool
  212. _XcursorWriteFileHeader(XcursorFile *file, XcursorFileHeader *fileHeader)
  213. {
  214. XcursorUInt toc;
  215. if(!file || !fileHeader) return XcursorFalse;
  216. if(!_XcursorWriteUInt(file, fileHeader->magic)) return XcursorFalse;
  217. if(!_XcursorWriteUInt(file, fileHeader->header)) return XcursorFalse;
  218. if(!_XcursorWriteUInt(file, fileHeader->version)) return XcursorFalse;
  219. if(!_XcursorWriteUInt(file, fileHeader->ntoc)) return XcursorFalse;
  220. for(toc = 0; toc < fileHeader->ntoc; toc++)
  221. {
  222. if(!_XcursorWriteUInt(file, fileHeader->tocs[toc].type)) return XcursorFalse;
  223. if(!_XcursorWriteUInt(file, fileHeader->tocs[toc].subtype)) return XcursorFalse;
  224. if(!_XcursorWriteUInt(file, fileHeader->tocs[toc].position)) return XcursorFalse;
  225. }
  226. return XcursorTrue;
  227. }
  228. static XcursorBool
  229. _XcursorSeekToToc(XcursorFile *file, XcursorFileHeader *fileHeader, int toc)
  230. {
  231. if(!file || !fileHeader || (*file->seek)(file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
  232. return XcursorFalse;
  233. return XcursorTrue;
  234. }
  235. static XcursorBool
  236. _XcursorFileReadChunkHeader(XcursorFile *file,
  237. XcursorFileHeader *fileHeader,
  238. int toc,
  239. XcursorChunkHeader *chunkHeader)
  240. {
  241. if(!file || !fileHeader || !chunkHeader) return XcursorFalse;
  242. if(!_XcursorSeekToToc(file, fileHeader, toc)) return XcursorFalse;
  243. if(!_XcursorReadUInt(file, &chunkHeader->header)) return XcursorFalse;
  244. if(!_XcursorReadUInt(file, &chunkHeader->type)) return XcursorFalse;
  245. if(!_XcursorReadUInt(file, &chunkHeader->subtype)) return XcursorFalse;
  246. if(!_XcursorReadUInt(file, &chunkHeader->version)) return XcursorFalse;
  247. /* sanity check */
  248. if(chunkHeader->type != fileHeader->tocs[toc].type ||
  249. chunkHeader->subtype != fileHeader->tocs[toc].subtype)
  250. return XcursorFalse;
  251. return XcursorTrue;
  252. }
  253. static XcursorBool
  254. _XcursorFileWriteChunkHeader(XcursorFile *file,
  255. XcursorFileHeader *fileHeader,
  256. int toc,
  257. XcursorChunkHeader *chunkHeader)
  258. {
  259. if(!file || !fileHeader || !chunkHeader) return XcursorFalse;
  260. if(!_XcursorSeekToToc(file, fileHeader, toc)) return XcursorFalse;
  261. if(!_XcursorWriteUInt(file, chunkHeader->header)) return XcursorFalse;
  262. if(!_XcursorWriteUInt(file, chunkHeader->type)) return XcursorFalse;
  263. if(!_XcursorWriteUInt(file, chunkHeader->subtype)) return XcursorFalse;
  264. if(!_XcursorWriteUInt(file, chunkHeader->version)) return XcursorFalse;
  265. return XcursorTrue;
  266. }
  267. #define dist(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
  268. static XcursorDim
  269. _XcursorFindBestSize(XcursorFileHeader *fileHeader, XcursorDim size, int *nsizesp)
  270. {
  271. XcursorUInt n;
  272. int nsizes = 0;
  273. XcursorDim bestSize = 0;
  274. XcursorDim thisSize;
  275. if(!fileHeader || !nsizesp) return 0;
  276. for(n = 0; n < fileHeader->ntoc; n++)
  277. {
  278. if(fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) continue;
  279. thisSize = fileHeader->tocs[n].subtype;
  280. if(!bestSize || dist(thisSize, size) < dist(bestSize, size))
  281. {
  282. bestSize = thisSize;
  283. nsizes = 1;
  284. }
  285. else if(thisSize == bestSize)
  286. nsizes++;
  287. }
  288. *nsizesp = nsizes;
  289. return bestSize;
  290. }
  291. static int
  292. _XcursorFindImageToc(XcursorFileHeader *fileHeader, XcursorDim size, int count)
  293. {
  294. XcursorUInt toc;
  295. XcursorDim thisSize;
  296. if(!fileHeader) return 0;
  297. for(toc = 0; toc < fileHeader->ntoc; toc++)
  298. {
  299. if(fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) continue;
  300. thisSize = fileHeader->tocs[toc].subtype;
  301. if(thisSize != size) continue;
  302. if(!count) break;
  303. count--;
  304. }
  305. if(toc == fileHeader->ntoc) return -1;
  306. return (int)toc;
  307. }
  308. static XcursorImage *
  309. _XcursorReadImage(XcursorFile *file, XcursorFileHeader *fileHeader, int toc)
  310. {
  311. XcursorChunkHeader chunkHeader;
  312. XcursorImage head;
  313. XcursorImage *image;
  314. int n;
  315. XcursorPixel *p;
  316. if(!file || !fileHeader) return NULL;
  317. if(!_XcursorFileReadChunkHeader(file, fileHeader, toc, &chunkHeader)) return NULL;
  318. if(!_XcursorReadUInt(file, &head.width)) return NULL;
  319. if(!_XcursorReadUInt(file, &head.height)) return NULL;
  320. if(!_XcursorReadUInt(file, &head.xhot)) return NULL;
  321. if(!_XcursorReadUInt(file, &head.yhot)) return NULL;
  322. if(!_XcursorReadUInt(file, &head.delay)) return NULL;
  323. /* sanity check data */
  324. if(head.width > XCURSOR_IMAGE_MAX_SIZE || head.height > XCURSOR_IMAGE_MAX_SIZE) return NULL;
  325. if(head.width == 0 || head.height == 0) return NULL;
  326. if(head.xhot > head.width || head.yhot > head.height) return NULL;
  327. /* Create the image and initialize it */
  328. image = XcursorImageCreate((int)head.width, (int)head.height);
  329. if(image == NULL) return NULL;
  330. if(chunkHeader.version < image->version) image->version = chunkHeader.version;
  331. image->size = chunkHeader.subtype;
  332. image->xhot = head.xhot;
  333. image->yhot = head.yhot;
  334. image->delay = head.delay;
  335. n = (int)(image->width * image->height);
  336. p = image->pixels;
  337. while(n--)
  338. {
  339. if(!_XcursorReadUInt(file, p))
  340. {
  341. XcursorImageDestroy(image);
  342. return NULL;
  343. }
  344. p++;
  345. }
  346. return image;
  347. }
  348. static XcursorUInt
  349. _XcursorImageLength(XcursorImage *image)
  350. {
  351. if(!image) return 0;
  352. return XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4;
  353. }
  354. static XcursorBool
  355. _XcursorWriteImage(XcursorFile *file, XcursorFileHeader *fileHeader, int toc, XcursorImage *image)
  356. {
  357. XcursorChunkHeader chunkHeader;
  358. int n;
  359. XcursorPixel *p;
  360. if(!file || !fileHeader || !image) return XcursorFalse;
  361. /* sanity check data */
  362. if(image->width > XCURSOR_IMAGE_MAX_SIZE || image->height > XCURSOR_IMAGE_MAX_SIZE)
  363. return XcursorFalse;
  364. if(image->width == 0 || image->height == 0) return XcursorFalse;
  365. if(image->xhot > image->width || image->yhot > image->height) return XcursorFalse;
  366. /* write chunk header */
  367. chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN;
  368. chunkHeader.type = XCURSOR_IMAGE_TYPE;
  369. chunkHeader.subtype = image->size;
  370. chunkHeader.version = XCURSOR_IMAGE_VERSION;
  371. if(!_XcursorFileWriteChunkHeader(file, fileHeader, toc, &chunkHeader)) return XcursorFalse;
  372. /* write extra image header fields */
  373. if(!_XcursorWriteUInt(file, image->width)) return XcursorFalse;
  374. if(!_XcursorWriteUInt(file, image->height)) return XcursorFalse;
  375. if(!_XcursorWriteUInt(file, image->xhot)) return XcursorFalse;
  376. if(!_XcursorWriteUInt(file, image->yhot)) return XcursorFalse;
  377. if(!_XcursorWriteUInt(file, image->delay)) return XcursorFalse;
  378. /* write the image */
  379. n = (int)(image->width * image->height);
  380. p = image->pixels;
  381. while(n--)
  382. {
  383. if(!_XcursorWriteUInt(file, *p)) return XcursorFalse;
  384. p++;
  385. }
  386. return XcursorTrue;
  387. }
  388. static XcursorComment *
  389. _XcursorReadComment(XcursorFile *file, XcursorFileHeader *fileHeader, int toc)
  390. {
  391. XcursorChunkHeader chunkHeader;
  392. XcursorUInt length;
  393. XcursorComment *comment;
  394. if(!file || !fileHeader) return NULL;
  395. /* read chunk header */
  396. if(!_XcursorFileReadChunkHeader(file, fileHeader, toc, &chunkHeader)) return NULL;
  397. /* read extra comment header fields */
  398. if(!_XcursorReadUInt(file, &length)) return NULL;
  399. comment = XcursorCommentCreate(chunkHeader.subtype, (int)length);
  400. if(!comment) return NULL;
  401. if(!_XcursorReadBytes(file, comment->comment, (int)length))
  402. {
  403. XcursorCommentDestroy(comment);
  404. return NULL;
  405. }
  406. comment->comment[length] = '\0';
  407. return comment;
  408. }
  409. static XcursorUInt
  410. _XcursorCommentLength(XcursorComment *comment)
  411. {
  412. return XCURSOR_COMMENT_HEADER_LEN + (XcursorUInt)strlen(comment->comment);
  413. }
  414. static XcursorBool
  415. _XcursorWriteComment(XcursorFile *file,
  416. XcursorFileHeader *fileHeader,
  417. int toc,
  418. XcursorComment *comment)
  419. {
  420. XcursorChunkHeader chunkHeader;
  421. XcursorUInt length;
  422. if(!file || !fileHeader || !comment || !comment->comment) return XcursorFalse;
  423. length = (XcursorUInt)strlen(comment->comment);
  424. /* sanity check data */
  425. if(length > XCURSOR_COMMENT_MAX_LEN) return XcursorFalse;
  426. /* read chunk header */
  427. chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN;
  428. chunkHeader.type = XCURSOR_COMMENT_TYPE;
  429. chunkHeader.subtype = comment->comment_type;
  430. chunkHeader.version = XCURSOR_COMMENT_VERSION;
  431. if(!_XcursorFileWriteChunkHeader(file, fileHeader, toc, &chunkHeader)) return XcursorFalse;
  432. /* write extra comment header fields */
  433. if(!_XcursorWriteUInt(file, length)) return XcursorFalse;
  434. if(!_XcursorWriteBytes(file, comment->comment, (int)length)) return XcursorFalse;
  435. return XcursorTrue;
  436. }
  437. XcursorImage *
  438. XcursorXcFileLoadImage(XcursorFile *file, int size)
  439. {
  440. XcursorFileHeader *fileHeader;
  441. XcursorDim bestSize;
  442. int nsize;
  443. int toc;
  444. XcursorImage *image;
  445. if(size < 0) return NULL;
  446. fileHeader = _XcursorReadFileHeader(file);
  447. if(!fileHeader) return NULL;
  448. bestSize = _XcursorFindBestSize(fileHeader, (XcursorDim)size, &nsize);
  449. if(!bestSize) return NULL;
  450. toc = _XcursorFindImageToc(fileHeader, bestSize, 0);
  451. if(toc < 0) return NULL;
  452. image = _XcursorReadImage(file, fileHeader, toc);
  453. _XcursorFileHeaderDestroy(fileHeader);
  454. return image;
  455. }
  456. XcursorImages *
  457. XcursorXcFileLoadImages(XcursorFile *file, int size)
  458. {
  459. XcursorFileHeader *fileHeader;
  460. XcursorDim bestSize;
  461. int nsize;
  462. XcursorImages *images;
  463. int n;
  464. if(!file || size < 0) return NULL;
  465. fileHeader = _XcursorReadFileHeader(file);
  466. if(!fileHeader) return NULL;
  467. bestSize = _XcursorFindBestSize(fileHeader, (XcursorDim)size, &nsize);
  468. if(!bestSize)
  469. {
  470. _XcursorFileHeaderDestroy(fileHeader);
  471. return NULL;
  472. }
  473. images = XcursorImagesCreate(nsize);
  474. if(!images)
  475. {
  476. _XcursorFileHeaderDestroy(fileHeader);
  477. return NULL;
  478. }
  479. for(n = 0; n < nsize; n++)
  480. {
  481. int toc = _XcursorFindImageToc(fileHeader, bestSize, n);
  482. if(toc < 0) break;
  483. images->images[images->nimage] = _XcursorReadImage(file, fileHeader, toc);
  484. if(!images->images[images->nimage]) break;
  485. images->nimage++;
  486. }
  487. _XcursorFileHeaderDestroy(fileHeader);
  488. if(images->nimage != nsize)
  489. {
  490. XcursorImagesDestroy(images);
  491. images = NULL;
  492. }
  493. return images;
  494. }
  495. XcursorImages *
  496. XcursorXcFileLoadAllImages(XcursorFile *file)
  497. {
  498. XcursorFileHeader *fileHeader;
  499. XcursorImage *image;
  500. XcursorImages *images;
  501. int nimage;
  502. XcursorUInt n;
  503. XcursorUInt toc;
  504. if(!file) return NULL;
  505. fileHeader = _XcursorReadFileHeader(file);
  506. if(!fileHeader) return NULL;
  507. nimage = 0;
  508. for(n = 0; n < fileHeader->ntoc; n++)
  509. {
  510. switch(fileHeader->tocs[n].type)
  511. {
  512. case XCURSOR_IMAGE_TYPE:
  513. nimage++;
  514. break;
  515. }
  516. }
  517. images = XcursorImagesCreate(nimage);
  518. if(!images)
  519. {
  520. _XcursorFileHeaderDestroy(fileHeader);
  521. return NULL;
  522. }
  523. for(toc = 0; toc < fileHeader->ntoc; toc++)
  524. {
  525. switch(fileHeader->tocs[toc].type)
  526. {
  527. case XCURSOR_IMAGE_TYPE:
  528. image = _XcursorReadImage(file, fileHeader, (int)toc);
  529. if(image)
  530. {
  531. images->images[images->nimage] = image;
  532. images->nimage++;
  533. }
  534. break;
  535. }
  536. }
  537. _XcursorFileHeaderDestroy(fileHeader);
  538. if(images->nimage != nimage)
  539. {
  540. XcursorImagesDestroy(images);
  541. images = NULL;
  542. }
  543. return images;
  544. }
  545. XcursorBool
  546. XcursorXcFileLoad(XcursorFile *file, XcursorComments **commentsp, XcursorImages **imagesp)
  547. {
  548. XcursorFileHeader *fileHeader;
  549. int nimage;
  550. int ncomment;
  551. XcursorImages *images;
  552. XcursorImage *image;
  553. XcursorComment *comment;
  554. XcursorComments *comments;
  555. XcursorUInt toc;
  556. if(!file) return 0;
  557. fileHeader = _XcursorReadFileHeader(file);
  558. if(!fileHeader) return 0;
  559. nimage = 0;
  560. ncomment = 0;
  561. for(toc = 0; toc < fileHeader->ntoc; toc++)
  562. {
  563. switch(fileHeader->tocs[toc].type)
  564. {
  565. case XCURSOR_COMMENT_TYPE:
  566. ncomment++;
  567. break;
  568. case XCURSOR_IMAGE_TYPE:
  569. nimage++;
  570. break;
  571. }
  572. }
  573. images = XcursorImagesCreate(nimage);
  574. if(!images) return 0;
  575. comments = XcursorCommentsCreate(ncomment);
  576. if(!comments)
  577. {
  578. XcursorImagesDestroy(images);
  579. return 0;
  580. }
  581. for(toc = 0; toc < fileHeader->ntoc; toc++)
  582. {
  583. switch(fileHeader->tocs[toc].type)
  584. {
  585. case XCURSOR_COMMENT_TYPE:
  586. comment = _XcursorReadComment(file, fileHeader, (int)toc);
  587. if(comment)
  588. {
  589. comments->comments[comments->ncomment] = comment;
  590. comments->ncomment++;
  591. }
  592. break;
  593. case XCURSOR_IMAGE_TYPE:
  594. image = _XcursorReadImage(file, fileHeader, (int)toc);
  595. if(image)
  596. {
  597. images->images[images->nimage] = image;
  598. images->nimage++;
  599. }
  600. break;
  601. }
  602. }
  603. _XcursorFileHeaderDestroy(fileHeader);
  604. if(images->nimage != nimage || comments->ncomment != ncomment)
  605. {
  606. XcursorImagesDestroy(images);
  607. XcursorCommentsDestroy(comments);
  608. images = NULL;
  609. comments = NULL;
  610. return XcursorFalse;
  611. }
  612. *imagesp = images;
  613. *commentsp = comments;
  614. return XcursorTrue;
  615. }
  616. XcursorBool
  617. XcursorXcFileSave(XcursorFile *file, const XcursorComments *comments, const XcursorImages *images)
  618. {
  619. XcursorFileHeader *fileHeader;
  620. XcursorUInt position;
  621. int n;
  622. int toc;
  623. if(!file || !comments || !images) return XcursorFalse;
  624. fileHeader = _XcursorFileHeaderCreate((XcursorUInt)(comments->ncomment + images->nimage));
  625. if(!fileHeader) return XcursorFalse;
  626. position = _XcursorFileHeaderLength(fileHeader);
  627. /*
  628. * Compute the toc. Place the images before the comments
  629. * as they're more often read
  630. */
  631. toc = 0;
  632. for(n = 0; n < images->nimage; n++)
  633. {
  634. fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE;
  635. fileHeader->tocs[toc].subtype = images->images[n]->size;
  636. fileHeader->tocs[toc].position = position;
  637. position += _XcursorImageLength(images->images[n]);
  638. toc++;
  639. }
  640. for(n = 0; n < comments->ncomment; n++)
  641. {
  642. fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE;
  643. fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type;
  644. fileHeader->tocs[toc].position = position;
  645. position += _XcursorCommentLength(comments->comments[n]);
  646. toc++;
  647. }
  648. /*
  649. * Write the header and the toc
  650. */
  651. if(!_XcursorWriteFileHeader(file, fileHeader)) goto bail;
  652. /*
  653. * Write the images
  654. */
  655. toc = 0;
  656. for(n = 0; n < images->nimage; n++)
  657. {
  658. if(!_XcursorWriteImage(file, fileHeader, toc, images->images[n])) goto bail;
  659. toc++;
  660. }
  661. /*
  662. * Write the comments
  663. */
  664. for(n = 0; n < comments->ncomment; n++)
  665. {
  666. if(!_XcursorWriteComment(file, fileHeader, toc, comments->comments[n])) goto bail;
  667. toc++;
  668. }
  669. _XcursorFileHeaderDestroy(fileHeader);
  670. return XcursorTrue;
  671. bail:
  672. _XcursorFileHeaderDestroy(fileHeader);
  673. return XcursorFalse;
  674. }
  675. static int
  676. _XcursorStdioFileRead(XcursorFile *file, unsigned char *buf, int len)
  677. {
  678. FILE *f = file->closure;
  679. return (int)fread(buf, 1, (size_t)len, f);
  680. }
  681. static int
  682. _XcursorStdioFileWrite(XcursorFile *file, unsigned char *buf, int len)
  683. {
  684. FILE *f = file->closure;
  685. return (int)fwrite(buf, 1, (size_t)len, f);
  686. }
  687. static int
  688. _XcursorStdioFileSeek(XcursorFile *file, long offset, int whence)
  689. {
  690. FILE *f = file->closure;
  691. return fseek(f, offset, whence);
  692. }
  693. static void
  694. _XcursorStdioFileInitialize(FILE *stdfile, XcursorFile *file)
  695. {
  696. file->closure = stdfile;
  697. file->read = _XcursorStdioFileRead;
  698. file->write = _XcursorStdioFileWrite;
  699. file->seek = _XcursorStdioFileSeek;
  700. }
  701. XcursorImage *
  702. XcursorFileLoadImage(FILE *file, int size)
  703. {
  704. XcursorFile f;
  705. if(!file) return NULL;
  706. _XcursorStdioFileInitialize(file, &f);
  707. return XcursorXcFileLoadImage(&f, size);
  708. }
  709. XcursorImages *
  710. XcursorFileLoadImages(FILE *file, int size)
  711. {
  712. XcursorFile f;
  713. if(!file) return NULL;
  714. _XcursorStdioFileInitialize(file, &f);
  715. return XcursorXcFileLoadImages(&f, size);
  716. }
  717. XcursorImages *
  718. XcursorFileLoadAllImages(FILE *file)
  719. {
  720. XcursorFile f;
  721. if(!file) return NULL;
  722. _XcursorStdioFileInitialize(file, &f);
  723. return XcursorXcFileLoadAllImages(&f);
  724. }
  725. XcursorBool
  726. XcursorFileLoad(FILE *file, XcursorComments **commentsp, XcursorImages **imagesp)
  727. {
  728. XcursorFile f;
  729. if(!file || !commentsp || !imagesp) return XcursorFalse;
  730. _XcursorStdioFileInitialize(file, &f);
  731. return XcursorXcFileLoad(&f, commentsp, imagesp);
  732. }
  733. XcursorBool
  734. XcursorFileSaveImages(FILE *file, const XcursorImages *images)
  735. {
  736. XcursorComments *comments;
  737. XcursorFile f;
  738. XcursorBool ret;
  739. if(!file || !images) return 0;
  740. if((comments = XcursorCommentsCreate(0)) == NULL) return 0;
  741. _XcursorStdioFileInitialize(file, &f);
  742. ret = XcursorXcFileSave(&f, comments, images) && fflush(file) != EOF;
  743. XcursorCommentsDestroy(comments);
  744. return ret;
  745. }
  746. XcursorBool
  747. XcursorFileSave(FILE *file, const XcursorComments *comments, const XcursorImages *images)
  748. {
  749. XcursorFile f;
  750. if(!file || !comments || !images) return XcursorFalse;
  751. _XcursorStdioFileInitialize(file, &f);
  752. return XcursorXcFileSave(&f, comments, images) && fflush(file) != EOF;
  753. }
  754. XcursorImage *
  755. XcursorFilenameLoadImage(const char *file, int size)
  756. {
  757. FILE *f;
  758. XcursorImage *image;
  759. if(!file || size < 0) return NULL;
  760. f = fopen(file, "r");
  761. if(!f) return NULL;
  762. image = XcursorFileLoadImage(f, size);
  763. fclose(f);
  764. return image;
  765. }
  766. XcursorImages *
  767. XcursorFilenameLoadImages(const char *file, int size)
  768. {
  769. FILE *f;
  770. XcursorImages *images;
  771. if(!file || size < 0) return NULL;
  772. f = fopen(file, "r");
  773. if(!f) return NULL;
  774. images = XcursorFileLoadImages(f, size);
  775. fclose(f);
  776. return images;
  777. }
  778. XcursorImages *
  779. XcursorFilenameLoadAllImages(const char *file)
  780. {
  781. FILE *f;
  782. XcursorImages *images;
  783. if(!file) return NULL;
  784. f = fopen(file, "r");
  785. if(!f) return NULL;
  786. images = XcursorFileLoadAllImages(f);
  787. fclose(f);
  788. return images;
  789. }
  790. XcursorBool
  791. XcursorFilenameLoad(const char *file, XcursorComments **commentsp, XcursorImages **imagesp)
  792. {
  793. FILE *f;
  794. XcursorBool ret;
  795. if(!file) return XcursorFalse;
  796. f = fopen(file, "r");
  797. if(!f) return 0;
  798. ret = XcursorFileLoad(f, commentsp, imagesp);
  799. fclose(f);
  800. return ret;
  801. }
  802. XcursorBool
  803. XcursorFilenameSaveImages(const char *file, const XcursorImages *images)
  804. {
  805. FILE *f;
  806. XcursorBool ret;
  807. if(!file || !images) return XcursorFalse;
  808. f = fopen(file, "w");
  809. if(!f) return 0;
  810. ret = XcursorFileSaveImages(f, images);
  811. return fclose(f) != EOF && ret;
  812. }
  813. XcursorBool
  814. XcursorFilenameSave(const char *file, const XcursorComments *comments, const XcursorImages *images)
  815. {
  816. FILE *f;
  817. XcursorBool ret;
  818. if(!file || !comments || !images) return XcursorFalse;
  819. f = fopen(file, "w");
  820. if(!f) return 0;
  821. ret = XcursorFileSave(f, comments, images);
  822. return fclose(f) != EOF && ret;
  823. }