Fix: Proper error handling for STL, PLY, and OBJ IO

Properly handle exceptions from STL and PLY code to prevent crashes on
invalid file paths.

This will now also Report errors/warnings to the callers of these
formats as well. For the UI this means a Report banner and Info editor
entry. For Python scripts this means an exception instead of silently
continuing.

Related to #117881
Pull Request: https://projects.blender.org/blender/blender/pulls/118731
This commit is contained in:
Jesse Yurkovich
2024-02-26 20:45:46 +01:00
committed by Jesse Yurkovich
parent 3748e49034
commit 5d9e127234
17 changed files with 113 additions and 27 deletions
+4
View File
@@ -106,6 +106,8 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
export_params.export_smooth_groups = RNA_boolean_get(op->ptr, "export_smooth_groups");
export_params.smooth_groups_bitflags = RNA_boolean_get(op->ptr, "smooth_group_bitflags");
export_params.reports = op->reports;
OBJ_export(C, &export_params);
return OPERATOR_FINISHED;
@@ -404,6 +406,8 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op)
import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0);
import_params.clear_selection = true;
import_params.reports = op->reports;
const auto paths = blender::ed::io::paths_from_operator_properties(op->ptr);
if (paths.is_empty()) {
+6 -2
View File
@@ -70,7 +70,7 @@ static int wm_ply_export_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No filepath given");
return OPERATOR_CANCELLED;
}
PLYExportParams export_params = {"\0"};
PLYExportParams export_params{};
export_params.file_base_for_tests[0] = '\0';
RNA_string_get(op->ptr, "filepath", export_params.filepath);
export_params.blen_filepath = CTX_data_main(C)->filepath;
@@ -88,6 +88,8 @@ static int wm_ply_export_exec(bContext *C, wmOperator *op)
export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh");
export_params.ascii_format = RNA_boolean_get(op->ptr, "ascii_format");
export_params.reports = op->reports;
PLY_export(C, &export_params);
return OPERATOR_FINISHED;
@@ -251,6 +253,8 @@ static int wm_ply_import_exec(bContext *C, wmOperator *op)
params.import_attributes = RNA_boolean_get(op->ptr, "import_attributes");
params.vertex_colors = ePLYVertexColorMode(RNA_enum_get(op->ptr, "import_colors"));
params.reports = op->reports;
const auto paths = blender::ed::io::paths_from_operator_properties(op->ptr);
if (paths.is_empty()) {
@@ -259,7 +263,7 @@ static int wm_ply_import_exec(bContext *C, wmOperator *op)
}
for (const auto &path : paths) {
STRNCPY(params.filepath, path.c_str());
PLY_import(C, &params, op);
PLY_import(C, &params);
};
Scene *scene = CTX_data_scene(C);
+5 -1
View File
@@ -48,7 +48,7 @@ static int wm_stl_export_execute(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
STLExportParams export_params;
STLExportParams export_params{};
RNA_string_get(op->ptr, "filepath", export_params.filepath);
export_params.forward_axis = eIOAxis(RNA_enum_get(op->ptr, "forward_axis"));
export_params.up_axis = eIOAxis(RNA_enum_get(op->ptr, "up_axis"));
@@ -59,6 +59,8 @@ static int wm_stl_export_execute(bContext *C, wmOperator *op)
export_params.ascii_format = RNA_boolean_get(op->ptr, "ascii_format");
export_params.use_batch = RNA_boolean_get(op->ptr, "use_batch");
export_params.reports = op->reports;
STL_export(C, &export_params);
return OPERATOR_FINISHED;
@@ -189,6 +191,8 @@ static int wm_stl_import_exec(bContext *C, wmOperator *op)
params.global_scale = RNA_float_get(op->ptr, "global_scale");
params.use_mesh_validate = RNA_boolean_get(op->ptr, "use_mesh_validate");
params.reports = op->reports;
const auto paths = blender::ed::io::paths_from_operator_properties(op->ptr);
if (paths.is_empty()) {
+2 -2
View File
@@ -32,9 +32,9 @@ void PLY_export(bContext *C, const PLYExportParams *export_params)
report_duration("export", start_time, export_params->filepath);
}
void PLY_import(bContext *C, const PLYImportParams *import_params, wmOperator *op)
void PLY_import(bContext *C, const PLYImportParams *import_params)
{
TimePoint start_time = Clock::now();
blender::io::ply::importer_main(C, *import_params, op);
blender::io::ply::importer_main(C, *import_params);
report_duration("import", start_time, import_params->filepath);
}
+5 -1
View File
@@ -45,6 +45,8 @@ struct PLYExportParams {
ePLYVertexColorMode vertex_colors;
bool export_attributes;
bool export_triangulated_mesh;
ReportList *reports = nullptr;
};
struct PLYImportParams {
@@ -57,6 +59,8 @@ struct PLYImportParams {
ePLYVertexColorMode vertex_colors;
bool import_attributes;
bool merge_verts;
ReportList *reports = nullptr;
};
/**
@@ -64,4 +68,4 @@ struct PLYImportParams {
*/
void PLY_export(bContext *C, const PLYExportParams *export_params);
void PLY_import(bContext *C, const PLYImportParams *import_params, wmOperator *op);
void PLY_import(bContext *C, const PLYImportParams *import_params);
+17 -4
View File
@@ -6,7 +6,10 @@
* \ingroup ply
*/
#include <cstdio>
#include "BKE_layer.hh"
#include "BKE_report.h"
#include "DNA_collection_types.h"
#include "DNA_scene_types.h"
@@ -32,11 +35,21 @@ void exporter_main(bContext *C, const PLYExportParams &export_params)
std::unique_ptr<FileBuffer> buffer;
if (export_params.ascii_format) {
buffer = std::make_unique<FileBufferAscii>(export_params.filepath);
try {
if (export_params.ascii_format) {
buffer = std::make_unique<FileBufferAscii>(export_params.filepath);
}
else {
buffer = std::make_unique<FileBufferBinary>(export_params.filepath);
}
}
else {
buffer = std::make_unique<FileBufferBinary>(export_params.filepath);
catch (const std::system_error &ex) {
fprintf(stderr, "%s\n", ex.what());
BKE_reportf(export_params.reports,
RPT_ERROR,
"PLY Export: Cannot open file '%s'",
export_params.filepath);
return;
}
write_header(*buffer.get(), *plyData.get(), export_params);
+8 -8
View File
@@ -6,6 +6,7 @@
* \ingroup ply
*/
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_mesh.hh"
@@ -161,19 +162,18 @@ const char *read_header(PlyReadBuffer &file, PlyHeader &r_header)
return nullptr;
}
void importer_main(bContext *C, const PLYImportParams &import_params, wmOperator *op)
void importer_main(bContext *C, const PLYImportParams &import_params)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
importer_main(bmain, scene, view_layer, import_params, op);
importer_main(bmain, scene, view_layer, import_params);
}
void importer_main(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
const PLYImportParams &import_params,
wmOperator *op)
const PLYImportParams &import_params)
{
/* File base name used for both mesh and object. */
char ob_name[FILE_MAX];
@@ -187,7 +187,7 @@ void importer_main(Main *bmain,
const char *err = read_header(file, header);
if (err != nullptr) {
fprintf(stderr, "PLY Importer: %s: %s\n", ob_name, err);
BKE_reportf(op->reports, RPT_ERROR, "PLY Importer: %s: %s", ob_name, err);
BKE_reportf(import_params.reports, RPT_ERROR, "PLY Importer: %s: %s", ob_name, err);
return;
}
@@ -195,17 +195,17 @@ void importer_main(Main *bmain,
std::unique_ptr<PlyData> data = import_ply_data(file, header);
if (data == nullptr) {
fprintf(stderr, "PLY Importer: failed importing %s, unknown error\n", ob_name);
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed importing, unknown error");
BKE_report(import_params.reports, RPT_ERROR, "PLY Importer: failed importing, unknown error");
return;
}
if (!data->error.empty()) {
fprintf(stderr, "PLY Importer: failed importing %s: %s\n", ob_name, data->error.c_str());
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed importing, unknown error");
BKE_report(import_params.reports, RPT_ERROR, "PLY Importer: failed importing, unknown error");
return;
}
if (data->vertices.is_empty()) {
fprintf(stderr, "PLY Importer: file %s contains no vertices\n", ob_name);
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed importing, no vertices");
BKE_report(import_params.reports, RPT_ERROR, "PLY Importer: failed importing, no vertices");
return;
}
+2 -5
View File
@@ -15,17 +15,14 @@ namespace blender::io::ply {
class PlyReadBuffer;
void splitstr(std::string str, Vector<std::string> &words, const StringRef &deli);
/* Main import function used from within Blender. */
void importer_main(bContext *C, const PLYImportParams &import_params, wmOperator *op);
void importer_main(bContext *C, const PLYImportParams &import_params);
/* Used from tests, where full bContext does not exist. */
void importer_main(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
const PLYImportParams &import_params,
wmOperator *op);
const PLYImportParams &import_params);
const char *read_header(PlyReadBuffer &file, PlyHeader &r_header);
+4
View File
@@ -21,6 +21,8 @@ struct STLImportParams {
bool use_scene_unit;
float global_scale;
bool use_mesh_validate;
ReportList *reports = nullptr;
};
struct STLExportParams {
@@ -34,6 +36,8 @@ struct STLExportParams {
bool apply_modifiers;
bool ascii_format;
bool use_batch;
ReportList *reports = nullptr;
};
void STL_import(bContext *C, const STLImportParams *import_params);
+22 -2
View File
@@ -10,8 +10,10 @@
#include <memory>
#include <string>
#include "BKE_context.hh"
#include "BKE_mesh.hh"
#include "BKE_object.hh"
#include "BKE_report.h"
#include "BLI_string.h"
@@ -40,7 +42,17 @@ void export_frame(Depsgraph *depsgraph,
/* If not exporting in batch, create single writer for all objects. */
if (!export_params.use_batch) {
writer = std::make_unique<FileWriter>(export_params.filepath, export_params.ascii_format);
try {
writer = std::make_unique<FileWriter>(export_params.filepath, export_params.ascii_format);
}
catch (const std::runtime_error &ex) {
fprintf(stderr, "%s\n", ex.what());
BKE_reportf(export_params.reports,
RPT_ERROR,
"STL Export: Cannot open file '%s'",
export_params.filepath);
return;
}
}
DEGObjectIterSettings deg_iter_settings{};
@@ -70,7 +82,15 @@ void export_frame(Depsgraph *depsgraph,
char filepath[FILE_MAX];
STRNCPY(filepath, export_params.filepath);
BLI_path_extension_replace(filepath, FILE_MAX, suffix.c_str());
writer = std::make_unique<FileWriter>(filepath, export_params.ascii_format);
try {
writer = std::make_unique<FileWriter>(filepath, export_params.ascii_format);
}
catch (const std::runtime_error &ex) {
fprintf(stderr, "%s\n", ex.what());
BKE_reportf(
export_params.reports, RPT_ERROR, "STL Export: Cannot open file '%s'", filepath);
return;
}
}
Object *obj_eval = DEG_get_evaluated_object(depsgraph, object);
+15 -1
View File
@@ -8,12 +8,14 @@
#include <cstdio>
#include "BKE_customdata.hh"
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "BKE_mesh.hh"
#include "BKE_object.hh"
#include "BKE_report.h"
#include "DNA_collection_types.h"
#include "DNA_layer_types.h"
#include "DNA_scene_types.h"
#include "BLI_fileops.hh"
@@ -61,6 +63,10 @@ void importer_main(Main *bmain,
FILE *file = BLI_fopen(import_params.filepath, "rb");
if (!file) {
fprintf(stderr, "Failed to open STL file:'%s'.\n", import_params.filepath);
BKE_reportf(import_params.reports,
RPT_ERROR,
"STL Import: Cannot open file '%s'",
import_params.filepath);
return;
}
BLI_SCOPED_DEFER([&]() { fclose(file); });
@@ -74,6 +80,10 @@ void importer_main(Main *bmain,
fseek(file, BINARY_HEADER_SIZE, SEEK_SET);
if (fread(&num_tri, sizeof(uint32_t), 1, file) != 1) {
stl_import_report_error(file);
BKE_reportf(import_params.reports,
RPT_ERROR,
"STL Import: Failed to read file '%s'",
import_params.filepath);
return;
}
bool is_ascii_stl = (file_size != (BINARY_HEADER_SIZE + 4 + BINARY_STRIDE * num_tri));
@@ -89,6 +99,10 @@ void importer_main(Main *bmain,
if (mesh == nullptr) {
fprintf(stderr, "STL Importer: Failed to import mesh '%s'\n", import_params.filepath);
BKE_reportf(import_params.reports,
RPT_ERROR,
"STL Import: Failed to import mesh from file '%s'",
import_params.filepath);
return;
}
@@ -14,6 +14,8 @@
#include "DEG_depsgraph.hh"
#include "MEM_guardedalloc.h"
#include "IO_stl.hh"
#include "stl_export.hh"
@@ -59,6 +59,8 @@ struct OBJExportParams {
bool export_smooth_groups;
/* Create bitflags instead of the default "0"/"1" group IDs. */
bool smooth_groups_bitflags;
ReportList *reports = nullptr;
};
struct OBJImportParams {
@@ -76,6 +78,8 @@ struct OBJImportParams {
bool validate_meshes = false;
bool relative_paths = true;
bool clear_selection = true;
ReportList *reports = nullptr;
};
/**
@@ -7,9 +7,11 @@
*/
#include <cstdio>
#include <exception>
#include <memory>
#include <system_error>
#include "BKE_context.hh"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BLI_path_util.h"
@@ -259,6 +261,7 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
}
catch (const std::system_error &ex) {
print_exception_error(ex);
BKE_reportf(export_params.reports, RPT_ERROR, "OBJ Export: Cannot open file '%s'", filepath);
return;
}
if (!frame_writer) {
@@ -272,6 +275,10 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
}
catch (const std::system_error &ex) {
print_exception_error(ex);
BKE_reportf(export_params.reports,
RPT_WARNING,
"OBJ Export: Cannot create mtl file for '%s'",
filepath);
}
}
@@ -6,6 +6,8 @@
* \ingroup obj
*/
#include "BKE_report.h"
#include "BLI_map.hh"
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
@@ -441,6 +443,10 @@ OBJParser::OBJParser(const OBJImportParams &import_params, size_t read_buffer_si
obj_file_ = BLI_fopen(import_params_.filepath, "rb");
if (!obj_file_) {
fprintf(stderr, "Cannot read from OBJ file:'%s'.\n", import_params_.filepath);
BKE_reportf(import_params_.reports,
RPT_ERROR,
"OBJ Import: Cannot open file '%s'",
import_params_.filepath);
return;
}
}
@@ -15,6 +15,7 @@
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "BKE_scene.h"
@@ -40,6 +40,8 @@ struct OBJExportParamsDefault {
params.export_vertex_groups = false;
params.export_smooth_groups = true;
params.smooth_groups_bitflags = false;
params.reports = nullptr;
}
};