Support reading from ZIPs

This commit is contained in:
Jeffrey Pfau 2014-07-17 02:45:17 -07:00
parent ee5c918ff2
commit d2272ba9ba
4 changed files with 315 additions and 1 deletions

View File

@ -21,6 +21,16 @@ include_directories(${CMAKE_SOURCE_DIR}/src)
add_definitions(-DBINARY_NAME="${BINARY_NAME}" -DPROJECT_NAME="${PROJECT_NAME}")
include(FindPkgConfig)
pkg_search_module(LIBZIP libzip)
if(LIBZIP_FOUND)
include_directories(${LIBZIP_INCLUDE_DIRS})
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
add_definitions(-DENABLE_LIBZIP)
else()
message(WARNING "Could not find libzip for archive support")
endif()
if(WIN32)
add_definitions(-D_WIN32_WINNT=0x0600)
set(OS_LIB "${OS_LIB};Ws2_32")
@ -59,7 +69,7 @@ endif()
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${OS_SRC})
target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB})
target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB})
if(BUILD_SDL)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl)

View File

@ -173,6 +173,9 @@ void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threa
threadContext->gamedir = VDirOpen(opts->fname);
} else {
threadContext->rom = VFileOpen(opts->fname, O_RDONLY);
#if ENABLE_LIBZIP
threadContext->gamedir = VDirOpenZip(opts->fname, 0);
#endif
}
threadContext->fname = opts->fname;
threadContext->bios = VFileOpen(opts->bios, O_RDONLY);
@ -198,6 +201,11 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
threadContext->rewindBuffer = 0;
}
if (!GBAIsROM(threadContext->rom)) {
threadContext->rom->close(threadContext->rom);
threadContext->rom = 0;
}
if (threadContext->gamedir) {
threadContext->gamedir->rewind(threadContext->gamedir);
struct VDirEntry* dirent = threadContext->gamedir->listNext(threadContext->gamedir);

292
src/util/vfs-zip.c Normal file
View File

@ -0,0 +1,292 @@
#include "util/vfs.h"
#ifdef ENABLE_LIBZIP
#include <zip.h>
enum {
BLOCK_SIZE = 1024
};
struct VDirEntryZip {
struct VDirEntry d;
struct zip* z;
zip_int64_t index;
};
struct VDirZip {
struct VDir d;
struct zip* z;
struct VDirEntryZip dirent;
};
struct VFileZip {
struct VFile d;
struct zip_file* zf;
void* buffer;
size_t offset;
size_t bufferSize;
size_t readSize;
size_t fileSize;
};
static bool _vfzClose(struct VFile* vf);
static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
static ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size);
static ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size);
static void* _vfzMap(struct VFile* vf, size_t size, int flags);
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfzTruncate(struct VFile* vf, size_t size);
static bool _vdzClose(struct VDir* vd);
static void _vdzRewind(struct VDir* vd);
static struct VDirEntry* _vdzListNext(struct VDir* vd);
static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
static const char* _vdezName(struct VDirEntry* vde);
struct VDir* VDirOpenZip(const char* path, int flags) {
int zflags = 0;
if (flags & O_CREAT) {
zflags |= ZIP_CREATE;
}
if (flags & O_EXCL) {
zflags |= ZIP_EXCL;
}
struct zip* z = zip_open(path, zflags, 0);
if (!z) {
return 0;
}
struct VDirZip* vd = malloc(sizeof(struct VDirZip));
vd->d.close = _vdzClose;
vd->d.rewind = _vdzRewind;
vd->d.listNext = _vdzListNext;
vd->d.openFile = _vdzOpenFile;
vd->z = z;
vd->dirent.d.name = _vdezName;
vd->dirent.index = -1;
vd->dirent.z = z;
return &vd->d;
}
bool _vfzClose(struct VFile* vf) {
struct VFileZip* vfz = (struct VFileZip*) vf;
if (zip_fclose(vfz->zf) < 0) {
return false;
}
free(vfz);
return true;
}
off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
struct VFileZip* vfz = (struct VFileZip*) vf;
size_t position;
switch (whence) {
case SEEK_SET:
position = offset;
break;
case SEEK_CUR:
if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
return -1;
}
position = vfz->offset + offset;
break;
case SEEK_END:
if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
return -1;
}
position = vfz->fileSize + offset;
break;
}
if (position <= vfz->offset) {
vfz->offset = position;
return position;
}
if (position < vfz->fileSize) {
ssize_t read = vf->read(vf, 0, position - vfz->offset);
if (read < 0) {
return -1;
}
return vfz->offset;
}
return -1;
}
ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
struct VFileZip* vfz = (struct VFileZip*) vf;
size_t bytesRead = 0;
if (!vfz->buffer) {
vfz->bufferSize = BLOCK_SIZE;
vfz->buffer = malloc(BLOCK_SIZE);
}
while (bytesRead < size) {
if (vfz->offset < vfz->readSize) {
size_t diff = vfz->readSize - vfz->offset;
void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
if (diff > size) {
diff = size;
}
if (buffer) {
void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
memcpy(bufferOffset, start, diff);
}
vfz->offset += diff;
bytesRead += diff;
if (diff == size) {
break;
}
}
// offset == readSize
if (vfz->readSize == vfz->bufferSize) {
vfz->bufferSize *= 2;
if (vfz->bufferSize > vfz->fileSize) {
vfz->bufferSize = vfz->fileSize;
}
vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
}
if (vfz->readSize < vfz->bufferSize) {
void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
size_t toRead = vfz->bufferSize - vfz->readSize;
if (toRead > BLOCK_SIZE) {
toRead = BLOCK_SIZE;
}
ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
if (zipRead < 0) {
if (bytesRead == 0) {
return -1;
}
break;
}
if (zipRead == 0) {
break;
}
vfz->readSize += zipRead;
} else {
break;
}
}
return bytesRead;
}
ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
size_t bytesRead = 0;
while (bytesRead < size - 1) {
size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
bytesRead += newRead;
if (!newRead || buffer[bytesRead] == '\n') {
break;
}
}
return buffer[bytesRead] = '\0';
}
ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size) {
// TODO
UNUSED(vf);
UNUSED(buffer);
UNUSED(size);
return -1;
}
void* _vfzMap(struct VFile* vf, size_t size, int flags) {
struct VFileZip* vfz = (struct VFileZip*) vf;
UNUSED(flags);
if (size > vfz->readSize) {
vf->read(vf, 0, size - vfz->readSize);
}
return vfz->buffer;
}
void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
UNUSED(vf);
UNUSED(memory);
UNUSED(size);
}
void _vfzTruncate(struct VFile* vf, size_t size) {
// TODO
UNUSED(vf);
UNUSED(size);
}
bool _vdzClose(struct VDir* vd) {
struct VDirZip* vdz = (struct VDirZip*) vd;
if (zip_close(vdz->z) < 0) {
return false;
}
free(vdz);
return true;
}
void _vdzRewind(struct VDir* vd) {
struct VDirZip* vdz = (struct VDirZip*) vd;
vdz->dirent.index = -1;
}
struct VDirEntry* _vdzListNext(struct VDir* vd) {
struct VDirZip* vdz = (struct VDirZip*) vd;
zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
if (maxIndex <= vdz->dirent.index + 1) {
return 0;
}
++vdz->dirent.index;
return &vdz->dirent.d;
}
struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
UNUSED(mode);
// TODO: support truncating, appending and creating
struct VDirZip* vdz = (struct VDirZip*) vd;
struct zip_stat s;
if (zip_stat(vdz->z, path, 0, &s) < 0) {
return 0;
}
struct zip_file* zf = zip_fopen(vdz->z, path, 0);
if (!zf) {
return 0;
}
struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
vfz->zf = zf;
vfz->buffer = 0;
vfz->offset = 0;
vfz->bufferSize = 0;
vfz->readSize = 0;
vfz->fileSize = s.size;
vfz->d.close = _vfzClose;
vfz->d.seek = _vfzSeek;
vfz->d.read = _vfzRead;
vfz->d.readline = _vfzReadline;
vfz->d.write = _vfzWrite;
vfz->d.map = _vfzMap;
vfz->d.unmap = _vfzUnmap;
vfz->d.truncate = _vfzTruncate;
return &vfz->d;
}
const char* _vdezName(struct VDirEntry* vde) {
struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
struct zip_stat s;
if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
return 0;
}
return s.name;
}
#endif

View File

@ -32,4 +32,8 @@ struct VFile* VFileFromFD(int fd);
struct VDir* VDirOpen(const char* path);
#ifdef ENABLE_LIBZIP
struct VDir* VDirOpenZip(const char* path, int flags);
#endif
#endif