/*
 * Copyright (C) 2022 Collabora, Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "pan_afbc.h"
#include "pan_afrc.h"
#include "pan_format.h"
#include "pan_image.h"
#include "pan_layout.h"
#include "pan_mod.h"

#include <gtest/gtest.h>

TEST(Align, UTiledLinear)
{
   struct {
      unsigned arch;
      enum pipe_format format;
      unsigned plane_idx;
      unsigned alignment;
   } cases[] = {
      { 6, PIPE_FORMAT_ETC2_RGB8, 0, 8 },
      { 6, PIPE_FORMAT_R32G32B32_FLOAT, 0, 4 },
      { 6, PIPE_FORMAT_R8G8B8A8_UNORM, 0, 1 },
      { 6, PIPE_FORMAT_R5G6B5_UNORM, 0, 2 },
      { 6, PIPE_FORMAT_R8_G8B8_420_UNORM, 0, 1 },
      { 6, PIPE_FORMAT_R8_G8B8_420_UNORM, 1, 2 },
      { 7, PIPE_FORMAT_ETC2_RGB8, 0, 64 },
      { 7, PIPE_FORMAT_R32G32B32_FLOAT, 0, 64 },
      { 7, PIPE_FORMAT_R8G8B8A8_UNORM, 0, 64 },
      { 7, PIPE_FORMAT_R5G6B5_UNORM, 0, 64 },
      { 7, PIPE_FORMAT_R8_G8B8_420_UNORM, 0, 16 },
      { 7, PIPE_FORMAT_R8_G8B8_420_UNORM, 1, 16 },
      { 7, PIPE_FORMAT_R10_G10B10_420_UNORM, 0, 1 },
      { 7, PIPE_FORMAT_R10_G10B10_420_UNORM, 1, 1 },
   };
   for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
      unsigned align = pan_linear_or_tiled_row_align_req(
         cases[i].arch, cases[i].format, cases[i].plane_idx);

      EXPECT_EQ(align, cases[i].alignment);
   }
}

TEST(BlockSize, UInterleavedRegular)
{
   enum pipe_format format[] = {
      PIPE_FORMAT_R32G32B32_FLOAT,
      PIPE_FORMAT_R8G8B8_UNORM,
   };

   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
      struct pan_image_block_size blk =
         pan_u_interleaved_tile_size_el(format[i]);

      EXPECT_EQ(blk.width, 16);
      EXPECT_EQ(blk.height, 16);
   }
}

TEST(BlockSize, UInterleavedBlockCompressed)
{
   enum pipe_format format[] = {PIPE_FORMAT_ETC2_RGB8, PIPE_FORMAT_ASTC_5x5};

   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
      struct pan_image_block_size blk =
         pan_u_interleaved_tile_size_el(format[i]);

      EXPECT_EQ(blk.width, 4);
      EXPECT_EQ(blk.height, 4);
   }
}

TEST(BlockSize, AFBCFormatInvariant16x16)
{
   enum pipe_format format[] = {PIPE_FORMAT_R32G32B32_FLOAT,
                                PIPE_FORMAT_R8G8B8_UNORM};

   uint64_t modifier =
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
                              AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR);

   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
      struct pan_image_block_size blk =
         pan_afbc_superblock_size_el(format[i], modifier);

      EXPECT_EQ(blk.width, 16);
      EXPECT_EQ(blk.height, 16);
   }
}

TEST(BlockSize, AFBCFormatInvariant32x8)
{
   enum pipe_format format[] = {PIPE_FORMAT_R32G32B32_FLOAT,
                                PIPE_FORMAT_R8G8B8_UNORM};

   uint64_t modifier =
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
                              AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR);

   for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
      struct pan_image_block_size blk =
         pan_afbc_superblock_size_el(format[i], modifier);

      EXPECT_EQ(blk.width, 32);
      EXPECT_EQ(blk.height, 8);
   }
}

TEST(BlockSize, AFBCSuperblock16x16)
{
   uint64_t modifier =
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
                              AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR);

   EXPECT_EQ(pan_afbc_superblock_size(modifier).width, 16);
   EXPECT_EQ(pan_afbc_superblock_width(modifier), 16);

   EXPECT_EQ(pan_afbc_superblock_size(modifier).height, 16);
   EXPECT_EQ(pan_afbc_superblock_height(modifier), 16);

   EXPECT_FALSE(pan_afbc_is_wide(modifier));
}

TEST(BlockSize, AFBCSuperblock32x8)
{
   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
                                               AFBC_FORMAT_MOD_SPARSE);

   EXPECT_EQ(pan_afbc_superblock_size(modifier).width, 32);
   EXPECT_EQ(pan_afbc_superblock_width(modifier), 32);

   EXPECT_EQ(pan_afbc_superblock_size(modifier).height, 8);
   EXPECT_EQ(pan_afbc_superblock_height(modifier), 8);

   EXPECT_TRUE(pan_afbc_is_wide(modifier));
}

TEST(BlockSize, AFBCSuperblock64x4)
{
   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
                                               AFBC_FORMAT_MOD_SPARSE);

   EXPECT_EQ(pan_afbc_superblock_size(modifier).width, 64);
   EXPECT_EQ(pan_afbc_superblock_width(modifier), 64);

   EXPECT_EQ(pan_afbc_superblock_size(modifier).height, 4);
   EXPECT_EQ(pan_afbc_superblock_height(modifier), 4);

   EXPECT_TRUE(pan_afbc_is_wide(modifier));
}

/* Calculate Bifrost line stride, since we have reference formulas for Bifrost
 * stride calculations.
 */
