OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / tools / aapt / 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 }