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