cryptui: Add a stub implementation of CryptUIDlgViewCertificateW.
[wine] / dlls / fusion / assembly.c
1 /*
2  * assembly parser
3  *
4  * Copyright 2008 James Hawkins
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 <stdarg.h>
22 #include <stdio.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winver.h"
28 #include "wincrypt.h"
29 #include "dbghelp.h"
30 #include "ole2.h"
31 #include "fusion.h"
32 #include "corhdr.h"
33
34 #include "fusionpriv.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37
38 #define TableFromToken(tk) (TypeFromToken(tk) >> 24)
39 #define TokenFromTable(idx) (idx << 24)
40
41 #define MAX_CLR_TABLES  64
42
43 #define MD_STRINGS_BIT 0x1
44 #define MD_GUIDS_BIT   0x2
45 #define MD_BLOBS_BIT   0x4
46
47 typedef struct tagCLRTABLE
48 {
49     INT rows;
50     DWORD offset;
51 } CLRTABLE;
52
53 struct tagASSEMBLY
54 {
55     LPSTR path;
56
57     HANDLE hfile;
58     HANDLE hmap;
59     BYTE *data;
60
61     IMAGE_NT_HEADERS *nthdr;
62     IMAGE_COR20_HEADER *corhdr;
63
64     METADATAHDR *metadatahdr;
65
66     METADATATABLESHDR *tableshdr;
67     DWORD numtables;
68     DWORD *numrows;
69     CLRTABLE tables[MAX_CLR_TABLES];
70
71     DWORD stringsz;
72     DWORD guidsz;
73     DWORD blobsz;
74
75     BYTE *strings;
76     BYTE *blobs;
77 };
78
79 static LPSTR strdupWtoA(LPCWSTR str)
80 {
81     LPSTR ret = NULL;
82     DWORD len;
83
84     if (!str)
85         return ret;
86
87     len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
88     ret = HeapAlloc(GetProcessHeap(), 0, len);
89     if (ret)
90         WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len, NULL, NULL);
91
92     return ret;
93 }
94
95 static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
96 {
97     DWORD offset = rva, limit;
98     IMAGE_SECTION_HEADER *img;
99     WORD i;
100
101     img = IMAGE_FIRST_SECTION(nthdrs);
102
103     if (rva < img->PointerToRawData)
104         return rva;
105
106     for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
107     {
108         if (img[i].SizeOfRawData)
109             limit = img[i].SizeOfRawData;
110         else
111             limit = img[i].Misc.VirtualSize;
112
113         if (rva >= img[i].VirtualAddress &&
114             rva < (img[i].VirtualAddress + limit))
115         {
116             if (img[i].PointerToRawData != 0)
117             {
118                 offset -= img[i].VirtualAddress;
119                 offset += img[i].PointerToRawData;
120             }
121
122             return offset;
123         }
124     }
125
126     return 0;
127 }
128
129 static BYTE *GetData(BYTE *pData, ULONG *pLength)
130 {
131     if ((*pData & 0x80) == 0x00)
132     {
133         *pLength = (*pData & 0x7f);
134         return pData + 1;
135     }
136
137     if ((*pData & 0xC0) == 0x80)
138     {
139         *pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
140         return pData + 2;
141     }
142
143     if ((*pData & 0xE0) == 0xC0)
144     {
145         *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
146                     *(pData + 2) << 8 | *(pData + 3));
147         return pData + 4;
148     }
149
150     *pLength = (ULONG)-1;
151     return 0;
152 }
153
154 static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
155 {
156     return (VOID *)&assembly->data[offset];
157 }
158
159 #define MAX_TABLES_WORD 0xFFFF
160 #define MAX_TABLES_1BIT_ENCODE 32767
161 #define MAX_TABLES_2BIT_ENCODE 16383
162 #define MAX_TABLES_3BIT_ENCODE 8191
163 #define MAX_TABLES_5BIT_ENCODE 2047
164
165 static inline ULONG get_table_size(ASSEMBLY *assembly, DWORD index)
166 {
167     DWORD size;
168     INT tables;
169
170     switch (TokenFromTable(index))
171     {
172         case mdtModule:
173         {
174             size = sizeof(MODULETABLE) + (assembly->stringsz - sizeof(WORD)) +
175                    2 * (assembly->guidsz - sizeof(WORD));
176             break;
177         }
178         case mdtTypeRef:
179         {
180             size = sizeof(TYPEREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD));
181
182             /* ResolutionScope:ResolutionScope */
183             tables = max(assembly->tables[TableFromToken(mdtModule)].rows,
184                          assembly->tables[TableFromToken(mdtModuleRef)].rows);
185             tables = max(tables, assembly->tables[TableFromToken(mdtAssemblyRef)].rows);
186             tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows);
187             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
188             break;
189         }
190         case mdtTypeDef:
191         {
192             size = sizeof(TYPEDEFTABLE) + 2 * (assembly->stringsz - sizeof(WORD));
193
194             /* Extends:TypeDefOrRef */
195             tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
196                          assembly->tables[TableFromToken(mdtTypeRef)].rows);
197             tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
198             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
199
200             size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
201                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
202             size += (assembly->tables[TableFromToken(mdtMethodDef)].rows >
203                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
204             break;
205         }
206         case mdtFieldDef:
207         {
208             size = sizeof(FIELDTABLE) + (assembly->stringsz - sizeof(WORD)) +
209                    (assembly->blobsz - sizeof(WORD));
210             break;
211         }
212         case mdtMethodDef:
213         {
214             size = sizeof(METHODDEFTABLE) + (assembly->stringsz - sizeof(WORD)) +
215                    (assembly->blobsz - sizeof(WORD));
216
217             size += (assembly->tables[TableFromToken(mdtParamDef)].rows >
218                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
219             break;
220         }
221         case mdtParamDef:
222         {
223             size = sizeof(PARAMTABLE) + (assembly->stringsz - sizeof(WORD));
224             break;
225         }
226         case mdtInterfaceImpl:
227         {
228             size = sizeof(INTERFACEIMPLTABLE);
229
230             /* Interface:TypeDefOrRef */
231             tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
232                          assembly->tables[TableFromToken(mdtTypeRef)].rows);
233             tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
234             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
235             break;
236         }
237         case mdtMemberRef:
238         {
239             size = sizeof(MEMBERREFTABLE) + (assembly->stringsz - sizeof(WORD)) +
240                    (assembly->blobsz - sizeof(WORD));
241
242             /* Class:MemberRefParent */
243             tables = max(assembly->tables[TableFromToken(mdtTypeRef)].rows,
244                          assembly->tables[TableFromToken(mdtModuleRef)].rows);
245             tables = max(tables, assembly->tables[TableFromToken(mdtMethodDef)].rows);
246             tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
247             tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows);
248             size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0;
249             break;
250         }
251         case 0x0B000000: /* FIXME */
252         {
253             size = sizeof(CONSTANTTABLE) + (assembly->blobsz - sizeof(WORD));
254
255             /* Parent:HasConstant */
256             tables = max(assembly->tables[TableFromToken(mdtParamDef)].rows,
257                          assembly->tables[TableFromToken(mdtFieldDef)].rows);
258             tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows);
259             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
260             break;
261         }
262         case mdtCustomAttribute:
263         {
264             size = sizeof(CUSTOMATTRIBUTETABLE) + (assembly->blobsz - sizeof(WORD));
265
266             /* Parent:HasCustomAttribute */
267             tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
268                          assembly->tables[TableFromToken(mdtFieldDef)].rows);
269             tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows);
270             tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows);
271             tables = max(tables, assembly->tables[TableFromToken(mdtParamDef)].rows);
272             tables = max(tables, assembly->tables[TableFromToken(mdtInterfaceImpl)].rows);
273             tables = max(tables, assembly->tables[TableFromToken(mdtMemberRef)].rows);
274             tables = max(tables, assembly->tables[TableFromToken(mdtPermission)].rows);
275             tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows);
276             tables = max(tables, assembly->tables[TableFromToken(mdtEvent)].rows);
277             tables = max(tables, assembly->tables[TableFromToken(mdtSignature)].rows);
278             tables = max(tables, assembly->tables[TableFromToken(mdtModuleRef)].rows);
279             tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
280             tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows);
281             tables = max(tables, assembly->tables[TableFromToken(mdtFile)].rows);
282             tables = max(tables, assembly->tables[TableFromToken(mdtExportedType)].rows);
283             tables = max(tables, assembly->tables[TableFromToken(mdtManifestResource)].rows);
284             size += (tables > MAX_TABLES_5BIT_ENCODE) ? sizeof(WORD) : 0;
285
286             /* Type:CustomAttributeType */
287             tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
288                          assembly->tables[TableFromToken(mdtMemberRef)].rows);
289             size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0;
290             break;
291         }
292         case 0x0D000000: /* FIXME */
293         {
294             size = sizeof(FIELDMARSHALTABLE) + (assembly->blobsz - sizeof(WORD));
295
296             /* Parent:HasFieldMarshal */
297             tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows,
298                          assembly->tables[TableFromToken(mdtParamDef)].rows);
299             size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
300             break;
301         }
302         case mdtPermission:
303         {
304             size = sizeof(DECLSECURITYTABLE) + (assembly->blobsz - sizeof(WORD));
305
306             /* Parent:HasDeclSecurity */
307             tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
308                          assembly->tables[TableFromToken(mdtMethodDef)].rows);
309             tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows);
310             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
311             break;
312         }
313         case 0x0F000000: /* FIXME */
314         {
315             size = sizeof(CLASSLAYOUTTABLE);
316             size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
317                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
318             break;
319         }
320         case 0x10000000: /* FIXME */
321         {
322             size = sizeof(FIELDLAYOUTTABLE);
323             size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
324                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
325             break;
326         }
327         case mdtSignature:
328         {
329             size = sizeof(STANDALONESIGTABLE) + (assembly->blobsz - sizeof(WORD));
330             break;
331         }
332         case 0x12000000: /* FIXME */
333         {
334             size = sizeof(EVENTMAPTABLE);
335             size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
336                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
337             size += (assembly->tables[TableFromToken(mdtEvent)].rows >
338                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
339             break;
340         }
341         case mdtEvent:
342         {
343             size = sizeof(EVENTTABLE) + (assembly->stringsz - sizeof(WORD));
344
345             /* EventType:TypeDefOrRef */
346             tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
347                          assembly->tables[TableFromToken(mdtTypeRef)].rows);
348             tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
349             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
350             break;
351         }
352         case 0x15000000:/* FIXME */
353         {
354             size = sizeof(PROPERTYMAPTABLE);
355             size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
356                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
357             size += (assembly->tables[TableFromToken(mdtProperty)].rows >
358                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
359             break;
360         }
361         case mdtProperty:
362         {
363             size = sizeof(PROPERTYTABLE) + (assembly->stringsz - sizeof(WORD)) +
364                    (assembly->blobsz - sizeof(WORD));
365             break;
366         }
367         case 0x18000000: /* FIXME */
368         {
369             size = sizeof(METHODSEMANTICSTABLE);
370
371             /* Association:HasSemantics */
372             tables = max(assembly->tables[TableFromToken(mdtEvent)].rows,
373                          assembly->tables[TableFromToken(mdtProperty)].rows);
374             size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
375
376             size += (assembly->tables[TableFromToken(mdtMethodDef)].rows >
377                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
378             break;
379         }
380         case 0x19000000: /* FIXME */
381         {
382             size = sizeof(METHODIMPLTABLE);
383
384             /* MethodBody:MethodDefOrRef, MethodDeclaration:MethodDefOrRef */
385             tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
386                          assembly->tables[TableFromToken(mdtMemberRef)].rows);
387             size += (tables > MAX_TABLES_1BIT_ENCODE) ? 2 * sizeof(WORD) : 0;
388
389             size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
390                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
391             break;
392         }
393         case mdtModuleRef:
394         {
395             size = sizeof(MODULEREFTABLE) + (assembly->stringsz - sizeof(WORD));
396             break;
397         }
398         case mdtTypeSpec:
399         {
400             size = sizeof(TYPESPECTABLE) + (assembly->blobsz - sizeof(WORD));
401             break;
402         }
403         case 0x1C000000: /* FIXME */
404         {
405             size = sizeof(IMPLMAPTABLE) + (assembly->stringsz - sizeof(WORD));
406
407             /* MemberForwarded:MemberForwarded */
408             tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows,
409                          assembly->tables[TableFromToken(mdtMethodDef)].rows);
410             size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
411
412             size += (assembly->tables[TableFromToken(mdtModuleRef)].rows >
413                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
414             break;
415         }
416         case 0x1D000000: /* FIXME */
417         {
418             size = sizeof(FIELDRVATABLE);
419             size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
420                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
421             break;
422         }
423         case mdtAssembly:
424         {
425             size = sizeof(ASSEMBLYTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) +
426                    (assembly->blobsz - sizeof(WORD));
427             break;
428         }
429         case 0x20000001: /* FIXME */
430         {
431             size = sizeof(ASSEMBLYPROCESSORTABLE);
432             break;
433         }
434         case 0x22000000: /* FIXME */
435         {
436             size = sizeof(ASSEMBLYOSTABLE);
437             break;
438         }
439         case mdtAssemblyRef:
440         {
441             size = sizeof(ASSEMBLYREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) +
442                    2 * (assembly->blobsz - sizeof(WORD));
443             break;
444         }
445         case 0x24000000: /* FIXME */
446         {
447             size = sizeof(ASSEMBLYREFPROCESSORTABLE);
448             size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows >
449                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
450             break;
451         }
452         case 0x25000000: /* FIXME */
453         {
454             size = sizeof(ASSEMBLYREFOSTABLE);
455             size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows >
456                      MAX_TABLES_WORD) ? sizeof(WORD) : 0;
457             break;
458         }
459         case mdtFile:
460         {
461             size = sizeof(FILETABLE) + (assembly->stringsz - sizeof(WORD)) +
462                    (assembly->blobsz - sizeof(WORD));
463             break;
464         }
465         case mdtExportedType:
466         {
467             size = sizeof(EXPORTEDTYPETABLE) + 2 * (assembly->stringsz - sizeof(WORD));
468
469             /* Implementation:Implementation */
470             tables = max(assembly->tables[TableFromToken(mdtFile)].rows,
471                          assembly->tables[TableFromToken(mdtMethodDef)].rows);
472             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
473             break;
474         }
475         case mdtManifestResource:
476         {
477             size = sizeof(MANIFESTRESTABLE) + (assembly->stringsz - sizeof(WORD));
478
479             /* Implementation:Implementation */
480             tables = max(assembly->tables[TableFromToken(mdtFile)].rows,
481                          assembly->tables[TableFromToken(mdtAssemblyRef)].rows);
482             size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
483             break;
484         }
485         case 0x29000000: /* FIXME */
486         {
487             size = sizeof(NESTEDCLASSTABLE);
488             size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
489                      MAX_TABLES_WORD) ? 2 * sizeof(WORD) : 0;
490             break;
491         }
492         default:
493             return 0;
494     }
495
496     return size;
497 }
498
499 static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
500 {
501     DWORD i, previ, offidx;
502     ULONG currofs;
503
504     currofs = offset;
505     assembly->tableshdr = (METADATATABLESHDR *)assembly_data_offset(assembly, currofs);
506     if (!assembly->tableshdr)
507         return E_FAIL;
508
509     assembly->stringsz = (assembly->tableshdr->HeapOffsetSizes & MD_STRINGS_BIT) ?
510                          sizeof(DWORD) : sizeof(WORD);
511     assembly->guidsz = (assembly->tableshdr->HeapOffsetSizes & MD_GUIDS_BIT) ?
512                        sizeof(DWORD) : sizeof(WORD);
513     assembly->blobsz = (assembly->tableshdr->HeapOffsetSizes & MD_BLOBS_BIT) ?
514                        sizeof(DWORD) : sizeof(WORD);
515
516     currofs += sizeof(METADATATABLESHDR);
517     assembly->numrows = (DWORD *)assembly_data_offset(assembly, currofs);
518     if (!assembly->numrows)
519         return E_FAIL;
520
521     assembly->numtables = 0;
522     for (i = 0; i < MAX_CLR_TABLES; i++)
523     {
524         if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
525             (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
526         {
527             assembly->numtables++;
528         }
529     }
530
531     currofs += assembly->numtables * sizeof(DWORD);
532     memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
533
534     if (assembly->tableshdr->MaskValid.u.LowPart & 1)
535         assembly->tables[0].offset = currofs;
536
537     offidx = 0;
538     for (i = 0; i < MAX_CLR_TABLES; i++)
539     {
540         if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
541             (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
542         {
543             assembly->tables[i].rows = assembly->numrows[offidx];
544             offidx++;
545         }
546     }
547
548     previ = 0;
549     offidx = 1;
550     for (i = 1; i < MAX_CLR_TABLES; i++)
551     {
552         if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
553             (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
554         {
555             currofs += get_table_size(assembly, previ) * assembly->numrows[offidx - 1];
556             assembly->tables[i].offset = currofs;
557             offidx++;
558             previ = i;
559         }
560     }
561
562     return S_OK;
563 }
564
565 static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz)
566 {
567     METADATAHDR *metadatahdr;
568     BYTE *ptr, *dest;
569     DWORD size, ofs;
570     ULONG rva;
571
572     rva = assembly->corhdr->MetaData.VirtualAddress;
573     ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva, NULL);
574     if (!ptr)
575         return E_FAIL;
576
577     metadatahdr = (METADATAHDR *)ptr;
578
579     assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR));
580     if (!assembly->metadatahdr)
581         return E_OUTOFMEMORY;
582
583     size = FIELD_OFFSET(METADATAHDR, Version);
584     memcpy(assembly->metadatahdr, metadatahdr, size);
585
586     /* we don't care about the version string */
587
588     ofs = FIELD_OFFSET(METADATAHDR, Flags);
589     ptr += FIELD_OFFSET(METADATAHDR, Version) + metadatahdr->VersionLength + 1;
590     dest = (BYTE *)assembly->metadatahdr + ofs;
591     memcpy(dest, ptr, sizeof(METADATAHDR) - ofs);
592
593     *hdrsz = sizeof(METADATAHDR) - sizeof(LPSTR) + metadatahdr->VersionLength + 1;
594
595     return S_OK;
596 }
597
598 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
599 {
600     METADATASTREAMHDR *streamhdr;
601     ULONG rva, i, ofs;
602     LPSTR stream;
603     HRESULT hr;
604     DWORD hdrsz;
605     BYTE *ptr;
606
607     hr = parse_metadata_header(assembly, &hdrsz);
608     if (FAILED(hr))
609         return hr;
610
611     rva = assembly->corhdr->MetaData.VirtualAddress;
612     ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva + hdrsz, NULL);
613     if (!ptr)
614         return E_FAIL;
615
616     for (i = 0; i < assembly->metadatahdr->Streams; i++)
617     {
618         streamhdr = (METADATASTREAMHDR *)ptr;
619         ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
620
621         ptr += sizeof(METADATASTREAMHDR);
622         stream = (LPSTR)ptr;
623
624         if (!lstrcmpA(stream, "#~"))
625         {
626             hr = parse_clr_tables(assembly, ofs);
627             if (FAILED(hr))
628                 return hr;
629         }
630         else if (!lstrcmpA(stream, "#Strings") || !lstrcmpA(stream, "Strings"))
631             assembly->strings = (BYTE *)assembly_data_offset(assembly, ofs);
632         else if (!lstrcmpA(stream, "#Blob") || !lstrcmpA(stream, "Blob"))
633             assembly->blobs = (BYTE *)assembly_data_offset(assembly, ofs);
634
635         ptr += lstrlenA(stream) + 1;
636         ptr = (BYTE *)(((UINT_PTR)ptr + 3) & ~3); /* align on DWORD boundary */
637     }
638
639     return S_OK;
640 }
641
642 static HRESULT parse_pe_header(ASSEMBLY *assembly)
643 {
644     IMAGE_DATA_DIRECTORY *datadirs;
645
646     assembly->nthdr = ImageNtHeader(assembly->data);
647     if (!assembly->nthdr)
648         return E_FAIL;
649
650     if (assembly->nthdr->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
651     {
652         IMAGE_OPTIONAL_HEADER64 *opthdr =
653                 (IMAGE_OPTIONAL_HEADER64 *)&assembly->nthdr->OptionalHeader;
654         datadirs = opthdr->DataDirectory;
655     }
656     else
657         datadirs = assembly->nthdr->OptionalHeader.DataDirectory;
658
659     if (!datadirs)
660         return E_FAIL;
661
662     if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
663         !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
664     {
665         return E_FAIL;
666     }
667
668     assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data,
669         datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL);
670     if (!assembly->corhdr)
671         return E_FAIL;
672
673     return S_OK;
674 }
675
676 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
677 {
678     ASSEMBLY *assembly;
679     HRESULT hr;
680
681     *out = NULL;
682
683     assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
684     if (!assembly)
685         return E_OUTOFMEMORY;
686
687     assembly->path = strdupWtoA(file);
688     if (!assembly->path)
689     {
690         hr = E_OUTOFMEMORY;
691         goto failed;
692     }
693
694     assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
695                                   NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
696     if (assembly->hfile == INVALID_HANDLE_VALUE)
697     {
698         hr = HRESULT_FROM_WIN32(GetLastError());
699         goto failed;
700     }
701
702     assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
703                                         0, 0, NULL);
704     if (!assembly->hmap)
705     {
706         hr = HRESULT_FROM_WIN32(GetLastError());
707         goto failed;
708     }
709
710     assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
711     if (!assembly->data)
712     {
713         hr = HRESULT_FROM_WIN32(GetLastError());
714         goto failed;
715     }
716
717     hr = parse_pe_header(assembly);
718     if (FAILED(hr)) goto failed;
719
720     hr = parse_clr_metadata(assembly);
721     if (FAILED(hr)) goto failed;
722
723     *out = assembly;
724     return S_OK;
725
726 failed:
727     assembly_release(assembly);
728     return hr;
729 }
730
731 HRESULT assembly_release(ASSEMBLY *assembly)
732 {
733     if (!assembly)
734         return S_OK;
735
736     HeapFree(GetProcessHeap(), 0, assembly->metadatahdr);
737     HeapFree(GetProcessHeap(), 0, assembly->path);
738     UnmapViewOfFile(assembly->data);
739     CloseHandle(assembly->hmap);
740     CloseHandle(assembly->hfile);
741     HeapFree(GetProcessHeap(), 0, assembly);
742
743     return S_OK;
744 }
745
746 static LPSTR assembly_dup_str(ASSEMBLY *assembly, DWORD index)
747 {
748     LPSTR str = (LPSTR)&assembly->strings[index];
749     LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(str)+1);
750     if (cpy)
751        strcpy(cpy, str);
752     return cpy;
753 }
754
755 HRESULT assembly_get_name(ASSEMBLY *assembly, LPSTR *name)
756 {
757     BYTE *ptr;
758     LONG offset;
759     DWORD stridx;
760
761     offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
762     if (offset == -1)
763         return E_FAIL;
764
765     ptr = assembly_data_offset(assembly, offset);
766     if (!ptr)
767         return E_FAIL;
768
769     ptr += FIELD_OFFSET(ASSEMBLYTABLE, PublicKey) + assembly->blobsz;
770     if (assembly->stringsz == sizeof(DWORD))
771         stridx = *((DWORD *)ptr);
772     else
773         stridx = *((WORD *)ptr);
774
775     *name = assembly_dup_str(assembly, stridx);
776     if (!*name)
777         return E_OUTOFMEMORY;
778
779     return S_OK;
780 }
781
782 HRESULT assembly_get_path(ASSEMBLY *assembly, LPSTR *path)
783 {
784     LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(assembly->path)+1);
785     *path = cpy;
786     if (cpy)
787         strcpy(cpy, assembly->path);
788     else
789         return E_OUTOFMEMORY;
790
791     return S_OK;
792 }
793
794 HRESULT assembly_get_version(ASSEMBLY *assembly, LPSTR *version)
795 {
796     LPSTR verdata;
797     VS_FIXEDFILEINFO *ffi;
798     HRESULT hr = S_OK;
799     DWORD size;
800
801     size = GetFileVersionInfoSizeA(assembly->path, NULL);
802     if (!size)
803         return HRESULT_FROM_WIN32(GetLastError());
804
805     verdata = HeapAlloc(GetProcessHeap(), 0, size);
806     if (!verdata)
807         return E_OUTOFMEMORY;
808
809     if (!GetFileVersionInfoA(assembly->path, 0, size, verdata))
810     {
811         hr = HRESULT_FROM_WIN32(GetLastError());
812         goto done;
813     }
814
815     if (!VerQueryValueA(verdata, "\\", (LPVOID *)&ffi, &size))
816     {
817         hr = HRESULT_FROM_WIN32(GetLastError());
818         goto done;
819     }
820
821     *version = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
822     if (!*version)
823     {
824         hr = E_OUTOFMEMORY;
825         goto done;
826     }
827
828     sprintf(*version, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS),
829             LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS),
830             LOWORD(ffi->dwFileVersionLS));
831
832 done:
833     HeapFree(GetProcessHeap(), 0, verdata);
834     return hr;
835 }
836
837 HRESULT assembly_get_architecture(ASSEMBLY *assembly, DWORD fixme)
838 {
839     /* FIXME */
840     return S_OK;
841 }
842
843 static BYTE *assembly_get_blob(ASSEMBLY *assembly, WORD index, ULONG *size)
844 {
845     return GetData(&assembly->blobs[index], size);
846 }
847
848 static void bytes_to_str(BYTE *bytes, DWORD len, LPSTR str)
849 {
850     DWORD i;
851
852     static const char hexval[16] = {
853         '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
854     };
855
856     for(i = 0; i < len; i++)
857     {
858         str[i * 2] = hexval[((bytes[i] >> 4) & 0xF)];
859         str[i * 2 + 1] = hexval[(bytes[i]) & 0x0F];
860     }
861 }
862
863 #define BYTES_PER_TOKEN 8
864 #define CHARS_PER_BYTE 2
865 #define TOKEN_LENGTH (BYTES_PER_TOKEN * CHARS_PER_BYTE + 1)
866
867 HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPSTR *token)
868 {
869     ASSEMBLYTABLE *asmtbl;
870     ULONG i, size;
871     LONG offset;
872     BYTE *hashdata;
873     HCRYPTPROV crypt;
874     HCRYPTHASH hash;
875     BYTE *pubkey;
876     BYTE tokbytes[BYTES_PER_TOKEN];
877     HRESULT hr = E_FAIL;
878     LPSTR tok;
879
880     *token = NULL;
881
882     offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
883     if (offset == -1)
884         return E_FAIL;
885
886     asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
887     if (!asmtbl)
888         return E_FAIL;
889
890     pubkey = assembly_get_blob(assembly, asmtbl->PublicKey, &size);
891
892     if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
893                               CRYPT_VERIFYCONTEXT))
894         return E_FAIL;
895
896     if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
897         return E_FAIL;
898
899     if (!CryptHashData(hash, pubkey, size, 0))
900         return E_FAIL;
901
902     size = 0;
903     if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
904         return E_FAIL;
905
906     hashdata = HeapAlloc(GetProcessHeap(), 0, size);
907     if (!hashdata)
908     {
909         hr = E_OUTOFMEMORY;
910         goto done;
911     }
912
913     if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
914         goto done;
915
916     for (i = size - 1; i >= size - 8; i--)
917         tokbytes[size - i - 1] = hashdata[i];
918
919     tok = HeapAlloc(GetProcessHeap(), 0, TOKEN_LENGTH);
920     if (!tok)
921     {
922         hr = E_OUTOFMEMORY;
923         goto done;
924     }
925
926     bytes_to_str(tokbytes, BYTES_PER_TOKEN, tok);
927     tok[TOKEN_LENGTH - 1] = '\0';
928
929     *token = tok;
930     hr = S_OK;
931
932 done:
933     HeapFree(GetProcessHeap(), 0, hashdata);
934     CryptDestroyHash(hash);
935     CryptReleaseContext(crypt, 0);
936
937     return hr;
938 }