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