| 646 | } |
| 647 | |
| 648 | void ValidationContext::validateLevelIndex() { |
| 649 | levelIndices.resize(numLevels); |
| 650 | |
| 651 | const auto levelIndexOffset = sizeof(KTX_header2); |
| 652 | const auto levelIndexSize = sizeof(ktxLevelIndexEntry) * numLevels; |
| 653 | read(levelIndexOffset, levelIndices.data(), levelIndexSize, "the level index"); |
| 654 | |
| 655 | const auto blockByteLength = (expectedBytePlanes ? expectedBytePlanes->at(0) : (parsedBlockByteLength == 0 ? uint8_t(1) : parsedBlockByteLength.value_or(0))); |
| 656 | std::size_t requiredLevelAlignment = calcLevelAlignment(ktxSupercmpScheme(header.supercompressionScheme), blockByteLength); |
| 657 | |
| 658 | std::size_t expectedFirstLevelOffset = 0; |
| 659 | if (header.supercompressionGlobalData.byteLength != 0) |
| 660 | expectedFirstLevelOffset = header.supercompressionGlobalData.byteLength + header.supercompressionGlobalData.byteOffset; |
| 661 | else if (header.keyValueData.byteLength != 0) |
| 662 | expectedFirstLevelOffset = header.keyValueData.byteLength + header.keyValueData.byteOffset; |
| 663 | else if (header.dataFormatDescriptor.byteLength != 0) |
| 664 | expectedFirstLevelOffset = header.dataFormatDescriptor.byteLength + header.dataFormatDescriptor.byteOffset; |
| 665 | else |
| 666 | expectedFirstLevelOffset = levelIndexOffset + levelIndexSize; |
| 667 | expectedFirstLevelOffset = align(expectedFirstLevelOffset, requiredLevelAlignment); |
| 668 | |
| 669 | // The first (largest) mip level is the first in the index and the last in the file. |
| 670 | for (std::size_t i = 1; i < levelIndices.size(); ++i) { |
| 671 | if (levelIndices[i].uncompressedByteLength > levelIndices[i - 1].uncompressedByteLength) |
| 672 | error(LevelIndex::IncorrectIndexOrder, i - 1, levelIndices[i - 1].uncompressedByteLength, i, levelIndices[i].uncompressedByteLength); |
| 673 | |
| 674 | if (levelIndices[i].byteOffset > levelIndices[i - 1].byteOffset) |
| 675 | error(LevelIndex::IncorrectLevelOrder, i - 1, levelIndices[i - 1].byteOffset, i, levelIndices[i].byteOffset); |
| 676 | } |
| 677 | |
| 678 | std::size_t lastByteOffset = expectedFirstLevelOffset; // Reuse lastByteOffset to inject first offset to expectedOffset |
| 679 | std::size_t lastByteLength = 0; |
| 680 | |
| 681 | // Count down, so we can check the distance between levels for the UNDEFINED and SUPERCOMPRESSION cases. |
| 682 | for (auto it = static_cast<uint32_t>(levelIndices.size()); it != 0; --it) { |
| 683 | const auto index = it - 1; |
| 684 | const auto& level = levelIndices[index]; |
| 685 | |
| 686 | // Validate byteOffset |
| 687 | const auto knownLevelOffset = header.vkFormat != VK_FORMAT_UNDEFINED && |
| 688 | header.supercompressionScheme == KTX_SS_NONE; |
| 689 | // If the exact level sizes are unknown we have to trust the byteLengths. |
| 690 | // In that case we know where the first level must be in the file, and we can calculate |
| 691 | // the offsets by progressively summing the lengths and paddings so far. |
| 692 | const auto expectedOffset = knownLevelOffset ? |
| 693 | calcLevelOffset(expectedFirstLevelOffset, requiredLevelAlignment, index) : |
| 694 | align(lastByteOffset + lastByteLength, requiredLevelAlignment); |
| 695 | |
| 696 | if (level.byteOffset % requiredLevelAlignment != 0) |
| 697 | error(LevelIndex::IncorrectByteOffsetUnaligned, index, level.byteOffset, requiredLevelAlignment, expectedOffset); |
| 698 | else if (level.byteOffset != expectedOffset) |
| 699 | error(LevelIndex::IncorrectByteOffset, index, level.byteOffset, expectedOffset); |
| 700 | |
| 701 | // Workaround: Disable ByteLength validations for the 3D ASTC encoder which currently ignores partial Z blocks in our test files |
| 702 | const auto disableByteLengthValidation = isFormat3DBlockCompressed(VkFormat(header.vkFormat)) |
| 703 | && header.pixelDepth % (expectedBlockDimension2.value_or(0) + 1) != 0; |
| 704 | |
| 705 | if (!disableByteLengthValidation) { |
nothing calls this directly
no test coverage detected