mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Use C++20 in the C++ wrapper
This commit mostly just fixes compilation errors when compiling in C++20 mode, though the change to std::filesystem::path::u8string()'s return type means it's not backwards-compatible with C++17.
This commit is contained in:
@@ -45,6 +45,12 @@ std::string convert(const ::rust::String& string) {
|
||||
return std::string(string);
|
||||
}
|
||||
|
||||
std::filesystem::path to_path(const ::rust::String& string) {
|
||||
std::u8string_view view(reinterpret_cast<const char8_t*>(string.data()),
|
||||
string.length());
|
||||
return std::filesystem::path(view);
|
||||
}
|
||||
|
||||
// Although there's an explicit conversion operator declared, it seems that
|
||||
// building the CXX wrapper with MSVC doesn't set __cplusplus correctly as using
|
||||
// the operator causes a linker error, so this just reimpls it as a function.
|
||||
@@ -148,6 +154,10 @@ loot::Vertex convert(const loot::rust::Vertex& vertex) {
|
||||
return ::rust::Str(view.data(), view.length());
|
||||
}
|
||||
|
||||
::rust::String convert(const std::filesystem::path& path) {
|
||||
return ::rust::String(reinterpret_cast<const char*>(path.u8string().data()));
|
||||
}
|
||||
|
||||
::rust::Box<loot::rust::Group> convert(const loot::Group& group) {
|
||||
return loot::rust::new_group(
|
||||
group.GetName(), group.GetDescription(), convert(group.GetAfterGroups()));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef LOOT_API_CONVERT
|
||||
#define LOOT_API_CONVERT
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "libloot-cpp/src/lib.rs.h"
|
||||
#include "loot/metadata/group.h"
|
||||
#include "loot/metadata/plugin_metadata.h"
|
||||
@@ -12,6 +14,8 @@ namespace loot {
|
||||
|
||||
std::string convert(const ::rust::String& string);
|
||||
|
||||
std::filesystem::path to_path(const ::rust::String& string);
|
||||
|
||||
std::string_view convert(::rust::Str string);
|
||||
|
||||
loot::Group convert(const loot::rust::Group& group);
|
||||
@@ -41,6 +45,8 @@ loot::Vertex convert(const loot::rust::Vertex& vertex);
|
||||
|
||||
::rust::Str convert(std::string_view view);
|
||||
|
||||
::rust::String convert(const std::filesystem::path& path);
|
||||
|
||||
::rust::Box<loot::rust::Group> convert(const loot::Group& group);
|
||||
|
||||
::rust::Box<loot::rust::File> convert(const loot::File& file);
|
||||
|
||||
@@ -10,7 +10,7 @@ Database::Database(::rust::Box<loot::rust::Database>&& database) :
|
||||
|
||||
void Database::LoadMasterlist(const std::filesystem::path& masterlistPath) {
|
||||
try {
|
||||
database_->load_masterlist(masterlistPath.u8string());
|
||||
database_->load_masterlist(convert(masterlistPath));
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
@@ -20,8 +20,8 @@ void Database::LoadMasterlistWithPrelude(
|
||||
const std::filesystem::path& masterlistPath,
|
||||
const std::filesystem::path& masterlistPreludePath) {
|
||||
try {
|
||||
database_->load_masterlist_with_prelude(masterlistPath.u8string(),
|
||||
masterlistPreludePath.u8string());
|
||||
database_->load_masterlist_with_prelude(convert(masterlistPath),
|
||||
convert(masterlistPreludePath));
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
@@ -29,7 +29,7 @@ void Database::LoadMasterlistWithPrelude(
|
||||
|
||||
void Database::LoadUserlist(const std::filesystem::path& userlistPath) {
|
||||
try {
|
||||
database_->load_userlist(userlistPath.u8string());
|
||||
database_->load_userlist(convert(userlistPath));
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
@@ -38,7 +38,7 @@ void Database::LoadUserlist(const std::filesystem::path& userlistPath) {
|
||||
void Database::WriteUserMetadata(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const {
|
||||
try {
|
||||
database_->write_user_metadata(outputFile.u8string(), overwrite);
|
||||
database_->write_user_metadata(convert(outputFile), overwrite);
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
@@ -165,7 +165,7 @@ void Database::DiscardAllUserMetadata() {
|
||||
void Database::WriteMinimalList(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const {
|
||||
try {
|
||||
database_->write_minimal_list(outputFile.u8string(), overwrite);
|
||||
database_->write_minimal_list(convert(outputFile), overwrite);
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
|
||||
@@ -77,10 +77,11 @@ rust::Box<loot::rust::Game> constructGame(
|
||||
const std::filesystem::path& localDataPath) {
|
||||
try {
|
||||
if (localDataPath.empty()) {
|
||||
return loot::rust::new_game(convert(gameType), gamePath.u8string());
|
||||
return loot::rust::new_game(convert(gameType), loot::convert(gamePath));
|
||||
} else {
|
||||
return loot::rust::new_game_with_local_path(
|
||||
convert(gameType), gamePath.u8string(), localDataPath.u8string());
|
||||
return loot::rust::new_game_with_local_path(convert(gameType),
|
||||
loot::convert(gamePath),
|
||||
loot::convert(localDataPath));
|
||||
}
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(loot::mapError(e));
|
||||
@@ -134,7 +135,7 @@ void Game::SetAdditionalDataPaths(
|
||||
std::vector<::rust::String> path_strings;
|
||||
std::vector<::rust::Str> path_strs;
|
||||
for (const auto& path : additionalDataPaths) {
|
||||
path_strings.push_back(path.u8string());
|
||||
path_strings.push_back(convert(path));
|
||||
path_strs.push_back(path_strings.back());
|
||||
}
|
||||
try {
|
||||
@@ -146,7 +147,7 @@ void Game::SetAdditionalDataPaths(
|
||||
}
|
||||
|
||||
bool Game::IsValidPlugin(const std::filesystem::path& pluginPath) const {
|
||||
return game_->is_valid_plugin(pluginPath.u8string());
|
||||
return game_->is_valid_plugin(convert(pluginPath));
|
||||
}
|
||||
|
||||
void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
|
||||
@@ -154,7 +155,7 @@ void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
|
||||
std::vector<::rust::String> path_strings;
|
||||
std::vector<::rust::Str> path_strs;
|
||||
for (const auto& path : pluginPaths) {
|
||||
path_strings.push_back(path.u8string());
|
||||
path_strings.push_back(convert(path));
|
||||
path_strs.push_back(path_strings.back());
|
||||
}
|
||||
|
||||
|
||||
@@ -154,28 +154,28 @@ TEST_P(
|
||||
}
|
||||
}
|
||||
#else
|
||||
TEST_P(CreateGameHandleTest,
|
||||
shouldNotThrowOnWindowsIfLocalPathIsNotGiven) {
|
||||
TEST_P(CreateGameHandleTest, shouldNotThrowOnWindowsIfLocalPathIsNotGiven) {
|
||||
EXPECT_NO_THROW(CreateGameHandle(GetParam(), gamePath));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(CreateGameHandleTest,
|
||||
shouldNotThrowIfGameAndLocalPathsAreNotEmpty) {
|
||||
TEST_P(CreateGameHandleTest, shouldNotThrowIfGameAndLocalPathsAreNotEmpty) {
|
||||
EXPECT_NO_THROW(CreateGameHandle(GetParam(), gamePath, localPath));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
CreateGameHandleTest,
|
||||
TEST_P(CreateGameHandleTest,
|
||||
shouldSetAdditionalDataPathsForFallout4FromMicrosoftStoreOrStarfield) {
|
||||
if (GetParam() == GameType::fo4) {
|
||||
// Create the file that indicates it's a Microsoft Store install.
|
||||
touch(gamePath / "appxmanifest.xml");
|
||||
} else if (GetParam() == GameType::openmw) {
|
||||
std::ofstream out(gamePath / "openmw.cfg");
|
||||
out << "data-local=\"" << (localPath / "data").u8string() << "\""
|
||||
<< std::endl
|
||||
<< "config=\"" << localPath.u8string() << "\"";
|
||||
out << "data-local=\""
|
||||
<< reinterpret_cast<const char*>(
|
||||
(localPath / "data").u8string().c_str())
|
||||
<< "\"" << std::endl
|
||||
<< "config=\""
|
||||
<< reinterpret_cast<const char*>(localPath.u8string().c_str()) << "\"";
|
||||
}
|
||||
|
||||
const auto game = CreateGameHandle(GetParam(), gamePath, localPath);
|
||||
@@ -200,8 +200,8 @@ TEST_P(
|
||||
|
||||
const auto expectedSuffix = std::filesystem::u8path("Documents") /
|
||||
"My Games" / "Starfield" / "Data";
|
||||
EXPECT_TRUE(endsWith(game->GetAdditionalDataPaths()[0].u8string(),
|
||||
expectedSuffix.u8string()));
|
||||
EXPECT_TRUE(game->GetAdditionalDataPaths()[0].u8string().ends_with(
|
||||
expectedSuffix.u8string()));
|
||||
} else if (GetParam() == GameType::openmw) {
|
||||
EXPECT_EQ(std::vector<std::filesystem::path>{localPath / "data"},
|
||||
game->GetAdditionalDataPaths());
|
||||
|
||||
@@ -35,7 +35,7 @@ constexpr unsigned int ESP_ERROR_PLUGIN_METADATA_NOT_FOUND = 14;
|
||||
class GameInterfaceTest : public ApiGameOperationsTest {
|
||||
protected:
|
||||
GameInterfaceTest() :
|
||||
emptyFile("EmptyFile.esm"), nonAsciiEsm(u8"non\u00C1scii.esm") {
|
||||
emptyFile("EmptyFile.esm"), nonAsciiEsm(reinterpret_cast<const char*>(u8"non\u00C1scii.esm")) {
|
||||
// Make sure the plugin with a non-ASCII filename exists.
|
||||
std::filesystem::copy_file(dataPath / blankEsm,
|
||||
dataPath / std::filesystem::u8path(nonAsciiEsm));
|
||||
@@ -531,7 +531,8 @@ TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) {
|
||||
|
||||
std::vector<std::string> pluginsToSort;
|
||||
for (const auto& plugin : pluginsToLoad) {
|
||||
pluginsToSort.push_back(plugin.filename().u8string());
|
||||
const auto filename = plugin.filename().u8string();
|
||||
pluginsToSort.push_back(std::string(reinterpret_cast<const char *>(filename.data()), filename.length()));
|
||||
}
|
||||
|
||||
std::vector<std::string> actualOrder = handle_->SortPlugins(pluginsToSort);
|
||||
|
||||
@@ -33,8 +33,8 @@ class PluginInterfaceTest : public ApiGameOperationsTest {
|
||||
protected:
|
||||
PluginInterfaceTest() :
|
||||
ApiGameOperationsTest(),
|
||||
nonAsciiEsp(u8"non\u00C1scii.esp"),
|
||||
otherNonAsciiEsp(u8"other non\u00C1scii.esp"),
|
||||
nonAsciiEsp(reinterpret_cast<const char*>(u8"non\u00C1scii.esp")),
|
||||
otherNonAsciiEsp(reinterpret_cast<const char*>(u8"other non\u00C1scii.esp")),
|
||||
blankArchive("Blank" + GetArchiveFileExtension(GetParam())),
|
||||
blankSuffixArchive("Blank - Different - suffix" +
|
||||
GetArchiveFileExtension(GetParam())) {}
|
||||
@@ -90,12 +90,12 @@ protected:
|
||||
touch(dataPath / blankSuffixArchive);
|
||||
|
||||
auto nonAsciiArchivePath =
|
||||
dataPath / std::filesystem::u8path(u8"non\u00E1scii" +
|
||||
dataPath / std::filesystem::u8path(reinterpret_cast<const char*>(u8"non\u00E1scii") +
|
||||
GetArchiveFileExtension(GetParam()));
|
||||
touch(dataPath / nonAsciiArchivePath);
|
||||
|
||||
auto nonAsciiPrefixArchivePath =
|
||||
dataPath / std::filesystem::u8path(u8"other non\u00E1scii2 - suffix" +
|
||||
dataPath / std::filesystem::u8path(reinterpret_cast<const char*>(u8"other non\u00E1scii2 - suffix") +
|
||||
GetArchiveFileExtension(GetParam()));
|
||||
touch(dataPath / nonAsciiPrefixArchivePath);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#ifdef _WIN32
|
||||
TEST(Filesystem,
|
||||
pathStringConstructorDoesNotConvertCharacterEncodingFromUtf8ToNative) {
|
||||
std::string utf8 = u8"Andr\u00E9_settings.toml";
|
||||
std::string utf8 = reinterpret_cast<const char*>(u8"Andr\u00E9_settings.toml");
|
||||
std::u16string utf16 = u"Andr\u00E9_settings.toml";
|
||||
|
||||
ASSERT_EQ('\xc3', utf8[4]);
|
||||
@@ -38,14 +38,14 @@ TEST(Filesystem,
|
||||
std::filesystem::path path(utf8);
|
||||
|
||||
EXPECT_EQ(utf8, path.string());
|
||||
EXPECT_NE(utf8, path.u8string());
|
||||
EXPECT_NE(utf8, reinterpret_cast<const char*>(path.u8string().c_str()));
|
||||
EXPECT_NE(utf16, path.u16string());
|
||||
}
|
||||
|
||||
TEST(
|
||||
Filesystem,
|
||||
pathStringAndLocaleConstructorDoesNotConvertCharacterEncodingFromUtf8WithClassicLocale) {
|
||||
std::string utf8 = u8"Andr\u00E9_settings.toml";
|
||||
std::string utf8 = reinterpret_cast<const char*>(u8"Andr\u00E9_settings.toml");
|
||||
std::u16string utf16 = u"Andr\u00E9_settings.toml";
|
||||
|
||||
ASSERT_EQ('\xc3', utf8[4]);
|
||||
@@ -55,12 +55,12 @@ TEST(
|
||||
|
||||
EXPECT_EQ(utf8, path.string());
|
||||
|
||||
EXPECT_NE(utf8, path.u8string());
|
||||
EXPECT_NE(utf8, reinterpret_cast<const char *>(path.u8string().c_str()));
|
||||
EXPECT_NE(utf16, path.u16string());
|
||||
}
|
||||
#else
|
||||
TEST(Filesystem, pathStringConstructorUsesNativeEncodingOfUtf8) {
|
||||
std::string utf8 = u8"Andr\u00E9_settings.toml";
|
||||
std::string utf8 = reinterpret_cast<const char*>(u8"Andr\u00E9_settings.toml");
|
||||
std::u16string utf16 = u"Andr\u00E9_settings.toml";
|
||||
|
||||
ASSERT_EQ('\xc3', utf8[4]);
|
||||
@@ -69,13 +69,13 @@ TEST(Filesystem, pathStringConstructorUsesNativeEncodingOfUtf8) {
|
||||
std::filesystem::path path(utf8);
|
||||
|
||||
EXPECT_EQ(utf8, path.string());
|
||||
EXPECT_EQ(utf8, path.u8string());
|
||||
EXPECT_EQ(utf8, reinterpret_cast<const char *>(path.u8string().c_str()));
|
||||
EXPECT_EQ(utf16, path.u16string());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Filesystem, u8pathConvertsCharacterEncodingFromUtf8ToNative) {
|
||||
std::string utf8 = u8"Andr\u00E9_settings.toml";
|
||||
std::string utf8 = reinterpret_cast<const char*>(u8"Andr\u00E9_settings.toml");
|
||||
std::u16string utf16 = u"Andr\u00E9_settings.toml";
|
||||
|
||||
ASSERT_EQ('\xc3', utf8[4]);
|
||||
@@ -89,7 +89,7 @@ TEST(Filesystem, u8pathConvertsCharacterEncodingFromUtf8ToNative) {
|
||||
EXPECT_EQ(utf8, path.string());
|
||||
#endif
|
||||
|
||||
EXPECT_EQ(utf8, path.u8string());
|
||||
EXPECT_EQ(utf8, reinterpret_cast<const char *>(path.u8string().c_str()));
|
||||
EXPECT_EQ(utf16, path.u16string());
|
||||
}
|
||||
|
||||
|
||||
@@ -226,14 +226,25 @@ protected:
|
||||
it != std::filesystem::directory_iterator();
|
||||
++it) {
|
||||
if (std::filesystem::is_regular_file(it->status())) {
|
||||
std::string filename = it->path().filename().u8string();
|
||||
if (filename == nonPluginFile)
|
||||
std::u8string u8Filename = it->path().filename().u8string();
|
||||
std::string filename(reinterpret_cast<const char*>(u8Filename.data()),
|
||||
u8Filename.length());
|
||||
|
||||
if (filename == nonPluginFile) {
|
||||
continue;
|
||||
if (endsWith(filename, ".ghost"))
|
||||
filename = it->path().stem().u8string();
|
||||
if (endsWith(filename, ".esp") || endsWith(filename, ".esm"))
|
||||
}
|
||||
|
||||
if (filename.ends_with(".ghost")) {
|
||||
u8Filename = it->path().stem().u8string();
|
||||
filename =
|
||||
std::string(reinterpret_cast<const char*>(u8Filename.data()),
|
||||
u8Filename.length());
|
||||
}
|
||||
|
||||
if (filename.ends_with(".esp") || filename.ends_with(".esm")) {
|
||||
loadOrder.emplace(std::filesystem::last_write_time(it->path()),
|
||||
filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& plugin : loadOrder) actual.push_back(plugin.second);
|
||||
@@ -384,17 +395,6 @@ protected:
|
||||
WriteFile(path, bytes);
|
||||
}
|
||||
|
||||
static bool endsWith(const std::string& str, const std::string& suffix) {
|
||||
if (str.length() < suffix.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto view = std::string_view(str);
|
||||
view.remove_prefix(str.length() - suffix.length());
|
||||
|
||||
return view == suffix;
|
||||
}
|
||||
|
||||
private:
|
||||
GameType gameType_;
|
||||
const std::filesystem::path rootTestPath;
|
||||
|
||||
@@ -71,10 +71,10 @@ std::filesystem::path getRootTestPath() {
|
||||
|
||||
// The non-ASCII character is there to ensure test coverage of non-ASCII path
|
||||
// handling.
|
||||
std::string directoryName = u8"libloot-t\u00E9st-";
|
||||
std::u8string directoryName = u8"libloot-t\u00E9st-";
|
||||
|
||||
for (int i = 0; i < 16; i += 1) {
|
||||
directoryName.push_back(static_cast<char>(dist(prng)));
|
||||
directoryName.push_back(static_cast<char8_t>(dist(prng)));
|
||||
}
|
||||
|
||||
return std::filesystem::absolute(std::filesystem::temp_directory_path() /
|
||||
|
||||
Reference in New Issue
Block a user