This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenkernel/intern/packedFile.c
Brecht Van Lommel b0a1cf2c9a Objects: add Volume object type, and prototypes for Hair and PointCloud
Only the volume object is exposed in the user interface. It is based on OpenVDB
internally. Drawing and rendering code will follow in another commit.
https://wiki.blender.org/wiki/Source/Objects/Volume
https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Volumes

Hair and PointCloud object types are hidden behind a WITH_NEW_OBJECT_TYPES
build option. These are unfinished, and included only to make it easier to
cooperate on development in the future and avoid tricky merges.
https://wiki.blender.org/wiki/Source/Objects/New_Object_Types

Ref T73201, T68981

Differential Revision: https://developer.blender.org/D6945
2020-03-18 11:23:05 +01:00

854 lines
22 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup bke
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifndef WIN32
# include <unistd.h>
#else
# include <io.h>
#endif
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_image_types.h"
#include "DNA_ID.h"
#include "DNA_packedFile_types.h"
#include "DNA_sound_types.h"
#include "DNA_volume_types.h"
#include "DNA_vfont_types.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BKE_font.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_packedFile.h"
#include "BKE_report.h"
#include "BKE_sound.h"
#include "BKE_volume.h"
int BKE_packedfile_seek(PackedFile *pf, int offset, int whence)
{
int oldseek = -1, seek = 0;
if (pf) {
oldseek = pf->seek;
switch (whence) {
case SEEK_CUR:
seek = oldseek + offset;
break;
case SEEK_END:
seek = pf->size + offset;
break;
case SEEK_SET:
seek = offset;
break;
default:
oldseek = -1;
break;
}
if (seek < 0) {
seek = 0;
}
else if (seek > pf->size) {
seek = pf->size;
}
pf->seek = seek;
}
return (oldseek);
}
void BKE_packedfile_rewind(PackedFile *pf)
{
BKE_packedfile_seek(pf, 0, SEEK_SET);
}
int BKE_packedfile_read(PackedFile *pf, void *data, int size)
{
if ((pf != NULL) && (size >= 0) && (data != NULL)) {
if (size + pf->seek > pf->size) {
size = pf->size - pf->seek;
}
if (size > 0) {
memcpy(data, ((char *)pf->data) + pf->seek, size);
}
else {
size = 0;
}
pf->seek += size;
}
else {
size = -1;
}
return (size);
}
int BKE_packedfile_count_all(Main *bmain)
{
Image *ima;
VFont *vf;
bSound *sound;
Volume *volume;
int count = 0;
/* let's check if there are packed files... */
for (ima = bmain->images.first; ima; ima = ima->id.next) {
if (BKE_image_has_packedfile(ima)) {
count++;
}
}
for (vf = bmain->fonts.first; vf; vf = vf->id.next) {
if (vf->packedfile) {
count++;
}
}
for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
if (sound->packedfile) {
count++;
}
}
for (volume = bmain->volumes.first; volume; volume = volume->id.next) {
if (volume->packedfile) {
count++;
}
}
return count;
}
void BKE_packedfile_free(PackedFile *pf)
{
if (pf) {
BLI_assert(pf->data != NULL);
MEM_SAFE_FREE(pf->data);
MEM_freeN(pf);
}
else {
printf("%s: Trying to free a NULL pointer\n", __func__);
}
}
PackedFile *BKE_packedfile_duplicate(const PackedFile *pf_src)
{
BLI_assert(pf_src != NULL);
BLI_assert(pf_src->data != NULL);
PackedFile *pf_dst;
pf_dst = MEM_dupallocN(pf_src);
pf_dst->data = MEM_dupallocN(pf_src->data);
return pf_dst;
}
PackedFile *BKE_packedfile_new_from_memory(void *mem, int memlen)
{
BLI_assert(mem != NULL);
PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
pf->data = mem;
pf->size = memlen;
return pf;
}
PackedFile *BKE_packedfile_new(ReportList *reports, const char *filename, const char *basepath)
{
PackedFile *pf = NULL;
int file, filelen;
char name[FILE_MAX];
void *data;
/* render result has no filename and can be ignored
* any other files with no name can be ignored too */
if (filename[0] == '\0') {
return pf;
}
// XXX waitcursor(1);
/* convert relative filenames to absolute filenames */
BLI_strncpy(name, filename, sizeof(name));
BLI_path_abs(name, basepath);
/* open the file
* and create a PackedFile structure */
file = BLI_open(name, O_BINARY | O_RDONLY, 0);
if (file == -1) {
BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path '%s' not found", name);
}
else {
filelen = BLI_file_descriptor_size(file);
if (filelen == 0) {
/* MEM_mallocN complains about MEM_mallocN(0, "bla");
* we don't care.... */
data = MEM_mallocN(1, "packFile");
}
else {
data = MEM_mallocN(filelen, "packFile");
}
if (read(file, data, filelen) == filelen) {
pf = BKE_packedfile_new_from_memory(data, filelen);
}
else {
MEM_freeN(data);
}
close(file);
}
// XXX waitcursor(0);
return (pf);
}
/* no libraries for now */
void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
{
Image *ima;
VFont *vfont;
bSound *sound;
Volume *volume;
int tot = 0;
for (ima = bmain->images.first; ima; ima = ima->id.next) {
if (BKE_image_has_packedfile(ima) == false && !ID_IS_LINKED(ima)) {
if (ima->source == IMA_SRC_FILE) {
BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id));
tot++;
}
else if (BKE_image_has_multiple_ibufs(ima) && verbose) {
BKE_reportf(reports,
RPT_WARNING,
"Image '%s' skipped, movies, image sequences and packed files not supported",
ima->id.name + 2);
}
}
}
for (vfont = bmain->fonts.first; vfont; vfont = vfont->id.next) {
if (vfont->packedfile == NULL && !ID_IS_LINKED(vfont) &&
BKE_vfont_is_builtin(vfont) == false) {
vfont->packedfile = BKE_packedfile_new(reports, vfont->name, BKE_main_blendfile_path(bmain));
tot++;
}
}
for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
if (sound->packedfile == NULL && !ID_IS_LINKED(sound)) {
sound->packedfile = BKE_packedfile_new(reports, sound->name, BKE_main_blendfile_path(bmain));
tot++;
}
}
for (volume = bmain->volumes.first; volume; volume = volume->id.next) {
if (volume->packedfile == NULL && !ID_IS_LINKED(volume)) {
volume->packedfile = BKE_packedfile_new(
reports, volume->filepath, BKE_main_blendfile_path(bmain));
tot++;
}
}
if (tot > 0) {
BKE_reportf(reports, RPT_INFO, "Packed %d file(s)", tot);
}
else if (verbose) {
BKE_report(reports, RPT_INFO, "No new files have been packed");
}
}
int BKE_packedfile_write_to_file(ReportList *reports,
const char *ref_file_name,
const char *filename,
PackedFile *pf,
const bool guimode)
{
int file, number;
int ret_value = RET_OK;
bool remove_tmp = false;
char name[FILE_MAX];
char tempname[FILE_MAX];
/* void *data; */
if (guimode) {
} // XXX waitcursor(1);
BLI_strncpy(name, filename, sizeof(name));
BLI_path_abs(name, ref_file_name);
if (BLI_exists(name)) {
for (number = 1; number <= 999; number++) {
BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
if (!BLI_exists(tempname)) {
if (BLI_copy(name, tempname) == RET_OK) {
remove_tmp = true;
}
break;
}
}
}
/* make sure the path to the file exists... */
BLI_make_existing_file(name);
file = BLI_open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
if (file == -1) {
BKE_reportf(reports, RPT_ERROR, "Error creating file '%s'", name);
ret_value = RET_ERROR;
}
else {
if (write(file, pf->data, pf->size) != pf->size) {
BKE_reportf(reports, RPT_ERROR, "Error writing file '%s'", name);
ret_value = RET_ERROR;
}
else {
BKE_reportf(reports, RPT_INFO, "Saved packed file to: %s", name);
}
close(file);
}
if (remove_tmp) {
if (ret_value == RET_ERROR) {
if (BLI_rename(tempname, name) != 0) {
BKE_reportf(reports,
RPT_ERROR,
"Error restoring temp file (check files '%s' '%s')",
tempname,
name);
}
}
else {
if (BLI_delete(tempname, false, false) != 0) {
BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
}
}
}
if (guimode) {
} // XXX waitcursor(0);
return (ret_value);
}
/**
* This function compares a packed file to a 'real' file.
* It returns an integer indicating if:
*
* - PF_EQUAL: the packed file and original file are identical
* - PF_DIFFERENT: the packed file and original file differ
* - PF_NOFILE: the original file doesn't exist
*/
enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name,
const char *filename,
PackedFile *pf)
{
BLI_stat_t st;
enum ePF_FileCompare ret_val;
char buf[4096];
char name[FILE_MAX];
BLI_strncpy(name, filename, sizeof(name));
BLI_path_abs(name, ref_file_name);
if (BLI_stat(name, &st) == -1) {
ret_val = PF_CMP_NOFILE;
}
else if (st.st_size != pf->size) {
ret_val = PF_CMP_DIFFERS;
}
else {
/* we'll have to compare the two... */
const int file = BLI_open(name, O_BINARY | O_RDONLY, 0);
if (file == -1) {
ret_val = PF_CMP_NOFILE;
}
else {
ret_val = PF_CMP_EQUAL;
for (int i = 0; i < pf->size; i += sizeof(buf)) {
int len = pf->size - i;
if (len > sizeof(buf)) {
len = sizeof(buf);
}
if (read(file, buf, len) != len) {
/* read error ... */
ret_val = PF_CMP_DIFFERS;
break;
}
else {
if (memcmp(buf, ((char *)pf->data) + i, len)) {
ret_val = PF_CMP_DIFFERS;
break;
}
}
}
close(file);
}
}
return (ret_val);
}
/**
* #BKE_packedfile_unpack_to_file() looks at the existing files (abs_name, local_name)
* and a packed file.
*
* It returns a char *to the existing file name / new file name or NULL when
* there was an error or when the user decides to cancel the operation.
*
* \warning 'abs_name' may be relative still! (use a "//" prefix)
* be sure to run #BLI_path_abs on it first.
*/
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)
{
char *newname = NULL;
const char *temp = NULL;
if (pf != NULL) {
switch (how) {
case PF_KEEP:
break;
case PF_REMOVE:
temp = abs_name;
break;
case PF_USE_LOCAL: {
char temp_abs[FILE_MAX];
BLI_strncpy(temp_abs, local_name, sizeof(temp_abs));
BLI_path_abs(temp_abs, ref_file_name);
/* if file exists use it */
if (BLI_exists(temp_abs)) {
temp = local_name;
break;
}
/* else create it */
ATTR_FALLTHROUGH;
}
case PF_WRITE_LOCAL:
if (BKE_packedfile_write_to_file(reports, ref_file_name, local_name, pf, 1) == RET_OK) {
temp = local_name;
}
break;
case PF_USE_ORIGINAL: {
char temp_abs[FILE_MAX];
BLI_strncpy(temp_abs, abs_name, sizeof(temp_abs));
BLI_path_abs(temp_abs, ref_file_name);
/* if file exists use it */
if (BLI_exists(temp_abs)) {
BKE_reportf(reports, RPT_INFO, "Use existing file (instead of packed): %s", abs_name);
temp = abs_name;
break;
}
/* else create it */
ATTR_FALLTHROUGH;
}
case PF_WRITE_ORIGINAL:
if (BKE_packedfile_write_to_file(reports, ref_file_name, abs_name, pf, 1) == RET_OK) {
temp = abs_name;
}
break;
default:
printf("%s: unknown return_value %u\n", __func__, how);
break;
}
if (temp) {
newname = BLI_strdup(temp);
}
}
return newname;
}
static void unpack_generate_paths(const char *name,
ID *id,
char *r_abspath,
char *r_relpath,
size_t abspathlen,
size_t relpathlen)
{
char tempname[FILE_MAX];
char tempdir[FILE_MAXDIR];
BLI_split_dirfile(name, tempdir, tempname, sizeof(tempdir), sizeof(tempname));
if (tempname[0] == '\0') {
/* Note: we do not have any real way to re-create extension out of data... */
BLI_strncpy(tempname, id->name + 2, sizeof(tempname));
printf("%s\n", tempname);
BLI_filename_make_safe(tempname);
printf("%s\n", tempname);
}
if (tempdir[0] == '\0') {
/* Fallback to relative dir. */
BLI_strncpy(tempdir, "//", sizeof(tempdir));
}
switch (GS(id->name)) {
case ID_VF:
BLI_snprintf(r_relpath, relpathlen, "//fonts/%s", tempname);
break;
case ID_SO:
BLI_snprintf(r_relpath, relpathlen, "//sounds/%s", tempname);
break;
case ID_IM:
BLI_snprintf(r_relpath, relpathlen, "//textures/%s", tempname);
break;
case ID_VO:
BLI_snprintf(r_relpath, relpathlen, "//volumes/%s", tempname);
break;
default:
break;
}
{
size_t len = BLI_strncpy_rlen(r_abspath, tempdir, abspathlen);
BLI_strncpy(r_abspath + len, tempname, abspathlen - len);
}
}
int BKE_packedfile_unpack_vfont(Main *bmain,
ReportList *reports,
VFont *vfont,
enum ePF_FileStatus how)
{
char localname[FILE_MAX], absname[FILE_MAX];
char *newname;
int ret_value = RET_ERROR;
if (vfont != NULL) {
unpack_generate_paths(
vfont->name, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname));
newname = BKE_packedfile_unpack_to_file(
reports, BKE_main_blendfile_path(bmain), absname, localname, vfont->packedfile, how);
if (newname != NULL) {
ret_value = RET_OK;
BKE_packedfile_free(vfont->packedfile);
vfont->packedfile = NULL;
BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
MEM_freeN(newname);
}
}
return (ret_value);
}
int BKE_packedfile_unpack_sound(Main *bmain,
ReportList *reports,
bSound *sound,
enum ePF_FileStatus how)
{
char localname[FILE_MAX], absname[FILE_MAX];
char *newname;
int ret_value = RET_ERROR;
if (sound != NULL) {
unpack_generate_paths(
sound->name, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname));
newname = BKE_packedfile_unpack_to_file(
reports, BKE_main_blendfile_path(bmain), absname, localname, sound->packedfile, how);
if (newname != NULL) {
BLI_strncpy(sound->name, newname, sizeof(sound->name));
MEM_freeN(newname);
BKE_packedfile_free(sound->packedfile);
sound->packedfile = NULL;
BKE_sound_load(bmain, sound);
ret_value = RET_OK;
}
}
return (ret_value);
}
int BKE_packedfile_unpack_image(Main *bmain,
ReportList *reports,
Image *ima,
enum ePF_FileStatus how)
{
int ret_value = RET_ERROR;
if (ima != NULL) {
while (ima->packedfiles.last) {
char localname[FILE_MAX], absname[FILE_MAX];
char *newname;
ImagePackedFile *imapf = ima->packedfiles.last;
unpack_generate_paths(
imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname));
newname = BKE_packedfile_unpack_to_file(
reports, BKE_main_blendfile_path(bmain), absname, localname, imapf->packedfile, how);
if (newname != NULL) {
ImageView *iv;
ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK;
BKE_packedfile_free(imapf->packedfile);
imapf->packedfile = NULL;
/* update the new corresponding view filepath */
iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath));
if (iv) {
BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath));
}
/* keep the new name in the image for non-pack specific reasons */
if (how != PF_REMOVE) {
BLI_strncpy(ima->name, newname, sizeof(imapf->filepath));
}
MEM_freeN(newname);
}
else {
ret_value = RET_ERROR;
}
BLI_remlink(&ima->packedfiles, imapf);
MEM_freeN(imapf);
}
}
if (ret_value == RET_OK) {
BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
}
return (ret_value);
}
int BKE_packedfile_unpack_volume(Main *bmain,
ReportList *reports,
Volume *volume,
enum ePF_FileStatus how)
{
char localname[FILE_MAX], absname[FILE_MAX];
char *newfilepath;
int ret_value = RET_ERROR;
if (volume != NULL) {
unpack_generate_paths(
volume->filepath, (ID *)volume, absname, localname, sizeof(absname), sizeof(localname));
newfilepath = BKE_packedfile_unpack_to_file(
reports, BKE_main_blendfile_path(bmain), absname, localname, volume->packedfile, how);
if (newfilepath != NULL) {
BLI_strncpy(volume->filepath, newfilepath, sizeof(volume->filepath));
MEM_freeN(newfilepath);
BKE_packedfile_free(volume->packedfile);
volume->packedfile = NULL;
BKE_volume_unload(volume);
ret_value = RET_OK;
}
}
return (ret_value);
}
int BKE_packedfile_unpack_all_libraries(Main *bmain, ReportList *reports)
{
Library *lib;
char *newname;
int ret_value = RET_ERROR;
for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
if (lib->packedfile && lib->name[0]) {
newname = BKE_packedfile_unpack_to_file(reports,
BKE_main_blendfile_path(bmain),
lib->filepath,
lib->filepath,
lib->packedfile,
PF_WRITE_ORIGINAL);
if (newname != NULL) {
ret_value = RET_OK;
printf("Unpacked .blend library: %s\n", newname);
BKE_packedfile_free(lib->packedfile);
lib->packedfile = NULL;
MEM_freeN(newname);
}
}
}
return (ret_value);
}
void BKE_packedfile_pack_all_libraries(Main *bmain, ReportList *reports)
{
Library *lib;
/* test for relativenss */
for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
if (!BLI_path_is_rel(lib->name)) {
break;
}
}
if (lib) {
BKE_reportf(reports, RPT_ERROR, "Cannot pack absolute file: '%s'", lib->name);
return;
}
for (lib = bmain->libraries.first; lib; lib = lib->id.next) {
if (lib->packedfile == NULL) {
lib->packedfile = BKE_packedfile_new(reports, lib->name, BKE_main_blendfile_path(bmain));
}
}
}
void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileStatus how)
{
Image *ima;
VFont *vf;
bSound *sound;
Volume *volume;
for (ima = bmain->images.first; ima; ima = ima->id.next) {
if (BKE_image_has_packedfile(ima)) {
BKE_packedfile_unpack_image(bmain, reports, ima, how);
}
}
for (vf = bmain->fonts.first; vf; vf = vf->id.next) {
if (vf->packedfile) {
BKE_packedfile_unpack_vfont(bmain, reports, vf, how);
}
}
for (sound = bmain->sounds.first; sound; sound = sound->id.next) {
if (sound->packedfile) {
BKE_packedfile_unpack_sound(bmain, reports, sound, how);
}
}
for (volume = bmain->volumes.first; volume; volume = volume->id.next) {
if (volume->packedfile) {
BKE_packedfile_unpack_volume(bmain, reports, volume, how);
}
}
}
/* ID should be not NULL, return 1 if there's a packed file */
bool BKE_packedfile_id_check(ID *id)
{
switch (GS(id->name)) {
case ID_IM: {
Image *ima = (Image *)id;
return BKE_image_has_packedfile(ima);
}
case ID_VF: {
VFont *vf = (VFont *)id;
return vf->packedfile != NULL;
}
case ID_SO: {
bSound *snd = (bSound *)id;
return snd->packedfile != NULL;
}
case ID_VO: {
Volume *volume = (Volume *)id;
return volume->packedfile != NULL;
}
case ID_LI: {
Library *li = (Library *)id;
return li->packedfile != NULL;
}
default:
break;
}
return false;
}
/* ID should be not NULL */
void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF_FileStatus how)
{
switch (GS(id->name)) {
case ID_IM: {
Image *ima = (Image *)id;
if (BKE_image_has_packedfile(ima)) {
BKE_packedfile_unpack_image(bmain, reports, ima, how);
}
break;
}
case ID_VF: {
VFont *vf = (VFont *)id;
if (vf->packedfile) {
BKE_packedfile_unpack_vfont(bmain, reports, vf, how);
}
break;
}
case ID_SO: {
bSound *snd = (bSound *)id;
if (snd->packedfile) {
BKE_packedfile_unpack_sound(bmain, reports, snd, how);
}
break;
}
case ID_VO: {
Volume *volume = (Volume *)id;
if (volume->packedfile) {
BKE_packedfile_unpack_volume(bmain, reports, volume, how);
}
break;
}
case ID_LI: {
Library *li = (Library *)id;
BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->name);
break;
}
default:
break;
}
}