read_R2004_section_map (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
  BITCODE_RC *decomp, *ptr;
  int i, error = 0, found_section_map_id = 0;
  uint64_t section_address;
  int64_t bytes_remaining;
  const uint32_t comp_data_size = dwg->r2004_header.comp_data_size;
  const uint32_t decomp_data_size = dwg->r2004_header.decomp_data_size;
  const int32_t section_array_size = (int32_t)dwg->r2004_header.section_array_size;
  const uint64_t section_map_address = dwg->r2004_header.section_map_address + 0x100;
  const BITCODE_RL section_map_id = dwg->r2004_header.section_map_id;
  int max_id = 0;

  dwg->header.num_sections = 0;
  dwg->header.section = 0;

  // decompressed data
  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;
    }
  LOG_TRACE ("\n#### Read 2004 Section Page Map ####\n")

  section_address = 0x100; // starting address
  i = 0;
  bytes_remaining = (int64_t)decomp_data_size;
  ptr = decomp;
  dwg->header.num_sections = 0;

  while (bytes_remaining >= 8)
    {
      error |= add_section (dwg);
      if (error > DWG_ERR_CRITICAL)
        return error;

      /* endian specific code: */
      bfr_read (&dwg->header.section[i], &ptr, 8);
      bytes_remaining -= 8;
      LOG_TRACE ("Section[%2d]=%2d,", i, (int)dwg->header.section[i].number)
      LOG_TRACE (" size: %5u,", dwg->header.section[i].size)
      dwg->header.section[i].address = section_address;
      if (dwg->header.section[i].number <= section_array_size) // GH #144
        {
          section_address += dwg->header.section[i].size;
          LOG_TRACE (" address: 0x%04lx\n",
                     (unsigned long)dwg->header.section[i].address)
        }
      else
        LOG_TRACE (" (ignored > %d section_array_size)\n",
                   (int)section_array_size);
      if (dwg->header.section[i].number > max_id)
        max_id = dwg->header.section[i].number;

      // repair section_map_id.address from section_map_address
      if ((BITCODE_RL)dwg->header.section[i].number == section_map_id)
        {
          found_section_map_id++;
          if (dwg->header.section[i].address != section_map_address)
            {
              LOG_WARN ("Repair invalid section_map_address: %" PRIx64 " != %" PRIx64,
                        dwg->header.section[i].address, section_map_address);
              error |= DWG_ERR_VALUEOUTOFBOUNDS;
              dwg->header.section[i].address = section_map_address;
            }
        }
      if (i >= (int)section_array_size)
        {
          error |= DWG_ERR_VALUEOUTOFBOUNDS;
          LOG_WARN ("Overflow section_array_size: %d >= %d",
                    i, (int)section_array_size);
          if (i > 1000)
            return error;
        }

      if (bytes_remaining >= 16
          && dwg->header.section[i].number < 0) // negative: gap/unused data
        //|| dwg->header.section[i].number > section_array_size))
        {
          /* endian specific code: */
          bfr_read (&dwg->header.section[i].parent, &ptr, 16);
          bytes_remaining -= 16;
          LOG_TRACE ("  Parent: %d, ", dwg->header.section[i].parent)
          LOG_TRACE ("Left:   %d, ", dwg->header.section[i].left)
          LOG_TRACE ("Right:  %d, ", dwg->header.section[i].right)
          LOG_TRACE ("0x00:   %d\n", dwg->header.section[i].x00)
        }

      i++;
    }
  i--;
  free (decomp);

  if (max_id != (int32_t)section_array_size)
    {
      LOG_WARN ("Invalid section_array_size: [%d].%d != %d",
                i, max_id, (int)section_array_size);
    }
  if (section_address
      != (uint64_t)dwg->r2004_header.last_section_address + 0x100)
    {
      error |= DWG_ERR_VALUEOUTOFBOUNDS;
      LOG_WARN ("Invalid last_section_address: %" PRIx64 " != %" PRIx64,
                section_address, dwg->r2004_header.last_section_address);
    }
  if (dwg->header.num_sections
      != dwg->r2004_header.num_gaps + dwg->r2004_header.num_sections)
    {
      error |= DWG_ERR_VALUEOUTOFBOUNDS;
      LOG_WARN ("Invalid num_sections: %d != gaps: " FORMAT_RL
                " + sects: " FORMAT_RL,
                dwg->header.num_sections, dwg->r2004_header.num_gaps,
                dwg->r2004_header.num_sections);
    }
  if (!found_section_map_id)
    {
      BITCODE_RLx section_type;
      Dwg_Section *info;
      LOG_WARN ("section_map_id " FORMAT_RL " not found", section_map_id);
      info = find_section (dwg, section_map_id);
      if (!info)
        {
          i = dwg->header.num_sections;
          add_section (dwg);
          LOG_WARN ("Add section_map_id [%d] %d => address 0x%" PRIx64, i,
                    section_map_id, section_map_address);
          error |= DWG_ERR_VALUEOUTOFBOUNDS;
          dwg->header.section[i].number = section_map_id;
          dwg->header.section[i].address = section_map_address;
          if (i > 0)
            dwg->header.section[i].size
              = section_map_address - dwg->header.section[i - 1].address;
        }
      info = find_section (dwg, dwg->r2004_header.section_info_id);
      if (!info)
        goto repair_info_id;
      dat->bit = 0;
      dat->byte = info->address;
      section_type = bit_read_RL (dat);
      if (section_type != 0x4163003b)
        {
        repair_info_id:
          LOG_WARN ("Repair invalid section_info_id [%d]: => %u",
                    i-1, dwg->r2004_header.section_info_id);
          error |= DWG_ERR_VALUEOUTOFBOUNDS;
          for (i = 0; i < (int)dwg->header.num_sections; ++i)
            {
              dat->bit = 0;
              dat->byte = dwg->header.section[i].address;
              if (dat->byte > dat->size)
                break;
              section_type = bit_read_RL (dat);
              if (section_type == 0x4163003b)
                {
                  LOG_WARN ("Fixed section_info_id [%d]: => %u @" FORMAT_RLL,
                            i, dwg->r2004_header.section_info_id,
                            dwg->header.section[i].address);
                  if (!info)
                    info = &dwg->header.section[i];
                  info->address = dwg->header.section[i].address;
                  info->size = dwg->header.section[i].size;
                  info->number = dwg->r2004_header.section_info_id;
                }
            }
          if (!info || info->number != (int)dwg->r2004_header.section_info_id)
            {
              i = dwg->header.num_sections;
              add_section (dwg);
              error |= DWG_ERR_VALUEOUTOFBOUNDS;
              dwg->header.section[i].number = dwg->r2004_header.section_info_id;
              dwg->header.section[i].address = dwg->header.section[i-1].address +
                dwg->header.section[i-1].size;
              LOG_WARN ("Add section_info_id [%d] %u => address 0x%" PRIx64, i,
                        dwg->r2004_header.section_info_id,
                        dwg->header.section[i].address);
            }
        }
    }
  if (found_section_map_id > 1)
    {
      error |= DWG_ERR_VALUEOUTOFBOUNDS;
      LOG_WARN ("Illegal 2004 Section Page Map. Found %d section_map_id sections",
                found_section_map_id)
    }

  return error;
}