diff --git a/FCHost/CMakeLists.txt b/FCHost/CMakeLists.txt index d54e523a94771a1177590ed273364744ccb63889..ab5d14bc26601b4d5dbd83876787536de3686895 100644 --- a/FCHost/CMakeLists.txt +++ b/FCHost/CMakeLists.txt @@ -97,6 +97,9 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release) # Project name. project(fchost) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Use folders in the resulting project files. set_property(GLOBAL PROPERTY OS_FOLDERS ON) @@ -113,7 +116,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON) # Set the CEF_ROOT environment variable before executing CMake. For example: # > set CEF_ROOT=c:\path\to\cef_binary_3.2704.xxxx.gyyyyyyy_windows32 # -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # diff --git a/FCHost/fchost/fchost_app.cc b/FCHost/fchost/fchost_app.cc index 13807a9207bf32495101cae16c4d39003b9a08fa..b15304c1594101e8fb2b807e417e1496d6b8ab90 100644 --- a/FCHost/fchost/fchost_app.cc +++ b/FCHost/fchost/fchost_app.cc @@ -4,8 +4,6 @@ #include "fchost_app.h" -#include <string> - #include "include/cef_browser.h" #include "include/cef_command_line.h" #include "include/views/cef_browser_view.h" @@ -14,6 +12,13 @@ #include "fchost_handler.h" #include "fchost_storage_js.h" +#include <filesystem> +#include <string> + +#if defined(OS_LINUX) + #include <unistd.h> +#endif + namespace { // When using the Views framework this object provides the delegate @@ -55,6 +60,36 @@ class SimpleWindowDelegate : public CefWindowDelegate { DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate); }; +std::filesystem::path executablePath() { +#if defined(OS_WIN) + wchar_t target_path[_MAX_PATH]; + GetModuleFileNameW(NULL, target_path, _MAX_PATH); + return std::filesystem::path(target_path); +#elif defined(OS_LINUX) + std::string buf; + buf.resize(32); // initial size estimate + + for (;; ) { + ssize_t ret = readlink("/proc/self/exe", buf.data(), buf.size()); + + if (ret == -1) { + perror("getexename() failed"); + break; + } + + if (static_cast<std::size_t>(ret) >= buf.size()) { // >= because we need the terminating NUL too + buf.resize(buf.size() * 2); + continue; + } + + return buf; + } + + return {}; +#else + #error "Platform-specific code required" +#endif +} } // namespace FCHostApp::FCHostApp() {} @@ -86,15 +121,11 @@ void FCHostApp::OnContextInitialized() { // For now, read from external file. Probably want to make this a resource // at least on Windows. + std::filesystem::path gameHTML = executablePath().parent_path() / "FC_pregmod.html"; #if defined(OS_WIN) - wchar_t target_path[_MAX_PATH]; - GetModuleFileNameW(NULL, target_path, _MAX_PATH); - std::wstring url = target_path; - size_t pos = url.find_last_of(L"\\/"); - url = url.substr(0, pos); - url += L"/FC_pregmod.html"; + std::wstring url = gameHTML.native(); #else - #error "Platform-specific code needed" + std::string url = "file://" + gameHTML.string(); #endif // Allow some access flexibility for our local file @@ -127,4 +158,3 @@ void FCHostApp::OnContextInitialized() { void FCHostApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) { FCHostStorageRegister(GetLocalStorePath().ToWString() + L"/FCHostPersistentStorage", context->GetGlobal()); } - diff --git a/FCHost/fchost/fchost_handler.cc b/FCHost/fchost/fchost_handler.cc index b3c8cd2808282618eb3a8a700324148139fb94c8..95eec1b6e56592ccd2c081c94b970ed189bfbef1 100644 --- a/FCHost/fchost/fchost_handler.cc +++ b/FCHost/fchost/fchost_handler.cc @@ -206,7 +206,13 @@ bool FCHostHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser, CefWindowInfo windowInfo; CefBrowserSettings settings; CefPoint point; + #if defined (OS_WIN) windowInfo.SetAsPopup(browser->GetHost()->GetWindowHandle(), "DevTools"); + #elif defined(OS_LINUX) + windowInfo.SetAsChild(browser->GetHost()->GetWindowHandle(), CefRect(100, 100, 800, 600)); + #else + #error "Platform-specific code required" + #endif browser->GetHost()->ShowDevTools(windowInfo, browser->GetHost()->GetClient(), settings, point); return true; diff --git a/FCHost/fchost/fchost_handler.h b/FCHost/fchost/fchost_handler.h index 1756d701c91d7aab0b1ab092bb4713ff7234d785..19aeb67c102126d9160f6d7f2b896da85503bec9 100644 --- a/FCHost/fchost/fchost_handler.h +++ b/FCHost/fchost/fchost_handler.h @@ -13,7 +13,8 @@ class FCHostHandler : public CefClient, public CefLifeSpanHandler, public CefLoadHandler, public CefDownloadHandler, - public CefKeyboardHandler { + public CefKeyboardHandler, + private CefDialogHandler { public: explicit FCHostHandler(bool use_views); ~FCHostHandler(); @@ -32,6 +33,10 @@ class FCHostHandler : public CefClient, virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE { return this; } virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE { return this; } + CefRefPtr< CefDialogHandler > GetDialogHandler() override { return this; } + bool OnFileDialog(CefRefPtr<CefBrowser> browser, CefDialogHandler::FileDialogMode mode, + const CefString& title, const CefString& default_file_path, const std::vector<CefString>& accept_filters, + int selected_accept_filter, CefRefPtr<CefFileDialogCallback> callback) override; // CefDisplayHandler methods: virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, diff --git a/FCHost/fchost/fchost_handler_linux.cc b/FCHost/fchost/fchost_handler_linux.cc new file mode 100644 index 0000000000000000000000000000000000000000..f960f287892e3759892627a28edfd9076d77931f --- /dev/null +++ b/FCHost/fchost/fchost_handler_linux.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "fchost_handler.h" + +#if defined(CEF_X11) +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#endif + +#include "include/base/cef_logging.h" +#include "include/cef_browser.h" + +#include <cstdlib> +#include <cstdio> +#include <filesystem> +#include <iomanip> +#include <memory> +#include <sstream> +#include <string> + +// #define TRACE + +#ifdef TRACE + #include <iostream> +#endif + +namespace { + + std::string executeProcessAndReadStdOut(const std::string& command) + { +#ifdef TRACE + std::cerr << "Command: " << command << std::endl; +#endif + std::unique_ptr<FILE, decltype(&::pclose)> f(::popen(command.c_str(), "r"), &::pclose); + std::ostringstream output; + const int BUF_SIZE = 1024; + char buf[BUF_SIZE]; + while(!std::feof(f.get())){ + std::size_t read = std::fread(buf, 1, BUF_SIZE, f.get()); + output.write(buf, static_cast<std::streamsize>(read)); + } + std::string res = output.str(); + return res.substr(0, res.size() - 1); // to clear out CR + } + + std::string runKDialog(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path, + const std::vector<CefString>& accept_filters) + { + std::ostringstream cmdLine; + cmdLine << "kdialog"; + if (!title.empty()) { + cmdLine << " --title " << std::quoted(title.ToString()); + } + +#ifdef TRACE + std::cerr << "mode: " << std::hex << mode << std::dec << std::endl + << "default_file_path: " << default_file_path << std::endl + << "accept_filters: " << std::endl; + + for (const auto& f: accept_filters) { + std::cerr << f.ToString() << std::endl; + } +#endif + + // kdialog does not support these + mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG)); + mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_HIDEREADONLY_FLAG)); + + switch (mode) { + case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_MULTIPLE: + cmdLine << " --multiple"; [[fallthrough]] + case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN: + cmdLine << " --getopenfilename"; + break; + case CefDialogHandler::FileDialogMode::FILE_DIALOG_SAVE: + cmdLine << " --getsavefilename"; + break; + case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_FOLDER: + cmdLine << " --getexistingdirectory"; + break; + default: + break; // TODO + } + + const std::string dsp = default_file_path.ToString(); + if (dsp.find('/') != std::string::npos) { + cmdLine << ' ' << std::quoted(dsp); + } else { + // TODO save last used directory and put it here instead of $HOME + cmdLine << ' ' << std::quoted(std::string(getenv("HOME")) + '/' + dsp); + } + + if (accept_filters.size()) { + cmdLine << " \'"; + for (const auto& f: accept_filters) { + const auto s = f.ToString(); + cmdLine << " |" << s << " file(*" << s << ')'; + } + cmdLine << '\''; + } + + return executeProcessAndReadStdOut(cmdLine.str()); + } + + std::string runZenity(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path, + const std::vector<CefString>& accept_filters) + { + std::ostringstream cmdLine; + cmdLine << "zenity --file-selection"; + + // zenity does not support these + mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_HIDEREADONLY_FLAG)); + + if (mode & CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG) { + cmdLine << " --confirm-overwrite"; + } + mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG)); + + switch (mode) { + case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_MULTIPLE: + cmdLine << " --multiple"; + break; + case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN: + // this is the implicit default mode for zenity + break; + case CefDialogHandler::FileDialogMode::FILE_DIALOG_SAVE: + cmdLine << " --save"; + break; + case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_FOLDER: + cmdLine << " --directory"; + break; + default: + break; // TODO ? + } + + const std::string dsp = default_file_path.ToString(); + if (dsp.find('/') != std::string::npos) { + cmdLine << " --filename=" << std::quoted(dsp); + } else { + // TODO save last used directory and put it here instead of $HOME + cmdLine << " --filename=" << std::quoted(std::string(getenv("HOME")) + '/' + dsp); + } + + for (const auto& f: accept_filters) { + const auto s = f.ToString(); + cmdLine << " --file-filter=\"" << s << " file | *" << s << '"'; + } + + return executeProcessAndReadStdOut(cmdLine.str()); + } + + using DialogRunFunc = std::string (*)(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path, + const std::vector<CefString>& accept_filters); + + class DialogHelper { + public: + DialogHelper(); + std::string operator()(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path, + const std::vector<CefString>& accept_filters) const + { + return func_ ? func_(mode, title, default_file_path, accept_filters) : ""; + } + private: + DialogRunFunc func_; + }; + + DialogHelper::DialogHelper() + { + // we will try to launch kdialog or zenity + std::string dialogExecutable; + // try to determine which environment we run inside + std::string desktop = getenv("XDG_CURRENT_DESKTOP"); + + const auto checkExeExists = [](const char* name) { + int ec = ::system((std::string(name) + " --help > /dev/null").c_str()); + return ec >= 0 && ec < 127; + }; + + if (desktop == "KDE") { + dialogExecutable = "kdialog"; + } else if (desktop == "GNOME") { + dialogExecutable = "zenity"; + } else { + // well, let's check executables + if (checkExeExists("zenity")) { + dialogExecutable = "zenity"; + } else if (checkExeExists("kdialog")){ + dialogExecutable = "kdialog"; + } + } + +#ifdef TRACE + std::cerr << "dialogExecutable: " << dialogExecutable << std::endl + << "checkExeExists: " << checkExeExists(dialogExecutable.c_str()) << std::endl; +#endif + + if (!dialogExecutable.empty() && checkExeExists(dialogExecutable.c_str())) { + if (dialogExecutable == "kdialog") { + func_ = &runKDialog; + } else if (dialogExecutable == "zenity") { + func_ = &runZenity; + } else { + func_ = nullptr; + } + } + } + + +} + +void FCHostHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) { + std::string titleStr(title); + +#if defined(CEF_X11) + // Retrieve the X11 display shared with Chromium. + ::Display* display = cef_get_xdisplay(); + DCHECK(display); + + // Retrieve the X11 window handle for the browser. + ::Window window = browser->GetHost()->GetWindowHandle(); + if (window == kNullWindowHandle) + return; + + // Retrieve the atoms required by the below XChangeProperty call. + const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"}; + Atom atoms[2]; + int result = + XInternAtoms(display, const_cast<char**>(kAtoms), 2, false, atoms); + if (!result) + NOTREACHED(); + + // Set the window title. + XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace, + reinterpret_cast<const unsigned char*>(titleStr.c_str()), + titleStr.size()); + + // TODO(erg): This is technically wrong. So XStoreName and friends expect + // this in Host Portable Character Encoding instead of UTF-8, which I believe + // is Compound Text. This shouldn't matter 90% of the time since this is the + // fallback to the UTF8 property above. + XStoreName(display, browser->GetHost()->GetWindowHandle(), titleStr.c_str()); +#endif // defined(CEF_X11) +} + +bool FCHostHandler::OnFileDialog(CefRefPtr<CefBrowser> /* browser */, CefDialogHandler::FileDialogMode mode, + const CefString& title, const CefString& default_file_path, const std::vector<CefString>& accept_filters, + int /* selected_accept_filter */, CefRefPtr<CefFileDialogCallback> callback) +{ + static DialogHelper helper; + + std::string fn = helper(mode, title, default_file_path, accept_filters); +#ifdef TRACE + std::cerr << "fn: " << fn << std::endl; +#endif + if (fn.empty()) { + callback->Cancel(); + return false; + } + + std::vector<CefString> selected; + selected.push_back(fn); + callback->Continue(0, selected); + return true; +} diff --git a/FCHost/fchost/fchost_handler_win.cc b/FCHost/fchost/fchost_handler_win.cc index 106787eb5f89706a422d859b8024aeb3371e5374..483f69258c702e8f01a486ff6601767b3a631c67 100644 --- a/FCHost/fchost/fchost_handler_win.cc +++ b/FCHost/fchost/fchost_handler_win.cc @@ -26,3 +26,10 @@ void FCHostHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser, SetWindowText(hwnd, std::wstring(title).c_str()); } + +bool FCHostHandler::OnFileDialog(CefRefPtr<CefBrowser> /* browser */, CefDialogHandler::FileDialogMode /* mode */, + const CefString& /* title */, const CefString& /* default_file_path */, const std::vector<CefString>& /* accept_filters */, + int /* selected_accept_filter */, CefRefPtr<CefFileDialogCallback> /* callback */) +{ + return false; // to display the default dialog +} diff --git a/FCHost/fchost/fchost_linux.cc b/FCHost/fchost/fchost_linux.cc new file mode 100644 index 0000000000000000000000000000000000000000..837df28715be7c8b36e0aad937a9993c716c9b5a --- /dev/null +++ b/FCHost/fchost/fchost_linux.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "fchost_app.h" + +#if defined(CEF_X11) +#include <X11/Xlib.h> +#endif + +#include "include/base/cef_logging.h" +#include "include/cef_command_line.h" + +#include <filesystem> + +CefString FCHostApp::GetLocalStorePath() +{ + std::filesystem::path ret = std::filesystem::path{getenv("HOME")} / ".local" / "share" / "FreeCities_Pregmod"; + + return CefString(ret.string()); +} + +#if defined(CEF_X11) +namespace { + +int XErrorHandlerImpl(Display* display, XErrorEvent* event) { + LOG(WARNING) << "X error received: " + << "type " << event->type << ", " + << "serial " << event->serial << ", " + << "error_code " << static_cast<int>(event->error_code) << ", " + << "request_code " << static_cast<int>(event->request_code) + << ", " + << "minor_code " << static_cast<int>(event->minor_code); + return 0; +} + +int XIOErrorHandlerImpl(Display* display) { + return 0; +} + +} // namespace +#endif // defined(CEF_X11) + +// Entry point function for all processes. +int main(int argc, char* argv[]) { + // Provide CEF with command-line arguments. + CefMainArgs main_args(argc, argv); + + // CEF applications have multiple sub-processes (render, plugin, GPU, etc) + // that share the same executable. This function checks the command-line and, + // if this is a sub-process, executes the appropriate logic. + int exit_code = CefExecuteProcess(main_args, nullptr, nullptr); + if (exit_code >= 0) { + // The sub-process has completed so return here. + return exit_code; + } + +#if defined(CEF_X11) + // Install xlib error handlers so that the application won't be terminated + // on non-fatal errors. + XSetErrorHandler(XErrorHandlerImpl); + XSetIOErrorHandler(XIOErrorHandlerImpl); +#endif + + // Parse command-line arguments for use in this method. + CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine(); + command_line->InitFromArgv(argc, argv); + + // It will create the first browser instance in OnContextInitialized() after + // CEF has initialized. + CefRefPtr<FCHostApp> app(new FCHostApp); + + // Specify CEF global settings here. + CefSettings settings; + + if (command_line->HasSwitch("enable-chrome-runtime")) { + // Enable experimental Chrome runtime. See issue #2969 for details. + settings.chrome_runtime = true; + } + + // Cache location is required for local storage + CefString local_storage = app->GetLocalStorePath(); + cef_string_from_utf16(local_storage.c_str(), local_storage.length(), &settings.cache_path); + +// When generating projects with CMake the CEF_USE_SANDBOX value will be defined +// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable +// use of the sandbox. +#if !defined(CEF_USE_SANDBOX) + settings.no_sandbox = true; +#endif + + // Initialize CEF for the browser process. + CefInitialize(main_args, settings, app.get(), nullptr); + + // Run the CEF message loop. This will block until CefQuitMessageLoop() is + // called. + CefRunMessageLoop(); + + // Shut down CEF. + CefShutdown(); + + return 0; +} diff --git a/FCHost/fchost/fchost_storage.cc b/FCHost/fchost/fchost_storage.cc index af13e4c8817c8d8490afceac1ec65083be6ed143..7326e838cfa1d654c867f9610da783ef4356ed53 100644 --- a/FCHost/fchost/fchost_storage.cc +++ b/FCHost/fchost/fchost_storage.cc @@ -1,9 +1,20 @@ #include "fchost_storage.h" #include <algorithm> -#if defined(OS_WIN) -#include "shlwapi.h" -#endif +#include <cstdio> +#include <memory> +#include <vector> + +namespace fs = std::filesystem; + +namespace { + struct closeFile { + void operator()(std::FILE* f) const noexcept + { + std::fclose(f); + } + }; +} CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const { @@ -17,7 +28,6 @@ CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const return ret; } -#if defined(OS_WIN) /* This shouldn't happen, so don't waste time on it. Sugarcube really only writes simple alphanumeric keys. static bool SanitizePath(std::wstring& inpath) { @@ -31,96 +41,89 @@ static bool SanitizePath(std::wstring& inpath) } */ +FCHostPersistentStorage::FCHostPersistentStorage(const CefString& _path) +#if defined(OS_WIN) + : path(_path.ToWString()) +#else + : path(_path.ToString()) +#endif +{ + ensure_folder_exists(); + load(); +} + void FCHostPersistentStorage::set(const CefString& key, CefRefPtr<CefV8Value> val) { - __super::set(key, val); + base::set(key, val); // 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 - DWORD written; - HANDLE fh = CreateFile(get_filename(key).c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + std::unique_ptr<std::FILE, closeFile> fh; +#if defined(OS_WIN) + fh.reset(_wfopen(get_filename(key).c_str(), L"w")); +#else + fh.reset(std::fopen(get_filename(key).c_str(), "w")); +#endif CefString valStr = val->GetStringValue(); - if (valStr.size() > 0) - { - WriteFile(fh, valStr.c_str(), static_cast<DWORD>(valStr.size() * sizeof(valStr.c_str()[0])), &written, NULL); + if (valStr.size() > 0) { + std::fwrite(valStr.c_str(), static_cast<size_t>(valStr.size() * sizeof(valStr.c_str()[0])), 1, fh.get()); } - CloseHandle(fh); } } bool FCHostPersistentStorage::remove(const CefString& key) { - bool retval = __super::remove(key); - - DeleteFile(get_filename(key).c_str()); - - return retval; + fs::remove(get_filename(key)); + return base::remove(key); } void FCHostPersistentStorage::clear() { - __super::clear(); - - WIN32_FIND_DATAW w32fd; - HANDLE hFind = FindFirstFile((path + L"\\*").c_str(), &w32fd); - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - if (w32fd.dwFileAttributes & ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) - { - DeleteFile(get_filename(w32fd.cFileName).c_str()); - } - } while (FindNextFile(hFind, &w32fd)); - FindClose(hFind); + for (const auto& entry: fs::directory_iterator(path)) { + if (fs::is_regular_file(entry.path())) { + fs::remove_all(entry.path()); + } } + base::clear(); } void FCHostPersistentStorage::load() { - constexpr size_t bufsize = 1024 * 1024 * 1024; // 1gb should be enough - char* readbuf = new char[bufsize]; - WIN32_FIND_DATAW w32fd; - HANDLE hFind = FindFirstFile((path + L"\\*").c_str(), &w32fd); - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - if (w32fd.dwFileAttributes & ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) - { - DWORD bytesread = 0; - HANDLE fh = CreateFile(get_filename(w32fd.cFileName).c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fh) - { - BOOL success = ReadFile(fh, readbuf, static_cast<DWORD>(bufsize - 1), &bytesread, NULL); - if (success) - { - readbuf[bytesread] = L'\0'; - readbuf[bytesread+1] = L'\0'; // null terminate - CefString val = reinterpret_cast<wchar_t*>(readbuf); - storage.emplace(w32fd.cFileName, CefV8Value::CreateString(val)); - } - CloseHandle(fh); - } + 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 wchar_t + +#if defined(OS_WIN) + fh.reset(_wfopen(entry.path().c_str(), L"r")); +#else + fh.reset(std::fopen(entry.path().c_str(), "r")); +#endif + if (std::fread(&readbuf[0], entrySize, 1, fh.get())) { + readbuf[entrySize + 1] = readbuf[entrySize] = 0; // null terminate + CefString val = static_cast<const wchar_t*>(static_cast<const void*>(readbuf.data())); + storage.emplace(entry.path().filename().native(), CefV8Value::CreateString(val)); } - } while (FindNextFile(hFind, &w32fd)); - FindClose(hFind); + } } - delete[] readbuf; } -std::wstring FCHostPersistentStorage::get_filename(const CefString& key) const +fs::path FCHostPersistentStorage::get_filename(const CefString& key) const { - return path + L"\\" + key.c_str(); +#if defined (OS_WIN) + return path / key.ToWString(); +#else + return path / key.ToString(); +#endif } void FCHostPersistentStorage::ensure_folder_exists() const { // ignore returned errors - CreateDirectory(path.c_str(), NULL); + fs::create_directories(path); } -#else -#error "Platform-specific persistent storage implementation required." -#endif diff --git a/FCHost/fchost/fchost_storage.h b/FCHost/fchost/fchost_storage.h index bb7536dc83096f9e081bfc7c92730e98bb3f93a5..db036577572d4428add530e5e02b7426057c99ca 100644 --- a/FCHost/fchost/fchost_storage.h +++ b/FCHost/fchost/fchost_storage.h @@ -1,6 +1,8 @@ #pragma once #include "include/cef_app.h" // probably excessive + +#include <filesystem> #include <map> #include <string> @@ -30,8 +32,9 @@ protected: // memory-backed, disk-persistent local storage class FCHostPersistentStorage : public FCHostSessionStorage { + using base = FCHostSessionStorage; public: - FCHostPersistentStorage(const std::wstring& _path) : path(_path) { ensure_folder_exists(); load(); }; + FCHostPersistentStorage(const CefString& _path); virtual void set(const CefString& key, CefRefPtr<CefV8Value> val) override; virtual bool remove(const CefString& key) override; @@ -40,7 +43,7 @@ public: private: void load(); void ensure_folder_exists() const; - std::wstring get_filename(const CefString& key) const; + std::filesystem::path get_filename(const CefString& key) const; - std::wstring path; + std::filesystem::path path; };