1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-02 15:41:32 +00:00

Merge branch 'master' into 'cellstore_refactor'

# Conflicts:
#   apps/openmw/mwworld/scene.cpp
This commit is contained in:
psi29a 2022-09-11 15:03:37 +00:00
commit 29f3de30c2
236 changed files with 2829 additions and 2719 deletions

View file

@ -1,5 +1,14 @@
default: default:
interruptible: true interruptible: true
# Merge requests have two pipelines, filter pipelines using branch and merge request event together.
# See https://docs.gitlab.com/ee/ci/pipelines/merge_request_pipelines.html#two-pipelines-when-pushing-to-a-branch
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
# Note: We set `needs` on each job to control the job DAG. # Note: We set `needs` on each job to control the job DAG.
# See https://docs.gitlab.com/ee/ci/yaml/#needs # See https://docs.gitlab.com/ee/ci/yaml/#needs
@ -20,8 +29,6 @@ variables:
- docker - docker
- linux - linux
image: ubuntu:22.04 image: ubuntu:22.04
rules:
- if: $CI_PIPELINE_SOURCE == "push"
.Ubuntu: .Ubuntu:
extends: .Ubuntu_Image extends: .Ubuntu_Image
@ -388,8 +395,6 @@ macOS12_Xcode13:
.Windows_Ninja_Base: .Windows_Ninja_Base:
tags: tags:
- windows - windows
rules:
- if: $CI_PIPELINE_SOURCE == "push"
before_script: before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1 - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
@ -491,8 +496,6 @@ macOS12_Xcode13:
.Windows_MSBuild_Base: .Windows_MSBuild_Base:
tags: tags:
- windows - windows
rules:
- if: $CI_PIPELINE_SOURCE == "push"
before_script: before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1 - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
@ -590,9 +593,9 @@ Windows_MSBuild_RelWithDebInfo:
rules: rules:
# run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it # run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
- if: $CI_PIPELINE_SOURCE == "push" - if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_PIPELINE_SOURCE == "schedule"
.Ubuntu_AndroidNDK_arm64-v8a: .Ubuntu_AndroidNDK_arm64-v8a:
tags: tags:
- linux - linux

View file

@ -20,6 +20,11 @@ if(POLICY CMP0092)
cmake_policy(SET CMP0092 NEW) cmake_policy(SET CMP0092 NEW)
endif() endif()
# set the timestamps of extracted contents to the time of extraction
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
project(OpenMW) project(OpenMW)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)

View file

@ -8,6 +8,9 @@
#include <components/bsa/compressedbsafile.hpp> #include <components/bsa/compressedbsafile.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/conversion.hpp>
#include <components/misc/strings/conversion.hpp>
#define BSATOOL_VERSION 1.1 #define BSATOOL_VERSION 1.1
@ -17,10 +20,10 @@ namespace bpo = boost::program_options;
struct Arguments struct Arguments
{ {
std::string mode; std::string mode;
std::string filename; std::filesystem::path filename;
std::string extractfile; std::filesystem::path extractfile;
std::string addfile; std::filesystem::path addfile;
std::string outdir; std::filesystem::path outdir;
bool longformat; bool longformat;
bool fullpath; bool fullpath;
@ -55,7 +58,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
hidden.add_options() hidden.add_options()
( "mode,m", bpo::value<std::string>(), "bsatool mode") ( "mode,m", bpo::value<std::string>(), "bsatool mode")
( "input-file,i", bpo::value< std::vector<std::string> >(), "input file") ( "input-file,i", bpo::value< Files::MaybeQuotedPathContainer >(), "input file")
; ;
bpo::positional_options_description p; bpo::positional_options_description p;
@ -112,37 +115,39 @@ bool parseOptions (int argc, char** argv, Arguments &info)
<< desc << std::endl; << desc << std::endl;
return false; return false;
} }
info.filename = variables["input-file"].as< std::vector<std::string> >()[0]; auto inputFiles = variables["input-file"].as< Files::MaybeQuotedPathContainer >();
info.filename = inputFiles[0].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
// Default output to the working directory // Default output to the working directory
info.outdir = "."; info.outdir = std::filesystem::current_path();
if (info.mode == "extract") if (info.mode == "extract")
{ {
if (variables["input-file"].as< std::vector<std::string> >().size() < 2) if (inputFiles.size() < 2)
{ {
std::cout << "\nERROR: file to extract unspecified\n\n" std::cout << "\nERROR: file to extract unspecified\n\n"
<< desc << std::endl; << desc << std::endl;
return false; return false;
} }
if (variables["input-file"].as< std::vector<std::string> >().size() > 1) if (inputFiles.size() > 1)
info.extractfile = variables["input-file"].as< std::vector<std::string> >()[1]; info.extractfile = inputFiles[1].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
if (variables["input-file"].as< std::vector<std::string> >().size() > 2) if (inputFiles.size() > 2)
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2]; info.outdir = inputFiles[2].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
} }
else if (info.mode == "add") else if (info.mode == "add")
{ {
if (variables["input-file"].as< std::vector<std::string> >().size() < 1) if (inputFiles.empty())
{ {
std::cout << "\nERROR: file to add unspecified\n\n" std::cout << "\nERROR: file to add unspecified\n\n"
<< desc << std::endl; << desc << std::endl;
return false; return false;
} }
if (variables["input-file"].as< std::vector<std::string> >().size() > 1) if (inputFiles.size() > 1)
info.addfile = variables["input-file"].as< std::vector<std::string> >()[1]; info.addfile = inputFiles[1].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
} }
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1) else if (inputFiles.size() > 1)
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1]; info.outdir = inputFiles[1].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
info.longformat = variables.count("long") != 0; info.longformat = variables.count("long") != 0;
info.fullpath = variables.count("full-path") != 0; info.fullpath = variables.count("full-path") != 0;
@ -176,17 +181,17 @@ int list(std::unique_ptr<File>& bsa, Arguments& info)
template<typename File> template<typename File>
int extract(std::unique_ptr<File>& bsa, Arguments& info) int extract(std::unique_ptr<File>& bsa, Arguments& info)
{ {
std::string archivePath = info.extractfile; auto archivePath = info.extractfile.u8string();
Misc::StringUtils::replaceAll(archivePath, "/", "\\"); Misc::StringUtils::replaceAll(archivePath, u8"/", u8"\\");
std::string extractPath = info.extractfile; auto extractPath = info.extractfile.u8string();
Misc::StringUtils::replaceAll(extractPath, "\\", "/"); Misc::StringUtils::replaceAll(extractPath, u8"\\", u8"/");
Files::IStreamPtr stream; Files::IStreamPtr stream;
// Get a stream for the file to extract // Get a stream for the file to extract
for (auto it = bsa->getList().rbegin(); it != bsa->getList().rend(); ++it) for (auto it = bsa->getList().rbegin(); it != bsa->getList().rend(); ++it)
{ {
if (Misc::StringUtils::ciEqual(std::string(it->name()), archivePath)) if (Misc::StringUtils::ciEqual(Misc::StringUtils::stringToU8String(it->name()), archivePath))
{ {
stream = bsa->getFile(&*it); stream = bsa->getFile(&*it);
break; break;
@ -194,20 +199,19 @@ int extract(std::unique_ptr<File>& bsa, Arguments& info)
} }
if (!stream) if (!stream)
{ {
std::cout << "ERROR: file '" << archivePath << "' not found\n"; std::cout << "ERROR: file '" << Misc::StringUtils::u8StringToString(archivePath) << "' not found\n";
std::cout << "In archive: " << info.filename << std::endl; std::cout << "In archive: " << Files::pathToUnicodeString(info.filename) << std::endl;
return 3; return 3;
} }
// Get the target path (the path the file will be extracted to) // Get the target path (the path the file will be extracted to)
std::filesystem::path relPath (extractPath); std::filesystem::path relPath (extractPath);
std::filesystem::path outdir (info.outdir);
std::filesystem::path target; std::filesystem::path target;
if (info.fullpath) if (info.fullpath)
target = outdir / relPath; target = info.outdir / relPath;
else else
target = outdir / relPath.filename(); target = info.outdir / relPath.filename();
// Create the directory hierarchy // Create the directory hierarchy
std::filesystem::create_directories(target.parent_path()); std::filesystem::create_directories(target.parent_path());
@ -215,14 +219,14 @@ int extract(std::unique_ptr<File>& bsa, Arguments& info)
std::filesystem::file_status s = std::filesystem::status(target.parent_path()); std::filesystem::file_status s = std::filesystem::status(target.parent_path());
if (!std::filesystem::is_directory(s)) if (!std::filesystem::is_directory(s))
{ {
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl; std::cout << "ERROR: " << Files::pathToUnicodeString(target.parent_path()) << " is not a directory." << std::endl;
return 3; return 3;
} }
std::ofstream out(target, std::ios::binary); std::ofstream out(target, std::ios::binary);
// Write the file to disk // Write the file to disk
std::cout << "Extracting " << info.extractfile << " to " << target << std::endl; std::cout << "Extracting " << Files::pathToUnicodeString(info.extractfile) << " to " << Files::pathToUnicodeString(target) << std::endl;
out << stream->rdbuf(); out << stream->rdbuf();
out.close(); out.close();
@ -239,8 +243,8 @@ int extractAll(std::unique_ptr<File>& bsa, Arguments& info)
Misc::StringUtils::replaceAll(extractPath, "\\", "/"); Misc::StringUtils::replaceAll(extractPath, "\\", "/");
// Get the target path (the path the file will be extracted to) // Get the target path (the path the file will be extracted to)
std::filesystem::path target (info.outdir); auto target = info.outdir;
target /= extractPath; target /= Misc::StringUtils::stringToU8String(extractPath);
// Create the directory hierarchy // Create the directory hierarchy
std::filesystem::create_directories(target.parent_path()); std::filesystem::create_directories(target.parent_path());
@ -257,7 +261,7 @@ int extractAll(std::unique_ptr<File>& bsa, Arguments& info)
std::ofstream out(target, std::ios::binary); std::ofstream out(target, std::ios::binary);
// Write the file to disk // Write the file to disk
std::cout << "Extracting " << target << std::endl; std::cout << "Extracting " << Files::pathToUnicodeString(target) << std::endl;
out << data->rdbuf(); out << data->rdbuf();
out.close(); out.close();
} }
@ -269,7 +273,7 @@ template<typename File>
int add(std::unique_ptr<File>& bsa, Arguments& info) int add(std::unique_ptr<File>& bsa, Arguments& info)
{ {
std::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in); std::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in);
bsa->addFile(info.addfile, stream); bsa->addFile(Files::pathToUnicodeString(info.addfile), stream);
return 0; return 0;
} }

View file

@ -145,7 +145,7 @@ namespace
const auto fsStrict = variables["fs-strict"].as<bool>(); const auto fsStrict = variables["fs-strict"].as<bool>();
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>(); const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
Version::Version v = Version::getOpenmwVersion(resDir.string()); const auto v = Version::getOpenmwVersion(resDir);
Log(Debug::Info) << v.describe(); Log(Debug::Info) << v.describe();
dataDirs.insert(dataDirs.begin(), resDir / "vfs"); dataDirs.insert(dataDirs.begin(), resDir / "vfs");
const auto fileCollections = Files::Collections(dataDirs, !fsStrict); const auto fileCollections = Files::Collections(dataDirs, !fsStrict);

View file

@ -3,7 +3,7 @@
#include <vector> #include <vector>
#include <optional> #include <optional>
#include <string> #include <filesystem>
#include <components/esm/format.hpp> #include <components/esm/format.hpp>
@ -18,8 +18,8 @@ namespace EsmTool
std::string mode; std::string mode;
std::string encoding; std::string encoding;
std::string filename; std::filesystem::path filename;
std::string outname; std::filesystem::path outname;
std::vector<std::string> types; std::vector<std::string> types;
std::string name; std::string name;

View file

@ -17,6 +17,8 @@
#include <components/esm/format.hpp> #include <components/esm/format.hpp>
#include <components/files/openfile.hpp> #include <components/files/openfile.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/conversion.hpp>
#include "record.hpp" #include "record.hpp"
#include "labels.hpp" #include "labels.hpp"
@ -83,7 +85,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
hidden.add_options() hidden.add_options()
( "mode,m", bpo::value<std::string>(), "esmtool mode") ( "mode,m", bpo::value<std::string>(), "esmtool mode")
( "input-file,i", bpo::value< std::vector<std::string> >(), "input file") ( "input-file,i", bpo::value< Files::MaybeQuotedPathContainer >(), "input file")
; ;
bpo::positional_options_description p; bpo::positional_options_description p;
@ -154,9 +156,10 @@ bool parseOptions (int argc, char** argv, Arguments &info)
return false; return false;
}*/ }*/
info.filename = variables["input-file"].as< std::vector<std::string> >()[0]; const auto inputFiles = variables["input-file"].as< Files::MaybeQuotedPathContainer >();
if (variables["input-file"].as< std::vector<std::string> >().size() > 1) info.filename = inputFiles[0].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
info.outname = variables["input-file"].as< std::vector<std::string> >()[1]; if (inputFiles.size() > 1)
info.outname = inputFiles[1].u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
if (const auto it = variables.find("raw"); it != variables.end()) if (const auto it = variables.find("raw"); it != variables.end())
info.mRawFormat = ESM::parseFormat(it->second.as<std::string>()); info.mRawFormat = ESM::parseFormat(it->second.as<std::string>());
@ -284,9 +287,9 @@ void loadCell(const Arguments& info, ESM::Cell &cell, ESM::ESMReader &esm, ESMDa
} }
} }
void printRawTes3(std::string_view path) void printRawTes3(const std::filesystem::path &path)
{ {
std::cout << "TES3 RAW file listing: " << path << '\n'; std::cout << "TES3 RAW file listing: " << Files::pathToUnicodeString(path) << '\n';
ESM::ESMReader esm; ESM::ESMReader esm;
esm.openRaw(path); esm.openRaw(path);
while(esm.hasMoreRecs()) while(esm.hasMoreRecs())
@ -419,7 +422,7 @@ int load(const Arguments& info, ESMData* data)
printRawTes3(info.filename); printRawTes3(info.filename);
break; break;
case ESM::Format::Tes4: case ESM::Format::Tes4:
std::cout << "Printing raw TES4 file is not supported: " << info.filename << "\n"; std::cout << "Printing raw TES4 file is not supported: " << Files::pathToUnicodeString(info.filename) << "\n";
break; break;
} }
return 0; return 0;
@ -490,7 +493,7 @@ int clone(const Arguments& info)
if (i % 3 != 0) if (i % 3 != 0)
std::cout << '\n'; std::cout << '\n';
std::cout << "\nSaving records to: " << info.outname << "...\n"; std::cout << "\nSaving records to: " << Files::pathToUnicodeString(info.outname) << "...\n";
ESM::ESMWriter esm; ESM::ESMWriter esm;
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
@ -499,7 +502,7 @@ int clone(const Arguments& info)
esm.setVersion(ESM::VER_13); esm.setVersion(ESM::VER_13);
esm.setRecordCount (recordCount); esm.setRecordCount (recordCount);
std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); std::fstream save(info.outname, std::fstream::out | std::fstream::binary);
esm.save(save); esm.save(save);
int saved = 0; int saved = 0;
@ -563,14 +566,14 @@ int comp(const Arguments& info)
ESMData dataOne; ESMData dataOne;
if (load(fileOne, &dataOne) != 0) if (load(fileOne, &dataOne) != 0)
{ {
std::cout << "Failed to load " << info.filename << ", aborting comparison." << std::endl; std::cout << "Failed to load " << Files::pathToUnicodeString(info.filename) << ", aborting comparison." << std::endl;
return 1; return 1;
} }
ESMData dataTwo; ESMData dataTwo;
if (load(fileTwo, &dataTwo) != 0) if (load(fileTwo, &dataTwo) != 0)
{ {
std::cout << "Failed to load " << info.outname << ", aborting comparison." << std::endl; std::cout << "Failed to load " << Files::pathToUnicodeString(info.outname) << ", aborting comparison." << std::endl;
return 1; return 1;
} }

View file

@ -9,6 +9,7 @@
#include <components/esm/esmcommon.hpp> #include <components/esm/esmcommon.hpp>
#include <components/esm4/reader.hpp> #include <components/esm4/reader.hpp>
#include <components/esm4/records.hpp> #include <components/esm4/records.hpp>
#include <components/to_utf8/to_utf8.hpp>
namespace EsmTool namespace EsmTool
{ {

View file

@ -85,7 +85,7 @@ namespace
namespace ESSImport namespace ESSImport
{ {
Importer::Importer(const std::string &essfile, const std::string &outfile, const std::string &encoding) Importer::Importer(const std::filesystem::path &essfile, const std::filesystem::path &outfile, const std::string &encoding)
: mEssFile(essfile) : mEssFile(essfile)
, mOutFile(outfile) , mOutFile(outfile)
, mEncoding(encoding) , mEncoding(encoding)
@ -112,7 +112,7 @@ namespace ESSImport
std::vector<Record> mRecords; std::vector<Record> mRecords;
}; };
void read(const std::string& filename, File& file) void read(const std::filesystem::path &filename, File& file)
{ {
ESM::ESMReader esm; ESM::ESMReader esm;
esm.open(filename); esm.open(filename);
@ -345,7 +345,7 @@ namespace ESSImport
writer.setFormat (ESM::SavedGame::sCurrentFormat); writer.setFormat (ESM::SavedGame::sCurrentFormat);
std::ofstream stream(std::filesystem::path(mOutFile), std::ios::out | std::ios::binary); std::ofstream stream(mOutFile, std::ios::out | std::ios::binary);
// all unused // all unused
writer.setVersion(0); writer.setVersion(0);
writer.setType(0); writer.setType(0);

View file

@ -1,7 +1,7 @@
#ifndef OPENMW_ESSIMPORTER_IMPORTER_H #ifndef OPENMW_ESSIMPORTER_IMPORTER_H
#define OPENMW_ESSIMPORTER_IMPORTER_H #define OPENMW_ESSIMPORTER_IMPORTER_H
#include <string> #include <filesystem>
namespace ESSImport namespace ESSImport
{ {
@ -9,15 +9,15 @@ namespace ESSImport
class Importer class Importer
{ {
public: public:
Importer(const std::string& essfile, const std::string& outfile, const std::string& encoding); Importer(const std::filesystem::path &essfile, const std::filesystem::path &outfile, const std::string& encoding);
void run(); void run();
void compare(); void compare();
private: private:
std::string mEssFile; std::filesystem::path mEssFile;
std::string mOutFile; std::filesystem::path mOutFile;
std::string mEncoding; std::string mEncoding;
}; };

View file

@ -18,8 +18,8 @@ int main(int argc, char** argv)
bpo::positional_options_description p_desc; bpo::positional_options_description p_desc;
desc.add_options() desc.add_options()
("help,h", "produce help message") ("help,h", "produce help message")
("mwsave,m", bpo::value<std::string>(), "morrowind .ess save file") ("mwsave,m", bpo::value<Files::MaybeQuotedPath>(), "morrowind .ess save file")
("output,o", bpo::value<std::string>(), "output file (.omwsave)") ("output,o", bpo::value<Files::MaybeQuotedPath>(), "output file (.omwsave)")
("compare,c", "compare two .ess files") ("compare,c", "compare two .ess files")
("encoding", boost::program_options::value<std::string>()->default_value("win1252"), "encoding of the save file") ("encoding", boost::program_options::value<std::string>()->default_value("win1252"), "encoding of the save file")
; ;
@ -45,8 +45,8 @@ int main(int argc, char** argv)
Files::ConfigurationManager cfgManager(true); Files::ConfigurationManager cfgManager(true);
cfgManager.readConfiguration(variables, desc); cfgManager.readConfiguration(variables, desc);
std::string essFile = variables["mwsave"].as<std::string>(); const auto essFile = variables["mwsave"].as<Files::MaybeQuotedPath>();
std::string outputFile = variables["output"].as<std::string>(); const auto outputFile = variables["output"].as<Files::MaybeQuotedPath>();
std::string encoding = variables["encoding"].as<std::string>(); std::string encoding = variables["encoding"].as<std::string>();
ESSImport::Importer importer(essFile, outputFile, encoding); ESSImport::Importer importer(essFile, outputFile, encoding);
@ -55,9 +55,10 @@ int main(int argc, char** argv)
importer.compare(); importer.compare();
else else
{ {
const std::string& ext = ".omwsave"; static constexpr std::u8string_view ext{u8".omwsave"};
if (std::filesystem::exists(std::filesystem::path(outputFile)) const auto length = outputFile.native().size();
&& (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext)) if (std::filesystem::exists(outputFile)
&& (length < ext.size() || outputFile.u8string().substr(length-ext.size()) != ext))
{ {
throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?"); throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?");
} }

View file

@ -19,13 +19,15 @@
#include <components/config/gamesettings.hpp> #include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp> #include <components/config/launchersettings.hpp>
#include <components/settings/settings.hpp>
#include <components/bsa/compressedbsafile.hpp> #include <components/bsa/compressedbsafile.hpp>
#include <components/files/qtconversion.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/navmeshtool/protocol.hpp> #include <components/navmeshtool/protocol.hpp>
#include <components/settings/settings.hpp>
#include <components/vfs/bsaarchive.hpp> #include <components/vfs/bsaarchive.hpp>
#include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp" #include "utils/profilescombobox.hpp"
#include "utils/textinputdialog.hpp"
#include "ui_directorypicker.h" #include "ui_directorypicker.h"
@ -230,9 +232,9 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (!mDataLocal.isEmpty()) if (!mDataLocal.isEmpty())
directories.insert(0, mDataLocal); directories.insert(0, mDataLocal);
const auto globalDataDir = QString(mGameSettings.getGlobalDataDir().c_str()); const auto& globalDataDir = mGameSettings.getGlobalDataDir();
if (!globalDataDir.isEmpty()) if (!globalDataDir.empty())
directories.insert(0, globalDataDir); directories.insert(0, Files::pathToQString(globalDataDir));
// normalize user supplied directories: resolve symlink, convert to native separator, make absolute // normalize user supplied directories: resolve symlink, convert to native separator, make absolute
for (auto& currentDir : directories) for (auto& currentDir : directories)
@ -264,7 +266,8 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
} }
// deactivate data-local and global data directory: they are always included // deactivate data-local and global data directory: they are always included
if (currentDir == mDataLocal || currentDir == globalDataDir) const auto tmp = currentDir.toUtf8();
if (currentDir == mDataLocal || std::filesystem::path(Misc::StringUtils::stringToU8String(tmp)) == globalDataDir)
{ {
auto flags = item->flags(); auto flags = item->flags();
item->setFlags(flags & ~(Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsEnabled)); item->setFlags(flags & ~(Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsEnabled));

View file

@ -12,12 +12,14 @@
#include <boost/program_options/options_description.hpp> #include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include "playpage.hpp"
#include "graphicspage.hpp"
#include "datafilespage.hpp"
#include "settingspage.hpp"
#include "advancedpage.hpp" #include "advancedpage.hpp"
#include "datafilespage.hpp"
#include "graphicspage.hpp"
#include "playpage.hpp"
#include "settingspage.hpp"
using namespace Process; using namespace Process;
@ -155,14 +157,14 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
return FirstRunDialogResultFailure; return FirstRunDialogResultFailure;
// Dialog wizard and setup will fail if the config directory does not already exist // Dialog wizard and setup will fail if the config directory does not already exist
QDir userConfigDir = QDir(QString::fromStdString(mCfgMgr.getUserConfigPath().string())); const auto& userConfigDir = mCfgMgr.getUserConfigPath();
if ( ! userConfigDir.exists() ) { if ( ! exists(userConfigDir) ) {
if ( ! userConfigDir.mkpath(".") ) if ( ! create_directories(userConfigDir) )
{ {
cfgError(tr("Error opening OpenMW configuration file"), cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not create directory %0</b><br><br> \ tr("<br><b>Could not create directory %0</b><br><br> \
Please make sure you have the right permissions \ Please make sure you have the right permissions \
and try again.<br>").arg(userConfigDir.canonicalPath()) and try again.<br>").arg(Files::pathToQString(canonical(userConfigDir)))
); );
return FirstRunDialogResultFailure; return FirstRunDialogResultFailure;
} }
@ -295,7 +297,7 @@ bool Launcher::MainDialog::setupLauncherSettings()
mLauncherSettings.setMultiValueEnabled(true); mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); const auto userPath = Files::pathToQString(mCfgMgr.getUserConfigPath());
QStringList paths; QStringList paths;
paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName)); paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName));
@ -328,9 +330,9 @@ bool Launcher::MainDialog::setupGameSettings()
{ {
mGameSettings.clear(); mGameSettings.clear();
QString localPath = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str()); const auto localPath = Files::pathToQString(mCfgMgr.getLocalPath());
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); const auto userPath = Files::pathToQString(mCfgMgr.getUserConfigPath());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); const auto globalPath = Files::pathToQString(mCfgMgr.getGlobalPath());
QFile file; QFile file;
@ -479,21 +481,24 @@ bool Launcher::MainDialog::writeSettings()
mSettingsPage->saveSettings(); mSettingsPage->saveSettings();
mAdvancedPage->saveSettings(); mAdvancedPage->saveSettings();
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); const auto& userPath = mCfgMgr.getUserConfigPath();
QDir dir(userPath);
if (!dir.exists()) { if (!exists(userPath)) {
if (!dir.mkpath(userPath)) { if (!create_directories(userPath)) {
cfgError(tr("Error creating OpenMW configuration directory"), cfgError(tr("Error creating OpenMW configuration directory"),
tr("<br><b>Could not create %0</b><br><br> \ tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \ Please make sure you have the right permissions \
and try again.<br>").arg(userPath)); and try again.<br>").arg(Files::pathToQString(userPath)));
return false; return false;
} }
} }
// Game settings // Game settings
QFile file(userPath + QString("openmw.cfg")); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFile file(userPath / "openmw.cfg");
#else
QFile file(Files::pathToQString(userPath / "openmw.cfg"));
#endif
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created // File cannot be opened or created
@ -509,19 +514,19 @@ bool Launcher::MainDialog::writeSettings()
file.close(); file.close();
// Graphics settings // Graphics settings
const std::string settingsPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); const auto settingsPath = mCfgMgr.getUserConfigPath() / "settings.cfg";
try { try {
Settings::Manager::saveUser(settingsPath); Settings::Manager::saveUser(settingsPath);
} }
catch (std::exception& e) { catch (std::exception& e) {
std::string msg = "<br><b>Error writing settings.cfg</b><br><br>" + std::string msg = "<br><b>Error writing settings.cfg</b><br><br>" +
settingsPath + "<br><br>" + e.what(); Files::pathToUnicodeString(settingsPath) + "<br><br>" + e.what();
cfgError(tr("Error writing user settings file"), tr(msg.c_str())); cfgError(tr("Error writing user settings file"), tr(msg.c_str()));
return false; return false;
} }
// Launcher settings // Launcher settings
file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); file.setFileName(Files::pathToQString(userPath / Config::LauncherSettings::sLauncherConfigFileName));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created // File cannot be opened or created

