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