static uint32_t
pan_afbc_line_stride(uint64_t modifier, uint32_t width)
{
   return pan_afbc_stride_blocks(modifier,
                                 pan_afbc_row_stride(modifier, width));
}

/* Which form of the stride we specify is hardware specific (row stride for
 * Valhall, line stride for Bifrost). However, the layout code is hardware
 * independent, so we test both row stride and line stride calculations.
 */
TEST(AFBCStride, Linear)
{
   uint64_t modifiers[] = {
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
                              AFBC_FORMAT_MOD_SPARSE),
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
                              AFBC_FORMAT_MOD_SPARSE),
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
                              AFBC_FORMAT_MOD_SPARSE),
   };

   for (unsigned m = 0; m < ARRAY_SIZE(modifiers); ++m) {
      uint64_t modifier = modifiers[m];

      uint32_t sw = pan_afbc_superblock_width(modifier);
      uint32_t cases[] = {1, 4, 17, 39};

      for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
         uint32_t width = sw * cases[i];

         EXPECT_EQ(pan_afbc_row_stride(modifier, width),
                   16 * DIV_ROUND_UP(width, sw));

         EXPECT_EQ(pan_afbc_line_stride(modifier, width),
                   DIV_ROUND_UP(width, sw));
      }
   }
}

TEST(AFBCStride, Tiled)
{
   uint64_t modifiers[] = {
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
                              AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE),
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
                              AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE),
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
                              AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE),
   };

   for (unsigned m = 0; m < ARRAY_SIZE(modifiers); ++m) {
      uint64_t modifier = modifiers[m];

      uint32_t sw = pan_afbc_superblock_width(modifier);
      uint32_t cases[] = {1, 4, 17, 39};

      for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
         uint32_t width = sw * 8 * cases[i];

         EXPECT_EQ(pan_afbc_row_stride(modifier, width),
                   16 * DIV_ROUND_UP(width, (sw * 8)) * 8 * 8);

         EXPECT_EQ(pan_afbc_line_stride(modifier, width),
                   DIV_ROUND_UP(width, sw * 8) * 8);
      }
   }
}

static bool
layout_init(unsigned arch, const struct pan_image_props *props,
            unsigned plane_idx,
            const struct pan_image_layout_constraints *layout_constraints,
            struct pan_image_layout *layout)
{
   /* Pick the first supported arch if it's zero. */
   if (!arch)
      arch = 4;

   struct pan_image_plane plane = {
   };
   struct pan_image img = {
      .props = *props,
      .mod_handler = pan_mod_get_handler(arch, props->modifier),
   };

   img.planes[plane_idx] = &plane;

   if (!pan_image_layout_init(arch, &img, plane_idx, layout_constraints))
      return false;

   *layout = plane.layout;
   return true;
}

