AaptAssets.cpp revision 03589cc65355220e0a4a0c816189a9fa25cc81fc
1// 2// Copyright 2006 The Android Open Source Project 3// 4 5#include "AaptAssets.h" 6#include "Main.h" 7 8#include <utils/misc.h> 9#include <utils/SortedVector.h> 10 11#include <ctype.h> 12#include <dirent.h> 13#include <errno.h> 14 15static const char* kDefaultLocale = "default"; 16static const char* kWildcardName = "any"; 17static const char* kAssetDir = "assets"; 18static const char* kResourceDir = "res"; 19static const char* kInvalidChars = "/\\:"; 20static const size_t kMaxAssetFileName = 100; 21 22static const String8 kResString(kResourceDir); 23 24/* 25 * Names of asset files must meet the following criteria: 26 * 27 * - the filename length must be less than kMaxAssetFileName bytes long 28 * (and can't be empty) 29 * - all characters must be 7-bit printable ASCII 30 * - none of { '/' '\\' ':' } 31 * 32 * Pass in just the filename, not the full path. 33 */ 34static bool validateFileName(const char* fileName) 35{ 36 const char* cp = fileName; 37 size_t len = 0; 38 39 while (*cp != '\0') { 40 if ((*cp & 0x80) != 0) 41 return false; // reject high ASCII 42 if (*cp < 0x20 || *cp >= 0x7f) 43 return false; // reject control chars and 0x7f 44 if (strchr(kInvalidChars, *cp) != NULL) 45 return false; // reject path sep chars 46 cp++; 47 len++; 48 } 49 50 if (len < 1 || len > kMaxAssetFileName) 51 return false; // reject empty or too long 52 53 return true; 54} 55 56static bool isHidden(const char *root, const char *path) 57{ 58 const char *ext = NULL; 59 const char *type = NULL; 60 61 // Skip all hidden files. 62 if (path[0] == '.') { 63 // Skip ., .. and .svn but don't chatter about it. 64 if (strcmp(path, ".") == 0 65 || strcmp(path, "..") == 0 66 || strcmp(path, ".svn") == 0) { 67 return true; 68 } 69 type = "hidden"; 70 } else if (path[0] == '_') { 71 // skip directories starting with _ (don't chatter about it) 72 String8 subdirName(root); 73 subdirName.appendPath(path); 74 if (getFileType(subdirName.string()) == kFileTypeDirectory) { 75 return true; 76 } 77 } else if (strcmp(path, "CVS") == 0) { 78 // Skip CVS but don't chatter about it. 79 return true; 80 } else if (strcasecmp(path, "thumbs.db") == 0 81 || strcasecmp(path, "picasa.ini") == 0) { 82 // Skip suspected image indexes files. 83 type = "index"; 84 } else if (path[strlen(path)-1] == '~') { 85 // Skip suspected emacs backup files. 86 type = "backup"; 87 } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) { 88 // Skip VisualSourceSafe files and don't chatter about it 89 return true; 90 } else { 91 // Let everything else through. 92 return false; 93 } 94 95 /* If we get this far, "type" should be set and the file 96 * should be skipped. 97 */ 98 String8 subdirName(root); 99 subdirName.appendPath(path); 100 fprintf(stderr, " (skipping %s %s '%s')\n", type, 101 getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file", 102 subdirName.string()); 103 104 return true; 105} 106 107// ========================================================================= 108// ========================================================================= 109// ========================================================================= 110 111status_t 112AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) 113{ 114 ResTable_config config; 115 116 // IMSI - MCC 117 if (getMccName(part.string(), &config)) { 118 *axis = AXIS_MCC; 119 *value = config.mcc; 120 return 0; 121 } 122 123 // IMSI - MNC 124 if (getMncName(part.string(), &config)) { 125 *axis = AXIS_MNC; 126 *value = config.mnc; 127 return 0; 128 } 129 130 // locale - language 131 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { 132 *axis = AXIS_LANGUAGE; 133 *value = part[1] << 8 | part[0]; 134 return 0; 135 } 136 137 // locale - language_REGION 138 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1]) 139 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) { 140 *axis = AXIS_LANGUAGE; 141 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]); 142 return 0; 143 } 144 145 // screen layout size 146 if (getScreenLayoutSizeName(part.string(), &config)) { 147 *axis = AXIS_SCREENLAYOUTSIZE; 148 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); 149 return 0; 150 } 151 152 // screen layout long 153 if (getScreenLayoutLongName(part.string(), &config)) { 154 *axis = AXIS_SCREENLAYOUTLONG; 155 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG); 156 return 0; 157 } 158 159 // orientation 160 if (getOrientationName(part.string(), &config)) { 161 *axis = AXIS_ORIENTATION; 162 *value = config.orientation; 163 return 0; 164 } 165 166 // ui mode type 167 if (getUiModeTypeName(part.string(), &config)) { 168 *axis = AXIS_UIMODETYPE; 169 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); 170 return 0; 171 } 172 173 // ui mode night 174 if (getUiModeNightName(part.string(), &config)) { 175 *axis = AXIS_UIMODENIGHT; 176 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); 177 return 0; 178 } 179 180 // density 181 if (getDensityName(part.string(), &config)) { 182 *axis = AXIS_DENSITY; 183 *value = config.density; 184 return 0; 185 } 186 187 // touchscreen 188 if (getTouchscreenName(part.string(), &config)) { 189 *axis = AXIS_TOUCHSCREEN; 190 *value = config.touchscreen; 191 return 0; 192 } 193 194 // keyboard hidden 195 if (getKeysHiddenName(part.string(), &config)) { 196 *axis = AXIS_KEYSHIDDEN; 197 *value = config.inputFlags; 198 return 0; 199 } 200 201 // keyboard 202 if (getKeyboardName(part.string(), &config)) { 203 *axis = AXIS_KEYBOARD; 204 *value = config.keyboard; 205 return 0; 206 } 207 208 // navigation hidden 209 if (getNavHiddenName(part.string(), &config)) { 210 *axis = AXIS_NAVHIDDEN; 211 *value = config.inputFlags; 212 return 0; 213 } 214 215 // navigation 216 if (getNavigationName(part.string(), &config)) { 217 *axis = AXIS_NAVIGATION; 218 *value = config.navigation; 219 return 0; 220 } 221 222 // screen size 223 if (getScreenSizeName(part.string(), &config)) { 224 *axis = AXIS_SCREENSIZE; 225 *value = config.screenSize; 226 return 0; 227 } 228 229 // version 230 if (getVersionName(part.string(), &config)) { 231 *axis = AXIS_VERSION; 232 *value = config.version; 233 return 0; 234 } 235 236 return 1; 237} 238 239bool 240AaptGroupEntry::initFromDirName(const char* dir, String8* resType) 241{ 242 Vector<String8> parts; 243 244 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den; 245 String8 touch, key, keysHidden, nav, navHidden, size, vers; 246 String8 uiModeType, uiModeNight; 247 248 const char *p = dir; 249 const char *q; 250 while (NULL != (q = strchr(p, '-'))) { 251 String8 val(p, q-p); 252 val.toLower(); 253 parts.add(val); 254 //printf("part: %s\n", parts[parts.size()-1].string()); 255 p = q+1; 256 } 257 String8 val(p); 258 val.toLower(); 259 parts.add(val); 260 //printf("part: %s\n", parts[parts.size()-1].string()); 261 262 const int N = parts.size(); 263 int index = 0; 264 String8 part = parts[index]; 265 266 // resource type 267 if (!isValidResourceType(part)) { 268 return false; 269 } 270 *resType = part; 271 272 index++; 273 if (index == N) { 274 goto success; 275 } 276 part = parts[index]; 277 278 // imsi - mcc 279 if (getMccName(part.string())) { 280 mcc = part; 281 282 index++; 283 if (index == N) { 284 goto success; 285 } 286 part = parts[index]; 287 } else { 288 //printf("not mcc: %s\n", part.string()); 289 } 290 291 // imsi - mnc 292 if (getMncName(part.string())) { 293 mnc = part; 294 295 index++; 296 if (index == N) { 297 goto success; 298 } 299 part = parts[index]; 300 } else { 301 //printf("not mcc: %s\n", part.string()); 302 } 303 304 // locale - language 305 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { 306 loc = part; 307 308 index++; 309 if (index == N) { 310 goto success; 311 } 312 part = parts[index]; 313 } else { 314 //printf("not language: %s\n", part.string()); 315 } 316 317 // locale - region 318 if (loc.length() > 0 319 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) { 320 loc += "-"; 321 part.toUpper(); 322 loc += part.string() + 1; 323 324 index++; 325 if (index == N) { 326 goto success; 327 } 328 part = parts[index]; 329 } else { 330 //printf("not region: %s\n", part.string()); 331 } 332 333 if (getScreenLayoutSizeName(part.string())) { 334 layoutsize = part; 335 336 index++; 337 if (index == N) { 338 goto success; 339 } 340 part = parts[index]; 341 } else { 342 //printf("not screen layout size: %s\n", part.string()); 343 } 344 345 if (getScreenLayoutLongName(part.string())) { 346 layoutlong = part; 347 348 index++; 349 if (index == N) { 350 goto success; 351 } 352 part = parts[index]; 353 } else { 354 //printf("not screen layout long: %s\n", part.string()); 355 } 356 357 // orientation 358 if (getOrientationName(part.string())) { 359 orient = part; 360 361 index++; 362 if (index == N) { 363 goto success; 364 } 365 part = parts[index]; 366 } else { 367 //printf("not orientation: %s\n", part.string()); 368 } 369 370 // ui mode type 371 if (getUiModeTypeName(part.string())) { 372 uiModeType = part; 373 374 index++; 375 if (index == N) { 376 goto success; 377 } 378 part = parts[index]; 379 } else { 380 //printf("not ui mode type: %s\n", part.string()); 381 } 382 383 // ui mode night 384 if (getUiModeNightName(part.string())) { 385 uiModeNight = part; 386 387 index++; 388 if (index == N) { 389 goto success; 390 } 391 part = parts[index]; 392 } else { 393 //printf("not ui mode night: %s\n", part.string()); 394 } 395 396 // density 397 if (getDensityName(part.string())) { 398 den = part; 399 400 index++; 401 if (index == N) { 402 goto success; 403 } 404 part = parts[index]; 405 } else { 406 //printf("not density: %s\n", part.string()); 407 } 408 409 // touchscreen 410 if (getTouchscreenName(part.string())) { 411 touch = part; 412 413 index++; 414 if (index == N) { 415 goto success; 416 } 417 part = parts[index]; 418 } else { 419 //printf("not touchscreen: %s\n", part.string()); 420 } 421 422 // keyboard hidden 423 if (getKeysHiddenName(part.string())) { 424 keysHidden = part; 425 426 index++; 427 if (index == N) { 428 goto success; 429 } 430 part = parts[index]; 431 } else { 432 //printf("not keysHidden: %s\n", part.string()); 433 } 434 435 // keyboard 436 if (getKeyboardName(part.string())) { 437 key = part; 438 439 index++; 440 if (index == N) { 441 goto success; 442 } 443 part = parts[index]; 444 } else { 445 //printf("not keyboard: %s\n", part.string()); 446 } 447 448 // navigation hidden 449 if (getNavHiddenName(part.string())) { 450 navHidden = part; 451 452 index++; 453 if (index == N) { 454 goto success; 455 } 456 part = parts[index]; 457 } else { 458 //printf("not navHidden: %s\n", part.string()); 459 } 460 461 if (getNavigationName(part.string())) { 462 nav = part; 463 464 index++; 465 if (index == N) { 466 goto success; 467 } 468 part = parts[index]; 469 } else { 470 //printf("not navigation: %s\n", part.string()); 471 } 472 473 if (getScreenSizeName(part.string())) { 474 size = part; 475 476 index++; 477 if (index == N) { 478 goto success; 479 } 480 part = parts[index]; 481 } else { 482 //printf("not screen size: %s\n", part.string()); 483 } 484 485 if (getVersionName(part.string())) { 486 vers = part; 487 488 index++; 489 if (index == N) { 490 goto success; 491 } 492 part = parts[index]; 493 } else { 494 //printf("not version: %s\n", part.string()); 495 } 496 497 // if there are extra parts, it doesn't match 498 return false; 499 500success: 501 this->mcc = mcc; 502 this->mnc = mnc; 503 this->locale = loc; 504 this->screenLayoutSize = layoutsize; 505 this->screenLayoutLong = layoutlong; 506 this->orientation = orient; 507 this->uiModeType = uiModeType; 508 this->uiModeNight = uiModeNight; 509 this->density = den; 510 this->touchscreen = touch; 511 this->keysHidden = keysHidden; 512 this->keyboard = key; 513 this->navHidden = navHidden; 514 this->navigation = nav; 515 this->screenSize = size; 516 this->version = vers; 517 518 // what is this anyway? 519 this->vendor = ""; 520 521 return true; 522} 523 524String8 525AaptGroupEntry::toString() const 526{ 527 String8 s = this->mcc; 528 s += ","; 529 s += this->mnc; 530 s += ","; 531 s += this->locale; 532 s += ","; 533 s += screenLayoutSize; 534 s += ","; 535 s += screenLayoutLong; 536 s += ","; 537 s += this->orientation; 538 s += ","; 539 s += uiModeType; 540 s += ","; 541 s += uiModeNight; 542 s += ","; 543 s += density; 544 s += ","; 545 s += touchscreen; 546 s += ","; 547 s += keysHidden; 548 s += ","; 549 s += keyboard; 550 s += ","; 551 s += navHidden; 552 s += ","; 553 s += navigation; 554 s += ","; 555 s += screenSize; 556 s += ","; 557 s += version; 558 return s; 559} 560 561String8 562AaptGroupEntry::toDirName(const String8& resType) const 563{ 564 String8 s = resType; 565 if (this->mcc != "") { 566 s += "-"; 567 s += mcc; 568 } 569 if (this->mnc != "") { 570 s += "-"; 571 s += mnc; 572 } 573 if (this->locale != "") { 574 s += "-"; 575 s += locale; 576 } 577 if (this->screenLayoutSize != "") { 578 s += "-"; 579 s += screenLayoutSize; 580 } 581 if (this->screenLayoutLong != "") { 582 s += "-"; 583 s += screenLayoutLong; 584 } 585 if (this->orientation != "") { 586 s += "-"; 587 s += orientation; 588 } 589 if (this->uiModeType != "") { 590 s += "-"; 591 s += uiModeType; 592 } 593 if (this->uiModeNight != "") { 594 s += "-"; 595 s += uiModeNight; 596 } 597 if (this->density != "") { 598 s += "-"; 599 s += density; 600 } 601 if (this->touchscreen != "") { 602 s += "-"; 603 s += touchscreen; 604 } 605 if (this->keysHidden != "") { 606 s += "-"; 607 s += keysHidden; 608 } 609 if (this->keyboard != "") { 610 s += "-"; 611 s += keyboard; 612 } 613 if (this->navHidden != "") { 614 s += "-"; 615 s += navHidden; 616 } 617 if (this->navigation != "") { 618 s += "-"; 619 s += navigation; 620 } 621 if (this->screenSize != "") { 622 s += "-"; 623 s += screenSize; 624 } 625 if (this->version != "") { 626 s += "-"; 627 s += version; 628 } 629 630 return s; 631} 632 633bool AaptGroupEntry::getMccName(const char* name, 634 ResTable_config* out) 635{ 636 if (strcmp(name, kWildcardName) == 0) { 637 if (out) out->mcc = 0; 638 return true; 639 } 640 const char* c = name; 641 if (tolower(*c) != 'm') return false; 642 c++; 643 if (tolower(*c) != 'c') return false; 644 c++; 645 if (tolower(*c) != 'c') return false; 646 c++; 647 648 const char* val = c; 649 650 while (*c >= '0' && *c <= '9') { 651 c++; 652 } 653 if (*c != 0) return false; 654 if (c-val != 3) return false; 655 656 int d = atoi(val); 657 if (d != 0) { 658 if (out) out->mcc = d; 659 return true; 660 } 661 662 return false; 663} 664 665bool AaptGroupEntry::getMncName(const char* name, 666 ResTable_config* out) 667{ 668 if (strcmp(name, kWildcardName) == 0) { 669 if (out) out->mcc = 0; 670 return true; 671 } 672 const char* c = name; 673 if (tolower(*c) != 'm') return false; 674 c++; 675 if (tolower(*c) != 'n') return false; 676 c++; 677 if (tolower(*c) != 'c') return false; 678 c++; 679 680 const char* val = c; 681 682 while (*c >= '0' && *c <= '9') { 683 c++; 684 } 685 if (*c != 0) return false; 686 if (c-val == 0 || c-val > 3) return false; 687 688 if (out) { 689 out->mnc = atoi(val); 690 } 691 692 return true; 693} 694 695/* 696 * Does this directory name fit the pattern of a locale dir ("en-rUS" or 697 * "default")? 698 * 699 * TODO: Should insist that the first two letters are lower case, and the 700 * second two are upper. 701 */ 702bool AaptGroupEntry::getLocaleName(const char* fileName, 703 ResTable_config* out) 704{ 705 if (strcmp(fileName, kWildcardName) == 0 706 || strcmp(fileName, kDefaultLocale) == 0) { 707 if (out) { 708 out->language[0] = 0; 709 out->language[1] = 0; 710 out->country[0] = 0; 711 out->country[1] = 0; 712 } 713 return true; 714 } 715 716 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) { 717 if (out) { 718 out->language[0] = fileName[0]; 719 out->language[1] = fileName[1]; 720 out->country[0] = 0; 721 out->country[1] = 0; 722 } 723 return true; 724 } 725 726 if (strlen(fileName) == 5 && 727 isalpha(fileName[0]) && 728 isalpha(fileName[1]) && 729 fileName[2] == '-' && 730 isalpha(fileName[3]) && 731 isalpha(fileName[4])) { 732 if (out) { 733 out->language[0] = fileName[0]; 734 out->language[1] = fileName[1]; 735 out->country[0] = fileName[3]; 736 out->country[1] = fileName[4]; 737 } 738 return true; 739 } 740 741 return false; 742} 743 744bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, 745 ResTable_config* out) 746{ 747 if (strcmp(name, kWildcardName) == 0) { 748 if (out) out->screenLayout = 749 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 750 | ResTable_config::SCREENSIZE_ANY; 751 return true; 752 } else if (strcmp(name, "small") == 0) { 753 if (out) out->screenLayout = 754 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 755 | ResTable_config::SCREENSIZE_SMALL; 756 return true; 757 } else if (strcmp(name, "normal") == 0) { 758 if (out) out->screenLayout = 759 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 760 | ResTable_config::SCREENSIZE_NORMAL; 761 return true; 762 } else if (strcmp(name, "large") == 0) { 763 if (out) out->screenLayout = 764 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 765 | ResTable_config::SCREENSIZE_LARGE; 766 return true; 767 } else if (strcmp(name, "xlarge") == 0) { 768 if (out) out->screenLayout = 769 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 770 | ResTable_config::SCREENSIZE_XLARGE; 771 return true; 772 } 773 774 return false; 775} 776 777bool AaptGroupEntry::getScreenLayoutLongName(const char* name, 778 ResTable_config* out) 779{ 780 if (strcmp(name, kWildcardName) == 0) { 781 if (out) out->screenLayout = 782 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 783 | ResTable_config::SCREENLONG_ANY; 784 return true; 785 } else if (strcmp(name, "long") == 0) { 786 if (out) out->screenLayout = 787 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 788 | ResTable_config::SCREENLONG_YES; 789 return true; 790 } else if (strcmp(name, "notlong") == 0) { 791 if (out) out->screenLayout = 792 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 793 | ResTable_config::SCREENLONG_NO; 794 return true; 795 } 796 797 return false; 798} 799 800bool AaptGroupEntry::getOrientationName(const char* name, 801 ResTable_config* out) 802{ 803 if (strcmp(name, kWildcardName) == 0) { 804 if (out) out->orientation = out->ORIENTATION_ANY; 805 return true; 806 } else if (strcmp(name, "port") == 0) { 807 if (out) out->orientation = out->ORIENTATION_PORT; 808 return true; 809 } else if (strcmp(name, "land") == 0) { 810 if (out) out->orientation = out->ORIENTATION_LAND; 811 return true; 812 } else if (strcmp(name, "square") == 0) { 813 if (out) out->orientation = out->ORIENTATION_SQUARE; 814 return true; 815 } 816 817 return false; 818} 819 820bool AaptGroupEntry::getUiModeTypeName(const char* name, 821 ResTable_config* out) 822{ 823 if (strcmp(name, kWildcardName) == 0) { 824 if (out) out->uiMode = 825 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 826 | ResTable_config::UI_MODE_TYPE_ANY; 827 return true; 828 } else if (strcmp(name, "desk") == 0) { 829 if (out) out->uiMode = 830 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 831 | ResTable_config::UI_MODE_TYPE_DESK; 832 return true; 833 } else if (strcmp(name, "car") == 0) { 834 if (out) out->uiMode = 835 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 836 | ResTable_config::UI_MODE_TYPE_CAR; 837 return true; 838 } 839 840 return false; 841} 842 843bool AaptGroupEntry::getUiModeNightName(const char* name, 844 ResTable_config* out) 845{ 846 if (strcmp(name, kWildcardName) == 0) { 847 if (out) out->uiMode = 848 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 849 | ResTable_config::UI_MODE_NIGHT_ANY; 850 return true; 851 } else if (strcmp(name, "night") == 0) { 852 if (out) out->uiMode = 853 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 854 | ResTable_config::UI_MODE_NIGHT_YES; 855 return true; 856 } else if (strcmp(name, "notnight") == 0) { 857 if (out) out->uiMode = 858 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 859 | ResTable_config::UI_MODE_NIGHT_NO; 860 return true; 861 } 862 863 return false; 864} 865 866bool AaptGroupEntry::getDensityName(const char* name, 867 ResTable_config* out) 868{ 869 if (strcmp(name, kWildcardName) == 0) { 870 if (out) out->density = ResTable_config::DENSITY_DEFAULT; 871 return true; 872 } 873 874 if (strcmp(name, "nodpi") == 0) { 875 if (out) out->density = ResTable_config::DENSITY_NONE; 876 return true; 877 } 878 879 if (strcmp(name, "ldpi") == 0) { 880 if (out) out->density = ResTable_config::DENSITY_LOW; 881 return true; 882 } 883 884 if (strcmp(name, "mdpi") == 0) { 885 if (out) out->density = ResTable_config::DENSITY_MEDIUM; 886 return true; 887 } 888 889 if (strcmp(name, "hdpi") == 0) { 890 if (out) out->density = ResTable_config::DENSITY_HIGH; 891 return true; 892 } 893 894 if (strcmp(name, "xhdpi") == 0) { 895 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2; 896 return true; 897 } 898 899 char* c = (char*)name; 900 while (*c >= '0' && *c <= '9') { 901 c++; 902 } 903 904 // check that we have 'dpi' after the last digit. 905 if (toupper(c[0]) != 'D' || 906 toupper(c[1]) != 'P' || 907 toupper(c[2]) != 'I' || 908 c[3] != 0) { 909 return false; 910 } 911 912 // temporarily replace the first letter with \0 to 913 // use atoi. 914 char tmp = c[0]; 915 c[0] = '\0'; 916 917 int d = atoi(name); 918 c[0] = tmp; 919 920 if (d != 0) { 921 if (out) out->density = d; 922 return true; 923 } 924 925 return false; 926} 927 928bool AaptGroupEntry::getTouchscreenName(const char* name, 929 ResTable_config* out) 930{ 931 if (strcmp(name, kWildcardName) == 0) { 932 if (out) out->touchscreen = out->TOUCHSCREEN_ANY; 933 return true; 934 } else if (strcmp(name, "notouch") == 0) { 935 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; 936 return true; 937 } else if (strcmp(name, "stylus") == 0) { 938 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; 939 return true; 940 } else if (strcmp(name, "finger") == 0) { 941 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; 942 return true; 943 } 944 945 return false; 946} 947 948bool AaptGroupEntry::getKeysHiddenName(const char* name, 949 ResTable_config* out) 950{ 951 uint8_t mask = 0; 952 uint8_t value = 0; 953 if (strcmp(name, kWildcardName) == 0) { 954 mask = ResTable_config::MASK_KEYSHIDDEN; 955 value = ResTable_config::KEYSHIDDEN_ANY; 956 } else if (strcmp(name, "keysexposed") == 0) { 957 mask = ResTable_config::MASK_KEYSHIDDEN; 958 value = ResTable_config::KEYSHIDDEN_NO; 959 } else if (strcmp(name, "keyshidden") == 0) { 960 mask = ResTable_config::MASK_KEYSHIDDEN; 961 value = ResTable_config::KEYSHIDDEN_YES; 962 } else if (strcmp(name, "keyssoft") == 0) { 963 mask = ResTable_config::MASK_KEYSHIDDEN; 964 value = ResTable_config::KEYSHIDDEN_SOFT; 965 } 966 967 if (mask != 0) { 968 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 969 return true; 970 } 971 972 return false; 973} 974 975bool AaptGroupEntry::getKeyboardName(const char* name, 976 ResTable_config* out) 977{ 978 if (strcmp(name, kWildcardName) == 0) { 979 if (out) out->keyboard = out->KEYBOARD_ANY; 980 return true; 981 } else if (strcmp(name, "nokeys") == 0) { 982 if (out) out->keyboard = out->KEYBOARD_NOKEYS; 983 return true; 984 } else if (strcmp(name, "qwerty") == 0) { 985 if (out) out->keyboard = out->KEYBOARD_QWERTY; 986 return true; 987 } else if (strcmp(name, "12key") == 0) { 988 if (out) out->keyboard = out->KEYBOARD_12KEY; 989 return true; 990 } 991 992 return false; 993} 994 995bool AaptGroupEntry::getNavHiddenName(const char* name, 996 ResTable_config* out) 997{ 998 uint8_t mask = 0; 999 uint8_t value = 0; 1000 if (strcmp(name, kWildcardName) == 0) { 1001 mask = ResTable_config::MASK_NAVHIDDEN; 1002 value = ResTable_config::NAVHIDDEN_ANY; 1003 } else if (strcmp(name, "navexposed") == 0) { 1004 mask = ResTable_config::MASK_NAVHIDDEN; 1005 value = ResTable_config::NAVHIDDEN_NO; 1006 } else if (strcmp(name, "navhidden") == 0) { 1007 mask = ResTable_config::MASK_NAVHIDDEN; 1008 value = ResTable_config::NAVHIDDEN_YES; 1009 } 1010 1011 if (mask != 0) { 1012 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1013 return true; 1014 } 1015 1016 return false; 1017} 1018 1019bool AaptGroupEntry::getNavigationName(const char* name, 1020 ResTable_config* out) 1021{ 1022 if (strcmp(name, kWildcardName) == 0) { 1023 if (out) out->navigation = out->NAVIGATION_ANY; 1024 return true; 1025 } else if (strcmp(name, "nonav") == 0) { 1026 if (out) out->navigation = out->NAVIGATION_NONAV; 1027 return true; 1028 } else if (strcmp(name, "dpad") == 0) { 1029 if (out) out->navigation = out->NAVIGATION_DPAD; 1030 return true; 1031 } else if (strcmp(name, "trackball") == 0) { 1032 if (out) out->navigation = out->NAVIGATION_TRACKBALL; 1033 return true; 1034 } else if (strcmp(name, "wheel") == 0) { 1035 if (out) out->navigation = out->NAVIGATION_WHEEL; 1036 return true; 1037 } 1038 1039 return false; 1040} 1041 1042bool AaptGroupEntry::getScreenSizeName(const char* name, 1043 ResTable_config* out) 1044{ 1045 if (strcmp(name, kWildcardName) == 0) { 1046 if (out) { 1047 out->screenWidth = out->SCREENWIDTH_ANY; 1048 out->screenHeight = out->SCREENHEIGHT_ANY; 1049 } 1050 return true; 1051 } 1052 1053 const char* x = name; 1054 while (*x >= '0' && *x <= '9') x++; 1055 if (x == name || *x != 'x') return false; 1056 String8 xName(name, x-name); 1057 x++; 1058 1059 const char* y = x; 1060 while (*y >= '0' && *y <= '9') y++; 1061 if (y == name || *y != 0) return false; 1062 String8 yName(x, y-x); 1063 1064 uint16_t w = (uint16_t)atoi(xName.string()); 1065 uint16_t h = (uint16_t)atoi(yName.string()); 1066 if (w < h) { 1067 return false; 1068 } 1069 1070 if (out) { 1071 out->screenWidth = w; 1072 out->screenHeight = h; 1073 } 1074 1075 return true; 1076} 1077 1078bool AaptGroupEntry::getVersionName(const char* name, 1079 ResTable_config* out) 1080{ 1081 if (strcmp(name, kWildcardName) == 0) { 1082 if (out) { 1083 out->sdkVersion = out->SDKVERSION_ANY; 1084 out->minorVersion = out->MINORVERSION_ANY; 1085 } 1086 return true; 1087 } 1088 1089 if (*name != 'v') { 1090 return false; 1091 } 1092 1093 name++; 1094 const char* s = name; 1095 while (*s >= '0' && *s <= '9') s++; 1096 if (s == name || *s != 0) return false; 1097 String8 sdkName(name, s-name); 1098 1099 if (out) { 1100 out->sdkVersion = (uint16_t)atoi(sdkName.string()); 1101 out->minorVersion = 0; 1102 } 1103 1104 return true; 1105} 1106 1107int AaptGroupEntry::compare(const AaptGroupEntry& o) const 1108{ 1109 int v = mcc.compare(o.mcc); 1110 if (v == 0) v = mnc.compare(o.mnc); 1111 if (v == 0) v = locale.compare(o.locale); 1112 if (v == 0) v = vendor.compare(o.vendor); 1113 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); 1114 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); 1115 if (v == 0) v = orientation.compare(o.orientation); 1116 if (v == 0) v = uiModeType.compare(o.uiModeType); 1117 if (v == 0) v = uiModeNight.compare(o.uiModeNight); 1118 if (v == 0) v = density.compare(o.density); 1119 if (v == 0) v = touchscreen.compare(o.touchscreen); 1120 if (v == 0) v = keysHidden.compare(o.keysHidden); 1121 if (v == 0) v = keyboard.compare(o.keyboard); 1122 if (v == 0) v = navHidden.compare(o.navHidden); 1123 if (v == 0) v = navigation.compare(o.navigation); 1124 if (v == 0) v = screenSize.compare(o.screenSize); 1125 if (v == 0) v = version.compare(o.version); 1126 return v; 1127} 1128 1129ResTable_config AaptGroupEntry::toParams() const 1130{ 1131 ResTable_config params; 1132 memset(¶ms, 0, sizeof(params)); 1133 getMccName(mcc.string(), ¶ms); 1134 getMncName(mnc.string(), ¶ms); 1135 getLocaleName(locale.string(), ¶ms); 1136 getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); 1137 getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); 1138 getOrientationName(orientation.string(), ¶ms); 1139 getUiModeTypeName(uiModeType.string(), ¶ms); 1140 getUiModeNightName(uiModeNight.string(), ¶ms); 1141 getDensityName(density.string(), ¶ms); 1142 getTouchscreenName(touchscreen.string(), ¶ms); 1143 getKeysHiddenName(keysHidden.string(), ¶ms); 1144 getKeyboardName(keyboard.string(), ¶ms); 1145 getNavHiddenName(navHidden.string(), ¶ms); 1146 getNavigationName(navigation.string(), ¶ms); 1147 getScreenSizeName(screenSize.string(), ¶ms); 1148 getVersionName(version.string(), ¶ms); 1149 1150 // Fix up version number based on specified parameters. 1151 int minSdk = 0; 1152 if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) 1153 != ResTable_config::UI_MODE_TYPE_ANY 1154 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) 1155 != ResTable_config::UI_MODE_NIGHT_ANY) { 1156 minSdk = SDK_FROYO; 1157 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) 1158 != ResTable_config::SCREENSIZE_ANY 1159 || (params.screenLayout&ResTable_config::MASK_SCREENLONG) 1160 != ResTable_config::SCREENLONG_ANY 1161 || params.density != ResTable_config::DENSITY_DEFAULT) { 1162 minSdk = SDK_DONUT; 1163 } 1164 1165 if (minSdk > params.sdkVersion) { 1166 params.sdkVersion = minSdk; 1167 } 1168 1169 return params; 1170} 1171 1172// ========================================================================= 1173// ========================================================================= 1174// ========================================================================= 1175 1176void* AaptFile::editData(size_t size) 1177{ 1178 if (size <= mBufferSize) { 1179 mDataSize = size; 1180 return mData; 1181 } 1182 size_t allocSize = (size*3)/2; 1183 void* buf = realloc(mData, allocSize); 1184 if (buf == NULL) { 1185 return NULL; 1186 } 1187 mData = buf; 1188 mDataSize = size; 1189 mBufferSize = allocSize; 1190 return buf; 1191} 1192 1193void* AaptFile::editData(size_t* outSize) 1194{ 1195 if (outSize) { 1196 *outSize = mDataSize; 1197 } 1198 return mData; 1199} 1200 1201void* AaptFile::padData(size_t wordSize) 1202{ 1203 const size_t extra = mDataSize%wordSize; 1204 if (extra == 0) { 1205 return mData; 1206 } 1207 1208 size_t initial = mDataSize; 1209 void* data = editData(initial+(wordSize-extra)); 1210 if (data != NULL) { 1211 memset(((uint8_t*)data) + initial, 0, wordSize-extra); 1212 } 1213 return data; 1214} 1215 1216status_t AaptFile::writeData(const void* data, size_t size) 1217{ 1218 size_t end = mDataSize; 1219 size_t total = size + end; 1220 void* buf = editData(total); 1221 if (buf == NULL) { 1222 return UNKNOWN_ERROR; 1223 } 1224 memcpy(((char*)buf)+end, data, size); 1225 return NO_ERROR; 1226} 1227 1228void AaptFile::clearData() 1229{ 1230 if (mData != NULL) free(mData); 1231 mData = NULL; 1232 mDataSize = 0; 1233 mBufferSize = 0; 1234} 1235 1236String8 AaptFile::getPrintableSource() const 1237{ 1238 if (hasData()) { 1239 String8 name(mGroupEntry.locale.string()); 1240 name.appendPath(mGroupEntry.vendor.string()); 1241 name.appendPath(mPath); 1242 name.append(" #generated"); 1243 return name; 1244 } 1245 return mSourceFile; 1246} 1247 1248// ========================================================================= 1249// ========================================================================= 1250// ========================================================================= 1251 1252status_t AaptGroup::addFile(const sp<AaptFile>& file) 1253{ 1254 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) { 1255 file->mPath = mPath; 1256 mFiles.add(file->getGroupEntry(), file); 1257 return NO_ERROR; 1258 } 1259 1260 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.", 1261 getPrintableSource().string()); 1262 return UNKNOWN_ERROR; 1263} 1264 1265void AaptGroup::removeFile(size_t index) 1266{ 1267 mFiles.removeItemsAt(index); 1268} 1269 1270void AaptGroup::print() const 1271{ 1272 printf(" %s\n", getPath().string()); 1273 const size_t N=mFiles.size(); 1274 size_t i; 1275 for (i=0; i<N; i++) { 1276 sp<AaptFile> file = mFiles.valueAt(i); 1277 const AaptGroupEntry& e = file->getGroupEntry(); 1278 if (file->hasData()) { 1279 printf(" Gen: (%s) %d bytes\n", e.toString().string(), 1280 (int)file->getSize()); 1281 } else { 1282 printf(" Src: %s\n", file->getPrintableSource().string()); 1283 } 1284 } 1285} 1286 1287String8 AaptGroup::getPrintableSource() const 1288{ 1289 if (mFiles.size() > 0) { 1290 // Arbitrarily pull the first source file out of the list. 1291 return mFiles.valueAt(0)->getPrintableSource(); 1292 } 1293 1294 // Should never hit this case, but to be safe... 1295 return getPath(); 1296 1297} 1298 1299// ========================================================================= 1300// ========================================================================= 1301// ========================================================================= 1302 1303status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) 1304{ 1305 if (mFiles.indexOfKey(name) >= 0) { 1306 return ALREADY_EXISTS; 1307 } 1308 mFiles.add(name, file); 1309 return NO_ERROR; 1310} 1311 1312status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) 1313{ 1314 if (mDirs.indexOfKey(name) >= 0) { 1315 return ALREADY_EXISTS; 1316 } 1317 mDirs.add(name, dir); 1318 return NO_ERROR; 1319} 1320 1321sp<AaptDir> AaptDir::makeDir(const String8& path) 1322{ 1323 String8 name; 1324 String8 remain = path; 1325 1326 sp<AaptDir> subdir = this; 1327 while (name = remain.walkPath(&remain), remain != "") { 1328 subdir = subdir->makeDir(name); 1329 } 1330 1331 ssize_t i = subdir->mDirs.indexOfKey(name); 1332 if (i >= 0) { 1333 return subdir->mDirs.valueAt(i); 1334 } 1335 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); 1336 subdir->mDirs.add(name, dir); 1337 return dir; 1338} 1339 1340void AaptDir::removeFile(const String8& name) 1341{ 1342 mFiles.removeItem(name); 1343} 1344 1345void AaptDir::removeDir(const String8& name) 1346{ 1347 mDirs.removeItem(name); 1348} 1349 1350status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName) 1351{ 1352 sp<AaptGroup> origGroup; 1353 1354 // Find and remove the given file with shear, brute force! 1355 const size_t NG = mFiles.size(); 1356 size_t i; 1357 for (i=0; origGroup == NULL && i<NG; i++) { 1358 sp<AaptGroup> g = mFiles.valueAt(i); 1359 const size_t NF = g->getFiles().size(); 1360 for (size_t j=0; j<NF; j++) { 1361 if (g->getFiles().valueAt(j) == file) { 1362 origGroup = g; 1363 g->removeFile(j); 1364 if (NF == 1) { 1365 mFiles.removeItemsAt(i); 1366 } 1367 break; 1368 } 1369 } 1370 } 1371 1372 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string()); 1373 1374 // Place the file under its new name. 1375 if (origGroup != NULL) { 1376 return addLeafFile(newName, file); 1377 } 1378 1379 return NO_ERROR; 1380} 1381 1382status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file) 1383{ 1384 sp<AaptGroup> group; 1385 if (mFiles.indexOfKey(leafName) >= 0) { 1386 group = mFiles.valueFor(leafName); 1387 } else { 1388 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); 1389 mFiles.add(leafName, group); 1390 } 1391 1392 return group->addFile(file); 1393} 1394 1395ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, 1396 const AaptGroupEntry& kind, const String8& resType, 1397 sp<FilePathStore>& fullResPaths) 1398{ 1399 Vector<String8> fileNames; 1400 { 1401 DIR* dir = NULL; 1402 1403 dir = opendir(srcDir.string()); 1404 if (dir == NULL) { 1405 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1406 return UNKNOWN_ERROR; 1407 } 1408 1409 /* 1410 * Slurp the filenames out of the directory. 1411 */ 1412 while (1) { 1413 struct dirent* entry; 1414 1415 entry = readdir(dir); 1416 if (entry == NULL) 1417 break; 1418 1419 if (isHidden(srcDir.string(), entry->d_name)) 1420 continue; 1421 1422 String8 name(entry->d_name); 1423 fileNames.add(name); 1424 // Add fully qualified path for dependency purposes 1425 // if we're collecting them 1426 if (fullResPaths != NULL) { 1427 fullResPaths->add(srcDir.appendPathCopy(name)); 1428 } 1429 } 1430 closedir(dir); 1431 } 1432 1433 ssize_t count = 0; 1434 1435 /* 1436 * Stash away the files and recursively descend into subdirectories. 1437 */ 1438 const size_t N = fileNames.size(); 1439 size_t i; 1440 for (i = 0; i < N; i++) { 1441 String8 pathName(srcDir); 1442 FileType type; 1443 1444 pathName.appendPath(fileNames[i].string()); 1445 type = getFileType(pathName.string()); 1446 if (type == kFileTypeDirectory) { 1447 sp<AaptDir> subdir; 1448 bool notAdded = false; 1449 if (mDirs.indexOfKey(fileNames[i]) >= 0) { 1450 subdir = mDirs.valueFor(fileNames[i]); 1451 } else { 1452 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); 1453 notAdded = true; 1454 } 1455 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, 1456 resType, fullResPaths); 1457 if (res < NO_ERROR) { 1458 return res; 1459 } 1460 if (res > 0 && notAdded) { 1461 mDirs.add(fileNames[i], subdir); 1462 } 1463 count += res; 1464 } else if (type == kFileTypeRegular) { 1465 sp<AaptFile> file = new AaptFile(pathName, kind, resType); 1466 status_t err = addLeafFile(fileNames[i], file); 1467 if (err != NO_ERROR) { 1468 return err; 1469 } 1470 1471 count++; 1472 1473 } else { 1474 if (bundle->getVerbose()) 1475 printf(" (ignoring non-file/dir '%s')\n", pathName.string()); 1476 } 1477 } 1478 1479 return count; 1480} 1481 1482status_t AaptDir::validate() const 1483{ 1484 const size_t NF = mFiles.size(); 1485 const size_t ND = mDirs.size(); 1486 size_t i; 1487 for (i = 0; i < NF; i++) { 1488 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) { 1489 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1490 "Invalid filename. Unable to add."); 1491 return UNKNOWN_ERROR; 1492 } 1493 1494 size_t j; 1495 for (j = i+1; j < NF; j++) { 1496 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1497 mFiles.valueAt(j)->getLeaf().string()) == 0) { 1498 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1499 "File is case-insensitive equivalent to: %s", 1500 mFiles.valueAt(j)->getPrintableSource().string()); 1501 return UNKNOWN_ERROR; 1502 } 1503 1504 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" 1505 // (this is mostly caught by the "marked" stuff, below) 1506 } 1507 1508 for (j = 0; j < ND; j++) { 1509 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1510 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1511 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1512 "File conflicts with dir from: %s", 1513 mDirs.valueAt(j)->getPrintableSource().string()); 1514 return UNKNOWN_ERROR; 1515 } 1516 } 1517 } 1518 1519 for (i = 0; i < ND; i++) { 1520 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) { 1521 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1522 "Invalid directory name, unable to add."); 1523 return UNKNOWN_ERROR; 1524 } 1525 1526 size_t j; 1527 for (j = i+1; j < ND; j++) { 1528 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(), 1529 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1530 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1531 "Directory is case-insensitive equivalent to: %s", 1532 mDirs.valueAt(j)->getPrintableSource().string()); 1533 return UNKNOWN_ERROR; 1534 } 1535 } 1536 1537 status_t err = mDirs.valueAt(i)->validate(); 1538 if (err != NO_ERROR) { 1539 return err; 1540 } 1541 } 1542 1543 return NO_ERROR; 1544} 1545 1546void AaptDir::print() const 1547{ 1548 const size_t ND=getDirs().size(); 1549 size_t i; 1550 for (i=0; i<ND; i++) { 1551 getDirs().valueAt(i)->print(); 1552 } 1553 1554 const size_t NF=getFiles().size(); 1555 for (i=0; i<NF; i++) { 1556 getFiles().valueAt(i)->print(); 1557 } 1558} 1559 1560String8 AaptDir::getPrintableSource() const 1561{ 1562 if (mFiles.size() > 0) { 1563 // Arbitrarily pull the first file out of the list as the source dir. 1564 return mFiles.valueAt(0)->getPrintableSource().getPathDir(); 1565 } 1566 if (mDirs.size() > 0) { 1567 // Or arbitrarily pull the first dir out of the list as the source dir. 1568 return mDirs.valueAt(0)->getPrintableSource().getPathDir(); 1569 } 1570 1571 // Should never hit this case, but to be safe... 1572 return mPath; 1573 1574} 1575 1576// ========================================================================= 1577// ========================================================================= 1578// ========================================================================= 1579 1580sp<AaptFile> AaptAssets::addFile( 1581 const String8& filePath, const AaptGroupEntry& entry, 1582 const String8& srcDir, sp<AaptGroup>* outGroup, 1583 const String8& resType) 1584{ 1585 sp<AaptDir> dir = this; 1586 sp<AaptGroup> group; 1587 sp<AaptFile> file; 1588 String8 root, remain(filePath), partialPath; 1589 while (remain.length() > 0) { 1590 root = remain.walkPath(&remain); 1591 partialPath.appendPath(root); 1592 1593 const String8 rootStr(root); 1594 1595 if (remain.length() == 0) { 1596 ssize_t i = dir->getFiles().indexOfKey(rootStr); 1597 if (i >= 0) { 1598 group = dir->getFiles().valueAt(i); 1599 } else { 1600 group = new AaptGroup(rootStr, filePath); 1601 status_t res = dir->addFile(rootStr, group); 1602 if (res != NO_ERROR) { 1603 return NULL; 1604 } 1605 } 1606 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); 1607 status_t res = group->addFile(file); 1608 if (res != NO_ERROR) { 1609 return NULL; 1610 } 1611 break; 1612 1613 } else { 1614 ssize_t i = dir->getDirs().indexOfKey(rootStr); 1615 if (i >= 0) { 1616 dir = dir->getDirs().valueAt(i); 1617 } else { 1618 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); 1619 status_t res = dir->addDir(rootStr, subdir); 1620 if (res != NO_ERROR) { 1621 return NULL; 1622 } 1623 dir = subdir; 1624 } 1625 } 1626 } 1627 1628 mGroupEntries.add(entry); 1629 if (outGroup) *outGroup = group; 1630 return file; 1631} 1632 1633void AaptAssets::addResource(const String8& leafName, const String8& path, 1634 const sp<AaptFile>& file, const String8& resType) 1635{ 1636 sp<AaptDir> res = AaptDir::makeDir(kResString); 1637 String8 dirname = file->getGroupEntry().toDirName(resType); 1638 sp<AaptDir> subdir = res->makeDir(dirname); 1639 sp<AaptGroup> grr = new AaptGroup(leafName, path); 1640 grr->addFile(file); 1641 1642 subdir->addFile(leafName, grr); 1643} 1644 1645 1646ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) 1647{ 1648 int count; 1649 int totalCount = 0; 1650 FileType type; 1651 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); 1652 const size_t dirCount =resDirs.size(); 1653 sp<AaptAssets> current = this; 1654 1655 const int N = bundle->getFileSpecCount(); 1656 1657 /* 1658 * If a package manifest was specified, include that first. 1659 */ 1660 if (bundle->getAndroidManifestFile() != NULL) { 1661 // place at root of zip. 1662 String8 srcFile(bundle->getAndroidManifestFile()); 1663 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), 1664 NULL, String8()); 1665 totalCount++; 1666 } 1667 1668 /* 1669 * If a directory of custom assets was supplied, slurp 'em up. 1670 */ 1671 if (bundle->getAssetSourceDir()) { 1672 const char* assetDir = bundle->getAssetSourceDir(); 1673 1674 FileType type = getFileType(assetDir); 1675 if (type == kFileTypeNonexistent) { 1676 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir); 1677 return UNKNOWN_ERROR; 1678 } 1679 if (type != kFileTypeDirectory) { 1680 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1681 return UNKNOWN_ERROR; 1682 } 1683 1684 String8 assetRoot(assetDir); 1685 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); 1686 AaptGroupEntry group; 1687 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, 1688 String8(), mFullAssetPaths); 1689 if (count < 0) { 1690 totalCount = count; 1691 goto bail; 1692 } 1693 if (count > 0) { 1694 mGroupEntries.add(group); 1695 } 1696 totalCount += count; 1697 1698 if (bundle->getVerbose()) 1699 printf("Found %d custom asset file%s in %s\n", 1700 count, (count==1) ? "" : "s", assetDir); 1701 } 1702 1703 /* 1704 * If a directory of resource-specific assets was supplied, slurp 'em up. 1705 */ 1706 for (size_t i=0; i<dirCount; i++) { 1707 const char *res = resDirs[i]; 1708 if (res) { 1709 type = getFileType(res); 1710 if (type == kFileTypeNonexistent) { 1711 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); 1712 return UNKNOWN_ERROR; 1713 } 1714 if (type == kFileTypeDirectory) { 1715 if (i>0) { 1716 sp<AaptAssets> nextOverlay = new AaptAssets(); 1717 current->setOverlay(nextOverlay); 1718 current = nextOverlay; 1719 current->setFullResPaths(mFullResPaths); 1720 } 1721 count = current->slurpResourceTree(bundle, String8(res)); 1722 1723 if (count < 0) { 1724 totalCount = count; 1725 goto bail; 1726 } 1727 totalCount += count; 1728 } 1729 else { 1730 fprintf(stderr, "ERROR: '%s' is not a directory\n", res); 1731 return UNKNOWN_ERROR; 1732 } 1733 } 1734 1735 } 1736 /* 1737 * Now do any additional raw files. 1738 */ 1739 for (int arg=0; arg<N; arg++) { 1740 const char* assetDir = bundle->getFileSpecEntry(arg); 1741 1742 FileType type = getFileType(assetDir); 1743 if (type == kFileTypeNonexistent) { 1744 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); 1745 return UNKNOWN_ERROR; 1746 } 1747 if (type != kFileTypeDirectory) { 1748 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1749 return UNKNOWN_ERROR; 1750 } 1751 1752 String8 assetRoot(assetDir); 1753 1754 if (bundle->getVerbose()) 1755 printf("Processing raw dir '%s'\n", (const char*) assetDir); 1756 1757 /* 1758 * Do a recursive traversal of subdir tree. We don't make any 1759 * guarantees about ordering, so we're okay with an inorder search 1760 * using whatever order the OS happens to hand back to us. 1761 */ 1762 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths); 1763 if (count < 0) { 1764 /* failure; report error and remove archive */ 1765 totalCount = count; 1766 goto bail; 1767 } 1768 totalCount += count; 1769 1770 if (bundle->getVerbose()) 1771 printf("Found %d asset file%s in %s\n", 1772 count, (count==1) ? "" : "s", assetDir); 1773 } 1774 1775 count = validate(); 1776 if (count != NO_ERROR) { 1777 totalCount = count; 1778 goto bail; 1779 } 1780 1781 1782bail: 1783 return totalCount; 1784} 1785 1786ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, 1787 const AaptGroupEntry& kind, 1788 const String8& resType, 1789 sp<FilePathStore>& fullResPaths) 1790{ 1791 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths); 1792 if (res > 0) { 1793 mGroupEntries.add(kind); 1794 } 1795 1796 return res; 1797} 1798 1799ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) 1800{ 1801 ssize_t err = 0; 1802 1803 DIR* dir = opendir(srcDir.string()); 1804 if (dir == NULL) { 1805 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1806 return UNKNOWN_ERROR; 1807 } 1808 1809 status_t count = 0; 1810 1811 /* 1812 * Run through the directory, looking for dirs that match the 1813 * expected pattern. 1814 */ 1815 while (1) { 1816 struct dirent* entry = readdir(dir); 1817 if (entry == NULL) { 1818 break; 1819 } 1820 1821 if (isHidden(srcDir.string(), entry->d_name)) { 1822 continue; 1823 } 1824 1825 String8 subdirName(srcDir); 1826 subdirName.appendPath(entry->d_name); 1827 1828 AaptGroupEntry group; 1829 String8 resType; 1830 bool b = group.initFromDirName(entry->d_name, &resType); 1831 if (!b) { 1832 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), 1833 entry->d_name); 1834 err = -1; 1835 continue; 1836 } 1837 1838 if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) { 1839 int maxResInt = atoi(bundle->getMaxResVersion()); 1840 const char *verString = group.version.string(); 1841 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name 1842 if (dirVersionInt > maxResInt) { 1843 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name); 1844 continue; 1845 } 1846 } 1847 1848 FileType type = getFileType(subdirName.string()); 1849 1850 if (type == kFileTypeDirectory) { 1851 sp<AaptDir> dir = makeDir(String8(entry->d_name)); 1852 ssize_t res = dir->slurpFullTree(bundle, subdirName, group, 1853 resType, mFullResPaths); 1854 if (res < 0) { 1855 count = res; 1856 goto bail; 1857 } 1858 if (res > 0) { 1859 mGroupEntries.add(group); 1860 count += res; 1861 } 1862 1863 mDirs.add(dir); 1864 } else { 1865 if (bundle->getVerbose()) { 1866 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string()); 1867 } 1868 } 1869 } 1870 1871bail: 1872 closedir(dir); 1873 dir = NULL; 1874 1875 if (err != 0) { 1876 return err; 1877 } 1878 return count; 1879} 1880 1881ssize_t 1882AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) 1883{ 1884 int count = 0; 1885 SortedVector<AaptGroupEntry> entries; 1886 1887 ZipFile* zip = new ZipFile; 1888 status_t err = zip->open(filename, ZipFile::kOpenReadOnly); 1889 if (err != NO_ERROR) { 1890 fprintf(stderr, "error opening zip file %s\n", filename); 1891 count = err; 1892 delete zip; 1893 return -1; 1894 } 1895 1896 const int N = zip->getNumEntries(); 1897 for (int i=0; i<N; i++) { 1898 ZipEntry* entry = zip->getEntryByIndex(i); 1899 if (entry->getDeleted()) { 1900 continue; 1901 } 1902 1903 String8 entryName(entry->getFileName()); 1904 1905 String8 dirName = entryName.getPathDir(); 1906 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); 1907 1908 String8 resType; 1909 AaptGroupEntry kind; 1910 1911 String8 remain; 1912 if (entryName.walkPath(&remain) == kResourceDir) { 1913 // these are the resources, pull their type out of the directory name 1914 kind.initFromDirName(remain.walkPath().string(), &resType); 1915 } else { 1916 // these are untyped and don't have an AaptGroupEntry 1917 } 1918 if (entries.indexOf(kind) < 0) { 1919 entries.add(kind); 1920 mGroupEntries.add(kind); 1921 } 1922 1923 // use the one from the zip file if they both exist. 1924 dir->removeFile(entryName.getPathLeaf()); 1925 1926 sp<AaptFile> file = new AaptFile(entryName, kind, resType); 1927 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); 1928 if (err != NO_ERROR) { 1929 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); 1930 count = err; 1931 goto bail; 1932 } 1933 file->setCompressionMethod(entry->getCompressionMethod()); 1934 1935#if 0 1936 if (entryName == "AndroidManifest.xml") { 1937 printf("AndroidManifest.xml\n"); 1938 } 1939 printf("\n\nfile: %s\n", entryName.string()); 1940#endif 1941 1942 size_t len = entry->getUncompressedLen(); 1943 void* data = zip->uncompress(entry); 1944 void* buf = file->editData(len); 1945 memcpy(buf, data, len); 1946 1947#if 0 1948 const int OFF = 0; 1949 const unsigned char* p = (unsigned char*)data; 1950 const unsigned char* end = p+len; 1951 p += OFF; 1952 for (int i=0; i<32 && p < end; i++) { 1953 printf("0x%03x ", i*0x10 + OFF); 1954 for (int j=0; j<0x10 && p < end; j++) { 1955 printf(" %02x", *p); 1956 p++; 1957 } 1958 printf("\n"); 1959 } 1960#endif 1961 1962 free(data); 1963 1964 count++; 1965 } 1966 1967bail: 1968 delete zip; 1969 return count; 1970} 1971 1972sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) 1973{ 1974 sp<AaptSymbols> sym = mSymbols.valueFor(name); 1975 if (sym == NULL) { 1976 sym = new AaptSymbols(); 1977 mSymbols.add(name, sym); 1978 } 1979 return sym; 1980} 1981 1982status_t AaptAssets::buildIncludedResources(Bundle* bundle) 1983{ 1984 if (!mHaveIncludedAssets) { 1985 // Add in all includes. 1986 const Vector<const char*>& incl = bundle->getPackageIncludes(); 1987 const size_t N=incl.size(); 1988 for (size_t i=0; i<N; i++) { 1989 if (bundle->getVerbose()) 1990 printf("Including resources from package: %s\n", incl[i]); 1991 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) { 1992 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", 1993 incl[i]); 1994 return UNKNOWN_ERROR; 1995 } 1996 } 1997 mHaveIncludedAssets = true; 1998 } 1999 2000 return NO_ERROR; 2001} 2002 2003status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) 2004{ 2005 const ResTable& res = getIncludedResources(); 2006 // XXX dirty! 2007 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL); 2008} 2009 2010const ResTable& AaptAssets::getIncludedResources() const 2011{ 2012 return mIncludedAssets.getResources(false); 2013} 2014 2015void AaptAssets::print() const 2016{ 2017 printf("Locale/Vendor pairs:\n"); 2018 const size_t N=mGroupEntries.size(); 2019 for (size_t i=0; i<N; i++) { 2020 printf(" %s/%s\n", 2021 mGroupEntries.itemAt(i).locale.string(), 2022 mGroupEntries.itemAt(i).vendor.string()); 2023 } 2024 2025 printf("\nFiles:\n"); 2026 AaptDir::print(); 2027} 2028 2029sp<AaptDir> AaptAssets::resDir(const String8& name) 2030{ 2031 const Vector<sp<AaptDir> >& dirs = mDirs; 2032 const size_t N = dirs.size(); 2033 for (size_t i=0; i<N; i++) { 2034 const sp<AaptDir>& d = dirs.itemAt(i); 2035 if (d->getLeaf() == name) { 2036 return d; 2037 } 2038 } 2039 return NULL; 2040} 2041 2042bool 2043valid_symbol_name(const String8& symbol) 2044{ 2045 static char const * const KEYWORDS[] = { 2046 "abstract", "assert", "boolean", "break", 2047 "byte", "case", "catch", "char", "class", "const", "continue", 2048 "default", "do", "double", "else", "enum", "extends", "final", 2049 "finally", "float", "for", "goto", "if", "implements", "import", 2050 "instanceof", "int", "interface", "long", "native", "new", "package", 2051 "private", "protected", "public", "return", "short", "static", 2052 "strictfp", "super", "switch", "synchronized", "this", "throw", 2053 "throws", "transient", "try", "void", "volatile", "while", 2054 "true", "false", "null", 2055 NULL 2056 }; 2057 const char*const* k = KEYWORDS; 2058 const char*const s = symbol.string(); 2059 while (*k) { 2060 if (0 == strcmp(s, *k)) { 2061 return false; 2062 } 2063 k++; 2064 } 2065 return true; 2066} 2067