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