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