View file

@ -5,6 +5,9 @@
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include "utils/textinputdialog.hpp" #include "utils/textinputdialog.hpp"
#include "datafilespage.hpp" #include "datafilespage.hpp"
@ -102,9 +105,13 @@ void Launcher::SettingsPage::on_importerButton_clicked()
mMain->writeSettings(); mMain->writeSettings();
// Create the file if it doesn't already exist, else the importer will fail // Create the file if it doesn't already exist, else the importer will fail
QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str())); auto path = mCfgMgr.getUserConfigPath();
path.append(QLatin1String("openmw.cfg")); path /= "openmw.cfg";
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFile file(path); QFile file(path);
#else
QFile file(Files::pathToQString(path));
#endif
if (!file.exists()) { if (!file.exists()) {
if (!file.open(QIODevice::ReadWrite)) { if (!file.open(QIODevice::ReadWrite)) {
@ -137,7 +144,7 @@ void Launcher::SettingsPage::on_importerButton_clicked()
arguments.append(QString("--ini")); arguments.append(QString("--ini"));
arguments.append(settingsComboBox->currentText()); arguments.append(settingsComboBox->currentText());
arguments.append(QString("--cfg")); arguments.append(QString("--cfg"));
arguments.append(path); arguments.append(Files::pathToQString(path));
qDebug() << "arguments " << arguments; qDebug() << "arguments " << arguments;

View file

@ -15,7 +15,6 @@ openmw_add_executable(openmw-iniimporter
target_link_libraries(openmw-iniimporter target_link_libraries(openmw-iniimporter
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
components components
) )

View file

@ -1,16 +1,19 @@
#include "importer.hpp" #include "importer.hpp"
#include <iostream> #include <iostream>
#include <filesystem>
#include <fstream>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/format.hpp> #include <components/misc/strings/format.hpp>
#include <components/misc/strings/lower.hpp> #include <components/misc/strings/lower.hpp>
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/misc/timeconvert.hpp>
#include <boost/filesystem.hpp> #include <components/files/conversion.hpp>
#include <boost/filesystem/fstream.hpp>
namespace bfs = boost::filesystem;
namespace sfs = std::filesystem;
MwIniImporter::MwIniImporter() MwIniImporter::MwIniImporter()
: mVerbose(false) : mVerbose(false)
@ -654,12 +657,12 @@ void MwIniImporter::setVerbose(bool verbose) {
mVerbose = verbose; mVerbose = verbose;
} }
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const { MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::filesystem::path& filename) const {
std::cout << "load ini file: " << filename << std::endl; std::cout << "load ini file: " << Files::pathToUnicodeString(filename) << std::endl;
std::string section(""); std::string section("");
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
bfs::ifstream file((bfs::path(filename))); std::ifstream file(filename);
ToUTF8::Utf8Encoder encoder(mEncoding); ToUTF8::Utf8Encoder encoder(mEncoding);
std::string line; std::string line;
@ -715,11 +718,11 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::p
return map; return map;
} }
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::path& filename) { MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::filesystem::path& filename) {
std::cout << "load cfg file: " << filename << std::endl; std::cout << "load cfg file: " << Files::pathToUnicodeString(filename) << std::endl;
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
bfs::ifstream file((bfs::path(filename))); std::ifstream file(filename);
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
@ -861,7 +864,7 @@ std::vector<std::string>::iterator MwIniImporter::findString(std::vector<std::st
}); });
} }
void MwIniImporter::addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input) { void MwIniImporter::addPaths(std::vector<std::filesystem::path>& output, std::vector<std::string> input) {
for (auto& path : input) for (auto& path : input)
{ {
if (path.front() == '"') if (path.front() == '"')
@ -869,18 +872,18 @@ void MwIniImporter::addPaths(std::vector<boost::filesystem::path>& output, std::
// Drop first and last characters - quotation marks // Drop first and last characters - quotation marks
path = path.substr(1, path.size() - 2); path = path.substr(1, path.size() - 2);
} }
output.emplace_back(path); output.emplace_back(Files::pathFromUnicodeString(path));
} }
} }
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const std::filesystem::path& iniFilename) const
{ {
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles; std::vector<std::pair<std::time_t, std::filesystem::path>> contentFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::time_t defaultTime = 0; std::time_t defaultTime = 0;
ToUTF8::Utf8Encoder encoder(mEncoding); ToUTF8::Utf8Encoder encoder(mEncoding);
std::vector<boost::filesystem::path> dataPaths; std::vector<std::filesystem::path> dataPaths;
if (cfg.count("data")) if (cfg.count("data"))
addPaths(dataPaths, cfg["data"]); addPaths(dataPaths, cfg["data"]);
@ -909,7 +912,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
bool found = false; bool found = false;
for (auto & dataPath : dataPaths) for (auto & dataPath : dataPaths)
{ {
boost::filesystem::path path = dataPath / *entry; std::filesystem::path path = dataPath / *entry;
std::time_t time = lastWriteTime(path, defaultTime); std::time_t time = lastWriteTime(path, defaultTime);
if (time != defaultTime) if (time != defaultTime)
{ {
@ -936,13 +939,13 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
reader.setEncoder(&encoder); reader.setEncoder(&encoder);
for (auto& file : contentFiles) for (auto& file : contentFiles)
{ {
reader.open(file.second.string()); reader.open(file.second);
std::vector<std::string> dependencies; std::vector<std::string> dependencies;
for (auto& gameFile : reader.getGameFiles()) for (auto& gameFile : reader.getGameFiles())
{ {
dependencies.push_back(gameFile.name); dependencies.push_back(gameFile.name);
} }
unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), dependencies); unsortedFiles.emplace_back(Files::pathToUnicodeString(reader.getName().filename()), dependencies);
reader.close(); reader.close();
} }
@ -983,13 +986,13 @@ void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
mEncoding = encoding; mEncoding = encoding;
} }
std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime) std::time_t MwIniImporter::lastWriteTime(const std::filesystem::path& filename, std::time_t defaultTime)
{ {
std::time_t writeTime(defaultTime); std::time_t writeTime(defaultTime);
if (boost::filesystem::exists(filename)) if (std::filesystem::exists(filename))
{ {
boost::filesystem::path resolved = boost::filesystem::canonical(filename); std::filesystem::path resolved = std::filesystem::canonical(filename);
writeTime = boost::filesystem::last_write_time(resolved); writeTime = Misc::to_time_t(std::filesystem::last_write_time (resolved));
// print timestamp // print timestamp
const int size=1024; const int size=1024;

View file

@ -6,7 +6,7 @@
#include <vector> #include <vector>
#include <exception> #include <exception>
#include <iosfwd> #include <iosfwd>
#include <boost/filesystem/path.hpp> #include <filesystem>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
@ -19,12 +19,12 @@ class MwIniImporter {
MwIniImporter(); MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding); void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose); void setVerbose(bool verbose);
multistrmap loadIniFile(const boost::filesystem::path& filename) const; multistrmap loadIniFile(const std::filesystem::path& filename) const;
static multistrmap loadCfgFile(const boost::filesystem::path& filename); static multistrmap loadCfgFile(const std::filesystem::path& filename);
void merge(multistrmap &cfg, const multistrmap &ini) const; void merge(multistrmap &cfg, const multistrmap &ini) const;
void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; void mergeFallback(multistrmap &cfg, const multistrmap &ini) const;
void importGameFiles(multistrmap &cfg, const multistrmap &ini, void importGameFiles(multistrmap &cfg, const multistrmap &ini,
const boost::filesystem::path& iniFilename) const; const std::filesystem::path& iniFilename) const;
void importArchives(multistrmap &cfg, const multistrmap &ini) const; void importArchives(multistrmap &cfg, const multistrmap &ini) const;
static void writeToFile(std::ostream &out, const multistrmap &cfg); static void writeToFile(std::ostream &out, const multistrmap &cfg);
@ -35,10 +35,10 @@ class MwIniImporter {
static std::vector<std::string>::iterator findString(std::vector<std::string>& source, const std::string& string); static std::vector<std::string>::iterator findString(std::vector<std::string>& source, const std::string& string);
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
static void addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input); static void addPaths(std::vector<std::filesystem::path>& output, std::vector<std::string> input);
/// \return file's "last modified time", used in original MW to determine plug-in load order /// \return file's "last modified time", used in original MW to determine plug-in load order
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime); static std::time_t lastWriteTime(const std::filesystem::path& filename, std::time_t defaultTime);
bool mVerbose; bool mVerbose;
strmap mMergeMap; strmap mMergeMap;

View file

@ -1,13 +1,16 @@
#include "importer.hpp" #include "importer.hpp"
#include <iostream> #include <iostream>
#include <filesystem>
#include <fstream>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <components/files/configurationmanager.hpp>
#include <components/files/conversion.hpp>
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
namespace bfs = boost::filesystem; namespace sfs = std::filesystem;
#ifndef _WIN32 #ifndef _WIN32
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -52,7 +55,6 @@ private:
int wmain(int argc, wchar_t *wargv[]) { int wmain(int argc, wchar_t *wargv[]) {
utf8argv converter(argc, wargv); utf8argv converter(argc, wargv);
char **argv = converter.get(); char **argv = converter.get();
boost::filesystem::path::imbue(boost::locale::generator().generate(""));
#endif #endif
try try
@ -62,9 +64,9 @@ int wmain(int argc, wchar_t *wargv[]) {
desc.add_options() desc.add_options()
("help,h", "produce help message") ("help,h", "produce help message")
("verbose,v", "verbose output") ("verbose,v", "verbose output")
("ini,i", bpo::value<std::string>(), "morrowind.ini file") ("ini,i", bpo::value<Files::MaybeQuotedPath>(), "morrowind.ini file")
("cfg,c", bpo::value<std::string>(), "openmw.cfg file") ("cfg,c", bpo::value<Files::MaybeQuotedPath>(), "openmw.cfg file")
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file") ("output,o", bpo::value<Files::MaybeQuotedPath>()->default_value({}), "openmw.cfg file")
("game-files,g", "import esm and esp files") ("game-files,g", "import esm and esp files")
("fonts,f", "import bitmap fonts") ("fonts,f", "import bitmap fonts")
("no-archives,A", "disable bsa archives import") ("no-archives,A", "disable bsa archives import")
@ -92,20 +94,20 @@ int wmain(int argc, wchar_t *wargv[]) {
bpo::notify(vm); bpo::notify(vm);
boost::filesystem::path iniFile(vm["ini"].as<std::string>()); std::filesystem::path iniFile(vm["ini"].as<Files::MaybeQuotedPath>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
boost::filesystem::path cfgFile(vm["cfg"].as<std::string>()); std::filesystem::path cfgFile(vm["cfg"].as<Files::MaybeQuotedPath>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
// if no output is given, write back to cfg file // if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>()); std::filesystem::path outputFile = vm["output"].as<Files::MaybeQuotedPath>().u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
if(vm["output"].defaulted()) { if(vm["output"].defaulted()) {
outputFile = vm["cfg"].as<std::string>(); outputFile = vm["cfg"].as<Files::MaybeQuotedPath>().u8string(); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
} }
if(!boost::filesystem::exists(iniFile)) { if(!std::filesystem::exists(iniFile)) {
std::cerr << "ini file does not exist" << std::endl; std::cerr << "ini file does not exist" << std::endl;
return -3; return -3;
} }
if(!boost::filesystem::exists(cfgFile)) if(!std::filesystem::exists(cfgFile))
std::cerr << "cfg file does not exist" << std::endl; std::cerr << "cfg file does not exist" << std::endl;
MwIniImporter importer; MwIniImporter importer;
@ -136,8 +138,8 @@ int wmain(int argc, wchar_t *wargv[]) {
importer.importArchives(cfg, ini); importer.importArchives(cfg, ini);
} }
std::cout << "write to: " << outputFile << std::endl; std::cout << "write to: " << Files::pathToUnicodeString(outputFile) << std::endl;
bfs::ofstream file((bfs::path(outputFile))); std::ofstream file(outputFile);
importer.writeToFile(file, cfg); importer.writeToFile(file, cfg);
} }
catch (std::exception& e) catch (std::exception& e)

View file

@ -24,6 +24,7 @@
#include <components/esm3/readerscache.hpp> #include <components/esm3/readerscache.hpp>
#include <components/platform/platform.hpp> #include <components/platform/platform.hpp>
#include <components/detournavigator/agentbounds.hpp> #include <components/detournavigator/agentbounds.hpp>
#include <components/files/conversion.hpp>
#include <osg/Vec3f> #include <osg/Vec3f>
@ -143,7 +144,7 @@ namespace NavMeshTool
const auto fsStrict = variables["fs-strict"].as<bool>(); const auto fsStrict = variables["fs-strict"].as<bool>();
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>(); const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
Version::Version v = Version::getOpenmwVersion(resDir.string()); Version::Version v = Version::getOpenmwVersion(resDir);
Log(Debug::Info) << v.describe(); Log(Debug::Info) << v.describe();
dataDirs.insert(dataDirs.begin(), resDir / "vfs"); dataDirs.insert(dataDirs.begin(), resDir / "vfs");
const auto fileCollections = Files::Collections(dataDirs, !fsStrict); const auto fileCollections = Files::Collections(dataDirs, !fsStrict);
@ -179,7 +180,7 @@ namespace NavMeshTool
const osg::Vec3f agentHalfExtents = Settings::Manager::getVector3("default actor pathfind half extents", "Game"); const osg::Vec3f agentHalfExtents = Settings::Manager::getVector3("default actor pathfind half extents", "Game");
const DetourNavigator::AgentBounds agentBounds {agentCollisionShape, agentHalfExtents}; const DetourNavigator::AgentBounds agentBounds {agentCollisionShape, agentHalfExtents};
const std::uint64_t maxDbFileSize = static_cast<std::uint64_t>(Settings::Manager::getInt64("max navmeshdb file size", "Navigator")); const std::uint64_t maxDbFileSize = static_cast<std::uint64_t>(Settings::Manager::getInt64("max navmeshdb file size", "Navigator"));
const std::string dbPath = (config.getUserDataPath() / "navmesh.db").string(); const auto dbPath = Files::pathToUnicodeString(config.getUserDataPath() / "navmesh.db");
DetourNavigator::NavMeshDb db(dbPath, maxDbFileSize); DetourNavigator::NavMeshDb db(dbPath, maxDbFileSize);

View file

@ -276,11 +276,13 @@ namespace NavMeshTool
{ {
it = navMeshInputs.emplace(cell.mCellId.mWorldspace, it = navMeshInputs.emplace(cell.mCellId.mWorldspace,
std::make_unique<WorldspaceNavMeshInput>(cell.mCellId.mWorldspace, settings.mRecast)).first; std::make_unique<WorldspaceNavMeshInput>(cell.mCellId.mWorldspace, settings.mRecast)).first;
it->second->mTileCachedRecastMeshManager.setWorldspace(cell.mCellId.mWorldspace); it->second->mTileCachedRecastMeshManager.setWorldspace(cell.mCellId.mWorldspace, nullptr);
} }
return *it->second; return *it->second;
} (); } ();
const TileCachedRecastMeshManager::UpdateGuard guard(navMeshInput.mTileCachedRecastMeshManager);
if (exterior) if (exterior)
{ {
const auto it = std::lower_bound(esmData.mLands.begin(), esmData.mLands.end(), cellPosition, LessByXY {}); const auto it = std::lower_bound(esmData.mLands.begin(), esmData.mLands.end(), cellPosition, LessByXY {});
@ -292,14 +294,14 @@ namespace NavMeshTool
mergeOrAssign(getAabb(cellPosition, minHeight, maxHeight), mergeOrAssign(getAabb(cellPosition, minHeight, maxHeight),
navMeshInput.mAabb, navMeshInput.mAabbInitialized); navMeshInput.mAabb, navMeshInput.mAabbInitialized);
navMeshInput.mTileCachedRecastMeshManager.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, heightfieldShape); navMeshInput.mTileCachedRecastMeshManager.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, heightfieldShape, &guard);
navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1); navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1, &guard);
} }
else else
{ {
if ((cell.mData.mFlags & ESM::Cell::HasWater) != 0) if ((cell.mData.mFlags & ESM::Cell::HasWater) != 0)
navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, std::numeric_limits<int>::max(), cell.mWater); navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, std::numeric_limits<int>::max(), cell.mWater, &guard);
} }
forEachObject(cell, esmData, vfs, bulletShapeManager, readers, forEachObject(cell, esmData, vfs, bulletShapeManager, readers,
@ -318,13 +320,13 @@ namespace NavMeshTool
const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform()); const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform());
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform,
DetourNavigator::AreaType_ground); DetourNavigator::AreaType_ground, &guard);
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get()) if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
{ {
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform()); const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform, navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform,
DetourNavigator::AreaType_null); DetourNavigator::AreaType_null, &guard);
} }
data.mObjects.emplace_back(std::move(object)); data.mObjects.emplace_back(std::move(object));

View file

