lzma implementation
[swftools.git] / installer / archive.c
1 /* archive.c
2
3    Part of the swftools installer.
4    
5    Copyright (c) 2004 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #ifdef WIN32
25 #include <windows.h>
26 #include <shlwapi.h>
27 #else
28 #include <sys/stat.h>
29 #endif
30 #include "archive.h"
31
32 #ifdef ZLIB
33 #include "../z/zlib.h"
34 #define ZLIB_BUFFER_SIZE 16384
35 #else
36 #include "lzma/LzmaDecode.h"
37 #endif
38
39 static int verbose = 0;
40 static void msg(char*format, ...)
41 {
42     char buf[1024];
43     int l;
44     va_list arglist;
45     if(!verbose)
46         return;
47     va_start(arglist, format);
48     vsprintf(buf, format, arglist);
49     va_end(arglist);
50     l = strlen(buf);
51     while(l && buf[l-1]=='\n') {
52         buf[l-1] = 0;
53         l--;
54     }
55     printf("(archive) %s\n", buf);
56     fflush(stdout);
57 }
58
59
60 typedef struct _reader
61 {
62     int (*read)(struct _reader*, void*data, int len);
63     void (*dealloc)(struct _reader*);
64     void *internal;
65     int pos;
66 } reader_t;
67
68 /* ---------------------------- mem reader ------------------------------- */
69
70 struct memread_t
71 {
72     unsigned char*data;
73     int length;
74 };
75 static int reader_memread(reader_t*reader, void* data, int _len) 
76 {
77     struct memread_t*mr = (struct memread_t*)reader->internal;
78
79     int len = _len;
80     if(mr->length - reader->pos < len) {
81         len = mr->length - reader->pos;
82     }
83     memcpy(data, &mr->data[reader->pos], len);
84     msg("at pos %d, asked to read %d bytes, did read %d bytes\n", reader->pos, _len, len);
85     reader->pos += len;
86     return len;
87 }
88 static void reader_memread_dealloc(reader_t*reader)
89 {
90     if(reader->internal)
91         free(reader->internal);
92     memset(reader, 0, sizeof(reader_t));
93 }
94 reader_t*reader_init_memreader(void*newdata, int newlength)
95 {
96     reader_t*r = malloc(sizeof(reader_t));
97     struct memread_t*mr = (struct memread_t*)malloc(sizeof(struct memread_t));
98     mr->data = (unsigned char*)newdata;
99     mr->length = newlength;
100     r->read = reader_memread;
101     r->dealloc = reader_memread_dealloc;
102     r->internal = (void*)mr;
103     r->pos = 0;
104     return r;
105
106 /* ---------------------------- lzma reader -------------------------- */
107 typedef struct
108 {
109     reader_t*input;
110     CLzmaDecoderState state;
111     unsigned char*mem;
112     int pos;
113     int len;
114     int lzmapos;
115     int available;
116 } lzma_t;
117
118 static void reader_lzma_dealloc(reader_t*reader)
119 {
120     lzma_t*i = (lzma_t*)reader->internal;
121     free(i->state.Probs);i->state.Probs = 0;
122     free(i->state.Dictionary);i->state.Dictionary = 0;
123     free(reader->internal);reader->internal=0;
124 }
125
126 static int reader_lzma_read(reader_t*reader, void*data, int len)
127 {
128     lzma_t*i = (lzma_t*)reader->internal;
129
130     SizeT processed = 0;
131     if(len>i->available)
132         len = i->available;
133     int ret = LzmaDecode(&i->state, 
134                          &i->mem[i->pos], i->len-i->pos, &i->lzmapos, 
135                          data, len, &processed);
136     i->available -= processed;
137     i->pos += i->lzmapos;
138     return processed;
139 }
140
141 reader_t* reader_init_lzma(void*mem, int len)
142 {
143     reader_t*r = malloc(sizeof(reader_t));
144     memset(r, 0, sizeof(reader_t));
145
146     lzma_t*i = (lzma_t*)malloc(sizeof(lzma_t));
147     memset(i, 0, sizeof(lzma_t));
148     r->internal = i;
149     r->read = reader_lzma_read;
150     r->dealloc = reader_lzma_dealloc;
151     r->pos = 0;
152
153     i->mem = mem;
154     i->len = len;
155     i->lzmapos = 0;
156
157     if(LzmaDecodeProperties(&i->state.Properties, mem, LZMA_PROPERTIES_SIZE)) {
158         printf("Couldn't decode properties\n");
159         return 0;
160     }
161     i->pos += LZMA_PROPERTIES_SIZE;
162
163     unsigned char*l = &i->mem[i->pos];
164     i->available = (long long)l[0]     | (long long)l[1]<<8  | (long long)l[2]<<16 | (long long)l[3]<<24 | 
165                   (long long)l[4]<<32 | (long long)l[5]<<40 | (long long)l[6]<<48 | (long long)l[7]<<56;
166     i->pos += 8; //uncompressed size
167
168     i->state.Probs = (CProb *)malloc(LzmaGetNumProbs(&i->state.Properties) * sizeof(CProb));
169     i->state.Dictionary = (unsigned char *)malloc(i->state.Properties.DictionarySize);
170     LzmaDecoderInit(&i->state);
171
172     return r;
173 }
174
175 #ifdef ZLIB
176 /* ---------------------------- zlibinflate reader -------------------------- */
177 struct zlibinflate_t
178 {
179     z_stream zs;
180     reader_t*input;
181     unsigned char readbuffer[ZLIB_BUFFER_SIZE];
182 };
183
184 static void zlib_error(int ret, char* msg, z_stream*zs)
185 {
186     fprintf(stderr, "%s: zlib error (%d): last zlib error: %s\n", msg, ret, zs->msg?zs->msg:"unknown");
187     perror("errno:");
188     exit(1);
189 }
190
191 static int reader_zlibinflate(reader_t*reader, void* data, int len) 
192 {
193     struct zlibinflate_t*z = (struct zlibinflate_t*)reader->internal;
194     int ret;
195     if(!z) {
196         return 0;
197     }
198     if(!len)
199         return 0;
200     
201     z->zs.next_out = (Bytef *)data;
202     z->zs.avail_out = len;
203
204     while(1) {
205         if(!z->zs.avail_in) {
206             z->zs.avail_in = z->input->read(z->input, z->readbuffer, ZLIB_BUFFER_SIZE);
207             z->zs.next_in = z->readbuffer;
208         }
209         if(z->zs.avail_in)
210             ret = inflate(&z->zs, Z_NO_FLUSH);
211         else
212             ret = inflate(&z->zs, Z_FINISH);
213     
214         if (ret != Z_OK &&
215             ret != Z_STREAM_END) zlib_error(ret, "bitio:inflate_inflate", &z->zs);
216
217         if (ret == Z_STREAM_END) {
218                 int pos = z->zs.next_out - (Bytef*)data;
219                 ret = inflateEnd(&z->zs);
220                 if (ret != Z_OK) zlib_error(ret, "bitio:inflate_end", &z->zs);
221                 free(reader->internal);
222                 reader->internal = 0;
223                 reader->pos += pos;
224                 return pos;
225         }
226         if(!z->zs.avail_out) {
227             break;
228         }
229     }
230     reader->pos += len;
231     return len;
232 }
233 static void reader_zlibinflate_dealloc(reader_t*reader)
234 {
235     struct zlibinflate_t*z = (struct zlibinflate_t*)reader->internal;
236     if(z) {
237         if(z->input) {
238             z->input->dealloc(z->input);z->input = 0;
239         }
240         inflateEnd(&z->zs);
241         free(reader->internal);
242     }
243     memset(reader, 0, sizeof(reader_t));
244 }
245 reader_t* reader_init_zlibinflate(reader_t*input)
246 {
247     reader_t*r = malloc(sizeof(reader_t));
248     struct zlibinflate_t*z;
249     int ret;
250     memset(r, 0, sizeof(reader_t));
251     z = (struct zlibinflate_t*)malloc(sizeof(struct zlibinflate_t));
252     memset(z, 0, sizeof(struct zlibinflate_t));
253     r->internal = z;
254     r->read = reader_zlibinflate;
255     r->dealloc = reader_zlibinflate_dealloc;
256     r->pos = 0;
257     z->input = input;
258     memset(&z->zs,0,sizeof(z_stream));
259     z->zs.zalloc = Z_NULL;
260     z->zs.zfree  = Z_NULL;
261     z->zs.opaque = Z_NULL;
262     ret = inflateInit(&z->zs);
263     if (ret != Z_OK) zlib_error(ret, "bitio:inflate_init", &z->zs);
264     return r;
265 }
266
267 /* -------------------------------------------------------------------------- */
268 #endif
269
270
271 static int create_directory(char*path,statusfunc_t f)
272 {
273     if(!path || !*path || (*path=='.' && (!path[1] || path[1]=='.')))
274         return 1; //nothing to do
275     while(path[0]=='.' && (path[1]=='/' || path[1]=='\\'))
276         path+=2;
277
278 #ifdef WIN32
279     if(PathIsDirectoryA(path))
280         return 1;
281 #else
282     struct stat st;
283     if(stat(path, &st)>=0) {
284         if(S_ISDIR(st.st_mode)) {
285             return 1; /* already exists */
286         }
287     }
288 #endif
289
290     if(mkdir(path,0755)<0) {
291         perror("mkdir");
292         char buf[1024];
293         sprintf(buf, "create directory \"%s\" FAILED", path);
294         f(-1, buf);
295         return 0;
296     }
297     return 1;
298 }
299 static int goto_directory(char*path,statusfunc_t f)
300 {
301     if(chdir(path)<0) {
302         char buf[1024];
303         sprintf(buf, "changing to directory \"%s\" FAILED", path);
304         f(-1, buf);
305         return 0;
306     }
307     return 1;
308 }
309 static char basenamebuf[256];
310 static char*get_directory(char*filename)
311 {
312     char*r1 = strrchr(filename, '\\');
313     char*r2 = strrchr(filename, '/');
314     char*r = r1>r2?r1:r2;
315     if(!r)
316         return "";
317     memcpy(basenamebuf, filename, r-filename);
318     basenamebuf[r-filename] = 0;
319     //msg("directory name of \"%s\" is \"%s\"", filename, basenamebuf);
320     return basenamebuf;
321 }
322 static int write_file(char*filename, reader_t*r, int len,statusfunc_t f)
323 {
324     while(filename[0]=='.' && (filename[1]=='/' || filename[1]=='\\'))
325         filename+=2;
326
327     filename=strdup(filename);
328
329     char*p = filename;
330     while(*p) {
331         if(*p=='/') *p='\\';
332         p++;
333     }
334
335     msg("create file \"%s\" (%d bytes)", filename, len);
336     FILE*fo = fopen(filename, "wb");
337
338     if(!fo) {
339         char buf[1024];
340         sprintf(buf, "Couldn't create file %s", filename);
341         f(-1, buf);
342         free(filename);
343         return 0;
344     }
345     int pos=0;
346     char buf[4096];
347     while(pos<len) {
348         int l = 4096;
349         if(pos+l>len)
350             l = len-pos;
351         int n = r->read(r, buf, l);
352         if(n < l) {
353             char buf[1024];
354             sprintf(buf, "Couldn't read byte %d (pos+%d) from input buffer for file %s", pos+n, n, filename);
355             f(-1, buf);
356             return 0;
357         }
358         fwrite(buf, l, 1, fo);
359         pos+=l;
360     }
361     fclose(fo);
362     free(filename);
363     return 1;
364 }
365
366 int unpack_archive(void*data, int len, char*destdir, statusfunc_t f)
367 {
368     reader_t*m = reader_init_memreader(data, len);
369 #ifdef ZLIB
370     reader_t*z = reader_init_zlibinflate(m);
371 #else
372     reader_t*z = reader_init_lzma(data, len);
373 #endif
374     if(!z) {
375         f(1, "Couldn't decompress installation files");
376         return 0;
377     }
378
379     f(0, "Creating installation directory");
380     if(!create_directory(destdir,f)) return 0;
381     f(0, "Changing to installation directory");
382     if(!goto_directory(destdir,f)) return 0;
383     
384     f(0, "Uncompressing files...");
385     while(1) {
386         /* read id */
387         unsigned char id[4];
388         id[3] = 0;
389         if(z->read(z, id, 3)<3) {
390             f(-1, "Unexpected end of archive");
391             return 0;
392         }
393         if(!strcmp(id, "END"))
394             break;
395
396         unsigned b1=0,b2=0,b3=0,b4=0;
397         int l = 0;
398         l+=z->read(z, &b1, 1);
399         l+=z->read(z, &b2, 1);
400         l+=z->read(z, &b3, 1);
401         l+=z->read(z, &b4, 1);
402         if(l<4)
403             return 0;
404
405         /* read size */
406         int len = b1|b2<<8|b3<<16|b4<<24;
407         
408         /* read filename */
409         unsigned char filename_len;
410         z->read(z, &filename_len, 1);
411         char*filename = malloc(filename_len+1);
412         z->read(z, filename, filename_len);
413         filename[(int)filename_len] = 0;
414
415         if(verbose) printf("[%s] %s %d\n", id, filename, len);
416         char buf[2048];
417         sprintf(buf, "[%s] %s (%d bytes)", id, filename, len);
418         f(0, buf);
419         if(!strcmp(id, "DIR")) {
420             if(!create_directory(filename,f)) return 0;
421         } else {
422             if(!create_directory(get_directory(filename),f)) return 0;
423
424             if(!write_file(filename,z,len,f)) return 0;
425         }
426     }
427     f(0, "Finishing Installation");
428     return 1;
429 }
430