what the fuck it's 1:30 AM. god

This commit is contained in:
Kae 2023-06-24 01:30:55 +10:00
parent 6832c10ed5
commit 2798d4bf66
11 changed files with 491 additions and 347 deletions

View File

@ -1,4 +1,5 @@
#include "StarAssets.hpp"
#include "StarAssetPath.hpp"
#include "StarFile.hpp"
#include "StarTime.hpp"
#include "StarDirectoryAssetSource.hpp"
@ -59,169 +60,6 @@ static void validatePath(AssetPath const& components, bool canContainSubPath, bo
throw AssetException::format("Path '%s' cannot contain directives", components);
}
// The filename is everything after the last slash (excluding directives) and
// up to the first directive marker.
static Maybe<pair<size_t, size_t>> findFilenameRange(std::string const& pathUtf8) {
size_t firstDirectiveOrSubPath = pathUtf8.find_first_of(":?");
size_t filenameStart = 0;
while (true) {
size_t find = pathUtf8.find('/', filenameStart);
if (find >= firstDirectiveOrSubPath)
break;
filenameStart = find + 1;
}
if (filenameStart == NPos) {
return {};
} else if (firstDirectiveOrSubPath == NPos) {
return {{filenameStart, pathUtf8.size()}};
} else {
return {{filenameStart, firstDirectiveOrSubPath}};
}
}
AssetPath AssetPath::split(String const& path) {
auto i = path.begin();
auto end = path.end();
AssetPath components;
// base paths cannot have any ':' or '?' characters, stop at the first one.
while (i != end) {
String::Char c = *i;
if (c == ':' || c == '?')
break;
components.basePath += c;
++i;
}
// Sub-paths must immediately follow base paths and must start with a ':',
// after this point any further ':' characters are not special.
if (i != end && *i == ':') {
++i;
while (i != end) {
String::Char c = *i;
if (c == '?')
break;
if (!components.subPath)
components.subPath.emplace();
*components.subPath += c;
++i;
}
}
// Directives must follow the base path and optional sub-path, and each
// directive is separated by one or more '?' characters.
while (i != end && *i == '?') {
++i;
String directive;
while (i != end) {
String::Char c = *i;
if (c == '?')
break;
directive += c;
++i;
}
if (!directive.empty())
components.directives.append(move(directive));
}
starAssert(i == end);
return components;
}
String AssetPath::join(AssetPath const& components) {
return toString(components);
}
String AssetPath::setSubPath(String const& path, String const& subPath) {
auto components = split(path);
components.subPath = subPath;
return join(components);
}
String AssetPath::removeSubPath(String const& path) {
auto components = split(path);
components.subPath.reset();
return join(components);
}
String AssetPath::getDirectives(String const& path) {
size_t firstDirective = path.find('?');
if (firstDirective == NPos)
return {};
return path.substr(firstDirective + 1);
}
String AssetPath::addDirectives(String const& path, String const& directives) {
return String::joinWith("?", path, directives);
}
String AssetPath::removeDirectives(String const& path) {
size_t firstDirective = path.find('?');
if (firstDirective == NPos)
return path;
return path.substr(0, firstDirective);
}
String AssetPath::directory(String const& path) {
if (auto p = findFilenameRange(path.utf8())) {
return String(path.utf8().substr(0, p->first));
} else {
return String();
}
}
String AssetPath::filename(String const& path) {
if (auto p = findFilenameRange(path.utf8())) {
return String(path.utf8().substr(p->first, p->second));
} else {
return String();
}
}
String AssetPath::extension(String const& path) {
auto file = filename(path);
auto lastDot = file.findLast(".");
if (lastDot == NPos)
return "";
return file.substr(lastDot + 1);
}
String AssetPath::relativeTo(String const& sourcePath, String const& givenPath) {
if (!givenPath.empty() && givenPath[0] == '/')
return givenPath;
auto path = directory(sourcePath);
path.append(givenPath);
return path;
}
bool AssetPath::operator==(AssetPath const& rhs) const {
return tie(basePath, subPath, directives) == tie(rhs.basePath, rhs.subPath, rhs.directives);
}
std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << rhs.basePath;
if (rhs.subPath) {
os << ":";
os << *rhs.subPath;
}
for (auto const& directive : rhs.directives) {
os << "?";
os << directive;
}
return os;
}
Maybe<RectU> FramesSpecification::getRect(String const& frame) const {
if (auto alias = aliases.ptr(frame)) {
return frames.get(*alias);

View File

@ -7,6 +7,7 @@
#include "StarBiMap.hpp"
#include "StarThread.hpp"
#include "StarAssetSource.hpp"
#include "StarAssetPath.hpp"
namespace Star {
@ -18,61 +19,6 @@ STAR_CLASS(Assets);
STAR_EXCEPTION(AssetException, StarException);
// Asset paths are not filesystem paths. '/' is always the directory separator,
// and it is not possible to escape any asset source directory. '\' is never a
// valid directory separator. All asset paths are considered case-insensitive.
//
// In addition to the path portion of the asset path, some asset types may also
// have a sub-path, which is always separated from the path portion of the asset
// by ':'. There can be at most 1 sub-path component.
//
// Image paths may also have a directives portion of the full asset path, which
// must come after the path and optional sub-path comopnent. The directives
// portion of the path starts with a '?', and '?' separates each subsquent
// directive.
struct AssetPath {
static AssetPath split(String const& path);
static String join(AssetPath const& path);
// Get / modify sub-path directly on a joined path string
static String setSubPath(String const& joinedPath, String const& subPath);
static String removeSubPath(String const& joinedPath);
// Get / modify directives directly on a joined path string
static String getDirectives(String const& joinedPath);
static String addDirectives(String const& joinedPath, String const& directives);
static String removeDirectives(String const& joinedPath);
// The base directory name for any given path, including the trailing '/'.
// Ignores sub-path and directives.
static String directory(String const& path);
// The file part of any given path, ignoring sub-path and directives. Path
// must be a file not a directory.
static String filename(String const& path);
// The file extension of a given file path, ignoring directives and
// sub-paths.
static String extension(String const& path);
// Computes an absolute asset path from a relative path relative to another
// asset. The sourcePath must be an absolute path (may point to a directory
// or an asset in a directory, and ignores ':' sub-path or ? directives),
// and the givenPath may be either an absolute *or* a relative path. If it
// is an absolute path, it is returned unchanged. If it is a relative path,
// then it is computed as relative to the directory component of the
// sourcePath.
static String relativeTo(String const& sourcePath, String const& givenPath);
String basePath;
Maybe<String> subPath;
StringList directives;
bool operator==(AssetPath const& rhs) const;
};
std::ostream& operator<<(std::ostream& os, AssetPath const& rhs);
// The contents of an assets .frames file, which can be associated with one or
// more images, and specifies named sub-rects of those images.
struct FramesSpecification {

View File

@ -7,6 +7,7 @@ SET (star_core_HEADERS
StarAStar.hpp
StarAlgorithm.hpp
StarArray.hpp
StarAssetPath.hpp
StarAtomicSharedPtr.hpp
StarAudio.hpp
StarBTree.hpp
@ -23,6 +24,7 @@ SET (star_core_HEADERS
StarDataStream.hpp
StarDataStreamDevices.hpp
StarDataStreamExtra.hpp
StarDirectives.hpp
StarDynamicLib.hpp
StarEither.hpp
StarEncode.hpp
@ -121,6 +123,7 @@ SET (star_core_HEADERS
SET (star_core_SOURCES
StarAudio.cpp
StarAssetPath.cpp
StarBTreeDatabase.cpp
StarBuffer.cpp
StarByteArray.cpp
@ -128,6 +131,7 @@ SET (star_core_SOURCES
StarCompression.cpp
StarDataStream.cpp
StarDataStreamDevices.cpp
StarDirectives.cpp
StarEncode.cpp
StarFile.cpp
StarFont.cpp

View File

@ -0,0 +1,165 @@
#include "StarAssetPath.hpp"
#include "StarLexicalCast.hpp"
namespace Star {
// The filename is everything after the last slash (excluding directives) and
// up to the first directive marker.
static Maybe<pair<size_t, size_t>> findFilenameRange(std::string const& pathUtf8) {
size_t firstDirectiveOrSubPath = pathUtf8.find_first_of(":?");
size_t filenameStart = 0;
while (true) {
size_t find = pathUtf8.find('/', filenameStart);
if (find >= firstDirectiveOrSubPath)
break;
filenameStart = find + 1;
}
if (filenameStart == NPos) {
return {};
} else if (firstDirectiveOrSubPath == NPos) {
return {{filenameStart, pathUtf8.size()}};
} else {
return {{filenameStart, firstDirectiveOrSubPath}};
}
}
AssetPath AssetPath::split(String const& path) {
auto i = path.begin();
auto end = path.end();
AssetPath components;
// base paths cannot have any ':' or '?' characters, stop at the first one.
while (i != end) {
String::Char c = *i;
if (c == ':' || c == '?')
break;
components.basePath += c;
++i;
}
// Sub-paths must immediately follow base paths and must start with a ':',
// after this point any further ':' characters are not special.
if (i != end && *i == ':') {
++i;
while (i != end) {
String::Char c = *i;
if (c == '?')
break;
if (!components.subPath)
components.subPath.emplace();
*components.subPath += c;
++i;
}
}
// Directives must follow the base path and optional sub-path, and each
// directive is separated by one or more '?' characters.
while (i != end && *i == '?') {
String directives;
while (i != end) {
directives.append(*i);
++i;
}
if (!directives.empty());
components.directives.append(move(directives));
}
starAssert(i == end);
return components;
}
String AssetPath::join(AssetPath const& components) {
return toString(components);
}
String AssetPath::setSubPath(String const& path, String const& subPath) {
auto components = split(path);
components.subPath = subPath;
return join(components);
}
String AssetPath::removeSubPath(String const& path) {
auto components = split(path);
components.subPath.reset();
return join(components);
}
String AssetPath::getDirectives(String const& path) {
size_t firstDirective = path.find('?');
if (firstDirective == NPos)
return {};
return path.substr(firstDirective + 1);
}
String AssetPath::addDirectives(String const& path, String const& directives) {
return String::joinWith("?", path, directives);
}
String AssetPath::removeDirectives(String const& path) {
size_t firstDirective = path.find('?');
if (firstDirective == NPos)
return path;
return path.substr(0, firstDirective);
}
String AssetPath::directory(String const& path) {
if (auto p = findFilenameRange(path.utf8())) {
return String(path.utf8().substr(0, p->first));
} else {
return String();
}
}
String AssetPath::filename(String const& path) {
if (auto p = findFilenameRange(path.utf8())) {
return String(path.utf8().substr(p->first, p->second));
} else {
return String();
}
}
String AssetPath::extension(String const& path) {
auto file = filename(path);
auto lastDot = file.findLast(".");
if (lastDot == NPos)
return "";
return file.substr(lastDot + 1);
}
String AssetPath::relativeTo(String const& sourcePath, String const& givenPath) {
if (!givenPath.empty() && givenPath[0] == '/')
return givenPath;
auto path = directory(sourcePath);
path.append(givenPath);
return path;
}
bool AssetPath::operator==(AssetPath const& rhs) const {
return tie(basePath, subPath, directives) == tie(rhs.basePath, rhs.subPath, rhs.directives);
}
std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << rhs.basePath;
if (rhs.subPath) {
os << ":";
os << *rhs.subPath;
}
rhs.directives.forEach([&](ImageOperation const& operation, String const& string) {
os << "?";
os << string;
});
return os;
}
}

View File

@ -0,0 +1,65 @@
#ifndef STAR_ASSET_PATH_HPP
#define STAR_ASSET_PATH_HPP
#include "StarDirectives.hpp"
namespace Star {
// Asset paths are not filesystem paths. '/' is always the directory separator,
// and it is not possible to escape any asset source directory. '\' is never a
// valid directory separator. All asset paths are considered case-insensitive.
//
// In addition to the path portion of the asset path, some asset types may also
// have a sub-path, which is always separated from the path portion of the asset
// by ':'. There can be at most 1 sub-path component.
//
// Image paths may also have a directives portion of the full asset path, which
// must come after the path and optional sub-path comopnent. The directives
// portion of the path starts with a '?', and '?' separates each subsquent
// directive.
struct AssetPath {
static AssetPath split(String const& path);
static String join(AssetPath const& path);
// Get / modify sub-path directly on a joined path string
static String setSubPath(String const& joinedPath, String const& subPath);
static String removeSubPath(String const& joinedPath);
// Get / modify directives directly on a joined path string
static String getDirectives(String const& joinedPath);
static String addDirectives(String const& joinedPath, String const& directives);
static String removeDirectives(String const& joinedPath);
// The base directory name for any given path, including the trailing '/'.
// Ignores sub-path and directives.
static String directory(String const& path);
// The file part of any given path, ignoring sub-path and directives. Path
// must be a file not a directory.
static String filename(String const& path);
// The file extension of a given file path, ignoring directives and
// sub-paths.
static String extension(String const& path);
// Computes an absolute asset path from a relative path relative to another
// asset. The sourcePath must be an absolute path (may point to a directory
// or an asset in a directory, and ignores ':' sub-path or ? directives),
// and the givenPath may be either an absolute *or* a relative path. If it
// is an absolute path, it is returned unchanged. If it is a relative path,
// then it is computed as relative to the directory component of the
// sourcePath.
static String relativeTo(String const& sourcePath, String const& givenPath);
String basePath;
Maybe<String> subPath;
NestedDirectives directives;
bool operator==(AssetPath const& rhs) const;
};
std::ostream& operator<<(std::ostream& os, AssetPath const& rhs);
}
#endif

View File

@ -0,0 +1,154 @@
#include "StarImage.hpp"
#include "StarImageProcessing.hpp"
#include "StarDirectives.hpp"
namespace Star {
NestedDirectives::NestedDirectives() : m_root(nullptr) {}
NestedDirectives::NestedDirectives(String const& directives) {
parseDirectivesIntoLeaf(directives);
}
NestedDirectives::NestedDirectives(String&& directives) {
String mine = move(directives); // most useless move constructor in the world
parseDirectivesIntoLeaf(mine);
}
void NestedDirectives::parseDirectivesIntoLeaf(String const& directives) {
Leaf leaf;
for (String& op : directives.split('?')) {
if (!op.empty()) {
leaf.operations.append(imageOperationFromString(op));
leaf.strings.append(move(op));
}
}
m_root = std::make_shared<Cell>(move(leaf));
}
bool NestedDirectives::empty() const {
return (bool)m_root;
}
void NestedDirectives::append(const NestedDirectives& other) {
convertToBranches().emplace_back(other.branch());
}
String NestedDirectives::toString() const {
String string;
addToString(string);
return string;
}
void NestedDirectives::addToString(String& string) const {
if (m_root)
m_root->buildString(string);
}
void NestedDirectives::forEach(LeafCallback callback) const {
if (m_root)
m_root->forEach(callback);
}
void NestedDirectives::forEachPair(LeafPairCallback callback) const {
if (m_root) {
LeafCallback pairCallback = [&](Leaf const& leaf) {
size_t length = leaf.length();
for (size_t i = 0; i != length; ++i)
callback(leaf.operations.at(i), leaf.strings.at(i));
};
m_root->forEach(pairCallback);
}
}
bool NestedDirectives::forEachAbortable(AbortableLeafCallback callback) const {
if (!m_root)
return false;
else
return m_root->forEachAbortable(callback);
}
bool NestedDirectives::forEachPairAbortable(AbortableLeafPairCallback callback) const {
if (!m_root)
return false;
else {
AbortableLeafCallback pairCallback = [&](Leaf const& leaf) -> bool {
size_t length = leaf.length();
for (size_t i = 0; i != length; ++i) {
if (!callback(leaf.operations.at(i), leaf.strings.at(i)))
return false;
}
return true;
};
return m_root->forEachAbortable(pairCallback);
}
}
Image NestedDirectives::apply(Image& image) const {
Image current = image;
forEach([&](Leaf const& leaf) {
current = processImageOperations(leaf.operations, current);
});
return current;
}
NestedDirectives::Branches& NestedDirectives::convertToBranches() {
if (!m_root) {
m_root = std::make_shared<Cell>(Branches());
}
else if (m_root->value.is<Branches>())
return;
Leaf& leaf = m_root->value.get<Leaf>();
Branches newBranches;
newBranches.emplace_back(std::make_shared<Cell const>(move(leaf)));
m_root->value = move(newBranches);
return m_root->value.get<Branches>();
}
size_t NestedDirectives::Leaf::length() const {
if (operations.size() != strings.size())
throw DirectivesException("NestedDirectives leaf has mismatching operation/string List sizes");
return operations.size();
}
NestedDirectives::Cell::Cell() : value(Leaf()) {};
NestedDirectives::Cell::Cell(Leaf&& leaf) : value(move(leaf)) {};
NestedDirectives::Cell::Cell(Branches&& branches) : value(move(branches)) {};
NestedDirectives::Cell::Cell(const Leaf& leaf) : value(leaf) {};
NestedDirectives::Cell::Cell(const Branches& branches) : value(branches) {};
void NestedDirectives::Cell::buildString(String& string) const {
if (auto leaf = value.ptr<Leaf>())
for (auto& leafString : leaf->strings) {
string += "?";
string += leafString;
}
else {
for (auto& branch : value.get<Branches>())
branch->buildString(string);
}
}
void NestedDirectives::Cell::forEach(LeafCallback& callback) const {
if (auto leaf = value.ptr<Leaf>())
callback(*leaf);
else {
for (auto& branch : value.get<Branches>())
branch->forEach(callback);
}
}
bool NestedDirectives::Cell::forEachAbortable(AbortableLeafCallback& callback) const {
if (auto leaf = value.ptr<Leaf>()) {
if (!callback(*leaf))
return false;
} else {
for (auto& branch : value.get<Branches>())
if (!branch->forEachAbortable(callback))
return false;
}
return true;
}
}

View File

@ -0,0 +1,78 @@
#ifndef STAR_DIRECTIVES_HPP
#define STAR_DIRECTIVES_HPP
#include "StarImageProcessing.hpp"
namespace Star {
STAR_CLASS(NestedDirectives);
STAR_EXCEPTION(DirectivesException, StarException);
// Kae: My attempt at reducing memory allocation and per-frame string parsing for extremely long directives
class NestedDirectives {
public:
struct Leaf {
List<ImageOperation> operations;
List<String> strings;
size_t length() const;
};
typedef function<void(Leaf const&)> LeafCallback;
typedef function<void(ImageOperation const&, String const&)> LeafPairCallback;
typedef function<bool(Leaf const&)> AbortableLeafCallback;
typedef function<bool(ImageOperation const&, String const&)> AbortableLeafPairCallback;
struct Cell;
typedef std::shared_ptr<Cell> Branch;
typedef std::shared_ptr<Cell const> ConstBranch;
typedef List<ConstBranch> Branches;
struct Cell {
Variant<Leaf, Branches> value;
Cell();
Cell(Leaf&& leaf);
Cell(Branches&& branches);
Cell(const Leaf& leaf);
Cell(const Branches& branches);
void buildString(String& string) const;
void forEach(LeafCallback& callback) const;
bool forEachAbortable(AbortableLeafCallback& callback) const;
};
NestedDirectives();
NestedDirectives(String const& directives);
NestedDirectives(String&& directives);
void parseDirectivesIntoLeaf(String const& directives);
bool empty() const;
void append(const NestedDirectives& other);
const ConstBranch& branch() const;
String toString() const;
void addToString(String& string) const;
void forEach(LeafCallback callback) const;
void forEachPair(LeafPairCallback callback) const;
bool forEachAbortable(AbortableLeafCallback callback) const;
bool forEachPairAbortable(AbortableLeafPairCallback callback) const;
Image apply(Image& image) const;
private:
void buildString(String& string, const Cell& cell) const;
Branches& convertToBranches();
Branch m_root;
};
typedef NestedDirectives ImageDirectives;
}
#endif

View File

@ -38,7 +38,6 @@ SET (star_game_HEADERS
StarDamageManager.hpp
StarDamageTypes.hpp
StarDanceDatabase.hpp
StarDirectives.hpp
StarDrawable.hpp
StarDungeonGenerator.hpp
StarDungeonImagePart.hpp
@ -297,7 +296,6 @@ SET (star_game_SOURCES
StarDamageManager.cpp
StarDamageTypes.cpp
StarDanceDatabase.cpp
StarDirectives.cpp
StarDrawable.cpp
StarDungeonGenerator.cpp
StarDungeonImagePart.cpp

View File

@ -1,51 +0,0 @@
#include "StarImage.hpp"
#include "StarImageProcessing.hpp"
#include "StarDirectives.hpp"
namespace Star {
NestedDirectives::NestedDirectives() {}
NestedDirectives::NestedDirectives(String const& string) : m_root{ Leaf{ parseImageOperations(string), string} } {}
void NestedDirectives::addBranch(const Branch& newBranch) {
convertToBranches();
m_root.value.get<Branches>().emplace_back(newBranch);
}
String NestedDirectives::toString() const {
String string;
buildString(string, m_root);
return string;
}
void NestedDirectives::forEach() const {
}
Image NestedDirectives::apply(Image& image) const {
Image current = image;
return current;
}
void NestedDirectives::buildString(String& string, const Cell& cell) const {
if (auto leaf = cell.value.ptr<Leaf>())
string += leaf->string;
else {
for (auto& branch : cell.value.get<Branches>())
buildString(string, *branch);
}
}
void NestedDirectives::convertToBranches() {
if (m_root.value.is<Branches>())
return;
Leaf& leaf = m_root.value.get<Leaf>();
Branches newBranches;
newBranches.emplace_back(std::make_shared<Cell const>(move(leaf)));
m_root.value = move(newBranches);
}
}

View File

@ -1,50 +0,0 @@
#ifndef STAR_DIRECTIVES_HPP
#define STAR_DIRECTIVES_HPP
#include "StarImageProcessing.hpp"
namespace Star {
STAR_CLASS(NestedDirectives);
// Attempt at reducing memory allocation and per-frame string parsing for extremely long directives
class NestedDirectives {
public:
struct Leaf {
List<ImageOperation> operations;
String string;
};
struct Cell;
typedef std::shared_ptr<Cell const> Branch;
typedef List<Branch> Branches;
struct Cell {
Variant<Leaf, Branches> value;
Cell() : value(Leaf()) {};
Cell(Leaf&& leaf) : value(move(leaf)) {};
Cell(Branches&& branches) : value(move(branches)) {};
Cell(const Leaf& leaf) : value(leaf) {};
Cell(const Branches& branches) : value(branches) {};
};
NestedDirectives();
NestedDirectives(String const& string);
void addBranch(const Branch& newBranch);
const Branch& branch() const;
String toString() const;
void forEach() const;
Image apply(Image& image) const;
private:
void buildString(String& string, const Cell& cell) const;
void convertToBranches();
Cell m_root;
};
}
#endif

View File

@ -119,22 +119,20 @@ RectU ImageMetadataDatabase::nonEmptyRegion(String const& path) const {
String ImageMetadataDatabase::filterProcessing(String const& path) {
AssetPath components = AssetPath::split(path);
auto directives = move(components.directives);
String joined = AssetPath::join(components);
components.directives.filter([](String const& directive) {
ImageOperation operation;
try {
operation = imageOperationFromString(directive);
} catch (StarException const&) {
return true;
}
return !(operation.is<HueShiftImageOperation>() ||
operation.is<SaturationShiftImageOperation>() ||
operation.is<BrightnessMultiplyImageOperation>() ||
operation.is<FadeToColorImageOperation>() ||
operation.is<ScanLinesImageOperation>() ||
operation.is<SetColorImageOperation>());
});
directives.forEachPair([&](ImageOperation const& operation, String const& string) {
if (!(operation.is<HueShiftImageOperation>() ||
operation.is<SaturationShiftImageOperation>() ||
operation.is<BrightnessMultiplyImageOperation>() ||
operation.is<FadeToColorImageOperation>() ||
operation.is<ScanLinesImageOperation>() ||
operation.is<SetColorImageOperation>())) {
joined += "?";
joined += string;
}
});
return AssetPath::join(components);
}
@ -231,19 +229,18 @@ Vec2U ImageMetadataDatabase::calculateImageSize(String const& path) const {
OperationSizeAdjust osa(imageSize);
for (auto const& directive : components.directives) {
ImageOperation operation;
try {
operation = imageOperationFromString(directive);
} catch (StarException const&) {
return fallback();
bool complete = components.directives.forEachAbortable([&](auto const& leaf) -> bool {
for (const ImageOperation& operation : leaf.operations) {
operation.call(osa);
if (osa.hasError())
return false;
else
return true;
}
});
operation.call(osa);
if (osa.hasError) {
return fallback();
}
}
if (!complete)
return fallback();
return imageSize;
}