pe_resource.c 16.4 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 * PE (Portable Execute) File Resources
Alexandre Julliard's avatar
Alexandre Julliard committed
3
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4 5
 * Copyright 1995 Thomas Sandford
 * Copyright 1996 Martin von Loewis
Alexandre Julliard's avatar
Alexandre Julliard committed
6
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
7 8 9
 * Based on the Win16 resource handling code in loader/resource.c
 * Copyright 1993 Robert J. Amstadt
 * Copyright 1995 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
10
 * Copyright 1997 Marcus Meissner
Alexandre Julliard's avatar
Alexandre Julliard committed
11 12
 */

13 14
#include "config.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
15
#include <stdlib.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
16
#include <sys/types.h>
17

18
#include "wine/unicode.h"
19
#include "windef.h"
20
#include "winnls.h"
21
#include "winerror.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
22 23
#include "module.h"
#include "stackframe.h"
24
#include "debugtools.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
25

26 27
DEFAULT_DEBUG_CHANNEL(resource);

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

/**********************************************************************
 *  get_module_base
 *
 * Get the base address of a module
 */
static const void *get_module_base( HMODULE hmod )
{
    if (!hmod) hmod = GetModuleHandleA( NULL );
    else if (!HIWORD(hmod))
    {
        FIXME("Enumeration of 16-bit resources is not supported\n");
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }

    /* clear low order bit in case of LOAD_LIBRARY_AS_DATAFILE module */
    return (void *)((ULONG_PTR)hmod & ~1);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
49
/**********************************************************************
50
 *  get_resdir
Alexandre Julliard's avatar
Alexandre Julliard committed
51
 *
52
 * Get the resource directory of a PE module
Alexandre Julliard's avatar
Alexandre Julliard committed
53
 */
54
static const IMAGE_RESOURCE_DIRECTORY* get_resdir( HMODULE hmod )
55
{
56 57
    const IMAGE_DATA_DIRECTORY *dir;
    const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
58
    const void *base = get_module_base( hmod );
59

60
    if (base)
61
    {
62 63 64
        dir = &PE_HEADER(base)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
        if (dir->Size && dir->VirtualAddress)
            ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)base + dir->VirtualAddress);
65
    }
66
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
67
}
Alexandre Julliard's avatar
Alexandre Julliard committed
68

69

70 71 72 73 74
/**********************************************************************
 *  find_entry_by_id
 *
 * Find an entry by id in a resource directory
 */
75 76
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                         WORD id, const void *root )
77 78 79 80 81 82 83 84 85 86
{
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
    int min, max, pos;

    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
    min = dir->NumberOfNamedEntries;
    max = min + dir->NumberOfIdEntries - 1;
    while (min <= max)
    {
        pos = (min + max) / 2;
87
        if (entry[pos].u1.s2.Id == id)
88
            return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
89
        if (entry[pos].u1.s2.Id > id) max = pos - 1;
90 91 92 93 94 95 96
        else min = pos + 1;
    }
    return NULL;
}


/**********************************************************************
97
 *  find_entry_by_nameW
98
 *
99
 * Find an entry by name in a resource directory
100
 */
101 102
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                            LPCWSTR name, const void *root )
103 104
{
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
105 106 107 108 109 110 111 112 113 114 115
    const IMAGE_RESOURCE_DIR_STRING_U *str;
    int min, max, res, pos, namelen;

    if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
    if (name[0] == '#')
    {
        char buf[16];
        if (!WideCharToMultiByte( CP_ACP, 0, name+1, -1, buf, sizeof(buf), NULL, NULL ))
            return NULL;
        return find_entry_by_id( dir, atoi(buf), root );
    }
116 117

    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
118 119 120 121 122 123
    namelen = strlenW(name);
    min = 0;
    max = dir->NumberOfNamedEntries - 1;
    while (min <= max)
    {
        pos = (min + max) / 2;
124
        str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
125 126
        res = strncmpiW( name, str->NameString, str->Length );
        if (!res && namelen == str->Length)
127
            return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
128 129 130
        if (res < 0) max = pos - 1;
        else min = pos + 1;
    }
131 132 133 134
    return NULL;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
135
/**********************************************************************
136
 *  find_entry_by_nameA
Alexandre Julliard's avatar
Alexandre Julliard committed
137
 *
138
 * Find an entry by name in a resource directory
Alexandre Julliard's avatar
Alexandre Julliard committed
139
 */
140 141
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                            LPCSTR name, const void *root )
Alexandre Julliard's avatar
Alexandre Julliard committed
142
{
143 144
    const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
    LPWSTR nameW;
145
    INT len;
146 147 148

    if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
    if (name[0] == '#')
149
    {
150
        return find_entry_by_id( dir, atoi(name+1), root );
151
    }
152

153 154
    len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
    if ((nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
155
    {
156
        MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len );
157 158
        ret = find_entry_by_nameW( dir, nameW, root );
        HeapFree( GetProcessHeap(), 0, nameW );
Alexandre Julliard's avatar
Alexandre Julliard committed
159
    }
160
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
161 162
}

163

164
/**********************************************************************
165 166 167
 *  find_entry_default
 *
 * Find a default entry in a resource directory
168
 */
169 170
static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                           const void *root )
171
{
172
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
173

174
    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
175
    return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
176 177
}

