msi: Make MsiEnumComponentCosts RPC-compatible.
[wine.git] / programs / winemsibuilder / main.c
blobea4607eb2b8d5d82dac0c24e7ac3c67628078e11
1 /*
2 * winemsibuilder - tool to build MSI packages
4 * Copyright 2010 Hans Leidekker 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 #define WIN32_LEAN_AND_MEAN
22 #define COBJMACROS
24 #include <stdio.h>
25 #include <windows.h>
26 #include <msi.h>
27 #include <msiquery.h>
28 #include <objbase.h>
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder);
35 static UINT open_database( const WCHAR *msifile, MSIHANDLE *handle )
37 UINT r;
38 MSIHANDLE hdb;
40 if (GetFileAttributesW( msifile ) == INVALID_FILE_ATTRIBUTES)
42 r = MsiOpenDatabaseW( msifile, MSIDBOPEN_CREATE, &hdb );
43 if (r != ERROR_SUCCESS)
45 WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile), r );
46 return r;
48 r = MsiDatabaseCommit( hdb );
49 if (r != ERROR_SUCCESS)
51 WINE_ERR( "failed to commit database (%u)\n", r );
52 MsiCloseHandle( hdb );
53 return r;
56 else
58 r = MsiOpenDatabaseW( msifile, MSIDBOPEN_TRANSACT, &hdb );
59 if (r != ERROR_SUCCESS)
61 WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile), r );
62 return r;
66 *handle = hdb;
67 return ERROR_SUCCESS;
70 static int import_tables( const WCHAR *msifile, WCHAR **tables )
72 UINT r;
73 MSIHANDLE hdb;
74 WCHAR *dir;
75 DWORD len;
77 r = open_database( msifile, &hdb );
78 if (r != ERROR_SUCCESS) return 1;
80 len = GetCurrentDirectoryW( 0, NULL );
81 if (!(dir = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
83 MsiCloseHandle( hdb );
84 return 1;
86 GetCurrentDirectoryW( len + 1, dir );
88 while (*tables)
90 r = MsiDatabaseImportW( hdb, dir, *tables );
91 if (r != ERROR_SUCCESS)
93 WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables), r );
94 break;
96 tables++;
99 if (r == ERROR_SUCCESS)
101 r = MsiDatabaseCommit( hdb );
102 if (r != ERROR_SUCCESS)
103 WINE_ERR( "failed to commit changes (%u)\n", r );
106 HeapFree( GetProcessHeap(), 0, dir );
107 MsiCloseHandle( hdb );
108 return (r != ERROR_SUCCESS);
111 /* taken from dlls/msi/table.c */
112 static int utf2mime( int x )
114 if (x >= '0' && x <= '9')
115 return x - '0';
116 if (x >= 'A' && x <= 'Z')
117 return x - 'A' + 10;
118 if (x >= 'a' && x <= 'z')
119 return x - 'a' + 10 + 26;
120 if (x == '.')
121 return 10 + 26 + 26;
122 if (x == '_')
123 return 10 + 26 + 26 + 1;
124 return -1;
127 #define MAX_STREAM_NAME 0x1f
129 static WCHAR *encode_stream( const WCHAR *in )
131 DWORD c, next, count;
132 WCHAR *out, *p;
134 count = strlenW( in );
135 if (count > MAX_STREAM_NAME)
136 return NULL;
138 count += 2;
139 if (!(out = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WCHAR) ))) return NULL;
140 p = out;
141 while (count--)
143 c = *in++;
144 if (!c)
146 *p = c;
147 return out;
149 if (c < 0x80 && utf2mime( c ) >= 0)
151 c = utf2mime( c ) + 0x4800;
152 next = *in;
153 if (next && next < 0x80)
155 next = utf2mime( next );
156 if (next != -1)
158 next += 0x3ffffc0;
159 c += next << 6;
160 in++;
164 *p++ = c;
166 HeapFree( GetProcessHeap(), 0, out );
167 return NULL;
170 static int add_stream( const WCHAR *msifile, const WCHAR *stream, const WCHAR *file )
172 UINT r;
173 HRESULT hr;
174 MSIHANDLE hdb;
175 IStorage *stg;
176 IStream *stm = NULL;
177 HANDLE handle;
178 char buffer[4096];
179 ULARGE_INTEGER size;
180 DWORD low, high, read;
181 WCHAR *encname;
182 int ret = 1;
184 /* make sure we have the right type of file */
185 r = open_database( msifile, &hdb );
186 if (r != ERROR_SUCCESS) return 1;
187 MsiCloseHandle( hdb );
189 hr = StgOpenStorage( msifile, NULL, STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
190 if (hr != S_OK)
192 WINE_WARN( "failed to open storage %s (0x%08x)\n", wine_dbgstr_w(msifile), hr );
193 return 1;
195 encname = encode_stream( stream );
196 if (!encname)
198 WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream) );
199 goto done;
201 hr = IStorage_CreateStream( stg, encname, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
202 if (hr != S_OK)
204 WINE_WARN( "failed to create stream %s (0x%08x)\n", wine_dbgstr_w(encname), hr );
205 goto done;
207 handle = CreateFileW( file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
208 if (handle == INVALID_HANDLE_VALUE)
210 WINE_WARN( "failed to open file %s (%u)\n", wine_dbgstr_w(file), GetLastError() );
211 goto done;
213 low = GetFileSize( handle, &high );
214 if (low == INVALID_FILE_SIZE || high)
216 WINE_WARN( "file %s too big\n", wine_dbgstr_w(file) );
217 CloseHandle( handle );
218 goto done;
220 size.QuadPart = low;
221 hr = IStream_SetSize( stm, size );
222 if (hr != S_OK) goto done;
224 while (ReadFile( handle, buffer, sizeof(buffer), &read, NULL ) && read)
226 hr = IStream_Write( stm, buffer, read, NULL );
227 if (hr != S_OK) break;
228 size.QuadPart -= read;
230 CloseHandle( handle );
231 if (size.QuadPart)
233 WINE_WARN( "failed to write stream contents\n" );
234 goto done;
236 IStorage_Commit( stg, 0 );
237 ret = 0;
239 done:
240 HeapFree( GetProcessHeap(), 0, encname );
241 if (stm) IStream_Release( stm );
242 IStorage_Release( stg );
243 return ret;
246 static void show_usage( void )
248 WINE_MESSAGE(
249 "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n"
250 "Options:\n"
251 " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n"
252 " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n"
253 "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
254 "will be created with an empty database.\n"
258 int wmain( int argc, WCHAR *argv[] )
260 if (argc < 3 || argv[1][0] != '-')
262 show_usage();
263 return 1;
266 switch (argv[1][1])
268 case 'i':
269 if (argc < 4) break;
270 return import_tables( argv[2], argv + 3 );
271 case 'a':
272 if (argc < 5) break;
273 return add_stream( argv[2], argv[3], argv[4] );
274 default:
275 WINE_WARN( "unknown option\n" );
276 break;
279 show_usage();
280 return 1;