BLI_open: check returned value for `-1` instead of `< 0`
[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_ID.h"
47 #include "DNA_packedFile_types.h"
48 #include "DNA_sound_types.h"
49 #include "DNA_vfont_types.h"
50
51 #include "BLI_blenlib.h"
52 #include "BLI_utildefines.h"
53
54 #include "BKE_font.h"
55 #include "BKE_global.h"
56 #include "BKE_image.h"
57 #include "BKE_main.h"
58 #include "BKE_packedFile.h"
59 #include "BKE_report.h"
60 #include "BKE_sound.h"
61
62 #ifdef _WIN32
63 #define open _open
64 #define close _close
65 #define read _read
66 #define write _write
67 #endif
68
69
70 int seekPackedFile(PackedFile *pf, int offset, int whence)
71 {
72         int oldseek = -1, seek = 0;
73
74         if (pf) {
75                 oldseek = pf->seek;
76                 switch (whence) {
77                         case SEEK_CUR:
78                                 seek = oldseek + offset;
79                                 break;
80                         case SEEK_END:
81                                 seek = pf->size + offset;
82                                 break;
83                         case SEEK_SET:
84                                 seek = offset;
85                                 break;
86                         default:
87                                 oldseek = -1;
88                                 break;
89                 }
90                 if (seek < 0) {
91                         seek = 0;
92                 }
93                 else if (seek > pf->size) {
94                         seek = pf->size;
95                 }
96                 pf->seek = seek;
97         }
98
99         return(oldseek);
100 }
101         
102 void rewindPackedFile(PackedFile *pf)
103 {
104         seekPackedFile(pf, 0, SEEK_SET);
105 }
106
107 int readPackedFile(PackedFile *pf, void *data, int size)
108
109         if ((pf != NULL) && (size >= 0) && (data != NULL)) {
110                 if (size + pf->seek > pf->size) {
111                         size = pf->size - pf->seek;
112                 }
113
114                 if (size > 0) {
115                         memcpy(data, ((char *) pf->data) + pf->seek, size);
116                 }
117                 else {
118                         size = 0;
119                 }
120
121                 pf->seek += size;
122         }
123         else {
124                 size = -1;
125         }
126
127         return(size);
128 }
129
130 int countPackedFiles(Main *bmain)
131 {
132         Image *ima;
133         VFont *vf;
134         bSound *sound;
135         int count = 0;
136         
137         /* let's check if there are packed files... */
138         for (ima = bmain->image.first; ima; ima = ima->id.next)
139                 if (ima->packedfile)
140                         count++;
141
142         for (vf = bmain->vfont.first; vf; vf = vf->id.next)
143                 if (vf->packedfile)
144                         count++;
145
146         for (sound = bmain->sound.first; sound; sound = sound->id.next)
147                 if (sound->packedfile)
148                         count++;
149
150         return count;
151 }
152
153 void freePackedFile(PackedFile *pf)
154 {
155         if (pf) {
156                 MEM_freeN(pf->data);
157                 MEM_freeN(pf);
158         }
159         else
160                 printf("freePackedFile: Trying to free a NULL pointer\n");
161 }
162
163 PackedFile *dupPackedFile(const PackedFile *pf_src)
164 {
165         PackedFile *pf_dst;
166
167         pf_dst       = MEM_dupallocN(pf_src);
168         pf_dst->data = MEM_dupallocN(pf_src->data);
169
170         return pf_dst;
171 }
172
173 PackedFile *newPackedFileMemory(void *mem, int memlen)
174 {
175         PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
176         pf->data = mem;
177         pf->size = memlen;
178         
179         return pf;
180 }
181
182 PackedFile *newPackedFile(ReportList *reports, const char *filename, const char *basepath)
183 {
184         PackedFile *pf = NULL;
185         int file, filelen;
186         char name[FILE_MAX];
187         void *data;
188         
189         /* render result has no filename and can be ignored
190          * any other files with no name can be ignored too */
191         if (filename[0] == '\0')
192                 return NULL;
193
194         //XXX waitcursor(1);
195         
196         /* convert relative filenames to absolute filenames */
197
198         BLI_strncpy(name, filename, sizeof(name));
199         BLI_path_abs(name, basepath);
200
201         /* open the file
202          * and create a PackedFile structure */
203
204         file = BLI_open(name, O_BINARY | O_RDONLY, 0);
205         if (file == -1) {
206                 BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path '%s' not found", name);
207         }
208         else {
209                 filelen = BLI_file_descriptor_size(file);
210
211                 if (filelen == 0) {
212                         /* MEM_mallocN complains about MEM_mallocN(0, "bla");
213                          * we don't care.... */
214                         data = MEM_mallocN(1, "packFile");
215                 }
216                 else {
217                         data = MEM_mallocN(filelen, "packFile");
218                 }
219                 if (read(file, data, filelen) == filelen) {
220                         pf = newPackedFileMemory(data, filelen);
221                 }
222                 else {
223                         MEM_freeN(data);
224                 }
225
226                 close(file);
227         }
228
229         //XXX waitcursor(0);
230                 
231         return (pf);
232 }
233
234 /* no libraries for now */
235 void packAll(Main *bmain, ReportList *reports)
236 {
237         Image *ima;
238         VFont *vfont;
239         bSound *sound;
240         int tot = 0;
241         
242         for (ima = bmain->image.first; ima; ima = ima->id.next) {
243                 if (ima->packedfile == NULL && ima->id.lib == NULL) {
244                         if (ima->source == IMA_SRC_FILE) {
245                                 ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
246                                 tot ++;
247                         }
248                         else if (BKE_image_is_animated(ima)) {
249                                 BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported",
250                                             ima->id.name + 2);
251                         }
252                 }
253         }
254
255         for (vfont = bmain->vfont.first; vfont; vfont = vfont->id.next) {
256                 if (vfont->packedfile == NULL && vfont->id.lib == NULL && BKE_vfont_is_builtin(vfont) == false) {
257                         vfont->packedfile = newPackedFile(reports, vfont->name, bmain->name);
258                         tot ++;
259                 }
260         }
261
262         for (sound = bmain->sound.first; sound; sound = sound->id.next) {
263                 if (sound->packedfile == NULL && sound->id.lib == NULL) {
264                         sound->packedfile = newPackedFile(reports, sound->name, bmain->name);
265                         tot++;
266                 }
267         }
268         
269         if (tot == 0)
270                 BKE_report(reports, RPT_INFO, "No new files have been packed");
271         else
272                 BKE_reportf(reports, RPT_INFO, "Packed %d files", tot);
273
274
275 }
276
277
278 #if 0
279
280 // attempt to create a function that generates an unique filename
281 // this will work when all funtions in fileops.c understand relative filenames...
282
283 static char *find_new_name(char *name)
284 {
285         char tempname[FILE_MAX];
286         char *newname;
287         size_t len;
288         
289         if (fop_exists(name)) {
290                 for (number = 1; number <= 999; number++) {
291                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d", name, number);
292                         if (!fop_exists(tempname)) {
293                                 break;
294                         }
295                 }
296         }
297         len = strlen(tempname) + 1;
298         newname = MEM_mallocN(len, "find_new_name");
299         memcpy(newname, tempname, len * sizeof(char));
300         return newname;
301 }
302 #endif
303
304 int writePackedFile(ReportList *reports, const char *filename, PackedFile *pf, int guimode)
305 {
306         int file, number;
307         int ret_value = RET_OK;
308         bool remove_tmp = false;
309         char name[FILE_MAX];
310         char tempname[FILE_MAX];
311 /*      void *data; */
312         
313         if (guimode) {} //XXX  waitcursor(1);
314         
315         BLI_strncpy(name, filename, sizeof(name));
316         BLI_path_abs(name, G.main->name);
317         
318         if (BLI_exists(name)) {
319                 for (number = 1; number <= 999; number++) {
320                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
321                         if (!BLI_exists(tempname)) {
322                                 if (BLI_copy(name, tempname) == RET_OK) {
323                                         remove_tmp = true;
324                                 }
325                                 break;
326                         }
327                 }
328         }
329         
330         /* make sure the path to the file exists... */
331         BLI_make_existing_file(name);
332         
333         file = BLI_open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
334         if (file == -1) {
335                 BKE_reportf(reports, RPT_ERROR, "Error creating file '%s'", name);
336                 ret_value = RET_ERROR;
337         }
338         else {
339                 if (write(file, pf->data, pf->size) != pf->size) {
340                         BKE_reportf(reports, RPT_ERROR, "Error writing file '%s'", name);
341                         ret_value = RET_ERROR;
342                 }
343                 else {
344                         BKE_reportf(reports, RPT_INFO, "Saved packed file to: %s", name);
345                 }
346                 
347                 close(file);
348         }
349         
350         if (remove_tmp) {
351                 if (ret_value == RET_ERROR) {
352                         if (BLI_rename(tempname, name) != 0) {
353                                 BKE_reportf(reports, RPT_ERROR, "Error restoring temp file (check files '%s' '%s')", tempname, name);
354                         }
355                 }
356                 else {
357                         if (BLI_delete(tempname, false, false) != 0) {
358                                 BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
359                         }
360                 }
361         }
362         
363         if (guimode) {} //XXX waitcursor(0);
364
365         return (ret_value);
366 }
367         
368 /*
369  * This function compares a packed file to a 'real' file.
370  * It returns an integer indicating if:
371  *
372  * PF_EQUAL             - the packed file and original file are identical
373  * PF_DIFFERENT - the packed file and original file differ
374  * PF_NOFILE    - the original file doens't exist
375  */
376
377 int checkPackedFile(const char *filename, PackedFile *pf)
378 {
379         struct stat st;
380         int ret_val, i, len, file;
381         char buf[4096];
382         char name[FILE_MAX];
383         
384         BLI_strncpy(name, filename, sizeof(name));
385         BLI_path_abs(name, G.main->name);
386         
387         if (BLI_stat(name, &st)) {
388                 ret_val = PF_NOFILE;
389         }
390         else if (st.st_size != pf->size) {
391                 ret_val = PF_DIFFERS;
392         }
393         else {
394                 /* we'll have to compare the two... */
395
396                 file = BLI_open(name, O_BINARY | O_RDONLY, 0);
397                 if (file == -1) {
398                         ret_val = PF_NOFILE;
399                 }
400                 else {
401                         ret_val = PF_EQUAL;
402
403                         for (i = 0; i < pf->size; i += sizeof(buf)) {
404                                 len = pf->size - i;
405                                 if (len > sizeof(buf)) {
406                                         len = sizeof(buf);
407                                 }
408
409                                 if (read(file, buf, len) != len) {
410                                         /* read error ... */
411                                         ret_val = PF_DIFFERS;
412                                         break;
413                                 }
414                                 else {
415                                         if (memcmp(buf, ((char *)pf->data) + i, len)) {
416                                                 ret_val = PF_DIFFERS;
417                                                 break;
418                                         }
419                                 }
420                         }
421                         
422                         close(file);
423                 }
424         }
425         
426         return(ret_val);
427 }
428
429 /* unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
430  *
431  * It returns a char *to the existing file name / new file name or NULL when
432  * there was an error or when the user decides to cancel the operation.
433  */
434
435 char *unpackFile(ReportList *reports, const char *abs_name, const char *local_name, PackedFile *pf, int how)
436 {
437         char *newname = NULL;
438         const char *temp = NULL;
439         
440         // char newabs[FILE_MAX];
441         // char newlocal[FILE_MAX];
442         
443         if (pf != NULL) {
444                 switch (how) {
445                         case -1:
446                         case PF_KEEP:
447                                 break;
448                         case PF_REMOVE:
449                                 temp = abs_name;
450                                 break;
451                         case PF_USE_LOCAL:
452                                 /* if file exists use it */
453                                 if (BLI_exists(local_name)) {
454                                         temp = local_name;
455                                         break;
456                                 }
457                                 /* else create it */
458                                 /* fall-through */
459                         case PF_WRITE_LOCAL:
460                                 if (writePackedFile(reports, local_name, pf, 1) == RET_OK) {
461                                         temp = local_name;
462                                 }
463                                 break;
464                         case PF_USE_ORIGINAL:
465                                 /* if file exists use it */
466                                 if (BLI_exists(abs_name)) {
467                                         BKE_reportf(reports, RPT_INFO, "Use existing file (instead of packed): %s", abs_name);
468                                         temp = abs_name;
469                                         break;
470                                 }
471                                 /* else create it */
472                                 /* fall-through */
473                         case PF_WRITE_ORIGINAL:
474                                 if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) {
475                                         temp = abs_name;
476                                 }
477                                 break;
478                         default:
479                                 printf("unpackFile: unknown return_value %d\n", how);
480                                 break;
481                 }
482                 
483                 if (temp) {
484                         newname = BLI_strdup(temp);
485                 }
486         }
487         
488         return newname;
489 }
490
491
492 int unpackVFont(ReportList *reports, VFont *vfont, int how)
493 {
494         char localname[FILE_MAX], fi[FILE_MAXFILE];
495         char *newname;
496         int ret_value = RET_ERROR;
497         
498         if (vfont != NULL) {
499                 BLI_split_file_part(vfont->name, fi, sizeof(fi));
500                 BLI_snprintf(localname, sizeof(localname), "//fonts/%s", fi);
501                 newname = unpackFile(reports, vfont->name, localname, vfont->packedfile, how);
502                 if (newname != NULL) {
503                         ret_value = RET_OK;
504                         freePackedFile(vfont->packedfile);
505                         vfont->packedfile = NULL;
506                         BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
507                         MEM_freeN(newname);
508                 }
509         }
510         
511         return (ret_value);
512 }
513
514 int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
515 {
516         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
517         char *newname;
518         int ret_value = RET_ERROR;
519
520         if (sound != NULL) {
521                 BLI_split_file_part(sound->name, fi, sizeof(fi));
522                 BLI_snprintf(localname, sizeof(localname), "//sounds/%s", fi);
523                 newname = unpackFile(reports, sound->name, localname, sound->packedfile, how);
524                 if (newname != NULL) {
525                         BLI_strncpy(sound->name, newname, sizeof(sound->name));
526                         MEM_freeN(newname);
527
528                         freePackedFile(sound->packedfile);
529                         sound->packedfile = NULL;
530
531                         sound_load(bmain, sound);
532
533                         ret_value = RET_OK;
534                 }
535         }
536         
537         return(ret_value);
538 }
539
540 int unpackImage(ReportList *reports, Image *ima, int how)
541 {
542         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
543         char *newname;
544         int ret_value = RET_ERROR;
545         
546         if (ima != NULL && ima->name[0]) {
547                 BLI_split_file_part(ima->name, fi, sizeof(fi));
548                 BLI_snprintf(localname, sizeof(localname), "//textures/%s", fi);
549                 newname = unpackFile(reports, ima->name, localname, ima->packedfile, how);
550                 if (newname != NULL) {
551                         ret_value = RET_OK;
552                         freePackedFile(ima->packedfile);
553                         ima->packedfile = NULL;
554                         BLI_strncpy(ima->name, newname, sizeof(ima->name));
555                         MEM_freeN(newname);
556                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
557                 }
558         }
559         
560         return(ret_value);
561 }
562
563 int unpackLibraries(Main *bmain, ReportList *reports)
564 {
565         Library *lib;
566         char *newname;
567         int ret_value = RET_ERROR;
568         
569         for (lib = bmain->library.first; lib; lib = lib->id.next) {
570                 if (lib->packedfile && lib->name[0]) {
571                         
572                         newname = unpackFile(reports, lib->filepath, lib->filepath, lib->packedfile, PF_WRITE_ORIGINAL);
573                         if (newname != NULL) {
574                                 ret_value = RET_OK;
575                                 
576                                 printf("Unpacked .blend library: %s\n", newname);
577                                 
578                                 freePackedFile(lib->packedfile);
579                                 lib->packedfile = NULL;
580
581                                 MEM_freeN(newname);
582                         }
583                 }
584         }
585         
586         return(ret_value);
587 }
588
589 void packLibraries(Main *bmain, ReportList *reports)
590 {
591         Library *lib;
592         
593         /* test for relativenss */
594         for (lib = bmain->library.first; lib; lib = lib->id.next)
595                 if (!BLI_path_is_rel(lib->name))
596                         break;
597         
598         if (lib) {
599                 BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->name);
600                 return;
601         }
602         
603         for (lib = bmain->library.first; lib; lib = lib->id.next)
604                 if (lib->packedfile == NULL)
605                         lib->packedfile = newPackedFile(reports, lib->name, bmain->name);
606 }
607
608 void unpackAll(Main *bmain, ReportList *reports, int how)
609 {
610         Image *ima;
611         VFont *vf;
612         bSound *sound;
613
614         for (ima = bmain->image.first; ima; ima = ima->id.next)
615                 if (ima->packedfile)
616                         unpackImage(reports, ima, how);
617
618         for (vf = bmain->vfont.first; vf; vf = vf->id.next)
619                 if (vf->packedfile)
620                         unpackVFont(reports, vf, how);
621
622         for (sound = bmain->sound.first; sound; sound = sound->id.next)
623                 if (sound->packedfile)
624                         unpackSound(bmain, reports, sound, how);
625 }
626
627 /* ID should be not NULL, return 1 if there's a packed file */
628 bool BKE_pack_check(ID *id)
629 {
630         if (GS(id->name) == ID_IM) {
631                 Image *ima = (Image *)id;
632                 return ima->packedfile != NULL;
633         }
634         if (GS(id->name) == ID_VF) {
635                 VFont *vf = (VFont *)id;
636                 return vf->packedfile != NULL;
637         }
638         if (GS(id->name) == ID_SO) {
639                 bSound *snd = (bSound *)id;
640                 return snd->packedfile != NULL;
641         }
642         if (GS(id->name) == ID_LI) {
643                 Library *li = (Library *)id;
644                 return li->packedfile != NULL;
645         }
646         return false;
647 }
648
649 /* ID should be not NULL */
650 void BKE_unpack_id(Main *bmain, ID *id, ReportList *reports, int how)
651 {
652         if (GS(id->name) == ID_IM) {
653                 Image *ima = (Image *)id;
654                 if (ima->packedfile)
655                         unpackImage(reports, ima, how);
656         }
657         if (GS(id->name) == ID_VF) {
658                 VFont *vf = (VFont *)id;
659                 if (vf->packedfile)
660                         unpackVFont(reports, vf, how);
661         }
662         if (GS(id->name) == ID_SO) {
663                 bSound *snd = (bSound *)id;
664                 if (snd->packedfile)
665                         unpackSound(bmain, reports, snd, how);
666         }
667         if (GS(id->name) == ID_LI) {
668                 Library *li = (Library *)id;
669                 BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->name);
670         }
671 }