added zipped rom support

This commit is contained in:
Shaun Inman 2023-03-03 22:24:21 -05:00
parent 127c31bcf4
commit f196202ce3
3 changed files with 194 additions and 5 deletions

View file

@ -13,6 +13,7 @@
#include <time.h> #include <time.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <zlib.h>
#include "libretro.h" #include "libretro.h"
#include "defines.h" #include "defines.h"
@ -96,6 +97,7 @@ static struct Core {
const char tag[8]; // eg. GBC const char tag[8]; // eg. GBC
const char name[128]; // eg. gambatte const char name[128]; // eg. gambatte
const char version[128]; // eg. Gambatte (v0.5.0-netlink 7e02df6) const char version[128]; // eg. Gambatte (v0.5.0-netlink 7e02df6)
const char extensions[128]; // eg. gb|gbc|dmg
CoreOverrides* overrides; CoreOverrides* overrides;
@ -130,12 +132,89 @@ static struct Core {
// retro_audio_buffer_status_callback_t audio_buffer_status; // retro_audio_buffer_status_callback_t audio_buffer_status;
} core; } 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 { static struct Game {
char path[MAX_PATH]; char path[MAX_PATH];
char name[MAX_PATH]; // TODO: rename to basename? char name[MAX_PATH]; // TODO: rename to basename?
char m3u_path[MAX_PATH]; char m3u_path[MAX_PATH];
char tmp_path[MAX_PATH]; // location of unzipped file
void* data; void* data;
size_t size; size_t size;
int is_open; int is_open;
@ -146,12 +225,113 @@ static void Game_open(char* path) {
strcpy((char*)game.path, path); strcpy((char*)game.path, path);
strcpy((char*)game.name, strrchr(path, '/')+1); 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 // some cores handle opening files themselves, eg. pcsx_rearmed
// if the frontend tries to load a 500MB file itself bad things happen // if the frontend tries to load a 500MB file itself bad things happen
if (!core.need_fullpath) { 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) { 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; return;
} }
@ -161,7 +341,7 @@ static void Game_open(char* path) {
rewind(file); rewind(file);
game.data = malloc(game.size); game.data = malloc(game.size);
if (game.data==NULL) { 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; return;
} }
@ -203,6 +383,7 @@ static void Game_open(char* path) {
} }
static void Game_close(void) { static void Game_close(void) {
if (game.data) free(game.data); if (game.data) free(game.data);
if (game.tmp_path) remove(game.tmp_path);
game.is_open = 0; game.is_open = 0;
VIB_setStrength(0); // just in case 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); Core_getName((char*)core_path, (char*)core.name);
sprintf((char*)core.version, "%s (%s)", info.library_name, info.library_version); sprintf((char*)core.version, "%s (%s)", info.library_name, info.library_version);
strcpy((char*)core.tag, tag_name); strcpy((char*)core.tag, tag_name);
strcpy((char*)core.extensions, info.valid_extensions);
core.need_fullpath = info.need_fullpath; core.need_fullpath = info.need_fullpath;
@ -2772,7 +2954,7 @@ enum {
MENU_CALLBACK_EXIT, MENU_CALLBACK_EXIT,
MENU_CALLBACK_NEXT_ITEM, 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 { typedef struct MenuItem {
char* name; char* name;
char* desc; char* desc;

View file

@ -7,7 +7,7 @@ TARGET = minarch.elf
CC = $(CROSS_COMPILE)gcc CC = $(CROSS_COMPILE)gcc
CFLAGS = -marm -mtune=cortex-a9 -mfpu=neon-fp16 -mfloat-abi=hard -march=armv7-a -fomit-frame-pointer 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 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 += -Wall -Wno-unused-variable -Wno-unused-function
# CFLAGS += -fsanitize=address -fno-common # CFLAGS += -fsanitize=address -fno-common
# LDFLAGS += -lasan # LDFLAGS += -lasan

View file

@ -18,6 +18,11 @@ Please see the README.txt in the zip file for installation and update instructio
------------------------------- -------------------------------
add additional info to installation readme
TF1 should have stock installed (Garlic will work too)
it will have 4 partitions, 2 hidden Linux partitions plus MISC and ROMS
TF2 should be formatted FAT32
create a clean image to flash and install over create a clean image to flash and install over
advanced users seem to be confused about how simple it is to install :sweat_smile: advanced users seem to be confused about how simple it is to install :sweat_smile:
@ -31,6 +36,7 @@ hdmi
audio audio
minarch minarch
zip support
figure out how to handle different button mappings for cores with different modes figure out how to handle different button mappings for cores with different modes
eg. GG or SMS with picodrive eg. GG or SMS with picodrive
move overrides to a text file that lives in the pak? move overrides to a text file that lives in the pak?
@ -50,6 +56,7 @@ minui
misc misc
port say, show, blank, and confirm port say, show, blank, and confirm
make my own simple file browser? :sweat_smile: make my own simple file browser? :sweat_smile:
port Random Game.pak + daemon
checkout eggs tools checkout eggs tools
I wonder if I could patch in Commander-11, BPreplayBold is too bold at that size I wonder if I could patch in Commander-11, BPreplayBold is too bold at that size