Pixel-Composer/extensions/file_dropper/file_dropper.cpp
2022-12-16 15:18:09 +07:00

496 lines
14 KiB
C++

#pragma once
#include "stdafx.h"
// As per http://help.yoyogames.com/hc/en-us/articles/216755258:
typedef int gml_ds_map;
//
typedef void (*gml_event_perform_async_t)(gml_ds_map map, int event_type);
typedef int (*gml_ds_map_create_ext_t)(int n, ...);
typedef bool (*gml_ds_map_set_double_t)(gml_ds_map map, char* key, double value);
typedef bool (*gml_ds_map_set_string_t)(gml_ds_map map, char* key, const char* value);
//
extern gml_event_perform_async_t gml_event_perform_async;
extern gml_ds_map_create_ext_t gml_ds_map_create_ext;
extern gml_ds_map_set_double_t gml_ds_map_set_double;
extern gml_ds_map_set_string_t gml_ds_map_set_string;
//
inline gml_ds_map gml_ds_map_create() {
return gml_ds_map_create_ext(0);
}
// A wrapper for queuing async events for GML easier.
class gml_async_event {
private:
gml_ds_map map;
public:
gml_async_event() {
map = gml_ds_map_create();
}
gml_async_event(char* type) {
map = gml_ds_map_create();
gml_ds_map_set_string(map, "event_type", type);
}
~gml_async_event() {
//
}
/// Dispatches this event and cleans up the map.
void dispatch(int kind) {
gml_event_perform_async(map, kind);
}
bool set(char* key, double value) {
return gml_ds_map_set_double(map, key, value);
}
bool set(char* key, const char* value) {
return gml_ds_map_set_string(map, key, value);
}
};#pragma once
#include "stdafx.h"
#include <vector>
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
#include <optional>
#endif
#include <stdint.h>
#include <cstring>
#include <tuple>
using namespace std;
#define dllg /* tag */
#if defined(WIN32)
#define dllx extern "C" __declspec(dllexport)
#elif defined(GNUC)
#define dllx extern "C" __attribute__ ((visibility("default")))
#else
#define dllx extern "C"
#endif
#ifdef _WINDEF_
typedef HWND GAME_HWND;
#endif
struct gml_buffer {
private:
uint8_t* _data;
int32_t _size;
int32_t _tell;
public:
gml_buffer() : _data(nullptr), _tell(0), _size(0) {}
gml_buffer(uint8_t* data, int32_t size, int32_t tell) : _data(data), _size(size), _tell(tell) {}
inline uint8_t* data() { return _data; }
inline int32_t tell() { return _tell; }
inline int32_t size() { return _size; }
};
class gml_istream {
uint8_t* pos;
uint8_t* start;
public:
gml_istream(void* origin) : pos((uint8_t*)origin), start((uint8_t*)origin) {}
template<class T> T read() {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable to be read");
T result{};
std::memcpy(&result, pos, sizeof(T));
pos += sizeof(T);
return result;
}
char* read_string() {
char* r = (char*)pos;
while (*pos != 0) pos++;
pos++;
return r;
}
template<class T> std::vector<T> read_vector() {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable to be read");
auto n = read<uint32_t>();
std::vector<T> vec(n);
std::memcpy(vec.data(), pos, sizeof(T) * n);
pos += sizeof(T) * n;
return vec;
}
gml_buffer read_gml_buffer() {
auto _data = (uint8_t*)read<int64_t>();
auto _size = read<int32_t>();
auto _tell = read<int32_t>();
return gml_buffer(_data, _size, _tell);
}
#pragma region Tuples
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template<typename... Args>
std::tuple<Args...> read_tuple() {
std::tuple<Args...> tup;
std::apply([this](auto&&... arg) {
((
arg = this->read<std::remove_reference_t<decltype(arg)>>()
), ...);
}, tup);
return tup;
}
template<class T> optional<T> read_optional() {
if (read<bool>()) {
return read<T>;
} else return {};
}
#else
template<class A, class B> std::tuple<A, B> read_tuple() {
A a = read<A>();
B b = read<B>();
return std::tuple<A, B>(a, b);
}
template<class A, class B, class C> std::tuple<A, B, C> read_tuple() {
A a = read<A>();
B b = read<B>();
C c = read<C>();
return std::tuple<A, B, C>(a, b, c);
}
template<class A, class B, class C, class D> std::tuple<A, B, C, D> read_tuple() {
A a = read<A>();
B b = read<B>();
C c = read<C>();
D d = read<d>();
return std::tuple<A, B, C, D>(a, b, c, d);
}
#endif
};
class gml_ostream {
uint8_t* pos;
uint8_t* start;
public:
gml_ostream(void* origin) : pos((uint8_t*)origin), start((uint8_t*)origin) {}
template<class T> void write(T val) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable to be write");
memcpy(pos, &val, sizeof(T));
pos += sizeof(T);
}
void write_string(const char* s) {
for (int i = 0; s[i] != 0; i++) write<char>(s[i]);
write<char>(0);
}
template<class T> void write_vector(std::vector<T>& vec) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable to be write");
auto n = vec.size();
write<uint32_t>(n);
memcpy(pos, vec.data(), n * sizeof(T));
pos += n * sizeof(T);
}
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template<typename... Args>
void write_tuple(std::tuple<Args...> tup) {
std::apply([this](auto&&... arg) {
(this->write(arg), ...);
}, tup);
}
template<class T> void write_optional(optional<T>& val) {
auto hasValue = val.has_value();
write<bool>(hasValue);
if (hasValue) write<T>(val.value());
}
#else
template<class A, class B> void write_tuple(std::tuple<A, B>& tup) {
write<A>(std::get<0>(tup));
write<B>(std::get<1>(tup));
}
template<class A, class B, class C> void write_tuple(std::tuple<A, B, C>& tup) {
write<A>(std::get<0>(tup));
write<B>(std::get<1>(tup));
write<C>(std::get<2>(tup));
}
template<class A, class B, class C, class D> void write_tuple(std::tuple<A, B, C, D>& tup) {
write<A>(std::get<0>(tup));
write<B>(std::get<1>(tup));
write<C>(std::get<2>(tup));
write<D>(std::get<3>(tup));
}
#endif
};
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by file_dropper.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#ifdef _WINDOWS
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#endif
#if defined(WIN32)
#define dllx extern "C" __declspec(dllexport)
#elif defined(GNUC)
#define dllx extern "C" __attribute__ ((visibility("default")))
#else
#define dllx extern "C"
#endif
#define trace(...) { printf("[file_dropper:%d] ", __LINE__); printf(__VA_ARGS__); printf("\n"); fflush(stdout); }
#include "gml_ext.h"
// TODO: reference additional headers your program requires here#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>
#pragma once
#include "stdafx.h"
template<typename T> T* malloc_arr(size_t count) {
return (T*)malloc(sizeof(T) * count);
}
template<typename T> T* realloc_arr(T* arr, size_t count) {
return (T*)realloc(arr, sizeof(T) * count);
}
template<typename T> T* memcpy_arr(T* dst, const T* src, size_t count) {
return (T*)memcpy(dst, src, sizeof(T) * count);
}
template<typename C> class tiny_string_t {
C* _data = nullptr;
size_t _size = 0;
size_t _capacity = 0;
public:
tiny_string_t() {}
inline void init(size_t capacity = 32) {
_data = malloc_arr<C>(capacity);
_size = 0;
_capacity = capacity;
}
inline void init(const C* val) {
init(4);
set(val);
}
/// Returns current size, in characters (not including final NUL)
inline size_t size() { return _size; }
inline void setSize(size_t size) { _size = size; }
inline bool empty() {
return _size == 0;
}
inline C* c_str() {
return _data;
}
inline C* prepare(size_t capacity) {
if (_capacity < capacity) {
auto new_data = realloc_arr(_data, capacity);
if (new_data == nullptr) {
trace("Failed to reallocate %zu bytes in tiny_string::prepare", sizeof(C) * capacity);
return nullptr;
}
_data = new_data;
_capacity = capacity;
}
return _data;
}
inline const C* set(const C* value, size_t len = SIZE_MAX) {
if (len == SIZE_MAX) {
const C* iter = value;
len = 1;
while (*iter) { iter++; len++; }
}
C* result = prepare(len);
memcpy_arr(result, value, len);
_size = len - 1;
return result;
}
//
inline void operator=(const C* value) { set(value); }
template<size_t size> inline void operator =(const C(&value)[size]) { set(value, size); }
};
struct tiny_string : public tiny_string_t<char> {
public:
inline char* conv(const wchar_t* wstr) {
auto size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
auto str = prepare(size);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, size, NULL, NULL);
setSize(size - 1);
return str;
}
inline void operator=(const char* value) { set(value); }
template<size_t size> inline void operator =(const char(&value)[size]) { set(value, size); }
};
struct tiny_wstring : public tiny_string_t<wchar_t> {
public:
inline wchar_t* conv(const char* str) {
auto size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
auto wstr = prepare(size);
MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, size);
setSize(size - 1);
return wstr;
}
inline void operator=(const wchar_t* value) { set(value); }
template<size_t size> inline void operator =(const wchar_t(&value)[size]) { set(value, size); }
};#include "gml_ext.h"
extern bool file_dropper_init(GAME_HWND hwnd);
dllx double file_dropper_init_raw(void* _in_ptr, double _in_ptr_size) {
gml_istream _in(_in_ptr);
GAME_HWND _arg_hwnd;
_arg_hwnd = (GAME_HWND)_in.read<uint64_t>();
return file_dropper_init(_arg_hwnd);
}
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
/// @author YellowAfterlife
#include <oleidl.h>
#include "stdafx.h"
#include "gml_async_glue.h"
#include "tiny_string.h"
static tiny_string utf8c;
static UINT GMDropTarget_refCount = 0;
struct GMDropTarget : IDropTarget {
// Inherited via IDropTarget
virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override {
if (IsEqualIID(riid, IID_IDropTarget) || IsEqualIID(riid, IID_IUnknown)) {
*ppvObject = this;
this->AddRef();
return NOERROR;
} else {
*ppvObject = nullptr;
return E_NOINTERFACE;
}
}
virtual ULONG __stdcall AddRef(void) override {
return ++GMDropTarget_refCount;
}
virtual ULONG __stdcall Release(void) override {
return --GMDropTarget_refCount;
}
virtual HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override {
return S_OK;
}
virtual HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override {
return S_OK;
}
virtual HRESULT __stdcall DragLeave(void) override {
return S_OK;
}
virtual HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override {
//trace("drop");
FORMATETC formatEtc;
formatEtc.cfFormat = CF_HDROP;
formatEtc.dwAspect = DVASPECT_CONTENT;
formatEtc.lindex = -1;
formatEtc.ptd = NULL;
formatEtc.tymed = TYMED_HGLOBAL;
STGMEDIUM medium;
auto hr = pDataObj->GetData(&formatEtc, &medium);
if (FAILED(hr)) return hr;
if (medium.tymed != TYMED_HGLOBAL) return S_OK;
auto drop = (HDROP)medium.hGlobal;
auto fileCount = DragQueryFileW(drop, UINT32_MAX, NULL, 0);
//trace("fileCount=%d", fileCount);
for (auto k = 0u; k < fileCount; k++) {
auto nameLen = DragQueryFileW(drop, k, nullptr, 0);
if (nameLen == 0) continue;
auto wname = malloc_arr<wchar_t>(nameLen + 1);
DragQueryFile(drop, k, wname, nameLen + 1);
auto name = utf8c.conv(wname);
gml_async_event e("file_drop");
e.set("filename", name);
e.dispatch(75); // async system
delete wname;
}
return S_OK;
}
};
static GMDropTarget* dropTarget = nullptr;
dllg bool file_dropper_init(GAME_HWND hwnd) {
if (dropTarget != nullptr) return true;
auto hr = OleInitialize(0);
if (hr != S_OK && hr != S_FALSE) {
trace("OleInitialize failed, hresult=0x%x", hr);
return false;
}
dropTarget = new GMDropTarget();
utf8c.init();
hr = RegisterDragDrop(hwnd, dropTarget);
if (FAILED(hr)) trace("RegisterDragDrop failed, hresult=0x%x", hr);
return SUCCEEDED(hr);
}
#include "gml_async_glue.h"
gml_event_perform_async_t gml_event_perform_async = nullptr;
gml_ds_map_create_ext_t gml_ds_map_create_ext = nullptr;
gml_ds_map_set_double_t gml_ds_map_set_double = nullptr;
gml_ds_map_set_string_t gml_ds_map_set_string = nullptr;
// Called by GM on DLL init
dllx double RegisterCallbacks(void* f1, void* f2, void* f3, void* f4) {
gml_event_perform_async = (gml_event_perform_async_t)f1;
gml_ds_map_create_ext = (gml_ds_map_create_ext_t)f2;
gml_ds_map_set_double = (gml_ds_map_set_double_t)f3;
gml_ds_map_set_string = (gml_ds_map_set_string_t)f4;
return 0;
}// stdafx.cpp : source file that includes just the standard includes
// file_dropper.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file