diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-03-12 07:08:09 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-03-12 07:08:09 -0500 |
| commit | 755698f1adceef55f52c8e44c9746537885c9bf7 (patch) | |
| tree | 23d1a9fea936805708eeccf2ac2684620565d328 /libs/preview_dds_native/src/ddsfile.cpp | |
| parent | 019870ffd8308d94289d72d45abeb3db39ccde92 (diff) | |
Replace Python plugins with native C++ .so, remove bundled Python (~95MB)
- Add native plugins: form43_checker, script_extender_checker, preview_dds,
basic_games (75 game defs via IPluginProxy pattern with Steam/GOG detection)
- Remove portable Python from Dockerfile and build-inner.sh
- Add Python settings tab with venv support (system python3 + PyQt6)
- Skip loading plugin_python.so when Python disabled (default: off)
- Update pythonrunner.cpp to use venv at ~/.local/share/fluorine/python-venv/
- Preserve real file permissions in FUSE VFS (fixes native executable +x bits)
- Add FUSE_SET_ATTR_MODE handler so chmod works through VFS
- Fix desktop shortcut Exec= to use fluorine-manager launcher, not bare binary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/preview_dds_native/src/ddsfile.cpp')
| -rw-r--r-- | libs/preview_dds_native/src/ddsfile.cpp | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/libs/preview_dds_native/src/ddsfile.cpp b/libs/preview_dds_native/src/ddsfile.cpp new file mode 100644 index 0000000..91dc627 --- /dev/null +++ b/libs/preview_dds_native/src/ddsfile.cpp @@ -0,0 +1,132 @@ +#include "ddsfile.h" + +#include <QFile> + +#include <algorithm> +#include <cstring> + +bool DDSFile::loadFromFile(const QString& path) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + return false; + } + return parse(file.readAll()); +} + +bool DDSFile::loadFromData(const QByteArray& data) +{ + return parse(data); +} + +bool DDSFile::parse(const QByteArray& data) +{ + const char* ptr = data.constData(); + int offset = 0; + int size = data.size(); + + // Magic number + if (size < 4) + return false; + uint32_t magic; + std::memcpy(&magic, ptr + offset, 4); + offset += 4; + if (magic != DDS_MAGIC) + return false; + + // DDS header + if (size - offset < static_cast<int>(sizeof(DDSHeader))) + return false; + DDSHeader header; + std::memcpy(&header, ptr + offset, sizeof(DDSHeader)); + offset += sizeof(DDSHeader); + + if (header.dwSize != 124) + return false; + + // DXT10 extended header + DDSHeaderDXT10 dxt10{}; + bool hasDXT10 = false; + if ((header.ddspf.dwFlags & DDPF_FOURCC) && + header.ddspf.dwFourCC == makeFourCC('D', 'X', '1', '0')) { + if (size - offset < static_cast<int>(sizeof(DDSHeaderDXT10))) + return false; + std::memcpy(&dxt10, ptr + offset, sizeof(DDSHeaderDXT10)); + offset += sizeof(DDSHeaderDXT10); + hasDXT10 = true; + } + + m_width = header.dwWidth; + m_height = header.dwHeight; + + // Determine GL format + m_glFormat = getGLFormat(header.ddspf, hasDXT10 ? &dxt10 : nullptr); + if (!m_glFormat.valid) { + return false; + } + + // Determine DXGI format for size calculations + if (hasDXT10) { + m_dxgiFormat = dxt10.dxgiFormat; + } else if (header.ddspf.dwFlags & DDPF_FOURCC) { + m_dxgiFormat = fourCCToDXGI(header.ddspf.dwFourCC); + } else { + m_dxgiFormat = DXGIFormat::UNKNOWN; + } + + // Description + m_description = formatDescription(header.ddspf, hasDXT10 ? &dxt10 : nullptr); + m_description += + QString(" | %1x%2").arg(m_width).arg(m_height); + + // Cubemap detection + m_cubemap = false; + int layers = 1; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP) { + m_cubemap = true; + layers = 0; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEX) layers++; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) layers++; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEY) layers++; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) layers++; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) layers++; + if (header.dwCaps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) layers++; + m_description += " | Cubemap"; + } else { + m_description += " | 2D"; + } + + // Mipmap count + int mipCount = 1; + if (header.dwFlags & DDSD_MIPMAPCOUNT) { + mipCount = std::max(1u, header.dwMipMapCount); + } + if (mipCount > 1) { + m_description += QString(" | %1 mips").arg(mipCount); + } + + // Read pixel data for each face and mip level + m_faces.resize(layers); + for (int face = 0; face < layers; ++face) { + m_faces[face].mips.resize(mipCount); + uint32_t w = m_width; + uint32_t h = m_height; + for (int mip = 0; mip < mipCount; ++mip) { + uint32_t dataSize = mipDataSize(m_dxgiFormat, header.ddspf, w, h); + if (offset + static_cast<int>(dataSize) > size) { + // Truncated file, keep what we have + m_faces[face].mips.resize(mip); + break; + } + m_faces[face].mips[mip].width = w; + m_faces[face].mips[mip].height = h; + m_faces[face].mips[mip].data = + QByteArray(ptr + offset, static_cast<int>(dataSize)); + offset += dataSize; + w = std::max(1u, w / 2); + h = std::max(1u, h / 2); + } + } + + return !m_faces.isEmpty() && !m_faces[0].mips.isEmpty(); +} |
