added zipped rom support
This commit is contained in:
parent
127c31bcf4
commit
f196202ce3
3 changed files with 194 additions and 5 deletions
|
|
@ -13,6 +13,7 @@
|
|||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "libretro.h"
|
||||
#include "defines.h"
|
||||
|
|
@ -96,6 +97,7 @@ static struct Core {
|
|||
const char tag[8]; // eg. GBC
|
||||
const char name[128]; // eg. gambatte
|
||||
const char version[128]; // eg. Gambatte (v0.5.0-netlink 7e02df6)
|
||||
const char extensions[128]; // eg. gb|gbc|dmg
|
||||
|
||||
CoreOverrides* overrides;
|
||||
|
||||
|
|
@ -130,12 +132,89 @@ static struct Core {
|
|||
// retro_audio_buffer_status_callback_t audio_buffer_status;
|
||||
} core;
|
||||
|
||||
///////////////////////////////////////
|
||||
// based on picoarch/unzip.c
|
||||
|
||||
#define ZIP_HEADER_SIZE 30
|
||||
#define ZIP_CHUNK_SIZE 65536
|
||||
#define ZIP_LE_READ16(buf) ((uint16_t)(((uint8_t *)(buf))[1] << 8 | ((uint8_t *)(buf))[0]))
|
||||
#define ZIP_LE_READ32(buf) ((uint32_t)(((uint8_t *)(buf))[3] << 24 | ((uint8_t *)(buf))[2] << 16 | ((uint8_t *)(buf))[1] << 8 | ((uint8_t *)(buf))[0]))
|
||||
typedef int (*Zip_extract_t)(FILE* zip, FILE* dst, size_t size);
|
||||
|
||||
static int Zip_copy(FILE* zip, FILE* dst, size_t size) { // uncompressed
|
||||
uint8_t buffer[ZIP_CHUNK_SIZE];
|
||||
while (size) {
|
||||
size_t sz = MIN(size, ZIP_CHUNK_SIZE);
|
||||
if (sz!= fread(buffer, 1, sz, zip)) return -1;
|
||||
if (sz!=fwrite(buffer, 1, sz, dst)) return -1;
|
||||
size -= sz;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int Zip_inflate(FILE* zip, FILE* dst, size_t size) { // compressed
|
||||
z_stream stream = {0};
|
||||
size_t have = 0;
|
||||
uint8_t in[ZIP_CHUNK_SIZE];
|
||||
uint8_t out[ZIP_CHUNK_SIZE];
|
||||
int ret = -1;
|
||||
|
||||
ret = inflateInit2(&stream, -MAX_WBITS);
|
||||
if (ret != Z_OK)
|
||||
return ret;
|
||||
|
||||
do {
|
||||
size_t insize = MIN(size, ZIP_CHUNK_SIZE);
|
||||
|
||||
stream.avail_in = fread(in, 1, insize, zip);
|
||||
if (ferror(zip)) {
|
||||
(void)inflateEnd(&stream);
|
||||
return Z_ERRNO;
|
||||
}
|
||||
|
||||
if (!stream.avail_in)
|
||||
break;
|
||||
stream.next_in = in;
|
||||
|
||||
do {
|
||||
stream.avail_out = ZIP_CHUNK_SIZE;
|
||||
stream.next_out = out;
|
||||
|
||||
ret = inflate(&stream, Z_NO_FLUSH);
|
||||
switch(ret) {
|
||||
case Z_NEED_DICT:
|
||||
ret = Z_DATA_ERROR;
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
(void)inflateEnd(&stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
have = ZIP_CHUNK_SIZE - stream.avail_out;
|
||||
if (fwrite(out, 1, have, dst) != have || ferror(dst)) {
|
||||
(void)inflateEnd(&stream);
|
||||
return Z_ERRNO;
|
||||
}
|
||||
} while (stream.avail_out == 0);
|
||||
|
||||
size -= insize;
|
||||
} while (size && ret != Z_STREAM_END);
|
||||
|
||||
(void)inflateEnd(&stream);
|
||||
|
||||
if (!size || ret == Z_STREAM_END) {
|
||||
return Z_OK;
|
||||
} else {
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
|
||||
static struct Game {
|
||||
char path[MAX_PATH];
|
||||
char name[MAX_PATH]; // TODO: rename to basename?
|
||||
char m3u_path[MAX_PATH];
|
||||
char tmp_path[MAX_PATH]; // location of unzipped file
|
||||
void* data;
|
||||
size_t size;
|
||||
int is_open;
|
||||
|
|
@ -146,12 +225,113 @@ static void Game_open(char* path) {
|
|||
strcpy((char*)game.path, path);
|
||||
strcpy((char*)game.name, strrchr(path, '/')+1);
|
||||
|
||||
// if we have a zip file
|
||||
if (suffixMatch(".zip", game.path)) {
|
||||
LOG_info("is zip file\n");
|
||||
int supports_zip = 0;
|
||||
int i = 0;
|
||||
char* ext;
|
||||
char exts[128];
|
||||
char* extensions[32];
|
||||
strcpy(exts,core.extensions);
|
||||
while ((ext=strtok(i?NULL:exts,"|"))) {
|
||||
extensions[i++] = ext;
|
||||
if (!strcmp("zip", ext)) {
|
||||
supports_zip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
extensions[i] = NULL;
|
||||
|
||||
// if the core doesn't support zip files natively
|
||||
if (!supports_zip) {
|
||||
FILE *zip = fopen(game.path, "r");
|
||||
if (zip==NULL) {
|
||||
LOG_error("Error opening archive: %s\n\t%s\n", game.path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
// extract a known file format
|
||||
uint8_t header[ZIP_HEADER_SIZE];
|
||||
uint32_t next = 0;
|
||||
uint16_t len = 0;
|
||||
char filename[MAX_PATH];
|
||||
uint32_t compressed_size = 0;
|
||||
char extension[8];
|
||||
while (1) {
|
||||
if (next) fseek(zip, next, SEEK_CUR);
|
||||
|
||||
if (ZIP_HEADER_SIZE!=fread(header, 1, ZIP_HEADER_SIZE, zip)) break;
|
||||
|
||||
if ((uint16_t)(header[6]) & 0x0008) break;
|
||||
|
||||
len = ZIP_LE_READ16(&header[26]);
|
||||
if (len>=MAX_PATH) break;
|
||||
|
||||
if (len!=fread(filename,1,len,zip)) break;
|
||||
filename[len] = '\0';
|
||||
LOG_info("filename: %s\n", filename);
|
||||
|
||||
compressed_size = ZIP_LE_READ32(&header[18]);
|
||||
|
||||
fseek(zip, ZIP_LE_READ16(&header[28]), SEEK_CUR);
|
||||
next = compressed_size;
|
||||
|
||||
int found = 0;
|
||||
for (i=0; extensions[i]; i++) {
|
||||
sprintf(extension, ".%s", extensions[i]);
|
||||
if (suffixMatch(extension, filename)) {
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) continue;
|
||||
|
||||
char tmp_template[MAX_PATH];
|
||||
strcpy(tmp_template, "/tmp/minarch-XXXXXX");
|
||||
char* tmp_dirname = mkdtemp(tmp_template);
|
||||
LOG_info("tmp_dirname: %s\n", tmp_dirname);
|
||||
sprintf(game.tmp_path, "%s/%s", tmp_dirname, basename(filename));
|
||||
|
||||
// TODO: we need to clear game.tmp_path if anything below this point fails!
|
||||
|
||||
FILE* dst = fopen(game.tmp_path, "w");
|
||||
if (dst==NULL) {
|
||||
game.tmp_path[0] = '\0';
|
||||
LOG_error("Error extracting file: %s\n\t%s\n", filename, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
Zip_extract_t extract = NULL;
|
||||
switch (ZIP_LE_READ16(&header[8])) {
|
||||
case 0: extract = Zip_copy; break;
|
||||
case 8: extract = Zip_inflate; break;
|
||||
}
|
||||
|
||||
if (!extract || extract(zip,dst,compressed_size)) {
|
||||
game.tmp_path[0] = '\0';
|
||||
LOG_error("Error extracting file: %s\n\t%s\n", filename, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(dst);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(zip);
|
||||
}
|
||||
}
|
||||
|
||||
// some cores handle opening files themselves, eg. pcsx_rearmed
|
||||
// if the frontend tries to load a 500MB file itself bad things happen
|
||||
if (!core.need_fullpath) {
|
||||
FILE *file = fopen(game.path, "r");
|
||||
path = game.tmp_path[0]=='\0'?game.path:game.tmp_path;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (file==NULL) {
|
||||
LOG_error("Error opening game: %s\n\t%s\n", game.path, strerror(errno));
|
||||
LOG_error("Error opening game: %s\n\t%s\n", path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +341,7 @@ static void Game_open(char* path) {
|
|||
rewind(file);
|
||||
game.data = malloc(game.size);
|
||||
if (game.data==NULL) {
|
||||
LOG_error("Couldn't allocate memory for file: %s\n", game.path);
|
||||
LOG_error("Couldn't allocate memory for file: %s\n", path);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -203,6 +383,7 @@ static void Game_open(char* path) {
|
|||
}
|
||||
static void Game_close(void) {
|
||||
if (game.data) free(game.data);
|
||||
if (game.tmp_path) remove(game.tmp_path);
|
||||
game.is_open = 0;
|
||||
VIB_setStrength(0); // just in case
|
||||
}
|
||||
|
|
@ -2600,6 +2781,7 @@ void Core_open(const char* core_path, const char* tag_name) {
|
|||
Core_getName((char*)core_path, (char*)core.name);
|
||||
sprintf((char*)core.version, "%s (%s)", info.library_name, info.library_version);
|
||||
strcpy((char*)core.tag, tag_name);
|
||||
strcpy((char*)core.extensions, info.valid_extensions);
|
||||
|
||||
core.need_fullpath = info.need_fullpath;
|
||||
|
||||
|
|
@ -2772,7 +2954,7 @@ enum {
|
|||
MENU_CALLBACK_EXIT,
|
||||
MENU_CALLBACK_NEXT_ITEM,
|
||||
};
|
||||
typedef int(*MenuList_callback_t)(MenuList* list, int i);
|
||||
typedef int (*MenuList_callback_t)(MenuList* list, int i);
|
||||
typedef struct MenuItem {
|
||||
char* name;
|
||||
char* desc;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ TARGET = minarch.elf
|
|||
CC = $(CROSS_COMPILE)gcc
|
||||
CFLAGS = -marm -mtune=cortex-a9 -mfpu=neon-fp16 -mfloat-abi=hard -march=armv7-a -fomit-frame-pointer
|
||||
CFLAGS += -I. -I../common -I./libretro-common/include -DPLATFORM=\"$(UNION_PLATFORM)\" -Ofast
|
||||
LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf -lmsettings -lpthread
|
||||
LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf -lmsettings -lpthread -lz
|
||||
# CFLAGS += -Wall -Wno-unused-variable -Wno-unused-function
|
||||
# CFLAGS += -fsanitize=address -fno-common
|
||||
# LDFLAGS += -lasan
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue