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