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