blob: b1df3dc85db380ede087400b33ac1b01ebebbd48 [file]
#ifdef WITH_RIVE_SCRIPTING
#include "rive/lua/rive_lua_libs.hpp"
#include "libhydrogen.h"
#include "rive/importers/text_asset_importer.hpp"
#endif
#include "rive/assets/script_asset.hpp"
#include "rive/signed_content_header.hpp"
#include "rive/file.hpp"
#include "rive/script_input_artboard.hpp"
#include "rive/script_input_boolean.hpp"
#include "rive/script_input_color.hpp"
#include "rive/script_input_number.hpp"
#include "rive/script_input_string.hpp"
#include "rive/script_input_trigger.hpp"
#include "rive/script_input_viewmodel_property.hpp"
#include "rive/scripted/scripted_drawable.hpp"
#include "rive/scripted/scripted_layout.hpp"
#include "rive/scripted/scripted_object.hpp"
#include "rive/data_bind/data_bind.hpp"
using namespace rive;
ScriptInput::~ScriptInput()
{
if (m_ownsDataBind && m_dataBind)
{
delete m_dataBind;
}
}
ScriptInput* ScriptInput::from(Core* component)
{
switch (component->coreType())
{
case ScriptInputArtboard::typeKey:
return component->as<ScriptInputArtboard>();
case ScriptInputBoolean::typeKey:
return component->as<ScriptInputBoolean>();
case ScriptInputColor::typeKey:
return component->as<ScriptInputColor>();
case ScriptInputNumber::typeKey:
return component->as<ScriptInputNumber>();
case ScriptInputString::typeKey:
return component->as<ScriptInputString>();
case ScriptInputTrigger::typeKey:
return component->as<ScriptInputTrigger>();
case ScriptInputViewModelProperty::typeKey:
return component->as<ScriptInputViewModelProperty>();
}
return nullptr;
}
void ScriptInput::initScriptedValue() {}
bool ScriptInput::validateForColdScriptInit()
{
return validateForScriptInit();
}
bool ScriptInput::hydrateScriptInput()
{
initScriptedValue();
return true;
}
bool ScriptInput::validateHydrationPrerequisites() { return true; }
#ifdef WITH_RIVE_SCRIPTING
ScriptingVM* ScriptAsset::scriptingVM()
{
if (m_file == nullptr)
{
return nullptr;
}
return m_file->scriptingVM();
}
lua_State* ScriptAsset::vm()
{
auto scriptingVM = this->scriptingVM();
return scriptingVM ? scriptingVM->state() : nullptr;
}
#endif
bool ScriptAsset::initScriptedObject(ScriptedObject* object)
{
#ifdef WITH_RIVE_SCRIPTING
if (scriptingVM() == nullptr)
{
return false;
}
return initScriptedObjectWith(object);
#else
return false;
#endif
}
#if defined(WITH_RIVE_SCRIPTING)
void ScriptAsset::registrationComplete(int ref)
{
if (isModule())
{
m_scriptRegistered = true;
}
else
{
generatorFunctionRef(ref);
// Force re-verification on next init so that method detection (e.g.
// drawCanvas) reflects the newly compiled script.
m_initted = false;
}
}
#endif
bool ScriptAsset::initScriptedObjectWith(ScriptedObject* object)
{
#if defined(WITH_RIVE_SCRIPTING)
auto scriptVM = scriptingVM();
if (scriptVM == nullptr)
{
return false;
}
lua_State* state = scriptVM->state();
int ref = 0;
#ifdef WITH_RIVE_TOOLS
ScriptingContext* context =
static_cast<ScriptingContext*>(lua_getthreaddata(state));
if (context != nullptr && context->hasGeneratorRef(generatorFunctionRef()))
{
ref = context->getGeneratorRef(generatorFunctionRef());
}
else
#endif
{
ref = generatorFunctionRef();
}
if (ref == 0)
{
fprintf(stderr,
"ScriptAsset doesn't have a generator function %s\n",
name().c_str());
return false;
}
rive_lua_pushRef(state, ref);
if (!m_initted)
{
// The editor detected the implemented methods (by executing the
// generator in its workspace) and serialized the bitfield; the runtime
// never detects. Files predating this property decode to the all-bits
// default (2097151), so they behave as "implements everything" and rely
// on graceful dispatch (each callback no-ops when the method isn't
// actually a function on the returned table).
OptionalScriptedMethods::implementedMethods(
serializedImplementedMethods() & methodMask);
m_initted = true;
}
object->implementedMethods(implementedMethods());
return object->ensureScriptInitialized(scriptVM);
#else
return false;
#endif
}
bool ScriptAsset::decode(SimpleArray<uint8_t>& data, Factory* factory)
{
#ifdef WITH_RIVE_SCRIPTING
m_verified = false;
// For in-band bytecode, isSigned should always be false (signature is
// stored separately for aggregate verification).
SignedContentHeader header(Span<const uint8_t>(data.data(), data.size()));
if (!header.isValid())
{
return false;
}
// Store just the bytecode (without header) for later verification and use.
auto bytecode = header.content();
m_bytecode = SimpleArray<uint8_t>(bytecode.data(), bytecode.size());
#endif
return true;
}
bool ScriptAsset::bytecode(Span<uint8_t> data)
{
#ifdef WITH_RIVE_SCRIPTING
SignedContentHeader header(Span<const uint8_t>(data.data(), data.size()));
if (!header.isValid())
{
m_verified = false;
return false;
}
auto bytecode = header.content();
if (!header.isSigned())
{
// Unsigned bytecode - mark as unverified
m_verified = false;
m_bytecode = SimpleArray<uint8_t>(bytecode.data(), bytecode.size());
return true;
}
auto signature = header.signature();
if (hydro_sign_verify(signature.data(),
bytecode.data(),
bytecode.size(),
"RiveCode",
g_scriptVerificationPublicKey) != 0)
{
// Forged.
m_verified = false;
return false;
}
m_verified = true;
m_bytecode = SimpleArray<uint8_t>(bytecode.data(), bytecode.size());
#endif
return true;
}