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 Nov 28, 2023
1 parent 51e55ba commit dd51803
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 40 deletions.
42 changes: 21 additions & 21 deletions lib/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,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 @@ -328,8 +328,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 @@ -346,8 +346,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 @@ -383,8 +383,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 @@ -540,7 +540,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 @@ -583,14 +583,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 @@ -626,7 +626,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 @@ -639,7 +639,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 @@ -719,7 +719,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 @@ -777,7 +777,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 @@ -925,7 +925,7 @@ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{
md5 = NULL;
#endif
unshield_reader_destroy(reader);
FCLOSE(output);
FCLOSE(unshield, output);
FREE(input_buffer);
FREE(output_buffer);
return success;
Expand Down Expand Up @@ -992,7 +992,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 @@ -1042,7 +1042,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 @@ -1110,7 +1110,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 @@ -1295,7 +1295,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
99 changes: 94 additions & 5 deletions lib/libunshield.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,82 @@
#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)
{
(void)userdata;
return fopen(filename, modes);
}

int unshield_default_fseek(void *file, long int offset, int whence, void *userdata)
{
(void)userdata;
return unshield_native_fseek(file, offset, whence);
}

long int unshield_default_ftell(void *file, void *userdata)
{
(void)userdata;
return unshield_native_ftell(file);
}

size_t unshield_default_fread(void *ptr, size_t size, size_t n, void *file, void *userdata)
{
(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)
{
(void)userdata;
return fwrite(ptr, size, n, file);
}

int unshield_default_fclose(void *ptr, void *userdata)
{
(void)userdata;
return fclose(ptr);
}

void *unshield_default_opendir(const char *name, void *userdata)
{
(void)userdata;
return opendir(name);
}

int unshield_default_closedir(void *dir, void *userdata)
{
(void)userdata;
return closedir(dir);
}

struct dirent* unshield_default_readdir(void *dir, void *userdata)
{
(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,24 +315,24 @@ 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);
FCLOSE(file);
FCLOSE(unshield, file);
goto error;
}

header->data = malloc(header->size);
if (!header->data)
{
unshield_error("Failed to allocate memory for header file %i", i);
FCLOSE(file);
FCLOSE(unshield, file);
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 @@ -346,6 +422,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 @@ -354,6 +440,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
Loading

0 comments on commit dd51803

Please sign in to comment.