Merged changes in the trunk up to revision 54110.
[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_sound_types.h"
48 #include "DNA_vfont_types.h"
49 #include "DNA_packedFile_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         
237         for (ima = bmain->image.first; ima; ima = ima->id.next) {
238                 if (ima->packedfile == NULL && ima->id.lib == NULL) {
239                         if (ima->source == IMA_SRC_FILE) {
240                                 ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
241                         }
242                         else if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
243                                 BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported",
244                                             ima->id.name + 2);
245                         }
246                 }
247         }
248
249         for (vfont = bmain->vfont.first; vfont; vfont = vfont->id.next)
250                 if (vfont->packedfile == NULL && vfont->id.lib == NULL && BKE_vfont_is_builtin(vfont) == FALSE)
251                         vfont->packedfile = newPackedFile(reports, vfont->name, bmain->name);
252
253         for (sound = bmain->sound.first; sound; sound = sound->id.next)
254                 if (sound->packedfile == NULL && sound->id.lib == NULL)
255                         sound->packedfile = newPackedFile(reports, sound->name, bmain->name);
256 }
257
258
259 #if 0
260
261 // attempt to create a function that generates an unique filename
262 // this will work when all funtions in fileops.c understand relative filenames...
263
264 static char *find_new_name(char *name)
265 {
266         char tempname[FILE_MAX];
267         char *newname;
268         size_t len;
269         
270         if (fop_exists(name)) {
271                 for (number = 1; number <= 999; number++) {
272                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d", name, number);
273                         if (!fop_exists(tempname)) {
274                                 break;
275                         }
276                 }
277         }
278         len = strlen(tempname) + 1;
279         newname = MEM_mallocN(len, "find_new_name");
280         memcpy(newname, tempname, len * sizeof(char));
281         return newname;
282 }
283 #endif
284
285 int writePackedFile(ReportList *reports, const char *filename, PackedFile *pf, int guimode)
286 {
287         int file, number, remove_tmp = FALSE;
288         int ret_value = RET_OK;
289         char name[FILE_MAX];
290         char tempname[FILE_MAX];
291 /*      void *data; */
292         
293         if (guimode) {} //XXX  waitcursor(1);
294         
295         BLI_strncpy(name, filename, sizeof(name));
296         BLI_path_abs(name, G.main->name);
297         
298         if (BLI_exists(name)) {
299                 for (number = 1; number <= 999; number++) {
300                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
301                         if (!BLI_exists(tempname)) {
302                                 if (BLI_copy(name, tempname) == RET_OK) {
303                                         remove_tmp = TRUE;
304                                 }
305                                 break;
306                         }
307                 }
308         }
309         
310         /* make sure the path to the file exists... */
311         BLI_make_existing_file(name);
312         
313         file = BLI_open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
314         if (file >= 0) {
315                 if (write(file, pf->data, pf->size) != pf->size) {
316                         BKE_reportf(reports, RPT_ERROR, "Error writing file '%s'", name);
317                         ret_value = RET_ERROR;
318                 }
319                 close(file);
320         }
321         else {
322                 BKE_reportf(reports, RPT_ERROR, "Error creating file '%s'", name);
323                 ret_value = RET_ERROR;
324         }
325         
326         if (remove_tmp) {
327                 if (ret_value == RET_ERROR) {
328                         if (BLI_rename(tempname, name) != 0) {
329                                 BKE_reportf(reports, RPT_ERROR, "Error restoring temp file (check files '%s' '%s')", tempname, name);
330                         }
331                 }
332                 else {
333                         if (BLI_delete(tempname, 0, 0) != 0) {
334                                 BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
335                         }
336                 }
337         }
338         
339         if (guimode) {} //XXX waitcursor(0);
340
341         return (ret_value);
342 }
343         
344 /*
345  * This function compares a packed file to a 'real' file.
346  * It returns an integer indicating if:
347  *
348  * PF_EQUAL             - the packed file and original file are identical
349  * PF_DIFFERENT - the packed file and original file differ
350  * PF_NOFILE    - the original file doens't exist
351  */
352
353 int checkPackedFile(const char *filename, PackedFile *pf)
354 {
355         struct stat st;
356         int ret_val, i, len, file;
357         char buf[4096];
358         char name[FILE_MAX];
359         
360         BLI_strncpy(name, filename, sizeof(name));
361         BLI_path_abs(name, G.main->name);
362         
363         if (stat(name, &st)) {
364                 ret_val = PF_NOFILE;
365         }
366         else if (st.st_size != pf->size) {
367                 ret_val = PF_DIFFERS;
368         }
369         else {
370                 /* we'll have to compare the two... */
371
372                 file = BLI_open(name, O_BINARY | O_RDONLY, 0);
373                 if (file < 0) {
374                         ret_val = PF_NOFILE;
375                 }
376                 else {
377                         ret_val = PF_EQUAL;
378
379                         for (i = 0; i < pf->size; i += sizeof(buf)) {
380                                 len = pf->size - i;
381                                 if (len > sizeof(buf)) {
382                                         len = sizeof(buf);
383                                 }
384
385                                 if (read(file, buf, len) != len) {
386                                         /* read error ... */
387                                         ret_val = PF_DIFFERS;
388                                         break;
389                                 }
390                                 else {
391                                         if (memcmp(buf, ((char *)pf->data) + i, len)) {
392                                                 ret_val = PF_DIFFERS;
393                                                 break;
394                                         }
395                                 }
396                         }
397                         
398                         close(file);
399                 }
400         }
401         
402         return(ret_val);
403 }
404
405 /* unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
406  *
407  * It returns a char *to the existing file name / new file name or NULL when
408  * there was an error or when the user decides to cancel the operation.
409  */
410
411 char *unpackFile(ReportList *reports, const char *abs_name, const char *local_name, PackedFile *pf, int how)
412 {
413         char *newname = NULL;
414         const char *temp = NULL;
415         
416         // char newabs[FILE_MAX];
417         // char newlocal[FILE_MAX];
418         
419         if (pf != NULL) {
420                 switch (how) {
421                         case -1:
422                         case PF_KEEP:
423                                 break;
424                         case PF_REMOVE:
425                                 temp = abs_name;
426                                 break;
427                         case PF_USE_LOCAL:
428                                 /* if file exists use it */
429                                 if (BLI_exists(local_name)) {
430                                         temp = local_name;
431                                         break;
432                                 }
433                         /* else fall through and create it */
434                         case PF_WRITE_LOCAL:
435                                 if (writePackedFile(reports, local_name, pf, 1) == RET_OK) {
436                                         temp = local_name;
437                                 }
438                                 break;
439                         case PF_USE_ORIGINAL:
440                                 /* if file exists use it */
441                                 if (BLI_exists(abs_name)) {
442                                         temp = abs_name;
443                                         break;
444                                 }
445                         /* else fall through and create it */
446                         case PF_WRITE_ORIGINAL:
447                                 if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) {
448                                         temp = abs_name;
449                                 }
450                                 break;
451                         default:
452                                 printf("unpackFile: unknown return_value %d\n", how);
453                                 break;
454                 }
455                 
456                 if (temp) {
457                         newname = BLI_strdup(temp);
458                 }
459         }
460         
461         return newname;
462 }
463
464
465 int unpackVFont(ReportList *reports, VFont *vfont, int how)
466 {
467         char localname[FILE_MAX], fi[FILE_MAXFILE];
468         char *newname;
469         int ret_value = RET_ERROR;
470         
471         if (vfont != NULL) {
472                 BLI_strncpy(localname, vfont->name, sizeof(localname));
473                 BLI_splitdirstring(localname, fi);
474                 
475                 BLI_snprintf(localname, sizeof(localname), "//fonts/%s", fi);
476                 
477                 newname = unpackFile(reports, vfont->name, localname, vfont->packedfile, how);
478                 if (newname != NULL) {
479                         ret_value = RET_OK;
480                         freePackedFile(vfont->packedfile);
481                         vfont->packedfile = NULL;
482                         BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
483                         MEM_freeN(newname);
484                 }
485         }
486         
487         return (ret_value);
488 }
489
490 int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
491 {
492         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
493         char *newname;
494         int ret_value = RET_ERROR;
495
496         if (sound != NULL) {
497                 BLI_strncpy(localname, sound->name, sizeof(localname));
498                 BLI_splitdirstring(localname, fi);
499                 BLI_snprintf(localname, sizeof(localname), "//sounds/%s", fi);
500
501                 newname = unpackFile(reports, sound->name, localname, sound->packedfile, how);
502                 if (newname != NULL) {
503                         BLI_strncpy(sound->name, newname, sizeof(sound->name));
504                         MEM_freeN(newname);
505
506                         freePackedFile(sound->packedfile);
507                         sound->packedfile = NULL;
508
509                         sound_load(bmain, sound);
510
511                         ret_value = RET_OK;
512                 }
513         }
514         
515         return(ret_value);
516 }
517
518 int unpackImage(ReportList *reports, Image *ima, int how)
519 {
520         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
521         char *newname;
522         int ret_value = RET_ERROR;
523         
524         if (ima != NULL && ima->name[0]) {
525                 BLI_strncpy(localname, ima->name, sizeof(localname));
526                 BLI_splitdirstring(localname, fi);
527                 BLI_snprintf(localname, sizeof(localname), "//textures/%s", fi);
528
529                 newname = unpackFile(reports, ima->name, localname, ima->packedfile, how);
530                 if (newname != NULL) {
531                         ret_value = RET_OK;
532                         freePackedFile(ima->packedfile);
533                         ima->packedfile = NULL;
534                         BLI_strncpy(ima->name, newname, sizeof(ima->name));
535                         MEM_freeN(newname);
536                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
537                 }
538         }
539         
540         return(ret_value);
541 }
542
543 int unpackLibraries(Main *bmain, ReportList *reports)
544 {
545         Library *lib;
546         char *newname;
547         int ret_value = RET_ERROR;
548         
549         for (lib = bmain->library.first; lib; lib = lib->id.next) {
550                 if (lib->packedfile && lib->name[0]) {
551                         
552                         newname = unpackFile(reports, lib->filepath, lib->filepath, lib->packedfile, PF_WRITE_ORIGINAL);
553                         if (newname != NULL) {
554                                 ret_value = RET_OK;
555                                 
556                                 printf("Unpacked .blend library: %s\n", newname);
557                                 
558                                 freePackedFile(lib->packedfile);
559                                 lib->packedfile = NULL;
560
561                                 MEM_freeN(newname);
562                         }
563                 }
564         }
565         
566         return(ret_value);
567 }
568
569 void packLibraries(Main *bmain, ReportList *reports)
570 {
571         Library *lib;
572         
573         /* test for relativenss */
574         for (lib = bmain->library.first; lib; lib = lib->id.next)
575                 if (0 == BLI_path_is_rel(lib->name))
576                         break;
577         
578         if (lib) {
579                 BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->name);
580                 return;
581         }
582         
583         for (lib = bmain->library.first; lib; lib = lib->id.next)
584                 if (lib->packedfile == NULL)
585                         lib->packedfile = newPackedFile(reports, lib->name, bmain->name);
586 }
587
588 void unpackAll(Main *bmain, ReportList *reports, int how)
589 {
590         Image *ima;
591         VFont *vf;
592         bSound *sound;
593
594         for (ima = bmain->image.first; ima; ima = ima->id.next)
595                 if (ima->packedfile)
596                         unpackImage(reports, ima, how);
597
598         for (vf = bmain->vfont.first; vf; vf = vf->id.next)
599                 if (vf->packedfile)
600                         unpackVFont(reports, vf, how);
601
602         for (sound = bmain->sound.first; sound; sound = sound->id.next)
603                 if (sound->packedfile)
604                         unpackSound(bmain, reports, sound, how);
605 }
606