Get rid of the non-standard ICOM_VTABLE macro.
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 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 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "wine/debug.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "msipriv.h"
35 #include "winnls.h"
36
37 #include "wine/unicode.h"
38
39 #include "query.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 typedef struct tagMSICOLUMNINFO
44 {
45     LPWSTR tablename;
46     UINT   number;
47     LPWSTR colname;
48     UINT   type;
49     UINT   offset;
50 } MSICOLUMNINFO;
51
52 struct tagMSITABLE
53 {
54     USHORT **data;
55     UINT ref_count;
56     UINT row_count;
57     struct tagMSITABLE *next;
58     struct tagMSITABLE *prev;
59     WCHAR name[1];
60 };
61
62 #define MAX_STREAM_NAME 0x1f
63
64 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
65        MSICOLUMNINFO **pcols, UINT *pcount );
66 static UINT get_tablecolumns( MSIDATABASE *db, 
67        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
68
69 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
70 {
71     if( col->type & MSITYPE_STRING )
72         return 2;
73     if( (col->type & 0xff) > 4 )
74         ERR("Invalid column size!\n");
75     return col->type & 0xff;
76 }
77
78 static int utf2mime(int x)
79 {
80     if( (x>='0') && (x<='9') )
81         return x-'0';
82     if( (x>='A') && (x<='Z') )
83         return x-'A'+10;
84     if( (x>='a') && (x<='z') )
85         return x-'a'+10+26;
86     if( x=='.' )
87         return 10+26+26;
88     if( x=='_' )
89         return 10+26+26+1;
90     return -1;
91 }
92
93 static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
94 {
95     DWORD count = MAX_STREAM_NAME;
96     DWORD ch, next;
97     LPWSTR out, p;
98
99     if( !bTable )
100         count = strlenW( in )+2;
101     out = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
102     p = out;
103
104     if( bTable )
105     {
106          *p++ = 0x4840;
107          count --;
108     }
109     while( count -- ) 
110     {
111         ch = *in++;
112         if( !ch )
113         {
114             *p = ch;
115             return out;
116         }
117         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
118         {
119             ch = utf2mime(ch) + 0x4800;
120             next = *in;
121             if( next && (next<0x80) )
122             {
123                 next = utf2mime(next);
124                 if( next >= 0  )
125                 {
126                      next += 0x3ffffc0;
127                      ch += (next<<6);
128                      in++;
129                 }
130             }
131         }
132         *p++ = ch;
133     }
134     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
135     HeapFree( GetProcessHeap(), 0, out );
136     return NULL;
137 }
138
139 static int mime2utf(int x)
140 {
141     if( x<10 )
142         return x + '0';
143     if( x<(10+26))
144         return x - 10 + 'A';
145     if( x<(10+26+26))
146         return x - 10 - 26 + 'a';
147     if( x == (10+26+26) )
148         return '.';
149     return '_';
150 }
151
152 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
153 {
154     WCHAR ch;
155     DWORD count = 0;
156
157     while ( (ch = *in++) )
158     {
159         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
160         {
161             if( ch >= 0x4800 )
162                 ch = mime2utf(ch-0x4800);
163             else
164             {
165                 ch -= 0x3800;
166                 *out++ = mime2utf(ch&0x3f);
167                 count++;
168                 ch = mime2utf((ch>>6)&0x3f);
169             }
170         }
171         *out++ = ch;
172         count++;
173     }
174     *out = 0;
175     return count;
176 }
177
178 void enum_stream_names( IStorage *stg )
179 {
180     IEnumSTATSTG *stgenum = NULL;
181     HRESULT r;
182     STATSTG stat;
183     ULONG n, count;
184     WCHAR name[0x40];
185
186     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
187     if( FAILED( r ) )
188         return;
189
190     n = 0;
191     while( 1 )
192     {
193         count = 0;
194         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
195         if( FAILED( r ) || !count )
196             break;
197         decode_streamname( stat.pwcsName, name );
198         ERR("stream %2ld -> %s %s\n", n, 
199             debugstr_w(stat.pwcsName), debugstr_w(name) );
200         n++;
201     }
202
203     IEnumSTATSTG_Release( stgenum );
204 }
205
206 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
207                               USHORT **pdata, UINT *psz )
208 {
209     HRESULT r;
210     UINT ret = ERROR_FUNCTION_FAILED;
211     VOID *data;
212     ULONG sz, count;
213     IStream *stm = NULL;
214     STATSTG stat;
215     LPWSTR encname;
216
217     encname = encode_streamname(TRUE, stname);
218
219     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
220
221     r = IStorage_OpenStream(stg, encname, NULL, 
222             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
223     HeapFree( GetProcessHeap(), 0, encname );
224     if( FAILED( r ) )
225     {
226         WARN("open stream failed r = %08lx - empty table?\n",r);
227         return ret;
228     }
229
230     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
231     if( FAILED( r ) )
232     {
233         ERR("open stream failed r = %08lx!\n",r);
234         goto end;
235     }
236
237     if( stat.cbSize.QuadPart >> 32 )
238     {
239         ERR("Too big!\n");
240         goto end;
241     }
242         
243     sz = stat.cbSize.QuadPart;
244     data = HeapAlloc( GetProcessHeap(), 0, sz );
245     if( !data )
246     {
247         ERR("couldn't allocate memory r=%08lx!\n",r);
248         ret = ERROR_NOT_ENOUGH_MEMORY;
249         goto end;
250     }
251         
252     r = IStream_Read(stm, data, sz, &count );
253     if( FAILED( r ) || ( count != sz ) )
254     {
255         HeapFree( GetProcessHeap(), 0, data );
256         ERR("read stream failed r = %08lx!\n",r);
257         goto end;
258     }
259
260     *pdata = data;
261     *psz = sz;
262     ret = ERROR_SUCCESS;
263
264 end:
265     IStream_Release( stm );
266
267     return ret;
268 }
269
270 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
271 {
272     LPWSTR encname;
273     HRESULT r;
274
275     encname = encode_streamname(FALSE, stname);
276
277     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
278
279     r = IStorage_OpenStream(db->storage, encname, NULL, 
280             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
281     HeapFree( GetProcessHeap(), 0, encname );
282     if( FAILED( r ) )
283     {
284         WARN("open stream failed r = %08lx - empty table?\n",r);
285         return ERROR_FUNCTION_FAILED;
286     }
287
288     return ERROR_SUCCESS;
289 }
290
291 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
292                               USHORT **pdata, UINT *psz )
293 {
294     HRESULT r;
295     UINT ret = ERROR_FUNCTION_FAILED;
296     VOID *data;
297     ULONG sz, count;
298     IStream *stm = NULL;
299     STATSTG stat;
300
301     r = db_get_raw_stream( db, stname, &stm );
302     if( r != ERROR_SUCCESS)
303         return ret;
304     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
305     if( FAILED( r ) )
306     {
307         ERR("open stream failed r = %08lx!\n",r);
308         goto end;
309     }
310
311     if( stat.cbSize.QuadPart >> 32 )
312     {
313         ERR("Too big!\n");
314         goto end;
315     }
316         
317     sz = stat.cbSize.QuadPart;
318     data = HeapAlloc( GetProcessHeap(), 0, sz );
319     if( !data )
320     {
321         ERR("couldn't allocate memory r=%08lx!\n",r);
322         ret = ERROR_NOT_ENOUGH_MEMORY;
323         goto end;
324     }
325         
326     r = IStream_Read(stm, data, sz, &count );
327     if( FAILED( r ) || ( count != sz ) )
328     {
329         HeapFree( GetProcessHeap(), 0, data );
330         ERR("read stream failed r = %08lx!\n",r);
331         goto end;
332     }
333
334     *pdata = data;
335     *psz = sz;
336     ret = ERROR_SUCCESS;
337
338 end:
339     IStream_Release( stm );
340
341     return ret;
342 }
343
344 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
345                                LPVOID data, UINT sz )
346 {
347     HRESULT r;
348     UINT ret = ERROR_FUNCTION_FAILED;
349     ULONG count;
350     IStream *stm = NULL;
351     ULARGE_INTEGER size;
352     LARGE_INTEGER pos;
353     LPWSTR encname;
354
355     encname = encode_streamname(TRUE, stname );
356     r = IStorage_OpenStream( stg, encname, NULL, 
357             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
358     HeapFree( GetProcessHeap(), 0, encname );
359     if( FAILED(r) )
360     {
361         r = IStorage_CreateStream( stg, encname,
362                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
363     }
364     if( FAILED( r ) )
365     {
366         ERR("open stream failed r = %08lx\n",r);
367         return ret;
368     }
369
370     size.QuadPart = sz;
371     r = IStream_SetSize( stm, size );
372     if( FAILED( r ) )
373     {
374         ERR("Failed to SetSize\n");
375         goto end;
376     }
377
378     pos.QuadPart = 0;
379     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
380     if( FAILED( r ) )
381     {
382         ERR("Failed to Seek\n");
383         goto end;
384     }
385
386     r = IStream_Write(stm, data, sz, &count );
387     if( FAILED( r ) || ( count != sz ) )
388     {
389         ERR("Failed to Write\n");
390         goto end;
391     }
392
393     ret = ERROR_SUCCESS;
394
395 end:
396     IStream_Release( stm );
397
398     return ret;
399 }
400
401 UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
402 {
403     MSITABLE *t;
404     USHORT *rawdata = NULL;
405     UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
406     MSICOLUMNINFO *cols, *last_col;
407
408     TRACE("%s\n",debugstr_w(name));
409
410     /* non-existing tables should be interpretted as empty tables */
411     t = HeapAlloc( GetProcessHeap(), 0, 
412                    sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
413     if( !t )
414         return ERROR_NOT_ENOUGH_MEMORY;
415
416     r = table_get_column_info( db, name, &cols, &num_cols );
417     if( r != ERROR_SUCCESS )
418     {
419         HeapFree( GetProcessHeap(), 0, t );
420         return r;
421     }
422     last_col = &cols[num_cols-1];
423     row_size = last_col->offset + bytes_per_column( last_col );
424
425     t->row_count = 0;
426     t->data = NULL;
427     lstrcpyW( t->name, name );
428     t->ref_count = 1;
429     *ptable = t;
430
431     /* if we can't read the table, just assume that it's empty */
432     read_stream_data( db->storage, name, &rawdata, &rawsize );
433     if( !rawdata )
434         return ERROR_SUCCESS;
435
436     TRACE("Read %d bytes\n", rawsize );
437
438     if( rawsize % row_size )
439     {
440         ERR("Table size is invalid %d/%d\n", rawsize, row_size );
441         return ERROR_FUNCTION_FAILED;
442     }
443
444     t->row_count = rawsize / row_size;
445     t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
446                          t->row_count * sizeof (USHORT*) );
447     if( !t->data )
448         return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
449
450     /* transpose all the data */
451     TRACE("Transposing data from %d columns\n", t->row_count );
452     for( i=0; i<t->row_count; i++ )
453     {
454         t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
455         if( !t->data[i] )
456             return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
457         for( j=0; j<num_cols; j++ )
458         {
459             UINT ofs = cols[j].offset/2;
460             UINT n = bytes_per_column( &cols[j] );
461
462             switch( n )
463             {
464             case 2:
465                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
466                 break;
467             case 4:
468                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
469                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i + 1];
470                 break;
471             default:
472                 ERR("oops - unknown column width %d\n", n);
473                 return ERROR_FUNCTION_FAILED;
474             }
475         }
476     }
477
478     HeapFree( GetProcessHeap(), 0, cols );
479     HeapFree( GetProcessHeap(), 0, rawdata );
480
481     return ERROR_SUCCESS;
482 }
483
484 /* add this table to the list of cached tables in the database */
485 void add_table(MSIDATABASE *db, MSITABLE *table)
486 {
487     table->next = db->first_table;
488     table->prev = NULL;
489     if( db->first_table )
490         db->first_table->prev = table;
491     else
492         db->last_table = table;
493     db->first_table = table;
494 }
495  
496 /* remove from the list of cached tables */
497 void remove_table( MSIDATABASE *db, MSITABLE *table )
498 {
499     if( table->next )
500         table->next->prev = table->prev;
501     else
502         db->last_table = table->prev;
503     if( table->prev )
504         table->prev->next = table->next;
505     else
506         db->first_table = table->next;
507     table->next = NULL;
508     table->prev = NULL;
509 }
510
511 void release_table( MSIDATABASE *db, MSITABLE *table )
512 {
513     if( !table->ref_count )
514         ERR("Trying to destroy table with refcount 0\n");
515     table->ref_count --;
516     if( !table->ref_count )
517     {
518         remove_table( db, table );
519         HeapFree( GetProcessHeap(), 0, table->data );
520         HeapFree( GetProcessHeap(), 0, table );
521         TRACE("Destroyed table %s\n", debugstr_w(table->name));
522     }
523 }
524
525 void free_cached_tables( MSIDATABASE *db )
526 {
527     while( db->first_table )
528     {
529         MSITABLE *t = db->first_table;
530
531         if ( --t->ref_count )
532             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
533         remove_table( db, t );
534         HeapFree( GetProcessHeap(), 0, t->data );
535         HeapFree( GetProcessHeap(), 0, t );
536     }
537 }
538
539 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
540 {
541     MSITABLE *t;
542
543     for( t = db->first_table; t; t=t->next )
544     {
545         if( !lstrcmpW( name, t->name ) )
546         {
547             *ptable = t;
548             return ERROR_SUCCESS;
549         }
550     }
551
552     return ERROR_FUNCTION_FAILED;
553 }
554
555 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
556 {
557     UINT r, column_count;
558     MSICOLUMNINFO *columns;
559
560     /* get the number of columns in this table */
561     column_count = 0;
562     r = get_tablecolumns( db, name, NULL, &column_count );
563     if( r != ERROR_SUCCESS )
564         return r;
565
566     /* if there's no columns, there's no table */
567     if( column_count == 0 )
568         return ERROR_INVALID_PARAMETER;
569
570     TRACE("Table %s found\n", debugstr_w(name) );
571
572     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
573     if( !columns )
574         return ERROR_FUNCTION_FAILED;
575
576     r = get_tablecolumns( db, name, columns, &column_count );
577     if( r != ERROR_SUCCESS )
578     {
579         HeapFree( GetProcessHeap(), 0, columns );
580         return ERROR_FUNCTION_FAILED;
581     }
582
583     *pcols = columns;
584     *pcount = column_count;
585
586     return r;
587 }
588
589 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
590 {
591     UINT r;
592
593     *ptable = NULL;
594
595     /* first, see if the table is cached */
596     r = find_cached_table( db, name, ptable );
597     if( r == ERROR_SUCCESS )
598     {
599         (*ptable)->ref_count++;
600         return r;
601     }
602
603     r = read_table_from_storage( db, name, ptable );
604     if( r != ERROR_SUCCESS )
605         return r;
606
607     /* add the table to the list */
608     add_table( db, *ptable );
609     (*ptable)->ref_count++;
610
611     return ERROR_SUCCESS;
612 }
613
614 UINT save_table( MSIDATABASE *db, MSITABLE *t )
615 {
616     USHORT *rawdata = NULL, *p;
617     UINT rawsize, r, i, j, row_size, num_cols = 0;
618     MSICOLUMNINFO *cols, *last_col;
619
620     TRACE("Saving %s\n", debugstr_w( t->name ) );
621
622     r = table_get_column_info( db, t->name, &cols, &num_cols );
623     if( r != ERROR_SUCCESS )
624         return r;
625     
626     last_col = &cols[num_cols-1];
627     row_size = last_col->offset + bytes_per_column( last_col );
628
629     rawsize = t->row_count * row_size;
630     rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
631     if( !rawdata )
632         return ERROR_NOT_ENOUGH_MEMORY;
633
634     p = rawdata;
635     for( i=0; i<num_cols; i++ )
636     {
637         for( j=0; j<t->row_count; j++ )
638         {
639             UINT offset = cols[i].offset;
640
641             *p++ = t->data[j][offset/2];
642             if( 4 == bytes_per_column( &cols[i] ) )
643                 *p++ = t->data[j][offset/2+1];
644         }
645     }
646
647     TRACE("writing %d bytes\n", rawsize);
648     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
649
650     HeapFree( GetProcessHeap(), 0, rawdata );
651
652     return r;
653 }
654
655 HRESULT init_string_table( IStorage *stg )
656 {
657     HRESULT r;
658     static const WCHAR szStringData[] = {
659         '_','S','t','r','i','n','g','D','a','t','a',0 };
660     static const WCHAR szStringPool[] = {
661         '_','S','t','r','i','n','g','P','o','o','l',0 };
662     USHORT zero[2] = { 0, 0 };
663     ULONG count = 0;
664     IStream *stm = NULL;
665     LPWSTR encname;
666
667     encname = encode_streamname(TRUE, szStringPool );
668
669     /* create the StringPool stream... add the zero string to it*/
670     r = IStorage_CreateStream( stg, encname,
671             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
672     HeapFree( GetProcessHeap(), 0, encname );
673     if( r ) 
674     {
675         TRACE("Failed\n");
676         return r;
677     }
678
679     r = IStream_Write(stm, zero, sizeof zero, &count );
680     IStream_Release( stm );
681
682     if( FAILED( r ) || ( count != sizeof zero ) )
683     {
684         TRACE("Failed\n");
685         return E_FAIL;
686     }
687
688     /* create the StringData stream... make it zero length */
689     encname = encode_streamname(TRUE, szStringData );
690     r = IStorage_CreateStream( stg, encname,
691             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
692     HeapFree( GetProcessHeap(), 0, encname );
693     if( r ) 
694     {
695         TRACE("Failed\n");
696         return E_FAIL;
697     }
698     IStream_Release( stm );
699
700     return r;
701 }
702
703 UINT load_string_table( MSIDATABASE *db )
704 {
705     CHAR *data;
706     USHORT *pool;
707     UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0, codepage;
708     DWORD i, count, offset, len, n;
709     static const WCHAR szStringData[] = {
710         '_','S','t','r','i','n','g','D','a','t','a',0 };
711     static const WCHAR szStringPool[] = {
712         '_','S','t','r','i','n','g','P','o','o','l',0 };
713
714     if( db->strings )
715     {
716         msi_destroy_stringtable( db->strings );
717         db->strings = NULL;
718     }
719
720     r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
721     if( r != ERROR_SUCCESS)
722         goto end;
723     r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
724     if( r != ERROR_SUCCESS)
725         goto end;
726
727     count = poolsize/4;
728     if( poolsize > 4 )
729         codepage = pool[0] | ( pool[1] << 16 );
730     else
731         codepage = CP_ACP;
732     db->strings = msi_init_stringtable( count, codepage );
733
734     offset = 0;
735     for( i=1; i<count; i++ )
736     {
737         len = pool[i*2];
738         n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] );
739         if( n != i )
740             ERR("Failed to add string %ld\n", i );
741         offset += len;
742     }
743
744     TRACE("Loaded %ld strings\n", count);
745
746     ret = ERROR_SUCCESS;
747
748 end:
749     if( pool )
750         HeapFree( GetProcessHeap(), 0, pool );
751     if( data )
752         HeapFree( GetProcessHeap(), 0, data );
753
754     return ret;
755 }
756
757 UINT save_string_table( MSIDATABASE *db )
758 {
759     UINT i, count, datasize, poolsize, sz, used, r, codepage;
760     UINT ret = ERROR_FUNCTION_FAILED;
761     static const WCHAR szStringData[] = {
762         '_','S','t','r','i','n','g','D','a','t','a',0 };
763     static const WCHAR szStringPool[] = {
764         '_','S','t','r','i','n','g','P','o','o','l',0 };
765     CHAR *data = NULL;
766     USHORT *pool = NULL;
767
768     TRACE("\n");
769
770     /* construct the new table in memory first */
771     datasize = msi_string_totalsize( db->strings, &count );
772     poolsize = count*2*sizeof(USHORT);
773
774     pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
775     if( ! pool )
776     {
777         ERR("Failed to alloc pool %d bytes\n", poolsize );
778         goto err;
779     }
780     data = HeapAlloc( GetProcessHeap(), 0, datasize );
781     if( ! data )
782     {
783         ERR("Failed to alloc data %d bytes\n", poolsize );
784         goto err;
785     }
786
787     used = 0;
788     codepage = msi_string_get_codepage( db->strings );
789     pool[0]=codepage&0xffff;
790     pool[1]=(codepage>>16);
791     for( i=1; i<count; i++ )
792     {
793         sz = datasize - used;
794         r = msi_id2stringA( db->strings, i, data+used, &sz );
795         if( r != ERROR_SUCCESS )
796         {
797             ERR("failed to fetch string\n");
798             sz = 0;
799         }
800         if( sz && (sz < (datasize - used ) ) )
801             sz--;
802         TRACE("adding %u bytes %s\n", sz, data+used );
803         pool[ i*2 ] = sz;
804         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
805         used += sz;
806         if( used > datasize  )
807         {
808             ERR("oops overran %d >= %d\n", used, datasize);
809             goto err;
810         }
811     }
812
813     if( used != datasize )
814     {
815         ERR("oops used %d != datasize %d\n", used, datasize);
816         goto err;
817     }
818
819     /* write the streams */
820     r = write_stream_data( db->storage, szStringData, data, datasize );
821     TRACE("Wrote StringData r=%08x\n", r);
822     if( r )
823         goto err;
824     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
825     TRACE("Wrote StringPool r=%08x\n", r);
826     if( r )
827         goto err;
828
829     ret = ERROR_SUCCESS;
830
831 err:
832     if( data )
833         HeapFree( GetProcessHeap(), 0, data );
834     if( pool )
835         HeapFree( GetProcessHeap(), 0, pool );
836
837     return ret;
838 }
839
840 static LPWSTR strdupW( LPCWSTR str )
841 {
842     UINT len = lstrlenW( str ) + 1;
843     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
844     if( ret )
845         lstrcpyW( ret, str );
846     return ret;
847 }
848
849 /* information for default tables */
850 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
851 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
852 static const WCHAR szName[]    = { 'N','a','m','e',0 };
853 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
854 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
855 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
856 static const WCHAR szType[]    = { 'T','y','p','e',0 };
857
858 struct standard_table {
859     LPCWSTR tablename;
860     LPCWSTR columnname;
861     UINT number;
862     UINT type;
863 } MSI_standard_tables[] =
864 {
865   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
866   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
867   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
868   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
869   { szColumns, szType,   4, MSITYPE_VALID | 2},
870 };
871
872 #define STANDARD_TABLE_COUNT \
873      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
874
875 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
876 {
877     DWORD i, n=0;
878
879     for(i=0; i<STANDARD_TABLE_COUNT; i++)
880     {
881         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
882             continue;
883         if(colinfo && (n < *sz) )
884         {
885             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
886             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
887             colinfo[n].number = MSI_standard_tables[i].number;
888             colinfo[n].type = MSI_standard_tables[i].type;
889             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
890                     debugstr_w(colinfo[n].colname)); */
891             if( n )
892                 colinfo[n].offset = colinfo[n-1].offset
893                                   + bytes_per_column( &colinfo[n-1] );
894             else
895                 colinfo[n].offset = 0;
896         }
897         n++;
898         if( colinfo && (n >= *sz) )
899             break;
900     }
901     *sz = n;
902     return ERROR_SUCCESS;
903 }
904
905 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
906 {
907     UINT sz=0, r;
908     LPWSTR str;
909
910     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
911     if( r != ERROR_SUCCESS )
912         return NULL;
913     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
914     if( !str )
915         return str;
916     r = msi_id2stringW( db->strings, stringid, str, &sz );
917     if( r == ERROR_SUCCESS )
918         return str;
919     HeapFree(  GetProcessHeap(), 0, str );
920     return NULL;
921 }
922
923 static UINT get_tablecolumns( MSIDATABASE *db, 
924        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
925 {
926     UINT r, i, n=0, table_id, count, maxcount = *sz;
927     MSITABLE *table = NULL;
928     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
929
930     /* first check if there is a default table with that name */
931     r = get_defaulttablecolumns( szTableName, colinfo, sz );
932     if( ( r == ERROR_SUCCESS ) && *sz )
933         return r;
934
935     r = get_table( db, szColumns, &table);
936     if( r != ERROR_SUCCESS )
937     {
938         ERR("table %s not available\n", debugstr_w(szColumns));
939         return r;
940     }
941
942     /* convert table and column names to IDs from the string table */
943     r = msi_string2idW( db->strings, szTableName, &table_id );
944     if( r != ERROR_SUCCESS )
945     {
946         release_table( db, table );
947         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
948         return r;
949     }
950
951     TRACE("Table id is %d\n", table_id);
952
953     count = table->row_count;
954     for( i=0; i<count; i++ )
955     {
956         if( table->data[ i ][ 0 ] != table_id )
957             continue;
958         if( colinfo )
959         {
960             UINT id = table->data[ i ] [ 2 ];
961             colinfo[n].tablename = MSI_makestring( db, table_id );
962             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
963             colinfo[n].colname = MSI_makestring( db, id );
964             colinfo[n].type = table->data[ i ] [ 3 ];
965             /* this assumes that columns are in order in the table */
966             if( n )
967                 colinfo[n].offset = colinfo[n-1].offset
968                                   + bytes_per_column( &colinfo[n-1] );
969             else
970                 colinfo[n].offset = 0;
971             TRACE("table %s column %d is [%s] (%d) with type %08x "
972                   "offset %d at row %d\n", debugstr_w(szTableName),
973                    colinfo[n].number, debugstr_w(colinfo[n].colname),
974                    id, colinfo[n].type, colinfo[n].offset, i);
975             if( n != (colinfo[n].number-1) )
976             {
977                 ERR("oops. data in the _Columns table isn't in the right "
978                     "order for table %s\n", debugstr_w(szTableName));
979                 return ERROR_FUNCTION_FAILED;
980             }
981         }
982         n++;
983         if( colinfo && ( n >= maxcount ) )
984             break;
985     }
986     *sz = n;
987
988     release_table( db, table );
989
990     return ERROR_SUCCESS;
991 }
992
993 /* try to find the table name in the _Tables table */
994 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
995 {
996     static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
997     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
998     UINT r, table_id = 0, i, count;
999     MSITABLE *table = NULL;
1000
1001     if( !lstrcmpW( name, szTables ) )
1002         return TRUE;
1003     if( !lstrcmpW( name, szColumns ) )
1004         return TRUE;
1005
1006     r = msi_string2idW( db->strings, name, &table_id );
1007     if( r != ERROR_SUCCESS )
1008     {
1009         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1010         return FALSE;
1011     }
1012
1013     r = get_table( db, szTables, &table);
1014     if( r != ERROR_SUCCESS )
1015     {
1016         ERR("table %s not available\n", debugstr_w(szTables));
1017         return FALSE;
1018     }
1019
1020     /* count = table->size/2; */
1021     count = table->row_count;
1022     for( i=0; i<count; i++ )
1023         if( table->data[ i ][ 0 ] == table_id )
1024             break;
1025
1026     release_table( db, table );
1027
1028     if (i!=count)
1029         return TRUE;
1030
1031     ERR("Searched %d tables, but %d was not found\n", count, table_id );
1032
1033     return FALSE;
1034 }
1035
1036 /* below is the query interface to a table */
1037
1038 typedef struct tagMSITABLEVIEW
1039 {
1040     MSIVIEW        view;
1041     MSIDATABASE   *db;
1042     MSITABLE      *table;
1043     MSICOLUMNINFO *columns;
1044     UINT           num_cols;
1045     UINT           row_size;
1046     WCHAR          name[1];
1047 } MSITABLEVIEW;
1048
1049 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1050 {
1051     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1052     UINT offset, num_rows, n;
1053
1054     if( !tv->table )
1055         return ERROR_INVALID_PARAMETER;
1056
1057     if( (col==0) || (col>tv->num_cols) )
1058         return ERROR_INVALID_PARAMETER;
1059
1060     /* how many rows are there ? */
1061     num_rows = tv->table->row_count;
1062     if( row >= num_rows )
1063         return ERROR_NO_MORE_ITEMS;
1064
1065     if( tv->columns[col-1].offset >= tv->row_size )
1066     {
1067         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1068         ERR("%p %p\n", tv, tv->columns );
1069         return ERROR_FUNCTION_FAILED;
1070     }
1071
1072     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1073     n = bytes_per_column( &tv->columns[col-1] );
1074     switch( n )
1075     {
1076     case 4:
1077         offset = tv->columns[col-1].offset/2;
1078         *val = tv->table->data[row][offset] + 
1079                (tv->table->data[row][offset + 1] << 16);
1080         break;
1081     case 2:
1082         offset = tv->columns[col-1].offset/2;
1083         *val = tv->table->data[row][offset];
1084         break;
1085     default:
1086         ERR("oops! what is %d bytes per column?\n", n );
1087         return ERROR_FUNCTION_FAILED;
1088     }
1089
1090     TRACE("Data [%d][%d] = %d \n", row, col, *val );
1091
1092     return ERROR_SUCCESS;
1093 }
1094
1095 /*
1096  * We need a special case for streams, as we need to reference column with
1097  * the name of the stream in the same table, and the table name
1098  * which may not be available at higher levels of the query
1099  */
1100 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1101 {
1102     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1103     UINT ival = 0, refcol = 0, r;
1104     LPWSTR sval;
1105     LPWSTR full_name;
1106     DWORD len;
1107     static const WCHAR szDot[] = { '.', 0 };
1108
1109     if( !view->ops->fetch_int )
1110         return ERROR_INVALID_PARAMETER;
1111
1112     /*
1113      * The column marked with the type stream data seems to have a single number
1114      * which references the column containing the name of the stream data
1115      *
1116      * Fetch the column to reference first.
1117      */
1118     r = view->ops->fetch_int( view, row, col, &ival );
1119     if( r != ERROR_SUCCESS )
1120         return r;
1121
1122     /* now get the column with the name of the stream */
1123     r = view->ops->fetch_int( view, row, ival, &refcol );
1124     if( r != ERROR_SUCCESS )
1125         return r;
1126
1127     /* lookup the string value from the string table */
1128     sval = MSI_makestring( tv->db, refcol );
1129     if( !sval )
1130         return ERROR_INVALID_PARAMETER;
1131
1132     len = strlenW( tv->name ) + 2 + strlenW( sval );
1133     full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1134     strcpyW( full_name, tv->name );
1135     strcatW( full_name, szDot );
1136     strcatW( full_name, sval );
1137
1138     r = db_get_raw_stream( tv->db, full_name, stm );
1139     if( r )
1140         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1141     HeapFree( GetProcessHeap(), 0, full_name );
1142     HeapFree( GetProcessHeap(), 0, sval );
1143
1144     return r;
1145 }
1146
1147 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1148 {
1149     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1150     UINT offset, n;
1151
1152     if( !tv->table )
1153         return ERROR_INVALID_PARAMETER;
1154
1155     if( (col==0) || (col>tv->num_cols) )
1156         return ERROR_INVALID_PARAMETER;
1157
1158     if( tv->columns[col-1].offset >= tv->row_size )
1159     {
1160         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1161         ERR("%p %p\n", tv, tv->columns );
1162         return ERROR_FUNCTION_FAILED;
1163     }
1164
1165     n = bytes_per_column( &tv->columns[col-1] );
1166     switch( n )
1167     {
1168     case 4:
1169         offset = tv->columns[col-1].offset/2;
1170         tv->table->data[row][offset]     = val & 0xffff;
1171         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1172         break;
1173     case 2:
1174         offset = tv->columns[col-1].offset/2;
1175         tv->table->data[row][offset] = val;
1176         break;
1177     default:
1178         ERR("oops! what is %d bytes per column?\n", n );
1179         return ERROR_FUNCTION_FAILED;
1180     }
1181     return ERROR_SUCCESS;
1182 }
1183
1184 UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
1185 {
1186     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1187     USHORT **p, *row;
1188     UINT sz;
1189
1190     TRACE("%p\n", view);
1191
1192     if( !tv->table )
1193         return ERROR_INVALID_PARAMETER;
1194
1195     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1196     if( !row )
1197         return ERROR_NOT_ENOUGH_MEMORY;
1198
1199     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1200     if( tv->table->data )
1201         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1202     else
1203         p = HeapAlloc( GetProcessHeap(), 0, sz );
1204     if( !p )
1205         return ERROR_NOT_ENOUGH_MEMORY;
1206
1207     tv->table->data = p;
1208     tv->table->data[tv->table->row_count] = row;
1209     *num = tv->table->row_count;
1210     tv->table->row_count++;
1211
1212     return ERROR_SUCCESS;
1213 }
1214
1215 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1216 {
1217     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1218     UINT r;
1219
1220     TRACE("%p %p\n", tv, record);
1221
1222     if( tv->table )
1223         return ERROR_FUNCTION_FAILED;
1224
1225     r = get_table( tv->db, tv->name, &tv->table );
1226     if( r != ERROR_SUCCESS )
1227         return r;
1228     
1229     return ERROR_SUCCESS;
1230 }
1231
1232 static UINT TABLE_close( struct tagMSIVIEW *view )
1233 {
1234     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1235
1236     TRACE("%p\n", view );
1237
1238     if( !tv->table )
1239         return ERROR_FUNCTION_FAILED;
1240
1241     release_table( tv->db, tv->table );
1242     tv->table = NULL;
1243     
1244     return ERROR_SUCCESS;
1245 }
1246
1247 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1248 {
1249     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1250
1251     TRACE("%p %p %p\n", view, rows, cols );
1252
1253     if( cols )
1254         *cols = tv->num_cols;
1255     if( rows )
1256     {
1257         if( !tv->table )
1258             return ERROR_INVALID_PARAMETER;
1259         *rows = tv->table->row_count;
1260     }
1261
1262     return ERROR_SUCCESS;
1263 }
1264
1265 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1266                 UINT n, LPWSTR *name, UINT *type )
1267 {
1268     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1269
1270     TRACE("%p %d %p %p\n", tv, n, name, type );
1271
1272     if( ( n == 0 ) || ( n > tv->num_cols ) )
1273         return ERROR_INVALID_PARAMETER;
1274
1275     if( name )
1276     {
1277         *name = strdupW( tv->columns[n-1].colname );
1278         if( !*name )
1279             return ERROR_FUNCTION_FAILED;
1280     }
1281     if( type )
1282         *type = tv->columns[n-1].type;
1283
1284     return ERROR_SUCCESS;
1285 }
1286
1287 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
1288 {
1289     FIXME("%p %d %ld\n", view, eModifyMode, hrec );
1290     return ERROR_CALL_NOT_IMPLEMENTED;
1291 }
1292
1293 static UINT TABLE_delete( struct tagMSIVIEW *view )
1294 {
1295     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1296
1297     TRACE("%p\n", view );
1298
1299     if( tv->table )
1300         release_table( tv->db, tv->table );
1301     tv->table = NULL;
1302
1303     if( tv->columns )
1304     {
1305         UINT i;
1306         for( i=0; i<tv->num_cols; i++)
1307         {
1308             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
1309             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
1310         }
1311         HeapFree( GetProcessHeap(), 0, tv->columns );
1312     }
1313     tv->columns = NULL;
1314
1315     HeapFree( GetProcessHeap(), 0, tv );
1316
1317     return ERROR_SUCCESS;
1318 }
1319
1320
1321 MSIVIEWOPS table_ops =
1322 {
1323     TABLE_fetch_int,
1324     TABLE_fetch_stream,
1325     TABLE_set_int,
1326     TABLE_insert_row,
1327     TABLE_execute,
1328     TABLE_close,
1329     TABLE_get_dimensions,
1330     TABLE_get_column_info,
1331     TABLE_modify,
1332     TABLE_delete
1333 };
1334
1335 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1336 {
1337     MSITABLEVIEW *tv ;
1338     UINT r, sz, column_count;
1339     MSICOLUMNINFO *columns, *last_col;
1340
1341     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1342
1343     /* get the number of columns in this table */
1344     column_count = 0;
1345     r = get_tablecolumns( db, name, NULL, &column_count );
1346     if( r != ERROR_SUCCESS )
1347         return r;
1348
1349     /* if there's no columns, there's no table */
1350     if( column_count == 0 )
1351         return ERROR_INVALID_PARAMETER;
1352
1353     TRACE("Table found\n");
1354
1355     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1356     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
1357     if( !tv )
1358         return ERROR_FUNCTION_FAILED;
1359     
1360     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
1361     if( !columns )
1362     {
1363         HeapFree( GetProcessHeap(), 0, tv );
1364         return ERROR_FUNCTION_FAILED;
1365     }
1366
1367     r = get_tablecolumns( db, name, columns, &column_count );
1368     if( r != ERROR_SUCCESS )
1369     {
1370         HeapFree( GetProcessHeap(), 0, columns );
1371         HeapFree( GetProcessHeap(), 0, tv );
1372         return ERROR_FUNCTION_FAILED;
1373     }
1374
1375     TRACE("Table has %d columns\n", column_count);
1376
1377     last_col = &columns[column_count-1];
1378
1379     /* fill the structure */
1380     tv->view.ops = &table_ops;
1381     tv->db = db;
1382     tv->columns = columns;
1383     tv->num_cols = column_count;
1384     tv->table = NULL;
1385     tv->row_size = last_col->offset + bytes_per_column( last_col );
1386
1387     TRACE("one row is %d bytes\n", tv->row_size );
1388
1389     *view = (MSIVIEW*) tv;
1390     lstrcpyW( tv->name, name );
1391
1392     return ERROR_SUCCESS;
1393 }
1394
1395 UINT MSI_CommitTables( MSIDATABASE *db )
1396 {
1397     UINT r;
1398     MSITABLE *table = NULL;
1399
1400     TRACE("%p\n",db);
1401
1402     r = save_string_table( db );
1403     if( r != ERROR_SUCCESS )
1404     {
1405         ERR("failed to save string table r=%08x\n",r);
1406         return r;
1407     }
1408
1409     for( table = db->first_table; table; table = table->next )
1410     {
1411         r = save_table( db, table );
1412         if( r != ERROR_SUCCESS )
1413         {
1414             ERR("failed to save table %s (r=%08x)\n",
1415                   debugstr_w(table->name), r);
1416             return r;
1417         }
1418     }
1419
1420     /* force everything to reload next time */
1421     free_cached_tables( db );
1422
1423     return ERROR_SUCCESS;
1424 }