Blender V5.0
packedFile.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstdio>
11#include <fcntl.h>
12#include <sys/stat.h>
13
14#ifndef WIN32
15# include <unistd.h>
16#else
17# include <io.h>
18#endif
19#include "MEM_guardedalloc.h"
20#include <cstring>
21
23
24#include "DNA_ID.h"
25#include "DNA_image_types.h"
26#include "DNA_modifier_types.h"
28#include "DNA_sound_types.h"
29#include "DNA_vfont_types.h"
30#include "DNA_volume_types.h"
31
32#include "BLI_listbase.h"
33#include "BLI_path_utils.hh"
34#include "BLI_string.h"
35#include "BLI_utildefines.h"
36
39#include "BKE_image.hh"
40#include "BKE_image_format.hh"
41#include "BKE_library.hh"
42#include "BKE_main.hh"
43#include "BKE_packedFile.hh"
44#include "BKE_report.hh"
45#include "BKE_sound.h"
46#include "BKE_vfont.hh"
47#include "BKE_volume.hh"
48
49#include "DEG_depsgraph.hh"
50
51#include "IMB_imbuf.hh"
52
53#include "BLO_read_write.hh"
54
55#include "CLG_log.h"
56
57static CLG_LogRef LOG = {"lib.packedfile"};
58
59using namespace blender;
60
61int BKE_packedfile_seek(PackedFile *pf, int offset, int whence)
62{
63 int oldseek = -1, seek = 0;
64
65 if (pf) {
66 oldseek = pf->seek;
67 switch (whence) {
68 case SEEK_CUR:
69 seek = oldseek + offset;
70 break;
71 case SEEK_END:
72 seek = pf->size + offset;
73 break;
74 case SEEK_SET:
75 seek = offset;
76 break;
77 default:
78 oldseek = -1;
79 break;
80 }
81 if (seek < 0) {
82 seek = 0;
83 }
84 else if (seek > pf->size) {
85 seek = pf->size;
86 }
87 pf->seek = seek;
88 }
89
90 return oldseek;
91}
92
94{
95 BKE_packedfile_seek(pf, 0, SEEK_SET);
96}
97
99{
100 if ((pf != nullptr) && (size >= 0) && (data != nullptr)) {
101 if (size + pf->seek > pf->size) {
102 size = pf->size - pf->seek;
103 }
104
105 if (size > 0) {
106 memcpy(data, ((const char *)pf->data) + pf->seek, size);
107 }
108 else {
109 size = 0;
110 }
111
112 pf->seek += size;
113 }
114 else {
115 size = -1;
116 }
117
118 return size;
119}
120
122{
123 Image *ima;
124 VFont *vf;
125 bSound *sound;
126 Volume *volume;
127
129
130 /* let's check if there are packed files... */
131 for (ima = static_cast<Image *>(bmain->images.first); ima;
132 ima = static_cast<Image *>(ima->id.next))
133 {
134 if (BKE_image_has_packedfile(ima) && !ID_IS_LINKED(ima)) {
135 count.individual_files++;
136 }
137 }
138
139 for (vf = static_cast<VFont *>(bmain->fonts.first); vf; vf = static_cast<VFont *>(vf->id.next)) {
140 if (vf->packedfile && !ID_IS_LINKED(vf)) {
141 count.individual_files++;
142 }
143 }
144
145 for (sound = static_cast<bSound *>(bmain->sounds.first); sound;
146 sound = static_cast<bSound *>(sound->id.next))
147 {
148 if (sound->packedfile && !ID_IS_LINKED(sound)) {
149 count.individual_files++;
150 }
151 }
152
153 for (volume = static_cast<Volume *>(bmain->volumes.first); volume;
154 volume = static_cast<Volume *>(volume->id.next))
155 {
156 if (volume->packedfile && !ID_IS_LINKED(volume)) {
157 count.individual_files++;
158 }
159 }
160
161 LISTBASE_FOREACH (Object *, object, &bmain->objects) {
162 if (ID_IS_LINKED(object)) {
163 continue;
164 }
165 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
166 if (md->type == eModifierType_Nodes) {
167 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
168 for (const NodesModifierBake &bake : blender::Span{nmd->bakes, nmd->bakes_num}) {
169 if (bake.packed) {
170 count.bakes++;
171 }
172 }
173 }
174 }
175 }
176
177 return count;
178}
179
181{
182 if (pf) {
183 BLI_assert(pf->data != nullptr);
184 BLI_assert(pf->sharing_info != nullptr);
185
186 pf->sharing_info->remove_user_and_delete_if_last();
187 MEM_freeN(pf);
188 }
189 else {
190 printf("%s: Trying to free a nullptr pointer\n", __func__);
191 }
192}
193
195{
196 BLI_assert(pf_src != nullptr);
197 BLI_assert(pf_src->data != nullptr);
198
199 PackedFile *pf_dst;
200
201 pf_dst = static_cast<PackedFile *>(MEM_dupallocN(pf_src));
202 pf_dst->sharing_info->add_user();
203
204 return pf_dst;
205}
206
208 int memlen,
209 const blender::ImplicitSharingInfo *sharing_info)
210{
211 BLI_assert(mem != nullptr);
212 if (!sharing_info) {
213 /* Assume we are the only owner of that memory currently. */
214 sharing_info = blender::implicit_sharing::info_for_mem_free(const_cast<void *>(mem));
215 }
216
217 PackedFile *pf = MEM_callocN<PackedFile>("PackedFile");
218 pf->data = mem;
219 pf->size = memlen;
220 pf->sharing_info = sharing_info;
221
222 return pf;
223}
224
225PackedFile *BKE_packedfile_new(ReportList *reports, const char *filepath_rel, const char *basepath)
226{
227 char filepath[FILE_MAX];
228
229 /* render result has no filepath and can be ignored
230 * any other files with no name can be ignored too */
231 if (filepath_rel[0] == '\0') {
232 return nullptr;
233 }
234
235 // XXX waitcursor(1);
236
237 /* convert relative filenames to absolute filenames */
238
239 STRNCPY(filepath, filepath_rel);
240 BLI_path_abs(filepath, basepath);
241
242 /* open the file
243 * and create a PackedFile structure */
244
245 const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
246 if (file == -1) {
247 BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path '%s' not found", filepath);
248 return nullptr;
249 }
250
251 PackedFile *pf = nullptr;
252 const size_t file_size = BLI_file_descriptor_size(file);
253 if (file_size == size_t(-1)) {
254 BKE_reportf(reports, RPT_ERROR, "Unable to access the size of, source path '%s'", filepath);
255 }
256 else if (file_size > PACKED_FILE_MAX_SIZE) {
257 BKE_reportf(reports, RPT_ERROR, "Unable to pack files over 2gb, source path '%s'", filepath);
258 }
259 else {
260 /* #MEM_mallocN complains about `MEM_mallocN(0, "...")`,
261 * a single allocation is harmless and doesn't cause any complications. */
262 void *data = MEM_mallocN(std::max(file_size, size_t(1)), "packFile");
263 if (BLI_read(file, data, file_size) == file_size) {
265 }
266 else {
268 }
269 }
270
271 close(file);
272
273 // XXX waitcursor(0);
274
275 return pf;
276}
277
278void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
279{
280 Image *ima;
281 VFont *vfont;
282 bSound *sound;
283 Volume *volume;
284 int tot = 0;
285
286 for (ima = static_cast<Image *>(bmain->images.first); ima;
287 ima = static_cast<Image *>(ima->id.next))
288 {
289 if (BKE_image_has_packedfile(ima) == false && ID_IS_EDITABLE(ima)) {
291 BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id));
292 tot++;
293 }
294 else if (ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE) && verbose) {
295 BKE_reportf(reports,
297 "Image '%s' skipped, packing movies or image sequences not supported",
298 ima->id.name + 2);
299 }
300 }
301 }
302
303 for (vfont = static_cast<VFont *>(bmain->fonts.first); vfont;
304 vfont = static_cast<VFont *>(vfont->id.next))
305 {
306 if (vfont->packedfile == nullptr && ID_IS_EDITABLE(vfont) &&
307 BKE_vfont_is_builtin(vfont) == false)
308 {
310 reports, vfont->filepath, BKE_main_blendfile_path(bmain));
311 tot++;
312 }
313 }
314
315 for (sound = static_cast<bSound *>(bmain->sounds.first); sound;
316 sound = static_cast<bSound *>(sound->id.next))
317 {
318 if (sound->packedfile == nullptr && ID_IS_EDITABLE(sound)) {
320 reports, sound->filepath, BKE_main_blendfile_path(bmain));
321 tot++;
322 }
323 }
324
325 for (volume = static_cast<Volume *>(bmain->volumes.first); volume;
326 volume = static_cast<Volume *>(volume->id.next))
327 {
328 if (volume->packedfile == nullptr && ID_IS_EDITABLE(volume)) {
330 reports, volume->filepath, BKE_main_blendfile_path(bmain));
331 tot++;
332 }
333 }
334
335 LISTBASE_FOREACH (Object *, object, &bmain->objects) {
336 if (ID_IS_LINKED(object)) {
337 continue;
338 }
339 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
340 if (md->type == eModifierType_Nodes) {
341 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
343 blender::bke::bake::pack_geometry_nodes_bake(*bmain, reports, *object, *nmd, bake);
344 }
345 }
346 }
347 }
348
349 if (tot > 0) {
350 BKE_reportf(reports, RPT_INFO, "Packed %d file(s)", tot);
351 }
352 else if (verbose) {
353 BKE_report(reports, RPT_INFO, "No new files have been packed");
354 }
355}
356
358 const char *ref_file_name,
359 const char *filepath_rel,
360 PackedFile *pf)
361{
362 int file, number;
363 int ret_value = RET_OK;
364 bool remove_tmp = false;
365 char filepath[FILE_MAX];
366 char filepath_temp[FILE_MAX];
367 // void *data;
368
369 STRNCPY(filepath, filepath_rel);
370 BLI_path_abs(filepath, ref_file_name);
371
372 if (BLI_exists(filepath)) {
373 for (number = 1; number <= 999; number++) {
374 SNPRINTF(filepath_temp, "%s.%03d_", filepath, number);
375 if (!BLI_exists(filepath_temp)) {
376 if (BLI_copy(filepath, filepath_temp) == RET_OK) {
377 remove_tmp = true;
378 }
379 break;
380 }
381 }
382 }
383
385
386 file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
387 if (file == -1) {
388 BKE_reportf(reports, RPT_ERROR, "Error creating file '%s'", filepath);
389 ret_value = RET_ERROR;
390 }
391 else {
392 if (write(file, pf->data, pf->size) != pf->size) {
393 BKE_reportf(reports, RPT_ERROR, "Error writing file '%s'", filepath);
394 ret_value = RET_ERROR;
395 }
396 else {
397 BKE_reportf(reports, RPT_INFO, "Saved packed file to: %s", filepath);
398 }
399
400 close(file);
401 }
402
403 if (remove_tmp) {
404 if (ret_value == RET_ERROR) {
405 if (BLI_rename_overwrite(filepath_temp, filepath) != 0) {
406 BKE_reportf(reports,
407 RPT_ERROR,
408 "Error restoring temp file (check files '%s' '%s')",
409 filepath_temp,
410 filepath);
411 }
412 }
413 else {
414 if (BLI_delete(filepath_temp, false, false) != 0) {
415 BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", filepath_temp);
416 }
417 }
418 }
419
420 return ret_value;
421}
422
423enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name,
424 const char *filepath_rel,
425 const PackedFile *pf)
426{
427 BLI_stat_t st;
428 enum ePF_FileCompare ret_val;
429 char buf[4096];
430 char filepath[FILE_MAX];
431
432 STRNCPY(filepath, filepath_rel);
433 BLI_path_abs(filepath, ref_file_name);
434
435 if (BLI_stat(filepath, &st) == -1) {
436 ret_val = PF_CMP_NOFILE;
437 }
438 else if (st.st_size != pf->size) {
439 ret_val = PF_CMP_DIFFERS;
440 }
441 else {
442 /* we'll have to compare the two... */
443
444 const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
445 if (file == -1) {
446 ret_val = PF_CMP_NOFILE;
447 }
448 else {
449 ret_val = PF_CMP_EQUAL;
450
451 for (int i = 0; i < pf->size; i += sizeof(buf)) {
452 int len = pf->size - i;
453 len = std::min<ulong>(len, sizeof(buf));
454
455 if (BLI_read(file, buf, len) != len) {
456 /* read error ... */
457 ret_val = PF_CMP_DIFFERS;
458 break;
459 }
460
461 if (memcmp(buf, ((const char *)pf->data) + i, len) != 0) {
462 ret_val = PF_CMP_DIFFERS;
463 break;
464 }
465 }
466
467 close(file);
468 }
469 }
470
471 return ret_val;
472}
473
475 const char *ref_file_name,
476 const char *abs_name,
477 const char *local_name,
478 PackedFile *pf,
479 enum ePF_FileStatus how)
480{
481 char *newname = nullptr;
482 const char *temp = nullptr;
483
484 if (pf != nullptr) {
485 switch (how) {
486 case PF_KEEP:
487 break;
488 case PF_REMOVE:
489 temp = abs_name;
490 break;
491 case PF_USE_LOCAL: {
492 char temp_abs[FILE_MAX];
493
494 STRNCPY(temp_abs, local_name);
495 BLI_path_abs(temp_abs, ref_file_name);
496
497 /* if file exists use it */
498 if (BLI_exists(temp_abs)) {
499 temp = local_name;
500 break;
501 }
502 /* else create it */
504 }
505 case PF_WRITE_LOCAL:
506 if (BKE_packedfile_write_to_file(reports, ref_file_name, local_name, pf) == RET_OK) {
507 temp = local_name;
508 }
509 break;
510 case PF_USE_ORIGINAL: {
511 char temp_abs[FILE_MAX];
512
513 STRNCPY(temp_abs, abs_name);
514 BLI_path_abs(temp_abs, ref_file_name);
515
516 /* if file exists use it */
517 if (BLI_exists(temp_abs)) {
518 BKE_reportf(reports, RPT_INFO, "Use existing file (instead of packed): %s", abs_name);
519 temp = abs_name;
520 break;
521 }
522 /* else create it */
524 }
526 if (BKE_packedfile_write_to_file(reports, ref_file_name, abs_name, pf) == RET_OK) {
527 temp = abs_name;
528 }
529 break;
530 default:
531 printf("%s: unknown return_value %d\n", __func__, how);
532 break;
533 }
534
535 if (temp) {
536 newname = BLI_strdup(temp);
537 }
538 }
539
540 return newname;
541}
542
543static void unpack_generate_paths(const char *filepath,
544 ID *id,
545 char *r_abspath,
546 size_t abspath_maxncpy,
547 char *r_relpath,
548 size_t relpath_maxncpy)
549{
550 const short id_type = GS(id->name);
551 char temp_filename[FILE_MAX];
552 char temp_dirname[FILE_MAXDIR];
553
555 filepath, temp_dirname, sizeof(temp_dirname), temp_filename, sizeof(temp_filename));
556
557 if (temp_filename[0] == '\0') {
558 /* NOTE: we generally do not have any real way to re-create extension out of data. */
559 const size_t len = STRNCPY_RLEN(temp_filename, id->name + 2);
560 printf("%s\n", temp_filename);
561
562 /* For images ensure that the temporary filename contains tile number information as well as
563 * a file extension based on the file magic. */
564 if (id_type == ID_IM) {
565 Image *ima = (Image *)id;
566 ImagePackedFile *imapf = static_cast<ImagePackedFile *>(ima->packedfiles.last);
567 if (imapf != nullptr && imapf->packedfile != nullptr) {
568 const PackedFile *pf = imapf->packedfile;
569 enum eImbFileType ftype = eImbFileType(
570 IMB_test_image_type_from_memory((const uchar *)pf->data, pf->size));
571 if (ima->source == IMA_SRC_TILED) {
572 char tile_number[6];
573 SNPRINTF(tile_number, ".%d", imapf->tile_number);
574 BLI_strncpy(temp_filename + len, tile_number, sizeof(temp_filename) - len);
575 }
576 if (ftype != IMB_FTYPE_NONE) {
577 const int imtype = BKE_ftype_to_imtype(ftype, nullptr);
578 BKE_image_path_ext_from_imtype_ensure(temp_filename, sizeof(temp_filename), imtype);
579 }
580 }
581 }
582
583 BLI_path_make_safe_filename(temp_filename);
584 printf("%s\n", temp_filename);
585 }
586
587 if (temp_dirname[0] == '\0') {
588 /* Fall back to relative dir. */
589 STRNCPY(temp_dirname, "//");
590 }
591
592 {
593 const char *dir_name = nullptr;
594 switch (id_type) {
595 case ID_VF:
596 dir_name = "fonts";
597 break;
598 case ID_SO:
599 dir_name = "sounds";
600 break;
601 case ID_IM:
602 dir_name = "textures";
603 break;
604 case ID_VO:
605 dir_name = "volumes";
606 break;
607 default:
608 break;
609 }
610 if (dir_name) {
611 BLI_path_join(r_relpath, relpath_maxncpy, "//", dir_name, temp_filename);
612 }
613 }
614
615 {
616 size_t len = BLI_strncpy_rlen(r_abspath, temp_dirname, abspath_maxncpy);
617 BLI_strncpy(r_abspath + len, temp_filename, abspath_maxncpy - len);
618 }
619}
620
622 ReportList *reports,
623 ID *id,
624 const char *orig_file_path,
625 PackedFile *pf,
626 enum ePF_FileStatus how)
627{
628 char localname[FILE_MAX], absname[FILE_MAX];
629 char *new_name = nullptr;
630
631 if (id != nullptr) {
633 orig_file_path, id, absname, sizeof(absname), localname, sizeof(localname));
635 reports, BKE_main_blendfile_path(bmain), absname, localname, pf, how);
636 }
637
638 return new_name;
639}
640
642 ReportList *reports,
643 VFont *vfont,
644 enum ePF_FileStatus how)
645{
646 int ret_value = RET_ERROR;
647 if (vfont) {
648 char *new_file_path = BKE_packedfile_unpack(
649 bmain, reports, (ID *)vfont, vfont->filepath, vfont->packedfile, how);
650
651 if (new_file_path != nullptr) {
652 ret_value = RET_OK;
653 BKE_packedfile_free(vfont->packedfile);
654 vfont->packedfile = nullptr;
655 STRNCPY(vfont->filepath, new_file_path);
656 MEM_freeN(new_file_path);
657 }
658 }
659
660 return ret_value;
661}
662
664 ReportList *reports,
665 bSound *sound,
666 enum ePF_FileStatus how)
667{
668 int ret_value = RET_ERROR;
669
670 if (sound != nullptr) {
671 char *new_file_path = BKE_packedfile_unpack(
672 bmain, reports, (ID *)sound, sound->filepath, sound->packedfile, how);
673 if (new_file_path != nullptr) {
674 STRNCPY(sound->filepath, new_file_path);
675 MEM_freeN(new_file_path);
676
677 BKE_packedfile_free(sound->packedfile);
678 sound->packedfile = nullptr;
679
680 BKE_sound_load(bmain, sound);
681
682 ret_value = RET_OK;
683 }
684 }
685
686 return ret_value;
687}
688
690 ReportList *reports,
691 Image *ima,
692 enum ePF_FileStatus how)
693{
694 int ret_value = RET_ERROR;
695
696 if (ima != nullptr) {
697 while (ima->packedfiles.last) {
698 ImagePackedFile *imapf = static_cast<ImagePackedFile *>(ima->packedfiles.last);
699 char *new_file_path = BKE_packedfile_unpack(
700 bmain, reports, (ID *)ima, imapf->filepath, imapf->packedfile, how);
701
702 if (new_file_path != nullptr) {
703 ImageView *iv;
704
705 ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK;
707 imapf->packedfile = nullptr;
708
709 /* update the new corresponding view filepath */
710 iv = static_cast<ImageView *>(
711 BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath)));
712 if (iv) {
713 STRNCPY(iv->filepath, new_file_path);
714 }
715
716 /* keep the new name in the image for non-pack specific reasons */
717 if (how != PF_REMOVE) {
718 STRNCPY(ima->filepath, new_file_path);
719 if (ima->source == IMA_SRC_TILED) {
720 /* Ensure that the Image filepath is kept in a tokenized format. */
721 BKE_image_ensure_tile_token(ima->filepath, sizeof(ima->filepath));
722 }
723 }
724 MEM_freeN(new_file_path);
725 }
726 else {
727 ret_value = RET_ERROR;
728 }
729
730 BLI_remlink(&ima->packedfiles, imapf);
731 MEM_freeN(imapf);
732 }
733 }
734
735 if (ret_value == RET_OK) {
736 BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_RELOAD);
737 }
738
739 return ret_value;
740}
741
743 ReportList *reports,
744 Volume *volume,
745 enum ePF_FileStatus how)
746{
747 int ret_value = RET_ERROR;
748
749 if (volume != nullptr) {
750 char *new_file_path = BKE_packedfile_unpack(
751 bmain, reports, (ID *)volume, volume->filepath, volume->packedfile, how);
752 if (new_file_path != nullptr) {
753 STRNCPY(volume->filepath, new_file_path);
754 MEM_freeN(new_file_path);
755
756 BKE_packedfile_free(volume->packedfile);
757 volume->packedfile = nullptr;
758
759 BKE_volume_unload(volume);
760
761 ret_value = RET_OK;
762 }
763 }
764
765 return ret_value;
766}
767
769{
770 Library *lib;
771 char *newname;
772 int ret_value = RET_ERROR;
773
774 for (lib = static_cast<Library *>(bmain->libraries.first); lib;
775 lib = static_cast<Library *>(lib->id.next))
776 {
777 if (lib->packedfile && lib->filepath[0]) {
778
779 newname = BKE_packedfile_unpack_to_file(reports,
781 lib->runtime->filepath_abs,
782 lib->runtime->filepath_abs,
783 lib->packedfile,
785 if (newname != nullptr) {
786 ret_value = RET_OK;
787
788 printf("Unpacked .blend library: %s\n", newname);
789
790 BKE_packedfile_free(lib->packedfile);
791 lib->packedfile = nullptr;
792
793 MEM_freeN(newname);
794 }
795 }
796 }
797
798 return ret_value;
799}
800
802{
803 Library *lib;
804
805 /* Only allow libraries with relative paths (to avoid issues when unpacking, a limitation that we
806 * might want to lift since the "relativeness" does not really ensure sanity significantly more).
807 */
808 for (lib = static_cast<Library *>(bmain->libraries.first); lib;
809 lib = static_cast<Library *>(lib->id.next))
810 {
811 /* Exception to the above: essential assets have an absolute path and should not prevent to
812 * operator from continuing. */
814 lib->filepath))
815 {
816 continue;
817 }
818
819 if (!BLI_path_is_rel(lib->filepath)) {
820 break;
821 }
822 }
823
824 if (lib) {
825 BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->filepath);
826 return;
827 }
828
829 for (lib = static_cast<Library *>(bmain->libraries.first); lib;
830 lib = static_cast<Library *>(lib->id.next))
831 {
832 /* Do not really pack essential assets though (see above). */
834 lib->filepath))
835 {
836 continue;
837 }
838 if (lib->packedfile == nullptr) {
839 lib->packedfile = BKE_packedfile_new(reports, lib->filepath, BKE_main_blendfile_path(bmain));
840 }
841 }
842}
843
845{
846 Image *ima;
847 VFont *vf;
848 bSound *sound;
849 Volume *volume;
850
851 for (ima = static_cast<Image *>(bmain->images.first); ima;
852 ima = static_cast<Image *>(ima->id.next))
853 {
854 if (BKE_image_has_packedfile(ima) && !ID_IS_LINKED(ima)) {
855 BKE_packedfile_unpack_image(bmain, reports, ima, how);
856 }
857 }
858
859 for (vf = static_cast<VFont *>(bmain->fonts.first); vf; vf = static_cast<VFont *>(vf->id.next)) {
860 if (vf->packedfile && !ID_IS_LINKED(vf)) {
861 BKE_packedfile_unpack_vfont(bmain, reports, vf, how);
862 }
863 }
864
865 for (sound = static_cast<bSound *>(bmain->sounds.first); sound;
866 sound = static_cast<bSound *>(sound->id.next))
867 {
868 if (sound->packedfile && !ID_IS_LINKED(sound)) {
869 BKE_packedfile_unpack_sound(bmain, reports, sound, how);
870 }
871 }
872
873 for (volume = static_cast<Volume *>(bmain->volumes.first); volume;
874 volume = static_cast<Volume *>(volume->id.next))
875 {
876 if (volume->packedfile && !ID_IS_LINKED(volume)) {
877 BKE_packedfile_unpack_volume(bmain, reports, volume, how);
878 }
879 }
880
881 LISTBASE_FOREACH (Object *, object, &bmain->objects) {
882 if (ID_IS_LINKED(object)) {
883 continue;
884 }
885 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
886 if (md->type == eModifierType_Nodes) {
887 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
890 *bmain, reports, *object, *nmd, bake, how);
891 }
892 }
893 }
894 }
895}
896
898{
899 switch (GS(id->name)) {
900 case ID_IM: {
901 const Image *ima = (const Image *)id;
902 return BKE_image_has_packedfile(ima);
903 }
904 case ID_VF: {
905 const VFont *vf = (const VFont *)id;
906 return vf->packedfile != nullptr;
907 }
908 case ID_SO: {
909 const bSound *snd = (const bSound *)id;
910 return snd->packedfile != nullptr;
911 }
912 case ID_VO: {
913 const Volume *volume = (const Volume *)id;
914 return volume->packedfile != nullptr;
915 }
916 case ID_LI: {
917 const Library *li = (const Library *)id;
918 return li->packedfile != nullptr;
919 }
920 default:
921 break;
922 }
923 return false;
924}
925
926void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF_FileStatus how)
927{
928 /* Only unpack when datablock is editable. */
929 if (!ID_IS_EDITABLE(id)) {
930 return;
931 }
932
933 switch (GS(id->name)) {
934 case ID_IM: {
935 Image *ima = (Image *)id;
936 if (BKE_image_has_packedfile(ima)) {
937 BKE_packedfile_unpack_image(bmain, reports, ima, how);
938 }
939 break;
940 }
941 case ID_VF: {
942 VFont *vf = (VFont *)id;
943 if (vf->packedfile) {
944 BKE_packedfile_unpack_vfont(bmain, reports, vf, how);
945 }
946 break;
947 }
948 case ID_SO: {
949 bSound *snd = (bSound *)id;
950 if (snd->packedfile) {
951 BKE_packedfile_unpack_sound(bmain, reports, snd, how);
952 }
953 break;
954 }
955 case ID_VO: {
956 Volume *volume = (Volume *)id;
957 if (volume->packedfile) {
958 BKE_packedfile_unpack_volume(bmain, reports, volume, how);
959 }
960 break;
961 }
962 case ID_LI: {
963 Library *li = (Library *)id;
964 BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->filepath);
965 break;
966 }
967 default:
968 break;
969 }
970}
971
973{
974 if (pf == nullptr) {
975 return;
976 }
977 BLO_write_shared(writer, pf->data, pf->size, pf->sharing_info, [&]() {
978 BLO_write_raw(writer, pf->size, pf->data);
979 });
981}
982
984{
985 BLO_read_struct(reader, PackedFile, pf_p);
986 PackedFile *pf = *pf_p;
987 if (pf == nullptr) {
988 return;
989 }
990 /* NOTE: this is endianness-sensitive. */
991 /* NOTE: there is no way to handle endianness switch here. */
992 pf->sharing_info = BLO_read_shared(reader, &pf->data, [&]() {
993 BLO_read_data_address(reader, &pf->data);
994 /* Do not create an implicit sharing if read data pointer is `nullptr`. */
995 return pf->data ? blender::implicit_sharing::info_for_mem_free(const_cast<void *>(pf->data)) :
996 nullptr;
997 });
998 if (pf->data == nullptr) {
999 /* We cannot allow a #PackedFile with a nullptr data field,
1000 * the whole code assumes this is not possible. See #70315. */
1001 CLOG_WARN(&LOG,
1002 "%s: nullptr packedfile data (source: '%s'), cleaning up...",
1003 __func__,
1004 filepath.c_str());
1005 BLI_assert(pf->sharing_info == nullptr);
1006 MEM_SAFE_FREE(*pf_p);
1007 }
1008}
void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath)
void BKE_image_ensure_tile_token(char *filepath, size_t filepath_maxncpy)
#define IMA_SIGNAL_RELOAD
Definition BKE_image.hh:168
bool BKE_image_has_packedfile(const Image *image)
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
char BKE_ftype_to_imtype(int ftype, const ImbFormatOptions *options)
int BKE_image_path_ext_from_imtype_ensure(char *filepath, size_t filepath_maxncpy, char imtype)
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:887
ePF_FileCompare
@ PF_CMP_EQUAL
@ PF_CMP_NOFILE
@ PF_CMP_DIFFERS
#define RET_OK
constexpr int64_t PACKED_FILE_MAX_SIZE
#define RET_ERROR
ePF_FileStatus
@ PF_USE_ORIGINAL
@ PF_USE_LOCAL
@ PF_KEEP
@ PF_REMOVE
@ PF_WRITE_ORIGINAL
@ PF_WRITE_LOCAL
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_sound_load(struct Main *bmain, struct bSound *sound)
bool BKE_vfont_is_builtin(const VFont *vfont)
Definition vfont.cc:277
Volume data-block.
void BKE_volume_unload(Volume *volume)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
int BLI_copy(const char *path_src, const char *path_dst) ATTR_NONNULL()
#define O_BINARY
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT
Definition storage.cc:217
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
struct stat BLI_stat_t
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:528
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int64_t BLI_read(int fd, void *buf, size_t nbytes)
Definition fileops_c.cc:96
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:452
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
bool BLI_path_contains(const char *container_path, const char *containee_path) ATTR_NONNULL(1
#define BLI_path_join(...)
void BLI_path_split_dir_file(const char *filepath, char *dir, size_t dir_maxncpy, char *file, size_t file_maxncpy) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_make_safe_filename(char *filename) ATTR_NONNULL(1)
#define FILE_MAXDIR
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define STRNCPY_RLEN(dst, src)
Definition BLI_string.h:603
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char char size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned char uchar
#define ELEM(...)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define BLO_read_struct(reader, struct_name, ptr_p)
void BLO_write_shared(BlendWriter *writer, const void *data, size_t approximate_size_in_bytes, const blender::ImplicitSharingInfo *sharing_info, blender::FunctionRef< void()> write_fn)
const blender::ImplicitSharingInfo * BLO_read_shared(BlendDataReader *reader, T **data_ptr, blender::FunctionRef< const blender::ImplicitSharingInfo *()> read_fn)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
ID and Library types, which are fundamental for SDNA.
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_BLEND_PATH(_bmain, _id)
Definition DNA_ID.h:685
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
@ ID_LI
@ ID_IM
@ ID_VO
@ ID_SO
@ ID_VF
@ IMA_SRC_FILE
@ IMA_SRC_MOVIE
@ IMA_SRC_TILED
@ IMA_SRC_SEQUENCE
@ eModifierType_Nodes
int IMB_test_image_type_from_memory(const unsigned char *buf, size_t buf_size)
eImbFileType
@ IMB_FTYPE_NONE
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static int verbose
Definition cineonlib.cc:30
constexpr const char * c_str() const
#define offsetof(t, d)
#define GS(x)
#define pf(_x, _i)
Prefetch 64.
Definition gim_memory.h:48
#define printf(...)
int count
#define LOG(level)
Definition log.h:97
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
StringRefNull essentials_directory_path()
UnpackGeometryNodesBakeResult unpack_geometry_nodes_bake(Main &bmain, ReportList *reports, Object &object, NodesModifierData &nmd, NodesModifierBake &bake, ePF_FileStatus how)
PackGeometryNodesBakeResult pack_geometry_nodes_bake(Main &bmain, ReportList *reports, Object &object, NodesModifierData &nmd, NodesModifierBake &bake)
const ImplicitSharingInfo * info_for_mem_free(void *data)
int BKE_packedfile_unpack_all_libraries(Main *bmain, ReportList *reports)
PackedFile * BKE_packedfile_duplicate(const PackedFile *pf_src)
void BKE_packedfile_free(PackedFile *pf)
PackedFileCount BKE_packedfile_count_all(Main *bmain)
PackedFile * BKE_packedfile_new_from_memory(const void *mem, int memlen, const blender::ImplicitSharingInfo *sharing_info)
void BKE_packedfile_blend_read(BlendDataReader *reader, PackedFile **pf_p, StringRefNull filepath)
bool BKE_packedfile_id_check(const ID *id)
int BKE_packedfile_unpack_sound(Main *bmain, ReportList *reports, bSound *sound, enum ePF_FileStatus how)
void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileStatus how)
void BKE_packedfile_pack_all_libraries(Main *bmain, ReportList *reports)
int BKE_packedfile_read(PackedFile *pf, void *data, int size)
Definition packedFile.cc:98
int BKE_packedfile_unpack_image(Main *bmain, ReportList *reports, Image *ima, enum ePF_FileStatus how)
char * BKE_packedfile_unpack_to_file(ReportList *reports, const char *ref_file_name, const char *abs_name, const char *local_name, PackedFile *pf, enum ePF_FileStatus how)
int BKE_packedfile_unpack_vfont(Main *bmain, ReportList *reports, VFont *vfont, enum ePF_FileStatus how)
void BKE_packedfile_blend_write(BlendWriter *writer, const PackedFile *pf)
char * BKE_packedfile_unpack(Main *bmain, ReportList *reports, ID *id, const char *orig_file_path, PackedFile *pf, enum ePF_FileStatus how)
void BKE_packedfile_rewind(PackedFile *pf)
Definition packedFile.cc:93
int BKE_packedfile_seek(PackedFile *pf, int offset, int whence)
Definition packedFile.cc:61
PackedFile * BKE_packedfile_new(ReportList *reports, const char *filepath_rel, const char *basepath)
static void unpack_generate_paths(const char *filepath, ID *id, char *r_abspath, size_t abspath_maxncpy, char *r_relpath, size_t relpath_maxncpy)
int BKE_packedfile_write_to_file(ReportList *reports, const char *ref_file_name, const char *filepath_rel, PackedFile *pf)
int BKE_packedfile_unpack_volume(Main *bmain, ReportList *reports, Volume *volume, enum ePF_FileStatus how)
enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name, const char *filepath_rel, const PackedFile *pf)
void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF_FileStatus how)
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * next
Definition DNA_ID.h:417
struct PackedFile * packedfile
char filepath[1024]
char filepath[1024]
struct ListBase packedfiles
short source
struct PackedFile * packedfile
Definition DNA_ID.h:572
char filepath[1024]
Definition DNA_ID.h:552
void * last
void * first
ListBase volumes
Definition BKE_main.hh:322
ListBase fonts
Definition BKE_main.hh:293
ListBase sounds
Definition BKE_main.hh:297
ListBase libraries
Definition BKE_main.hh:279
ListBase images
Definition BKE_main.hh:286
ListBase objects
Definition BKE_main.hh:280
NodesModifierBake * bakes
const void * data
const ImplicitSharingInfoHandle * sharing_info
char filepath[1024]
struct PackedFile * packedfile
char filepath[1024]
struct PackedFile * packedfile
struct PackedFile * packedfile
char filepath[1024]
i
Definition text_draw.cc:230
uint len
static DynamicLibrary lib