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