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