178

Alexandre Julliard's avatar
Alexandre Julliard committed
179
/**********************************************************************
180
 *	    PE_FindResourceExW
181 182 183 184 185 186
 *
 * FindResourceExA/W does search in the following order:
 * 1. Exact specified language
 * 2. Language with neutral sublanguage
 * 3. Neutral language with neutral sublanguage
 * 4. Neutral language with default sublanguage
Alexandre Julliard's avatar
Alexandre Julliard committed
187
 */
188
HRSRC PE_FindResourceExW( HMODULE hmod, LPCWSTR name, LPCWSTR type, WORD lang )
189
{
190 191
    const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
    const void *root;
192
    HRSRC result;
Alexandre Julliard's avatar
Alexandre Julliard committed
193

194
    if (!resdirptr) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
195

196
    root = resdirptr;
197 198
    if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
    if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
199 200

    /* 1. Exact specified language */
201
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
202

203 204 205
    /* 2. Language with neutral sublanguage */
    lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
206

207 208 209
    /* 3. Neutral language with neutral sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
210

211 212 213
    /* 4. Neutral language with default sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
    result = (HRSRC)find_entry_by_id( resdirptr, lang, root );
214

215
 found:
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    return result;
}

/**********************************************************************
 *	    PE_FindResourceW
 *
 * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
 * FindResourceA/W does search in the following order:
 * 1. Neutral language with neutral sublanguage
 * 2. Neutral language with default sublanguage
 * 3. Current locale lang id
 * 4. Current locale lang id with neutral sublanguage
 * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
 * 6. Return first in the list
 */
HRSRC PE_FindResourceW( HMODULE hmod, LPCWSTR name, LPCWSTR type )
{
233 234
    const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
    const void *root;
235 236 237 238 239
    HRSRC result;
    WORD lang;

    if (!resdirptr) return 0;

240
    root = resdirptr;
241 242
    if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
    if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
243 244 245

    /* 1. Neutral language with neutral sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
246
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
247

248 249 250
    /* 2. Neutral language with default sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
251

252 253 254
    /* 3. Current locale lang id */
    lang = LANGIDFROMLCID(GetUserDefaultLCID());
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
255

256 257 258
    /* 4. Current locale lang id with neutral sublanguage */
    lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
259

260 261 262
    /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
    lang = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
263

264 265
    /* 6. Return first in the list */
    result = (HRSRC)find_entry_default( resdirptr, root );
266

267
 found:
Alexandre Julliard's avatar
Alexandre Julliard committed
268
    return result;
Alexandre Julliard's avatar
Alexandre Julliard committed
269 270
}

Alexandre Julliard's avatar
Alexandre Julliard committed
271

Alexandre Julliard's avatar
Alexandre Julliard committed
272
/**********************************************************************
273
 *	    PE_LoadResource
Alexandre Julliard's avatar
Alexandre Julliard committed
274
 */
275
HGLOBAL PE_LoadResource( HMODULE hmod, HRSRC hRsrc )
Alexandre Julliard's avatar
Alexandre Julliard committed
276
{
277
    const void *base = get_module_base( hmod );
278
    if (!hRsrc) return 0;
279
    return (HANDLE)((char *)base + ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->OffsetToData);
Alexandre Julliard's avatar
Alexandre Julliard committed
280 281
}

Alexandre Julliard's avatar
Alexandre Julliard committed
282

Alexandre Julliard's avatar
Alexandre Julliard committed
283
/**********************************************************************
284
 *	    PE_SizeofResource
Alexandre Julliard's avatar
Alexandre Julliard committed
285
 */
286
DWORD PE_SizeofResource( HRSRC hRsrc )
Alexandre Julliard's avatar
Alexandre Julliard committed
287
{
288
    if (!hRsrc) return 0;
289
    return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
Alexandre Julliard's avatar
Alexandre Julliard committed
290 291
}

292

Alexandre Julliard's avatar
Alexandre Julliard committed
293
/**********************************************************************
294
 *	EnumResourceTypesA	(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
295
 */
296 297
BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG lparam)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
298
    int		i;
299 300
    const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
301
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
302

303
    if (!resdir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
304

305
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
306 307
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
308 309
        LPSTR type;

310
        if (et[i].u1.s1.NameIsString)
311
        {
312
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
313 314 315 316 317 318 319 320 321 322 323 324
            DWORD len = WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
                                             NULL, 0, NULL, NULL);
            if (!(type = HeapAlloc(GetProcessHeap(), 0, len + 1)))
                return FALSE;
            WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
                                 type, len, NULL, NULL);
            type[len] = '\0';
            ret = lpfun(hmod,type,lparam);
            HeapFree(GetProcessHeap(), 0, type);
        }
        else
        {
325
            type = (LPSTR)(int)et[i].u1.s2.Id;
326 327
            ret = lpfun(hmod,type,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
328 329 330 331 332 333
	if (!ret)
		break;
    }
    return ret;
}

