70 lines
2.9 KiB
C++
70 lines
2.9 KiB
C++
/*
|
|
Crafter®.Asset
|
|
Copyright (C) 2026 Catcrafts®
|
|
catcrafts.net
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License version 3.0 as published by the Free Software Foundation;
|
|
|
|
This library 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
export module Crafter.Asset:Compression;
|
|
import std;
|
|
|
|
export namespace Crafter::Compression {
|
|
// Hard-aborts the process after printing `msg`. Used in place of
|
|
// `throw std::runtime_error(...)` so the module is buildable under
|
|
// `-fno-exceptions` (wasm targets ship without the C++ unwinding
|
|
// runtime). Asset-loading errors are unrecoverable in practice — a
|
|
// bad magic, version mismatch, or stream-size mismatch always means
|
|
// the build's data is inconsistent with the binary.
|
|
[[noreturn]] inline void Fatal(std::string_view msg) {
|
|
std::println(std::cerr, "Crafter.Asset fatal: {}", msg);
|
|
std::abort();
|
|
}
|
|
|
|
// One independently-decompressable GDeflate stream inside CompressedBlob::bytes.
|
|
// Layout matches what VK_EXT_memory_decompression's VkDecompressMemoryRegionEXT
|
|
// expects, minus the device addresses (which the consumer fills in).
|
|
struct RegionMeta {
|
|
std::uint64_t srcOffset;
|
|
std::uint64_t compressedSize;
|
|
std::uint64_t decompressedSize;
|
|
};
|
|
|
|
struct CompressedBlob {
|
|
std::vector<std::byte> bytes;
|
|
std::vector<RegionMeta> regions;
|
|
|
|
std::uint64_t TotalDecompressedSize() const noexcept {
|
|
std::uint64_t sum = 0;
|
|
for (const RegionMeta& r : regions) sum += r.decompressedSize;
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
// Compresses each input span as its own GDeflate tile-stream; concatenates
|
|
// them into one byte buffer with a parallel region table. Streams are
|
|
// independent and can be addressed individually by VkDecompressMemoryRegionEXT
|
|
// entries on the GPU path.
|
|
CompressedBlob CompressStreams(std::span<const std::span<const std::byte>> streams);
|
|
|
|
// CPU fallback decoder. outputs.size() must equal blob.regions.size();
|
|
// outputs[i].size() must equal blob.regions[i].decompressedSize.
|
|
void DecompressCPU(const CompressedBlob& blob, std::span<const std::span<std::byte>> outputs);
|
|
|
|
// Length-prefixed serialization of a CompressedBlob. Used by per-asset
|
|
// SaveCompressed/LoadCompressed implementations after they've written
|
|
// their own type-specific header.
|
|
void WriteBlob(std::ostream& file, const CompressedBlob& blob);
|
|
CompressedBlob ReadBlob(std::istream& file);
|
|
}
|