Files
ppsspp/ext/native/file/zip_read.cpp
T
Unknown W. Brackets a676721683 Qt: Remove bundled assets reader.
It was no longer being used or linked, so it just meant the assets were missing.
With this removed, the assets (lang inis, shaders, etc.) are available again.

Fixes #9831 - likely broken since #9061, which stopped using the qrc.
2018-06-09 10:47:53 -07:00

369 lines
9.6 KiB
C++

#include <algorithm>
#include <ctype.h>
#include <set>
#include <stdio.h>
#ifdef __ANDROID__
#include <zip.h>
#endif
#include "base/basictypes.h"
#include "base/logging.h"
#include "file/zip_read.h"
#ifdef __ANDROID__
uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size) {
// Figure out the file size first.
struct zip_stat zstat;
zip_file *file = zip_fopen(archive, filename, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED);
if (!file) {
ELOG("Error opening %s from ZIP", filename);
return 0;
}
zip_stat(archive, filename, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED, &zstat);
uint8_t *contents = new uint8_t[zstat.size + 1];
zip_fread(file, contents, zstat.size);
zip_fclose(file);
contents[zstat.size] = 0;
*size = zstat.size;
return contents;
}
#endif
// The return is non-const because - why not?
uint8_t *ReadLocalFile(const char *filename, size_t *size) {
FILE *file = openCFile(filename, "rb");
if (!file) {
*size = 0;
return nullptr;
}
fseek(file, 0, SEEK_END);
size_t f_size = ftell(file);
if ((long)f_size < 0) {
*size = 0;
fclose(file);
return nullptr;
}
fseek(file, 0, SEEK_SET);
uint8_t *contents = new uint8_t[f_size+1];
if (fread(contents, 1, f_size, file) != f_size) {
delete [] contents;
contents = nullptr;
*size = 0;
} else {
contents[f_size] = 0;
*size = f_size;
}
fclose(file);
return contents;
}
#ifdef __ANDROID__
ZipAssetReader::ZipAssetReader(const char *zip_file, const char *in_zip_path) {
zip_file_ = zip_open(zip_file, 0, NULL);
strcpy(in_zip_path_, in_zip_path);
if (!zip_file_) {
ELOG("Failed to open %s as a zip file", zip_file);
}
std::vector<FileInfo> info;
GetFileListing("assets", &info, 0);
for (size_t i = 0; i < info.size(); i++) {
if (info[i].isDirectory) {
DLOG("Directory: %s", info[i].name.c_str());
} else {
DLOG("File: %s", info[i].name.c_str());
}
}
}
ZipAssetReader::~ZipAssetReader() {
zip_close(zip_file_);
}
uint8_t *ZipAssetReader::ReadAsset(const char *path, size_t *size) {
char temp_path[1024];
strcpy(temp_path, in_zip_path_);
strcat(temp_path, path);
return ReadFromZip(zip_file_, temp_path, size);
}
bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector<FileInfo> *listing, const char *filter = 0) {
char path[1024];
strcpy(path, in_zip_path_);
strcat(path, orig_path);
std::set<std::string> filters;
std::string tmp;
if (filter) {
while (*filter) {
if (*filter == ':') {
filters.insert(tmp);
tmp = "";
} else {
tmp.push_back(*filter);
}
filter++;
}
}
if (tmp.size())
filters.insert(tmp);
// We just loop through the whole ZIP file and deduce what files are in this directory, and what subdirectories there are.
std::set<std::string> files;
std::set<std::string> directories;
int numFiles = zip_get_num_files(zip_file_);
size_t pathlen = strlen(path);
if (path[pathlen-1] == '/')
pathlen--;
for (int i = 0; i < numFiles; i++) {
const char* name = zip_get_name(zip_file_, i, 0);
if (!name)
continue;
if (!memcmp(name, path, pathlen)) {
// The prefix is right. Let's see if this is a file or path.
const char *slashPos = strchr(name + pathlen + 1, '/');
if (slashPos != 0) {
// A directory.
std::string dirName = std::string(name + pathlen + 1, slashPos - (name + pathlen + 1));
directories.insert(dirName);
} else if (name[pathlen] == '/') {
const char *fn = name + pathlen + 1;
files.insert(std::string(fn));
} // else, it was a file with the same prefix as the path. like langregion.ini next to lang/.
}
}
for (auto diter = directories.begin(); diter != directories.end(); ++diter) {
FileInfo info;
info.name = *diter;
info.fullName = std::string(path);
if (info.fullName[info.fullName.size() - 1] == '/')
info.fullName = info.fullName.substr(0, info.fullName.size() - 1);
// Remove the "inzip" part of the fullname.
info.fullName = info.fullName.substr(strlen(in_zip_path_));
info.fullName += "/" + *diter;
info.exists = true;
info.isWritable = false;
info.isDirectory = true;
listing->push_back(info);
}
for (auto fiter = files.begin(); fiter != files.end(); ++fiter) {
FileInfo info;
info.name = *fiter;
info.fullName = std::string(path);
if (info.fullName[info.fullName.size() - 1] == '/')
info.fullName = info.fullName.substr(0, info.fullName.size() - 1);
info.fullName = info.fullName.substr(strlen(in_zip_path_));
info.fullName += "/" + *fiter;
info.exists = true;
info.isWritable = false;
info.isDirectory = false;
std::string ext = getFileExtension(info.fullName);
if (filter) {
if (filters.find(ext) == filters.end())
continue;
}
listing->push_back(info);
}
std::sort(listing->begin(), listing->end());
return true;
}
bool ZipAssetReader::GetFileInfo(const char *path, FileInfo *info) {
struct zip_stat zstat;
char temp_path[1024];
strcpy(temp_path, in_zip_path_);
strcat(temp_path, path);
if (0 != zip_stat(zip_file_, temp_path, ZIP_FL_NOCASE|ZIP_FL_UNCHANGED, &zstat)) {
// ZIP files do not have real directories, so we'll end up here if we
// try to stat one. For now that's fine.
info->exists = false;
info->size = 0;
return false;
}
info->fullName = path;
info->exists = true; // TODO
info->isWritable = false;
info->isDirectory = false; // TODO
info->size = zstat.size;
return true;
}
#endif
uint8_t *DirectoryAssetReader::ReadAsset(const char *path, size_t *size) {
char new_path[2048];
new_path[0] = '\0';
// Check if it already contains the path
if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) {
}
else {
strcpy(new_path, path_);
}
strcat(new_path, path);
return ReadLocalFile(new_path, size);
}
bool DirectoryAssetReader::GetFileListing(const char *path, std::vector<FileInfo> *listing, const char *filter = 0)
{
char new_path[2048];
new_path[0] = '\0';
// Check if it already contains the path
if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) {
}
else {
strcpy(new_path, path_);
}
strcat(new_path, path);
FileInfo info;
if (!getFileInfo(new_path, &info))
return false;
if (info.isDirectory)
{
getFilesInDir(new_path, listing, filter);
return true;
}
else
{
return false;
}
}
bool DirectoryAssetReader::GetFileInfo(const char *path, FileInfo *info)
{
char new_path[2048];
new_path[0] = '\0';
// Check if it already contains the path
if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) {
}
else {
strcpy(new_path, path_);
}
strcat(new_path, path);
return getFileInfo(new_path, info);
}
struct VFSEntry {
const char *prefix;
AssetReader *reader;
};
static VFSEntry entries[16];
static int num_entries = 0;
void VFSRegister(const char *prefix, AssetReader *reader) {
entries[num_entries].prefix = prefix;
entries[num_entries].reader = reader;
DLOG("Registered VFS for prefix %s: %s", prefix, reader->toString().c_str());
num_entries++;
}
void VFSShutdown() {
for (int i = 0; i < num_entries; i++) {
delete entries[i].reader;
}
num_entries = 0;
}
static bool IsLocalPath(const char *path) {
bool isUnixLocal = path[0] == '/';
#ifdef _WIN32
bool isWindowsLocal = isalpha(path[0]) && path[1] == ':';
#else
bool isWindowsLocal = false;
#endif
return isUnixLocal || isWindowsLocal;
}
uint8_t *VFSReadFile(const char *filename, size_t *size) {
if (IsLocalPath(filename)) {
// Local path, not VFS.
// ILOG("Not a VFS path: %s . Reading local file.", filename);
return ReadLocalFile(filename, size);
}
int fn_len = (int)strlen(filename);
bool fileSystemFound = false;
for (int i = 0; i < num_entries; i++) {
int prefix_len = (int)strlen(entries[i].prefix);
if (prefix_len >= fn_len) continue;
if (0 == memcmp(filename, entries[i].prefix, prefix_len)) {
fileSystemFound = true;
// ILOG("Prefix match: %s (%s) -> %s", entries[i].prefix, filename, filename + prefix_len);
uint8_t *data = entries[i].reader->ReadAsset(filename + prefix_len, size);
if (data)
return data;
else
continue;
// Else try the other registered file systems.
}
}
if (!fileSystemFound) {
ELOG("Missing filesystem for '%s'", filename);
} // Otherwise, the file was just missing. No need to log.
return 0;
}
bool VFSGetFileListing(const char *path, std::vector<FileInfo> *listing, const char *filter) {
if (IsLocalPath(path)) {
// Local path, not VFS.
// ILOG("Not a VFS path: %s . Reading local directory.", path);
getFilesInDir(path, listing, filter);
return true;
}
int fn_len = (int)strlen(path);
bool fileSystemFound = false;
for (int i = 0; i < num_entries; i++) {
int prefix_len = (int)strlen(entries[i].prefix);
if (prefix_len >= fn_len) continue;
if (0 == memcmp(path, entries[i].prefix, prefix_len)) {
fileSystemFound = true;
if (entries[i].reader->GetFileListing(path + prefix_len, listing, filter)) {
return true;
}
}
}
if (!fileSystemFound) {
ELOG("Missing filesystem for %s", path);
} // Otherwise, the file was just missing. No need to log.
return false;
}
bool VFSGetFileInfo(const char *path, FileInfo *info) {
if (IsLocalPath(path)) {
// Local path, not VFS.
// ILOG("Not a VFS path: %s . Getting local file info.", path);
return getFileInfo(path, info);
}
bool fileSystemFound = false;
int fn_len = (int)strlen(path);
for (int i = 0; i < num_entries; i++) {
int prefix_len = (int)strlen(entries[i].prefix);
if (prefix_len >= fn_len) continue;
if (0 == memcmp(path, entries[i].prefix, prefix_len)) {
fileSystemFound = true;
if (entries[i].reader->GetFileInfo(path + prefix_len, info))
return true;
else
continue;
}
}
if (!fileSystemFound) {
ELOG("Missing filesystem for %s", path);
} // Otherwise, the file was just missing. No need to log.
return false;
}