file.c revision 4d939ec7
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 (size_t) (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 = (XcursorDim) (width > height ? width : height); 44 image->width = (XcursorDim) width; 45 image->height = (XcursorDim) 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_t) 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) + (size_t) 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_t) 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 166 *u = ((XcursorUInt)(bytes[0]) << 0) | 167 ((XcursorUInt)(bytes[1]) << 8) | 168 ((XcursorUInt)(bytes[2]) << 16) | 169 ((XcursorUInt)(bytes[3]) << 24); 170 return XcursorTrue; 171} 172 173static XcursorBool 174_XcursorReadBytes (XcursorFile *file, char *bytes, int length) 175{ 176 if (!file || !bytes || (*file->read) (file, (unsigned char *) bytes, length) != length) 177 return XcursorFalse; 178 return XcursorTrue; 179} 180 181static XcursorBool 182_XcursorWriteUInt (XcursorFile *file, XcursorUInt u) 183{ 184 unsigned char bytes[4]; 185 186 if (!file) 187 return XcursorFalse; 188 189 bytes[0] = (unsigned char)(u); 190 bytes[1] = (unsigned char)(u >> 8); 191 bytes[2] = (unsigned char)(u >> 16); 192 bytes[3] = (unsigned char)(u >> 24); 193 if ((*file->write) (file, bytes, 4) != 4) 194 return XcursorFalse; 195 return XcursorTrue; 196} 197 198static XcursorBool 199_XcursorWriteBytes (XcursorFile *file, char *bytes, int length) 200{ 201 if (!file || !bytes || (*file->write) (file, (unsigned char *) bytes, length) != length) 202 return XcursorFalse; 203 return XcursorTrue; 204} 205 206static void 207_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) 208{ 209 free (fileHeader); 210} 211 212static XcursorFileHeader * 213_XcursorFileHeaderCreate (XcursorUInt ntoc) 214{ 215 XcursorFileHeader *fileHeader; 216 217 if (ntoc > 0x10000) 218 return NULL; 219 fileHeader = malloc (sizeof (XcursorFileHeader) + 220 ntoc * sizeof (XcursorFileToc)); 221 if (!fileHeader) 222 return NULL; 223 fileHeader->magic = XCURSOR_MAGIC; 224 fileHeader->header = XCURSOR_FILE_HEADER_LEN; 225 fileHeader->version = XCURSOR_FILE_VERSION; 226 fileHeader->ntoc = ntoc; 227 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); 228 return fileHeader; 229} 230 231static XcursorFileHeader * 232_XcursorReadFileHeader (XcursorFile *file) 233{ 234 XcursorFileHeader head, *fileHeader; 235 XcursorUInt skip; 236 XcursorUInt n; 237 238 if (!file) 239 return NULL; 240 241 if (!_XcursorReadUInt (file, &head.magic)) 242 return NULL; 243 if (head.magic != XCURSOR_MAGIC) 244 return NULL; 245 if (!_XcursorReadUInt (file, &head.header)) 246 return NULL; 247 if (!_XcursorReadUInt (file, &head.version)) 248 return NULL; 249 if (!_XcursorReadUInt (file, &head.ntoc)) 250 return NULL; 251 skip = head.header - XCURSOR_FILE_HEADER_LEN; 252 if (skip) 253 if ((*file->seek) (file, skip, SEEK_CUR) == EOF) 254 return NULL; 255 fileHeader = _XcursorFileHeaderCreate (head.ntoc); 256 if (!fileHeader) 257 return NULL; 258 fileHeader->magic = head.magic; 259 fileHeader->header = head.header; 260 fileHeader->version = head.version; 261 fileHeader->ntoc = head.ntoc; 262 for (n = 0; n < fileHeader->ntoc; n++) 263 { 264 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) 265 break; 266 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) 267 break; 268 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) 269 break; 270 } 271 if (n != fileHeader->ntoc) 272 { 273 _XcursorFileHeaderDestroy (fileHeader); 274 return NULL; 275 } 276 return fileHeader; 277} 278 279static XcursorUInt 280_XcursorFileHeaderLength (XcursorFileHeader *fileHeader) 281{ 282 return (XCURSOR_FILE_HEADER_LEN + 283 fileHeader->ntoc * XCURSOR_FILE_TOC_LEN); 284} 285 286static XcursorBool 287_XcursorWriteFileHeader (XcursorFile *file, XcursorFileHeader *fileHeader) 288{ 289 XcursorUInt toc; 290 291 if (!file || !fileHeader) 292 return XcursorFalse; 293 294 if (!_XcursorWriteUInt (file, fileHeader->magic)) 295 return XcursorFalse; 296 if (!_XcursorWriteUInt (file, fileHeader->header)) 297 return XcursorFalse; 298 if (!_XcursorWriteUInt (file, fileHeader->version)) 299 return XcursorFalse; 300 if (!_XcursorWriteUInt (file, fileHeader->ntoc)) 301 return XcursorFalse; 302 for (toc = 0; toc < fileHeader->ntoc; toc++) 303 { 304 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].type)) 305 return XcursorFalse; 306 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].subtype)) 307 return XcursorFalse; 308 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].position)) 309 return XcursorFalse; 310 } 311 return XcursorTrue; 312} 313 314static XcursorBool 315_XcursorSeekToToc (XcursorFile *file, 316 XcursorFileHeader *fileHeader, 317 int toc) 318{ 319 if (!file || !fileHeader || \ 320 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) 321 return XcursorFalse; 322 return XcursorTrue; 323} 324 325static XcursorBool 326_XcursorFileReadChunkHeader (XcursorFile *file, 327 XcursorFileHeader *fileHeader, 328 int toc, 329 XcursorChunkHeader *chunkHeader) 330{ 331 if (!file || !fileHeader || !chunkHeader) 332 return XcursorFalse; 333 if (!_XcursorSeekToToc (file, fileHeader, toc)) 334 return XcursorFalse; 335 if (!_XcursorReadUInt (file, &chunkHeader->header)) 336 return XcursorFalse; 337 if (!_XcursorReadUInt (file, &chunkHeader->type)) 338 return XcursorFalse; 339 if (!_XcursorReadUInt (file, &chunkHeader->subtype)) 340 return XcursorFalse; 341 if (!_XcursorReadUInt (file, &chunkHeader->version)) 342 return XcursorFalse; 343 /* sanity check */ 344 if (chunkHeader->type != fileHeader->tocs[toc].type || 345 chunkHeader->subtype != fileHeader->tocs[toc].subtype) 346 return XcursorFalse; 347 return XcursorTrue; 348} 349 350static XcursorBool 351_XcursorFileWriteChunkHeader (XcursorFile *file, 352 XcursorFileHeader *fileHeader, 353 int toc, 354 XcursorChunkHeader *chunkHeader) 355{ 356 if (!file || !fileHeader || !chunkHeader) 357 return XcursorFalse; 358 if (!_XcursorSeekToToc (file, fileHeader, toc)) 359 return XcursorFalse; 360 if (!_XcursorWriteUInt (file, chunkHeader->header)) 361 return XcursorFalse; 362 if (!_XcursorWriteUInt (file, chunkHeader->type)) 363 return XcursorFalse; 364 if (!_XcursorWriteUInt (file, chunkHeader->subtype)) 365 return XcursorFalse; 366 if (!_XcursorWriteUInt (file, chunkHeader->version)) 367 return XcursorFalse; 368 return XcursorTrue; 369} 370 371#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) 372 373static XcursorDim 374_XcursorFindBestSize (XcursorFileHeader *fileHeader, 375 XcursorDim size, 376 int *nsizesp) 377{ 378 XcursorUInt n; 379 int nsizes = 0; 380 XcursorDim bestSize = 0; 381 XcursorDim thisSize; 382 383 if (!fileHeader || !nsizesp) 384 return 0; 385 386 for (n = 0; n < fileHeader->ntoc; n++) 387 { 388 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) 389 continue; 390 thisSize = fileHeader->tocs[n].subtype; 391 if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) 392 { 393 bestSize = thisSize; 394 nsizes = 1; 395 } 396 else if (thisSize == bestSize) 397 nsizes++; 398 } 399 *nsizesp = nsizes; 400 return bestSize; 401} 402 403static int 404_XcursorFindImageToc (XcursorFileHeader *fileHeader, 405 XcursorDim size, 406 int count) 407{ 408 XcursorUInt toc; 409 XcursorDim thisSize; 410 411 if (!fileHeader) 412 return 0; 413 414 for (toc = 0; toc < fileHeader->ntoc; toc++) 415 { 416 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) 417 continue; 418 thisSize = fileHeader->tocs[toc].subtype; 419 if (thisSize != size) 420 continue; 421 if (!count) 422 break; 423 count--; 424 } 425 if (toc == fileHeader->ntoc) 426 return -1; 427 return (int) toc; 428} 429 430static XcursorImage * 431_XcursorReadImage (XcursorFile *file, 432 XcursorFileHeader *fileHeader, 433 int toc) 434{ 435 XcursorChunkHeader chunkHeader; 436 XcursorImage head; 437 XcursorImage *image; 438 int n; 439 XcursorPixel *p; 440 441 if (!file || !fileHeader) 442 return NULL; 443 444 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) 445 return NULL; 446 if (!_XcursorReadUInt (file, &head.width)) 447 return NULL; 448 if (!_XcursorReadUInt (file, &head.height)) 449 return NULL; 450 if (!_XcursorReadUInt (file, &head.xhot)) 451 return NULL; 452 if (!_XcursorReadUInt (file, &head.yhot)) 453 return NULL; 454 if (!_XcursorReadUInt (file, &head.delay)) 455 return NULL; 456 /* sanity check data */ 457 if (head.width > XCURSOR_IMAGE_MAX_SIZE || 458 head.height > XCURSOR_IMAGE_MAX_SIZE) 459 return NULL; 460 if (head.width == 0 || head.height == 0) 461 return NULL; 462 if (head.xhot > head.width || head.yhot > head.height) 463 return NULL; 464 465 /* Create the image and initialize it */ 466 image = XcursorImageCreate ((int) head.width, (int) head.height); 467 if (image == NULL) 468 return NULL; 469 if (chunkHeader.version < image->version) 470 image->version = chunkHeader.version; 471 image->size = chunkHeader.subtype; 472 image->xhot = head.xhot; 473 image->yhot = head.yhot; 474 image->delay = head.delay; 475 n = (int) (image->width * image->height); 476 p = image->pixels; 477 while (n--) 478 { 479 if (!_XcursorReadUInt (file, p)) 480 { 481 XcursorImageDestroy (image); 482 return NULL; 483 } 484 p++; 485 } 486 return image; 487} 488 489static XcursorUInt 490_XcursorImageLength (XcursorImage *image) 491{ 492 if (!image) 493 return 0; 494 495 return XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4; 496} 497 498static XcursorBool 499_XcursorWriteImage (XcursorFile *file, 500 XcursorFileHeader *fileHeader, 501 int toc, 502 XcursorImage *image) 503{ 504 XcursorChunkHeader chunkHeader; 505 int n; 506 XcursorPixel *p; 507 508 if (!file || !fileHeader || !image) 509 return XcursorFalse; 510 511 /* sanity check data */ 512 if (image->width > XCURSOR_IMAGE_MAX_SIZE || 513 image->height > XCURSOR_IMAGE_MAX_SIZE) 514 return XcursorFalse; 515 if (image->width == 0 || image->height == 0) 516 return XcursorFalse; 517 if (image->xhot > image->width || image->yhot > image->height) 518 return XcursorFalse; 519 520 /* write chunk header */ 521 chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN; 522 chunkHeader.type = XCURSOR_IMAGE_TYPE; 523 chunkHeader.subtype = image->size; 524 chunkHeader.version = XCURSOR_IMAGE_VERSION; 525 526 if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader)) 527 return XcursorFalse; 528 529 /* write extra image header fields */ 530 if (!_XcursorWriteUInt (file, image->width)) 531 return XcursorFalse; 532 if (!_XcursorWriteUInt (file, image->height)) 533 return XcursorFalse; 534 if (!_XcursorWriteUInt (file, image->xhot)) 535 return XcursorFalse; 536 if (!_XcursorWriteUInt (file, image->yhot)) 537 return XcursorFalse; 538 if (!_XcursorWriteUInt (file, image->delay)) 539 return XcursorFalse; 540 541 /* write the image */ 542 n = (int) (image->width * image->height); 543 p = image->pixels; 544 while (n--) 545 { 546 if (!_XcursorWriteUInt (file, *p)) 547 return XcursorFalse; 548 p++; 549 } 550 return XcursorTrue; 551} 552 553static XcursorComment * 554_XcursorReadComment (XcursorFile *file, 555 XcursorFileHeader *fileHeader, 556 int toc) 557{ 558 XcursorChunkHeader chunkHeader; 559 XcursorUInt length; 560 XcursorComment *comment; 561 562 if (!file || !fileHeader) 563 return NULL; 564 565 /* read chunk header */ 566 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) 567 return NULL; 568 /* read extra comment header fields */ 569 if (!_XcursorReadUInt (file, &length)) 570 return NULL; 571 comment = XcursorCommentCreate (chunkHeader.subtype, (int) length); 572 if (!comment) 573 return NULL; 574 if (!_XcursorReadBytes (file, comment->comment, (int) length)) 575 { 576 XcursorCommentDestroy (comment); 577 return NULL; 578 } 579 comment->comment[length] = '\0'; 580 return comment; 581} 582 583static XcursorUInt 584_XcursorCommentLength (XcursorComment *comment) 585{ 586 return XCURSOR_COMMENT_HEADER_LEN + (XcursorUInt) strlen (comment->comment); 587} 588 589static XcursorBool 590_XcursorWriteComment (XcursorFile *file, 591 XcursorFileHeader *fileHeader, 592 int toc, 593 XcursorComment *comment) 594{ 595 XcursorChunkHeader chunkHeader; 596 XcursorUInt length; 597 598 if (!file || !fileHeader || !comment || !comment->comment) 599 return XcursorFalse; 600 601 length = (XcursorUInt) strlen (comment->comment); 602 603 /* sanity check data */ 604 if (length > XCURSOR_COMMENT_MAX_LEN) 605 return XcursorFalse; 606 607 /* read chunk header */ 608 chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN; 609 chunkHeader.type = XCURSOR_COMMENT_TYPE; 610 chunkHeader.subtype = comment->comment_type; 611 chunkHeader.version = XCURSOR_COMMENT_VERSION; 612 613 if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader)) 614 return XcursorFalse; 615 616 /* write extra comment header fields */ 617 if (!_XcursorWriteUInt (file, length)) 618 return XcursorFalse; 619 620 if (!_XcursorWriteBytes (file, comment->comment, (int) length)) 621 return XcursorFalse; 622 return XcursorTrue; 623} 624 625XcursorImage * 626XcursorXcFileLoadImage (XcursorFile *file, int size) 627{ 628 XcursorFileHeader *fileHeader; 629 XcursorDim bestSize; 630 int nsize; 631 int toc; 632 XcursorImage *image; 633 634 if (size < 0) 635 return NULL; 636 fileHeader = _XcursorReadFileHeader (file); 637 if (!fileHeader) 638 return NULL; 639 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); 640 if (!bestSize) 641 return NULL; 642 toc = _XcursorFindImageToc (fileHeader, bestSize, 0); 643 if (toc < 0) 644 return NULL; 645 image = _XcursorReadImage (file, fileHeader, toc); 646 _XcursorFileHeaderDestroy (fileHeader); 647 return image; 648} 649 650XcursorImages * 651XcursorXcFileLoadImages (XcursorFile *file, int size) 652{ 653 XcursorFileHeader *fileHeader; 654 XcursorDim bestSize; 655 int nsize; 656 XcursorImages *images; 657 int n; 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 int 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 XcursorUInt n; 704 XcursorUInt 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, (int) 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 XcursorUInt 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, (int) 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, (int) 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 ((XcursorUInt) (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 (int) fread (buf, 1, (size_t) len, f); 907} 908 909static int 910_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) 911{ 912 FILE *f = file->closure; 913 return (int) fwrite (buf, 1, (size_t) 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