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