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