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