filename was being copied into a string only the length of the max file.
[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, int guimode)
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                         if(guimode) error("Error writing file: %s", name);
313                         ret_value = RET_ERROR;
314                 }
315                 close(file);
316         } else {
317                 if(guimode) 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                                 if(guimode) error("Error restoring tempfile. Check files: '%s' '%s'", tempname, name);
325                         }
326                 } else {
327                         if (BLI_delete(tempname, 0, 0) == RET_ERROR) {
328                                 if(guimode) error("Error deleting '%s' (ignored)");
329                         }
330                 }
331         }
332         
333         if(guimode) 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                         sprintf(menu, "UnPack file%%t|Remove Pack %%x%d", PF_REMOVE);
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_REMOVE:
464                                 temp= abs_name;
465                                 break;
466                         case PF_USE_LOCAL:
467                                 // if file exists use it
468                                 if (BLI_exists(local_name)) {
469                                         temp = local_name;
470                                         break;
471                                 }
472                                 // else fall through and create it
473                         case PF_WRITE_LOCAL:
474                                 if (writePackedFile(local_name, pf, 1) == RET_OK) {
475                                         temp = local_name;
476                                 }
477                                 break;
478                         case PF_USE_ORIGINAL:
479                                 // if file exists use it
480                                 if (BLI_exists(abs_name)) {
481                                         temp = abs_name;
482                                         break;
483                                 }
484                                 // else fall through and create it
485                         case PF_WRITE_ORIGINAL:
486                                 if (writePackedFile(abs_name, pf, 1) == RET_OK) {
487                                         temp = abs_name;
488                                 }
489                                 break;
490                         default:
491                                 printf("unpackFile: unknown return_value %d\n", how);
492                                 break;
493                 }
494                 
495                 if (temp) {
496                         newname = MEM_mallocN(strlen(temp) + 1, "unpack_file newname");
497                         strcpy(newname, temp);
498                 }
499         }
500         
501         return (newname);
502 }
503
504
505 int unpackVFont(VFont * vfont, int how)
506 {
507         char localname[FILE_MAXDIR + FILE_MAXFILE], fi[FILE_MAXFILE];
508         char * newname;
509         int ret_value = RET_ERROR;
510         
511         if (vfont != NULL) {
512                 strcpy(localname, vfont->name);
513                 BLI_splitdirstring(localname, fi);
514                 
515                 sprintf(localname, "//fonts/%s", fi);
516                 
517                 newname = unpackFile(vfont->name, localname, vfont->packedfile, how);
518                 if (newname != NULL) {
519                         ret_value = RET_OK;
520                         freePackedFile(vfont->packedfile);
521                         vfont->packedfile = 0;
522                         strcpy(vfont->name, newname);
523                         MEM_freeN(newname);
524                 }
525         }
526         
527         return (ret_value);
528 }
529
530 int unpackSample(bSample *sample, int how)
531 {
532         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
533         char * newname;
534         int ret_value = RET_ERROR;
535         PackedFile *pf;
536         
537         if (sample != NULL) {
538                 strcpy(localname, sample->name);
539                 BLI_splitdirstring(localname, fi);
540                 sprintf(localname, "//samples/%s", fi);
541                 
542                 newname = unpackFile(sample->name, localname, sample->packedfile, how);
543                 if (newname != NULL) {
544                         strcpy(sample->name, newname);
545                         MEM_freeN(newname);
546
547                         pf = sample->packedfile;
548                         // because samples and sounds can point to the
549                         // same packedfile we have to check them all
550                         sound_set_packedfile(sample, NULL);
551                         freePackedFile(pf);
552
553                         ret_value = RET_OK;
554                 }
555         }
556         
557         return(ret_value);
558 }
559
560 int unpackImage(Image * ima, int how)
561 {
562         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
563         char * newname;
564         int ret_value = RET_ERROR;
565         
566         if (ima != NULL) {
567                 strcpy(localname, ima->name);
568                 BLI_splitdirstring(localname, fi);
569                 sprintf(localname, "//textures/%s", fi);
570                         
571                 newname = unpackFile(ima->name, localname, ima->packedfile, how);
572                 if (newname != NULL) {
573                         ret_value = RET_OK;
574                         freePackedFile(ima->packedfile);
575                         ima->packedfile = NULL;
576                         strcpy(ima->name, newname);
577                         MEM_freeN(newname);
578                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
579                 }
580         }
581         
582         return(ret_value);
583 }
584
585 void unpackAll(int how)
586 {
587         Image *ima;
588         VFont *vf;
589         bSample *sample;
590                 
591         ima = G.main->image.first;
592         while (ima) {
593                 if (ima->packedfile) {
594                         unpackImage(ima, how);
595                 }
596                 ima= ima->id.next;
597         }
598         
599         vf = G.main->vfont.first;
600         while (vf) {
601                 if (vf->packedfile) {
602                         unpackVFont(vf, how);
603                 }
604                 vf = vf->id.next;
605         }
606
607         sample = samples->first;
608         while (sample) {
609                 if (sample->packedfile) {
610                         unpackSample(sample, how);
611                 }
612                 sample = sample->id.next;
613         }
614 }