Skip to content

Commit

Permalink
Add io operation callbacks
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bwrsandman committed May 29, 2022
1 parent 005b9d0 commit f50514b
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 38 deletions.
42 changes: 21 additions & 21 deletions lib/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,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)
Expand All @@ -331,8 +331,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))
Expand All @@ -349,8 +349,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;
Expand Down Expand Up @@ -386,8 +386,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;
Expand Down Expand Up @@ -543,7 +543,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,
Expand Down Expand Up @@ -586,14 +586,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;
}
Expand Down Expand Up @@ -629,7 +629,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;

/*
Expand All @@ -642,7 +642,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);

Expand Down Expand Up @@ -722,7 +722,7 @@ static void unshield_reader_destroy(UnshieldReader* reader)/*{{{*/
{
if (reader)
{
FCLOSE(reader->volume_file);
FCLOSE(reader->unshield, reader->volume_file);
free(reader);
}
}/*}}}*/
Expand Down Expand Up @@ -774,7 +774,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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
21 changes: 10 additions & 11 deletions lib/helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
#include <unistd.h>

#ifdef _WIN32
#define fseek _fseeki64
#define ftell _ftelli64
#define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
#include <direct.h>
#ifndef PATH_MAX
Expand Down Expand Up @@ -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)))
{
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down
8 changes: 5 additions & 3 deletions lib/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ struct _Unshield
{
Header* header_list;
char* filename_pattern;
const UnshieldIoCallbacks* io_callbacks;
void* io_userdata;
};

/*
Expand All @@ -77,7 +79,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);
Expand All @@ -100,8 +102,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))

static inline uint16_t get_unaligned_le16(const uint8_t *p)
Expand Down
86 changes: 83 additions & 3 deletions lib/libunshield.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,73 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#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()
Expand Down Expand Up @@ -239,7 +306,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);
Expand All @@ -253,8 +320,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)
{
Expand Down Expand Up @@ -344,6 +411,16 @@ Unshield* unshield_open(const char* filename)/*{{{*/
}/*}}}*/

Unshield* unshield_open_force_version(const char* filename, int version)/*{{{*/
{
return unshield_open2_force_version(filename, version, NULL, 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)
Expand All @@ -352,6 +429,9 @@ Unshield* unshield_open_force_version(const char* filename, int version)/*{{{*/
goto error;
}

unshield->io_callbacks = callbacks == NULL ? &unshield_default_io_callbacks : callbacks;
unshield->io_userdata = userdata;

if (!unshield_create_filename_pattern(unshield, filename))
{
unshield_error("Failed to create filename pattern");
Expand Down
16 changes: 16 additions & 0 deletions lib/libunshield.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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
*/
Expand Down

0 comments on commit f50514b

Please sign in to comment.