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