diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index ac9fc17..e58b00f 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -13,8 +13,6 @@ #define WIN32_LEAN_AND_MEAN #define NOMINMAX #define NODRAWTEXT -#define NOGDI -#define NOBITMAP #define NOMCX #define NOSERVICE #define NOHELP @@ -428,6 +426,10 @@ namespace { inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; } + struct handle_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) CloseHandle(h); } }; + + typedef std::unique_ptr ScopedHandle; + struct find_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } }; typedef public std::unique_ptr ScopedFindHandle; @@ -952,6 +954,128 @@ namespace float normalizedLinear = pow(std::max(pow(abs(ST2084), 1.0f / 78.84375f) - 0.8359375f, 0.0f) / (18.8515625f - 18.6875f * pow(abs(ST2084), 1.0f / 78.84375f)), 1.0f / 0.1593017578f); return normalizedLinear; } + + + HRESULT ReadData(_In_z_ const wchar_t* szFile, std::unique_ptr& blob, size_t& bmpSize) + { + blob.reset(); + + ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, nullptr))); + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Get the file size + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough) + if (fileInfo.EndOfFile.HighPart > 0) + { + return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE); + } + + // Zero-sized files assumed to be invalid + if (fileInfo.EndOfFile.LowPart < 1) + { + return E_FAIL; + } + + // Read file + blob.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!blob) + { + return E_OUTOFMEMORY; + } + + DWORD bytesRead = 0; + if (!ReadFile(hFile.get(), blob.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesRead != fileInfo.EndOfFile.LowPart) + { + return E_FAIL; + } + + bmpSize = fileInfo.EndOfFile.LowPart; + + return S_OK; + } + + HRESULT LoadFromExtendedBMPMemory(_In_reads_bytes_(size) const void* pSource, _In_ size_t size, _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image) + { + // This loads from non-standard BMP files that are not supported by WIC + image.Release(); + + if (size < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) + return E_FAIL; + + // Valid BMP files always start with 'BM' at the top + auto filehdr = reinterpret_cast(pSource); + if (filehdr->bfType != 0x4D42) + return E_FAIL; + + if (size < filehdr->bfOffBits) + return E_FAIL; + + auto header = reinterpret_cast(reinterpret_cast(pSource) + sizeof(BITMAPFILEHEADER)); + if (header->biSize != sizeof(BITMAPINFOHEADER)) + return E_FAIL; + + if (header->biWidth < 1 || header->biHeight < 1 || header->biPlanes != 1 || header->biBitCount != 16) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + switch (header->biCompression) + { + case 0x31545844: // FourCC "DXT1" + format = DXGI_FORMAT_BC1_UNORM; + break; + case 0x33545844: // FourCC "DXT3" + format = DXGI_FORMAT_BC2_UNORM; + break; + case 0x35545844: // FourCC "DXT5" + format = DXGI_FORMAT_BC3_UNORM; + break; + + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + HRESULT hr = image.Initialize2D(format, header->biWidth, header->biHeight, 1, 1); + if (FAILED(hr)) + return hr; + + if (header->biSizeImage != image.GetPixelsSize()) + return E_UNEXPECTED; + + size_t remaining = size - filehdr->bfOffBits; + if (!remaining) + return E_FAIL; + + if (remaining < image.GetPixelsSize()) + return E_UNEXPECTED; + + auto pixels = reinterpret_cast(pSource) + filehdr->bfOffBits; + + memcpy(image.GetPixels(), pixels, image.GetPixelsSize()); + + if (metadata) + { + *metadata = image.GetMetadata(); + } + + return S_OK; + } } @@ -1578,6 +1702,28 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) image->OverrideFormat(info.format); } } + else if (_wcsicmp(ext, L".bmp") == 0) + { + std::unique_ptr bmpData; + size_t bmpSize; + hr = ReadData(pConv->szSrc, bmpData, bmpSize); + if (SUCCEEDED(hr)) + { + hr = LoadFromWICMemory(bmpData.get(), bmpSize, dwFilter, &info, *image); + if (FAILED(hr)) + { + if (SUCCEEDED(LoadFromExtendedBMPMemory(bmpData.get(), bmpSize, &info, *image))) + { + hr = S_OK; + } + } + } + if (FAILED(hr)) + { + wprintf(L" FAILED (%x)\n", hr); + continue; + } + } else if (_wcsicmp(ext, L".tga") == 0) { hr = LoadFromTGAFile(pConv->szSrc, &info, *image);