• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4 // Information about assets being operated on.
5 //
6 #ifndef __AAPT_ASSETS_H
7 #define __AAPT_ASSETS_H
8 
9 #include <androidfw/AssetManager.h>
10 #include <androidfw/ResourceTypes.h>
11 #include <stdlib.h>
12 #include <set>
13 #include <utils/KeyedVector.h>
14 #include <utils/RefBase.h>
15 #include <utils/SortedVector.h>
16 #include <utils/String8.h>
17 #include <utils/Vector.h>
18 
19 #include "AaptConfig.h"
20 #include "Bundle.h"
21 #include "ConfigDescription.h"
22 #include "SourcePos.h"
23 #include "ZipFile.h"
24 
25 using namespace android;
26 
27 extern const char * const gDefaultIgnoreAssets;
28 extern const char * gUserIgnoreAssets;
29 
30 bool valid_symbol_name(const String8& str);
31 
32 class AaptAssets;
33 
34 enum {
35     AXIS_NONE = 0,
36     AXIS_MCC = 1,
37     AXIS_MNC,
38     AXIS_LOCALE,
39     AXIS_SCREENLAYOUTSIZE,
40     AXIS_SCREENLAYOUTLONG,
41     AXIS_ORIENTATION,
42     AXIS_UIMODETYPE,
43     AXIS_UIMODENIGHT,
44     AXIS_DENSITY,
45     AXIS_TOUCHSCREEN,
46     AXIS_KEYSHIDDEN,
47     AXIS_KEYBOARD,
48     AXIS_NAVHIDDEN,
49     AXIS_NAVIGATION,
50     AXIS_SCREENSIZE,
51     AXIS_SMALLESTSCREENWIDTHDP,
52     AXIS_SCREENWIDTHDP,
53     AXIS_SCREENHEIGHTDP,
54     AXIS_LAYOUTDIR,
55     AXIS_VERSION,
56 
57     AXIS_START = AXIS_MCC,
58     AXIS_END = AXIS_VERSION,
59 };
60 
61 struct AaptLocaleValue {
62      char language[4];
63      char region[4];
64      char script[4];
65      char variant[8];
66 
AaptLocaleValueAaptLocaleValue67      AaptLocaleValue() {
68          memset(this, 0, sizeof(AaptLocaleValue));
69      }
70 
71      // Initialize this AaptLocaleValue from a config string.
72      bool initFromFilterString(const String8& config);
73 
74      int initFromDirName(const Vector<String8>& parts, const int startIndex);
75 
76      // Initialize this AaptLocaleValue from a ResTable_config.
77      void initFromResTable(const ResTable_config& config);
78 
79      void writeTo(ResTable_config* out) const;
80 
81      String8 toDirName() const;
82 
compareAaptLocaleValue83      int compare(const AaptLocaleValue& other) const {
84          return memcmp(this, &other, sizeof(AaptLocaleValue));
85      }
86 
87      inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
88      inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
89      inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
90      inline bool operator!=(const AaptLocaleValue& o) const { return compare(o) != 0; }
91      inline bool operator>=(const AaptLocaleValue& o) const { return compare(o) >= 0; }
92      inline bool operator>(const AaptLocaleValue& o) const { return compare(o) > 0; }
93 private:
94      void setLanguage(const char* language);
95      void setRegion(const char* language);
96      void setScript(const char* script);
97      void setVariant(const char* variant);
98 };
99 
100 /**
101  * This structure contains a specific variation of a single file out
102  * of all the variations it can have that we can have.
103  */
104 struct AaptGroupEntry
105 {
106 public:
AaptGroupEntryAaptGroupEntry107     AaptGroupEntry() {}
AaptGroupEntryAaptGroupEntry108     AaptGroupEntry(const ConfigDescription& config) : mParams(config) {}
109 
110     bool initFromDirName(const char* dir, String8* resType);
111 
toParamsAaptGroupEntry112     inline const ConfigDescription& toParams() const { return mParams; }
113 
compareAaptGroupEntry114     inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); }
115     inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
116     inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
117     inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
118     inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
119     inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
120     inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
121 
toStringAaptGroupEntry122     String8 toString() const { return mParams.toString(); }
123     String8 toDirName(const String8& resType) const;
124 
getVersionStringAaptGroupEntry125     const String8 getVersionString() const { return AaptConfig::getVersion(mParams); }
126 
127 private:
128     ConfigDescription mParams;
129 };
130 
compare_type(const AaptGroupEntry & lhs,const AaptGroupEntry & rhs)131 inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
132 {
133     return lhs.compare(rhs);
134 }
135 
strictly_order_type(const AaptGroupEntry & lhs,const AaptGroupEntry & rhs)136 inline int strictly_order_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
137 {
138     return compare_type(lhs, rhs) < 0;
139 }
140 
141 class AaptGroup;
142 class FilePathStore;
143 
144 /**
145  * A single asset file we know about.
146  */
147 class AaptFile : public RefBase
148 {
149 public:
AaptFile(const String8 & sourceFile,const AaptGroupEntry & groupEntry,const String8 & resType)150     AaptFile(const String8& sourceFile, const AaptGroupEntry& groupEntry,
151              const String8& resType)
152         : mGroupEntry(groupEntry)
153         , mResourceType(resType)
154         , mSourceFile(sourceFile)
155         , mData(NULL)
156         , mDataSize(0)
157         , mBufferSize(0)
158         , mCompression(ZipEntry::kCompressStored)
159         {
160             //printf("new AaptFile created %s\n", (const char*)sourceFile);
161         }
~AaptFile()162     virtual ~AaptFile() {
163         free(mData);
164     }
165 
getPath()166     const String8& getPath() const { return mPath; }
getGroupEntry()167     const AaptGroupEntry& getGroupEntry() const { return mGroupEntry; }
168 
169     // Data API.  If there is data attached to the file,
170     // getSourceFile() is not used.
hasData()171     bool hasData() const { return mData != NULL; }
getData()172     const void* getData() const { return mData; }
getSize()173     size_t getSize() const { return mDataSize; }
174     void* editData(size_t size);
175     void* editData(size_t* outSize = NULL);
176     void* editDataInRange(size_t offset, size_t size);
177     void* padData(size_t wordSize);
178     status_t writeData(const void* data, size_t size);
179     void clearData();
180 
getResourceType()181     const String8& getResourceType() const { return mResourceType; }
182 
183     // File API.  If the file does not hold raw data, this is
184     // a full path to a file on the filesystem that holds its data.
getSourceFile()185     const String8& getSourceFile() const { return mSourceFile; }
186 
187     String8 getPrintableSource() const;
188 
189     // Desired compression method, as per utils/ZipEntry.h.  For example,
190     // no compression is ZipEntry::kCompressStored.
getCompressionMethod()191     int getCompressionMethod() const { return mCompression; }
setCompressionMethod(int c)192     void setCompressionMethod(int c) { mCompression = c; }
193 private:
194     friend class AaptGroup;
195 
196     String8 mPath;
197     AaptGroupEntry mGroupEntry;
198     String8 mResourceType;
199     String8 mSourceFile;
200     void* mData;
201     size_t mDataSize;
202     size_t mBufferSize;
203     int mCompression;
204 };
205 
206 /**
207  * A group of related files (the same file, with different
208  * vendor/locale variations).
209  */
210 class AaptGroup : public RefBase
211 {
212 public:
AaptGroup(const String8 & leaf,const String8 & path)213     AaptGroup(const String8& leaf, const String8& path)
214         : mLeaf(leaf), mPath(path) { }
~AaptGroup()215     virtual ~AaptGroup() { }
216 
getLeaf()217     const String8& getLeaf() const { return mLeaf; }
218 
219     // Returns the relative path after the AaptGroupEntry dirs.
getPath()220     const String8& getPath() const { return mPath; }
221 
getFiles()222     const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& getFiles() const
223         { return mFiles; }
224 
225     status_t addFile(const sp<AaptFile>& file, const bool overwriteDuplicate=false);
226     void removeFile(size_t index);
227 
228     void print(const String8& prefix) const;
229 
230     String8 getPrintableSource() const;
231 
232 private:
233     String8 mLeaf;
234     String8 mPath;
235 
236     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > mFiles;
237 };
238 
239 /**
240  * A single directory of assets, which can contain files and other
241  * sub-directories.
242  */
243 class AaptDir : public RefBase
244 {
245 public:
AaptDir(const String8 & leaf,const String8 & path)246     AaptDir(const String8& leaf, const String8& path)
247         : mLeaf(leaf), mPath(path) { }
~AaptDir()248     virtual ~AaptDir() { }
249 
getLeaf()250     const String8& getLeaf() const { return mLeaf; }
251 
getPath()252     const String8& getPath() const { return mPath; }
253 
getFiles()254     const DefaultKeyedVector<String8, sp<AaptGroup> >& getFiles() const { return mFiles; }
getDirs()255     const DefaultKeyedVector<String8, sp<AaptDir> >& getDirs() const { return mDirs; }
256 
257     virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
258 
259     void removeFile(const String8& name);
260     void removeDir(const String8& name);
261 
262     /*
263      * Perform some sanity checks on the names of files and directories here.
264      * In particular:
265      *  - Check for illegal chars in filenames.
266      *  - Check filename length.
267      *  - Check for presence of ".gz" and non-".gz" copies of same file.
268      *  - Check for multiple files whose names match in a case-insensitive
269      *    fashion (problematic for some systems).
270      *
271      * Comparing names against all other names is O(n^2).  We could speed
272      * it up some by sorting the entries and being smarter about what we
273      * compare against, but I'm not expecting to have enough files in a
274      * single directory to make a noticeable difference in speed.
275      *
276      * Note that sorting here is not enough to guarantee that the package
277      * contents are sorted -- subsequent updates can rearrange things.
278      */
279     status_t validate() const;
280 
281     void print(const String8& prefix) const;
282 
283     String8 getPrintableSource() const;
284 
285 private:
286     friend class AaptAssets;
287 
288     status_t addDir(const String8& name, const sp<AaptDir>& dir);
289     sp<AaptDir> makeDir(const String8& name);
290     status_t addLeafFile(const String8& leafName,
291                          const sp<AaptFile>& file,
292                          const bool overwrite=false);
293     virtual ssize_t slurpFullTree(Bundle* bundle,
294                                   const String8& srcDir,
295                                   const AaptGroupEntry& kind,
296                                   const String8& resType,
297                                   sp<FilePathStore>& fullResPaths,
298                                   const bool overwrite=false);
299 
300     String8 mLeaf;
301     String8 mPath;
302 
303     DefaultKeyedVector<String8, sp<AaptGroup> > mFiles;
304     DefaultKeyedVector<String8, sp<AaptDir> > mDirs;
305 };
306 
307 /**
308  * All information we know about a particular symbol.
309  */
310 class AaptSymbolEntry
311 {
312 public:
AaptSymbolEntry()313     AaptSymbolEntry()
314         : isPublic(false), isJavaSymbol(false), typeCode(TYPE_UNKNOWN)
315     {
316     }
AaptSymbolEntry(const String8 & _name)317     AaptSymbolEntry(const String8& _name)
318         : name(_name), isPublic(false), isJavaSymbol(false), typeCode(TYPE_UNKNOWN)
319     {
320     }
AaptSymbolEntry(const AaptSymbolEntry & o)321     AaptSymbolEntry(const AaptSymbolEntry& o)
322         : name(o.name), sourcePos(o.sourcePos), isPublic(o.isPublic)
323         , isJavaSymbol(o.isJavaSymbol), comment(o.comment), typeComment(o.typeComment)
324         , typeCode(o.typeCode), int32Val(o.int32Val), stringVal(o.stringVal)
325     {
326     }
327     AaptSymbolEntry operator=(const AaptSymbolEntry& o)
328     {
329         sourcePos = o.sourcePos;
330         isPublic = o.isPublic;
331         isJavaSymbol = o.isJavaSymbol;
332         comment = o.comment;
333         typeComment = o.typeComment;
334         typeCode = o.typeCode;
335         int32Val = o.int32Val;
336         stringVal = o.stringVal;
337         return *this;
338     }
339 
340     const String8 name;
341 
342     SourcePos sourcePos;
343     bool isPublic;
344     bool isJavaSymbol;
345 
346     String16 comment;
347     String16 typeComment;
348 
349     enum {
350         TYPE_UNKNOWN = 0,
351         TYPE_INT32,
352         TYPE_STRING
353     };
354 
355     int typeCode;
356 
357     // Value.  May be one of these.
358     int32_t int32Val;
359     String8 stringVal;
360 };
361 
362 /**
363  * A group of related symbols (such as indices into a string block)
364  * that have been generated from the assets.
365  */
366 class AaptSymbols : public RefBase
367 {
368 public:
AaptSymbols()369     AaptSymbols() { }
~AaptSymbols()370     virtual ~AaptSymbols() { }
371 
addSymbol(const String8 & name,int32_t value,const SourcePos & pos)372     status_t addSymbol(const String8& name, int32_t value, const SourcePos& pos) {
373         if (!check_valid_symbol_name(name, pos, "symbol")) {
374             return BAD_VALUE;
375         }
376         AaptSymbolEntry& sym = edit_symbol(name, &pos);
377         sym.typeCode = AaptSymbolEntry::TYPE_INT32;
378         sym.int32Val = value;
379         return NO_ERROR;
380     }
381 
addStringSymbol(const String8 & name,const String8 & value,const SourcePos & pos)382     status_t addStringSymbol(const String8& name, const String8& value,
383             const SourcePos& pos) {
384         if (!check_valid_symbol_name(name, pos, "symbol")) {
385             return BAD_VALUE;
386         }
387         AaptSymbolEntry& sym = edit_symbol(name, &pos);
388         sym.typeCode = AaptSymbolEntry::TYPE_STRING;
389         sym.stringVal = value;
390         return NO_ERROR;
391     }
392 
makeSymbolPublic(const String8 & name,const SourcePos & pos)393     status_t makeSymbolPublic(const String8& name, const SourcePos& pos) {
394         if (!check_valid_symbol_name(name, pos, "symbol")) {
395             return BAD_VALUE;
396         }
397         AaptSymbolEntry& sym = edit_symbol(name, &pos);
398         sym.isPublic = true;
399         return NO_ERROR;
400     }
401 
makeSymbolJavaSymbol(const String8 & name,const SourcePos & pos)402     status_t makeSymbolJavaSymbol(const String8& name, const SourcePos& pos) {
403         if (!check_valid_symbol_name(name, pos, "symbol")) {
404             return BAD_VALUE;
405         }
406         AaptSymbolEntry& sym = edit_symbol(name, &pos);
407         sym.isJavaSymbol = true;
408         return NO_ERROR;
409     }
410 
appendComment(const String8 & name,const String16 & comment,const SourcePos & pos)411     void appendComment(const String8& name, const String16& comment, const SourcePos& pos) {
412         if (comment.size() <= 0) {
413             return;
414         }
415         AaptSymbolEntry& sym = edit_symbol(name, &pos);
416         if (sym.comment.size() == 0) {
417             sym.comment = comment;
418         } else {
419             sym.comment.append(String16("\n"));
420             sym.comment.append(comment);
421         }
422     }
423 
appendTypeComment(const String8 & name,const String16 & comment)424     void appendTypeComment(const String8& name, const String16& comment) {
425         if (comment.size() <= 0) {
426             return;
427         }
428         AaptSymbolEntry& sym = edit_symbol(name, NULL);
429         if (sym.typeComment.size() == 0) {
430             sym.typeComment = comment;
431         } else {
432             sym.typeComment.append(String16("\n"));
433             sym.typeComment.append(comment);
434         }
435     }
436 
addNestedSymbol(const String8 & name,const SourcePos & pos)437     sp<AaptSymbols> addNestedSymbol(const String8& name, const SourcePos& pos) {
438         if (!check_valid_symbol_name(name, pos, "nested symbol")) {
439             return NULL;
440         }
441 
442         sp<AaptSymbols> sym = mNestedSymbols.valueFor(name);
443         if (sym == NULL) {
444             sym = new AaptSymbols();
445             mNestedSymbols.add(name, sym);
446         }
447 
448         return sym;
449     }
450 
451     status_t applyJavaSymbols(const sp<AaptSymbols>& javaSymbols);
452 
getSymbols()453     const KeyedVector<String8, AaptSymbolEntry>& getSymbols() const
454         { return mSymbols; }
getNestedSymbols()455     const DefaultKeyedVector<String8, sp<AaptSymbols> >& getNestedSymbols() const
456         { return mNestedSymbols; }
457 
getComment(const String8 & name)458     const String16& getComment(const String8& name) const
459         { return get_symbol(name).comment; }
getTypeComment(const String8 & name)460     const String16& getTypeComment(const String8& name) const
461         { return get_symbol(name).typeComment; }
462 
463 private:
check_valid_symbol_name(const String8 & symbol,const SourcePos & pos,const char * label)464     bool check_valid_symbol_name(const String8& symbol, const SourcePos& pos, const char* label) {
465         if (valid_symbol_name(symbol)) {
466             return true;
467         }
468         pos.error("invalid %s: '%s'\n", label, symbol.string());
469         return false;
470     }
edit_symbol(const String8 & symbol,const SourcePos * pos)471     AaptSymbolEntry& edit_symbol(const String8& symbol, const SourcePos* pos) {
472         ssize_t i = mSymbols.indexOfKey(symbol);
473         if (i < 0) {
474             i = mSymbols.add(symbol, AaptSymbolEntry(symbol));
475         }
476         AaptSymbolEntry& sym = mSymbols.editValueAt(i);
477         if (pos != NULL && sym.sourcePos.line < 0) {
478             sym.sourcePos = *pos;
479         }
480         return sym;
481     }
get_symbol(const String8 & symbol)482     const AaptSymbolEntry& get_symbol(const String8& symbol) const {
483         ssize_t i = mSymbols.indexOfKey(symbol);
484         if (i >= 0) {
485             return mSymbols.valueAt(i);
486         }
487         return mDefSymbol;
488     }
489 
490     KeyedVector<String8, AaptSymbolEntry>           mSymbols;
491     DefaultKeyedVector<String8, sp<AaptSymbols> >   mNestedSymbols;
492     AaptSymbolEntry                                 mDefSymbol;
493 };
494 
495 class ResourceTypeSet : public RefBase,
496                         public KeyedVector<String8,sp<AaptGroup> >
497 {
498 public:
499     ResourceTypeSet();
500 };
501 
502 // Storage for lists of fully qualified paths for
503 // resources encountered during slurping.
504 class FilePathStore : public RefBase,
505                       public Vector<String8>
506 {
507 public:
508     FilePathStore();
509 };
510 
511 /**
512  * Asset hierarchy being operated on.
513  */
514 class AaptAssets : public AaptDir
515 {
516 public:
517     AaptAssets();
~AaptAssets()518     virtual ~AaptAssets() { delete mRes; }
519 
getPackage()520     const String8& getPackage() const { return mPackage; }
setPackage(const String8 & package)521     void setPackage(const String8& package) {
522         mPackage = package;
523         mSymbolsPrivatePackage = package;
524         mHavePrivateSymbols = false;
525     }
526 
527     const SortedVector<AaptGroupEntry>& getGroupEntries() const;
528 
529     virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
530 
531     sp<AaptFile> addFile(const String8& filePath,
532                          const AaptGroupEntry& entry,
533                          const String8& srcDir,
534                          sp<AaptGroup>* outGroup,
535                          const String8& resType);
536 
537     void addResource(const String8& leafName,
538                      const String8& path,
539                      const sp<AaptFile>& file,
540                      const String8& resType);
541 
addGroupEntry(const AaptGroupEntry & entry)542     void addGroupEntry(const AaptGroupEntry& entry) { mGroupEntries.add(entry); }
543 
544     ssize_t slurpFromArgs(Bundle* bundle);
545 
546     sp<AaptSymbols> getSymbolsFor(const String8& name);
547 
548     sp<AaptSymbols> getJavaSymbolsFor(const String8& name);
549 
550     status_t applyJavaSymbols();
551 
getSymbols()552     const DefaultKeyedVector<String8, sp<AaptSymbols> >& getSymbols() const { return mSymbols; }
553 
getSymbolsPrivatePackage()554     String8 getSymbolsPrivatePackage() const { return mSymbolsPrivatePackage; }
setSymbolsPrivatePackage(const String8 & pkg)555     void setSymbolsPrivatePackage(const String8& pkg) {
556         mSymbolsPrivatePackage = pkg;
557         mHavePrivateSymbols = mSymbolsPrivatePackage != mPackage;
558     }
559 
havePrivateSymbols()560     bool havePrivateSymbols() const { return mHavePrivateSymbols; }
561 
562     bool isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const;
563 
564     status_t buildIncludedResources(Bundle* bundle);
565     status_t addIncludedResources(const sp<AaptFile>& file);
566     const ResTable& getIncludedResources() const;
567     AssetManager& getAssetManager();
568 
569     void print(const String8& prefix) const;
570 
resDirs()571     inline const Vector<sp<AaptDir> >& resDirs() const { return mResDirs; }
572     sp<AaptDir> resDir(const String8& name) const;
573 
getOverlay()574     inline sp<AaptAssets> getOverlay() { return mOverlay; }
setOverlay(sp<AaptAssets> & overlay)575     inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
576 
getResources()577     inline KeyedVector<String8, sp<ResourceTypeSet> >* getResources() { return mRes; }
578     inline void
setResources(KeyedVector<String8,sp<ResourceTypeSet>> * res)579         setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { delete mRes; mRes = res; }
580 
getFullResPaths()581     inline sp<FilePathStore>& getFullResPaths() { return mFullResPaths; }
582     inline void
setFullResPaths(sp<FilePathStore> & res)583         setFullResPaths(sp<FilePathStore>& res) { mFullResPaths = res; }
584 
getFullAssetPaths()585     inline sp<FilePathStore>& getFullAssetPaths() { return mFullAssetPaths; }
586     inline void
setFullAssetPaths(sp<FilePathStore> & res)587         setFullAssetPaths(sp<FilePathStore>& res) { mFullAssetPaths = res; }
588 
589 private:
590     virtual ssize_t slurpFullTree(Bundle* bundle,
591                                   const String8& srcDir,
592                                   const AaptGroupEntry& kind,
593                                   const String8& resType,
594                                   sp<FilePathStore>& fullResPaths);
595 
596     ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
597     ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
598 
599     status_t filter(Bundle* bundle);
600 
601     String8 mPackage;
602     SortedVector<AaptGroupEntry> mGroupEntries;
603     DefaultKeyedVector<String8, sp<AaptSymbols> > mSymbols;
604     DefaultKeyedVector<String8, sp<AaptSymbols> > mJavaSymbols;
605     String8 mSymbolsPrivatePackage;
606     bool mHavePrivateSymbols;
607 
608     Vector<sp<AaptDir> > mResDirs;
609 
610     bool mChanged;
611 
612     bool mHaveIncludedAssets;
613     AssetManager mIncludedAssets;
614 
615     sp<AaptAssets> mOverlay;
616     KeyedVector<String8, sp<ResourceTypeSet> >* mRes;
617 
618     sp<FilePathStore> mFullResPaths;
619     sp<FilePathStore> mFullAssetPaths;
620 };
621 
622 #endif // __AAPT_ASSETS_H
623 
624