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