file.c revision e6d5e4e0
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 (XcursorUInt 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    {
717	_XcursorFileHeaderDestroy (fileHeader);
718	return NULL;
719    }
720    for (toc = 0; toc < fileHeader->ntoc; toc++)
721    {
722	switch (fileHeader->tocs[toc].type) {
723	case XCURSOR_IMAGE_TYPE:
724	    image = _XcursorReadImage (file, fileHeader, toc);
725	    if (image)
726	    {
727		images->images[images->nimage] = image;
728		images->nimage++;
729	    }
730	    break;
731	}
732    }
733    _XcursorFileHeaderDestroy (fileHeader);
734    if (images->nimage != nimage)
735    {
736	XcursorImagesDestroy (images);
737	images = NULL;
738    }
739    return images;
740}
741
742XcursorBool
743XcursorXcFileLoad (XcursorFile	    *file,
744		   XcursorComments  **commentsp,
745		   XcursorImages    **imagesp)
746{
747    XcursorFileHeader	*fileHeader;
748    int			nimage;
749    int			ncomment;
750    XcursorImages	*images;
751    XcursorImage	*image;
752    XcursorComment	*comment;
753    XcursorComments	*comments;
754    int			toc;
755
756    if (!file)
757        return 0;
758    fileHeader = _XcursorReadFileHeader (file);
759    if (!fileHeader)
760	return 0;
761    nimage = 0;
762    ncomment = 0;
763    for (toc = 0; toc < fileHeader->ntoc; toc++)
764    {
765	switch (fileHeader->tocs[toc].type) {
766	case XCURSOR_COMMENT_TYPE:
767	    ncomment++;
768	    break;
769	case XCURSOR_IMAGE_TYPE:
770	    nimage++;
771	    break;
772	}
773    }
774    images = XcursorImagesCreate (nimage);
775    if (!images)
776	return 0;
777    comments = XcursorCommentsCreate (ncomment);
778    if (!comments)
779    {
780	XcursorImagesDestroy (images);
781	return 0;
782    }
783    for (toc = 0; toc < fileHeader->ntoc; toc++)
784    {
785	switch (fileHeader->tocs[toc].type) {
786	case XCURSOR_COMMENT_TYPE:
787	    comment = _XcursorReadComment (file, fileHeader, toc);
788	    if (comment)
789	    {
790		comments->comments[comments->ncomment] = comment;
791		comments->ncomment++;
792	    }
793	    break;
794	case XCURSOR_IMAGE_TYPE:
795	    image = _XcursorReadImage (file, fileHeader, toc);
796	    if (image)
797	    {
798		images->images[images->nimage] = image;
799		images->nimage++;
800	    }
801	    break;
802	}
803    }
804    _XcursorFileHeaderDestroy (fileHeader);
805    if (images->nimage != nimage || comments->ncomment != ncomment)
806    {
807	XcursorImagesDestroy (images);
808	XcursorCommentsDestroy (comments);
809	images = NULL;
810	comments = NULL;
811	return XcursorFalse;
812    }
813    *imagesp = images;
814    *commentsp = comments;
815    return XcursorTrue;
816}
817
818XcursorBool
819XcursorXcFileSave (XcursorFile		    *file,
820		   const XcursorComments    *comments,
821		   const XcursorImages	    *images)
822{
823    XcursorFileHeader	*fileHeader;
824    XcursorUInt		position;
825    int			n;
826    int			toc;
827
828    if (!file || !comments || !images)
829        return XcursorFalse;
830
831    fileHeader = _XcursorFileHeaderCreate (comments->ncomment + images->nimage);
832    if (!fileHeader)
833	return XcursorFalse;
834
835    position = _XcursorFileHeaderLength (fileHeader);
836
837    /*
838     * Compute the toc.  Place the images before the comments
839     * as they're more often read
840     */
841
842    toc = 0;
843    for (n = 0; n < images->nimage; n++)
844    {
845	fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE;
846	fileHeader->tocs[toc].subtype = images->images[n]->size;
847	fileHeader->tocs[toc].position = position;
848	position += _XcursorImageLength (images->images[n]);
849	toc++;
850    }
851
852    for (n = 0; n < comments->ncomment; n++)
853    {
854	fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE;
855	fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type;
856	fileHeader->tocs[toc].position = position;
857	position += _XcursorCommentLength (comments->comments[n]);
858	toc++;
859    }
860
861    /*
862     * Write the header and the toc
863     */
864    if (!_XcursorWriteFileHeader (file, fileHeader))
865	goto bail;
866
867    /*
868     * Write the images
869     */
870    toc = 0;
871    for (n = 0; n < images->nimage; n++)
872    {
873	if (!_XcursorWriteImage (file, fileHeader, toc, images->images[n]))
874	    goto bail;
875	toc++;
876    }
877
878    /*
879     * Write the comments
880     */
881    for (n = 0; n < comments->ncomment; n++)
882    {
883	if (!_XcursorWriteComment (file, fileHeader, toc, comments->comments[n]))
884	    goto bail;
885	toc++;
886    }
887
888    _XcursorFileHeaderDestroy (fileHeader);
889    return XcursorTrue;
890bail:
891    _XcursorFileHeaderDestroy (fileHeader);
892    return XcursorFalse;
893}
894
895static int
896_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
897{
898    FILE    *f = file->closure;
899    return fread (buf, 1, len, f);
900}
901
902static int
903_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
904{
905    FILE    *f = file->closure;
906    return fwrite (buf, 1, len, f);
907}
908
909static int
910_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
911{
912    FILE    *f = file->closure;
913    return fseek (f, offset, whence);
914}
915
916static void
917_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
918{
919    file->closure = stdfile;
920    file->read = _XcursorStdioFileRead;
921    file->write = _XcursorStdioFileWrite;
922    file->seek = _XcursorStdioFileSeek;
923}
924
925XcursorImage *
926XcursorFileLoadImage (FILE *file, int size)
927{
928    XcursorFile	f;
929
930    if (!file)
931        return NULL;
932
933    _XcursorStdioFileInitialize (file, &f);
934    return XcursorXcFileLoadImage (&f, size);
935}
936
937XcursorImages *
938XcursorFileLoadImages (FILE *file, int size)
939{
940    XcursorFile	f;
941
942    if (!file)
943        return NULL;
944
945    _XcursorStdioFileInitialize (file, &f);
946    return XcursorXcFileLoadImages (&f, size);
947}
948
949XcursorImages *
950XcursorFileLoadAllImages (FILE *file)
951{
952    XcursorFile	f;
953
954    if (!file)
955        return NULL;
956
957    _XcursorStdioFileInitialize (file, &f);
958    return XcursorXcFileLoadAllImages (&f);
959}
960
961XcursorBool
962XcursorFileLoad (FILE		    *file,
963		 XcursorComments    **commentsp,
964		 XcursorImages	    **imagesp)
965{
966    XcursorFile	f;
967
968    if (!file || !commentsp || !imagesp)
969        return XcursorFalse;
970
971    _XcursorStdioFileInitialize (file, &f);
972    return XcursorXcFileLoad (&f, commentsp, imagesp);
973}
974
975XcursorBool
976XcursorFileSaveImages (FILE *file, const XcursorImages *images)
977{
978    XcursorComments *comments;
979    XcursorFile	    f;
980    XcursorBool	    ret;
981
982    if (!file || !images)
983	return 0;
984    if ((comments = XcursorCommentsCreate (0)) == NULL)
985	return 0;
986    _XcursorStdioFileInitialize (file, &f);
987    ret = XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
988    XcursorCommentsDestroy (comments);
989    return ret;
990}
991
992XcursorBool
993XcursorFileSave (FILE *			file,
994		 const XcursorComments	*comments,
995		 const XcursorImages	*images)
996{
997    XcursorFile	    f;
998
999    if (!file || !comments || !images)
1000        return XcursorFalse;
1001
1002    _XcursorStdioFileInitialize (file, &f);
1003    return XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
1004}
1005
1006XcursorImage *
1007XcursorFilenameLoadImage (const char *file, int size)
1008{
1009    FILE	    *f;
1010    XcursorImage    *image;
1011
1012    if (!file || size < 0)
1013        return NULL;
1014
1015    f = fopen (file, "r");
1016    if (!f)
1017	return NULL;
1018    image = XcursorFileLoadImage (f, size);
1019    fclose (f);
1020    return image;
1021}
1022
1023XcursorImages *
1024XcursorFilenameLoadImages (const char *file, int size)
1025{
1026    FILE	    *f;
1027    XcursorImages   *images;
1028
1029    if (!file || size < 0)
1030        return NULL;
1031
1032    f = fopen (file, "r");
1033    if (!f)
1034	return NULL;
1035    images = XcursorFileLoadImages (f, size);
1036    fclose (f);
1037    return images;
1038}
1039
1040XcursorImages *
1041XcursorFilenameLoadAllImages (const char *file)
1042{
1043    FILE	    *f;
1044    XcursorImages   *images;
1045
1046    if (!file)
1047        return NULL;
1048
1049    f = fopen (file, "r");
1050    if (!f)
1051	return NULL;
1052    images = XcursorFileLoadAllImages (f);
1053    fclose (f);
1054    return images;
1055}
1056
1057XcursorBool
1058XcursorFilenameLoad (const char		*file,
1059		     XcursorComments	**commentsp,
1060		     XcursorImages	**imagesp)
1061{
1062    FILE	    *f;
1063    XcursorBool	    ret;
1064
1065    if (!file)
1066        return XcursorFalse;
1067
1068    f = fopen (file, "r");
1069    if (!f)
1070	return 0;
1071    ret = XcursorFileLoad (f, commentsp, imagesp);
1072    fclose (f);
1073    return ret;
1074}
1075
1076XcursorBool
1077XcursorFilenameSaveImages (const char *file, const XcursorImages *images)
1078{
1079    FILE	    *f;
1080    XcursorBool	    ret;
1081
1082    if (!file || !images)
1083        return XcursorFalse;
1084
1085    f = fopen (file, "w");
1086    if (!f)
1087	return 0;
1088    ret = XcursorFileSaveImages (f, images);
1089    return fclose (f) != EOF && ret;
1090}
1091
1092XcursorBool
1093XcursorFilenameSave (const char		    *file,
1094		     const XcursorComments  *comments,
1095		     const XcursorImages    *images)
1096{
1097    FILE	    *f;
1098    XcursorBool	    ret;
1099
1100    if (!file || !comments || !images)
1101        return XcursorFalse;
1102
1103    f = fopen (file, "w");
1104    if (!f)
1105	return 0;
1106    ret = XcursorFileSave (f, comments, images);
1107    return fclose (f) != EOF && ret;
1108}
1109