aboutsummaryrefslogtreecommitdiff
path: root/libs/libbsarch/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /libs/libbsarch/src
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem, Proton/umu-run integration, and Flatpak packaging. Key features: - FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak) - Proton/GE-Proton/umu-run launcher with env var forwarding - Flatpak support (sandbox-aware VFS, NXM handler, umu-run) - Wine prefix management UI - Case-insensitive path resolution for Linux filesystems - QSettings-safe INI handling (avoids Bethesda INI corruption) - Portable instance support with auto-generated launcher scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/libbsarch/src')
-rw-r--r--libs/libbsarch/src/bs_archive.cpp185
-rw-r--r--libs/libbsarch/src/bs_archive_auto.cpp87
-rw-r--r--libs/libbsarch/src/bs_archive_entries.cpp52
-rw-r--r--libs/libbsarch/src/libbsarch.cpp206
-rw-r--r--libs/libbsarch/src/libbsarch.def38
-rw-r--r--libs/libbsarch/src/utils/convertible_ostream.cpp58
-rw-r--r--libs/libbsarch/src/utils/convertible_string.cpp108
-rw-r--r--libs/libbsarch/src/utils/string_convert.cpp39
8 files changed, 773 insertions, 0 deletions
diff --git a/libs/libbsarch/src/bs_archive.cpp b/libs/libbsarch/src/bs_archive.cpp
new file mode 100644
index 0000000..b50ef5b
--- /dev/null
+++ b/libs/libbsarch/src/bs_archive.cpp
@@ -0,0 +1,185 @@
+#include "bs_archive.h"
+
+namespace libbsarch {
+
+bs_archive::bs_archive()
+ : archive_(bsa_create())
+{}
+
+bs_archive::bs_archive(const bsa_archive_type_t &type)
+ : archive_(bsa_create())
+ , type_(type)
+{}
+
+bs_archive::~bs_archive()
+{
+ bsa_close(archive_);
+ bsa_free(archive_);
+}
+
+/* Properties */
+convertible_string bs_archive::get_filename()
+{
+ wchar_t name[max_string_buffer_size];
+ bsa_filename_get(archive_, max_string_buffer_size, name);
+ return name;
+}
+
+bsa_archive_type_t bs_archive::get_type()
+{
+ return bsa_archive_type_get(archive_);
+}
+
+uint32_t bs_archive::get_version()
+{
+ return bsa_version_get(archive_);
+}
+
+convertible_string bs_archive::get_format_name()
+{
+ wchar_t format_name[max_string_buffer_size];
+ bsa_format_name_get(archive_, max_string_buffer_size, format_name);
+ return format_name;
+}
+
+uint32_t bs_archive::get_file_count()
+{
+ return bsa_file_count_get(archive_);
+}
+
+uint32_t bs_archive::get_archive_flags()
+{
+ return bsa_archive_flags_get(archive_);
+}
+
+void bs_archive::set_archive_flags(uint32_t flags)
+{
+ bsa_archive_flags_set(archive_, flags);
+}
+
+uint32_t bs_archive::get_file_flags()
+{
+ return bsa_file_flags_get(archive_);
+}
+
+void bs_archive::set_file_flags(uint32_t flags)
+{
+ bsa_file_flags_set(archive_, flags);
+}
+
+void bs_archive::set_compressed(bool value)
+{
+ bsa_compress_set(archive_, value);
+}
+
+void bs_archive::set_share_data(bool value)
+{
+ bsa_share_data_set(archive_, value);
+}
+
+bool bs_archive::get_compressed()
+{
+ return bsa_compress_get(archive_);
+}
+
+bool bs_archive::get_share_data()
+{
+ return bsa_share_data_get(archive_);
+}
+
+void bs_archive::load_from_disk(const convertible_string &archive_path)
+{
+ debug_log() << "Opening archive: " << archive_path;
+ const std::wstring str = archive_path;
+ const auto &result = bsa_load_from_file(archive_, str.c_str());
+ libbsarch::checkResult(result);
+}
+
+void bs_archive::create(const convertible_string &archiveName, const bs_archive_entries &entries)
+{
+ create(archiveName, entries, type_);
+}
+
+void bs_archive::create(const convertible_string &archiveName,
+ const bs_archive_entries &entries,
+ const bsa_archive_type_t type)
+{
+ debug_log() << "Creating archive. Archive name: " << archiveName << '\n'
+ << "type: " << type_ << '\n'
+ << "entries: " << entries.getEntries();
+ bsa_create_archive(archive_, archiveName, type, entries.getEntries());
+}
+
+void bs_archive::save() const
+{
+ debug_log() << "Saving archive: " << archive_;
+
+ const auto &result = bsa_save(archive_);
+
+ libbsarch::checkResult(result);
+}
+
+void bs_archive::add_file_from_disk(const disk_blob &blob)
+{
+ debug_log() << "Adding file from disk root. Path in archive: " << blob.path_in_archive << '\n'
+ << "Filepath: " << blob.path_on_disk << '\n'
+ << "Archive: " << archive_;
+
+ const auto &result = bsa_add_file_from_disk(archive_, blob.path_in_archive, blob.path_on_disk);
+
+ libbsarch::checkResult(result);
+}
+
+void bs_archive::add_file_from_memory(const convertible_string &path_in_archive,
+ const memory_blob &memory_data) //NOTE UNTESTED
+{
+ debug_log() << "Adding file from memory. Filename: " << path_in_archive << '\n'
+ << "Data size: " << memory_data.size;
+ const auto &result = bsa_add_file_from_memory(archive_, path_in_archive, memory_data.size, memory_data.data);
+ libbsarch::checkResult(result);
+}
+
+bsa_file_record_t bs_archive::find_file_record(const convertible_string &filename)
+{
+ debug_log() << "Finding file record of: " << filename;
+ const auto &result = bsa_find_file_record(archive_, filename);
+ return result;
+}
+
+memory_blob bs_archive::extract_to_memory(bsa_file_record_t record)
+{
+ const auto &result = bsa_extract_file_data_by_record(archive_, record);
+ libbsarch::checkResult(result);
+ return memory_blob(result.buffer, archive_);
+}
+
+memory_blob bs_archive::extract_to_memory(const convertible_string &filename)
+{
+ const auto &result = bsa_extract_file_data_by_filename(archive_, filename);
+ libbsarch::checkResult(result);
+ return memory_blob(result.buffer, archive_);
+}
+
+void bs_archive::extract_to_disk(const convertible_string &filename, const convertible_string &save_as) const
+{
+ debug_log() << "Extracting: " << filename << " saved as " << save_as;
+ const auto &result = bsa_extract_file(archive_, filename, save_as);
+
+ libbsarch::checkResult(result);
+}
+
+std::vector<convertible_string> bs_archive::list_files() const
+{
+ bsa_entry_list_t list = bsa_entry_list_create();
+ const auto &result = bsa_get_resource_list(archive_, list, L"");
+
+ libbsarch::checkResult(result);
+
+ return bs_archive_entries(list).list();
+}
+
+bsa_archive_t bs_archive::get_archive() const
+{
+ return archive_;
+}
+} // namespace libbsarch
diff --git a/libs/libbsarch/src/bs_archive_auto.cpp b/libs/libbsarch/src/bs_archive_auto.cpp
new file mode 100644
index 0000000..1ad8e3e
--- /dev/null
+++ b/libs/libbsarch/src/bs_archive_auto.cpp
@@ -0,0 +1,87 @@
+/* Copyright (C) 2019 G'k
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "bs_archive_auto.hpp"
+#include <filesystem>
+namespace libbsarch {
+bs_archive_auto::bs_archive_auto(const bsa_archive_type_t &type)
+ : bs_archive(type)
+{}
+
+void bs_archive_auto::add_file_from_disk(const disk_blob &blob)
+{
+ files_.emplace_back(blob);
+}
+
+void bs_archive_auto::add_file_from_disk(const std::vector<disk_blob> &blobs)
+{
+ files_.reserve(blobs.size());
+ files_.insert(files_.end(), blobs.cbegin(), blobs.cend());
+}
+
+void bs_archive_auto::add_file_from_memory(const convertible_string &path_in_archive, const memory_blob &memory_data)
+{
+ files_.emplace_back(bs_packed_file(path_in_archive, memory_data));
+}
+
+void bs_archive_auto::add_file(const bs_packed_file &file)
+{
+ files_.emplace_back(file);
+}
+
+void bs_archive_auto::extract_all(const convertible_string &directory, bool overwrite_current_files)
+{
+ namespace fs = std::filesystem;
+ for (const auto &file : list_files())
+ {
+ convertible_string path_string = std::string(directory) + "/" + std::string(file);
+ fs::path current_file(path_string);
+ fs::path current_path = fs::path(current_file).remove_filename();
+
+ fs::create_directories(current_path);
+ if (fs::exists(current_file) && overwrite_current_files)
+ {
+ fs::remove(current_file);
+ extract_to_disk(file, current_file.string());
+ }
+ else if (!fs::exists(current_file))
+ extract_to_disk(file, current_file.string());
+ }
+}
+
+void bs_archive_auto::load_from_disk(const convertible_string &archive_path)
+{
+ bs_archive::load_from_disk(archive_path);
+ for (const auto &file : list_files())
+ entries_.add(file);
+}
+
+void bs_archive_auto::save_to_disk(const convertible_string &archive_path)
+{
+ for (const auto &file : files_)
+ entries_.add(file.path_in_archive);
+
+ bs_archive::create(archive_path, entries_);
+
+ for (const auto &file : files_)
+ {
+ switch (file.data.index())
+ {
+ case 0: {
+ const auto &blob = std::get<memory_blob>(file.data);
+ bs_archive::add_file_from_memory(file.path_in_archive, blob);
+ break;
+ }
+ case 1: {
+ const auto &file_path = std::get<convertible_string>(file.data);
+ const auto &blob = disk_blob(file.path_in_archive, file_path, bool());
+ bs_archive::add_file_from_disk(blob);
+ }
+ }
+ }
+ bs_archive::save();
+}
+
+} // namespace libbsarch
diff --git a/libs/libbsarch/src/bs_archive_entries.cpp b/libs/libbsarch/src/bs_archive_entries.cpp
new file mode 100644
index 0000000..4dc9c6d
--- /dev/null
+++ b/libs/libbsarch/src/bs_archive_entries.cpp
@@ -0,0 +1,52 @@
+#include "bs_archive_entries.h"
+
+namespace libbsarch {
+bs_archive_entries::bs_archive_entries()
+ : _entries(bsa_entry_list_create())
+{}
+
+bs_archive_entries::bs_archive_entries(const std::vector<convertible_string> &entries)
+ : _entries(bsa_entry_list_create())
+{
+ for (const auto &entry : entries)
+ add(entry);
+}
+
+bs_archive_entries::bs_archive_entries(const bsa_entry_list_t &entries)
+ : _entries(entries)
+{}
+
+bs_archive_entries::~bs_archive_entries()
+{
+ bsa_entry_list_free(_entries);
+}
+
+void bs_archive_entries::add(const convertible_string &filepath)
+{
+ debug_log() << "Adding to entries: " << filepath;
+ const auto &result = bsa_entry_list_add(_entries, filepath);
+ libbsarch::checkResult(result);
+}
+
+uint32_t bs_archive_entries::count()
+{
+ return bsa_entry_list_count(_entries);
+}
+
+std::vector<convertible_string> bs_archive_entries::list()
+{
+ std::vector<convertible_string> list;
+ for (uint32_t i = 0; i < count(); ++i)
+ {
+ wchar_t buffer[max_string_buffer_size];
+ bsa_entry_list_get(_entries, i, max_string_buffer_size, buffer);
+ list.push_back(buffer);
+ }
+ return list;
+}
+
+bsa_entry_list_t bs_archive_entries::getEntries() const
+{
+ return _entries;
+}
+} // namespace libbsarch
diff --git a/libs/libbsarch/src/libbsarch.cpp b/libs/libbsarch/src/libbsarch.cpp
new file mode 100644
index 0000000..52ded9f
--- /dev/null
+++ b/libs/libbsarch/src/libbsarch.cpp
@@ -0,0 +1,206 @@
+#ifdef BSARCH_DLL_EXPORT
+#include "libbsarch.h"
+
+namespace libbsarch {
+
+BSARCH_DLL_API(bsa_entry_list_t) bsa_entry_list_create()
+{
+ return nullptr;
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_entry_list_free(bsa_entry_list_t entry_list)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(uint32_t) bsa_entry_list_count(bsa_entry_list_t entry_list)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_entry_list_add(bsa_entry_list_t entry_list, const wchar_t *entry_string)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(uint32_t)
+bsa_entry_list_get(bsa_entry_list_t entry_list, uint32_t index, uint32_t string_buffer_size, wchar_t *string_buffer)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(bsa_archive_t) bsa_create()
+{
+ return nullptr;
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_free(bsa_archive_t archive)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_load_from_file(bsa_archive_t archive, const wchar_t *file_path)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_create_archive(bsa_archive_t archive,
+ const wchar_t *file_path,
+ bsa_archive_type_t archive_type,
+ bsa_entry_list_t entry_list)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_save(bsa_archive_t archive)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_add_file_from_disk(bsa_archive_t archive, const wchar_t *file_path, const wchar_t *source_path)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_add_file_from_disk_root(bsa_archive_t archive, const wchar_t *root_dir, const wchar_t *source_path)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_add_file_from_memory(bsa_archive_t archive, const wchar_t *file_path, uint32_t size, bsa_buffer_t data)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_file_record_t) bsa_find_file_record(bsa_archive_t archive, const wchar_t *file_path)
+{
+ return nullptr;
+}
+
+BSARCH_DLL_API(bsa_result_message_buffer_t)
+bsa_extract_file_data_by_record(bsa_archive_t archive, bsa_file_record_t file_record)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_buffer_t)
+bsa_extract_file_data_by_filename(bsa_archive_t archive, const wchar_t *file_path)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_file_data_free(bsa_archive_t archive, bsa_result_buffer_t file_data_result)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_extract_file(bsa_archive_t archive, const wchar_t *file_path, const wchar_t *save_as)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_iterate_files(bsa_archive_t archive, bsa_file_iteration_proc_t file_iteration_proc, void *context)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bool) bsa_file_exists(bsa_archive_t archive, const wchar_t *file_path)
+{
+ return false;
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_get_resource_list(bsa_archive_t archive, bsa_entry_list_t entry_result_list, const wchar_t *folder)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t)
+bsa_resolve_hash(bsa_archive_t archive, uint64_t hash, bsa_entry_list_t entry_result_list)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(bsa_result_message_t) bsa_close(bsa_archive_t archive)
+{
+ return {0};
+}
+
+BSARCH_DLL_API(uint32_t) bsa_filename_get(bsa_archive_t archive, uint32_t string_buffer_size, wchar_t *string_buffer)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(bsa_archive_type_t) bsa_archive_type_get(bsa_archive_t archive)
+{
+ return bsa_archive_type_t::baNone;
+}
+
+BSARCH_DLL_API(uint32_t) bsa_version_get(bsa_archive_t archive)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(uint32_t) bsa_format_name_get(bsa_archive_t archive, uint32_t string_buffer_size, wchar_t *string_buffer)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(uint32_t) bsa_file_count_get(bsa_archive_t archive)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(uint32_t) bsa_archive_flags_get(bsa_archive_t archive)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(void) bsa_archive_flags_set(bsa_archive_t archive, uint32_t flags)
+{
+ return;
+}
+
+BSARCH_DLL_API(uint32_t) bsa_file_flags_get(bsa_archive_t archive)
+{
+ return 0;
+}
+
+BSARCH_DLL_API(void) bsa_file_flags_set(bsa_archive_t archive, uint32_t flags)
+{
+ return;
+}
+
+BSARCH_DLL_API(bool) bsa_compress_get(bsa_archive_t archive)
+{
+ return false;
+}
+
+BSARCH_DLL_API(void) bsa_compress_set(bsa_archive_t archive, bool flags)
+{
+ return;
+}
+
+BSARCH_DLL_API(bool) bsa_share_data_get(bsa_archive_t archive)
+{
+ return false;
+}
+
+BSARCH_DLL_API(void) bsa_share_data_set(bsa_archive_t archive, bool flags)
+{
+ return;
+}
+
+BSARCH_DLL_API(void)
+bsa_file_dds_info_callback_set(bsa_archive_t archive, bsa_file_dds_info_proc_t file_dds_info_proc, void *context)
+{
+ return;
+}
+} // namespace libbsarch
+#endif
diff --git a/libs/libbsarch/src/libbsarch.def b/libs/libbsarch/src/libbsarch.def
new file mode 100644
index 0000000..d6386b0
--- /dev/null
+++ b/libs/libbsarch/src/libbsarch.def
@@ -0,0 +1,38 @@
+;DESCRIPTION "BSArch DLL"
+EXPORTS
+ bsa_entry_list_create
+ bsa_entry_list_free
+ bsa_entry_list_count
+ bsa_entry_list_add
+ bsa_entry_list_get
+ bsa_create
+ bsa_free
+ bsa_load_from_file
+ bsa_create_archive
+ bsa_save
+ bsa_add_file_from_disk
+ bsa_add_file_from_disk_root
+ bsa_add_file_from_memory
+ bsa_find_file_record
+ bsa_extract_file_data_by_record
+ bsa_extract_file_data_by_filename
+ bsa_file_data_free
+ bsa_extract_file
+ bsa_iterate_files
+ bsa_file_exists
+ bsa_get_resource_list
+ bsa_close
+ bsa_filename_get
+ bsa_archive_type_get
+ bsa_version_get
+ bsa_format_name_get
+ bsa_file_count_get
+ bsa_archive_flags_get
+ bsa_archive_flags_set
+ bsa_file_flags_get
+ bsa_file_flags_set
+ bsa_compress_get
+ bsa_compress_set
+ bsa_share_data_get
+ bsa_share_data_set
+ bsa_file_dds_info_callback_set
diff --git a/libs/libbsarch/src/utils/convertible_ostream.cpp b/libs/libbsarch/src/utils/convertible_ostream.cpp
new file mode 100644
index 0000000..d5ac238
--- /dev/null
+++ b/libs/libbsarch/src/utils/convertible_ostream.cpp
@@ -0,0 +1,58 @@
+/* Copyright (C) 2019 G'k
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+#include "convertible_ostream.hpp"
+
+namespace libbsarch {
+convertible_ostream &convertible_ostream::operator<<(const std::string &rh)
+{
+ std::cout << rh;
+ return *this;
+}
+convertible_ostream &convertible_ostream::operator<<(const char *rh)
+{
+ std::cout << rh;
+ return *this;
+}
+convertible_ostream &convertible_ostream::operator<<(const char rh)
+{
+ std::cout << rh;
+ return *this;
+}
+convertible_ostream &convertible_ostream::operator<<(const std::wstring &rh)
+{
+ std::wcout << rh;
+ return *this;
+}
+convertible_ostream &convertible_ostream::operator<<(const void *rh)
+{
+ std::cout << rh;
+ return *this;
+}
+convertible_ostream &convertible_ostream::operator<<(const convertible_string &rh)
+{
+ return *this << std::string(rh);
+}
+
+convertible_ostream::operator std::ostream &()
+{
+ return std::cout;
+}
+convertible_ostream::operator std::wostream &()
+{
+ return std::wcout;
+}
+
+#ifdef LIBBSARCH_QT_SUPPORT
+convertible_ostream::operator QDebug()
+{
+ return qDebug();
+}
+convertible_ostream &convertible_ostream::operator<<(const QString &rh)
+{
+ qDebug() << rh;
+ return *this;
+}
+#endif
+} // namespace libbsarch
diff --git a/libs/libbsarch/src/utils/convertible_string.cpp b/libs/libbsarch/src/utils/convertible_string.cpp
new file mode 100644
index 0000000..c965202
--- /dev/null
+++ b/libs/libbsarch/src/utils/convertible_string.cpp
@@ -0,0 +1,108 @@
+/* Copyright (C) 2019 G'k
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "convertible_string.hpp"
+#include <cwchar>
+#include <filesystem>
+
+namespace libbsarch {
+/* conversion ctors */
+convertible_string::convertible_string(std::string value, bool to_native_path)
+ : str_(std::move(value))
+ , auto_convert_to_native_path(to_native_path)
+{
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+}
+
+convertible_string::convertible_string(const char *const val_array, bool to_native_path)
+ : str_(std::string(val_array))
+ , auto_convert_to_native_path(to_native_path)
+
+{
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+}
+convertible_string::convertible_string(const std::wstring &wvalue, bool to_native_path)
+ : str_(to_string(wvalue))
+ , auto_convert_to_native_path(to_native_path)
+
+{
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+}
+convertible_string::convertible_string(wchar_t const *wval_array, bool to_native_path)
+ : str_(to_string(std::wstring(wval_array)))
+ , auto_convert_to_native_path(to_native_path)
+{
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+}
+
+/* assignment operators */
+convertible_string &convertible_string::operator=(const std::string &value)
+{
+ str_ = value;
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+ return *this;
+}
+convertible_string &convertible_string::operator=(const std::wstring &wvalue)
+{
+ str_ = to_string(wvalue);
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+ return *this;
+}
+
+convertible_string &convertible_string::operator=(const wchar_t *wvalue)
+{
+ str_ = to_string(std::wstring(wvalue));
+ if (auto_convert_to_native_path)
+ this->to_native_path();
+ return *this;
+}
+
+/* implicit conversion operators */
+convertible_string::operator std::string() const
+{
+ return str_;
+}
+convertible_string::operator std::wstring() const
+{
+ return to_wstring(str_);
+}
+convertible_string::operator const wchar_t *() const
+{
+ const std::wstring wstr = to_wstring(str_);
+ wchar_t *wchar_array = new wchar_t[wstr.length() + 1];
+#ifdef _WIN32
+ wcscpy_s(wchar_array, wstr.length() + 1, wstr.c_str());
+#else
+ std::wmemcpy(wchar_array, wstr.c_str(), wstr.length() + 1);
+#endif
+ return wchar_array;
+}
+
+/* Util */
+bool convertible_string::remove_substring(const convertible_string &sub_str)
+{
+ size_t pos = str_.find(sub_str);
+ if (pos != std::string::npos)
+ {
+ str_.erase(pos, std::string(sub_str).length());
+ return true;
+ }
+ return false;
+}
+
+convertible_string &convertible_string::to_native_path()
+{
+ std::filesystem::path path(str_);
+ str_ = path.make_preferred().string();
+ return *this;
+}
+
+} // namespace libbsarch
diff --git a/libs/libbsarch/src/utils/string_convert.cpp b/libs/libbsarch/src/utils/string_convert.cpp
new file mode 100644
index 0000000..1e8ce9e
--- /dev/null
+++ b/libs/libbsarch/src/utils/string_convert.cpp
@@ -0,0 +1,39 @@
+/* Copyright (C) 2019 G'k
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+#include "string_convert.hpp"
+
+#include <cstdlib>
+#include <cstring>
+
+namespace libbsarch {
+
+std::string to_string(const std::wstring &str)
+{
+ if (str.empty()) return {};
+ std::mbstate_t state{};
+ const wchar_t* src = str.data();
+ std::size_t len = std::wcsrtombs(nullptr, &src, 0, &state);
+ if (len == static_cast<std::size_t>(-1)) return {};
+ std::string result(len, '\0');
+ src = str.data();
+ state = std::mbstate_t{};
+ std::wcsrtombs(result.data(), &src, len + 1, &state);
+ return result;
+}
+
+std::wstring to_wstring(const std::string &str)
+{
+ if (str.empty()) return {};
+ std::mbstate_t state{};
+ const char* src = str.data();
+ std::size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
+ if (len == static_cast<std::size_t>(-1)) return {};
+ std::wstring result(len, L'\0');
+ src = str.data();
+ state = std::mbstate_t{};
+ std::mbsrtowcs(result.data(), &src, len + 1, &state);
+ return result;
+}
+} // namespace libbsarch