usp10: Implement ScriptGetProperties.
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2005 Mike McCormack for CodeWeavers
5  *
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.
10  *
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.
15  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "wine/debug.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "objbase.h"
34 #include "objidl.h"
35 #include "msipriv.h"
36 #include "winnls.h"
37
38 #include "query.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
41
42 typedef struct tagMSICOLUMNINFO
43 {
44     LPCWSTR tablename;
45     UINT   number;
46     LPCWSTR colname;
47     UINT   type;
48     UINT   offset;
49 } MSICOLUMNINFO;
50
51 struct tagMSITABLE
52 {
53     USHORT **data;
54     UINT row_count;
55     struct list entry;
56     WCHAR name[1];
57 };
58
59 typedef struct tagMSITRANSFORM {
60     struct list entry;
61     IStorage *stg;
62 } MSITRANSFORM;
63
64 #define MAX_STREAM_NAME 0x1f
65
66 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
67        MSICOLUMNINFO **pcols, UINT *pcount );
68 static UINT get_tablecolumns( MSIDATABASE *db, 
69        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
70 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count );
71
72 static inline UINT bytes_per_column( const MSICOLUMNINFO *col )
73 {
74     if( col->type & MSITYPE_STRING )
75         return 2;
76     if( (col->type & 0xff) > 4 )
77         ERR("Invalid column size!\n");
78     return col->type & 0xff;
79 }
80
81 static int utf2mime(int x)
82 {
83     if( (x>='0') && (x<='9') )
84         return x-'0';
85     if( (x>='A') && (x<='Z') )
86         return x-'A'+10;
87     if( (x>='a') && (x<='z') )
88         return x-'a'+10+26;
89     if( x=='.' )
90         return 10+26+26;
91     if( x=='_' )
92         return 10+26+26+1;
93     return -1;
94 }
95
96 static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
97 {
98     DWORD count = MAX_STREAM_NAME;
99     DWORD ch, next;
100     LPWSTR out, p;
101
102     if( !bTable )
103         count = lstrlenW( in )+2;
104     out = msi_alloc( count*sizeof(WCHAR) );
105     p = out;
106
107     if( bTable )
108     {
109          *p++ = 0x4840;
110          count --;
111     }
112     while( count -- ) 
113     {
114         ch = *in++;
115         if( !ch )
116         {
117             *p = ch;
118             return out;
119         }
120         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
121         {
122             ch = utf2mime(ch) + 0x4800;
123             next = *in;
124             if( next && (next<0x80) )
125             {
126                 next = utf2mime(next);
127                 if( next >= 0  )
128                 {
129                      next += 0x3ffffc0;
130                      ch += (next<<6);
131                      in++;
132                 }
133             }
134         }
135         *p++ = ch;
136     }
137     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
138     msi_free( out );
139     return NULL;
140 }
141
142 static int mime2utf(int x)
143 {
144     if( x<10 )
145         return x + '0';
146     if( x<(10+26))
147         return x - 10 + 'A';
148     if( x<(10+26+26))
149         return x - 10 - 26 + 'a';
150     if( x == (10+26+26) )
151         return '.';
152     return '_';
153 }
154
155 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
156 {
157     WCHAR ch;
158     DWORD count = 0;
159
160     while ( (ch = *in++) )
161     {
162         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
163         {
164             if( ch >= 0x4800 )
165                 ch = mime2utf(ch-0x4800);
166             else
167             {
168                 ch -= 0x3800;
169                 *out++ = mime2utf(ch&0x3f);
170                 count++;
171                 ch = mime2utf((ch>>6)&0x3f);
172             }
173         }
174         *out++ = ch;
175         count++;
176     }
177     *out = 0;
178     return count;
179 }
180
181 void enum_stream_names( IStorage *stg )
182 {
183     IEnumSTATSTG *stgenum = NULL;
184     HRESULT r;
185     STATSTG stat;
186     ULONG n, count;
187     WCHAR name[0x40];
188
189     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
190     if( FAILED( r ) )
191         return;
192
193     n = 0;
194     while( 1 )
195     {
196         count = 0;
197         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
198         if( FAILED( r ) || !count )
199             break;
200         decode_streamname( stat.pwcsName, name );
201         TRACE("stream %2ld -> %s %s\n", n, 
202               debugstr_w(stat.pwcsName), debugstr_w(name) );
203         n++;
204     }
205
206     IEnumSTATSTG_Release( stgenum );
207 }
208
209 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
210                               USHORT **pdata, UINT *psz )
211 {
212     HRESULT r;
213     UINT ret = ERROR_FUNCTION_FAILED;
214     VOID *data;
215     ULONG sz, count;
216     IStream *stm = NULL;
217     STATSTG stat;
218     LPWSTR encname;
219
220     encname = encode_streamname(TRUE, stname);
221
222     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
223
224     r = IStorage_OpenStream(stg, encname, NULL, 
225             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
226     msi_free( encname );
227     if( FAILED( r ) )
228     {
229         WARN("open stream failed r = %08lx - empty table?\n",r);
230         return ret;
231     }
232
233     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
234     if( FAILED( r ) )
235     {
236         WARN("open stream failed r = %08lx!\n",r);
237         goto end;
238     }
239
240     if( stat.cbSize.QuadPart >> 32 )
241     {
242         WARN("Too big!\n");
243         goto end;
244     }
245         
246     sz = stat.cbSize.QuadPart;
247     data = msi_alloc( sz );
248     if( !data )
249     {
250         WARN("couldn't allocate memory r=%08lx!\n",r);
251         ret = ERROR_NOT_ENOUGH_MEMORY;
252         goto end;
253     }
254         
255     r = IStream_Read(stm, data, sz, &count );
256     if( FAILED( r ) || ( count != sz ) )
257     {
258         msi_free( data );
259         WARN("read stream failed r = %08lx!\n",r);
260         goto end;
261     }
262
263     *pdata = data;
264     *psz = sz;
265     ret = ERROR_SUCCESS;
266
267 end:
268     IStream_Release( stm );
269
270     return ret;
271 }
272
273 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
274 {
275     LPWSTR encname;
276     HRESULT r;
277
278     encname = encode_streamname(FALSE, stname);
279
280     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
281
282     r = IStorage_OpenStream(db->storage, encname, NULL, 
283             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
284     if( FAILED( r ) )
285     {
286         MSITRANSFORM *transform;
287
288         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
289         {
290             TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
291             r = IStorage_OpenStream( transform->stg, encname, NULL, 
292                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
293             if (SUCCEEDED(r))
294                 break;
295         }
296     }
297
298     msi_free( encname );
299
300     return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
301 }
302
303 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
304                               USHORT **pdata, UINT *psz )
305 {
306     HRESULT r;
307     UINT ret = ERROR_FUNCTION_FAILED;
308     VOID *data;
309     ULONG sz, count;
310     IStream *stm = NULL;
311     STATSTG stat;
312
313     r = db_get_raw_stream( db, stname, &stm );
314     if( r != ERROR_SUCCESS)
315         return ret;
316     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
317     if( FAILED( r ) )
318     {
319         WARN("open stream failed r = %08lx!\n",r);
320         goto end;
321     }
322
323     if( stat.cbSize.QuadPart >> 32 )
324     {
325         WARN("Too big!\n");
326         goto end;
327     }
328         
329     sz = stat.cbSize.QuadPart;
330     data = msi_alloc( sz );
331     if( !data )
332     {
333         WARN("couldn't allocate memory r=%08lx!\n",r);
334         ret = ERROR_NOT_ENOUGH_MEMORY;
335         goto end;
336     }
337         
338     r = IStream_Read(stm, data, sz, &count );
339     if( FAILED( r ) || ( count != sz ) )
340     {
341         msi_free( data );
342         WARN("read stream failed r = %08lx!\n",r);
343         goto end;
344     }
345
346     *pdata = data;
347     *psz = sz;
348     ret = ERROR_SUCCESS;
349
350 end:
351     IStream_Release( stm );
352
353     return ret;
354 }
355
356 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
357                                LPVOID data, UINT sz )
358 {
359     HRESULT r;
360     UINT ret = ERROR_FUNCTION_FAILED;
361     ULONG count;
362     IStream *stm = NULL;
363     ULARGE_INTEGER size;
364     LARGE_INTEGER pos;
365     LPWSTR encname;
366
367     encname = encode_streamname(TRUE, stname );
368     r = IStorage_OpenStream( stg, encname, NULL, 
369             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
370     if( FAILED(r) )
371     {
372         r = IStorage_CreateStream( stg, encname,
373                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
374     }
375     msi_free( encname );
376     if( FAILED( r ) )
377     {
378         WARN("open stream failed r = %08lx\n",r);
379         return ret;
380     }
381
382     size.QuadPart = sz;
383     r = IStream_SetSize( stm, size );
384     if( FAILED( r ) )
385     {
386         WARN("Failed to SetSize\n");
387         goto end;
388     }
389
390     pos.QuadPart = 0;
391     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
392     if( FAILED( r ) )
393     {
394         WARN("Failed to Seek\n");
395         goto end;
396     }
397
398     r = IStream_Write(stm, data, sz, &count );
399     if( FAILED( r ) || ( count != sz ) )
400     {
401         WARN("Failed to Write\n");
402         goto end;
403     }
404
405     ret = ERROR_SUCCESS;
406
407 end:
408     IStream_Release( stm );
409
410     return ret;
411 }
412
413 static void free_table( MSITABLE *table )
414 {
415     int i;
416     for( i=0; i<table->row_count; i++ )
417         msi_free( table->data[i] );
418     msi_free( table->data );
419     msi_free( table );
420 }
421
422 static UINT msi_table_get_row_size( const MSICOLUMNINFO *cols, UINT count )
423 {
424     const MSICOLUMNINFO *last_col = &cols[count-1];
425     if (!count)
426         return 0;
427     return last_col->offset + bytes_per_column( last_col );
428 }
429
430 /* add this table to the list of cached tables in the database */
431 static MSITABLE *read_table_from_storage( IStorage *stg, LPCWSTR name,
432                                     const MSICOLUMNINFO *cols, UINT num_cols )
433 {
434     MSITABLE *t;
435     USHORT *rawdata = NULL;
436     UINT rawsize = 0, i, j, row_size = 0;
437
438     TRACE("%s\n",debugstr_w(name));
439
440     /* nonexistent tables should be interpreted as empty tables */
441     t = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
442     if( !t )
443         return t;
444
445     row_size = msi_table_get_row_size( cols, num_cols );
446
447     t->row_count = 0;
448     t->data = NULL;
449     lstrcpyW( t->name, name );
450
451     /* if we can't read the table, just assume that it's empty */
452     read_stream_data( stg, name, &rawdata, &rawsize );
453     if( !rawdata )
454         return t;
455
456     TRACE("Read %d bytes\n", rawsize );
457
458     if( rawsize % row_size )
459     {
460         WARN("Table size is invalid %d/%d\n", rawsize, row_size );
461         goto err;
462     }
463
464     t->row_count = rawsize / row_size;
465     t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
466     if( !t->data )
467         goto err;
468
469     /* transpose all the data */
470     TRACE("Transposing data from %d columns\n", t->row_count );
471     for( i=0; i<t->row_count; i++ )
472     {
473         t->data[i] = msi_alloc( row_size );
474         if( !t->data[i] )
475             goto err;
476
477         for( j=0; j<num_cols; j++ )
478         {
479             UINT ofs = cols[j].offset/2;
480             UINT n = bytes_per_column( &cols[j] );
481
482             switch( n )
483             {
484             case 2:
485                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
486                 break;
487             case 4:
488                 t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
489                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
490                 break;
491             default:
492                 ERR("oops - unknown column width %d\n", n);
493                 goto err;
494             }
495         }
496     }
497
498     msi_free( rawdata );
499     return t;
500 err:
501     msi_free( rawdata );
502     free_table( t );
503     return NULL;
504 }
505
506 void free_cached_tables( MSIDATABASE *db )
507 {
508     while( !list_empty( &db->tables ) )
509     {
510         MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
511
512         list_remove( &t->entry );
513         free_table( t );
514     }
515 }
516
517 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
518 {
519     MSITABLE *t;
520
521     LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
522         if( !lstrcmpW( name, t->name ) )
523             return t;
524
525     return NULL;
526 }
527
528 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
529 {
530     UINT r, column_count = 0;
531     MSICOLUMNINFO *columns;
532
533     /* get the number of columns in this table */
534     column_count = 0;
535     r = get_tablecolumns( db, name, NULL, &column_count );
536     if( r != ERROR_SUCCESS )
537         return r;
538
539     /* if there's no columns, there's no table */
540     if( column_count == 0 )
541         return ERROR_INVALID_PARAMETER;
542
543     TRACE("Table %s found\n", debugstr_w(name) );
544
545     columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) );
546     if( !columns )
547         return ERROR_FUNCTION_FAILED;
548
549     r = get_tablecolumns( db, name, columns, &column_count );
550     if( r != ERROR_SUCCESS )
551     {
552         msi_free( columns );
553         return ERROR_FUNCTION_FAILED;
554     }
555
556     *pcols = columns;
557     *pcount = column_count;
558
559     return r;
560 }
561
562 static MSITABLE *get_table( MSIDATABASE *db, LPCWSTR name,
563                             const MSICOLUMNINFO *cols, UINT num_cols )
564 {
565     MSITABLE *table;
566
567     /* first, see if the table is cached */
568     table = find_cached_table( db, name );
569     if( table )
570         return table;
571
572     table = read_table_from_storage( db->storage, name, cols, num_cols );
573     if( table )
574         list_add_head( &db->tables, &table->entry );
575
576     return table;
577 }
578
579 static UINT save_table( MSIDATABASE *db, MSITABLE *t )
580 {
581     USHORT *rawdata = NULL, *p;
582     UINT rawsize, r, i, j, row_size, num_cols = 0;
583     MSICOLUMNINFO *cols = NULL;
584
585     TRACE("Saving %s\n", debugstr_w( t->name ) );
586
587     r = table_get_column_info( db, t->name, &cols, &num_cols );
588     if( r != ERROR_SUCCESS )
589         return r;
590     
591     row_size = msi_table_get_row_size( cols, num_cols );
592
593     rawsize = t->row_count * row_size;
594     rawdata = msi_alloc_zero( rawsize );
595     if( !rawdata )
596     {
597         r = ERROR_NOT_ENOUGH_MEMORY;
598         goto err;
599     }
600
601     p = rawdata;
602     for( i=0; i<num_cols; i++ )
603     {
604         for( j=0; j<t->row_count; j++ )
605         {
606             UINT offset = cols[i].offset;
607
608             *p++ = t->data[j][offset/2];
609             if( 4 == bytes_per_column( &cols[i] ) )
610                 *p++ = t->data[j][offset/2+1];
611         }
612     }
613
614     TRACE("writing %d bytes\n", rawsize);
615     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
616
617 err:
618     msi_free_colinfo( cols, num_cols );
619     msi_free( cols );
620     msi_free( rawdata );
621
622     return r;
623 }
624
625 HRESULT init_string_table( IStorage *stg )
626 {
627     HRESULT r;
628     static const WCHAR szStringData[] = {
629         '_','S','t','r','i','n','g','D','a','t','a',0 };
630     static const WCHAR szStringPool[] = {
631         '_','S','t','r','i','n','g','P','o','o','l',0 };
632     USHORT zero[2] = { 0, 0 };
633     ULONG count = 0;
634     IStream *stm = NULL;
635     LPWSTR encname;
636
637     encname = encode_streamname(TRUE, szStringPool );
638
639     /* create the StringPool stream... add the zero string to it*/
640     r = IStorage_CreateStream( stg, encname,
641             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
642     msi_free( encname );
643     if( r ) 
644     {
645         TRACE("Failed\n");
646         return r;
647     }
648
649     r = IStream_Write(stm, zero, sizeof zero, &count );
650     IStream_Release( stm );
651
652     if( FAILED( r ) || ( count != sizeof zero ) )
653     {
654         TRACE("Failed\n");
655         return E_FAIL;
656     }
657
658     /* create the StringData stream... make it zero length */
659     encname = encode_streamname(TRUE, szStringData );
660     r = IStorage_CreateStream( stg, encname,
661             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
662     msi_free( encname );
663     if( r ) 
664     {
665         TRACE("Failed\n");
666         return E_FAIL;
667     }
668     IStream_Release( stm );
669
670     return r;
671 }
672
673 string_table *load_string_table( IStorage *stg )
674 {
675     string_table *st = NULL;
676     CHAR *data;
677     USHORT *pool;
678     UINT r, datasize = 0, poolsize = 0, codepage;
679     DWORD i, count, offset, len, n;
680     static const WCHAR szStringData[] = {
681         '_','S','t','r','i','n','g','D','a','t','a',0 };
682     static const WCHAR szStringPool[] = {
683         '_','S','t','r','i','n','g','P','o','o','l',0 };
684
685     r = read_stream_data( stg, szStringPool, &pool, &poolsize );
686     if( r != ERROR_SUCCESS)
687         goto end;
688     r = read_stream_data( stg, szStringData, (USHORT**)&data, &datasize );
689     if( r != ERROR_SUCCESS)
690         goto end;
691
692     count = poolsize/4;
693     if( poolsize > 4 )
694         codepage = pool[0] | ( pool[1] << 16 );
695     else
696         codepage = CP_ACP;
697     st = msi_init_stringtable( count, codepage );
698
699     offset = 0;
700     n = 1;
701     for( i=1; i<count; i++ )
702     {
703         len = pool[i*2];
704
705         /*
706          * If a string is over 64k, the previous string entry is made null
707          * and its the high word of the length is inserted in the null string's
708          * reference count field.
709          */
710         if( pool[i*2-2] == 0 )
711             len += pool[i*2-1] * 0x10000;
712
713         if( (offset + len) > datasize )
714         {
715             ERR("string table corrupt?\n");
716             break;
717         }
718
719         /* don't add the high word of a string's length as a string */
720         if ( len || !pool[i*2+1] )
721         {
722             r = msi_addstring( st, n, data+offset, len, pool[i*2+1] );
723             if( r != n )
724                 ERR("Failed to add string %ld\n", n );
725             n++;
726         }
727
728         offset += len;
729     }
730
731     if ( datasize != offset )
732         ERR("string table load failed! (%08x != %08lx)\n", datasize, offset );
733
734     TRACE("Loaded %ld strings\n", count);
735
736 end:
737     msi_free( pool );
738     msi_free( data );
739
740     return st;
741 }
742
743 static UINT save_string_table( MSIDATABASE *db )
744 {
745     UINT i, count, datasize, poolsize, sz, used, r, codepage;
746     UINT ret = ERROR_FUNCTION_FAILED;
747     static const WCHAR szStringData[] = {
748         '_','S','t','r','i','n','g','D','a','t','a',0 };
749     static const WCHAR szStringPool[] = {
750         '_','S','t','r','i','n','g','P','o','o','l',0 };
751     CHAR *data = NULL;
752     USHORT *pool = NULL;
753
754     TRACE("\n");
755
756     /* construct the new table in memory first */
757     datasize = msi_string_totalsize( db->strings, &count );
758     poolsize = count*2*sizeof(USHORT);
759
760     pool = msi_alloc( poolsize );
761     if( ! pool )
762     {
763         WARN("Failed to alloc pool %d bytes\n", poolsize );
764         goto err;
765     }
766     data = msi_alloc( datasize );
767     if( ! data )
768     {
769         WARN("Failed to alloc data %d bytes\n", poolsize );
770         goto err;
771     }
772
773     used = 0;
774     codepage = msi_string_get_codepage( db->strings );
775     pool[0]=codepage&0xffff;
776     pool[1]=(codepage>>16);
777     for( i=1; i<count; i++ )
778     {
779         sz = datasize - used;
780         r = msi_id2stringA( db->strings, i, data+used, &sz );
781         if( r != ERROR_SUCCESS )
782         {
783             ERR("failed to fetch string\n");
784             sz = 0;
785         }
786         if( sz && (sz < (datasize - used ) ) )
787             sz--;
788         TRACE("adding %u bytes %s\n", sz, debugstr_a(data+used) );
789         pool[ i*2 ] = sz;
790         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
791         used += sz;
792         if( used > datasize  )
793         {
794             ERR("oops overran %d >= %d\n", used, datasize);
795             goto err;
796         }
797     }
798
799     if( used != datasize )
800     {
801         ERR("oops used %d != datasize %d\n", used, datasize);
802         goto err;
803     }
804
805     /* write the streams */
806     r = write_stream_data( db->storage, szStringData, data, datasize );
807     TRACE("Wrote StringData r=%08x\n", r);
808     if( r )
809         goto err;
810     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
811     TRACE("Wrote StringPool r=%08x\n", r);
812     if( r )
813         goto err;
814
815     ret = ERROR_SUCCESS;
816
817 err:
818     msi_free( data );
819     msi_free( pool );
820
821     return ret;
822 }
823
824 /* information for default tables */
825 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
826 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
827 static const WCHAR szName[]    = { 'N','a','m','e',0 };
828 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
829 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
830 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
831 static const WCHAR szType[]    = { 'T','y','p','e',0 };
832
833 static const MSICOLUMNINFO _Columns_cols[4] = {
834     { szColumns, 1, szTable,  MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
835     { szColumns, 2, szNumber, MSITYPE_VALID | 2,                   2 },
836     { szColumns, 3, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 4 },
837     { szColumns, 4, szType,   MSITYPE_VALID | 2,                   6 },
838 };
839 static const MSICOLUMNINFO _Tables_cols[1] = {
840     { szTables,  1, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
841 };
842
843 static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
844 {
845     const MSICOLUMNINFO *p;
846     DWORD i, n;
847
848     TRACE("%s\n", debugstr_w(name));
849
850     if (!lstrcmpW( name, szTables ))
851     {
852         p = _Tables_cols;
853         n = 1;
854     }
855     else if (!lstrcmpW( name, szColumns ))
856     {
857         p = _Columns_cols;
858         n = 4;
859     }
860     else
861         return ERROR_FUNCTION_FAILED;
862
863     /* duplicate the string data so we can free it in msi_free_colinfo */
864     for (i=0; i<n; i++)
865     {
866         if (colinfo && (i < *sz) )
867         {
868             memcpy( &colinfo[i], &p[i], sizeof(MSICOLUMNINFO) );
869             colinfo[i].tablename = strdupW( p[i].tablename );
870             colinfo[i].colname = strdupW( p[i].colname );
871         }
872         if( colinfo && (i >= *sz) )
873             break;
874     }
875     *sz = n;
876     return ERROR_SUCCESS;
877 }
878
879 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
880 {
881     UINT i;
882
883     for( i=0; i<count; i++ )
884     {
885         msi_free( (LPWSTR) colinfo[i].tablename );
886         msi_free( (LPWSTR) colinfo[i].colname );
887     }
888 }
889
890 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
891 {
892     UINT sz=0, r;
893     LPWSTR str;
894
895     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
896     if( r != ERROR_SUCCESS )
897         return NULL;
898     str = msi_alloc( sz*sizeof (WCHAR) );
899     if( !str )
900         return str;
901     r = msi_id2stringW( db->strings, stringid, str, &sz );
902     if( r == ERROR_SUCCESS )
903         return str;
904     msi_free( str );
905     return NULL;
906 }
907
908 static UINT get_tablecolumns( MSIDATABASE *db, 
909        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
910 {
911     UINT r, i, n=0, table_id, count, maxcount = *sz;
912     MSITABLE *table = NULL;
913
914     /* first check if there is a default table with that name */
915     r = get_defaulttablecolumns( szTableName, colinfo, sz );
916     if( ( r == ERROR_SUCCESS ) && *sz )
917         return r;
918
919     table = get_table( db, szColumns, _Columns_cols, 4 );
920     if( !table )
921     {
922         ERR("couldn't load _Columns table\n");
923         return ERROR_FUNCTION_FAILED;
924     }
925
926     /* convert table and column names to IDs from the string table */
927     r = msi_string2idW( db->strings, szTableName, &table_id );
928     if( r != ERROR_SUCCESS )
929     {
930         WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
931         return r;
932     }
933
934     TRACE("Table id is %d\n", table_id);
935
936     count = table->row_count;
937     for( i=0; i<count; i++ )
938     {
939         if( table->data[ i ][ 0 ] != table_id )
940             continue;
941         if( colinfo )
942         {
943             UINT id = table->data[ i ] [ 2 ];
944             colinfo[n].tablename = MSI_makestring( db, table_id );
945             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
946             colinfo[n].colname = MSI_makestring( db, id );
947             colinfo[n].type = table->data[ i ] [ 3 ] ^ 0x8000;
948             /* this assumes that columns are in order in the table */
949             if( n )
950                 colinfo[n].offset = colinfo[n-1].offset
951                                   + bytes_per_column( &colinfo[n-1] );
952             else
953                 colinfo[n].offset = 0;
954             TRACE("table %s column %d is [%s] (%d) with type %08x "
955                   "offset %d at row %d\n", debugstr_w(szTableName),
956                    colinfo[n].number, debugstr_w(colinfo[n].colname),
957                    id, colinfo[n].type, colinfo[n].offset, i);
958             if( n != (colinfo[n].number-1) )
959             {
960                 ERR("oops. data in the _Columns table isn't in the right "
961                     "order for table %s\n", debugstr_w(szTableName));
962                 return ERROR_FUNCTION_FAILED;
963             }
964         }
965         n++;
966         if( colinfo && ( n >= maxcount ) )
967             break;
968     }
969     *sz = n;
970
971     return ERROR_SUCCESS;
972 }
973
974 /* try to find the table name in the _Tables table */
975 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
976 {
977     UINT r, table_id = 0, i, count;
978     MSITABLE *table = NULL;
979
980     if( !lstrcmpW( name, szTables ) )
981         return TRUE;
982     if( !lstrcmpW( name, szColumns ) )
983         return TRUE;
984
985     r = msi_string2idW( db->strings, name, &table_id );
986     if( r != ERROR_SUCCESS )
987     {
988         TRACE("Couldn't find id for %s\n", debugstr_w(name));
989         return FALSE;
990     }
991
992     table = get_table( db, szTables, _Tables_cols, 1 );
993     if( !table )
994     {
995         TRACE("table %s not available\n", debugstr_w(szTables));
996         return FALSE;
997     }
998
999     /* count = table->size/2; */
1000     count = table->row_count;
1001     for( i=0; i<count; i++ )
1002         if( table->data[ i ][ 0 ] == table_id )
1003             break;
1004
1005     if (i!=count)
1006         return TRUE;
1007
1008     TRACE("Searched %d tables, but %d was not found\n", count, table_id );
1009
1010     return FALSE;
1011 }
1012
1013 /* below is the query interface to a table */
1014
1015 typedef struct tagMSITABLEVIEW
1016 {
1017     MSIVIEW        view;
1018     MSIDATABASE   *db;
1019     MSITABLE      *table;
1020     MSICOLUMNINFO *columns;
1021     UINT           num_cols;
1022     UINT           row_size;
1023     WCHAR          name[1];
1024 } MSITABLEVIEW;
1025
1026 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1027 {
1028     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1029     UINT offset, num_rows, n;
1030
1031     if( !tv->table )
1032         return ERROR_INVALID_PARAMETER;
1033
1034     if( (col==0) || (col>tv->num_cols) )
1035         return ERROR_INVALID_PARAMETER;
1036
1037     /* how many rows are there ? */
1038     num_rows = tv->table->row_count;
1039     if( row >= num_rows )
1040         return ERROR_NO_MORE_ITEMS;
1041
1042     if( tv->columns[col-1].offset >= tv->row_size )
1043     {
1044         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1045         ERR("%p %p\n", tv, tv->columns );
1046         return ERROR_FUNCTION_FAILED;
1047     }
1048
1049     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1050     n = bytes_per_column( &tv->columns[col-1] );
1051     switch( n )
1052     {
1053     case 4:
1054         offset = tv->columns[col-1].offset/2;
1055         *val = tv->table->data[row][offset] + 
1056                (tv->table->data[row][offset + 1] << 16);
1057         break;
1058     case 2:
1059         offset = tv->columns[col-1].offset/2;
1060         *val = tv->table->data[row][offset];
1061         break;
1062     default:
1063         ERR("oops! what is %d bytes per column?\n", n );
1064         return ERROR_FUNCTION_FAILED;
1065     }
1066
1067     /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1068
1069     return ERROR_SUCCESS;
1070 }
1071
1072 /*
1073  * We need a special case for streams, as we need to reference column with
1074  * the name of the stream in the same table, and the table name
1075  * which may not be available at higher levels of the query
1076  */
1077 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1078 {
1079     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1080     UINT ival = 0, refcol = 0, r;
1081     LPWSTR sval;
1082     LPWSTR full_name;
1083     DWORD len;
1084     static const WCHAR szDot[] = { '.', 0 };
1085
1086     if( !view->ops->fetch_int )
1087         return ERROR_INVALID_PARAMETER;
1088
1089     /*
1090      * The column marked with the type stream data seems to have a single number
1091      * which references the column containing the name of the stream data
1092      *
1093      * Fetch the column to reference first.
1094      */
1095     r = view->ops->fetch_int( view, row, col, &ival );
1096     if( r != ERROR_SUCCESS )
1097         return r;
1098
1099     /* now get the column with the name of the stream */
1100     r = view->ops->fetch_int( view, row, ival, &refcol );
1101     if( r != ERROR_SUCCESS )
1102         return r;
1103
1104     /* lookup the string value from the string table */
1105     sval = MSI_makestring( tv->db, refcol );
1106     if( !sval )
1107         return ERROR_INVALID_PARAMETER;
1108
1109     len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
1110     full_name = msi_alloc( len*sizeof(WCHAR) );
1111     lstrcpyW( full_name, tv->name );
1112     lstrcatW( full_name, szDot );
1113     lstrcatW( full_name, sval );
1114
1115     r = db_get_raw_stream( tv->db, full_name, stm );
1116     if( r )
1117         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1118     msi_free( full_name );
1119     msi_free( sval );
1120
1121     return r;
1122 }
1123
1124 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1125 {
1126     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1127     UINT offset, n;
1128
1129     if( !tv->table )
1130         return ERROR_INVALID_PARAMETER;
1131
1132     if( (col==0) || (col>tv->num_cols) )
1133         return ERROR_INVALID_PARAMETER;
1134
1135     if( tv->columns[col-1].offset >= tv->row_size )
1136     {
1137         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1138         ERR("%p %p\n", tv, tv->columns );
1139         return ERROR_FUNCTION_FAILED;
1140     }
1141
1142     n = bytes_per_column( &tv->columns[col-1] );
1143     switch( n )
1144     {
1145     case 4:
1146         offset = tv->columns[col-1].offset/2;
1147         tv->table->data[row][offset]     = val & 0xffff;
1148         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1149         break;
1150     case 2:
1151         offset = tv->columns[col-1].offset/2;
1152         tv->table->data[row][offset] = val;
1153         break;
1154     default:
1155         ERR("oops! what is %d bytes per column?\n", n );
1156         return ERROR_FUNCTION_FAILED;
1157     }
1158     return ERROR_SUCCESS;
1159 }
1160
1161 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num )
1162 {
1163     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1164     USHORT **p, *row;
1165     UINT sz;
1166
1167     TRACE("%p\n", view);
1168
1169     if( !tv->table )
1170         return ERROR_INVALID_PARAMETER;
1171
1172     row = msi_alloc_zero( tv->row_size );
1173     if( !row )
1174         return ERROR_NOT_ENOUGH_MEMORY;
1175
1176     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1177     if( tv->table->data )
1178         p = msi_realloc( tv->table->data, sz );
1179     else
1180         p = msi_alloc( sz );
1181     if( !p )
1182     {
1183         msi_free( row );
1184         return ERROR_NOT_ENOUGH_MEMORY;
1185     }
1186
1187     tv->table->data = p;
1188     tv->table->data[tv->table->row_count] = row;
1189     *num = tv->table->row_count;
1190     tv->table->row_count++;
1191
1192     return ERROR_SUCCESS;
1193 }
1194
1195 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1196 {
1197     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1198
1199     TRACE("%p %p\n", tv, record);
1200
1201     TRACE("There are %d columns\n", tv->num_cols );
1202     tv->table = get_table( tv->db, tv->name, tv->columns, tv->num_cols );
1203     if( !tv->table )
1204         return ERROR_FUNCTION_FAILED;
1205
1206     return ERROR_SUCCESS;
1207 }
1208
1209 static UINT TABLE_close( struct tagMSIVIEW *view )
1210 {
1211     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1212
1213     TRACE("%p\n", view );
1214
1215     if( !tv->table )
1216         return ERROR_FUNCTION_FAILED;
1217
1218     tv->table = NULL;
1219     
1220     return ERROR_SUCCESS;
1221 }
1222
1223 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1224 {
1225     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1226
1227     TRACE("%p %p %p\n", view, rows, cols );
1228
1229     if( cols )
1230         *cols = tv->num_cols;
1231     if( rows )
1232     {
1233         if( !tv->table )
1234             return ERROR_INVALID_PARAMETER;
1235         *rows = tv->table->row_count;
1236     }
1237
1238     return ERROR_SUCCESS;
1239 }
1240
1241 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1242                 UINT n, LPWSTR *name, UINT *type )
1243 {
1244     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1245
1246     TRACE("%p %d %p %p\n", tv, n, name, type );
1247
1248     if( ( n == 0 ) || ( n > tv->num_cols ) )
1249         return ERROR_INVALID_PARAMETER;
1250
1251     if( name )
1252     {
1253         *name = strdupW( tv->columns[n-1].colname );
1254         if( !*name )
1255             return ERROR_FUNCTION_FAILED;
1256     }
1257     if( type )
1258         *type = tv->columns[n-1].type;
1259
1260     return ERROR_SUCCESS;
1261 }
1262
1263 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row );
1264
1265 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
1266 {
1267     UINT r, row;
1268
1269     r = msi_table_find_row( tv, rec, &row );
1270     if (r != ERROR_SUCCESS)
1271         return ERROR_SUCCESS;
1272     return ERROR_INVALID_DATA;
1273 }
1274
1275 static UINT msi_table_modify_row( MSITABLEVIEW *tv, MSIRECORD *rec,
1276                                   UINT row, UINT mask )
1277 {
1278     UINT i, val, r = ERROR_SUCCESS;
1279
1280     TRACE("%p %p %u %08x\n", tv, rec, row, mask );
1281
1282     for( i = 0; i < tv->num_cols; i++ )
1283     {
1284         /* set keys or values specified in the mask */
1285         if( (~tv->columns[i].type & MSITYPE_KEY) && (~mask & (1<<i)) )
1286             continue;
1287
1288         if( (tv->columns[i].type & MSITYPE_STRING) &&
1289             ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1290         {
1291             const WCHAR *str = MSI_RecordGetString( rec, i+1 );
1292             val = msi_addstringW( tv->db->strings, 0, str, -1, 1 );
1293         }
1294         else
1295         {
1296             val = MSI_RecordGetInteger( rec, i+1 );
1297             if ( 2 == bytes_per_column( &tv->columns[i] ) )
1298                 val ^= 0x8000;
1299         }
1300         r = TABLE_set_int( &tv->view, row, i+1, val );
1301         if( r )
1302             break;
1303     }
1304
1305     return r;
1306 }
1307
1308 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
1309 {
1310     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1311     UINT r, row = -1;
1312
1313     TRACE("%p %p\n", tv, rec );
1314
1315     r = table_create_new_row( view, &row );
1316     TRACE("insert_row returned %08x\n", r);
1317     if( r != ERROR_SUCCESS )
1318         return r;
1319
1320     return msi_table_modify_row( tv, rec, row, ~0 );
1321 }
1322
1323 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1324                 MSIRECORD *rec)
1325 {
1326     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1327     UINT r;
1328
1329     TRACE("%p %d %p\n", view, eModifyMode, rec );
1330
1331     if (!tv->table)
1332     {
1333         r = TABLE_execute( view, NULL );
1334         if( r )
1335             return r;
1336     }
1337
1338     switch (eModifyMode)
1339     {
1340     case MSIMODIFY_VALIDATE_NEW:
1341         r = table_validate_new( tv, rec );
1342         break;
1343
1344     case MSIMODIFY_INSERT_TEMPORARY:
1345         r = table_validate_new( tv, rec );
1346         if (r != ERROR_SUCCESS)
1347             break;
1348         r = TABLE_insert_row( view, rec );
1349         break;
1350
1351     case MSIMODIFY_REFRESH:
1352     case MSIMODIFY_INSERT:
1353     case MSIMODIFY_UPDATE:
1354     case MSIMODIFY_ASSIGN:
1355     case MSIMODIFY_REPLACE:
1356     case MSIMODIFY_MERGE:
1357     case MSIMODIFY_DELETE:
1358     case MSIMODIFY_VALIDATE:
1359     case MSIMODIFY_VALIDATE_FIELD:
1360     case MSIMODIFY_VALIDATE_DELETE:
1361         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1362         r = ERROR_CALL_NOT_IMPLEMENTED;
1363         break;
1364
1365     default:
1366         r = ERROR_INVALID_DATA;
1367     }
1368
1369     return r;
1370 }
1371
1372 static UINT TABLE_delete( struct tagMSIVIEW *view )
1373 {
1374     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1375
1376     TRACE("%p\n", view );
1377
1378     tv->table = NULL;
1379
1380     if( tv->columns )
1381     {
1382         msi_free_colinfo( tv->columns, tv->num_cols );
1383         msi_free( tv->columns );
1384     }
1385     tv->columns = NULL;
1386
1387     msi_free( tv );
1388
1389     return ERROR_SUCCESS;
1390 }
1391
1392
1393 MSIVIEWOPS table_ops =
1394 {
1395     TABLE_fetch_int,
1396     TABLE_fetch_stream,
1397     TABLE_set_int,
1398     TABLE_insert_row,
1399     TABLE_execute,
1400     TABLE_close,
1401     TABLE_get_dimensions,
1402     TABLE_get_column_info,
1403     TABLE_modify,
1404     TABLE_delete
1405 };
1406
1407 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1408 {
1409     MSITABLEVIEW *tv ;
1410     UINT r, sz, column_count;
1411     MSICOLUMNINFO *columns;
1412
1413     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1414
1415     /* get the number of columns in this table */
1416     column_count = 0;
1417     r = get_tablecolumns( db, name, NULL, &column_count );
1418     if( r != ERROR_SUCCESS )
1419         return r;
1420
1421     /* if there's no columns, there's no table */
1422     if( column_count == 0 )
1423         return ERROR_INVALID_PARAMETER;
1424
1425     TRACE("Table found\n");
1426
1427     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1428     tv = msi_alloc_zero( sz );
1429     if( !tv )
1430         return ERROR_FUNCTION_FAILED;
1431     
1432     columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO));
1433     if( !columns )
1434     {
1435         msi_free( tv );
1436         return ERROR_FUNCTION_FAILED;
1437     }
1438
1439     r = get_tablecolumns( db, name, columns, &column_count );
1440     if( r != ERROR_SUCCESS )
1441     {
1442         msi_free( columns );
1443         msi_free( tv );
1444         return ERROR_FUNCTION_FAILED;
1445     }
1446
1447     TRACE("Table has %d columns\n", column_count);
1448
1449     /* fill the structure */
1450     tv->view.ops = &table_ops;
1451     tv->db = db;
1452     tv->columns = columns;
1453     tv->num_cols = column_count;
1454     tv->table = NULL;
1455     tv->row_size = msi_table_get_row_size( columns, column_count );
1456
1457     TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
1458
1459     *view = (MSIVIEW*) tv;
1460     lstrcpyW( tv->name, name );
1461
1462     return ERROR_SUCCESS;
1463 }
1464
1465 UINT MSI_CommitTables( MSIDATABASE *db )
1466 {
1467     UINT r;
1468     MSITABLE *table = NULL;
1469
1470     TRACE("%p\n",db);
1471
1472     r = save_string_table( db );
1473     if( r != ERROR_SUCCESS )
1474     {
1475         WARN("failed to save string table r=%08x\n",r);
1476         return r;
1477     }
1478
1479     LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
1480     {
1481         r = save_table( db, table );
1482         if( r != ERROR_SUCCESS )
1483         {
1484             WARN("failed to save table %s (r=%08x)\n",
1485                   debugstr_w(table->name), r);
1486             return r;
1487         }
1488     }
1489
1490     /* force everything to reload next time */
1491     free_cached_tables( db );
1492
1493     return ERROR_SUCCESS;
1494 }
1495
1496 static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata )
1497 {
1498     UINT i, val, ofs = 0;
1499     USHORT mask = *rawdata++;
1500     MSICOLUMNINFO *columns = tv->columns;
1501     MSIRECORD *rec;
1502     const int debug_transform = 0;
1503
1504     rec = MSI_CreateRecord( tv->num_cols );
1505     if( !rec )
1506         return rec;
1507
1508     if( debug_transform ) MESSAGE("row -> ");
1509     for( i=0; i<tv->num_cols; i++ )
1510     {
1511         UINT n = bytes_per_column( &columns[i] );
1512
1513         if ( (mask&1) && (i>=(mask>>8)) )
1514             break;
1515         /* all keys must be present */
1516         if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
1517             continue;
1518
1519         switch( n )
1520         {
1521         case 2:
1522             val = rawdata[ofs];
1523             if( (columns[i].type & MSITYPE_STRING) &&
1524                 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1525             {
1526                 LPCWSTR sval = msi_string_lookup_id( st, val );
1527                 MSI_RecordSetStringW( rec, i+1, sval );
1528                 if( debug_transform ) MESSAGE("[%s]", debugstr_w(sval));
1529             }
1530             else
1531             {
1532                 val ^= 0x8000;
1533                 MSI_RecordSetInteger( rec, i+1, val );
1534                 if( debug_transform) MESSAGE("[0x%04x]", val );
1535             }
1536             break;
1537         case 4:
1538             val = rawdata[ofs] + (rawdata[ofs + 1]<<16);
1539             /* val ^= 0x80000000; */
1540             MSI_RecordSetInteger( rec, i+1, val );
1541             if( debug_transform ) MESSAGE("[0x%08x]", val );
1542             break;
1543         default:
1544             ERR("oops - unknown column width %d\n", n);
1545             break;
1546         }
1547         ofs += n/2;
1548     }
1549     if( debug_transform) MESSAGE("\n");
1550     return rec;
1551 }
1552
1553 static void dump_record( MSIRECORD *rec )
1554 {
1555     UINT i, n;
1556
1557     MESSAGE("row -> ");
1558     n = MSI_RecordGetFieldCount( rec );
1559     for( i=1; i<=n; i++ )
1560     {
1561         LPCWSTR sval = MSI_RecordGetString( rec, i );
1562
1563         if( MSI_RecordIsNull( rec, i ) )
1564             MESSAGE("[]");
1565         else if( (sval = MSI_RecordGetString( rec, i )) )
1566             MESSAGE("[%s]", debugstr_w(sval));
1567         else
1568             MESSAGE("[0x%08x]", MSI_RecordGetInteger( rec, i ) );
1569     }
1570     MESSAGE("\n");
1571 }
1572
1573 static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize )
1574 {
1575     LPCWSTR sval;
1576     UINT i;
1577
1578     for( i=0; i<(rawsize/2); i++ )
1579     {
1580         sval = msi_string_lookup_id( st, rawdata[i] );
1581         if( !sval ) sval = (WCHAR[]) {0};
1582         MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
1583     }
1584 }
1585
1586 static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec )
1587 {
1588     LPCWSTR str;
1589     UINT i, r, *data;
1590
1591     data = msi_alloc( tv->num_cols *sizeof (UINT) );
1592     for( i=0; i<tv->num_cols; i++ )
1593     {
1594         data[i] = 0;
1595
1596         if ( ~tv->columns[i].type & MSITYPE_KEY )
1597             continue;
1598
1599         /* turn the transform column value into a row value */
1600         if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
1601              ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1602         {
1603             str = MSI_RecordGetString( rec, i+1 );
1604             r = msi_string2idW( tv->db->strings, str, &data[i] );
1605
1606             /* if there's no matching string in the string table,
1607                these keys can't match any record, so fail now. */
1608             if( ERROR_SUCCESS != r )
1609             {
1610                 msi_free( data );
1611                 return NULL;
1612             }
1613         }
1614         else
1615             data[i] = MSI_RecordGetInteger( rec, i+1 );
1616     }
1617     return data;
1618 }
1619
1620 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data )
1621 {
1622     UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
1623
1624     for( i=0; i<tv->num_cols; i++ )
1625     {
1626         if ( ~tv->columns[i].type & MSITYPE_KEY )
1627             continue;
1628
1629         /* turn the transform column value into a row value */
1630         r = TABLE_fetch_int( &tv->view, row, i+1, &x );
1631         if ( r != ERROR_SUCCESS )
1632         {
1633             ERR("TABLE_fetch_int shouldn't fail here\n");
1634             break;
1635         }
1636
1637         /* if this key matches, move to the next column */
1638         if ( x != data[i] )
1639         {
1640             ret = ERROR_FUNCTION_FAILED;
1641             break;
1642         }
1643
1644         ret = ERROR_SUCCESS;
1645     }
1646
1647     return ret;
1648 }
1649
1650 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
1651 {
1652     UINT i, r = ERROR_FUNCTION_FAILED, *data;
1653
1654     data = msi_record_to_row( tv, rec );
1655     if( !data )
1656         return r;
1657     for( i=0; i<tv->table->row_count; i++ )
1658     {
1659         r = msi_row_matches( tv, i, data );
1660         if( r == ERROR_SUCCESS )
1661         {
1662             *row = i;
1663             break;
1664         }
1665     }
1666     msi_free( data );
1667     return r;
1668 }
1669
1670 static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row )
1671 {
1672     UINT i;
1673     for( i=1; i<=tv->num_cols; i++ )
1674         tv->view.ops->set_int( &tv->view, row, i, 0 );
1675     return ERROR_SUCCESS;
1676 }
1677
1678 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
1679                                       string_table *st, LPCWSTR name )
1680 {
1681     UINT rawsize = 0;
1682     USHORT *rawdata = NULL;
1683     MSITABLEVIEW *tv = NULL;
1684     UINT r, n, sz, i, mask;
1685     MSIRECORD *rec = NULL;
1686     const int debug_transform = 0;
1687
1688     TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
1689
1690     /* create a table view */
1691     r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
1692     if( r != ERROR_SUCCESS )
1693         goto err;
1694
1695     r = tv->view.ops->execute( &tv->view, NULL );
1696     if( r != ERROR_SUCCESS )
1697         goto err;
1698
1699     /* read the transform data */
1700     r = ERROR_FUNCTION_FAILED;
1701     read_stream_data( stg, name, &rawdata, &rawsize );
1702     if( !rawdata || (rawsize < 2) )
1703     {
1704         ERR("odd sized transform for table %s\n", debugstr_w(name));
1705         goto err;
1706     }
1707
1708     TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
1709           debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
1710
1711     /* interpret the data */
1712     r = ERROR_SUCCESS;
1713     for( n=0; n < (rawsize/2);  )
1714     {
1715         mask = rawdata[n];
1716
1717         if (mask&1)
1718         {
1719             /*
1720              * if the low bit is set, columns are continuous and
1721              * the number of columns is specified in the high byte
1722              */
1723             sz = 2 + tv->row_size;
1724         }
1725         else
1726         {
1727             /*
1728              * If the low bit is not set, rowdata[n] is a bitmask.
1729              * Excepting for key fields, which are always present,
1730              *  each bit indicates that a field is present in the transform record.
1731              *
1732              * rawdata[n] == 0 is a special case ... only the keys will be present
1733              * and it means that this row should be deleted.
1734              */
1735             sz = 2;
1736             for( i=0; i<tv->num_cols; i++ )
1737             {
1738                 if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
1739                     sz += bytes_per_column( &tv->columns[i] );
1740             }
1741         }
1742
1743         /* check we didn't run of the end of the table */
1744         if ( (n+sz) > rawsize )
1745         {
1746             ERR("borked.\n");
1747             dump_table( st, rawdata, rawsize );
1748             break;
1749         }
1750
1751         rec = msi_get_transform_record( tv, st, &rawdata[n] );
1752         if (rec)
1753         {
1754             UINT row = 0;
1755
1756             r = msi_table_find_row( tv, rec, &row );
1757
1758             if( rawdata[n] & 1)
1759             {
1760                 if( debug_transform ) MESSAGE("insert [%d]: ", row);
1761                 TABLE_insert_row( &tv->view, rec );
1762             }
1763             else if( mask & 0xff )
1764             {
1765                 if( debug_transform ) MESSAGE("modify [%d]: ", row);
1766                 msi_table_modify_row( tv, rec, row, mask );
1767             }
1768             else
1769             {
1770                 if( debug_transform ) MESSAGE("delete [%d]: ", row);
1771                 msi_delete_row( tv, row );
1772             }
1773             if( debug_transform ) dump_record( rec );
1774             msiobj_release( &rec->hdr );
1775         }
1776
1777         n += sz/2;
1778         
1779     }
1780
1781 err:
1782     /* no need to free the table, it's associated with the database */
1783     msi_free( rawdata );
1784     if( tv )
1785         tv->view.ops->delete( &tv->view );
1786
1787     return ERROR_SUCCESS;
1788 }
1789
1790 /*
1791  * msi_table_apply_transform
1792  *
1793  * Enumerate the table transforms in a transform storage and apply each one.
1794  */
1795 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
1796 {
1797     IEnumSTATSTG *stgenum = NULL;
1798     HRESULT r;
1799     STATSTG stat;
1800     ULONG n, count;
1801     WCHAR name[0x40];
1802     string_table *strings;
1803     UINT ret = ERROR_FUNCTION_FAILED;
1804
1805     TRACE("%p %p\n", db, stg );
1806
1807     strings = load_string_table( stg );
1808     if( !strings )
1809         goto end;
1810
1811     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
1812     if( FAILED( r ) )
1813         goto end;
1814
1815     n = 0;
1816     ret = ERROR_SUCCESS;
1817
1818     while( r == ERROR_SUCCESS )
1819     {
1820         count = 0;
1821         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
1822         if( FAILED( r ) || !count )
1823             break;
1824         decode_streamname( stat.pwcsName, name );
1825         if( ( name[0] == 0x4840 ) && ( name[1] != '_' ) )
1826             ret = msi_table_load_transform( db, stg, strings, name+1 );
1827         else
1828             TRACE("transform contains stream %s\n", debugstr_w(name));
1829         n++;
1830     }
1831
1832     if ( ret == ERROR_SUCCESS )
1833     {
1834         MSITRANSFORM *t;
1835
1836         t = msi_alloc( sizeof *t );
1837         t->stg = stg;
1838         IStorage_AddRef( stg );
1839         list_add_tail( &db->transforms, &t->entry );
1840     }
1841
1842 end:
1843     if ( stgenum )
1844         IEnumSTATSTG_Release( stgenum );
1845     if ( strings )
1846         msi_destroy_stringtable( strings );
1847
1848     return ret;
1849 }
1850
1851 void msi_free_transforms( MSIDATABASE *db )
1852 {
1853     while( !list_empty( &db->transforms ) )
1854     {
1855         MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
1856                                       MSITRANSFORM, entry );
1857         list_remove( &t->entry );
1858         IStorage_Release( t->stg );
1859         msi_free( t );
1860     }
1861 }