msi: Add an offset to sequence numbers belonging to files added by a patch.
[wine.git] / dlls / msi / font.c
blob7a493867c6a6f31313d433812543ec20839b4b55
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winerror.h"
25 #include "winreg.h"
26 #include "wine/debug.h"
27 #include "msipriv.h"
28 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(msi);
32 typedef struct _tagTT_OFFSET_TABLE {
33 USHORT uMajorVersion;
34 USHORT uMinorVersion;
35 USHORT uNumOfTables;
36 USHORT uSearchRange;
37 USHORT uEntrySelector;
38 USHORT uRangeShift;
39 } TT_OFFSET_TABLE;
41 typedef struct _tagTT_TABLE_DIRECTORY {
42 char szTag[4]; /* table name */
43 ULONG uCheckSum; /* Check sum */
44 ULONG uOffset; /* Offset from beginning of file */
45 ULONG uLength; /* length of the table in bytes */
46 } TT_TABLE_DIRECTORY;
48 typedef struct _tagTT_NAME_TABLE_HEADER {
49 USHORT uFSelector; /* format selector. Always 0 */
50 USHORT uNRCount; /* Name Records count */
51 USHORT uStorageOffset; /* Offset for strings storage,
52 * from start of the table */
53 } TT_NAME_TABLE_HEADER;
55 #define NAME_ID_FULL_FONT_NAME 4
56 #define NAME_ID_VERSION 5
58 typedef struct _tagTT_NAME_RECORD {
59 USHORT uPlatformID;
60 USHORT uEncodingID;
61 USHORT uLanguageID;
62 USHORT uNameID;
63 USHORT uStringLength;
64 USHORT uStringOffset; /* from start of storage area */
65 } TT_NAME_RECORD;
67 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
68 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
70 static const WCHAR regfont1[] =
71 {'S','o','f','t','w','a','r','e','\\',
72 'M','i','c','r','o','s','o','f','t','\\',
73 'W','i','n','d','o','w','s',' ','N','T','\\',
74 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
75 'F','o','n','t','s',0};
76 static const WCHAR regfont2[] =
77 {'S','o','f','t','w','a','r','e','\\',
78 'M','i','c','r','o','s','o','f','t','\\',
79 'W','i','n','d','o','w','s','\\',
80 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
81 'F','o','n','t','s',0};
84 * Code based off of code located here
85 * http://www.codeproject.com/gdi/fontnamefromfile.asp
87 WCHAR *load_ttf_name_id( const WCHAR *filename, DWORD id )
89 TT_TABLE_DIRECTORY tblDir;
90 BOOL bFound = FALSE;
91 TT_OFFSET_TABLE ttOffsetTable;
92 TT_NAME_TABLE_HEADER ttNTHeader;
93 TT_NAME_RECORD ttRecord;
94 DWORD dwRead;
95 HANDLE handle;
96 LPWSTR ret = NULL;
97 int i;
99 handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
100 FILE_ATTRIBUTE_NORMAL, 0 );
101 if (handle == INVALID_HANDLE_VALUE)
103 ERR("Unable to open font file %s\n", debugstr_w(filename));
104 return NULL;
107 if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
108 goto end;
110 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
111 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
112 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
114 if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
115 goto end;
117 for (i=0; i< ttOffsetTable.uNumOfTables; i++)
119 if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
120 break;
121 if (memcmp(tblDir.szTag,"name",4)==0)
123 bFound = TRUE;
124 tblDir.uLength = SWAPLONG(tblDir.uLength);
125 tblDir.uOffset = SWAPLONG(tblDir.uOffset);
126 break;
130 if (!bFound)
131 goto end;
133 SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
134 if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
135 goto end;
137 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
138 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
139 bFound = FALSE;
140 for(i=0; i<ttNTHeader.uNRCount; i++)
142 if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
143 break;
145 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
146 if (ttRecord.uNameID == id)
148 int nPos;
149 LPSTR buf;
151 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
152 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
153 nPos = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
154 SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset,
155 NULL, FILE_BEGIN);
156 buf = msi_alloc_zero( ttRecord.uStringLength + 1 );
157 ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
158 if (strlen(buf) > 0)
160 ret = strdupAtoW(buf);
161 msi_free(buf);
162 break;
164 msi_free(buf);
165 SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
169 end:
170 CloseHandle(handle);
171 TRACE("Returning %s\n", debugstr_w(ret));
172 return ret;
175 static WCHAR *font_name_from_file( const WCHAR *filename )
177 static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0};
178 WCHAR *name, *ret = NULL;
180 if ((name = load_ttf_name_id( filename, NAME_ID_FULL_FONT_NAME )))
182 ret = msi_alloc( (strlenW( name ) + strlenW( truetypeW ) + 1 ) * sizeof(WCHAR) );
183 strcpyW( ret, name );
184 strcatW( ret, truetypeW );
185 msi_free( name );
187 return ret;
190 WCHAR *font_version_from_file( const WCHAR *filename )
192 WCHAR *version, *p, *ret = NULL;
194 if ((p = version = load_ttf_name_id( filename, NAME_ID_VERSION )))
196 while (*p && !isdigitW( *p )) p++;
197 ret = msi_alloc( (strlenW( p ) + 1) * sizeof(WCHAR) );
198 strcpyW( ret, p );
199 msi_free( version );
201 return ret;
204 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
206 MSIPACKAGE *package = param;
207 LPWSTR name;
208 LPCWSTR filename;
209 MSIFILE *file;
210 HKEY hkey1, hkey2;
211 MSIRECORD *uirow;
212 LPWSTR uipath, p;
214 filename = MSI_RecordGetString( row, 1 );
215 file = get_loaded_file( package, filename );
216 if (!file)
218 ERR("Unable to load file\n");
219 return ERROR_SUCCESS;
222 if (!file->Component->Enabled)
224 TRACE("component is disabled\n");
225 return ERROR_SUCCESS;
228 if (file->Component->ActionRequest != INSTALLSTATE_LOCAL)
230 TRACE("Component not scheduled for installation\n");
231 return ERROR_SUCCESS;
234 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
235 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
237 if (MSI_RecordIsNull(row,2))
238 name = font_name_from_file( file->TargetPath );
239 else
240 name = msi_dup_record_field(row,2);
242 if (name)
244 msi_reg_set_val_str( hkey1, name, file->TargetPath);
245 msi_reg_set_val_str( hkey2, name, file->TargetPath);
248 msi_free(name);
249 RegCloseKey(hkey1);
250 RegCloseKey(hkey2);
252 /* the UI chunk */
253 uirow = MSI_CreateRecord( 1 );
254 uipath = strdupW( file->TargetPath );
255 p = strrchrW(uipath,'\\');
256 if (p) p++;
257 else p = uipath;
258 MSI_RecordSetStringW( uirow, 1, p );
259 ui_actiondata( package, szRegisterFonts, uirow);
260 msiobj_release( &uirow->hdr );
261 msi_free( uipath );
262 /* FIXME: call ui_progress? */
264 return ERROR_SUCCESS;
267 UINT ACTION_RegisterFonts(MSIPACKAGE *package)
269 UINT rc;
270 MSIQUERY * view;
271 static const WCHAR ExecSeqQuery[] =
272 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
273 '`','F','o','n','t','`',0};
275 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
276 if (rc != ERROR_SUCCESS)
278 TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
279 return ERROR_SUCCESS;
282 MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
283 msiobj_release(&view->hdr);
285 return ERROR_SUCCESS;
288 static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param )
290 MSIPACKAGE *package = param;
291 LPWSTR name;
292 LPCWSTR filename;
293 MSIFILE *file;
294 HKEY hkey1, hkey2;
295 MSIRECORD *uirow;
296 LPWSTR uipath, p;
298 filename = MSI_RecordGetString( row, 1 );
299 file = get_loaded_file( package, filename );
300 if (!file)
302 ERR("Unable to load file\n");
303 return ERROR_SUCCESS;
306 if (!file->Component->Enabled)
308 TRACE("component is disabled\n");
309 return ERROR_SUCCESS;
312 if (file->Component->ActionRequest != INSTALLSTATE_ABSENT)
314 TRACE("Component not scheduled for removal\n");
315 return ERROR_SUCCESS;
318 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 );
319 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 );
321 if (MSI_RecordIsNull( row, 2 ))
322 name = font_name_from_file( file->TargetPath );
323 else
324 name = msi_dup_record_field( row, 2 );
326 if (name)
328 RegDeleteValueW( hkey1, name );
329 RegDeleteValueW( hkey2, name );
332 msi_free( name );
333 RegCloseKey( hkey1 );
334 RegCloseKey( hkey2 );
336 /* the UI chunk */
337 uirow = MSI_CreateRecord( 1 );
338 uipath = strdupW( file->TargetPath );
339 p = strrchrW( uipath,'\\' );
340 if (p) p++;
341 else p = uipath;
342 MSI_RecordSetStringW( uirow, 1, p );
343 ui_actiondata( package, szUnregisterFonts, uirow );
344 msiobj_release( &uirow->hdr );
345 msi_free( uipath );
346 /* FIXME: call ui_progress? */
348 return ERROR_SUCCESS;
351 UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
353 UINT r;
354 MSIQUERY *view;
355 static const WCHAR query[] =
356 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
357 '`','F','o','n','t','`',0};
359 r = MSI_DatabaseOpenViewW( package->db, query, &view );
360 if (r != ERROR_SUCCESS)
362 TRACE("MSI_DatabaseOpenViewW failed: %u\n", r);
363 return ERROR_SUCCESS;
366 MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package );
367 msiobj_release( &view->hdr );
369 return ERROR_SUCCESS;