981b3b31e71de3c0a943d4a07d9a2c08eba0ee76
[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 #include <stdio.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35
36 #ifndef WIN32 
37 #include <unistd.h>
38 #else
39 #include <io.h>
40 #endif
41 #include <string.h>
42 #include "MEM_guardedalloc.h"
43
44 #include "DNA_image_types.h"
45 #include "DNA_sound_types.h"
46 #include "DNA_vfont_types.h"
47 #include "DNA_packedFile_types.h"
48
49 #include "BLI_blenlib.h"
50 #include "BLI_utildefines.h"
51
52 #include "BKE_utildefines.h"
53 #include "BKE_global.h"
54 #include "BKE_main.h"
55 #include "BKE_sound.h"
56 #include "BKE_image.h"
57 #include "BKE_packedFile.h"
58 #include "BKE_report.h"
59
60 #ifdef _WIN32
61 #define open _open
62 #define close _close
63 #define read _read
64 #define write _write
65 #endif
66
67
68 int seekPackedFile(PackedFile *pf, int offset, int whence)
69 {
70         int oldseek = -1, seek = 0;
71
72         if (pf) {
73                 oldseek = pf->seek;
74                 switch(whence) {
75                 case SEEK_CUR:
76                         seek = oldseek + offset;
77                         break;
78                 case SEEK_END:
79                         seek = pf->size + offset;
80                         break;
81                 case SEEK_SET:
82                         seek = offset;
83                         break;
84                 default:
85                         oldseek = -1;
86                 }
87                 if (seek < 0) {
88                         seek = 0;
89                 } else if (seek > pf->size) {
90                         seek = pf->size;
91                 }
92                 pf->seek = seek;
93         }
94
95         return(oldseek);
96 }
97         
98 void rewindPackedFile(PackedFile *pf)
99 {
100         seekPackedFile(pf, 0, SEEK_SET);
101 }
102
103 int readPackedFile(PackedFile *pf, void *data, int size)
104
105         if ((pf != NULL) && (size >= 0) && (data != NULL)) {
106                 if (size + pf->seek > pf->size) {
107                         size = pf->size - pf->seek;
108                 }
109
110                 if (size > 0) {
111                         memcpy(data, ((char *) pf->data) + pf->seek, size);
112                 } else {
113                         size = 0;
114                 }
115
116                 pf->seek += size;
117         } else {
118                 size = -1;
119         }
120
121         return(size);
122 }
123
124 int countPackedFiles(Main *bmain)
125 {
126         Image *ima;
127         VFont *vf;
128         bSound *sound;
129         int count = 0;
130         
131         // let's check if there are packed files...
132         for(ima=bmain->image.first; ima; ima=ima->id.next)
133                 if(ima->packedfile)
134                         count++;
135
136         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
137                 if(vf->packedfile)
138                         count++;
139
140         for(sound=bmain->sound.first; sound; sound=sound->id.next)
141                 if(sound->packedfile)
142                         count++;
143
144         return count;
145 }
146
147 void freePackedFile(PackedFile *pf)
148 {
149         if(pf) {
150                 MEM_freeN(pf->data);
151                 MEM_freeN(pf);
152         }
153         else
154                 printf("freePackedFile: Trying to free a NULL pointer\n");
155 }
156         
157 PackedFile *newPackedFileMemory(void *mem, int memlen)
158 {
159         PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
160         pf->data = mem;
161         pf->size = memlen;
162         
163         return pf;
164 }
165
166 PackedFile *newPackedFile(ReportList *reports, const char *filename)
167 {
168         PackedFile *pf = NULL;
169         int file, filelen;
170         char name[FILE_MAXDIR+FILE_MAXFILE];
171         void *data;
172         
173         /* render result has no filename and can be ignored
174          * any other files with no name can be ignored too */
175         if(filename[0]=='\0')
176                 return NULL;
177
178         //XXX waitcursor(1);
179         
180         // convert relative filenames to absolute filenames
181         
182         strcpy(name, filename);
183         BLI_path_abs(name, G.main->name);
184         
185         // open the file
186         // and create a PackedFile structure
187
188         file= open(name, O_BINARY|O_RDONLY);
189         if (file <= 0) {
190                 BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path not found: \"%s\"", name);
191         } else {
192                 filelen = BLI_filesize(file);
193
194                 if (filelen == 0) {
195                         // MEM_mallocN complains about MEM_mallocN(0, "bla");
196                         // we don't care....
197                         data = MEM_mallocN(1, "packFile");
198                 } else {
199                         data = MEM_mallocN(filelen, "packFile");
200                 }
201                 if (read(file, data, filelen) == filelen) {
202                         pf = newPackedFileMemory(data, filelen);
203                 }
204
205                 close(file);
206         }
207
208         //XXX waitcursor(0);
209                 
210         return (pf);
211 }
212
213 void packAll(Main *bmain, ReportList *reports)
214 {
215         Image *ima;
216         VFont *vf;
217         bSound *sound;
218         
219         for(ima=bmain->image.first; ima; ima=ima->id.next) {
220                 if(ima->packedfile == NULL && ima->id.lib==NULL) { 
221                         if(ima->source==IMA_SRC_FILE) {
222                                 ima->packedfile = newPackedFile(reports, ima->name);
223                         }
224                         else if(ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
225                                 BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported.", ima->id.name+2);
226                         }
227                 }
228         }
229
230         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
231                 if(vf->packedfile == NULL && vf->id.lib==NULL && strcmp(vf->name, FO_BUILTIN_NAME) != 0)
232                         vf->packedfile = newPackedFile(reports, vf->name);
233
234         for(sound=bmain->sound.first; sound; sound=sound->id.next)
235                 if(sound->packedfile == NULL && sound->id.lib==NULL)
236                         sound->packedfile = newPackedFile(reports, sound->name);
237 }
238
239
240 /*
241
242 // attempt to create a function that generates an unique filename
243 // this will work when all funtions in fileops.c understand relative filenames...
244
245 static char *find_new_name(char *name)
246 {
247         char tempname[FILE_MAXDIR + FILE_MAXFILE];
248         char *newname;
249         
250         if (fop_exists(name)) {
251                 for (number = 1; number <= 999; number++) {
252                         sprintf(tempname, "%s.%03d", name, number);
253                         if (! fop_exists(tempname)) {
254                                 break;
255                         }
256                 }
257         }
258         
259         newname = mallocN(strlen(tempname) + 1, "find_new_name");
260         strcpy(newname, tempname);
261         
262         return(newname);
263 }
264         
265 */
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_MAXDIR + FILE_MAXFILE];
272         char tempname[FILE_MAXDIR + FILE_MAXFILE];
273 /*      void *data; */
274         
275         if (guimode) {} //XXX  waitcursor(1);
276         
277         strcpy(name, filename);
278         BLI_path_abs(name, G.main->name);
279         
280         if (BLI_exists(name)) {
281                 for (number = 1; number <= 999; number++) {
282                         sprintf(tempname, "%s.%03d_", name, number);
283                         if (! BLI_exists(tempname)) {
284                                 if (BLI_copy_fileops(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_MAXDIR + FILE_MAXFILE];
341         
342         strcpy(name, filename);
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, char *abs_name, char *local_name, PackedFile *pf, int how)
393 {
394         char *newname = NULL, *temp = NULL;
395         
396         // char newabs[FILE_MAXDIR + FILE_MAXFILE];
397         // char newlocal[FILE_MAXDIR + FILE_MAXFILE];
398         
399         if (pf != NULL) {
400                 switch (how) {
401                         case -1:
402                         case PF_KEEP:
403                                 break;
404                         case PF_REMOVE:
405                                 temp= abs_name;
406                                 break;
407                         case PF_USE_LOCAL:
408                                 // if file exists use it
409                                 if (BLI_exists(local_name)) {
410                                         temp = local_name;
411                                         break;
412                                 }
413                                 // else fall through and create it
414                         case PF_WRITE_LOCAL:
415                                 if (writePackedFile(reports, local_name, pf, 1) == RET_OK) {
416                                         temp = local_name;
417                                 }
418                                 break;
419                         case PF_USE_ORIGINAL:
420                                 // if file exists use it
421                                 if (BLI_exists(abs_name)) {
422                                         temp = abs_name;
423                                         break;
424                                 }
425                                 // else fall through and create it
426                         case PF_WRITE_ORIGINAL:
427                                 if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) {
428                                         temp = abs_name;
429                                 }
430                                 break;
431                         default:
432                                 printf("unpackFile: unknown return_value %d\n", how);
433                                 break;
434                 }
435                 
436                 if (temp) {
437                         newname = MEM_mallocN(strlen(temp) + 1, "unpack_file newname");
438                         strcpy(newname, temp);
439                 }
440         }
441         
442         return (newname);
443 }
444
445
446 int unpackVFont(ReportList *reports, VFont *vfont, int how)
447 {
448         char localname[FILE_MAXDIR + FILE_MAXFILE], fi[FILE_MAXFILE];
449         char *newname;
450         int ret_value = RET_ERROR;
451         
452         if (vfont != NULL) {
453                 strcpy(localname, vfont->name);
454                 BLI_splitdirstring(localname, fi);
455                 
456                 sprintf(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                         strcpy(vfont->name, newname);
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                 strcpy(localname, sound->name);
479                 BLI_splitdirstring(localname, fi);
480                 sprintf(localname, "//sounds/%s", fi);
481
482                 newname = unpackFile(reports, sound->name, localname, sound->packedfile, how);
483                 if (newname != NULL) {
484                         strcpy(sound->name, newname);
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                 strcpy(localname, ima->name);
507                 BLI_splitdirstring(localname, fi);
508                 sprintf(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                         strcpy(ima->name, newname);
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