/* dEQP-GLES3.functional.texture.format.compressed.etc1_2d_pot */
TEST(Layout, ImplicitLayoutInterleavedETC2)
{
   struct pan_image_props p = {
      .modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
      .format = PIPE_FORMAT_ETC2_RGB8,
      .extent_px = {
         .width = 128,
         .height = 128,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 8,
   };
   struct pan_image_layout l = {};

   unsigned offsets[9] = {0,     8192,  10240, 10752, 10880,
                          11008, 11136, 11264, 11392};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   for (unsigned i = 0; i < 8; ++i) {
      unsigned size = (offsets[i + 1] - offsets[i]);
      EXPECT_EQ(l.slices[i].offset_B, offsets[i]);

      if (size == 64)
         EXPECT_TRUE(l.slices[i].size_B < 64);
      else
         EXPECT_EQ(l.slices[i].size_B, size);
   }
}

TEST(Layout, ImplicitLayoutInterleavedASTC5x5)
{
   struct pan_image_props p = {
      .modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
      .format = PIPE_FORMAT_ASTC_5x5,
      .extent_px = {
         .width = 50,
         .height = 50,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   /* The image is 50x50 pixels, with 5x5 blocks. So it is a 10x10 grid of ASTC
    * blocks. 4x4 tiles of ASTC blocks are u-interleaved, so we have to round up
    * to a 12x12 grid. So we need space for 144 ASTC blocks. Each ASTC block is
    * 16 bytes (128-bits), so we require 2304 bytes, with a row stride of 12 *
    * 16 * 4 = 192 bytes.
    */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].tiled_or_linear.row_stride_B, 768);
   EXPECT_EQ(l.slices[0].tiled_or_linear.surface_stride_B, 2304);
   EXPECT_EQ(l.slices[0].size_B, 2304);
}

TEST(Layout, ImplicitLayoutLinearASTC5x5)
{
   struct pan_image_props p = {
      .modifier = DRM_FORMAT_MOD_LINEAR,
      .format = PIPE_FORMAT_ASTC_5x5,
      .extent_px = {
         .width = 50,
         .height = 50,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   /* The image is 50x50 pixels, with 5x5 blocks. So it is a 10x10 grid of ASTC
    * blocks. Each ASTC block is 16 bytes, so the row stride is 160 bytes,
    * rounded up to the cache line (192 bytes).  There are 10 rows, so we have
    * 1920 bytes total.
    */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].tiled_or_linear.row_stride_B, 192);
   EXPECT_EQ(l.slices[0].tiled_or_linear.surface_stride_B, 1920);
   EXPECT_EQ(l.slices[0].size_B, 1920);
}

/* dEQP-GLES3.functional.texture.format.unsized.rgba_unsigned_byte_3d_pot */
TEST(AFBCLayout, Linear3D)
{
   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
      AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);

   struct pan_image_props p = {
      .modifier = modifier,
      .format = PIPE_FORMAT_R8G8B8A8_UNORM,
      .extent_px = {
         .width = 8,
         .height = 32,
         .depth = 16,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_3D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   /* AFBC Surface size is the size of headers for a single surface. At superblock
    * size 16x16, the 8x32 layer has 1x2 superblocks, so the header size is 2 *
    * 16 = 32 bytes. Body offset needs to be aligned on 64 bytes on v6-.
    * Header/body sections of a 3D image are interleaved, so the surface stride is
    * is the header size, aligned to meet body offset alignment constraints, plus
    * the body of a single surface.
    *
    * There is only 1 superblock per row, so the row stride is the bytes per 1
    * header block = 16.
    *
    * There are 16 layers of size 64 so afbc.header_size = 16 * 64 = 1024.
    *
    * Each 16x16 superblock consumes 16 * 16 * 4 = 1024 bytes. There are 2 * 1 *
    * 16 superblocks in the image, so body size is 32768.
    */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16);
   EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 32);
   EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 64 + 2048);
   EXPECT_EQ(l.slices[0].size_B, (64 + 2048) * 16);
}

TEST(AFBCLayout, Tiled16x16)
{
   uint64_t modifier =
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
                              AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE);

   struct pan_image_props p = {
      .modifier = modifier,
      .format = PIPE_FORMAT_R8G8B8A8_UNORM,
      .extent_px = {
         .width = 917,
         .height = 417,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   /* The image is 917x417. Superblocks are 16x16, so there are 58x27
    * superblocks. Superblocks are grouped into 8x8 tiles, so there are 8x4
    * tiles of superblocks. So the row stride is 16 * 8 * 8 * 8 = 8192 bytes.
    * There are 4 tiles vertically, so the header is 8192 * 4 = 32768 bytes.
    * This is already 4096-byte aligned.
    *
    * Each tile of superblock contains 128x128 pixels and each pixel is 4 bytes,
    * so tiles are 65536 bytes, meaning the payload is 8 * 4 * 65536 = 2097152
    * bytes.
    *
    * In total, the AFBC surface is 32768 + 2097152 = 2129920 bytes.
    */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 8192);
   EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 32768);
   EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 2129920);
   EXPECT_EQ(l.slices[0].size_B, 2129920);
}

TEST(AFBCLayout, Linear16x16Minimal)
{
   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
      AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);

   struct pan_image_props p = {
      .modifier = modifier,
      .format = PIPE_FORMAT_R8_UNORM,
      .extent_px = {
         .width = 1,
         .height = 1,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   /* Image is 1x1 to test for correct alignment everywhere. */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16);
   EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 16);
   EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 64 + (32 * 8));
   EXPECT_EQ(l.slices[0].size_B, 64 + (32 * 8));
}

TEST(AFBCLayout, Linear16x16Minimalv6)
{
   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
      AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);

   struct pan_image_props p = {
      .modifier = modifier,
      .format = PIPE_FORMAT_R8_UNORM,
      .extent_px = {
         .width = 1,
         .height = 1,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(6, &p, 0, NULL, &l));

   /* Image is 1x1 to test for correct alignment everywhere. */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16);
   EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 16);
   EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 128 + (32 * 8));
   EXPECT_EQ(l.slices[0].size_B, 128 + (32 * 8));
}

TEST(AFBCLayout, Tiled16x16Minimal)
{
   uint64_t modifier =
      DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
                              AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE);

   struct pan_image_props p = {
      .modifier = modifier,
      .format = PIPE_FORMAT_R8_UNORM,
      .extent_px = {
         .width = 1,
         .height = 1,
         .depth = 1,
      },
      .nr_samples = 1,
      .dim = MALI_TEXTURE_DIMENSION_2D,
      .nr_slices = 1,
   };
   struct pan_image_layout l = {};

   ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));

   /* Image is 1x1 to test for correct alignment everywhere. */
   EXPECT_EQ(l.slices[0].offset_B, 0);
   EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16 * 8 * 8);
   EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 16 * 8 * 8);
   EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 4096 + (32 * 8 * 8 * 8));
   EXPECT_EQ(l.slices[0].size_B, 4096 + (32 * 8 * 8 * 8));
}

