Advance N-023 through N-026 audited parity
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "BLI_endian_defines.h"
|
||||
#include "BLI_filereader.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLO_core_bhead.hh"
|
||||
@@ -36,6 +37,9 @@
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_sdna_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_enums.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -44,6 +48,73 @@ using namespace blender;
|
||||
|
||||
constexpr uint64_t MAX_BLEND_BLOCK_BYTES = UINT64_C(1024) * 1024 * 1024;
|
||||
constexpr size_t MAX_NON_MESH_POINTS = 1000000;
|
||||
constexpr size_t MAX_SCRIPT_SOURCE_BYTES = 1024 * 1024;
|
||||
|
||||
uint32_t rotate_right(const uint32_t value, const uint32_t bits)
|
||||
{
|
||||
return (value >> bits) | (value << (32 - bits));
|
||||
}
|
||||
|
||||
std::string sha256_hex(const std::string &value)
|
||||
{
|
||||
static constexpr std::array<uint32_t, 64> round_constants = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
||||
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
|
||||
std::array<uint32_t, 8> digest = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
|
||||
std::vector<uint8_t> message(value.begin(), value.end());
|
||||
const uint64_t bit_length = uint64_t(message.size()) * 8;
|
||||
message.push_back(0x80);
|
||||
while (message.size() % 64 != 56) message.push_back(0);
|
||||
for (int shift = 56; shift >= 0; shift -= 8) message.push_back(uint8_t(bit_length >> shift));
|
||||
for (size_t offset = 0; offset < message.size(); offset += 64) {
|
||||
std::array<uint32_t, 64> words{};
|
||||
for (size_t index = 0; index < 16; index++) {
|
||||
const size_t byte = offset + index * 4;
|
||||
words[index] = uint32_t(message[byte]) << 24 | uint32_t(message[byte + 1]) << 16 |
|
||||
uint32_t(message[byte + 2]) << 8 | uint32_t(message[byte + 3]);
|
||||
}
|
||||
for (size_t index = 16; index < words.size(); index++) {
|
||||
const uint32_t s0 = rotate_right(words[index - 15], 7) ^
|
||||
rotate_right(words[index - 15], 18) ^ (words[index - 15] >> 3);
|
||||
const uint32_t s1 = rotate_right(words[index - 2], 17) ^
|
||||
rotate_right(words[index - 2], 19) ^ (words[index - 2] >> 10);
|
||||
words[index] = words[index - 16] + s0 + words[index - 7] + s1;
|
||||
}
|
||||
uint32_t a = digest[0], b = digest[1], c = digest[2], d = digest[3];
|
||||
uint32_t e = digest[4], f = digest[5], g = digest[6], h = digest[7];
|
||||
for (size_t index = 0; index < words.size(); index++) {
|
||||
const uint32_t sum1 = rotate_right(e, 6) ^ rotate_right(e, 11) ^ rotate_right(e, 25);
|
||||
const uint32_t choice = (e & f) ^ (~e & g);
|
||||
const uint32_t temporary1 = h + sum1 + choice + round_constants[index] + words[index];
|
||||
const uint32_t sum0 = rotate_right(a, 2) ^ rotate_right(a, 13) ^ rotate_right(a, 22);
|
||||
const uint32_t majority = (a & b) ^ (a & c) ^ (b & c);
|
||||
const uint32_t temporary2 = sum0 + majority;
|
||||
h = g; g = f; f = e; e = d + temporary1;
|
||||
d = c; c = b; b = a; a = temporary1 + temporary2;
|
||||
}
|
||||
digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d;
|
||||
digest[4] += e; digest[5] += f; digest[6] += g; digest[7] += h;
|
||||
}
|
||||
static constexpr char hex[] = "0123456789abcdef";
|
||||
std::string result(64, '0');
|
||||
for (size_t index = 0; index < digest.size(); index++) {
|
||||
for (size_t nibble = 0; nibble < 8; nibble++) {
|
||||
result[index * 8 + nibble] = hex[(digest[index] >> (28 - nibble * 4)) & 0xf];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct FileReaderDeleter {
|
||||
void operator()(FileReader *reader) const
|
||||
@@ -465,6 +536,9 @@ std::string id_prefix(const std::string &type_name)
|
||||
if (type_name == "Text") return "text";
|
||||
if (type_name == "VFont") return "vfont";
|
||||
if (type_name == "Mask") return "mask";
|
||||
if (type_name == "Library") return "library";
|
||||
if (type_name == "WorkSpace") return "workspace";
|
||||
if (type_name == "bScreen") return "screen";
|
||||
return "datablock";
|
||||
}
|
||||
|
||||
@@ -477,7 +551,8 @@ bool is_exported_id_type(const std::string &type_name)
|
||||
type_name == "bArmature" || type_name == "World" || type_name == "Curve" ||
|
||||
type_name == "MetaBall" || type_name == "PointCloud" || type_name == "Curves" ||
|
||||
type_name == "Volume" || type_name == "GreasePencil" || type_name == "Text" ||
|
||||
type_name == "VFont" || type_name == "Mask";
|
||||
type_name == "VFont" || type_name == "Mask" || type_name == "Library" ||
|
||||
type_name == "WorkSpace" || type_name == "bScreen";
|
||||
}
|
||||
|
||||
std::string unique_id(const std::string &prefix,
|
||||
@@ -1050,6 +1125,178 @@ std::optional<json> mask_from_record(const ParsedBlend &blend,
|
||||
return json{{"id", mask_id}, {"name", mask_name}, {"layers", std::move(layers)}};
|
||||
}
|
||||
|
||||
const char *editor_type_from_space(const int64_t space_type)
|
||||
{
|
||||
switch (space_type) {
|
||||
case SPACE_VIEW3D: return "VIEW_3D";
|
||||
case SPACE_OUTLINER: return "OUTLINER";
|
||||
case SPACE_PROPERTIES: return "PROPERTIES";
|
||||
case SPACE_IMAGE: return "UV_IMAGE";
|
||||
case SPACE_NODE: return "NODE";
|
||||
case SPACE_GRAPH: return "GRAPH";
|
||||
case SPACE_ACTION: return "DOPE_SHEET";
|
||||
case SPACE_NLA: return "NLA";
|
||||
case SPACE_SPREADSHEET: return "SPREADSHEET";
|
||||
case SPACE_SEQ: return "SEQUENCER";
|
||||
case SPACE_CLIP: return "CLIP";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char *editor_region_kind(const int64_t region_type)
|
||||
{
|
||||
if (region_type == RGN_TYPE_WINDOW || region_type == RGN_TYPE_PREVIEW) return "MAIN";
|
||||
if (region_type == RGN_TYPE_HEADER || region_type == RGN_TYPE_TOOL_HEADER ||
|
||||
region_type == RGN_TYPE_ASSET_SHELF_HEADER || region_type == RGN_TYPE_SCRUBBING)
|
||||
{
|
||||
return "HEADER";
|
||||
}
|
||||
if (region_type == RGN_TYPE_TOOLS || region_type == RGN_TYPE_TOOL_PROPS) return "TOOLBAR";
|
||||
if (region_type == RGN_TYPE_UI || region_type == RGN_TYPE_CHANNELS ||
|
||||
region_type == RGN_TYPE_NAV_BAR || region_type == RGN_TYPE_ASSET_SHELF)
|
||||
{
|
||||
return "SIDEBAR";
|
||||
}
|
||||
if (region_type == RGN_TYPE_FOOTER || region_type == RGN_TYPE_EXECUTE) return "FOOTER";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<json> editor_workflow_from_records(
|
||||
const ParsedBlend &blend,
|
||||
const std::vector<IdRecord> &records,
|
||||
const uint64_t revision,
|
||||
const std::string &active_object_id)
|
||||
{
|
||||
json workspaces = json::array();
|
||||
size_t area_count = 0;
|
||||
size_t region_count = 0;
|
||||
for (const IdRecord &record : records) {
|
||||
if (record.type_name != "WorkSpace" || workspaces.size() >= 64) continue;
|
||||
const ElementRef workspace{record.block, 0, record.type_name};
|
||||
const std::vector<ElementRef> layouts = linked_list_elements(blend, workspace, "layouts");
|
||||
if (layouts.empty()) continue;
|
||||
const std::optional<uint64_t> screen_pointer = read_pointer(*blend.sdna, layouts.front(), "screen");
|
||||
const std::optional<ElementRef> screen = screen_pointer ? element_for_pointer(blend, *screen_pointer) :
|
||||
std::nullopt;
|
||||
if (!screen) continue;
|
||||
const std::vector<ElementRef> source_areas = linked_list_elements(blend, *screen, "areabase");
|
||||
if (source_areas.empty() || area_count > 1024 - source_areas.size()) continue;
|
||||
|
||||
struct AreaBounds {
|
||||
int x_min;
|
||||
int y_min;
|
||||
int x_max;
|
||||
int y_max;
|
||||
};
|
||||
std::vector<AreaBounds> bounds;
|
||||
bounds.reserve(source_areas.size());
|
||||
bool valid = true;
|
||||
int screen_x_min = std::numeric_limits<int>::max();
|
||||
int screen_y_min = std::numeric_limits<int>::max();
|
||||
int screen_x_max = std::numeric_limits<int>::min();
|
||||
int screen_y_max = std::numeric_limits<int>::min();
|
||||
for (const ElementRef &area : source_areas) {
|
||||
if (editor_type_from_space(read_integer(*blend.sdna, area, "spacetype").value_or(-1)) == nullptr) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
const std::optional<ElementRef> total_rect = embedded_element(*blend.sdna, area, "totrct");
|
||||
if (!total_rect) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
const std::vector<int64_t> area_rect = {
|
||||
read_integer(*blend.sdna, *total_rect, "xmin").value_or(0),
|
||||
read_integer(*blend.sdna, *total_rect, "ymin").value_or(0),
|
||||
read_integer(*blend.sdna, *total_rect, "xmax").value_or(0),
|
||||
read_integer(*blend.sdna, *total_rect, "ymax").value_or(0)};
|
||||
if (area_rect[2] <= area_rect[0] || area_rect[3] <= area_rect[1]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
bounds.push_back({int(area_rect[0]), int(area_rect[1]), int(area_rect[2]), int(area_rect[3])});
|
||||
screen_x_min = std::min(screen_x_min, int(area_rect[0]));
|
||||
screen_y_min = std::min(screen_y_min, int(area_rect[1]));
|
||||
screen_x_max = std::max(screen_x_max, int(area_rect[2]));
|
||||
screen_y_max = std::max(screen_y_max, int(area_rect[3]));
|
||||
}
|
||||
const int screen_width = screen_x_max - screen_x_min;
|
||||
const int screen_height = screen_y_max - screen_y_min;
|
||||
if (!valid || screen_width <= 0 || screen_height <= 0) continue;
|
||||
|
||||
json areas = json::array();
|
||||
size_t workspace_region_count = 0;
|
||||
for (size_t area_index = 0; area_index < source_areas.size(); area_index++) {
|
||||
const ElementRef &area = source_areas[area_index];
|
||||
const char *editor = editor_type_from_space(
|
||||
read_integer(*blend.sdna, area, "spacetype").value_or(-1));
|
||||
json regions = json::array();
|
||||
bool has_main_region = false;
|
||||
for (const ElementRef ®ion : linked_list_elements(blend, area, "regionbase")) {
|
||||
const char *kind = editor_region_kind(
|
||||
read_integer(*blend.sdna, region, "regiontype").value_or(-1));
|
||||
if (kind == nullptr) continue;
|
||||
if (++workspace_region_count > 4096 - region_count) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
const int64_t flags = read_integer(*blend.sdna, region, "flag").value_or(0);
|
||||
regions.push_back({{"id", record.id + ":area:" + std::to_string(area_index) +
|
||||
":region:" + std::to_string(regions.size())},
|
||||
{"kind", kind},
|
||||
{"visible", (flags & RGN_FLAG_HIDDEN) == 0}});
|
||||
has_main_region |= std::strcmp(kind, "MAIN") == 0;
|
||||
}
|
||||
if (!valid || regions.empty() || !has_main_region) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
const AreaBounds &rect = bounds[area_index];
|
||||
const double x = double(rect.x_min - screen_x_min) / screen_width;
|
||||
const double y = double(rect.y_min - screen_y_min) / screen_height;
|
||||
const double width = rect.x_max == screen_x_max ? 1.0 - x :
|
||||
double(rect.x_max - rect.x_min) / screen_width;
|
||||
const double height = rect.y_max == screen_y_max ? 1.0 - y :
|
||||
double(rect.y_max - rect.y_min) / screen_height;
|
||||
areas.push_back({{"id", record.id + ":area:" + std::to_string(area_index)},
|
||||
{"editor", editor},
|
||||
{"regions", std::move(regions)},
|
||||
{"rect",
|
||||
{{"x", x}, {"y", y}, {"width", width}, {"height", height}}},
|
||||
{"maximized", false}});
|
||||
}
|
||||
if (!valid || areas.empty()) continue;
|
||||
area_count += areas.size();
|
||||
region_count += workspace_region_count;
|
||||
workspaces.push_back({{"id", record.id},
|
||||
{"name", record.name},
|
||||
{"areas", std::move(areas)},
|
||||
{"activeAreaId", record.id + ":area:0"},
|
||||
{"revision", revision}});
|
||||
}
|
||||
if (workspaces.empty()) return std::nullopt;
|
||||
const json &workspace = workspaces.front();
|
||||
const json &active_area = workspace["areas"].front();
|
||||
const std::string workspace_id = workspace.value("id", "");
|
||||
const std::string active_area_id = active_area.value("id", "");
|
||||
const std::string active_editor = active_area.value("editor", "");
|
||||
json selection = json::array();
|
||||
if (!active_object_id.empty()) selection.push_back(active_object_id);
|
||||
return json{{"schemaVersion", 1},
|
||||
{"workspaces", std::move(workspaces)},
|
||||
{"context",
|
||||
{{"workspaceId", workspace_id},
|
||||
{"activeAreaId", active_area_id},
|
||||
{"activeEditor", active_editor},
|
||||
{"mode", "OBJECT"},
|
||||
{"activeObjectId", active_object_id.empty() ? json(nullptr) : json(active_object_id)},
|
||||
{"selection", std::move(selection)},
|
||||
{"viewLayer", "ViewLayer"},
|
||||
{"pinnedData", nullptr},
|
||||
{"revision", revision}}},
|
||||
{"keymaps", json::array()}};
|
||||
}
|
||||
|
||||
struct RawBlockSpan {
|
||||
const BlendBlock *block = nullptr;
|
||||
size_t offset = 0;
|
||||
@@ -1670,6 +1917,64 @@ std::string read_raw_string(const ParsedBlend &blend,
|
||||
return std::string(data, strnlen(data, available));
|
||||
}
|
||||
|
||||
std::optional<json> script_source_from_record(const ParsedBlend &blend,
|
||||
const ElementRef &element,
|
||||
const IdRecord &record)
|
||||
{
|
||||
const std::vector<ElementRef> lines = linked_list_elements(blend, element, "lines");
|
||||
if (lines.empty() || lines.size() > 65536) return std::nullopt;
|
||||
std::string source;
|
||||
for (size_t index = 0; index < lines.size(); index++) {
|
||||
const int64_t length = read_integer(*blend.sdna, lines[index], "len").value_or(-1);
|
||||
const std::optional<uint64_t> pointer = read_pointer(*blend.sdna, lines[index], "line");
|
||||
const size_t separator_bytes = index > 0 ? 1 : 0;
|
||||
if (length < 0 || length > int64_t(MAX_SCRIPT_SOURCE_BYTES) || !pointer ||
|
||||
size_t(length) > MAX_SCRIPT_SOURCE_BYTES - separator_bytes ||
|
||||
source.size() > MAX_SCRIPT_SOURCE_BYTES - separator_bytes - size_t(length))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::vector<uint8_t> bytes = read_raw_byte_array(blend, *pointer, size_t(length));
|
||||
if (bytes.size() != size_t(length) ||
|
||||
std::find(bytes.begin(), bytes.end(), uint8_t(0)) != bytes.end() ||
|
||||
(!bytes.empty() &&
|
||||
BLI_str_utf8_invalid_byte(reinterpret_cast<const char *>(bytes.data()), bytes.size()) != -1))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (index > 0) source.push_back('\n');
|
||||
source.append(reinterpret_cast<const char *>(bytes.data()), bytes.size());
|
||||
}
|
||||
const std::optional<uint64_t> path_pointer = read_pointer(*blend.sdna, element, "filepath");
|
||||
const std::string source_path = path_pointer ? read_raw_string(blend, *path_pointer, 2049) :
|
||||
std::string();
|
||||
if (!source_path.empty()) {
|
||||
const bool project_path = source_path.rfind("//", 0) == 0 && source_path.size() > 2 &&
|
||||
source_path.size() <= 2050 &&
|
||||
source_path.find('\\') == std::string::npos &&
|
||||
source_path.find('%') == std::string::npos &&
|
||||
source_path.find("/../") == std::string::npos &&
|
||||
source_path.find("//", 2) == std::string::npos;
|
||||
if (!project_path) return std::nullopt;
|
||||
}
|
||||
const int64_t flags = read_integer(*blend.sdna, element, "flags").value_or(0);
|
||||
const bool module_autorun_requested = (flags & (1 << 4)) != 0;
|
||||
json result = {{"id", record.id},
|
||||
{"name", record.name},
|
||||
{"source", source},
|
||||
{"sourceSha256", sha256_hex(source)},
|
||||
{"byteLength", source.size()},
|
||||
{"lineCount", lines.size()},
|
||||
{"internal", source_path.empty()},
|
||||
{"moduleAutorunRequested", module_autorun_requested},
|
||||
{"readOnly", true},
|
||||
{"executionStatus", "BLOCKED"},
|
||||
{"errorCode", module_autorun_requested ? "SCRIPT_POLICY_DENIED" :
|
||||
"SCRIPT_SANDBOX_UNAVAILABLE"}};
|
||||
if (!source_path.empty()) result["sourcePath"] = source_path;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> read_raw_pointer_array(const ParsedBlend &blend,
|
||||
const uint64_t pointer,
|
||||
const size_t count)
|
||||
@@ -1829,8 +2134,7 @@ json non_mesh_data_from_record(const ParsedBlend &blend,
|
||||
record.type_name == "MetaBall" ? "METABALL" :
|
||||
record.type_name == "PointCloud" ? "POINT_CLOUD" :
|
||||
record.type_name == "Curves" ? "CURVES" :
|
||||
record.type_name == "Volume" ? "VOLUME" :
|
||||
record.type_name == "Text" ? "FONT" : "CURVE";
|
||||
record.type_name == "Volume" ? "VOLUME" : "CURVE";
|
||||
json data = { {"id", record.id},
|
||||
{"name", record.name},
|
||||
{"type", type_name},
|
||||
@@ -2122,19 +2426,6 @@ json non_mesh_data_from_record(const ParsedBlend &blend,
|
||||
"NON_MESH_DATA_UNSUPPORTED";
|
||||
}
|
||||
}
|
||||
else if (record.type_name == "Text") {
|
||||
json lines = json::array();
|
||||
size_t total_chars = 0;
|
||||
for (const ElementRef &line : linked_list_elements(blend, element, "lines")) {
|
||||
const std::optional<uint64_t> pointer = read_pointer(*blend.sdna, line, "line");
|
||||
const std::string value = pointer ? read_raw_string(blend, *pointer, 4096) : std::string();
|
||||
total_chars += value.size();
|
||||
lines.push_back(value);
|
||||
}
|
||||
data["text"] = lines.empty() ? "" : lines[0].get<std::string>();
|
||||
data["pointCount"] = total_chars;
|
||||
data["splineCount"] = lines.size();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -2756,8 +3047,12 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
json non_mesh_data = json::array();
|
||||
json vfonts = json::array();
|
||||
json libraries = json::array();
|
||||
json script_sources = json::array();
|
||||
size_t script_source_bytes = 0;
|
||||
json masks = json::array();
|
||||
bool masks_blocked = false;
|
||||
bool libraries_blocked = false;
|
||||
bool script_sources_blocked = false;
|
||||
json animations = json::array();
|
||||
json nla_tracks = json::array();
|
||||
json armatures = json::array();
|
||||
@@ -3234,9 +3529,29 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
}
|
||||
else if (record.type_name == "Curve" || record.type_name == "MetaBall" ||
|
||||
record.type_name == "PointCloud" || record.type_name == "Curves" ||
|
||||
record.type_name == "Volume" || record.type_name == "Text") {
|
||||
record.type_name == "Volume") {
|
||||
non_mesh_data.push_back(non_mesh_data_from_record(blend, element, record, ids_by_pointer));
|
||||
}
|
||||
else if (record.type_name == "Text") {
|
||||
if (script_sources.size() >= 1024) {
|
||||
script_sources_blocked = true;
|
||||
}
|
||||
else if (const std::optional<json> source = script_source_from_record(
|
||||
blend, element, record))
|
||||
{
|
||||
const size_t byte_length = source->value("byteLength", size_t(0));
|
||||
if (byte_length > MAX_SCRIPT_SOURCE_BYTES - script_source_bytes) {
|
||||
script_sources_blocked = true;
|
||||
}
|
||||
else {
|
||||
script_source_bytes += byte_length;
|
||||
script_sources.push_back(*source);
|
||||
}
|
||||
}
|
||||
else {
|
||||
script_sources_blocked = true;
|
||||
}
|
||||
}
|
||||
else if (record.type_name == "VFont") {
|
||||
const std::string filepath = read_string(*blend.sdna, element, "filepath");
|
||||
vfonts.push_back({{"id", record.id}, {"name", record.name}, {"sourcePath", filepath},
|
||||
@@ -3460,14 +3775,36 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
if (block.type_name != "Library") continue;
|
||||
const ElementRef library{&block, 0, block.type_name};
|
||||
const std::string path = read_string(*blend.sdna, library, "filepath");
|
||||
const std::string library_name = read_id_name(*blend.sdna, library);
|
||||
const std::string library_id = "library:" + library_name;
|
||||
const bool project_path = path.rfind("//", 0) == 0 && path.size() > 2 && path.size() <= 2050 &&
|
||||
path.find('\\') == std::string::npos && path.find('%') == std::string::npos &&
|
||||
path.find("/../") == std::string::npos && path.find("//", 2) == std::string::npos;
|
||||
if (!project_path || library_name.empty() || library_id.size() > 256) {
|
||||
libraries_blocked = true;
|
||||
continue;
|
||||
}
|
||||
const std::optional<uint64_t> packed_file = read_pointer(*blend.sdna, library, "packedfile");
|
||||
const std::vector<uint8_t> packed_bytes = packed_file ? packed_file_bytes(blend, *packed_file) :
|
||||
std::vector<uint8_t>();
|
||||
json resource = {{"id", "library:" + read_id_name(*blend.sdna, library)},
|
||||
{"name", read_id_name(*blend.sdna, library)},
|
||||
json dependencies = json::array();
|
||||
const std::optional<uint64_t> parent_pointer = read_pointer(
|
||||
*blend.sdna, library, "archive_parent_library");
|
||||
if (parent_pointer && *parent_pointer != 0) {
|
||||
const auto parent_id = ids_by_pointer.find(*parent_pointer);
|
||||
if (parent_id == ids_by_pointer.end()) {
|
||||
libraries_blocked = true;
|
||||
continue;
|
||||
}
|
||||
dependencies.push_back(parent_id->second);
|
||||
}
|
||||
json resource = {{"id", library_id},
|
||||
{"name", library_name},
|
||||
{"sourcePath", path},
|
||||
{"packed", !packed_bytes.empty()},
|
||||
{"status", packed_bytes.empty() ? "EXTERNAL_REQUIRED" : "PACKED"}};
|
||||
{"status", packed_bytes.empty() ? "EXTERNAL_REQUIRED" : "PACKED"},
|
||||
{"dependencyIds", std::move(dependencies)},
|
||||
{"readOnly", true}};
|
||||
if (packed_bytes.empty()) resource["errorCode"] = "LINKED_LIBRARY_RESOURCE_REQUIRED";
|
||||
else resource["packedByteLength"] = packed_bytes.size();
|
||||
libraries.push_back(std::move(resource));
|
||||
@@ -3511,13 +3848,17 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
sort_by_id(non_mesh_data);
|
||||
sort_by_id(vfonts);
|
||||
sort_by_id(libraries);
|
||||
sort_by_id(script_sources);
|
||||
sort_by_id(animations);
|
||||
sort_by_id(armatures);
|
||||
sort_by_id(collections);
|
||||
sort_by_id(scenes);
|
||||
populate_world_matrices(nodes);
|
||||
|
||||
|
||||
const std::string active_object_id = first_mesh_object_id.empty() ? first_object_id :
|
||||
first_mesh_object_id;
|
||||
std::optional<json> editor_workflow = editor_workflow_from_records(
|
||||
blend, records, revision, active_object_id);
|
||||
json snapshot = {
|
||||
{"schemaVersion", 1},
|
||||
{"revision", revision},
|
||||
@@ -3544,17 +3885,16 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
{"images", std::move(images)},
|
||||
{"nonMeshData", std::move(non_mesh_data)},
|
||||
{"vfonts", std::move(vfonts)},
|
||||
{"libraries", std::move(libraries)},
|
||||
{"libraryStatus", libraries_blocked ? "BLOCKED" : "AVAILABLE"},
|
||||
{"trackingMaskStatus", masks_blocked ? "BLOCKED" : "AVAILABLE"},
|
||||
{"editorWorkflowStatus", editor_workflow ? "AVAILABLE" : "BLOCKED"},
|
||||
{"scriptSourceStatus", script_sources_blocked ? "BLOCKED" : "AVAILABLE"},
|
||||
{"animations", std::move(animations)},
|
||||
{"nlaTracks", std::move(nla_tracks)},
|
||||
{"armatures", std::move(armatures)},
|
||||
{"collections", std::move(collections)},
|
||||
{"scenes", std::move(scenes)},
|
||||
{"activeObjectId",
|
||||
(first_mesh_object_id.empty() ? first_object_id : first_mesh_object_id).empty() ?
|
||||
json(nullptr) :
|
||||
json(first_mesh_object_id.empty() ? first_object_id : first_mesh_object_id)},
|
||||
{"activeObjectId", active_object_id.empty() ? json(nullptr) : json(active_object_id)},
|
||||
{"frame", {{"current", frame_current}, {"start", frame_start}, {"end", frame_end}}}};
|
||||
if (!masks_blocked) {
|
||||
snapshot["trackingMasks"] = {{"schemaVersion", 1},
|
||||
@@ -3563,6 +3903,11 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
{"masks", std::move(masks)},
|
||||
{"bindings", json::array()}};
|
||||
}
|
||||
if (!libraries_blocked) snapshot["libraries"] = std::move(libraries);
|
||||
if (editor_workflow) snapshot["editorWorkflow"] = std::move(*editor_workflow);
|
||||
if (!script_sources_blocked) {
|
||||
snapshot["scriptSources"] = {{"schemaVersion", 1}, {"sources", std::move(script_sources)}};
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user