Index PPI project layers

This commit is contained in:
2026-06-01 12:53:48 +02:00
parent 677d0b33a8
commit 7319cb9aa9
8 changed files with 317 additions and 47 deletions

View File

@@ -57,6 +57,48 @@ void print_error(std::string_view command, std::string_view message)
<< "\",\"error\":\"" << message << "\"}\n";
}
std::string json_escape(std::string_view value)
{
constexpr char hex[] = "0123456789abcdef";
std::string escaped;
escaped.reserve(value.size());
for (const unsigned char ch : value) {
switch (ch) {
case '"':
escaped += "\\\"";
break;
case '\\':
escaped += "\\\\";
break;
case '\b':
escaped += "\\b";
break;
case '\f':
escaped += "\\f";
break;
case '\n':
escaped += "\\n";
break;
case '\r':
escaped += "\\r";
break;
case '\t':
escaped += "\\t";
break;
default:
if (ch < 0x20U) {
escaped += "\\u00";
escaped.push_back(hex[(ch >> 4U) & 0x0fU]);
escaped.push_back(hex[ch & 0x0fU]);
} else {
escaped.push_back(static_cast<char>(ch));
}
break;
}
}
return escaped;
}
void print_help()
{
std::cout
@@ -287,34 +329,78 @@ int inspect_project(int argc, char** argv)
std::istreambuf_iterator<char>()
};
const auto* data = reinterpret_cast<const std::byte*>(chars.data());
const auto summary = pp::assets::parse_ppi_project_summary(std::span<const std::byte>(data, chars.size()));
if (!summary) {
print_error("inspect-project", summary.status().message);
const auto project = pp::assets::parse_ppi_project_index(std::span<const std::byte>(data, chars.size()));
if (!project) {
print_error("inspect-project", project.status().message);
return 2;
}
std::cout << "{\"ok\":true,\"command\":\"inspect-project\""
<< ",\"documentVersion\":\"" << summary.value().layout.header.document_version.major
<< "." << summary.value().layout.header.document_version.minor << "\""
<< ",\"softwareVersion\":\"" << summary.value().layout.header.software_version.major
<< "." << summary.value().layout.header.software_version.minor
<< "." << summary.value().layout.header.software_version.fix
<< "." << summary.value().layout.header.software_version.build << "\""
<< ",\"thumbnail\":{\"width\":" << summary.value().layout.header.thumbnail.width
<< ",\"height\":" << summary.value().layout.header.thumbnail.height
<< ",\"components\":" << summary.value().layout.header.thumbnail.components
<< ",\"bytes\":" << summary.value().layout.thumbnail_bytes
<< "},\"body\":{\"offset\":" << summary.value().layout.body_offset
<< ",\"bytes\":" << summary.value().layout.body_bytes
<< ",\"width\":" << summary.value().body.width
<< ",\"height\":" << summary.value().body.height
<< ",\"layers\":" << summary.value().body.layer_count
<< ",\"frames\":" << summary.value().body.declared_frame_count
<< ",\"dirtyFaces\":" << summary.value().body.dirty_face_count
<< ",\"rgbaFacePayloads\":" << summary.value().body.rgba_face_payload_count
<< ",\"compressedBytes\":" << summary.value().body.compressed_face_bytes
<< ",\"infoBytes\":" << summary.value().body.info_bytes
<< "}}\n";
<< ",\"documentVersion\":\"" << project.value().layout.header.document_version.major
<< "." << project.value().layout.header.document_version.minor << "\""
<< ",\"softwareVersion\":\"" << project.value().layout.header.software_version.major
<< "." << project.value().layout.header.software_version.minor
<< "." << project.value().layout.header.software_version.fix
<< "." << project.value().layout.header.software_version.build << "\""
<< ",\"thumbnail\":{\"width\":" << project.value().layout.header.thumbnail.width
<< ",\"height\":" << project.value().layout.header.thumbnail.height
<< ",\"components\":" << project.value().layout.header.thumbnail.components
<< ",\"bytes\":" << project.value().layout.thumbnail_bytes
<< "},\"body\":{\"offset\":" << project.value().layout.body_offset
<< ",\"bytes\":" << project.value().layout.body_bytes
<< ",\"width\":" << project.value().body.summary.width
<< ",\"height\":" << project.value().body.summary.height
<< ",\"layers\":" << project.value().body.summary.layer_count
<< ",\"frames\":" << project.value().body.summary.declared_frame_count
<< ",\"dirtyFaces\":" << project.value().body.summary.dirty_face_count
<< ",\"rgbaFacePayloads\":" << project.value().body.summary.rgba_face_payload_count
<< ",\"compressedBytes\":" << project.value().body.summary.compressed_face_bytes
<< ",\"infoBytes\":" << project.value().body.summary.info_bytes
<< "},\"layers\":[";
for (std::size_t layer_index = 0; layer_index < project.value().body.layers.size(); ++layer_index) {
const auto& layer = project.value().body.layers[layer_index];
if (layer_index != 0U) {
std::cout << ",";
}
std::cout << "{\"index\":" << layer_index
<< ",\"storedOrder\":" << layer.stored_order
<< ",\"name\":\"" << json_escape(layer.name) << "\""
<< ",\"opacity\":" << layer.opacity
<< ",\"blendMode\":" << layer.blend_mode
<< ",\"alphaLocked\":" << (layer.alpha_locked ? "true" : "false")
<< ",\"visible\":" << (layer.visible ? "true" : "false")
<< ",\"frames\":[";
for (std::size_t frame_index = 0; frame_index < layer.frames.size(); ++frame_index) {
const auto& frame = layer.frames[frame_index];
if (frame_index != 0U) {
std::cout << ",";
}
std::cout << "{\"index\":" << frame_index
<< ",\"durationMs\":" << frame.duration_ms
<< ",\"dirtyFaces\":[";
bool first_face = true;
for (std::size_t face_index = 0; face_index < frame.faces.size(); ++face_index) {
const auto& face = frame.faces[face_index];
if (!face.has_data) {
continue;
}
if (!first_face) {
std::cout << ",";
}
first_face = false;
std::cout << "{\"face\":" << face_index
<< ",\"box\":[" << face.x0 << "," << face.y0 << "," << face.x1 << "," << face.y1 << "]"
<< ",\"bodyPayloadOffset\":" << face.body_payload_offset
<< ",\"bytes\":" << face.payload_bytes
<< ",\"png\":{\"width\":" << face.png_width
<< ",\"height\":" << face.png_height
<< "}}";
}
std::cout << "]}";
}
std::cout << "]}";
}
std::cout << "]}\n";
return 0;
}