static unsigned archs[] = {4, 5, 6, 7, 9, 12, 13};

#define IMAGE_WIDTH  4096
#define IMAGE_HEIGHT 512
#define IMAGE_FORMAT                                                           \
   (PAN_BIND_DEPTH_STENCIL | PAN_BIND_RENDER_TARGET | PAN_BIND_SAMPLER_VIEW |  \
    PAN_BIND_STORAGE_IMAGE)

#define EXPECT_IMPORT_SUCCESS(__arch, __iprops, __plane, __wsi_layout,         \
                              __out_layout, __test_desc)                       \
   do {                                                                        \
      bool __result =                                                          \
         layout_init(__arch, __iprops, __plane, __wsi_layout, __out_layout);   \
      EXPECT_TRUE(__result)                                                    \
         << __test_desc                                                        \
         << " for <format=" << util_format_name((__iprops)->format)            \
         << ",plane=" << __plane << ",mod=" << std::hex                        \
         << (__iprops)->modifier << std::dec << "> rejected (arch=" << __arch  \
         << ")";                                                               \
                                                                               \
      if (!__result)                                                           \
         break;                                                                \
                                                                               \
      struct pan_image_plane img_plane = {                                     \
         .layout = *(__out_layout),                                            \
      };                                                                       \
      struct pan_image img = {                                                 \
         .props = *(__iprops),                                                 \
         .mod_handler = pan_mod_get_handler(__arch, (__iprops)->modifier),     \
      };                                                                       \
      img.planes[__plane] = &img_plane;                                        \
      unsigned __export_row_pitch_B =                                          \
         pan_image_get_wsi_row_pitch(&img, __plane, 0);                        \
      unsigned __export_offset_B = pan_image_get_wsi_offset(&img, __plane, 0); \
      EXPECT_TRUE(__export_row_pitch_B == (__wsi_layout)->wsi_row_pitch_B &&   \
                  __export_offset_B == (__wsi_layout)->offset_B)               \
         << " mismatch between import and export for <format="                 \
         << util_format_name(iprops.format) << ",plane=" << __plane            \
         << ",mod=" << std::hex << (__iprops)->modifier << std::dec            \
         << "> (arch=" << __arch << ")";                                       \
   } while (0)

#define EXPECT_IMPORT_FAIL(__arch, __iprops, __plane, __wsi_layout,            \
                           __out_layout, __test_desc)                          \
   EXPECT_FALSE(                                                               \
      layout_init(__arch, __iprops, __plane, __wsi_layout, __out_layout))      \
      << __test_desc                                                           \
      << " for <format=" << util_format_name((__iprops)->format)               \
      << ",plane=" << __plane << ",mod=" << std::hex << (__iprops)->modifier   \
      << std::dec << "> not rejected (arch=" << __arch << ")"

static bool
format_can_do_mod(unsigned arch, enum pipe_format format, unsigned plane_idx,
                  uint64_t modifier)
{
   if (drm_is_afbc(modifier)) {
      return pan_afbc_format(arch, format, plane_idx) != PAN_AFBC_MODE_INVALID;
   } else if (drm_is_afrc(modifier)) {
      return arch >= 10 && pan_afrc_supports_format(format);
   } else {
      assert(modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ||
             modifier == DRM_FORMAT_MOD_LINEAR);

      switch (format) {
      case PIPE_FORMAT_R8G8B8_420_UNORM_PACKED:
      case PIPE_FORMAT_R10G10B10_420_UNORM_PACKED:
         /* Those are only supported with AFBC. */
         return false;

      default:
         return true;
      }
   }
}

static unsigned
offset_align_for_mod(unsigned arch, const struct pan_image_props *iprops,
                     unsigned plane_idx)
{
   uint64_t modifier = iprops->modifier;
   enum pipe_format format = iprops->format;

   if (drm_is_afbc(modifier)) {
      return pan_afbc_header_align(arch, modifier);
   } else if (drm_is_afrc(modifier)) {
      return pan_afrc_buffer_alignment_from_modifier(modifier);
   } else {
      assert(modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ||
             modifier == DRM_FORMAT_MOD_LINEAR);

      return pan_linear_or_tiled_row_align_req(arch, format, plane_idx);
   }
}

