#include #include "loader/component_loader.hpp" #include "component/console.hpp" #include "component/logfile.hpp" #include "component/scheduler.hpp" #include "component/gsc/script_extension.hpp" #include "game/dvars.hpp" #include "game/game.hpp" #include "game/scripting/execution.hpp" #include #include #include #include namespace io { namespace { bool allow_root_io = false; 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 convert_path(const std::filesystem::path& path) { check_path(path); if (allow_root_io) { static const auto fs_base_game = game::Dvar_FindVar("fs_basepath"); const std::filesystem::path fs_base_game_path(fs_base_game->current.string); return (fs_base_game_path / path).generic_string(); } static const auto fs_game = game::Dvar_FindVar("fs_game"); if (fs_game->current.string && fs_game->current.string != ""s) { const std::filesystem::path fs_game_path(fs_game->current.string); return (fs_game_path / path).generic_string(); } throw std::runtime_error("fs_game is not properly defined"); } void replace(std::string& str, const std::string& from, const std::string& to) { const auto start_pos = str.find(from); if (start_pos == std::string::npos) { return; } str.replace(start_pos, from.length(), to); } } class component final : public component_interface { public: void post_unpack() override { allow_root_io = utils::flags::has_flag("allow_root_io"); if (allow_root_io) { console::warn("GSC has access to your game folder. Remove the '-allow_root_io' launch parameter to disable this feature."); } gsc::function::add("fileexists", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return utils::io::file_exists(path); }); gsc::function::add("writefile", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); const auto data = args[1].as(); auto append = false; if (args.size() > 2u) { append = args[2].as(); } return utils::io::write_file(path, data, append); }); gsc::function::add("readfile", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return utils::io::read_file(path); }); gsc::function::add("filesize", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return static_cast(utils::io::file_size(path)); }); gsc::function::add("createdirectory", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return utils::io::create_directory(path); }); gsc::function::add("directoryexists", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return utils::io::directory_exists(path); }); gsc::function::add("directoryisempty", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return utils::io::directory_is_empty(path); }); gsc::function::add("listfiles", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); const auto files = utils::io::list_files(path); scripting::array array{}; for (const auto& file : files) { array.push(file); } return array; }); gsc::function::add("copyfolder", [](const gsc::function_args& args) { const auto source = convert_path(args[0].as()); const auto target = convert_path(args[1].as()); utils::io::copy_folder(source, target); return scripting::script_value{}; }); gsc::function::add("removefile", [](const gsc::function_args& args) { const auto path = convert_path(args[0].as()); return utils::io::remove_file(path); }); gsc::function::add("va", [](const gsc::function_args& args) { auto fmt = args[0].as(); for (auto i = 1u; i < args.size(); i++) { const auto arg = args[i].to_string(); replace(fmt, "%s", arg); } return fmt; }); } }; } REGISTER_COMPONENT(io::component)