mirror of https://github.com/mgba-emu/mgba.git
Support reading from ZIPs
This commit is contained in:
parent
ee5c918ff2
commit
d2272ba9ba
|
@ -21,6 +21,16 @@ include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
add_definitions(-DBINARY_NAME="${BINARY_NAME}" -DPROJECT_NAME="${PROJECT_NAME}")
|
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)
|
if(WIN32)
|
||||||
add_definitions(-D_WIN32_WINNT=0x0600)
|
add_definitions(-D_WIN32_WINNT=0x0600)
|
||||||
set(OS_LIB "${OS_LIB};Ws2_32")
|
set(OS_LIB "${OS_LIB};Ws2_32")
|
||||||
|
@ -59,7 +69,7 @@ endif()
|
||||||
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
|
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
|
||||||
|
|
||||||
add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${OS_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)
|
if(BUILD_SDL)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl)
|
||||||
|
|
|
@ -173,6 +173,9 @@ void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threa
|
||||||
threadContext->gamedir = VDirOpen(opts->fname);
|
threadContext->gamedir = VDirOpen(opts->fname);
|
||||||
} else {
|
} else {
|
||||||
threadContext->rom = VFileOpen(opts->fname, O_RDONLY);
|
threadContext->rom = VFileOpen(opts->fname, O_RDONLY);
|
||||||
|
#if ENABLE_LIBZIP
|
||||||
|
threadContext->gamedir = VDirOpenZip(opts->fname, 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
threadContext->fname = opts->fname;
|
threadContext->fname = opts->fname;
|
||||||
threadContext->bios = VFileOpen(opts->bios, O_RDONLY);
|
threadContext->bios = VFileOpen(opts->bios, O_RDONLY);
|
||||||
|
@ -198,6 +201,11 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
|
||||||
threadContext->rewindBuffer = 0;
|
threadContext->rewindBuffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!GBAIsROM(threadContext->rom)) {
|
||||||
|
threadContext->rom->close(threadContext->rom);
|
||||||
|
threadContext->rom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (threadContext->gamedir) {
|
if (threadContext->gamedir) {
|
||||||
threadContext->gamedir->rewind(threadContext->gamedir);
|
threadContext->gamedir->rewind(threadContext->gamedir);
|
||||||
struct VDirEntry* dirent = threadContext->gamedir->listNext(threadContext->gamedir);
|
struct VDirEntry* dirent = threadContext->gamedir->listNext(threadContext->gamedir);
|
||||||
|
|
|
@ -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
|
|
@ -32,4 +32,8 @@ struct VFile* VFileFromFD(int fd);
|
||||||
|
|
||||||
struct VDir* VDirOpen(const char* path);
|
struct VDir* VDirOpen(const char* path);
|
||||||
|
|
||||||
|
#ifdef ENABLE_LIBZIP
|
||||||
|
struct VDir* VDirOpenZip(const char* path, int flags);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue