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
26 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
31 typedef struct _tagTT_OFFSET_TABLE
{
36 USHORT uEntrySelector
;
40 typedef struct _tagTT_TABLE_DIRECTORY
{
41 char szTag
[4]; /* table name */
42 ULONG uCheckSum
; /* Check sum */
43 ULONG uOffset
; /* Offset from beginning of file */
44 ULONG uLength
; /* length of the table in bytes */
47 typedef struct _tagTT_NAME_TABLE_HEADER
{
48 USHORT uFSelector
; /* format selector. Always 0 */
49 USHORT uNRCount
; /* Name Records count */
50 USHORT uStorageOffset
; /* Offset for strings storage,
51 * from start of the table */
52 } TT_NAME_TABLE_HEADER
;
54 #define NAME_ID_FULL_FONT_NAME 4
55 #define NAME_ID_VERSION 5
57 typedef struct _tagTT_NAME_RECORD
{
63 USHORT uStringOffset
; /* from start of storage area */
66 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
67 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
70 * Code based off of code located here
71 * http://www.codeproject.com/gdi/fontnamefromfile.asp
73 static WCHAR
*load_ttf_name_id( MSIPACKAGE
*package
, const WCHAR
*filename
, DWORD id
)
75 TT_TABLE_DIRECTORY tblDir
;
77 TT_OFFSET_TABLE ttOffsetTable
;
78 TT_NAME_TABLE_HEADER ttNTHeader
;
79 TT_NAME_RECORD ttRecord
;
86 handle
= msi_create_file( package
, filename
, GENERIC_READ
, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
);
88 handle
= CreateFileW( filename
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0 );
89 if (handle
== INVALID_HANDLE_VALUE
)
91 ERR("Unable to open font file %s\n", debugstr_w(filename
));
95 if (!ReadFile(handle
,&ttOffsetTable
, sizeof(TT_OFFSET_TABLE
),&dwRead
,NULL
))
98 ttOffsetTable
.uNumOfTables
= SWAPWORD(ttOffsetTable
.uNumOfTables
);
99 ttOffsetTable
.uMajorVersion
= SWAPWORD(ttOffsetTable
.uMajorVersion
);
100 ttOffsetTable
.uMinorVersion
= SWAPWORD(ttOffsetTable
.uMinorVersion
);
102 if ((ttOffsetTable
.uMajorVersion
!= 1 || ttOffsetTable
.uMinorVersion
!= 0) &&
103 (ttOffsetTable
.uMajorVersion
!= 0x4f54 || ttOffsetTable
.uMinorVersion
!= 0x544f))
106 for (i
=0; i
< ttOffsetTable
.uNumOfTables
; i
++)
108 if (!ReadFile(handle
,&tblDir
, sizeof(TT_TABLE_DIRECTORY
),&dwRead
,NULL
))
110 if (memcmp(tblDir
.szTag
,"name",4)==0)
113 tblDir
.uLength
= SWAPLONG(tblDir
.uLength
);
114 tblDir
.uOffset
= SWAPLONG(tblDir
.uOffset
);
122 SetFilePointer(handle
, tblDir
.uOffset
, NULL
, FILE_BEGIN
);
123 if (!ReadFile(handle
,&ttNTHeader
, sizeof(TT_NAME_TABLE_HEADER
), &dwRead
,NULL
))
126 ttNTHeader
.uNRCount
= SWAPWORD(ttNTHeader
.uNRCount
);
127 ttNTHeader
.uStorageOffset
= SWAPWORD(ttNTHeader
.uStorageOffset
);
128 for(i
=0; i
<ttNTHeader
.uNRCount
; i
++)
130 if (!ReadFile(handle
,&ttRecord
, sizeof(TT_NAME_RECORD
),&dwRead
,NULL
))
133 ttRecord
.uNameID
= SWAPWORD(ttRecord
.uNameID
);
134 ttRecord
.uPlatformID
= SWAPWORD(ttRecord
.uPlatformID
);
135 ttRecord
.uEncodingID
= SWAPWORD(ttRecord
.uEncodingID
);
136 if (ttRecord
.uNameID
== id
&& ttRecord
.uPlatformID
== 3 &&
137 (ttRecord
.uEncodingID
== 0 || ttRecord
.uEncodingID
== 1))
142 ttRecord
.uStringLength
= SWAPWORD(ttRecord
.uStringLength
);
143 ttRecord
.uStringOffset
= SWAPWORD(ttRecord
.uStringOffset
);
144 SetFilePointer(handle
, tblDir
.uOffset
+ ttRecord
.uStringOffset
+ ttNTHeader
.uStorageOffset
,
146 if (!(buf
= msi_alloc_zero( ttRecord
.uStringLength
+ sizeof(WCHAR
) ))) goto end
;
148 ReadFile(handle
, buf
, ttRecord
.uStringLength
, &dwRead
, NULL
);
149 if (dwRead
% sizeof(WCHAR
))
154 for (i
= 0; i
< dwRead
/ sizeof(WCHAR
); i
++) buf
[i
] = SWAPWORD(buf
[i
]);
166 static WCHAR
*font_name_from_file( MSIPACKAGE
*package
, const WCHAR
*filename
)
168 WCHAR
*name
, *ret
= NULL
;
170 if ((name
= load_ttf_name_id( package
, filename
, NAME_ID_FULL_FONT_NAME
)))
174 WARN("empty font name\n");
178 ret
= msi_alloc( (lstrlenW( name
) + lstrlenW( L
" (TrueType)" ) + 1 ) * sizeof(WCHAR
) );
179 lstrcpyW( ret
, name
);
180 lstrcatW( ret
, L
" (TrueType)" );
186 WCHAR
*msi_get_font_file_version( MSIPACKAGE
*package
, const WCHAR
*filename
)
188 WCHAR
*version
, *p
, *q
, *ret
= NULL
;
190 if ((version
= load_ttf_name_id( package
, filename
, NAME_ID_VERSION
)))
192 int len
, major
= 0, minor
= 0;
193 if ((p
= wcschr( version
, ';' ))) *p
= 0;
195 while (*p
&& !iswdigit( *p
)) p
++;
196 if ((q
= wcschr( p
, '.' )))
198 major
= wcstol( p
, NULL
, 10 );
200 while (*q
&& iswdigit( *q
)) q
++;
201 if (!*q
|| *q
== ' ') minor
= wcstol( p
, NULL
, 10 );
204 len
= lstrlenW( L
"%u.%u.0.0" ) + 20;
205 ret
= msi_alloc( len
* sizeof(WCHAR
) );
206 swprintf( ret
, len
, L
"%u.%u.0.0", major
, minor
);
212 static UINT
ITERATE_RegisterFonts(MSIRECORD
*row
, LPVOID param
)
214 MSIPACKAGE
*package
= param
;
223 filename
= MSI_RecordGetString( row
, 1 );
224 file
= msi_get_loaded_file( package
, filename
);
227 WARN("unable to find file %s\n", debugstr_w(filename
));
228 return ERROR_SUCCESS
;
230 comp
= msi_get_loaded_component( package
, file
->Component
->Component
);
233 WARN("unable to find component %s\n", debugstr_w(file
->Component
->Component
));
234 return ERROR_SUCCESS
;
236 comp
->Action
= msi_get_component_action( package
, comp
);
237 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
239 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
240 return ERROR_SUCCESS
;
243 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" ,&hkey1
);
244 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey2
);
246 if (MSI_RecordIsNull(row
,2))
247 name
= font_name_from_file( package
, file
->TargetPath
);
249 name
= msi_dup_record_field(row
,2);
253 msi_reg_set_val_str( hkey1
, name
, file
->TargetPath
);
254 msi_reg_set_val_str( hkey2
, name
, file
->TargetPath
);
262 uirow
= MSI_CreateRecord( 1 );
263 uipath
= strdupW( file
->TargetPath
);
264 p
= wcsrchr(uipath
,'\\');
267 MSI_RecordSetStringW( uirow
, 1, p
);
268 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
269 msiobj_release( &uirow
->hdr
);
271 /* FIXME: call msi_ui_progress? */
273 return ERROR_SUCCESS
;
276 UINT
ACTION_RegisterFonts(MSIPACKAGE
*package
)
281 if (package
->script
== SCRIPT_NONE
)
282 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"RegisterFonts");
284 rc
= MSI_DatabaseOpenViewW(package
->db
, L
"SELECT * FROM `Font`", &view
);
285 if (rc
!= ERROR_SUCCESS
)
286 return ERROR_SUCCESS
;
288 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterFonts
, package
);
289 msiobj_release(&view
->hdr
);
293 static UINT
ITERATE_UnregisterFonts( MSIRECORD
*row
, LPVOID param
)
295 MSIPACKAGE
*package
= param
;
304 filename
= MSI_RecordGetString( row
, 1 );
305 file
= msi_get_loaded_file( package
, filename
);
308 WARN("unable to find file %s\n", debugstr_w(filename
));
309 return ERROR_SUCCESS
;
311 comp
= msi_get_loaded_component( package
, file
->Component
->Component
);
314 WARN("unable to find component %s\n", debugstr_w(file
->Component
->Component
));
315 return ERROR_SUCCESS
;
317 comp
->Action
= msi_get_component_action( package
, comp
);
318 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
320 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
321 return ERROR_SUCCESS
;
324 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", &hkey1
);
325 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey2
);
327 if (MSI_RecordIsNull( row
, 2 ))
328 name
= font_name_from_file( package
, file
->TargetPath
);
330 name
= msi_dup_record_field( row
, 2 );
334 RegDeleteValueW( hkey1
, name
);
335 RegDeleteValueW( hkey2
, name
);
339 RegCloseKey( hkey1
);
340 RegCloseKey( hkey2
);
343 uirow
= MSI_CreateRecord( 1 );
344 uipath
= strdupW( file
->TargetPath
);
345 p
= wcsrchr( uipath
,'\\' );
348 MSI_RecordSetStringW( uirow
, 1, p
);
349 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
350 msiobj_release( &uirow
->hdr
);
352 /* FIXME: call msi_ui_progress? */
354 return ERROR_SUCCESS
;
357 UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
362 if (package
->script
== SCRIPT_NONE
)
363 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"UnregisterFonts");
365 r
= MSI_DatabaseOpenViewW( package
->db
, L
"SELECT * FROM `Font`", &view
);
366 if (r
!= ERROR_SUCCESS
)
367 return ERROR_SUCCESS
;
369 r
= MSI_IterateRecords( view
, NULL
, ITERATE_UnregisterFonts
, package
);
370 msiobj_release( &view
->hdr
);