@ -9,6 +9,8 @@
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/vfs/bsaarchive.hpp> #include <components/vfs/bsaarchive.hpp>
#include <components/vfs/filesystemarchive.hpp> #include <components/vfs/filesystemarchive.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/conversion.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
@ -16,26 +18,26 @@
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
///See if the file has the named extension ///See if the file has the named extension
bool hasExtension(std::string filename, std::string extensionToFind) bool hasExtension(const std::filesystem::path& filename, const std::string& extensionToFind)
{ {
std::string extension = filename.substr(filename.find_last_of('.')+1); const auto extension = Files::pathToUnicodeString(filename.extension());
return Misc::StringUtils::ciEqual(extension, extensionToFind); return Misc::StringUtils::ciEqual(extension, extensionToFind);
} }
///See if the file has the "nif" extension. ///See if the file has the "nif" extension.
bool isNIF(const std::string & filename) bool isNIF(const std::filesystem::path &filename)
{ {
return hasExtension(filename,"nif"); return hasExtension(filename,"nif");
} }
///See if the file has the "bsa" extension. ///See if the file has the "bsa" extension.
bool isBSA(const std::string & filename) bool isBSA(const std::filesystem::path &filename)
{ {
return hasExtension(filename,"bsa"); return hasExtension(filename,"bsa");
} }
/// Check all the nif files in a given VFS::Archive /// Check all the nif files in a given VFS::Archive
/// \note Can not read a bsa file inside of a bsa file. /// \note Can not read a bsa file inside of a bsa file.
void readVFS(std::unique_ptr<VFS::Archive>&& anArchive, std::string archivePath = "") void readVFS(std::unique_ptr<VFS::Archive>&& anArchive, const std::filesystem::path& archivePath = {})
{ {
VFS::Manager myManager(true); VFS::Manager myManager(true);
myManager.addArchive(std::move(anArchive)); myManager.addArchive(std::move(anArchive));
@ -47,14 +49,14 @@ void readVFS(std::unique_ptr<VFS::Archive>&& anArchive, std::string archivePath
if(isNIF(name)) if(isNIF(name))
{ {
// std::cout << "Decoding: " << name << std::endl; // std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(myManager.get(name),archivePath+name); Nif::NIFFile temp_nif(myManager.get(name),archivePath / name);
} }
else if(isBSA(name)) else if(isBSA(name))
{ {
if(!archivePath.empty() && !isBSA(archivePath)) if(!archivePath.empty() && !isBSA(archivePath))
{ {
// std::cout << "Reading BSA File: " << name << std::endl; // std::cout << "Reading BSA File: " << name << std::endl;
readVFS(std::make_unique<VFS::BsaArchive>(archivePath + name), archivePath + name + "/"); readVFS(std::make_unique<VFS::BsaArchive>(archivePath / name), archivePath / name);
// std::cout << "Done with BSA File: " << name << std::endl; // std::cout << "Done with BSA File: " << name << std::endl;
} }
} }
@ -66,7 +68,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& anArchive, std::string archivePath
} }
} }
bool parseOptions (int argc, char** argv, std::vector<std::string>& files) bool parseOptions (int argc, char** argv, std::vector<Files::MaybeQuotedPath> &files)
{ {
bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n" bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n"
"Usages:\n" "Usages:\n"
@ -75,7 +77,7 @@ bool parseOptions (int argc, char** argv, std::vector<std::string>& files)
"Allowed options"); "Allowed options");
desc.add_options() desc.add_options()
("help,h", "print help message.") ("help,h", "print help message.")
("input-file", bpo::value< std::vector<std::string> >(), "input file") ("input-file", bpo::value< Files::MaybeQuotedPathContainer >(), "input file")
; ;
//Default option if none provided //Default option if none provided
@ -96,7 +98,7 @@ bool parseOptions (int argc, char** argv, std::vector<std::string>& files)
} }
if (variables.count("input-file")) if (variables.count("input-file"))
{ {
files = variables["input-file"].as< std::vector<std::string> >(); files = variables["input-file"].as< Files::MaybeQuotedPathContainer >();
return true; return true;
} }
} }
@ -114,36 +116,34 @@ bool parseOptions (int argc, char** argv, std::vector<std::string>& files)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
std::vector<std::string> files; std::vector<Files::MaybeQuotedPath> files;
if(!parseOptions (argc, argv, files)) if(!parseOptions (argc, argv, files))
return 1; return 1;
Nif::NIFFile::setLoadUnsupportedFiles(true); Nif::NIFFile::setLoadUnsupportedFiles(true);
// std::cout << "Reading Files" << std::endl; // std::cout << "Reading Files" << std::endl;
for(auto it=files.begin(); it!=files.end(); ++it) for(const auto& path : files)
{ {
std::string name = *it;
try try
{ {
if(isNIF(name)) if(isNIF(path))
{ {
//std::cout << "Decoding: " << name << std::endl; //std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name), name); Nif::NIFFile temp_nif(Files::openConstrainedFileStream(path), path);
} }
else if(isBSA(name)) else if(isBSA(path))
{ {
// std::cout << "Reading BSA File: " << name << std::endl; // std::cout << "Reading BSA File: " << name << std::endl;
readVFS(std::make_unique<VFS::BsaArchive>(name)); readVFS(std::make_unique<VFS::BsaArchive>(path));
} }
else if(std::filesystem::is_directory(std::filesystem::path(name))) else if(std::filesystem::is_directory(path))
{ {
// std::cout << "Reading All Files in: " << name << std::endl; // std::cout << "Reading All Files in: " << name << std::endl;
readVFS(std::make_unique<VFS::FileSystemArchive>(name), name); readVFS(std::make_unique<VFS::FileSystemArchive>(path), path);
} }
else else
{ {
std::cerr << "ERROR: \"" << name << "\" is not a nif file, bsa file, or directory!" << std::endl; std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path) << "\" is not a nif file, bsa file, or directory!" << std::endl;
} }
} }
catch (std::exception& e) catch (std::exception& e)

View file

@ -224,8 +224,6 @@ target_link_libraries(openmw-cs
${OSGTEXT_LIBRARIES} ${OSGTEXT_LIBRARIES}
${OSG_LIBRARIES} ${OSG_LIBRARIES}
${EXTERN_OSGQT_LIBRARY} ${EXTERN_OSGQT_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
components_qt components_qt
) )

View file

@ -10,7 +10,10 @@
#include <components/debug/debugging.hpp> #include <components/debug/debugging.hpp>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -63,7 +66,7 @@ CS::Editor::Editor (int argc, char **argv)
connect (&mStartup, &CSVDoc::StartupDialogue::editConfig, this, &Editor::showSettings); connect (&mStartup, &CSVDoc::StartupDialogue::editConfig, this, &Editor::showSettings);
connect (&mFileDialog, &CSVDoc::FileDialog::signalOpenFiles, connect (&mFileDialog, &CSVDoc::FileDialog::signalOpenFiles,
this, [this](const boost::filesystem::path &savePath){ this->openFiles(savePath); }); this, [this](const std::filesystem::path &savePath){ this->openFiles(savePath); });
connect (&mFileDialog, &CSVDoc::FileDialog::signalCreateNewFile, this, &Editor::createNewFile); connect (&mFileDialog, &CSVDoc::FileDialog::signalCreateNewFile, this, &Editor::createNewFile);
connect (&mFileDialog, &CSVDoc::FileDialog::rejected, this, &Editor::cancelFileDialog); connect (&mFileDialog, &CSVDoc::FileDialog::rejected, this, &Editor::cancelFileDialog);
@ -77,9 +80,8 @@ CS::Editor::~Editor ()
mPidFile.close(); mPidFile.close();
if(mServer && boost::filesystem::exists(mPid)) if(mServer && std::filesystem::exists(mPid))
static_cast<void> ( // silence coverity warning std::filesystem::remove(mPid);
remove(mPid.string().c_str())); // ignore any error
} }
boost::program_options::variables_map CS::Editor::readConfiguration() boost::program_options::variables_map CS::Editor::readConfiguration()
@ -107,7 +109,7 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
mCfgMgr.readConfiguration(variables, desc, false); mCfgMgr.readConfiguration(variables, desc, false);
Settings::Manager::load(mCfgMgr, true); Settings::Manager::load(mCfgMgr, true);
setupLogging(mCfgMgr.getLogPath().string(), "OpenMW-CS"); setupLogging(mCfgMgr.getLogPath(), "OpenMW-CS");
return variables; return variables;
} }
@ -122,7 +124,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName)); mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName));
mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str())); mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str()));
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::MaybeQuotedPath>()); mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::MaybeQuotedPath>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
if (variables["script-blacklist-use"].as<bool>()) if (variables["script-blacklist-use"].as<bool>())
mDocumentManager.setBlacklistedScripts ( mDocumentManager.setBlacklistedScripts (
@ -135,10 +137,10 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
dataDirs = asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>()); dataDirs = asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>());
} }
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>()); Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
if (!local.empty()) if (!local.empty())
{ {
boost::filesystem::create_directories(local); std::filesystem::create_directories(local);
dataLocal.push_back(local); dataLocal.push_back(local);
} }
mCfgMgr.filterOutNonExistingPaths(dataDirs); mCfgMgr.filterOutNonExistingPaths(dataDirs);
@ -225,14 +227,15 @@ void CS::Editor::loadDocument()
mFileDialog.showDialog (CSVDoc::ContentAction_Edit); mFileDialog.showDialog (CSVDoc::ContentAction_Edit);
} }
void CS::Editor::openFiles (const boost::filesystem::path &savePath, const std::vector<boost::filesystem::path> &discoveredFiles) void CS::Editor::openFiles (const std::filesystem::path &savePath, const std::vector<std::filesystem::path> &discoveredFiles)
{ {
std::vector<boost::filesystem::path> files; std::vector<std::filesystem::path> files;
if(discoveredFiles.empty()) if(discoveredFiles.empty())
{ {
for (const QString &path : mFileDialog.selectedFilePaths()) for (const QString &path : mFileDialog.selectedFilePaths()) {
files.emplace_back(path.toUtf8().constData()); files.emplace_back(Files::pathFromQString(path));
}
} }
else else
{ {
@ -244,12 +247,12 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath, const std::
mFileDialog.hide(); mFileDialog.hide();
} }
void CS::Editor::createNewFile (const boost::filesystem::path &savePath) void CS::Editor::createNewFile (const std::filesystem::path &savePath)
{ {
std::vector<boost::filesystem::path> files; std::vector<std::filesystem::path> files;
for (const QString &path : mFileDialog.selectedFilePaths()) { for (const QString &path : mFileDialog.selectedFilePaths()) {
files.emplace_back(path.toUtf8().constData()); files.emplace_back(Files::pathFromQString(path));
} }
files.push_back (savePath); files.push_back (savePath);
@ -259,9 +262,9 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
mFileDialog.hide(); mFileDialog.hide();
} }
void CS::Editor::createNewGame (const boost::filesystem::path& file) void CS::Editor::createNewGame (const std::filesystem::path& file)
{ {
std::vector<boost::filesystem::path> files; std::vector<std::filesystem::path> files;
files.push_back (file); files.push_back (file);
@ -292,13 +295,13 @@ bool CS::Editor::makeIPCServer()
{ {
try try
{ {
mPid = boost::filesystem::temp_directory_path(); mPid = std::filesystem::temp_directory_path();
mPid /= "openmw-cs.pid"; mPid /= "openmw-cs.pid";
bool pidExists = boost::filesystem::exists(mPid); bool pidExists = std::filesystem::exists(mPid);
mPidFile.open(mPid); mPidFile.open(mPid);
mLock = boost::interprocess::file_lock(mPid.string().c_str()); mLock = boost::interprocess::file_lock(mPid.c_str());
if(!mLock.try_lock()) if(!mLock.try_lock())
{ {
Log(Debug::Error) << "Error: OpenMW-CS is already running."; Log(Debug::Error) << "Error: OpenMW-CS is already running.";
@ -321,12 +324,13 @@ bool CS::Editor::makeIPCServer()
mServer->close(); mServer->close();
fullPath.remove(QRegExp("dummy$")); fullPath.remove(QRegExp("dummy$"));
fullPath += mIpcServerName; fullPath += mIpcServerName;
if(boost::filesystem::exists(fullPath.toUtf8().constData())) const auto path = Files::pathFromQString(fullPath);
if(exists(path))
{ {
// TODO: compare pid of the current process with that in the file // TODO: compare pid of the current process with that in the file
Log(Debug::Info) << "Detected unclean shutdown."; Log(Debug::Info) << "Detected unclean shutdown.";
// delete the stale file // delete the stale file
if(remove(fullPath.toUtf8().constData())) if(remove(path))
Log(Debug::Error) << "Error: can not remove stale connection file."; Log(Debug::Error) << "Error: can not remove stale connection file.";
} }
} }
@ -374,27 +378,24 @@ int CS::Editor::run()
ESM::ESMReader fileReader; ESM::ESMReader fileReader;
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncodingName)); ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncodingName));
fileReader.setEncoder(&encoder); fileReader.setEncoder(&encoder);
fileReader.open(mFileToLoad.string()); fileReader.open(mFileToLoad);
std::vector<boost::filesystem::path> discoveredFiles; std::vector<std::filesystem::path> discoveredFiles;
for (std::vector<ESM::Header::MasterData>::const_iterator itemIter = fileReader.getGameFiles().begin(); for (const auto& item : fileReader.getGameFiles())
itemIter != fileReader.getGameFiles().end(); ++itemIter)
{ {
for (Files::PathContainer::const_iterator pathIter = mDataDirs.begin(); for (const auto& path : mDataDirs)
pathIter != mDataDirs.end(); ++pathIter)
{ {
const boost::filesystem::path masterPath = *pathIter / itemIter->name; if (auto masterPath = path / item.name; std::filesystem::exists(masterPath))
if (boost::filesystem::exists(masterPath))
{ {
discoveredFiles.push_back(masterPath); discoveredFiles.emplace_back(std::move(masterPath));
break; break;
} }
} }
} }
discoveredFiles.push_back(mFileToLoad); discoveredFiles.push_back(mFileToLoad);
QString extension = QString::fromStdString(mFileToLoad.extension().string()).toLower(); const auto extension = Files::pathToQString(mFileToLoad.extension()).toLower();
if (extension == ".esm") if (extension == ".esm")
{ {
mFileToLoad.replace_extension(".omwgame"); mFileToLoad.replace_extension(".omwgame");

View file

@ -1,8 +1,9 @@
#ifndef CS_EDITOR_H #ifndef CS_EDITOR_H
#define CS_EDITOR_H #define CS_EDITOR_H
#include <fstream>
#include <boost/interprocess/sync/file_lock.hpp> #include <boost/interprocess/sync/file_lock.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include <QObject> #include <QObject>
@ -48,15 +49,15 @@ namespace CS
CSVDoc::NewGameDialogue mNewGame; CSVDoc::NewGameDialogue mNewGame;
CSVPrefs::Dialogue mSettings; CSVPrefs::Dialogue mSettings;
CSVDoc::FileDialog mFileDialog; CSVDoc::FileDialog mFileDialog;
boost::filesystem::path mLocal; std::filesystem::path mLocal;
boost::filesystem::path mResources; std::filesystem::path mResources;
boost::filesystem::path mPid; std::filesystem::path mPid;
boost::interprocess::file_lock mLock; boost::interprocess::file_lock mLock;
boost::filesystem::ofstream mPidFile; std::ofstream mPidFile;
bool mFsStrict; bool mFsStrict;
CSVTools::Merge mMerge; CSVTools::Merge mMerge;
CSVDoc::ViewManager* mViewManager; CSVDoc::ViewManager* mViewManager;
boost::filesystem::path mFileToLoad; std::filesystem::path mFileToLoad;
Files::PathContainer mDataDirs; Files::PathContainer mDataDirs;
std::string mEncodingName; std::string mEncodingName;
@ -88,9 +89,9 @@ namespace CS
void cancelFileDialog(); void cancelFileDialog();
void loadDocument(); void loadDocument();
void openFiles (const boost::filesystem::path &path, const std::vector<boost::filesystem::path> &discoveredFiles = std::vector<boost::filesystem::path>()); void openFiles (const std::filesystem::path &path, const std::vector<std::filesystem::path> &discoveredFiles = {});
void createNewFile (const boost::filesystem::path& path); void createNewFile (const std::filesystem::path& path);
void createNewGame (const boost::filesystem::path& file); void createNewGame (const std::filesystem::path& file);
void showStartup(); void showStartup();

View file

@ -25,6 +25,6 @@ void CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type,
list.resize (size+ids.size()); list.resize (size+ids.size());
std::transform (ids.begin(), ids.end(), list.begin()+size, Misc::StringUtils::lowerCase); std::transform (ids.begin(), ids.end(), list.begin()+size, [](const std::string& s) { return Misc::StringUtils::lowerCase(s); } );
std::sort (list.begin(), list.end()); std::sort (list.begin(), list.end());
} }

View file

@ -4,9 +4,8 @@
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <filesystem>
#include <boost/filesystem.hpp> #include <utility>
#include <boost/filesystem/fstream.hpp>
#include "../world/defaultgmsts.hpp" #include "../world/defaultgmsts.hpp"
@ -15,6 +14,7 @@
#endif #endif
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/files/conversion.hpp>
void CSMDoc::Document::addGmsts() void CSMDoc::Document::addGmsts()
{ {
@ -277,14 +277,14 @@ void CSMDoc::Document::createBase()
} }
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files,bool new_, std::vector< std::filesystem::path > files,bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const std::filesystem::path& savePath, const std::filesystem::path& resDir,
ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts, ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts,
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives) bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, resDir), : mSavePath (savePath), mContentFiles (std::move(files)), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, resDir),
mTools (*this, encoding), mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().u8string() + u8".project")),
mSavingOperation (*this, mProjectPath, encoding), mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation), mSaving (&mSavingOperation),
mResDir(resDir), mRunner (mProjectPath), mResDir(resDir), mRunner (mProjectPath),
@ -293,21 +293,21 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
if (mContentFiles.empty()) if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence"); throw std::runtime_error ("Empty content file sequence");
if (mNew || !boost::filesystem::exists (mProjectPath)) if (mNew || !std::filesystem::exists (mProjectPath))
{ {
boost::filesystem::path filtersPath (configuration.getUserDataPath() / "defaultfilters"); auto filtersPath = configuration.getUserDataPath() / "defaultfilters";
boost::filesystem::ofstream destination(mProjectPath, std::ios::out | std::ios::binary); std::ofstream destination(mProjectPath, std::ios::out | std::ios::binary);
if (!destination.is_open()) if (!destination.is_open())
throw std::runtime_error("Can not create project file: " + mProjectPath.string()); throw std::runtime_error("Can not create project file: " + Files::pathToUnicodeString(mProjectPath));
destination.exceptions(std::ios::failbit | std::ios::badbit); destination.exceptions(std::ios::failbit | std::ios::badbit);
if (!boost::filesystem::exists (filtersPath)) if (!std::filesystem::exists (filtersPath))
filtersPath = mResDir / "defaultfilters"; filtersPath = mResDir / "defaultfilters";
boost::filesystem::ifstream source(filtersPath, std::ios::in | std::ios::binary); std::ifstream source(filtersPath, std::ios::in | std::ios::binary);
if (!source.is_open()) if (!source.is_open())
throw std::runtime_error("Can not read filters file: " + filtersPath.string()); throw std::runtime_error("Can not read filters file: " + Files::pathToUnicodeString(filtersPath));
source.exceptions(std::ios::failbit | std::ios::badbit); source.exceptions(std::ios::failbit | std::ios::badbit);
destination << source.rdbuf(); destination << source.rdbuf();
@ -369,22 +369,22 @@ int CSMDoc::Document::getState() const
return state; return state;
} }
const boost::filesystem::path& CSMDoc::Document::getResourceDir() const const std::filesystem::path& CSMDoc::Document::getResourceDir() const
{ {
return mResDir; return mResDir;
} }
const boost::filesystem::path& CSMDoc::Document::getSavePath() const const std::filesystem::path& CSMDoc::Document::getSavePath() const
{ {
return mSavePath; return mSavePath;
} }
const boost::filesystem::path& CSMDoc::Document::getProjectPath() const const std::filesystem::path& CSMDoc::Document::getProjectPath() const
{ {
return mProjectPath; return mProjectPath;
} }
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const const std::vector<std::filesystem::path>& CSMDoc::Document::getContentFiles() const
{ {
return mContentFiles; return mContentFiles;
} }
@ -481,11 +481,11 @@ bool CSMDoc::Document::isBlacklisted (const CSMWorld::UniversalId& id)
void CSMDoc::Document::startRunning (const std::string& profile, void CSMDoc::Document::startRunning (const std::string& profile,
const std::string& startupInstruction) const std::string& startupInstruction)
{ {
std::vector<std::string> contentFiles; std::vector<std::filesystem::path> contentFiles;
for (std::vector<boost::filesystem::path>::const_iterator iter (mContentFiles.begin()); for (const auto & mContentFile : mContentFiles) {
iter!=mContentFiles.end(); ++iter) contentFiles.emplace_back(mContentFile.filename());
contentFiles.push_back (iter->filename().string()); }
mRunner.configure (getData().getDebugProfiles().getRecord (profile).get(), contentFiles, mRunner.configure (getData().getDebugProfiles().getRecord (profile).get(), contentFiles,
startupInstruction); startupInstruction);

View file

@ -3,8 +3,6 @@
#include <string> #include <string>
#include <boost/filesystem/path.hpp>
#include <QUndoStack> #include <QUndoStack>
#include <QObject> #include <QObject>
@ -58,15 +56,15 @@ namespace CSMDoc
private: private:
boost::filesystem::path mSavePath; std::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles; std::vector<std::filesystem::path> mContentFiles;
bool mNew; bool mNew;
CSMWorld::Data mData; CSMWorld::Data mData;
CSMTools::Tools mTools; CSMTools::Tools mTools;
boost::filesystem::path mProjectPath; std::filesystem::path mProjectPath;
Saving mSavingOperation; Saving mSavingOperation;
OperationHolder mSaving; OperationHolder mSaving;
boost::filesystem::path mResDir; std::filesystem::path mResDir;
Blacklist mBlacklist; Blacklist mBlacklist;
Runner mRunner; Runner mRunner;
bool mDirty; bool mDirty;
@ -100,8 +98,8 @@ namespace CSMDoc
public: public:
Document (const Files::ConfigurationManager& configuration, Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_, std::vector< std::filesystem::path > files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, const std::filesystem::path& savePath, const std::filesystem::path& resDir,
ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts, ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts,
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives); bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
@ -111,13 +109,13 @@ namespace CSMDoc
int getState() const; int getState() const;
const boost::filesystem::path& getResourceDir() const; const std::filesystem::path& getResourceDir() const;
const boost::filesystem::path& getSavePath() const; const std::filesystem::path& getSavePath() const;
const boost::filesystem::path& getProjectPath() const; const std::filesystem::path& getProjectPath() const;
const std::vector<boost::filesystem::path>& getContentFiles() const; const std::vector<std::filesystem::path>& getContentFiles() const;
///< \attention The last element in this collection is the file that is being edited, ///< \attention The last element in this collection is the file that is being edited,
/// but with its original path instead of the save path. /// but with its original path instead of the save path.

View file

@ -1,6 +1,6 @@
#include "documentmanager.hpp" #include "documentmanager.hpp"
#include <boost/filesystem.hpp> #include <filesystem>
#ifndef Q_MOC_RUN #ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
@ -11,10 +11,10 @@
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mFsStrict(false) : mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mFsStrict(false)
{ {
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; std::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
if (!boost::filesystem::is_directory (projectPath)) if (!std::filesystem::is_directory (projectPath))
boost::filesystem::create_directories (projectPath); std::filesystem::create_directories (projectPath);
mLoader.moveToThread (&mLoaderThread); mLoader.moveToThread (&mLoaderThread);
mLoaderThread.start(); mLoaderThread.start();
@ -51,7 +51,7 @@ bool CSMDoc::DocumentManager::isEmpty()
return mDocuments.empty(); return mDocuments.empty();
} }
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, void CSMDoc::DocumentManager::addDocument (const std::vector<std::filesystem::path>& files, const std::filesystem::path& savePath,
bool new_) bool new_)
{ {
Document *document = makeDocument (files, savePath, new_); Document *document = makeDocument (files, savePath, new_);
@ -59,8 +59,8 @@ void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::
} }
CSMDoc::Document *CSMDoc::DocumentManager::makeDocument ( CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files, const std::vector< std::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_) const std::filesystem::path& savePath, bool new_)
{ {
return new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives); return new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);
} }
@ -93,9 +93,9 @@ void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document)
emit lastDocumentDeleted(); emit lastDocumentDeleted();
} }
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) void CSMDoc::DocumentManager::setResourceDir (const std::filesystem::path& parResDir)
{ {
mResDir = boost::filesystem::system_complete(parResDir); mResDir = std::filesystem::absolute(parResDir);
} }
void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding) void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)

View file

@ -4,8 +4,6 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <boost/filesystem/path.hpp>
#include <QObject> #include <QObject>
#include <QThread> #include <QThread>
@ -40,7 +38,7 @@ namespace CSMDoc
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
std::vector<std::string> mBlacklistedScripts; std::vector<std::string> mBlacklistedScripts;
boost::filesystem::path mResDir; std::filesystem::path mResDir;
bool mFsStrict; bool mFsStrict;
Files::PathContainer mDataPaths; Files::PathContainer mDataPaths;
@ -55,8 +53,8 @@ namespace CSMDoc
~DocumentManager(); ~DocumentManager();
void addDocument (const std::vector< boost::filesystem::path >& files, void addDocument (const std::vector< std::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_); const std::filesystem::path& savePath, bool new_);
///< \param new_ Do not load the last content file in \a files and instead create in an ///< \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way. /// appropriate way.
@ -66,10 +64,10 @@ namespace CSMDoc
/// ///
/// \param new_ Do not load the last content file in \a files and instead create in an /// \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way. /// appropriate way.
Document *makeDocument (const std::vector< boost::filesystem::path >& files, Document *makeDocument (const std::vector< std::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_); const std::filesystem::path& savePath, bool new_);
void setResourceDir (const boost::filesystem::path& parResDir); void setResourceDir (const std::filesystem::path& parResDir);
void setEncoding (ToUTF8::FromType encoding); void setEncoding (ToUTF8::FromType encoding);

View file

@ -1,5 +1,7 @@
#include "loader.hpp" #include "loader.hpp"
#include <components/files/conversion.hpp>
#include <iostream> #include <iostream>
#include "../tools/reportmodel.hpp" #include "../tools/reportmodel.hpp"
@ -87,13 +89,13 @@ void CSMDoc::Loader::load()
if (iter->second.mFile<size) // start loading the files if (iter->second.mFile<size) // start loading the files
{ {
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile]; std::filesystem::path path = document->getContentFiles()[iter->second.mFile];
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false); int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false);
iter->second.mRecordsLeft = true; iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0; iter->second.mRecordsLoaded = 0;
emit nextStage (document, path.filename().string(), steps); emit nextStage (document, Files::pathToUnicodeString(path.filename()), steps);
} }
else if (iter->second.mFile==size) // start loading the last (project) file else if (iter->second.mFile==size) // start loading the last (project) file
{ {

View file

@ -1,14 +1,19 @@
#include "runner.hpp" #include "runner.hpp"
#include <utility>
#include <QDir> #include <QDir>
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QTextStream> #include <QTextStream>
#include <QCoreApplication> #include <QCoreApplication>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include "operationholder.hpp" #include "operationholder.hpp"
CSMDoc::Runner::Runner (const boost::filesystem::path& projectPath) CSMDoc::Runner::Runner (std::filesystem::path projectPath)
: mRunning (false), mStartup (nullptr), mProjectPath (projectPath) : mRunning (false), mStartup (nullptr), mProjectPath (std::move(projectPath))
{ {
connect (&mProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), connect (&mProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
this, &Runner::finished); this, &Runner::finished);
@ -78,23 +83,21 @@ void CSMDoc::Runner::start (bool delayed)
else else
arguments << "--new-game=1"; arguments << "--new-game=1";
arguments << ("--script-run="+mStartup->fileName()); arguments << ("--script-run=" + mStartup->fileName());
arguments << arguments << "--data=\"" + Files::pathToQString(mProjectPath.parent_path()) + "\"";
QString::fromUtf8 (("--data=\""+mProjectPath.parent_path().string()+"\"").c_str());
arguments << "--replace=content"; arguments << "--replace=content";
for (std::vector<std::string>::const_iterator iter (mContentFiles.begin()); for (const auto& mContentFile : mContentFiles)
iter!=mContentFiles.end(); ++iter)
{ {
arguments << QString::fromUtf8 (("--content="+*iter).c_str()); arguments << "--content=" + Files::pathToQString(mContentFile);
} }
arguments arguments
<< QString::fromUtf8 (("--content="+mProjectPath.filename().string()).c_str()); << "--content=" + Files::pathToQString(mProjectPath.filename());
mProcess.start (path, arguments); mProcess.start(path, arguments);
} }
mRunning = true; mRunning = true;
@ -121,7 +124,7 @@ bool CSMDoc::Runner::isRunning() const
} }
void CSMDoc::Runner::configure (const ESM::DebugProfile& profile, void CSMDoc::Runner::configure (const ESM::DebugProfile& profile,
const std::vector<std::string>& contentFiles, const std::string& startupInstruction) const std::vector<std::filesystem::path> &contentFiles, const std::string& startupInstruction)
{ {
mProfile = profile; mProfile = profile;
mContentFiles = contentFiles; mContentFiles = contentFiles;

View file

@ -4,14 +4,14 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <boost/filesystem/path.hpp>
#include <QObject> #include <QObject>
#include <QProcess> #include <QProcess>
#include <QTextDocument> #include <QTextDocument>
#include <components/esm3/debugprofile.hpp> #include <components/esm3/debugprofile.hpp>
#include <filesystem>
class QTemporaryFile; class QTemporaryFile;
namespace CSMDoc namespace CSMDoc
@ -25,15 +25,15 @@ namespace CSMDoc
QProcess mProcess; QProcess mProcess;
bool mRunning; bool mRunning;
ESM::DebugProfile mProfile; ESM::DebugProfile mProfile;
std::vector<std::string> mContentFiles; std::vector<std::filesystem::path> mContentFiles;
std::string mStartupInstruction; std::string mStartupInstruction;
QTemporaryFile *mStartup; QTemporaryFile *mStartup;
QTextDocument mLog; QTextDocument mLog;
boost::filesystem::path mProjectPath; std::filesystem::path mProjectPath;
public: public:
Runner (const boost::filesystem::path& projectPath); Runner (std::filesystem::path projectPath);
~Runner(); ~Runner();
@ -48,7 +48,7 @@ namespace CSMDoc
bool isRunning() const; bool isRunning() const;
void configure (const ESM::DebugProfile& profile, void configure (const ESM::DebugProfile& profile,
const std::vector<std::string>& contentFiles, const std::vector<std::filesystem::path> &contentFiles,
const std::string& startupInstruction); const std::string& startupInstruction);
QTextDocument *getLog(); QTextDocument *getLog();

View file

@ -7,7 +7,7 @@
#include "savingstages.hpp" #include "savingstages.hpp"
#include "document.hpp" #include "document.hpp"
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath, CSMDoc::Saving::Saving (Document& document, const std::filesystem::path& projectPath,
ToUTF8::FromType encoding) ToUTF8::FromType encoding)
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath, encoding) : Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath, encoding)
{ {

View file

@ -1,8 +1,6 @@
#ifndef CSM_DOC_SAVING_H #ifndef CSM_DOC_SAVING_H
#define CSM_DOC_SAVING_H #define CSM_DOC_SAVING_H
#include <boost/filesystem/path.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "operation.hpp" #include "operation.hpp"
@ -21,7 +19,7 @@ namespace CSMDoc
public: public:
Saving (Document& document, const boost::filesystem::path& projectPath, Saving (Document& document, const std::filesystem::path& projectPath,
ToUTF8::FromType encoding); ToUTF8::FromType encoding);
}; };

View file

@ -1,10 +1,10 @@
#include "savingstages.hpp" #include "savingstages.hpp"
#include <sstream> #include <sstream>
#include <filesystem>
#include <boost/filesystem.hpp>
#include <components/esm3/loaddial.hpp> #include <components/esm3/loaddial.hpp>
#include <components/files/conversion.hpp>
#include "../world/infocollection.hpp" #include "../world/infocollection.hpp"
#include "../world/cellcoordinates.hpp" #include "../world/cellcoordinates.hpp"
@ -67,14 +67,14 @@ void CSMDoc::WriteHeaderStage::perform (int stage, Messages& messages)
mDocument.getData().count (CSMWorld::RecordBase::State_Deleted)); mDocument.getData().count (CSMWorld::RecordBase::State_Deleted));
/// \todo refine dependency list (at least remove redundant dependencies) /// \todo refine dependency list (at least remove redundant dependencies)
std::vector<boost::filesystem::path> dependencies = mDocument.getContentFiles(); std::vector<std::filesystem::path> dependencies = mDocument.getContentFiles();
std::vector<boost::filesystem::path>::const_iterator end (--dependencies.end()); std::vector<std::filesystem::path>::const_iterator end (--dependencies.end());
for (std::vector<boost::filesystem::path>::const_iterator iter (dependencies.begin()); for (std::vector<std::filesystem::path>::const_iterator iter (dependencies.begin());
iter!=end; ++iter) iter!=end; ++iter)
{ {
std::string name = iter->filename().string(); auto name = Files::pathToUnicodeString(iter->filename());
uint64_t size = boost::filesystem::file_size (*iter); auto size = std::filesystem::file_size (*iter);
mState.getWriter().addMaster (name, size); mState.getWriter().addMaster (name, size);
} }
@ -519,15 +519,15 @@ void CSMDoc::FinalSavingStage::perform (int stage, Messages& messages)
mState.getWriter().close(); mState.getWriter().close();
mState.getStream().close(); mState.getStream().close();
if (boost::filesystem::exists (mState.getTmpPath())) if (std::filesystem::exists (mState.getTmpPath()))
boost::filesystem::remove (mState.getTmpPath()); std::filesystem::remove (mState.getTmpPath());
} }
else if (!mState.isProjectFile()) else if (!mState.isProjectFile())
{ {
if (boost::filesystem::exists (mState.getPath())) if (std::filesystem::exists (mState.getPath()))
boost::filesystem::remove (mState.getPath()); std::filesystem::remove (mState.getPath());
boost::filesystem::rename (mState.getTmpPath(), mState.getPath()); std::filesystem::rename (mState.getTmpPath(), mState.getPath());
mDocument.getUndoStack().setClean(); mDocument.getUndoStack().setClean();
} }

View file

