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