read_R2004_section_info (Bit_Chain *restrict dat, Dwg_Data *restrict dwg,
                         uint32_t comp_data_size, uint32_t decomp_data_size)
{
  BITCODE_RC *decomp, *ptr, *decomp_end;
  BITCODE_BL i, j;
  int32_t section_number = 0;
  uint32_t data_size, maxsize;
  uint64_t offset;
  int error;

  decomp = (BITCODE_RC *)calloc (decomp_data_size + 1024, sizeof (BITCODE_RC));
  if (!decomp)
    {
      LOG_ERROR ("Out of memory");
      return DWG_ERR_OUTOFMEM;
    }

  error = decompress_R2004_section (dat, decomp, decomp_data_size + 1024,
                                    comp_data_size);
  if (error > DWG_ERR_CRITICAL || error == DWG_ERR_VALUEOUTOFBOUNDS)
    {
      free (decomp);
      return error;
    }

  ptr = decomp;
  bfr_read (&dwg->header.section_infohdr, &ptr, 20);
  LOG_TRACE ("\n#### Read 2004 section_infohdr ####\n")
  LOG_TRACE ("num_desc:   %d\n", dwg->header.section_infohdr.num_desc)
  LOG_TRACE ("compressed: %d\n", dwg->header.section_infohdr.compressed)
  LOG_TRACE ("max_size:   0x%x\n", dwg->header.section_infohdr.max_size)
  LOG_TRACE ("encrypted:  %d\n", dwg->header.section_infohdr.encrypted)
  LOG_TRACE ("num_desc2:  %d/0x%x\n", dwg->header.section_infohdr.num_desc2,
             dwg->header.section_infohdr.num_desc2)
  if (dwg->header.section_infohdr.num_desc > 0xf000000)
    {
      LOG_ERROR ("Illegal num_desc2");
      free (decomp);
      dwg->header.section_infohdr.num_desc = 0;
      dwg->header.section_infohdr.num_desc2 = 0;
      return error | DWG_ERR_INVALIDDWG;
    }
  dwg->header.section_info = (Dwg_Section_Info *)calloc (
      dwg->header.section_infohdr.num_desc, sizeof (Dwg_Section_Info));
  if (!dwg->header.section_info)
    {
      LOG_ERROR ("Out of memory");
      return error | DWG_ERR_OUTOFMEM;
    }
  decomp_end = decomp + decomp_data_size + 1024;
  for (i = 0; i < dwg->header.section_infohdr.num_desc; ++i)
    {
      Dwg_Section_Info *info;
      uint64_t sum_decomp = 0;
      uint64_t prev_address = 0;

      if (ptr + 64 >= decomp_end)
        {
          free (decomp);
          dwg->header.section_infohdr.num_desc = i;
          LOG_ERROR ("read_R2004_section_info out of range");
          return DWG_ERR_INVALIDDWG;
        }
      info = &dwg->header.section_info[i];
      /* endian specific code */
      bfr_read (info, &ptr, 32 + 64);

      LOG_TRACE ("\nsection_info[%d] fields:\n", i)
      LOG_TRACE ("size:            %ld\n", (long)info->size)
      LOG_TRACE ("num_sections:    %u\n", info->num_sections)
      LOG_TRACE ("max_decomp_size: %u / 0x%x\n", // normally 0x7400
                 info->max_decomp_size, info->max_decomp_size)
      LOG_TRACE ("unknown:         %u\n", info->unknown)
      LOG_TRACE ("compressed:      %u (1=no, 2=yes)\n", info->compressed)
      LOG_TRACE ("type:            0x%x\n", (unsigned)info->type)
      LOG_TRACE ("encrypted:       %d (0=no, 1=yes, 2=unknown)\n",
                 info->encrypted)
      LOG_TRACE ("name:            %s\n\n", info->name)

      if (ptr + (16 * info->num_sections) >= decomp_end)
        {
          info->name[0] = '\0';
          info->num_sections = 0;
          info->sections = NULL;
          dwg->header.section_infohdr.num_desc = i;
          free (decomp);
          LOG_ERROR ("read_R2004_section_info out of range");
          return DWG_ERR_INVALIDDWG;
        }

      if (info->num_sections < 1000000)
        {
          int32_t old_section_number = 0;
          LOG_INFO ("Page count %u in area %d\n", info->num_sections, i);
          info->sections = calloc (info->num_sections, sizeof (Dwg_Section *));
          if (!info->sections)
            {
              free (decomp);
              LOG_ERROR ("Out of memory with %u sections", info->num_sections);
              return error | DWG_ERR_OUTOFMEM;
            }
          prev_address = 0;

          for (j = 0; j < info->num_sections; j++)
            {
              struct _section_page
              { /* unused */
                int32_t number;
                uint32_t size;
                uint64_t address;
              } page;
              /* endian specific code: */
              bfr_read (&page, &ptr, 16);
#if 0
              section_number = *((int32_t*)ptr);     // Index into SectionMap
              data_size      = *((uint32_t*)ptr + 1);
              address        = *((uint64_t*)ptr + 2); // TODO avoid alignment ubsan
              //address   = *((uint32_t*)ptr + 2);
              //address <<= 32;
              //address  += *((uint32_t*)ptr + 3);
              ptr += 16; /* 4*4 */
#endif
              sum_decomp += page.size; /* TODO: uncompressed size */
#if 0
              if (page.address < sum_decomp)
                {
                  /* ODA: "If the start offset is smaller than the sum of the decompressed
                   * size of all previous pages, then this page is to be preceded by
                   * zero pages until this condition is met. */
                  LOG_WARN("address %lu < sum_decomp %lu", page.address, sum_decomp)
                }
#endif
              info->sections[j] = find_section (dwg, page.number);

              if (page.number < 0)
                { // gap/unused data
                  LOG_TRACE ("Page: %4" PRId32 " (-)", page.number)
                  info->num_sections++;
                  info->sections
                      = realloc (info->sections,
                                 info->num_sections * sizeof (Dwg_Section *));
                  info->sections[info->num_sections - 1] = NULL;
                }
              else if (page.address < prev_address)
                {
                  LOG_TRACE ("Page: %4" PRId32 " (a)", page.number)
                }
              else if (info->sections[0]
                       && page.number > (int32_t) (
                              info->num_sections + info->sections[0]->number))
                {
                  // for [7] ptr+160 seems to be AcDb:ObjFreeSpace
                  LOG_INFO ("Page: %4" PRId32 " (n)", page.number)
                }
              else if (!info->sections[j]
                       && page.number != old_section_number + 1)
                {
                  LOG_INFO ("Page: %4" PRId32 " (b)", page.number)
                  LOG_TRACE (" size: %5" PRIu32, page.size) // compressed
                  LOG_TRACE (" address: 0x%" PRIx64, page.address)
                  if (info->sections[j])
                    LOG_TRACE (" info: 0x%" PRIx64, info->sections[j]->address);
                  LOG_TRACE ("\n")
                  ptr -= 16;
                  break;
                }
              else
                {
                  LOG_TRACE ("Page: %4" PRId32 "    ", page.number)
                  old_section_number = page.number;
                  prev_address = page.address;
                }
              LOG_TRACE (" size: %5" PRIu32, page.size) // compressed
              LOG_TRACE (" address: 0x%" PRIx64, page.address)
              if (info->sections[j])
                LOG_TRACE (" info: 0x%" PRIx64, info->sections[j]->address);
              LOG_TRACE ("\n")
            }
        }
      else
        {
          LOG_ERROR ("Section count %u in area %d too high! Skipping",
                     info->num_sections, i);
          info->num_sections = 0;
          free (decomp);
          return error | DWG_ERR_VALUEOUTOFBOUNDS;
        }
    }
  free (decomp);
  return error;
}
read_R2004_section_info (Bit_Chain *restrict dat, Dwg_Data *restrict dwg,
                         uint32_t comp_data_size, uint32_t decomp_data_size)
{
  BITCODE_RC *decomp, *ptr, *decomp_end;
  BITCODE_BL i, j;
  int32_t section_number = 0;
  uint32_t data_size, maxsize;
  uint64_t offset;
  int error;

  decomp = (BITCODE_RC *)calloc (decomp_data_size + 1024, sizeof (BITCODE_RC));
  if (!decomp)
    {
      LOG_ERROR ("Out of memory");
      return DWG_ERR_OUTOFMEM;
    }

  error = decompress_R2004_section (dat, decomp, decomp_data_size + 1024,
                                    comp_data_size);
  if (error > DWG_ERR_CRITICAL || error == DWG_ERR_VALUEOUTOFBOUNDS)
    {
      free (decomp);
      return error;
    }

  ptr = decomp;
  bfr_read (&dwg->header.section_infohdr, &ptr, 20);
  LOG_TRACE ("\n#### Read 2004 section_infohdr ####\n")
  LOG_TRACE ("num_desc:   %d\n", dwg->header.section_infohdr.num_desc)
  LOG_TRACE ("compressed: %d\n", dwg->header.section_infohdr.compressed)
  LOG_TRACE ("max_size:   0x%x\n", dwg->header.section_infohdr.max_size)
  LOG_TRACE ("encrypted:  %d\n", dwg->header.section_infohdr.encrypted)
  LOG_TRACE ("num_desc2:  %d/0x%x\n", dwg->header.section_infohdr.num_desc2,
             dwg->header.section_infohdr.num_desc2)
  if (dwg->header.section_infohdr.num_desc > 0xf000000)
    {
      LOG_ERROR ("Illegal num_desc2");
      free (decomp);
      dwg->header.section_infohdr.num_desc = 0;
      dwg->header.section_infohdr.num_desc2 = 0;
      return error | DWG_ERR_INVALIDDWG;
    }
  dwg->header.section_info = (Dwg_Section_Info *)calloc (
      dwg->header.section_infohdr.num_desc, sizeof (Dwg_Section_Info));
  if (!dwg->header.section_info)
    {
      LOG_ERROR ("Out of memory");
      return error | DWG_ERR_OUTOFMEM;
    }
  decomp_end = decomp + decomp_data_size + 1024;
  for (i = 0; i < dwg->header.section_infohdr.num_desc; ++i)
    {
      Dwg_Section_Info *info;
      uint64_t sum_decomp = 0;
      uint64_t prev_address = 0;

      if (ptr + 64 >= decomp_end)
        {
          free (decomp);
          dwg->header.section_infohdr.num_desc = i;
          LOG_ERROR ("read_R2004_section_info out of range");
          return DWG_ERR_INVALIDDWG;
        }
      info = &dwg->header.section_info[i];
       
      bfr_read (info, &ptr, 32 + 64);

      LOG_TRACE ("\nsection_info[%d] fields:\n", i)
      LOG_TRACE ("size:            %ld\n", (long)info->size)
      LOG_TRACE ("num_sections:    %u\n", info->num_sections)
      LOG_TRACE ("max_decomp_size: %u / 0x%x\n",  
                 info->max_decomp_size, info->max_decomp_size)
      LOG_TRACE ("unknown:         %u\n", info->unknown)
      LOG_TRACE ("compressed:      %u (1=no, 2=yes)\n", info->compressed)
      LOG_TRACE ("type:            0x%x\n", (unsigned)info->type)
      LOG_TRACE ("encrypted:       %d (0=no, 1=yes, 2=unknown)\n",
                 info->encrypted)
      LOG_TRACE ("name:            %s\n\n", info->name)

      if (ptr + (16 * info->num_sections) >= decomp_end)
        {
          info->name[0] = '\0';
          info->num_sections = 0;
          info->sections = NULL;
          dwg->header.section_infohdr.num_desc = i;
          free (decomp);
          LOG_ERROR ("read_R2004_section_info out of range");
          return DWG_ERR_INVALIDDWG;
        }

      if (info->num_sections < 1000000)
        {
          int32_t old_section_number = 0;
          LOG_INFO ("Page count %u in area %d\n", info->num_sections, i);
          info->sections = calloc (info->num_sections, sizeof (Dwg_Section *));
          if (!info->sections)
            {
              free (decomp);
              LOG_ERROR ("Out of memory with %u sections", info->num_sections);
              return error | DWG_ERR_OUTOFMEM;
            }
          prev_address = 0;

          for (j = 0; j < info->num_sections; j++)
            {
              struct _section_page
              {  
                int32_t number;
                uint32_t size;
                uint64_t address;
              } page;
               
              bfr_read (&page, &ptr, 16);
#if 0
              section_number = *((int32_t*)ptr);      
              data_size      = *((uint32_t*)ptr + 1);
              address        = *((uint64_t*)ptr + 2);  
               
               
               
              ptr += 16;  
#endif
              sum_decomp += page.size;  
#if 0
              if (page.address < sum_decomp)
                {
                   
                  LOG_WARN("address %lu < sum_decomp %lu", page.address, sum_decomp)
                }
#endif
              info->sections[j] = find_section (dwg, page.number);

              if (page.number < 0)
                {  
                  LOG_TRACE ("Page: %4" PRId32 " (-)", page.number)
                  info->num_sections++;
                  info->sections
                      = realloc (info->sections,
                                 info->num_sections * sizeof (Dwg_Section *));
                  info->sections[info->num_sections - 1] = NULL;
                }
              else if (page.address < prev_address)
                {
                  LOG_TRACE ("Page: %4" PRId32 " (a)", page.number)
                }
              else if (info->sections[0]
                       && page.number > (int32_t) (
                              info->num_sections + info->sections[0]->number))
                {
                   
                  LOG_INFO ("Page: %4" PRId32 " (n)", page.number)
                }
              else if (!info->sections[j]
                       && page.number != old_section_number + 1)
                {
                  LOG_INFO ("Page: %4" PRId32 " (b)", page.number)
                  LOG_TRACE (" size: %5" PRIu32, page.size)  
                  LOG_TRACE (" address: 0x%" PRIx64, page.address)
                  if (info->sections[j])
                    LOG_TRACE (" info: 0x%" PRIx64, info->sections[j]->address);
                  LOG_TRACE ("\n")
                  ptr -= 16;
                  break;
                }
              else
                {
                  LOG_TRACE ("Page: %4" PRId32 "    ", page.number)
                  old_section_number = page.number;
                  prev_address = page.address;
                }
              LOG_TRACE (" size: %5" PRIu32, page.size)  
              LOG_TRACE (" address: 0x%" PRIx64, page.address)
              if (info->sections[j])
                LOG_TRACE (" info: 0x%" PRIx64, info->sections[j]->address);
              LOG_TRACE ("\n")
            }
        }
      else
        {
          LOG_ERROR ("Section count %u in area %d too high! Skipping",
                     info->num_sections, i);
          info->num_sections = 0;
          free (decomp);
          return error | DWG_ERR_VALUEOUTOFBOUNDS;
        }
    }
  free (decomp);
  return error;
}