@ -1,13 +1,14 @@
#include "savingstate.hpp" #include "savingstate.hpp"
#include <boost/filesystem/fstream.hpp> #include <filesystem>
#include <utility>
#include "operation.hpp" #include "operation.hpp"
#include "document.hpp" #include "document.hpp"
CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath, CSMDoc::SavingState::SavingState (Operation& operation, std::filesystem::path projectPath,
ToUTF8::FromType encoding) ToUTF8::FromType encoding)
: mOperation (operation), mEncoder (encoding), mProjectPath (projectPath), mProjectFile (false) : mOperation (operation), mEncoder (encoding), mProjectPath (std::move(projectPath)), mProjectFile (false)
{ {
mWriter.setEncoder (&mEncoder); mWriter.setEncoder (&mEncoder);
} }
@ -33,24 +34,24 @@ void CSMDoc::SavingState::start (Document& document, bool project)
else else
mPath = document.getSavePath(); mPath = document.getSavePath();
boost::filesystem::path file (mPath.filename().string() + ".tmp"); std::filesystem::path file (mPath.filename().u8string() + u8".tmp");
mTmpPath = mPath.parent_path(); mTmpPath = mPath.parent_path();
mTmpPath /= file; mTmpPath /= file;
} }
const boost::filesystem::path& CSMDoc::SavingState::getPath() const const std::filesystem::path& CSMDoc::SavingState::getPath() const
{ {
return mPath; return mPath;
} }
const boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const const std::filesystem::path& CSMDoc::SavingState::getTmpPath() const
{ {
return mTmpPath; return mTmpPath;
} }
boost::filesystem::ofstream& CSMDoc::SavingState::getStream() std::ofstream& CSMDoc::SavingState::getStream()
{ {
return mStream; return mStream;
} }

View file

@ -4,9 +4,7 @@
#include <fstream> #include <fstream>
#include <map> #include <map>
#include <deque> #include <deque>
#include <filesystem>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <components/esm3/esmwriter.hpp> #include <components/esm3/esmwriter.hpp>
@ -20,18 +18,18 @@ namespace CSMDoc
class SavingState class SavingState
{ {
Operation& mOperation; Operation& mOperation;
boost::filesystem::path mPath; std::filesystem::path mPath;
boost::filesystem::path mTmpPath; std::filesystem::path mTmpPath;
ToUTF8::Utf8Encoder mEncoder; ToUTF8::Utf8Encoder mEncoder;
boost::filesystem::ofstream mStream; std::ofstream mStream;
ESM::ESMWriter mWriter; ESM::ESMWriter mWriter;
boost::filesystem::path mProjectPath; std::filesystem::path mProjectPath;
bool mProjectFile; bool mProjectFile;
std::map<std::string, std::deque<int> > mSubRecords; // record ID, list of subrecords std::map<std::string, std::deque<int> > mSubRecords; // record ID, list of subrecords
public: public:
SavingState (Operation& operation, const boost::filesystem::path& projectPath, SavingState (Operation& operation, std::filesystem::path projectPath,
ToUTF8::FromType encoding); ToUTF8::FromType encoding);
bool hasError() const; bool hasError() const;
@ -39,11 +37,11 @@ namespace CSMDoc
void start (Document& document, bool project); void start (Document& document, bool project);
///< \param project Save project file instead of content file. ///< \param project Save project file instead of content file.
const boost::filesystem::path& getPath() const; const std::filesystem::path& getPath() const;
const boost::filesystem::path& getTmpPath() const; const std::filesystem::path& getTmpPath() const;
boost::filesystem::ofstream& getStream(); std::ofstream& getStream();
ESM::ESMWriter& getWriter(); ESM::ESMWriter& getWriter();

View file

@ -648,8 +648,7 @@ CSMPrefs::State::~State()
void CSMPrefs::State::save() void CSMPrefs::State::save()
{ {
boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile; Settings::Manager::saveUser (mConfigurationManager.getUserConfigPath() / mConfigFile);
Settings::Manager::saveUser (user.string());
} }
CSMPrefs::State::Iterator CSMPrefs::State::begin() CSMPrefs::State::Iterator CSMPrefs::State::begin()

View file

@ -40,11 +40,11 @@ void CSMTools::FinishMergedDocumentStage::perform (int stage, CSMDoc::Messages&
// We know that the content file list contains at least two entries and that the first one // We know that the content file list contains at least two entries and that the first one
// does exist on disc (otherwise it would have been impossible to initiate a merge on that // does exist on disc (otherwise it would have been impossible to initiate a merge on that
// document). // document).
boost::filesystem::path path = mState.mSource.getContentFiles()[0]; std::filesystem::path path = mState.mSource.getContentFiles()[0];
ESM::ESMReader reader; ESM::ESMReader reader;
reader.setEncoder (&mEncoder); reader.setEncoder (&mEncoder);
reader.open (path.string()); reader.open (path);
CSMWorld::MetaData source; CSMWorld::MetaData source;
source.mId = "sys::meta"; source.mId = "sys::meta";

View file

@ -8,8 +8,6 @@
#include <QObject> #include <QObject>
#include <boost/filesystem/path.hpp>
#include "../doc/operationholder.hpp" #include "../doc/operationholder.hpp"
namespace CSMWorld namespace CSMWorld

View file

@ -95,7 +95,9 @@ void CSMWorld::CommandDispatcher::setEditLock (bool locked)
void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection) void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection)
{ {
mSelection = selection; mSelection = selection;
std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCaseInPlace); for (auto& sel : mSelection) {
Misc::StringUtils::lowerCaseInPlace(sel);
}
std::sort (mSelection.begin(), mSelection.end()); std::sort (mSelection.begin(), mSelection.end());
} }

View file

@ -64,7 +64,7 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths, CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const boost::filesystem::path& resDir) const std::vector<std::string>& archives, const std::filesystem::path& resDir)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mReader (nullptr), mDialogue (nullptr), mReaderIndex(1), mReader (nullptr), mDialogue (nullptr), mReaderIndex(1),
mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives) mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)
@ -88,7 +88,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
defines[define.first] = define.second; defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string()); mResourceSystem->getSceneManager()->setShaderPath(resDir / "shaders");
int index = 0; int index = 0;
@ -958,7 +958,7 @@ void CSMWorld::Data::merge()
mGlobals.merge(); mGlobals.merge();
} }
int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>& files) int CSMWorld::Data::getTotalRecords (const std::vector<std::filesystem::path>& files)
{ {
int records = 0; int records = 0;
@ -966,10 +966,10 @@ int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>&
for (unsigned int i = 0; i < files.size(); ++i) for (unsigned int i = 0; i < files.size(); ++i)
{ {
if (!boost::filesystem::exists(files[i])) if (!std::filesystem::exists(files[i]))
continue; continue;
reader->open(files[i].string()); reader->open(files[i]);
records += reader->getRecordCount(); records += reader->getRecordCount();
reader->close(); reader->close();
} }
@ -977,7 +977,7 @@ int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>&
return records; return records;
} }
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project) int CSMWorld::Data::startLoading (const std::filesystem::path& path, bool base, bool project)
{ {
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
std::shared_ptr<ESM::ESMReader> ptr(mReader); std::shared_ptr<ESM::ESMReader> ptr(mReader);
@ -989,9 +989,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = new ESM::ESMReader; mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder); mReader->setEncoder (&mEncoder);
mReader->setIndex((project || !base) ? 0 : mReaderIndex++); mReader->setIndex((project || !base) ? 0 : mReaderIndex++);
mReader->open (path.string()); mReader->open (path);
mContentFileNames.insert(std::make_pair(path.filename().string(), mReader->getIndex()));
mBase = base; mBase = base;
mProject = project; mProject = project;

View file

@ -4,8 +4,6 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include <boost/filesystem/path.hpp>
#include <QObject> #include <QObject>
#include <QModelIndex> #include <QModelIndex>
@ -128,8 +126,6 @@ namespace CSMWorld
std::vector<std::shared_ptr<ESM::ESMReader> > mReaders; std::vector<std::shared_ptr<ESM::ESMReader> > mReaders;
std::map<std::string, int> mContentFileNames;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
Data& operator= (const Data&); Data& operator= (const Data&);
@ -148,7 +144,7 @@ namespace CSMWorld
public: public:
Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths, Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const boost::filesystem::path& resDir); const std::vector<std::string>& archives, const std::filesystem::path& resDir);
~Data() override; ~Data() override;
@ -290,9 +286,9 @@ namespace CSMWorld
void merge(); void merge();
///< Merge modified into base. ///< Merge modified into base.
int getTotalRecords (const std::vector<boost::filesystem::path>& files); // for better loading bar int getTotalRecords (const std::vector<std::filesystem::path>& files); // for better loading bar
int startLoading (const boost::filesystem::path& path, bool base, bool project); int startLoading (const std::filesystem::path& path, bool base, bool project);
///< Begin merging content of a file into base or modified. ///< Begin merging content of a file into base or modified.
/// ///
/// \param project load project file instead of content file /// \param project load project file instead of content file

View file

@ -169,7 +169,9 @@ void CSMWorld::RegionMap::updateRegions (const std::vector<std::string>& regions
{ {
std::vector<std::string> regions2 (regions); std::vector<std::string> regions2 (regions);
std::for_each (regions2.begin(), regions2.end(), Misc::StringUtils::lowerCaseInPlace); for (auto& region2 : regions2) {
Misc::StringUtils::lowerCaseInPlace(region2);
}
std::sort (regions2.begin(), regions2.end()); std::sort (regions2.begin(), regions2.end());
for (std::map<CellCoordinates, CellDescription>::const_iterator iter (mMap.begin()); for (std::map<CellCoordinates, CellDescription>::const_iterator iter (mMap.begin());

View file

@ -94,7 +94,9 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const
{ {
mIds = mData.getIds(); mIds = mData.getIds();
std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCaseInPlace); for (auto& id : mIds) {
Misc::StringUtils::lowerCaseInPlace(id);
}
std::sort (mIds.begin(), mIds.end()); std::sort (mIds.begin(), mIds.end());
mIdsUpdated = true; mIdsUpdated = true;

View file

@ -1,13 +1,14 @@
#include "adjusterwidget.hpp" #include "adjusterwidget.hpp"
#include <filesystem>
#include <components/misc/strings/lower.hpp>
#include <boost/filesystem.hpp>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QStyle> #include <QStyle>
#include <components/files/qtconversion.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/misc/strings/lower.hpp>
CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent)
: QWidget (parent), mValid (false), mAction (ContentAction_Undefined) : QWidget (parent), mValid (false), mAction (ContentAction_Undefined)
{ {
@ -33,12 +34,12 @@ void CSVDoc::AdjusterWidget::setAction (ContentAction action)
mAction = action; mAction = action;
} }
void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) void CSVDoc::AdjusterWidget::setLocalData (const std::filesystem::path& localData)
{ {
mLocalData = localData; mLocalData = localData;
} }
boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const std::filesystem::path CSVDoc::AdjusterWidget::getPath() const
{ {
if (!mValid) if (!mValid)
throw std::logic_error ("invalid content file path"); throw std::logic_error ("invalid content file path");
@ -69,14 +70,14 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
} }
else else
{ {
boost::filesystem::path path (name.toUtf8().data()); auto path = Files::pathFromQString(name);
std::string extension = Misc::StringUtils::lowerCase(path.extension().string()); const auto extension = Misc::StringUtils::lowerCase(path.extension().u8string());
bool isLegacyPath = (extension == ".esm" || bool isLegacyPath = (extension == u8".esm" ||
extension == ".esp"); extension == u8".esp");
bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); bool isFilePathChanged = (path.parent_path() != mLocalData);
if (isLegacyPath) if (isLegacyPath)
path.replace_extension (addon ? ".omwaddon" : ".omwgame"); path.replace_extension (addon ? ".omwaddon" : ".omwgame");
@ -86,7 +87,7 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
if (!isFilePathChanged && !isLegacyPath) if (!isFilePathChanged && !isLegacyPath)
{ {
// path already points to the local data directory // path already points to the local data directory
message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); message = "Will be saved as: " + Files::pathToQString(path);
mResultPath = path; mResultPath = path;
} }
//in all other cases, ensure the path points to data-local and do an existing file check //in all other cases, ensure the path points to data-local and do an existing file check
@ -96,10 +97,10 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
if (isFilePathChanged) if (isFilePathChanged)
path = mLocalData / path.filename(); path = mLocalData / path.filename();
message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); message = "Will be saved as: " + Files::pathToQString(path);
mResultPath = path; mResultPath = path;
if (boost::filesystem::exists (path)) if (std::filesystem::exists (path))
{ {
/// \todo add an user setting to make this an error. /// \todo add an user setting to make this an error.
message += "<p>A file with the same name already exists. If you continue, it will be overwritten."; message += "<p>A file with the same name already exists. If you continue, it will be overwritten.";

View file

@ -1,10 +1,11 @@
#ifndef CSV_DOC_ADJUSTERWIDGET_H #ifndef CSV_DOC_ADJUSTERWIDGET_H
#define CSV_DOC_ADJUSTERWIDGET_H #define CSV_DOC_ADJUSTERWIDGET_H
#include <boost/filesystem/path.hpp>
#include <QWidget> #include <QWidget>
#include <filesystem>
class QLabel; class QLabel;
namespace CSVDoc namespace CSVDoc
@ -22,11 +23,11 @@ namespace CSVDoc
public: public:
boost::filesystem::path mLocalData; std::filesystem::path mLocalData;
QLabel *mMessage; QLabel *mMessage;
QLabel *mIcon; QLabel *mIcon;
bool mValid; bool mValid;
boost::filesystem::path mResultPath; std::filesystem::path mResultPath;
ContentAction mAction; ContentAction mAction;
bool mDoFilenameCheck; bool mDoFilenameCheck;
@ -34,13 +35,13 @@ namespace CSVDoc
AdjusterWidget (QWidget *parent = nullptr); AdjusterWidget (QWidget *parent = nullptr);
void setLocalData (const boost::filesystem::path& localData); void setLocalData (const std::filesystem::path& localData);
void setAction (ContentAction action); void setAction (ContentAction action);
void setFilenameCheck (bool doCheck); void setFilenameCheck (bool doCheck);
bool isValid() const; bool isValid() const;
boost::filesystem::path getPath() const; std::filesystem::path getPath() const;
///< This function must not be called if there is no valid path. ///< This function must not be called if there is no valid path.
public slots: public slots:

View file

@ -20,7 +20,7 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) :
mAdjusterWidget = new AdjusterWidget (this); mAdjusterWidget = new AdjusterWidget (this);
} }
void CSVDoc::FileDialog::addFiles(const std::vector<boost::filesystem::path>& dataDirs) void CSVDoc::FileDialog::addFiles(const std::vector<std::filesystem::path>& dataDirs)
{ {
for (auto iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter) for (auto iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter)
{ {
@ -50,7 +50,7 @@ QStringList CSVDoc::FileDialog::selectedFilePaths()
return filePaths; return filePaths;
} }
void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) void CSVDoc::FileDialog::setLocalData (const std::filesystem::path& localData)
{ {
mAdjusterWidget->setLocalData (localData); mAdjusterWidget->setLocalData (localData);
} }

View file

@ -6,12 +6,11 @@
#ifndef Q_MOC_RUN #ifndef Q_MOC_RUN
#include <boost/filesystem/path.hpp>
#include "adjusterwidget.hpp" #include "adjusterwidget.hpp"
#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED #ifndef CS_QT_STD_FILESYSTEM_PATH_DECLARED
#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED #define CS_QT_STD_FILESYSTEM_PATH_DECLARED
Q_DECLARE_METATYPE (boost::filesystem::path) Q_DECLARE_METATYPE (std::filesystem::path)
#endif #endif
#endif #endif
@ -45,14 +44,14 @@ namespace CSVDoc
explicit FileDialog(QWidget *parent = nullptr); explicit FileDialog(QWidget *parent = nullptr);
void showDialog (ContentAction action); void showDialog (ContentAction action);
void addFiles(const std::vector<boost::filesystem::path>& dataDirs); void addFiles(const std::vector<std::filesystem::path>& dataDirs);
void setEncoding (const QString &encoding); void setEncoding (const QString &encoding);
void clearFiles (); void clearFiles ();
QString filename() const; QString filename() const;
QStringList selectedFilePaths(); QStringList selectedFilePaths();
void setLocalData (const boost::filesystem::path& localData); void setLocalData (const std::filesystem::path& localData);
private: private:
@ -61,8 +60,8 @@ namespace CSVDoc
signals: signals:
void signalOpenFiles (const boost::filesystem::path &path); void signalOpenFiles (const std::filesystem::path &path);
void signalCreateNewFile (const boost::filesystem::path &path); void signalCreateNewFile (const std::filesystem::path &path);
void signalUpdateAcceptButton (bool, int); void signalUpdateAcceptButton (bool, int);

View file

@ -8,6 +8,9 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QListWidget> #include <QListWidget>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event) void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
@ -19,7 +22,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
: mDocument (document), mTotalRecordsLabel (0), mRecordsLabel (0), mAborted (false), mMessages (nullptr), mRecords(0) : mDocument (document), mTotalRecordsLabel (0), mRecordsLabel (0), mAborted (false), mMessages (nullptr), mRecords(0)
{ {
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str())); setWindowTitle ("Opening " + Files::pathToQString(document->getSavePath().filename()));
setMinimumWidth (400); setMinimumWidth (400);
@ -89,16 +92,16 @@ void CSVDoc::LoadingDocument::nextStage (const std::string& name, int fileRecord
mRecords = fileRecords; mRecords = fileRecords;
} }
void CSVDoc::LoadingDocument::nextRecord (int records) void CSVDoc::LoadingDocument::nextRecord(int records)
{ {
if (records <= mRecords) if (records <= mRecords)
{ {
mTotalProgress->setValue (mTotalRecords+records); mTotalProgress->setValue(mTotalRecords + records);
mRecordProgress->setValue(records); mRecordProgress->setValue(records);
mRecordsLabel->setText(QString::fromStdString( mRecordsLabel->setText(
"Records: "+std::to_string(records)+" of "+std::to_string(mRecords))); "Records: " + QString::number(records) + " of " + QString::number(mRecords));
} }
} }

View file

@ -50,7 +50,7 @@ CSVDoc::NewGameDialogue::NewGameDialogue()
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
} }
void CSVDoc::NewGameDialogue::setLocalData (const boost::filesystem::path& localData) void CSVDoc::NewGameDialogue::setLocalData (const std::filesystem::path& localData)
{ {
mAdjusterWidget->setLocalData (localData); mAdjusterWidget->setLocalData (localData);
} }

View file

@ -1,14 +1,14 @@
#ifndef CSV_DOC_NEWGAME_H #ifndef CSV_DOC_NEWGAME_H
#define CSV_DOC_NEWGAME_H #define CSV_DOC_NEWGAME_H
#include <boost/filesystem/path.hpp>
#include <QDialog> #include <QDialog>
#include <QMetaType> #include <QMetaType>
#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED #include <filesystem>
#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
Q_DECLARE_METATYPE (boost::filesystem::path) #ifndef CS_QT_STD_FILESYSTEM_PATH_DECLARED
#define CS_QT_STD_FILESYSTEM_PATH_DECLARED
Q_DECLARE_METATYPE (std::filesystem::path)
#endif #endif
class QPushButton; class QPushButton;
@ -30,11 +30,11 @@ namespace CSVDoc
NewGameDialogue(); NewGameDialogue();
void setLocalData (const boost::filesystem::path& localData); void setLocalData (const std::filesystem::path& localData);
signals: signals:
void createRequest (const boost::filesystem::path& file); void createRequest (const std::filesystem::path& file);
void cancelCreateGame (); void cancelCreateGame ();

View file

