AaptAssets.cpp revision 588feee5e771de5ec71da213fbb1cba29392c690
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 int d = atoi(val); 689 if (d != 0) { 690 if (out) out->mnc = d; 691 return true; 692 } 693 694 return false; 695} 696 697/* 698 * Does this directory name fit the pattern of a locale dir ("en-rUS" or 699 * "default")? 700 * 701 * TODO: Should insist that the first two letters are lower case, and the 702 * second two are upper. 703 */ 704bool AaptGroupEntry::getLocaleName(const char* fileName, 705 ResTable_config* out) 706{ 707 if (strcmp(fileName, kWildcardName) == 0 708 || strcmp(fileName, kDefaultLocale) == 0) { 709 if (out) { 710 out->language[0] = 0; 711 out->language[1] = 0; 712 out->country[0] = 0; 713 out->country[1] = 0; 714 } 715 return true; 716 } 717 718 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) { 719 if (out) { 720 out->language[0] = fileName[0]; 721 out->language[1] = fileName[1]; 722 out->country[0] = 0; 723 out->country[1] = 0; 724 } 725 return true; 726 } 727 728 if (strlen(fileName) == 5 && 729 isalpha(fileName[0]) && 730 isalpha(fileName[1]) && 731 fileName[2] == '-' && 732 isalpha(fileName[3]) && 733 isalpha(fileName[4])) { 734 if (out) { 735 out->language[0] = fileName[0]; 736 out->language[1] = fileName[1]; 737 out->country[0] = fileName[3]; 738 out->country[1] = fileName[4]; 739 } 740 return true; 741 } 742 743 return false; 744} 745 746bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, 747 ResTable_config* out) 748{ 749 if (strcmp(name, kWildcardName) == 0) { 750 if (out) out->screenLayout = 751 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 752 | ResTable_config::SCREENSIZE_ANY; 753 return true; 754 } else if (strcmp(name, "small") == 0) { 755 if (out) out->screenLayout = 756 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 757 | ResTable_config::SCREENSIZE_SMALL; 758 return true; 759 } else if (strcmp(name, "normal") == 0) { 760 if (out) out->screenLayout = 761 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 762 | ResTable_config::SCREENSIZE_NORMAL; 763 return true; 764 } else if (strcmp(name, "large") == 0) { 765 if (out) out->screenLayout = 766 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 767 | ResTable_config::SCREENSIZE_LARGE; 768 return true; 769 } 770 771 return false; 772} 773 774bool AaptGroupEntry::getScreenLayoutLongName(const char* name, 775 ResTable_config* out) 776{ 777 if (strcmp(name, kWildcardName) == 0) { 778 if (out) out->screenLayout = 779 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 780 | ResTable_config::SCREENLONG_ANY; 781 return true; 782 } else if (strcmp(name, "long") == 0) { 783 if (out) out->screenLayout = 784 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 785 | ResTable_config::SCREENLONG_YES; 786 return true; 787 } else if (strcmp(name, "notlong") == 0) { 788 if (out) out->screenLayout = 789 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 790 | ResTable_config::SCREENLONG_NO; 791 return true; 792 } 793 794 return false; 795} 796 797bool AaptGroupEntry::getOrientationName(const char* name, 798 ResTable_config* out) 799{ 800 if (strcmp(name, kWildcardName) == 0) { 801 if (out) out->orientation = out->ORIENTATION_ANY; 802 return true; 803 } else if (strcmp(name, "port") == 0) { 804 if (out) out->orientation = out->ORIENTATION_PORT; 805 return true; 806 } else if (strcmp(name, "land") == 0) { 807 if (out) out->orientation = out->ORIENTATION_LAND; 808 return true; 809 } else if (strcmp(name, "square") == 0) { 810 if (out) out->orientation = out->ORIENTATION_SQUARE; 811 return true; 812 } 813 814 return false; 815} 816 817bool AaptGroupEntry::getUiModeTypeName(const char* name, 818 ResTable_config* out) 819{ 820 if (strcmp(name, kWildcardName) == 0) { 821 if (out) out->uiMode = 822 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 823 | ResTable_config::UI_MODE_TYPE_ANY; 824 return true; 825 } else if (strcmp(name, "desk") == 0) { 826 if (out) out->uiMode = 827 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 828 | ResTable_config::UI_MODE_TYPE_DESK; 829 return true; 830 } else if (strcmp(name, "car") == 0) { 831 if (out) out->uiMode = 832 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 833 | ResTable_config::UI_MODE_TYPE_CAR; 834 return true; 835 } 836 837 return false; 838} 839 840bool AaptGroupEntry::getUiModeNightName(const char* name, 841 ResTable_config* out) 842{ 843 if (strcmp(name, kWildcardName) == 0) { 844 if (out) out->uiMode = 845 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 846 | ResTable_config::UI_MODE_NIGHT_ANY; 847 return true; 848 } else if (strcmp(name, "night") == 0) { 849 if (out) out->uiMode = 850 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 851 | ResTable_config::UI_MODE_NIGHT_YES; 852 return true; 853 } else if (strcmp(name, "notnight") == 0) { 854 if (out) out->uiMode = 855 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 856 | ResTable_config::UI_MODE_NIGHT_NO; 857 return true; 858 } 859 860 return false; 861} 862 863bool AaptGroupEntry::getDensityName(const char* name, 864 ResTable_config* out) 865{ 866 if (strcmp(name, kWildcardName) == 0) { 867 if (out) out->density = ResTable_config::DENSITY_DEFAULT; 868 return true; 869 } 870 871 if (strcmp(name, "nodpi") == 0) { 872 if (out) out->density = ResTable_config::DENSITY_NONE; 873 return true; 874 } 875 876 if (strcmp(name, "ldpi") == 0) { 877 if (out) out->density = ResTable_config::DENSITY_LOW; 878 return true; 879 } 880 881 if (strcmp(name, "mdpi") == 0) { 882 if (out) out->density = ResTable_config::DENSITY_MEDIUM; 883 return true; 884 } 885 886 if (strcmp(name, "hdpi") == 0) { 887 if (out) out->density = ResTable_config::DENSITY_HIGH; 888 return true; 889 } 890 891 if (strcmp(name, "xhdpi") == 0) { 892 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2; 893 return true; 894 } 895 896 char* c = (char*)name; 897 while (*c >= '0' && *c <= '9') { 898 c++; 899 } 900 901 // check that we have 'dpi' after the last digit. 902 if (toupper(c[0]) != 'D' || 903 toupper(c[1]) != 'P' || 904 toupper(c[2]) != 'I' || 905 c[3] != 0) { 906 return false; 907 } 908 909 // temporarily replace the first letter with \0 to 910 // use atoi. 911 char tmp = c[0]; 912 c[0] = '\0'; 913 914 int d = atoi(name); 915 c[0] = tmp; 916 917 if (d != 0) { 918 if (out) out->density = d; 919 return true; 920 } 921 922 return false; 923} 924 925bool AaptGroupEntry::getTouchscreenName(const char* name, 926 ResTable_config* out) 927{ 928 if (strcmp(name, kWildcardName) == 0) { 929 if (out) out->touchscreen = out->TOUCHSCREEN_ANY; 930 return true; 931 } else if (strcmp(name, "notouch") == 0) { 932 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; 933 return true; 934 } else if (strcmp(name, "stylus") == 0) { 935 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; 936 return true; 937 } else if (strcmp(name, "finger") == 0) { 938 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; 939 return true; 940 } 941 942 return false; 943} 944 945bool AaptGroupEntry::getKeysHiddenName(const char* name, 946 ResTable_config* out) 947{ 948 uint8_t mask = 0; 949 uint8_t value = 0; 950 if (strcmp(name, kWildcardName) == 0) { 951 mask = ResTable_config::MASK_KEYSHIDDEN; 952 value = ResTable_config::KEYSHIDDEN_ANY; 953 } else if (strcmp(name, "keysexposed") == 0) { 954 mask = ResTable_config::MASK_KEYSHIDDEN; 955 value = ResTable_config::KEYSHIDDEN_NO; 956 } else if (strcmp(name, "keyshidden") == 0) { 957 mask = ResTable_config::MASK_KEYSHIDDEN; 958 value = ResTable_config::KEYSHIDDEN_YES; 959 } else if (strcmp(name, "keyssoft") == 0) { 960 mask = ResTable_config::MASK_KEYSHIDDEN; 961 value = ResTable_config::KEYSHIDDEN_SOFT; 962 } 963 964 if (mask != 0) { 965 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 966 return true; 967 } 968 969 return false; 970} 971 972bool AaptGroupEntry::getKeyboardName(const char* name, 973 ResTable_config* out) 974{ 975 if (strcmp(name, kWildcardName) == 0) { 976 if (out) out->keyboard = out->KEYBOARD_ANY; 977 return true; 978 } else if (strcmp(name, "nokeys") == 0) { 979 if (out) out->keyboard = out->KEYBOARD_NOKEYS; 980 return true; 981 } else if (strcmp(name, "qwerty") == 0) { 982 if (out) out->keyboard = out->KEYBOARD_QWERTY; 983 return true; 984 } else if (strcmp(name, "12key") == 0) { 985 if (out) out->keyboard = out->KEYBOARD_12KEY; 986 return true; 987 } 988 989 return false; 990} 991 992bool AaptGroupEntry::getNavHiddenName(const char* name, 993 ResTable_config* out) 994{ 995 uint8_t mask = 0; 996 uint8_t value = 0; 997 if (strcmp(name, kWildcardName) == 0) { 998 mask = ResTable_config::MASK_NAVHIDDEN; 999 value = ResTable_config::NAVHIDDEN_ANY; 1000 } else if (strcmp(name, "navexposed") == 0) { 1001 mask = ResTable_config::MASK_NAVHIDDEN; 1002 value = ResTable_config::NAVHIDDEN_NO; 1003 } else if (strcmp(name, "navhidden") == 0) { 1004 mask = ResTable_config::MASK_NAVHIDDEN; 1005 value = ResTable_config::NAVHIDDEN_YES; 1006 } 1007 1008 if (mask != 0) { 1009 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1010 return true; 1011 } 1012 1013 return false; 1014} 1015 1016bool AaptGroupEntry::getNavigationName(const char* name, 1017 ResTable_config* out) 1018{ 1019 if (strcmp(name, kWildcardName) == 0) { 1020 if (out) out->navigation = out->NAVIGATION_ANY; 1021 return true; 1022 } else if (strcmp(name, "nonav") == 0) { 1023 if (out) out->navigation = out->NAVIGATION_NONAV; 1024 return true; 1025 } else if (strcmp(name, "dpad") == 0) { 1026 if (out) out->navigation = out->NAVIGATION_DPAD; 1027 return true; 1028 } else if (strcmp(name, "trackball") == 0) { 1029 if (out) out->navigation = out->NAVIGATION_TRACKBALL; 1030 return true; 1031 } else if (strcmp(name, "wheel") == 0) { 1032 if (out) out->navigation = out->NAVIGATION_WHEEL; 1033 return true; 1034 } 1035 1036 return false; 1037} 1038 1039bool AaptGroupEntry::getScreenSizeName(const char* name, 1040 ResTable_config* out) 1041{ 1042 if (strcmp(name, kWildcardName) == 0) { 1043 if (out) { 1044 out->screenWidth = out->SCREENWIDTH_ANY; 1045 out->screenHeight = out->SCREENHEIGHT_ANY; 1046 } 1047 return true; 1048 } 1049 1050 const char* x = name; 1051 while (*x >= '0' && *x <= '9') x++; 1052 if (x == name || *x != 'x') return false; 1053 String8 xName(name, x-name); 1054 x++; 1055 1056 const char* y = x; 1057 while (*y >= '0' && *y <= '9') y++; 1058 if (y == name || *y != 0) return false; 1059 String8 yName(x, y-x); 1060 1061 uint16_t w = (uint16_t)atoi(xName.string()); 1062 uint16_t h = (uint16_t)atoi(yName.string()); 1063 if (w < h) { 1064 return false; 1065 } 1066 1067 if (out) { 1068 out->screenWidth = w; 1069 out->screenHeight = h; 1070 } 1071 1072 return true; 1073} 1074 1075bool AaptGroupEntry::getVersionName(const char* name, 1076 ResTable_config* out) 1077{ 1078 if (strcmp(name, kWildcardName) == 0) { 1079 if (out) { 1080 out->sdkVersion = out->SDKVERSION_ANY; 1081 out->minorVersion = out->MINORVERSION_ANY; 1082 } 1083 return true; 1084 } 1085 1086 if (*name != 'v') { 1087 return false; 1088 } 1089 1090 name++; 1091 const char* s = name; 1092 while (*s >= '0' && *s <= '9') s++; 1093 if (s == name || *s != 0) return false; 1094 String8 sdkName(name, s-name); 1095 1096 if (out) { 1097 out->sdkVersion = (uint16_t)atoi(sdkName.string()); 1098 out->minorVersion = 0; 1099 } 1100 1101 return true; 1102} 1103 1104int AaptGroupEntry::compare(const AaptGroupEntry& o) const 1105{ 1106 int v = mcc.compare(o.mcc); 1107 if (v == 0) v = mnc.compare(o.mnc); 1108 if (v == 0) v = locale.compare(o.locale); 1109 if (v == 0) v = vendor.compare(o.vendor); 1110 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); 1111 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); 1112 if (v == 0) v = orientation.compare(o.orientation); 1113 if (v == 0) v = uiModeType.compare(o.uiModeType); 1114 if (v == 0) v = uiModeNight.compare(o.uiModeNight); 1115 if (v == 0) v = density.compare(o.density); 1116 if (v == 0) v = touchscreen.compare(o.touchscreen); 1117 if (v == 0) v = keysHidden.compare(o.keysHidden); 1118 if (v == 0) v = keyboard.compare(o.keyboard); 1119 if (v == 0) v = navHidden.compare(o.navHidden); 1120 if (v == 0) v = navigation.compare(o.navigation); 1121 if (v == 0) v = screenSize.compare(o.screenSize); 1122 if (v == 0) v = version.compare(o.version); 1123 return v; 1124} 1125 1126ResTable_config AaptGroupEntry::toParams() const 1127{ 1128 ResTable_config params; 1129 memset(¶ms, 0, sizeof(params)); 1130 getMccName(mcc.string(), ¶ms); 1131 getMncName(mnc.string(), ¶ms); 1132 getLocaleName(locale.string(), ¶ms); 1133 getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); 1134 getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); 1135 getOrientationName(orientation.string(), ¶ms); 1136 getUiModeTypeName(uiModeType.string(), ¶ms); 1137 getUiModeNightName(uiModeNight.string(), ¶ms); 1138 getDensityName(density.string(), ¶ms); 1139 getTouchscreenName(touchscreen.string(), ¶ms); 1140 getKeysHiddenName(keysHidden.string(), ¶ms); 1141 getKeyboardName(keyboard.string(), ¶ms); 1142 getNavHiddenName(navHidden.string(), ¶ms); 1143 getNavigationName(navigation.string(), ¶ms); 1144 getScreenSizeName(screenSize.string(), ¶ms); 1145 getVersionName(version.string(), ¶ms); 1146 1147 // Fix up version number based on specified parameters. 1148 int minSdk = 0; 1149 if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) 1150 != ResTable_config::UI_MODE_TYPE_ANY 1151 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) 1152 != ResTable_config::UI_MODE_NIGHT_ANY) { 1153 minSdk = SDK_FROYO; 1154 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) 1155 != ResTable_config::SCREENSIZE_ANY 1156 || (params.screenLayout&ResTable_config::MASK_SCREENLONG) 1157 != ResTable_config::SCREENLONG_ANY 1158 || params.density != ResTable_config::DENSITY_DEFAULT) { 1159 minSdk = SDK_DONUT; 1160 } 1161 1162 if (minSdk > params.sdkVersion) { 1163 params.sdkVersion = minSdk; 1164 } 1165 1166 return params; 1167} 1168 1169// ========================================================================= 1170// ========================================================================= 1171// ========================================================================= 1172 1173void* AaptFile::editData(size_t size) 1174{ 1175 if (size <= mBufferSize) { 1176 mDataSize = size; 1177 return mData; 1178 } 1179 size_t allocSize = (size*3)/2; 1180 void* buf = realloc(mData, allocSize); 1181 if (buf == NULL) { 1182 return NULL; 1183 } 1184 mData = buf; 1185 mDataSize = size; 1186 mBufferSize = allocSize; 1187 return buf; 1188} 1189 1190void* AaptFile::editData(size_t* outSize) 1191{ 1192 if (outSize) { 1193 *outSize = mDataSize; 1194 } 1195 return mData; 1196} 1197 1198void* AaptFile::padData(size_t wordSize) 1199{ 1200 const size_t extra = mDataSize%wordSize; 1201 if (extra == 0) { 1202 return mData; 1203 } 1204 1205 size_t initial = mDataSize; 1206 void* data = editData(initial+(wordSize-extra)); 1207 if (data != NULL) { 1208 memset(((uint8_t*)data) + initial, 0, wordSize-extra); 1209 } 1210 return data; 1211} 1212 1213status_t AaptFile::writeData(const void* data, size_t size) 1214{ 1215 size_t end = mDataSize; 1216 size_t total = size + end; 1217 void* buf = editData(total); 1218 if (buf == NULL) { 1219 return UNKNOWN_ERROR; 1220 } 1221 memcpy(((char*)buf)+end, data, size); 1222 return NO_ERROR; 1223} 1224 1225void AaptFile::clearData() 1226{ 1227 if (mData != NULL) free(mData); 1228 mData = NULL; 1229 mDataSize = 0; 1230 mBufferSize = 0; 1231} 1232 1233String8 AaptFile::getPrintableSource() const 1234{ 1235 if (hasData()) { 1236 String8 name(mGroupEntry.locale.string()); 1237 name.appendPath(mGroupEntry.vendor.string()); 1238 name.appendPath(mPath); 1239 name.append(" #generated"); 1240 return name; 1241 } 1242 return mSourceFile; 1243} 1244 1245// ========================================================================= 1246// ========================================================================= 1247// ========================================================================= 1248 1249status_t AaptGroup::addFile(const sp<AaptFile>& file) 1250{ 1251 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) { 1252 file->mPath = mPath; 1253 mFiles.add(file->getGroupEntry(), file); 1254 return NO_ERROR; 1255 } 1256 1257 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.", 1258 getPrintableSource().string()); 1259 return UNKNOWN_ERROR; 1260} 1261 1262void AaptGroup::removeFile(size_t index) 1263{ 1264 mFiles.removeItemsAt(index); 1265} 1266 1267void AaptGroup::print() const 1268{ 1269 printf(" %s\n", getPath().string()); 1270 const size_t N=mFiles.size(); 1271 size_t i; 1272 for (i=0; i<N; i++) { 1273 sp<AaptFile> file = mFiles.valueAt(i); 1274 const AaptGroupEntry& e = file->getGroupEntry(); 1275 if (file->hasData()) { 1276 printf(" Gen: (%s) %d bytes\n", e.toString().string(), 1277 (int)file->getSize()); 1278 } else { 1279 printf(" Src: %s\n", file->getPrintableSource().string()); 1280 } 1281 } 1282} 1283 1284String8 AaptGroup::getPrintableSource() const 1285{ 1286 if (mFiles.size() > 0) { 1287 // Arbitrarily pull the first source file out of the list. 1288 return mFiles.valueAt(0)->getPrintableSource(); 1289 } 1290 1291 // Should never hit this case, but to be safe... 1292 return getPath(); 1293 1294} 1295 1296// ========================================================================= 1297// ========================================================================= 1298// ========================================================================= 1299 1300status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) 1301{ 1302 if (mFiles.indexOfKey(name) >= 0) { 1303 return ALREADY_EXISTS; 1304 } 1305 mFiles.add(name, file); 1306 return NO_ERROR; 1307} 1308 1309status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) 1310{ 1311 if (mDirs.indexOfKey(name) >= 0) { 1312 return ALREADY_EXISTS; 1313 } 1314 mDirs.add(name, dir); 1315 return NO_ERROR; 1316} 1317 1318sp<AaptDir> AaptDir::makeDir(const String8& path) 1319{ 1320 String8 name; 1321 String8 remain = path; 1322 1323 sp<AaptDir> subdir = this; 1324 while (name = remain.walkPath(&remain), remain != "") { 1325 subdir = subdir->makeDir(name); 1326 } 1327 1328 ssize_t i = subdir->mDirs.indexOfKey(name); 1329 if (i >= 0) { 1330 return subdir->mDirs.valueAt(i); 1331 } 1332 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); 1333 subdir->mDirs.add(name, dir); 1334 return dir; 1335} 1336 1337void AaptDir::removeFile(const String8& name) 1338{ 1339 mFiles.removeItem(name); 1340} 1341 1342void AaptDir::removeDir(const String8& name) 1343{ 1344 mDirs.removeItem(name); 1345} 1346 1347status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName) 1348{ 1349 sp<AaptGroup> origGroup; 1350 1351 // Find and remove the given file with shear, brute force! 1352 const size_t NG = mFiles.size(); 1353 size_t i; 1354 for (i=0; origGroup == NULL && i<NG; i++) { 1355 sp<AaptGroup> g = mFiles.valueAt(i); 1356 const size_t NF = g->getFiles().size(); 1357 for (size_t j=0; j<NF; j++) { 1358 if (g->getFiles().valueAt(j) == file) { 1359 origGroup = g; 1360 g->removeFile(j); 1361 if (NF == 1) { 1362 mFiles.removeItemsAt(i); 1363 } 1364 break; 1365 } 1366 } 1367 } 1368 1369 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string()); 1370 1371 // Place the file under its new name. 1372 if (origGroup != NULL) { 1373 return addLeafFile(newName, file); 1374 } 1375 1376 return NO_ERROR; 1377} 1378 1379status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file) 1380{ 1381 sp<AaptGroup> group; 1382 if (mFiles.indexOfKey(leafName) >= 0) { 1383 group = mFiles.valueFor(leafName); 1384 } else { 1385 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); 1386 mFiles.add(leafName, group); 1387 } 1388 1389 return group->addFile(file); 1390} 1391 1392ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, 1393 const AaptGroupEntry& kind, const String8& resType) 1394{ 1395 Vector<String8> fileNames; 1396 1397 { 1398 DIR* dir = NULL; 1399 1400 dir = opendir(srcDir.string()); 1401 if (dir == NULL) { 1402 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1403 return UNKNOWN_ERROR; 1404 } 1405 1406 /* 1407 * Slurp the filenames out of the directory. 1408 */ 1409 while (1) { 1410 struct dirent* entry; 1411 1412 entry = readdir(dir); 1413 if (entry == NULL) 1414 break; 1415 1416 if (isHidden(srcDir.string(), entry->d_name)) 1417 continue; 1418 1419 fileNames.add(String8(entry->d_name)); 1420 } 1421 1422 closedir(dir); 1423 } 1424 1425 ssize_t count = 0; 1426 1427 /* 1428 * Stash away the files and recursively descend into subdirectories. 1429 */ 1430 const size_t N = fileNames.size(); 1431 size_t i; 1432 for (i = 0; i < N; i++) { 1433 String8 pathName(srcDir); 1434 FileType type; 1435 1436 pathName.appendPath(fileNames[i].string()); 1437 type = getFileType(pathName.string()); 1438 if (type == kFileTypeDirectory) { 1439 sp<AaptDir> subdir; 1440 bool notAdded = false; 1441 if (mDirs.indexOfKey(fileNames[i]) >= 0) { 1442 subdir = mDirs.valueFor(fileNames[i]); 1443 } else { 1444 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); 1445 notAdded = true; 1446 } 1447 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, 1448 resType); 1449 if (res < NO_ERROR) { 1450 return res; 1451 } 1452 if (res > 0 && notAdded) { 1453 mDirs.add(fileNames[i], subdir); 1454 } 1455 count += res; 1456 } else if (type == kFileTypeRegular) { 1457 sp<AaptFile> file = new AaptFile(pathName, kind, resType); 1458 status_t err = addLeafFile(fileNames[i], file); 1459 if (err != NO_ERROR) { 1460 return err; 1461 } 1462 1463 count++; 1464 1465 } else { 1466 if (bundle->getVerbose()) 1467 printf(" (ignoring non-file/dir '%s')\n", pathName.string()); 1468 } 1469 } 1470 1471 return count; 1472} 1473 1474status_t AaptDir::validate() const 1475{ 1476 const size_t NF = mFiles.size(); 1477 const size_t ND = mDirs.size(); 1478 size_t i; 1479 for (i = 0; i < NF; i++) { 1480 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) { 1481 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1482 "Invalid filename. Unable to add."); 1483 return UNKNOWN_ERROR; 1484 } 1485 1486 size_t j; 1487 for (j = i+1; j < NF; j++) { 1488 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1489 mFiles.valueAt(j)->getLeaf().string()) == 0) { 1490 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1491 "File is case-insensitive equivalent to: %s", 1492 mFiles.valueAt(j)->getPrintableSource().string()); 1493 return UNKNOWN_ERROR; 1494 } 1495 1496 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" 1497 // (this is mostly caught by the "marked" stuff, below) 1498 } 1499 1500 for (j = 0; j < ND; j++) { 1501 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1502 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1503 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1504 "File conflicts with dir from: %s", 1505 mDirs.valueAt(j)->getPrintableSource().string()); 1506 return UNKNOWN_ERROR; 1507 } 1508 } 1509 } 1510 1511 for (i = 0; i < ND; i++) { 1512 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) { 1513 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1514 "Invalid directory name, unable to add."); 1515 return UNKNOWN_ERROR; 1516 } 1517 1518 size_t j; 1519 for (j = i+1; j < ND; j++) { 1520 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(), 1521 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1522 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1523 "Directory is case-insensitive equivalent to: %s", 1524 mDirs.valueAt(j)->getPrintableSource().string()); 1525 return UNKNOWN_ERROR; 1526 } 1527 } 1528 1529 status_t err = mDirs.valueAt(i)->validate(); 1530 if (err != NO_ERROR) { 1531 return err; 1532 } 1533 } 1534 1535 return NO_ERROR; 1536} 1537 1538void AaptDir::print() const 1539{ 1540 const size_t ND=getDirs().size(); 1541 size_t i; 1542 for (i=0; i<ND; i++) { 1543 getDirs().valueAt(i)->print(); 1544 } 1545 1546 const size_t NF=getFiles().size(); 1547 for (i=0; i<NF; i++) { 1548 getFiles().valueAt(i)->print(); 1549 } 1550} 1551 1552String8 AaptDir::getPrintableSource() const 1553{ 1554 if (mFiles.size() > 0) { 1555 // Arbitrarily pull the first file out of the list as the source dir. 1556 return mFiles.valueAt(0)->getPrintableSource().getPathDir(); 1557 } 1558 if (mDirs.size() > 0) { 1559 // Or arbitrarily pull the first dir out of the list as the source dir. 1560 return mDirs.valueAt(0)->getPrintableSource().getPathDir(); 1561 } 1562 1563 // Should never hit this case, but to be safe... 1564 return mPath; 1565 1566} 1567 1568// ========================================================================= 1569// ========================================================================= 1570// ========================================================================= 1571 1572sp<AaptFile> AaptAssets::addFile( 1573 const String8& filePath, const AaptGroupEntry& entry, 1574 const String8& srcDir, sp<AaptGroup>* outGroup, 1575 const String8& resType) 1576{ 1577 sp<AaptDir> dir = this; 1578 sp<AaptGroup> group; 1579 sp<AaptFile> file; 1580 String8 root, remain(filePath), partialPath; 1581 while (remain.length() > 0) { 1582 root = remain.walkPath(&remain); 1583 partialPath.appendPath(root); 1584 1585 const String8 rootStr(root); 1586 1587 if (remain.length() == 0) { 1588 ssize_t i = dir->getFiles().indexOfKey(rootStr); 1589 if (i >= 0) { 1590 group = dir->getFiles().valueAt(i); 1591 } else { 1592 group = new AaptGroup(rootStr, filePath); 1593 status_t res = dir->addFile(rootStr, group); 1594 if (res != NO_ERROR) { 1595 return NULL; 1596 } 1597 } 1598 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); 1599 status_t res = group->addFile(file); 1600 if (res != NO_ERROR) { 1601 return NULL; 1602 } 1603 break; 1604 1605 } else { 1606 ssize_t i = dir->getDirs().indexOfKey(rootStr); 1607 if (i >= 0) { 1608 dir = dir->getDirs().valueAt(i); 1609 } else { 1610 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); 1611 status_t res = dir->addDir(rootStr, subdir); 1612 if (res != NO_ERROR) { 1613 return NULL; 1614 } 1615 dir = subdir; 1616 } 1617 } 1618 } 1619 1620 mGroupEntries.add(entry); 1621 if (outGroup) *outGroup = group; 1622 return file; 1623} 1624 1625void AaptAssets::addResource(const String8& leafName, const String8& path, 1626 const sp<AaptFile>& file, const String8& resType) 1627{ 1628 sp<AaptDir> res = AaptDir::makeDir(kResString); 1629 String8 dirname = file->getGroupEntry().toDirName(resType); 1630 sp<AaptDir> subdir = res->makeDir(dirname); 1631 sp<AaptGroup> grr = new AaptGroup(leafName, path); 1632 grr->addFile(file); 1633 1634 subdir->addFile(leafName, grr); 1635} 1636 1637 1638ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) 1639{ 1640 int count; 1641 int totalCount = 0; 1642 FileType type; 1643 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); 1644 const size_t dirCount =resDirs.size(); 1645 sp<AaptAssets> current = this; 1646 1647 const int N = bundle->getFileSpecCount(); 1648 1649 /* 1650 * If a package manifest was specified, include that first. 1651 */ 1652 if (bundle->getAndroidManifestFile() != NULL) { 1653 // place at root of zip. 1654 String8 srcFile(bundle->getAndroidManifestFile()); 1655 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), 1656 NULL, String8()); 1657 totalCount++; 1658 } 1659 1660 /* 1661 * If a directory of custom assets was supplied, slurp 'em up. 1662 */ 1663 if (bundle->getAssetSourceDir()) { 1664 const char* assetDir = bundle->getAssetSourceDir(); 1665 1666 FileType type = getFileType(assetDir); 1667 if (type == kFileTypeNonexistent) { 1668 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir); 1669 return UNKNOWN_ERROR; 1670 } 1671 if (type != kFileTypeDirectory) { 1672 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1673 return UNKNOWN_ERROR; 1674 } 1675 1676 String8 assetRoot(assetDir); 1677 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); 1678 AaptGroupEntry group; 1679 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, 1680 String8()); 1681 if (count < 0) { 1682 totalCount = count; 1683 goto bail; 1684 } 1685 if (count > 0) { 1686 mGroupEntries.add(group); 1687 } 1688 totalCount += count; 1689 1690 if (bundle->getVerbose()) 1691 printf("Found %d custom asset file%s in %s\n", 1692 count, (count==1) ? "" : "s", assetDir); 1693 } 1694 1695 /* 1696 * If a directory of resource-specific assets was supplied, slurp 'em up. 1697 */ 1698 for (size_t i=0; i<dirCount; i++) { 1699 const char *res = resDirs[i]; 1700 if (res) { 1701 type = getFileType(res); 1702 if (type == kFileTypeNonexistent) { 1703 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); 1704 return UNKNOWN_ERROR; 1705 } 1706 if (type == kFileTypeDirectory) { 1707 if (i>0) { 1708 sp<AaptAssets> nextOverlay = new AaptAssets(); 1709 current->setOverlay(nextOverlay); 1710 current = nextOverlay; 1711 } 1712 count = current->slurpResourceTree(bundle, String8(res)); 1713 1714 if (count < 0) { 1715 totalCount = count; 1716 goto bail; 1717 } 1718 totalCount += count; 1719 } 1720 else { 1721 fprintf(stderr, "ERROR: '%s' is not a directory\n", res); 1722 return UNKNOWN_ERROR; 1723 } 1724 } 1725 1726 } 1727 /* 1728 * Now do any additional raw files. 1729 */ 1730 for (int arg=0; arg<N; arg++) { 1731 const char* assetDir = bundle->getFileSpecEntry(arg); 1732 1733 FileType type = getFileType(assetDir); 1734 if (type == kFileTypeNonexistent) { 1735 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); 1736 return UNKNOWN_ERROR; 1737 } 1738 if (type != kFileTypeDirectory) { 1739 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1740 return UNKNOWN_ERROR; 1741 } 1742 1743 String8 assetRoot(assetDir); 1744 1745 if (bundle->getVerbose()) 1746 printf("Processing raw dir '%s'\n", (const char*) assetDir); 1747 1748 /* 1749 * Do a recursive traversal of subdir tree. We don't make any 1750 * guarantees about ordering, so we're okay with an inorder search 1751 * using whatever order the OS happens to hand back to us. 1752 */ 1753 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8()); 1754 if (count < 0) { 1755 /* failure; report error and remove archive */ 1756 totalCount = count; 1757 goto bail; 1758 } 1759 totalCount += count; 1760 1761 if (bundle->getVerbose()) 1762 printf("Found %d asset file%s in %s\n", 1763 count, (count==1) ? "" : "s", assetDir); 1764 } 1765 1766 count = validate(); 1767 if (count != NO_ERROR) { 1768 totalCount = count; 1769 goto bail; 1770 } 1771 1772 1773bail: 1774 return totalCount; 1775} 1776 1777ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, 1778 const AaptGroupEntry& kind, 1779 const String8& resType) 1780{ 1781 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType); 1782 if (res > 0) { 1783 mGroupEntries.add(kind); 1784 } 1785 1786 return res; 1787} 1788 1789ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) 1790{ 1791 ssize_t err = 0; 1792 1793 DIR* dir = opendir(srcDir.string()); 1794 if (dir == NULL) { 1795 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1796 return UNKNOWN_ERROR; 1797 } 1798 1799 status_t count = 0; 1800 1801 /* 1802 * Run through the directory, looking for dirs that match the 1803 * expected pattern. 1804 */ 1805 while (1) { 1806 struct dirent* entry = readdir(dir); 1807 if (entry == NULL) { 1808 break; 1809 } 1810 1811 if (isHidden(srcDir.string(), entry->d_name)) { 1812 continue; 1813 } 1814 1815 String8 subdirName(srcDir); 1816 subdirName.appendPath(entry->d_name); 1817 1818 AaptGroupEntry group; 1819 String8 resType; 1820 bool b = group.initFromDirName(entry->d_name, &resType); 1821 if (!b) { 1822 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), 1823 entry->d_name); 1824 err = -1; 1825 continue; 1826 } 1827 1828 FileType type = getFileType(subdirName.string()); 1829 1830 if (type == kFileTypeDirectory) { 1831 sp<AaptDir> dir = makeDir(String8(entry->d_name)); 1832 ssize_t res = dir->slurpFullTree(bundle, subdirName, group, 1833 resType); 1834 if (res < 0) { 1835 count = res; 1836 goto bail; 1837 } 1838 if (res > 0) { 1839 mGroupEntries.add(group); 1840 count += res; 1841 } 1842 1843 mDirs.add(dir); 1844 } else { 1845 if (bundle->getVerbose()) { 1846 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string()); 1847 } 1848 } 1849 } 1850 1851bail: 1852 closedir(dir); 1853 dir = NULL; 1854 1855 if (err != 0) { 1856 return err; 1857 } 1858 return count; 1859} 1860 1861ssize_t 1862AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) 1863{ 1864 int count = 0; 1865 SortedVector<AaptGroupEntry> entries; 1866 1867 ZipFile* zip = new ZipFile; 1868 status_t err = zip->open(filename, ZipFile::kOpenReadOnly); 1869 if (err != NO_ERROR) { 1870 fprintf(stderr, "error opening zip file %s\n", filename); 1871 count = err; 1872 delete zip; 1873 return -1; 1874 } 1875 1876 const int N = zip->getNumEntries(); 1877 for (int i=0; i<N; i++) { 1878 ZipEntry* entry = zip->getEntryByIndex(i); 1879 if (entry->getDeleted()) { 1880 continue; 1881 } 1882 1883 String8 entryName(entry->getFileName()); 1884 1885 String8 dirName = entryName.getPathDir(); 1886 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); 1887 1888 String8 resType; 1889 AaptGroupEntry kind; 1890 1891 String8 remain; 1892 if (entryName.walkPath(&remain) == kResourceDir) { 1893 // these are the resources, pull their type out of the directory name 1894 kind.initFromDirName(remain.walkPath().string(), &resType); 1895 } else { 1896 // these are untyped and don't have an AaptGroupEntry 1897 } 1898 if (entries.indexOf(kind) < 0) { 1899 entries.add(kind); 1900 mGroupEntries.add(kind); 1901 } 1902 1903 // use the one from the zip file if they both exist. 1904 dir->removeFile(entryName.getPathLeaf()); 1905 1906 sp<AaptFile> file = new AaptFile(entryName, kind, resType); 1907 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); 1908 if (err != NO_ERROR) { 1909 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); 1910 count = err; 1911 goto bail; 1912 } 1913 file->setCompressionMethod(entry->getCompressionMethod()); 1914 1915#if 0 1916 if (entryName == "AndroidManifest.xml") { 1917 printf("AndroidManifest.xml\n"); 1918 } 1919 printf("\n\nfile: %s\n", entryName.string()); 1920#endif 1921 1922 size_t len = entry->getUncompressedLen(); 1923 void* data = zip->uncompress(entry); 1924 void* buf = file->editData(len); 1925 memcpy(buf, data, len); 1926 1927#if 0 1928 const int OFF = 0; 1929 const unsigned char* p = (unsigned char*)data; 1930 const unsigned char* end = p+len; 1931 p += OFF; 1932 for (int i=0; i<32 && p < end; i++) { 1933 printf("0x%03x ", i*0x10 + OFF); 1934 for (int j=0; j<0x10 && p < end; j++) { 1935 printf(" %02x", *p); 1936 p++; 1937 } 1938 printf("\n"); 1939 } 1940#endif 1941 1942 free(data); 1943 1944 count++; 1945 } 1946 1947bail: 1948 delete zip; 1949 return count; 1950} 1951 1952sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) 1953{ 1954 sp<AaptSymbols> sym = mSymbols.valueFor(name); 1955 if (sym == NULL) { 1956 sym = new AaptSymbols(); 1957 mSymbols.add(name, sym); 1958 } 1959 return sym; 1960} 1961 1962status_t AaptAssets::buildIncludedResources(Bundle* bundle) 1963{ 1964 if (!mHaveIncludedAssets) { 1965 // Add in all includes. 1966 const Vector<const char*>& incl = bundle->getPackageIncludes(); 1967 const size_t N=incl.size(); 1968 for (size_t i=0; i<N; i++) { 1969 if (bundle->getVerbose()) 1970 printf("Including resources from package: %s\n", incl[i]); 1971 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) { 1972 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", 1973 incl[i]); 1974 return UNKNOWN_ERROR; 1975 } 1976 } 1977 mHaveIncludedAssets = true; 1978 } 1979 1980 return NO_ERROR; 1981} 1982 1983status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) 1984{ 1985 const ResTable& res = getIncludedResources(); 1986 // XXX dirty! 1987 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL); 1988} 1989 1990const ResTable& AaptAssets::getIncludedResources() const 1991{ 1992 return mIncludedAssets.getResources(false); 1993} 1994 1995void AaptAssets::print() const 1996{ 1997 printf("Locale/Vendor pairs:\n"); 1998 const size_t N=mGroupEntries.size(); 1999 for (size_t i=0; i<N; i++) { 2000 printf(" %s/%s\n", 2001 mGroupEntries.itemAt(i).locale.string(), 2002 mGroupEntries.itemAt(i).vendor.string()); 2003 } 2004 2005 printf("\nFiles:\n"); 2006 AaptDir::print(); 2007} 2008 2009sp<AaptDir> AaptAssets::resDir(const String8& name) 2010{ 2011 const Vector<sp<AaptDir> >& dirs = mDirs; 2012 const size_t N = dirs.size(); 2013 for (size_t i=0; i<N; i++) { 2014 const sp<AaptDir>& d = dirs.itemAt(i); 2015 if (d->getLeaf() == name) { 2016 return d; 2017 } 2018 } 2019 return NULL; 2020} 2021 2022bool 2023valid_symbol_name(const String8& symbol) 2024{ 2025 static char const * const KEYWORDS[] = { 2026 "abstract", "assert", "boolean", "break", 2027 "byte", "case", "catch", "char", "class", "const", "continue", 2028 "default", "do", "double", "else", "enum", "extends", "final", 2029 "finally", "float", "for", "goto", "if", "implements", "import", 2030 "instanceof", "int", "interface", "long", "native", "new", "package", 2031 "private", "protected", "public", "return", "short", "static", 2032 "strictfp", "super", "switch", "synchronized", "this", "throw", 2033 "throws", "transient", "try", "void", "volatile", "while", 2034 "true", "false", "null", 2035 NULL 2036 }; 2037 const char*const* k = KEYWORDS; 2038 const char*const s = symbol.string(); 2039 while (*k) { 2040 if (0 == strcmp(s, *k)) { 2041 return false; 2042 } 2043 k++; 2044 } 2045 return true; 2046} 2047