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