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(&section_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