@ -30,6 +30,7 @@
#include <components/misc/helpviewer.hpp> #include <components/misc/helpviewer.hpp>
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/files/conversion.hpp>
#include "viewmanager.hpp" #include "viewmanager.hpp"
#include "operations.hpp" #include "operations.hpp"
@ -387,7 +388,7 @@ void CSVDoc::View::updateTitle()
{ {
std::ostringstream stream; std::ostringstream stream;
stream << mDocument->getSavePath().filename().string(); stream << Files::pathToUnicodeString(mDocument->getSavePath().filename());
if (mDocument->getState() & CSMDoc::State_Modified) if (mDocument->getState() & CSMDoc::State_Modified)
stream << " *"; stream << " *";
@ -747,7 +748,7 @@ void CSVDoc::View::tutorial()
void CSVDoc::View::infoAbout() void CSVDoc::View::infoAbout()
{ {
// Get current OpenMW version // Get current OpenMW version
QString versionInfo = (Version::getOpenmwVersionDescription(mDocument->getResourceDir().string())+ QString versionInfo = (Version::getOpenmwVersionDescription(mDocument->getResourceDir())+
#if defined(__x86_64__) || defined(_M_X64) #if defined(__x86_64__) || defined(_M_X64)
" (64-bit)").c_str(); " (64-bit)").c_str();
#else #else

View file

@ -7,8 +7,10 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include "../../model/doc/documentmanager.hpp" #include <components/files/qtconversion.hpp>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/doc/documentmanager.hpp"
#include "../../model/doc/state.hpp" #include "../../model/doc/state.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
@ -261,7 +263,7 @@ bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view)
QMessageBox messageBox(view); QMessageBox messageBox(view);
CSMDoc::Document *document = view->getDocument(); CSMDoc::Document *document = view->getDocument();
messageBox.setWindowTitle (QString::fromUtf8(document->getSavePath().filename().string().c_str())); messageBox.setWindowTitle (Files::pathToQString(document->getSavePath().filename()));
messageBox.setText ("The document has been modified."); messageBox.setText ("The document has been modified.");
messageBox.setInformativeText ("Do you want to save your changes?"); messageBox.setInformativeText ("Do you want to save your changes?");
messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);

View file

@ -1,13 +1,16 @@
#include "merge.hpp" #include "merge.hpp"
#include <QVBoxLayout>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QSplitter>
#include <QPushButton>
#include <QListWidget>
#include <QLabel>
#include <QKeyEvent> #include <QKeyEvent>
#include <QLabel>
#include <QListWidget>
#include <QPushButton>
#include <QSplitter>
#include <QVBoxLayout>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/doc/documentmanager.hpp" #include "../../model/doc/documentmanager.hpp"
@ -98,14 +101,14 @@ void CSVTools::Merge::configure (CSMDoc::Document *document)
while (mFiles->count()) while (mFiles->count())
delete mFiles->takeItem (0); delete mFiles->takeItem (0);
std::vector<boost::filesystem::path> files = document->getContentFiles(); std::vector<std::filesystem::path> files = document->getContentFiles();
for (std::vector<boost::filesystem::path>::const_iterator iter (files.begin()); for (std::vector<std::filesystem::path>::const_iterator iter (files.begin());
iter!=files.end(); ++iter) iter!=files.end(); ++iter)
mFiles->addItem (QString::fromUtf8 (iter->filename().string().c_str())); mFiles->addItem (Files::pathToQString(iter->filename()));
} }
void CSVTools::Merge::setLocalData (const boost::filesystem::path& localData) void CSVTools::Merge::setLocalData (const std::filesystem::path& localData)
{ {
mAdjuster->setLocalData (localData); mAdjuster->setLocalData (localData);
} }
@ -125,7 +128,7 @@ void CSVTools::Merge::accept()
{ {
if ((mDocument->getState() & CSMDoc::State_Merging)==0) if ((mDocument->getState() & CSMDoc::State_Merging)==0)
{ {
std::vector< boost::filesystem::path > files (1, mAdjuster->getPath()); std::vector< std::filesystem::path > files { mAdjuster->getPath() };
std::unique_ptr<CSMDoc::Document> target ( std::unique_ptr<CSMDoc::Document> target (
mDocumentManager.makeDocument (files, files[0], true)); mDocumentManager.makeDocument (files, files[0], true));

View file

@ -3,9 +3,7 @@
#include <QWidget> #include <QWidget>
#ifndef Q_MOC_RUN #include <filesystem>
#include <boost/filesystem/path.hpp>
#endif
class QPushButton; class QPushButton;
class QListWidget; class QListWidget;
@ -44,7 +42,7 @@ namespace CSVTools
/// Configure dialogue for a new merge /// Configure dialogue for a new merge
void configure (CSMDoc::Document *document); void configure (CSMDoc::Document *document);
void setLocalData (const boost::filesystem::path& localData); void setLocalData (const std::filesystem::path& localData);
CSMDoc::Document *getDocument() const; CSMDoc::Document *getDocument() const;

View file

@ -143,9 +143,6 @@ target_link_libraries(openmw
${OSGDB_LIBRARIES} ${OSGDB_LIBRARIES}
${OSGUTIL_LIBRARIES} ${OSGUTIL_LIBRARIES}
${OSG_LIBRARIES} ${OSG_LIBRARIES}
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
${OPENAL_LIBRARY} ${OPENAL_LIBRARY}
${FFmpeg_LIBRARIES} ${FFmpeg_LIBRARIES}

View file

@ -1,11 +1,7 @@
#include "engine.hpp" #include "engine.hpp"
#include <iomanip>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <filesystem>
#include <boost/filesystem/fstream.hpp>
#include <osgViewer/ViewerEventHandlers> #include <osgViewer/ViewerEventHandlers>
#include <osgDB/WriteFile> #include <osgDB/WriteFile>
@ -532,7 +528,7 @@ void OMW::Engine::enableFSStrict(bool fsStrict)
void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs)
{ {
mDataDirs = dataDirs; mDataDirs = dataDirs;
mDataDirs.insert(mDataDirs.begin(), (mResDir / "vfs")); mDataDirs.insert(mDataDirs.begin(), mResDir / "vfs");
mFileCollections = Files::Collections (mDataDirs, !mFSStrict); mFileCollections = Files::Collections (mDataDirs, !mFSStrict);
} }
@ -542,7 +538,7 @@ void OMW::Engine::addArchive (const std::string& archive) {
} }
// Set resource dir // Set resource dir
void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir) void OMW::Engine::setResourceDir (const std::filesystem::path& parResDir)
{ {
mResDir = parResDir; mResDir = parResDir;
} }
@ -712,7 +708,7 @@ void OMW::Engine::createWindow()
void OMW::Engine::setWindowIcon() void OMW::Engine::setWindowIcon()
{ {
std::ifstream windowIconStream; std::ifstream windowIconStream;
std::string windowIcon = (mResDir / "openmw.png").string(); const auto windowIcon = mResDir / "openmw.png";
windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary); windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary);
if (windowIconStream.fail()) if (windowIconStream.fail())
Log(Debug::Error) << "Error: Failed to open " << windowIcon; Log(Debug::Error) << "Error: Failed to open " << windowIcon;
@ -769,7 +765,7 @@ void OMW::Engine::prepareEngine()
mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation( mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation(
mWorkQueue, mWorkQueue,
new SceneUtil::WriteScreenshotToFileOperation( new SceneUtil::WriteScreenshotToFileOperation(
mCfgMgr.getScreenshotPath().string(), mCfgMgr.getScreenshotPath(),
Settings::Manager::getString("screenshot format", "General"), Settings::Manager::getString("screenshot format", "General"),
Settings::Manager::getBool("notify on saved screenshot", "General") Settings::Manager::getBool("notify on saved screenshot", "General")
? std::function<void (std::string)>(ScheduleNonDialogMessageBox {}) ? std::function<void (std::string)>(ScheduleNonDialogMessageBox {})
@ -781,35 +777,34 @@ void OMW::Engine::prepareEngine()
mViewer->addEventHandler(mScreenCaptureHandler); mViewer->addEventHandler(mScreenCaptureHandler);
mLuaManager = std::make_unique<MWLua::LuaManager>(mVFS.get(), (mResDir / "lua_libs").string()); mLuaManager = std::make_unique<MWLua::LuaManager>(mVFS.get(), mResDir / "lua_libs");
mEnvironment.setLuaManager(*mLuaManager); mEnvironment.setLuaManager(*mLuaManager);
// Create input and UI first to set up a bootstrapping environment for // Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so // showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v3.xml").string(); const auto keybinderUser = mCfgMgr.getUserConfigPath() / "input_v3.xml";
bool keybinderUserExists = std::filesystem::exists(keybinderUser); bool keybinderUserExists = std::filesystem::exists(keybinderUser);
if(!keybinderUserExists) if(!keybinderUserExists)
{ {
std::string input2 = (mCfgMgr.getUserConfigPath() / "input_v2.xml").string(); const auto input2 = (mCfgMgr.getUserConfigPath() / "input_v2.xml");
if(std::filesystem::exists(input2)) { if(std::filesystem::exists(input2)) {
std::filesystem::copy_file(input2, keybinderUser); keybinderUserExists = std::filesystem::copy_file(input2, keybinderUser);
keybinderUserExists = std::filesystem::exists(keybinderUser);
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser; Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
} }
} }
else else
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser; Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
const std::string userdefault = mCfgMgr.getUserConfigPath().string() + "/gamecontrollerdb.txt"; const auto userdefault = mCfgMgr.getUserConfigPath() / "gamecontrollerdb.txt";
const std::string localdefault = mCfgMgr.getLocalPath().string() + "/gamecontrollerdb.txt"; const auto localdefault = mCfgMgr.getLocalPath() / "gamecontrollerdb.txt";
const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/gamecontrollerdb.txt"; const auto globaldefault = mCfgMgr.getGlobalPath() / "gamecontrollerdb.txt";
std::string userGameControllerdb; std::filesystem::path userGameControllerdb;
if (std::filesystem::exists(userdefault)) if (std::filesystem::exists(userdefault))
userGameControllerdb = userdefault; userGameControllerdb = userdefault;
std::string gameControllerdb; std::filesystem::path gameControllerdb;
if (std::filesystem::exists(localdefault)) if (std::filesystem::exists(localdefault))
gameControllerdb = localdefault; gameControllerdb = localdefault;
else if (std::filesystem::exists(globaldefault)) else if (std::filesystem::exists(globaldefault))
@ -817,7 +812,7 @@ void OMW::Engine::prepareEngine()
//else if it doesn't exist, pass in an empty string //else if it doesn't exist, pass in an empty string
// gui needs our shaders path before everything else // gui needs our shaders path before everything else
mResourceSystem->getSceneManager()->setShaderPath((mResDir / "shaders").string()); mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders");
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false); osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f); bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
@ -835,9 +830,9 @@ void OMW::Engine::prepareEngine()
rootNode->addChild(guiRoot); rootNode->addChild(guiRoot);
mWindowManager = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mWindowManager = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getLogPath(),
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mScriptConsoleMode, mTranslationDataStorage, mEncoding,
Version::getOpenmwVersionDescription(mResDir.string()), shadersSupported); Version::getOpenmwVersionDescription(mResDir), shadersSupported);
mEnvironment.setWindowManager(*mWindowManager); mEnvironment.setWindowManager(*mWindowManager);
mInputManager = std::make_unique<MWInput::InputManager>(mWindow, mViewer, mScreenCaptureHandler, mInputManager = std::make_unique<MWInput::InputManager>(mWindow, mViewer, mScreenCaptureHandler,
@ -858,7 +853,7 @@ void OMW::Engine::prepareEngine()
// Create the world // Create the world
mWorld = std::make_unique<MWWorld::World>(mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(), *mUnrefQueue, mWorld = std::make_unique<MWWorld::World>(mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(), *mUnrefQueue,
mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder.get(), mActivationDistanceOverride, mCellName, mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder.get(), mActivationDistanceOverride, mCellName,
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()); mStartupScript, mResDir, mCfgMgr.getUserDataPath());
mWorld->setupPlayer(); mWorld->setupPlayer();
mWorld->setRandomSeed(mRandomSeed); mWorld->setRandomSeed(mRandomSeed);
mEnvironment.setWorld(*mWorld); mEnvironment.setWorld(*mWorld);
@ -869,8 +864,8 @@ void OMW::Engine::prepareEngine()
//Load translation data //Load translation data
mTranslationDataStorage.setEncoder(mEncoder.get()); mTranslationDataStorage.setEncoder(mEncoder.get());
for (size_t i = 0; i < mContentFiles.size(); i++) for (auto & mContentFile : mContentFiles)
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFile);
Compiler::registerExtensions (mExtensions); Compiler::registerExtensions (mExtensions);
@ -914,7 +909,7 @@ void OMW::Engine::prepareEngine()
} }
mLuaManager->init(); mLuaManager->init();
mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath().string()); mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath());
} }
class OMW::Engine::LuaWorker class OMW::Engine::LuaWorker
@ -1018,7 +1013,7 @@ void OMW::Engine::go()
Misc::Rng::init(mRandomSeed); Misc::Rng::init(mRandomSeed);
Settings::ShaderManager::get().load((mCfgMgr.getUserConfigPath() / "shaders.yaml").string()); Settings::ShaderManager::get().load(mCfgMgr.getUserConfigPath() / "shaders.yaml");
MWClass::registerClasses(); MWClass::registerClasses();
@ -1036,8 +1031,18 @@ void OMW::Engine::go()
prepareEngine(); prepareEngine();
#ifdef _WIN32
const auto* stats_file = _wgetenv(L"OPENMW_OSG_STATS_FILE");
#else
const auto* stats_file = std::getenv("OPENMW_OSG_STATS_FILE");
#endif
std::filesystem::path path;
if (stats_file != nullptr)
path = stats_file;
std::ofstream stats; std::ofstream stats;
if (const auto path = std::getenv("OPENMW_OSG_STATS_FILE")) if (!path.empty())
{ {
stats.open(path, std::ios_base::out); stats.open(path, std::ios_base::out);
if (stats.is_open()) if (stats.is_open())
@ -1140,9 +1145,9 @@ void OMW::Engine::go()
luaWorker.join(); luaWorker.join();
// Save user settings // Save user settings
Settings::Manager::saveUser((mCfgMgr.getUserConfigPath() / "settings.cfg").string()); Settings::Manager::saveUser(mCfgMgr.getUserConfigPath() / "settings.cfg");
Settings::ShaderManager::get().save(); Settings::ShaderManager::get().save();
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath().string()); mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath());
Log(Debug::Info) << "Quitting peacefully."; Log(Debug::Info) << "Quitting peacefully.";
} }
@ -1197,7 +1202,7 @@ void OMW::Engine::setScriptBlacklistUse (bool use)
mScriptBlacklistUse = use; mScriptBlacklistUse = use;
} }
void OMW::Engine::setSaveGameFile(const std::string &savegame) void OMW::Engine::setSaveGameFile(const std::filesystem::path &savegame)
{ {
mSaveGameFile = savegame; mSaveGameFile = savegame;
} }

View file

@ -1,6 +1,8 @@
#ifndef ENGINE_H #ifndef ENGINE_H
#define ENGINE_H #define ENGINE_H
#include <filesystem>
#include <components/compiler/extensions.hpp> #include <components/compiler/extensions.hpp>
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
@ -135,7 +137,7 @@ namespace OMW
std::unique_ptr<ToUTF8::Utf8Encoder> mEncoder; std::unique_ptr<ToUTF8::Utf8Encoder> mEncoder;
Files::PathContainer mDataDirs; Files::PathContainer mDataDirs;
std::vector<std::string> mArchives; std::vector<std::string> mArchives;
boost::filesystem::path mResDir; std::filesystem::path mResDir;
osg::ref_ptr<osgViewer::Viewer> mViewer; osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler; osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osg::ref_ptr<SceneUtil::AsyncScreenCaptureOperation> mScreenCaptureOperation; osg::ref_ptr<SceneUtil::AsyncScreenCaptureOperation> mScreenCaptureOperation;
@ -156,7 +158,7 @@ namespace OMW
bool mScriptConsoleMode; bool mScriptConsoleMode;
std::string mStartupScript; std::string mStartupScript;
int mActivationDistanceOverride; int mActivationDistanceOverride;
std::string mSaveGameFile; std::filesystem::path mSaveGameFile;
// Grab mouse? // Grab mouse?
bool mGrab; bool mGrab;
@ -203,7 +205,7 @@ namespace OMW
void addArchive(const std::string& archive); void addArchive(const std::string& archive);
/// Set resource dir /// Set resource dir
void setResourceDir(const boost::filesystem::path& parResDir); void setResourceDir(const std::filesystem::path& parResDir);
/// Set start cell name /// Set start cell name
void setCell(const std::string& cellName); void setCell(const std::string& cellName);
@ -254,7 +256,7 @@ namespace OMW
void setScriptBlacklistUse (bool use); void setScriptBlacklistUse (bool use);
/// Set the save game file to load after initialising the engine. /// Set the save game file to load after initialising the engine.
void setSaveGameFile(const std::string& savegame); void setSaveGameFile(const std::filesystem::path &savegame);
void setRandomSeed(unsigned int seed); void setRandomSeed(unsigned int seed);

View file

@ -61,7 +61,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
{ {
cfgMgr.readConfiguration(variables, desc, true); cfgMgr.readConfiguration(variables, desc, true);
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::MaybeQuotedPath>().string()); Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::MaybeQuotedPath>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
getRawStdout() << v.describe() << std::endl; getRawStdout() << v.describe() << std::endl;
return false; return false;
} }
@ -69,10 +69,10 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
cfgMgr.readConfiguration(variables, desc); cfgMgr.readConfiguration(variables, desc);
Settings::Manager::load(cfgMgr); Settings::Manager::load(cfgMgr);
setupLogging(cfgMgr.getLogPath().string(), "OpenMW"); setupLogging(cfgMgr.getLogPath(), "OpenMW");
MWGui::DebugWindow::startLogRecording(); MWGui::DebugWindow::startLogRecording();
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::MaybeQuotedPath>().string()); Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::MaybeQuotedPath>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
Log(Debug::Info) << v.describe(); Log(Debug::Info) << v.describe();
engine.setGrabMouse(!variables["no-grab"].as<bool>()); engine.setGrabMouse(!variables["no-grab"].as<bool>());
@ -87,13 +87,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
Files::PathContainer dataDirs(asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>())); Files::PathContainer dataDirs(asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>()));
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>()); Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
if (!local.empty()) if (!local.empty())
dataDirs.push_back(local); dataDirs.push_back(local);
cfgMgr.filterOutNonExistingPaths(dataDirs); cfgMgr.filterOutNonExistingPaths(dataDirs);
engine.setResourceDir(variables["resources"].as<Files::MaybeQuotedPath>()); engine.setResourceDir(variables["resources"].as<Files::MaybeQuotedPath>().u8string()); // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
engine.setDataDirs(dataDirs); engine.setDataDirs(dataDirs);
// fallback archives // fallback archives
@ -150,7 +150,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setWarningsMode (variables["script-warn"].as<int>()); engine.setWarningsMode (variables["script-warn"].as<int>());
engine.setScriptBlacklist (variables["script-blacklist"].as<StringsVector>()); engine.setScriptBlacklist (variables["script-blacklist"].as<StringsVector>());
engine.setScriptBlacklistUse (variables["script-blacklist-use"].as<bool>()); engine.setScriptBlacklistUse (variables["script-blacklist-use"].as<bool>());
engine.setSaveGameFile (variables["load-savegame"].as<Files::MaybeQuotedPath>().string()); engine.setSaveGameFile (variables["load-savegame"].as<Files::MaybeQuotedPath>().u8string());
// other settings // other settings
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap); Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);

View file

@ -62,12 +62,12 @@ namespace MWBase
/// ///
/// \note Slot must belong to the current character. /// \note Slot must belong to the current character.
virtual void loadGame (const std::string& filepath) = 0; virtual void loadGame (const std::filesystem::path &filepath) = 0;
///< Load a saved game directly from the given file path. This will search the CharacterManager ///< Load a saved game directly from the given file path. This will search the CharacterManager
/// for a Character containing this save file, and set this Character current if one was found. /// for a Character containing this save file, and set this Character current if one was found.
/// Otherwise, a new Character will be created. /// Otherwise, a new Character will be created.
virtual void loadGame (const MWState::Character *character, const std::string& filepath) = 0; virtual void loadGame (const MWState::Character *character, const std::filesystem::path &filepath) = 0;
///< Load a saved game file belonging to the given character. ///< Load a saved game file belonging to the given character.
///Simple saver, writes over the file if already existing ///Simple saver, writes over the file if already existing

View file

@ -639,7 +639,7 @@ namespace MWBase
/// Export scene graph to a file and return the filename. /// Export scene graph to a file and return the filename.
/// \param ptr object to export scene graph for (if empty, export entire scene graph) /// \param ptr object to export scene graph for (if empty, export entire scene graph)
virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr) = 0; virtual std::filesystem::path exportSceneGraph(const MWWorld::Ptr& ptr) = 0;
/// Preload VFX associated with this effect list /// Preload VFX associated with this effect list
virtual void preloadEffects(const ESM::EffectList* effectList) = 0; virtual void preloadEffects(const ESM::EffectList* effectList) = 0;

View file

@ -20,6 +20,8 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/files/memorystream.hpp> #include <components/files/memorystream.hpp>
#include <components/misc/timeconvert.hpp>
#include <components/files/conversion.hpp>
#include <components/esm3/loadclas.hpp> #include <components/esm3/loadclas.hpp>
@ -199,7 +201,7 @@ namespace MWGui
if (mCurrentCharacter == &*it || if (mCurrentCharacter == &*it ||
(!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase ( (!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase (
it->begin()->mPath.parent_path().filename().string()))) Files::pathToUnicodeString(it->begin()->mPath.parent_path().filename()))))
{ {
mCurrentCharacter = &*it; mCurrentCharacter = &*it;
selectedIndex = mCharacterSelection->getItemCount()-1; selectedIndex = mCharacterSelection->getItemCount()-1;
@ -302,7 +304,7 @@ namespace MWGui
else else
{ {
assert (mCurrentCharacter && mCurrentSlot); assert (mCurrentCharacter && mCurrentSlot);
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot->mPath.string()); MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot->mPath);
} }
} }
@ -403,7 +405,7 @@ namespace MWGui
throw std::runtime_error("Can't find selected slot"); throw std::runtime_error("Can't find selected slot");
std::stringstream text; std::stringstream text;
time_t time = mCurrentSlot->mTimeStamp; time_t time = Misc::to_time_t(mCurrentSlot->mTimeStamp);
struct tm* timeinfo; struct tm* timeinfo;
timeinfo = localtime(&time); timeinfo = localtime(&time);
@ -438,7 +440,7 @@ namespace MWGui
const std::vector<char>& data = mCurrentSlot->mProfile.mScreenshot; const std::vector<char>& data = mCurrentSlot->mProfile.mScreenshot;
if (!data.size()) if (!data.size())
{ {
Log(Debug::Warning) << "Selected save file '" << mCurrentSlot->mPath.filename().string() << "' has no savegame screenshot"; Log(Debug::Warning) << "Selected save file '" << Files::pathToUnicodeString(mCurrentSlot->mPath.filename()) << "' has no savegame screenshot";
return; return;
} }

View file

@ -120,7 +120,7 @@ namespace MWGui
{ {
WindowManager::WindowManager( WindowManager::WindowManager(
SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders) ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders)
: mOldUpdateMask(0) : mOldUpdateMask(0)
, mOldCullMask(0) , mOldCullMask(0)
@ -180,7 +180,7 @@ namespace MWGui
mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f); mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f);
mGuiPlatform = std::make_unique<osgMyGUI::Platform>(viewer, guiRoot, resourceSystem->getImageManager(), mGuiPlatform = std::make_unique<osgMyGUI::Platform>(viewer, guiRoot, resourceSystem->getImageManager(),
resourceSystem->getVFS(), mScalingFactor, "mygui", resourceSystem->getVFS(), mScalingFactor, "mygui",
(std::filesystem::path(logpath) / "MyGUI.log").generic_string()); logpath / "MyGUI.log");
mGui = std::make_unique<MyGUI::Gui>(); mGui = std::make_unique<MyGUI::Gui>();
mGui->initialise(""); mGui->initialise("");

View file

@ -37,6 +37,7 @@
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_KeyCode.h> #include <MyGUI_KeyCode.h>
#include <MyGUI_Types.h> #include <MyGUI_Types.h>
#include <filesystem>
namespace MyGUI namespace MyGUI
{ {
@ -124,7 +125,7 @@ namespace MWGui
typedef std::vector<Faction> FactionList; typedef std::vector<Faction> FactionList;
WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders); ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders);
virtual ~WindowManager(); virtual ~WindowManager();

View file

@ -6,6 +6,7 @@
#include <extern/oics/ICSInputControlSystem.h> #include <extern/oics/ICSInputControlSystem.h>
#include <components/sdlutil/sdlmappings.hpp> #include <components/sdlutil/sdlmappings.hpp>
#include <components/files/conversion.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
@ -43,8 +44,8 @@ namespace MWInput
class InputControlSystem : public ICS::InputControlSystem class InputControlSystem : public ICS::InputControlSystem
{ {
public: public:
InputControlSystem(const std::string& bindingsFile) InputControlSystem(const std::filesystem::path &bindingsFile)
: ICS::InputControlSystem(bindingsFile, true, nullptr, nullptr, A_Last) : ICS::InputControlSystem(Files::pathToUnicodeString(bindingsFile), true, nullptr, nullptr, A_Last)
{ {
} }
}; };
@ -167,11 +168,11 @@ namespace MWInput
bool mDetectingKeyboard; bool mDetectingKeyboard;
}; };
BindingsManager::BindingsManager(const std::string& userFile, bool userFileExists) BindingsManager::BindingsManager(const std::filesystem::path &userFile, bool userFileExists)
: mUserFile(userFile) : mUserFile(userFile)
, mDragDrop(false) , mDragDrop(false)
{ {
std::string file = userFileExists ? userFile : ""; const auto file = userFileExists ? userFile : std::filesystem::path();
mInputBinder = std::make_unique<InputControlSystem>(file); mInputBinder = std::make_unique<InputControlSystem>(file);
mListener = std::make_unique<BindingsListener>(mInputBinder.get(), this); mListener = std::make_unique<BindingsListener>(mInputBinder.get(), this);
mInputBinder->setDetectingBindingListener(mListener.get()); mInputBinder->setDetectingBindingListener(mListener.get());
@ -192,7 +193,7 @@ namespace MWInput
BindingsManager::~BindingsManager() BindingsManager::~BindingsManager()
{ {
mInputBinder->save(mUserFile); mInputBinder->save(Files::pathToUnicodeString(mUserFile));
} }
void BindingsManager::update(float dt) void BindingsManager::update(float dt)

View file

@ -4,6 +4,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <filesystem>
#include <components/sdlutil/events.hpp> #include <components/sdlutil/events.hpp>
@ -15,7 +16,7 @@ namespace MWInput
class BindingsManager class BindingsManager
{ {
public: public:
BindingsManager(const std::string& userFile, bool userFileExists); BindingsManager(const std::filesystem::path &userFile, bool userFileExists);
virtual ~BindingsManager(); virtual ~BindingsManager();
@ -70,7 +71,7 @@ namespace MWInput
std::unique_ptr<InputControlSystem> mInputBinder; std::unique_ptr<InputControlSystem> mInputBinder;
std::unique_ptr<BindingsListener> mListener; std::unique_ptr<BindingsListener> mListener;
std::string mUserFile; std::filesystem::path mUserFile;
bool mDragDrop; bool mDragDrop;
}; };

View file

@ -7,6 +7,7 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlmappings.hpp> #include <components/sdlutil/sdlmappings.hpp>
#include <components/files/conversion.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
@ -27,8 +28,8 @@ namespace MWInput
ControllerManager::ControllerManager(BindingsManager* bindingsManager, ControllerManager::ControllerManager(BindingsManager* bindingsManager,
ActionManager* actionManager, ActionManager* actionManager,
MouseManager* mouseManager, MouseManager* mouseManager,
const std::string& userControllerBindingsFile, const std::filesystem::path &userControllerBindingsFile,
const std::string& controllerBindingsFile) const std::filesystem::path &controllerBindingsFile)
: mBindingsManager(bindingsManager) : mBindingsManager(bindingsManager)
, mActionManager(actionManager) , mActionManager(actionManager)
, mMouseManager(mouseManager) , mMouseManager(mouseManager)
@ -43,12 +44,12 @@ namespace MWInput
{ {
if (!controllerBindingsFile.empty()) if (!controllerBindingsFile.empty())
{ {
SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str()); SDL_GameControllerAddMappingsFromFile(Files::pathToUnicodeString(controllerBindingsFile).c_str());
} }
if (!userControllerBindingsFile.empty()) if (!userControllerBindingsFile.empty())
{ {
SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str()); SDL_GameControllerAddMappingsFromFile(Files::pathToUnicodeString(userControllerBindingsFile).c_str());
} }
// Open all presently connected sticks // Open all presently connected sticks

View file

@ -5,6 +5,7 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp> #include <components/sdlutil/events.hpp>
#include <filesystem>
namespace MWInput namespace MWInput
{ {
@ -18,8 +19,8 @@ namespace MWInput
ControllerManager(BindingsManager* bindingsManager, ControllerManager(BindingsManager* bindingsManager,
ActionManager* actionManager, ActionManager* actionManager,
MouseManager* mouseManager, MouseManager* mouseManager,
const std::string& userControllerBindingsFile, const std::filesystem::path &userControllerBindingsFile,
const std::string& controllerBindingsFile); const std::filesystem::path &controllerBindingsFile);
virtual ~ControllerManager() = default; virtual ~ControllerManager() = default;

View file

@ -28,8 +28,8 @@ namespace MWInput
osg::ref_ptr<osgViewer::Viewer> viewer, osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler, osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::filesystem::path &userFile, bool userFileExists, const std::filesystem::path &userControllerBindingsFile,
const std::string& controllerBindingsFile, bool grab) const std::filesystem::path &controllerBindingsFile, bool grab)
: mControlsDisabled(false) : mControlsDisabled(false)
, mInputWrapper(std::make_unique<SDLUtil::InputWrapper>(window, viewer, grab)) , mInputWrapper(std::make_unique<SDLUtil::InputWrapper>(window, viewer, grab))
, mBindingsManager(std::make_unique<BindingsManager>(userFile, userFileExists)) , mBindingsManager(std::make_unique<BindingsManager>(userFile, userFileExists))

View file

@ -8,6 +8,7 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp> #include <components/sdlutil/events.hpp>
#include <filesystem>
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
@ -52,9 +53,9 @@ namespace MWInput
osg::ref_ptr<osgViewer::Viewer> viewer, osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler, osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
const std::string& userFile, bool userFileExists, const std::filesystem::path &userFile, bool userFileExists,
const std::string& userControllerBindingsFile, const std::filesystem::path &userControllerBindingsFile,
const std::string& controllerBindingsFile, bool grab); const std::filesystem::path &controllerBindingsFile, bool grab);
~InputManager() final; ~InputManager() final;

View file

@ -31,7 +31,7 @@
namespace MWLua namespace MWLua
{ {
LuaManager::LuaManager(const VFS::Manager* vfs, const std::string& libsDir) LuaManager::LuaManager(const VFS::Manager* vfs, const std::filesystem::path &libsDir)
: mLua(vfs, &mConfiguration) : mLua(vfs, &mConfiguration)
, mUiResourceManager(vfs) , mUiResourceManager(vfs)
, mL10n(vfs, &mLua) , mL10n(vfs, &mLua)
@ -110,21 +110,20 @@ namespace MWLua
return mL10n.translate(contextName, key); return mL10n.translate(contextName, key);
} }
void LuaManager::loadPermanentStorage(const std::string& userConfigPath) void LuaManager::loadPermanentStorage(const std::filesystem::path &userConfigPath)
{ {
auto globalPath = std::filesystem::path(userConfigPath) / "global_storage.bin"; const auto globalPath = userConfigPath / "global_storage.bin";
auto playerPath = std::filesystem::path(userConfigPath) / "player_storage.bin"; const auto playerPath = userConfigPath / "player_storage.bin";
if (std::filesystem::exists(globalPath)) if (std::filesystem::exists(globalPath))
mGlobalStorage.load(globalPath.string()); mGlobalStorage.load(globalPath);
if (std::filesystem::exists(playerPath)) if (std::filesystem::exists(playerPath))
mPlayerStorage.load(playerPath.string()); mPlayerStorage.load(playerPath);
} }
void LuaManager::savePermanentStorage(const std::string& userConfigPath) void LuaManager::savePermanentStorage(const std::filesystem::path &userConfigPath)
{ {
std::filesystem::path confDir(userConfigPath); mGlobalStorage.save(userConfigPath / "global_storage.bin");
mGlobalStorage.save((confDir / "global_storage.bin").string()); mPlayerStorage.save(userConfigPath / "player_storage.bin");
mPlayerStorage.save((confDir / "player_storage.bin").string());
} }
void LuaManager::update() void LuaManager::update()

View file

