1 /*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission. Keith Packard makes no
11 * representations about the suitability of this software for any purpose. It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23 #include "xcursorint.h"
24 #include <stdlib.h>
25 #include <string.h>
26
27 XcursorImage *
XcursorImageCreate(int width,int height)28 XcursorImageCreate (int width, int height)
29 {
30 XcursorImage *image;
31
32 if (width < 0 || height < 0)
33 return NULL;
34 if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE)
35 return NULL;
36
37 image = malloc (sizeof (XcursorImage) +
38 width * height * sizeof (XcursorPixel));
39 if (!image)
40 return NULL;
41 image->version = XCURSOR_IMAGE_VERSION;
42 image->pixels = (XcursorPixel *) (image + 1);
43 image->size = width > height ? width : height;
44 image->width = width;
45 image->height = height;
46 image->delay = 0;
47 return image;
48 }
49
50 void
XcursorImageDestroy(XcursorImage * image)51 XcursorImageDestroy (XcursorImage *image)
52 {
53 free (image);
54 }
55
56 XcursorImages *
XcursorImagesCreate(int size)57 XcursorImagesCreate (int size)
58 {
59 XcursorImages *images;
60
61 images = malloc (sizeof (XcursorImages) +
62 size * sizeof (XcursorImage *));
63 if (!images)
64 return NULL;
65 images->nimage = 0;
66 images->images = (XcursorImage **) (images + 1);
67 images->name = NULL;
68 return images;
69 }
70
71 void
XcursorImagesDestroy(XcursorImages * images)72 XcursorImagesDestroy (XcursorImages *images)
73 {
74 int n;
75
76 if (!images)
77 return;
78
79 for (n = 0; n < images->nimage; n++)
80 XcursorImageDestroy (images->images[n]);
81 if (images->name)
82 free (images->name);
83 free (images);
84 }
85
86 void
XcursorImagesSetName(XcursorImages * images,const char * name)87 XcursorImagesSetName (XcursorImages *images, const char *name)
88 {
89 char *new;
90
91 if (!images || !name)
92 return;
93
94 new = strdup (name);
95
96 if (!new)
97 return;
98
99 if (images->name)
100 free (images->name);
101 images->name = new;
102 }
103
104 XcursorComment *
XcursorCommentCreate(XcursorUInt comment_type,int length)105 XcursorCommentCreate (XcursorUInt comment_type, int length)
106 {
107 XcursorComment *comment;
108
109 if (length < 0 || length > XCURSOR_COMMENT_MAX_LEN)
110 return NULL;
111
112 comment = malloc (sizeof (XcursorComment) + length + 1);
113 if (!comment)
114 return NULL;
115 comment->version = XCURSOR_COMMENT_VERSION;
116 comment->comment_type = comment_type;
117 comment->comment = (char *) (comment + 1);
118 comment->comment[0] = '\0';
119 return comment;
120 }
121
122 void
XcursorCommentDestroy(XcursorComment * comment)123 XcursorCommentDestroy (XcursorComment *comment)
124 {
125 free (comment);
126 }
127
128 XcursorComments *
XcursorCommentsCreate(int size)129 XcursorCommentsCreate (int size)
130 {
131 XcursorComments *comments;
132
133 comments = malloc (sizeof (XcursorComments) +
134 size * sizeof (XcursorComment *));
135 if (!comments)
136 return NULL;
137 comments->ncomment = 0;
138 comments->comments = (XcursorComment **) (comments + 1);
139 return comments;
140 }
141
142 void
XcursorCommentsDestroy(XcursorComments * comments)143 XcursorCommentsDestroy (XcursorComments *comments)
144 {
145 int n;
146
147 if (!comments)
148 return;
149
150 for (n = 0; n < comments->ncomment; n++)
151 XcursorCommentDestroy (comments->comments[n]);
152 free (comments);
153 }
154
155 static XcursorBool
_XcursorReadUInt(XcursorFile * file,XcursorUInt * u)156 _XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
157 {
158 unsigned char bytes[4];
159
160 if (!file || !u)
161 return XcursorFalse;
162
163 if ((*file->read) (file, bytes, 4) != 4)
164 return XcursorFalse;
165 *u = ((bytes[0] << 0) |
166 (bytes[1] << 8) |
167 (bytes[2] << 16) |
168 (bytes[3] << 24));
169 return XcursorTrue;
170 }
171
172 static XcursorBool
_XcursorReadBytes(XcursorFile * file,char * bytes,int length)173 _XcursorReadBytes (XcursorFile *file, char *bytes, int length)
174 {
175 if (!file || !bytes || (*file->read) (file, (unsigned char *) bytes, length) != length)
176 return XcursorFalse;
177 return XcursorTrue;
178 }
179
180 static XcursorBool
_XcursorWriteUInt(XcursorFile * file,XcursorUInt u)181 _XcursorWriteUInt (XcursorFile *file, XcursorUInt u)
182 {
183 unsigned char bytes[4];
184
185 if (!file)
186 return XcursorFalse;
187
188 bytes[0] = u;
189 bytes[1] = u >> 8;
190 bytes[2] = u >> 16;
191 bytes[3] = u >> 24;
192 if ((*file->write) (file, bytes, 4) != 4)
193 return XcursorFalse;
194 return XcursorTrue;
195 }
196
197 static XcursorBool
_XcursorWriteBytes(XcursorFile * file,char * bytes,int length)198 _XcursorWriteBytes (XcursorFile *file, char *bytes, int length)
199 {
200 if (!file || !bytes || (*file->write) (file, (unsigned char *) bytes, length) != length)
201 return XcursorFalse;
202 return XcursorTrue;
203 }
204
205 static void
_XcursorFileHeaderDestroy(XcursorFileHeader * fileHeader)206 _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
207 {
208 free (fileHeader);
209 }
210
211 static XcursorFileHeader *
_XcursorFileHeaderCreate(XcursorUInt ntoc)212 _XcursorFileHeaderCreate (XcursorUInt ntoc)
213 {
214 XcursorFileHeader *fileHeader;
215
216 if (ntoc > 0x10000)
217 return NULL;
218 fileHeader = malloc (sizeof (XcursorFileHeader) +
219 ntoc * sizeof (XcursorFileToc));
220 if (!fileHeader)
221 return NULL;
222 fileHeader->magic = XCURSOR_MAGIC;
223 fileHeader->header = XCURSOR_FILE_HEADER_LEN;
224 fileHeader->version = XCURSOR_FILE_VERSION;
225 fileHeader->ntoc = ntoc;
226 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
227 return fileHeader;
228 }
229
230 static XcursorFileHeader *
_XcursorReadFileHeader(XcursorFile * file)231 _XcursorReadFileHeader (XcursorFile *file)
232 {
233 XcursorFileHeader head, *fileHeader;
234 XcursorUInt skip;
235 int n;
236
237 if (!file)
238 return NULL;
239
240 if (!_XcursorReadUInt (file, &head.magic))
241 return NULL;
242 if (head.magic != XCURSOR_MAGIC)
243 return NULL;
244 if (!_XcursorReadUInt (file, &head.header))
245 return NULL;
246 if (!_XcursorReadUInt (file, &head.version))
247 return NULL;
248 if (!_XcursorReadUInt (file, &head.ntoc))
249 return NULL;
250 skip = head.header - XCURSOR_FILE_HEADER_LEN;
251 if (skip)
252 if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
253 return NULL;
254 fileHeader = _XcursorFileHeaderCreate (head.ntoc);
255 if (!fileHeader)
256 return NULL;
257 fileHeader->magic = head.magic;
258 fileHeader->header = head.header;
259 fileHeader->version = head.version;
260 fileHeader->ntoc = head.ntoc;
261 for (n = 0; n < fileHeader->ntoc; n++)
262 {
263 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
264 break;
265 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
266 break;
267 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
268 break;
269 }
270 if (n != fileHeader->ntoc)
271 {
272 _XcursorFileHeaderDestroy (fileHeader);
273 return NULL;
274 }
275 return fileHeader;
276 }
277
278 static XcursorUInt
_XcursorFileHeaderLength(XcursorFileHeader * fileHeader)279 _XcursorFileHeaderLength (XcursorFileHeader *fileHeader)
280 {
281 return (XCURSOR_FILE_HEADER_LEN +
282 fileHeader->ntoc * XCURSOR_FILE_TOC_LEN);
283 }
284
285 static XcursorBool
_XcursorWriteFileHeader(XcursorFile * file,XcursorFileHeader * fileHeader)286 _XcursorWriteFileHeader (XcursorFile *file, XcursorFileHeader *fileHeader)
287 {
288 int toc;
289
290 if (!file || !fileHeader)
291 return XcursorFalse;
292
293 if (!_XcursorWriteUInt (file, fileHeader->magic))
294 return XcursorFalse;
295 if (!_XcursorWriteUInt (file, fileHeader->header))
296 return XcursorFalse;
297 if (!_XcursorWriteUInt (file, fileHeader->version))
298 return XcursorFalse;
299 if (!_XcursorWriteUInt (file, fileHeader->ntoc))
300 return XcursorFalse;
301 for (toc = 0; toc < fileHeader->ntoc; toc++)
302 {
303 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].type))
304 return XcursorFalse;
305 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].subtype))
306 return XcursorFalse;
307 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].position))
308 return XcursorFalse;
309 }
310 return XcursorTrue;
311 }
312
313 static XcursorBool
_XcursorSeekToToc(XcursorFile * file,XcursorFileHeader * fileHeader,int toc)314 _XcursorSeekToToc (XcursorFile *file,
315 XcursorFileHeader *fileHeader,
316 int toc)
317 {
318 if (!file || !fileHeader || \
319 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
320 return XcursorFalse;
321 return XcursorTrue;
322 }
323
324 static XcursorBool
_XcursorFileReadChunkHeader(XcursorFile * file,XcursorFileHeader * fileHeader,int toc,XcursorChunkHeader * chunkHeader)325 _XcursorFileReadChunkHeader (XcursorFile *file,
326 XcursorFileHeader *fileHeader,
327 int toc,
328 XcursorChunkHeader *chunkHeader)
329 {
330 if (!file || !fileHeader || !chunkHeader)
331 return XcursorFalse;
332 if (!_XcursorSeekToToc (file, fileHeader, toc))
333 return XcursorFalse;
334 if (!_XcursorReadUInt (file, &chunkHeader->header))
335 return XcursorFalse;
336 if (!_XcursorReadUInt (file, &chunkHeader->type))
337 return XcursorFalse;
338 if (!_XcursorReadUInt (file, &chunkHeader->subtype))
339 return XcursorFalse;
340 if (!_XcursorReadUInt (file, &chunkHeader->version))
341 return XcursorFalse;
342 /* sanity check */
343 if (chunkHeader->type != fileHeader->tocs[toc].type ||
344 chunkHeader->subtype != fileHeader->tocs[toc].subtype)
345 return XcursorFalse;
346 return XcursorTrue;
347 }
348
349 static XcursorBool
_XcursorFileWriteChunkHeader(XcursorFile * file,XcursorFileHeader * fileHeader,int toc,XcursorChunkHeader * chunkHeader)350 _XcursorFileWriteChunkHeader (XcursorFile *file,
351 XcursorFileHeader *fileHeader,
352 int toc,
353 XcursorChunkHeader *chunkHeader)
354 {
355 if (!file || !fileHeader || !chunkHeader)
356 return XcursorFalse;
357 if (!_XcursorSeekToToc (file, fileHeader, toc))
358 return XcursorFalse;
359 if (!_XcursorWriteUInt (file, chunkHeader->header))
360 return XcursorFalse;
361 if (!_XcursorWriteUInt (file, chunkHeader->type))
362 return XcursorFalse;
363 if (!_XcursorWriteUInt (file, chunkHeader->subtype))
364 return XcursorFalse;
365 if (!_XcursorWriteUInt (file, chunkHeader->version))
366 return XcursorFalse;
367 return XcursorTrue;
368 }
369
370 #define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
371
372 static XcursorDim
_XcursorFindBestSize(XcursorFileHeader * fileHeader,XcursorDim size,int * nsizesp)373 _XcursorFindBestSize (XcursorFileHeader *fileHeader,
374 XcursorDim size,
375 int *nsizesp)
376 {
377 int n;
378 int nsizes = 0;
379 XcursorDim bestSize = 0;
380 XcursorDim thisSize;
381
382 if (!fileHeader || !nsizesp)
383 return 0;
384
385 for (n = 0; n < fileHeader->ntoc; n++)
386 {
387 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
388 continue;
389 thisSize = fileHeader->tocs[n].subtype;
390 if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
391 {
392 bestSize = thisSize;
393 nsizes = 1;
394 }
395 else if (thisSize == bestSize)
396 nsizes++;
397 }
398 *nsizesp = nsizes;
399 return bestSize;
400 }
401
402 static int
_XcursorFindImageToc(XcursorFileHeader * fileHeader,XcursorDim size,int count)403 _XcursorFindImageToc (XcursorFileHeader *fileHeader,
404 XcursorDim size,
405 int count)
406 {
407 int toc;
408 XcursorDim thisSize;
409
410 if (!fileHeader)
411 return 0;
412
413 for (toc = 0; toc < fileHeader->ntoc; toc++)
414 {
415 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
416 continue;
417 thisSize = fileHeader->tocs[toc].subtype;
418 if (thisSize != size)
419 continue;
420 if (!count)
421 break;
422 count--;
423 }
424 if (toc == fileHeader->ntoc)
425 return -1;
426 return toc;
427 }
428
429 static XcursorImage *
_XcursorReadImage(XcursorFile * file,XcursorFileHeader * fileHeader,int toc)430 _XcursorReadImage (XcursorFile *file,
431 XcursorFileHeader *fileHeader,
432 int toc)
433 {
434 XcursorChunkHeader chunkHeader;
435 XcursorImage head;
436 XcursorImage *image;
437 int n;
438 XcursorPixel *p;
439
440 if (!file || !fileHeader)
441 return NULL;
442
443 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
444 return NULL;
445 if (!_XcursorReadUInt (file, &head.width))
446 return NULL;
447 if (!_XcursorReadUInt (file, &head.height))
448 return NULL;
449 if (!_XcursorReadUInt (file, &head.xhot))
450 return NULL;
451 if (!_XcursorReadUInt (file, &head.yhot))
452 return NULL;
453 if (!_XcursorReadUInt (file, &head.delay))
454 return NULL;
455 /* sanity check data */
456 if (head.width > XCURSOR_IMAGE_MAX_SIZE ||
457 head.height > XCURSOR_IMAGE_MAX_SIZE)
458 return NULL;
459 if (head.width == 0 || head.height == 0)
460 return NULL;
461 if (head.xhot > head.width || head.yhot > head.height)
462 return NULL;
463
464 /* Create the image and initialize it */
465 image = XcursorImageCreate (head.width, head.height);
466 if (image == NULL)
467 return NULL;
468 if (chunkHeader.version < image->version)
469 image->version = chunkHeader.version;
470 image->size = chunkHeader.subtype;
471 image->xhot = head.xhot;
472 image->yhot = head.yhot;
473 image->delay = head.delay;
474 n = image->width * image->height;
475 p = image->pixels;
476 while (n--)
477 {
478 if (!_XcursorReadUInt (file, p))
479 {
480 XcursorImageDestroy (image);
481 return NULL;
482 }
483 p++;
484 }
485 return image;
486 }
487
488 static XcursorUInt
_XcursorImageLength(XcursorImage * image)489 _XcursorImageLength (XcursorImage *image)
490 {
491 if (!image)
492 return 0;
493
494 return XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4;
495 }
496
497 static XcursorBool
_XcursorWriteImage(XcursorFile * file,XcursorFileHeader * fileHeader,int toc,XcursorImage * image)498 _XcursorWriteImage (XcursorFile *file,
499 XcursorFileHeader *fileHeader,
500 int toc,
501 XcursorImage *image)
502 {
503 XcursorChunkHeader chunkHeader;
504 int n;
505 XcursorPixel *p;
506
507 if (!file || !fileHeader || !image)
508 return XcursorFalse;
509
510 /* sanity check data */
511 if (image->width > XCURSOR_IMAGE_MAX_SIZE ||
512 image->height > XCURSOR_IMAGE_MAX_SIZE)
513 return XcursorFalse;
514 if (image->width == 0 || image->height == 0)
515 return XcursorFalse;
516 if (image->xhot > image->width || image->yhot > image->height)
517 return XcursorFalse;
518
519 /* write chunk header */
520 chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN;
521 chunkHeader.type = XCURSOR_IMAGE_TYPE;
522 chunkHeader.subtype = image->size;
523 chunkHeader.version = XCURSOR_IMAGE_VERSION;
524
525 if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader))
526 return XcursorFalse;
527
528 /* write extra image header fields */
529 if (!_XcursorWriteUInt (file, image->width))
530 return XcursorFalse;
531 if (!_XcursorWriteUInt (file, image->height))
532 return XcursorFalse;
533 if (!_XcursorWriteUInt (file, image->xhot))
534 return XcursorFalse;
535 if (!_XcursorWriteUInt (file, image->yhot))
536 return XcursorFalse;
537 if (!_XcursorWriteUInt (file, image->delay))
538 return XcursorFalse;
539
540 /* write the image */
541 n = image->width * image->height;
542 p = image->pixels;
543 while (n--)
544 {
545 if (!_XcursorWriteUInt (file, *p))
546 return XcursorFalse;
547 p++;
548 }
549 return XcursorTrue;
550 }
551
552 static XcursorComment *
_XcursorReadComment(XcursorFile * file,XcursorFileHeader * fileHeader,int toc)553 _XcursorReadComment (XcursorFile *file,
554 XcursorFileHeader *fileHeader,
555 int toc)
556 {
557 XcursorChunkHeader chunkHeader;
558 XcursorUInt length;
559 XcursorComment *comment;
560
561 if (!file || !fileHeader)
562 return NULL;
563
564 /* read chunk header */
565 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
566 return NULL;
567 /* read extra comment header fields */
568 if (!_XcursorReadUInt (file, &length))
569 return NULL;
570 comment = XcursorCommentCreate (chunkHeader.subtype, length);
571 if (!comment)
572 return NULL;
573 if (!_XcursorReadBytes (file, comment->comment, length))
574 {
575 XcursorCommentDestroy (comment);
576 return NULL;
577 }
578 comment->comment[length] = '\0';
579 return comment;
580 }
581
582 static XcursorUInt
_XcursorCommentLength(XcursorComment * comment)583 _XcursorCommentLength (XcursorComment *comment)
584 {
585 return XCURSOR_COMMENT_HEADER_LEN + strlen (comment->comment);
586 }
587
588 static XcursorBool
_XcursorWriteComment(XcursorFile * file,XcursorFileHeader * fileHeader,int toc,XcursorComment * comment)589 _XcursorWriteComment (XcursorFile *file,
590 XcursorFileHeader *fileHeader,
591 int toc,
592 XcursorComment *comment)
593 {
594 XcursorChunkHeader chunkHeader;
595 XcursorUInt length;
596
597 if (!file || !fileHeader || !comment || !comment->comment)
598 return XcursorFalse;
599
600 length = strlen (comment->comment);
601
602 /* sanity check data */
603 if (length > XCURSOR_COMMENT_MAX_LEN)
604 return XcursorFalse;
605
606 /* read chunk header */
607 chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN;
608 chunkHeader.type = XCURSOR_COMMENT_TYPE;
609 chunkHeader.subtype = comment->comment_type;
610 chunkHeader.version = XCURSOR_COMMENT_VERSION;
611
612 if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader))
613 return XcursorFalse;
614
615 /* write extra comment header fields */
616 if (!_XcursorWriteUInt (file, length))
617 return XcursorFalse;
618
619 if (!_XcursorWriteBytes (file, comment->comment, length))
620 return XcursorFalse;
621 return XcursorTrue;
622 }
623
624 XcursorImage *
XcursorXcFileLoadImage(XcursorFile * file,int size)625 XcursorXcFileLoadImage (XcursorFile *file, int size)
626 {
627 XcursorFileHeader *fileHeader;
628 XcursorDim bestSize;
629 int nsize;
630 int toc;
631 XcursorImage *image;
632
633 if (size < 0)
634 return NULL;
635 fileHeader = _XcursorReadFileHeader (file);
636 if (!fileHeader)
637 return NULL;
638 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
639 if (!bestSize)
640 return NULL;
641 toc = _XcursorFindImageToc (fileHeader, bestSize, 0);
642 if (toc < 0)
643 return NULL;
644 image = _XcursorReadImage (file, fileHeader, toc);
645 _XcursorFileHeaderDestroy (fileHeader);
646 return image;
647 }
648
649 XcursorImages *
XcursorXcFileLoadImages(XcursorFile * file,int size)650 XcursorXcFileLoadImages (XcursorFile *file, int size)
651 {
652 XcursorFileHeader *fileHeader;
653 XcursorDim bestSize;
654 int nsize;
655 XcursorImages *images;
656 int n;
657 int toc;
658
659 if (!file || size < 0)
660 return NULL;
661 fileHeader = _XcursorReadFileHeader (file);
662 if (!fileHeader)
663 return NULL;
664 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
665 if (!bestSize)
666 {
667 _XcursorFileHeaderDestroy (fileHeader);
668 return NULL;
669 }
670 images = XcursorImagesCreate (nsize);
671 if (!images)
672 {
673 _XcursorFileHeaderDestroy (fileHeader);
674 return NULL;
675 }
676 for (n = 0; n < nsize; n++)
677 {
678 toc = _XcursorFindImageToc (fileHeader, bestSize, n);
679 if (toc < 0)
680 break;
681 images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
682 toc);
683 if (!images->images[images->nimage])
684 break;
685 images->nimage++;
686 }
687 _XcursorFileHeaderDestroy (fileHeader);
688 if (images->nimage != nsize)
689 {
690 XcursorImagesDestroy (images);
691 images = NULL;
692 }
693 return images;
694 }
695
696 XcursorImages *
XcursorXcFileLoadAllImages(XcursorFile * file)697 XcursorXcFileLoadAllImages (XcursorFile *file)
698 {
699 XcursorFileHeader *fileHeader;
700 XcursorImage *image;
701 XcursorImages *images;
702 int nimage;
703 int n;
704 int toc;
705
706 if (!file)
707 return NULL;
708
709 fileHeader = _XcursorReadFileHeader (file);
710 if (!fileHeader)
711 return NULL;
712 nimage = 0;
713 for (n = 0; n < fileHeader->ntoc; n++)
714 {
715 switch (fileHeader->tocs[n].type) {
716 case XCURSOR_IMAGE_TYPE:
717 nimage++;
718 break;
719 }
720 }
721 images = XcursorImagesCreate (nimage);
722 if (!images)
723 {
724 _XcursorFileHeaderDestroy (fileHeader);
725 return NULL;
726 }
727 for (toc = 0; toc < fileHeader->ntoc; toc++)
728 {
729 switch (fileHeader->tocs[toc].type) {
730 case XCURSOR_IMAGE_TYPE:
731 image = _XcursorReadImage (file, fileHeader, toc);
732 if (image)
733 {
734 images->images[images->nimage] = image;
735 images->nimage++;
736 }
737 break;
738 }
739 }
740 _XcursorFileHeaderDestroy (fileHeader);
741 if (images->nimage != nimage)
742 {
743 XcursorImagesDestroy (images);
744 images = NULL;
745 }
746 return images;
747 }
748
749 XcursorBool
XcursorXcFileLoad(XcursorFile * file,XcursorComments ** commentsp,XcursorImages ** imagesp)750 XcursorXcFileLoad (XcursorFile *file,
751 XcursorComments **commentsp,
752 XcursorImages **imagesp)
753 {
754 XcursorFileHeader *fileHeader;
755 int nimage;
756 int ncomment;
757 XcursorImages *images;
758 XcursorImage *image;
759 XcursorComment *comment;
760 XcursorComments *comments;
761 int toc;
762
763 if (!file)
764 return 0;
765 fileHeader = _XcursorReadFileHeader (file);
766 if (!fileHeader)
767 return 0;
768 nimage = 0;
769 ncomment = 0;
770 for (toc = 0; toc < fileHeader->ntoc; toc++)
771 {
772 switch (fileHeader->tocs[toc].type) {
773 case XCURSOR_COMMENT_TYPE:
774 ncomment++;
775 break;
776 case XCURSOR_IMAGE_TYPE:
777 nimage++;
778 break;
779 }
780 }
781 images = XcursorImagesCreate (nimage);
782 if (!images)
783 return 0;
784 comments = XcursorCommentsCreate (ncomment);
785 if (!comments)
786 {
787 XcursorImagesDestroy (images);
788 return 0;
789 }
790 for (toc = 0; toc < fileHeader->ntoc; toc++)
791 {
792 switch (fileHeader->tocs[toc].type) {
793 case XCURSOR_COMMENT_TYPE:
794 comment = _XcursorReadComment (file, fileHeader, toc);
795 if (comment)
796 {
797 comments->comments[comments->ncomment] = comment;
798 comments->ncomment++;
799 }
800 break;
801 case XCURSOR_IMAGE_TYPE:
802 image = _XcursorReadImage (file, fileHeader, toc);
803 if (image)
804 {
805 images->images[images->nimage] = image;
806 images->nimage++;
807 }
808 break;
809 }
810 }
811 _XcursorFileHeaderDestroy (fileHeader);
812 if (images->nimage != nimage || comments->ncomment != ncomment)
813 {
814 XcursorImagesDestroy (images);
815 XcursorCommentsDestroy (comments);
816 images = NULL;
817 comments = NULL;
818 return XcursorFalse;
819 }
820 *imagesp = images;
821 *commentsp = comments;
822 return XcursorTrue;
823 }
824
825 XcursorBool
XcursorXcFileSave(XcursorFile * file,const XcursorComments * comments,const XcursorImages * images)826 XcursorXcFileSave (XcursorFile *file,
827 const XcursorComments *comments,
828 const XcursorImages *images)
829 {
830 XcursorFileHeader *fileHeader;
831 XcursorUInt position;
832 int n;
833 int toc;
834
835 if (!file || !comments || !images)
836 return XcursorFalse;
837
838 fileHeader = _XcursorFileHeaderCreate (comments->ncomment + images->nimage);
839 if (!fileHeader)
840 return XcursorFalse;
841
842 position = _XcursorFileHeaderLength (fileHeader);
843
844 /*
845 * Compute the toc. Place the images before the comments
846 * as they're more often read
847 */
848
849 toc = 0;
850 for (n = 0; n < images->nimage; n++)
851 {
852 fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE;
853 fileHeader->tocs[toc].subtype = images->images[n]->size;
854 fileHeader->tocs[toc].position = position;
855 position += _XcursorImageLength (images->images[n]);
856 toc++;
857 }
858
859 for (n = 0; n < comments->ncomment; n++)
860 {
861 fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE;
862 fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type;
863 fileHeader->tocs[toc].position = position;
864 position += _XcursorCommentLength (comments->comments[n]);
865 toc++;
866 }
867
868 /*
869 * Write the header and the toc
870 */
871 if (!_XcursorWriteFileHeader (file, fileHeader))
872 goto bail;
873
874 /*
875 * Write the images
876 */
877 toc = 0;
878 for (n = 0; n < images->nimage; n++)
879 {
880 if (!_XcursorWriteImage (file, fileHeader, toc, images->images[n]))
881 goto bail;
882 toc++;
883 }
884
885 /*
886 * Write the comments
887 */
888 for (n = 0; n < comments->ncomment; n++)
889 {
890 if (!_XcursorWriteComment (file, fileHeader, toc, comments->comments[n]))
891 goto bail;
892 toc++;
893 }
894
895 _XcursorFileHeaderDestroy (fileHeader);
896 return XcursorTrue;
897 bail:
898 _XcursorFileHeaderDestroy (fileHeader);
899 return XcursorFalse;
900 }
901
902 static int
_XcursorStdioFileRead(XcursorFile * file,unsigned char * buf,int len)903 _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
904 {
905 FILE *f = file->closure;
906 return fread (buf, 1, len, f);
907 }
908
909 static int
_XcursorStdioFileWrite(XcursorFile * file,unsigned char * buf,int len)910 _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
911 {
912 FILE *f = file->closure;
913 return fwrite (buf, 1, len, f);
914 }
915
916 static int
_XcursorStdioFileSeek(XcursorFile * file,long offset,int whence)917 _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
918 {
919 FILE *f = file->closure;
920 return fseek (f, offset, whence);
921 }
922
923 static void
_XcursorStdioFileInitialize(FILE * stdfile,XcursorFile * file)924 _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
925 {
926 file->closure = stdfile;
927 file->read = _XcursorStdioFileRead;
928 file->write = _XcursorStdioFileWrite;
929 file->seek = _XcursorStdioFileSeek;
930 }
931
932 XcursorImage *
XcursorFileLoadImage(FILE * file,int size)933 XcursorFileLoadImage (FILE *file, int size)
934 {
935 XcursorFile f;
936
937 if (!file)
938 return NULL;
939
940 _XcursorStdioFileInitialize (file, &f);
941 return XcursorXcFileLoadImage (&f, size);
942 }
943
944 XcursorImages *
XcursorFileLoadImages(FILE * file,int size)945 XcursorFileLoadImages (FILE *file, int size)
946 {
947 XcursorFile f;
948
949 if (!file)
950 return NULL;
951
952 _XcursorStdioFileInitialize (file, &f);
953 return XcursorXcFileLoadImages (&f, size);
954 }
955
956 XcursorImages *
XcursorFileLoadAllImages(FILE * file)957 XcursorFileLoadAllImages (FILE *file)
958 {
959 XcursorFile f;
960
961 if (!file)
962 return NULL;
963
964 _XcursorStdioFileInitialize (file, &f);
965 return XcursorXcFileLoadAllImages (&f);
966 }
967
968 XcursorBool
XcursorFileLoad(FILE * file,XcursorComments ** commentsp,XcursorImages ** imagesp)969 XcursorFileLoad (FILE *file,
970 XcursorComments **commentsp,
971 XcursorImages **imagesp)
972 {
973 XcursorFile f;
974
975 if (!file || !commentsp || !imagesp)
976 return XcursorFalse;
977
978 _XcursorStdioFileInitialize (file, &f);
979 return XcursorXcFileLoad (&f, commentsp, imagesp);
980 }
981
982 XcursorBool
XcursorFileSaveImages(FILE * file,const XcursorImages * images)983 XcursorFileSaveImages (FILE *file, const XcursorImages *images)
984 {
985 XcursorComments *comments;
986 XcursorFile f;
987 XcursorBool ret;
988
989 if (!file || !images)
990 return 0;
991 if ((comments = XcursorCommentsCreate (0)) == NULL)
992 return 0;
993 _XcursorStdioFileInitialize (file, &f);
994 ret = XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
995 XcursorCommentsDestroy (comments);
996 return ret;
997 }
998
999 XcursorBool
XcursorFileSave(FILE * file,const XcursorComments * comments,const XcursorImages * images)1000 XcursorFileSave (FILE * file,
1001 const XcursorComments *comments,
1002 const XcursorImages *images)
1003 {
1004 XcursorFile f;
1005
1006 if (!file || !comments || !images)
1007 return XcursorFalse;
1008
1009 _XcursorStdioFileInitialize (file, &f);
1010 return XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
1011 }
1012
1013 XcursorImage *
XcursorFilenameLoadImage(const char * file,int size)1014 XcursorFilenameLoadImage (const char *file, int size)
1015 {
1016 FILE *f;
1017 XcursorImage *image;
1018
1019 if (!file || size < 0)
1020 return NULL;
1021
1022 f = fopen (file, "r");
1023 if (!f)
1024 return NULL;
1025 image = XcursorFileLoadImage (f, size);
1026 fclose (f);
1027 return image;
1028 }
1029
1030 XcursorImages *
XcursorFilenameLoadImages(const char * file,int size)1031 XcursorFilenameLoadImages (const char *file, int size)
1032 {
1033 FILE *f;
1034 XcursorImages *images;
1035
1036 if (!file || size < 0)
1037 return NULL;
1038
1039 f = fopen (file, "r");
1040 if (!f)
1041 return NULL;
1042 images = XcursorFileLoadImages (f, size);
1043 fclose (f);
1044 return images;
1045 }
1046
1047 XcursorImages *
XcursorFilenameLoadAllImages(const char * file)1048 XcursorFilenameLoadAllImages (const char *file)
1049 {
1050 FILE *f;
1051 XcursorImages *images;
1052
1053 if (!file)
1054 return NULL;
1055
1056 f = fopen (file, "r");
1057 if (!f)
1058 return NULL;
1059 images = XcursorFileLoadAllImages (f);
1060 fclose (f);
1061 return images;
1062 }
1063
1064 XcursorBool
XcursorFilenameLoad(const char * file,XcursorComments ** commentsp,XcursorImages ** imagesp)1065 XcursorFilenameLoad (const char *file,
1066 XcursorComments **commentsp,
1067 XcursorImages **imagesp)
1068 {
1069 FILE *f;
1070 XcursorBool ret;
1071
1072 if (!file)
1073 return XcursorFalse;
1074
1075 f = fopen (file, "r");
1076 if (!f)
1077 return 0;
1078 ret = XcursorFileLoad (f, commentsp, imagesp);
1079 fclose (f);
1080 return ret;
1081 }
1082
1083 XcursorBool
XcursorFilenameSaveImages(const char * file,const XcursorImages * images)1084 XcursorFilenameSaveImages (const char *file, const XcursorImages *images)
1085 {
1086 FILE *f;
1087 XcursorBool ret;
1088
1089 if (!file || !images)
1090 return XcursorFalse;
1091
1092 f = fopen (file, "w");
1093 if (!f)
1094 return 0;
1095 ret = XcursorFileSaveImages (f, images);
1096 return fclose (f) != EOF && ret;
1097 }
1098
1099 XcursorBool
XcursorFilenameSave(const char * file,const XcursorComments * comments,const XcursorImages * images)1100 XcursorFilenameSave (const char *file,
1101 const XcursorComments *comments,
1102 const XcursorImages *images)
1103 {
1104 FILE *f;
1105 XcursorBool ret;
1106
1107 if (!file || !comments || !images)
1108 return XcursorFalse;
1109
1110 f = fopen (file, "w");
1111 if (!f)
1112 return 0;
1113 ret = XcursorFileSave (f, comments, images);
1114 return fclose (f) != EOF && ret;
1115 }
1116