1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 //
15 // Functions for reading version information from PE (Windows EXE) files
16 //
17 //=============================================================================
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include "pe.h"
23
24
25 // Simplified structs for PE files
26
27 typedef struct {
28 char padding[60];
29 unsigned int e_lfanew;
30 } IMAGE_DOS_HEADER;
31
32 typedef struct {
33 char padding[2];
34 unsigned short NumberOfSections;
35 char padding1[16];
36 } IMAGE_FILE_HEADER;
37
38 typedef struct {
39 unsigned int Signature;
40 IMAGE_FILE_HEADER FileHeader;
41 char padding[224];
42 } IMAGE_NT_HEADERS;
43
44 typedef struct {
45 char Name[8];
46 char padding[4];
47 unsigned int VirtualAddress;
48 char padding1[4];
49 unsigned int PointerToRawData;
50 char padding2[16];
51 } IMAGE_SECTION_HEADER;
52
53
54 // These structs are original
55
56 typedef struct {
57 unsigned int Characteristics;
58 unsigned int TimeDateStamp;
59 unsigned short MajorVersion;
60 unsigned short MinorVersion;
61 unsigned short NumberOfNamedEntries;
62 unsigned short NumberOfIdEntries;
63 } IMAGE_RESOURCE_DIRECTORY;
64
65 typedef struct {
66 union {
67 struct {
68 unsigned int NameOffset:31;
69 unsigned int NameIsString:1;
70 };
71 unsigned int Name;
72 unsigned short Id;
73 };
74 union {
75 unsigned int OffsetToData;
76 struct {
77 unsigned int OffsetToDirectory:31;
78 unsigned int DataIsDirectory:1;
79 };
80 };
81 } IMAGE_RESOURCE_DIRECTORY_ENTRY;
82
83 typedef struct {
84 unsigned int OffsetToData;
85 unsigned int Size;
86 unsigned int CodePage;
87 unsigned int Reserved;
88 } IMAGE_RESOURCE_DATA_ENTRY;
89
90
91
92 // Version information
93
94 typedef struct {
95 unsigned int dwSignature;
96 unsigned int dwStrucVersion;
97 unsigned int dwFileVersionMS;
98 unsigned int dwFileVersionLS;
99 unsigned int dwProductVersionMS;
100 unsigned int dwProductVersionLS;
101 unsigned int dwFileFlagsMask;
102 unsigned int dwFileFlags;
103 unsigned int dwFileOS;
104 unsigned int dwFileType;
105 unsigned int dwFileSubtype;
106 unsigned int dwFileDateMS;
107 unsigned int dwFileDateLS;
108 } VS_FIXEDFILEINFO;
109
110 typedef struct {
111 unsigned short wLength;
112 unsigned short wValueLength;
113 unsigned short wType;
114 } STRINGFILEINFO_HEADER;
115
116
117
118 IMAGE_DOS_HEADER dos_header;
119 IMAGE_NT_HEADERS nt_headers;
120 IMAGE_SECTION_HEADER section_header;
121 IMAGE_RESOURCE_DIRECTORY resource_directory;
122 IMAGE_RESOURCE_DIRECTORY_ENTRY resource_directory_entry;
123 IMAGE_RESOURCE_DATA_ENTRY resource_data_entry;
124
125 unsigned int resource_virtual_address;
126 unsigned int resource_start;
127
128
129
130
fillBufferFromWidechar(unsigned short * inputBuffer,char * outputText)131 void fillBufferFromWidechar(unsigned short* inputBuffer, char* outputText)
132 {
133 unsigned short* input = inputBuffer;
134 char* output = outputText;
135
136 while (*input)
137 *output++ = *input++;
138
139 *output = '\0';
140 }
141
142
143
getVersionString(char * version_data,unsigned int size,char * buffer,char * name)144 int getVersionString(char* version_data, unsigned int size, char* buffer, char* name)
145 {
146 char* current = version_data;
147 char* last = version_data + size;
148
149 char temp[200];
150
151 // Skip header
152 current += 0x28;
153
154 // Skip VS_FIXEDFILEINFO
155 current += sizeof(VS_FIXEDFILEINFO);
156
157 // Now comes either "StringFileInfo" or "VarFileInfo"
158 STRINGFILEINFO_HEADER* stringfileinfo_header = (STRINGFILEINFO_HEADER*)current;
159 current += sizeof(STRINGFILEINFO_HEADER);
160 fillBufferFromWidechar((unsigned short*)current, temp);
161 if (strcmp(temp, "VarFileInfo") == 0)
162 {
163 current += (stringfileinfo_header->wLength - sizeof(STRINGFILEINFO_HEADER));
164
165 // Skip "StringFileInfo" header too
166 stringfileinfo_header = (STRINGFILEINFO_HEADER*)current;
167 current += 0x3C;
168 }
169 else
170 current += (0x3C - sizeof(STRINGFILEINFO_HEADER));
171
172 while (current < last)
173 {
174 STRINGFILEINFO_HEADER* header = (STRINGFILEINFO_HEADER*)current;
175 current += sizeof(STRINGFILEINFO_HEADER);
176
177 // Read name
178 fillBufferFromWidechar((unsigned short*)current, temp);
179
180 if (strcmp(temp, name) == 0)
181 {
182 current += (2 + 2 * strlen(temp));
183
184 // Next value is 32 bit aligned
185 current = (char*)((unsigned long)(current + 3) & (~3));
186
187 // Read value
188 fillBufferFromWidechar((unsigned short*)current, buffer);
189
190 return 1;
191 }
192 else
193 current += (header->wLength - sizeof(STRINGFILEINFO_HEADER));
194
195 // Next value is 32 bit aligned
196 current = (char*)((unsigned long)(current + 3) & (~3));
197 }
198
199 return 0;
200 }
201
202
203
seekToResource(FILE * pe,int id)204 int seekToResource(FILE* pe, int id)
205 {
206 int i;
207
208 // Read in resource directory
209 fread(&resource_directory, sizeof(IMAGE_RESOURCE_DIRECTORY), 1, pe);
210
211 // Loop through root node entries till we find the entry with the given id
212 for (i = 0; i < resource_directory.NumberOfIdEntries + resource_directory.NumberOfNamedEntries; i++)
213 {
214 // Read in resource node
215 fread(&resource_directory_entry, sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY), 1, pe);
216
217 if ((!resource_directory_entry.NameIsString) && (resource_directory_entry.Id == id))
218 {
219 // Seek to end of subdirectory
220 fseek(pe, resource_start + resource_directory_entry.OffsetToDirectory, SEEK_SET);
221 return 1;
222 }
223 }
224
225 return 0;
226 }
227
228
229
getVersionInformation(char * filename,version_info_t * version_info)230 int getVersionInformation(char* filename, version_info_t* version_info)
231 {
232 FILE* pe = fopen(filename, "rb");
233
234 if (!pe)
235 return 0;
236
237 // Read in the DOS header, get the offset to the PE header and seek
238 fread(&dos_header, sizeof(IMAGE_DOS_HEADER), 1, pe);
239 fseek(pe, dos_header.e_lfanew, SEEK_SET);
240
241 // Read in the PE header
242 fread(&nt_headers, sizeof(IMAGE_NT_HEADERS), 1, pe);
243
244 // Loop through sections till we find the resource section
245 int i;
246 for (i = 0; i < nt_headers.FileHeader.NumberOfSections; i++)
247 {
248 fread(§ion_header, sizeof(IMAGE_SECTION_HEADER), 1, pe);
249
250 if (strcmp(".rsrc", (char*)section_header.Name) == 0)
251 break;
252 }
253
254 if (i == nt_headers.FileHeader.NumberOfSections)
255 goto error_exit;
256
257 // Save virtual address of the resource section
258 resource_virtual_address = section_header.VirtualAddress;
259
260 // Seek to the resource section
261 fseek(pe, section_header.PointerToRawData, SEEK_SET);
262
263 // Save file offset to the resource section
264 resource_start = section_header.PointerToRawData;
265
266 // Search for the version resource in the resource tree
267 if (!seekToResource(pe, 16))
268 goto error_exit;
269
270 // Enter the first subdirectory in the version resource
271 if (!seekToResource(pe, 1))
272 goto error_exit;
273
274 // Hopefully found the resource
275 fread(&resource_directory, sizeof(IMAGE_RESOURCE_DATA_ENTRY), 1, pe);
276
277 // Read in resource node
278 fread(&resource_directory_entry, sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY), 1, pe);
279
280 // Seek to the resource data
281 fseek(pe, resource_start + resource_directory_entry.OffsetToData, SEEK_SET);
282
283 // Read in version info
284 fread(&resource_data_entry, sizeof(IMAGE_RESOURCE_DATA_ENTRY), 1, pe);
285
286 // Finally we got a virtual address of the resource, now seek to it
287 fseek(pe, resource_start + resource_data_entry.OffsetToData - resource_virtual_address, SEEK_SET);
288
289 // Read version resource
290 char* version_data = (char*)malloc(resource_data_entry.Size);
291 fread(version_data, resource_data_entry.Size, 1, pe);
292
293 memset(version_info, 0, sizeof(version_info_t));
294 getVersionString(version_data, resource_data_entry.Size, version_info->version, "FileVersion");
295 getVersionString(version_data, resource_data_entry.Size, version_info->description, "FileDescription");
296 getVersionString(version_data, resource_data_entry.Size, version_info->internal_name, "InternalName");
297
298 free(version_data);
299 fclose(pe);
300
301 return 1;
302
303 error_exit:
304 fclose(pe);
305 return 0;
306 }
307