@ -11,6 +11,7 @@
#include <components/lua_ui/resources.hpp> #include <components/lua_ui/resources.hpp>
#include <components/misc/color.hpp> #include <components/misc/color.hpp>
#include <filesystem>
#include "../mwbase/luamanager.hpp" #include "../mwbase/luamanager.hpp"
@ -26,7 +27,7 @@ namespace MWLua
class LuaManager : public MWBase::LuaManager class LuaManager : public MWBase::LuaManager
{ {
public: public:
LuaManager(const VFS::Manager* vfs, const std::string& libsDir); LuaManager(const VFS::Manager* vfs, const std::filesystem::path &libsDir);
// Called by engine.cpp before UI setup. // Called by engine.cpp before UI setup.
void initL10n(); void initL10n();
@ -34,8 +35,8 @@ namespace MWLua
// Called by engine.cpp when the environment is fully initialized. // Called by engine.cpp when the environment is fully initialized.
void init(); void init();
void loadPermanentStorage(const std::string& userConfigPath); void loadPermanentStorage(const std::filesystem::path &userConfigPath);
void savePermanentStorage(const std::string& userConfigPath); void savePermanentStorage(const std::filesystem::path &userConfigPath);
// Called by engine.cpp every frame. For performance reasons it works in a separate // Called by engine.cpp every frame. For performance reasons it works in a separate
// thread (in parallel with osg Cull). Can not use scene graph. // thread (in parallel with osg Cull). Can not use scene graph.

View file

@ -23,6 +23,7 @@
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/stereo/multiview.hpp> #include <components/stereo/multiview.hpp>
#include <components/stereo/stereomanager.hpp> #include <components/stereo/stereomanager.hpp>
#include <components/files/conversion.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -217,12 +218,12 @@ namespace MWRender
{ {
for (const auto& name : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir)) for (const auto& name : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir))
{ {
std::filesystem::path path = name; std::filesystem::path path = Files::pathFromUnicodeString(name);
std::string fileExt = Misc::StringUtils::lowerCase(path.extension().string()); std::string fileExt = Misc::StringUtils::lowerCase(Files::pathToUnicodeString(path.extension()));
if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt) if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt)
{ {
auto absolutePath = std::filesystem::path(mVFS->getAbsoluteFileName(name)); const auto absolutePath = mVFS->getAbsoluteFileName(path);
mTechniqueFileMap[absolutePath.stem().string()] = absolutePath; mTechniqueFileMap[Files::pathToUnicodeString(absolutePath.stem())] = absolutePath;
} }
} }
} }
@ -387,7 +388,7 @@ namespace MWRender
std::this_thread::sleep_for(std::chrono::milliseconds(5)); std::this_thread::sleep_for(std::chrono::milliseconds(5));
if (technique->compile()) if (technique->compile())
Log(Debug::Info) << "Reloaded technique : " << mTechniqueFileMap[technique->getName()].string(); Log(Debug::Info) << "Reloaded technique : " << mTechniqueFileMap[technique->getName()];
mReload = technique->isValid(); mReload = technique->isValid();
} }

View file

@ -173,6 +173,7 @@ namespace MWRender
stateset->addUniform(new osg::Uniform("far", 0.f)); stateset->addUniform(new osg::Uniform("far", 0.f));
stateset->addUniform(new osg::Uniform("skyBlendingStart", 0.f)); stateset->addUniform(new osg::Uniform("skyBlendingStart", 0.f));
stateset->addUniform(new osg::Uniform("screenRes", osg::Vec2f{})); stateset->addUniform(new osg::Uniform("screenRes", osg::Vec2f{}));
stateset->addUniform(new osg::Uniform("isReflection", false));
if (mUsePlayerUniforms) if (mUsePlayerUniforms)
{ {
stateset->addUniform(new osg::Uniform("windSpeed", 0.0f)); stateset->addUniform(new osg::Uniform("windSpeed", 0.0f));
@ -368,7 +369,7 @@ namespace MWRender
}; };
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& resourcePath, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& resourcePath,
DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore,
SceneUtil::UnrefQueue& unrefQueue) SceneUtil::UnrefQueue& unrefQueue)
: mSkyBlending(Settings::Manager::getBool("sky blending", "Fog")) : mSkyBlending(Settings::Manager::getBool("sky blending", "Fog"))
@ -1525,7 +1526,7 @@ namespace MWRender
updateProjectionMatrix(); updateProjectionMatrix();
} }
} }
void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format) void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::filesystem::path& filename, const std::string &format)
{ {
osg::Node* node = mViewer->getSceneData(); osg::Node* node = mViewer->getSceneData();
if (!ptr.isEmpty()) if (!ptr.isEmpty())

View file

@ -102,7 +102,7 @@ namespace MWRender
{ {
public: public:
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& resourcePath, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& resourcePath,
DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore,
SceneUtil::UnrefQueue& unrefQueue); SceneUtil::UnrefQueue& unrefQueue);
~RenderingManager(); ~RenderingManager();
@ -232,7 +232,7 @@ namespace MWRender
osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& object) const; osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& object) const;
void exportSceneGraph(const MWWorld::Ptr& ptr, const std::string& filename, const std::string& format); void exportSceneGraph(const MWWorld::Ptr& ptr, const std::filesystem::path& filename, const std::string& format);
LandManager* getLandManager() const; LandManager* getLandManager() const;

View file

@ -238,7 +238,7 @@ private:
float mRainIntensity; float mRainIntensity;
}; };
osg::ref_ptr<osg::Image> readPngImage (const std::string& file) osg::ref_ptr<osg::Image> readPngImage (const std::filesystem::path& file)
{ {
std::ifstream inStream; std::ifstream inStream;
inStream.open(file, std::ios_base::in | std::ios_base::binary); inStream.open(file, std::ios_base::in | std::ios_base::binary);
@ -351,6 +351,9 @@ public:
camera->setName("ReflectionCamera"); camera->setName("ReflectionCamera");
camera->addCullCallback(new InheritViewPointCallback); camera->addCullCallback(new InheritViewPointCallback);
// Inform the shader that we're in a reflection
camera->getOrCreateStateSet()->addUniform(new osg::Uniform("isReflection", true));
// XXX: should really flip the FrontFace on each renderable instead of forcing clockwise. // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise.
osg::ref_ptr<osg::FrontFace> frontFace(new osg::FrontFace); osg::ref_ptr<osg::FrontFace> frontFace(new osg::FrontFace);
frontFace->setMode(osg::FrontFace::CLOCKWISE); frontFace->setMode(osg::FrontFace::CLOCKWISE);
@ -441,7 +444,7 @@ public:
}; };
Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem, Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem,
osgUtil::IncrementalCompileOperation *ico, const std::string& resourcePath) osgUtil::IncrementalCompileOperation *ico, const std::filesystem::path& resourcePath)
: mRainIntensityUpdater(nullptr) : mRainIntensityUpdater(nullptr)
, mParent(parent) , mParent(parent)
, mSceneRoot(sceneRoot) , mSceneRoot(sceneRoot)
@ -474,7 +477,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
geom2->setNodeMask(Mask_SimpleWater); geom2->setNodeMask(Mask_SimpleWater);
geom2->setName("Simple Water Geometry"); geom2->setName("Simple Water Geometry");
mWaterNode->addChild(geom2); mWaterNode->addChild(geom2);
mSceneRoot->addChild(mWaterNode); mSceneRoot->addChild(mWaterNode);
setHeight(mTop); setHeight(mTop);
@ -692,7 +695,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT)); osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT));
osg::ref_ptr<osg::Program> program = shaderMgr.getProgram(vertexShader, fragmentShader); osg::ref_ptr<osg::Program> program = shaderMgr.getProgram(vertexShader, fragmentShader);
osg::ref_ptr<osg::Texture2D> normalMap(new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png"))); osg::ref_ptr<osg::Texture2D> normalMap(new osg::Texture2D(readPngImage(mResourcePath / "shaders" / "water_nm.png")));
if (normalMap->getImage()) if (normalMap->getImage())
normalMap->getImage()->flipVertical(); normalMap->getImage()->flipVertical();
@ -701,7 +704,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
normalMap->setMaxAnisotropy(16); normalMap->setMaxAnisotropy(16);
normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
mRainIntensityUpdater = new RainIntensityUpdater(); mRainIntensityUpdater = new RainIntensityUpdater();
node->setUpdateCallback(mRainIntensityUpdater); node->setUpdateCallback(mRainIntensityUpdater);

View file

@ -65,7 +65,7 @@ namespace MWRender
osg::ref_ptr<Refraction> mRefraction; osg::ref_ptr<Refraction> mRefraction;
osg::ref_ptr<Reflection> mReflection; osg::ref_ptr<Reflection> mReflection;
const std::string mResourcePath; const std::filesystem::path mResourcePath;
bool mEnabled; bool mEnabled;
bool mToggled; bool mToggled;
@ -90,7 +90,7 @@ namespace MWRender
public: public:
Water(osg::Group* parent, osg::Group* sceneRoot, Water(osg::Group* parent, osg::Group* sceneRoot,
Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico,
const std::string& resourcePath); const std::filesystem::path& resourcePath);
~Water(); ~Water();
void setCullCallback(osg::Callback* callback); void setCullCallback(osg::Callback* callback);

View file

@ -26,6 +26,7 @@
#include <components/esm3/loaddoor.hpp> #include <components/esm3/loaddoor.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/files/conversion.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -1529,8 +1530,8 @@ namespace MWScript
runtime.getContext().report("Exporting the entire scene graph will result in a large file. Confirm this action using 'showscenegraph 1' or select an object instead."); runtime.getContext().report("Exporting the entire scene graph will result in a large file. Confirm this action using 'showscenegraph 1' or select an object instead.");
else else
{ {
const std::string& filename = MWBase::Environment::get().getWorld()->exportSceneGraph(ptr); const auto filename = MWBase::Environment::get().getWorld()->exportSceneGraph(ptr);
runtime.getContext().report("Wrote '" + filename + "'"); runtime.getContext().report("Wrote '" + Files::pathToUnicodeString(filename) + "'");
} }
} }
}; };

View file

@ -35,7 +35,7 @@ namespace MWScript
mScriptBlacklist.resize (scriptBlacklist.size()); mScriptBlacklist.resize (scriptBlacklist.size());
std::transform (scriptBlacklist.begin(), scriptBlacklist.end(), std::transform (scriptBlacklist.begin(), scriptBlacklist.end(),
mScriptBlacklist.begin(), Misc::StringUtils::lowerCase); mScriptBlacklist.begin(), [](const std::string& s) { return Misc::StringUtils::lowerCase(s); });
std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end()); std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end());
} }

View file

@ -1,13 +1,12 @@
#include "character.hpp" #include "character.hpp"
#include <cctype> #include <cctype>
#include <filesystem>
#include <sstream> #include <sstream>
#include <utility>
#include <boost/filesystem.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/misc/utf8stream.hpp> #include <components/misc/utf8stream.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
@ -27,14 +26,14 @@ std::string MWState::getFirstGameFile(const std::vector<std::string>& contentFil
return ""; return "";
} }
void MWState::Character::addSlot (const boost::filesystem::path& path, const std::string& game) void MWState::Character::addSlot (const std::filesystem::path& path, const std::string& game)
{ {
Slot slot; Slot slot;
slot.mPath = path; slot.mPath = path;
slot.mTimeStamp = boost::filesystem::last_write_time (path); slot.mTimeStamp = std::filesystem::last_write_time (path);
ESM::ESMReader reader; ESM::ESMReader reader;
reader.open (slot.mPath.string()); reader.open (slot.mPath);
if (reader.getRecName()!=ESM::REC_SAVE) if (reader.getRecName()!=ESM::REC_SAVE)
return; // invalid save file -> ignore return; // invalid save file -> ignore
@ -71,35 +70,32 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile)
// Append an index if necessary to ensure a unique file // Append an index if necessary to ensure a unique file
int i=0; int i=0;
while (boost::filesystem::exists(slot.mPath)) while (std::filesystem::exists(slot.mPath))
{ {
const std::string test = stream.str() + " - " + std::to_string(++i); const std::string test = stream.str() + " - " + std::to_string(++i);
slot.mPath = mPath / (test + ext); slot.mPath = mPath / (test + ext);
} }
slot.mProfile = profile; slot.mProfile = profile;
slot.mTimeStamp = std::time (nullptr); slot.mTimeStamp = std::filesystem::file_time_type ();
mSlots.push_back (slot); mSlots.push_back (slot);
} }
MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game) MWState::Character::Character (std::filesystem::path saves, const std::string& game)
: mPath (saves) : mPath (std::move(saves))
{ {
if (!boost::filesystem::is_directory (mPath)) if (!std::filesystem::is_directory (mPath))
{ {
boost::filesystem::create_directories (mPath); std::filesystem::create_directories (mPath);
} }
else else
{ {
for (boost::filesystem::directory_iterator iter (mPath); for (const auto& iter : std::filesystem::directory_iterator (mPath))
iter!=boost::filesystem::directory_iterator(); ++iter)
{ {
boost::filesystem::path slotPath = *iter;
try try
{ {
addSlot (slotPath, game); addSlot (iter, game);
} }
catch (...) {} // ignoring bad saved game files for now catch (...) {} // ignoring bad saved game files for now
} }
@ -110,15 +106,15 @@ MWState::Character::Character (const boost::filesystem::path& saves, const std::
void MWState::Character::cleanup() void MWState::Character::cleanup()
{ {
if (mSlots.size() == 0) if (mSlots.empty())
{ {
// All slots are gone, no need to keep the empty directory // All slots are gone, no need to keep the empty directory
if (boost::filesystem::is_directory (mPath)) if (std::filesystem::is_directory (mPath))
{ {
// Extra safety check to make sure the directory is empty (e.g. slots failed to parse header) // Extra safety check to make sure the directory is empty (e.g. slots failed to parse header)
boost::filesystem::directory_iterator it(mPath); std::filesystem::directory_iterator it(mPath);
if (it == boost::filesystem::directory_iterator()) if (it == std::filesystem::directory_iterator())
boost::filesystem::remove_all(mPath); std::filesystem::remove_all(mPath);
} }
} }
} }
@ -140,7 +136,7 @@ void MWState::Character::deleteSlot (const Slot *slot)
throw std::logic_error ("slot not found"); throw std::logic_error ("slot not found");
} }
boost::filesystem::remove(slot->mPath); std::filesystem::remove(slot->mPath);
mSlots.erase (mSlots.begin()+index); mSlots.erase (mSlots.begin()+index);
} }
@ -157,7 +153,7 @@ const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM
Slot newSlot = *slot; Slot newSlot = *slot;
newSlot.mProfile = profile; newSlot.mProfile = profile;
newSlot.mTimeStamp = std::time (nullptr); newSlot.mTimeStamp = std::filesystem::file_time_type ();
mSlots.erase (mSlots.begin()+index); mSlots.erase (mSlots.begin()+index);
@ -195,7 +191,7 @@ ESM::SavedGame MWState::Character::getSignature() const
return slot.mProfile; return slot.mProfile;
} }
const boost::filesystem::path& MWState::Character::getPath() const const std::filesystem::path& MWState::Character::getPath() const
{ {
return mPath; return mPath;
} }

View file

