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