22e4e8a8309e71b0234298820e555affd72a9327
[blender.git] / source / blender / blenkernel / intern / packedFile.c
1 /**
2  * blenkernel/packedFile.c - (cleaned up mar-01 nzc)
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #ifndef WIN32 
41 #include <unistd.h>
42 #else
43 #include <io.h>
44 #endif
45 #include <string.h>
46 #include "MEM_guardedalloc.h"
47
48 #include "DNA_image_types.h"
49 #include "DNA_sound_types.h"
50 #include "DNA_vfont_types.h"
51 #include "DNA_packedFile_types.h"
52 #include "DNA_scene_types.h"
53
54 #include "BLI_blenlib.h"
55
56 #include "BKE_utildefines.h"
57 #include "BKE_global.h"
58 #include "BKE_main.h"
59 #include "BKE_screen.h"
60 #include "BKE_sound.h"
61 #include "BKE_image.h"
62 #include "BKE_font.h"
63 #include "BKE_packedFile.h"
64
65 int seekPackedFile(PackedFile * pf, int offset, int whence)
66 {
67         int oldseek = -1, seek = 0;
68
69         if (pf) {
70                 oldseek = pf->seek;
71                 switch(whence) {
72                 case SEEK_CUR:
73                         seek = oldseek + offset;
74                         break;
75                 case SEEK_END:
76                         seek = pf->size + offset;
77                         break;
78                 case SEEK_SET:
79                         seek = offset;
80                         break;
81                 default:
82                         oldseek = -1;
83                 }
84                 if (seek < 0) {
85                         seek = 0;
86                 } else if (seek > pf->size) {
87                         seek = pf->size;
88                 }
89                 pf->seek = seek;
90         }
91
92         return(oldseek);
93 }
94         
95 void rewindPackedFile(PackedFile * pf)
96 {
97         seekPackedFile(pf, 0, SEEK_SET);
98 }
99
100 int readPackedFile(PackedFile * pf, void * data, int size)
101
102         if ((pf != NULL) && (size >= 0) && (data != NULL)) {
103                 if (size + pf->seek > pf->size) {
104                         size = pf->size - pf->seek;
105                 }
106
107                 if (size > 0) {
108                         memcpy(data, ((char *) pf->data) + pf->seek, size);
109                 } else {
110                         size = 0;
111                 }
112
113                 pf->seek += size;
114         } else {
115                 size = -1;
116         }
117
118         return(size);
119 }
120
121 int countPackedFiles()
122 {
123         int count = 0;
124         Image *ima;
125         VFont *vf;
126         bSample *sample;
127         
128         // let's check if there are packed files...
129         ima = G.main->image.first;
130         while (ima) {
131                 if (ima->packedfile) {
132                         count++;
133                 }
134                 ima= ima->id.next;
135         }
136
137         vf = G.main->vfont.first;
138         while (vf) {
139                 if (vf->packedfile) {
140                         count++;
141                 }
142                 vf = vf->id.next;
143         }
144
145         sample = samples->first;
146         while (sample) {
147                 if (sample->packedfile) {
148                         count++;
149                 }
150                 sample = sample->id.next;
151         }
152
153         return(count);
154 }
155
156 void freePackedFile(PackedFile * pf)
157 {
158         if (pf) {
159                 MEM_freeN(pf->data);
160                 MEM_freeN(pf);
161         } else {
162                 printf("freePackedFile: Trying to free a NULL pointer\n");
163         }
164 }
165         
166 PackedFile * newPackedFileMemory(void *mem, int memlen)
167 {
168         PackedFile * pf = MEM_callocN(sizeof(*pf), "PackedFile");
169         pf->data = mem;
170         pf->size = memlen;
171         
172         return pf;
173 }
174
175 PackedFile * newPackedFile(char * filename)
176 {
177         PackedFile * pf = NULL;
178         int file, filelen;
179         char name[FILE_MAXDIR+FILE_MAXFILE];
180         void * data;
181         
182         //XXX waitcursor(1);
183         
184         // convert relative filenames to absolute filenames
185         
186         strcpy(name, filename);
187         BLI_convertstringcode(name, G.sce);
188         
189         // open the file
190         // and create a PackedFile structure
191
192         file= open(name, O_BINARY|O_RDONLY);
193         if (file <= 0) {
194                 // error("Can't open file: %s", name);
195         } else {
196                 filelen = BLI_filesize(file);
197
198                 if (filelen == 0) {
199                         // MEM_mallocN complains about MEM_mallocN(0, "bla");
200                         // we don't care....
201                         data = MEM_mallocN(1, "packFile");
202                 } else {
203                         data = MEM_mallocN(filelen, "packFile");
204                 }
205                 if (read(file, data, filelen) == filelen) {
206                         pf = newPackedFileMemory(data, filelen);
207                 }
208
209                 close(file);
210         }
211
212         //XXX waitcursor(0);
213                 
214         return (pf);
215 }
216
217 void packAll()
218 {
219         Image *ima;
220         VFont *vf;
221         bSample *sample;
222         
223         ima = G.main->image.first;
224         while (ima) {
225                 if (ima->packedfile == NULL) {
226                         ima->packedfile = newPackedFile(ima->name);
227                 }
228                 ima= ima->id.next;
229         }
230         
231         vf = G.main->vfont.first;
232         while (vf) {
233                 if (vf->packedfile == NULL) {
234                         vf->packedfile = newPackedFile(vf->name);
235                 }
236                 vf = vf->id.next;
237         }
238
239
240         sample = samples->first;
241         while (sample) {
242                 if (sample->packedfile == NULL) {
243                         sound_set_packedfile(sample, newPackedFile(sample->name));
244                 }
245                 sample = sample->id.next;
246         }
247 }
248
249
250 /*
251
252 // attempt to create a function that generates an unique filename
253 // this will work when all funtions in fileops.c understand relative filenames...
254
255 char * find_new_name(char * name)
256 {
257         char tempname[FILE_MAXDIR + FILE_MAXFILE];
258         char * newname;
259         
260         if (fop_exists(name)) {
261                 for (number = 1; number <= 999; number++) {
262                         sprintf(tempname, "%s.%03d", name, number);
263                         if (! fop_exists(tempname)) {
264                                 break;
265                         }
266                 }
267         }
268         
269         newname = mallocN(strlen(tempname) + 1, "find_new_name");
270         strcpy(newname, tempname);
271         
272         return(newname);
273 }
274         
275 */
276
277 int writePackedFile(char * filename, PackedFile *pf, int guimode)
278 {
279         int file, number, remove_tmp = FALSE;
280         int ret_value = RET_OK;
281         char name[FILE_MAXDIR + FILE_MAXFILE];
282         char tempname[FILE_MAXDIR + FILE_MAXFILE];
283 /*      void * data; */
284         
285         if (guimode); //XXX  waitcursor(1);
286         
287         strcpy(name, filename);
288         BLI_convertstringcode(name, G.sce);
289         
290         if (BLI_exists(name)) {
291                 for (number = 1; number <= 999; number++) {
292                         sprintf(tempname, "%s.%03d_", name, number);
293                         if (! BLI_exists(tempname)) {
294                                 if (BLI_copy_fileops(name, tempname) == RET_OK) {
295                                         remove_tmp = TRUE;
296                                 }
297                                 break;
298                         }
299                 }
300         }
301         
302         // make sure the path to the file exists...
303         BLI_make_existing_file(name);
304         
305         file = open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
306         if (file >= 0) {
307                 if (write(file, pf->data, pf->size) != pf->size) {
308                         if(guimode) ; //XXX error("Error writing file: %s", name);
309                         ret_value = RET_ERROR;
310                 }
311                 close(file);
312         } else {
313                 if(guimode); //XXX error("Error creating file: %s", name);
314                 ret_value = RET_ERROR;
315         }
316         
317         if (remove_tmp) {
318                 if (ret_value == RET_ERROR) {
319                         if (BLI_rename(tempname, name) != 0) {
320                                 if(guimode); //XXX error("Error restoring tempfile. Check files: '%s' '%s'", tempname, name);
321                         }
322                 } else {
323                         if (BLI_delete(tempname, 0, 0) != 0) {
324                                 if(guimode); //XXX error("Error deleting '%s' (ignored)");
325                         }
326                 }
327         }
328         
329         if(guimode); //XXX waitcursor(0);
330
331         return (ret_value);
332 }
333         
334 /* 
335
336 This function compares a packed file to a 'real' file.
337 It returns an integer indicating if:
338
339 PF_EQUAL                - the packed file and original file are identical
340 PF_DIFFERENT    - the packed file and original file differ
341 PF_NOFILE               - the original file doens't exist
342
343 */
344
345 int checkPackedFile(char * filename, PackedFile * pf)
346 {
347         struct stat st;
348         int ret_val, i, len, file;
349         char buf[4096];
350         char name[FILE_MAXDIR + FILE_MAXFILE];
351         
352         strcpy(name, filename);
353         BLI_convertstringcode(name, G.sce);
354         
355         if (stat(name, &st)) {
356                 ret_val = PF_NOFILE;
357         } else if (st.st_size != pf->size) {
358                 ret_val = PF_DIFFERS;
359         } else {
360                 // we'll have to compare the two...
361                 
362                 file = open(name, O_BINARY | O_RDONLY);
363                 if (file < 0) {
364                         ret_val = PF_NOFILE;
365                 } else {
366                         ret_val = PF_EQUAL;
367                         
368                         for (i = 0; i < pf->size; i += sizeof(buf)) {
369                                 len = pf->size - i;
370                                 if (len > sizeof(buf)) {
371                                         len = sizeof(buf);
372                                 }
373                                 
374                                 if (read(file, buf, len) != len) {
375                                         // read error ...
376                                         ret_val = PF_DIFFERS;
377                                         break;
378                                 } else {
379                                         if (memcmp(buf, ((char *)pf->data) + i, len)) {
380                                                 ret_val = PF_DIFFERS;
381                                                 break;
382                                         }
383                                 }
384                         }
385                 }
386         }
387         
388         return(ret_val);
389 }
390
391 /*
392
393 unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
394 If how == PF_ASK it offers the user a couple of options what to do with the packed file.
395
396 It returns a char * to the existing file name / new file name or NULL when
397 there was an error or when the user desides to cancel the operation.
398
399 */
400
401 char *unpackFile(char * abs_name, char * local_name, PackedFile * pf, int how)
402 {
403         char menu[6 * (FILE_MAXDIR + FILE_MAXFILE + 100)];
404         char line[FILE_MAXDIR + FILE_MAXFILE + 100];
405         char * newname = NULL, * temp = NULL;
406         
407         // char newabs[FILE_MAXDIR + FILE_MAXFILE];
408         // char newlocal[FILE_MAXDIR + FILE_MAXFILE];
409         
410         if (pf != NULL) {
411                 if (how == PF_ASK) {
412                         sprintf(menu, "UnPack file%%t|Remove Pack %%x%d", PF_REMOVE);
413                         
414                         if (strcmp(abs_name, local_name)) {
415                                 switch (checkPackedFile(local_name, pf)) {
416                                         case PF_NOFILE:
417                                                 sprintf(line, "|Create %s%%x%d", local_name, PF_WRITE_LOCAL);
418                                                 strcat(menu, line);
419                                                 break;
420                                         case PF_EQUAL:
421                                                 sprintf(line, "|Use %s (identical)%%x%d", local_name, PF_USE_LOCAL);
422                                                 strcat(menu, line);
423                                                 break;
424                                         case PF_DIFFERS:
425                                                 sprintf(line, "|Use %s (differs)%%x%d", local_name, PF_USE_LOCAL);
426                                                 strcat(menu, line);
427                                                 sprintf(line, "|Overwrite %s%%x%d", local_name, PF_WRITE_LOCAL);
428                                                 strcat(menu, line);
429                                                 break;
430                                 }
431                                 // sprintf(line, "|%%x%d", PF_INVALID);
432                                 // strcat(menu, line);
433                         }
434                         
435                         switch (checkPackedFile(abs_name, pf)) {
436                                 case PF_NOFILE:
437                                         sprintf(line, "|Create %s%%x%d", abs_name, PF_WRITE_ORIGINAL);
438                                         strcat(menu, line);
439                                         break;
440                                 case PF_EQUAL:
441                                         sprintf(line, "|Use %s (identical)%%x%d", abs_name, PF_USE_ORIGINAL);
442                                         strcat(menu, line);
443                                         break;
444                                 case PF_DIFFERS:
445                                         sprintf(line, "|Use %s (differs)%%x%d", abs_name, PF_USE_ORIGINAL);
446                                         strcat(menu, line);
447                                         sprintf(line, "|Overwrite %s%%x%d", abs_name, PF_WRITE_ORIGINAL);
448                                         strcat(menu, line);
449                                         break;
450                         }
451                         
452                         //XXX how = pupmenu(menu);
453                 }
454                 
455                 switch (how) {
456                         case -1:
457                         case PF_KEEP:
458                                 break;
459                         case PF_REMOVE:
460                                 temp= abs_name;
461                                 break;
462                         case PF_USE_LOCAL:
463                                 // if file exists use it
464                                 if (BLI_exists(local_name)) {
465                                         temp = local_name;
466                                         break;
467                                 }
468                                 // else fall through and create it
469                         case PF_WRITE_LOCAL:
470                                 if (writePackedFile(local_name, pf, 1) == RET_OK) {
471                                         temp = local_name;
472                                 }
473                                 break;
474                         case PF_USE_ORIGINAL:
475                                 // if file exists use it
476                                 if (BLI_exists(abs_name)) {
477                                         temp = abs_name;
478                                         break;
479                                 }
480                                 // else fall through and create it
481                         case PF_WRITE_ORIGINAL:
482                                 if (writePackedFile(abs_name, pf, 1) == RET_OK) {
483                                         temp = abs_name;
484                                 }
485                                 break;
486                         default:
487                                 printf("unpackFile: unknown return_value %d\n", how);
488                                 break;
489                 }
490                 
491                 if (temp) {
492                         newname = MEM_mallocN(strlen(temp) + 1, "unpack_file newname");
493                         strcpy(newname, temp);
494                 }
495         }
496         
497         return (newname);
498 }
499
500
501 int unpackVFont(VFont * vfont, int how)
502 {
503         char localname[FILE_MAXDIR + FILE_MAXFILE], fi[FILE_MAXFILE];
504         char * newname;
505         int ret_value = RET_ERROR;
506         
507         if (vfont != NULL) {
508                 strcpy(localname, vfont->name);
509                 BLI_splitdirstring(localname, fi);
510                 
511                 sprintf(localname, "//fonts/%s", fi);
512                 
513                 newname = unpackFile(vfont->name, localname, vfont->packedfile, how);
514                 if (newname != NULL) {
515                         ret_value = RET_OK;
516                         freePackedFile(vfont->packedfile);
517                         vfont->packedfile = 0;
518                         strcpy(vfont->name, newname);
519                         MEM_freeN(newname);
520                 }
521         }
522         
523         return (ret_value);
524 }
525
526 int unpackSample(bSample *sample, int how)
527 {
528         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
529         char * newname;
530         int ret_value = RET_ERROR;
531         PackedFile *pf;
532         
533         if (sample != NULL) {
534                 strcpy(localname, sample->name);
535                 BLI_splitdirstring(localname, fi);
536                 sprintf(localname, "//samples/%s", fi);
537                 
538                 newname = unpackFile(sample->name, localname, sample->packedfile, how);
539                 if (newname != NULL) {
540                         strcpy(sample->name, newname);
541                         MEM_freeN(newname);
542
543                         pf = sample->packedfile;
544                         // because samples and sounds can point to the
545                         // same packedfile we have to check them all
546                         sound_set_packedfile(sample, NULL);
547                         freePackedFile(pf);
548
549                         ret_value = RET_OK;
550                 }
551         }
552         
553         return(ret_value);
554 }
555
556 int unpackImage(Image * ima, int how)
557 {
558         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
559         char * newname;
560         int ret_value = RET_ERROR;
561         
562         if (ima != NULL) {
563                 strcpy(localname, ima->name);
564                 BLI_splitdirstring(localname, fi);
565                 sprintf(localname, "//textures/%s", fi);
566                         
567                 newname = unpackFile(ima->name, localname, ima->packedfile, how);
568                 if (newname != NULL) {
569                         ret_value = RET_OK;
570                         freePackedFile(ima->packedfile);
571                         ima->packedfile = NULL;
572                         strcpy(ima->name, newname);
573                         MEM_freeN(newname);
574                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
575                 }
576         }
577         
578         return(ret_value);
579 }
580
581 void unpackAll(int how)
582 {
583         Image *ima;
584         VFont *vf;
585         bSample *sample;
586                 
587         ima = G.main->image.first;
588         while (ima) {
589                 if (ima->packedfile) {
590                         unpackImage(ima, how);
591                 }
592                 ima= ima->id.next;
593         }
594         
595         vf = G.main->vfont.first;
596         while (vf) {
597                 if (vf->packedfile) {
598                         unpackVFont(vf, how);
599                 }
600                 vf = vf->id.next;
601         }
602
603         sample = samples->first;
604         while (sample) {
605                 if (sample->packedfile) {
606                         unpackSample(sample, how);
607                 }
608                 sample = sample->id.next;
609         }
610 }