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