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