https://github.com/shader-slang/slang
Raw File
Tip revision: c5c8cfbb360d9a763f549df48636effde839eacd authored by Sai Praveen Bangaru on 27 September 2023, 00:50:13 UTC
Handle the case where the parent if-else region's after-block is unreachable. (#3241)
Tip revision: c5c8cfb
slang-riff-file-system.cpp
#include "slang-riff-file-system.h"

#include "../../slang-com-helper.h"
#include "../../slang-com-ptr.h"

#include "slang-blob.h"

// Compression systems
#include "slang-deflate-compression-system.h"
#include "slang-lz4-compression-system.h"

namespace Slang
{

RiffFileSystem::RiffFileSystem(ICompressionSystem* compressionSystem):
    m_compressionSystem(compressionSystem)
{
}

void* RiffFileSystem::getInterface(const Guid& guid)
{
    if (auto ptr = Super::getInterface(guid))
    {
        return ptr;
    }
    else if (guid == IArchiveFileSystem::getTypeGuid())
    {
        return static_cast<IArchiveFileSystem*>(this);
    }
    return nullptr;
}

void* RiffFileSystem::getObject(const Guid& guid)
{
    SLANG_UNUSED(guid);
    return nullptr;
}

void* RiffFileSystem::castAs(const Guid& guid)
{
    if (auto ptr = getInterface(guid))
    {
        return ptr;
    }
    return getObject(guid);
}

SlangResult RiffFileSystem::loadFile(char const* path, ISlangBlob** outBlob)
{
    Entry* entry;
    SLANG_RETURN_ON_FAIL(_loadFile(path, &entry));

    ISlangBlob* contents = entry->m_contents;

    if (m_compressionSystem)
    {
        // Okay lets decompress into a blob
        ScopedAllocation alloc;
        void* dst = alloc.allocateTerminated(entry->m_uncompressedSizeInBytes);
        SLANG_RETURN_ON_FAIL(m_compressionSystem->decompress(contents->getBufferPointer(), contents->getBufferSize(), entry->m_uncompressedSizeInBytes, dst));

        auto blob = RawBlob::moveCreate(alloc);

        *outBlob = blob.detach();
        return SLANG_OK;
    }
    else
    {
        // Just return as is
        contents->addRef();
        *outBlob = contents;
        return SLANG_OK;
    }
}

SlangResult RiffFileSystem::saveFile(const char* path, const void* data, size_t size)
{   
    Entry* entry;
    SLANG_RETURN_ON_FAIL(_requireFile(path, &entry));

    ComPtr<ISlangBlob> contents;
    if (m_compressionSystem)
    {
        // Lets try compressing the input
        SLANG_RETURN_ON_FAIL(m_compressionSystem->compress(&m_compressionStyle, data, size, contents.writeRef()));
    }
    else
    {
        // Just store the data directly.
        contents = RawBlob::create(data, size);
    }
    entry->setContents(size, contents);
    return SLANG_OK;
}

SlangResult RiffFileSystem::saveFileBlob(const char* path, ISlangBlob* dataBlob)
{
    if (!dataBlob)
    {
        return SLANG_E_INVALID_ARG;
    }

    if (m_compressionSystem)
    {
        return saveFile(path, dataBlob->getBufferPointer(), dataBlob->getBufferSize());
    }
    else
    {
        return Super::saveFileBlob(path, dataBlob);
    }
}

SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeInBytes)
{
    // Load the riff
    RiffContainer container;

    MemoryStreamBase stream(FileAccess::Read, archive, archiveSizeInBytes);
    SLANG_RETURN_ON_FAIL(RiffUtil::read(&stream, container));

    RiffContainer::ListChunk* rootList = container.getRoot();
    // Make sure it's the right type
    if (rootList == nullptr || rootList->m_fourCC != RiffFileSystemBinary::kContainerFourCC)
    {
        return SLANG_FAIL;
    }

    // Clear the contents
    _clear();

    // Find the header
    const auto header = rootList->findContainedData<RiffFileSystemBinary::Header>(RiffFileSystemBinary::kHeaderFourCC);

    CompressionSystemType compressionType = CompressionSystemType(header->compressionSystemType);
    switch (compressionType)
    {
        case CompressionSystemType::None:
        {
            // Null m_compressionSystem means no compression
            m_compressionSystem.setNull();
            break;
        }
        case CompressionSystemType::Deflate:
        {
            m_compressionSystem = DeflateCompressionSystem::getSingleton();
            break;
        }
        case CompressionSystemType::LZ4:
        {
            m_compressionSystem = LZ4CompressionSystem::getSingleton();
            break;
        }
        default: return SLANG_FAIL;
    }

    // Read all of the contained data

    {
        List<RiffContainer::DataChunk*> srcEntries;
        rootList->findContained(RiffFileSystemBinary::kEntryFourCC, srcEntries);

        for (auto chunk : srcEntries)
        {
            auto data = chunk->getSingleData();

            const uint8_t* srcData = (const uint8_t*)data->getPayload();
            const size_t dataSize = data->getSize();

            if (dataSize < sizeof(RiffFileSystemBinary::Entry))
            {
                return SLANG_FAIL;
            }

            auto srcEntry = (const RiffFileSystemBinary::Entry*)srcData;
            srcData += sizeof(*srcEntry);

            // Check if seems plausible
            if (sizeof(RiffFileSystemBinary::Entry) + srcEntry->compressedSize + srcEntry->pathSize != dataSize)
            {
                return SLANG_FAIL;
            }

            Entry dstEntry;

            const char* path = (const char*)srcData;
            srcData += srcEntry->pathSize;

            dstEntry.m_canonicalPath = UnownedStringSlice(path, srcEntry->pathSize - 1);
            dstEntry.m_type = (SlangPathType)srcEntry->pathType;
            dstEntry.m_uncompressedSizeInBytes = srcEntry->uncompressedSize;
            
            switch (dstEntry.m_type)
            {
                case SLANG_PATH_TYPE_FILE:
                {
                    if (srcData + srcEntry->compressedSize != data->getPayloadEnd())
                    {
                        return SLANG_FAIL;
                    }

                    // Get the compressed data
                    dstEntry.m_contents = RawBlob::create(srcData, srcEntry->compressedSize);
                    break;
                }
                case SLANG_PATH_TYPE_DIRECTORY: break;
                default: return SLANG_FAIL;
            }

            // If it's the root entry we can ignore (as already added)
            if (dstEntry.m_canonicalPath == ".")
            {
                continue;
            }

            // Add to the list of entries
            m_entries.add(dstEntry.m_canonicalPath, dstEntry);
        }
    }

    return SLANG_OK;
}