@ -1,7 +1,7 @@
#ifndef GAME_STATE_CHARACTER_H #ifndef GAME_STATE_CHARACTER_H
#define GAME_STATE_CHARACTER_H #define GAME_STATE_CHARACTER_H
#include <boost/filesystem/path.hpp> #include <filesystem>
#include <components/esm3/savedgame.hpp> #include <components/esm3/savedgame.hpp>
@ -9,9 +9,9 @@ namespace MWState
{ {
struct Slot struct Slot
{ {
boost::filesystem::path mPath; std::filesystem::path mPath;
ESM::SavedGame mProfile; ESM::SavedGame mProfile;
std::time_t mTimeStamp; std::filesystem::file_time_type mTimeStamp;
}; };
bool operator< (const Slot& left, const Slot& right); bool operator< (const Slot& left, const Slot& right);
@ -26,16 +26,16 @@ namespace MWState
private: private:
boost::filesystem::path mPath; std::filesystem::path mPath;
std::vector<Slot> mSlots; std::vector<Slot> mSlots;
void addSlot (const boost::filesystem::path& path, const std::string& game); void addSlot (const std::filesystem::path& path, const std::string& game);
void addSlot (const ESM::SavedGame& profile); void addSlot (const ESM::SavedGame& profile);
public: public:
Character (const boost::filesystem::path& saves, const std::string& game); Character (std::filesystem::path saves, const std::string& game);
void cleanup(); void cleanup();
///< Delete the directory we used, if it is empty ///< Delete the directory we used, if it is empty
@ -60,7 +60,7 @@ namespace MWState
SlotIterator end() const; SlotIterator end() const;
const boost::filesystem::path& getPath() const; const std::filesystem::path& getPath() const;
ESM::SavedGame getSignature() const; ESM::SavedGame getSignature() const;
///< Return signature information for this character. ///< Return signature information for this character.

View file

@ -2,27 +2,27 @@
#include <cctype> #include <cctype>
#include <sstream> #include <sstream>
#include <filesystem>
#include <boost/filesystem.hpp> #include <utility>
#include <components/misc/utf8stream.hpp> #include <components/misc/utf8stream.hpp>
MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves, MWState::CharacterManager::CharacterManager (std::filesystem::path saves,
const std::vector<std::string>& contentFiles) const std::vector<std::string>& contentFiles)
: mPath (saves), mCurrent (nullptr), mGame (getFirstGameFile(contentFiles)) : mPath (std::move(saves)), mCurrent (nullptr), mGame (getFirstGameFile(contentFiles))
{ {
if (!boost::filesystem::is_directory (mPath)) if (!std::filesystem::is_directory (mPath))
{ {
boost::filesystem::create_directories (mPath); std::filesystem::create_directories (mPath);
} }
else else
{ {
for (boost::filesystem::directory_iterator iter (mPath); for (std::filesystem::directory_iterator iter (mPath);
iter!=boost::filesystem::directory_iterator(); ++iter) iter!=std::filesystem::directory_iterator(); ++iter)
{ {
boost::filesystem::path characterDir = *iter; std::filesystem::path characterDir = *iter;
if (boost::filesystem::is_directory (characterDir)) if (std::filesystem::is_directory (characterDir))
{ {
Character character (characterDir, mGame); Character character (characterDir, mGame);
@ -69,11 +69,11 @@ MWState::Character* MWState::CharacterManager::createCharacter(const std::string
stream << '_'; stream << '_';
} }
boost::filesystem::path path = mPath / stream.str(); std::filesystem::path path = mPath / stream.str();
// Append an index if necessary to ensure a unique directory // Append an index if necessary to ensure a unique directory
int i=0; int i=0;
while (boost::filesystem::exists(path)) while (std::filesystem::exists(path))
{ {
std::ostringstream test; std::ostringstream test;
test << stream.str(); test << stream.str();

View file

@ -1,7 +1,8 @@
#ifndef GAME_STATE_CHARACTERMANAGER_H #ifndef GAME_STATE_CHARACTERMANAGER_H
#define GAME_STATE_CHARACTERMANAGER_H #define GAME_STATE_CHARACTERMANAGER_H
#include <boost/filesystem/path.hpp> #include <filesystem>
#include <list>
#include "character.hpp" #include "character.hpp"
@ -9,7 +10,7 @@ namespace MWState
{ {
class CharacterManager class CharacterManager
{ {
boost::filesystem::path mPath; std::filesystem::path mPath;
// Uses std::list, so that mCurrent stays valid when characters are deleted // Uses std::list, so that mCurrent stays valid when characters are deleted
std::list<Character> mCharacters; std::list<Character> mCharacters;
@ -29,7 +30,7 @@ namespace MWState
public: public:
CharacterManager (const boost::filesystem::path& saves, const std::vector<std::string>& contentFiles); CharacterManager (std::filesystem::path saves, const std::vector<std::string>& contentFiles);
Character *getCurrentCharacter (); Character *getCurrentCharacter ();
///< @note May return null ///< @note May return null

View file

@ -13,14 +13,12 @@
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/files/conversion.hpp>
#include <osg/Image> #include <osg/Image>
#include <osgDB/Registry> #include <osgDB/Registry>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
@ -91,7 +89,7 @@ std::map<int, int> MWState::StateManager::buildContentFileIndexMap (const ESM::E
return map; return map;
} }
MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::vector<std::string>& contentFiles) MWState::StateManager::StateManager (const std::filesystem::path& saves, const std::vector<std::string>& contentFiles)
: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, contentFiles), mTimePlayed (0) : mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, contentFiles), mTimePlayed (0)
{ {
@ -301,14 +299,14 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
throw std::runtime_error("Write operation failed (memory stream)"); throw std::runtime_error("Write operation failed (memory stream)");
// All good, write to file // All good, write to file
boost::filesystem::ofstream filestream (slot->mPath, std::ios::binary); std::ofstream filestream (slot->mPath, std::ios::binary);
filestream << stream.rdbuf(); filestream << stream.rdbuf();
if (filestream.fail()) if (filestream.fail())
throw std::runtime_error("Write operation failed (file stream)"); throw std::runtime_error("Write operation failed (file stream)");
Settings::Manager::setString ("character", "Saves", Settings::Manager::setString ("character", "Saves",
slot->mPath.parent_path().filename().string()); Files::pathToUnicodeString(slot->mPath.parent_path().filename()));
const auto finish = std::chrono::steady_clock::now(); const auto finish = std::chrono::steady_clock::now();
@ -327,7 +325,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons);
// If no file was written, clean up the slot // If no file was written, clean up the slot
if (character && slot && !boost::filesystem::exists(slot->mPath)) if (character && slot && !std::filesystem::exists(slot->mPath))
{ {
character->deleteSlot(slot); character->deleteSlot(slot);
character->cleanup(); character->cleanup();
@ -367,15 +365,15 @@ void MWState::StateManager::quickSave (std::string name)
saveGame(name, saveFinder.getNextQuickSaveSlot()); saveGame(name, saveFinder.getNextQuickSaveSlot());
} }
void MWState::StateManager::loadGame(const std::string& filepath) void MWState::StateManager::loadGame(const std::filesystem::path &filepath)
{ {
for (const auto& character : mCharacterManager) for (const auto& character : mCharacterManager)
{ {
for (const auto& slot : character) for (const auto& slot : character)
{ {
if (slot.mPath == boost::filesystem::path(filepath)) if (slot.mPath == filepath)
{ {
loadGame(&character, slot.mPath.string()); loadGame(&character, slot.mPath);
return; return;
} }
} }
@ -385,13 +383,13 @@ void MWState::StateManager::loadGame(const std::string& filepath)
loadGame(character, filepath); loadGame(character, filepath);
} }
void MWState::StateManager::loadGame (const Character *character, const std::string& filepath) void MWState::StateManager::loadGame (const Character *character, const std::filesystem::path &filepath)
{ {
try try
{ {
cleanup(); cleanup();
Log(Debug::Info) << "Reading save file " << std::filesystem::path(filepath).filename().string(); Log(Debug::Info) << "Reading save file " << filepath.filename();
ESM::ESMReader reader; ESM::ESMReader reader;
reader.open (filepath); reader.open (filepath);
@ -524,7 +522,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
if (character) if (character)
Settings::Manager::setString ("character", "Saves", Settings::Manager::setString ("character", "Saves",
character->getPath().filename().string()); Files::pathToUnicodeString(character->getPath().filename()));
MWBase::Environment::get().getWindowManager()->setNewGame(false); MWBase::Environment::get().getWindowManager()->setNewGame(false);
MWBase::Environment::get().getWorld()->saveLoaded(); MWBase::Environment::get().getWorld()->saveLoaded();
@ -596,7 +594,7 @@ void MWState::StateManager::quickLoad()
{ {
if (currentCharacter->begin() == currentCharacter->end()) if (currentCharacter->begin() == currentCharacter->end())
return; return;
loadGame (currentCharacter, currentCharacter->begin()->mPath.string()); //Get newest save loadGame (currentCharacter, currentCharacter->begin()->mPath); //Get newest save
} }
} }
@ -635,7 +633,7 @@ void MWState::StateManager::update (float duration)
//Load last saved game for current character //Load last saved game for current character
MWState::Slot lastSave = *curCharacter->begin(); MWState::Slot lastSave = *curCharacter->begin();
loadGame(curCharacter, lastSave.mPath.string()); loadGame(curCharacter, lastSave.mPath);
} }
else if(iButton==1) else if(iButton==1)
{ {

View file

@ -2,11 +2,10 @@
#define GAME_STATE_STATEMANAGER_H #define GAME_STATE_STATEMANAGER_H
#include <map> #include <map>
#include <filesystem>
#include "../mwbase/statemanager.hpp" #include "../mwbase/statemanager.hpp"
#include <boost/filesystem/path.hpp>
#include "charactermanager.hpp" #include "charactermanager.hpp"
namespace MWState namespace MWState
@ -31,7 +30,7 @@ namespace MWState
public: public:
StateManager (const boost::filesystem::path& saves, const std::vector<std::string>& contentFiles); StateManager (const std::filesystem::path& saves, const std::vector<std::string>& contentFiles);
void requestQuit() override; void requestQuit() override;
@ -67,12 +66,12 @@ namespace MWState
/** Used for quickload **/ /** Used for quickload **/
void quickLoad() override; void quickLoad() override;
void loadGame (const std::string& filepath) override; void loadGame (const std::filesystem::path &filepath) override;
///< Load a saved game directly from the given file path. This will search the CharacterManager ///< Load a saved game directly from the given file path. This will search the CharacterManager
/// for a Character containing this save file, and set this Character current if one was found. /// for a Character containing this save file, and set this Character current if one was found.
/// Otherwise, a new Character will be created. /// Otherwise, a new Character will be created.
void loadGame (const Character *character, const std::string &filepath) override; void loadGame (const Character *character, const std::filesystem::path &filepath) override;
///< Load a saved game file belonging to the given character. ///< Load a saved game file belonging to the given character.
Character *getCurrentCharacter () override; Character *getCurrentCharacter () override;

View file

@ -1,7 +1,7 @@
#ifndef CONTENTLOADER_HPP #ifndef CONTENTLOADER_HPP
#define CONTENTLOADER_HPP #define CONTENTLOADER_HPP
#include <boost/filesystem/path.hpp> #include <filesystem>
namespace Loading namespace Loading
{ {
@ -15,7 +15,7 @@ struct ContentLoader
{ {
virtual ~ContentLoader() = default; virtual ~ContentLoader() = default;
virtual void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) = 0; virtual void load(const std::filesystem::path& filepath, int& index, Loading::Listener* listener) = 0;
}; };
} /* namespace MWWorld */ } /* namespace MWWorld */

View file

@ -3,6 +3,7 @@
#include <components/esm3/esmreader.hpp> #include <components/esm3/esmreader.hpp>
#include <components/esm3/readerscache.hpp> #include <components/esm3/readerscache.hpp>
#include <components/files/conversion.hpp>
namespace MWWorld namespace MWWorld
{ {
@ -16,19 +17,19 @@ EsmLoader::EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF
{ {
} }
void EsmLoader::load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) void EsmLoader::load(const std::filesystem::path& filepath, int& index, Loading::Listener* listener)
{ {
const ESM::ReadersCache::BusyItem reader = mReaders.get(static_cast<std::size_t>(index)); const ESM::ReadersCache::BusyItem reader = mReaders.get(static_cast<std::size_t>(index));
reader->setEncoder(mEncoder); reader->setEncoder(mEncoder);
reader->setIndex(index); reader->setIndex(index);
reader->open(filepath.string()); reader->open(filepath);
reader->resolveParentFileIndices(mReaders); reader->resolveParentFileIndices(mReaders);
assert(reader->getGameFiles().size() == reader->getParentFileIndices().size()); assert(reader->getGameFiles().size() == reader->getParentFileIndices().size());
for (std::size_t i = 0, n = reader->getParentFileIndices().size(); i < n; ++i) for (std::size_t i = 0, n = reader->getParentFileIndices().size(); i < n; ++i)
if (i == static_cast<std::size_t>(reader->getIndex())) if (i == static_cast<std::size_t>(reader->getIndex()))
throw std::runtime_error("File " + reader->getName() + " asks for parent file " throw std::runtime_error("File " + Files::pathToUnicodeString(reader->getName()) + " asks for parent file "
+ reader->getGameFiles()[i].name + reader->getGameFiles()[i].name
+ ", but it is not available or has been loaded in the wrong order. " + ", but it is not available or has been loaded in the wrong order. "
"Please run the launcher to fix this issue."); "Please run the launcher to fix this issue.");
@ -36,8 +37,8 @@ void EsmLoader::load(const boost::filesystem::path& filepath, int& index, Loadin
mESMVersions[index] = reader->getVer(); mESMVersions[index] = reader->getVer();
mStore.load(*reader, listener, mDialogue); mStore.load(*reader, listener, mDialogue);
if (!mMasterFileFormat.has_value() && (Misc::StringUtils::ciEndsWith(reader->getName(), ".esm") if (!mMasterFileFormat.has_value() && (Misc::StringUtils::ciEndsWith(reader->getName().u8string(), u8".esm")
|| Misc::StringUtils::ciEndsWith(reader->getName(), ".omwgame"))) || Misc::StringUtils::ciEndsWith(reader->getName().u8string(), u8".omwgame")))
mMasterFileFormat = reader->getFormat(); mMasterFileFormat = reader->getFormat();
} }

View file

@ -2,6 +2,7 @@
#define ESMLOADER_HPP #define ESMLOADER_HPP
#include <optional> #include <optional>
#include <vector>
#include "contentloader.hpp" #include "contentloader.hpp"
@ -27,7 +28,7 @@ struct EsmLoader : public ContentLoader
std::optional<int> getMasterFileFormat() const { return mMasterFileFormat; } std::optional<int> getMasterFileFormat() const { return mMasterFileFormat; }
void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) override; void load(const std::filesystem::path& filepath, int& index, Loading::Listener* listener) override;
private: private:
ESM::ReadersCache& mReaders; ESM::ReadersCache& mReaders;

View file

@ -321,13 +321,13 @@ ESM::LuaScriptsCfg ESMStore::getLuaScriptsCfg() const
ESM::LuaScriptsCfg cfg; ESM::LuaScriptsCfg cfg;
for (const LuaContent& c : mLuaContent) for (const LuaContent& c : mLuaContent)
{ {
if (std::holds_alternative<std::string>(c)) if (std::holds_alternative<std::filesystem::path>(c))
{ {
// *.omwscripts are intentionally reloaded every time when `getLuaScriptsCfg` is called. // *.omwscripts are intentionally reloaded every time when `getLuaScriptsCfg` is called.
// It is important for the `reloadlua` console command. // It is important for the `reloadlua` console command.
try try
{ {
auto file = std::ifstream(std::get<std::string>(c)); auto file = std::ifstream(std::get<std::filesystem::path>(c));
std::string fileContent(std::istreambuf_iterator<char>(file), {}); std::string fileContent(std::istreambuf_iterator<char>(file), {});
LuaUtil::parseOMWScripts(cfg, fileContent); LuaUtil::parseOMWScripts(cfg, fileContent);
} }

View file

@ -5,6 +5,7 @@
#include <stdexcept> #include <stdexcept>
#include <unordered_map> #include <unordered_map>
#include <tuple> #include <tuple>
#include <filesystem>
#include <components/esm/luascripts.hpp> #include <components/esm/luascripts.hpp>
#include <components/esm3/loadgmst.hpp> #include <components/esm3/loadgmst.hpp>
@ -159,11 +160,11 @@ namespace MWWorld
using LuaContent = std::variant< using LuaContent = std::variant<
ESM::LuaScriptsCfg, // data from an omwaddon ESM::LuaScriptsCfg, // data from an omwaddon
std::string>; // path to an omwscripts file std::filesystem::path>; // path to an omwscripts file
std::vector<LuaContent> mLuaContent; std::vector<LuaContent> mLuaContent;
public: public:
void addOMWScripts(std::string filePath) { mLuaContent.push_back(std::move(filePath)); } void addOMWScripts(std::filesystem::path filePath) { mLuaContent.push_back(std::move(filePath)); }
ESM::LuaScriptsCfg getLuaScriptsCfg() const; ESM::LuaScriptsCfg getLuaScriptsCfg() const;
/// \todo replace with SharedIterator<StoreBase> /// \todo replace with SharedIterator<StoreBase>

View file

@ -17,6 +17,7 @@
#include <components/detournavigator/agentbounds.hpp> #include <components/detournavigator/agentbounds.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
#include <components/detournavigator/heightfieldshape.hpp> #include <components/detournavigator/heightfieldshape.hpp>
#include <components/detournavigator/navigatorimpl.hpp>
#include <components/esm/records.hpp> #include <components/esm/records.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -136,7 +137,7 @@ namespace
} }
void addObject(const MWWorld::Ptr& ptr, const MWWorld::World& world, const MWPhysics::PhysicsSystem& physics, void addObject(const MWWorld::Ptr& ptr, const MWWorld::World& world, const MWPhysics::PhysicsSystem& physics,
DetourNavigator::Navigator& navigator) DetourNavigator::Navigator& navigator, const DetourNavigator::UpdateGuard* navigatorUpdateGuard = nullptr)
{ {
if (const auto object = physics.getObject(ptr)) if (const auto object = physics.getObject(ptr))
{ {
@ -174,7 +175,8 @@ namespace
navigator.addObject( navigator.addObject(
DetourNavigator::ObjectId(object), DetourNavigator::ObjectId(object),
DetourNavigator::DoorShapes(object->getShapeInstance(), objectTransform, connectionStart, connectionEnd), DetourNavigator::DoorShapes(object->getShapeInstance(), objectTransform, connectionStart, connectionEnd),
transform transform,
navigatorUpdateGuard
); );
} }
else if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) else if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None)
@ -182,7 +184,8 @@ namespace
navigator.addObject( navigator.addObject(
DetourNavigator::ObjectId(object), DetourNavigator::ObjectId(object),
DetourNavigator::ObjectShapes(object->getShapeInstance(), objectTransform), DetourNavigator::ObjectShapes(object->getShapeInstance(), objectTransform),
object->getTransform() object->getTransform(),
navigatorUpdateGuard
); );
} }
} }
@ -321,7 +324,7 @@ namespace MWWorld
preloadCells(duration); preloadCells(duration);
} }
void Scene::unloadCell(CellStore* cell) void Scene::unloadCell(CellStore* cell, const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
{ {
if (mActiveCells.find(cell) == mActiveCells.end()) if (mActiveCells.find(cell) == mActiveCells.end())
return; return;
@ -336,7 +339,7 @@ namespace MWWorld
if (const auto object = mPhysics->getObject(ptr)) if (const auto object = mPhysics->getObject(ptr))
{ {
if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None)
mNavigator.removeObject(DetourNavigator::ObjectId(object)); mNavigator.removeObject(DetourNavigator::ObjectId(object), navigatorUpdateGuard);
mPhysics->remove(ptr); mPhysics->remove(ptr);
ptr.mRef->mData.mPhysicsPostponed = false; ptr.mRef->mData.mPhysicsPostponed = false;
} }
@ -355,13 +358,13 @@ namespace MWWorld
if (cell->getCell()->isExterior()) if (cell->getCell()->isExterior())
{ {
if (mPhysics->getHeightField(cellX, cellY) != nullptr) if (mPhysics->getHeightField(cellX, cellY) != nullptr)
mNavigator.removeHeightfield(osg::Vec2i(cellX, cellY)); mNavigator.removeHeightfield(osg::Vec2i(cellX, cellY), navigatorUpdateGuard);
mPhysics->removeHeightField(cellX, cellY); mPhysics->removeHeightField(cellX, cellY);
} }
if (cell->getCell()->hasWater()) if (cell->getCell()->hasWater())
mNavigator.removeWater(osg::Vec2i(cellX, cellY)); mNavigator.removeWater(osg::Vec2i(cellX, cellY), navigatorUpdateGuard);
if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(*cell->getCell())) if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
mNavigator.removePathgrid(*pathgrid); mNavigator.removePathgrid(*pathgrid);
@ -380,7 +383,8 @@ namespace MWWorld
mRendering.notifyWorldSpaceChanged(); mRendering.notifyWorldSpaceChanged();
} }
void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position) void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
{ {
using DetourNavigator::HeightfieldShape; using DetourNavigator::HeightfieldShape;
@ -429,7 +433,7 @@ namespace MWWorld
return heights; return heights;
} }
} (); } ();
mNavigator.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, shape); mNavigator.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, shape, navigatorUpdateGuard);
} }
} }
@ -443,7 +447,7 @@ namespace MWWorld
if (respawn) if (respawn)
cell->respawn(); cell->respawn();
insertCell(*cell, loadingListener); insertCell(*cell, loadingListener, navigatorUpdateGuard);
mRendering.addCell(cell); mRendering.addCell(cell);
@ -459,18 +463,18 @@ namespace MWWorld
if (cell->getCell()->isExterior()) if (cell->getCell()->isExterior())
{ {
if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, waterLevel); mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, waterLevel,
navigatorUpdateGuard);
} }
else else
{ {
mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(), waterLevel); mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(), waterLevel,
navigatorUpdateGuard);
} }
} }
else else
mPhysics->disableWater(); mPhysics->disableWater();
mNavigator.update(position);
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
mRendering.configureAmbient(cell->getCell()); mRendering.configureAmbient(cell->getCell());
@ -479,11 +483,13 @@ namespace MWWorld
void Scene::clear() void Scene::clear()
{ {
auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); ) for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); )
{ {
auto* cell = *iter++; auto* cell = *iter++;
unloadCell (cell); unloadCell(cell, navigatorUpdateGuard.get());
} }
navigatorUpdateGuard.reset();
assert(mActiveCells.empty()); assert(mActiveCells.empty());
mCurrentCell = nullptr; mCurrentCell = nullptr;
@ -526,6 +532,8 @@ namespace MWWorld
void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent)
{ {
auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
for (auto iter = mActiveCells.begin(); iter != mActiveCells.end(); ) for (auto iter = mActiveCells.begin(); iter != mActiveCells.end(); )
{ {
auto* cell = *iter++; auto* cell = *iter++;
@ -534,14 +542,15 @@ namespace MWWorld
const auto dx = std::abs(playerCellX - cell->getCell()->getGridX()); const auto dx = std::abs(playerCellX - cell->getCell()->getGridX());
const auto dy = std::abs(playerCellY - cell->getCell()->getGridY()); const auto dy = std::abs(playerCellY - cell->getCell()->getGridY());
if (dx > mHalfGridSize || dy > mHalfGridSize) if (dx > mHalfGridSize || dy > mHalfGridSize)
unloadCell(cell); unloadCell(cell, navigatorUpdateGuard.get());
} }
else else
unloadCell (cell); unloadCell(cell, navigatorUpdateGuard.get());
} }
mNavigator.setWorldspace(mWorld.getExterior(playerCellX, playerCellY)->getCell()->mCellId.mWorldspace); mNavigator.setWorldspace(mWorld.getExterior(playerCellX, playerCellY)->getCell()->mCellId.mWorldspace,
mNavigator.updateBounds(pos); navigatorUpdateGuard.get());
mNavigator.updateBounds(pos, navigatorUpdateGuard.get());
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
@ -602,10 +611,14 @@ namespace MWWorld
if (!isCellInCollection(x, y, mActiveCells)) if (!isCellInCollection(x, y, mActiveCells))
{ {
CellStore *cell = mWorld.getExterior(x, y); CellStore *cell = mWorld.getExterior(x, y);
loadCell(cell, loadingListener, changeEvent, pos); loadCell(cell, loadingListener, changeEvent, pos, navigatorUpdateGuard.get());
} }
} }
mNavigator.update(pos, navigatorUpdateGuard.get());
navigatorUpdateGuard.reset();
CellStore* current = mWorld.getExterior(playerCellX, playerCellY); CellStore* current = mWorld.getExterior(playerCellX, playerCellY);
MWBase::Environment::get().getWindowManager()->changeCell(current); MWBase::Environment::get().getWindowManager()->changeCell(current);
@ -654,15 +667,21 @@ namespace MWWorld
MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin(); MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin();
int i = 1; int i = 1;
auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
for (; it != cells.extEnd(); ++it) for (; it != cells.extEnd(); ++it)
{ {
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")..."); loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
CellStore *cell = mWorld.getExterior(it->mData.mX, it->mData.mY); CellStore *cell = mWorld.getExterior(it->mData.mX, it->mData.mY);
mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace); mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace, navigatorUpdateGuard.get());
const osg::Vec3f position = osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits; const osg::Vec3f position = osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits;
mNavigator.updateBounds(position); mNavigator.updateBounds(position, navigatorUpdateGuard.get());
loadCell(cell, nullptr, false, position); loadCell(cell, nullptr, false, position, navigatorUpdateGuard.get());
mNavigator.update(position, navigatorUpdateGuard.get());
navigatorUpdateGuard.reset();
mNavigator.wait(DetourNavigator::WaitConditionType::requiredTilesPresent, nullptr);
navigatorUpdateGuard = mNavigator.makeUpdateGuard();
auto iter = mActiveCells.begin(); auto iter = mActiveCells.begin();
while (iter != mActiveCells.end()) while (iter != mActiveCells.end())
@ -670,7 +689,7 @@ namespace MWWorld
if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() && if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() &&
it->mData.mY == (*iter)->getCell()->getGridY()) it->mData.mY == (*iter)->getCell()->getGridY())
{ {
unloadCell(*iter); unloadCell(*iter, navigatorUpdateGuard.get());
break; break;
} }
@ -702,16 +721,22 @@ namespace MWWorld
int i = 1; int i = 1;
MWWorld::Store<ESM::Cell>::iterator it = cells.intBegin(); MWWorld::Store<ESM::Cell>::iterator it = cells.intBegin();
auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
for (; it != cells.intEnd(); ++it) for (; it != cells.intEnd(); ++it)
{ {
loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")..."); loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")...");
CellStore *cell = mWorld.getInterior(it->mName); CellStore *cell = mWorld.getInterior(it->mName);
mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace); mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace, navigatorUpdateGuard.get());
ESM::Position position; ESM::Position position;
mWorld.findInteriorPosition(it->mName, position); mWorld.findInteriorPosition(it->mName, position);
mNavigator.updateBounds(position.asVec3()); mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get());
loadCell(cell, nullptr, false, position.asVec3()); loadCell(cell, nullptr, false, position.asVec3(), navigatorUpdateGuard.get());
mNavigator.update(position.asVec3(), navigatorUpdateGuard.get());
navigatorUpdateGuard.reset();
mNavigator.wait(DetourNavigator::WaitConditionType::requiredTilesPresent, nullptr);
navigatorUpdateGuard = mNavigator.makeUpdateGuard();
auto iter = mActiveCells.begin(); auto iter = mActiveCells.begin();
while (iter != mActiveCells.end()) while (iter != mActiveCells.end())
@ -720,7 +745,7 @@ namespace MWWorld
if (it->mName == (*iter)->getCell()->mName) if (it->mName == (*iter)->getCell()->mName)
{ {
unloadCell(*iter); unloadCell(*iter, navigatorUpdateGuard.get());
break; break;
} }
@ -839,22 +864,26 @@ namespace MWWorld
Log(Debug::Info) << "Changing to interior"; Log(Debug::Info) << "Changing to interior";
auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
// unload // unload
for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); ) for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); )
{ {
auto* cellToUnload = *iter++; auto* cellToUnload = *iter++;
unloadCell(cellToUnload); unloadCell(cellToUnload, navigatorUpdateGuard.get());
} }
assert(mActiveCells.empty()); assert(mActiveCells.empty());
loadingListener->setProgressRange(cell->count()); loadingListener->setProgressRange(cell->count());
mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace); mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace, navigatorUpdateGuard.get());
mNavigator.updateBounds(position.asVec3()); mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get());
// Load cell. // Load cell.
mPagedRefs.clear(); mPagedRefs.clear();
loadCell(cell, loadingListener, changeEvent, position.asVec3()); loadCell(cell, loadingListener, changeEvent, position.asVec3(), navigatorUpdateGuard.get());
navigatorUpdateGuard.reset();
changePlayerCell(cell, position, adjustPlayerPos); changePlayerCell(cell, position, adjustPlayerPos);
@ -905,12 +934,13 @@ namespace MWWorld
mCellChanged = false; mCellChanged = false;
} }
void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener) void Scene::insertCell(CellStore &cell, Loading::Listener* loadingListener,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
{ {
InsertVisitor insertVisitor(cell, loadingListener); InsertVisitor insertVisitor(cell, loadingListener);
cell.forEach (insertVisitor); cell.forEach (insertVisitor);
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, mPagedRefs, *mPhysics, mRendering); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, mPagedRefs, *mPhysics, mRendering); });
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, *mPhysics, mNavigator); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, *mPhysics, mNavigator, navigatorUpdateGuard); });
} }
void Scene::addObjectToScene (const Ptr& ptr) void Scene::addObjectToScene (const Ptr& ptr)
@ -939,7 +969,7 @@ namespace MWWorld
if (const auto object = mPhysics->getObject(ptr)) if (const auto object = mPhysics->getObject(ptr))
{ {
if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None)
mNavigator.removeObject(DetourNavigator::ObjectId(object)); mNavigator.removeObject(DetourNavigator::ObjectId(object), nullptr);
} }
else if (mPhysics->getActor(ptr)) else if (mPhysics->getActor(ptr))
{ {

View file

@ -38,6 +38,7 @@ namespace Loading
namespace DetourNavigator namespace DetourNavigator
{ {
struct Navigator; struct Navigator;
class UpdateGuard;
} }
namespace MWRender namespace MWRender
@ -110,7 +111,9 @@ namespace MWWorld
std::optional<ChangeCellGridRequest> mChangeCellGridRequest; std::optional<ChangeCellGridRequest> mChangeCellGridRequest;
void insertCell(CellStore &cell, Loading::Listener* loadingListener); void insertCell(CellStore &cell, Loading::Listener* loadingListener,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard);
osg::Vec2i mCurrentGridCenter; osg::Vec2i mCurrentGridCenter;
// Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
@ -128,8 +131,9 @@ namespace MWWorld
osg::Vec4i gridCenterToBounds(const osg::Vec2i &centerCell) const; osg::Vec4i gridCenterToBounds(const osg::Vec2i &centerCell) const;
osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const;
void unloadCell(CellStore* cell); void unloadCell(CellStore* cell, const DetourNavigator::UpdateGuard* navigatorUpdateGuard);
void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position); void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard);
public: public:

View file