334

Alexandre Julliard's avatar
Alexandre Julliard committed
335
/**********************************************************************
336
 *	EnumResourceTypesW	(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
337
 */
338 339
BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG lparam)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
340
    int		i;
341 342
    const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
343
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
344

345
    if (!resdir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
346

347
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
348 349 350 351
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
	LPWSTR	type;

352
        if (et[i].u1.s1.NameIsString)
353
        {
354
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
355 356 357 358 359 360 361 362 363
            if (!(type = HeapAlloc(GetProcessHeap(), 0, (pResString->Length+1) * sizeof (WCHAR))))
                return FALSE;
            memcpy(type, pResString->NameString, pResString->Length * sizeof (WCHAR));
            type[pResString->Length] = '\0';
            ret = lpfun(hmod,type,lparam);
            HeapFree(GetProcessHeap(), 0, type);
        }
        else
        {
364
            type = (LPWSTR)(int)et[i].u1.s2.Id;
365 366
            ret = lpfun(hmod,type,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
367 368 369 370 371 372
	if (!ret)
		break;
    }
    return ret;
}

373

Alexandre Julliard's avatar
Alexandre Julliard committed
374
/**********************************************************************
375
 *	EnumResourceNamesA	(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
376
 */
377 378
BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
379
    int		i;
380 381 382
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
383
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
384

385 386
    if (!basedir) return FALSE;

387
    if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
388

389
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
390 391
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
392 393
        LPSTR name;

394
        if (et[i].u1.s1.NameIsString)
395
        {
396
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
397 398 399 400 401 402 403 404 405 406 407 408
            DWORD len = WideCharToMultiByte(CP_ACP, 0, pResString->NameString, pResString->Length,
                                            NULL, 0, NULL, NULL);
            if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 )))
                return FALSE;
            WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
                                 name, len, NULL, NULL );
            name[len] = '\0';
            ret = lpfun(hmod,type,name,lparam);
            HeapFree( GetProcessHeap(), 0, name );
        }
        else
        {
409
            name = (LPSTR)(int)et[i].u1.s2.Id;
410 411
            ret = lpfun(hmod,type,name,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
412 413 414 415 416 417
	if (!ret)
		break;
    }
    return ret;
}

418

Alexandre Julliard's avatar
Alexandre Julliard committed
419
/**********************************************************************
420
 *	EnumResourceNamesW	(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
421
 */
422 423
BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
424
    int		i;
425 426 427
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
428
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
429

430
    if (!basedir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
431

432 433
    if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;

434
    et = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
435 436
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
437 438
        LPWSTR name;

439
        if (et[i].u1.s1.NameIsString)
440
        {
441
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
442 443 444 445 446 447 448 449 450
            if (!(name = HeapAlloc(GetProcessHeap(), 0, (pResString->Length + 1) * sizeof (WCHAR))))
                return FALSE;
            memcpy(name, pResString->NameString, pResString->Length * sizeof (WCHAR));
            name[pResString->Length] = '\0';
            ret = lpfun(hmod,type,name,lparam);
            HeapFree(GetProcessHeap(), 0, name);
        }
        else
        {
451
            name = (LPWSTR)(int)et[i].u1.s2.Id;
452 453
            ret = lpfun(hmod,type,name,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
454 455 456 457 458 459
	if (!ret)
		break;
    }
    return ret;
}

460

Alexandre Julliard's avatar
Alexandre Julliard committed
461
/**********************************************************************
462
 *	EnumResourceLanguagesA	(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
463
 */
464 465 466
BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name,
                                    ENUMRESLANGPROCA lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
467
    int		i;
468 469 470
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
471
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
472

473
    if (!basedir) return FALSE;
474 475
    if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
    if (!(resdir = find_entry_by_nameA( resdir, name, basedir ))) return FALSE;
476 477

    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
478 479
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
480
        /* languages are just ids... I hope */
481
	ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
Alexandre Julliard's avatar
Alexandre Julliard committed
482 483 484 485 486 487
	if (!ret)
		break;
    }
    return ret;
}

488

Alexandre Julliard's avatar
Alexandre Julliard committed
489
/**********************************************************************
490
 *	EnumResourceLanguagesW	(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
491
 */
492 493 494
BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name,
                                    ENUMRESLANGPROCW lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
495
    int		i;
496 497 498
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
499
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
500

501
    if (!basedir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
502

503 504 505
    if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
    if (!(resdir = find_entry_by_nameW( resdir, name, basedir ))) return FALSE;

506
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
507 508
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
509
	ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
Alexandre Julliard's avatar
Alexandre Julliard committed
510 511 512 513
	if (!ret)
		break;
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
514
}