blob: 6330c7384f74d605066b2990ce3569542d5f959f [file] [edit]
#include "rive/assets/shader_asset.hpp"
#include "rive/signed_content_header.hpp"
using namespace rive;
bool rive::ShaderAsset::decode(Span<uint8_t> data, Factory* factory)
{
// `data` is always a SignedContentHeader envelope
// (`[flags:1][sig:64?][RSTB...]`) shared with ScriptAsset. Raw-RSTB
// callers (editor live-preview) must prepend a single 0x00 flag byte.
SignedContentHeader envelope(std::move(data));
if (!envelope.isValid())
{
return false;
}
auto inner = envelope.content();
m_bytes = SimpleArray<uint8_t>(inner.data(), inner.size());
m_index.clear();
m_pairs.clear();
const uint8_t* p = m_bytes.data();
const uint8_t* end = p + m_bytes.size();
// Header: magic(u32LE) + version(u16LE) + variant_count(u8) +
// section_count(u8)
if (m_bytes.size() < 8)
{
return false;
}
uint32_t magic = (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
if (magic != 0x52535442u) // "RSTB"
{
return false;
}
uint16_t version = (uint16_t)(p[4] | (p[5] << 8));
if (version != 4)
{
return false;
}
uint8_t variantCount = p[6];
uint8_t sectionCount = p[7];
p += 8;
// VariantDescriptor: target(u8) + blob_offset(u32) + blob_size(u32) = 9
constexpr size_t kVariantDescriptorSize = 9;
if (p + (size_t)variantCount * kVariantDescriptorSize > end)
{
return false;
}
for (uint8_t v = 0; v < variantCount; v++)
{
uint8_t target = p[0];
uint32_t blobOffset = (uint32_t)p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16) | ((uint32_t)p[4] << 24);
uint32_t blobSize = (uint32_t)p[5] | ((uint32_t)p[6] << 8) |
((uint32_t)p[7] << 16) | ((uint32_t)p[8] << 24);
p += kVariantDescriptorSize;
m_index[target] = {blobOffset, blobSize};
}
// Tagged metadata sections.
for (uint8_t sec = 0; sec < sectionCount; sec++)
{
if (p + 3 > end)
{
return false;
}
uint8_t tag = p[0];
uint16_t length = (uint16_t)(p[1] | (p[2] << 8));
p += 3;
if (p + length > end)
{
return false;
}
if (tag == 1 && length >= 1)
{
// Tag 1: texture-sampler pairs.
uint8_t pairCount = p[0];
if (length >= 1 + (size_t)pairCount * 4)
{
const uint8_t* pd = p + 1;
for (uint8_t i = 0; i < pairCount; i++)
{
m_pairs.push_back({pd[0], pd[1], pd[2], pd[3]});
pd += 4;
}
}
}
// Unknown tags: skip.
p += length;
}
// p now points to the start of the blob data section.
size_t blobDataStart = (size_t)(p - m_bytes.data());
// Fix up offsets to absolute and validate that each variant's range
// stays within m_bytes. Arithmetic widened to size_t so an adversarial
// (offset, size) pair can't overflow uint32_t past the bounds check.
for (auto& kv : m_index)
{
size_t absOffset = (size_t)kv.second.offset + blobDataStart;
if (absOffset > m_bytes.size() ||
(size_t)kv.second.size > m_bytes.size() - absOffset)
{
m_index.clear();
return false;
}
kv.second.offset = (uint32_t)absOffset;
}
return true;
}
Span<const uint8_t> ShaderAsset::findShader(uint8_t target) const
{
auto it = m_index.find(target);
if (it == m_index.end())
{
return {};
}
const auto& v = it->second;
return Span<const uint8_t>(m_bytes.data() + v.offset, v.size);
}