svn merge ^/trunk/blender -r45927:46000
[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_sound_types.h"
47 #include "DNA_vfont_types.h"
48 #include "DNA_packedFile_types.h"
49
50 #include "BLI_blenlib.h"
51 #include "BLI_utildefines.h"
52
53 #include "BKE_utildefines.h"
54 #include "BKE_global.h"
55 #include "BKE_main.h"
56 #include "BKE_sound.h"
57 #include "BKE_image.h"
58 #include "BKE_packedFile.h"
59 #include "BKE_report.h"
60
61 #ifdef _WIN32
62 #define open _open
63 #define close _close
64 #define read _read
65 #define write _write
66 #endif
67
68
69 int seekPackedFile(PackedFile *pf, int offset, int whence)
70 {
71         int oldseek = -1, seek = 0;
72
73         if (pf) {
74                 oldseek = pf->seek;
75                 switch(whence) {
76                 case SEEK_CUR:
77                         seek = oldseek + offset;
78                         break;
79                 case SEEK_END:
80                         seek = pf->size + offset;
81                         break;
82                 case SEEK_SET:
83                         seek = offset;
84                         break;
85                 default:
86                         oldseek = -1;
87                 }
88                 if (seek < 0) {
89                         seek = 0;
90                 }
91                 else if (seek > pf->size) {
92                         seek = pf->size;
93                 }
94                 pf->seek = seek;
95         }
96
97         return(oldseek);
98 }
99         
100 void rewindPackedFile(PackedFile *pf)
101 {
102         seekPackedFile(pf, 0, SEEK_SET);
103 }
104
105 int readPackedFile(PackedFile *pf, void *data, int size)
106
107         if ((pf != NULL) && (size >= 0) && (data != NULL)) {
108                 if (size + pf->seek > pf->size) {
109                         size = pf->size - pf->seek;
110                 }
111
112                 if (size > 0) {
113                         memcpy(data, ((char *) pf->data) + pf->seek, size);
114                 }
115                 else {
116                         size = 0;
117                 }
118
119                 pf->seek += size;
120         }
121         else {
122                 size = -1;
123         }
124
125         return(size);
126 }
127
128 int countPackedFiles(Main *bmain)
129 {
130         Image *ima;
131         VFont *vf;
132         bSound *sound;
133         int count = 0;
134         
135         // let's check if there are packed files...
136         for (ima=bmain->image.first; ima; ima=ima->id.next)
137                 if (ima->packedfile)
138                         count++;
139
140         for (vf=bmain->vfont.first; vf; vf=vf->id.next)
141                 if (vf->packedfile)
142                         count++;
143
144         for (sound=bmain->sound.first; sound; sound=sound->id.next)
145                 if (sound->packedfile)
146                         count++;
147
148         return count;
149 }
150
151 void freePackedFile(PackedFile *pf)
152 {
153         if (pf) {
154                 MEM_freeN(pf->data);
155                 MEM_freeN(pf);
156         }
157         else
158                 printf("freePackedFile: Trying to free a NULL pointer\n");
159 }
160         
161 PackedFile *newPackedFileMemory(void *mem, int memlen)
162 {
163         PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
164         pf->data = mem;
165         pf->size = memlen;
166         
167         return pf;
168 }
169
170 PackedFile *newPackedFile(ReportList *reports, const char *filename, const char *basepath)
171 {
172         PackedFile *pf = NULL;
173         int file, filelen;
174         char name[FILE_MAX];
175         void *data;
176         
177         /* render result has no filename and can be ignored
178          * any other files with no name can be ignored too */
179         if (filename[0]=='\0')
180                 return NULL;
181
182         //XXX waitcursor(1);
183         
184         // convert relative filenames to absolute filenames
185         
186         BLI_strncpy(name, filename, sizeof(name));
187         BLI_path_abs(name, basepath);
188         
189         // open the file
190         // and create a PackedFile structure
191
192         file= BLI_open(name, O_BINARY|O_RDONLY,0);
193         if (file <= 0) {
194                 BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path not found: \"%s\"", name);
195         }
196         else {
197                 filelen = BLI_file_descriptor_size(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                 }
204                 else {
205                         data = MEM_mallocN(filelen, "packFile");
206                 }
207                 if (read(file, data, filelen) == filelen) {
208                         pf = newPackedFileMemory(data, filelen);
209                 }
210
211                 close(file);
212         }
213
214         //XXX waitcursor(0);
215                 
216         return (pf);
217 }
218
219 void packAll(Main *bmain, ReportList *reports)
220 {
221         Image *ima;
222         VFont *vf;
223         bSound *sound;
224         
225         for (ima=bmain->image.first; ima; ima=ima->id.next) {
226                 if (ima->packedfile == NULL && ima->id.lib==NULL) { 
227                         if (ima->source==IMA_SRC_FILE) {
228                                 ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
229                         }
230                         else if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
231                                 BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported.", ima->id.name+2);
232                         }
233                 }
234         }
235
236         for (vf=bmain->vfont.first; vf; vf=vf->id.next)
237                 if (vf->packedfile == NULL && vf->id.lib==NULL && strcmp(vf->name, FO_BUILTIN_NAME) != 0)
238                         vf->packedfile = newPackedFile(reports, vf->name, bmain->name);
239
240         for (sound=bmain->sound.first; sound; sound=sound->id.next)
241                 if (sound->packedfile == NULL && sound->id.lib==NULL)
242                         sound->packedfile = newPackedFile(reports, sound->name, bmain->name);
243 }
244
245
246 #if 0
247
248 // attempt to create a function that generates an unique filename
249 // this will work when all funtions in fileops.c understand relative filenames...
250
251 static char *find_new_name(char *name)
252 {
253         char tempname[FILE_MAX];
254         char *newname;
255         size_t len;
256         
257         if (fop_exists(name)) {
258                 for (number = 1; number <= 999; number++) {
259                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d", name, number);
260                         if (! fop_exists(tempname)) {
261                                 break;
262                         }
263                 }
264         }
265         len= strlen(tempname) + 1;
266         newname = MEM_mallocN(len, "find_new_name");
267         memcpy(newname, tempname, len * sizeof(char));
268         return newname;
269 }
270 #endif
271
272 int writePackedFile(ReportList *reports, const char *filename, PackedFile *pf, int guimode)
273 {
274         int file, number, remove_tmp = FALSE;
275         int ret_value = RET_OK;
276         char name[FILE_MAX];
277         char tempname[FILE_MAX];
278 /*      void *data; */
279         
280         if (guimode) {} //XXX  waitcursor(1);
281         
282         BLI_strncpy(name, filename, sizeof(name));
283         BLI_path_abs(name, G.main->name);
284         
285         if (BLI_exists(name)) {
286                 for (number = 1; number <= 999; number++) {
287                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
288                         if (! BLI_exists(tempname)) {
289                                 if (BLI_copy(name, tempname) == RET_OK) {
290                                         remove_tmp = TRUE;
291                                 }
292                                 break;
293                         }
294                 }
295         }
296         
297         // make sure the path to the file exists...
298         BLI_make_existing_file(name);
299         
300         file = BLI_open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
301         if (file >= 0) {
302                 if (write(file, pf->data, pf->size) != pf->size) {
303                         BKE_reportf(reports, RPT_ERROR, "Error writing file: %s", name);
304                         ret_value = RET_ERROR;
305                 }
306                 close(file);
307         }
308         else {
309                 BKE_reportf(reports, RPT_ERROR, "Error creating file: %s", name);
310                 ret_value = RET_ERROR;
311         }
312         
313         if (remove_tmp) {
314                 if (ret_value == RET_ERROR) {
315                         if (BLI_rename(tempname, name) != 0) {
316                                 BKE_reportf(reports, RPT_ERROR, "Error restoring tempfile. Check files: '%s' '%s'", tempname, name);
317                         }
318                 }
319                 else {
320                         if (BLI_delete(tempname, 0, 0) != 0) {
321                                 BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
322                         }
323                 }
324         }
325         
326         if (guimode) {} //XXX waitcursor(0);
327
328         return (ret_value);
329 }
330         
331 /*
332  * This function compares a packed file to a 'real' file.
333  * It returns an integer indicating if:
334  *
335  * PF_EQUAL             - the packed file and original file are identical
336  * PF_DIFFERENT - the packed file and original file differ
337  * PF_NOFILE    - the original file doens't exist
338  */
339
340 int checkPackedFile(const char *filename, PackedFile *pf)
341 {
342         struct stat st;
343         int ret_val, i, len, file;
344         char buf[4096];
345         char name[FILE_MAX];
346         
347         BLI_strncpy(name, filename, sizeof(name));
348         BLI_path_abs(name, G.main->name);
349         
350         if (stat(name, &st)) {
351                 ret_val = PF_NOFILE;
352         }
353         else if (st.st_size != pf->size) {
354                 ret_val = PF_DIFFERS;
355         }
356         else {
357                 // we'll have to compare the two...
358                 
359                 file = BLI_open(name, O_BINARY | O_RDONLY, 0);
360                 if (file < 0) {
361                         ret_val = PF_NOFILE;
362                 }
363                 else {
364                         ret_val = PF_EQUAL;
365                         
366                         for (i = 0; i < pf->size; i += sizeof(buf)) {
367                                 len = pf->size - i;
368                                 if (len > sizeof(buf)) {
369                                         len = sizeof(buf);
370                                 }
371                                 
372                                 if (read(file, buf, len) != len) {
373                                         // read error ...
374                                         ret_val = PF_DIFFERS;
375                                         break;
376                                 }
377                                 else {
378                                         if (memcmp(buf, ((char *)pf->data) + i, len)) {
379                                                 ret_val = PF_DIFFERS;
380                                                 break;
381                                         }
382                                 }
383                         }
384                         
385                         close(file);
386                 }
387         }
388         
389         return(ret_val);
390 }
391
392 /* unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
393  *
394  * It returns a char *to the existing file name / new file name or NULL when
395  * there was an error or when the user decides to cancel the operation.
396  */
397
398 char *unpackFile(ReportList *reports, const char *abs_name, const char *local_name, PackedFile *pf, int how)
399 {
400         char *newname = NULL;
401         const char *temp = NULL;
402         
403         // char newabs[FILE_MAX];
404         // char newlocal[FILE_MAX];
405         
406         if (pf != NULL) {
407                 switch (how) {
408                         case -1:
409                         case PF_KEEP:
410                                 break;
411                         case PF_REMOVE:
412                                 temp= abs_name;
413                                 break;
414                         case PF_USE_LOCAL:
415                                 // if file exists use it
416                                 if (BLI_exists(local_name)) {
417                                         temp = local_name;
418                                         break;
419                                 }
420                                 // else fall through and create it
421                         case PF_WRITE_LOCAL:
422                                 if (writePackedFile(reports, local_name, pf, 1) == RET_OK) {
423                                         temp = local_name;
424                                 }
425                                 break;
426                         case PF_USE_ORIGINAL:
427                                 // if file exists use it
428                                 if (BLI_exists(abs_name)) {
429                                         temp = abs_name;
430                                         break;
431                                 }
432                                 // else fall through and create it
433                         case PF_WRITE_ORIGINAL:
434                                 if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) {
435                                         temp = abs_name;
436                                 }
437                                 break;
438                         default:
439                                 printf("unpackFile: unknown return_value %d\n", how);
440                                 break;
441                 }
442                 
443                 if (temp) {
444                         newname= BLI_strdup(temp);
445                 }
446         }
447         
448         return newname;
449 }
450
451
452 int unpackVFont(ReportList *reports, VFont *vfont, int how)
453 {
454         char localname[FILE_MAX], fi[FILE_MAXFILE];
455         char *newname;
456         int ret_value = RET_ERROR;
457         
458         if (vfont != NULL) {
459                 BLI_strncpy(localname, vfont->name, sizeof(localname));
460                 BLI_splitdirstring(localname, fi);
461                 
462                 BLI_snprintf(localname, sizeof(localname), "//fonts/%s", fi);
463                 
464                 newname = unpackFile(reports, vfont->name, localname, vfont->packedfile, how);
465                 if (newname != NULL) {
466                         ret_value = RET_OK;
467                         freePackedFile(vfont->packedfile);
468                         vfont->packedfile = NULL;
469                         BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
470                         MEM_freeN(newname);
471                 }
472         }
473         
474         return (ret_value);
475 }
476
477 int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
478 {
479         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
480         char *newname;
481         int ret_value = RET_ERROR;
482
483         if (sound != NULL) {
484                 BLI_strncpy(localname, sound->name, sizeof(localname));
485                 BLI_splitdirstring(localname, fi);
486                 BLI_snprintf(localname, sizeof(localname), "//sounds/%s", fi);
487
488                 newname = unpackFile(reports, sound->name, localname, sound->packedfile, how);
489                 if (newname != NULL) {
490                         BLI_strncpy(sound->name, newname, sizeof(sound->name));
491                         MEM_freeN(newname);
492
493                         freePackedFile(sound->packedfile);
494                         sound->packedfile = NULL;
495
496                         sound_load(bmain, sound);
497
498                         ret_value = RET_OK;
499                 }
500         }
501         
502         return(ret_value);
503 }
504
505 int unpackImage(ReportList *reports, Image *ima, int how)
506 {
507         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
508         char *newname;
509         int ret_value = RET_ERROR;
510         
511         if (ima != NULL && ima->name[0]) {
512                 BLI_strncpy(localname, ima->name, sizeof(localname));
513                 BLI_splitdirstring(localname, fi);
514                 BLI_snprintf(localname, sizeof(localname), "//textures/%s", fi);
515
516                 newname = unpackFile(reports, ima->name, localname, ima->packedfile, how);
517                 if (newname != NULL) {
518                         ret_value = RET_OK;
519                         freePackedFile(ima->packedfile);
520                         ima->packedfile = NULL;
521                         BLI_strncpy(ima->name, newname, sizeof(ima->name));
522                         MEM_freeN(newname);
523                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
524                 }
525         }
526         
527         return(ret_value);
528 }
529
530 void unpackAll(Main *bmain, ReportList *reports, int how)
531 {
532         Image *ima;
533         VFont *vf;
534         bSound *sound;
535
536         for (ima=bmain->image.first; ima; ima=ima->id.next)
537                 if (ima->packedfile)
538                         unpackImage(reports, ima, how);
539
540         for (vf=bmain->vfont.first; vf; vf=vf->id.next)
541                 if (vf->packedfile)
542                         unpackVFont(reports, vf, how);
543
544         for (sound=bmain->sound.first; sound; sound=sound->id.next)
545                 if (sound->packedfile)
546                         unpackSound(bmain, reports, sound, how);
547 }
548