Skip to content
Snippets Groups Projects
fchost_storage.cc 3.2 KiB
Newer Older
#include "fchost_storage.h"

#include <algorithm>
ezsh's avatar
ezsh committed
#include <cstdio>
#include <memory>
#include <vector>

static_assert(CEF_STRING_TYPE_UTF16);

ezsh's avatar
ezsh committed
namespace fs = std::filesystem;

namespace {
	struct closeFile {
		void operator()(std::FILE* f) const noexcept
		{
			std::fclose(f);
		}
	};
}

CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const
{
klorpa's avatar
klorpa committed
	// holy hell this is awful. I hope Sugarcube doesn't actually USE this...
	CefRefPtr<CefV8Value> ret = CefV8Value::CreateArray(static_cast<int>(storage.size()));
	auto itr = storage.cbegin();
	for (int i = 0; i < static_cast<int>(storage.size()); ++i, ++itr)
	{
		ret->SetValue(i, CefV8Value::CreateString(itr->first));
	}
	return ret;
}

klorpa's avatar
klorpa committed
/* This shouldn't happen, so don't waste time on it. Sugarcube really only writes simple alphanumeric keys.
static bool SanitizePath(std::wstring& inpath)
{
	std::transform(inpath.begin(), inpath.end(), inpath.begin(), [](wchar_t c)
	{
		if (PathGetCharTypeW(c) != GCT_LFNCHAR)
			return L'_';
		else
			return c;
	});
}
*/

FCHostPersistentStorage::FCHostPersistentStorage(const std::filesystem::path& _path)
	: path{_path}
ezsh's avatar
ezsh committed
{
	ensure_folder_exists();
	load();
}

void FCHostPersistentStorage::set(const CefString& key, CefRefPtr<CefV8Value> val)
{
ezsh's avatar
ezsh committed
	base::set(key, val);
klorpa's avatar
klorpa committed
	// only strings get persisted (should be OK, Sugarcube will serialize first)
	if (val->IsString())
	{
		// we should probably be doing this async but TBT Sugarcube is the slow part, not the file IO
ezsh's avatar
ezsh committed
		std::unique_ptr<std::FILE, closeFile> fh;
#if defined(OS_WIN)
		fh.reset(_wfopen(get_filename(key).c_str(), L"wb"));
ezsh's avatar
ezsh committed
#else
		fh.reset(std::fopen(get_filename(key).c_str(), "wb"));
ezsh's avatar
ezsh committed
#endif
		CefString valStr = val->GetStringValue();
ezsh's avatar
ezsh committed
		if (valStr.size() > 0) {
			std::fwrite(valStr.c_str(), static_cast<size_t>(valStr.size() * sizeof(valStr.c_str()[0])), 1, fh.get());
		}
	}
}

bool FCHostPersistentStorage::remove(const CefString& key)
{
ezsh's avatar
ezsh committed
	fs::remove(get_filename(key));
	return base::remove(key);
ezsh's avatar
ezsh committed
	for (const auto& entry: fs::directory_iterator(path)) {
		if (fs::is_regular_file(entry.path())) {
			fs::remove_all(entry.path());
		}
ezsh's avatar
ezsh committed
	base::clear();
ezsh's avatar
ezsh committed
	std::vector<char> readbuf;

	std::unique_ptr<std::FILE, closeFile> fh;
	for (const auto& entry: fs::directory_iterator(path)) {
		if (fs::is_regular_file(entry.path())) {
			const auto entrySize = fs::file_size(entry.path());
			readbuf.resize(static_cast<std::size_t>(entrySize + 2)); // +1 char16
ezsh's avatar
ezsh committed

#if defined(OS_WIN)
			fh.reset(_wfopen(entry.path().c_str(), L"rb"));
ezsh's avatar
ezsh committed
#else
			fh.reset(std::fopen(entry.path().c_str(), "rb"));
ezsh's avatar
ezsh committed
#endif
			if (std::fread(&readbuf[0], entrySize, 1, fh.get())) {
				readbuf[entrySize + 1] = readbuf[entrySize] = 0; // null terminate
				CefString val;
				val.FromString16(static_cast<const char16_t*>(static_cast<const void*>(readbuf.data())));
ezsh's avatar
ezsh committed
				storage.emplace(entry.path().filename().native(), CefV8Value::CreateString(val));
ezsh's avatar
ezsh committed
fs::path FCHostPersistentStorage::get_filename(const CefString& key) const
ezsh's avatar
ezsh committed
#if defined (OS_WIN)
	return path / key.ToWString();
#else
	return path / key.ToString();
#endif
}

void FCHostPersistentStorage::ensure_folder_exists() const
{
	// ignore returned errors
ezsh's avatar
ezsh committed
	fs::create_directories(path);