ClangFormat: apply to source, most of intern
[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 seekPackedFile(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 rewindPackedFile(PackedFile *pf)
85 {
86   seekPackedFile(pf, 0, SEEK_SET);
87 }
88
89 int readPackedFile(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 countPackedFiles(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   for (vf = bmain->fonts.first; vf; vf = vf->id.next)
125     if (vf->packedfile)
126       count++;
127
128   for (sound = bmain->sounds.first; sound; sound = sound->id.next)
129     if (sound->packedfile)
130       count++;
131
132   return count;
133 }
134
135 void freePackedFile(PackedFile *pf)
136 {
137   if (pf) {
138     MEM_freeN(pf->data);
139     MEM_freeN(pf);
140   }
141   else
142     printf("freePackedFile: Trying to free a NULL pointer\n");
143 }
144
145 PackedFile *dupPackedFile(const PackedFile *pf_src)
146 {
147   PackedFile *pf_dst;
148
149   pf_dst = MEM_dupallocN(pf_src);
150   pf_dst->data = MEM_dupallocN(pf_src->data);
151
152   return pf_dst;
153 }
154
155 PackedFile *newPackedFileMemory(void *mem, int memlen)
156 {
157   PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
158   pf->data = mem;
159   pf->size = memlen;
160
161   return pf;
162 }
163
164 PackedFile *newPackedFile(ReportList *reports, const char *filename, const char *basepath)
165 {
166   PackedFile *pf = NULL;
167   int file, filelen;
168   char name[FILE_MAX];
169   void *data;
170
171   /* render result has no filename and can be ignored
172    * any other files with no name can be ignored too */
173   if (filename[0] == '\0')
174     return NULL;
175
176   //XXX waitcursor(1);
177
178   /* convert relative filenames to absolute filenames */
179
180   BLI_strncpy(name, filename, sizeof(name));
181   BLI_path_abs(name, basepath);
182
183   /* open the file
184    * and create a PackedFile structure */
185
186   file = BLI_open(name, O_BINARY | O_RDONLY, 0);
187   if (file == -1) {
188     BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path '%s' not found", name);
189   }
190   else {
191     filelen = BLI_file_descriptor_size(file);
192
193     if (filelen == 0) {
194       /* MEM_mallocN complains about MEM_mallocN(0, "bla");
195        * we don't care.... */
196       data = MEM_mallocN(1, "packFile");
197     }
198     else {
199       data = MEM_mallocN(filelen, "packFile");
200     }
201     if (read(file, data, filelen) == filelen) {
202       pf = newPackedFileMemory(data, filelen);
203     }
204     else {
205       MEM_freeN(data);
206     }
207
208     close(file);
209   }
210
211   //XXX waitcursor(0);
212
213   return (pf);
214 }
215
216 /* no libraries for now */
217 void packAll(Main *bmain, ReportList *reports, bool verbose)
218 {
219   Image *ima;
220   VFont *vfont;
221   bSound *sound;
222   int tot = 0;
223
224   for (ima = bmain->images.first; ima; ima = ima->id.next) {
225     if (BKE_image_has_packedfile(ima) == false && !ID_IS_LINKED(ima)) {
226       if (ima->source == IMA_SRC_FILE) {
227         BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id));
228         tot++;
229       }
230       else if (BKE_image_is_animated(ima) && verbose) {
231         BKE_reportf(reports,
232                     RPT_WARNING,
233                     "Image '%s' skipped, movies and image sequences not supported",
234                     ima->id.name + 2);
235       }
236     }
237   }
238
239   for (vfont = bmain->fonts.first; vfont; vfont = vfont->id.next) {
240     if (vfont->packedfile == NULL && !ID_IS_LINKED(vfont) &&
241         BKE_vfont_is_builtin(vfont) == false) {
242       vfont->packedfile = newPackedFile(reports, vfont->name, BKE_main_blendfile_path(bmain));
243       tot++;
244     }
245   }
246
247   for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
248     if (sound->packedfile == NULL && !ID_IS_LINKED(sound)) {
249       sound->packedfile = newPackedFile(reports, sound->name, BKE_main_blendfile_path(bmain));
250       tot++;
251     }
252   }
253
254   if (tot > 0)
255     BKE_reportf(reports, RPT_INFO, "Packed %d files", tot);
256   else if (verbose)
257     BKE_report(reports, RPT_INFO, "No new files have been packed");
258 }
259
260 int writePackedFile(ReportList *reports,
261                     const char *ref_file_name,
262                     const char *filename,
263                     PackedFile *pf,
264                     const bool guimode)
265 {
266   int file, number;
267   int ret_value = RET_OK;
268   bool remove_tmp = false;
269   char name[FILE_MAX];
270   char tempname[FILE_MAX];
271   /*      void *data; */
272
273   if (guimode) {
274   }  //XXX  waitcursor(1);
275
276   BLI_strncpy(name, filename, sizeof(name));
277   BLI_path_abs(name, ref_file_name);
278
279   if (BLI_exists(name)) {
280     for (number = 1; number <= 999; number++) {
281       BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
282       if (!BLI_exists(tempname)) {
283         if (BLI_copy(name, tempname) == RET_OK) {
284           remove_tmp = true;
285         }
286         break;
287       }
288     }
289   }
290
291   /* make sure the path to the file exists... */
292   BLI_make_existing_file(name);
293
294   file = BLI_open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
295   if (file == -1) {
296     BKE_reportf(reports, RPT_ERROR, "Error creating file '%s'", name);
297     ret_value = RET_ERROR;
298   }
299   else {
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     else {
305       BKE_reportf(reports, RPT_INFO, "Saved packed file to: %s", name);
306     }
307
308     close(file);
309   }
310
311   if (remove_tmp) {
312     if (ret_value == RET_ERROR) {
313       if (BLI_rename(tempname, name) != 0) {
314         BKE_reportf(reports,
315                     RPT_ERROR,
316                     "Error restoring temp file (check files '%s' '%s')",
317                     tempname,
318                     name);
319       }
320     }
321     else {
322       if (BLI_delete(tempname, false, false) != 0) {
323         BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
324       }
325     }
326   }
327
328   if (guimode) {
329   }  //XXX waitcursor(0);
330
331   return (ret_value);
332 }
333
334 /**
335  * This function compares a packed file to a 'real' file.
336  * It returns an integer indicating if:
337  *
338  * - PF_EQUAL:     the packed file and original file are identical
339  * - PF_DIFFERENT: the packed file and original file differ
340  * - PF_NOFILE:    the original file doesn't exist
341  */
342 int checkPackedFile(const char *ref_file_name, const char *filename, PackedFile *pf)
343 {
344   BLI_stat_t st;
345   int ret_val, i, len, file;
346   char buf[4096];
347   char name[FILE_MAX];
348
349   BLI_strncpy(name, filename, sizeof(name));
350   BLI_path_abs(name, ref_file_name);
351
352   if (BLI_stat(name, &st) == -1) {
353     ret_val = PF_NOFILE;
354   }
355   else if (st.st_size != pf->size) {
356     ret_val = PF_DIFFERS;
357   }
358   else {
359     /* we'll have to compare the two... */
360
361     file = BLI_open(name, O_BINARY | O_RDONLY, 0);
362     if (file == -1) {
363       ret_val = PF_NOFILE;
364     }
365     else {
366       ret_val = PF_EQUAL;
367
368       for (i = 0; i < pf->size; i += sizeof(buf)) {
369         len = pf->size - i;
370         if (len > sizeof(buf)) {
371           len = sizeof(buf);
372         }
373
374         if (read(file, buf, len) != len) {
375           /* read error ... */
376           ret_val = PF_DIFFERS;
377           break;
378         }
379         else {
380           if (memcmp(buf, ((char *)pf->data) + i, len)) {
381             ret_val = PF_DIFFERS;
382             break;
383           }
384         }
385       }
386
387       close(file);
388     }
389   }
390
391   return (ret_val);
392 }
393
394 /**
395  * unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
396  *
397  * It returns a char *to the existing file name / new file name or NULL when
398  * there was an error or when the user decides to cancel the operation.
399  *
400  * \warning 'abs_name' may be relative still! (use a "//" prefix) be sure to run #BLI_path_abs on it first.
401  */
402 char *unpackFile(ReportList *reports,
403                  const char *ref_file_name,
404                  const char *abs_name,
405                  const char *local_name,
406                  PackedFile *pf,
407                  int how)
408 {
409   char *newname = NULL;
410   const char *temp = NULL;
411
412   if (pf != NULL) {
413     switch (how) {
414       case -1:
415       case PF_KEEP:
416         break;
417       case PF_REMOVE:
418         temp = abs_name;
419         break;
420       case PF_USE_LOCAL: {
421         char temp_abs[FILE_MAX];
422
423         BLI_strncpy(temp_abs, local_name, sizeof(temp_abs));
424         BLI_path_abs(temp_abs, ref_file_name);
425
426         /* if file exists use it */
427         if (BLI_exists(temp_abs)) {
428           temp = local_name;
429           break;
430         }
431         /* else create it */
432         ATTR_FALLTHROUGH;
433       }
434       case PF_WRITE_LOCAL:
435         if (writePackedFile(reports, ref_file_name, local_name, pf, 1) == RET_OK) {
436           temp = local_name;
437         }
438         break;
439       case PF_USE_ORIGINAL: {
440         char temp_abs[FILE_MAX];
441
442         BLI_strncpy(temp_abs, abs_name, sizeof(temp_abs));
443         BLI_path_abs(temp_abs, ref_file_name);
444
445         /* if file exists use it */
446         if (BLI_exists(temp_abs)) {
447           BKE_reportf(reports, RPT_INFO, "Use existing file (instead of packed): %s", abs_name);
448           temp = abs_name;
449           break;
450         }
451         /* else create it */
452         ATTR_FALLTHROUGH;
453       }
454       case PF_WRITE_ORIGINAL:
455         if (writePackedFile(reports, ref_file_name, abs_name, pf, 1) == RET_OK) {
456           temp = abs_name;
457         }
458         break;
459       default:
460         printf("unpackFile: unknown return_value %d\n", how);
461         break;
462     }
463
464     if (temp) {
465       newname = BLI_strdup(temp);
466     }
467   }
468
469   return newname;
470 }
471
472 static void unpack_generate_paths(const char *name,
473                                   ID *id,
474                                   char *r_abspath,
475                                   char *r_relpath,
476                                   size_t abspathlen,
477                                   size_t relpathlen)
478 {
479   char tempname[FILE_MAX];
480   char tempdir[FILE_MAXDIR];
481
482   BLI_split_dirfile(name, tempdir, tempname, sizeof(tempdir), sizeof(tempname));
483
484   if (tempname[0] == '\0') {
485     /* Note: we do not have any real way to re-create extension out of data... */
486     BLI_strncpy(tempname, id->name + 2, sizeof(tempname));
487     printf("%s\n", tempname);
488     BLI_filename_make_safe(tempname);
489     printf("%s\n", tempname);
490   }
491
492   if (tempdir[0] == '\0') {
493     /* Fallback to relative dir. */
494     BLI_strncpy(tempdir, "//", sizeof(tempdir));
495   }
496
497   switch (GS(id->name)) {
498     case ID_VF:
499       BLI_snprintf(r_relpath, relpathlen, "//fonts/%s", tempname);
500       break;
501     case ID_SO:
502       BLI_snprintf(r_relpath, relpathlen, "//sounds/%s", tempname);
503       break;
504     case ID_IM:
505       BLI_snprintf(r_relpath, relpathlen, "//textures/%s", tempname);
506       break;
507     default:
508       break;
509   }
510
511   {
512     size_t len = BLI_strncpy_rlen(r_abspath, tempdir, abspathlen);
513     BLI_strncpy(r_abspath + len, tempname, abspathlen - len);
514   }
515 }
516
517 int unpackVFont(Main *bmain, ReportList *reports, VFont *vfont, int how)
518 {
519   char localname[FILE_MAX], absname[FILE_MAX];
520   char *newname;
521   int ret_value = RET_ERROR;
522
523   if (vfont != NULL) {
524     unpack_generate_paths(
525         vfont->name, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname));
526     newname = unpackFile(
527         reports, BKE_main_blendfile_path(bmain), absname, localname, vfont->packedfile, how);
528     if (newname != NULL) {
529       ret_value = RET_OK;
530       freePackedFile(vfont->packedfile);
531       vfont->packedfile = NULL;
532       BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
533       MEM_freeN(newname);
534     }
535   }
536
537   return (ret_value);
538 }
539
540 int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
541 {
542   char localname[FILE_MAX], absname[FILE_MAX];
543   char *newname;
544   int ret_value = RET_ERROR;
545
546   if (sound != NULL) {
547     unpack_generate_paths(
548         sound->name, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname));
549     newname = unpackFile(
550         reports, BKE_main_blendfile_path(bmain), absname, localname, sound->packedfile, how);
551     if (newname != NULL) {
552       BLI_strncpy(sound->name, newname, sizeof(sound->name));
553       MEM_freeN(newname);
554
555       freePackedFile(sound->packedfile);
556       sound->packedfile = NULL;
557
558       BKE_sound_load(bmain, sound);
559
560       ret_value = RET_OK;
561     }
562   }
563
564   return (ret_value);
565 }
566
567 int unpackImage(Main *bmain, ReportList *reports, Image *ima, int how)
568 {
569   int ret_value = RET_ERROR;
570
571   if (ima != NULL) {
572     while (ima->packedfiles.last) {
573       char localname[FILE_MAX], absname[FILE_MAX];
574       char *newname;
575       ImagePackedFile *imapf = ima->packedfiles.last;
576
577       unpack_generate_paths(
578           imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname));
579       newname = unpackFile(
580           reports, BKE_main_blendfile_path(bmain), absname, localname, imapf->packedfile, how);
581
582       if (newname != NULL) {
583         ImageView *iv;
584
585         ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK;
586         freePackedFile(imapf->packedfile);
587         imapf->packedfile = NULL;
588
589         /* update the new corresponding view filepath */
590         iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath));
591         if (iv) {
592           BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath));
593         }
594
595         /* keep the new name in the image for non-pack specific reasons */
596         if (how != PF_REMOVE) {
597           BLI_strncpy(ima->name, newname, sizeof(imapf->filepath));
598         }
599         MEM_freeN(newname);
600       }
601       else {
602         ret_value = RET_ERROR;
603       }
604
605       BLI_remlink(&ima->packedfiles, imapf);
606       MEM_freeN(imapf);
607     }
608   }
609
610   if (ret_value == RET_OK) {
611     BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
612   }
613
614   return (ret_value);
615 }
616
617 int unpackLibraries(Main *bmain, ReportList *reports)
618 {
619   Library *lib;
620   char *newname;
621   int ret_value = RET_ERROR;
622
623   for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
624     if (lib->packedfile && lib->name[0]) {
625
626       newname = unpackFile(reports,
627                            BKE_main_blendfile_path(bmain),
628                            lib->filepath,
629                            lib->filepath,
630                            lib->packedfile,
631                            PF_WRITE_ORIGINAL);
632       if (newname != NULL) {
633         ret_value = RET_OK;
634
635         printf("Unpacked .blend library: %s\n", newname);
636
637         freePackedFile(lib->packedfile);
638         lib->packedfile = NULL;
639
640         MEM_freeN(newname);
641       }
642     }
643   }
644
645   return (ret_value);
646 }
647
648 void packLibraries(Main *bmain, ReportList *reports)
649 {
650   Library *lib;
651
652   /* test for relativenss */
653   for (lib = bmain->libraries.first; lib; lib = lib->id.next)
654     if (!BLI_path_is_rel(lib->name))
655       break;
656
657   if (lib) {
658     BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->name);
659     return;
660   }
661
662   for (lib = bmain->libraries.first; lib; lib = lib->id.next)
663     if (lib->packedfile == NULL)
664       lib->packedfile = newPackedFile(reports, lib->name, BKE_main_blendfile_path(bmain));
665 }
666
667 void unpackAll(Main *bmain, ReportList *reports, int how)
668 {
669   Image *ima;
670   VFont *vf;
671   bSound *sound;
672
673   for (ima = bmain->images.first; ima; ima = ima->id.next)
674     if (BKE_image_has_packedfile(ima))
675       unpackImage(bmain, reports, ima, how);
676
677   for (vf = bmain->fonts.first; vf; vf = vf->id.next)
678     if (vf->packedfile)
679       unpackVFont(bmain, reports, vf, how);
680
681   for (sound = bmain->sounds.first; sound; sound = sound->id.next)
682     if (sound->packedfile)
683       unpackSound(bmain, reports, sound, how);
684 }
685
686 /* ID should be not NULL, return 1 if there's a packed file */
687 bool BKE_pack_check(ID *id)
688 {
689   switch (GS(id->name)) {
690     case ID_IM: {
691       Image *ima = (Image *)id;
692       return BKE_image_has_packedfile(ima);
693     }
694     case ID_VF: {
695       VFont *vf = (VFont *)id;
696       return vf->packedfile != NULL;
697     }
698     case ID_SO: {
699       bSound *snd = (bSound *)id;
700       return snd->packedfile != NULL;
701     }
702     case ID_LI: {
703       Library *li = (Library *)id;
704       return li->packedfile != NULL;
705     }
706     default:
707       break;
708   }
709   return false;
710 }
711
712 /* ID should be not NULL */
713 void BKE_unpack_id(Main *bmain, ID *id, ReportList *reports, int how)
714 {
715   switch (GS(id->name)) {
716     case ID_IM: {
717       Image *ima = (Image *)id;
718       if (BKE_image_has_packedfile(ima)) {
719         unpackImage(bmain, reports, ima, how);
720       }
721       break;
722     }
723     case ID_VF: {
724       VFont *vf = (VFont *)id;
725       if (vf->packedfile) {
726         unpackVFont(bmain, reports, vf, how);
727       }
728       break;
729     }
730     case ID_SO: {
731       bSound *snd = (bSound *)id;
732       if (snd->packedfile) {
733         unpackSound(bmain, reports, snd, how);
734       }
735       break;
736     }
737     case ID_LI: {
738       Library *li = (Library *)id;
739       BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->name);
740       break;
741     }
742     default:
743       break;
744   }
745 }