mirror of https://github.com/OpenMW/openmw.git
Merge branch 'master' of gitlab.com:openmw/openmw into lua_record_services
commit
f287b2f436
@ -0,0 +1,346 @@
|
||||
#include "vfsbindings.hpp"
|
||||
|
||||
#include <components/files/istreamptr.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "context.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Too many arguments may cause stack corruption and crash.
|
||||
constexpr std::size_t sMaximumReadArguments = 20;
|
||||
|
||||
// Print a message if we read a large chunk of file to string.
|
||||
constexpr std::size_t sFileSizeWarningThreshold = 1024 * 1024;
|
||||
|
||||
struct FileHandle
|
||||
{
|
||||
public:
|
||||
FileHandle(Files::IStreamPtr stream, std::string_view fileName)
|
||||
{
|
||||
mFilePtr = std::move(stream);
|
||||
mFileName = fileName;
|
||||
}
|
||||
|
||||
Files::IStreamPtr mFilePtr;
|
||||
std::string mFileName;
|
||||
};
|
||||
|
||||
std::ios_base::seekdir getSeekDir(FileHandle& self, std::string_view whence)
|
||||
{
|
||||
if (whence == "cur")
|
||||
return std::ios_base::cur;
|
||||
if (whence == "set")
|
||||
return std::ios_base::beg;
|
||||
if (whence == "end")
|
||||
return std::ios_base::end;
|
||||
|
||||
throw std::runtime_error(
|
||||
"Error when handling '" + self.mFileName + "': invalid seek direction: '" + std::string(whence) + "'.");
|
||||
}
|
||||
|
||||
size_t getBytesLeftInStream(Files::IStreamPtr& file)
|
||||
{
|
||||
auto oldPos = file->tellg();
|
||||
file->seekg(0, std::ios_base::end);
|
||||
auto newPos = file->tellg();
|
||||
file->seekg(oldPos, std::ios_base::beg);
|
||||
|
||||
return newPos - oldPos;
|
||||
}
|
||||
|
||||
void printLargeDataMessage(FileHandle& file, size_t size)
|
||||
{
|
||||
if (!file.mFilePtr || !Settings::lua().mLuaDebug || size < sFileSizeWarningThreshold)
|
||||
return;
|
||||
|
||||
Log(Debug::Verbose) << "Read a large data chunk (" << size << " bytes) from '" << file.mFileName << "'.";
|
||||
}
|
||||
|
||||
sol::object readFile(LuaUtil::LuaState* lua, FileHandle& file)
|
||||
{
|
||||
std::ostringstream os;
|
||||
if (file.mFilePtr && file.mFilePtr->peek() != EOF)
|
||||
os << file.mFilePtr->rdbuf();
|
||||
|
||||
auto result = os.str();
|
||||
printLargeDataMessage(file, result.size());
|
||||
return sol::make_object<std::string>(lua->sol(), std::move(result));
|
||||
}
|
||||
|
||||
sol::object readLineFromFile(LuaUtil::LuaState* lua, FileHandle& file)
|
||||
{
|
||||
std::string result;
|
||||
if (file.mFilePtr && std::getline(*file.mFilePtr, result))
|
||||
{
|
||||
printLargeDataMessage(file, result.size());
|
||||
return sol::make_object<std::string>(lua->sol(), result);
|
||||
}
|
||||
|
||||
return sol::nil;
|
||||
}
|
||||
|
||||
sol::object readNumberFromFile(LuaUtil::LuaState* lua, Files::IStreamPtr& file)
|
||||
{
|
||||
double number = 0;
|
||||
if (file && *file >> number)
|
||||
return sol::make_object<double>(lua->sol(), number);
|
||||
|
||||
return sol::nil;
|
||||
}
|
||||
|
||||
sol::object readCharactersFromFile(LuaUtil::LuaState* lua, FileHandle& file, size_t count)
|
||||
{
|
||||
if (count <= 0 && file.mFilePtr->peek() != EOF)
|
||||
return sol::make_object<std::string>(lua->sol(), std::string());
|
||||
|
||||
auto bytesLeft = getBytesLeftInStream(file.mFilePtr);
|
||||
if (bytesLeft <= 0)
|
||||
return sol::nil;
|
||||
|
||||
if (count > bytesLeft)
|
||||
count = bytesLeft;
|
||||
|
||||
std::string result(count, '\0');
|
||||
if (file.mFilePtr->read(&result[0], count))
|
||||
{
|
||||
printLargeDataMessage(file, result.size());
|
||||
return sol::make_object<std::string>(lua->sol(), result);
|
||||
}
|
||||
|
||||
return sol::nil;
|
||||
}
|
||||
|
||||
void validateFile(const FileHandle& self)
|
||||
{
|
||||
if (self.mFilePtr)
|
||||
return;
|
||||
|
||||
throw std::runtime_error("Error when handling '" + self.mFileName + "': attempt to use a closed file.");
|
||||
}
|
||||
|
||||
sol::variadic_results seek(
|
||||
LuaUtil::LuaState* lua, FileHandle& self, std::ios_base::seekdir dir, std::streamoff off)
|
||||
{
|
||||
sol::variadic_results values;
|
||||
try
|
||||
{
|
||||
self.mFilePtr->seekg(off, dir);
|
||||
if (self.mFilePtr->fail() || self.mFilePtr->bad())
|
||||
{
|
||||
auto msg = "Failed to seek in file '" + self.mFileName + "'";
|
||||
values.push_back(sol::nil);
|
||||
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
|
||||
}
|
||||
else
|
||||
values.push_back(sol::make_object<std::streampos>(lua->sol(), self.mFilePtr->tellg()));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
auto msg = "Failed to seek in file '" + self.mFileName + "': " + std::string(e.what());
|
||||
values.push_back(sol::nil);
|
||||
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
sol::table initVFSPackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
|
||||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||
|
||||
sol::usertype<FileHandle> handle = context.mLua->sol().new_usertype<FileHandle>("FileHandle");
|
||||
handle["fileName"] = sol::readonly_property([](const FileHandle& self) { return self.mFileName; });
|
||||
handle[sol::meta_function::to_string] = [](const FileHandle& self) {
|
||||
return "FileHandle{'" + self.mFileName + "'" + (!self.mFilePtr ? ", closed" : "") + "}";
|
||||
};
|
||||
handle["seek"] = sol::overload(
|
||||
[lua = context.mLua](FileHandle& self, std::string_view whence, sol::optional<long> offset) {
|
||||
validateFile(self);
|
||||
|
||||
auto off = static_cast<std::streamoff>(offset.value_or(0));
|
||||
auto dir = getSeekDir(self, whence);
|
||||
|
||||
return seek(lua, self, dir, off);
|
||||
},
|
||||
[lua = context.mLua](FileHandle& self, sol::optional<long> offset) {
|
||||
validateFile(self);
|
||||
|
||||
auto off = static_cast<std::streamoff>(offset.value_or(0));
|
||||
|
||||
return seek(lua, self, std::ios_base::cur, off);
|
||||
});
|
||||
handle["lines"] = [lua = context.mLua](FileHandle& self) {
|
||||
return sol::as_function([&lua, &self]() mutable {
|
||||
validateFile(self);
|
||||
return readLineFromFile(lua, self);
|
||||
});
|
||||
};
|
||||
|
||||
api["lines"] = [lua = context.mLua, vfs](std::string_view fileName) {
|
||||
auto normalizedName = VFS::Path::normalizeFilename(fileName);
|
||||
return sol::as_function(
|
||||
[lua, file = FileHandle(vfs->getNormalized(normalizedName), normalizedName)]() mutable {
|
||||
validateFile(file);
|
||||
auto result = readLineFromFile(lua, file);
|
||||
if (result == sol::nil)
|
||||
file.mFilePtr.reset();
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
handle["close"] = [lua = context.mLua](FileHandle& self) {
|
||||
sol::variadic_results values;
|
||||
try
|
||||
{
|
||||
self.mFilePtr.reset();
|
||||
if (self.mFilePtr)
|
||||
{
|
||||
auto msg = "Can not close file '" + self.mFileName + "': file handle is still opened.";
|
||||
values.push_back(sol::nil);
|
||||
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
|
||||
}
|
||||
else
|
||||
values.push_back(sol::make_object<bool>(lua->sol(), true));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
auto msg = "Can not close file '" + self.mFileName + "': " + std::string(e.what());
|
||||
values.push_back(sol::nil);
|
||||
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
handle["read"] = [lua = context.mLua](FileHandle& self, const sol::variadic_args args) {
|
||||
validateFile(self);
|
||||
|
||||
if (args.size() > sMaximumReadArguments)
|
||||
throw std::runtime_error(
|
||||
"Error when handling '" + self.mFileName + "': too many arguments for 'read'.");
|
||||
|
||||
sol::variadic_results values;
|
||||
// If there are no arguments, read a string
|
||||
if (args.size() == 0)
|
||||
{
|
||||
values.push_back(readLineFromFile(lua, self));
|
||||
return values;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
size_t i = 0;
|
||||
for (i = 0; i < args.size() && success; i++)
|
||||
{
|
||||
if (args[i].is<std::string_view>())
|
||||
{
|
||||
auto format = args[i].as<std::string_view>();
|
||||
|
||||
if (format == "*a" || format == "*all")
|
||||
{
|
||||
values.push_back(readFile(lua, self));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (format == "*n" || format == "*number")
|
||||
{
|
||||
auto result = readNumberFromFile(lua, self.mFilePtr);
|
||||
values.push_back(result);
|
||||
if (result == sol::nil)
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (format == "*l" || format == "*line")
|
||||
{
|
||||
auto result = readLineFromFile(lua, self);
|
||||
values.push_back(result);
|
||||
if (result == sol::nil)
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Error when handling '" + self.mFileName + "': bad argument #"
|
||||
+ std::to_string(i + 1) + " to 'read' (invalid format)");
|
||||
}
|
||||
else if (args[i].is<int>())
|
||||
{
|
||||
int number = args[i].as<int>();
|
||||
auto result = readCharactersFromFile(lua, self, number);
|
||||
values.push_back(result);
|
||||
if (result == sol::nil)
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We should return nil if we just reached the end of stream
|
||||
if (!success && self.mFilePtr->eof())
|
||||
return values;
|
||||
|
||||
if (!success && (self.mFilePtr->fail() || self.mFilePtr->bad()))
|
||||
{
|
||||
auto msg = "Error when handling '" + self.mFileName + "': can not read data for argument #"
|
||||
+ std::to_string(i);
|
||||
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
api["open"] = [lua = context.mLua, vfs](std::string_view fileName) {
|
||||
sol::variadic_results values;
|
||||
try
|
||||
{
|
||||
auto normalizedName = VFS::Path::normalizeFilename(fileName);
|
||||
auto handle = FileHandle(vfs->getNormalized(normalizedName), normalizedName);
|
||||
values.push_back(sol::make_object<FileHandle>(lua->sol(), std::move(handle)));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
auto msg = "Can not open file: " + std::string(e.what());
|
||||
values.push_back(sol::nil);
|
||||
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
api["type"] = sol::overload(
|
||||
[](const FileHandle& handle) -> std::string {
|
||||
if (handle.mFilePtr)
|
||||
return "file";
|
||||
|
||||
return "closed file";
|
||||
},
|
||||
[](const sol::object&) -> sol::object { return sol::nil; });
|
||||
|
||||
api["fileExists"] = [vfs](std::string_view fileName) -> bool { return vfs->exists(fileName); };
|
||||
api["pathsWithPrefix"] = [vfs](std::string_view prefix) {
|
||||
auto iterator = vfs->getRecursiveDirectoryIterator(prefix);
|
||||
return sol::as_function([iterator, current = iterator.begin()]() mutable -> sol::optional<std::string> {
|
||||
if (current != iterator.end())
|
||||
{
|
||||
const std::string& result = *current;
|
||||
++current;
|
||||
return result;
|
||||
}
|
||||
|
||||
return sol::nullopt;
|
||||
});
|
||||
};
|
||||
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#ifndef MWLUA_VFSBINDINGS_H
|
||||
#define MWLUA_VFSBINDINGS_H
|
||||
|
||||
#include <sol/forward.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initVFSPackage(const Context&);
|
||||
}
|
||||
|
||||
#endif // MWLUA_VFSBINDINGS_H
|
@ -0,0 +1,233 @@
|
||||
#include <codecvt>
|
||||
#include <components/misc/strings/format.hpp>
|
||||
|
||||
#include "utf8.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr std::string_view UTF8PATT = "[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*"; // %z is deprecated in Lua5.2
|
||||
constexpr uint32_t MAXUTF = 0x7FFFFFFFu;
|
||||
// constexpr uint32_t MAXUNICODE = 0x10FFFFu;
|
||||
|
||||
inline bool isNilOrNone(const sol::stack_proxy arg)
|
||||
{
|
||||
return (arg.get_type() == sol::type::lua_nil || arg.get_type() == sol::type::none);
|
||||
}
|
||||
|
||||
inline double getInteger(const sol::stack_proxy arg, const size_t n, std::string_view name)
|
||||
{
|
||||
double integer;
|
||||
if (!arg.is<double>())
|
||||
throw std::runtime_error(Misc::StringUtils::format("bad argument #%i to '%s' (number expected, got %s)", n,
|
||||
name, sol::type_name(arg.lua_state(), arg.get_type())));
|
||||
|
||||
if (std::modf(arg, &integer) != 0)
|
||||
throw std::runtime_error(
|
||||
Misc::StringUtils::format("bad argument #{} to '{}' (number has no integer representation)", n, name));
|
||||
|
||||
return integer;
|
||||
}
|
||||
|
||||
// If the input 'pos' is negative, it is treated as counting from the end of the string,
|
||||
// where -1 represents the last character position, -2 represents the second-to-last position,
|
||||
// and so on. If 'pos' is non-negative, it is used as-is.
|
||||
inline void relativePosition(int64_t& pos, const size_t len)
|
||||
{
|
||||
if (pos < 0)
|
||||
pos = std::max<int64_t>(0, pos + len + 1);
|
||||
}
|
||||
|
||||
// returns: first - character pos in bytes, second - character codepoint
|
||||
std::pair<int64_t, int64_t> decodeNextUTF8Character(std::string_view s, std::vector<int64_t>& pos_byte)
|
||||
{
|
||||
const int64_t pos = pos_byte.back() - 1;
|
||||
const unsigned char ch = static_cast<unsigned char>(s[pos]);
|
||||
int64_t codepoint = -1;
|
||||
size_t byteSize = 0;
|
||||
|
||||
if ((ch & 0b10000000) == 0)
|
||||
{
|
||||
codepoint = ch;
|
||||
byteSize = 1;
|
||||
}
|
||||
else if ((ch & 0b11100000) == 0b11000000)
|
||||
{
|
||||
codepoint = ch & 0b00011111;
|
||||
byteSize = 2;
|
||||
}
|
||||
else if ((ch & 0b11110000) == 0b11100000)
|
||||
{
|
||||
codepoint = ch & 0b00001111;
|
||||
byteSize = 3;
|
||||
}
|
||||
else if ((ch & 0b11111000) == 0b11110000)
|
||||
{
|
||||
codepoint = ch & 0b00000111;
|
||||
byteSize = 4;
|
||||
}
|
||||
|
||||
// construct codepoint for non-ascii
|
||||
for (size_t i = 1; i < byteSize; ++i)
|
||||
{
|
||||
// if not a continuation byte
|
||||
if ((pos + i) >= s.size() || (static_cast<unsigned char>(s[pos + i]) & 0b11000000) != 0b10000000)
|
||||
{
|
||||
return std::make_pair(0, -1);
|
||||
}
|
||||
codepoint = (codepoint << 6) | (static_cast<unsigned char>(s[pos + i]) & 0b00111111);
|
||||
}
|
||||
|
||||
std::pair<size_t, int64_t> res = std::make_pair(pos_byte.back(), codepoint);
|
||||
|
||||
pos_byte.push_back(pos_byte.back() + byteSize); /* the next character (if exists) starts at this byte */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace LuaUtf8
|
||||
{
|
||||
sol::table initUtf8Package(sol::state_view& lua)
|
||||
{
|
||||
sol::table utf8(lua, sol::create);
|
||||
|
||||
utf8["charpattern"] = UTF8PATT;
|
||||
|
||||
utf8["char"] = [](const sol::variadic_args args) -> std::string {
|
||||
std::string result{};
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
int64_t codepoint = getInteger(args[i], (i + 1), "char");
|
||||
if (codepoint < 0 || codepoint > MAXUTF)
|
||||
throw std::runtime_error(
|
||||
Misc::StringUtils::format("bad argument #{} to 'char' (value out of range)", (i + 1)));
|
||||
|
||||
result += converter.to_bytes(codepoint);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
utf8["codes"] = [](std::string_view s) {
|
||||
std::vector<int64_t> pos_byte{ 1 };
|
||||
return sol::as_function([s, pos_byte]() mutable -> sol::optional<std::pair<int64_t, int64_t>> {
|
||||
if (pos_byte.back() <= static_cast<int64_t>(s.size()))
|
||||
{
|
||||
const auto pair = decodeNextUTF8Character(s, pos_byte);
|
||||
if (pair.second == -1)
|
||||
throw std::runtime_error("Invalid UTF-8 code at position " + std::to_string(pos_byte.size()));
|
||||
|
||||
return pair;
|
||||
}
|
||||
return sol::nullopt;
|
||||
});
|
||||
};
|
||||
|
||||
utf8["len"] = [](std::string_view s,
|
||||
const sol::variadic_args args) -> std::variant<size_t, std::pair<sol::object, int64_t>> {
|
||||
const size_t len = s.size();
|
||||
int64_t iv = isNilOrNone(args[0]) ? 1 : getInteger(args[0], 2, "len");
|
||||
int64_t fv = isNilOrNone(args[1]) ? -1 : getInteger(args[1], 3, "len");
|
||||
|
||||
relativePosition(iv, len);
|
||||
relativePosition(fv, len);
|
||||
|
||||
if (iv <= 0)
|
||||
throw std::runtime_error("bad argument #2 to 'len' (initial position out of bounds)");
|
||||
if (fv > static_cast<int64_t>(len))
|
||||
throw std::runtime_error("bad argument #3 to 'len' (final position out of bounds)");
|
||||
|
||||
if (len == 0)
|
||||
return len;
|
||||
|
||||
std::vector<int64_t> pos_byte = { iv };
|
||||
|
||||
while (pos_byte.back() <= fv)
|
||||
{
|
||||
if (decodeNextUTF8Character(s, pos_byte).second == -1)
|
||||
return std::pair(sol::lua_nil, pos_byte.back());
|
||||
}
|
||||
return pos_byte.size() - 1;
|
||||
};
|
||||
|
||||
utf8["codepoint"]
|
||||
= [](std::string_view s, const sol::variadic_args args) -> sol::as_returns_t<std::vector<int64_t>> {
|
||||
size_t len = s.size();
|
||||
int64_t iv = isNilOrNone(args[0]) ? 1 : getInteger(args[0], 2, "codepoint");
|
||||
int64_t fv = isNilOrNone(args[1]) ? iv : getInteger(args[1], 3, "codepoint");
|
||||
|
||||
relativePosition(iv, len);
|
||||
relativePosition(fv, len);
|
||||
|
||||
if (iv <= 0)
|
||||
throw std::runtime_error("bad argument #2 to 'codepoint' (initial position out of bounds)");
|
||||
if (fv > static_cast<int64_t>(len))
|
||||
throw std::runtime_error("bad argument #3 to 'codepoint' (final position out of bounds)");
|
||||
|
||||
if (iv > fv)
|
||||
return sol::as_returns(std::vector<int64_t>{}); /* empty interval; return nothing */
|
||||
|
||||
std::vector<int64_t> pos_byte = { iv };
|
||||
std::vector<int64_t> codepoints;
|
||||
|
||||
while (pos_byte.back() <= fv)
|
||||
{
|
||||
codepoints.push_back(decodeNextUTF8Character(s, pos_byte).second);
|
||||
if (codepoints.back() == -1)
|
||||
throw std::runtime_error("Invalid UTF-8 code at position " + std::to_string(pos_byte.size()));
|
||||
}
|
||||
|
||||
return sol::as_returns(std::move(codepoints));
|
||||
};
|
||||
|
||||
utf8["offset"]
|
||||
= [](std::string_view s, const int64_t n, const sol::variadic_args args) -> sol::optional<int64_t> {
|
||||
size_t len = s.size();
|
||||
int64_t iv;
|
||||
|
||||
if (isNilOrNone(args[0]))
|
||||
{
|
||||
if (n >= 0)
|
||||
iv = 1;
|
||||
else
|
||||
iv = s.size() + 1;
|
||||
}
|
||||
else
|
||||
iv = getInteger(args[0], 3, "offset");
|
||||
|
||||
std::vector<int64_t> pos_byte = { 1 };
|
||||
|
||||
relativePosition(iv, len);
|
||||
|
||||
if (iv > static_cast<int64_t>(len) + 1)
|
||||
throw std::runtime_error("bad argument #3 to 'offset' (position out of bounds)");
|
||||
|
||||
while (pos_byte.back() <= static_cast<int64_t>(len))
|
||||
decodeNextUTF8Character(s, pos_byte);
|
||||
|
||||
for (auto it = pos_byte.begin(); it != pos_byte.end(); ++it)
|
||||
{
|
||||
if (*it == iv)
|
||||
{
|
||||
if (n <= 0 && it + n >= pos_byte.begin())
|
||||
return *(it + n);
|
||||
if (n > 0 && it + n - 1 < pos_byte.end())
|
||||
return *(it + n - 1);
|
||||
break;
|
||||
}
|
||||
else if (*it > iv) /* a continuation byte */
|
||||
{
|
||||
if (n == 0)
|
||||
return *(it - 1); /* special case */
|
||||
else
|
||||
throw std::runtime_error("initial position is a continuation byte");
|
||||
}
|
||||
}
|
||||
|
||||
return sol::nullopt;
|
||||
};
|
||||
|
||||
return utf8;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
#ifndef COMPONENTS_LUA_UTF8_H
|
||||
#define COMPONENTS_LUA_UTF8_H
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
namespace LuaUtf8
|
||||
{
|
||||
sol::table initUtf8Package(sol::state_view&);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,140 +0,0 @@
|
||||
#include "controlled.hpp"
|
||||
|
||||
#include "data.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
void NiSourceTexture::read(NIFStream* nif)
|
||||
{
|
||||
Named::read(nif);
|
||||
|
||||
external = nif->getChar() != 0;
|
||||
bool internal = false;
|
||||
if (external)
|
||||
filename = nif->getString();
|
||||
else
|
||||
{
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 3))
|
||||
internal = nif->getChar();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
filename = nif->getString(); // Original file path of the internal texture
|
||||
}
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 3))
|
||||
{
|
||||
if (!external && internal)
|
||||
data.read(nif);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.read(nif);
|
||||
}
|
||||
|
||||
pixel = nif->getUInt();
|
||||
mipmap = nif->getUInt();
|
||||
alpha = nif->getUInt();
|
||||
|
||||
// Renderer hints, typically of no use for us
|
||||
/* bool mIsStatic = */ nif->getChar();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 103))
|
||||
/* bool mDirectRendering = */ nif->getBoolean();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 4))
|
||||
/* bool mPersistRenderData = */ nif->getBoolean();
|
||||
}
|
||||
|
||||
void NiSourceTexture::post(Reader& nif)
|
||||
{
|
||||
Named::post(nif);
|
||||
data.post(nif);
|
||||
}
|
||||
|
||||
void BSShaderTextureSet::read(NIFStream* nif)
|
||||
{
|
||||
nif->getSizedStrings(textures, nif->getUInt());
|
||||
}
|
||||
|
||||
void NiParticleModifier::read(NIFStream* nif)
|
||||
{
|
||||
next.read(nif);
|
||||
controller.read(nif);
|
||||
}
|
||||
|
||||
void NiParticleModifier::post(Reader& nif)
|
||||
{
|
||||
next.post(nif);
|
||||
controller.post(nif);
|
||||
}
|
||||
|
||||
void NiParticleGrowFade::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
growTime = nif->getFloat();
|
||||
fadeTime = nif->getFloat();
|
||||
}
|
||||
|
||||
void NiParticleColorModifier::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
data.read(nif);
|
||||
}
|
||||
|
||||
void NiParticleColorModifier::post(Reader& nif)
|
||||
{
|
||||
NiParticleModifier::post(nif);
|
||||
data.post(nif);
|
||||
}
|
||||
|
||||
void NiGravity::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
mDecay = nif->getFloat();
|
||||
mForce = nif->getFloat();
|
||||
mType = nif->getUInt();
|
||||
mPosition = nif->getVector3();
|
||||
mDirection = nif->getVector3();
|
||||
}
|
||||
|
||||
void NiParticleCollider::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
mBounceFactor = nif->getFloat();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 0, 2))
|
||||
{
|
||||
// Unused in NifSkope. Need to figure out what these do.
|
||||
/*bool mSpawnOnCollision = */ nif->getBoolean();
|
||||
/*bool mDieOnCollision = */ nif->getBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
void NiPlanarCollider::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleCollider::read(nif);
|
||||
|
||||
mExtents = nif->getVector2();
|
||||
mPosition = nif->getVector3();
|
||||
mXVector = nif->getVector3();
|
||||
mYVector = nif->getVector3();
|
||||
mPlaneNormal = nif->getVector3();
|
||||
mPlaneDistance = nif->getFloat();
|
||||
}
|
||||
|
||||
void NiParticleRotation::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
/* bool mRandomInitialAxis = */ nif->getChar();
|
||||
/* osg::Vec3f mInitialAxis = */ nif->getVector3();
|
||||
/* float mRotationSpeed = */ nif->getFloat();
|
||||
}
|
||||
|
||||
void NiSphericalCollider::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleCollider::read(nif);
|
||||
|
||||
mRadius = nif->getFloat();
|
||||
mCenter = nif->getVector3();
|
||||
}
|
||||
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2010 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: https://openmw.org/
|
||||
|
||||
This file (controlled.h) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
https://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
#ifndef OPENMW_COMPONENTS_NIF_CONTROLLED_HPP
|
||||
#define OPENMW_COMPONENTS_NIF_CONTROLLED_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
struct NiSourceTexture : public Named
|
||||
{
|
||||
// Is this an external (references a separate texture file) or
|
||||
// internal (data is inside the nif itself) texture?
|
||||
bool external;
|
||||
|
||||
std::string filename; // In case of external textures
|
||||
NiPixelDataPtr data; // In case of internal textures
|
||||
|
||||
/* Pixel layout
|
||||
0 - Palettised
|
||||
1 - High color 16
|
||||
2 - True color 32
|
||||
3 - Compressed
|
||||
4 - Bumpmap
|
||||
5 - Default */
|
||||
unsigned int pixel;
|
||||
|
||||
/* Mipmap format
|
||||
0 - no
|
||||
1 - yes
|
||||
2 - default */
|
||||
unsigned int mipmap;
|
||||
|
||||
/* Alpha
|
||||
0 - none
|
||||
1 - binary
|
||||
2 - smooth
|
||||
3 - default (use material alpha, or multiply material with texture if present)
|
||||
*/
|
||||
unsigned int alpha;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct BSShaderTextureSet : public Record
|
||||
{
|
||||
enum TextureType
|
||||
{
|
||||
TextureType_Base = 0,
|
||||
TextureType_Normal = 1,
|
||||
TextureType_Glow = 2,
|
||||
TextureType_Parallax = 3,
|
||||
TextureType_Env = 4,
|
||||
TextureType_EnvMask = 5,
|
||||
TextureType_Subsurface = 6,
|
||||
TextureType_BackLighting = 7
|
||||
};
|
||||
std::vector<std::string> textures;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleModifier : public Record
|
||||
{
|
||||
NiParticleModifierPtr next;
|
||||
ControllerPtr controller;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleGrowFade : public NiParticleModifier
|
||||
{
|
||||
float growTime;
|
||||
float fadeTime;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleColorModifier : public NiParticleModifier
|
||||
{
|
||||
NiColorDataPtr data;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct NiGravity : public NiParticleModifier
|
||||
{
|
||||
float mForce;
|
||||
/* 0 - Wind (fixed direction)
|
||||
* 1 - Point (fixed origin)
|
||||
*/
|
||||
int mType;
|
||||
float mDecay;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mDirection;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleCollider : public NiParticleModifier
|
||||
{
|
||||
float mBounceFactor;
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
// NiPinaColada
|
||||
struct NiPlanarCollider : public NiParticleCollider
|
||||
{
|
||||
osg::Vec2f mExtents;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mXVector, mYVector;
|
||||
osg::Vec3f mPlaneNormal;
|
||||
float mPlaneDistance;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiSphericalCollider : public NiParticleCollider
|
||||
{
|
||||
float mRadius;
|
||||
osg::Vec3f mCenter;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleRotation : public NiParticleModifier
|
||||
{
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
#endif
|
@ -0,0 +1,95 @@
|
||||
#include "particle.hpp"
|
||||
|
||||
#include "data.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
void NiParticleModifier::read(NIFStream* nif)
|
||||
{
|
||||
mNext.read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
|
||||
mController.read(nif);
|
||||
}
|
||||
|
||||
void NiParticleModifier::post(Reader& nif)
|
||||
{
|
||||
mNext.post(nif);
|
||||
mController.post(nif);
|
||||
}
|
||||
|
||||
void NiParticleGrowFade::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
nif->read(mGrowTime);
|
||||
nif->read(mFadeTime);
|
||||
}
|
||||
|
||||
void NiParticleColorModifier::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
mData.read(nif);
|
||||
}
|
||||
|
||||
void NiParticleColorModifier::post(Reader& nif)
|
||||
{
|
||||
NiParticleModifier::post(nif);
|
||||
|
||||
mData.post(nif);
|
||||
}
|
||||
|
||||
void NiGravity::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
|
||||
nif->read(mDecay);
|
||||
nif->read(mForce);
|
||||
mType = static_cast<ForceType>(nif->get<uint32_t>());
|
||||
nif->read(mPosition);
|
||||
nif->read(mDirection);
|
||||
}
|
||||
|
||||
void NiParticleCollider::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
nif->read(mBounceFactor);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 0, 2))
|
||||
{
|
||||
nif->read(mSpawnOnCollision);
|
||||
nif->read(mDieOnCollision);
|
||||
}
|
||||
}
|
||||
|
||||
void NiPlanarCollider::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleCollider::read(nif);
|
||||
|
||||
nif->read(mExtents);
|
||||
nif->read(mPosition);
|
||||
nif->read(mXVector);
|
||||
nif->read(mYVector);
|
||||
nif->read(mPlaneNormal);
|
||||
nif->read(mPlaneDistance);
|
||||
}
|
||||
|
||||
void NiSphericalCollider::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleCollider::read(nif);
|
||||
|
||||
nif->read(mRadius);
|
||||
nif->read(mCenter);
|
||||
}
|
||||
|
||||
void NiParticleRotation::read(NIFStream* nif)
|
||||
{
|
||||
NiParticleModifier::read(nif);
|
||||
|
||||
nif->read(mRandomInitialAxis);
|
||||
nif->read(mInitialAxis);
|
||||
nif->read(mRotationSpeed);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
#ifndef OPENMW_COMPONENTS_NIF_PARTICLE_HPP
|
||||
#define OPENMW_COMPONENTS_NIF_PARTICLE_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
struct NiParticleModifier : public Record
|
||||
{
|
||||
NiParticleModifierPtr mNext;
|
||||
ControllerPtr mController;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleGrowFade : public NiParticleModifier
|
||||
{
|
||||
float mGrowTime;
|
||||
float mFadeTime;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleColorModifier : public NiParticleModifier
|
||||
{
|
||||
NiColorDataPtr mData;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct NiGravity : public NiParticleModifier
|
||||
{
|
||||
enum class ForceType : uint32_t
|
||||
{
|
||||
Wind = 0, // Fixed direction
|
||||
Point = 1, // Fixed origin
|
||||
};
|
||||
|
||||
float mDecay{ 0.f };
|
||||
float mForce;
|
||||
ForceType mType;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mDirection;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleCollider : public NiParticleModifier
|
||||
{
|
||||
float mBounceFactor;
|
||||
bool mSpawnOnCollision{ false };
|
||||
bool mDieOnCollision{ false };
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
// NiPinaColada
|
||||
struct NiPlanarCollider : public NiParticleCollider
|
||||
{
|
||||
osg::Vec2f mExtents;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mXVector, mYVector;
|
||||
osg::Vec3f mPlaneNormal;
|
||||
float mPlaneDistance;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiSphericalCollider : public NiParticleCollider
|
||||
{
|
||||
float mRadius;
|
||||
osg::Vec3f mCenter;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiParticleRotation : public NiParticleModifier
|
||||
{
|
||||
uint8_t mRandomInitialAxis;
|
||||
osg::Vec3f mInitialAxis;
|
||||
float mRotationSpeed;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,47 @@
|
||||
#include "texture.hpp"
|
||||
|
||||
#include "data.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
void NiSourceTexture::read(NIFStream* nif)
|
||||
{
|
||||
NiTexture::read(nif);
|
||||
|
||||
nif->read(mExternal);
|
||||
if (mExternal || nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
nif->read(mFile);
|
||||
|
||||
bool hasData = nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 4);
|
||||
if (!hasData && !mExternal)
|
||||
nif->read(hasData);
|
||||
|
||||
if (hasData)
|
||||
mData.read(nif);
|
||||
|
||||
mPrefs.mPixelLayout = static_cast<PixelLayout>(nif->get<uint32_t>());
|
||||
mPrefs.mUseMipMaps = static_cast<MipMapFormat>(nif->get<uint32_t>());
|
||||
mPrefs.mAlphaFormat = static_cast<AlphaFormat>(nif->get<uint32_t>());
|
||||
|
||||
nif->read(mIsStatic);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 103))
|
||||
{
|
||||
nif->read(mDirectRendering);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 4))
|
||||
nif->read(mPersistRenderData);
|
||||
}
|
||||
}
|
||||
|
||||
void NiSourceTexture::post(Reader& nif)
|
||||
{
|
||||
NiTexture::post(nif);
|
||||
mData.post(nif);
|
||||
}
|
||||
|
||||
void BSShaderTextureSet::read(NIFStream* nif)
|
||||
{
|
||||
nif->getSizedStrings(mTextures, nif->get<uint32_t>());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
#ifndef OPENMW_COMPONENTS_NIF_TEXTURE_HPP
|
||||
#define OPENMW_COMPONENTS_NIF_TEXTURE_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
struct NiTexture : public Named
|
||||
{
|
||||
};
|
||||
|
||||
struct NiSourceTexture : public NiTexture
|
||||
{
|
||||
enum class PixelLayout : uint32_t
|
||||
{
|
||||
Palette = 0,
|
||||
HighColor = 1,
|
||||
TrueColor = 2,
|
||||
Compressed = 3,
|
||||
BumpMap = 4,
|
||||
Default = 5,
|
||||
};
|
||||
|
||||
enum class MipMapFormat : uint32_t
|
||||
{
|
||||
No = 0,
|
||||
Yes = 1,
|
||||
Default = 2,
|
||||
};
|
||||
|
||||
enum class AlphaFormat : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
Binary = 1,
|
||||
Smooth = 2,
|
||||
Default = 3,
|
||||
};
|
||||
|
||||
struct FormatPrefs
|
||||
{
|
||||
PixelLayout mPixelLayout;
|
||||
MipMapFormat mUseMipMaps;
|
||||
AlphaFormat mAlphaFormat;
|
||||
};
|
||||
|
||||
char mExternal; // References external file
|
||||
|
||||
std::string mFile;
|
||||
NiPixelDataPtr mData;
|
||||
|
||||
FormatPrefs mPrefs;
|
||||
|
||||
char mIsStatic{ 1 };
|
||||
bool mDirectRendering{ true };
|
||||
bool mPersistRenderData{ false };
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct BSShaderTextureSet : public Record
|
||||
{
|
||||
enum class TextureType : uint32_t
|
||||
{
|
||||
Base = 0,
|
||||
Normal = 1,
|
||||
Glow = 2,
|
||||
Parallax = 3,
|
||||
Environment = 4,
|
||||
EnvironmentMask = 5,
|
||||
Subsurface = 6,
|
||||
BackLighting = 7,
|
||||
};
|
||||
std::vector<std::string> mTextures;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,7 @@
|
||||
Package openmw.vfs
|
||||
==================
|
||||
|
||||
.. include:: version.rst
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/openmw_vfs.html
|
@ -0,0 +1,159 @@
|
||||
---
|
||||
-- `openmw.vfs` provides read-only access to data directories via VFS.
|
||||
-- Interface is very similar to "io" library.
|
||||
-- @module vfs
|
||||
-- @usage local vfs = require('openmw.vfs')
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- @type FileHandle
|
||||
-- @field #string fileName VFS path to related file
|
||||
|
||||
---
|
||||
-- Close a file handle
|
||||
-- @function [parent=#FileHandle] close
|
||||
-- @param self
|
||||
-- @return #boolean true if a call succeeds without errors.
|
||||
-- @return #nil, #string nil plus the error message in case of any error.
|
||||
|
||||
---
|
||||
-- Get an iterator function to fetch the next line from given file.
|
||||
-- Throws an exception if file is closed.
|
||||
--
|
||||
-- Hint: since garbage collection works once per frame,
|
||||
-- you will get the whole file in RAM if you read it in one frame.
|
||||
-- So if you need to read a really large file, it is better to split reading
|
||||
-- between different frames (e.g. by keeping a current position in file
|
||||
-- and using a "seek" to read from saved position).
|
||||
-- @function [parent=#FileHandle] lines
|
||||
-- @param self
|
||||
-- @return #function Iterator function to get next line
|
||||
-- @usage f = vfs.open("Test\\test.txt");
|
||||
-- for line in f:lines() do
|
||||
-- print(line);
|
||||
-- end
|
||||
|
||||
---
|
||||
-- Set new position in file.
|
||||
-- Throws an exception if file is closed or seek base is incorrect.
|
||||
-- @function [parent=#FileHandle] seek
|
||||
-- @param self
|
||||
-- @param #string whence Seek base (optional, "cur" by default). Can be:
|
||||
--
|
||||
-- * "set" - seek from beginning of file;
|
||||
-- * "cur" - seek from current position;
|
||||
-- * "end" - seek from end of file (offset needs to be <= 0);
|
||||
-- @param #number offset Offset from given base (optional, 0 by default)
|
||||
-- @return #number new position in file if a call succeeds without errors.
|
||||
-- @return #nil, #string nil plus the error message in case of any error.
|
||||
-- @usage -- set pointer to beginning of file
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- f:seek("set");
|
||||
-- @usage -- print current position in file
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- print(f:seek());
|
||||
-- @usage -- print file size
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- print(f:seek("end"));
|
||||
|
||||
---
|
||||
-- Read data from file to strings.
|
||||
-- Throws an exception if file is closed, if there is too many arguments or if an invalid format encountered.
|
||||
--
|
||||
-- Hint: since garbage collection works once per frame,
|
||||
-- you will get the whole file in RAM if you read it in one frame.
|
||||
-- So if you need to read a really large file, it is better to split reading
|
||||
-- between different frames (e.g. by keeping a current position in file
|
||||
-- and using a "seek" to read from saved position).
|
||||
-- @function [parent=#FileHandle] read
|
||||
-- @param self
|
||||
-- @param ... Read formats (up to 20 arguments, default value is one "*l"). Can be:
|
||||
--
|
||||
-- * "\*a" (or "*all") - reads the whole file, starting at the current position as #string. On end of file, it returns the empty string.
|
||||
-- * "\*l" (or "*line") - reads the next line (skipping the end of line), returning nil on end of file (nil and error message if error occured);
|
||||
-- * "\*n" (or "*number") - read a floating point value as #number (nil and error message if error occured);
|
||||
-- * number - reads a #string with up to this number of characters, returning nil on end of file (nil and error message if error occured). If number is 0 and end of file is not reached, it reads nothing and returns an empty string;
|
||||
-- @return #string One #string for every format if a call succeeds without errors. One #string for every successfully handled format, nil for first failed format.
|
||||
-- @usage -- read three numbers from file
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- local n1, n2, n3 = f:read("*number", "*number", "*number");
|
||||
-- @usage -- read 10 bytes from file
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- local n4 = f:read(10);
|
||||
-- @usage -- read until end of file
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- local n5 = f:read("*all");
|
||||
-- @usage -- read a line from file
|
||||
-- f = vfs.open("Test\\test.txt");
|
||||
-- local n6 = f:read();
|
||||
-- @usage -- try to read three numbers from file with "1" content
|
||||
-- f = vfs.open("one.txt");
|
||||
-- print(f:read("*number", "*number", "*number"));
|
||||
-- -- prints(1, nil)
|
||||
|
||||
---
|
||||
-- Check if file exists in VFS
|
||||
-- @function [parent=#vfs] fileExists
|
||||
-- @param #string fileName Path to file in VFS
|
||||
-- @return #boolean (true - exists, false - does not exist)
|
||||
-- @usage local exists = vfs.fileExists("Test\\test.txt");
|
||||
|
||||
---
|
||||
-- Open a file
|
||||
-- @function [parent=#vfs] open
|
||||
-- @param #string fileName Path to file in VFS
|
||||
-- @return #FileHandle Opened file handle if a call succeeds without errors.
|
||||
-- @return #nil, #string nil plus the error message in case of any error.
|
||||
-- @usage f, msg = vfs.open("Test\\test.txt");
|
||||
-- -- print file name or error message
|
||||
-- if (f == nil)
|
||||
-- print(msg);
|
||||
-- else
|
||||
-- print(f.fileName);
|
||||
-- end
|
||||
|
||||
---
|
||||
-- Get an iterator function to fetch the next line from file with given path.
|
||||
-- Throws an exception if file is closed or file with given path does not exist.
|
||||
-- Closes file automatically when it fails to read any more bytes.
|
||||
--
|
||||
-- Hint: since garbage collection works once per frame,
|
||||
-- you will get the whole file in RAM if you read it in one frame.
|
||||
-- So if you need to read a really large file, it is better to split reading
|
||||
-- between different frames (e.g. by keeping a current position in file
|
||||
-- and using a "seek" to read from saved position).
|
||||
-- @function [parent=#vfs] lines
|
||||
-- @param #string fileName Path to file in VFS
|
||||
-- @return #function Iterator function to get next line
|
||||
-- @usage for line in vfs.lines("Test\\test.txt") do
|
||||
-- print(line);
|
||||
-- end
|
||||
|
||||
---
|
||||
-- Get iterator function to fetch file names with given path prefix from VFS
|
||||
-- @function [parent=#vfs] pathsWithPrefix
|
||||
-- @param #string path Path prefix
|
||||
-- @return #function Function to get next file name
|
||||
-- @usage -- get all files with given prefix from VFS index
|
||||
-- for fileName in vfs.pathsWithPrefix("Music\\Explore") do
|
||||
-- print(fileName);
|
||||
-- end
|
||||
-- @usage -- get some first files
|
||||
-- local getNextFile = vfs.pathsWithPrefix("Music\\Explore");
|
||||
-- local firstFile = getNextFile();
|
||||
-- local secondFile = getNextFile();
|
||||
|
||||
---
|
||||
-- Detect a file handle type
|
||||
-- @function [parent=#vfs] type
|
||||
-- @param #any handle Object to check
|
||||
-- @return #string File handle type. Can be:
|
||||
--
|
||||
-- * "file" - an argument is a valid opened @{openmw.vfs#FileHandle};
|
||||
-- * "closed file" - an argument is a valid closed @{openmw.vfs#FileHandle};
|
||||
-- * nil - an argument is not a @{openmw.vfs#FileHandle};
|
||||
-- @usage f = vfs.open("Test\\test.txt");
|
||||
-- print(vfs.type(f));
|
||||
|
||||
return nil
|
@ -0,0 +1,77 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- UTF-8 Support.
|
||||
-- This library provides basic support for UTF-8 encoding.
|
||||
-- It provides all its functions inside the table utf8.
|
||||
-- This library does not provide any support for Unicode other than the handling of the encoding.
|
||||
-- Any operation that needs the meaning of a character, such as character classification, is outside its scope.
|
||||
--
|
||||
-- Unless stated otherwise, all functions that expect a byte position as a parameter assume that
|
||||
-- the given position is either the start of a byte sequence or one plus the length of the subject string.
|
||||
-- As in the string library, negative indices count from the end of the string.
|
||||
-- @module utf8
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Receives zero or more integers, converts each one to its
|
||||
-- corresponding UTF-8 byte sequence, and returns a string with the concatenation
|
||||
-- of all these sequences.
|
||||
-- @function [parent=#utf8] char
|
||||
-- @param ... zero or more integers.
|
||||
-- @return #string
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- The pattern which matches exactly one UTF-8 byte sequence, assuming that
|
||||
-- the subject is a valid UTF-8 string.
|
||||
-- @function [parent=#utf8] charpattern
|
||||
-- @return #string
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Returns values so that the construction
|
||||
--
|
||||
-- for p, c in utf8.codes(s) do body end
|
||||
--
|
||||
-- will iterate over all characters in string s, with p being the position (in bytes)
|
||||
-- and c the code point of each character.
|
||||
-- It raises an error if it meets any invalid byte sequence.
|
||||
-- @function [parent=#utf8] codes
|
||||
-- @param #string s string to handle.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Returns the codepoints (as integers) from all characters in s that start
|
||||
-- between byte position i and j (both included). The default for i is 1 and for j is i.
|
||||
-- It raises an error if it meets any invalid byte sequence.
|
||||
-- @function [parent=#utf8] codepoint
|
||||
-- @param #string s string to handle
|
||||
-- @param #number i the initial position (default value is 1)
|
||||
-- @param #number j the final position (default value is i)
|
||||
-- @return #number the codepoints of each character in s
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Returns the number of UTF-8 characters in string s that start
|
||||
-- between positions i and j (both inclusive).
|
||||
-- The default for i is 1 and for j is -1.
|
||||
-- If it finds any invalid byte sequence,
|
||||
-- returns a false value plus the position of the first invalid byte.
|
||||
-- @function [parent=#utf8] len
|
||||
-- @param #string s string to handle
|
||||
-- @param #number i the initial position (default value is 1)
|
||||
-- @param #number j the final position (default value is -1)
|
||||
-- @return #number the number of utf8 characters in s
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Returns the position (in bytes) where the encoding of the n-th character of s
|
||||
-- (counting from position i) starts. A negative n gets characters before position i.
|
||||
-- The default for i is 1 when n is non-negative and #s + 1 otherwise,
|
||||
-- so that utf8.offset(s, -n) gets the offset of the n-th character from the end of the string.
|
||||
-- If the specified character is neither in the subject nor right after its end, the function returns nil.
|
||||
--
|
||||
-- As a special case, when n is 0 the function returns the
|
||||
-- start of the encoding of the character that contains the i-th byte of s.
|
||||
--
|
||||
-- This function assumes that s is a valid UTF-8 string.
|
||||
-- @function [parent=#utf8] offset
|
||||
-- @param #string s string to handle
|
||||
-- @param #number n the n-th character
|
||||
-- @param #number i the initial position (default value is 1 if n is is non-negative and #s + 1 otherwise)
|
||||
-- @return #number
|
||||
|
||||
return nil
|
Loading…
Reference in New Issue