Cleanup: move enum unto BKE_packedFile.h
[blender.git] / source / blender / blenkernel / intern / packedFile.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27
28 #ifndef WIN32
29 #  include <unistd.h>
30 #else
31 #  include <io.h>
32 #endif
33 #include <string.h>
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_image_types.h"
37 #include "DNA_ID.h"
38 #include "DNA_packedFile_types.h"
39 #include "DNA_sound_types.h"
40 #include "DNA_vfont_types.h"
41
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_font.h"
46 #include "BKE_image.h"
47 #include "BKE_main.h"
48 #include "BKE_packedFile.h"
49 #include "BKE_report.h"
50 #include "BKE_sound.h"
51
52 int BKE_packedfile_seek(PackedFile *pf, int offset, int whence)
53 {
54   int oldseek = -1, seek = 0;
55
56   if (pf) {
57     oldseek = pf->seek;
58     switch (whence) {
59       case SEEK_CUR:
60         seek = oldseek + offset;
61         break;
62       case SEEK_END:
63         seek = pf->size + offset;
64         break;
65       case SEEK_SET:
66         seek = offset;
67         break;
68       default:
69         oldseek = -1;
70         break;
71     }
72     if (seek < 0) {
73       seek = 0;
74     }
75     else if (seek > pf->size) {
76       seek = pf->size;
77     }
78     pf->seek = seek;
79   }
80
81   return (oldseek);
82 }
83
84 void BKE_packedfile_rewind(PackedFile *pf)
85 {
86   BKE_packedfile_seek(pf, 0, SEEK_SET);
87 }
88
89 int BKE_packedfile_read(PackedFile *pf, void *data, int size)
90 {
91   if ((pf != NULL) && (size >= 0) && (data != NULL)) {
92     if (size + pf->seek > pf->size) {
93       size = pf->size - pf->seek;
94     }
95
96     if (size > 0) {
97       memcpy(data, ((char *)pf->data) + pf->seek, size);
98     }
99     else {
100       size = 0;
101     }
102
103     pf->seek += size;
104   }
105   else {
106     size = -1;
107   }
108
109   return (size);
110 }
111
112 int BKE_packedfile_count_all(Main *bmain)
113 {
114   Image *ima;
115   VFont *vf;
116   bSound *sound;
117   int count = 0;
118
119   /* let's check if there are packed files... */
120   for (ima = bmain->images.first; ima; ima = ima->id.next) {
121     if (BKE_image_has_packedfile(ima)) {
122       count++;
123     }
124   }
125
126   for (vf = bmain->fonts.first; vf; vf = vf->id.next) {
127     if (vf->packedfile) {
128       count++;
129     }
130   }
131
132   for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
133     if (sound->packedfile) {
134       count++;
135     }
136   }
137
138   return count;
139 }
140
141 void BKE_packedfile_free(PackedFile *pf)
142 {
143   if (pf) {
144     MEM_freeN(pf->data);
145     MEM_freeN(pf);
146   }
147   else {
148     printf("%s: Trying to free a NULL pointer\n", __func__);
149   }
150 }
151
152 PackedFile *BKE_packedfile_duplicate(const PackedFile *pf_src)
153 {
154   PackedFile *pf_dst;
155
156   pf_dst = MEM_dupallocN(pf_src);
157   pf_dst->data = MEM_dupallocN(pf_src->data);
158
159   return pf_dst;
160 }
161
162 PackedFile *BKE_packedfile_new_from_memory(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 *BKE_packedfile_new(ReportList *reports, const char *filename, const char *basepath)
172 {
173   PackedFile *pf = NULL;
174   int file, filelen;
175   char name[FILE_MAX];
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
184   // XXX waitcursor(1);
185
186   /* convert relative filenames to absolute filenames */
187
188   BLI_strncpy(name, filename, sizeof(name));
189   BLI_path_abs(name, basepath);
190
191   /* open the file
192    * and create a PackedFile structure */
193
194   file = BLI_open(name, O_BINARY | O_RDONLY, 0);
195   if (file == -1) {
196     BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path '%s' not found", name);
197   }
198   else {
199     filelen = BLI_file_descriptor_size(file);
200
201     if (filelen == 0) {
202       /* MEM_mallocN complains about MEM_mallocN(0, "bla");
203        * we don't care.... */
204       data = MEM_mallocN(1, "packFile");
205     }
206     else {
207       data = MEM_mallocN(filelen, "packFile");
208     }
209     if (read(file, data, filelen) == filelen) {
210       pf = BKE_packedfile_new_from_memory(data, filelen);
211     }
212     else {
213       MEM_freeN(data);
214     }
215
216     close(file);
217   }
218
219   // XXX waitcursor(0);
220
221   return (pf);
222 }
223
224 /* no libraries for now */
225 void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
226 {
227   Image *ima;
228   VFont *vfont;
229   bSound *sound;
230   int tot = 0;
231
232   for (ima = bmain->images.first; ima; ima = ima->id.next) {
233     if (BKE_image_has_packedfile(ima) == false && !ID_IS_LINKED(ima)) {
234       if (ima->source == IMA_SRC_FILE) {
235         BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id));
236         tot++;
237       }
238       else if (BKE_image_is_animated(ima) && verbose) {
239         BKE_reportf(reports,
240                     RPT_WARNING,
241                     "Image '%s' skipped, movies and image sequences not supported",
242                     ima->id.name + 2);
243       }
244     }
245   }
246
247   for (vfont = bmain->fonts.first; vfont; vfont = vfont->id.next) {
248     if (vfont->packedfile == NULL && !ID_IS_LINKED(vfont) &&
249         BKE_vfont_is_builtin(vfont) == false) {
250       vfont->packedfile = BKE_packedfile_new(reports, vfont->name, BKE_main_blendfile_path(bmain));
251       tot++;
252     }
253   }
254
255   for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
256     if (sound->packedfile == NULL && !ID_IS_LINKED(sound)) {
257       sound->packedfile = BKE_packedfile_new(reports, sound->name, BKE_main_blendfile_path(bmain));
258       tot++;
259     }
260   }
261
262   if (tot > 0) {
263     BKE_reportf(reports, RPT_INFO, "Packed %d files", tot);
264   }
265   else if (verbose) {
266     BKE_report(reports, RPT_INFO, "No new files have been packed");
267   }
268 }
269
270 int BKE_packedfile_write_to_file(ReportList *reports,
271                                  const char *ref_file_name,
272                                  const char *filename,
273                                  PackedFile *pf,
274                                  const bool guimode)
275 {
276   int file, number;
277   int ret_value = RET_OK;
278   bool remove_tmp = false;
279   char name[FILE_MAX];
280   char tempname[FILE_MAX];
281   /*      void *data; */
282
283   if (guimode) {
284   }  // XXX  waitcursor(1);
285
286   BLI_strncpy(name, filename, sizeof(name));
287   BLI_path_abs(name, ref_file_name);
288
289   if (BLI_exists(name)) {
290     for (number = 1; number <= 999; number++) {
291       BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
292       if (!BLI_exists(tempname)) {
293         if (BLI_copy(name, tempname) == RET_OK) {
294           remove_tmp = true;
295         }
296         break;
297       }
298     }
299   }
300
301   /* make sure the path to the file exists... */
302   BLI_make_existing_file(name);
303
304   file = BLI_open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
305   if (file == -1) {
306     BKE_reportf(reports, RPT_ERROR, "Error creating file '%s'", name);
307     ret_value = RET_ERROR;
308   }
309   else {
310     if (write(file, pf->data, pf->size) != pf->size) {
311       BKE_reportf(reports, RPT_ERROR, "Error writing file '%s'", name);
312       ret_value = RET_ERROR;
313     }
314     else {
315       BKE_reportf(reports, RPT_INFO, "Saved packed file to: %s", name);
316     }
317
318     close(file);
319   }
320
321   if (remove_tmp) {
322     if (ret_value == RET_ERROR) {
323       if (BLI_rename(tempname, name) != 0) {
324         BKE_reportf(reports,
325                     RPT_ERROR,
326                     "Error restoring temp file (check files '%s' '%s')",
327                     tempname,
328                     name);
329       }
330     }
331     else {
332       if (BLI_delete(tempname, false, false) != 0) {
333         BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
334       }
335     }
336   }
337
338   if (guimode) {
339   }  // XXX waitcursor(0);
340
341   return (ret_value);
342 }
343
344 /**
345  * This function compares a packed file to a 'real' file.
346  * It returns an integer indicating if:
347  *
348  * - PF_EQUAL:     the packed file and original file are identical
349  * - PF_DIFFERENT: the packed file and original file differ
350  * - PF_NOFILE:    the original file doesn't exist
351  */
352 int BKE_packedfile_compare_to_file(const char *ref_file_name, const char *filename, PackedFile *pf)
353 {
354   BLI_stat_t st;
355   int ret_val, i, len, file;
356   char buf[4096];
357   char name[FILE_MAX];
358
359   BLI_strncpy(name, filename, sizeof(name));
360   BLI_path_abs(name, ref_file_name);
361
362   if (BLI_stat(name, &st) == -1) {
363     ret_val = PF_NOFILE;
364   }
365   else if (st.st_size != pf->size) {
366     ret_val = PF_DIFFERS;
367   }
368   else {
369     /* we'll have to compare the two... */
370
371     file = BLI_open(name, O_BINARY | O_RDONLY, 0);
372     if (file == -1) {
373       ret_val = PF_NOFILE;
374     }
375     else {
376       ret_val = PF_EQUAL;
377
378       for (i = 0; i < pf->size; i += sizeof(buf)) {
379         len = pf->size - i;
380         if (len > sizeof(buf)) {
381           len = sizeof(buf);
382         }
383
384         if (read(file, buf, len) != len) {
385           /* read error ... */
386           ret_val = PF_DIFFERS;
387           break;
388         }
389         else {
390           if (memcmp(buf, ((char *)pf->data) + i, len)) {
391             ret_val = PF_DIFFERS;
392             break;
393           }
394         }
395       }
396
397       close(file);
398     }
399   }
400
401   return (ret_val);
402 }
403
404 /**
405  * #BKE_packedfile_unpack_to_file() looks at the existing files (abs_name, local_name)
406  * and a packed file.
407  *
408  * It returns a char *to the existing file name / new file name or NULL when
409  * there was an error or when the user decides to cancel the operation.
410  *
411  * \warning 'abs_name' may be relative still! (use a "//" prefix)
412  * be sure to run #BLI_path_abs on it first.
413  */
414 char *BKE_packedfile_unpack_to_file(ReportList *reports,
415                                     const char *ref_file_name,
416                                     const char *abs_name,
417                                     const char *local_name,
418                                     PackedFile *pf,
419                                     enum ePF_FileStatus how)
420 {
421   char *newname = NULL;
422   const char *temp = NULL;
423
424   if (pf != NULL) {
425     switch (how) {
426       case PF_KEEP:
427         break;
428       case PF_REMOVE:
429         temp = abs_name;
430         break;
431       case PF_USE_LOCAL: {
432         char temp_abs[FILE_MAX];
433
434         BLI_strncpy(temp_abs, local_name, sizeof(temp_abs));
435         BLI_path_abs(temp_abs, ref_file_name);
436
437         /* if file exists use it */
438         if (BLI_exists(temp_abs)) {
439           temp = local_name;
440           break;
441         }
442         /* else create it */
443         ATTR_FALLTHROUGH;
444       }
445       case PF_WRITE_LOCAL:
446         if (BKE_packedfile_write_to_file(reports, ref_file_name, local_name, pf, 1) == RET_OK) {
447           temp = local_name;
448         }
449         break;
450       case PF_USE_ORIGINAL: {
451         char temp_abs[FILE_MAX];
452
453         BLI_strncpy(temp_abs, abs_name, sizeof(temp_abs));
454         BLI_path_abs(temp_abs, ref_file_name);
455
456         /* if file exists use it */
457         if (BLI_exists(temp_abs)) {
458           BKE_reportf(reports, RPT_INFO, "Use existing file (instead of packed): %s", abs_name);
459           temp = abs_name;
460           break;
461         }
462         /* else create it */
463         ATTR_FALLTHROUGH;
464       }
465       case PF_WRITE_ORIGINAL:
466         if (BKE_packedfile_write_to_file(reports, ref_file_name, abs_name, pf, 1) == RET_OK) {
467           temp = abs_name;
468         }
469         break;
470       default:
471         printf("%s: unknown return_value %u\n", __func__, how);
472         break;
473     }
474
475     if (temp) {
476       newname = BLI_strdup(temp);
477     }
478   }
479
480   return newname;
481 }
482
483 static void unpack_generate_paths(const char *name,
484                                   ID *id,
485                                   char *r_abspath,
486                                   char *r_relpath,
487                                   size_t abspathlen,
488                                   size_t relpathlen)
489 {
490   char tempname[FILE_MAX];
491   char tempdir[FILE_MAXDIR];
492
493   BLI_split_dirfile(name, tempdir, tempname, sizeof(tempdir), sizeof(tempname));
494
495   if (tempname[0] == '\0') {
496     /* Note: we do not have any real way to re-create extension out of data... */
497     BLI_strncpy(tempname, id->name + 2, sizeof(tempname));
498     printf("%s\n", tempname);
499     BLI_filename_make_safe(tempname);
500     printf("%s\n", tempname);
501   }
502
503   if (tempdir[0] == '\0') {
504     /* Fallback to relative dir. */
505     BLI_strncpy(tempdir, "//", sizeof(tempdir));
506   }
507
508   switch (GS(id->name)) {
509     case ID_VF:
510       BLI_snprintf(r_relpath, relpathlen, "//fonts/%s", tempname);
511       break;
512     case ID_SO:
513       BLI_snprintf(r_relpath, relpathlen, "//sounds/%s", tempname);
514       break;
515     case ID_IM:
516       BLI_snprintf(r_relpath, relpathlen, "//textures/%s", tempname);
517       break;
518     default:
519       break;
520   }
521
522   {
523     size_t len = BLI_strncpy_rlen(r_abspath, tempdir, abspathlen);
524     BLI_strncpy(r_abspath + len, tempname, abspathlen - len);
525   }
526 }
527
528 int BKE_packedfile_unpack_vfont(Main *bmain,
529                                 ReportList *reports,
530                                 VFont *vfont,
531                                 enum ePF_FileStatus how)
532 {
533   char localname[FILE_MAX], absname[FILE_MAX];
534   char *newname;
535   int ret_value = RET_ERROR;
536
537   if (vfont != NULL) {
538     unpack_generate_paths(
539         vfont->name, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname));
540     newname = BKE_packedfile_unpack_to_file(
541         reports, BKE_main_blendfile_path(bmain), absname, localname, vfont->packedfile, how);
542     if (newname != NULL) {
543       ret_value = RET_OK;
544       BKE_packedfile_free(vfont->packedfile);
545       vfont->packedfile = NULL;
546       BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
547       MEM_freeN(newname);
548     }
549   }
550
551   return (ret_value);
552 }
553
554 int BKE_packedfile_unpack_sound(Main *bmain,
555                                 ReportList *reports,
556                                 bSound *sound,
557                                 enum ePF_FileStatus how)
558 {
559   char localname[FILE_MAX], absname[FILE_MAX];
560   char *newname;
561   int ret_value = RET_ERROR;
562
563   if (sound != NULL) {
564     unpack_generate_paths(
565         sound->name, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname));
566     newname = BKE_packedfile_unpack_to_file(
567         reports, BKE_main_blendfile_path(bmain), absname, localname, sound->packedfile, how);
568     if (newname != NULL) {
569       BLI_strncpy(sound->name, newname, sizeof(sound->name));
570       MEM_freeN(newname);
571
572       BKE_packedfile_free(sound->packedfile);
573       sound->packedfile = NULL;
574
575       BKE_sound_load(bmain, sound);
576
577       ret_value = RET_OK;
578     }
579   }
580
581   return (ret_value);
582 }
583
584 int BKE_packedfile_unpack_image(Main *bmain,
585                                 ReportList *reports,
586                                 Image *ima,
587                                 enum ePF_FileStatus how)
588 {
589   int ret_value = RET_ERROR;
590
591   if (ima != NULL) {
592     while (ima->packedfiles.last) {
593       char localname[FILE_MAX], absname[FILE_MAX];
594       char *newname;
595       ImagePackedFile *imapf = ima->packedfiles.last;
596
597       unpack_generate_paths(
598           imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname));
599       newname = BKE_packedfile_unpack_to_file(
600           reports, BKE_main_blendfile_path(bmain), absname, localname, imapf->packedfile, how);
601
602       if (newname != NULL) {
603         ImageView *iv;
604
605         ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK;
606         BKE_packedfile_free(imapf->packedfile);
607         imapf->packedfile = NULL;
608
609         /* update the new corresponding view filepath */
610         iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath));
611         if (iv) {
612           BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath));
613         }
614
615         /* keep the new name in the image for non-pack specific reasons */
616         if (how != PF_REMOVE) {
617           BLI_strncpy(ima->name, newname, sizeof(imapf->filepath));
618         }
619         MEM_freeN(newname);
620       }
621       else {
622         ret_value = RET_ERROR;
623       }
624
625       BLI_remlink(&ima->packedfiles, imapf);
626       MEM_freeN(imapf);
627     }
628   }
629
630   if (ret_value == RET_OK) {
631     BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
632   }
633
634   return (ret_value);
635 }
636
637 int BKE_packedfile_unpack_all_libraries(Main *bmain, ReportList *reports)
638 {
639   Library *lib;
640   char *newname;
641   int ret_value = RET_ERROR;
642
643   for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
644     if (lib->packedfile && lib->name[0]) {
645
646       newname = BKE_packedfile_unpack_to_file(reports,
647                                               BKE_main_blendfile_path(bmain),
648                                               lib->filepath,
649                                               lib->filepath,
650                                               lib->packedfile,
651                                               PF_WRITE_ORIGINAL);
652       if (newname != NULL) {
653         ret_value = RET_OK;
654
655         printf("Unpacked .blend library: %s\n", newname);
656
657         BKE_packedfile_free(lib->packedfile);
658         lib->packedfile = NULL;
659
660         MEM_freeN(newname);
661       }
662     }
663   }
664
665   return (ret_value);
666 }
667
668 void BKE_packedfile_pack_all_libraries(Main *bmain, ReportList *reports)
669 {
670   Library *lib;
671
672   /* test for relativenss */
673   for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
674     if (!BLI_path_is_rel(lib->name)) {
675       break;
676     }
677   }
678
679   if (lib) {
680     BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->name);
681     return;
682   }
683
684   for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
685     if (lib->packedfile == NULL) {
686       lib->packedfile = BKE_packedfile_new(reports, lib->name, BKE_main_blendfile_path(bmain));
687     }
688   }
689 }
690
691 void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileStatus how)
692 {
693   Image *ima;
694   VFont *vf;
695   bSound *sound;
696
697   for (ima = bmain->images.first; ima; ima = ima->id.next) {
698     if (BKE_image_has_packedfile(ima)) {
699       BKE_packedfile_unpack_image(bmain, reports, ima, how);
700     }
701   }
702
703   for (vf = bmain->fonts.first; vf; vf = vf->id.next) {
704     if (vf->packedfile) {
705       BKE_packedfile_unpack_vfont(bmain, reports, vf, how);
706     }
707   }
708
709   for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
710     if (sound->packedfile) {
711       BKE_packedfile_unpack_sound(bmain, reports, sound, how);
712     }
713   }
714 }
715
716 /* ID should be not NULL, return 1 if there's a packed file */
717 bool BKE_packedfile_id_check(ID *id)
718 {
719   switch (GS(id->name)) {
720     case ID_IM: {
721       Image *ima = (Image *)id;
722       return BKE_image_has_packedfile(ima);
723     }
724     case ID_VF: {
725       VFont *vf = (VFont *)id;
726       return vf->packedfile != NULL;
727     }
728     case ID_SO: {
729       bSound *snd = (bSound *)id;
730       return snd->packedfile != NULL;
731     }
732     case ID_LI: {
733       Library *li = (Library *)id;
734       return li->packedfile != NULL;
735     }
736     default:
737       break;
738   }
739   return false;
740 }
741
742 /* ID should be not NULL */
743 void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF_FileStatus how)
744 {
745   switch (GS(id->name)) {
746     case ID_IM: {
747       Image *ima = (Image *)id;
748       if (BKE_image_has_packedfile(ima)) {
749         BKE_packedfile_unpack_image(bmain, reports, ima, how);
750       }
751       break;
752     }
753     case ID_VF: {
754       VFont *vf = (VFont *)id;
755       if (vf->packedfile) {
756         BKE_packedfile_unpack_vfont(bmain, reports, vf, how);
757       }
758       break;
759     }
760     case ID_SO: {
761       bSound *snd = (bSound *)id;
762       if (snd->packedfile) {
763         BKE_packedfile_unpack_sound(bmain, reports, snd, how);
764       }
765       break;
766     }
767     case ID_LI: {
768       Library *li = (Library *)id;
769       BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->name);
770       break;
771     }
772     default:
773       break;
774   }
775 }