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