Make the dsound tests run with DirectX < 8.
[wine] / dlls / shlwapi / clist.c
1 /*
2  * SHLWAPI DataBlock List functions
3  *
4  * Copyright 2002 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdarg.h>
21 #include <string.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "objbase.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(shell);
29
30 /* DataBlock list element (ordinals 17-22) */
31 typedef struct tagSHLWAPI_CLIST
32 {
33   ULONG ulSize;        /* Size of this list element and its data */
34   ULONG ulId;          /* If 0xFFFFFFFF, The real element follows        */
35   /* Item data (or a contained SHLWAPI_CLIST) follows...         */
36 } SHLWAPI_CLIST, *LPSHLWAPI_CLIST;
37
38 typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST;
39
40 /* ulId for contained SHLWAPI_CLIST items */
41 #define CLIST_ID_CONTAINER (~0UL)
42
43 HRESULT WINAPI SHAddDataBlock(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST);
44
45 /*************************************************************************
46  * NextItem
47  *
48  * Internal helper: move a DataBlock pointer to the next item.
49  */
50 inline static LPSHLWAPI_CLIST NextItem(LPCSHLWAPI_CLIST lpList)
51 {
52   const char* address = (char*)lpList;
53   address += lpList->ulSize;
54   return (LPSHLWAPI_CLIST)address;
55 }
56
57 /*************************************************************************
58  *      @       [SHLWAPI.17]
59  *
60  * Write a DataBlock list to an IStream object.
61  *
62  * PARAMS
63  *  lpStream  [I] IStream object to write the list to
64  *  lpList    [I] List of items to write
65  *
66  * RETURNS
67  *  Success: S_OK. The object is written to the stream.
68  *  Failure: An HRESULT error code
69  *
70  * NOTES
71  *  Ordinals 17,18,19,20,21 and 22 are related and together provide a compact
72  *  list structure (a "DataBlock List"), which may be stored and retrieved from
73  *  an IStream object.
74  *
75  *  The exposed API consists of:
76  *
77  *  - SHWriteDataBlockList() - Write a DataBlock list to a stream,
78  *  - SHReadDataBlockList() - Read and create a list from a stream,
79  *  - SHFreeDataBlockList() - Free a list,
80  *  - SHAddDataBlock() - Insert a new item into a list,
81  *  - SHRemoveDataBlock() - Remove an item from a list,
82  *  - SHFindDataBlock() - Find an item in a list.
83  *
84  *  The DataBlock list is stored packed into a memory array. Each element has a
85  *  size and an associated ID. Elements must be less than 64k if the list is
86  *  to be subsequently read from a stream.
87  *
88  *  Elements are aligned on DWORD boundaries. If an elements data size is not
89  *  a DWORD size multiple, the element is wrapped by inserting a surrounding
90  *  element with an Id of 0xFFFFFFFF, and size sufficient to pad to a DWORD boundary.
91  *
92  *  These functions are slow for large objects and long lists.
93  */
94 HRESULT WINAPI SHWriteDataBlockList(IStream* lpStream, LPSHLWAPI_CLIST lpList)
95 {
96   ULONG ulSize;
97   HRESULT hRet = E_FAIL;
98
99   TRACE("(%p,%p)\n", lpStream, lpList);
100
101   if(lpList)
102   {
103     while (lpList->ulSize)
104     {
105       LPSHLWAPI_CLIST lpItem = lpList;
106
107       if(lpList->ulId == CLIST_ID_CONTAINER)
108         lpItem++;
109
110       hRet = IStream_Write(lpStream,lpItem,lpItem->ulSize,&ulSize);
111       if (FAILED(hRet))
112         return hRet;
113
114       if(lpItem->ulSize != ulSize)
115         return STG_E_MEDIUMFULL;
116
117       lpList = NextItem(lpList);
118     }
119   }
120
121   if(SUCCEEDED(hRet))
122   {
123     ULONG ulDummy;
124     ulSize = 0;
125
126     /* Write a terminating list entry with zero size */
127     hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy);
128   }
129
130   return hRet;
131 }
132
133 /*************************************************************************
134  *      @       [SHLWAPI.18]
135  *
136  * Read and create a DataBlock list from an IStream object.
137  *
138  * PARAMS
139  *  lpStream  [I] Stream to read the list from
140  *  lppList   [0] Pointer to receive the new List
141  *
142  * RETURNS
143  *  Success: S_OK
144  *  Failure: An HRESULT error code
145  *
146  * NOTES
147  *  When read from a file, list objects are limited in size to 64k.
148  *  See SHWriteDataBlockList.
149  */
150 HRESULT WINAPI SHReadDataBlockList(IStream* lpStream, LPSHLWAPI_CLIST* lppList)
151 {
152   SHLWAPI_CLIST bBuff[128]; /* Temporary storage for new list item */
153   ULONG ulBuffSize = sizeof(bBuff);
154   LPSHLWAPI_CLIST pItem = bBuff;
155   ULONG ulRead, ulSize;
156   HRESULT hRet = S_OK;
157
158   TRACE("(%p,%p)\n", lpStream, lppList);
159
160   if(*lppList)
161   {
162     /* Free any existing list */
163     LocalFree((HLOCAL)*lppList);
164     *lppList = NULL;
165   }
166
167   do
168   {
169     /* Read the size of the next item */
170     hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead);
171
172     if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize)
173       break; /* Read failed or read zero size (the end of the list) */
174
175     if(ulSize > 0xFFFF)
176     {
177       LARGE_INTEGER liZero;
178       ULARGE_INTEGER ulPos;
179
180       liZero.QuadPart = 0;
181
182       /* Back the stream up; this object is too big for the list */
183       if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos)))
184       {
185         liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG);
186         IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL);
187       }
188       break;
189     }
190     else if (ulSize >= sizeof(SHLWAPI_CLIST))
191     {
192       /* Add this new item to the list */
193       if(ulSize > ulBuffSize)
194       {
195         /* We need more buffer space, allocate it */
196         LPSHLWAPI_CLIST lpTemp;
197
198         if (pItem == bBuff)
199           lpTemp = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT, ulSize);
200         else
201           lpTemp = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)pItem, ulSize,
202                                                  LMEM_ZEROINIT|LMEM_MOVEABLE);
203
204         if(!lpTemp)
205         {
206           hRet = E_OUTOFMEMORY;
207           break;
208         }
209         ulBuffSize = ulSize;
210         pItem = lpTemp;
211       }
212
213       pItem->ulSize = ulSize;
214       ulSize -= sizeof(pItem->ulSize); /* already read this member */
215
216       /* Read the item Id and data */
217       hRet = IStream_Read(lpStream, &pItem->ulId, ulSize, &ulRead);
218
219       if(FAILED(hRet) || ulRead != ulSize)
220         break;
221
222       SHAddDataBlock(lppList, pItem); /* Insert Item */
223     }
224   } while(1);
225
226   /* If we allocated space, free it */
227   if(pItem != bBuff)
228     LocalFree((HLOCAL)pItem);
229
230   return hRet;
231 }
232
233 /*************************************************************************
234  *      @       [SHLWAPI.19]
235  *
236  * Free a DataBlock list.
237  *
238  * PARAMS
239  *  lpList [I] List to free
240  *
241  * RETURNS
242  *  Nothing.
243  *
244  * NOTES
245  *  See SHWriteDataBlockList.
246  */
247 VOID WINAPI SHFreeDataBlockList(LPSHLWAPI_CLIST lpList)
248 {
249   TRACE("(%p)\n", lpList);
250
251   if (lpList)
252     LocalFree((HLOCAL)lpList);
253 }
254
255 /*************************************************************************
256  *      @       [SHLWAPI.20]
257  *
258  * Insert a new item into a DataBlock list.
259  *
260  * PARAMS
261  *  lppList   [0] Pointer to the List
262  *  lpNewItem [I] The new item to add to the list
263  *
264  * RETURNS
265  *  Success: S_OK. The item is added to the list.
266  *  Failure: An HRESULT error code.
267  *
268  * NOTES
269  *  If the size of the element to be inserted is less than the size of a
270  *  SHLWAPI_CLIST node, or the Id for the item is CLIST_ID_CONTAINER,
271  *  the call returns S_OK but does not actually add the element.
272  *  See SHWriteDataBlockList.
273  */
274 HRESULT WINAPI SHAddDataBlock(LPSHLWAPI_CLIST* lppList, LPCSHLWAPI_CLIST lpNewItem)
275 {
276   LPSHLWAPI_CLIST lpInsertAt = NULL;
277   ULONG ulSize;
278
279   TRACE("(%p,%p)\n", lppList, lpNewItem);
280
281   if(!lppList || !lpNewItem )
282     return E_INVALIDARG;
283
284   if (lpNewItem->ulSize < sizeof(SHLWAPI_CLIST) ||
285       lpNewItem->ulId == CLIST_ID_CONTAINER)
286     return S_OK;
287
288   ulSize = lpNewItem->ulSize;
289
290   if(ulSize & 0x3)
291   {
292     /* Tune size to a ULONG boundary, add space for container element */
293     ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(SHLWAPI_CLIST);
294     TRACE("Creating container item, new size = %ld\n", ulSize);
295   }
296
297   if(!*lppList)
298   {
299     /* An empty list. Allocate space for terminal ulSize also */
300     *lppList = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT,
301                                            ulSize + sizeof(ULONG));
302     lpInsertAt = *lppList;
303   }
304   else
305   {
306     /* Append to the end of the list */
307     ULONG ulTotalSize = 0;
308     LPSHLWAPI_CLIST lpIter = *lppList;
309
310     /* Iterate to the end of the list, calculating the total size */
311     while (lpIter->ulSize)
312     {
313       ulTotalSize += lpIter->ulSize;
314       lpIter = NextItem(lpIter);
315     }
316
317     /* Increase the size of the list */
318     lpIter = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList,
319                                           ulTotalSize + ulSize+sizeof(ULONG),
320                                           LMEM_ZEROINIT | LMEM_MOVEABLE);
321     if(lpIter)
322     {
323       *lppList = lpIter;
324       lpInsertAt = (LPSHLWAPI_CLIST)((char*)lpIter + ulTotalSize); /* At end */
325     }
326   }
327
328   if(lpInsertAt)
329   {
330     /* Copy in the new item */
331     LPSHLWAPI_CLIST lpDest = lpInsertAt;
332
333     if(ulSize != lpNewItem->ulSize)
334     {
335       lpInsertAt->ulSize = ulSize;
336       lpInsertAt->ulId = CLIST_ID_CONTAINER;
337       lpDest++;
338     }
339     memcpy(lpDest, lpNewItem, lpNewItem->ulSize);
340
341     /* Terminate the list */
342     lpInsertAt = NextItem(lpInsertAt);
343     lpInsertAt->ulSize = 0;
344
345     return lpNewItem->ulSize;
346   }
347   return S_OK;
348 }
349
350 /*************************************************************************
351  *      @       [SHLWAPI.21]
352  *
353  * Remove an item from a DataBlock list.
354  *
355  * PARAMS
356  *  lppList [O] List to remove the item from
357  *  ulId    [I] Id of item to remove
358  *
359  * RETURNS
360  *  Success: TRUE.
361  *  Failure: FALSE, If any parameters are invalid, or the item was not found.
362  *
363  * NOTES
364  *  See SHWriteDataBlockList.
365  */
366 BOOL WINAPI SHRemoveDataBlock(LPSHLWAPI_CLIST* lppList, ULONG ulId)
367 {
368   LPSHLWAPI_CLIST lpList = 0;
369   LPSHLWAPI_CLIST lpItem = NULL;
370   LPSHLWAPI_CLIST lpNext;
371   ULONG ulNewSize;
372
373   TRACE("(%p,%ld)\n", lppList, ulId);
374
375   if(lppList && (lpList = *lppList))
376   {
377     /* Search for item in list */
378     while (lpList->ulSize)
379     {
380       if(lpList->ulId == ulId ||
381         (lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId))
382       {
383         lpItem = lpList; /* Found */
384         break;
385       }
386       lpList = NextItem(lpList);
387     }
388   }
389
390   if(!lpItem)
391     return FALSE;
392
393   lpList = lpNext = NextItem(lpItem);
394
395   /* Locate the end of the list */
396   while (lpList->ulSize)
397     lpList = NextItem(lpList);
398
399   /* Resize the list */
400   ulNewSize = LocalSize((HLOCAL)*lppList) - lpItem->ulSize;
401
402   /* Copy following elements over lpItem */
403   memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG));
404
405   if(ulNewSize <= sizeof(ULONG))
406   {
407     LocalFree((HLOCAL)*lppList);
408     *lppList = NULL; /* Removed the last element */
409   }
410   else
411   {
412     lpList = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList, ulNewSize,
413                                            LMEM_ZEROINIT|LMEM_MOVEABLE);
414     if(lpList)
415       *lppList = lpList;
416   }
417   return TRUE;
418 }
419
420 /*************************************************************************
421  *      @       [SHLWAPI.22]
422  *
423  * Find an item in a DataBlock list.
424  *
425  * PARAMS
426  *  lpList [I] List to search
427  *  ulId   [I] Id of item to find
428  *
429  * RETURNS
430  *  Success: A pointer to the list item found
431  *  Failure: NULL
432  *
433  * NOTES
434  *  See SHWriteDataBlockList.
435  */
436 LPSHLWAPI_CLIST WINAPI SHFindDataBlock(LPSHLWAPI_CLIST lpList, ULONG ulId)
437 {
438   TRACE("(%p,%ld)\n", lpList, ulId);
439
440   if(lpList)
441   {
442     while(lpList->ulSize)
443     {
444       if(lpList->ulId == ulId)
445         return lpList; /* Matched */
446       else if(lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId)
447         return lpList + 1; /* Contained item matches */
448
449       lpList = NextItem(lpList);
450     }
451   }
452   return NULL;
453 }