2022-03-19 23:06:00 +01:00
|
|
|
#include <std_include.hpp>
|
|
|
|
#include "loader/component_loader.hpp"
|
|
|
|
|
|
|
|
#include "filesystem.hpp"
|
2022-07-15 02:02:50 +02:00
|
|
|
#include "console.hpp"
|
2022-08-22 01:42:13 +02:00
|
|
|
#include "mods.hpp"
|
2023-02-15 20:50:40 +01:00
|
|
|
#include "language.hpp"
|
2022-07-15 02:02:50 +02:00
|
|
|
|
|
|
|
#include "game/game.hpp"
|
2022-03-19 23:06:00 +01:00
|
|
|
|
|
|
|
#include <utils/io.hpp>
|
2022-07-15 02:02:50 +02:00
|
|
|
#include <utils/hook.hpp>
|
2022-08-22 01:42:13 +02:00
|
|
|
#include <utils/flags.hpp>
|
2022-08-27 16:16:49 +02:00
|
|
|
#include <utils/properties.hpp>
|
2022-03-19 23:06:00 +01:00
|
|
|
|
|
|
|
namespace filesystem
|
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
namespace
|
2022-03-19 23:06:00 +01:00
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
bool initialized = false;
|
|
|
|
|
2022-07-19 19:42:59 +02:00
|
|
|
std::deque<std::filesystem::path>& get_search_paths_internal()
|
2022-07-15 02:02:50 +02:00
|
|
|
{
|
2022-07-19 19:42:59 +02:00
|
|
|
static std::deque<std::filesystem::path> search_paths{};
|
2022-07-15 02:02:50 +02:00
|
|
|
return search_paths;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fs_startup_stub(const char* name)
|
|
|
|
{
|
|
|
|
console::info("[FS] Startup\n");
|
|
|
|
|
|
|
|
initialized = true;
|
|
|
|
|
2022-08-27 16:16:49 +02:00
|
|
|
filesystem::register_path(utils::properties::get_appdata_path() / CLIENT_DATA_FOLDER);
|
2022-07-15 02:02:50 +02:00
|
|
|
filesystem::register_path(L".");
|
|
|
|
filesystem::register_path(L"h2-mod");
|
|
|
|
|
2022-08-22 01:42:13 +02:00
|
|
|
const auto mod_path = utils::flags::get_flag("mod");
|
|
|
|
if (mod_path.has_value())
|
|
|
|
{
|
2022-11-12 21:32:43 +01:00
|
|
|
mods::set_mod(mod_path.value());
|
2022-08-22 01:42:13 +02:00
|
|
|
}
|
|
|
|
|
2022-07-15 02:02:50 +02:00
|
|
|
utils::hook::invoke<void>(0x14060BF50, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::filesystem::path> get_paths(const std::filesystem::path& path)
|
|
|
|
{
|
|
|
|
std::vector<std::filesystem::path> paths{};
|
|
|
|
|
2022-07-31 23:59:01 +02:00
|
|
|
const auto code = game::SEH_GetCurrentLanguageName();
|
2022-07-15 02:02:50 +02:00
|
|
|
|
|
|
|
paths.push_back(path);
|
2022-08-22 10:15:45 +03:00
|
|
|
paths.push_back(path / code);
|
|
|
|
|
2022-07-15 02:02:50 +02:00
|
|
|
return paths;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool can_insert_path(const std::filesystem::path& path)
|
|
|
|
{
|
2022-11-06 12:36:59 +00:00
|
|
|
const auto& paths = get_search_paths_internal();
|
|
|
|
return std::ranges::none_of(paths.cbegin(), paths.cend(), [path](const auto& elem)
|
2022-07-15 02:02:50 +02:00
|
|
|
{
|
2022-11-06 12:36:59 +00:00
|
|
|
return elem == path;
|
|
|
|
});
|
2022-07-15 02:02:50 +02:00
|
|
|
}
|
2022-08-07 21:11:17 +02:00
|
|
|
|
|
|
|
const char* sys_default_install_path_stub()
|
|
|
|
{
|
|
|
|
static auto current_path = std::filesystem::current_path().string();
|
|
|
|
return current_path.data();
|
|
|
|
}
|
2023-02-15 18:24:35 +01:00
|
|
|
|
|
|
|
bool is_parent_path(const std::filesystem::path& parent, const std::filesystem::path& child)
|
|
|
|
{
|
|
|
|
std::filesystem::path iter = child;
|
|
|
|
|
|
|
|
while (iter != iter.parent_path())
|
|
|
|
{
|
|
|
|
if (iter == parent)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
iter = iter.parent_path();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2022-03-19 23:06:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string read_file(const std::string& path)
|
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
for (const auto& search_path : get_search_paths_internal())
|
2022-03-19 23:06:00 +01:00
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
const auto path_ = search_path / path;
|
|
|
|
if (utils::io::file_exists(path_.generic_string()))
|
2022-03-19 23:06:00 +01:00
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
return utils::io::read_file(path_.generic_string());
|
2022-03-19 23:06:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-04-28 22:41:29 +02:00
|
|
|
bool read_file(const std::string& path, std::string* data, std::string* real_path)
|
2022-03-19 23:06:00 +01:00
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
for (const auto& search_path : get_search_paths_internal())
|
2022-03-19 23:06:00 +01:00
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
const auto path_ = search_path / path;
|
|
|
|
if (utils::io::read_file(path_.generic_string(), data))
|
2022-03-19 23:06:00 +01:00
|
|
|
{
|
2022-04-28 22:41:29 +02:00
|
|
|
if (real_path != nullptr)
|
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
*real_path = path_.generic_string();
|
2022-04-28 22:41:29 +02:00
|
|
|
}
|
|
|
|
|
2022-03-19 23:06:00 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-22 19:51:26 +02:00
|
|
|
bool find_file(const std::string& path, std::string* real_path)
|
|
|
|
{
|
|
|
|
for (const auto& search_path : get_search_paths_internal())
|
|
|
|
{
|
|
|
|
const auto path_ = search_path / path;
|
|
|
|
if (utils::io::file_exists(path_.generic_string()))
|
|
|
|
{
|
|
|
|
*real_path = path_.generic_string();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-22 01:42:13 +02:00
|
|
|
bool exists(const std::string& path)
|
|
|
|
{
|
|
|
|
for (const auto& search_path : get_search_paths_internal())
|
|
|
|
{
|
|
|
|
const auto path_ = search_path / path;
|
|
|
|
if (utils::io::file_exists(path_.generic_string()))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-15 02:02:50 +02:00
|
|
|
void register_path(const std::filesystem::path& path)
|
|
|
|
{
|
|
|
|
if (!initialized)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto paths = get_paths(path);
|
|
|
|
for (const auto& path_ : paths)
|
|
|
|
{
|
|
|
|
if (can_insert_path(path_))
|
|
|
|
{
|
|
|
|
console::info("[FS] Registering path '%s'\n", path_.generic_string().data());
|
2022-07-19 19:42:59 +02:00
|
|
|
get_search_paths_internal().push_front(path_);
|
2022-07-15 02:02:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void unregister_path(const std::filesystem::path& path)
|
|
|
|
{
|
|
|
|
if (!initialized)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto paths = get_paths(path);
|
|
|
|
for (const auto& path_ : paths)
|
|
|
|
{
|
|
|
|
auto& search_paths = get_search_paths_internal();
|
|
|
|
for (auto i = search_paths.begin(); i != search_paths.end();)
|
|
|
|
{
|
|
|
|
if (*i == path_)
|
|
|
|
{
|
|
|
|
console::info("[FS] Unregistering path '%s'\n", path_.generic_string().data());
|
|
|
|
i = search_paths.erase(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> get_search_paths()
|
|
|
|
{
|
|
|
|
std::vector<std::string> paths{};
|
|
|
|
|
|
|
|
for (const auto& path : get_search_paths_internal())
|
|
|
|
{
|
|
|
|
paths.push_back(path.generic_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
return paths;
|
|
|
|
}
|
|
|
|
|
2022-07-19 19:42:59 +02:00
|
|
|
std::vector<std::string> get_search_paths_rev()
|
|
|
|
{
|
|
|
|
std::vector<std::string> paths{};
|
|
|
|
const auto& search_paths = get_search_paths_internal();
|
|
|
|
|
|
|
|
for (auto i = search_paths.rbegin(); i != search_paths.rend(); ++i)
|
|
|
|
{
|
|
|
|
paths.push_back(i->generic_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
return paths;
|
|
|
|
}
|
|
|
|
|
2023-02-15 18:24:35 +01:00
|
|
|
void check_path(const std::filesystem::path& path)
|
|
|
|
{
|
|
|
|
if (path.generic_string().find("..") != std::string::npos)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("directory traversal is not allowed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string get_safe_path(const std::filesystem::path& path)
|
|
|
|
{
|
|
|
|
check_path(path);
|
|
|
|
const auto absolute = std::filesystem::weakly_canonical(path);
|
|
|
|
|
|
|
|
static std::vector<std::filesystem::path> allowed_directories =
|
|
|
|
{
|
|
|
|
{std::filesystem::weakly_canonical("mods")},
|
|
|
|
{std::filesystem::weakly_canonical("h2-mod")},
|
|
|
|
{std::filesystem::weakly_canonical("players2/default")},
|
|
|
|
};
|
|
|
|
|
|
|
|
auto is_allowed = false;
|
|
|
|
for (const auto& dir : allowed_directories)
|
|
|
|
{
|
|
|
|
if (is_parent_path(dir, absolute))
|
|
|
|
{
|
|
|
|
is_allowed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_allowed)
|
|
|
|
{
|
|
|
|
throw std::runtime_error(std::format("Disallowed access to directory \"{}\"", path.generic_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return path.generic_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool safe_write_file(const std::string& file, const std::string& data, bool append)
|
|
|
|
{
|
|
|
|
const auto path = filesystem::get_safe_path(file);
|
|
|
|
return utils::io::write_file(path, data, append);
|
|
|
|
}
|
|
|
|
|
2022-03-19 23:06:00 +01:00
|
|
|
class component final : public component_interface
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void post_unpack() override
|
|
|
|
{
|
2022-07-15 02:02:50 +02:00
|
|
|
utils::hook::call(0x14060B052, fs_startup_stub);
|
2022-08-07 21:11:17 +02:00
|
|
|
utils::hook::jump(0x140624050, sys_default_install_path_stub);
|
2022-03-19 23:06:00 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-08-22 10:15:45 +03:00
|
|
|
REGISTER_COMPONENT(filesystem::component)
|