blob: 77dfca4090df805bf4b62ea1613fd3b85382271f [file] [log] [blame] [edit]
#ifdef WITH_RIVE_SCRIPTING
#include "rive/lua/rive_lua_libs.hpp"
#include "libhydrogen.h"
#include "rive/importers/script_asset_importer.hpp"
#endif
#include "rive/assets/script_asset.hpp"
#include "rive/bytecode_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"
using namespace rive;
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 OptionalScriptedMethods::verifyImplementation(ScriptedObject* object,
LuaState* luaState)
{
#ifdef WITH_RIVE_SCRIPTING
auto state = luaState->state;
lua_pushvalue(state, -1);
if (static_cast<lua_Status>(rive_lua_pcall(state, 0, 1)) != LUA_OK)
{
fprintf(stderr, "Verifying implementation pcall failed\n");
rive_lua_pop(state, 1);
return false;
}
if (static_cast<lua_Type>(lua_type(state, -1)) != LUA_TTABLE)
{
fprintf(stderr, "Verifying implementation not a table?\n");
rive_lua_pop(state, 1);
return false;
}
m_implementedMethods = 0;
auto scriptProtocol = object->scriptProtocol();
if (scriptProtocol == ScriptProtocol::node ||
scriptProtocol == ScriptProtocol::layout ||
scriptProtocol == ScriptProtocol::converter ||
scriptProtocol == ScriptProtocol::pathEffect)
{
if (static_cast<lua_Type>(lua_getfield(state, -1, "update")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_updatesBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "advance")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_advancesBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "pointerDown")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_wantsPointerDownBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "pointerUp")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_wantsPointerUpBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "pointerMove")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_wantsPointerMoveBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "pointerCanceled")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_wantsPointerCancelBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "pointerExit")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_wantsPointerExitBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "init")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_initsBit;
}
rive_lua_pop(state, 1);
}
if (scriptProtocol == ScriptProtocol::layout)
{
if (static_cast<lua_Type>(lua_getfield(state, -1, "measure")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_measuresBit;
}
rive_lua_pop(state, 1);
if (static_cast<lua_Type>(lua_getfield(state, -1, "draw")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_drawsBit;
}
rive_lua_pop(state, 1);
}
else if (scriptProtocol == ScriptProtocol::node)
{
if (static_cast<lua_Type>(lua_getfield(state, -1, "draw")) ==
LUA_TFUNCTION)
{
m_implementedMethods |= m_drawsBit;
}
rive_lua_pop(state, 1);
}
rive_lua_pop(state, 1);
return true;
#else
return false;
#endif
}
#ifdef WITH_RIVE_SCRIPTING
LuaState* ScriptAsset::vm()
{
if (m_file == nullptr)
{
return nullptr;
}
// We get the scripting VM from File for now, however,
// this will need to change if/when we support multiple VMs
return m_file->scriptingVM();
}
#endif
bool ScriptAsset::initScriptedObject(ScriptedObject* object)
{
#ifdef WITH_RIVE_SCRIPTING
if (vm() == 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);
}
}
#endif
bool ScriptAsset::initScriptedObjectWith(ScriptedObject* object)
{
#if defined(WITH_RIVE_SCRIPTING)
if (vm() == nullptr)
{
return false;
}
if (generatorFunctionRef() == 0)
{
fprintf(stderr,
"ScriptAsset doesn't have a generator function %s\n",
name().c_str());
return false;
}
auto vmState = vm()->state;
rive_lua_pushRef(vmState, generatorFunctionRef());
if (!m_initted)
{
if (!verifyImplementation(object, vm()))
{
fprintf(stderr,
"ScriptAsset failed to verify method implementation %s\n",
name().c_str());
rive_lua_pop(vmState, 1);
return false;
}
m_initted = true;
}
object->implementedMethods(implementedMethods());
return object->scriptInit(vm());
#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).
BytecodeHeader 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.bytecode();
m_bytecode = SimpleArray<uint8_t>(bytecode.data(), bytecode.size());
#endif
return true;
}
bool ScriptAsset::bytecode(Span<uint8_t> data)
{
#ifdef WITH_RIVE_SCRIPTING
BytecodeHeader header(Span<const uint8_t>(data.data(), data.size()));
if (!header.isValid())
{
m_verified = false;
return false;
}
auto bytecode = header.bytecode();
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;
}