mf/tests: Test IMediaObject_GetOutputSizeInfo.
[wine.git] / programs / winemsibuilder / main.c
blob3bbd0735505d6ddec783d56e480840f93ff330ad
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"
32 WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder);
34 static UINT open_database( const WCHAR *msifile, MSIHANDLE *handle )
36 UINT r;
37 MSIHANDLE hdb;
39 if (GetFileAttributesW( msifile ) == INVALID_FILE_ATTRIBUTES)
41 r = MsiOpenDatabaseW( msifile, MSIDBOPEN_CREATE, &hdb );
42 if (r != ERROR_SUCCESS)
44 WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile), r );
45 return r;
47 r = MsiDatabaseCommit( hdb );
48 if (r != ERROR_SUCCESS)
50 WINE_ERR( "failed to commit database (%u)\n", r );
51 MsiCloseHandle( hdb );
52 return r;
55 else
57 r = MsiOpenDatabaseW( msifile, MSIDBOPEN_TRANSACT, &hdb );
58 if (r != ERROR_SUCCESS)
60 WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile), r );
61 return r;
65 *handle = hdb;
66 return ERROR_SUCCESS;
69 static int import_tables( const WCHAR *msifile, WCHAR **tables )
71 UINT r;
72 MSIHANDLE hdb;
73 WCHAR *dir;
74 DWORD len;
76 r = open_database( msifile, &hdb );
77 if (r != ERROR_SUCCESS) return 1;
79 len = GetCurrentDirectoryW( 0, NULL );
80 if (!(dir = malloc( (len + 1) * sizeof(WCHAR) )))
82 MsiCloseHandle( hdb );
83 return 1;
85 GetCurrentDirectoryW( len + 1, dir );
87 while (*tables)
89 r = MsiDatabaseImportW( hdb, dir, *tables );
90 if (r != ERROR_SUCCESS)
92 WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables), r );
93 break;
95 tables++;
98 if (r == ERROR_SUCCESS)
100 r = MsiDatabaseCommit( hdb );
101 if (r != ERROR_SUCCESS)
102 WINE_ERR( "failed to commit changes (%u)\n", r );
105 free( dir );
106 MsiCloseHandle( hdb );
107 return (r != ERROR_SUCCESS);
110 /* taken from dlls/msi/table.c */
111 static int utf2mime( int x )
113 if (x >= '0' && x <= '9')
114 return x - '0';
115 if (x >= 'A' && x <= 'Z')
116 return x - 'A' + 10;
117 if (x >= 'a' && x <= 'z')
118 return x - 'a' + 10 + 26;
119 if (x == '.')
120 return 10 + 26 + 26;
121 if (x == '_')
122 return 10 + 26 + 26 + 1;
123 return -1;
126 #define MAX_STREAM_NAME 0x1f
128 static WCHAR *encode_stream( const WCHAR *in )
130 DWORD c, next, count;
131 WCHAR *out, *p;
133 count = lstrlenW( in );
134 if (count > MAX_STREAM_NAME)
135 return NULL;
137 count += 2;
138 if (!(out = malloc( count * sizeof(WCHAR) ))) return NULL;
139 p = out;
140 while (count--)
142 c = *in++;
143 if (!c)
145 *p = c;
146 return out;
148 if (c < 0x80 && utf2mime( c ) >= 0)
150 c = utf2mime( c ) + 0x4800;
151 next = *in;
152 if (next && next < 0x80)
154 next = utf2mime( next );
155 if (next != -1)
157 next += 0x3ffffc0;
158 c += next << 6;
159 in++;
163 *p++ = c;
165 free( out );
166 return NULL;
169 static int add_stream( const WCHAR *msifile, const WCHAR *stream, const WCHAR *file )
171 UINT r;
172 HRESULT hr;
173 MSIHANDLE hdb;
174 IStorage *stg;
175 IStream *stm = NULL;
176 HANDLE handle;
177 char buffer[4096];
178 ULARGE_INTEGER size;
179 DWORD low, high, read;
180 WCHAR *encname;
181 int ret = 1;
183 /* make sure we have the right type of file */
184 r = open_database( msifile, &hdb );
185 if (r != ERROR_SUCCESS) return 1;
186 MsiCloseHandle( hdb );
188 hr = StgOpenStorage( msifile, NULL, STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
189 if (hr != S_OK)
191 WINE_WARN( "failed to open storage %s (0x%08lx)\n", wine_dbgstr_w(msifile), hr );
192 return 1;
194 encname = encode_stream( stream );
195 if (!encname)
197 WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream) );
198 goto done;
200 hr = IStorage_CreateStream( stg, encname, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
201 if (hr != S_OK)
203 WINE_WARN( "failed to create stream %s (0x%08lx)\n", wine_dbgstr_w(encname), hr );
204 goto done;
206 handle = CreateFileW( file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
207 if (handle == INVALID_HANDLE_VALUE)
209 WINE_WARN( "failed to open file %s (%lu)\n", wine_dbgstr_w(file), GetLastError() );
210 goto done;
212 low = GetFileSize( handle, &high );
213 if (low == INVALID_FILE_SIZE || high)
215 WINE_WARN( "file %s too big\n", wine_dbgstr_w(file) );
216 CloseHandle( handle );
217 goto done;
219 size.QuadPart = low;
220 hr = IStream_SetSize( stm, size );
221 if (hr != S_OK) goto done;
223 while (ReadFile( handle, buffer, sizeof(buffer), &read, NULL ) && read)
225 hr = IStream_Write( stm, buffer, read, NULL );
226 if (hr != S_OK) break;
227 size.QuadPart -= read;
229 CloseHandle( handle );
230 if (size.QuadPart)
232 WINE_WARN( "failed to write stream contents\n" );
233 goto done;
235 IStorage_Commit( stg, 0 );
236 ret = 0;
238 done:
239 free( encname );
240 if (stm) IStream_Release( stm );
241 IStorage_Release( stg );
242 return ret;
245 static void show_usage( void )
247 WINE_MESSAGE(
248 "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n"
249 "Options:\n"
250 " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n"
251 " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n"
252 "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
253 "will be created with an empty database.\n"
257 int __cdecl wmain( int argc, WCHAR *argv[] )
259 if (argc < 3 || argv[1][0] != '-')
261 show_usage();
262 return 1;
265 switch (argv[1][1])
267 case 'i':
268 if (argc < 4) break;
269 return import_tables( argv[2], argv + 3 );
270 case 'a':
271 if (argc < 5) break;
272 return add_stream( argv[2], argv[3], argv[4] );
273 default:
274 WINE_WARN( "unknown option\n" );
275 break;
278 show_usage();
279 return 1;