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