1116 lines
26 KiB
C
1116 lines
26 KiB
C
/*
|
|
* Copyright © 2002 Keith Packard
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation, and that the name of Keith Packard not be used in
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Keith Packard makes no
|
|
* representations about the suitability of this software for any purpose. It
|
|
* is provided "as is" without express or implied warranty.
|
|
*
|
|
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "xcursorint.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
XcursorImage *
|
|
XcursorImageCreate (int width, int height)
|
|
{
|
|
XcursorImage *image;
|
|
|
|
if (width < 0 || height < 0)
|
|
return NULL;
|
|
if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE)
|
|
return NULL;
|
|
|
|
image = malloc (sizeof (XcursorImage) +
|
|
width * height * sizeof (XcursorPixel));
|
|
if (!image)
|
|
return NULL;
|
|
image->version = XCURSOR_IMAGE_VERSION;
|
|
image->pixels = (XcursorPixel *) (image + 1);
|
|
image->size = width > height ? width : height;
|
|
image->width = width;
|
|
image->height = height;
|
|
image->delay = 0;
|
|
return image;
|
|
}
|
|
|
|
void
|
|
XcursorImageDestroy (XcursorImage *image)
|
|
{
|
|
free (image);
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorImagesCreate (int size)
|
|
{
|
|
XcursorImages *images;
|
|
|
|
images = malloc (sizeof (XcursorImages) +
|
|
size * sizeof (XcursorImage *));
|
|
if (!images)
|
|
return NULL;
|
|
images->nimage = 0;
|
|
images->images = (XcursorImage **) (images + 1);
|
|
images->name = NULL;
|
|
return images;
|
|
}
|
|
|
|
void
|
|
XcursorImagesDestroy (XcursorImages *images)
|
|
{
|
|
int n;
|
|
|
|
if (!images)
|
|
return;
|
|
|
|
for (n = 0; n < images->nimage; n++)
|
|
XcursorImageDestroy (images->images[n]);
|
|
if (images->name)
|
|
free (images->name);
|
|
free (images);
|
|
}
|
|
|
|
void
|
|
XcursorImagesSetName (XcursorImages *images, const char *name)
|
|
{
|
|
char *new;
|
|
|
|
if (!images || !name)
|
|
return;
|
|
|
|
new = strdup (name);
|
|
|
|
if (!new)
|
|
return;
|
|
|
|
if (images->name)
|
|
free (images->name);
|
|
images->name = new;
|
|
}
|
|
|
|
XcursorComment *
|
|
XcursorCommentCreate (XcursorUInt comment_type, int length)
|
|
{
|
|
XcursorComment *comment;
|
|
|
|
if (length < 0 || length > XCURSOR_COMMENT_MAX_LEN)
|
|
return NULL;
|
|
|
|
comment = malloc (sizeof (XcursorComment) + length + 1);
|
|
if (!comment)
|
|
return NULL;
|
|
comment->version = XCURSOR_COMMENT_VERSION;
|
|
comment->comment_type = comment_type;
|
|
comment->comment = (char *) (comment + 1);
|
|
comment->comment[0] = '\0';
|
|
return comment;
|
|
}
|
|
|
|
void
|
|
XcursorCommentDestroy (XcursorComment *comment)
|
|
{
|
|
free (comment);
|
|
}
|
|
|
|
XcursorComments *
|
|
XcursorCommentsCreate (int size)
|
|
{
|
|
XcursorComments *comments;
|
|
|
|
comments = malloc (sizeof (XcursorComments) +
|
|
size * sizeof (XcursorComment *));
|
|
if (!comments)
|
|
return NULL;
|
|
comments->ncomment = 0;
|
|
comments->comments = (XcursorComment **) (comments + 1);
|
|
return comments;
|
|
}
|
|
|
|
void
|
|
XcursorCommentsDestroy (XcursorComments *comments)
|
|
{
|
|
int n;
|
|
|
|
if (!comments)
|
|
return;
|
|
|
|
for (n = 0; n < comments->ncomment; n++)
|
|
XcursorCommentDestroy (comments->comments[n]);
|
|
free (comments);
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
|
|
{
|
|
unsigned char bytes[4];
|
|
|
|
if (!file || !u)
|
|
return XcursorFalse;
|
|
|
|
if ((*file->read) (file, bytes, 4) != 4)
|
|
return XcursorFalse;
|
|
*u = ((bytes[0] << 0) |
|
|
(bytes[1] << 8) |
|
|
(bytes[2] << 16) |
|
|
(bytes[3] << 24));
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorReadBytes (XcursorFile *file, char *bytes, int length)
|
|
{
|
|
if (!file || !bytes || (*file->read) (file, (unsigned char *) bytes, length) != length)
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorWriteUInt (XcursorFile *file, XcursorUInt u)
|
|
{
|
|
unsigned char bytes[4];
|
|
|
|
if (!file)
|
|
return XcursorFalse;
|
|
|
|
bytes[0] = u;
|
|
bytes[1] = u >> 8;
|
|
bytes[2] = u >> 16;
|
|
bytes[3] = u >> 24;
|
|
if ((*file->write) (file, bytes, 4) != 4)
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorWriteBytes (XcursorFile *file, char *bytes, int length)
|
|
{
|
|
if (!file || !bytes || (*file->write) (file, (unsigned char *) bytes, length) != length)
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static void
|
|
_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
|
|
{
|
|
free (fileHeader);
|
|
}
|
|
|
|
static XcursorFileHeader *
|
|
_XcursorFileHeaderCreate (XcursorUInt ntoc)
|
|
{
|
|
XcursorFileHeader *fileHeader;
|
|
|
|
if (ntoc > 0x10000)
|
|
return NULL;
|
|
fileHeader = malloc (sizeof (XcursorFileHeader) +
|
|
ntoc * sizeof (XcursorFileToc));
|
|
if (!fileHeader)
|
|
return NULL;
|
|
fileHeader->magic = XCURSOR_MAGIC;
|
|
fileHeader->header = XCURSOR_FILE_HEADER_LEN;
|
|
fileHeader->version = XCURSOR_FILE_VERSION;
|
|
fileHeader->ntoc = ntoc;
|
|
fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
|
|
return fileHeader;
|
|
}
|
|
|
|
static XcursorFileHeader *
|
|
_XcursorReadFileHeader (XcursorFile *file)
|
|
{
|
|
XcursorFileHeader head, *fileHeader;
|
|
XcursorUInt skip;
|
|
int n;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
if (!_XcursorReadUInt (file, &head.magic))
|
|
return NULL;
|
|
if (head.magic != XCURSOR_MAGIC)
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.header))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.version))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.ntoc))
|
|
return NULL;
|
|
skip = head.header - XCURSOR_FILE_HEADER_LEN;
|
|
if (skip)
|
|
if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
|
|
return NULL;
|
|
fileHeader = _XcursorFileHeaderCreate (head.ntoc);
|
|
if (!fileHeader)
|
|
return NULL;
|
|
fileHeader->magic = head.magic;
|
|
fileHeader->header = head.header;
|
|
fileHeader->version = head.version;
|
|
fileHeader->ntoc = head.ntoc;
|
|
for (n = 0; n < fileHeader->ntoc; n++)
|
|
{
|
|
if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
|
|
break;
|
|
if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
|
|
break;
|
|
if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
|
|
break;
|
|
}
|
|
if (n != fileHeader->ntoc)
|
|
{
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return NULL;
|
|
}
|
|
return fileHeader;
|
|
}
|
|
|
|
static XcursorUInt
|
|
_XcursorFileHeaderLength (XcursorFileHeader *fileHeader)
|
|
{
|
|
return (XCURSOR_FILE_HEADER_LEN +
|
|
fileHeader->ntoc * XCURSOR_FILE_TOC_LEN);
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorWriteFileHeader (XcursorFile *file, XcursorFileHeader *fileHeader)
|
|
{
|
|
int toc;
|
|
|
|
if (!file || !fileHeader)
|
|
return XcursorFalse;
|
|
|
|
if (!_XcursorWriteUInt (file, fileHeader->magic))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, fileHeader->header))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, fileHeader->version))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, fileHeader->ntoc))
|
|
return XcursorFalse;
|
|
for (toc = 0; toc < fileHeader->ntoc; toc++)
|
|
{
|
|
if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].type))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].subtype))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].position))
|
|
return XcursorFalse;
|
|
}
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorSeekToToc (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc)
|
|
{
|
|
if (!file || !fileHeader || \
|
|
(*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorFileReadChunkHeader (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc,
|
|
XcursorChunkHeader *chunkHeader)
|
|
{
|
|
if (!file || !fileHeader || !chunkHeader)
|
|
return XcursorFalse;
|
|
if (!_XcursorSeekToToc (file, fileHeader, toc))
|
|
return XcursorFalse;
|
|
if (!_XcursorReadUInt (file, &chunkHeader->header))
|
|
return XcursorFalse;
|
|
if (!_XcursorReadUInt (file, &chunkHeader->type))
|
|
return XcursorFalse;
|
|
if (!_XcursorReadUInt (file, &chunkHeader->subtype))
|
|
return XcursorFalse;
|
|
if (!_XcursorReadUInt (file, &chunkHeader->version))
|
|
return XcursorFalse;
|
|
/* sanity check */
|
|
if (chunkHeader->type != fileHeader->tocs[toc].type ||
|
|
chunkHeader->subtype != fileHeader->tocs[toc].subtype)
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorFileWriteChunkHeader (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc,
|
|
XcursorChunkHeader *chunkHeader)
|
|
{
|
|
if (!file || !fileHeader || !chunkHeader)
|
|
return XcursorFalse;
|
|
if (!_XcursorSeekToToc (file, fileHeader, toc))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, chunkHeader->header))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, chunkHeader->type))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, chunkHeader->subtype))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, chunkHeader->version))
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
|
|
|
|
static XcursorDim
|
|
_XcursorFindBestSize (XcursorFileHeader *fileHeader,
|
|
XcursorDim size,
|
|
int *nsizesp)
|
|
{
|
|
int n;
|
|
int nsizes = 0;
|
|
XcursorDim bestSize = 0;
|
|
XcursorDim thisSize;
|
|
|
|
if (!fileHeader || !nsizesp)
|
|
return 0;
|
|
|
|
for (n = 0; n < fileHeader->ntoc; n++)
|
|
{
|
|
if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
|
|
continue;
|
|
thisSize = fileHeader->tocs[n].subtype;
|
|
if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
|
|
{
|
|
bestSize = thisSize;
|
|
nsizes = 1;
|
|
}
|
|
else if (thisSize == bestSize)
|
|
nsizes++;
|
|
}
|
|
*nsizesp = nsizes;
|
|
return bestSize;
|
|
}
|
|
|
|
static int
|
|
_XcursorFindImageToc (XcursorFileHeader *fileHeader,
|
|
XcursorDim size,
|
|
int count)
|
|
{
|
|
int toc;
|
|
XcursorDim thisSize;
|
|
|
|
if (!fileHeader)
|
|
return 0;
|
|
|
|
for (toc = 0; toc < fileHeader->ntoc; toc++)
|
|
{
|
|
if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
|
|
continue;
|
|
thisSize = fileHeader->tocs[toc].subtype;
|
|
if (thisSize != size)
|
|
continue;
|
|
if (!count)
|
|
break;
|
|
count--;
|
|
}
|
|
if (toc == fileHeader->ntoc)
|
|
return -1;
|
|
return toc;
|
|
}
|
|
|
|
static XcursorImage *
|
|
_XcursorReadImage (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc)
|
|
{
|
|
XcursorChunkHeader chunkHeader;
|
|
XcursorImage head;
|
|
XcursorImage *image;
|
|
int n;
|
|
XcursorPixel *p;
|
|
|
|
if (!file || !fileHeader)
|
|
return NULL;
|
|
|
|
if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.width))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.height))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.xhot))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.yhot))
|
|
return NULL;
|
|
if (!_XcursorReadUInt (file, &head.delay))
|
|
return NULL;
|
|
/* sanity check data */
|
|
if (head.width > XCURSOR_IMAGE_MAX_SIZE ||
|
|
head.height > XCURSOR_IMAGE_MAX_SIZE)
|
|
return NULL;
|
|
if (head.width == 0 || head.height == 0)
|
|
return NULL;
|
|
if (head.xhot > head.width || head.yhot > head.height)
|
|
return NULL;
|
|
|
|
/* Create the image and initialize it */
|
|
image = XcursorImageCreate (head.width, head.height);
|
|
if (image == NULL)
|
|
return NULL;
|
|
if (chunkHeader.version < image->version)
|
|
image->version = chunkHeader.version;
|
|
image->size = chunkHeader.subtype;
|
|
image->xhot = head.xhot;
|
|
image->yhot = head.yhot;
|
|
image->delay = head.delay;
|
|
n = image->width * image->height;
|
|
p = image->pixels;
|
|
while (n--)
|
|
{
|
|
if (!_XcursorReadUInt (file, p))
|
|
{
|
|
XcursorImageDestroy (image);
|
|
return NULL;
|
|
}
|
|
p++;
|
|
}
|
|
return image;
|
|
}
|
|
|
|
static XcursorUInt
|
|
_XcursorImageLength (XcursorImage *image)
|
|
{
|
|
if (!image)
|
|
return 0;
|
|
|
|
return XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4;
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorWriteImage (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc,
|
|
XcursorImage *image)
|
|
{
|
|
XcursorChunkHeader chunkHeader;
|
|
int n;
|
|
XcursorPixel *p;
|
|
|
|
if (!file || !fileHeader || !image)
|
|
return XcursorFalse;
|
|
|
|
/* sanity check data */
|
|
if (image->width > XCURSOR_IMAGE_MAX_SIZE ||
|
|
image->height > XCURSOR_IMAGE_MAX_SIZE)
|
|
return XcursorFalse;
|
|
if (image->width == 0 || image->height == 0)
|
|
return XcursorFalse;
|
|
if (image->xhot > image->width || image->yhot > image->height)
|
|
return XcursorFalse;
|
|
|
|
/* write chunk header */
|
|
chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN;
|
|
chunkHeader.type = XCURSOR_IMAGE_TYPE;
|
|
chunkHeader.subtype = image->size;
|
|
chunkHeader.version = XCURSOR_IMAGE_VERSION;
|
|
|
|
if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader))
|
|
return XcursorFalse;
|
|
|
|
/* write extra image header fields */
|
|
if (!_XcursorWriteUInt (file, image->width))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, image->height))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, image->xhot))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, image->yhot))
|
|
return XcursorFalse;
|
|
if (!_XcursorWriteUInt (file, image->delay))
|
|
return XcursorFalse;
|
|
|
|
/* write the image */
|
|
n = image->width * image->height;
|
|
p = image->pixels;
|
|
while (n--)
|
|
{
|
|
if (!_XcursorWriteUInt (file, *p))
|
|
return XcursorFalse;
|
|
p++;
|
|
}
|
|
return XcursorTrue;
|
|
}
|
|
|
|
static XcursorComment *
|
|
_XcursorReadComment (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc)
|
|
{
|
|
XcursorChunkHeader chunkHeader;
|
|
XcursorUInt length;
|
|
XcursorComment *comment;
|
|
|
|
if (!file || !fileHeader)
|
|
return NULL;
|
|
|
|
/* read chunk header */
|
|
if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
|
|
return NULL;
|
|
/* read extra comment header fields */
|
|
if (!_XcursorReadUInt (file, &length))
|
|
return NULL;
|
|
comment = XcursorCommentCreate (chunkHeader.subtype, length);
|
|
if (!comment)
|
|
return NULL;
|
|
if (!_XcursorReadBytes (file, comment->comment, length))
|
|
{
|
|
XcursorCommentDestroy (comment);
|
|
return NULL;
|
|
}
|
|
comment->comment[length] = '\0';
|
|
return comment;
|
|
}
|
|
|
|
static XcursorUInt
|
|
_XcursorCommentLength (XcursorComment *comment)
|
|
{
|
|
return XCURSOR_COMMENT_HEADER_LEN + strlen (comment->comment);
|
|
}
|
|
|
|
static XcursorBool
|
|
_XcursorWriteComment (XcursorFile *file,
|
|
XcursorFileHeader *fileHeader,
|
|
int toc,
|
|
XcursorComment *comment)
|
|
{
|
|
XcursorChunkHeader chunkHeader;
|
|
XcursorUInt length;
|
|
|
|
if (!file || !fileHeader || !comment || !comment->comment)
|
|
return XcursorFalse;
|
|
|
|
length = strlen (comment->comment);
|
|
|
|
/* sanity check data */
|
|
if (length > XCURSOR_COMMENT_MAX_LEN)
|
|
return XcursorFalse;
|
|
|
|
/* read chunk header */
|
|
chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN;
|
|
chunkHeader.type = XCURSOR_COMMENT_TYPE;
|
|
chunkHeader.subtype = comment->comment_type;
|
|
chunkHeader.version = XCURSOR_COMMENT_VERSION;
|
|
|
|
if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader))
|
|
return XcursorFalse;
|
|
|
|
/* write extra comment header fields */
|
|
if (!_XcursorWriteUInt (file, length))
|
|
return XcursorFalse;
|
|
|
|
if (!_XcursorWriteBytes (file, comment->comment, length))
|
|
return XcursorFalse;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
XcursorImage *
|
|
XcursorXcFileLoadImage (XcursorFile *file, int size)
|
|
{
|
|
XcursorFileHeader *fileHeader;
|
|
XcursorDim bestSize;
|
|
int nsize;
|
|
int toc;
|
|
XcursorImage *image;
|
|
|
|
if (size < 0)
|
|
return NULL;
|
|
fileHeader = _XcursorReadFileHeader (file);
|
|
if (!fileHeader)
|
|
return NULL;
|
|
bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
|
|
if (!bestSize)
|
|
return NULL;
|
|
toc = _XcursorFindImageToc (fileHeader, bestSize, 0);
|
|
if (toc < 0)
|
|
return NULL;
|
|
image = _XcursorReadImage (file, fileHeader, toc);
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return image;
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorXcFileLoadImages (XcursorFile *file, int size)
|
|
{
|
|
XcursorFileHeader *fileHeader;
|
|
XcursorDim bestSize;
|
|
int nsize;
|
|
XcursorImages *images;
|
|
int n;
|
|
int toc;
|
|
|
|
if (!file || size < 0)
|
|
return NULL;
|
|
fileHeader = _XcursorReadFileHeader (file);
|
|
if (!fileHeader)
|
|
return NULL;
|
|
bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
|
|
if (!bestSize)
|
|
{
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return NULL;
|
|
}
|
|
images = XcursorImagesCreate (nsize);
|
|
if (!images)
|
|
{
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return NULL;
|
|
}
|
|
for (n = 0; n < nsize; n++)
|
|
{
|
|
toc = _XcursorFindImageToc (fileHeader, bestSize, n);
|
|
if (toc < 0)
|
|
break;
|
|
images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
|
|
toc);
|
|
if (!images->images[images->nimage])
|
|
break;
|
|
images->nimage++;
|
|
}
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
if (images->nimage != nsize)
|
|
{
|
|
XcursorImagesDestroy (images);
|
|
images = NULL;
|
|
}
|
|
return images;
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorXcFileLoadAllImages (XcursorFile *file)
|
|
{
|
|
XcursorFileHeader *fileHeader;
|
|
XcursorImage *image;
|
|
XcursorImages *images;
|
|
int nimage;
|
|
int n;
|
|
int toc;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
fileHeader = _XcursorReadFileHeader (file);
|
|
if (!fileHeader)
|
|
return NULL;
|
|
nimage = 0;
|
|
for (n = 0; n < fileHeader->ntoc; n++)
|
|
{
|
|
switch (fileHeader->tocs[n].type) {
|
|
case XCURSOR_IMAGE_TYPE:
|
|
nimage++;
|
|
break;
|
|
}
|
|
}
|
|
images = XcursorImagesCreate (nimage);
|
|
if (!images)
|
|
{
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return NULL;
|
|
}
|
|
for (toc = 0; toc < fileHeader->ntoc; toc++)
|
|
{
|
|
switch (fileHeader->tocs[toc].type) {
|
|
case XCURSOR_IMAGE_TYPE:
|
|
image = _XcursorReadImage (file, fileHeader, toc);
|
|
if (image)
|
|
{
|
|
images->images[images->nimage] = image;
|
|
images->nimage++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
if (images->nimage != nimage)
|
|
{
|
|
XcursorImagesDestroy (images);
|
|
images = NULL;
|
|
}
|
|
return images;
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorXcFileLoad (XcursorFile *file,
|
|
XcursorComments **commentsp,
|
|
XcursorImages **imagesp)
|
|
{
|
|
XcursorFileHeader *fileHeader;
|
|
int nimage;
|
|
int ncomment;
|
|
XcursorImages *images;
|
|
XcursorImage *image;
|
|
XcursorComment *comment;
|
|
XcursorComments *comments;
|
|
int toc;
|
|
|
|
if (!file)
|
|
return 0;
|
|
fileHeader = _XcursorReadFileHeader (file);
|
|
if (!fileHeader)
|
|
return 0;
|
|
nimage = 0;
|
|
ncomment = 0;
|
|
for (toc = 0; toc < fileHeader->ntoc; toc++)
|
|
{
|
|
switch (fileHeader->tocs[toc].type) {
|
|
case XCURSOR_COMMENT_TYPE:
|
|
ncomment++;
|
|
break;
|
|
case XCURSOR_IMAGE_TYPE:
|
|
nimage++;
|
|
break;
|
|
}
|
|
}
|
|
images = XcursorImagesCreate (nimage);
|
|
if (!images)
|
|
return 0;
|
|
comments = XcursorCommentsCreate (ncomment);
|
|
if (!comments)
|
|
{
|
|
XcursorImagesDestroy (images);
|
|
return 0;
|
|
}
|
|
for (toc = 0; toc < fileHeader->ntoc; toc++)
|
|
{
|
|
switch (fileHeader->tocs[toc].type) {
|
|
case XCURSOR_COMMENT_TYPE:
|
|
comment = _XcursorReadComment (file, fileHeader, toc);
|
|
if (comment)
|
|
{
|
|
comments->comments[comments->ncomment] = comment;
|
|
comments->ncomment++;
|
|
}
|
|
break;
|
|
case XCURSOR_IMAGE_TYPE:
|
|
image = _XcursorReadImage (file, fileHeader, toc);
|
|
if (image)
|
|
{
|
|
images->images[images->nimage] = image;
|
|
images->nimage++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
if (images->nimage != nimage || comments->ncomment != ncomment)
|
|
{
|
|
XcursorImagesDestroy (images);
|
|
XcursorCommentsDestroy (comments);
|
|
images = NULL;
|
|
comments = NULL;
|
|
return XcursorFalse;
|
|
}
|
|
*imagesp = images;
|
|
*commentsp = comments;
|
|
return XcursorTrue;
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorXcFileSave (XcursorFile *file,
|
|
const XcursorComments *comments,
|
|
const XcursorImages *images)
|
|
{
|
|
XcursorFileHeader *fileHeader;
|
|
XcursorUInt position;
|
|
int n;
|
|
int toc;
|
|
|
|
if (!file || !comments || !images)
|
|
return XcursorFalse;
|
|
|
|
fileHeader = _XcursorFileHeaderCreate (comments->ncomment + images->nimage);
|
|
if (!fileHeader)
|
|
return XcursorFalse;
|
|
|
|
position = _XcursorFileHeaderLength (fileHeader);
|
|
|
|
/*
|
|
* Compute the toc. Place the images before the comments
|
|
* as they're more often read
|
|
*/
|
|
|
|
toc = 0;
|
|
for (n = 0; n < images->nimage; n++)
|
|
{
|
|
fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE;
|
|
fileHeader->tocs[toc].subtype = images->images[n]->size;
|
|
fileHeader->tocs[toc].position = position;
|
|
position += _XcursorImageLength (images->images[n]);
|
|
toc++;
|
|
}
|
|
|
|
for (n = 0; n < comments->ncomment; n++)
|
|
{
|
|
fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE;
|
|
fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type;
|
|
fileHeader->tocs[toc].position = position;
|
|
position += _XcursorCommentLength (comments->comments[n]);
|
|
toc++;
|
|
}
|
|
|
|
/*
|
|
* Write the header and the toc
|
|
*/
|
|
if (!_XcursorWriteFileHeader (file, fileHeader))
|
|
goto bail;
|
|
|
|
/*
|
|
* Write the images
|
|
*/
|
|
toc = 0;
|
|
for (n = 0; n < images->nimage; n++)
|
|
{
|
|
if (!_XcursorWriteImage (file, fileHeader, toc, images->images[n]))
|
|
goto bail;
|
|
toc++;
|
|
}
|
|
|
|
/*
|
|
* Write the comments
|
|
*/
|
|
for (n = 0; n < comments->ncomment; n++)
|
|
{
|
|
if (!_XcursorWriteComment (file, fileHeader, toc, comments->comments[n]))
|
|
goto bail;
|
|
toc++;
|
|
}
|
|
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return XcursorTrue;
|
|
bail:
|
|
_XcursorFileHeaderDestroy (fileHeader);
|
|
return XcursorFalse;
|
|
}
|
|
|
|
static int
|
|
_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
|
|
{
|
|
FILE *f = file->closure;
|
|
return fread (buf, 1, len, f);
|
|
}
|
|
|
|
static int
|
|
_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
|
|
{
|
|
FILE *f = file->closure;
|
|
return fwrite (buf, 1, len, f);
|
|
}
|
|
|
|
static int
|
|
_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
|
|
{
|
|
FILE *f = file->closure;
|
|
return fseek (f, offset, whence);
|
|
}
|
|
|
|
static void
|
|
_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
|
|
{
|
|
file->closure = stdfile;
|
|
file->read = _XcursorStdioFileRead;
|
|
file->write = _XcursorStdioFileWrite;
|
|
file->seek = _XcursorStdioFileSeek;
|
|
}
|
|
|
|
XcursorImage *
|
|
XcursorFileLoadImage (FILE *file, int size)
|
|
{
|
|
XcursorFile f;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
_XcursorStdioFileInitialize (file, &f);
|
|
return XcursorXcFileLoadImage (&f, size);
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorFileLoadImages (FILE *file, int size)
|
|
{
|
|
XcursorFile f;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
_XcursorStdioFileInitialize (file, &f);
|
|
return XcursorXcFileLoadImages (&f, size);
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorFileLoadAllImages (FILE *file)
|
|
{
|
|
XcursorFile f;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
_XcursorStdioFileInitialize (file, &f);
|
|
return XcursorXcFileLoadAllImages (&f);
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorFileLoad (FILE *file,
|
|
XcursorComments **commentsp,
|
|
XcursorImages **imagesp)
|
|
{
|
|
XcursorFile f;
|
|
|
|
if (!file || !commentsp || !imagesp)
|
|
return XcursorFalse;
|
|
|
|
_XcursorStdioFileInitialize (file, &f);
|
|
return XcursorXcFileLoad (&f, commentsp, imagesp);
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorFileSaveImages (FILE *file, const XcursorImages *images)
|
|
{
|
|
XcursorComments *comments;
|
|
XcursorFile f;
|
|
XcursorBool ret;
|
|
|
|
if (!file || !images)
|
|
return 0;
|
|
if ((comments = XcursorCommentsCreate (0)) == NULL)
|
|
return 0;
|
|
_XcursorStdioFileInitialize (file, &f);
|
|
ret = XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
|
|
XcursorCommentsDestroy (comments);
|
|
return ret;
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorFileSave (FILE * file,
|
|
const XcursorComments *comments,
|
|
const XcursorImages *images)
|
|
{
|
|
XcursorFile f;
|
|
|
|
if (!file || !comments || !images)
|
|
return XcursorFalse;
|
|
|
|
_XcursorStdioFileInitialize (file, &f);
|
|
return XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
|
|
}
|
|
|
|
XcursorImage *
|
|
XcursorFilenameLoadImage (const char *file, int size)
|
|
{
|
|
FILE *f;
|
|
XcursorImage *image;
|
|
|
|
if (!file || size < 0)
|
|
return NULL;
|
|
|
|
f = fopen (file, "r");
|
|
if (!f)
|
|
return NULL;
|
|
image = XcursorFileLoadImage (f, size);
|
|
fclose (f);
|
|
return image;
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorFilenameLoadImages (const char *file, int size)
|
|
{
|
|
FILE *f;
|
|
XcursorImages *images;
|
|
|
|
if (!file || size < 0)
|
|
return NULL;
|
|
|
|
f = fopen (file, "r");
|
|
if (!f)
|
|
return NULL;
|
|
images = XcursorFileLoadImages (f, size);
|
|
fclose (f);
|
|
return images;
|
|
}
|
|
|
|
XcursorImages *
|
|
XcursorFilenameLoadAllImages (const char *file)
|
|
{
|
|
FILE *f;
|
|
XcursorImages *images;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
f = fopen (file, "r");
|
|
if (!f)
|
|
return NULL;
|
|
images = XcursorFileLoadAllImages (f);
|
|
fclose (f);
|
|
return images;
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorFilenameLoad (const char *file,
|
|
XcursorComments **commentsp,
|
|
XcursorImages **imagesp)
|
|
{
|
|
FILE *f;
|
|
XcursorBool ret;
|
|
|
|
if (!file)
|
|
return XcursorFalse;
|
|
|
|
f = fopen (file, "r");
|
|
if (!f)
|
|
return 0;
|
|
ret = XcursorFileLoad (f, commentsp, imagesp);
|
|
fclose (f);
|
|
return ret;
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorFilenameSaveImages (const char *file, const XcursorImages *images)
|
|
{
|
|
FILE *f;
|
|
XcursorBool ret;
|
|
|
|
if (!file || !images)
|
|
return XcursorFalse;
|
|
|
|
f = fopen (file, "w");
|
|
if (!f)
|
|
return 0;
|
|
ret = XcursorFileSaveImages (f, images);
|
|
return fclose (f) != EOF && ret;
|
|
}
|
|
|
|
XcursorBool
|
|
XcursorFilenameSave (const char *file,
|
|
const XcursorComments *comments,
|
|
const XcursorImages *images)
|
|
{
|
|
FILE *f;
|
|
XcursorBool ret;
|
|
|
|
if (!file || !comments || !images)
|
|
return XcursorFalse;
|
|
|
|
f = fopen (file, "w");
|
|
if (!f)
|
|
return 0;
|
|
ret = XcursorFileSave (f, comments, images);
|
|
return fclose (f) != EOF && ret;
|
|
}
|