@ -43,8 +43,10 @@
#include <components/detournavigator/settings.hpp> #include <components/detournavigator/settings.hpp>
#include <components/detournavigator/agentbounds.hpp> #include <components/detournavigator/agentbounds.hpp>
#include <components/detournavigator/stats.hpp> #include <components/detournavigator/stats.hpp>
#include <components/detournavigator/navigatorimpl.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/files/conversion.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -103,21 +105,21 @@ namespace MWWorld
mLoaders.emplace(std::move(extension), &loader); mLoaders.emplace(std::move(extension), &loader);
} }
void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) override void load(const std::filesystem::path& filepath, int& index, Loading::Listener* listener) override
{ {
const auto it = mLoaders.find(Misc::StringUtils::lowerCase(filepath.extension().string())); const auto it = mLoaders.find(Misc::StringUtils::lowerCase( Files::pathToUnicodeString(filepath.extension())));
if (it != mLoaders.end()) if (it != mLoaders.end())
{ {
const std::string filename = filepath.filename().string(); const auto filename = filepath.filename();
Log(Debug::Info) << "Loading content file " << filename; Log(Debug::Info) << "Loading content file " << filename;
if (listener != nullptr) if (listener != nullptr)
listener->setLabel(MyGUI::TextIterator::toTagsString(filename)); listener->setLabel(MyGUI::TextIterator::toTagsString(Files::pathToUnicodeString(filename)));
it->second->load(filepath, index, listener); it->second->load(filepath, index, listener);
} }
else else
{ {
std::string msg("Cannot load file: "); std::string msg("Cannot load file: ");
msg += filepath.string(); msg += Files::pathToUnicodeString(filepath);
throw std::runtime_error(msg.c_str()); throw std::runtime_error(msg.c_str());
} }
} }
@ -130,9 +132,9 @@ namespace MWWorld
{ {
ESMStore& mStore; ESMStore& mStore;
OMWScriptsLoader(ESMStore& store) : mStore(store) {} OMWScriptsLoader(ESMStore& store) : mStore(store) {}
void load(const boost::filesystem::path& filepath, int& /*index*/, Loading::Listener* /*listener*/) override void load(const std::filesystem::path& filepath, int& /*index*/, Loading::Listener* /*listener*/) override
{ {
mStore.addOMWScripts(filepath.string()); mStore.addOMWScripts(filepath);
} }
}; };
@ -157,7 +159,7 @@ namespace MWWorld
const std::vector<std::string>& groundcoverFiles, const std::vector<std::string>& groundcoverFiles,
ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride, ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride,
const std::string& startCell, const std::string& startupScript, const std::string& startCell, const std::string& startupScript,
const std::string& resourcePath, const std::string& userDataPath) const std::filesystem::path& resourcePath, const std::filesystem::path& userDataPath)
: mResourceSystem(resourceSystem), mLocalScripts(mStore), : mResourceSystem(resourceSystem), mLocalScripts(mStore),
mCells(mStore, mReaders), mSky(true), mCells(mStore, mReaders), mSky(true),
mGodMode(false), mScriptsEnabled(true), mDiscardMovements(true), mContentFiles (contentFiles), mGodMode(false), mScriptsEnabled(true), mDiscardMovements(true), mContentFiles (contentFiles),
@ -1543,28 +1545,32 @@ namespace MWWorld
void World::updateNavigator() void World::updateNavigator()
{ {
auto navigatorUpdateGuard = mNavigator->makeUpdateGuard();
mPhysics->forEachAnimatedObject([&] (const auto& pair) mPhysics->forEachAnimatedObject([&] (const auto& pair)
{ {
const auto [object, changed] = pair; const auto [object, changed] = pair;
if (changed) if (changed)
updateNavigatorObject(*object); updateNavigatorObject(*object, navigatorUpdateGuard.get());
}); });
for (const auto& door : mDoorStates) for (const auto& door : mDoorStates)
if (const auto object = mPhysics->getObject(door.first)) if (const auto object = mPhysics->getObject(door.first))
updateNavigatorObject(*object); updateNavigatorObject(*object, navigatorUpdateGuard.get());
mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3()); mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3(), navigatorUpdateGuard.get());
} }
void World::updateNavigatorObject(const MWPhysics::Object& object) void World::updateNavigatorObject(const MWPhysics::Object& object,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
{ {
if (object.getShapeInstance()->mVisualCollisionType != Resource::VisualCollisionType::None) if (object.getShapeInstance()->mVisualCollisionType != Resource::VisualCollisionType::None)
return; return;
const MWWorld::Ptr ptr = object.getPtr(); const MWWorld::Ptr ptr = object.getPtr();
const DetourNavigator::ObjectShapes shapes(object.getShapeInstance(), const DetourNavigator::ObjectShapes shapes(object.getShapeInstance(),
DetourNavigator::ObjectTransform {ptr.getRefData().getPosition(), ptr.getCellRef().getScale()}); DetourNavigator::ObjectTransform {ptr.getRefData().getPosition(), ptr.getCellRef().getScale()});
mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform()); mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform(),
navigatorUpdateGuard);
} }
const MWPhysics::RayCastingInterface* World::getRayCasting() const const MWPhysics::RayCastingInterface* World::getRayCasting() const
@ -1856,8 +1862,8 @@ namespace MWWorld
if (mWorldScene->hasCellLoaded()) if (mWorldScene->hasCellLoaded())
{ {
mNavigator->wait(*MWBase::Environment::get().getWindowManager()->getLoadingScreen(), mNavigator->wait(DetourNavigator::WaitConditionType::requiredTilesPresent,
DetourNavigator::WaitConditionType::requiredTilesPresent); MWBase::Environment::get().getWindowManager()->getLoadingScreen());
mWorldScene->resetCellLoaded(); mWorldScene->resetCellLoaded();
} }
} }
@ -2960,8 +2966,8 @@ namespace MWWorld
int idx = 0; int idx = 0;
for (const std::string &file : content) for (const std::string &file : content)
{ {
boost::filesystem::path filename(file); const auto filename = Files::pathFromUnicodeString( file);
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string()); const Files::MultiDirCollection& col = fileCollections.getCollection(Files::pathToUnicodeString(filename.extension()));
if (col.doesExist(file)) if (col.doesExist(file))
{ {
gameContentLoader.load(col.getPath(file), idx, listener); gameContentLoader.load(col.getPath(file), idx, listener);
@ -3691,9 +3697,9 @@ namespace MWWorld
return mPhysics->getHalfExtents(object); return mPhysics->getHalfExtents(object);
} }
std::string World::exportSceneGraph(const Ptr &ptr) std::filesystem::path World::exportSceneGraph(const Ptr& ptr)
{ {
std::string file = mUserDataPath + "/openmw.osgt"; auto file = mUserDataPath / "openmw.osgt";
if (!ptr.isEmpty()) if (!ptr.isEmpty())
{ {
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);

View file

@ -109,7 +109,7 @@ namespace MWWorld
bool mDiscardMovements; bool mDiscardMovements;
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
std::string mUserDataPath; std::filesystem::path mUserDataPath;
osg::Vec3f mDefaultHalfExtents; osg::Vec3f mDefaultHalfExtents;
DetourNavigator::CollisionShapeType mDefaultActorCollisionShapeType; DetourNavigator::CollisionShapeType mDefaultActorCollisionShapeType;
@ -166,7 +166,8 @@ namespace MWWorld
void updateNavigator(); void updateNavigator();
void updateNavigatorObject(const MWPhysics::Object& object); void updateNavigatorObject(const MWPhysics::Object& object,
const DetourNavigator::UpdateGuard* navigatorUpdateGuard = nullptr);
void ensureNeededRecords(); void ensureNeededRecords();
@ -202,7 +203,7 @@ namespace MWWorld
const std::vector<std::string>& groundcoverFiles, const std::vector<std::string>& groundcoverFiles,
ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride, ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride,
const std::string& startCell, const std::string& startupScript, const std::string& startCell, const std::string& startupScript,
const std::string& resourcePath, const std::string& userDataPath); const std::filesystem::path& resourcePath, const std::filesystem::path& userDataPath);
virtual ~World(); virtual ~World();
@ -729,7 +730,7 @@ namespace MWWorld
/// Export scene graph to a file and return the filename. /// Export scene graph to a file and return the filename.
/// \param ptr object to export scene graph for (if empty, export entire scene graph) /// \param ptr object to export scene graph for (if empty, export entire scene graph)
std::string exportSceneGraph(const MWWorld::Ptr& ptr) override; std::filesystem::path exportSceneGraph(const MWWorld::Ptr& ptr) override;
/// Preload VFX associated with this effect list /// Preload VFX associated with this effect list
void preloadEffects(const ESM::EffectList* effectList) override; void preloadEffects(const ESM::EffectList* effectList) override;

View file

@ -75,6 +75,7 @@ file(GLOB UNITTEST_SRC_FILES
esmloader/record.cpp esmloader/record.cpp
files/hash.cpp files/hash.cpp
files/conversion_tests.cpp
toutf8/toutf8.cpp toutf8/toutf8.cpp

View file

@ -25,7 +25,7 @@ namespace
void addHeightFieldPlane(TileCachedRecastMeshManager& recastMeshManager, const osg::Vec2i cellPosition = osg::Vec2i(0, 0)) void addHeightFieldPlane(TileCachedRecastMeshManager& recastMeshManager, const osg::Vec2i cellPosition = osg::Vec2i(0, 0))
{ {
const int cellSize = 8192; const int cellSize = 8192;
recastMeshManager.addHeightfield(cellPosition, cellSize, HeightfieldPlane {0}); recastMeshManager.addHeightfield(cellPosition, cellSize, HeightfieldPlane {0}, nullptr);
} }
void addObject(const btBoxShape& shape, TileCachedRecastMeshManager& recastMeshManager) void addObject(const btBoxShape& shape, TileCachedRecastMeshManager& recastMeshManager)
@ -42,7 +42,7 @@ namespace
osg::ref_ptr<Resource::BulletShapeInstance>(new Resource::BulletShapeInstance(bulletShape)), osg::ref_ptr<Resource::BulletShapeInstance>(new Resource::BulletShapeInstance(bulletShape)),
shape, objectTransform shape, objectTransform
); );
recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground); recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground, nullptr);
} }
struct DetourNavigatorAsyncNavMeshUpdaterTest : Test struct DetourNavigatorAsyncNavMeshUpdaterTest : Test
@ -60,43 +60,43 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, for_all_jobs_done_when_empty_wait_should_terminate) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, for_all_jobs_done_when_empty_wait_should_terminate)
{ {
AsyncNavMeshUpdater updater {mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr}; AsyncNavMeshUpdater updater {mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr};
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, for_required_tiles_present_when_empty_wait_should_terminate) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, for_required_tiles_present_when_empty_wait_should_terminate)
{ {
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
updater.wait(mListener, WaitConditionType::requiredTilesPresent); updater.wait(WaitConditionType::requiredTilesPresent, &mListener);
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_generate_navmesh_tile) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_generate_navmesh_tile)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u); EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, repeated_post_should_lead_to_cache_hit) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, repeated_post_should_lead_to_cache_hit)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mGetCount, 1);
ASSERT_EQ(stats.mCache.mHitCount, 0); ASSERT_EQ(stats.mCache.mHitCount, 0);
} }
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
EXPECT_EQ(stats.mCache.mGetCount, 2); EXPECT_EQ(stats.mCache.mGetCount, 2);
@ -106,20 +106,20 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_for_update_change_type_should_not_update_cache) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_for_update_change_type_should_not_update_cache)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::update}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::update}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mGetCount, 1);
ASSERT_EQ(stats.mCache.mHitCount, 0); ASSERT_EQ(stats.mCache.mHitCount, 0);
} }
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
EXPECT_EQ(stats.mCache.mGetCount, 2); EXPECT_EQ(stats.mCache.mGetCount, 2);
@ -129,7 +129,7 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_write_generated_tile_to_db) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_write_generated_tile_to_db)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
addObject(mBox, mRecastMeshManager); addObject(mBox, mRecastMeshManager);
auto db = std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max()); auto db = std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max());
@ -139,7 +139,7 @@ namespace
const TilePosition tilePosition {0, 0}; const TilePosition tilePosition {0, 0};
const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
updater.stop(); updater.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
ASSERT_NE(recastMesh, nullptr); ASSERT_NE(recastMesh, nullptr);
@ -155,7 +155,7 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_tiles) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_tiles)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
addObject(mBox, mRecastMeshManager); addObject(mBox, mRecastMeshManager);
auto db = std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max()); auto db = std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max());
@ -166,7 +166,7 @@ namespace
const TilePosition tilePosition {0, 0}; const TilePosition tilePosition {0, 0};
const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
updater.stop(); updater.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
ASSERT_NE(recastMesh, nullptr); ASSERT_NE(recastMesh, nullptr);
@ -180,7 +180,7 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_shapes) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_shapes)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
addObject(mBox, mRecastMeshManager); addObject(mBox, mRecastMeshManager);
auto db = std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max()); auto db = std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max());
@ -191,7 +191,7 @@ namespace
const TilePosition tilePosition {0, 0}; const TilePosition tilePosition {0, 0};
const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{tilePosition, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
updater.stop(); updater.stop();
const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition);
ASSERT_NE(recastMesh, nullptr); ASSERT_NE(recastMesh, nullptr);
@ -202,7 +202,7 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_read_from_db_on_cache_miss) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_read_from_db_on_cache_miss)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
mSettings.mMaxNavMeshTilesCacheSize = 0; mSettings.mMaxNavMeshTilesCacheSize = 0;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager,
@ -210,7 +210,7 @@ namespace
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTiles {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mGetCount, 1);
@ -220,7 +220,7 @@ namespace
ASSERT_EQ(stats.mDbGetTileHits, 0); ASSERT_EQ(stats.mDbGetTileHits, 0);
} }
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
{ {
const auto stats = updater.getStats(); const auto stats = updater.getStats();
EXPECT_EQ(stats.mCache.mGetCount, 2); EXPECT_EQ(stats.mCache.mGetCount, 2);
@ -233,24 +233,24 @@ namespace
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, on_changing_player_tile_post_should_remove_tiles_out_of_range) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, on_changing_player_tile_post_should_remove_tiles_out_of_range)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager); addHeightFieldPlane(mRecastMeshManager);
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), 1);
const std::map<TilePosition, ChangeType> changedTilesAdd {{TilePosition {0, 0}, ChangeType::add}}; const std::map<TilePosition, ChangeType> changedTilesAdd {{TilePosition {0, 0}, ChangeType::add}};
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTilesAdd); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTilesAdd);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
ASSERT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u); ASSERT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
const std::map<TilePosition, ChangeType> changedTilesRemove {{TilePosition {0, 0}, ChangeType::remove}}; const std::map<TilePosition, ChangeType> changedTilesRemove {{TilePosition {0, 0}, ChangeType::remove}};
const TilePosition playerTile(100, 100); const TilePosition playerTile(100, 100);
updater.post(mAgentBounds, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove); updater.post(mAgentBounds, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u); EXPECT_EQ(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_stop_writing_to_db_when_size_limit_is_reached) TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_stop_writing_to_db_when_size_limit_is_reached)
{ {
mRecastMeshManager.setWorldspace(mWorldspace); mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
for (int x = -1; x <= 1; ++x) for (int x = -1; x <= 1; ++x)
for (int y = -1; y <= 1; ++y) for (int y = -1; y <= 1; ++y)
addHeightFieldPlane(mRecastMeshManager, osg::Vec2i(x, y)); addHeightFieldPlane(mRecastMeshManager, osg::Vec2i(x, y));
@ -264,7 +264,7 @@ namespace
for (int y = -5; y <= 5; ++y) for (int y = -5; y <= 5; ++y)
changedTiles.emplace(TilePosition {x, y}, ChangeType::add); changedTiles.emplace(TilePosition {x, y}, ChangeType::add);
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(mListener, WaitConditionType::allJobsDone); updater.wait(WaitConditionType::allJobsDone, &mListener);
updater.stop(); updater.stop();
const std::set<TilePosition> present { const std::set<TilePosition> present {
TilePosition(-2, 0), TilePosition(-2, 0),

View file

@ -155,9 +155,11 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); auto updateGuard = mNavigator->makeUpdateGuard();
mNavigator->update(mPlayerPosition); mNavigator->addHeightfield(mCellPosition, cellSize, surface, updateGuard.get());
mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); mNavigator->update(mPlayerPosition, updateGuard.get());
updateGuard.reset();
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -207,9 +209,9 @@ namespace
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -239,9 +241,12 @@ namespace
Vec3fEq(460, 56.66666412353515625, 1.99998295307159423828125) Vec3fEq(460, 56.66666412353515625, 1.99998295307159423828125)
)) << mPath; )) << mPath;
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); {
mNavigator->update(mPlayerPosition); auto updateGuard = mNavigator->makeUpdateGuard();
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, updateGuard.get());
mNavigator->update(mPlayerPosition, updateGuard.get());
}
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
@ -291,10 +296,10 @@ namespace
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -327,9 +332,9 @@ namespace
compound.shape().updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0))); compound.shape().updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0)));
mNavigator->updateObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->updateObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mPath.clear(); mPath.clear();
mOut = std::back_inserter(mPath); mOut = std::back_inserter(mPath);
@ -385,10 +390,10 @@ namespace
heightfield2.shape().setLocalScaling(btVector3(128, 128, 1)); heightfield2.shape().setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -442,15 +447,15 @@ namespace
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1); const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1); mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(); const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion();
mNavigator->addHeightfield(mCellPosition, cellSize2, surface2); mNavigator->addHeightfield(mCellPosition, cellSize2, surface2, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version); EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version);
} }
@ -484,9 +489,9 @@ namespace
osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape)); osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addObject(ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform); mNavigator->addObject(ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -531,10 +536,10 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize, 300); mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mStart.x() = 256; mStart.x() = 256;
mStart.z() = 300; mStart.z() = 300;
@ -579,10 +584,10 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize, -25); mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
@ -625,10 +630,10 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addWater(mCellPosition, std::numeric_limits<int>::max(), -25); mNavigator->addWater(mCellPosition, std::numeric_limits<int>::max(), -25, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
@ -671,10 +676,10 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize, -25); mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mStart.x() = 256; mStart.x() = 256;
mEnd.x() = 256; mEnd.x() = 256;
@ -715,17 +720,17 @@ namespace
heightfield.shape().setLocalScaling(btVector3(128, 128, 1)); heightfield.shape().setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mNavigator->removeObject(ObjectId(&heightfield.shape())); mNavigator->removeObject(ObjectId(&heightfield.shape()), nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -769,17 +774,17 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mNavigator->removeHeightfield(mCellPosition); mNavigator->removeHeightfield(mCellPosition, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -824,9 +829,9 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
Misc::Rng::init(42); Misc::Rng::init(42);
@ -862,12 +867,12 @@ namespace
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
for (std::size_t i = 0; i < boxes.size(); ++i) for (std::size_t i = 0; i < boxes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(shift.x() + i * 10, shift.y() + i * 10, i * 10)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(shift.x() + i * 10, shift.y() + i * 10, i * 10));
mNavigator->addObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform); mNavigator->addObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform, nullptr);
} }
std::this_thread::sleep_for(std::chrono::microseconds(1)); std::this_thread::sleep_for(std::chrono::microseconds(1));
@ -875,11 +880,11 @@ namespace
for (std::size_t i = 0; i < boxes.size(); ++i) for (std::size_t i = 0; i < boxes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(shift.x() + i * 10 + 1, shift.y() + i * 10 + 1, i * 10 + 1)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(shift.x() + i * 10 + 1, shift.y() + i * 10 + 1, i * 10 + 1));
mNavigator->updateObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform); mNavigator->updateObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform, nullptr);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -921,27 +926,27 @@ namespace
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32));
mNavigator->addObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform); mNavigator->addObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform, nullptr);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const auto start = std::chrono::steady_clock::now(); const auto start = std::chrono::steady_clock::now();
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1));
mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform); mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform, nullptr);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2));
mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform); mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform, nullptr);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const auto duration = std::chrono::steady_clock::now() - start; const auto duration = std::chrono::steady_clock::now() - start;
@ -962,9 +967,9 @@ namespace
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const osg::Vec3f start(57, 460, 1); const osg::Vec3f start(57, 460, 1);
const osg::Vec3f end(460, 57, 1); const osg::Vec3f end(460, 57, 1);
@ -991,14 +996,14 @@ namespace
CollisionShapeInstance borderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50))); CollisionShapeInstance borderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50)));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform),
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition)); btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition), nullptr);
// add this box to make navmesh bound box independent from oscillatingBoxShape rotations // add this box to make navmesh bound box independent from oscillatingBoxShape rotations
mNavigator->addObject(ObjectId(&borderBox.shape()), ObjectShapes(borderBox.instance(), mObjectTransform), mNavigator->addObject(ObjectId(&borderBox.shape()), ObjectShapes(borderBox.instance(), mObjectTransform),
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)), nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const Version expectedVersion {1, 4}; const Version expectedVersion {1, 4};
@ -1010,9 +1015,9 @@ namespace
{ {
const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10), const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10),
oscillatingBoxShapePosition); oscillatingBoxShapePosition);
mNavigator->updateObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), transform); mNavigator->updateObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), transform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
} }
ASSERT_EQ(navMeshes.size(), 1); ASSERT_EQ(navMeshes.size(), 1);
@ -1025,9 +1030,9 @@ namespace
const int cellSize = mHeightfieldTileSize * 4; const int cellSize = mHeightfieldTileSize * 4;
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, plane); mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::Success); Status::Success);
@ -1075,10 +1080,10 @@ namespace
new btBoxShape(btVector3(200, 200, 1000))); new btBoxShape(btVector3(200, 200, 1000)));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::PartialPath); Status::PartialPath);
@ -1114,10 +1119,10 @@ namespace
new btBoxShape(btVector3(100, 100, 1000))); new btBoxShape(btVector3(100, 100, 1000)));
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addHeightfield(mCellPosition, cellSize, surface); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const float endTolerance = 1000.0f; const float endTolerance = 1000.0f;
@ -1154,15 +1159,15 @@ namespace
const float level2 = 2; const float level2 = 2;
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
mNavigator->addWater(mCellPosition, cellSize1, level1); mNavigator->addWater(mCellPosition, cellSize1, level1, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(); const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion();
mNavigator->addWater(mCellPosition, cellSize2, level2); mNavigator->addWater(mCellPosition, cellSize2, level2, nullptr);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version); EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version);
} }

View file

@ -43,20 +43,12 @@ namespace
EXPECT_EQ(manager.getRevision(), 0); EXPECT_EQ(manager.getRevision(), 0);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, for_each_tile_position_for_empty_should_call_none)
{
TileCachedRecastMeshManager manager(mSettings);
std::size_t calls = 0;
manager.forEachTile([&] (const TilePosition&, const CachedRecastMeshManager&) { ++calls; });
EXPECT_EQ(calls, 0);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_new_object_should_return_true) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_new_object_should_return_true)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false)
@ -64,17 +56,17 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
@ -88,9 +80,9 @@ namespace
TileBounds bounds; TileBounds bounds;
bounds.mMin = osg::Vec2f(182, 182); bounds.mMin = osg::Vec2f(182, 182);
bounds.mMax = osg::Vec2f(1000, 1000); bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds); manager.setBounds(bounds, nullptr);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_THAT(manager.takeChangedTiles(), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::add))); EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::add)));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_add_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_add_changed_tiles)
@ -102,11 +94,11 @@ namespace
TileBounds bounds; TileBounds bounds;
bounds.mMin = osg::Vec2f(-1000, -1000); bounds.mMin = osg::Vec2f(-1000, -1000);
bounds.mMax = osg::Vec2f(1000, 1000); bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds); manager.setBounds(bounds, nullptr);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr);
manager.takeChangedTiles(); manager.takeChangedTiles(nullptr);
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
EXPECT_THAT(manager.takeChangedTiles(), ElementsAre( EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre(
std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(-1, -1), ChangeType::add),
std::pair(TilePosition(-1, 0), ChangeType::add), std::pair(TilePosition(-1, 0), ChangeType::add),
std::pair(TilePosition(0, -1), ChangeType::update), std::pair(TilePosition(0, -1), ChangeType::update),
@ -121,10 +113,10 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
manager.takeChangedTiles(); manager.takeChangedTiles(nullptr);
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
EXPECT_THAT(manager.takeChangedTiles(), IsEmpty()); EXPECT_THAT(manager.takeChangedTiles(nullptr), IsEmpty());
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_return_add_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_return_add_changed_tiles)
@ -135,20 +127,20 @@ namespace
TileBounds bounds; TileBounds bounds;
bounds.mMin = osg::Vec2f(182, 182); bounds.mMin = osg::Vec2f(182, 182);
bounds.mMax = osg::Vec2f(1000, 1000); bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds); manager.setBounds(bounds, nullptr);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
manager.takeChangedTiles(); manager.takeChangedTiles(nullptr);
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape), nullptr);
EXPECT_THAT(manager.takeChangedTiles(), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::remove))); EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::remove)));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
@ -158,10 +150,10 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
} }
@ -171,20 +163,20 @@ namespace
TileBounds bounds; TileBounds bounds;
bounds.mMin = osg::Vec2f(-1000, -1000); bounds.mMin = osg::Vec2f(-1000, -1000);
bounds.mMax = osg::Vec2f(1000, 1000); bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds); manager.setBounds(bounds, nullptr);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
@ -194,17 +186,17 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
} }
@ -212,11 +204,11 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
@ -226,17 +218,17 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
@ -249,7 +241,7 @@ namespace
const auto initialRevision = manager.getRevision(); const auto initialRevision = manager.getRevision();
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getRevision(), initialRevision + 1); EXPECT_EQ(manager.getRevision(), initialRevision + 1);
} }
@ -258,9 +250,9 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
const auto beforeAddRevision = manager.getRevision(); const auto beforeAddRevision = manager.getRevision();
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
EXPECT_EQ(manager.getRevision(), beforeAddRevision); EXPECT_EQ(manager.getRevision(), beforeAddRevision);
} }
@ -270,21 +262,21 @@ namespace
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr);
const auto beforeUpdateRevision = manager.getRevision(); const auto beforeUpdateRevision = manager.getRevision();
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_update_not_changed_object_should_return_same_value) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_update_not_changed_object_should_return_same_value)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
const auto beforeUpdateRevision = manager.getRevision(); const auto beforeUpdateRevision = manager.getRevision();
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);
} }
@ -293,9 +285,9 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
const auto beforeRemoveRevision = manager.getRevision(); const auto beforeRemoveRevision = manager.getRevision();
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape), nullptr);
EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1);
} }
@ -303,7 +295,7 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const auto beforeRemoveRevision = manager.getRevision(); const auto beforeRemoveRevision = manager.getRevision();
manager.removeObject(ObjectId(&manager)); manager.removeObject(ObjectId(&manager), nullptr);
EXPECT_EQ(manager.getRevision(), beforeRemoveRevision); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision);
} }
@ -312,8 +304,8 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
const auto changedTiles = manager.takeChangedTiles(); const auto changedTiles = manager.takeChangedTiles(nullptr);
EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1)); EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1));
EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11)); EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11));
for (const auto& [k, v] : changedTiles) for (const auto& [k, v] : changedTiles)
@ -323,10 +315,10 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
for (int x = -1; x < 12; ++x) for (int x = -1; x < 12; ++x)
for (int y = -1; y < 12; ++y) for (int y = -1; y < 12; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
@ -335,13 +327,13 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = std::numeric_limits<int>::max(); const int cellSize = std::numeric_limits<int>::max();
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
for (int x = -6; x < 6; ++x) for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y) for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0); ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0);
@ -350,8 +342,8 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_not_add_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_not_add_changed_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.removeWater(osg::Vec2i(0, 0)); manager.removeWater(osg::Vec2i(0, 0), nullptr);
EXPECT_THAT(manager.takeChangedTiles(), ElementsAre()); EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre());
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_add_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_add_changed_tiles)
@ -359,10 +351,10 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
manager.takeChangedTiles(); manager.takeChangedTiles(nullptr);
manager.removeWater(cellPosition); manager.removeWater(cellPosition, nullptr);
const auto changedTiles = manager.takeChangedTiles(); const auto changedTiles = manager.takeChangedTiles(nullptr);
EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1)); EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1));
EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11)); EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11));
for (const auto& [k, v] : changedTiles) for (const auto& [k, v] : changedTiles)
@ -372,11 +364,11 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
manager.removeWater(cellPosition); manager.removeWater(cellPosition, nullptr);
for (int x = -6; x < 6; ++x) for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y) for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
@ -385,14 +377,14 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
manager.removeWater(cellPosition); manager.removeWater(cellPosition, nullptr);
for (int x = -6; x < 6; ++x) for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y) for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0); ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0);
@ -401,14 +393,14 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_not_remove_tile_with_water) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_not_remove_tile_with_water)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
manager.addWater(cellPosition, cellSize, 0.0f); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape), nullptr);
for (int x = -1; x < 12; ++x) for (int x = -1; x < 12; ++x)
for (int y = -1; y < 12; ++y) for (int y = -1; y < 12; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
@ -417,11 +409,11 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_new_worldspace_should_remove_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_new_worldspace_should_remove_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace", nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(nullptr, boxShape, mObjectTransform); const CollisionShape shape(nullptr, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
manager.setWorldspace("other"); manager.setWorldspace("other", nullptr);
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr); ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr);
@ -435,13 +427,13 @@ namespace
TileBounds bounds; TileBounds bounds;
bounds.mMin = osg::Vec2f(182, 0); bounds.mMin = osg::Vec2f(182, 0);
bounds.mMax = osg::Vec2f(1000, 1000); bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds); manager.setBounds(bounds, nullptr);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
bounds.mMin = osg::Vec2f(-1000, -1000); bounds.mMin = osg::Vec2f(-1000, -1000);
bounds.mMax = osg::Vec2f(0, -182); bounds.mMax = osg::Vec2f(0, -182);
manager.takeChangedTiles(); manager.takeChangedTiles(nullptr);
manager.setBounds(bounds); manager.setBounds(bounds, nullptr);
EXPECT_THAT(manager.takeChangedTiles(), ElementsAre( EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre(
std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(-1, -1), ChangeType::add),
std::pair(TilePosition(0, 0), ChangeType::remove) std::pair(TilePosition(0, 0), ChangeType::remove)
)); ));

View file

@ -42,7 +42,7 @@ namespace
const Files::PathContainer mDataDirs {{std::string(OPENMW_DATA_DIR)}}; const Files::PathContainer mDataDirs {{std::string(OPENMW_DATA_DIR)}};
const Files::Collections mFileCollections {mDataDirs, true}; const Files::Collections mFileCollections {mDataDirs, true};
const std::string mContentFile = "template.omwgame"; const std::string mContentFile = "template.omwgame";
const std::string mContentFilePath = mFileCollections.getCollection(".omwgame").getPath(mContentFile).string(); const std::filesystem::path mContentFilePath = mFileCollections.getCollection(".omwgame").getPath(mContentFile);
}; };
TEST_F(ESM3ReadersCacheWithContentFile, shouldKeepOpenReleasedOpenReader) TEST_F(ESM3ReadersCacheWithContentFile, shouldKeepOpenReleasedOpenReader)

View file

@ -0,0 +1,25 @@
#include <components/files/conversion.hpp>
#include <components/misc/strings/conversion.hpp>
#include <gtest/gtest.h>
namespace
{
using namespace testing;
using namespace Files;
constexpr auto test_path_u8 = u8"./tmp/ÒĎƎɠˠΏЌԹעڨ/ऊঋਐઊଊ/ஐఋಋഊ/ฎນ༈ႩᄇḮὯ⁂₁₩ℒ/Ⅷ↝∑/☝✌〥ぐズ㌎丕.갔3갛";
constexpr auto test_path = "./tmp/ÒĎƎɠˠΏЌԹעڨ/ऊঋਐઊଊ/ஐఋಋഊ/ฎນ༈ႩᄇḮὯ⁂₁₩ℒ/Ⅷ↝∑/☝✌〥ぐズ㌎丕.갔3갛";
TEST(OpenMWConversion, should_support_unicode_string_to_path)
{
auto p = Files::pathFromUnicodeString(test_path);
EXPECT_EQ(Misc::StringUtils::u8StringToString(p.u8string()), Misc::StringUtils::u8StringToString(test_path_u8));
}
TEST(OpenMWConversion, should_support_path_to_unicode_string)
{
std::filesystem::path p{ test_path_u8 };
EXPECT_EQ(Files::pathToUnicodeString(p), test_path);
}
}

Some files were not shown because too many files have changed in this diff Show more