/* * travesty, pure C VST3-compatible interface * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include /** * deal with C vs C++ differences */ #if !defined(__cplusplus) && !defined(constexpr) # define constexpr #endif /** * various types */ typedef int32_t v3_result; typedef int16_t v3_str_128[128]; typedef uint8_t v3_bool; typedef uint32_t v3_param_id; /** * low-level ABI nonsense */ typedef uint8_t v3_tuid[16]; static inline bool v3_tuid_match(const v3_tuid a, const v3_tuid b) { return memcmp(a, b, sizeof(v3_tuid)) == 0; } #if defined(_WIN32) # define V3_COM_COMPAT 1 # define V3_API __stdcall #else # define V3_COM_COMPAT 0 # define V3_API #endif #if V3_COM_COMPAT enum { _V3_SIGNEDNESS = -1, V3_NO_INTERFACE = 0x80004002L, V3_OK = 0, V3_TRUE = 0, V3_FALSE = 1, V3_INVALID_ARG = 0x80070057L, V3_NOT_IMPLEMENTED = 0x80004001L, V3_INTERNAL_ERR = 0x80004005L, V3_NOT_INITIALIZED = 0x8000FFFFL, V3_NOMEM = 0x8007000EL }; # define V3_ID(a, b, c, d) { \ ((a) & 0x000000FF), \ ((a) & 0x0000FF00) >> 8, \ ((a) & 0x00FF0000) >> 16, \ ((a) & 0xFF000000) >> 24, \ \ ((b) & 0x00FF0000) >> 16, \ ((b) & 0xFF000000) >> 24, \ ((b) & 0x000000FF), \ ((b) & 0x0000FF00) >> 8, \ \ ((c) & 0xFF000000) >> 24, \ ((c) & 0x00FF0000) >> 16, \ ((c) & 0x0000FF00) >> 8, \ ((c) & 0x000000FF), \ \ ((d) & 0xFF000000) >> 24, \ ((d) & 0x00FF0000) >> 16, \ ((d) & 0x0000FF00) >> 8, \ ((d) & 0x000000FF), \ } #else // V3_COM_COMPAT enum { V3_NO_INTERFACE = -1, V3_OK, V3_TRUE = V3_OK, V3_FALSE, V3_INVALID_ARG, V3_NOT_IMPLEMENTED, V3_INTERNAL_ERR, V3_NOT_INITIALIZED, V3_NOMEM }; # define V3_ID(a, b, c, d) { \ ((a) & 0xFF000000) >> 24, \ ((a) & 0x00FF0000) >> 16, \ ((a) & 0x0000FF00) >> 8, \ ((a) & 0x000000FF), \ \ ((b) & 0xFF000000) >> 24, \ ((b) & 0x00FF0000) >> 16, \ ((b) & 0x0000FF00) >> 8, \ ((b) & 0x000000FF), \ \ ((c) & 0xFF000000) >> 24, \ ((c) & 0x00FF0000) >> 16, \ ((c) & 0x0000FF00) >> 8, \ ((c) & 0x000000FF), \ \ ((d) & 0xFF000000) >> 24, \ ((d) & 0x00FF0000) >> 16, \ ((d) & 0x0000FF00) >> 8, \ ((d) & 0x000000FF), \ } #endif // V3_COM_COMPAT #define V3_ID_COPY(iid) \ { iid[0], iid[1], iid[ 2], iid[ 3], iid[ 4], iid[ 5], iid[ 6], iid[ 7], \ iid[8], iid[9], iid[10], iid[11], iid[12], iid[13], iid[14], iid[15] } /** * funknown */ struct v3_funknown { v3_result (V3_API* query_interface)(void* self, const v3_tuid iid, void** obj); uint32_t (V3_API* ref)(void* self); uint32_t (V3_API* unref)(void* self); }; static constexpr const v3_tuid v3_funknown_iid = V3_ID(0x00000000, 0x00000000, 0xC0000000, 0x00000046); /** * plugin base */ struct v3_plugin_base { #ifndef __cplusplus struct v3_funknown; #endif v3_result (V3_API* initialize)(void* self, struct v3_funknown** context); v3_result (V3_API* terminate)(void* self); }; static constexpr const v3_tuid v3_plugin_base_iid = V3_ID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625); #ifdef __cplusplus /** * cast object into its proper C++ type. * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. * * we can use this as a little helper for keeping both C and C++ compatiblity. * specialized templated calls are defined where required * (that is, object inherits from something other than `v3_funknown`) * * example usage: `v3_cpp_obj(obj)->method(obj, args...);` */ template static inline constexpr T* v3_cpp_obj(T** obj) { /** * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, * but we need everything to be `static_cast` for it to be `constexpr` compatible. */ return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); } /** * helper C++ functions to manually call v3_funknown methods on an object. */ template static inline v3_result v3_cpp_obj_query_interface(T** obj, const v3_tuid iid, M*** obj2) { return static_cast(static_cast(*obj))->query_interface(obj, iid, (void**)obj2); } template static inline uint32_t v3_cpp_obj_ref(T** obj) { return static_cast(static_cast(*obj))->ref(obj); } template static inline uint32_t v3_cpp_obj_unref(T** obj) { return static_cast(static_cast(*obj))->unref(obj); } template static inline v3_result v3_cpp_obj_initialize(T** obj, v3_funknown** context) { return static_cast( static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3))->initialize(obj, context); } template static inline v3_result v3_cpp_obj_terminate(T** obj) { return static_cast( static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3))->terminate(obj); } #endif