Commit f9100240 authored by Gregory M. Turner's avatar Gregory M. Turner Committed by Alexandre Julliard

Many words about cabinets.

parent 95c5a2b6
...@@ -20,13 +20,40 @@ ...@@ -20,13 +20,40 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
* *
* This is (or will be) a largely redundant reimplementation of the stuff in * This is a largely redundant reimplementation of the stuff in cabextract.c. It
* cabextract.c... it would theoretically be preferable to have only one, shared * would be theoretically preferable to have only one, shared implementation, however
* implementation, however there are semantic differences which may discourage efforts * there are semantic differences which may discourage efforts to unify the two. It
* to unify the two. It should be possible, if awkward, to go back and reimplement * should be possible, if awkward, to go back and reimplement cabextract.c using FDI.
* cabextract.c using FDI (once the FDI implementation is complete, of course). Also, * But this approach would be quite a bit less performant. Probably a better way
* cabextract's implementation is pretty efficient; fdi.c is, by contrast, extremely * would be to create a "library" of routines in cabextract.c which do the actual
* wasteful... * decompression, and have both fdi.c and cabextract share those routines. The rest
* of the code is not sufficiently similar to merit a shared implementation.
*
* The worst thing about this API is the bug. "The bug" is this: when you extract a
* cabinet, it /always/ informs you (via the hasnext field of PFDICABINETINFO), that
* there is no subsequent cabinet, even if there is one. wine faithfully reproduces
* this behavior.
*
* TODO:
*
* Wine does not implement the AFAIK undocumented "enumerate" callback during
* FDICopy. It is implemented in Windows and therefore worth investigating...
*
* Lots of pointers flying around here... am I leaking RAM?
*
* WTF is FDITruncate?
*
* Probably, I need to weed out some dead code-paths.
*
* Test unit(s).
*
* The fdintNEXT_CABINET callbacks are probably not working quite as they should.
* There are several FIXME's in the source describing some of the deficiencies in
* some detail. Additionally, we do not do a very good job of returning the right
* error codes to this callback.
*
* FDICopy and fdi_decomp are incomprehensibly large; separating these into smaller
* functions would be nice.
* *
* -gmt * -gmt
*/ */
...@@ -55,7 +82,7 @@ struct fdi_file { ...@@ -55,7 +82,7 @@ struct fdi_file {
cab_ULONG offset; /* uncompressed offset in folder */ cab_ULONG offset; /* uncompressed offset in folder */
cab_UWORD index; /* magic index number of folder */ cab_UWORD index; /* magic index number of folder */
cab_UWORD time, date, attribs; /* MS-DOS time/date/attributes */ cab_UWORD time, date, attribs; /* MS-DOS time/date/attributes */
BOOL oppressed; /* never to be processed */ BOOL oppressed; /* never to be processed */
}; };
struct fdi_folder { struct fdi_folder {
...@@ -71,7 +98,7 @@ struct fdi_folder { ...@@ -71,7 +98,7 @@ struct fdi_folder {
* this structure fills the gaps between what is available in a PFDICABINETINFO * this structure fills the gaps between what is available in a PFDICABINETINFO
* vs what is needed by FDICopy. Memory allocated for these becomes the responsibility * vs what is needed by FDICopy. Memory allocated for these becomes the responsibility
* of the caller to free. Yes, I am aware that this is totally, utterly inelegant. * of the caller to free. Yes, I am aware that this is totally, utterly inelegant.
* To make things even more unneccesarily confusing, we now attach these to the * To make things even more unnecessarily confusing, we now attach these to the
* fdi_decomp_state. * fdi_decomp_state.
*/ */
typedef struct { typedef struct {
...@@ -84,7 +111,7 @@ typedef struct { ...@@ -84,7 +111,7 @@ typedef struct {
/* /*
* ugh, well, this ended up being pretty damn silly... * ugh, well, this ended up being pretty damn silly...
* now that I've conceeded to build equivalent structures to struct cab.*, * now that I've conceded to build equivalent structures to struct cab.*,
* I should have just used those, or, better yet, unified the two... sue me. * I should have just used those, or, better yet, unified the two... sue me.
* (Note to Microsoft: That's a joke. Please /don't/ actually sue me! -gmt). * (Note to Microsoft: That's a joke. Please /don't/ actually sue me! -gmt).
* Nevertheless, I've come this far, it works, so I'm not gonna change it * Nevertheless, I've come this far, it works, so I'm not gonna change it
...@@ -122,6 +149,42 @@ typedef struct fdi_cds_fwd { ...@@ -122,6 +149,42 @@ typedef struct fdi_cds_fwd {
/*********************************************************************** /***********************************************************************
* FDICreate (CABINET.20) * FDICreate (CABINET.20)
*
* Provided with several callbacks (all of them are mandatory),
* returns a handle which can be used to perform operations
* on cabinet files.
*
* PARAMS
* pfnalloc [I] A pointer to a function which allocates ram. Uses
* the same interface as malloc.
* pfnfree [I] A pointer to a function which frees ram. Uses the
* same interface as free.
* pfnopen [I] A pointer to a function which opens a file. Uses
* the same interface as _open.
* pfnread [I] A pointer to a function which reads from a file into
* a caller-provided buffer. Uses the same interface
* as _read
* pfnwrite [I] A pointer to a function which writes to a file from
* a caller-provided buffer. Uses the same interface
* as _write.
* pfnclose [I] A pointer to a function which closes a file handle.
* Uses the same interface as _close.
* pfnseek [I] A pointer to a function which seeks in a file.
* Uses the same interface as _lseek.
* cpuType [I] The type of CPU; ignored in wine (recommended value:
* cpuUNKNOWN, aka -1).
* perf [IO] A pointer to an ERF structure. When FDICreate
* returns an error condition, error information may
* be found here as well as from GetLastError.
*
* RETURNS
* On success, returns an FDI handle of type HFDI.
* On failure, the NULL file handle is returned. Error
* info can be retrieved from perf.
*
* INCLUDES
* fdi.h
*
*/ */
HFDI __cdecl FDICreate( HFDI __cdecl FDICreate(
PFNALLOC pfnalloc, PFNALLOC pfnalloc,
...@@ -177,7 +240,7 @@ HFDI __cdecl FDICreate( ...@@ -177,7 +240,7 @@ HFDI __cdecl FDICreate(
/******************************************************************* /*******************************************************************
* FDI_getoffset (internal) * FDI_getoffset (internal)
* *
* returns the file pointer position of a cab * returns the file pointer position of a file handle.
*/ */
long FDI_getoffset(HFDI hfdi, INT_PTR hf) long FDI_getoffset(HFDI hfdi, INT_PTR hf)
{ {
...@@ -188,7 +251,7 @@ long FDI_getoffset(HFDI hfdi, INT_PTR hf) ...@@ -188,7 +251,7 @@ long FDI_getoffset(HFDI hfdi, INT_PTR hf)
* FDI_realloc (internal) * FDI_realloc (internal)
* *
* we can't use _msize; the user might not be using malloc, so we require * we can't use _msize; the user might not be using malloc, so we require
* an explicit specification of the previous size. utterly inefficient. * an explicit specification of the previous size. inefficient.
*/ */
void *FDI_realloc(HFDI hfdi, void *mem, size_t prevsize, size_t newsize) void *FDI_realloc(HFDI hfdi, void *mem, size_t prevsize, size_t newsize)
{ {
...@@ -207,7 +270,7 @@ void *FDI_realloc(HFDI hfdi, void *mem, size_t prevsize, size_t newsize) ...@@ -207,7 +270,7 @@ void *FDI_realloc(HFDI hfdi, void *mem, size_t prevsize, size_t newsize)
/********************************************************************** /**********************************************************************
* FDI_read_string (internal) * FDI_read_string (internal)
* *
* allocate and read an aribitrarily long string from the cabinet * allocate and read an arbitrarily long string from the cabinet
*/ */
char *FDI_read_string(HFDI hfdi, INT_PTR hf, long cabsize) char *FDI_read_string(HFDI hfdi, INT_PTR hf, long cabsize)
{ {
...@@ -261,7 +324,6 @@ char *FDI_read_string(HFDI hfdi, INT_PTR hf, long cabsize) ...@@ -261,7 +324,6 @@ char *FDI_read_string(HFDI hfdi, INT_PTR hf, long cabsize)
* *
* process the cabinet header in the style of FDIIsCabinet, but * process the cabinet header in the style of FDIIsCabinet, but
* without the sanity checks (and bug) * without the sanity checks (and bug)
*
*/ */
BOOL FDI_read_entries( BOOL FDI_read_entries(
HFDI hfdi, HFDI hfdi,
...@@ -277,6 +339,36 @@ BOOL FDI_read_entries( ...@@ -277,6 +339,36 @@ BOOL FDI_read_entries(
TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici); TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici);
/*
* FIXME: I just noticed that I am memorizing the initial file pointer
* offset and restoring it before reading in the rest of the header
* information in the cabinet. Perhaps that's correct -- that is, perhaps
* this API is supposed to support "streaming" cabinets which are embedded
* in other files, or cabinets which begin at file offsets other than zero.
* Otherwise, I should instead go to the absolute beginning of the file.
* (Either way, the semantics of wine's FDICopy require me to leave the
* file pointer where it is afterwards -- If Windows does not do so, we
* ought to duplicate the native behavior in the FDIIsCabinet API, not here.
*
* So, the answer lies in Windows; will native cabinet.dll recognize a
* cabinet "file" embedded in another file? Note that cabextract.c does
* support this, which implies that Microsoft's might. I haven't tried it
* yet so I don't know. ATM, most of wine's FDI cabinet routines (except
* this one) would not work in this way. To fix it, we could just make the
* various references to absolute file positions in the code relative to an
* initial "beginning" offset. Because the FDICopy API doesn't take a
* file-handle like this one, we would therein need to search through the
* file for the beginning of the cabinet (as we also do in cabextract.c).
* Note that this limits us to a maximum of one cabinet per. file: the first.
*
* So, in summary: either the code below is wrong, or the rest of fdi.c is
* wrong... I cannot imagine that both are correct ;) One of these flaws
* should be fixed after determining the behavior on Windows. We ought
* to check both FDIIsCabinet and FDICopy for the right behavior.
*
* -gmt
*/
/* get basic offset & size info */ /* get basic offset & size info */
base_offset = FDI_getoffset(hfdi, hf); base_offset = FDI_getoffset(hfdi, hf);
...@@ -449,6 +541,30 @@ BOOL FDI_read_entries( ...@@ -449,6 +541,30 @@ BOOL FDI_read_entries(
/*********************************************************************** /***********************************************************************
* FDIIsCabinet (CABINET.21) * FDIIsCabinet (CABINET.21)
*
* Informs the caller as to whether or not the provided file handle is
* really a cabinet or not, filling out the provided PFDICABINETINFO
* structure with information about the cabinet. Brief explanations of
* the elements of this structure are available as comments accompanying
* its definition in wine's include/fdi.h.
*
* PARAMS
* hfdi [I] An HFDI from FDICreate
* hf [I] The file handle about which the caller inquires
* pfdici [IO] Pointer to a PFDICABINETINFO structure which will
* be filled out with information about the cabinet
* file indicated by hf if, indeed, it is determined
* to be a cabinet.
*
* RETURNS
* TRUE if the file is a cabinet. The info pointed to by pfdici will
* be provided.
* FALSE if the file is not a cabinet, or if an error was encountered
* while processing the cabinet. The PERF structure provided to
* FDICreate can be queried for more error information.
*
* INCLUDES
* fdi.c
*/ */
BOOL __cdecl FDIIsCabinet( BOOL __cdecl FDIIsCabinet(
HFDI hfdi, HFDI hfdi,
...@@ -493,7 +609,7 @@ BOOL __cdecl FDIIsCabinet( ...@@ -493,7 +609,7 @@ BOOL __cdecl FDIIsCabinet(
/****************************************************************** /******************************************************************
* QTMfdi_initmodel (internal) * QTMfdi_initmodel (internal)
* *
* Initialise a model which decodes symbols from [s] to [s]+[n]-1 * Initialize a model which decodes symbols from [s] to [s]+[n]-1
*/ */
void QTMfdi_initmodel(struct QTMmodel *m, struct QTMmodelsym *sym, int n, int s) { void QTMfdi_initmodel(struct QTMmodel *m, struct QTMmodelsym *sym, int n, int s) {
int i; int i;
...@@ -530,7 +646,7 @@ int QTMfdi_init(int window, int level, fdi_decomp_state *decomp_state) { ...@@ -530,7 +646,7 @@ int QTMfdi_init(int window, int level, fdi_decomp_state *decomp_state) {
QTM(window_size) = wndsize; QTM(window_size) = wndsize;
QTM(window_posn) = 0; QTM(window_posn) = 0;
/* initialise static slot/extrabits tables */ /* initialize static slot/extrabits tables */
for (i = 0, j = 0; i < 27; i++) { for (i = 0, j = 0; i < 27; i++) {
CAB(q_length_extra)[i] = (i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2; CAB(q_length_extra)[i] = (i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2;
CAB(q_length_base)[i] = j; j += 1 << ((i == 26) ? 5 : CAB(q_length_extra)[i]); CAB(q_length_base)[i] = j; j += 1 << ((i == 26) ? 5 : CAB(q_length_extra)[i]);
...@@ -540,7 +656,7 @@ int QTMfdi_init(int window, int level, fdi_decomp_state *decomp_state) { ...@@ -540,7 +656,7 @@ int QTMfdi_init(int window, int level, fdi_decomp_state *decomp_state) {
CAB(q_position_base)[i] = j; j += 1 << CAB(q_extra_bits)[i]; CAB(q_position_base)[i] = j; j += 1 << CAB(q_extra_bits)[i];
} }
/* initialise arithmetic coding models */ /* initialize arithmetic coding models */
QTMfdi_initmodel(&QTM(model7), &QTM(m7sym)[0], 7, 0); QTMfdi_initmodel(&QTM(model7), &QTM(m7sym)[0], 7, 0);
...@@ -580,7 +696,7 @@ int LZXfdi_init(int window, fdi_decomp_state *decomp_state) { ...@@ -580,7 +696,7 @@ int LZXfdi_init(int window, fdi_decomp_state *decomp_state) {
} }
LZX(window_size) = wndsize; LZX(window_size) = wndsize;
/* initialise static tables */ /* initialize static tables */
for (i=0, j=0; i <= 50; i += 2) { for (i=0, j=0; i <= 50; i += 2) {
CAB(extra_bits)[i] = CAB(extra_bits)[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */ CAB(extra_bits)[i] = CAB(extra_bits)[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */
if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */ if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */
...@@ -607,7 +723,7 @@ int LZXfdi_init(int window, fdi_decomp_state *decomp_state) { ...@@ -607,7 +723,7 @@ int LZXfdi_init(int window, fdi_decomp_state *decomp_state) {
LZX(intel_started) = 0; LZX(intel_started) = 0;
LZX(window_posn) = 0; LZX(window_posn) = 0;
/* initialise tables to 0 (because deltas will be applied to them) */ /* initialize tables to 0 (because deltas will be applied to them) */
for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0; for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0;
for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LZX(LENGTH_len)[i] = 0; for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LZX(LENGTH_len)[i] = 0;
...@@ -1694,13 +1810,14 @@ int LZXfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) { ...@@ -1694,13 +1810,14 @@ int LZXfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) {
/********************************************************** /**********************************************************
* fdi_decomp (internal) * fdi_decomp (internal)
* *
* Decompress the "appropriate" number of bytes. If savemode is zero, * Decompress the requested number of bytes. If savemode is zero,
* do not save the output anywhere, just plow through blocks until we * do not save the output anywhere, just plow through blocks until we
* reach the starting point for fi, and remember the position of the * reach the specified (uncompressed) distance from the starting point,
* cabfile pointer after we are done; otherwise, save it out, decompressing * and remember the position of the cabfile pointer (and which cabfile)
* the number of bytes in the file specified by fi. This is also where we * after we are done; otherwise, save the data out to CAB(filehf),
* jumping to additional cabinets in the case of split cab's, and provide * decompressing the requested number of bytes and writing them out. This
* (most of) the NEXT_CABINET notification semantics described in the SDK. * is also where we jump to additional cabinets in the case of split
* cab's, and provide (some of) the NEXT_CABINET notification semantics.
*/ */
int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state, int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state,
char *pszCabPath, PFNFDINOTIFY pfnfdin, void *pvUser) char *pszCabPath, PFNFDINOTIFY pfnfdin, void *pvUser)
...@@ -1788,7 +1905,7 @@ int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state ...@@ -1788,7 +1905,7 @@ int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state
if (pathlen < 256) { if (pathlen < 256) {
for (i = 0; i <= pathlen; i++) for (i = 0; i <= pathlen; i++)
userpath[i] = pszCabPath[i]; userpath[i] = pszCabPath[i];
} /* else we are in a wierd place... let's leave it blank and see if the user fixes it */ } /* else we are in a weird place... let's leave it blank and see if the user fixes it */
} }
/* initial fdintNEXT_CABINET notification */ /* initial fdintNEXT_CABINET notification */
...@@ -1950,7 +2067,8 @@ int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state ...@@ -1950,7 +2067,8 @@ int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state
} }
} }
} }
if (!success) goto tryanothercab; /* this should never happen */ if (!success) goto tryanothercab; /* FIXME: shouldn't this trigger
"Wrong Cabinet" notification? */
} }
} }
...@@ -1967,6 +2085,167 @@ int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state ...@@ -1967,6 +2085,167 @@ int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state
/*********************************************************************** /***********************************************************************
* FDICopy (CABINET.22) * FDICopy (CABINET.22)
*
* Iterates through the files in the Cabinet file indicated by name and
* file-location. May chain forward to additional cabinets (typically
* only one) if files which begin in this Cabinet are continued in another
* cabinet. For each file which is partially contained in this cabinet,
* and partially contained in a prior cabinet, provides fdintPARTIAL_FILE
* notification to the pfnfdin callback. For each file which begins in
* this cabinet, fdintCOPY_FILE notification is provided to the pfnfdin
* callback, and the file is optionally decompressed and saved to disk.
* Notification is not provided for files which are not at least partially
* contained in the specified cabinet file.
*
* See below for a thorough explanation of the various notification
* callbacks.
*
* PARAMS
* hfdi [I] An HFDI from FDICreate
* pszCabinet [I] C-style string containing the filename of the cabinet
* pszCabPath [I] C-style string containing the file path of the cabinet
* flags [I] "Decoder parameters". Ignored. Suggested value: 0.
* pfnfdin [I] Pointer to a notification function. See CALLBACKS below.
* pfnfdid [I] Pointer to a decryption function. Ignored. Suggested
* value: NULL.
* pvUser [I] arbitrary void * value which is passed to callbacks.
*
* RETURNS
* TRUE if successful.
* FALSE if unsuccessful (error information is provided in the ERF structure
* associated with the provided decompression handle by FDICreate).
*
* CALLBACKS
*
* Two pointers to callback functions are provided as parameters to FDICopy:
* pfnfdin(of type PFNFDINOTIFY), and pfnfdid (of type PFNFDIDECRYPT). These
* types are as follows:
*
* typedef INT_PTR (__cdecl *PFNFDINOTIFY) ( FDINOTIFICATIONTYPE fdint,
* PFDINOTIFICATION pfdin );
*
* typedef int (__cdecl *PFNFDIDECRYPT) ( PFDIDECRYPT pfdid );
*
* You can create functions of this type using the FNFDINOTIFY() and
* FNFDIDECRYPT() macros, respectively. For example:
*
* FNFDINOTIFY(mycallback) {
* / * use variables fdint and pfdin to process notification * /
* }
*
* The second callback, which could be used for decrypting encrypted data,
* is not used at all.
*
* Each notification informs the user of some event which has occurred during
* decompression of the cabinet file; each notification is also an opportunity
* for the callee to abort decompression. The information provided to the
* callback and the meaning of the callback's return value vary drastically
* across the various types of notification. The type of notification is the
* fdint parameter; all other information is provided to the callback in
* notification-specific parts of the FDINOTIFICATION structure pointed to by
* pfdin. The only part of that structure which is assigned for every callback
* is the pv element, which contains the arbitrary value which was passed to
* FDICopy in the pvUser argument (psz1 is also used each time, but its meaning
* is highly dependant on fdint).
*
* If you encounter unknown notifications, you should return zero if you want
* decompression to continue (or -1 to abort). All strings used in the
* callbacks are regular C-style strings. Detailed descriptions of each
* notification type follow:
*
* fdintCABINET_INFO:
*
* This is the first notification provided after calling FDICopy, and provides
* the user with various information about the cabinet. Note that this is
* called for each cabinet FDICopy opens, not just the first one. In the
* structure pointed to by pfdin, psz1 contains a pointer to the name of the
* next cabinet file in the set after the one just loaded (if any), psz2
* contains a pointer to the name or "info" of the next disk, psz3
* contains a pointer to the file-path of the current cabinet, setID
* contains an arbitrary constant associated with this set of cabinet files,
* and iCabinet contains the numerical index of the current cabinet within
* that set. Return zero, or -1 to abort.
*
* fdintPARTIAL_FILE:
*
* This notification is provided when FDICopy encounters a part of a file
* contained in this cabinet which is missing its beginning. Files can be
* split across cabinets, so this is not necessarily an abnormality; it just
* means that the file in question begins in another cabinet. No file
* corresponding to this notification is extracted from the cabinet. In the
* structure pointed to by pfdin, psz1 contains a pointer to the name of the
* partial file, psz2 contains a pointer to the file name of the cabinet in
* which this file begins, and psz3 contains a pointer to the disk name or
* "info" of the cabinet where the file begins. Return zero, or -1 to abort.
*
* fdintCOPY_FILE:
*
* This notification is provided when FDICopy encounters a file which starts
* in the cabinet file, provided to FDICopy in pszCabinet. (FDICopy will not
* look for files in cabinets after the first one). One notification will be
* sent for each such file, before the file is decompressed. By returning
* zero, the callback can instruct FDICopy to skip the file. In the structure
* pointed to by pfdin, psz1 contains a pointer to the file's name, cb contains
* the size of the file (uncompressed), attribs contains the file attributes,
* and date and time contain the date and time of the file. attributes, date,
* and time are of the 16-bit ms-dos variety. Return -1 to abort decompression
* for the entire cabinet, 0 to skip just this file but continue scanning the
* cabinet for more files, or an FDIClose()-compatible file-handle.
*
* fdintCLOSE_FILE_INFO:
*
* This notification is important, don't forget to implement it. This
* notification indicates that a file has been successfully uncompressed and
* written to disk. Upon receipt of this notification, the callee is expected
* to close the file handle, to set the attributes and date/time of the
* closed file, and possibly to execute the file. In the structure pointed to
* by pfdin, psz1 contains a pointer to the name of the file, hf will be the
* open file handle (close it), cb contains 1 or zero, indicating respectively
* that the callee should or should not execute the file, and date, time
* and attributes will be set as in fdintCOPY_FILE. Bizarrely, the Cabinet SDK
* specifies that _A_EXEC will be xor'ed out of attributes! wine does not do
* do so. Return TRUE, or FALSE to abort decompression.
*
* fdintNEXT_CABINET:
*
* This notification is called when FDICopy must load in another cabinet. This
* can occur when a file's data is "split" across multiple cabinets. The
* callee has the opportunity to request that FDICopy look in a different file
* path for the specified cabinet file, by writing that data into a provided
* buffer (see below for more information). This notification will be received
* more than once per-cabinet in the instance that FDICopy failed to find a
* valid cabinet at the location specified by the first per-cabinet
* fdintNEXT_CABINET notification. In such instances, the fdie element of the
* structure pointed to by pfdin indicates the error which prevented FDICopy
* from proceeding successfully. Return zero to indicate success, or -1 to
* indicate failure and abort FDICopy.
*
* Upon receipt of this notification, the structure pointed to by pfdin will
* contain the following values: psz1 pointing to the name of the cabinet
* which FDICopy is attempting to open, psz2 pointing to the name ("info") of
* the next disk, psz3 pointing to the presumed file-location of the cabinet,
* and fdie containing either FDIERROR_NONE, or one of the following:
*
* FDIERROR_CABINET_NOT_FOUND, FDIERROR_NOT_A_CABINET,
* FDIERROR_UNKNOWN_CABINET_VERSION, FDIERROR_CORRUPT_CABINET,
* FDIERROR_BAD_COMPR_TYPE, FDIERROR_RESERVE_MISMATCH, and
* FDIERROR_WRONG_CABINET.
*
* The callee may choose to change the path where FDICopy will look for the
* cabinet after this notification. To do so, the caller may write the new
* pathname to the buffer pointed to by psz3, which is 256 characters in
* length, including the terminating null character, before returning zero.
*
* fdintENUMERATE:
*
* Undocumented and unimplemented in wine, this seems to be sent each time
* a cabinet is opened, along with the fdintCABINET_INFO notification. It
* probably has an interface similar to that of fdintCABINET_INFO; maybe this
* provides information about the current cabinet instead of the next one....
* this is just a guess, it has not been looked at closely.
*
* INCLUDES
* fdi.c
*/ */
BOOL __cdecl FDICopy( BOOL __cdecl FDICopy(
HFDI hfdi, HFDI hfdi,
...@@ -2145,25 +2424,54 @@ BOOL __cdecl FDICopy( ...@@ -2145,25 +2424,54 @@ BOOL __cdecl FDICopy(
} }
for (file = CAB(firstfile); (file); file = file->next) { for (file = CAB(firstfile); (file); file = file->next) {
/* partial-file notification (do it just once for the first cabinet) */
/*
* FIXME: This implementation keeps multiple cabinet files open at once
* when encountering a split cabinet. It is a quirk of this implementation
* that sometimes we decrypt the same block of data more than once, to find
* the right starting point for a file, moving the file-pointer backwards.
* If we kept a cache of certain file-pointer information, we could eliminate
* that behavior... in fact I am not sure that the caching we already have
* is not sufficient.
*
* The current implementation seems to work fine in straightforward situations
* where all the cabinet files needed for decryption are simultaneously
* available. But presumably, the API is supposed to support cabinets which
* are split across multiple CDROMS; we may need to change our implementation
* to strictly serialize it's file usage so that it opens only one cabinet
* at a time. Some experimentation with Windows is needed to figure out the
* precise semantics required. The relevant code is here and in fdi_decomp().
*/
/* partial-file notification */
if ((file->index & cffileCONTINUED_FROM_PREV) == cffileCONTINUED_FROM_PREV) { if ((file->index & cffileCONTINUED_FROM_PREV) == cffileCONTINUED_FROM_PREV) {
/* OK, more MS bugs to simulate here, I think. I don't have a huge spanning /*
* cabinet to test this theory on ATM, but here's the deal. The SDK says that we * FIXME: Need to create a Cabinet with a single file spanning multiple files
* are supposed to notify the user of the filename and "disk name" (info) of * and perform some tests to figure out the right behavior. The SDK says
* the cabinet where the spanning file /started/. That would certainly be convenient * FDICopy will notify the user of the filename and "disk name" (info) of
* for the consumer, who could decide to abort everything and try to start over with * the cabinet where the spanning file /started/.
* that cabinet so as to avoid partial file notification and successfully unpack. This *
* task would be a horrible bitch from the implementor's (wine's) perspective: the * That would certainly be convenient for the API-user, who could abort,
* information is associated nowhere with the file header and is not to be found in * everything (or parallelize, if that's allowed (it is in wine)), and call
* the cabinet header. So we would have to open the previous cabinet, and check * FDICopy again with the provided filename, so as to avoid partial file
* if it contains a single spanning file that's continued from yet another prior cabinet, * notification and successfully unpack. This task could be quite unpleasant
* and so-on, until we find the beginning. Note that cabextract.c has code to do exactly * from wine's perspective: the information specifying the "start cabinet" for
* this. Luckily, MS clearly didn't implement this logic, so we don't have to either. * a file is associated nowhere with the file header and is not to be found in
* Watching the callbacks (and debugmsg +file) clearly shows that they don't open * the cabinet header. We have only the index of the cabinet wherein the folder
* the preceeding cabinet -- and therefore, I deduce, there is NO WAY they could * begins, which contains the file. To find that cabinet, we must consider the
* have implemented what's in the spec. Instead, they are obviously just returning * index of the current cabinet, and chain backwards, cabinet-by-cabinet (for
* the previous cabinet and it's info from the header of this cabinet. So we shall * each cabinet refers to its "next" and "previous" cabinet only, like a linked
* do the same. Of course, I could be missing something... * list).
*
* Bear in mind that, in the spirit of CABINET.DLL, we must assume that any
* cabinet other than the active one might be at another filepath than the
* current one, or on another CDROM. This could get rather dicey, especially
* if we imagine parallelized access to the FDICopy API.
*
* The current implementation punts -- it just returns the previous cabinet and
* it's info from the header of this cabinet. This provides the right answer in
* 95% of the cases; its worth checking if Microsoft cuts the same corner before
* we "fix" it.
*/ */
ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); ZeroMemory(&fdin, sizeof(FDINOTIFICATION));
fdin.pv = pvUser; fdin.pv = pvUser;
...@@ -2231,7 +2539,7 @@ BOOL __cdecl FDICopy( ...@@ -2231,7 +2539,7 @@ BOOL __cdecl FDICopy(
TRACE("Resetting folder for file %s.\n", debugstr_a(file->filename)); TRACE("Resetting folder for file %s.\n", debugstr_a(file->filename));
/* free stuff for the old decompressor */ /* free stuff for the old decompresser */
switch (ct2) { switch (ct2) {
case cffoldCOMPTYPE_LZX: case cffoldCOMPTYPE_LZX:
if (LZX(window)) { if (LZX(window)) {
...@@ -2252,7 +2560,7 @@ BOOL __cdecl FDICopy( ...@@ -2252,7 +2560,7 @@ BOOL __cdecl FDICopy(
CAB(offset) = 0; CAB(offset) = 0;
CAB(outlen) = 0; CAB(outlen) = 0;
/* initialize the new decompressor */ /* initialize the new decompresser */
switch (ct1) { switch (ct1) {
case cffoldCOMPTYPE_NONE: case cffoldCOMPTYPE_NONE:
CAB(decompress) = NONEfdi_decomp; CAB(decompress) = NONEfdi_decomp;
...@@ -2346,10 +2654,10 @@ BOOL __cdecl FDICopy( ...@@ -2346,10 +2654,10 @@ BOOL __cdecl FDICopy(
fdin.pv = pvUser; fdin.pv = pvUser;
fdin.psz1 = (char *)file->filename; fdin.psz1 = (char *)file->filename;
fdin.hf = filehf; fdin.hf = filehf;
fdin.cb = (file->attribs & cffile_A_EXEC) ? TRUE : FALSE; fdin.cb = (file->attribs & cffile_A_EXEC) ? TRUE : FALSE; /* FIXME: is that right? */
fdin.date = file->date; fdin.date = file->date;
fdin.time = file->time; fdin.time = file->time;
fdin.attribs = file->attribs; fdin.attribs = file->attribs; /* FIXME: filter _A_EXEC? */
err = ((*pfnfdin)(fdintCLOSE_FILE_INFO, &fdin)); err = ((*pfnfdin)(fdintCLOSE_FILE_INFO, &fdin));
if (err == FALSE || err == -1) { if (err == FALSE || err == -1) {
/* /*
...@@ -2450,6 +2758,16 @@ BOOL __cdecl FDICopy( ...@@ -2450,6 +2758,16 @@ BOOL __cdecl FDICopy(
/*********************************************************************** /***********************************************************************
* FDIDestroy (CABINET.23) * FDIDestroy (CABINET.23)
*
* Frees a handle created by FDICreate. Do /not/ call this in the middle
* of FDICopy. Only reason for failure would be an invalid handle.
*
* PARAMS
* hfdi [I] The HFDI to free
*
* RETURNS
* TRUE for success
* FALSE for failure
*/ */
BOOL __cdecl FDIDestroy(HFDI hfdi) BOOL __cdecl FDIDestroy(HFDI hfdi)
{ {
...@@ -2466,6 +2784,8 @@ BOOL __cdecl FDIDestroy(HFDI hfdi) ...@@ -2466,6 +2784,8 @@ BOOL __cdecl FDIDestroy(HFDI hfdi)
/*********************************************************************** /***********************************************************************
* FDITruncateCabinet (CABINET.24) * FDITruncateCabinet (CABINET.24)
*
* Undocumented and unimplemented.
*/ */
BOOL __cdecl FDITruncateCabinet( BOOL __cdecl FDITruncateCabinet(
HFDI hfdi, HFDI hfdi,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment