v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
158
source/base/StarPackedAssetSource.cpp
Normal file
158
source/base/StarPackedAssetSource.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue