1 /* 2 * Resources 3 * 4 * Copyright 1993 Robert J. Amstadt 5 * Copyright 1995, 2003 Alexandre Julliard 6 * Copyright 2006 Mike McCormack 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include <k32.h> 24 25 #include <wine/list.h> 26 27 #define NDEBUG 28 #include <debug.h> 29 DEBUG_CHANNEL(resource); 30 31 /* retrieve the resource name to pass to the ntdll functions */ 32 static NTSTATUS get_res_nameA( LPCSTR name, UNICODE_STRING *str ) 33 { 34 if (IS_INTRESOURCE(name)) 35 { 36 str->Buffer = ULongToPtr(LOWORD(name)); 37 return STATUS_SUCCESS; 38 } 39 if (name[0] == '#') 40 { 41 ULONG value; 42 if (RtlCharToInteger( name + 1, 10, &value ) != STATUS_SUCCESS || HIWORD(value)) 43 return STATUS_INVALID_PARAMETER; 44 str->Buffer = ULongToPtr(value); 45 return STATUS_SUCCESS; 46 } 47 RtlCreateUnicodeStringFromAsciiz( str, name ); 48 RtlUpcaseUnicodeString( str, str, FALSE ); 49 return STATUS_SUCCESS; 50 } 51 52 /* retrieve the resource name to pass to the ntdll functions */ 53 static NTSTATUS get_res_nameW( LPCWSTR name, UNICODE_STRING *str ) 54 { 55 if (IS_INTRESOURCE(name)) 56 { 57 str->Buffer = ULongToPtr(LOWORD(name)); 58 return STATUS_SUCCESS; 59 } 60 if (name[0] == '#') 61 { 62 ULONG value; 63 RtlInitUnicodeString( str, name + 1 ); 64 if (RtlUnicodeStringToInteger( str, 10, &value ) != STATUS_SUCCESS || HIWORD(value)) 65 return STATUS_INVALID_PARAMETER; 66 str->Buffer = ULongToPtr(value); 67 return STATUS_SUCCESS; 68 } 69 RtlCreateUnicodeString( str, name ); 70 RtlUpcaseUnicodeString( str, str, FALSE ); 71 return STATUS_SUCCESS; 72 } 73 74 /* implementation of FindResourceExA */ 75 static HRSRC find_resourceA( HMODULE hModule, LPCSTR type, LPCSTR name, WORD lang ) 76 { 77 NTSTATUS status; 78 UNICODE_STRING nameW, typeW; 79 LDR_RESOURCE_INFO info; 80 IMAGE_RESOURCE_DATA_ENTRY *entry = NULL; 81 82 nameW.Buffer = NULL; 83 typeW.Buffer = NULL; 84 85 __TRY 86 { 87 if ((status = get_res_nameA( name, &nameW )) != STATUS_SUCCESS) goto done; 88 if ((status = get_res_nameA( type, &typeW )) != STATUS_SUCCESS) goto done; 89 info.Type = (ULONG_PTR)typeW.Buffer; 90 info.Name = (ULONG_PTR)nameW.Buffer; 91 info.Language = lang; 92 status = LdrFindResource_U( hModule, &info, 3, &entry ); 93 done: 94 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 95 } 96 __EXCEPT_PAGE_FAULT 97 { 98 SetLastError( ERROR_INVALID_PARAMETER ); 99 } 100 __ENDTRY 101 102 if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); 103 if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); 104 return (HRSRC)entry; 105 } 106 107 108 /* implementation of FindResourceExW */ 109 static HRSRC find_resourceW( HMODULE hModule, LPCWSTR type, LPCWSTR name, WORD lang ) 110 { 111 NTSTATUS status; 112 UNICODE_STRING nameW, typeW; 113 LDR_RESOURCE_INFO info; 114 IMAGE_RESOURCE_DATA_ENTRY *entry = NULL; 115 116 nameW.Buffer = typeW.Buffer = NULL; 117 118 __TRY 119 { 120 if ((status = get_res_nameW( name, &nameW )) != STATUS_SUCCESS) goto done; 121 if ((status = get_res_nameW( type, &typeW )) != STATUS_SUCCESS) goto done; 122 info.Type = (ULONG_PTR)typeW.Buffer; 123 info.Name = (ULONG_PTR)nameW.Buffer; 124 info.Language = lang; 125 status = LdrFindResource_U( hModule, &info, 3, &entry ); 126 done: 127 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 128 } 129 __EXCEPT_PAGE_FAULT 130 { 131 SetLastError( ERROR_INVALID_PARAMETER ); 132 } 133 __ENDTRY 134 135 if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); 136 if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); 137 return (HRSRC)entry; 138 } 139 140 /********************************************************************** 141 * FindResourceExA (KERNEL32.@) 142 */ 143 HRSRC WINAPI FindResourceExA( HMODULE hModule, LPCSTR type, LPCSTR name, WORD lang ) 144 { 145 TRACE( "%p %s %s %04x\n", hModule, debugstr_a(type), debugstr_a(name), lang ); 146 147 if (!hModule) hModule = GetModuleHandleW(0); 148 return find_resourceA( hModule, type, name, lang ); 149 } 150 151 152 /********************************************************************** 153 * FindResourceA (KERNEL32.@) 154 */ 155 HRSRC WINAPI FindResourceA( HMODULE hModule, LPCSTR name, LPCSTR type ) 156 { 157 return FindResourceExA( hModule, type, name, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) ); 158 } 159 160 161 /********************************************************************** 162 * FindResourceExW (KERNEL32.@) 163 */ 164 HRSRC WINAPI FindResourceExW( HMODULE hModule, LPCWSTR type, LPCWSTR name, WORD lang ) 165 { 166 TRACE( "%p %s %s %04x\n", hModule, debugstr_w(type), debugstr_w(name), lang ); 167 168 if (!hModule) hModule = GetModuleHandleW(0); 169 return find_resourceW( hModule, type, name, lang ); 170 } 171 172 173 /********************************************************************** 174 * FindResourceW (KERNEL32.@) 175 */ 176 HRSRC WINAPI FindResourceW( HINSTANCE hModule, LPCWSTR name, LPCWSTR type ) 177 { 178 return FindResourceExW( hModule, type, name, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) ); 179 } 180 181 182 /********************************************************************** 183 * EnumResourceTypesA (KERNEL32.@) 184 */ 185 BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG_PTR lparam ) 186 { 187 int i; 188 BOOL ret = FALSE; 189 LPSTR type = NULL; 190 DWORD len = 0, newlen; 191 NTSTATUS status; 192 IMAGE_RESOURCE_DIRECTORY *resdir; 193 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; 194 const IMAGE_RESOURCE_DIR_STRING_U *str; 195 196 TRACE( "%p %p %lx\n", hmod, lpfun, lparam ); 197 198 if (!hmod) hmod = GetModuleHandleA( NULL ); 199 200 if ((status = LdrFindResourceDirectory_U( hmod, NULL, 0, &resdir )) != STATUS_SUCCESS) 201 { 202 SetLastError( RtlNtStatusToDosError(status) ); 203 return FALSE; 204 } 205 et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); 206 for (i = 0; i < resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries; i++) 207 { 208 if (et[i].NameIsString) 209 { 210 str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)resdir + et[i].NameOffset); 211 newlen = WideCharToMultiByte( CP_ACP, 0, str->NameString, str->Length, NULL, 0, NULL, NULL); 212 if (newlen + 1 > len) 213 { 214 len = newlen + 1; 215 HeapFree( GetProcessHeap(), 0, type ); 216 if (!(type = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; 217 } 218 WideCharToMultiByte( CP_ACP, 0, str->NameString, str->Length, type, len, NULL, NULL); 219 type[newlen] = 0; 220 ret = lpfun(hmod,type,lparam); 221 } 222 else 223 { 224 ret = lpfun( hmod, UIntToPtr(et[i].Id), lparam ); 225 } 226 if (!ret) break; 227 } 228 HeapFree( GetProcessHeap(), 0, type ); 229 return ret; 230 } 231 232 233 /********************************************************************** 234 * EnumResourceTypesW (KERNEL32.@) 235 */ 236 BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG_PTR lparam ) 237 { 238 int i, len = 0; 239 BOOL ret = FALSE; 240 LPWSTR type = NULL; 241 NTSTATUS status; 242 IMAGE_RESOURCE_DIRECTORY *resdir; 243 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; 244 const IMAGE_RESOURCE_DIR_STRING_U *str; 245 246 TRACE( "%p %p %lx\n", hmod, lpfun, lparam ); 247 248 if (!hmod) hmod = GetModuleHandleW( NULL ); 249 250 if ((status = LdrFindResourceDirectory_U( hmod, NULL, 0, &resdir )) != STATUS_SUCCESS) 251 { 252 SetLastError( RtlNtStatusToDosError(status) ); 253 return FALSE; 254 } 255 et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); 256 for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) 257 { 258 if (et[i].NameIsString) 259 { 260 str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)resdir + et[i].NameOffset); 261 if (str->Length + 1 > len) 262 { 263 len = str->Length + 1; 264 HeapFree( GetProcessHeap(), 0, type ); 265 if (!(type = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; 266 } 267 memcpy(type, str->NameString, str->Length * sizeof (WCHAR)); 268 type[str->Length] = 0; 269 ret = lpfun(hmod,type,lparam); 270 } 271 else 272 { 273 ret = lpfun( hmod, UIntToPtr(et[i].Id), lparam ); 274 } 275 if (!ret) break; 276 } 277 HeapFree( GetProcessHeap(), 0, type ); 278 return ret; 279 } 280 281 282 /********************************************************************** 283 * EnumResourceNamesA (KERNEL32.@) 284 */ 285 BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG_PTR lparam ) 286 { 287 int i; 288 BOOL ret = FALSE; 289 DWORD len = 0, newlen; 290 LPSTR name = NULL; 291 NTSTATUS status; 292 UNICODE_STRING typeW; 293 LDR_RESOURCE_INFO info; 294 IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; 295 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; 296 const IMAGE_RESOURCE_DIR_STRING_U *str; 297 298 TRACE( "%p %s %p %lx\n", hmod, debugstr_a(type), lpfun, lparam ); 299 300 if (!hmod) hmod = GetModuleHandleA( NULL ); 301 typeW.Buffer = NULL; 302 if ((status = LdrFindResourceDirectory_U( hmod, NULL, 0, &basedir )) != STATUS_SUCCESS) 303 goto done; 304 if ((status = get_res_nameA( type, &typeW )) != STATUS_SUCCESS) 305 goto done; 306 info.Type = (ULONG_PTR)typeW.Buffer; 307 if ((status = LdrFindResourceDirectory_U( hmod, &info, 1, &resdir )) != STATUS_SUCCESS) 308 goto done; 309 310 et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); 311 __TRY 312 { 313 for (i = 0; i < resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries; i++) 314 { 315 if (et[i].NameIsString) 316 { 317 str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)basedir + et[i].NameOffset); 318 newlen = WideCharToMultiByte(CP_ACP, 0, str->NameString, str->Length, NULL, 0, NULL, NULL); 319 if (newlen + 1 > len) 320 { 321 len = newlen + 1; 322 HeapFree( GetProcessHeap(), 0, name ); 323 if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 ))) 324 { 325 ret = FALSE; 326 break; 327 } 328 } 329 WideCharToMultiByte( CP_ACP, 0, str->NameString, str->Length, name, len, NULL, NULL ); 330 name[newlen] = 0; 331 ret = lpfun(hmod,type,name,lparam); 332 } 333 else 334 { 335 ret = lpfun( hmod, type, UIntToPtr(et[i].Id), lparam ); 336 } 337 if (!ret) break; 338 } 339 } 340 __EXCEPT_PAGE_FAULT 341 { 342 ret = FALSE; 343 status = STATUS_ACCESS_VIOLATION; 344 } 345 __ENDTRY; 346 347 done: 348 HeapFree( GetProcessHeap(), 0, name ); 349 if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); 350 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 351 return ret; 352 } 353 354 355 /********************************************************************** 356 * EnumResourceNamesW (KERNEL32.@) 357 */ 358 BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG_PTR lparam ) 359 { 360 int i, len = 0; 361 BOOL ret = FALSE; 362 LPWSTR name = NULL; 363 NTSTATUS status; 364 UNICODE_STRING typeW; 365 LDR_RESOURCE_INFO info; 366 IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; 367 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; 368 const IMAGE_RESOURCE_DIR_STRING_U *str; 369 370 TRACE( "%p %s %p %lx\n", hmod, debugstr_w(type), lpfun, lparam ); 371 372 if (!hmod) hmod = GetModuleHandleW( NULL ); 373 typeW.Buffer = NULL; 374 if ((status = LdrFindResourceDirectory_U( hmod, NULL, 0, &basedir )) != STATUS_SUCCESS) 375 goto done; 376 if ((status = get_res_nameW( type, &typeW )) != STATUS_SUCCESS) 377 goto done; 378 info.Type = (ULONG_PTR)typeW.Buffer; 379 if ((status = LdrFindResourceDirectory_U( hmod, &info, 1, &resdir )) != STATUS_SUCCESS) 380 goto done; 381 382 et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); 383 __TRY 384 { 385 for (i = 0; i < resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries; i++) 386 { 387 if (et[i].NameIsString) 388 { 389 str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)basedir + et[i].NameOffset); 390 if (str->Length + 1 > len) 391 { 392 len = str->Length + 1; 393 HeapFree( GetProcessHeap(), 0, name ); 394 if (!(name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) 395 { 396 ret = FALSE; 397 break; 398 } 399 } 400 memcpy(name, str->NameString, str->Length * sizeof (WCHAR)); 401 name[str->Length] = 0; 402 ret = lpfun(hmod,type,name,lparam); 403 } 404 else 405 { 406 ret = lpfun( hmod, type, UIntToPtr(et[i].Id), lparam ); 407 } 408 if (!ret) break; 409 } 410 } 411 __EXCEPT_PAGE_FAULT 412 { 413 ret = FALSE; 414 status = STATUS_ACCESS_VIOLATION; 415 } 416 __ENDTRY 417 done: 418 HeapFree( GetProcessHeap(), 0, name ); 419 if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); 420 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 421 return ret; 422 } 423 424 425 /********************************************************************** 426 * EnumResourceLanguagesA (KERNEL32.@) 427 */ 428 BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name, 429 ENUMRESLANGPROCA lpfun, LONG_PTR lparam ) 430 { 431 int i; 432 BOOL ret = FALSE; 433 NTSTATUS status; 434 UNICODE_STRING typeW, nameW; 435 LDR_RESOURCE_INFO info; 436 IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; 437 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; 438 439 TRACE( "%p %s %s %p %lx\n", hmod, debugstr_a(type), debugstr_a(name), lpfun, lparam ); 440 441 if (!hmod) hmod = GetModuleHandleA( NULL ); 442 typeW.Buffer = nameW.Buffer = NULL; 443 if ((status = LdrFindResourceDirectory_U( hmod, NULL, 0, &basedir )) != STATUS_SUCCESS) 444 goto done; 445 if ((status = get_res_nameA( type, &typeW )) != STATUS_SUCCESS) 446 goto done; 447 if ((status = get_res_nameA( name, &nameW )) != STATUS_SUCCESS) 448 goto done; 449 info.Type = (ULONG_PTR)typeW.Buffer; 450 info.Name = (ULONG_PTR)nameW.Buffer; 451 if ((status = LdrFindResourceDirectory_U( hmod, &info, 2, &resdir )) != STATUS_SUCCESS) 452 goto done; 453 454 et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); 455 __TRY 456 { 457 for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) 458 { 459 ret = lpfun( hmod, type, name, et[i].Id, lparam ); 460 if (!ret) break; 461 } 462 } 463 __EXCEPT_PAGE_FAULT 464 { 465 ret = FALSE; 466 status = STATUS_ACCESS_VIOLATION; 467 } 468 __ENDTRY 469 done: 470 if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); 471 if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); 472 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 473 return ret; 474 } 475 476 477 /********************************************************************** 478 * EnumResourceLanguagesW (KERNEL32.@) 479 */ 480 BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name, 481 ENUMRESLANGPROCW lpfun, LONG_PTR lparam ) 482 { 483 int i; 484 BOOL ret = FALSE; 485 NTSTATUS status; 486 UNICODE_STRING typeW, nameW; 487 LDR_RESOURCE_INFO info; 488 IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; 489 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; 490 491 TRACE( "%p %s %s %p %lx\n", hmod, debugstr_w(type), debugstr_w(name), lpfun, lparam ); 492 493 if (!hmod) hmod = GetModuleHandleW( NULL ); 494 typeW.Buffer = nameW.Buffer = NULL; 495 if ((status = LdrFindResourceDirectory_U( hmod, NULL, 0, &basedir )) != STATUS_SUCCESS) 496 goto done; 497 if ((status = get_res_nameW( type, &typeW )) != STATUS_SUCCESS) 498 goto done; 499 if ((status = get_res_nameW( name, &nameW )) != STATUS_SUCCESS) 500 goto done; 501 info.Type = (ULONG_PTR)typeW.Buffer; 502 info.Name = (ULONG_PTR)nameW.Buffer; 503 if ((status = LdrFindResourceDirectory_U( hmod, &info, 2, &resdir )) != STATUS_SUCCESS) 504 goto done; 505 506 et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); 507 __TRY 508 { 509 for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) 510 { 511 ret = lpfun( hmod, type, name, et[i].Id, lparam ); 512 if (!ret) break; 513 } 514 } 515 __EXCEPT_PAGE_FAULT 516 { 517 ret = FALSE; 518 status = STATUS_ACCESS_VIOLATION; 519 } 520 __ENDTRY 521 done: 522 if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); 523 if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); 524 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 525 return ret; 526 } 527 528 529 /********************************************************************** 530 * LoadResource (KERNEL32.@) 531 */ 532 HGLOBAL WINAPI LoadResource( HINSTANCE hModule, HRSRC hRsrc ) 533 { 534 NTSTATUS status; 535 void *ret = NULL; 536 537 TRACE( "%p %p\n", hModule, hRsrc ); 538 539 if (!hRsrc) return 0; 540 if (!hModule) hModule = GetModuleHandleA( NULL ); 541 status = LdrAccessResource( hModule, (IMAGE_RESOURCE_DATA_ENTRY *)hRsrc, &ret, NULL ); 542 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 543 return ret; 544 } 545 546 547 /********************************************************************** 548 * LockResource (KERNEL32.@) 549 */ 550 LPVOID WINAPI LockResource( HGLOBAL handle ) 551 { 552 return handle; 553 } 554 555 556 /********************************************************************** 557 * FreeResource (KERNEL32.@) 558 */ 559 BOOL WINAPI FreeResource( HGLOBAL handle ) 560 { 561 return FALSE; 562 } 563 564 565 /********************************************************************** 566 * SizeofResource (KERNEL32.@) 567 */ 568 DWORD WINAPI SizeofResource( HINSTANCE hModule, HRSRC hRsrc ) 569 { 570 if (!hRsrc) return 0; 571 return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size; 572 } 573 574 /* 575 * Data structure for updating resources. 576 * Type/Name/Language is a keyset for accessing resource data. 577 * 578 * QUEUEDUPDATES (root) -> 579 * list of struct resource_dir_entry (Type) -> 580 * list of struct resource_dir_entry (Name) -> 581 * list of struct resource_data Language + Data 582 */ 583 584 typedef struct 585 { 586 LPWSTR pFileName; 587 BOOL bDeleteExistingResources; 588 struct list root; 589 } QUEUEDUPDATES; 590 591 /* this structure is shared for types and names */ 592 struct resource_dir_entry { 593 struct list entry; 594 LPWSTR id; 595 struct list children; 596 }; 597 598 /* this structure is the leaf */ 599 struct resource_data { 600 struct list entry; 601 LANGID lang; 602 DWORD codepage; 603 DWORD cbData; 604 void *lpData; 605 }; 606 607 static int resource_strcmp( LPCWSTR a, LPCWSTR b ) 608 { 609 if ( a == b ) 610 return 0; 611 if (!IS_INTRESOURCE( a ) && !IS_INTRESOURCE( b ) ) 612 return lstrcmpW( a, b ); 613 /* strings come before ids */ 614 if (!IS_INTRESOURCE( a ) && IS_INTRESOURCE( b )) 615 return -1; 616 if (!IS_INTRESOURCE( b ) && IS_INTRESOURCE( a )) 617 return 1; 618 return ( a < b ) ? -1 : 1; 619 } 620 621 static struct resource_dir_entry *find_resource_dir_entry( struct list *dir, LPCWSTR id ) 622 { 623 struct resource_dir_entry *ent; 624 625 /* match either IDs or strings */ 626 LIST_FOR_EACH_ENTRY( ent, dir, struct resource_dir_entry, entry ) 627 if (!resource_strcmp( id, ent->id )) 628 return ent; 629 630 return NULL; 631 } 632 633 static struct resource_data *find_resource_data( struct list *dir, LANGID lang ) 634 { 635 struct resource_data *res_data; 636 637 /* match only languages here */ 638 LIST_FOR_EACH_ENTRY( res_data, dir, struct resource_data, entry ) 639 if ( lang == res_data->lang ) 640 return res_data; 641 642 return NULL; 643 } 644 645 static void add_resource_dir_entry( struct list *dir, struct resource_dir_entry *resdir ) 646 { 647 struct resource_dir_entry *ent; 648 649 LIST_FOR_EACH_ENTRY( ent, dir, struct resource_dir_entry, entry ) 650 { 651 if (0>resource_strcmp( ent->id, resdir->id )) 652 continue; 653 654 list_add_before( &ent->entry, &resdir->entry ); 655 return; 656 } 657 list_add_tail( dir, &resdir->entry ); 658 } 659 660 static void add_resource_data_entry( struct list *dir, struct resource_data *resdata ) 661 { 662 struct resource_data *ent; 663 664 LIST_FOR_EACH_ENTRY( ent, dir, struct resource_data, entry ) 665 { 666 if (ent->lang < resdata->lang) 667 continue; 668 669 list_add_before( &ent->entry, &resdata->entry ); 670 return; 671 } 672 list_add_tail( dir, &resdata->entry ); 673 } 674 675 static LPWSTR res_strdupW( LPCWSTR str ) 676 { 677 LPWSTR ret; 678 UINT len; 679 680 if (IS_INTRESOURCE(str)) 681 return (LPWSTR) (UINT_PTR) LOWORD(str); 682 len = (lstrlenW( str ) + 1) * sizeof (WCHAR); 683 ret = HeapAlloc( GetProcessHeap(), 0, len ); 684 memcpy( ret, str, len ); 685 return ret; 686 } 687 688 static void res_free_str( LPWSTR str ) 689 { 690 if (!IS_INTRESOURCE(str)) 691 HeapFree( GetProcessHeap(), 0, str ); 692 } 693 694 static BOOL update_add_resource( QUEUEDUPDATES *updates, LPCWSTR Type, LPCWSTR Name, 695 LANGID Lang, struct resource_data *resdata, 696 BOOL overwrite_existing ) 697 { 698 struct resource_dir_entry *restype, *resname; 699 struct resource_data *existing; 700 701 TRACE("%p %s %s %p %d\n", updates, 702 debugstr_w(Type), debugstr_w(Name), resdata, overwrite_existing ); 703 704 restype = find_resource_dir_entry( &updates->root, Type ); 705 if (!restype) 706 { 707 restype = HeapAlloc( GetProcessHeap(), 0, sizeof *restype ); 708 restype->id = res_strdupW( Type ); 709 list_init( &restype->children ); 710 add_resource_dir_entry( &updates->root, restype ); 711 } 712 713 resname = find_resource_dir_entry( &restype->children, Name ); 714 if (!resname) 715 { 716 resname = HeapAlloc( GetProcessHeap(), 0, sizeof *resname ); 717 resname->id = res_strdupW( Name ); 718 list_init( &resname->children ); 719 add_resource_dir_entry( &restype->children, resname ); 720 } 721 722 /* 723 * If there's an existing resource entry with matching (Type,Name,Language) 724 * it needs to be removed before adding the new data. 725 */ 726 existing = find_resource_data( &resname->children, Lang ); 727 if (existing) 728 { 729 if (!overwrite_existing) 730 return FALSE; 731 list_remove( &existing->entry ); 732 HeapFree( GetProcessHeap(), 0, existing ); 733 } 734 735 if (resdata) 736 add_resource_data_entry( &resname->children, resdata ); 737 738 return TRUE; 739 } 740 741 static struct resource_data *allocate_resource_data( WORD Language, DWORD codepage, 742 LPVOID lpData, DWORD cbData, BOOL copy_data ) 743 { 744 struct resource_data *resdata; 745 746 if (!lpData || !cbData) 747 return NULL; 748 749 resdata = HeapAlloc( GetProcessHeap(), 0, sizeof *resdata + (copy_data ? cbData : 0) ); 750 if (resdata) 751 { 752 resdata->lang = Language; 753 resdata->codepage = codepage; 754 resdata->cbData = cbData; 755 if (copy_data) 756 { 757 resdata->lpData = &resdata[1]; 758 memcpy( resdata->lpData, lpData, cbData ); 759 } 760 else 761 resdata->lpData = lpData; 762 } 763 764 return resdata; 765 } 766 767 static void free_resource_directory( struct list *head, int level ) 768 { 769 struct list *ptr = NULL; 770 771 while ((ptr = list_head( head ))) 772 { 773 list_remove( ptr ); 774 if (level) 775 { 776 struct resource_dir_entry *ent; 777 778 ent = LIST_ENTRY( ptr, struct resource_dir_entry, entry ); 779 res_free_str( ent->id ); 780 free_resource_directory( &ent->children, level - 1 ); 781 HeapFree(GetProcessHeap(), 0, ent); 782 } 783 else 784 { 785 struct resource_data *data; 786 787 data = LIST_ENTRY( ptr, struct resource_data, entry ); 788 HeapFree( GetProcessHeap(), 0, data ); 789 } 790 } 791 } 792 793 static IMAGE_NT_HEADERS *get_nt_header( void *base, DWORD mapping_size ) 794 { 795 IMAGE_NT_HEADERS *nt; 796 IMAGE_DOS_HEADER *dos; 797 798 if (mapping_size<sizeof (*dos)) 799 return NULL; 800 801 dos = base; 802 if (dos->e_magic != IMAGE_DOS_SIGNATURE) 803 return NULL; 804 805 if ((dos->e_lfanew + sizeof (*nt)) > mapping_size) 806 return NULL; 807 808 nt = (void*) ((BYTE*)base + dos->e_lfanew); 809 810 if (nt->Signature != IMAGE_NT_SIGNATURE) 811 return NULL; 812 813 return nt; 814 } 815 816 static IMAGE_SECTION_HEADER *get_section_header( void *base, DWORD mapping_size, DWORD *num_sections ) 817 { 818 IMAGE_NT_HEADERS *nt; 819 DWORD section_ofs; 820 821 nt = get_nt_header( base, mapping_size ); 822 if (!nt) 823 return NULL; 824 825 /* check that we don't go over the end of the file accessing the sections */ 826 section_ofs = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + nt->FileHeader.SizeOfOptionalHeader; 827 if ((nt->FileHeader.NumberOfSections * sizeof (IMAGE_SECTION_HEADER) + section_ofs) > mapping_size) 828 return NULL; 829 830 if (num_sections) 831 *num_sections = nt->FileHeader.NumberOfSections; 832 833 /* from here we have a valid PE exe to update */ 834 return (void*) ((BYTE*)nt + section_ofs); 835 } 836 837 static BOOL check_pe_exe( HANDLE file, QUEUEDUPDATES *updates ) 838 { 839 const IMAGE_NT_HEADERS32 *nt; 840 const IMAGE_NT_HEADERS64 *nt64; 841 const IMAGE_SECTION_HEADER *sec; 842 const IMAGE_DATA_DIRECTORY *dd; 843 BOOL ret = FALSE; 844 HANDLE mapping; 845 DWORD mapping_size, num_sections = 0; 846 void *base = NULL; 847 848 mapping_size = GetFileSize( file, NULL ); 849 850 mapping = CreateFileMappingW( file, NULL, PAGE_READONLY, 0, 0, NULL ); 851 if (!mapping) 852 goto done; 853 854 base = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, mapping_size ); 855 if (!base) 856 goto done; 857 858 nt = (IMAGE_NT_HEADERS32 *)get_nt_header( base, mapping_size ); 859 if (!nt) 860 goto done; 861 862 nt64 = (IMAGE_NT_HEADERS64*)nt; 863 dd = &nt->OptionalHeader.DataDirectory[0]; 864 if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 865 dd = &nt64->OptionalHeader.DataDirectory[0]; 866 867 TRACE("resources: %08x %08x\n", 868 dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress, 869 dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size); 870 871 sec = get_section_header( base, mapping_size, &num_sections ); 872 if (!sec) 873 goto done; 874 875 ret = TRUE; 876 877 done: 878 if (base) 879 UnmapViewOfFile( base ); 880 if (mapping) 881 CloseHandle( mapping ); 882 883 return ret; 884 } 885 886 struct resource_size_info { 887 DWORD types_ofs; 888 DWORD names_ofs; 889 DWORD langs_ofs; 890 DWORD data_entry_ofs; 891 DWORD strings_ofs; 892 DWORD data_ofs; 893 DWORD total_size; 894 }; 895 896 struct mapping_info { 897 HANDLE file; 898 void *base; 899 DWORD size; 900 BOOL read_write; 901 }; 902 903 static const IMAGE_SECTION_HEADER *section_from_rva( void *base, DWORD mapping_size, DWORD rva ) 904 { 905 const IMAGE_SECTION_HEADER *sec; 906 DWORD num_sections = 0; 907 int i; 908 909 sec = get_section_header( base, mapping_size, &num_sections ); 910 if (!sec) 911 return NULL; 912 913 for (i=num_sections-1; i>=0; i--) 914 { 915 if (sec[i].VirtualAddress <= rva && 916 rva <= (DWORD)sec[i].VirtualAddress + sec[i].SizeOfRawData) 917 { 918 return &sec[i]; 919 } 920 } 921 922 return NULL; 923 } 924 925 static void *address_from_rva( void *base, DWORD mapping_size, DWORD rva, DWORD len ) 926 { 927 const IMAGE_SECTION_HEADER *sec; 928 929 sec = section_from_rva( base, mapping_size, rva ); 930 if (!sec) 931 return NULL; 932 933 if (rva + len <= (DWORD)sec->VirtualAddress + sec->SizeOfRawData) 934 return (void*)((LPBYTE) base + (sec->PointerToRawData + rva - sec->VirtualAddress)); 935 936 return NULL; 937 } 938 939 static LPWSTR resource_dup_string( const IMAGE_RESOURCE_DIRECTORY *root, const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry ) 940 { 941 const IMAGE_RESOURCE_DIR_STRING_U* string; 942 LPWSTR s; 943 944 if (!entry->NameIsString) 945 return UIntToPtr(entry->Id); 946 947 string = (const IMAGE_RESOURCE_DIR_STRING_U*) (((const char *)root) + entry->NameOffset); 948 s = HeapAlloc(GetProcessHeap(), 0, (string->Length + 1)*sizeof (WCHAR) ); 949 memcpy( s, string->NameString, (string->Length + 1)*sizeof (WCHAR) ); 950 s[string->Length] = 0; 951 952 return s; 953 } 954 955 /* this function is based on the code in winedump's pe.c */ 956 static BOOL enumerate_mapped_resources( QUEUEDUPDATES *updates, 957 void *base, DWORD mapping_size, 958 const IMAGE_RESOURCE_DIRECTORY *root ) 959 { 960 const IMAGE_RESOURCE_DIRECTORY *namedir, *langdir; 961 const IMAGE_RESOURCE_DIRECTORY_ENTRY *e1, *e2, *e3; 962 const IMAGE_RESOURCE_DATA_ENTRY *data; 963 DWORD i, j, k; 964 965 TRACE("version (%d.%d) %d named %d id entries\n", 966 root->MajorVersion, root->MinorVersion, root->NumberOfNamedEntries, root->NumberOfIdEntries); 967 968 for (i = 0; i< root->NumberOfNamedEntries + root->NumberOfIdEntries; i++) 969 { 970 LPWSTR Type; 971 972 e1 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(root + 1) + i; 973 974 Type = resource_dup_string( root, e1 ); 975 976 namedir = (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + e1->OffsetToDirectory); 977 for (j = 0; j < namedir->NumberOfNamedEntries + namedir->NumberOfIdEntries; j++) 978 { 979 LPWSTR Name; 980 981 e2 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(namedir + 1) + j; 982 983 Name = resource_dup_string( root, e2 ); 984 985 langdir = (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + e2->OffsetToDirectory); 986 for (k = 0; k < langdir->NumberOfNamedEntries + langdir->NumberOfIdEntries; k++) 987 { 988 LANGID Lang; 989 void *p; 990 struct resource_data *resdata; 991 992 e3 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(langdir + 1) + k; 993 994 Lang = e3->Id; 995 996 data = (const IMAGE_RESOURCE_DATA_ENTRY *)((const char *)root + e3->OffsetToData); 997 998 p = address_from_rva( base, mapping_size, data->OffsetToData, data->Size ); 999 1000 resdata = allocate_resource_data( Lang, data->CodePage, p, data->Size, FALSE ); 1001 if (resdata) 1002 { 1003 if (!update_add_resource( updates, Type, Name, Lang, resdata, FALSE )) 1004 HeapFree( GetProcessHeap(), 0, resdata ); 1005 } 1006 } 1007 res_free_str( Name ); 1008 } 1009 res_free_str( Type ); 1010 } 1011 1012 return TRUE; 1013 } 1014 1015 static BOOL read_mapped_resources( QUEUEDUPDATES *updates, void *base, DWORD mapping_size ) 1016 { 1017 const IMAGE_RESOURCE_DIRECTORY *root; 1018 const IMAGE_NT_HEADERS *nt; 1019 const IMAGE_SECTION_HEADER *sec; 1020 DWORD num_sections = 0, i; 1021 1022 nt = get_nt_header( base, mapping_size ); 1023 if (!nt) 1024 return FALSE; 1025 1026 sec = get_section_header( base, mapping_size, &num_sections ); 1027 if (!sec) 1028 return FALSE; 1029 1030 for (i=0; i<num_sections; i++) 1031 if (!memcmp(sec[i].Name, ".rsrc", 6)) 1032 break; 1033 1034 if (i == num_sections) 1035 return TRUE; 1036 1037 /* check the resource data is inside the mapping */ 1038 if (sec[i].PointerToRawData > mapping_size || 1039 (sec[i].PointerToRawData + sec[i].SizeOfRawData) > mapping_size) 1040 return TRUE; 1041 1042 TRACE("found .rsrc at %08x, size %08x\n", sec[i].PointerToRawData, sec[i].SizeOfRawData); 1043 1044 if (!sec[i].PointerToRawData || sec[i].SizeOfRawData < sizeof(IMAGE_RESOURCE_DIRECTORY)) 1045 return TRUE; 1046 1047 root = (void*) ((BYTE*)base + sec[i].PointerToRawData); 1048 enumerate_mapped_resources( updates, base, mapping_size, root ); 1049 1050 return TRUE; 1051 } 1052 1053 static BOOL map_file_into_memory( struct mapping_info *mi ) 1054 { 1055 DWORD page_attr, perm; 1056 HANDLE mapping; 1057 1058 if (mi->read_write) 1059 { 1060 page_attr = PAGE_READWRITE; 1061 perm = FILE_MAP_WRITE | FILE_MAP_READ; 1062 } 1063 else 1064 { 1065 page_attr = PAGE_READONLY; 1066 perm = FILE_MAP_READ; 1067 } 1068 1069 mapping = CreateFileMappingW( mi->file, NULL, page_attr, 0, 0, NULL ); 1070 if (!mapping) return FALSE; 1071 1072 mi->base = MapViewOfFile( mapping, perm, 0, 0, mi->size ); 1073 CloseHandle( mapping ); 1074 1075 return mi->base != NULL; 1076 } 1077 1078 static BOOL unmap_file_from_memory( struct mapping_info *mi ) 1079 { 1080 if (mi->base) 1081 UnmapViewOfFile( mi->base ); 1082 mi->base = NULL; 1083 return TRUE; 1084 } 1085 1086 static void destroy_mapping( struct mapping_info *mi ) 1087 { 1088 if (!mi) 1089 return; 1090 unmap_file_from_memory( mi ); 1091 if (mi->file) 1092 CloseHandle( mi->file ); 1093 HeapFree( GetProcessHeap(), 0, mi ); 1094 } 1095 1096 static struct mapping_info *create_mapping( LPCWSTR name, BOOL rw ) 1097 { 1098 struct mapping_info *mi; 1099 1100 mi = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *mi ); 1101 if (!mi) 1102 return NULL; 1103 1104 mi->read_write = rw; 1105 1106 mi->file = CreateFileW( name, GENERIC_READ | (rw ? GENERIC_WRITE : 0), 1107 0, NULL, OPEN_EXISTING, 0, 0 ); 1108 1109 if (mi->file != INVALID_HANDLE_VALUE) 1110 { 1111 mi->size = GetFileSize( mi->file, NULL ); 1112 1113 if (map_file_into_memory( mi )) 1114 return mi; 1115 } 1116 destroy_mapping( mi ); 1117 return NULL; 1118 } 1119 1120 static BOOL resize_mapping( struct mapping_info *mi, DWORD new_size ) 1121 { 1122 if (!unmap_file_from_memory( mi )) 1123 return FALSE; 1124 1125 /* change the file size */ 1126 SetFilePointer( mi->file, new_size, NULL, FILE_BEGIN ); 1127 if (!SetEndOfFile( mi->file )) 1128 { 1129 ERR("failed to set file size to %08x\n", new_size ); 1130 return FALSE; 1131 } 1132 1133 mi->size = new_size; 1134 1135 return map_file_into_memory( mi ); 1136 } 1137 1138 static void get_resource_sizes( QUEUEDUPDATES *updates, struct resource_size_info *si ) 1139 { 1140 struct resource_dir_entry *types, *names; 1141 struct resource_data *data; 1142 DWORD num_types = 0, num_names = 0, num_langs = 0, strings_size = 0, data_size = 0; 1143 1144 memset( si, 0, sizeof *si ); 1145 1146 LIST_FOR_EACH_ENTRY( types, &updates->root, struct resource_dir_entry, entry ) 1147 { 1148 num_types++; 1149 if (!IS_INTRESOURCE( types->id )) 1150 strings_size += sizeof (WORD) + lstrlenW( types->id )*sizeof (WCHAR); 1151 1152 LIST_FOR_EACH_ENTRY( names, &types->children, struct resource_dir_entry, entry ) 1153 { 1154 num_names++; 1155 1156 if (!IS_INTRESOURCE( names->id )) 1157 strings_size += sizeof (WORD) + lstrlenW( names->id )*sizeof (WCHAR); 1158 1159 LIST_FOR_EACH_ENTRY( data, &names->children, struct resource_data, entry ) 1160 { 1161 num_langs++; 1162 data_size += (data->cbData + 3) & ~3; 1163 } 1164 } 1165 } 1166 1167 /* names are at the end of the types */ 1168 si->names_ofs = sizeof (IMAGE_RESOURCE_DIRECTORY) + 1169 num_types * sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY); 1170 1171 /* language directories are at the end of the names */ 1172 si->langs_ofs = si->names_ofs + 1173 num_types * sizeof (IMAGE_RESOURCE_DIRECTORY) + 1174 num_names * sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY); 1175 1176 si->data_entry_ofs = si->langs_ofs + 1177 num_names * sizeof (IMAGE_RESOURCE_DIRECTORY) + 1178 num_langs * sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY); 1179 1180 si->strings_ofs = si->data_entry_ofs + 1181 num_langs * sizeof (IMAGE_RESOURCE_DATA_ENTRY); 1182 1183 si->data_ofs = si->strings_ofs + ((strings_size + 3) & ~3); 1184 1185 si->total_size = si->data_ofs + data_size; 1186 1187 TRACE("names %08x langs %08x data entries %08x strings %08x data %08x total %08x\n", 1188 si->names_ofs, si->langs_ofs, si->data_entry_ofs, 1189 si->strings_ofs, si->data_ofs, si->total_size); 1190 } 1191 1192 static void res_write_padding( BYTE *res_base, DWORD size ) 1193 { 1194 static const BYTE pad[] = { 1195 'P','A','D','D','I','N','G','X','X','P','A','D','D','I','N','G' }; 1196 DWORD i; 1197 1198 for ( i = 0; i < size / sizeof pad; i++ ) 1199 memcpy( &res_base[i*sizeof pad], pad, sizeof pad ); 1200 memcpy( &res_base[i*sizeof pad], pad, size%sizeof pad ); 1201 } 1202 1203 static BOOL write_resources( QUEUEDUPDATES *updates, LPBYTE base, struct resource_size_info *si, DWORD rva ) 1204 { 1205 struct resource_dir_entry *types, *names; 1206 struct resource_data *data; 1207 IMAGE_RESOURCE_DIRECTORY *root; 1208 1209 TRACE("%p %p %p %08x\n", updates, base, si, rva ); 1210 1211 memset( base, 0, si->total_size ); 1212 1213 /* the root entry always exists */ 1214 root = (IMAGE_RESOURCE_DIRECTORY*) base; 1215 memset( root, 0, sizeof *root ); 1216 root->MajorVersion = 4; 1217 si->types_ofs = sizeof *root; 1218 LIST_FOR_EACH_ENTRY( types, &updates->root, struct resource_dir_entry, entry ) 1219 { 1220 IMAGE_RESOURCE_DIRECTORY_ENTRY *e1; 1221 IMAGE_RESOURCE_DIRECTORY *namedir; 1222 1223 e1 = (IMAGE_RESOURCE_DIRECTORY_ENTRY*) &base[si->types_ofs]; 1224 memset( e1, 0, sizeof *e1 ); 1225 if (!IS_INTRESOURCE( types->id )) 1226 { 1227 WCHAR *strings; 1228 DWORD len; 1229 1230 root->NumberOfNamedEntries++; 1231 e1->NameIsString = 1; 1232 e1->NameOffset = si->strings_ofs; 1233 1234 strings = (WCHAR*) &base[si->strings_ofs]; 1235 len = lstrlenW( types->id ); 1236 strings[0] = len; 1237 memcpy( &strings[1], types->id, len * sizeof (WCHAR) ); 1238 si->strings_ofs += (len + 1) * sizeof (WCHAR); 1239 } 1240 else 1241 { 1242 root->NumberOfIdEntries++; 1243 e1->Id = LOWORD( types->id ); 1244 } 1245 e1->OffsetToDirectory = si->names_ofs; 1246 e1->DataIsDirectory = TRUE; 1247 si->types_ofs += sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY); 1248 1249 namedir = (IMAGE_RESOURCE_DIRECTORY*) &base[si->names_ofs]; 1250 memset( namedir, 0, sizeof *namedir ); 1251 namedir->MajorVersion = 4; 1252 si->names_ofs += sizeof (IMAGE_RESOURCE_DIRECTORY); 1253 1254 LIST_FOR_EACH_ENTRY( names, &types->children, struct resource_dir_entry, entry ) 1255 { 1256 IMAGE_RESOURCE_DIRECTORY_ENTRY *e2; 1257 IMAGE_RESOURCE_DIRECTORY *langdir; 1258 1259 e2 = (IMAGE_RESOURCE_DIRECTORY_ENTRY*) &base[si->names_ofs]; 1260 memset( e2, 0, sizeof *e2 ); 1261 if (!IS_INTRESOURCE( names->id )) 1262 { 1263 WCHAR *strings; 1264 DWORD len; 1265 1266 namedir->NumberOfNamedEntries++; 1267 e2->NameIsString = 1; 1268 e2->NameOffset = si->strings_ofs; 1269 1270 strings = (WCHAR*) &base[si->strings_ofs]; 1271 len = lstrlenW( names->id ); 1272 strings[0] = len; 1273 memcpy( &strings[1], names->id, len * sizeof (WCHAR) ); 1274 si->strings_ofs += (len + 1) * sizeof (WCHAR); 1275 } 1276 else 1277 { 1278 namedir->NumberOfIdEntries++; 1279 e2->Id = LOWORD( names->id ); 1280 } 1281 e2->OffsetToDirectory = si->langs_ofs; 1282 e2->DataIsDirectory = TRUE; 1283 si->names_ofs += sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY); 1284 1285 langdir = (IMAGE_RESOURCE_DIRECTORY*) &base[si->langs_ofs]; 1286 memset( langdir, 0, sizeof *langdir ); 1287 langdir->MajorVersion = 4; 1288 si->langs_ofs += sizeof (IMAGE_RESOURCE_DIRECTORY); 1289 1290 LIST_FOR_EACH_ENTRY( data, &names->children, struct resource_data, entry ) 1291 { 1292 IMAGE_RESOURCE_DIRECTORY_ENTRY *e3; 1293 IMAGE_RESOURCE_DATA_ENTRY *de; 1294 int pad_size; 1295 1296 e3 = (IMAGE_RESOURCE_DIRECTORY_ENTRY*) &base[si->langs_ofs]; 1297 memset( e3, 0, sizeof *e3 ); 1298 langdir->NumberOfIdEntries++; 1299 e3->Id = LOWORD( data->lang ); 1300 e3->OffsetToData = si->data_entry_ofs; 1301 1302 si->langs_ofs += sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY); 1303 1304 /* write out all the data entries */ 1305 de = (IMAGE_RESOURCE_DATA_ENTRY*) &base[si->data_entry_ofs]; 1306 memset( de, 0, sizeof *de ); 1307 de->OffsetToData = si->data_ofs + rva; 1308 de->Size = data->cbData; 1309 de->CodePage = data->codepage; 1310 si->data_entry_ofs += sizeof (IMAGE_RESOURCE_DATA_ENTRY); 1311 1312 /* write out the resource data */ 1313 memcpy( &base[si->data_ofs], data->lpData, data->cbData ); 1314 si->data_ofs += data->cbData; 1315 1316 pad_size = (-si->data_ofs)&3; 1317 res_write_padding( &base[si->data_ofs], pad_size ); 1318 si->data_ofs += pad_size; 1319 } 1320 } 1321 } 1322 1323 return TRUE; 1324 } 1325 1326 /* 1327 * FIXME: 1328 * Assumes that the resources are in .rsrc 1329 * and .rsrc is the last section in the file. 1330 * Not sure whether updating resources will other cases on Windows. 1331 * If the resources lie in a section containing other data, 1332 * resizing that section could possibly cause trouble. 1333 * If the section with the resources isn't last, the remaining 1334 * sections need to be moved down in the file, and the section header 1335 * would need to be adjusted. 1336 * If we needed to add a section, what would we name it? 1337 * If we needed to add a section and there wasn't space in the file 1338 * header, how would that work? 1339 * Seems that at least some of these cases can't be handled properly. 1340 */ 1341 static IMAGE_SECTION_HEADER *get_resource_section( void *base, DWORD mapping_size ) 1342 { 1343 IMAGE_SECTION_HEADER *sec; 1344 IMAGE_NT_HEADERS *nt; 1345 DWORD i, num_sections = 0; 1346 1347 nt = get_nt_header( base, mapping_size ); 1348 if (!nt) 1349 return NULL; 1350 1351 sec = get_section_header( base, mapping_size, &num_sections ); 1352 if (!sec) 1353 return NULL; 1354 1355 /* find the resources section */ 1356 for (i=0; i<num_sections; i++) 1357 if (!memcmp(sec[i].Name, ".rsrc", 6)) 1358 break; 1359 1360 if (i == num_sections) 1361 return NULL; 1362 1363 return &sec[i]; 1364 } 1365 1366 static DWORD get_init_data_size( void *base, DWORD mapping_size ) 1367 { 1368 DWORD i, sz = 0, num_sections = 0; 1369 IMAGE_SECTION_HEADER *s; 1370 1371 s = get_section_header( base, mapping_size, &num_sections ); 1372 1373 for (i=0; i<num_sections; i++) 1374 if (s[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) 1375 sz += s[i].SizeOfRawData; 1376 1377 TRACE("size = %08x\n", sz); 1378 1379 return sz; 1380 } 1381 1382 static BOOL write_raw_resources( QUEUEDUPDATES *updates ) 1383 { 1384 static const WCHAR prefix[] = { 'r','e','s','u',0 }; 1385 WCHAR tempdir[MAX_PATH], tempfile[MAX_PATH]; 1386 DWORD i, section_size; 1387 BOOL ret = FALSE; 1388 IMAGE_SECTION_HEADER *sec; 1389 IMAGE_NT_HEADERS32 *nt; 1390 IMAGE_NT_HEADERS64 *nt64; 1391 struct resource_size_info res_size; 1392 BYTE *res_base; 1393 struct mapping_info *read_map = NULL, *write_map = NULL; 1394 DWORD PeSectionAlignment, PeFileAlignment, PeSizeOfImage; 1395 1396 /* copy the exe to a temp file then update the temp file... */ 1397 tempdir[0] = 0; 1398 if (!GetTempPathW( MAX_PATH, tempdir )) 1399 return ret; 1400 1401 if (!GetTempFileNameW( tempdir, prefix, 0, tempfile )) 1402 return ret; 1403 1404 if (!CopyFileW( updates->pFileName, tempfile, FALSE )) 1405 goto done; 1406 1407 TRACE("tempfile %s\n", debugstr_w(tempfile)); 1408 1409 if (!updates->bDeleteExistingResources) 1410 { 1411 read_map = create_mapping( updates->pFileName, FALSE ); 1412 if (!read_map) 1413 goto done; 1414 1415 ret = read_mapped_resources( updates, read_map->base, read_map->size ); 1416 if (!ret) 1417 { 1418 ERR("failed to read existing resources\n"); 1419 goto done; 1420 } 1421 } 1422 1423 write_map = create_mapping( tempfile, TRUE ); 1424 if (!write_map) 1425 goto done; 1426 1427 nt = (IMAGE_NT_HEADERS32*)get_nt_header( write_map->base, write_map->size ); 1428 if (!nt) 1429 goto done; 1430 1431 nt64 = (IMAGE_NT_HEADERS64*)nt; 1432 if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { 1433 PeSectionAlignment = nt64->OptionalHeader.SectionAlignment; 1434 PeFileAlignment = nt64->OptionalHeader.FileAlignment; 1435 PeSizeOfImage = nt64->OptionalHeader.SizeOfImage; 1436 } else { 1437 PeSectionAlignment = nt->OptionalHeader.SectionAlignment; 1438 PeFileAlignment = nt->OptionalHeader.FileAlignment; 1439 PeSizeOfImage = nt->OptionalHeader.SizeOfImage; 1440 } 1441 1442 if ((LONG)PeSectionAlignment <= 0) 1443 { 1444 ERR("invalid section alignment %08x\n", PeSectionAlignment); 1445 goto done; 1446 } 1447 1448 if ((LONG)PeFileAlignment <= 0) 1449 { 1450 ERR("invalid file alignment %08x\n", PeFileAlignment); 1451 goto done; 1452 } 1453 1454 sec = get_resource_section( write_map->base, write_map->size ); 1455 if (!sec) /* no section, add one */ 1456 { 1457 DWORD num_sections; 1458 1459 sec = get_section_header( write_map->base, write_map->size, &num_sections ); 1460 if (!sec) 1461 goto done; 1462 1463 sec += num_sections; 1464 nt->FileHeader.NumberOfSections++; 1465 1466 memset( sec, 0, sizeof *sec ); 1467 memcpy( sec->Name, ".rsrc", 5 ); 1468 sec->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; 1469 sec->VirtualAddress = PeSizeOfImage; 1470 } 1471 1472 if (!sec->PointerToRawData) /* empty section */ 1473 { 1474 sec->PointerToRawData = write_map->size + (-write_map->size) % PeFileAlignment; 1475 sec->SizeOfRawData = 0; 1476 } 1477 1478 TRACE("before .rsrc at %08x, size %08x\n", sec->PointerToRawData, sec->SizeOfRawData); 1479 1480 get_resource_sizes( updates, &res_size ); 1481 1482 /* round up the section size */ 1483 section_size = res_size.total_size; 1484 section_size += (-section_size) % PeFileAlignment; 1485 1486 TRACE("requires %08x (%08x) bytes\n", res_size.total_size, section_size ); 1487 1488 /* check if the file size needs to be changed */ 1489 if (section_size != sec->SizeOfRawData) 1490 { 1491 DWORD old_size = write_map->size; 1492 DWORD virtual_section_size = res_size.total_size + (-res_size.total_size) % PeSectionAlignment; 1493 int delta = section_size - (sec->SizeOfRawData + (-sec->SizeOfRawData) % PeFileAlignment); 1494 int rva_delta = virtual_section_size - 1495 (sec->Misc.VirtualSize + (-sec->Misc.VirtualSize) % PeSectionAlignment); 1496 /* when new section is added it could end past current mapping size */ 1497 BOOL rsrc_is_last = sec->PointerToRawData + sec->SizeOfRawData >= old_size; 1498 /* align .rsrc size when possible */ 1499 DWORD mapping_size = rsrc_is_last ? sec->PointerToRawData + section_size : old_size + delta; 1500 1501 /* postpone file truncation if there are some data to be moved down from file end */ 1502 BOOL resize_after = mapping_size < old_size && !rsrc_is_last; 1503 1504 TRACE("file size %08x -> %08x\n", old_size, mapping_size); 1505 1506 if (!resize_after) 1507 { 1508 /* unmap the file before changing the file size */ 1509 ret = resize_mapping( write_map, mapping_size ); 1510 1511 /* get the pointers again - they might be different after remapping */ 1512 nt = (IMAGE_NT_HEADERS32*)get_nt_header( write_map->base, mapping_size ); 1513 if (!nt) 1514 { 1515 ERR("couldn't get NT header\n"); 1516 goto done; 1517 } 1518 nt64 = (IMAGE_NT_HEADERS64*)nt; 1519 1520 sec = get_resource_section( write_map->base, mapping_size ); 1521 if (!sec) 1522 goto done; 1523 } 1524 1525 if (!rsrc_is_last) /* not last section, relocate trailing sections */ 1526 { 1527 IMAGE_SECTION_HEADER *s; 1528 DWORD tail_start = sec->PointerToRawData + sec->SizeOfRawData; 1529 DWORD i, num_sections = 0; 1530 1531 memmove( (char*)write_map->base + tail_start + delta, (char*)write_map->base + tail_start, old_size - tail_start ); 1532 1533 s = get_section_header( write_map->base, mapping_size, &num_sections ); 1534 1535 for (i=0; i<num_sections; i++) 1536 { 1537 if (s[i].PointerToRawData > sec->PointerToRawData) 1538 { 1539 s[i].PointerToRawData += delta; 1540 s[i].VirtualAddress += rva_delta; 1541 } 1542 } 1543 } 1544 1545 if (resize_after) 1546 { 1547 ret = resize_mapping( write_map, mapping_size ); 1548 1549 nt = (IMAGE_NT_HEADERS32*)get_nt_header( write_map->base, mapping_size ); 1550 if (!nt) 1551 { 1552 ERR("couldn't get NT header\n"); 1553 goto done; 1554 } 1555 nt64 = (IMAGE_NT_HEADERS64*)nt; 1556 1557 sec = get_resource_section( write_map->base, mapping_size ); 1558 if (!sec) 1559 goto done; 1560 } 1561 1562 /* adjust the PE header information */ 1563 sec->SizeOfRawData = section_size; 1564 sec->Misc.VirtualSize = virtual_section_size; 1565 if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { 1566 nt64->OptionalHeader.SizeOfImage += rva_delta; 1567 nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = sec->VirtualAddress; 1568 nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = res_size.total_size; 1569 nt64->OptionalHeader.SizeOfInitializedData = get_init_data_size( write_map->base, mapping_size ); 1570 1571 for (i=0; i<nt64->OptionalHeader.NumberOfRvaAndSizes; i++) 1572 if (nt64->OptionalHeader.DataDirectory[i].VirtualAddress > sec->VirtualAddress) 1573 nt64->OptionalHeader.DataDirectory[i].VirtualAddress += rva_delta; 1574 } else { 1575 nt->OptionalHeader.SizeOfImage += rva_delta; 1576 nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = sec->VirtualAddress; 1577 nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = res_size.total_size; 1578 nt->OptionalHeader.SizeOfInitializedData = get_init_data_size( write_map->base, mapping_size ); 1579 1580 for (i=0; i<nt->OptionalHeader.NumberOfRvaAndSizes; i++) 1581 if (nt->OptionalHeader.DataDirectory[i].VirtualAddress > sec->VirtualAddress) 1582 nt->OptionalHeader.DataDirectory[i].VirtualAddress += rva_delta; 1583 } 1584 } 1585 1586 res_base = (LPBYTE) write_map->base + sec->PointerToRawData; 1587 1588 TRACE("base = %p offset = %08x\n", write_map->base, sec->PointerToRawData); 1589 1590 ret = write_resources( updates, res_base, &res_size, sec->VirtualAddress ); 1591 1592 res_write_padding( res_base + res_size.total_size, section_size - res_size.total_size ); 1593 1594 TRACE("after .rsrc at %08x, size %08x\n", sec->PointerToRawData, sec->SizeOfRawData); 1595 1596 done: 1597 destroy_mapping( read_map ); 1598 destroy_mapping( write_map ); 1599 1600 if (ret) 1601 ret = CopyFileW( tempfile, updates->pFileName, FALSE ); 1602 1603 DeleteFileW( tempfile ); 1604 1605 return ret; 1606 } 1607 1608 /*********************************************************************** 1609 * BeginUpdateResourceW (KERNEL32.@) 1610 */ 1611 HANDLE WINAPI BeginUpdateResourceW( LPCWSTR pFileName, BOOL bDeleteExistingResources ) 1612 { 1613 QUEUEDUPDATES *updates = NULL; 1614 HANDLE hUpdate, file, ret = NULL; 1615 1616 TRACE("%s, %d\n", debugstr_w(pFileName), bDeleteExistingResources); 1617 1618 hUpdate = GlobalAlloc(GHND, sizeof(QUEUEDUPDATES)); 1619 if (!hUpdate) 1620 return ret; 1621 1622 updates = GlobalLock(hUpdate); 1623 if (updates) 1624 { 1625 list_init( &updates->root ); 1626 updates->bDeleteExistingResources = bDeleteExistingResources; 1627 updates->pFileName = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(pFileName)+1)*sizeof(WCHAR)); 1628 if (updates->pFileName) 1629 { 1630 lstrcpyW(updates->pFileName, pFileName); 1631 1632 file = CreateFileW( pFileName, GENERIC_READ | GENERIC_WRITE, 1633 0, NULL, OPEN_EXISTING, 0, 0 ); 1634 1635 /* if resources are deleted, only the file's presence is checked */ 1636 if (file != INVALID_HANDLE_VALUE && 1637 (bDeleteExistingResources || check_pe_exe( file, updates ))) 1638 ret = hUpdate; 1639 else 1640 HeapFree( GetProcessHeap(), 0, updates->pFileName ); 1641 1642 CloseHandle( file ); 1643 } 1644 GlobalUnlock(hUpdate); 1645 } 1646 1647 if (!ret) 1648 GlobalFree(hUpdate); 1649 1650 return ret; 1651 } 1652 1653 1654 /*********************************************************************** 1655 * BeginUpdateResourceA (KERNEL32.@) 1656 */ 1657 HANDLE WINAPI BeginUpdateResourceA( LPCSTR pFileName, BOOL bDeleteExistingResources ) 1658 { 1659 UNICODE_STRING FileNameW; 1660 HANDLE ret; 1661 RtlCreateUnicodeStringFromAsciiz(&FileNameW, pFileName); 1662 ret = BeginUpdateResourceW(FileNameW.Buffer, bDeleteExistingResources); 1663 RtlFreeUnicodeString(&FileNameW); 1664 return ret; 1665 } 1666 1667 1668 /*********************************************************************** 1669 * EndUpdateResourceW (KERNEL32.@) 1670 */ 1671 BOOL WINAPI EndUpdateResourceW( HANDLE hUpdate, BOOL fDiscard ) 1672 { 1673 QUEUEDUPDATES *updates; 1674 BOOL ret; 1675 1676 TRACE("%p %d\n", hUpdate, fDiscard); 1677 1678 updates = GlobalLock(hUpdate); 1679 if (!updates) 1680 return FALSE; 1681 1682 ret = fDiscard || write_raw_resources( updates ); 1683 1684 free_resource_directory( &updates->root, 2 ); 1685 1686 HeapFree( GetProcessHeap(), 0, updates->pFileName ); 1687 GlobalUnlock( hUpdate ); 1688 GlobalFree( hUpdate ); 1689 1690 return ret; 1691 } 1692 1693 1694 /*********************************************************************** 1695 * EndUpdateResourceA (KERNEL32.@) 1696 */ 1697 BOOL WINAPI EndUpdateResourceA( HANDLE hUpdate, BOOL fDiscard ) 1698 { 1699 return EndUpdateResourceW(hUpdate, fDiscard); 1700 } 1701 1702 1703 /*********************************************************************** 1704 * UpdateResourceW (KERNEL32.@) 1705 */ 1706 BOOL WINAPI UpdateResourceW( HANDLE hUpdate, LPCWSTR lpType, LPCWSTR lpName, 1707 WORD wLanguage, LPVOID lpData, DWORD cbData) 1708 { 1709 QUEUEDUPDATES *updates; 1710 BOOL ret = FALSE; 1711 1712 TRACE("%p %s %s %08x %p %d\n", hUpdate, 1713 debugstr_w(lpType), debugstr_w(lpName), wLanguage, lpData, cbData); 1714 1715 updates = GlobalLock(hUpdate); 1716 if (updates) 1717 { 1718 if (lpData == NULL && cbData == 0) /* remove resource */ 1719 { 1720 ret = update_add_resource( updates, lpType, lpName, wLanguage, NULL, TRUE ); 1721 } 1722 else 1723 { 1724 struct resource_data *data; 1725 data = allocate_resource_data( wLanguage, 0, lpData, cbData, TRUE ); 1726 if (data) 1727 ret = update_add_resource( updates, lpType, lpName, wLanguage, data, TRUE ); 1728 } 1729 GlobalUnlock(hUpdate); 1730 } 1731 return ret; 1732 } 1733 1734 1735 /*********************************************************************** 1736 * UpdateResourceA (KERNEL32.@) 1737 */ 1738 BOOL WINAPI UpdateResourceA( HANDLE hUpdate, LPCSTR lpType, LPCSTR lpName, 1739 WORD wLanguage, LPVOID lpData, DWORD cbData) 1740 { 1741 BOOL ret; 1742 UNICODE_STRING TypeW; 1743 UNICODE_STRING NameW; 1744 if(IS_INTRESOURCE(lpType)) 1745 TypeW.Buffer = ULongToPtr(LOWORD(lpType)); 1746 else 1747 RtlCreateUnicodeStringFromAsciiz(&TypeW, lpType); 1748 if(IS_INTRESOURCE(lpName)) 1749 NameW.Buffer = ULongToPtr(LOWORD(lpName)); 1750 else 1751 RtlCreateUnicodeStringFromAsciiz(&NameW, lpName); 1752 ret = UpdateResourceW(hUpdate, TypeW.Buffer, NameW.Buffer, wLanguage, lpData, cbData); 1753 if(!IS_INTRESOURCE(lpType)) RtlFreeUnicodeString(&TypeW); 1754 if(!IS_INTRESOURCE(lpName)) RtlFreeUnicodeString(&NameW); 1755 return ret; 1756 } 1757