Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+425
View File
@@ -0,0 +1,425 @@
#include "scripts/binds.h"
#include "main/references.h"
#include "main/logging.h"
#include "util/refcount.h"
#include "str_util.h"
#include "manager.h"
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <time.h>
#include "files.h"
std::set<std::string> validPaths;
namespace scripts {
//Only allow access on files inside the star ruler directory or the profile
bool isAccessible(const std::string& filename) {
std::string workdir = getWorkingDirectory();
if(path_inside(workdir, filename))
return true;
std::string profiledir = getProfileRoot();
if(path_inside(profiledir, filename))
return true;
for(auto path = validPaths.begin(), end = validPaths.end(); path != end; ++path)
if(path_inside(*path, filename))
return true;
return false;
}
std::string getModProfile() {
return devices.mods.getProfile();
}
std::string getModProfileChild(const std::string& dirname) {
return devices.mods.getProfile(dirname);
}
std::string getBaseProfileChild(const std::string& dirname) {
return devices.mods.getGlobalProfile(dirname);
}
static void createDir(const std::string& dirname) {
if(!isAccessible(dirname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
makeDirectory(dirname);
}
static long long modTime(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return 0;
}
return (long long)getModifiedTime(fname);
}
static bool isDir(const std::string& dirname) {
if(!isAccessible(dirname))
return false;
return isDirectory(dirname);
}
static bool fileEx(const std::string& fname) {
if(!isAccessible(fname))
return false;
return fileExists(fname);
}
class ReadFile : public DataReader {
threads::atomic_int references;
public:
ReadFile(const std::string& filename, bool AllowLines) : DataReader(filename, AllowLines), references(1) {
if(!isAccessible(filename))
scripts::throwException("Cannot access file outside game or profile directories.");
}
void error(const std::string& str) {
::error("Error: %s\n %s", position().c_str(), str.c_str());
}
void grab() {
++references;
}
void drop() {
if(--references == 0)
delete this;
}
};
static ReadFile* makeReadFile(const std::string& filename, bool allowLines) {
return new ReadFile(filename.c_str(), allowLines);
}
static bool readNext(ReadFile& rf) {
return rf++;
}
class WriteFile : public AtomicRefCounted {
std::fstream file;
public:
int curIndent;
bool allowMultiline;
std::string endl;
WriteFile(const char* filename) : file(filename, std::ios_base::out | std::ios_base::binary), curIndent(0), allowMultiline(true), endl("\r\n") {
std::string fname = filename;
if(!isAccessible(fname))
scripts::throwException("Cannot access file outside game or profile directories.");
}
bool is_open() const { return file.is_open() && file.good(); }
bool is_atEnd() const { return file.eof(); }
void indent(int amount = 1) {
curIndent += amount;
}
void deindent(int amount = 1) {
curIndent -= amount;
}
void writeLine(const std::string& line) {
file << line << endl;
}
void writeKeyValue(const std::string& key, const std::string& val) {
if(!is_open()) {
throwException("File is not open");
return;
}
for(int i = 0; i < curIndent; ++i)
file << " ";
if(allowMultiline) {
size_t prev = 0;
size_t pos = val.find('\n');
if(pos != std::string::npos) {
file << key << ": <<" << endl;
do {
std::string line = val.substr(prev, pos - prev);
if(line.empty()) {
file << endl;
}
else {
for(int i = 0; i < curIndent+1; ++i)
file << " ";
file << val.substr(prev, pos - prev) << endl;
}
prev = pos+1;
if(prev < val.size()) {
pos = val.find('\n', prev);
if(pos == std::string::npos)
pos = val.size();
}
else {
break;
}
}
while(true);
for(int i = 0; i < curIndent; ++i)
file << " ";
file << ">>" << endl;
return;
}
}
else {
if(val.find('\n') != std::string::npos) {
file << key << ": " << escape(val) << endl;
return;
}
}
file << key << ": " << val << endl;
}
void writeEmptyLines(unsigned char n) {
unsigned i = n;
while(i--)
file << endl;
}
};
static WriteFile* makeWriteFile(const std::string& filename) {
return new WriteFile(filename.c_str());
}
const std::string err("ERR");
class FileList : public AtomicRefCounted {
public:
std::vector<std::pair<std::string,std::string>> files;
void navigate(const std::string& dirname, const std::string& filter, bool recurse, bool resolve) {
files.clear();
std::map<std::string, std::string> fmap;
std::string root = getProfileRoot();
if(!resolve || path_inside(root, dirname)) {
listDir(dirname, filter, recurse);
}
else {
devices.mods.listFiles(dirname, fmap, filter.c_str(), recurse);
files.reserve(fmap.size());
foreach(it, fmap)
files.push_back(std::pair<std::string,std::string>(it->first, it->second));
}
}
void listDir(const std::string& dirname, const std::string& filter, bool recurse = true) {
std::vector<std::string> flatfiles;
listDirectory(dirname, flatfiles, filter.c_str());
foreach(it, flatfiles) {
std::string abspath = path_join(dirname, *it);
files.push_back(std::pair<std::string,std::string>(*it, abspath));
if(recurse && ::isDirectory(abspath))
listDir(abspath, filter);
}
}
unsigned size() {
return (unsigned)files.size();
}
const std::string& relative(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
return files[i].first;
}
std::string basename(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
return getBasename(files[i].first);
}
std::string extension(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
auto pos = files[i].first.rfind('.');
if(pos == std::string::npos)
return "";
return files[i].first.substr(pos+1);
}
const std::string& absolute(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return err;
}
return files[i].second;
}
bool isDirectory(unsigned i) {
if(i >= files.size()) {
throwException("File index out of range.");
return false;
}
return ::isDirectory(files[i].second);
}
};
FileList* makeFileList(const std::string& dirname, const std::string& filter, bool recurse, bool resolve) {
FileList* flist = new FileList();
flist->navigate(dirname, filter, recurse, resolve);
return flist;
}
FileList* makeFileList_e() {
return new FileList();
}
static std::string resolveFilename(const std::string& fname) {
return devices.mods.resolve(fname);
}
//TODO: Restrict to the active mod?
static void deleteFile(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
if(isDirectory(fname)) {
std::vector<std::string> inside;
listDirectory(fname, inside);
foreach(it, inside)
deleteFile(path_join(fname, *it));
}
remove(fname.c_str());
}
static long long systemTime() {
time_t t;
::time(&t);
return (long long)t;
}
static std::string strftime_wrap(const std::string& format, long long stamp) {
time_t time = (time_t)stamp;
struct tm* timeinfo;
timeinfo = localtime(&time);
char buffer[120];
unsigned cnt = strftime(buffer, 120, format.c_str(), timeinfo);
return std::string(buffer, cnt);
}
void RegisterDatafiles() {
bind("string get_profileRoot()", asFUNCTION(getProfileRoot))
doc("", "Path to the game's global profile folder.");
bind("string get_modProfile()", asFUNCTION(getModProfile))
doc("", "Path to the current mod's profile folder.");
bind("string get_modProfile(const string&in folder)", asFUNCTION(getModProfileChild))
doc("Get the path to a child directory in the mod's profile.",
"Child directory", "Path to the folder.");
bind("string get_baseProfile(const string&in folder)", asFUNCTION(getBaseProfileChild))
doc("Get the path to a child directory in the base profile.",
"Child directory", "Path to the folder.");
bind("string resolve(const string&in filename)", asFUNCTION(resolveFilename))
doc("Resolves the filename to an absolute path to an overriden file. "
"The top-most mod with a file in the same relative path is used.",
"Relative filename to resolve.", "");
bind("void deleteFile(const string&in filename)", asFUNCTION(deleteFile))
doc("Deletes the specified file. Must be within the profile or game folders.", "");
bind("void makeDirectory(const string&in dirname)", asFUNCTION(createDir))
doc("Create a new directory.", "Name of the directory to create.");
bind("bool isDirectory(const string&in path)", asFUNCTION(isDir))
doc("Check whether a particular path is a directory.", "Path of the directory to check.",
"Whether a directory exists at that path.");
bind("int64 getModifiedTime(const string&in path)", asFUNCTION(modTime))
doc("Get the timestamp a file was last modified at.", "Path of file.",
"Last modification time of file.");
bind("bool fileExists(const string&in path)", asFUNCTION(fileEx))
doc("Check whether a particular file exists.", "Path of the file to check.",
"Whether a file exists at that path.");
bind("string path_join(const string&in first, const string&in second)", asFUNCTION(path_join))
doc("Concatenate two file paths.", "First path.", "Second path.", "Concatenated path.");
bind("string path_up(const string&in path)", asFUNCTION(path_up))
doc("Go up one directory from a path.", "Path to go up from.", "Parent path.");
bind("bool path_inside(const string&in dirname, const string&in fname)", asFUNCTION(path_inside))
doc("Check whether a filename is inside a directory.", "Folder name.", "Filename"., "Whether the file is inside the folder.");
bind("string getBasename(const string&in path, bool includeExtension = true)", asFUNCTION(getBasename))
doc("Get the basename of a path.", "Path to get basename from.", "Whether to include the extension in the basename.",
"Path basename.");
bind("int64 getSystemTime()", asFUNCTION(systemTime));
bind("string strftime(const string& format, int64 time)", asFUNCTION(strftime_wrap));
ClassBind read("ReadFile", asOBJ_REF);
read.addFactory("ReadFile@ f(const string &in filename, bool allowLines = false)", asFUNCTION(makeReadFile))
doc("Opens a text datafile for reading.", "Path to the file to open. Must be within the profile or game folders.", "", "");
read.setReferenceFuncs(asMETHOD(ReadFile,grab), asMETHOD(ReadFile,drop));
read.addMember("int indent", offsetof(ReadFile, indent))
doc("Number of indents for the current line. Each space or tab counts as one indent.");
read.addMember("bool allowLines", offsetof(ReadFile, allowLines));
read.addMember("bool fullLine", offsetof(ReadFile, fullLine));
read.addMember("bool allowMultiline", offsetof(ReadFile, allowMultiline));
read.addMember("bool skipComments", offsetof(ReadFile, skipComments));
read.addMember("bool skipEmpty", offsetof(ReadFile, skipEmpty));
read.addMember("string line", offsetof(ReadFile, line))
doc("Full (trimmed) line.");
read.addMember("string key", offsetof(ReadFile, key))
doc("Key from a Key:Value pair line.");
read.addMember("string value", offsetof(ReadFile, value))
doc("Value from a Key:Value pair line.");
read.addMethod("void error(const string& message)", asMETHOD(ReadFile, error))
doc("Display an error message sourced from this file.", "Error message.");
read.addMethod("string position()", asMETHOD(ReadFile, position))
doc("Returns a formatted representation of the position in the file.", "A string like 'data/states.txt | Line 80'");
read.addExternMethod("bool opPostInc()", asFUNCTION(readNext))
doc("Advances to the next non-empty line.", "Returns true if there was a line to advance to.");
ClassBind write("WriteFile", asOBJ_REF);
write.addFactory("WriteFile@ f(const string &in)", asFUNCTION(makeWriteFile));
write.setReferenceFuncs(asMETHOD(WriteFile,grab), asMETHOD(WriteFile,drop));
write.addMember("bool allowMultiline", offsetof(WriteFile, allowMultiline))
doc("Whether multi-line values should be written in multiline format or squashed into a single line.");
write.addMethod("bool get_open() const", asMETHOD(WriteFile,is_open));
write.addMethod("bool get_atEnd() const", asMETHOD(WriteFile,is_atEnd));
write.addMethod("void indent(int amount = 1)", asMETHOD(WriteFile, indent));
write.addMethod("void deindent(int amount = 1)", asMETHOD(WriteFile, deindent));
write.addMethod("void writeLine(const string &in)", asMETHOD(WriteFile, writeLine));
write.addMethod("void writeKeyValue(const string &in, const string &in)", asMETHOD(WriteFile, writeKeyValue));
write.addMethod("void writeEmptyLines(uint8 count = 1)", asMETHOD(WriteFile, writeEmptyLines));
ClassBind flist("FileList", asOBJ_REF);
flist.addFactory("FileList@ f(const string &in dirname, const string &in filter, bool recurse = false, bool resolve = true)", asFUNCTION(makeFileList));
flist.addFactory("FileList@ f()", asFUNCTION(makeFileList_e));
flist.setReferenceFuncs(asMETHOD(FileList,grab), asMETHOD(FileList,drop));
flist.addMethod("void navigate(const string&in dirname, const string&in filter, bool recurse = false, bool resolve = true)", asMETHOD(FileList, navigate));
flist.addMethod("uint get_length()", asMETHOD(FileList, size));
flist.addMethod("const string& get_relativePath(uint num)", asMETHOD(FileList, relative));
flist.addMethod("string get_basename(uint num)", asMETHOD(FileList, basename));
flist.addMethod("string get_extension(uint num)", asMETHOD(FileList, extension));
flist.addMethod("const string& get_path(uint num)", asMETHOD(FileList, absolute));
flist.addMethod("bool get_isDirectory(uint num)", asMETHOD(FileList, isDirectory));
}
};