Skip to content
Snippets Groups Projects
voltu.cpp 3.01 KiB
Newer Older
/**
 * @file voltu.cpp
 * @copyright Copyright (c) 2020 Nicolas Pope, MIT License
 * @author Nicolas Pope
 */

Nicolas Pope's avatar
Nicolas Pope committed
#include <voltu/initialise.hpp>
#include <voltu/types/errors.hpp>
#include <voltu/voltu.hpp>

#if defined(WIN32)
#include <windows.h>
#pragma comment(lib, "User32.lib")
Nicolas Pope's avatar
Nicolas Pope committed
#else
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
Nicolas Pope's avatar
Nicolas Pope committed
#endif

#include <iostream>

Nicolas Pope's avatar
Nicolas Pope committed
static bool g_init = false;

typedef void* Library;

static Library loadLibrary(const char *file)
{
#if defined(WIN32)
	return (Library)LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
#else
	return (Library)dlopen(file, RTLD_LOCAL | RTLD_NOW);
#endif
}

static void *getFunction(Library lib, const char *fname)
{
#if defined(WIN32)
	return (void*)GetProcAddress((HMODULE)lib, fname);
#else
	return dlsym(lib, fname);
#endif
}


static void unloadLibrary(Library lib)
{
	if (!lib) return;

#if defined(WIN32)
	FreeLibrary((HMODULE)lib);
#else
	dlclose(lib);
#endif
}

static bool is_file(const std::string &path) {
#ifdef WIN32
	WIN32_FIND_DATA ffd;
	HANDLE hFind = FindFirstFile(path.c_str(), &ffd);

	if (hFind == INVALID_HANDLE_VALUE) return false;
	FindClose(hFind);
	return true;
#else
	struct stat s;
	if (::stat(path.c_str(), &s) == 0) {
		return true;
	} else {
		return false;
	}
#endif
}

Nicolas Pope's avatar
Nicolas Pope committed
static std::string locateLibrary()
{
	// TODO: Use full paths and find correct versions
Nicolas Pope's avatar
Nicolas Pope committed
#if defined(WIN32)
	return "voltu.dll";
#else
	std::string name = "libvoltu.so";
	std::string vname = name + std::string(".") + std::to_string(VOLTU_VERSION_MAJOR) + std::string(".") + std::to_string(VOLTU_VERSION_MINOR);
	if (is_file(std::string("./") + vname)) return std::string("./") + vname;
	else if (is_file(std::string("./") + name)) return std::string("./") + name;
	else if (is_file(std::string("../") + vname)) return std::string("../") + vname;
	else if (is_file(std::string("../") + name)) return std::string("../") + name;
	else if (is_file(std::string("/usr/local/lib/") + vname)) return std::string("/usr/local/lib/") + vname;
	else if (is_file(std::string("/usr/local/lib/") + name)) return std::string("/usr/local/lib/") + name;
	return name;
Nicolas Pope's avatar
Nicolas Pope committed
#endif
}

std::shared_ptr<voltu::System> voltu::instance()
{
	if (g_init) return nullptr;
	
	std::string name = locateLibrary();
	std::cout << "Loading VolTu Runtime: " << name << std::endl;
Nicolas Pope's avatar
Nicolas Pope committed
	Library handle = loadLibrary(name.c_str());

	if (handle)
	{
		voltu::System* (*init)();
		init = (voltu::System* (*)())getFunction(handle, "voltu_initialise");

		if (init)
		{
			g_init = true;
			std::shared_ptr<voltu::System> instance(init());

			if (!instance)
			{
				throw voltu::exceptions::RuntimeAlreadyInUse();
			// FIXME: Perhaps use a C method instead for safety?
Nicolas Pope's avatar
Nicolas Pope committed
			auto ver = instance->getVersion();
			if (ver.major != VOLTU_VERSION_MAJOR || ver.minor != VOLTU_VERSION_MINOR)
			{
				throw voltu::exceptions::RuntimeVersionMismatch();
Nicolas Pope's avatar
Nicolas Pope committed
			}

			return instance;
		}
		else
		{
			throw voltu::exceptions::LibraryLoadFailed();
		}
	}
	else
	{
		throw voltu::exceptions::LibraryLoadFailed();
	}

	return nullptr;
}