//-------------------------------------------------------------------------------------- // File: Texassemble.cpp // // DirectX Texture assembler for cube maps, volume maps, and arrays // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- #pragma warning(push) #pragma warning(disable : 4005) #define WIN32_LEAN_AND_MEAN #define NOMINMAX #define NODRAWTEXT #define NOGDI #define NOBITMAP #define NOMCX #define NOSERVICE #define NOHELP #pragma warning(pop) #include #include #include #include #include #include #include #include "directxtex.h" using namespace DirectX; enum OPTIONS { OPT_RECURSIVE = 1, OPT_CUBE, OPT_VOLUME, OPT_ARRAY, OPT_CUBEARRAY, OPT_WIDTH, OPT_HEIGHT, OPT_FORMAT, OPT_FILTER, OPT_OUTPUTFILE, OPT_USE_DX10, OPT_NOLOGO, OPT_SEPALPHA, OPT_MAX }; static_assert(OPT_MAX <= 32, "dwOptions is a DWORD bitfield"); struct SConversion { wchar_t szSrc[MAX_PATH]; }; struct SValue { LPCWSTR pName; DWORD dwValue; }; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// SValue g_pOptions[] = { { L"r", OPT_RECURSIVE }, { L"cube", OPT_CUBE }, { L"volume", OPT_VOLUME }, { L"array", OPT_ARRAY }, { L"cubearray", OPT_CUBEARRAY }, { L"w", OPT_WIDTH }, { L"h", OPT_HEIGHT }, { L"f", OPT_FORMAT }, { L"if", OPT_FILTER }, { L"o", OPT_OUTPUTFILE }, { L"dx10", OPT_USE_DX10 }, { L"nologo", OPT_NOLOGO }, { L"sepalpha", OPT_SEPALPHA }, { nullptr, 0 } }; #define DEFFMT(fmt) { L#fmt, DXGI_FORMAT_ ## fmt } SValue g_pFormats[] = { // List does not include _TYPELESS or depth/stencil formats DEFFMT(R32G32B32A32_FLOAT), DEFFMT(R32G32B32A32_UINT), DEFFMT(R32G32B32A32_SINT), DEFFMT(R32G32B32_FLOAT), DEFFMT(R32G32B32_UINT), DEFFMT(R32G32B32_SINT), DEFFMT(R16G16B16A16_FLOAT), DEFFMT(R16G16B16A16_UNORM), DEFFMT(R16G16B16A16_UINT), DEFFMT(R16G16B16A16_SNORM), DEFFMT(R16G16B16A16_SINT), DEFFMT(R32G32_FLOAT), DEFFMT(R32G32_UINT), DEFFMT(R32G32_SINT), DEFFMT(R10G10B10A2_UNORM), DEFFMT(R10G10B10A2_UINT), DEFFMT(R11G11B10_FLOAT), DEFFMT(R8G8B8A8_UNORM), DEFFMT(R8G8B8A8_UNORM_SRGB), DEFFMT(R8G8B8A8_UINT), DEFFMT(R8G8B8A8_SNORM), DEFFMT(R8G8B8A8_SINT), DEFFMT(R16G16_FLOAT), DEFFMT(R16G16_UNORM), DEFFMT(R16G16_UINT), DEFFMT(R16G16_SNORM), DEFFMT(R16G16_SINT), DEFFMT(R32_FLOAT), DEFFMT(R32_UINT), DEFFMT(R32_SINT), DEFFMT(R8G8_UNORM), DEFFMT(R8G8_UINT), DEFFMT(R8G8_SNORM), DEFFMT(R8G8_SINT), DEFFMT(R16_FLOAT), DEFFMT(R16_UNORM), DEFFMT(R16_UINT), DEFFMT(R16_SNORM), DEFFMT(R16_SINT), DEFFMT(R8_UNORM), DEFFMT(R8_UINT), DEFFMT(R8_SNORM), DEFFMT(R8_SINT), DEFFMT(A8_UNORM), //DEFFMT(R1_UNORM) DEFFMT(R9G9B9E5_SHAREDEXP), DEFFMT(R8G8_B8G8_UNORM), DEFFMT(G8R8_G8B8_UNORM), DEFFMT(B5G6R5_UNORM), DEFFMT(B5G5R5A1_UNORM), // DXGI 1.1 formats DEFFMT(B8G8R8A8_UNORM), DEFFMT(B8G8R8X8_UNORM), DEFFMT(R10G10B10_XR_BIAS_A2_UNORM), DEFFMT(B8G8R8A8_UNORM_SRGB), DEFFMT(B8G8R8X8_UNORM_SRGB), // DXGI 1.2 formats DEFFMT(B4G4R4A4_UNORM), { nullptr, DXGI_FORMAT_UNKNOWN } }; SValue g_pFilters[] = { { L"POINT", TEX_FILTER_POINT }, { L"LINEAR", TEX_FILTER_LINEAR }, { L"CUBIC", TEX_FILTER_CUBIC }, { L"FANT", TEX_FILTER_FANT }, { L"BOX", TEX_FILTER_BOX }, { L"TRIANGLE", TEX_FILTER_TRIANGLE }, { L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER }, { L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER }, { L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER }, { L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER }, { L"BOX_DITHER", TEX_FILTER_BOX | TEX_FILTER_DITHER }, { L"TRIANGLE_DITHER", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER }, { L"POINT_DITHER_DIFFUSION", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION }, { L"LINEAR_DITHER_DIFFUSION", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION }, { L"CUBIC_DITHER_DIFFUSION", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION }, { L"FANT_DITHER_DIFFUSION", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION }, { L"BOX_DITHER_DIFFUSION", TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION }, { L"TRIANGLE_DITHER_DIFFUSION", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER_DIFFUSION }, { nullptr, TEX_FILTER_DEFAULT } }; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// namespace { inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; } struct find_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } }; typedef public std::unique_ptr ScopedFindHandle; #pragma prefast(disable : 26018, "Only used with static internal arrays") DWORD LookupByName(const wchar_t *pName, const SValue *pArray) { while (pArray->pName) { if (!_wcsicmp(pName, pArray->pName)) return pArray->dwValue; pArray++; } return 0; } const wchar_t* LookupByValue(DWORD pValue, const SValue *pArray) { while (pArray->pName) { if (pValue == pArray->dwValue) return pArray->pName; pArray++; } return L""; } void SearchForFiles(const wchar_t* path, std::list& files, bool recursive) { // Process files WIN32_FIND_DATA findData = {}; ScopedFindHandle hFile(safe_handle(FindFirstFileExW(path, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH))); if (hFile) { for (;;) { if (!(findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))) { wchar_t drive[_MAX_DRIVE] = {}; wchar_t dir[_MAX_DIR] = {}; _wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0); SConversion conv; _wmakepath_s(conv.szSrc, drive, dir, findData.cFileName, nullptr); files.push_back(conv); } if (!FindNextFile(hFile.get(), &findData)) break; } } // Process directories if (recursive) { wchar_t searchDir[MAX_PATH] = {}; { wchar_t drive[_MAX_DRIVE] = {}; wchar_t dir[_MAX_DIR] = {}; _wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0); _wmakepath_s(searchDir, drive, dir, L"*", nullptr); } hFile.reset(safe_handle(FindFirstFileExW(searchDir, FindExInfoBasic, &findData, FindExSearchLimitToDirectories, nullptr, FIND_FIRST_EX_LARGE_FETCH))); if (!hFile) return; for (;;) { if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (findData.cFileName[0] != L'.') { wchar_t subdir[MAX_PATH] = {}; { wchar_t drive[_MAX_DRIVE] = {}; wchar_t dir[_MAX_DIR] = {}; wchar_t fname[_MAX_FNAME] = {}; wchar_t ext[_MAX_FNAME] = {}; _wsplitpath_s(path, drive, dir, fname, ext); wcscat_s(dir, findData.cFileName); _wmakepath_s(subdir, drive, dir, fname, ext); } SearchForFiles(subdir, files, recursive); } } if (!FindNextFile(hFile.get(), &findData)) break; } } } void PrintFormat(DXGI_FORMAT Format) { for (SValue *pFormat = g_pFormats; pFormat->pName; pFormat++) { if ((DXGI_FORMAT)pFormat->dwValue == Format) { wprintf(pFormat->pName); break; } } } void PrintInfo(const TexMetadata& info) { wprintf(L" (%Iux%Iu", info.width, info.height); if (TEX_DIMENSION_TEXTURE3D == info.dimension) wprintf(L"x%Iu", info.depth); if (info.mipLevels > 1) wprintf(L",%Iu", info.mipLevels); if (info.arraySize > 1) wprintf(L",%Iu", info.arraySize); wprintf(L" "); PrintFormat(info.format); switch (info.dimension) { case TEX_DIMENSION_TEXTURE1D: wprintf((info.arraySize > 1) ? L" 1DArray" : L" 1D"); break; case TEX_DIMENSION_TEXTURE2D: if (info.IsCubemap()) { wprintf((info.arraySize > 6) ? L" CubeArray" : L" Cube"); } else { wprintf((info.arraySize > 1) ? L" 2DArray" : L" 2D"); } break; case TEX_DIMENSION_TEXTURE3D: wprintf(L" 3D"); break; } wprintf(L")"); } void PrintList(size_t cch, SValue *pValue) { while (pValue->pName) { size_t cchName = wcslen(pValue->pName); if (cch + cchName + 2 >= 80) { wprintf(L"\n "); cch = 6; } wprintf(L"%ls ", pValue->pName); cch += cchName + 2; pValue++; } wprintf(L"\n"); } void PrintLogo() { wprintf(L"Microsoft (R) DirectX Texture Assembler (DirectXTex version)\n"); wprintf(L"Copyright (C) Microsoft Corp. All rights reserved.\n"); #ifdef _DEBUG wprintf(L"*** Debug build ***\n"); #endif wprintf(L"\n"); } void PrintUsage() { PrintLogo(); wprintf(L"Usage: texassemble [-cube | - volume | -array | -cubearray] \n"); wprintf(L"\n"); wprintf(L" -r wildcard filename search is recursive\n"); wprintf(L" -cube create cubemap\n"); wprintf(L" -volume create volume map\n"); wprintf(L" -array create texture array\n"); wprintf(L" -cubearray create cubemap array\n"); wprintf(L" -w width\n"); wprintf(L" -h height\n"); wprintf(L" -f format\n"); wprintf(L" -if image filtering\n"); wprintf(L" -o output filename\n"); wprintf(L" -sepalpha resize alpha channel separately from color channels\n"); wprintf(L" -dx10 Force use of 'DX10' extended header\n"); wprintf(L" -nologo suppress copyright message\n"); wprintf(L"\n"); wprintf(L" : "); PrintList(13, g_pFormats); wprintf(L"\n"); wprintf(L" : "); PrintList(13, g_pFilters); } } //-------------------------------------------------------------------------------------- // Entry-point //-------------------------------------------------------------------------------------- #pragma prefast(disable : 28198, "Command-line tool, frees all memory on exit") int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) { // Parameters and defaults size_t width = 0; size_t height = 0; DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; DWORD dwFilter = TEX_FILTER_DEFAULT; DWORD dwFilterOpts = 0; wchar_t szOutputFile[MAX_PATH] = { 0 }; // Initialize COM (needed for WIC) HRESULT hr = hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(hr)) { wprintf(L"Failed to initialize COM (%08X)\n", hr); return 1; } // Process command line DWORD dwOptions = 0; std::list conversion; for (int iArg = 1; iArg < argc; iArg++) { PWSTR pArg = argv[iArg]; if (('-' == pArg[0]) || ('/' == pArg[0])) { pArg++; PWSTR pValue; for (pValue = pArg; *pValue && (':' != *pValue); pValue++); if (*pValue) *pValue++ = 0; DWORD dwOption = LookupByName(pArg, g_pOptions); if (!dwOption || (dwOptions & (1 << dwOption))) { PrintUsage(); return 1; } dwOptions |= 1 << dwOption; // Handle options with additional value parameter switch (dwOption) { case OPT_WIDTH: case OPT_HEIGHT: case OPT_FORMAT: case OPT_FILTER: case OPT_OUTPUTFILE: if (!*pValue) { if ((iArg + 1 >= argc)) { PrintUsage(); return 1; } iArg++; pValue = argv[iArg]; } } switch (dwOption) { case OPT_WIDTH: if (swscanf_s(pValue, L"%Iu", &width) != 1) { wprintf(L"Invalid value specified with -w (%ls)\n", pValue); return 1; } break; case OPT_HEIGHT: if (swscanf_s(pValue, L"%Iu", &height) != 1) { wprintf(L"Invalid value specified with -h (%ls)\n", pValue); return 1; } break; case OPT_FORMAT: format = (DXGI_FORMAT)LookupByName(pValue, g_pFormats); if (!format) { wprintf(L"Invalid value specified with -f (%ls)\n", pValue); return 1; } break; case OPT_FILTER: dwFilter = LookupByName(pValue, g_pFilters); if (!dwFilter) { wprintf(L"Invalid value specified with -if (%ls)\n", pValue); return 1; } break; case OPT_SEPALPHA: dwFilterOpts |= TEX_FILTER_SEPARATE_ALPHA; break; case OPT_OUTPUTFILE: wcscpy_s(szOutputFile, MAX_PATH, pValue); break; } } else if (wcspbrk(pArg, L"?*") != nullptr) { size_t count = conversion.size(); SearchForFiles(pArg, conversion, (dwOptions & (1 << OPT_RECURSIVE)) != 0); if (conversion.size() <= count) { wprintf(L"No matching files found for %ls\n", pArg); return 1; } } else { SConversion conv; wcscpy_s(conv.szSrc, MAX_PATH, pArg); conversion.push_back(conv); } } if (conversion.empty()) { PrintUsage(); return 0; } switch (dwOptions & ((1 << OPT_CUBE) | (1 << OPT_VOLUME) | (1 << OPT_ARRAY) | (1 << OPT_CUBEARRAY))) { case (1 << OPT_VOLUME): case (1 << OPT_ARRAY): case (1 << OPT_CUBE): case (1 << OPT_CUBEARRAY): break; default: wprintf(L"Must use one of: -cube, -volume, -array, or -cubearray\n\n"); return 1; } if (~dwOptions & (1 << OPT_NOLOGO)) PrintLogo(); // Convert images size_t images = 0; std::vector> loadedImages; for (auto pConv = conversion.begin(); pConv != conversion.end(); ++pConv) { wchar_t ext[_MAX_EXT]; wchar_t fname[_MAX_FNAME]; _wsplitpath_s(pConv->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT); // Load source image if (pConv != conversion.begin()) wprintf(L"\n"); else if (!*szOutputFile) { if (_wcsicmp(ext, L".dds") == 0) { wprintf(L"ERROR: Need to specify output file via -o\n"); return 1; } _wmakepath_s(szOutputFile, nullptr, nullptr, fname, L".dds"); } wprintf(L"reading %ls", pConv->szSrc); fflush(stdout); TexMetadata info; std::unique_ptr image(new (std::nothrow) ScratchImage); if (!image) { wprintf(L"\nERROR: Memory allocation failed\n"); return 1; } if (_wcsicmp(ext, L".dds") == 0) { hr = LoadFromDDSFile(pConv->szSrc, DDS_FLAGS_NONE, &info, *image.get()); if (FAILED(hr)) { wprintf(L" FAILED (%x)\n", hr); return 1; } if (info.depth > 1 || info.mipLevels > 1 || info.IsCubemap()) { wprintf(L"\nERROR: Can't assemble complex surfaces\n"); return 1; } } else if (_wcsicmp(ext, L".tga") == 0) { hr = LoadFromTGAFile(pConv->szSrc, &info, *image.get()); if (FAILED(hr)) { wprintf(L" FAILED (%x)\n", hr); return 1; } } else if (_wcsicmp(ext, L".hdr") == 0) { hr = LoadFromHDRFile(pConv->szSrc, &info, *image.get()); if (FAILED(hr)) { wprintf(L" FAILED (%x)\n", hr); return 1; } } else { // WIC shares the same filter values for mode and dither static_assert(WIC_FLAGS_DITHER == TEX_FILTER_DITHER, "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(WIC_FLAGS_DITHER_DIFFUSION == TEX_FILTER_DITHER_DIFFUSION, "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(WIC_FLAGS_FILTER_POINT == TEX_FILTER_POINT, "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(WIC_FLAGS_FILTER_LINEAR == TEX_FILTER_LINEAR, "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(WIC_FLAGS_FILTER_CUBIC == TEX_FILTER_CUBIC, "WIC_FLAGS_* & TEX_FILTER_* should match"); static_assert(WIC_FLAGS_FILTER_FANT == TEX_FILTER_FANT, "WIC_FLAGS_* & TEX_FILTER_* should match"); hr = LoadFromWICFile(pConv->szSrc, dwFilter | WIC_FLAGS_ALL_FRAMES, &info, *image.get()); if (FAILED(hr)) { wprintf(L" FAILED (%x)\n", hr); return 1; } } PrintInfo(info); // Convert texture fflush(stdout); // --- Decompress -------------------------------------------------------------- if (IsCompressed(info.format)) { const Image* img = image->GetImage(0, 0, 0); assert(img); size_t nimg = image->GetImageCount(); std::unique_ptr timage(new (std::nothrow) ScratchImage); if (!timage) { wprintf(L"\nERROR: Memory allocation failed\n"); return 1; } hr = Decompress(img, nimg, info, DXGI_FORMAT_UNKNOWN /* picks good default */, *timage.get()); if (FAILED(hr)) { wprintf(L" FAILED [decompress] (%x)\n", hr); continue; } const TexMetadata& tinfo = timage->GetMetadata(); info.format = tinfo.format; assert(info.width == tinfo.width); assert(info.height == tinfo.height); assert(info.depth == tinfo.depth); assert(info.arraySize == tinfo.arraySize); assert(info.mipLevels == tinfo.mipLevels); assert(info.miscFlags == tinfo.miscFlags); assert(info.miscFlags2 == tinfo.miscFlags2); assert(info.dimension == tinfo.dimension); image.swap(timage); } // --- Resize ------------------------------------------------------------------ if (!width) { width = info.width; } if (!height) { height = info.height; } if (info.width != width || info.height != height) { std::unique_ptr timage(new (std::nothrow) ScratchImage); if (!timage) { wprintf(L"\nERROR: Memory allocation failed\n"); return 1; } hr = Resize(image->GetImages(), image->GetImageCount(), image->GetMetadata(), width, height, dwFilter | dwFilterOpts, *timage.get()); if (FAILED(hr)) { wprintf(L" FAILED [resize] (%x)\n", hr); return 1; } const TexMetadata& tinfo = timage->GetMetadata(); assert(tinfo.width == width && tinfo.height == height && tinfo.mipLevels == 1); info.width = tinfo.width; info.height = tinfo.height; info.mipLevels = 1; assert(info.depth == tinfo.depth); assert(info.arraySize == tinfo.arraySize); assert(info.miscFlags == tinfo.miscFlags); assert(info.miscFlags2 == tinfo.miscFlags2); assert(info.format == tinfo.format); assert(info.dimension == tinfo.dimension); image.swap(timage); } // --- Convert ----------------------------------------------------------------- if (format == DXGI_FORMAT_UNKNOWN) { format = info.format; } else if (info.format != format && !IsCompressed(format)) { std::unique_ptr timage(new (std::nothrow) ScratchImage); if (!timage) { wprintf(L"\nERROR: Memory allocation failed\n"); return 1; } hr = Convert(image->GetImages(), image->GetImageCount(), image->GetMetadata(), format, dwFilter | dwFilterOpts, TEX_THRESHOLD_DEFAULT, *timage.get()); if (FAILED(hr)) { wprintf(L" FAILED [convert] (%x)\n", hr); return 1; } const TexMetadata& tinfo = timage->GetMetadata(); assert(tinfo.format == format); info.format = tinfo.format; assert(info.width == tinfo.width); assert(info.height == tinfo.height); assert(info.depth == tinfo.depth); assert(info.arraySize == tinfo.arraySize); assert(info.mipLevels == tinfo.mipLevels); assert(info.miscFlags == tinfo.miscFlags); assert(info.miscFlags2 == tinfo.miscFlags2); assert(info.dimension == tinfo.dimension); image.swap(timage); } images += info.arraySize; loadedImages.push_back(std::move(image)); } if (images < 2) { wprintf(L"\nERROR: Need at least 2 images to assemble\n\n"); return 1; } switch (dwOptions & ((1 << OPT_CUBE) | (1 << OPT_VOLUME) | (1 << OPT_ARRAY) | (1 << OPT_CUBEARRAY))) { case (1 << OPT_CUBE): if (images != 6) { wprintf(L"\nERROR: -cube requires six images to form the faces of the cubemap\n"); return 1; } break; case (1 << OPT_CUBEARRAY): if ((images < 6) || (images % 6) != 0) { wprintf(L"-cubearray requires a multiple of 6 images to form the faces of the cubemaps\n"); return 1; } break; } // --- Create result --------------------------------------------------------------- { std::vector imageArray; imageArray.reserve(images); for (auto it = loadedImages.cbegin(); it != loadedImages.cend(); ++it) { const ScratchImage* simage = it->get(); assert(simage != 0); for (size_t j = 0; j < simage->GetMetadata().arraySize; ++j) { const Image* img = simage->GetImage(0, j, 0); assert(img != 0); imageArray.push_back(*img); } } ScratchImage result; switch (dwOptions & ((1 << OPT_CUBE) | (1 << OPT_VOLUME) | (1 << OPT_ARRAY) | (1 << OPT_CUBEARRAY))) { case (1 << OPT_VOLUME): hr = result.Initialize3DFromImages(&imageArray[0], imageArray.size()); break; case (1 << OPT_ARRAY): hr = result.InitializeArrayFromImages(&imageArray[0], imageArray.size(), (dwOptions & (1 << OPT_USE_DX10)) != 0); break; case (1 << OPT_CUBE): case (1 << OPT_CUBEARRAY): hr = result.InitializeCubeFromImages(&imageArray[0], imageArray.size()); break; } if (FAILED(hr)) { wprintf(L"FAILED building result image (%x)\n", hr); return 1; } // Write texture wprintf(L"\nWriting %ls ", szOutputFile); PrintInfo(result.GetMetadata()); wprintf(L"\n"); fflush(stdout); hr = SaveToDDSFile(result.GetImages(), result.GetImageCount(), result.GetMetadata(), (dwOptions & (1 << OPT_USE_DX10)) ? (DDS_FLAGS_FORCE_DX10_EXT | DDS_FLAGS_FORCE_DX10_EXT_MISC2) : DDS_FLAGS_NONE, szOutputFile); if (FAILED(hr)) { wprintf(L"\nFAILED (%x)\n", hr); return 1; } wprintf(L"\n"); } return 0; }