SlangResult RiffFileSystem::storeArchive(bool blobOwnsContent, ISlangBlob** outBlob)
{
    // All blobs are owned in this style
    SLANG_UNUSED(blobOwnsContent)

    RiffContainer container;
    RiffContainer::ScopeChunk scopeContainer(&container, RiffContainer::Chunk::Kind::List, RiffFileSystemBinary::kContainerFourCC);

    {
        RiffFileSystemBinary::Header header;
        CompressionSystemType compressionSystemType = m_compressionSystem ? m_compressionSystem->getSystemType() : CompressionSystemType::None;
        header.compressionSystemType = uint32_t(compressionSystemType);
        container.addDataChunk(RiffFileSystemBinary::kHeaderFourCC, &header, sizeof(header));
    }

    for (const auto& [_, srcEntry] : m_entries)
    {
        // Ignore the root entry
        if (srcEntry.m_canonicalPath == toSlice("."))
        {
            continue;
        }

        RiffContainer::ScopeChunk scopeData(&container, RiffContainer::Chunk::Kind::Data, RiffFileSystemBinary::kEntryFourCC);

        RiffFileSystemBinary::Entry dstEntry;
        dstEntry.uncompressedSize = 0;
        dstEntry.compressedSize = 0;
        dstEntry.pathSize = uint32_t(srcEntry.m_canonicalPath.getLength() + 1);
        dstEntry.pathType = srcEntry.m_type;

        ISlangBlob* blob = srcEntry.m_contents;

        if (srcEntry.m_type == SLANG_PATH_TYPE_FILE)
        {
            dstEntry.compressedSize = uint32_t(blob->getBufferSize());
            dstEntry.uncompressedSize = uint32_t(srcEntry.m_uncompressedSizeInBytes);
        }

        // Entry header
        container.write(&dstEntry, sizeof(dstEntry));

        // Path
        container.write(srcEntry.m_canonicalPath.getBuffer(), srcEntry.m_canonicalPath.getLength() + 1);

        // Add the contained data without copying
        if (blob)
        {
            RiffContainer::Data* data = container.addData();
            container.setUnowned(data, const_cast<void*>(blob->getBufferPointer()), blob->getBufferSize());
        }
    }

    OwnedMemoryStream stream(FileAccess::Write);
    // We now write the RiffContainer to the stream
    SLANG_RETURN_ON_FAIL(RiffUtil::write(container.getRoot(), true, &stream));

    List<uint8_t> data;
    stream.swapContents(data);

    *outBlob = ListBlob::moveCreate(data).detach();
    return SLANG_OK;
}

/* static */bool RiffFileSystem::isArchive(const void* data, size_t sizeInBytes)
{
    MemoryStreamBase stream(FileAccess::Read, data, sizeInBytes);
    RiffListHeader header;
    return SLANG_SUCCEEDED(RiffUtil::readHeader(&stream, header)) && header.subType == RiffFileSystemBinary::kContainerFourCC;
}

} // namespace Slang
back to top