Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/base/StarPackedAssetSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/base/StarPackedAssetSource.cpp')
-rw-r--r--source/base/StarPackedAssetSource.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/source/base/StarPackedAssetSource.cpp b/source/base/StarPackedAssetSource.cpp
new file mode 100644
index 0000000..fe16a80
--- /dev/null
+++ b/source/base/StarPackedAssetSource.cpp
@@ -0,0 +1,158 @@
+#include "StarPackedAssetSource.hpp"
+#include "StarDirectoryAssetSource.hpp"
+#include "StarOrderedSet.hpp"
+#include "StarDataStreamDevices.hpp"
+#include "StarDataStreamExtra.hpp"
+#include "StarSha256.hpp"
+#include "StarFile.hpp"
+
+namespace Star {
+
+void PackedAssetSource::build(DirectoryAssetSource& directorySource, String const& targetPackedFile,
+ StringList const& extensionSorting, BuildProgressCallback progressCallback) {
+ FilePtr file = File::open(targetPackedFile, IOMode::ReadWrite | IOMode::Truncate);
+
+ DataStreamIODevice ds(file);
+
+ ds.writeData("SBAsset6", 8);
+ // Skip 8 bytes, this will be a pointer to the index once we are done.
+ ds.seek(8, IOSeek::Relative);
+
+ // Insert every found entry into the packed file, and also simultaneously
+ // compute the full index.
+ StringMap<pair<uint64_t, uint64_t>> index;
+
+ OrderedHashSet<String> extensionOrdering;
+ for (auto const& str : extensionSorting)
+ extensionOrdering.add(str.toLower());
+
+ StringList assetPaths = directorySource.assetPaths();
+
+ // Returns a value for the asset that can be used to predictably sort assets
+ // by name and then by extension, where every extension listed in
+ // "extensionSorting" will come first, and then any extension not listed will
+ // come after.
+ auto getOrderingValue = [&extensionOrdering](String const& asset) -> pair<size_t, String> {
+ String extension;
+ auto lastDot = asset.findLast(".");
+ if (lastDot != NPos)
+ extension = asset.substr(lastDot + 1);
+
+ if (auto i = extensionOrdering.indexOf(extension.toLower())) {
+ return {*i, asset.toLower()};
+ } else {
+ return {extensionOrdering.size(), asset.toLower()};
+ }
+ };
+
+ assetPaths.sort([&getOrderingValue](String const& a, String const& b) {
+ return getOrderingValue(a) < getOrderingValue(b);
+ });
+
+ for (size_t i = 0; i < assetPaths.size(); ++i) {
+ String const& assetPath = assetPaths[i];
+ ByteArray contents = directorySource.read(assetPath);
+
+ if (progressCallback)
+ progressCallback(i, assetPaths.size(), directorySource.toFilesystem(assetPath), assetPath);
+ index.add(assetPath, {ds.pos(), contents.size()});
+ ds.writeBytes(contents);
+ }
+
+ uint64_t indexStart = ds.pos();
+ ds.writeData("INDEX", 5);
+ ds.write(directorySource.metadata());
+ ds.write(index);
+
+ ds.seek(8);
+ ds.write(indexStart);
+}
+
+PackedAssetSource::PackedAssetSource(String const& filename) {
+ m_packedFile = File::open(filename, IOMode::Read);
+
+ DataStreamIODevice ds(m_packedFile);
+ if (ds.readBytes(8) != ByteArray("SBAsset6", 8))
+ throw AssetSourceException("Packed assets file format unrecognized!");
+
+ uint64_t indexStart = ds.read<uint64_t>();
+
+ ds.seek(indexStart);
+ ByteArray header = ds.readBytes(5);
+ if (header != ByteArray("INDEX", 5))
+ throw AssetSourceException("No index header found!");
+ ds.read(m_metadata);
+ ds.read(m_index);
+}
+
+JsonObject PackedAssetSource::metadata() const {
+ return m_metadata;
+}
+
+StringList PackedAssetSource::assetPaths() const {
+ return m_index.keys();
+}
+
+IODevicePtr PackedAssetSource::open(String const& path) {
+ struct AssetReader : public IODevice {
+ AssetReader(FilePtr file, StreamOffset offset, StreamOffset size)
+ : file(file), fileOffset(offset), assetSize(size), assetPos(0) {
+ setMode(IOMode::Read);
+ }
+
+ size_t read(char* data, size_t len) override {
+ len = min<StreamOffset>(len, assetSize - assetPos);
+ file->readFullAbsolute(fileOffset + assetPos, data, len);
+ assetPos += len;
+ return len;
+ }
+
+ size_t write(char const*, size_t) override {
+ throw IOException("Assets IODevices are read-only");
+ }
+
+ StreamOffset size() override {
+ return assetSize;
+ }
+
+ StreamOffset pos() override {
+ return assetPos;
+ }
+
+ bool atEnd() override {
+ return assetPos >= assetSize;
+ }
+
+ void seek(StreamOffset p, IOSeek mode) override {
+ if (mode == IOSeek::Absolute)
+ assetPos = p;
+ else if (mode == IOSeek::Relative)
+ assetPos = clamp<StreamOffset>(assetPos + p, 0, assetSize);
+ else
+ assetPos = clamp<StreamOffset>(assetSize - p, 0, assetSize);
+ }
+
+ FilePtr file;
+ StreamOffset fileOffset;
+ StreamOffset assetSize;
+ StreamOffset assetPos;
+ };
+
+ auto p = m_index.ptr(path);
+ if (!p)
+ throw AssetSourceException::format("Requested file '%s' does not exist in the packed assets file", path);
+
+ return make_shared<AssetReader>(m_packedFile, p->first, p->second);
+}
+
+ByteArray PackedAssetSource::read(String const& path) {
+ auto p = m_index.ptr(path);
+ if (!p)
+ throw AssetSourceException::format("Requested file '%s' does not exist in the packed assets file", path);
+
+ ByteArray data(p->second, 0);
+ m_packedFile->readFullAbsolute(p->first, data.ptr(), p->second);
+ return data;
+}
+
+}