| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1 | // | 
|  | 2 | // Copyright 2006 The Android Open Source Project | 
|  | 3 | // | 
|  | 4 |  | 
|  | 5 | #include "AaptAssets.h" | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 6 | #include "AaptConfig.h" | 
|  | 7 | #include "AaptUtil.h" | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 8 | #include "Main.h" | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 9 | #include "ResourceFilter.h" | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 10 | #include "Utils.h" | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 11 |  | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 12 | #include <androidfw/PathUtils.h> | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 13 | #include <utils/misc.h> | 
|  | 14 | #include <utils/SortedVector.h> | 
|  | 15 |  | 
|  | 16 | #include <ctype.h> | 
|  | 17 | #include <dirent.h> | 
|  | 18 | #include <errno.h> | 
|  | 19 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 20 | static const char* kAssetDir = "assets"; | 
|  | 21 | static const char* kResourceDir = "res"; | 
|  | 22 | static const char* kValuesDir = "values"; | 
|  | 23 | static const char* kMipmapDir = "mipmap"; | 
|  | 24 | static const char* kInvalidChars = "/\\:"; | 
|  | 25 | static const size_t kMaxAssetFileName = 100; | 
|  | 26 |  | 
|  | 27 | static const String8 kResString(kResourceDir); | 
|  | 28 |  | 
|  | 29 | /* | 
|  | 30 | * Names of asset files must meet the following criteria: | 
|  | 31 | * | 
|  | 32 | *  - the filename length must be less than kMaxAssetFileName bytes long | 
|  | 33 | *    (and can't be empty) | 
|  | 34 | *  - all characters must be 7-bit printable ASCII | 
|  | 35 | *  - none of { '/' '\\' ':' } | 
|  | 36 | * | 
|  | 37 | * Pass in just the filename, not the full path. | 
|  | 38 | */ | 
|  | 39 | static bool validateFileName(const char* fileName) | 
|  | 40 | { | 
|  | 41 | const char* cp = fileName; | 
|  | 42 | size_t len = 0; | 
|  | 43 |  | 
|  | 44 | while (*cp != '\0') { | 
|  | 45 | if ((*cp & 0x80) != 0) | 
|  | 46 | return false;           // reject high ASCII | 
|  | 47 | if (*cp < 0x20 || *cp >= 0x7f) | 
|  | 48 | return false;           // reject control chars and 0x7f | 
|  | 49 | if (strchr(kInvalidChars, *cp) != NULL) | 
|  | 50 | return false;           // reject path sep chars | 
|  | 51 | cp++; | 
|  | 52 | len++; | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 | if (len < 1 || len > kMaxAssetFileName) | 
|  | 56 | return false;               // reject empty or too long | 
|  | 57 |  | 
|  | 58 | return true; | 
|  | 59 | } | 
|  | 60 |  | 
|  | 61 | // The default to use if no other ignore pattern is defined. | 
|  | 62 | const char * const gDefaultIgnoreAssets = | 
|  | 63 | "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"; | 
|  | 64 | // The ignore pattern that can be passed via --ignore-assets in Main.cpp | 
|  | 65 | const char * gUserIgnoreAssets = NULL; | 
|  | 66 |  | 
|  | 67 | static bool isHidden(const char *root, const char *path) | 
|  | 68 | { | 
|  | 69 | // Patterns syntax: | 
|  | 70 | // - Delimiter is : | 
|  | 71 | // - Entry can start with the flag ! to avoid printing a warning | 
|  | 72 | //   about the file being ignored. | 
|  | 73 | // - Entry can have the flag "<dir>" to match only directories | 
|  | 74 | //   or <file> to match only files. Default is to match both. | 
|  | 75 | // - Entry can be a simplified glob "<prefix>*" or "*<suffix>" | 
|  | 76 | //   where prefix/suffix must have at least 1 character (so that | 
|  | 77 | //   we don't match a '*' catch-all pattern.) | 
|  | 78 | // - The special filenames "." and ".." are always ignored. | 
|  | 79 | // - Otherwise the full string is matched. | 
|  | 80 | // - match is not case-sensitive. | 
|  | 81 |  | 
|  | 82 | if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) { | 
|  | 83 | return true; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | const char *delim = ":"; | 
|  | 87 | const char *p = gUserIgnoreAssets; | 
|  | 88 | if (!p || !p[0]) { | 
|  | 89 | p = getenv("ANDROID_AAPT_IGNORE"); | 
|  | 90 | } | 
|  | 91 | if (!p || !p[0]) { | 
|  | 92 | p = gDefaultIgnoreAssets; | 
|  | 93 | } | 
|  | 94 | char *patterns = strdup(p); | 
|  | 95 |  | 
|  | 96 | bool ignore = false; | 
|  | 97 | bool chatty = true; | 
|  | 98 | char *matchedPattern = NULL; | 
|  | 99 |  | 
|  | 100 | String8 fullPath(root); | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 101 | appendPath(fullPath, String8(path)); | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 102 | FileType type = getFileType(fullPath.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 103 |  | 
|  | 104 | int plen = strlen(path); | 
|  | 105 |  | 
|  | 106 | // Note: we don't have strtok_r under mingw. | 
|  | 107 | for(char *token = strtok(patterns, delim); | 
|  | 108 | !ignore && token != NULL; | 
|  | 109 | token = strtok(NULL, delim)) { | 
|  | 110 | chatty = token[0] != '!'; | 
|  | 111 | if (!chatty) token++; // skip ! | 
|  | 112 | if (strncasecmp(token, "<dir>" , 5) == 0) { | 
|  | 113 | if (type != kFileTypeDirectory) continue; | 
|  | 114 | token += 5; | 
|  | 115 | } | 
|  | 116 | if (strncasecmp(token, "<file>", 6) == 0) { | 
|  | 117 | if (type != kFileTypeRegular) continue; | 
|  | 118 | token += 6; | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | matchedPattern = token; | 
|  | 122 | int n = strlen(token); | 
|  | 123 |  | 
|  | 124 | if (token[0] == '*') { | 
|  | 125 | // Match *suffix | 
|  | 126 | token++; | 
|  | 127 | n--; | 
|  | 128 | if (n <= plen) { | 
|  | 129 | ignore = strncasecmp(token, path + plen - n, n) == 0; | 
|  | 130 | } | 
|  | 131 | } else if (n > 1 && token[n - 1] == '*') { | 
|  | 132 | // Match prefix* | 
|  | 133 | ignore = strncasecmp(token, path, n - 1) == 0; | 
|  | 134 | } else { | 
|  | 135 | ignore = strcasecmp(token, path) == 0; | 
|  | 136 | } | 
|  | 137 | } | 
|  | 138 |  | 
|  | 139 | if (ignore && chatty) { | 
|  | 140 | fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n", | 
|  | 141 | type == kFileTypeDirectory ? "dir" : "file", | 
|  | 142 | path, | 
|  | 143 | matchedPattern ? matchedPattern : ""); | 
|  | 144 | } | 
|  | 145 |  | 
|  | 146 | free(patterns); | 
|  | 147 | return ignore; | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | // ========================================================================= | 
|  | 151 | // ========================================================================= | 
|  | 152 | // ========================================================================= | 
|  | 153 |  | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 154 | /* static */ | 
|  | 155 | inline bool isAlpha(const String8& string) { | 
|  | 156 | const size_t length = string.length(); | 
|  | 157 | for (size_t i = 0; i < length; ++i) { | 
|  | 158 | if (!isalpha(string[i])) { | 
|  | 159 | return false; | 
|  | 160 | } | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | return true; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | /* static */ | 
|  | 167 | inline bool isNumber(const String8& string) { | 
|  | 168 | const size_t length = string.length(); | 
|  | 169 | for (size_t i = 0; i < length; ++i) { | 
|  | 170 | if (!isdigit(string[i])) { | 
|  | 171 | return false; | 
|  | 172 | } | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | return true; | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 | void AaptLocaleValue::setLanguage(const char* languageChars) { | 
|  | 179 | size_t i = 0; | 
| Andreas Gampe | b8dc7bc | 2014-10-01 19:07:51 -0700 | [diff] [blame] | 180 | while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) { | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 181 | language[i++] = tolower(*languageChars); | 
|  | 182 | languageChars++; | 
|  | 183 | } | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | void AaptLocaleValue::setRegion(const char* regionChars) { | 
|  | 187 | size_t i = 0; | 
| Andreas Gampe | b8dc7bc | 2014-10-01 19:07:51 -0700 | [diff] [blame] | 188 | while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) { | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 189 | region[i++] = toupper(*regionChars); | 
|  | 190 | regionChars++; | 
|  | 191 | } | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | void AaptLocaleValue::setScript(const char* scriptChars) { | 
|  | 195 | size_t i = 0; | 
| Andreas Gampe | b8dc7bc | 2014-10-01 19:07:51 -0700 | [diff] [blame] | 196 | while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) { | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 197 | if (i == 0) { | 
|  | 198 | script[i++] = toupper(*scriptChars); | 
|  | 199 | } else { | 
|  | 200 | script[i++] = tolower(*scriptChars); | 
|  | 201 | } | 
|  | 202 | scriptChars++; | 
|  | 203 | } | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | void AaptLocaleValue::setVariant(const char* variantChars) { | 
|  | 207 | size_t i = 0; | 
| Andreas Gampe | b8dc7bc | 2014-10-01 19:07:51 -0700 | [diff] [blame] | 208 | while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) { | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 209 | variant[i++] = *variantChars; | 
|  | 210 | variantChars++; | 
|  | 211 | } | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 | bool AaptLocaleValue::initFromFilterString(const String8& str) { | 
|  | 215 | // A locale (as specified in the filter) is an underscore separated name such | 
|  | 216 | // as "en_US", "en_Latn_US", or "en_US_POSIX". | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 217 | Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_'); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 218 |  | 
|  | 219 | const int numTags = parts.size(); | 
|  | 220 | bool valid = false; | 
|  | 221 | if (numTags >= 1) { | 
|  | 222 | const String8& lang = parts[0]; | 
|  | 223 | if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 224 | setLanguage(lang.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 225 | valid = true; | 
|  | 226 | } | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | if (!valid || numTags == 1) { | 
|  | 230 | return valid; | 
|  | 231 | } | 
|  | 232 |  | 
|  | 233 | // At this point, valid == true && numTags > 1. | 
|  | 234 | const String8& part2 = parts[1]; | 
|  | 235 | if ((part2.length() == 2 && isAlpha(part2)) || | 
|  | 236 | (part2.length() == 3 && isNumber(part2))) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 237 | setRegion(part2.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 238 | } else if (part2.length() == 4 && isAlpha(part2)) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 239 | setScript(part2.c_str()); | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 240 | } else if (part2.length() >= 4 && part2.length() <= 8) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 241 | setVariant(part2.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 242 | } else { | 
|  | 243 | valid = false; | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | if (!valid || numTags == 2) { | 
|  | 247 | return valid; | 
|  | 248 | } | 
|  | 249 |  | 
|  | 250 | // At this point, valid == true && numTags > 1. | 
|  | 251 | const String8& part3 = parts[2]; | 
|  | 252 | if (((part3.length() == 2 && isAlpha(part3)) || | 
|  | 253 | (part3.length() == 3 && isNumber(part3))) && script[0]) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 254 | setRegion(part3.c_str()); | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 255 | } else if (part3.length() >= 4 && part3.length() <= 8) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 256 | setVariant(part3.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 257 | } else { | 
|  | 258 | valid = false; | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | if (!valid || numTags == 3) { | 
|  | 262 | return valid; | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | const String8& part4 = parts[3]; | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 266 | if (part4.length() >= 4 && part4.length() <= 8) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 267 | setVariant(part4.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 268 | } else { | 
|  | 269 | valid = false; | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | if (!valid || numTags > 4) { | 
|  | 273 | return false; | 
|  | 274 | } | 
|  | 275 |  | 
|  | 276 | return true; | 
|  | 277 | } | 
|  | 278 |  | 
|  | 279 | int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) { | 
|  | 280 | const int size = parts.size(); | 
|  | 281 | int currentIndex = startIndex; | 
|  | 282 |  | 
|  | 283 | String8 part = parts[currentIndex]; | 
|  | 284 | if (part[0] == 'b' && part[1] == '+') { | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 285 | // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags, | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 286 | // except that the separator is "+" and not "-". | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 287 | Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+'); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 288 | subtags.removeItemsAt(0); | 
|  | 289 | if (subtags.size() == 1) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 290 | setLanguage(subtags[0].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 291 | } else if (subtags.size() == 2) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 292 | setLanguage(subtags[0].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 293 |  | 
|  | 294 | // The second tag can either be a region, a variant or a script. | 
|  | 295 | switch (subtags[1].size()) { | 
|  | 296 | case 2: | 
|  | 297 | case 3: | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 298 | setRegion(subtags[1].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 299 | break; | 
|  | 300 | case 4: | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 301 | if (isAlpha(subtags[1])) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 302 | setScript(subtags[1].c_str()); | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 303 | break; | 
|  | 304 | } | 
|  | 305 | // This is not alphabetical, so we fall through to variant | 
| Stephen Hines | d17f06d | 2019-05-08 16:37:20 -0700 | [diff] [blame] | 306 | [[fallthrough]]; | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 307 | case 5: | 
|  | 308 | case 6: | 
|  | 309 | case 7: | 
|  | 310 | case 8: | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 311 | setVariant(subtags[1].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 312 | break; | 
|  | 313 | default: | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 314 | fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 315 | part.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 316 | return -1; | 
|  | 317 | } | 
|  | 318 | } else if (subtags.size() == 3) { | 
|  | 319 | // The language is always the first subtag. | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 320 | setLanguage(subtags[0].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 321 |  | 
|  | 322 | // The second subtag can either be a script or a region code. | 
|  | 323 | // If its size is 4, it's a script code, else it's a region code. | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 324 | if (subtags[1].size() == 4) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 325 | setScript(subtags[1].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 326 | } else if (subtags[1].size() == 2 || subtags[1].size() == 3) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 327 | setRegion(subtags[1].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 328 | } else { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 329 | fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 330 | return -1; | 
|  | 331 | } | 
|  | 332 |  | 
|  | 333 | // The third tag can either be a region code (if the second tag was | 
|  | 334 | // a script), else a variant code. | 
| Roozbeh Pournader | b927c55 | 2016-01-15 11:23:42 -0800 | [diff] [blame] | 335 | if (subtags[2].size() >= 4) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 336 | setVariant(subtags[2].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 337 | } else { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 338 | setRegion(subtags[2].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 339 | } | 
|  | 340 | } else if (subtags.size() == 4) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 341 | setLanguage(subtags[0].c_str()); | 
|  | 342 | setScript(subtags[1].c_str()); | 
|  | 343 | setRegion(subtags[2].c_str()); | 
|  | 344 | setVariant(subtags[3].c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 345 | } else { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 346 | fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name: %s\n", part.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 347 | return -1; | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | return ++currentIndex; | 
|  | 351 | } else { | 
| Narayan Kamath | 7f1a895 | 2015-02-10 16:11:55 +0000 | [diff] [blame] | 352 | if ((part.length() == 2 || part.length() == 3) | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 353 | && isAlpha(part) && strcmp("car", part.c_str())) { | 
| Tomasz Wasilczyk | 835dfe5 | 2023-08-17 16:27:22 +0000 | [diff] [blame] | 354 | setLanguage(part.c_str()); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 355 | if (++currentIndex == size) { | 
|  | 356 | return size; | 
|  | 357 | } | 
|  | 358 | } else { | 
|  | 359 | return currentIndex; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | part = parts[currentIndex]; | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 363 | if (part.c_str()[0] == 'r' && part.length() == 3) { | 
|  | 364 | setRegion(part.c_str() + 1); | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 365 | if (++currentIndex == size) { | 
|  | 366 | return size; | 
|  | 367 | } | 
|  | 368 | } | 
|  | 369 | } | 
|  | 370 |  | 
|  | 371 | return currentIndex; | 
|  | 372 | } | 
|  | 373 |  | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 374 | void AaptLocaleValue::initFromResTable(const ResTable_config& config) { | 
|  | 375 | config.unpackLanguage(language); | 
|  | 376 | config.unpackRegion(region); | 
| Roozbeh Pournader | 7960898 | 2016-03-03 15:06:46 -0800 | [diff] [blame] | 377 | if (config.localeScript[0] && !config.localeScriptWasComputed) { | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 378 | memcpy(script, config.localeScript, sizeof(config.localeScript)); | 
|  | 379 | } | 
|  | 380 |  | 
|  | 381 | if (config.localeVariant[0]) { | 
|  | 382 | memcpy(variant, config.localeVariant, sizeof(config.localeVariant)); | 
|  | 383 | } | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 | void AaptLocaleValue::writeTo(ResTable_config* out) const { | 
|  | 387 | out->packLanguage(language); | 
|  | 388 | out->packRegion(region); | 
|  | 389 |  | 
|  | 390 | if (script[0]) { | 
|  | 391 | memcpy(out->localeScript, script, sizeof(out->localeScript)); | 
|  | 392 | } | 
|  | 393 |  | 
|  | 394 | if (variant[0]) { | 
|  | 395 | memcpy(out->localeVariant, variant, sizeof(out->localeVariant)); | 
|  | 396 | } | 
|  | 397 | } | 
|  | 398 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 399 | bool | 
|  | 400 | AaptGroupEntry::initFromDirName(const char* dir, String8* resType) | 
|  | 401 | { | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 402 | const char* q = strchr(dir, '-'); | 
|  | 403 | size_t typeLen; | 
|  | 404 | if (q != NULL) { | 
|  | 405 | typeLen = q - dir; | 
|  | 406 | } else { | 
|  | 407 | typeLen = strlen(dir); | 
|  | 408 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 409 |  | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 410 | String8 type(dir, typeLen); | 
|  | 411 | if (!isValidResourceType(type)) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 412 | return false; | 
|  | 413 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 414 |  | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 415 | if (q != NULL) { | 
|  | 416 | if (!AaptConfig::parse(String8(q + 1), &mParams)) { | 
|  | 417 | return false; | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 418 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 419 | } | 
|  | 420 |  | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 421 | *resType = type; | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 422 | return true; | 
|  | 423 | } | 
|  | 424 |  | 
|  | 425 | String8 | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 426 | AaptGroupEntry::toDirName(const String8& resType) const | 
|  | 427 | { | 
|  | 428 | String8 s = resType; | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 429 | String8 params = mParams.toString(); | 
|  | 430 | if (params.length() > 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 431 | if (s.length() > 0) { | 
|  | 432 | s += "-"; | 
|  | 433 | } | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 434 | s += params; | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 435 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 436 | return s; | 
|  | 437 | } | 
|  | 438 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 439 |  | 
|  | 440 | // ========================================================================= | 
|  | 441 | // ========================================================================= | 
|  | 442 | // ========================================================================= | 
|  | 443 |  | 
|  | 444 | void* AaptFile::editData(size_t size) | 
|  | 445 | { | 
|  | 446 | if (size <= mBufferSize) { | 
|  | 447 | mDataSize = size; | 
|  | 448 | return mData; | 
|  | 449 | } | 
|  | 450 | size_t allocSize = (size*3)/2; | 
|  | 451 | void* buf = realloc(mData, allocSize); | 
|  | 452 | if (buf == NULL) { | 
|  | 453 | return NULL; | 
|  | 454 | } | 
|  | 455 | mData = buf; | 
|  | 456 | mDataSize = size; | 
|  | 457 | mBufferSize = allocSize; | 
|  | 458 | return buf; | 
|  | 459 | } | 
|  | 460 |  | 
| Adam Lesinski | de898ff | 2014-01-29 18:20:45 -0800 | [diff] [blame] | 461 | void* AaptFile::editDataInRange(size_t offset, size_t size) | 
|  | 462 | { | 
|  | 463 | return (void*)(((uint8_t*) editData(offset + size)) + offset); | 
|  | 464 | } | 
|  | 465 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 466 | void* AaptFile::editData(size_t* outSize) | 
|  | 467 | { | 
|  | 468 | if (outSize) { | 
|  | 469 | *outSize = mDataSize; | 
|  | 470 | } | 
|  | 471 | return mData; | 
|  | 472 | } | 
|  | 473 |  | 
|  | 474 | void* AaptFile::padData(size_t wordSize) | 
|  | 475 | { | 
|  | 476 | const size_t extra = mDataSize%wordSize; | 
|  | 477 | if (extra == 0) { | 
|  | 478 | return mData; | 
|  | 479 | } | 
|  | 480 |  | 
|  | 481 | size_t initial = mDataSize; | 
|  | 482 | void* data = editData(initial+(wordSize-extra)); | 
|  | 483 | if (data != NULL) { | 
|  | 484 | memset(((uint8_t*)data) + initial, 0, wordSize-extra); | 
|  | 485 | } | 
|  | 486 | return data; | 
|  | 487 | } | 
|  | 488 |  | 
|  | 489 | status_t AaptFile::writeData(const void* data, size_t size) | 
|  | 490 | { | 
|  | 491 | size_t end = mDataSize; | 
|  | 492 | size_t total = size + end; | 
|  | 493 | void* buf = editData(total); | 
|  | 494 | if (buf == NULL) { | 
|  | 495 | return UNKNOWN_ERROR; | 
|  | 496 | } | 
|  | 497 | memcpy(((char*)buf)+end, data, size); | 
|  | 498 | return NO_ERROR; | 
|  | 499 | } | 
|  | 500 |  | 
|  | 501 | void AaptFile::clearData() | 
|  | 502 | { | 
|  | 503 | if (mData != NULL) free(mData); | 
|  | 504 | mData = NULL; | 
|  | 505 | mDataSize = 0; | 
|  | 506 | mBufferSize = 0; | 
|  | 507 | } | 
|  | 508 |  | 
|  | 509 | String8 AaptFile::getPrintableSource() const | 
|  | 510 | { | 
|  | 511 | if (hasData()) { | 
|  | 512 | String8 name(mGroupEntry.toDirName(String8())); | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 513 | appendPath(name, mPath); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 514 | name.append(" #generated"); | 
|  | 515 | return name; | 
|  | 516 | } | 
|  | 517 | return mSourceFile; | 
|  | 518 | } | 
|  | 519 |  | 
|  | 520 | // ========================================================================= | 
|  | 521 | // ========================================================================= | 
|  | 522 | // ========================================================================= | 
|  | 523 |  | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 524 | status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate) | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 525 | { | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 526 | ssize_t index = mFiles.indexOfKey(file->getGroupEntry()); | 
|  | 527 | if (index >= 0 && overwriteDuplicate) { | 
|  | 528 | fprintf(stderr, "warning: overwriting '%s' with '%s'\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 529 | mFiles[index]->getSourceFile().c_str(), | 
|  | 530 | file->getSourceFile().c_str()); | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 531 | removeFile(index); | 
|  | 532 | index = -1; | 
|  | 533 | } | 
|  | 534 |  | 
|  | 535 | if (index < 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 536 | file->mPath = mPath; | 
|  | 537 | mFiles.add(file->getGroupEntry(), file); | 
|  | 538 | return NO_ERROR; | 
|  | 539 | } | 
|  | 540 |  | 
| Adam Lesinski | 48f05d2 | 2014-05-12 22:13:02 -0700 | [diff] [blame] | 541 | // Check if the version is automatically applied. This is a common source of | 
|  | 542 | // error. | 
|  | 543 | ConfigDescription withoutVersion = file->getGroupEntry().toParams(); | 
|  | 544 | withoutVersion.version = 0; | 
|  | 545 | AaptConfig::applyVersionForCompatibility(&withoutVersion); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 546 |  | 
| Adam Lesinski | 48f05d2 | 2014-05-12 22:13:02 -0700 | [diff] [blame] | 547 | const sp<AaptFile>& originalFile = mFiles.valueAt(index); | 
|  | 548 | SourcePos(file->getSourceFile(), -1) | 
|  | 549 | .error("Duplicate file.\n%s: Original is here. %s", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 550 | originalFile->getPrintableSource().c_str(), | 
| Adam Lesinski | 48f05d2 | 2014-05-12 22:13:02 -0700 | [diff] [blame] | 551 | (withoutVersion.version != 0) ? "The version qualifier may be implied." : ""); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 552 | return UNKNOWN_ERROR; | 
|  | 553 | } | 
|  | 554 |  | 
|  | 555 | void AaptGroup::removeFile(size_t index) | 
|  | 556 | { | 
|  | 557 | mFiles.removeItemsAt(index); | 
|  | 558 | } | 
|  | 559 |  | 
|  | 560 | void AaptGroup::print(const String8& prefix) const | 
|  | 561 | { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 562 | printf("%s%s\n", prefix.c_str(), getPath().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 563 | const size_t N=mFiles.size(); | 
|  | 564 | size_t i; | 
|  | 565 | for (i=0; i<N; i++) { | 
|  | 566 | sp<AaptFile> file = mFiles.valueAt(i); | 
|  | 567 | const AaptGroupEntry& e = file->getGroupEntry(); | 
|  | 568 | if (file->hasData()) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 569 | printf("%s  Gen: (%s) %d bytes\n", prefix.c_str(), e.toDirName(String8()).c_str(), | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 570 | (int)file->getSize()); | 
|  | 571 | } else { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 572 | printf("%s  Src: (%s) %s\n", prefix.c_str(), e.toDirName(String8()).c_str(), | 
|  | 573 | file->getPrintableSource().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 574 | } | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 575 | //printf("%s  File Group Entry: %s\n", prefix.c_str(), | 
|  | 576 | //        file->getGroupEntry().toDirName(String8()).c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 577 | } | 
|  | 578 | } | 
|  | 579 |  | 
|  | 580 | String8 AaptGroup::getPrintableSource() const | 
|  | 581 | { | 
|  | 582 | if (mFiles.size() > 0) { | 
|  | 583 | // Arbitrarily pull the first source file out of the list. | 
|  | 584 | return mFiles.valueAt(0)->getPrintableSource(); | 
|  | 585 | } | 
|  | 586 |  | 
|  | 587 | // Should never hit this case, but to be safe... | 
|  | 588 | return getPath(); | 
|  | 589 |  | 
|  | 590 | } | 
|  | 591 |  | 
|  | 592 | // ========================================================================= | 
|  | 593 | // ========================================================================= | 
|  | 594 | // ========================================================================= | 
|  | 595 |  | 
|  | 596 | status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) | 
|  | 597 | { | 
|  | 598 | if (mFiles.indexOfKey(name) >= 0) { | 
|  | 599 | return ALREADY_EXISTS; | 
|  | 600 | } | 
|  | 601 | mFiles.add(name, file); | 
|  | 602 | return NO_ERROR; | 
|  | 603 | } | 
|  | 604 |  | 
|  | 605 | status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) | 
|  | 606 | { | 
|  | 607 | if (mDirs.indexOfKey(name) >= 0) { | 
|  | 608 | return ALREADY_EXISTS; | 
|  | 609 | } | 
|  | 610 | mDirs.add(name, dir); | 
|  | 611 | return NO_ERROR; | 
|  | 612 | } | 
|  | 613 |  | 
|  | 614 | sp<AaptDir> AaptDir::makeDir(const String8& path) | 
|  | 615 | { | 
|  | 616 | String8 name; | 
|  | 617 | String8 remain = path; | 
|  | 618 |  | 
|  | 619 | sp<AaptDir> subdir = this; | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 620 | while (name = walkPath(remain, &remain), remain != "") { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 621 | subdir = subdir->makeDir(name); | 
|  | 622 | } | 
|  | 623 |  | 
|  | 624 | ssize_t i = subdir->mDirs.indexOfKey(name); | 
|  | 625 | if (i >= 0) { | 
|  | 626 | return subdir->mDirs.valueAt(i); | 
|  | 627 | } | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 628 | sp<AaptDir> dir = new AaptDir(name, appendPathCopy(subdir->mPath, name)); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 629 | subdir->mDirs.add(name, dir); | 
|  | 630 | return dir; | 
|  | 631 | } | 
|  | 632 |  | 
|  | 633 | void AaptDir::removeFile(const String8& name) | 
|  | 634 | { | 
|  | 635 | mFiles.removeItem(name); | 
|  | 636 | } | 
|  | 637 |  | 
|  | 638 | void AaptDir::removeDir(const String8& name) | 
|  | 639 | { | 
|  | 640 | mDirs.removeItem(name); | 
|  | 641 | } | 
|  | 642 |  | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 643 | status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file, | 
|  | 644 | const bool overwrite) | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 645 | { | 
|  | 646 | sp<AaptGroup> group; | 
|  | 647 | if (mFiles.indexOfKey(leafName) >= 0) { | 
|  | 648 | group = mFiles.valueFor(leafName); | 
|  | 649 | } else { | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 650 | group = new AaptGroup(leafName, appendPathCopy(mPath, leafName)); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 651 | mFiles.add(leafName, group); | 
|  | 652 | } | 
|  | 653 |  | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 654 | return group->addFile(file, overwrite); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 655 | } | 
|  | 656 |  | 
|  | 657 | ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, | 
|  | 658 | const AaptGroupEntry& kind, const String8& resType, | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 659 | sp<FilePathStore>& fullResPaths, const bool overwrite) | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 660 | { | 
|  | 661 | Vector<String8> fileNames; | 
|  | 662 | { | 
|  | 663 | DIR* dir = NULL; | 
|  | 664 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 665 | dir = opendir(srcDir.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 666 | if (dir == NULL) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 667 | fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.c_str(), strerror(errno)); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 668 | return UNKNOWN_ERROR; | 
|  | 669 | } | 
|  | 670 |  | 
|  | 671 | /* | 
|  | 672 | * Slurp the filenames out of the directory. | 
|  | 673 | */ | 
|  | 674 | while (1) { | 
|  | 675 | struct dirent* entry; | 
|  | 676 |  | 
|  | 677 | entry = readdir(dir); | 
|  | 678 | if (entry == NULL) | 
|  | 679 | break; | 
|  | 680 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 681 | if (isHidden(srcDir.c_str(), entry->d_name)) | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 682 | continue; | 
|  | 683 |  | 
|  | 684 | String8 name(entry->d_name); | 
|  | 685 | fileNames.add(name); | 
|  | 686 | // Add fully qualified path for dependency purposes | 
|  | 687 | // if we're collecting them | 
|  | 688 | if (fullResPaths != NULL) { | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 689 | fullResPaths->add(appendPathCopy(srcDir, name)); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 690 | } | 
|  | 691 | } | 
|  | 692 | closedir(dir); | 
|  | 693 | } | 
|  | 694 |  | 
|  | 695 | ssize_t count = 0; | 
|  | 696 |  | 
|  | 697 | /* | 
|  | 698 | * Stash away the files and recursively descend into subdirectories. | 
|  | 699 | */ | 
|  | 700 | const size_t N = fileNames.size(); | 
|  | 701 | size_t i; | 
|  | 702 | for (i = 0; i < N; i++) { | 
|  | 703 | String8 pathName(srcDir); | 
|  | 704 | FileType type; | 
|  | 705 |  | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 706 | appendPath(pathName, fileNames[i]); | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 707 | type = getFileType(pathName.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 708 | if (type == kFileTypeDirectory) { | 
|  | 709 | sp<AaptDir> subdir; | 
|  | 710 | bool notAdded = false; | 
|  | 711 | if (mDirs.indexOfKey(fileNames[i]) >= 0) { | 
|  | 712 | subdir = mDirs.valueFor(fileNames[i]); | 
|  | 713 | } else { | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 714 | subdir = new AaptDir(fileNames[i], appendPathCopy(mPath, fileNames[i])); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 715 | notAdded = true; | 
|  | 716 | } | 
|  | 717 | ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 718 | resType, fullResPaths, overwrite); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 719 | if (res < NO_ERROR) { | 
|  | 720 | return res; | 
|  | 721 | } | 
|  | 722 | if (res > 0 && notAdded) { | 
|  | 723 | mDirs.add(fileNames[i], subdir); | 
|  | 724 | } | 
|  | 725 | count += res; | 
|  | 726 | } else if (type == kFileTypeRegular) { | 
|  | 727 | sp<AaptFile> file = new AaptFile(pathName, kind, resType); | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 728 | status_t err = addLeafFile(fileNames[i], file, overwrite); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 729 | if (err != NO_ERROR) { | 
|  | 730 | return err; | 
|  | 731 | } | 
|  | 732 |  | 
|  | 733 | count++; | 
|  | 734 |  | 
|  | 735 | } else { | 
|  | 736 | if (bundle->getVerbose()) | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 737 | printf("   (ignoring non-file/dir '%s')\n", pathName.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 738 | } | 
|  | 739 | } | 
|  | 740 |  | 
|  | 741 | return count; | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | status_t AaptDir::validate() const | 
|  | 745 | { | 
|  | 746 | const size_t NF = mFiles.size(); | 
|  | 747 | const size_t ND = mDirs.size(); | 
|  | 748 | size_t i; | 
|  | 749 | for (i = 0; i < NF; i++) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 750 | if (!validateFileName(mFiles.valueAt(i)->getLeaf().c_str())) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 751 | SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( | 
|  | 752 | "Invalid filename.  Unable to add."); | 
|  | 753 | return UNKNOWN_ERROR; | 
|  | 754 | } | 
|  | 755 |  | 
|  | 756 | size_t j; | 
|  | 757 | for (j = i+1; j < NF; j++) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 758 | if (strcasecmp(mFiles.valueAt(i)->getLeaf().c_str(), | 
|  | 759 | mFiles.valueAt(j)->getLeaf().c_str()) == 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 760 | SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( | 
|  | 761 | "File is case-insensitive equivalent to: %s", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 762 | mFiles.valueAt(j)->getPrintableSource().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 763 | return UNKNOWN_ERROR; | 
|  | 764 | } | 
|  | 765 |  | 
|  | 766 | // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" | 
|  | 767 | // (this is mostly caught by the "marked" stuff, below) | 
|  | 768 | } | 
|  | 769 |  | 
|  | 770 | for (j = 0; j < ND; j++) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 771 | if (strcasecmp(mFiles.valueAt(i)->getLeaf().c_str(), | 
|  | 772 | mDirs.valueAt(j)->getLeaf().c_str()) == 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 773 | SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( | 
|  | 774 | "File conflicts with dir from: %s", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 775 | mDirs.valueAt(j)->getPrintableSource().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 776 | return UNKNOWN_ERROR; | 
|  | 777 | } | 
|  | 778 | } | 
|  | 779 | } | 
|  | 780 |  | 
|  | 781 | for (i = 0; i < ND; i++) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 782 | if (!validateFileName(mDirs.valueAt(i)->getLeaf().c_str())) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 783 | SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( | 
|  | 784 | "Invalid directory name, unable to add."); | 
|  | 785 | return UNKNOWN_ERROR; | 
|  | 786 | } | 
|  | 787 |  | 
|  | 788 | size_t j; | 
|  | 789 | for (j = i+1; j < ND; j++) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 790 | if (strcasecmp(mDirs.valueAt(i)->getLeaf().c_str(), | 
|  | 791 | mDirs.valueAt(j)->getLeaf().c_str()) == 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 792 | SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( | 
|  | 793 | "Directory is case-insensitive equivalent to: %s", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 794 | mDirs.valueAt(j)->getPrintableSource().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 795 | return UNKNOWN_ERROR; | 
|  | 796 | } | 
|  | 797 | } | 
|  | 798 |  | 
|  | 799 | status_t err = mDirs.valueAt(i)->validate(); | 
|  | 800 | if (err != NO_ERROR) { | 
|  | 801 | return err; | 
|  | 802 | } | 
|  | 803 | } | 
|  | 804 |  | 
|  | 805 | return NO_ERROR; | 
|  | 806 | } | 
|  | 807 |  | 
|  | 808 | void AaptDir::print(const String8& prefix) const | 
|  | 809 | { | 
|  | 810 | const size_t ND=getDirs().size(); | 
|  | 811 | size_t i; | 
|  | 812 | for (i=0; i<ND; i++) { | 
|  | 813 | getDirs().valueAt(i)->print(prefix); | 
|  | 814 | } | 
|  | 815 |  | 
|  | 816 | const size_t NF=getFiles().size(); | 
|  | 817 | for (i=0; i<NF; i++) { | 
|  | 818 | getFiles().valueAt(i)->print(prefix); | 
|  | 819 | } | 
|  | 820 | } | 
|  | 821 |  | 
|  | 822 | String8 AaptDir::getPrintableSource() const | 
|  | 823 | { | 
|  | 824 | if (mFiles.size() > 0) { | 
|  | 825 | // Arbitrarily pull the first file out of the list as the source dir. | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 826 | return getPathDir(mFiles.valueAt(0)->getPrintableSource()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 827 | } | 
|  | 828 | if (mDirs.size() > 0) { | 
|  | 829 | // Or arbitrarily pull the first dir out of the list as the source dir. | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 830 | return getPathDir(mDirs.valueAt(0)->getPrintableSource()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 831 | } | 
|  | 832 |  | 
|  | 833 | // Should never hit this case, but to be safe... | 
|  | 834 | return mPath; | 
|  | 835 |  | 
|  | 836 | } | 
|  | 837 |  | 
|  | 838 | // ========================================================================= | 
|  | 839 | // ========================================================================= | 
|  | 840 | // ========================================================================= | 
|  | 841 |  | 
|  | 842 | status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols) | 
|  | 843 | { | 
|  | 844 | status_t err = NO_ERROR; | 
|  | 845 | size_t N = javaSymbols->mSymbols.size(); | 
|  | 846 | for (size_t i=0; i<N; i++) { | 
|  | 847 | const String8& name = javaSymbols->mSymbols.keyAt(i); | 
|  | 848 | const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i); | 
|  | 849 | ssize_t pos = mSymbols.indexOfKey(name); | 
|  | 850 | if (pos < 0) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 851 | entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 852 | err = UNKNOWN_ERROR; | 
|  | 853 | continue; | 
|  | 854 | } | 
|  | 855 | //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 856 | //        i, N, name.c_str(), entry.isJavaSymbol ? 1 : 0); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 857 | mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol; | 
|  | 858 | } | 
|  | 859 |  | 
|  | 860 | N = javaSymbols->mNestedSymbols.size(); | 
|  | 861 | for (size_t i=0; i<N; i++) { | 
|  | 862 | const String8& name = javaSymbols->mNestedSymbols.keyAt(i); | 
|  | 863 | const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i); | 
|  | 864 | ssize_t pos = mNestedSymbols.indexOfKey(name); | 
|  | 865 | if (pos < 0) { | 
|  | 866 | SourcePos pos; | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 867 | pos.error("Java symbol dir %s not defined\n", name.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 868 | err = UNKNOWN_ERROR; | 
|  | 869 | continue; | 
|  | 870 | } | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 871 | //printf("**** applying java symbols in dir %s\n", name.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 872 | status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols); | 
|  | 873 | if (myerr != NO_ERROR) { | 
|  | 874 | err = myerr; | 
|  | 875 | } | 
|  | 876 | } | 
|  | 877 |  | 
|  | 878 | return err; | 
|  | 879 | } | 
|  | 880 |  | 
|  | 881 | // ========================================================================= | 
|  | 882 | // ========================================================================= | 
|  | 883 | // ========================================================================= | 
|  | 884 |  | 
|  | 885 | AaptAssets::AaptAssets() | 
|  | 886 | : AaptDir(String8(), String8()), | 
| Narayan Kamath | 91447d8 | 2014-01-21 15:32:36 +0000 | [diff] [blame] | 887 | mHavePrivateSymbols(false), | 
|  | 888 | mChanged(false), mHaveIncludedAssets(false), | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 889 | mRes(NULL) {} | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 890 |  | 
|  | 891 | const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const { | 
|  | 892 | if (mChanged) { | 
|  | 893 | } | 
|  | 894 | return mGroupEntries; | 
|  | 895 | } | 
|  | 896 |  | 
|  | 897 | status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file) | 
|  | 898 | { | 
|  | 899 | mChanged = true; | 
|  | 900 | return AaptDir::addFile(name, file); | 
|  | 901 | } | 
|  | 902 |  | 
|  | 903 | sp<AaptFile> AaptAssets::addFile( | 
|  | 904 | const String8& filePath, const AaptGroupEntry& entry, | 
|  | 905 | const String8& srcDir, sp<AaptGroup>* outGroup, | 
|  | 906 | const String8& resType) | 
|  | 907 | { | 
|  | 908 | sp<AaptDir> dir = this; | 
|  | 909 | sp<AaptGroup> group; | 
|  | 910 | sp<AaptFile> file; | 
|  | 911 | String8 root, remain(filePath), partialPath; | 
|  | 912 | while (remain.length() > 0) { | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 913 | root = walkPath(remain, &remain); | 
|  | 914 | appendPath(partialPath, root); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 915 |  | 
|  | 916 | const String8 rootStr(root); | 
|  | 917 |  | 
|  | 918 | if (remain.length() == 0) { | 
|  | 919 | ssize_t i = dir->getFiles().indexOfKey(rootStr); | 
|  | 920 | if (i >= 0) { | 
|  | 921 | group = dir->getFiles().valueAt(i); | 
|  | 922 | } else { | 
|  | 923 | group = new AaptGroup(rootStr, filePath); | 
|  | 924 | status_t res = dir->addFile(rootStr, group); | 
|  | 925 | if (res != NO_ERROR) { | 
|  | 926 | return NULL; | 
|  | 927 | } | 
|  | 928 | } | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 929 | file = new AaptFile(appendPathCopy(srcDir, filePath), entry, resType); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 930 | status_t res = group->addFile(file); | 
|  | 931 | if (res != NO_ERROR) { | 
|  | 932 | return NULL; | 
|  | 933 | } | 
|  | 934 | break; | 
|  | 935 |  | 
|  | 936 | } else { | 
|  | 937 | ssize_t i = dir->getDirs().indexOfKey(rootStr); | 
|  | 938 | if (i >= 0) { | 
|  | 939 | dir = dir->getDirs().valueAt(i); | 
|  | 940 | } else { | 
|  | 941 | sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); | 
|  | 942 | status_t res = dir->addDir(rootStr, subdir); | 
|  | 943 | if (res != NO_ERROR) { | 
|  | 944 | return NULL; | 
|  | 945 | } | 
|  | 946 | dir = subdir; | 
|  | 947 | } | 
|  | 948 | } | 
|  | 949 | } | 
|  | 950 |  | 
|  | 951 | mGroupEntries.add(entry); | 
|  | 952 | if (outGroup) *outGroup = group; | 
|  | 953 | return file; | 
|  | 954 | } | 
|  | 955 |  | 
|  | 956 | void AaptAssets::addResource(const String8& leafName, const String8& path, | 
|  | 957 | const sp<AaptFile>& file, const String8& resType) | 
|  | 958 | { | 
|  | 959 | sp<AaptDir> res = AaptDir::makeDir(kResString); | 
|  | 960 | String8 dirname = file->getGroupEntry().toDirName(resType); | 
|  | 961 | sp<AaptDir> subdir = res->makeDir(dirname); | 
|  | 962 | sp<AaptGroup> grr = new AaptGroup(leafName, path); | 
|  | 963 | grr->addFile(file); | 
|  | 964 |  | 
|  | 965 | subdir->addFile(leafName, grr); | 
|  | 966 | } | 
|  | 967 |  | 
| Guang Zhu | 8c2df71 | 2017-03-21 03:53:43 +0000 | [diff] [blame] | 968 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 969 | ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) | 
|  | 970 | { | 
|  | 971 | int count; | 
|  | 972 | int totalCount = 0; | 
|  | 973 | FileType type; | 
|  | 974 | const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); | 
|  | 975 | const size_t dirCount =resDirs.size(); | 
|  | 976 | sp<AaptAssets> current = this; | 
|  | 977 |  | 
|  | 978 | const int N = bundle->getFileSpecCount(); | 
|  | 979 |  | 
|  | 980 | /* | 
|  | 981 | * If a package manifest was specified, include that first. | 
|  | 982 | */ | 
|  | 983 | if (bundle->getAndroidManifestFile() != NULL) { | 
|  | 984 | // place at root of zip. | 
|  | 985 | String8 srcFile(bundle->getAndroidManifestFile()); | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 986 | addFile(getPathLeaf(srcFile), AaptGroupEntry(), getPathDir(srcFile), | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 987 | NULL, String8()); | 
|  | 988 | totalCount++; | 
|  | 989 | } | 
|  | 990 |  | 
|  | 991 | /* | 
|  | 992 | * If a directory of custom assets was supplied, slurp 'em up. | 
|  | 993 | */ | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 994 | const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs(); | 
|  | 995 | const int AN = assetDirs.size(); | 
|  | 996 | for (int i = 0; i < AN; i++) { | 
|  | 997 | FileType type = getFileType(assetDirs[i]); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 998 | if (type == kFileTypeNonexistent) { | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 999 | fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1000 | return UNKNOWN_ERROR; | 
|  | 1001 | } | 
|  | 1002 | if (type != kFileTypeDirectory) { | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 1003 | fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1004 | return UNKNOWN_ERROR; | 
|  | 1005 | } | 
|  | 1006 |  | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 1007 | String8 assetRoot(assetDirs[i]); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1008 | sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); | 
|  | 1009 | AaptGroupEntry group; | 
|  | 1010 | count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 1011 | String8(), mFullAssetPaths, true); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1012 | if (count < 0) { | 
|  | 1013 | totalCount = count; | 
|  | 1014 | goto bail; | 
|  | 1015 | } | 
|  | 1016 | if (count > 0) { | 
|  | 1017 | mGroupEntries.add(group); | 
|  | 1018 | } | 
|  | 1019 | totalCount += count; | 
|  | 1020 |  | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 1021 | if (bundle->getVerbose()) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1022 | printf("Found %d custom asset file%s in %s\n", | 
| Adam Lesinski | 0938430 | 2014-01-22 16:07:42 -0800 | [diff] [blame] | 1023 | count, (count==1) ? "" : "s", assetDirs[i]); | 
|  | 1024 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1025 | } | 
|  | 1026 |  | 
|  | 1027 | /* | 
|  | 1028 | * If a directory of resource-specific assets was supplied, slurp 'em up. | 
|  | 1029 | */ | 
|  | 1030 | for (size_t i=0; i<dirCount; i++) { | 
|  | 1031 | const char *res = resDirs[i]; | 
|  | 1032 | if (res) { | 
|  | 1033 | type = getFileType(res); | 
|  | 1034 | if (type == kFileTypeNonexistent) { | 
|  | 1035 | fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); | 
|  | 1036 | return UNKNOWN_ERROR; | 
|  | 1037 | } | 
|  | 1038 | if (type == kFileTypeDirectory) { | 
|  | 1039 | if (i>0) { | 
|  | 1040 | sp<AaptAssets> nextOverlay = new AaptAssets(); | 
|  | 1041 | current->setOverlay(nextOverlay); | 
|  | 1042 | current = nextOverlay; | 
|  | 1043 | current->setFullResPaths(mFullResPaths); | 
|  | 1044 | } | 
|  | 1045 | count = current->slurpResourceTree(bundle, String8(res)); | 
| Bryan Mawhinney | 9ab9b93 | 2014-01-24 16:18:13 +0000 | [diff] [blame] | 1046 | if (i > 0 && count > 0) { | 
|  | 1047 | count = current->filter(bundle); | 
|  | 1048 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1049 |  | 
|  | 1050 | if (count < 0) { | 
|  | 1051 | totalCount = count; | 
|  | 1052 | goto bail; | 
|  | 1053 | } | 
|  | 1054 | totalCount += count; | 
|  | 1055 | } | 
|  | 1056 | else { | 
|  | 1057 | fprintf(stderr, "ERROR: '%s' is not a directory\n", res); | 
|  | 1058 | return UNKNOWN_ERROR; | 
|  | 1059 | } | 
|  | 1060 | } | 
|  | 1061 |  | 
|  | 1062 | } | 
|  | 1063 | /* | 
|  | 1064 | * Now do any additional raw files. | 
|  | 1065 | */ | 
|  | 1066 | for (int arg=0; arg<N; arg++) { | 
|  | 1067 | const char* assetDir = bundle->getFileSpecEntry(arg); | 
|  | 1068 |  | 
|  | 1069 | FileType type = getFileType(assetDir); | 
|  | 1070 | if (type == kFileTypeNonexistent) { | 
|  | 1071 | fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); | 
|  | 1072 | return UNKNOWN_ERROR; | 
|  | 1073 | } | 
|  | 1074 | if (type != kFileTypeDirectory) { | 
|  | 1075 | fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); | 
|  | 1076 | return UNKNOWN_ERROR; | 
|  | 1077 | } | 
|  | 1078 |  | 
|  | 1079 | String8 assetRoot(assetDir); | 
|  | 1080 |  | 
|  | 1081 | if (bundle->getVerbose()) | 
|  | 1082 | printf("Processing raw dir '%s'\n", (const char*) assetDir); | 
|  | 1083 |  | 
|  | 1084 | /* | 
|  | 1085 | * Do a recursive traversal of subdir tree.  We don't make any | 
|  | 1086 | * guarantees about ordering, so we're okay with an inorder search | 
|  | 1087 | * using whatever order the OS happens to hand back to us. | 
|  | 1088 | */ | 
|  | 1089 | count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths); | 
|  | 1090 | if (count < 0) { | 
|  | 1091 | /* failure; report error and remove archive */ | 
|  | 1092 | totalCount = count; | 
|  | 1093 | goto bail; | 
|  | 1094 | } | 
|  | 1095 | totalCount += count; | 
|  | 1096 |  | 
|  | 1097 | if (bundle->getVerbose()) | 
|  | 1098 | printf("Found %d asset file%s in %s\n", | 
|  | 1099 | count, (count==1) ? "" : "s", assetDir); | 
|  | 1100 | } | 
|  | 1101 |  | 
|  | 1102 | count = validate(); | 
|  | 1103 | if (count != NO_ERROR) { | 
|  | 1104 | totalCount = count; | 
|  | 1105 | goto bail; | 
|  | 1106 | } | 
|  | 1107 |  | 
|  | 1108 | count = filter(bundle); | 
|  | 1109 | if (count != NO_ERROR) { | 
|  | 1110 | totalCount = count; | 
|  | 1111 | goto bail; | 
|  | 1112 | } | 
|  | 1113 |  | 
|  | 1114 | bail: | 
|  | 1115 | return totalCount; | 
|  | 1116 | } | 
|  | 1117 |  | 
|  | 1118 | ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, | 
|  | 1119 | const AaptGroupEntry& kind, | 
|  | 1120 | const String8& resType, | 
| Adam Lesinski | 40e8eef | 2014-09-16 14:43:29 -0700 | [diff] [blame] | 1121 | sp<FilePathStore>& fullResPaths, | 
|  | 1122 | const bool overwrite) | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1123 | { | 
| Adam Lesinski | 40e8eef | 2014-09-16 14:43:29 -0700 | [diff] [blame] | 1124 | ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1125 | if (res > 0) { | 
|  | 1126 | mGroupEntries.add(kind); | 
|  | 1127 | } | 
|  | 1128 |  | 
|  | 1129 | return res; | 
|  | 1130 | } | 
|  | 1131 |  | 
|  | 1132 | ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) | 
|  | 1133 | { | 
|  | 1134 | ssize_t err = 0; | 
|  | 1135 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1136 | DIR* dir = opendir(srcDir.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1137 | if (dir == NULL) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1138 | fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.c_str(), strerror(errno)); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1139 | return UNKNOWN_ERROR; | 
|  | 1140 | } | 
|  | 1141 |  | 
|  | 1142 | status_t count = 0; | 
|  | 1143 |  | 
|  | 1144 | /* | 
|  | 1145 | * Run through the directory, looking for dirs that match the | 
|  | 1146 | * expected pattern. | 
|  | 1147 | */ | 
|  | 1148 | while (1) { | 
|  | 1149 | struct dirent* entry = readdir(dir); | 
|  | 1150 | if (entry == NULL) { | 
|  | 1151 | break; | 
|  | 1152 | } | 
|  | 1153 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1154 | if (isHidden(srcDir.c_str(), entry->d_name)) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1155 | continue; | 
|  | 1156 | } | 
|  | 1157 |  | 
|  | 1158 | String8 subdirName(srcDir); | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1159 | appendPath(subdirName, entry->d_name); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1160 |  | 
|  | 1161 | AaptGroupEntry group; | 
|  | 1162 | String8 resType; | 
|  | 1163 | bool b = group.initFromDirName(entry->d_name, &resType); | 
|  | 1164 | if (!b) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1165 | fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.c_str(), | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1166 | entry->d_name); | 
|  | 1167 | err = -1; | 
|  | 1168 | continue; | 
|  | 1169 | } | 
|  | 1170 |  | 
|  | 1171 | if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) { | 
|  | 1172 | int maxResInt = atoi(bundle->getMaxResVersion()); | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1173 | const char *verString = group.getVersionString().c_str(); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1174 | int dirVersionInt = atoi(verString + 1); // skip 'v' in version name | 
|  | 1175 | if (dirVersionInt > maxResInt) { | 
|  | 1176 | fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name); | 
|  | 1177 | continue; | 
|  | 1178 | } | 
|  | 1179 | } | 
|  | 1180 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1181 | FileType type = getFileType(subdirName.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1182 |  | 
|  | 1183 | if (type == kFileTypeDirectory) { | 
|  | 1184 | sp<AaptDir> dir = makeDir(resType); | 
|  | 1185 | ssize_t res = dir->slurpFullTree(bundle, subdirName, group, | 
|  | 1186 | resType, mFullResPaths); | 
|  | 1187 | if (res < 0) { | 
|  | 1188 | count = res; | 
|  | 1189 | goto bail; | 
|  | 1190 | } | 
|  | 1191 | if (res > 0) { | 
|  | 1192 | mGroupEntries.add(group); | 
|  | 1193 | count += res; | 
|  | 1194 | } | 
|  | 1195 |  | 
|  | 1196 | // Only add this directory if we don't already have a resource dir | 
|  | 1197 | // for the current type.  This ensures that we only add the dir once | 
|  | 1198 | // for all configs. | 
|  | 1199 | sp<AaptDir> rdir = resDir(resType); | 
|  | 1200 | if (rdir == NULL) { | 
|  | 1201 | mResDirs.add(dir); | 
|  | 1202 | } | 
|  | 1203 | } else { | 
|  | 1204 | if (bundle->getVerbose()) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1205 | fprintf(stderr, "   (ignoring file '%s')\n", subdirName.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1206 | } | 
|  | 1207 | } | 
|  | 1208 | } | 
|  | 1209 |  | 
|  | 1210 | bail: | 
|  | 1211 | closedir(dir); | 
|  | 1212 | dir = NULL; | 
|  | 1213 |  | 
|  | 1214 | if (err != 0) { | 
|  | 1215 | return err; | 
|  | 1216 | } | 
|  | 1217 | return count; | 
|  | 1218 | } | 
|  | 1219 |  | 
|  | 1220 | ssize_t | 
| Andreas Gampe | 2412f84 | 2014-09-30 20:55:57 -0700 | [diff] [blame] | 1221 | AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename) | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1222 | { | 
|  | 1223 | int count = 0; | 
|  | 1224 | SortedVector<AaptGroupEntry> entries; | 
|  | 1225 |  | 
|  | 1226 | ZipFile* zip = new ZipFile; | 
|  | 1227 | status_t err = zip->open(filename, ZipFile::kOpenReadOnly); | 
|  | 1228 | if (err != NO_ERROR) { | 
|  | 1229 | fprintf(stderr, "error opening zip file %s\n", filename); | 
|  | 1230 | count = err; | 
|  | 1231 | delete zip; | 
|  | 1232 | return -1; | 
|  | 1233 | } | 
|  | 1234 |  | 
|  | 1235 | const int N = zip->getNumEntries(); | 
|  | 1236 | for (int i=0; i<N; i++) { | 
|  | 1237 | ZipEntry* entry = zip->getEntryByIndex(i); | 
|  | 1238 | if (entry->getDeleted()) { | 
|  | 1239 | continue; | 
|  | 1240 | } | 
|  | 1241 |  | 
|  | 1242 | String8 entryName(entry->getFileName()); | 
|  | 1243 |  | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1244 | String8 dirName = getPathDir(entryName); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1245 | sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); | 
|  | 1246 |  | 
|  | 1247 | String8 resType; | 
|  | 1248 | AaptGroupEntry kind; | 
|  | 1249 |  | 
|  | 1250 | String8 remain; | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1251 | if (walkPath(entryName, &remain) == kResourceDir) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1252 | // these are the resources, pull their type out of the directory name | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1253 | kind.initFromDirName(walkPath(remain).c_str(), &resType); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1254 | } else { | 
|  | 1255 | // these are untyped and don't have an AaptGroupEntry | 
|  | 1256 | } | 
|  | 1257 | if (entries.indexOf(kind) < 0) { | 
|  | 1258 | entries.add(kind); | 
|  | 1259 | mGroupEntries.add(kind); | 
|  | 1260 | } | 
|  | 1261 |  | 
|  | 1262 | // use the one from the zip file if they both exist. | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1263 | dir->removeFile(getPathLeaf(entryName)); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1264 |  | 
|  | 1265 | sp<AaptFile> file = new AaptFile(entryName, kind, resType); | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1266 | status_t err = dir->addLeafFile(getPathLeaf(entryName), file); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1267 | if (err != NO_ERROR) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1268 | fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1269 | count = err; | 
|  | 1270 | goto bail; | 
|  | 1271 | } | 
|  | 1272 | file->setCompressionMethod(entry->getCompressionMethod()); | 
|  | 1273 |  | 
|  | 1274 | #if 0 | 
|  | 1275 | if (entryName == "AndroidManifest.xml") { | 
|  | 1276 | printf("AndroidManifest.xml\n"); | 
|  | 1277 | } | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1278 | printf("\n\nfile: %s\n", entryName.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1279 | #endif | 
|  | 1280 |  | 
|  | 1281 | size_t len = entry->getUncompressedLen(); | 
|  | 1282 | void* data = zip->uncompress(entry); | 
|  | 1283 | void* buf = file->editData(len); | 
|  | 1284 | memcpy(buf, data, len); | 
|  | 1285 |  | 
|  | 1286 | #if 0 | 
|  | 1287 | const int OFF = 0; | 
|  | 1288 | const unsigned char* p = (unsigned char*)data; | 
|  | 1289 | const unsigned char* end = p+len; | 
|  | 1290 | p += OFF; | 
|  | 1291 | for (int i=0; i<32 && p < end; i++) { | 
|  | 1292 | printf("0x%03x ", i*0x10 + OFF); | 
|  | 1293 | for (int j=0; j<0x10 && p < end; j++) { | 
|  | 1294 | printf(" %02x", *p); | 
|  | 1295 | p++; | 
|  | 1296 | } | 
|  | 1297 | printf("\n"); | 
|  | 1298 | } | 
|  | 1299 | #endif | 
|  | 1300 |  | 
|  | 1301 | free(data); | 
|  | 1302 |  | 
|  | 1303 | count++; | 
|  | 1304 | } | 
|  | 1305 |  | 
|  | 1306 | bail: | 
|  | 1307 | delete zip; | 
|  | 1308 | return count; | 
|  | 1309 | } | 
|  | 1310 |  | 
|  | 1311 | status_t AaptAssets::filter(Bundle* bundle) | 
|  | 1312 | { | 
| Hans Boehm | 25dfa75 | 2016-08-10 19:56:50 -0700 | [diff] [blame] | 1313 | sp<WeakResourceFilter> reqFilter(new WeakResourceFilter()); | 
|  | 1314 | status_t err = reqFilter->parse(bundle->getConfigurations()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1315 | if (err != NO_ERROR) { | 
|  | 1316 | return err; | 
|  | 1317 | } | 
|  | 1318 |  | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1319 | uint32_t preferredDensity = 0; | 
|  | 1320 | if (bundle->getPreferredDensity().size() > 0) { | 
|  | 1321 | ResTable_config preferredConfig; | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1322 | if (!AaptConfig::parseDensity(bundle->getPreferredDensity().c_str(), &preferredConfig)) { | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1323 | fprintf(stderr, "Error parsing preferred density: %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1324 | bundle->getPreferredDensity().c_str()); | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1325 | return UNKNOWN_ERROR; | 
|  | 1326 | } | 
|  | 1327 | preferredDensity = preferredConfig.density; | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1328 | } | 
|  | 1329 |  | 
| Hans Boehm | 25dfa75 | 2016-08-10 19:56:50 -0700 | [diff] [blame] | 1330 | if (reqFilter->isEmpty() && preferredDensity == 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1331 | return NO_ERROR; | 
|  | 1332 | } | 
|  | 1333 |  | 
|  | 1334 | if (bundle->getVerbose()) { | 
| Hans Boehm | 25dfa75 | 2016-08-10 19:56:50 -0700 | [diff] [blame] | 1335 | if (!reqFilter->isEmpty()) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1336 | printf("Applying required filter: %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1337 | bundle->getConfigurations().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1338 | } | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1339 | if (preferredDensity > 0) { | 
|  | 1340 | printf("Applying preferred density filter: %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1341 | bundle->getPreferredDensity().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1342 | } | 
|  | 1343 | } | 
|  | 1344 |  | 
|  | 1345 | const Vector<sp<AaptDir> >& resdirs = mResDirs; | 
|  | 1346 | const size_t ND = resdirs.size(); | 
|  | 1347 | for (size_t i=0; i<ND; i++) { | 
|  | 1348 | const sp<AaptDir>& dir = resdirs.itemAt(i); | 
|  | 1349 | if (dir->getLeaf() == kValuesDir) { | 
|  | 1350 | // The "value" dir is special since a single file defines | 
|  | 1351 | // multiple resources, so we can not do filtering on the | 
|  | 1352 | // files themselves. | 
|  | 1353 | continue; | 
|  | 1354 | } | 
|  | 1355 | if (dir->getLeaf() == kMipmapDir) { | 
|  | 1356 | // We also skip the "mipmap" directory, since the point of this | 
|  | 1357 | // is to include all densities without stripping.  If you put | 
|  | 1358 | // other configurations in here as well they won't be stripped | 
|  | 1359 | // either...  So don't do that.  Seriously.  What is wrong with you? | 
|  | 1360 | continue; | 
|  | 1361 | } | 
|  | 1362 |  | 
|  | 1363 | const size_t NG = dir->getFiles().size(); | 
|  | 1364 | for (size_t j=0; j<NG; j++) { | 
|  | 1365 | sp<AaptGroup> grp = dir->getFiles().valueAt(j); | 
|  | 1366 |  | 
|  | 1367 | // First remove any configurations we know we don't need. | 
|  | 1368 | for (size_t k=0; k<grp->getFiles().size(); k++) { | 
|  | 1369 | sp<AaptFile> file = grp->getFiles().valueAt(k); | 
|  | 1370 | if (k == 0 && grp->getFiles().size() == 1) { | 
|  | 1371 | // If this is the only file left, we need to keep it. | 
|  | 1372 | // Otherwise the resource IDs we are using will be inconsistent | 
|  | 1373 | // with what we get when not stripping.  Sucky, but at least | 
|  | 1374 | // for now we can rely on the back-end doing another filtering | 
|  | 1375 | // pass to take this out and leave us with this resource name | 
|  | 1376 | // containing no entries. | 
|  | 1377 | continue; | 
|  | 1378 | } | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1379 | if (getPathExtension(file->getPath()) == ".xml") { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1380 | // We can't remove .xml files at this point, because when | 
|  | 1381 | // we parse them they may add identifier resources, so | 
|  | 1382 | // removing them can cause our resource identifiers to | 
|  | 1383 | // become inconsistent. | 
|  | 1384 | continue; | 
|  | 1385 | } | 
|  | 1386 | const ResTable_config& config(file->getGroupEntry().toParams()); | 
| Hans Boehm | 25dfa75 | 2016-08-10 19:56:50 -0700 | [diff] [blame] | 1387 | if (!reqFilter->match(config)) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1388 | if (bundle->getVerbose()) { | 
|  | 1389 | printf("Pruning unneeded resource: %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1390 | file->getPrintableSource().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1391 | } | 
|  | 1392 | grp->removeFile(k); | 
|  | 1393 | k--; | 
|  | 1394 | } | 
|  | 1395 | } | 
|  | 1396 |  | 
|  | 1397 | // Quick check: no preferred filters, nothing more to do. | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1398 | if (preferredDensity == 0) { | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1399 | continue; | 
|  | 1400 | } | 
|  | 1401 |  | 
| Adam Lesinski | 8cf6184 | 2013-10-18 13:42:09 -0700 | [diff] [blame] | 1402 | // Get the preferred density if there is one. We do not match exactly for density. | 
|  | 1403 | // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we | 
|  | 1404 | // pick xhdpi. | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1405 | for (size_t k=0; k<grp->getFiles().size(); k++) { | 
|  | 1406 | sp<AaptFile> file = grp->getFiles().valueAt(k); | 
|  | 1407 | if (k == 0 && grp->getFiles().size() == 1) { | 
|  | 1408 | // If this is the only file left, we need to keep it. | 
|  | 1409 | // Otherwise the resource IDs we are using will be inconsistent | 
|  | 1410 | // with what we get when not stripping.  Sucky, but at least | 
|  | 1411 | // for now we can rely on the back-end doing another filtering | 
|  | 1412 | // pass to take this out and leave us with this resource name | 
|  | 1413 | // containing no entries. | 
|  | 1414 | continue; | 
|  | 1415 | } | 
| Tomasz Wasilczyk | 804e819 | 2023-08-23 02:22:53 +0000 | [diff] [blame] | 1416 | if (getPathExtension(file->getPath()) == ".xml") { | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1417 | // We can't remove .xml files at this point, because when | 
|  | 1418 | // we parse them they may add identifier resources, so | 
|  | 1419 | // removing them can cause our resource identifiers to | 
|  | 1420 | // become inconsistent. | 
|  | 1421 | continue; | 
|  | 1422 | } | 
|  | 1423 | const ResTable_config& config(file->getGroupEntry().toParams()); | 
|  | 1424 | if (config.density != 0 && config.density != preferredDensity) { | 
|  | 1425 | // This is a resource we would prefer not to have.  Check | 
|  | 1426 | // to see if have a similar variation that we would like | 
|  | 1427 | // to have and, if so, we can drop it. | 
|  | 1428 | uint32_t bestDensity = config.density; | 
| Adam Lesinski | 8cf6184 | 2013-10-18 13:42:09 -0700 | [diff] [blame] | 1429 |  | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1430 | for (size_t m=0; m<grp->getFiles().size(); m++) { | 
|  | 1431 | if (m == k) { | 
|  | 1432 | continue; | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1433 | } | 
| Adam Lesinski | 8cf6184 | 2013-10-18 13:42:09 -0700 | [diff] [blame] | 1434 |  | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1435 | sp<AaptFile> mfile = grp->getFiles().valueAt(m); | 
|  | 1436 | const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); | 
|  | 1437 | if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) { | 
|  | 1438 | // See if there is a better density resource | 
|  | 1439 | if (mconfig.density < bestDensity && | 
| Bryan Mawhinney | b0db8de | 2014-06-06 13:27:11 +0100 | [diff] [blame] | 1440 | mconfig.density >= preferredDensity && | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1441 | bestDensity > preferredDensity) { | 
| Bryan Mawhinney | b0db8de | 2014-06-06 13:27:11 +0100 | [diff] [blame] | 1442 | // This density is our preferred density, or between our best density and | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1443 | // the preferred density, therefore it is better. | 
|  | 1444 | bestDensity = mconfig.density; | 
|  | 1445 | } else if (mconfig.density > bestDensity && | 
|  | 1446 | bestDensity < preferredDensity) { | 
|  | 1447 | // This density is better than our best density and | 
|  | 1448 | // our best density was smaller than our preferred | 
|  | 1449 | // density, so it is better. | 
|  | 1450 | bestDensity = mconfig.density; | 
| Adam Lesinski | 8cf6184 | 2013-10-18 13:42:09 -0700 | [diff] [blame] | 1451 | } | 
| Adam Lesinski | 8cf6184 | 2013-10-18 13:42:09 -0700 | [diff] [blame] | 1452 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1453 | } | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1454 |  | 
|  | 1455 | if (bestDensity != config.density) { | 
|  | 1456 | if (bundle->getVerbose()) { | 
|  | 1457 | printf("Pruning unneeded resource: %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1458 | file->getPrintableSource().c_str()); | 
| Adam Lesinski | fab5087 | 2014-04-16 14:40:42 -0700 | [diff] [blame] | 1459 | } | 
|  | 1460 | grp->removeFile(k); | 
|  | 1461 | k--; | 
|  | 1462 | } | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1463 | } | 
|  | 1464 | } | 
|  | 1465 | } | 
|  | 1466 | } | 
|  | 1467 |  | 
|  | 1468 | return NO_ERROR; | 
|  | 1469 | } | 
|  | 1470 |  | 
|  | 1471 | sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) | 
|  | 1472 | { | 
|  | 1473 | sp<AaptSymbols> sym = mSymbols.valueFor(name); | 
|  | 1474 | if (sym == NULL) { | 
|  | 1475 | sym = new AaptSymbols(); | 
|  | 1476 | mSymbols.add(name, sym); | 
|  | 1477 | } | 
|  | 1478 | return sym; | 
|  | 1479 | } | 
|  | 1480 |  | 
|  | 1481 | sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name) | 
|  | 1482 | { | 
|  | 1483 | sp<AaptSymbols> sym = mJavaSymbols.valueFor(name); | 
|  | 1484 | if (sym == NULL) { | 
|  | 1485 | sym = new AaptSymbols(); | 
|  | 1486 | mJavaSymbols.add(name, sym); | 
|  | 1487 | } | 
|  | 1488 | return sym; | 
|  | 1489 | } | 
|  | 1490 |  | 
|  | 1491 | status_t AaptAssets::applyJavaSymbols() | 
|  | 1492 | { | 
|  | 1493 | size_t N = mJavaSymbols.size(); | 
|  | 1494 | for (size_t i=0; i<N; i++) { | 
|  | 1495 | const String8& name = mJavaSymbols.keyAt(i); | 
|  | 1496 | const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i); | 
|  | 1497 | ssize_t pos = mSymbols.indexOfKey(name); | 
|  | 1498 | if (pos < 0) { | 
|  | 1499 | SourcePos pos; | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1500 | pos.error("Java symbol dir %s not defined\n", name.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1501 | return UNKNOWN_ERROR; | 
|  | 1502 | } | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1503 | //printf("**** applying java symbols in dir %s\n", name.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1504 | status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols); | 
|  | 1505 | if (err != NO_ERROR) { | 
|  | 1506 | return err; | 
|  | 1507 | } | 
|  | 1508 | } | 
|  | 1509 |  | 
|  | 1510 | return NO_ERROR; | 
|  | 1511 | } | 
|  | 1512 |  | 
|  | 1513 | bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const { | 
|  | 1514 | //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1515 | //        sym.name.c_str(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0, | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1516 | //        sym.isJavaSymbol ? 1 : 0); | 
|  | 1517 | if (!mHavePrivateSymbols) return true; | 
|  | 1518 | if (sym.isPublic) return true; | 
|  | 1519 | if (includePrivate && sym.isJavaSymbol) return true; | 
|  | 1520 | return false; | 
|  | 1521 | } | 
|  | 1522 |  | 
|  | 1523 | status_t AaptAssets::buildIncludedResources(Bundle* bundle) | 
|  | 1524 | { | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1525 | if (mHaveIncludedAssets) { | 
|  | 1526 | return NO_ERROR; | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1527 | } | 
|  | 1528 |  | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1529 | // Add in all includes. | 
|  | 1530 | const Vector<String8>& includes = bundle->getPackageIncludes(); | 
|  | 1531 | const size_t packageIncludeCount = includes.size(); | 
|  | 1532 | for (size_t i = 0; i < packageIncludeCount; i++) { | 
|  | 1533 | if (bundle->getVerbose()) { | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1534 | printf("Including resources from package: %s\n", includes[i].c_str()); | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1535 | } | 
|  | 1536 |  | 
|  | 1537 | if (!mIncludedAssets.addAssetPath(includes[i], NULL)) { | 
|  | 1538 | fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1539 | includes[i].c_str()); | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1540 | return UNKNOWN_ERROR; | 
|  | 1541 | } | 
|  | 1542 | } | 
|  | 1543 |  | 
|  | 1544 | const String8& featureOfBase = bundle->getFeatureOfPackage(); | 
| Tomasz Wasilczyk | 7e22cab | 2023-08-24 19:02:33 +0000 | [diff] [blame] | 1545 | if (!featureOfBase.empty()) { | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1546 | if (bundle->getVerbose()) { | 
|  | 1547 | printf("Including base feature resources from package: %s\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1548 | featureOfBase.c_str()); | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1549 | } | 
|  | 1550 |  | 
|  | 1551 | if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) { | 
|  | 1552 | fprintf(stderr, "ERROR: base feature package '%s' not found.\n", | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1553 | featureOfBase.c_str()); | 
| Adam Lesinski | 833f3cc | 2014-06-18 15:06:01 -0700 | [diff] [blame] | 1554 | return UNKNOWN_ERROR; | 
|  | 1555 | } | 
|  | 1556 | } | 
|  | 1557 |  | 
|  | 1558 | mHaveIncludedAssets = true; | 
|  | 1559 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1560 | return NO_ERROR; | 
|  | 1561 | } | 
|  | 1562 |  | 
|  | 1563 | status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) | 
|  | 1564 | { | 
|  | 1565 | const ResTable& res = getIncludedResources(); | 
|  | 1566 | // XXX dirty! | 
| Narayan Kamath | 00b3144 | 2014-01-27 17:32:37 +0000 | [diff] [blame] | 1567 | return const_cast<ResTable&>(res).add(file->getData(), file->getSize()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1568 | } | 
|  | 1569 |  | 
|  | 1570 | const ResTable& AaptAssets::getIncludedResources() const | 
|  | 1571 | { | 
|  | 1572 | return mIncludedAssets.getResources(false); | 
|  | 1573 | } | 
|  | 1574 |  | 
| Adam Lesinski | ad2d07d | 2014-08-27 16:21:08 -0700 | [diff] [blame] | 1575 | AssetManager& AaptAssets::getAssetManager() | 
|  | 1576 | { | 
|  | 1577 | return mIncludedAssets; | 
|  | 1578 | } | 
|  | 1579 |  | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1580 | void AaptAssets::print(const String8& prefix) const | 
|  | 1581 | { | 
|  | 1582 | String8 innerPrefix(prefix); | 
|  | 1583 | innerPrefix.append("  "); | 
|  | 1584 | String8 innerInnerPrefix(innerPrefix); | 
|  | 1585 | innerInnerPrefix.append("  "); | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1586 | printf("%sConfigurations:\n", prefix.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1587 | const size_t N=mGroupEntries.size(); | 
|  | 1588 | for (size_t i=0; i<N; i++) { | 
|  | 1589 | String8 cname = mGroupEntries.itemAt(i).toDirName(String8()); | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1590 | printf("%s %s\n", prefix.c_str(), | 
|  | 1591 | cname != "" ? cname.c_str() : "(default)"); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1592 | } | 
|  | 1593 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1594 | printf("\n%sFiles:\n", prefix.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1595 | AaptDir::print(innerPrefix); | 
|  | 1596 |  | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1597 | printf("\n%sResource Dirs:\n", prefix.c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1598 | const Vector<sp<AaptDir> >& resdirs = mResDirs; | 
|  | 1599 | const size_t NR = resdirs.size(); | 
|  | 1600 | for (size_t i=0; i<NR; i++) { | 
|  | 1601 | const sp<AaptDir>& d = resdirs.itemAt(i); | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1602 | printf("%s  Type %s\n", prefix.c_str(), d->getLeaf().c_str()); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1603 | d->print(innerInnerPrefix); | 
|  | 1604 | } | 
|  | 1605 | } | 
|  | 1606 |  | 
|  | 1607 | sp<AaptDir> AaptAssets::resDir(const String8& name) const | 
|  | 1608 | { | 
|  | 1609 | const Vector<sp<AaptDir> >& resdirs = mResDirs; | 
|  | 1610 | const size_t N = resdirs.size(); | 
|  | 1611 | for (size_t i=0; i<N; i++) { | 
|  | 1612 | const sp<AaptDir>& d = resdirs.itemAt(i); | 
|  | 1613 | if (d->getLeaf() == name) { | 
|  | 1614 | return d; | 
|  | 1615 | } | 
|  | 1616 | } | 
|  | 1617 | return NULL; | 
|  | 1618 | } | 
|  | 1619 |  | 
|  | 1620 | bool | 
|  | 1621 | valid_symbol_name(const String8& symbol) | 
|  | 1622 | { | 
|  | 1623 | static char const * const KEYWORDS[] = { | 
|  | 1624 | "abstract", "assert", "boolean", "break", | 
|  | 1625 | "byte", "case", "catch", "char", "class", "const", "continue", | 
|  | 1626 | "default", "do", "double", "else", "enum", "extends", "final", | 
|  | 1627 | "finally", "float", "for", "goto", "if", "implements", "import", | 
|  | 1628 | "instanceof", "int", "interface", "long", "native", "new", "package", | 
|  | 1629 | "private", "protected", "public", "return", "short", "static", | 
|  | 1630 | "strictfp", "super", "switch", "synchronized", "this", "throw", | 
|  | 1631 | "throws", "transient", "try", "void", "volatile", "while", | 
|  | 1632 | "true", "false", "null", | 
|  | 1633 | NULL | 
|  | 1634 | }; | 
|  | 1635 | const char*const* k = KEYWORDS; | 
| Tomasz Wasilczyk | d2a6983 | 2023-08-10 23:54:44 +0000 | [diff] [blame] | 1636 | const char*const s = symbol.c_str(); | 
| Adam Lesinski | 282e181 | 2014-01-23 18:17:42 -0800 | [diff] [blame] | 1637 | while (*k) { | 
|  | 1638 | if (0 == strcmp(s, *k)) { | 
|  | 1639 | return false; | 
|  | 1640 | } | 
|  | 1641 | k++; | 
|  | 1642 | } | 
|  | 1643 | return true; | 
|  | 1644 | } |