yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Theresa FoleyCleanups related to RIFF support (#7041)4c76b2759

master
8.5 KiB306 linesraw
1#include "slang-riff-file-system.h"
2
3#include "slang-blob.h"
4#include "slang-com-helper.h"
5#include "slang-com-ptr.h"
6
7// Compression systems
8#include "slang-deflate-compression-system.h"
9#include "slang-lz4-compression-system.h"
10
11namespace Slang
12{
13
14RiffFileSystem::RiffFileSystem(ICompressionSystem* compressionSystem)
15    : m_compressionSystem(compressionSystem)
16{
17}
18
19void* RiffFileSystem::getInterface(const Guid& guid)
20{
21    if (auto ptr = Super::getInterface(guid))
22    {
23        return ptr;
24    }
25    else if (guid == IArchiveFileSystem::getTypeGuid())
26    {
27        return static_cast<IArchiveFileSystem*>(this);
28    }
29    return nullptr;
30}
31
32void* RiffFileSystem::getObject(const Guid& guid)
33{
34    SLANG_UNUSED(guid);
35    return nullptr;
36}
37
38void* RiffFileSystem::castAs(const Guid& guid)
39{
40    if (auto ptr = getInterface(guid))
41    {
42        return ptr;
43    }
44    return getObject(guid);
45}
46
47SlangResult RiffFileSystem::loadFile(char const* path, ISlangBlob** outBlob)
48{
49    Entry* entry;
50    SLANG_RETURN_ON_FAIL(_loadFile(path, &entry));
51
52    ISlangBlob* contents = entry->m_contents;
53
54    if (m_compressionSystem)
55    {
56        // Okay lets decompress into a blob
57        ScopedAllocation alloc;
58        void* dst = alloc.allocateTerminated(entry->m_uncompressedSizeInBytes);
59        SLANG_RETURN_ON_FAIL(m_compressionSystem->decompress(
60            contents->getBufferPointer(),
61            contents->getBufferSize(),
62            entry->m_uncompressedSizeInBytes,
63            dst));
64
65        auto blob = RawBlob::moveCreate(alloc);
66
67        *outBlob = blob.detach();
68        return SLANG_OK;
69    }
70    else
71    {
72        // Just return as is
73        contents->addRef();
74        *outBlob = contents;
75        return SLANG_OK;
76    }
77}
78
79SlangResult RiffFileSystem::saveFile(const char* path, const void* data, size_t size)
80{
81    Entry* entry;
82    SLANG_RETURN_ON_FAIL(_requireFile(path, &entry));
83
84    ComPtr<ISlangBlob> contents;
85    if (m_compressionSystem)
86    {
87        // Lets try compressing the input
88        SLANG_RETURN_ON_FAIL(
89            m_compressionSystem->compress(&m_compressionStyle, data, size, contents.writeRef()));
90    }
91    else
92    {
93        // Just store the data directly.
94        contents = RawBlob::create(data, size);
95    }
96    entry->setContents(size, contents);
97    return SLANG_OK;
98}
99
100SlangResult RiffFileSystem::saveFileBlob(const char* path, ISlangBlob* dataBlob)
101{
102    if (!dataBlob)
103    {
104        return SLANG_E_INVALID_ARG;
105    }
106
107    if (m_compressionSystem)
108    {
109        return saveFile(path, dataBlob->getBufferPointer(), dataBlob->getBufferSize());
110    }
111    else
112    {
113        return Super::saveFileBlob(path, dataBlob);
114    }
115}
116
117SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeInBytes)
118{
119    // Load the riff
120    auto rootList = RIFF::RootChunk::getFromBlob(archive, archiveSizeInBytes);
121
122    // Make sure it's the right type
123    if (rootList == nullptr || rootList->getType() != RiffFileSystemBinary::kContainerFourCC)
124    {
125        return SLANG_FAIL;
126    }
127
128    // Clear the contents
129    _clear();
130
131    // Find the header
132    auto headerChunk = rootList->findDataChunk(RiffFileSystemBinary::kHeaderFourCC);
133
134    const auto header = headerChunk->readPayloadAs<RiffFileSystemBinary::Header>();
135
136    CompressionSystemType compressionType = CompressionSystemType(header.compressionSystemType);
137    switch (compressionType)
138    {
139    case CompressionSystemType::None:
140        {
141            // Null m_compressionSystem means no compression
142            m_compressionSystem.setNull();
143            break;
144        }
145    case CompressionSystemType::Deflate:
146        {
147            m_compressionSystem = DeflateCompressionSystem::getSingleton();
148            break;
149        }
150    case CompressionSystemType::LZ4:
151        {
152            m_compressionSystem = LZ4CompressionSystem::getSingleton();
153            break;
154        }
155    default:
156        return SLANG_FAIL;
157    }
158
159    // Read all of the contained data
160
161    {
162        for (auto chunk : rootList->getChildren())
163        {
164            auto dataChunk = as<RIFF::DataChunk>(chunk);
165            if (!dataChunk)
166                continue;
167
168            if (dataChunk->getType() != RiffFileSystemBinary::kEntryFourCC)
169                continue;
170
171            auto payloadData = (const uint8_t*)dataChunk->getPayload();
172            auto payloadSize = dataChunk->getPayloadSize();
173
174            if (payloadSize < sizeof(RiffFileSystemBinary::Entry))
175            {
176                return SLANG_FAIL;
177            }
178
179            MemoryReader reader(payloadData, payloadSize);
180
181            RiffFileSystemBinary::Entry srcEntry;
182            reader.read(srcEntry);
183
184            // Check if seems plausible
185            if (sizeof(RiffFileSystemBinary::Entry) + srcEntry.compressedSize + srcEntry.pathSize !=
186                payloadSize)
187            {
188                return SLANG_FAIL;
189            }
190
191            Entry dstEntry;
192
193            const char* path = (const char*)reader.getRemainingData();
194            reader.skip(srcEntry.pathSize);
195
196            dstEntry.m_canonicalPath = UnownedStringSlice(path, srcEntry.pathSize - 1);
197            dstEntry.m_type = (SlangPathType)srcEntry.pathType;
198            dstEntry.m_uncompressedSizeInBytes = srcEntry.uncompressedSize;
199
200            switch (dstEntry.m_type)
201            {
202            case SLANG_PATH_TYPE_FILE:
203                {
204                    if (reader.getRemainingSize() != srcEntry.compressedSize)
205                    {
206                        return SLANG_FAIL;
207                    }
208
209                    // Get the compressed data
210                    dstEntry.m_contents =
211                        RawBlob::create(reader.getRemainingData(), srcEntry.compressedSize);
212                    break;
213                }
214            case SLANG_PATH_TYPE_DIRECTORY:
215                break;
216            default:
217                return SLANG_FAIL;
218            }
219
220            // If it's the root entry we can ignore (as already added)
221            if (dstEntry.m_canonicalPath == ".")
222            {
223                continue;
224            }
225
226            // Add to the list of entries
227            m_entries.add(dstEntry.m_canonicalPath, dstEntry);
228        }
229    }
230
231    return SLANG_OK;
232}
233
234SlangResult RiffFileSystem::storeArchive(bool blobOwnsContent, ISlangBlob** outBlob)
235{
236    // All blobs are owned in this style
237    SLANG_UNUSED(blobOwnsContent)
238
239    RIFF::Builder builder;
240    RIFF::BuildCursor cursor(builder);
241    SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(cursor, RiffFileSystemBinary::kContainerFourCC);
242
243    {
244        RiffFileSystemBinary::Header header;
245        CompressionSystemType compressionSystemType = m_compressionSystem
246                                                          ? m_compressionSystem->getSystemType()
247                                                          : CompressionSystemType::None;
248        header.compressionSystemType = uint32_t(compressionSystemType);
249        cursor.addDataChunk(RiffFileSystemBinary::kHeaderFourCC, &header, sizeof(header));
250    }
251
252    for (const auto& [_, srcEntry] : m_entries)
253    {
254        // Ignore the root entry
255        if (srcEntry.m_canonicalPath == toSlice("."))
256        {
257            continue;
258        }
259
260        SLANG_SCOPED_RIFF_BUILDER_DATA_CHUNK(cursor, RiffFileSystemBinary::kEntryFourCC);
261
262        RiffFileSystemBinary::Entry dstEntry;
263        dstEntry.uncompressedSize = 0;
264        dstEntry.compressedSize = 0;
265        dstEntry.pathSize = uint32_t(srcEntry.m_canonicalPath.getLength() + 1);
266        dstEntry.pathType = srcEntry.m_type;
267
268        ISlangBlob* blob = srcEntry.m_contents;
269
270        if (srcEntry.m_type == SLANG_PATH_TYPE_FILE)
271        {
272            dstEntry.compressedSize = uint32_t(blob->getBufferSize());
273            dstEntry.uncompressedSize = uint32_t(srcEntry.m_uncompressedSizeInBytes);
274        }
275
276        // Entry header
277        cursor.addData(&dstEntry, sizeof(dstEntry));
278
279        // Path
280        cursor.addData(
281            srcEntry.m_canonicalPath.getBuffer(),
282            srcEntry.m_canonicalPath.getLength() + 1);
283
284        // Add the contained data without copying
285        if (blob)
286        {
287            cursor.addUnownedData(
288                const_cast<void*>(blob->getBufferPointer()),
289                blob->getBufferSize());
290        }
291    }
292
293    SLANG_RETURN_ON_FAIL(builder.writeToBlob(outBlob));
294    return SLANG_OK;
295}
296
297/* static */ bool RiffFileSystem::isArchive(const void* data, size_t sizeInBytes)
298{
299    auto rootList = RIFF::RootChunk::getFromBlob(data, sizeInBytes);
300    if (!rootList)
301        return false;
302
303    return rootList->getType() == RiffFileSystemBinary::kContainerFourCC;
304}
305
306} // namespace Slang