From 70d65e7e7143f80f3b57b2ddd6dc7d52b9406d45 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Tue, 9 Feb 2021 20:49:50 -0500 Subject: [PATCH] Add io operation callbacks Add `unshield_open2` and `unshield_open2_force_version` to allows overriding `fopen`, `fseek`, `ftell`, `fread`, `fwrite`, `fclose`, `opendir`, `closedir`, `readdir` which lets the user pipe data from a program without doing a round trip to the hard drive --- lib/file.c | 42 +++++++++++----------- lib/helper.c | 21 ++++++----- lib/internal.h | 8 +++-- lib/libunshield.c | 88 ++++++++++++++++++++++++++++++++++++++++++++--- lib/libunshield.h | 16 +++++++++ 5 files changed, 136 insertions(+), 39 deletions(-) diff --git a/lib/file.c b/lib/file.c index 9e4f15f..e8582f2 100644 --- a/lib/file.c +++ b/lib/file.c @@ -318,8 +318,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ #if VERBOSE >= 2 unshield_trace("Open volume %i", volume); #endif - - FCLOSE(reader->volume_file); + + FCLOSE(reader->unshield, reader->volume_file); reader->volume_file = unshield_fopen_for_reading(reader->unshield, volume, CABINET_SUFFIX); if (!reader->volume_file) @@ -332,8 +332,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ uint8_t tmp[COMMON_HEADER_SIZE]; uint8_t* p = tmp; - if (COMMON_HEADER_SIZE != - fread(&tmp, 1, COMMON_HEADER_SIZE, reader->volume_file)) + if (COMMON_HEADER_SIZE != + reader->unshield->io_callbacks->fread(&tmp, 1, COMMON_HEADER_SIZE, reader->volume_file, reader->unshield->io_userdata)) goto exit; if (!unshield_read_common_header(&p, &common_header)) @@ -350,8 +350,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ uint8_t five_header[VOLUME_HEADER_SIZE_V5]; uint8_t* p = five_header; - if (VOLUME_HEADER_SIZE_V5 != - fread(&five_header, 1, VOLUME_HEADER_SIZE_V5, reader->volume_file)) + if (VOLUME_HEADER_SIZE_V5 != + reader->unshield->io_callbacks->fread(&five_header, 1, VOLUME_HEADER_SIZE_V5, reader->volume_file, reader->unshield->io_userdata)) goto exit; reader->volume_header.data_offset = READ_UINT32(p); p += 4; @@ -387,8 +387,8 @@ static bool unshield_reader_open_volume(UnshieldReader* reader, int volume)/*{{{ uint8_t six_header[VOLUME_HEADER_SIZE_V6]; uint8_t* p = six_header; - if (VOLUME_HEADER_SIZE_V6 != - fread(&six_header, 1, VOLUME_HEADER_SIZE_V6, reader->volume_file)) + if (VOLUME_HEADER_SIZE_V6 != + reader->unshield->io_callbacks->fread(&six_header, 1, VOLUME_HEADER_SIZE_V6, reader->volume_file, reader->unshield->io_userdata)) goto exit; reader->volume_header.data_offset = READ_UINT32(p); p += 4; @@ -544,7 +544,7 @@ static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t si goto exit; } - if (bytes_to_read != fread(p, 1, bytes_to_read, reader->volume_file)) + if (bytes_to_read != reader->unshield->io_callbacks->fread(p, 1, bytes_to_read, reader->volume_file, reader->unshield->io_userdata)) { unshield_error("Failed to read 0x%08x bytes of file %i (%s) from volume %i. Current offset = 0x%08x", bytes_to_read, reader->index, @@ -587,14 +587,14 @@ static bool unshield_reader_read(UnshieldReader* reader, void* buffer, size_t si return success; }/*}}}*/ -int copy_file(FILE* infile, FILE* outfile) { +int copy_file(Unshield* unshield, FILE* infile, FILE* outfile) { #define SIZE (1024*1024) char buffer[SIZE]; size_t bytes; - while (0 < (bytes = fread(buffer, 1, sizeof(buffer), infile))) - fwrite(buffer, 1, bytes, outfile); + while (0 < (bytes = unshield->io_callbacks->fread(buffer, 1, sizeof(buffer), infile, unshield->io_userdata))) + unshield->io_callbacks->fwrite(buffer, 1, bytes, outfile, unshield->io_userdata); return 0; } @@ -630,7 +630,7 @@ static UnshieldReader* unshield_reader_create_external(/*{{{*/ } if (file_descriptor->flags & FILE_COMPRESSED) { - long file_size = FSIZE(reader->volume_file); + long file_size = FSIZE(unshield, reader->volume_file); FILE *temporary_file = NULL; /* @@ -643,7 +643,7 @@ static UnshieldReader* unshield_reader_create_external(/*{{{*/ if (diff > 0) { diff = MIN(sizeof(END_OF_CHUNK), diff); temporary_file = tmpfile(); - copy_file(reader->volume_file, temporary_file); + copy_file(reader->unshield, reader->volume_file, temporary_file); fwrite(END_OF_CHUNK + sizeof(END_OF_CHUNK) - diff, 1, diff, temporary_file); fseek(temporary_file, 0, SEEK_SET); @@ -723,7 +723,7 @@ static void unshield_reader_destroy(UnshieldReader* reader)/*{{{*/ { if (reader) { - FCLOSE(reader->volume_file); + FCLOSE(reader->unshield, reader->volume_file); free(reader); } }/*}}}*/ @@ -775,7 +775,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ goto exit; } - if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) + if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; @@ -910,7 +910,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{ exit: unshield_reader_destroy(reader); - FCLOSE(output); + FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; @@ -977,7 +977,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) goto exit; } - if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) + if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; @@ -1027,7 +1027,7 @@ bool unshield_file_save_raw(Unshield* unshield, int index, const char* filename) exit: unshield_reader_destroy(reader); - FCLOSE(output); + FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; @@ -1095,7 +1095,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) goto exit; } - if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) + if (unshield_fsize(unshield, reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet. Trying external file!", index); unshield_reader_destroy(reader); @@ -1280,7 +1280,7 @@ bool unshield_file_save_old(Unshield* unshield, int index, const char* filename) exit: unshield_reader_destroy(reader); - FCLOSE(output); + FCLOSE(unshield, output); FREE(input_buffer); FREE(output_buffer); return success; diff --git a/lib/helper.c b/lib/helper.c index 3d5d86f..a333a53 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -11,8 +11,6 @@ #include #ifdef _WIN32 - #define fseek _fseeki64 - #define ftell _ftelli64 #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) #include #ifndef PATH_MAX @@ -103,11 +101,12 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff else q=filename; - sourcedir = opendir(dirname); + sourcedir = unshield->io_callbacks->opendir(dirname, unshield->io_userdata); /* Search for the File case independent */ if (sourcedir) { - for (dent=readdir(sourcedir);dent;dent=readdir(sourcedir)) + for (dent=unshield->io_callbacks->readdir(sourcedir, unshield->io_userdata);dent; + dent=unshield->io_callbacks->readdir(sourcedir, unshield->io_userdata)) { if (!(strcasecmp(q, dent->d_name))) { @@ -134,11 +133,11 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff #if VERBOSE unshield_trace("Opening file '%s'", filename); #endif - result = fopen(filename, "rb"); + result = unshield->io_callbacks->fopen(filename, "rb", unshield->io_userdata); exit: if (sourcedir) - closedir(sourcedir); + unshield->io_callbacks->closedir(sourcedir, unshield->io_userdata); free(filename); free(dirname); return result; @@ -147,13 +146,13 @@ FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suff return NULL; } -long long unshield_fsize(FILE* file) +long long unshield_fsize(Unshield* unshield, FILE* file) { long long result; - long long previous = ftell(file); - fseek(file, 0L, SEEK_END); - result = ftell(file); - fseek(file, previous, SEEK_SET); + long long previous = unshield->io_callbacks->ftell(file, unshield->io_userdata); + unshield->io_callbacks->fseek(file, 0L, SEEK_END, unshield->io_userdata); + result = unshield->io_callbacks->ftell(file, unshield->io_userdata); + unshield->io_callbacks->fseek(file, previous, SEEK_SET, unshield->io_userdata); return result; } diff --git a/lib/internal.h b/lib/internal.h index f2de0f5..95cce2c 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -53,6 +53,8 @@ struct _Unshield { Header* header_list; char* filename_pattern; + const UnshieldIoCallbacks* io_callbacks; + void* io_userdata; }; /* @@ -78,7 +80,7 @@ void unshield_file_group_destroy(UnshieldFileGroup* self); char *unshield_get_base_directory_name(Unshield *unshield); long int unshield_get_path_max(Unshield* unshield); FILE* unshield_fopen_for_reading(Unshield* unshield, int index, const char* suffix); -long long unshield_fsize(FILE* file); +long long unshield_fsize(Unshield* unshield, FILE* file); bool unshield_read_common_header(uint8_t** buffer, CommonHeader* common); const char* unshield_get_utf8_string(Header* header, const void* buffer); @@ -101,8 +103,8 @@ uint8_t* unshield_header_get_buffer(Header* header, uint32_t offset); #define STRDUP(str) ((str) ? strdup(str) : NULL) #define NEW(type, count) ((type*)calloc(count, sizeof(type))) #define NEW1(type) ((type*)calloc(1, sizeof(type))) -#define FCLOSE(file) if (file) { fclose(file); (file) = NULL; } -#define FSIZE(file) ((file) ? unshield_fsize(file) : 0) +#define FCLOSE(unshield, file) if (file) { unshield->io_callbacks->fclose(file, unshield->io_userdata); (file) = NULL; } +#define FSIZE(unshield, file) ((file) ? unshield_fsize(unshield, file) : 0) #define STREQ(s1,s2) (0 == strcmp(s1,s2)) #if WORDS_BIGENDIAN diff --git a/lib/libunshield.c b/lib/libunshield.c index f953116..aa1c286 100644 --- a/lib/libunshield.c +++ b/lib/libunshield.c @@ -7,6 +7,73 @@ #include #include #include +#include + +#ifdef _WIN32 + #define unshield_native_fseek _fseeki64 + #define unshield_native_ftell _ftelli64 +#else + #define unshield_native_fseek fseek + #define unshield_native_ftell ftell +#endif + + +void *unshield_default_fopen(const char *filename, const char *modes, void *userdata) +{ + return fopen(filename, modes); +} + +int unshield_default_fseek(void *file, long int offset, int whence, void *userdata) +{ + return unshield_native_fseek(file, offset, whence); +} + +long int unshield_default_ftell(void *file, void *userdata) +{ + return unshield_native_ftell(file); +} + +size_t unshield_default_fread(void *ptr, size_t size, size_t n, void *file, void *userdata) +{ + return fread(ptr, size, n, file); +} + +size_t unshield_default_fwrite(const void *ptr, size_t size, size_t n, void *file, void *userdata) +{ + return fwrite(ptr, size, n, file); +} + +int unshield_default_fclose(void *ptr, void *userdata) +{ + return fclose(ptr); +} + +void *unshield_default_opendir(const char *name, void *userdata) +{ + return opendir(name); +} + +int unshield_default_closedir(void *dir, void *userdata) +{ + return closedir(dir); +} + +struct dirent* unshield_default_readdir(void *dir, void *userdata) +{ + return readdir(dir); +} + +static UnshieldIoCallbacks unshield_default_io_callbacks = { + .fopen = unshield_default_fopen, + .fseek = unshield_default_fseek, + .ftell = unshield_default_ftell, + .fread = unshield_default_fread, + .fwrite = unshield_default_fwrite, + .fclose = unshield_default_fclose, + .opendir = unshield_default_opendir, + .closedir = unshield_default_closedir, + .readdir = unshield_default_readdir, +}; /** Create filename pattern used by unshield_fopen_for_reading() @@ -240,7 +307,7 @@ static bool unshield_read_headers(Unshield* unshield, int version)/*{{{*/ Header* header = NEW1(Header); header->index = i; - header->size = FSIZE(file); + header->size = FSIZE(unshield, file); if (header->size < 4) { unshield_error("Header file %i too small", i); @@ -254,8 +321,8 @@ static bool unshield_read_headers(Unshield* unshield, int version)/*{{{*/ goto error; } - bytes_read = fread(header->data, 1, header->size, file); - FCLOSE(file); + bytes_read = unshield->io_callbacks->fread(header->data, 1, header->size, file, unshield->io_userdata); + FCLOSE(unshield, file); if (bytes_read != header->size) { @@ -341,10 +408,20 @@ static bool unshield_read_headers(Unshield* unshield, int version)/*{{{*/ Unshield* unshield_open(const char* filename)/*{{{*/ { - return unshield_open_force_version(filename, -1); + return unshield_open2(filename, &unshield_default_io_callbacks, NULL); }/*}}}*/ Unshield* unshield_open_force_version(const char* filename, int version)/*{{{*/ +{ + return unshield_open2_force_version(filename, version, &unshield_default_io_callbacks, NULL); +}/*}}}*/ + +Unshield* unshield_open2(const char* filename, const UnshieldIoCallbacks* callbacks, void* userdata)/*{{{*/ +{ + return unshield_open2_force_version(filename, -1, callbacks, userdata); +}/*}}}*/ + +Unshield* unshield_open2_force_version(const char* filename, int version, const UnshieldIoCallbacks* callbacks, void* userdata)/*{{{*/ { Unshield* unshield = NEW1(Unshield); if (!unshield) @@ -353,6 +430,9 @@ Unshield* unshield_open_force_version(const char* filename, int version)/*{{{*/ goto error; } + unshield->io_callbacks = callbacks; + unshield->io_userdata = userdata; + if (!unshield_create_filename_pattern(unshield, filename)) { unshield_error("Failed to create filename pattern"); diff --git a/lib/libunshield.h b/lib/libunshield.h index ee34894..f73719a 100644 --- a/lib/libunshield.h +++ b/lib/libunshield.h @@ -36,6 +36,22 @@ Unshield* unshield_open(const char* filename); Unshield* unshield_open_force_version(const char* filename, int version); void unshield_close(Unshield* unshield); +typedef struct +{ + void *(*fopen)(const char *filename, const char *modes, void *userdata); + int (*fseek)(void *file, long int offset, int whence, void *userdata); + long int (*ftell)(void *file, void *userdata); + size_t (*fread)(void *ptr, size_t size, size_t n, void *file, void *userdata); + size_t (*fwrite)(const void *ptr, size_t size, size_t n, void *file, void *userdata); + int (*fclose)(void *ptr, void *userdata); + void *(*opendir)(const char *name, void *userdata); + int (*closedir)(void *dir, void *userdata); + struct dirent* (*readdir)(void *dir, void *userdata); +} UnshieldIoCallbacks; + +Unshield* unshield_open2(const char* filename, const UnshieldIoCallbacks* callbacks, void* userdata); +Unshield* unshield_open2_force_version(const char* filename, int version, const UnshieldIoCallbacks* callbacks, void* userdata); + /* Component functions */