//--------------------------------------------------------------------------------------
// File: bmpdump.cpp
//
// BMP file content examination utility
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//--------------------------------------------------------------------------------------
#include
#include
#include
#ifdef __clang__
#pragma clang diagnostic ignored "-Wfour-char-constants"
#endif
namespace
{
//---------------------------------------------------------------------------------
struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } };
using ScopedHandle = std::unique_ptr;
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
//--------------------------------------------------------------------------------------
static_assert(sizeof(BITMAPFILEHEADER) == 14, "Mis-match size");
static_assert(sizeof(BITMAPCOREHEADER) == 12, "Mis-match size");
static_assert(sizeof(BITMAPINFOHEADER) == 40, "Mis-match size");
static_assert(sizeof(BITMAPV4HEADER) == 108, "Mis-match size");
static_assert(sizeof(BITMAPV5HEADER) == 124, "Mis-match size");
//--------------------------------------------------------------------------------------
HRESULT LoadTextureDataFromFile(_In_z_ const wchar_t* fileName,
std::unique_ptr& bmpData
)
{
// open the file
#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
ScopedHandle hFile(safe_handle(CreateFile2(fileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
nullptr)));
#else
ScopedHandle hFile(safe_handle(CreateFileW(fileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr)));
#endif
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
if (fileInfo.EndOfFile.HighPart > 0)
{
return E_FAIL;
}
// Need at least enough data to fill the basic header
if (fileInfo.EndOfFile.LowPart (bmpData.get());
// Valid BMP files always start with 'BM' at the top
if (filehdr->bfType != 0x4D42)
{
return E_FAIL;
}
// Check we have enough data for the discovered header
auto header = reinterpret_cast(bmpData.get() + sizeof(BITMAPFILEHEADER));
if (BytesRead bcSize))
{
return E_FAIL;
}
return S_OK;
}
void OutputHeader(const uint8_t* bmpData, const wchar_t* fname)
{
auto coreHeader = reinterpret_cast(bmpData + sizeof(BITMAPFILEHEADER));
if (coreHeader->bcSize >= sizeof(BITMAPV5HEADER))
{
wprintf(L"BMP %ls\n\tV5 header (%lu bytes)\n", fname, coreHeader->bcSize);
auto header = reinterpret_cast(bmpData + sizeof(BITMAPFILEHEADER));
wprintf(L"%ld x %ld\n", header->bV5Width, header->bV5Height);
wprintf(L"%u planes\n", header->bV5Planes);
wprintf(L"%u bits\n", header->bV5BitCount);
switch (header->bV5Compression)
{
case BI_RGB: wprintf(L"BI_RGB\n"); break;
case BI_RLE8: wprintf(L"BI_RLE8\n"); break;
case BI_RLE4: wprintf(L"BI_RLE4\n"); break;
case BI_BITFIELDS: wprintf(L"BI_BITFIELDS\n"); break;
case BI_JPEG: wprintf(L"BI_JPEG\n"); break;
case BI_PNG: wprintf(L"BI_PNG\n"); break;
default:
wprintf(L"%lu compression type\n", header->bV5Compression);
break;
}
wprintf(L"%lu image size\n", header->bV5SizeImage);
wprintf(L"%ld x %ld pels per meter\n", header->bV5XPelsPerMeter, header->bV5YPelsPerMeter);
wprintf(L"%lu colors used\n", header->bV5ClrUsed);
wprintf(L"%lu colors important\n", header->bV5ClrImportant);
if (header->bV5Compression == BI_BITFIELDS)
{
wprintf(L"R: %08lX, G: %08lX, B: %08lX, A: %08lX\n", header->bV5RedMask, header->bV5GreenMask, header->bV5BlueMask, header->bV5AlphaMask);
}
switch (header->bV5CSType)
{
case LCS_CALIBRATED_RGB: wprintf(L"LCS_CALIBRATED_RGB\n"); break;
case 0x73524742 /* 'sRGB' */: wprintf(L"LCS_sRGB\n"); break;
case 0x57696E20 /* 'Win ' */: wprintf(L"LCS_WINDOWS_COLOR_SPACE\n"); break;
case 0x4C494E4B /* 'LINK' */: wprintf(L"PROFILE_LINKED\n"); break;
case 0x4D424544 /* 'MBED' */: wprintf(L"PROFILE_EMBEDDED\n"); break;
default:
wprintf(L"%lu color space type\n", header->bV5CSType);
break;
}
switch (header->bV5Intent)
{
case LCS_GM_ABS_COLORIMETRIC: wprintf(L"LCS_GM_ABS_COLORIMETRIC\n"); break;
case LCS_GM_BUSINESS: wprintf(L"LCS_GM_BUSINESS\n"); break;
case LCS_GM_GRAPHICS: wprintf(L"LCS_GM_GRAPHICS\n"); break;
case LCS_GM_IMAGES: wprintf(L"LCS_GM_IMAGES\n"); break;
default:
wprintf(L"%lu color space type\n", header->bV5Intent);
break;
}
}
else if (coreHeader->bcSize >= sizeof(BITMAPV4HEADER))
{
wprintf(L"BMP %ls\n\tV4 header (%lu bytes)\n", fname, coreHeader->bcSize);
auto header = reinterpret_cast(bmpData + sizeof(BITMAPFILEHEADER));
wprintf(L"%ld x %ld\n", header->bV4Width, header->bV4Height);
wprintf(L"%u planes\n", header->bV4Planes);
wprintf(L"%u bits\n", header->bV4BitCount);
switch (header->bV4V4Compression)
{
case BI_RGB: wprintf(L"BI_RGB\n"); break;
case BI_RLE8: wprintf(L"BI_RLE8\n"); break;
case BI_RLE4: wprintf(L"BI_RLE4\n"); break;
case BI_BITFIELDS: wprintf(L"BI_BITFIELDS\n"); break;
case BI_JPEG: wprintf(L"BI_JPEG\n"); break;
case BI_PNG: wprintf(L"BI_PNG\n"); break;
default:
wprintf(L"%lu compression type\n", header->bV4V4Compression);
break;
}
wprintf(L"%lu image size\n", header->bV4SizeImage);
wprintf(L"%ld x %ld pels per meter\n", header->bV4XPelsPerMeter, header->bV4YPelsPerMeter);
wprintf(L"%lu colors used\n", header->bV4ClrUsed);
wprintf(L"%lu colors important\n", header->bV4ClrImportant);
if (header->bV4V4Compression == BI_BITFIELDS)
{
wprintf(L"R: %08lX, G: %08lX, B: %08lX, A: %08lX\n", header->bV4RedMask, header->bV4GreenMask, header->bV4BlueMask, header->bV4AlphaMask);
}
switch (header->bV4CSType)
{
case LCS_CALIBRATED_RGB: wprintf(L"LCS_CALIBRATED_RGB\n"); break;
default:
wprintf(L"%lu color space type\n", header->bV4CSType);
break;
}
}
else if (coreHeader->bcSize >= sizeof(BITMAPINFOHEADER))
{
wprintf(L"BMP %ls\n\tINFO header (%lu bytes)\n", fname, coreHeader->bcSize);
auto header = reinterpret_cast(bmpData + sizeof(BITMAPFILEHEADER));
wprintf(L"%ld x %ld\n", header->biWidth, header->biHeight);
wprintf(L"%u planes\n", header->biPlanes);
wprintf(L"%u bits\n", header->biBitCount);
switch (header->biCompression)
{
case BI_RGB: wprintf(L"BI_RGB\n"); break;
case BI_RLE8: wprintf(L"BI_RLE8\n"); break;
case BI_RLE4: wprintf(L"BI_RLE4\n"); break;
case BI_BITFIELDS: wprintf(L"BI_BITFIELDS\n"); break;
case BI_JPEG: wprintf(L"BI_JPEG\n"); break;
case BI_PNG: wprintf(L"BI_PNG\n"); break;
case 0x31545844: wprintf(L"DXT1 (Extended BMP)\n"); break;
case 0x33545844: wprintf(L"DXT3 (Extended BMP)\n"); break;
case 0x35545844: wprintf(L"DXT5 (Extended BMP)\n"); break;
default:
wprintf(L"%lu compression type\n", header->biCompression);
break;
}
wprintf(L"%lu image size\n", header->biSizeImage);
wprintf(L"%ld x %ld pels per meter\n", header->biXPelsPerMeter, header->biYPelsPerMeter);
wprintf(L"%lu colors used\n", header->biClrUsed);
wprintf(L"%lu colors important\n", header->biClrImportant);
}
else
{
wprintf(L"BMP %ls\n\tCORE header (%lu bytes)\n", fname, coreHeader->bcSize);
wprintf(L"%u x %u\n", coreHeader->bcWidth, coreHeader->bcHeight);
wprintf(L"%u planes\n", coreHeader->bcPlanes);
wprintf(L"%u bits\n", coreHeader->bcBitCount);
}
}
}
int __cdecl wmain(int argc, wchar_t* argv[])
{
if (argc 2)
{
printf("Usage: bmpdump \n");
return 0;
}
std::unique_ptr bmpData;
HRESULT hr = LoadTextureDataFromFile(argv[1], bmpData);
if (FAILED(hr))
{
wprintf(L"ERROR: failed to load %ls\n", argv[1]);
return -1;
}
OutputHeader(bmpData.get(), argv[1]);
return 0;
}