static unsigned
row_align_for_mod(unsigned arch, const struct pan_image_props *iprops,
                  unsigned plane_idx)
{
   uint64_t modifier = iprops->modifier;
   enum pipe_format format = iprops->format;

   if (drm_is_afbc(modifier)) {
      unsigned hdr_row_align =
         pan_afbc_header_row_stride_align(arch, format, modifier);
      unsigned ntiles = hdr_row_align / AFBC_HEADER_BYTES_PER_TILE;
      unsigned sb_width_el = pan_afbc_superblock_width(modifier) /
                             util_format_get_blockwidth(format);

      assert(pan_afbc_superblock_width(modifier) %
                util_format_get_blockwidth(format) ==
             0);
      return ntiles * sb_width_el *
             pan_format_get_plane_blocksize(format, plane_idx);
   } else if (drm_is_afrc(modifier)) {
      unsigned row_align = pan_afrc_buffer_alignment_from_modifier(modifier);
      struct pan_image_block_size tile_size_px =
         pan_afrc_tile_size(format, modifier);

      assert(row_align % tile_size_px.height == 0);
      return row_align / tile_size_px.height;
   } else {
      assert(modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ||
             modifier == DRM_FORMAT_MOD_LINEAR);

      unsigned tile_height_el = modifier == DRM_FORMAT_MOD_LINEAR   ? 1
                                : util_format_is_compressed(format) ? 4
                                                                    : 16;

      return DIV_ROUND_UP(offset_align_for_mod(arch, iprops, plane_idx),
                          tile_height_el);
   }
}

