diff --git a/src/resources.c b/src/resources.c index d7bfea0..09ee8a1 100644 --- a/src/resources.c +++ b/src/resources.c @@ -17,6 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *------------------------------------------------------------------------- */ +#include +#include #include #include "wintype.h" #include "scode.h" @@ -24,6 +26,17 @@ #include "log.h" #include "resources.h" +typedef struct tagRESFILE { + struct tagRESFILE *next; + struct tagRESFILE *prev; + zip_t *resource; +} RESFILE, *PRESFILE; + +typedef struct tagRESOURCEINFO { + zip_t *resource_file; + zip_stat_t entryinfo; +} RESOURCEINFO, *PRESOURCEINFO; + /* conversion table from zip error codes to our HRESULT values */ static const struct tagCONVERSIONTABLE { int zip_err_code; @@ -46,6 +59,7 @@ extern uint8_t _binary_sysresources_zip_end; extern uint8_t _binary_sysresources_zip_size; static zip_t *sysresource = NULL; /* system resource file */ +static PRESFILE resfiles = NULL; /* all open resource files */ static HRESULT ziperror_to_hresult(zip_error_t *errinfo) { @@ -57,8 +71,198 @@ static HRESULT ziperror_to_hresult(zip_error_t *errinfo) return MAKE_SCODE(SEVERITY_ERROR, FACILITY_ZIP, errinfo->zip_err); } +static BOOL check_in_list(PRESFILE presfile) +{ + register PRESFILE p = resfiles; + do + { + if (p == presfile) + return TRUE; + p = p->next; + } while (p != resfiles); + return FALSE; +} + +HRESULT Rsrc_load_file(LPCSTR filename, PHRESFILE newhandle) +{ + HRESULT hr = S_OK; + PRESFILE pfile = NULL; + zip_error_t errinfo; + + if (!newhandle) + return E_POINTER; + *newhandle = NULL; + + pfile = (PRESFILE)malloc(sizeof(RESFILE)); + if (!pfile) + return E_OUTOFMEMORY; + memset(pfile, 0, sizeof(RESFILE)); + + zip_error_init(&errinfo); + pfile->resource = zip_open(filename, ZIP_RDONLY, &errinfo); + if (!(pfile->resource)) + hr = ziperror_to_hresult(&errinfo); + zip_error_fini(&errinfo); + if (SUCCEEDED(hr)) + { + if (resfiles) + { + pfile->next = resfiles; + pfile->prev = resfiles->prev; + resfiles->prev->next = pfile; + resfiles->prev = pfile; + } + else + pfile->next = pfile->prev = pfile; + resfiles = pfile; + *newhandle = (HRESFILE)pfile; + } + return hr; +} + +static HRESULT internal_close(PRESFILE presfile) +{ + HRESULT hr = S_OK; + + if (zip_close(presfile->resource)) + hr = ziperror_to_hresult(zip_get_error(presfile->resource)); + if (resfiles == presfile) + { + resfiles = presfile->next; + if (resfiles == presfile) + resfiles = NULL; + } + presfile->prev->next = presfile->next; + presfile->next->prev = presfile->prev; + free(presfile); + return hr; +} + +HRESULT Rsrc_close_file(HRESFILE handle) +{ + if (check_in_list((PRESFILE)handle)) + return internal_close((PRESFILE)handle); + return E_HANDLE; +} + +HRESULT Rsrc_find_resource(HRESFILE hfile, LPCSTR name, LPCSTR type, PHRSRC presource) +{ + HRESULT hr = S_OK; + PRESFILE pfile = (PRESFILE)hfile; + PRESOURCEINFO info = NULL; + zip_t *theresource; + zip_int64_t index; + + if (pfile) + { + if (check_in_list(pfile)) + theresource = pfile->resource; + else + return E_HANDLE; + } + else + theresource = sysresource; + ASSERT(theresource); + if (!name) + return E_INVALIDARG; + if (!presource) + return E_POINTER; + *presource = NULL; + + index = zip_name_locate(theresource, name, ZIP_FL_NOCASE); + if (index < 0) + hr = ziperror_to_hresult(zip_get_error(theresource)); + if (SUCCEEDED(hr)) + { + info = (PRESOURCEINFO)malloc(sizeof(RESOURCEINFO)); + if (info) + { + if (zip_stat_index(theresource, index, ZIP_FL_NOCASE, &(info->entryinfo))) + hr = ziperror_to_hresult(zip_get_error(theresource)); + if (SUCCEEDED(hr)) + { + info->entryinfo.index = index; + info->entryinfo.valid |= ZIP_STAT_INDEX; + info->resource_file = theresource; + *presource = (HRSRC)info; + } + if (FAILED(hr)) + free(info); + } + else + hr = E_OUTOFMEMORY; + } + return hr; +} + +HRESULT Rsrc_free_resource(HRSRC resource) +{ + if (!resource) + return E_HANDLE; + free((PRESOURCEINFO)resource); + return S_OK; +} + +UINT Rsrc_sizeof_resource(HRSRC resource) +{ + PRESOURCEINFO p = (PRESOURCEINFO)resource; + if (!p) + return 0; + if (p->entryinfo.valid & ZIP_STAT_SIZE) + return (UINT)(p->entryinfo.size); + return 0; +} + +HRESULT Rsrc_read_resource_here(HRSRC resource, PVOID buffer, UINT size, PUINT actual_read) +{ + HRESULT hr = S_OK; + PRESOURCEINFO p = (PRESOURCEINFO)resource; + UINT max_size = size; + UINT already_read = 0; + PBYTE pbuffer = (PBYTE)buffer; + zip_int64_t nread; + zip_file_t *fp; + + if (!p) + return E_HANDLE; + if (!buffer) + return E_POINTER; + if (p->entryinfo.valid & ZIP_STAT_SIZE) + { + if ((UINT)(p->entryinfo.size) < max_size) + max_size = (UINT)(p->entryinfo.size); + else if ((UINT)(p->entryinfo.size) > max_size) + hr = UPIWIN_S_PARTIALRSRC; + } + + fp = zip_fopen_index(p->resource_file, p->entryinfo.index, ZIP_FL_NOCASE); + if (fp) + { + while (SUCCEEDED(hr) && (max_size > 0)) + { + nread = zip_fread(fp, pbuffer, max_size); + if (nread < 0) + { + hr = ziperror_to_hresult(zip_get_error(p->resource_file)); + break; + } + pbuffer += (UINT_PTR)nread; + max_size -= (UINT)nread; + already_read += (UINT)nread; + } + zip_fclose(fp); + } + else + hr = ziperror_to_hresult(zip_get_error(p->resource_file)); + if (actual_read) + *actual_read = already_read; + return hr; +} + static void rsrc_cleanup(void) { + while (resfiles) + internal_close(resfiles); zip_close(sysresource); sysresource = NULL; } diff --git a/src/resources.h b/src/resources.h index 2cc4f83..d6bae71 100644 --- a/src/resources.h +++ b/src/resources.h @@ -25,7 +25,15 @@ typedef HANDLE HRESFILE; /* handle to resource file */ typedef HANDLE HRSRC; /* handle to resource */ +typedef HRESFILE *PHRESFILE; +typedef HRSRC *PHRSRC; +extern HRESULT Rsrc_load_file(LPCSTR filename, PHRESFILE newhandle); +extern HRESULT Rsrc_close_file(HRESFILE handle); +extern HRESULT Rsrc_find_resource(HRESFILE hfile, LPCSTR name, LPCSTR type, PHRSRC presource); +extern HRESULT Rsrc_free_resource(HRSRC resource); +extern UINT Rsrc_sizeof_resource(HRSRC resource); +extern HRESULT Rsrc_read_resource_here(HRSRC resource, PVOID buffer, UINT size, PUINT actual_read); extern HRESULT Rsrc_setup(void); diff --git a/src/scode.h b/src/scode.h index e87f630..28aaf1a 100644 --- a/src/scode.h +++ b/src/scode.h @@ -107,6 +107,8 @@ #define UPIWIN_E_INVALIDSCRIPT SCODE_CAST(0x80060000) /* invalid script file */ #define UPIWIN_E_NOSCRIPT SCODE_CAST(0x80060001) /* no script specified */ +#define UPIWIN_S_PARTIALRSRC SCODE_CAST(0x00060000) /* partial resource read */ + /* libzip error codes */ #define ZIP_E_MULTIDISK MAKE_SCODE(SEVERITY_ERROR, FACILITY_ZIP, 1) /* multidisk not supported */ #define ZIP_E_RENAME MAKE_SCODE(SEVERITY_ERROR, FACILITY_ZIP, 2) /* rename temp file failed */