31 #include "third_party/unzip/unzip.h"
33 #include "third_party/zlib/src/contrib/minizip/ioapi.h"
40 static const char kManifestFilename[] =
"__asset_manifest__.txt";
44 ZipAssetManager::ZipAssetManager() {}
52 zlib_filefunc_def def;
54 mem_simple_create_file(&def, const_cast<void*>(data), data_size);
55 if (
void* zipfile = unzAttach(mem_zipfile, &def)) {
58 file_info.zip_handle = zipfile;
61 manager->zipfiles_.insert(zipfile);
63 bool contains_manifest =
false;
66 static const int kBufSize = 1024;
70 if (unzGetCurrentFileInfo(
71 zipfile, &info, buf.
Get(), kBufSize, 0, 0, 0, 0) == UNZ_OK) {
73 if (manager->file_cache_.find(buf.
Get()) !=
74 manager->file_cache_.end()) {
76 <<
"Same file registered multiple times risks use after free "
77 <<
"if the result of GetFileData is still in use. "
78 <<
"Duplicate entry: " << buf.
Get();
81 manager->file_cache_[buf.
Get()] = file_info;
82 manager->file_cache_[buf.
Get()].data_ptr.reset(
new std::string());
83 if (strcmp(kManifestFilename, buf.
Get()) == 0)
84 contains_manifest =
true;
86 }
while (unzGoToNextFile(zipfile) == UNZ_OK);
90 if (contains_manifest) {
93 const std::vector<std::string> mappings =
95 manager->GetFileDataLocked(kManifestFilename, NULL),
"\n");
96 const size_t count = mappings.size();
97 for (
size_t i = 0; i < count; ++i) {
98 std::vector<std::string> mapping =
103 FileCache::iterator it =
105 DCHECK(it != manager->file_cache_.end());
106 it->second.original_name = mapping[1];
107 it->second.timestamp = std::chrono::system_clock::time_point();
111 manager->file_cache_.erase(kManifestFilename);
123 return manager->ContainsFileLocked(filename);
126 bool ZipAssetManager::ContainsFileLocked(
const std::string& filename) {
127 return file_cache_.find(filename) != file_cache_.end();
133 FileCache::const_iterator it = manager->file_cache_.find(filename);
134 return it != manager->file_cache_.end() &&
135 manager->FileIsCached(it->second);
141 std::vector<std::string> filenames;
142 for (
const auto& file : manager->file_cache_)
143 filenames.push_back(file.first);
148 const std::string& filename) {
152 FileCache::iterator it = manager->file_cache_.find(filename);
153 return it->second.data_ptr;
155 return std::shared_ptr<const std::string>();
161 return manager->GetFileDataLocked(filename, NULL);
171 const std::string& ZipAssetManager::GetFileDataLocked(
172 const std::string& filename, std::string* out) {
173 if (!ContainsFileLocked(filename)) {
175 return InvalidReference<const std::string>();
177 FileCache::iterator it = file_cache_.find(filename);
178 if (!FileIsCached(it->second)) {
181 out = it->second.data_ptr.get();
186 if (unzLocateFile(it->second.zip_handle, filename.c_str(), 0) == UNZ_OK &&
187 unzOpenCurrentFile(it->second.zip_handle) == UNZ_OK &&
188 unzGetCurrentFileInfo(
189 it->second.zip_handle, &info, 0, 0, 0, 0, 0, 0) ==
192 out->resize(info.uncompressed_size);
194 unzReadCurrentFile(it->second.zip_handle, &(*out)[0],
195 static_cast<unsigned int>(info.uncompressed_size));
201 out->swap(*it->second.data_ptr);
204 return *it->second.data_ptr;
210 const std::string& source) {
213 if (!manager->ContainsFileLocked(filename)) {
216 FileCache::iterator it = manager->file_cache_.find(filename);
217 *it->second.data_ptr = source;
218 it->second.timestamp = std::chrono::system_clock::now();
226 const std::string& data = manager->GetFileDataLocked(filename, NULL);
228 FileCache::const_iterator it = manager->file_cache_.find(filename);
229 if (it != manager->file_cache_.end() &&
234 fwrite(data.c_str(),
sizeof(data[0]), data.length(), fp);
236 return count == data.length();
245 for (std::set<void*>::iterator it = manager->zipfiles_.begin();
246 it != manager->zipfiles_.end(); it++)
248 manager->file_cache_.clear();
249 manager->zipfiles_.clear();
253 const std::string& filename,
254 std::chrono::system_clock::time_point* timestamp) {
257 std::chrono::system_clock::time_point new_timestamp;
258 FileCache::iterator it = manager->file_cache_.find(filename);
259 if (it != manager->file_cache_.end() && !it->second.original_name.empty()) {
262 if (found && new_timestamp > it->second.timestamp) {
263 it->second.timestamp = new_timestamp;
267 fseek(fp, 0, SEEK_END);
268 const size_t length = ftell(fp);
272 it->second.data_ptr->resize(length);
273 fread(&((*it->second.data_ptr)[0]),
sizeof(
char), length, fp);
276 *timestamp = new_timestamp;
291 bool ZipAssetManager::FileIsCached(
const FileInfo& info) {
292 return !info.data_ptr->empty();
bool IsInvalidReference(const T &value)
IsInvalidReference() returns true if a passed const reference of type T has an address of InvalidRefe...
kShortTerm is used for objects that are very transient in nature, such as scratch memory used to comp...
bool StartsWith(const std::string &target, const std::string &start)
Returns whether target begins with start.
T * Get() const
Returns a pointer to the allocated T instance(s).
#define ION_DECLARE_SAFE_STATIC_POINTER(type, variable)
Declare a static non-array pointer and calls a default constructor.
static bool RegisterAssetData(const void *data, size_t data_size)
Registers a data pointer with a certain length with the manager.
ZipAssetManager manages all zipfile assets in Ion.
static void Reset()
Resets the manager back to its initial, empty state.
#define DLOG(severity)
Same as LOG(severity), but only logs in debug mode.
static bool SetFileData(const std::string &filename, const std::string &source)
Sets the data of the passed filename if the manager contains it.
static bool UpdateFileIfChanged(const std::string &filename, std::chrono::system_clock::time_point *timestamp)
If the source file of a zipped file is available on disk (based on the file's manifest), this function updates the cached unzipped data from the source file if it has changed since the data was registered and the source file is readable.
static std::vector< std::string > GetRegisteredFileNames()
Returns the list of registered filenames.
std::vector< std::string > ION_API SplitString(const std::string &str, const std::string &delimiters)
Splits a string into a vector of substrings, given a set of delimiter characters (expressed as a stri...
static const std::string & GetFileData(const std::string &filename)
Returns the data of the passed filename if the manager contains it.
bool RemovePrefix(const std::string &prefix, std::string *target)
Removes prefix from the beginning of target if target starts with it.
A LockGuard locks a mutex when created, and unlocks it when destroyed.
static bool ContainsFile(const std::string &filename)
Returns whether the manager contains the passed filename.
static bool SaveFileData(const std::string &filename)
Attempts to save the latest cached data of the passed filename back to the original source file on di...
Copyright 2016 Google Inc.
~ZipAssetManager()
The destructor is public so that the StaticDeleter that destroys the manager can access it...
#define DCHECK_EQ(val1, val2)
bool GetFileModificationTime(const std::string &path, std::chrono::system_clock::time_point *time)
Reads the last modification time of the passed file path into time and returns true, iff the file exists.
static bool IsFileCached(const std::string &filename)
Returns whether the file is cached in the manager.
std::string GetCanonicalFilePath(const std::string &path)
Returns a canonical version of a file path string.
static std::shared_ptr< const std::string > GetFileDataPtr(const std::string &filename)
Returns the shared pointer to the data of the passed filename if the manager contains it...
FILE * OpenFile(const std::string &path, const std::string &mode)
Opens the file at path and returns a FILE pointer suitable for passing to fread, fwrite, fclose, etc.
This template class can be used in situations where you want to allocate an object that is not necess...
static bool GetFileDataNoCache(const std::string &filename, std::string *out)
As above but the decompressed bytes are not internally cached.