static unsigned
default_wsi_row_pitch(unsigned arch, const struct pan_image_props *iprops,
                      unsigned plane_idx)
{
   uint64_t modifier = iprops->modifier;
   enum pipe_format format = iprops->format;
   unsigned fmt_blksz_B = pan_format_get_plane_blocksize(format, plane_idx);
   unsigned width_px =
      util_format_get_plane_width(format, plane_idx, iprops->extent_px.width);

   assert(width_px % util_format_get_blockwidth(format) == 0);

   if (drm_is_afbc(modifier)) {
      unsigned sb_width_el = pan_afbc_superblock_width(modifier) /
                             util_format_get_blockwidth(format);
      unsigned sb_height_el = pan_afbc_superblock_height(modifier) /
                              util_format_get_blockheight(format);
      unsigned ntiles =
         DIV_ROUND_UP(width_px, pan_afbc_superblock_width(modifier));
      unsigned tile_row_size_B =
         sb_width_el * sb_height_el * fmt_blksz_B * ntiles;

      assert(pan_afbc_superblock_width(modifier) %
                util_format_get_blockwidth(format) ==
             0);
      assert(pan_afbc_superblock_height(modifier) %
                util_format_get_blockheight(format) ==
             0);
      assert(tile_row_size_B % pan_afbc_superblock_height(modifier) == 0);
      return tile_row_size_B / pan_afbc_superblock_height(modifier);
   } else if (drm_is_afrc(modifier)) {
      struct pan_image_block_size tile_size =
         pan_afrc_tile_size(format, modifier);
      unsigned afrc_row_stride_B =
         pan_afrc_row_stride(format, modifier, width_px);

      assert(afrc_row_stride_B % tile_size.height == 0);

      return afrc_row_stride_B / tile_size.height;
   } else {
      assert(modifier == DRM_FORMAT_MOD_LINEAR ||
             modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED);

      unsigned row_pitch_B =
         (width_px / util_format_get_blockwidth(format)) * fmt_blksz_B;
      struct pan_image_block_size tile_size_el = {1, 1};

      if (modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {
         if (util_format_is_compressed(format)) {
            tile_size_el.width = 4;
            tile_size_el.height = 4;
         } else {
            tile_size_el.width = 16;
            tile_size_el.height = 16;
         }
      }

      assert(width_px %
                (util_format_get_blockwidth(format) * tile_size_el.width) ==
             0);

      return row_pitch_B;
   }
}

TEST(WSI, Import)
{
   /* We don't want to spam stderr with failure messages caused by our
    * EXPECT_FALSE() cases. */
   setenv("MESA_LOG", "null", 0);

   struct pan_image_layout layout;
   for (unsigned i = 0; i < ARRAY_SIZE(archs); i++) {
      unsigned arch = archs[i];
      const struct pan_format *ftable = pan_format_table(arch);
      PAN_SUPPORTED_MODIFIERS(mods);

      for (unsigned m = 0; m < ARRAY_SIZE(mods); m++) {
         for (unsigned fmt = PIPE_FORMAT_NONE + 1; fmt < PIPE_FORMAT_COUNT;
              fmt++) {
            if (!(ftable[fmt].bind & IMAGE_FORMAT))
               continue;


            struct pan_image_props iprops = {
               .modifier = mods[m],
               .format = (enum pipe_format)fmt,
               .extent_px = {
                  .width = IMAGE_WIDTH,
                  .height = IMAGE_HEIGHT,
                  .depth = 1,
               },
               .nr_samples = 1,
               .dim = MALI_TEXTURE_DIMENSION_2D,
               .nr_slices = 1,
               .array_size = 1,
               .crc = false,
            };

            bool supported = true;
            for (unsigned p = 0; p < util_format_get_num_planes(iprops.format);
                 p++) {
               if (!format_can_do_mod(arch, iprops.format, p,
                                      iprops.modifier)) {
                  supported = false;
                  break;
               }
            }

            if (!supported)
               continue;

            if (util_format_is_compressed(iprops.format)) {
               /* We multiply the image extent by the block extent to make sure
                * things are always aligned on a block. */
               iprops.extent_px.width *=
                  util_format_get_blockwidth(iprops.format);
               iprops.extent_px.height *=
                  util_format_get_blockheight(iprops.format);
            }

            for (unsigned p = 0; p < util_format_get_num_planes(iprops.format);
                 p++) {
               unsigned row_align_req_B =
                  row_align_for_mod(arch, &iprops, p);
               unsigned offset_align_req_B =
                  offset_align_for_mod(arch, &iprops, p);
               unsigned default_row_pitch_B =
                  default_wsi_row_pitch(arch, &iprops, p);

               assert(default_row_pitch_B > row_align_req_B);

               if (row_align_req_B > 1) {
                  struct pan_image_layout_constraints wsi_layout = {
                     .wsi_row_pitch_B = default_row_pitch_B + 1,
                     .strict = true,
                  };

                  EXPECT_IMPORT_FAIL(arch, &iprops, p, &wsi_layout, &layout,
                                     "unaligned WSI row pitch");
               }

               if (offset_align_req_B > 1) {
                  struct pan_image_layout_constraints wsi_layout = {
                     .offset_B = 1,
                     .wsi_row_pitch_B = default_row_pitch_B,
                     .strict = true,
                  };

                  EXPECT_IMPORT_FAIL(arch, &iprops, p, &wsi_layout, &layout,
                                     "unaligned WSI offset");
               }

               /* Exact match. */
               struct pan_image_layout_constraints wsi_layout = {
                  .wsi_row_pitch_B = default_row_pitch_B,
                  .strict = true,
               };

               EXPECT_IMPORT_SUCCESS(arch, &iprops, p, &wsi_layout, &layout,
                                     "tightly packed lines");

               wsi_layout.wsi_row_pitch_B =
                  default_row_pitch_B + row_align_req_B;
               EXPECT_IMPORT_SUCCESS(arch, &iprops, p, &wsi_layout, &layout,
                                     "lines with padding");

               wsi_layout.wsi_row_pitch_B =
                  default_row_pitch_B - row_align_req_B;
               EXPECT_IMPORT_FAIL(arch, &iprops, p, &wsi_layout, &layout,
                                  "partially aliased lines");

               wsi_layout.wsi_row_pitch_B = default_row_pitch_B;
               wsi_layout.offset_B = offset_align_req_B;
               EXPECT_IMPORT_SUCCESS(arch, &iprops, p, &wsi_layout, &layout,
                                     "properly aligned offset");
            }
         }
      }
   }
}

TEST(AFBCP, PayloadLayout)
{
   /* Test pan_afbc_payload_layout_packed(). The test data is a dump of the
    * first resource packed when packing is enabled for Weston. */

   unsigned arch = 7;
   enum pipe_format format = PIPE_FORMAT_R8G8B8A8_UNORM;
   uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
      AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);
   uint32_t uncompressed_size = pan_afbc_payload_uncompressed_size(
      format, modifier);
   const uint32_t nr_blocks = 241;

   static const uint8_t headers[] = {
      0x00, 0x0f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x8e, 0x6c, 0xa7, 0xb1, 0x07, 0x00, 0x00,
      0x00, 0x13, 0x00, 0x00, 0x2c, 0x7b, 0x00, 0x00, 0xc0, 0xb2, 0x5d, 0xb7, 0x69, 0x9e, 0xd7, 0x71,
      0x00, 0x17, 0x00, 0x00, 0xc7, 0x78, 0x00, 0x00, 0x00, 0x00, 0x40, 0xbc, 0xcf, 0x07, 0xcb, 0x1e,
      0x00, 0x1b, 0x00, 0x00, 0x7e, 0x7c, 0x00, 0x00, 0x00, 0xa0, 0x3d, 0xca, 0x9b, 0x24, 0xc7, 0x97,
      0x00, 0x1f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x23, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x27, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x33, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x37, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x43, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x47, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x53, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x57, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x63, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x67, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x73, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x77, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x83, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x87, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x93, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x97, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9b, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa7, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xab, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xaf, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb7, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbb, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbf, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc7, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xcb, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xcf, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xd3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xd7, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xdb, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xdf, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xe3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xe7, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xeb, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xef, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xf3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xf7, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xfb, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xff, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x03, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x07, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x0b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x0f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x13, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x17, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x1b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x1f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x23, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x27, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x33, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x37, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x43, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x47, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x53, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x57, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x63, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x67, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x73, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x77, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x83, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x87, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x93, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x97, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9b, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9f, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa7, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xab, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xaf, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb7, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbb, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbf, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc7, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x5e, 0x07, 0x00, 0x00,
      0x00, 0xcb, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x92, 0xe2, 0x78, 0x00,
      0x00, 0xcf, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x92, 0x26, 0x76, 0x00,
      0x00, 0xd3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x72, 0x63, 0x78, 0x00,
      0x00, 0xd7, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x65, 0x79, 0x00,
      0x00, 0xdb, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x96, 0xe4, 0x01, 0x00,
      0x00, 0xdf, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0xe4, 0x79, 0x00,
      0x00, 0xe3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0xe6, 0x78, 0x00,
      0x00, 0xe7, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x96, 0x67, 0x79, 0x00,
      0x00, 0xeb, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00,
      0x00, 0xef, 0x01, 0x00, 0x07, 0x00, 0x00, 0x2c, 0x07, 0xaa, 0xe3, 0x79, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xf3, 0x01, 0x00, 0xe3, 0x78, 0x69, 0x18, 0x07, 0x8e, 0xe7, 0x79, 0x00, 0x00, 0x70, 0x9e,
      0x00, 0xf7, 0x01, 0x00, 0x87, 0xca, 0x1e, 0x28, 0x7f, 0x2e, 0x47, 0x73, 0x00, 0x00, 0x30, 0x1e,
      0x00, 0xfb, 0x01, 0x00, 0xad, 0x3e, 0x92, 0x2e, 0x4f, 0xf1, 0x92, 0x72, 0x00, 0x00, 0x80, 0xde,
      0x00, 0xff, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x03, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x07, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x0b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x0f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x13, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x17, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x1b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x1f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x23, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x27, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x33, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x37, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x43, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x47, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x53, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x57, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x63, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x67, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x73, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x77, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x83, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x87, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x93, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x97, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9b, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9f, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa3, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa7, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xab, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xaf, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb3, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb7, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbb, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbf, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc3, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc7, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xcb, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xcf, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xd3, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xd7, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xdb, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xdf, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xe3, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xe7, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xeb, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xef, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xf3, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xf7, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xfb, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xff, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x03, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x07, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x0b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x0f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x13, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x17, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x1b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x1f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x23, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x27, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x2f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x33, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x37, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x3f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x43, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x47, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x4f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x53, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x57, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x5f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x63, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x67, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x6f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x73, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x77, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x7f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x83, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x87, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x8f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x93, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x97, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9b, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x9f, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa3, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xa7, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x59, 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xab, 0x03, 0x00, 0xe5, 0x58, 0x9a, 0xa4, 0x58, 0x92, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xaf, 0x03, 0x00, 0x26, 0x76, 0x94, 0xe7, 0x58, 0x92, 0x07, 0x00, 0x00, 0x00, 0x20, 0x8a,
      0x00, 0xb3, 0x03, 0x00, 0x22, 0x49, 0x96, 0xa2, 0x24, 0x82, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xb7, 0x03, 0x00, 0x63, 0x59, 0x7e, 0xe4, 0x41, 0x92, 0xc7, 0x74, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xbb, 0x03, 0x00, 0x5e, 0x78, 0x90, 0x21, 0x49, 0x96, 0x07, 0x00, 0x00, 0x00, 0x80, 0x1d,
      0x00, 0xbf, 0x03, 0x00, 0x64, 0x49, 0x92, 0x65, 0x58, 0x8e, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc3, 0x03, 0x00, 0xa2, 0x58, 0x9e, 0xa4, 0x08, 0x7e, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xc7, 0x03, 0x00, 0xa0, 0x68, 0x96, 0xa6, 0x39, 0x92, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0xcb, 0x03, 0x00, 0xc7, 0x38, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

      /* Manual addition. All subblocks have a size of 1 and must be converted to uncompressed. */
      0x00, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
   };

   static const struct pan_afbc_payload_extent expected_layout[] = {
      { 240,     0 }, { 416,   240 }, { 320,   656 }, { 528,   976 },
      {  16,  1504 }, {  16,  1520 }, {  16,  1536 }, {  16,  1552 },
      {  16,  1568 }, {  16,  1584 }, {  16,  1600 }, {  16,  1616 },
      {  16,  1632 }, {  16,  1648 }, {  16,  1664 }, {  16,  1680 },
      {  16,  1696 }, {  16,  1712 }, {  16,  1728 }, {  16,  1744 },
      {  16,  1760 }, {  16,  1776 }, {  16,  1792 }, {  16,  1808 },
      {  16,  1824 }, {  16,  1840 }, {  16,  1856 }, {  16,  1872 },
      {  16,  1888 }, {  16,  1904 }, {  16,  1920 }, {  16,  1936 },
      {  16,  1952 }, {  16,  1968 }, {  16,  1984 }, {  16,  2000 },
      {  16,  2016 }, {  16,  2032 }, {  16,  2048 }, {  16,  2064 },
      {  16,  2080 }, {  16,  2096 }, {  16,  2112 }, {  16,  2128 },
      {  16,  2144 }, {  16,  2160 }, {  16,  2176 }, {  16,  2192 },
      {  16,  2208 }, {  16,  2224 }, {  16,  2240 }, {  16,  2256 },
      {  16,  2272 }, {  16,  2288 }, {  16,  2304 }, {  16,  2320 },
      {  16,  2336 }, {  16,  2352 }, {  16,  2368 }, {  16,  2384 },
      {  16,  2400 }, {  16,  2416 }, {  16,  2432 }, {  16,  2448 },
      {  16,  2464 }, {  16,  2480 }, {  16,  2496 }, {  16,  2512 },
      {  16,  2528 }, {  16,  2544 }, {  16,  2560 }, {  16,  2576 },
      {  16,  2592 }, {  16,  2608 }, {  16,  2624 }, {  16,  2640 },
      {  16,  2656 }, {  16,  2672 }, {  16,  2688 }, {  16,  2704 },
      {  16,  2720 }, {  16,  2736 }, {  16,  2752 }, {  16,  2768 },
      {  16,  2784 }, {  16,  2800 }, {  16,  2816 }, {  16,  2832 },
      {  16,  2848 }, {  16,  2864 }, {  16,  2880 }, {  16,  2896 },
      {  16,  2912 }, {  16,  2928 }, {  16,  2944 }, {  16,  2960 },
      {  16,  2976 }, {  16,  2992 }, {  16,  3008 }, {  16,  3024 },
      {  16,  3040 }, {  16,  3056 }, {  16,  3072 }, {  16,  3088 },
      {  16,  3104 }, {  16,  3120 }, {  16,  3136 }, {  16,  3152 },
      {  16,  3168 }, {  16,  3184 }, {  80,  3200 }, { 160,  3280 },
      { 160,  3440 }, { 144,  3600 }, { 128,  3744 }, { 128,  3872 },
      { 176,  4000 }, { 160,  4176 }, { 176,  4336 }, {  64,  4512 },
      { 240,  4576 }, { 416,  4816 }, { 320,  5232 }, { 496,  5552 },
      {  16,  6048 }, {  16,  6064 }, {  16,  6080 }, {  16,  6096 },
      {  16,  6112 }, {  16,  6128 }, {  16,  6144 }, {  16,  6160 },
      {  16,  6176 }, {  16,  6192 }, {  16,  6208 }, {  16,  6224 },
      {  16,  6240 }, {  16,  6256 }, {  16,  6272 }, {  16,  6288 },
      {  16,  6304 }, {  16,  6320 }, {  16,  6336 }, {  16,  6352 },
      {  16,  6368 }, {  16,  6384 }, {  16,  6400 }, {  16,  6416 },
      {  16,  6432 }, {  16,  6448 }, {  16,  6464 }, {  16,  6480 },
      {  16,  6496 }, {  16,  6512 }, {  16,  6528 }, {  16,  6544 },
      {  16,  6560 }, {  16,  6576 }, {  16,  6592 }, {  16,  6608 },
      {  16,  6624 }, {  16,  6640 }, {  16,  6656 }, {  16,  6672 },
      {  16,  6688 }, {  16,  6704 }, {  16,  6720 }, {  16,  6736 },
      {  16,  6752 }, {  16,  6768 }, {  16,  6784 }, {  16,  6800 },
      {  16,  6816 }, {  16,  6832 }, {  16,  6848 }, {  16,  6864 },
      {  16,  6880 }, {  16,  6896 }, {  16,  6912 }, {  16,  6928 },
      {  16,  6944 }, {  16,  6960 }, {  16,  6976 }, {  16,  6992 },
      {  16,  7008 }, {  16,  7024 }, {  16,  7040 }, {  16,  7056 },
      {  16,  7072 }, {  16,  7088 }, {  16,  7104 }, {  16,  7120 },
      {  16,  7136 }, {  16,  7152 }, {  16,  7168 }, {  16,  7184 },
      {  16,  7200 }, {  16,  7216 }, {  16,  7232 }, {  16,  7248 },
      {  16,  7264 }, {  16,  7280 }, {  16,  7296 }, {  16,  7312 },
      {  16,  7328 }, {  16,  7344 }, {  16,  7360 }, {  16,  7376 },
      {  16,  7392 }, {  16,  7408 }, {  16,  7424 }, {  16,  7440 },
      {  16,  7456 }, {  16,  7472 }, {  16,  7488 }, {  16,  7504 },
      {  16,  7520 }, {  16,  7536 }, {  16,  7552 }, {  16,  7568 },
      {  16,  7584 }, {  16,  7600 }, {  16,  7616 }, {  16,  7632 },
      {  16,  7648 }, {  16,  7664 }, {  16,  7680 }, {  16,  7696 },
      {  16,  7712 }, {  16,  7728 }, { 112,  7744 }, { 304,  7856 },
      { 336,  8160 }, { 272,  8496 }, { 288,  8768 }, { 288,  9056 },
      { 304,  9344 }, { 288,  9648 }, { 304,  9936 }, {  96, 10240 },

      /* Manual addition. See headers. */
      {  16 * uncompressed_size, 10240 + 96 },
   };

   const uint32_t expected_body_size = 10240 + 96 + 16 * uncompressed_size;

   struct pan_afbc_payload_extent layout[nr_blocks] = { { 0, 0 }, };
   uint32_t body_size = 0;

   body_size = pan_afbc_payload_layout_packed(
      arch, (struct pan_afbc_headerblock *)headers, layout, nr_blocks, format,
      modifier);

   ASSERT_EQ(body_size, expected_body_size);

   for (unsigned i = 0; i < nr_blocks; i++) {
      ASSERT_EQ(layout[i].size, expected_layout[i].size);
      ASSERT_EQ(layout[i].offset, expected_layout[i].offset);
   }
}
