text:
#include <algorithm>
#include "dxbc_inspect.h"

#include "official/cvinfo.h"
#include "dxbc_spdb.h"

// uncomment the following to print (very verbose) debugging prints for SPDB processing
//#define SPDBLOG(...) RDCDEBUG(__VA_ARGS__)

#ifndef SPDBLOG
#define SPDBLOG(...) (void)(__VA_ARGS__)
#endif

namespace DXBC
{
static const uint32_t FOURCC_SPDB = MAKE_FOURCC('S', 'P', 'D', 'B');

SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk)
{
  m_HasDebugInfo = false;

  byte *data = NULL;

  m_ShaderFlags = 0;

  uint32_t spdblength;
  {
    uint32_t *raw = (uint32_t *)chunk;

    if(raw[0] != FOURCC_SPDB)
      return;

    spdblength = raw[1];

    data = (byte *)&raw[2];
  }

  FileHeaderPage *header = (FileHeaderPage *)data;

  if(memcmp(header->identifier, "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0", sizeof(header->identifier)))
  {
    RDCWARN("Unexpected SPDB type");
    return;
  }

  RDCASSERT(header->PageCount * header->PageSize == spdblength);

  const byte **pages = new const byte *[header->PageCount];
  for(uint32_t i = 0; i < header->PageCount; i++)
    pages[i] = &data[i * header->PageSize];

  uint32_t rootdirCount = header->PagesForByteSize(header->RootDirSize);
  uint32_t rootDirIndicesCount = header->PagesForByteSize(rootdirCount * sizeof(uint32_t));

  PageMapping rootdirIndicesMapping(pages, header->PageSize, header->RootDirectory,
                                    rootDirIndicesCount);
  const byte *rootdirIndices = rootdirIndicesMapping.Data();

  PageMapping directoryMapping(pages, header->PageSize, (uint32_t *)rootdirIndices, rootdirCount);
  const uint32_t *dirContents = (const uint32_t *)directoryMapping.Data();

  std::vector<PDBStream> streams;

  streams.resize(*dirContents);
  dirContents++;

  SPDBLOG("SPDB contains %zu streams", streams.size());

  for(size_t i = 0; i < streams.size(); i++)
  {
    streams[i].byteLength = *dirContents;
    SPDBLOG("Stream[%zu] is %u bytes", i, streams[i].byteLength);
    dirContents++;
  }

  for(size_t i = 0; i < streams.size(); i++)
  {
    if(streams[i].byteLength == 0)
      continue;

    for(uint32_t p = 0; p < header->PagesForByteSize(streams[i].byteLength); p++)
    {
      streams[i].pageIndices.push_back(*dirContents);
      dirContents++;
    }
  }

  RDCASSERT(streams.size() > 1);

  // stream 1: GUID + stream names
  PageMapping guidMapping(pages, header->PageSize, &streams[1].pageIndices[0],
                          (uint32_t)streams[1].pageIndices.size());
  GuidPageHeader *guid = (GuidPageHeader *)guidMapping.Data();

  uint32_t *hashtable = (uint32_t *)(guid->Strings + guid->StringBytes);

  uint32_t numSetBits = hashtable[0];
  hashtable++;
  uint32_t maxBit = hashtable[0];
  hashtable++;
  uint32_t setBitsetWords = hashtable[0];
  hashtable++;
  uint32_t *setBitset = hashtable;
  hashtable += setBitsetWords;
  RDCASSERT(hashtable[0] == 0);
  hashtable++;

  std::map<std::string, uint32_t> StreamNames;

  uint32_t numset = 0;
  for(uint32_t i = 0; i < maxBit; i++)
  {
    if((setBitset[(i / 32)] & (1 << (i % 32))) != 0)
    {
      uint32_t strOffs = hashtable[0];
      hashtable++;
      uint32_t stream = hashtable[0];
      hashtable++;

      char *streamName = guid->Strings + strOffs;

      StreamNames[streamName] = stream;

      SPDBLOG("Stream %u is %s", stream, streamName);

      numset++;
    }
  }
  RDCASSERT(numset == numSetBits);

  for(auto it = StreamNames.begin(); it != StreamNames.end(); ++it)
  {
    if(!strncmp(it->first.c_str(), "/src/files/", 11))
    {
      PDBStream &s = streams[it->second];
      PageMapping fileContents(pages, header->PageSize, &s.pageIndices[0],
                               (uint32_t)s.pageIndices.size());

      const char *filename = (const char *)it->first.c_str();
      filename += sizeof("/src/files/") - 1;

      SPDBLOG("Found file '%s' from stream %u", filename, it->second);

      if(filename[0] == 0)
        filename = "shader";

      Files.push_back({filename, (char *)fileContents.Data()});
    }
  }

  struct TypeMember
  {
    std::string name;
    uint16_t byteOffset;
    uint32_t typeIndex;
  };

  struct TypeDesc
  {
    std::string name;
    VarType baseType;
    uint32_t byteSize;
    uint16_t vecSize;
    uint16_t matArrayStride;
    LEAF_ENUM_e leafType;
    std::vector<TypeMember> members;
  };

  std::map<uint32_t, TypeDesc> typeInfo;

  // prepopulate with basic types
  // for now we stick to full-precision 32-bit VarTypes. It's not clear if HLSL even emits the other
  // types
  typeInfo[T_INT4] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_INT2] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_INT1] = {"int8_t", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_LONG] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_SHORT] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_CHAR] = {"char", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_UINT1] = {"uint8_t", VarType::UInt, 1, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_ULONG] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_USHORT] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_UCHAR] = {"unsigned char", VarType::UInt, 1, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_REAL16] = {"half", VarType::Float, 2, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_REAL32] = {"float", VarType::Float, 4, 1, 0, LF_NUMERIC, {}};
  // modern HLSL fake half
  typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, LF_NUMERIC, {}};
  typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, LF_NUMERIC, {}};

  if(streams.size() >= 3)
  {
    SPDBLOG("Got types stream");

    PDBStream &s = streams[2];
    PageMapping fileContents(pages, header->PageSize, &s.pageIndices[0],
                             (uint32_t)s.pageIndices.size());

    byte *bytes = (byte *)fileContents.Data();
    byte *end = bytes + s.byteLength;

    TPIHeader *tpi = (TPIHeader *)bytes;

    // skip header
    bytes += tpi->headerSize;

    RDCASSERT(bytes + tpi->dataSize == end);

// this isn't needed, but this is the hash stream
#if 0
    PageMapping hashContents;

    if(tpi->hash.streamNumber < streams.size())
    {
      PDBStream &hashstrm = streams[tpi->hash.streamNumber];
      hashContents = PageMapping(pages, header->PageSize, &hashstrm.pageIndices[0],
                                 (uint32_t)hashstrm.pageIndices.size());
    }
#endif

    uint32_t id = tpi->typeMin;

    while(bytes < end)
    {
      uint16_t *leafheader = (uint16_t *)bytes;

      uint16_t length = leafheader[0];
      LEAF_ENUM_e type = (LEAF_ENUM_e)leafheader[1];

      byte *leaf = (byte *)&leafheader[1];

      bytes += 2 + length;

      switch(type)
      {
        case LF_VECTOR:
        {
          lfVector *vector = (lfVector *)leaf;
          // documentation isn't clear, but seems like byte size is always a uint16_t
          uint16_t *bytelength = (uint16_t *)vector->data;
          char *name = (char *)(bytelength + 1);
          SPDBLOG("Type %x is '%s': a vector of %x with %u elements over %u bytes", id, name,
                  vector->elemtype, vector->count, *bytelength);

          typeInfo[id] = {
              name,        typeInfo[vector->elemtype].baseType,
              *bytelength, (uint16_t)vector->count,
              0,           type,
              {},
          };

          break;
        }
        case LF_MATRIX:
        {
          lfMatrix *matrix = (lfMatrix *)leaf;
          // documentation isn't clear, but seems like byte size is always a uint16_t
          uint16_t *bytelength = (uint16_t *)matrix->data;
          char *name = (char *)(bytelength + 1);
          SPDBLOG(
              "Type %x is '%s': a matrix of %x with %u rows, %u columns over %u bytes with %u "
              "byte %s major stride",
              id, name, matrix->elemtype, matrix->rows, matrix->cols, *bytelength,
              matrix->majorStride, matrix->matattr.row_major ? "row" : "column");

          typeInfo[id] = {
              name,
              typeInfo[matrix->elemtype].baseType,
              *bytelength,
              uint16_t(matrix->rows),
              uint16_t(*bytelength / matrix->cols),
              type,
              {},
          };

          break;
        }
        case LF_HLSL:
        {
          lfHLSL *hlsl = (lfHLSL *)leaf;
          // documentation mentions "numeric properties followed by byte size" but we don't need
          // that

          const char *hlslTypeName = "";
          switch((CV_builtin_e)hlsl->kind)
          {
            case CV_BI_HLSL_INTERFACE_POINTER: hlslTypeName = "INTERFACE_POINTER"; break;
            case CV_BI_HLSL_TEXTURE1D: hlslTypeName = "TEXTURE1D"; break;
            case CV_BI_HLSL_TEXTURE1D_ARRAY: hlslTypeName = "TEXTURE1D_ARRAY"; break;
            case CV_BI_HLSL_TEXTURE2D: hlslTypeName = "TEXTURE2D"; break;
            case CV_BI_HLSL_TEXTURE2D_ARRAY: hlslTypeName = "TEXTURE2D_ARRAY"; break;
            case CV_BI_HLSL_TEXTURE3D: hlslTypeName = "TEXTURE3D"; break;
            case CV_BI_HLSL_TEXTURECUBE: hlslTypeName = "TEXTURECUBE"; break;
            case CV_BI_HLSL_TEXTURECUBE_ARRAY: hlslTypeName = "TEXTURECUBE_ARRAY"; break;
            case CV_BI_HLSL_TEXTURE2DMS: hlslTypeName = "TEXTURE2DMS"; break;
            case CV_BI_HLSL_TEXTURE2DMS_ARRAY: hlslTypeName = "TEXTURE2DMS_ARRAY"; break;
            case CV_BI_HLSL_SAMPLER: hlslTypeName = "SAMPLER"; break;
            case CV_BI_HLSL_SAMPLERCOMPARISON: hlslTypeName = "SAMPLERCOMPARISON"; break;
            case CV_BI_HLSL_BUFFER: hlslTypeName = "BUFFER"; break;
            case CV_BI_HLSL_POINTSTREAM: hlslTypeName = "POINTSTREAM"; break;
            case CV_BI_HLSL_LINESTREAM: hlslTypeName = "LINESTREAM"; break;
            case CV_BI_HLSL_TRIANGLESTREAM: hlslTypeName = "TRIANGLESTREAM"; break;
            case CV_BI_HLSL_INPUTPATCH: hlslTypeName = "INPUTPATCH"; break;
            case CV_BI_HLSL_OUTPUTPATCH: hlslTypeName = "OUTPUTPATCH"; break;
            case CV_BI_HLSL_RWTEXTURE1D: hlslTypeName = "RWTEXTURE1D"; break;
            case CV_BI_HLSL_RWTEXTURE1D_ARRAY: hlslTypeName = "RWTEXTURE1D_ARRAY"; break;
            case CV_BI_HLSL_RWTEXTURE2D: hlslTypeName = "RWTEXTURE2D"; break;
            case CV_BI_HLSL_RWTEXTURE2D_ARRAY: hlslTypeName = "RWTEXTURE2D_ARRAY"; break;
            case CV_BI_HLSL_RWTEXTURE3D: hlslTypeName = "RWTEXTURE3D"; break;
            case CV_BI_HLSL_RWBUFFER: hlslTypeName = "RWBUFFER"; break;
            case CV_BI_HLSL_BYTEADDRESS_BUFFER: hlslTypeName = "BYTEADDRESS_BUFFER"; break;
            case CV_BI_HLSL_RWBYTEADDRESS_BUFFER: hlslTypeName = "RWBYTEADDRESS_BUFFER"; break;
            case CV_BI_HLSL_STRUCTURED_BUFFER: hlslTypeName = "STRUCTURED_BUFFER"; break;
            case CV_BI_HLSL_RWSTRUCTURED_BUFFER: hlslTypeName = "RWSTRUCTURED_BUFFER"; break;
            case CV_BI_HLSL_APPEND_STRUCTURED_BUFFER:
              hlslTypeName = "APPEND_STRUCTURED_BUFFER";
              break;
            case CV_BI_HLSL_CONSUME_STRUCTURED_BUFFER:
              hlslTypeName = "CONSUME_STRUCTURED_BUFFER";
              break;
            case CV_BI_HLSL_MIN8FLOAT: hlslTypeName = "MIN8FLOAT"; break;
            case CV_BI_HLSL_MIN10FLOAT: hlslTypeName = "MIN10FLOAT"; break;
            case CV_BI_HLSL_MIN16FLOAT: hlslTypeName = "MIN16FLOAT"; break;
            case CV_BI_HLSL_MIN12INT: hlslTypeName = "MIN12INT"; break;
            case CV_BI_HLSL_MIN16INT: hlslTypeName = "MIN16INT"; break;
            case CV_BI_HLSL_MIN16UINT: hlslTypeName = "MIN16UINT"; break;
            default: hlslTypeName = "Unknown type";
          }

          SPDBLOG("Type %x is an hlsl %s[%u] (subtype %x)", id, hlslTypeName, hlsl->numprops,
                  hlsl->subtype);
          break;
        }
        case LF_ALIAS:
        {
          lfAlias *alias = (lfAlias *)leaf;
          SPDBLOG("Type %x is an alias for %x", id, alias->utype);

          typeInfo[id] = typeInfo[alias->utype];
          typeInfo[id].name = (char *)alias->Name;

          break;
        }
        case LF_MODIFIER_EX:
        {
          lfModifierEx *modifier = (lfModifierEx *)leaf;
          SPDBLOG("Type %x is %x modified with:", id, modifier->type);

          typeInfo[id] = typeInfo[modifier->type];
          typeInfo[id].name = "modif " + typeInfo[id].name;

          uint16_t *mods = (uint16_t *)modifier->mods;
          for(unsigned short i = 0; i < modifier->count; i++)
          {
            CV_modifier_e mod = (CV_modifier_e)mods[i];

            const char *modName = "";
            switch(mod)
            {
              case CV_MOD_CONST: modName = "CONST"; break;
              case CV_MOD_VOLATILE: modName = "VOLATILE"; break;
              case CV_MOD_UNALIGNED: modName = "UNALIGNED"; break;
              case CV_MOD_HLSL_UNIFORM: modName = "HLSL_UNIFORM"; break;
              case CV_MOD_HLSL_LINE: modName = "HLSL_LINE"; break;
              case CV_MOD_HLSL_TRIANGLE: modName = "HLSL_TRIANGLE"; break;
              case CV_MOD_HLSL_LINEADJ: modName = "HLSL_LINEADJ"; break;
              case CV_MOD_HLSL_TRIANGLEADJ: modName = "HLSL_TRIANGLEADJ"; break;
              case CV_MOD_HLSL_LINEAR: modName = "HLSL_LINEAR"; break;
              case CV_MOD_HLSL_CENTROID: modName = "HLSL_CENTROID"; break;
              case CV_MOD_HLSL_CONSTINTERP: modName = "HLSL_CONSTINTERP"; break;
              case CV_MOD_HLSL_NOPERSPECTIVE: modName = "HLSL_NOPERSPECTIVE"; break;
              case CV_MOD_HLSL_SAMPLE: modName = "HLSL_SAMPLE"; break;
              case CV_MOD_HLSL_CENTER: modName = "HLSL_CENTER"; break;
              case CV_MOD_HLSL_SNORM: modName = "HLSL_SNORM"; break;
              case CV_MOD_HLSL_UNORM: modName = "HLSL_UNORM"; break;
              case CV_MOD_HLSL_PRECISE: modName = "HLSL_PRECISE"; break;
              case CV_MOD_HLSL_UAV_GLOBALLY_COHERENT: modName = "HLSL_UAV_GLOBALLY_COHERENT"; break;
              default: modName = "Unknown modification";
            }

            SPDBLOG("  + %s", modName);
          }
          break;
        }
        case LF_FIELDLIST:
        {
          lfFieldList *fieldList = (lfFieldList *)leaf;
          SPDBLOG("Type %x is a field list containing:", id);

          uint32_t idx = 0;

          std::vector<TypeMember> &members = typeInfo[id].members;

          byte *iter = (byte *)fieldList->data;
          while(iter < bytes)
          {
            if(*iter >= LF_PAD0)
            {
              iter += (*iter) - LF_PAD0;
              continue;
            }

            LEAF_ENUM_e memberType = LEAF_ENUM_e(*(uint16_t *)iter);

            switch(memberType)
            {
              case LF_MEMBER:
              {
                lfMember *member = (lfMember *)iter;

                uint16_t *byteoffset = (uint16_t *)member->offset;
                char *name = (char *)(byteoffset + 1);

                char *access = "???";

                if(member->attr.access == 1)
                  access = "private";
                else if(member->attr.access == 2)
                  access = "protected";
                else if(member->attr.access == 3)
                  access = "public";

                SPDBLOG("  [%u]: %x %s (%s) (at offset %u bytes)", idx, member->index, name, access,
                        *byteoffset);

                members.push_back({name, *byteoffset, member->index});

                idx++;

                iter = (byte *)(name + strlen(name) + 1);
                break;
              }
              case LF_ONEMETHOD:
              {
                lfOneMethod *method = (lfOneMethod *)iter;

                iter = (byte *)method->vbaseoff;

                // MTintro = 0x04, MTpureintro = 0x06
                if(method->attr.mprop == 0x04 || method->attr.mprop == 0x06)
                {
                  iter += 4;
                }

                char *name = (char *)iter;
                iter += strlen(name) + 1;

                char *access = "???";

                if(method->attr.access == 1)
                  access = "private";
                else if(method->attr.access == 2)
                  access = "protected";
                else if(method->attr.access == 3)
                  access = "public";

                SPDBLOG("  [%u]: Method %s (%s) (at offset %u bytes)", idx, name, access);

                idx++;
                break;
              }
              case LF_BCLASS:
              case LF_BINTERFACE:
              {
                lfBClass *binterface = (lfBClass *)iter;

                char *access = "???";

                if(binterface->attr.access == 1)
                  access = "private";
                else if(binterface->attr.access == 2)
                  access = "protected";
                else if(binterface->attr.access == 3)
                  access = "public";

                SPDBLOG("  [%u]: %x Base Class/Interface (%s)", idx, binterface->index, access);

                iter = (byte *)binterface->offset + 2;

                idx++;
                break;
              }
              default:
              {
                RDCERR("Unexpected member type %x", memberType);
                // skip the remaining data as we don't know how to safely advance - no length fields
                // to use
                iter = bytes;
                break;
              }
            }
          }
          break;
        }
        case LF_ARGLIST:
        {
          lfArgList *argList = (lfArgList *)leaf;
          SPDBLOG("Type %x is a field list containing:", id);

          for(unsigned long i = 0; i < argList->count; i++)
            SPDBLOG("  %x", argList->arg[i]);
          break;
        }
        case LF_INTERFACE:
        case LF_CLASS:
        case LF_STRUCTURE:
        {
          lfStructure *structure = (lfStructure *)leaf;
          // documentation isn't clear, but seems like byte size is always a uint16_t
          uint16_t *bytelength = (uint16_t *)structure->data;
          char *name = (char *)(bytelength + 1);

          const char *structType = "struct";
          if(type == LF_INTERFACE)
            structType = "interface";
          else if(type == LF_CLASS)
            structType = "class";

          typeInfo[id] = {
              name, VarType::Float, *bytelength, 1, 0, type, typeInfo[structure->field].members,
          };

          SPDBLOG(
              "Type %x is '%s': a %s with %u fields %x derived from %x and vshape %x over %u "
              "bytes",
              id, name, structType, structure->count, structure->field, structure->derived,
              structure->vshape, *bytelength);
          break;
        }
        case LF_PROCEDURE:
        {
          lfProc *procedure = (lfProc *)leaf;
          SPDBLOG("Type %x is a procedure returning %x with %u args: %x", id, procedure->rvtype,
                  procedure->parmcount, procedure->arglist);
          break;
        }
        case LF_MFUNCTION:
        {
          lfMFunc *mfunction = (lfMFunc *)leaf;
          SPDBLOG("Type %x is a member function of class %x returning %x with %u args: %x", id,
                  mfunction->classtype, mfunction->rvtype, mfunction->parmcount, mfunction->arglist);
          break;
        }
        case LF_STRIDED_ARRAY:
        {
          lfStridedArray *stridedArray = (lfStridedArray *)leaf;
          // documentation isn't clear, but seems like byte size is always a uint16_t
          uint16_t *bytelength = (uint16_t *)stridedArray->data;

          typeInfo[id] = {
              "",
              typeInfo[stridedArray->elemtype].baseType,
              *bytelength,
              1,
              uint16_t(stridedArray->stride),
              type,
              {},
          };

          break;
        }
        default:
        {
          RDCWARN("Encountered unknown type leaf %x", type);
          break;
        }
      }
      id++;
    }

    RDCASSERT(id == tpi->typeMax);
  }

  if(streams.size() >= 5)
  {
    SPDBLOG("Got function calls stream");

    PDBStream &s = streams[4];
    PageMapping fileContents(pages, header->PageSize, &s.pageIndices[0],
                             (uint32_t)s.pageIndices.size());

    byte *bytes = (byte *)fileContents.Data();
    byte *end = bytes + s.byteLength;

    TPIHeader *tpi = (TPIHeader *)bytes;

    // skip header
    bytes += tpi->headerSize;

    RDCASSERT(bytes + tpi->dataSize == end);

// this isn't needed, but this is the hash stream
#if 0
    PageMapping hashContents;

    if(tpi->hash.streamNumber < streams.size())
    {
      PDBStream &hashstrm = streams[tpi->hash.streamNumber];
      hashContents = PageMapping(pages, header->PageSize, &hashstrm.pageIndices[0],
                                 (uint32_t)hashstrm.pageIndices.size());
    }
#endif

    uint32_t id = tpi->typeMin;

    while(bytes < end)
    {
      uint16_t length = *(uint16_t *)bytes;
      bytes += 2;

      lfFuncId *func = (lfFuncId *)bytes;
      lfMFuncId *mfunc = (lfMFuncId *)bytes;
      bytes += length;

      if(func->leaf != LF_FUNC_ID && func->leaf != LF_MFUNC_ID)
      {
        SPDBLOG("Encountered leaf type %x, skipping as not function", func->leaf);
        id++;
        continue;
      }

      if(func->leaf == LF_FUNC_ID && func->scopeId != 0)
        SPDBLOG("Unexpected scope %u", func->scopeId);

      Function f;
      f.type = func->type;
      f.name = (char *)func->name;

      SPDBLOG("Function %x (%s) is type %x", id, f.name.c_str(), f.type);

      if(func->leaf == LF_MFUNC_ID)
        SPDBLOG("Member of %x", mfunc->parentType);

      m_Functions[id] = f;

      id++;
    }

    RDCASSERT(id == tpi->typeMax);
  }

  {
    Function mainFunc;
    mainFunc.name = "entrypoint";

    m_Functions[0] = mainFunc;
  }

  std::map<uint32_t, std::string> Names;

  if(StreamNames.find("/names") != StreamNames.end())
  {
    PDBStream &s = streams[StreamNames["/names"]];
    PageMapping namesMapping(pages, header->PageSize, &s.pageIndices[0],
                             (uint32_t)s.pageIndices.size());
    const uint32_t *contents = (const uint32_t *)namesMapping.Data();

    RDCASSERT(contents[0] == 0xeffeeffe && contents[1] == 1);

    uint32_t StringBytes = contents[2];
    char *Strings = (char *)&contents[3];

    contents += 3;

    contents = (uint32_t *)((byte *)contents + StringBytes);

    uint32_t numHashes = contents[0];
    contents++;

    for(uint32_t i = 0; i < numHashes; i++)
    {
      uint32_t idx = contents[0];
      contents++;

      if(idx != 0)
      {
        Names[idx] = Strings + idx;

        if(Names[idx].size() > 100)
          SPDBLOG("Got Name %u: '%s...'", idx, Names[idx].substr(0, 100).c_str());
        else
          SPDBLOG("Got Name %u: '%s'", idx, Names[idx].c_str());
      }
    }
  }

  std::vector<DBIModule> modules;

  {
    PageMapping dbiMapping(pages, header->PageSize, &streams[3].pageIndices[0],
                           (uint32_t)streams[3].pageIndices.size());
    DBIHeader *dbi = (DBIHeader *)dbiMapping.Data();

    RDCASSERT(dbi->sig == 0xffffffff);
    RDCASSERT(dbi->ver == 19990903);

    byte *cur = (byte *)(dbi + 1);
    byte *end = cur + dbi->gpmodiSize;
    while(cur < end)
    {
      DBIModule *mod = (DBIModule *)cur;
      cur += sizeof(DBIModule) - sizeof(std::string) * 2;

      char *moduleName = (char *)cur;
      cur += strlen(moduleName) + 1;

      char *objectName = (char *)cur;
      cur += strlen(objectName) + 1;

      // align up to DWORD boundary
      while((uintptr_t)cur & 0x3)
        cur++;

      DBIModule m;
      memcpy(&m, mod, sizeof(DBIModule) - sizeof(std::string) * 2);
      m.moduleName = moduleName;
      m.objectName = objectName;

      SPDBLOG("Got module named %s from object %s", moduleName, objectName);

      modules.push_back(m);
    }
    RDCASSERT(cur == end);
  }

  std::vector<Inlinee> inlines;

  PROCSYM32 main = {};

  std::map<uint32_t, int32_t> FileMapping;    // mapping from hash chunk to index in Files[], or -1

  for(size_t m = 0; m < modules.size(); m++)
  {
    if(modules[m].stream == -1)
      continue;

    PDBStream &s = streams[modules[m].stream];
    PageMapping modMapping(pages, header->PageSize, &s.pageIndices[0],
                           (uint32_t)s.pageIndices.size());
    uint32_t *moduledata = (uint32_t *)modMapping.Data();

    SPDBLOG("Examining module %s with %u symbols", modules[m].moduleName.c_str(), modules[m].cbSyms);

    RDCASSERT(moduledata[0] == CV_SIGNATURE_C13);

    std::string localName;
    CV_typ_t localType = 0;

    byte *basePtr = (byte *)&moduledata[1];

    byte *cur = basePtr;
    byte *end = (byte *)moduledata + modules[m].cbSyms;
    while(cur < end)
    {
      uint16_t *sym = (uint16_t *)cur;

      ptrdiff_t ptr = (byte *)(sym + 2) - basePtr;

      uint16_t len = sym[0];
      SYM_ENUM_e type = (SYM_ENUM_e)sym[1];
      cur += len + sizeof(uint16_t);    // len does not include itself

      if(type == S_GPROC32)
      {
        PROCSYM32 *gproc32 = (PROCSYM32 *)sym;
        main = *gproc32;

        m_Functions[0].name = (char *)gproc32->name;

        SPDBLOG("S_GPROC32 @ %llx: '%s' of type %x covering bytes %x -> %x", ptr, gproc32->name,
                gproc32->typind, gproc32->off, gproc32->off + gproc32->len);

        RDCASSERT(gproc32->DbgStart == 0);
        RDCASSERT(gproc32->DbgEnd == gproc32->len);
      }
      else if(type == S_COMPILE3)
      {
        COMPILESYM3 *compile3 = (COMPILESYM3 *)sym;

        m_CompilerSig = compile3->verSz;

        SPDBLOG("S_COMPILE3: %s (%d.%d.%d.%d)", compile3->verSz, compile3->verFEMajor,
                compile3->verFEMinor, compile3->verFEBuild, compile3->verFEQFE);

        // for hlsl/fxc
        RDCASSERT(compile3->flags.iLanguage == CV_CFL_HLSL &&
                  compile3->machine == CV_CFL_D3D11_SHADER);
      }
      else if(type == S_ENVBLOCK)
      {
        ENVBLOCKSYM *envblock = (ENVBLOCKSYM *)sym;

        RDCASSERT(envblock->flags.rev == 1);    // this is another edit & continue flag

        SPDBLOG("S_ENVBLOCK:");

        char *key = (char *)&envblock->rgsz[0];
        while(key[0])
        {
          char *value = key + strlen(key) + 1;

          SPDBLOG("  %s = \"%s\"", key, value);

          if(!strcmp(key, "hlslEntry"))
          {
            m_Entry = value;
          }
          else if(!strcmp(key, "hlslTarget"))
          {
            m_Profile = value;
          }
          else if(!strcmp(key, "hlslFlags"))
          {
            if(value[0] == '0' && value[1] == 'x')
            {
              int i = 2;

              m_ShaderFlags = 0;

              while(value[i] != 0)
              {
                uint32_t digit = 0;
                if(value[i] >= '0' && value[i] <= '9')
                  digit = (uint32_t)(value[i] - '0');
                if(value[i] >= 'a' && value[i] <= 'f')
                  digit = 0xa + (uint32_t)(value[i] - 'a');
                if(value[i] >= 'A' && value[i] <= 'F')
                  digit = 0xa + (uint32_t)(value[i] - 'A');

                m_ShaderFlags <<= 4;
                m_ShaderFlags |= digit;

                i++;
              }
            }
          }
          else if(!strcmp(key, "hlslDefines"))
          {
            std::string cmdlineDefines = "// Command line defines:\n\n";

            char *c = value;

            while(*c)
            {
              // skip whitespace
              while(*c && (*c == ' ' || *c == '\t' || *c == '\n'))
                c++;

              if(*c == 0)
                break;

              // start of a definition
              if(c[0] == '/' && c[1] == 'D')
              {
                c += 2;
                // skip whitespace
                while(*c && (*c == ' ' || *c == '\t' || *c == '\n'))
                  c++;

                if(*c == 0)
                  break;

                char *defstart = c;
                // the definition ends either at the next = or at the next whitespace, whichever
                // comes first
                char *defend = strpbrk(c, "= \t\n");

                if(defend == 0)
                  break;

                bool hasValue = defend[0] == '=';

                c = defend + 1;

                if(hasValue)
                {
                  char *valstart = c;

                  // skip to end or next whitespace
                  while(*c && *c != ' ' && *c != '\t' && *c != '\n')
                    c++;

                  char *valend = c;

                  cmdlineDefines += "#define ";
                  cmdlineDefines +=
                      std::string(defstart, defend) + " " + std::string(valstart, valend);
                  cmdlineDefines += "\n";
                }
                else
                {
                  cmdlineDefines += "#define ";
                  cmdlineDefines += std::string(defstart, defend);
                  cmdlineDefines += "\n";
                }
              }
              else
              {
                c++;
              }
            }

            Files.push_back({"@cmdline", cmdlineDefines});
          }

          key = value + strlen(value) + 1;
        }
      }
      else if(type == S_INLINESITE)
      {
        INLINESITESYM *inlinesite = (INLINESITESYM *)sym;

        SPDBLOG("S_INLINESITE @ %llx: function '%s' inlined into %x", ptr,
                m_Functions[inlinesite->inlinee].name.c_str(), inlinesite->pParent);

        uint32_t codeOffsetBase = 0;
        uint32_t codeOffset = main.off;
        uint32_t codeLength = 0;
        uint32_t currentLine = 0;
        uint32_t currentLineLength = 1;
        uint32_t currentColStart = 1;
        uint32_t currentColEnd = 100000;
        bool statement = true;

        Inlinee inlinee;

        inlinee.ptr = ptr;
        inlinee.parentPtr = inlinesite->pParent;
        inlinee.id = inlinesite->inlinee;

        byte *iter = inlinesite->binaryAnnotations;
        while(iter < cur)
        {
          CodeViewInfo::BinaryAnnotationOpcode op = (CodeViewInfo::BinaryAnnotationOpcode)*iter;

          // stop when reaching this, there may be padding ahead
          if(op == CodeViewInfo::BA_OP_Invalid)
            break;

          iter++;

          uint32_t parameter = CodeViewInfo::CVUncompressData(iter);

          uint32_t parameter2 = 0;
          if(CodeViewInfo::BinaryAnnotationInstructionOperandCount(op) == 2)
            parameter2 = CodeViewInfo::CVUncompressData(iter);

          bool apply = false;

          // apply op to current state
          switch(op)
          {
            case CodeViewInfo::BA_OP_Invalid: break;
            case CodeViewInfo::BA_OP_CodeOffset: codeOffset = parameter; break;
            case CodeViewInfo::BA_OP_ChangeCodeOffsetBase: codeOffsetBase = parameter; break;
            case CodeViewInfo::BA_OP_ChangeCodeOffset:
              codeOffset += parameter;
              apply = true;
              break;
            case CodeViewInfo::BA_OP_ChangeCodeLength:
            {
              // this applies to the previous/last instruction.
              if(!inlinee.locations.empty() &&
                 inlinee.locations.back().offsetEnd == inlinee.locations.back().offsetStart &&
                 inlinee.locations.back().statement == statement)
              {
                inlinee.locations.back().offsetEnd += parameter;
              }
              else
              {
                RDCERR("No valid previous instruction to apply codeLength to");
              }

              codeOffset += parameter;
              break;
            }
            case CodeViewInfo::BA_OP_ChangeFile:
              RDCERR("Unsupported change of file within inline site!");
              break;
            case CodeViewInfo::BA_OP_ChangeLineOffset:
            {
              currentLine += CodeViewInfo::DecodeSignedInt32(parameter);
              break;
            }
            case CodeViewInfo::BA_OP_ChangeLineEndDelta: currentLineLength = parameter; break;
            case CodeViewInfo::BA_OP_ChangeRangeKind:
            {
              statement = (parameter == 1);
              break;
            }
            case CodeViewInfo::BA_OP_ChangeColumnStart: currentColStart = parameter; break;
            case CodeViewInfo::BA_OP_ChangeColumnEndDelta:
            {
              currentColEnd += CodeViewInfo::DecodeSignedInt32(parameter);
              break;
            }
            case CodeViewInfo::BA_OP_ChangeCodeOffsetAndLineOffset:
            {
              uint32_t CodeDelta = parameter & 0xf;
              uint32_t sourceDelta = parameter >> 4;
              codeOffset += CodeDelta;
              currentLine += CodeViewInfo::DecodeSignedInt32(sourceDelta);
              apply = true;
              break;
            }
            case CodeViewInfo::BA_OP_ChangeCodeLengthAndCodeOffset:
            {
              codeLength = parameter;
              codeOffset += parameter2;
              apply = true;
              break;
            }
            case CodeViewInfo::BA_OP_ChangeColumnEnd: currentColEnd = parameter; break;
          }

          if(apply)
          {
            InstructionLocation loc;

            loc.statement = statement;
            loc.offsetStart = codeOffsetBase + codeOffset;
            loc.offsetEnd = loc.offsetStart + codeLength;
            loc.colStart = currentColStart;
            loc.colEnd = currentColEnd;
            loc.lineStart = currentLine;
            loc.lineEnd = currentLine + currentLineLength;

            // the behaviour seems to be that if codeLength is ephemeral, not sticky like the rest
            codeLength = 0;

            // if we have a previous location with implicit length, fix it up now
            if(!inlinee.locations.empty() &&
               inlinee.locations.back().offsetEnd == inlinee.locations.back().offsetStart &&
               inlinee.locations.back().statement == loc.statement)
            {
              inlinee.locations.back().offsetEnd = loc.offsetStart;
            }

            inlinee.locations.push_back(loc);

            SPDBLOG("inline annotation of %s, from %x (length %x), from %u:%u to %u:%u",
                    statement ? "statement" : "expression", loc.offsetStart,
                    loc.offsetEnd - loc.offsetStart, currentLine, currentColStart,
                    currentLine + currentLineLength, currentColEnd);
          }
        }

        inlines.push_back(inlinee);
      }
      else if(type == S_LOCAL)
      {
        LOCALSYM *local = (LOCALSYM *)sym;

        SPDBLOG("S_LOCAL: '%s' of type %x", local->name, local->typind);

        localName = (char *)local->name;
        localType = local->typind;

        if(local->flags.fIsParam)
          SPDBLOG("  fIsParam: variable is a parameter");
        if(local->flags.fAddrTaken)
          SPDBLOG("  fAddrTaken: address is taken");
        if(local->flags.fCompGenx)
          SPDBLOG("  fCompGenx: variable is compiler generated");
        if(local->flags.fIsAggregate)
          SPDBLOG(
              "  fIsAggregate: the symbol is splitted in temporaries, "
              "which are treated by compiler as independent entities");
        if(local->flags.fIsAggregated)
          SPDBLOG("  fIsAggregated: variable is a part of a fIsAggregate symbol");
        if(local->flags.fIsAliased)
          SPDBLOG("  fIsAliased: variable has multiple simultaneous lifetimes");
        if(local->flags.fIsAlias)
          SPDBLOG("  fIsAlias: variable represents one of the multiple simultaneous lifetimes");
        if(local->flags.fIsRetValue)
          SPDBLOG("  fIsRetValue: variable represents a function return value");
        if(local->flags.fIsOptimizedOut)
          SPDBLOG("  fIsOptimizedOut: variable variable has no lifetimes");
        if(local->flags.fIsEnregGlob)
          SPDBLOG("  fIsEnregGlob: variable is an enregistered global");
        if(local->flags.fIsEnregStat)
          SPDBLOG("  fIsEnregStat: variable is an enregistered static");
      }
      else if(type == S_DEFRANGE_HLSL)
      {
        DEFRANGESYMHLSL *defrange = (DEFRANGESYMHLSL *)sym;

        LocalMapping mapping;
        RegisterRange &range = mapping.var.registers[0];

        bool indexable = false;
        const char *regtype = "";
        const char *regprefix = "?";

        // CV_HLSLREG_e == OperandType

        switch((OperandType)defrange->regType)
        {
          case TYPE_TEMP:
            range.type = RegisterType::Temporary;
            regtype = "temp";
            regprefix = "r";
            break;
          case TYPE_INPUT:
          case TYPE_INPUT_PRIMITIVEID:
          case TYPE_INPUT_FORK_INSTANCE_ID:
          case TYPE_INPUT_JOIN_INSTANCE_ID:
          case TYPE_INPUT_CONTROL_POINT:
          case TYPE_INPUT_PATCH_CONSTANT:
          case TYPE_INPUT_DOMAIN_POINT:
          case TYPE_INPUT_THREAD_ID:
          case TYPE_INPUT_THREAD_GROUP_ID:
          case TYPE_INPUT_THREAD_ID_IN_GROUP:
          case TYPE_INPUT_COVERAGE_MASK:
          case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED:
          case TYPE_INPUT_GS_INSTANCE_ID:
            range.type = RegisterType::Input;
            regtype = "input";
            regprefix = "v";
            break;
          case TYPE_OUTPUT:
          case TYPE_OUTPUT_DEPTH:
          case TYPE_OUTPUT_DEPTH_LESS_EQUAL:
          case TYPE_OUTPUT_DEPTH_GREATER_EQUAL:
          case TYPE_OUTPUT_STENCIL_REF:
          case TYPE_OUTPUT_COVERAGE_MASK:
            range.type = RegisterType::Output;
            regtype = "output";
            regprefix = "o";
            break;
          case TYPE_INDEXABLE_TEMP:
            range.type = RegisterType::IndexedTemporary;
            regtype = "indexable";
            regprefix = "x";
            indexable = true;
            break;
          default: break;
        }

        // this is a virtual register, not stored
        if((OperandType)defrange->regType == TYPE_STREAM)
          continue;

        const char *space = "";
        switch((CV_HLSLMemorySpace_e)defrange->memorySpace)
        {
          case CV_HLSL_MEMSPACE_DATA: space = "data"; break;
          case CV_HLSL_MEMSPACE_SAMPLER: space = "sampler"; break;
          case CV_HLSL_MEMSPACE_RESOURCE: space = "resource"; break;
          case CV_HLSL_MEMSPACE_RWRESOURCE: space = "rwresource"; break;
          default: break;
        }

        SPDBLOG("S_DEFRANGE_HLSL: %u->%u bytes in parent: %s %s (dim %d) %s",
                defrange->offsetParent, defrange->offsetParent + defrange->sizeInParent, regtype,
                space, defrange->regIndices, defrange->spilledUdtMember ? "spilled" : "");

        if(defrange->regIndices > 1)
        {
          RDCWARN("More than one register index in mapping");
          // this is used for geometry shader inputs for example
        }

        uint32_t regoffset = *CV_DEFRANGESYMHLSL_OFFSET_CONST_PTR(defrange);

        char regcomps[] = "xyzw";

        uint32_t regindex = indexable ? regoffset : regoffset / 16;
        uint32_t regfirstcomp = indexable ? 0 : (regoffset % 16) / 4;
        uint32_t regnumcomps = indexable ? 4 : defrange->sizeInParent / 4;

        bool builtinoutput = false;
        mapping.var.builtin = ShaderBuiltin::Undefined;
        switch((OperandType)defrange->regType)
        {
          case TYPE_OUTPUT_DEPTH:
            builtinoutput = true;
            mapping.var.builtin = ShaderBuiltin::DepthOutput;
            break;
          case TYPE_OUTPUT_DEPTH_LESS_EQUAL:
            builtinoutput = true;
            mapping.var.builtin = ShaderBuiltin::DepthOutputLessEqual;
            break;
          case TYPE_OUTPUT_DEPTH_GREATER_EQUAL:
            builtinoutput = true;
            mapping.var.builtin = ShaderBuiltin::DepthOutputGreaterEqual;
            break;
          case TYPE_OUTPUT_STENCIL_REF:
            builtinoutput = true;
            mapping.var.builtin = ShaderBuiltin::StencilReference;
            break;
          case TYPE_OUTPUT_COVERAGE_MASK:
            builtinoutput = true;
            mapping.var.builtin = ShaderBuiltin::MSAACoverage;
            break;
          case TYPE_INPUT_PRIMITIVEID: mapping.var.builtin = ShaderBuiltin::PrimitiveIndex; break;
          case TYPE_INPUT_COVERAGE_MASK: mapping.var.builtin = ShaderBuiltin::MSAACoverage; break;
          case TYPE_INPUT_THREAD_ID:
            mapping.var.builtin = ShaderBuiltin::DispatchThreadIndex;
            break;
          case TYPE_INPUT_THREAD_GROUP_ID: mapping.var.builtin = ShaderBuiltin::GroupIndex; break;
          case TYPE_INPUT_THREAD_ID_IN_GROUP:
            mapping.var.builtin = ShaderBuiltin::GroupThreadIndex;
            break;
          case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED:
            mapping.var.builtin = ShaderBuiltin::GroupFlatIndex;
            break;
          case TYPE_INPUT_GS_INSTANCE_ID:
            mapping.var.builtin = ShaderBuiltin::GSInstanceIndex;
            break;
          default: break;
        }

        if(mapping.var.builtin != ShaderBuiltin::Undefined)
        {
          bool found = false;

          if(builtinoutput)
          {
            for(size_t i = 0; i < dxbc->m_OutputSig.size(); i++)
            {
              if(dxbc->m_OutputSig[i].systemValue == mapping.var.builtin)
              {
                regindex = (uint32_t)i;
                regfirstcomp = 0;
                found = true;
                break;
              }
            }
          }
          else
          {
            for(size_t i = 0; i < dxbc->m_InputSig.size(); i++)
            {
              if(dxbc->m_InputSig[i].systemValue == mapping.var.builtin)
              {
                regindex = (uint32_t)i;
                regfirstcomp = 0;
                found = true;
                break;
              }
            }
          }

          // if not found in the signatures, then it's a fixed-function input like threadid - it
          // will be matched by builtin
          if(!found)
            regindex = ~0U;
        }

        char *regswizzle = regcomps;
        regswizzle += regfirstcomp;
        regswizzle[regnumcomps] = 0;

        SPDBLOG("Stored in %s%u.%s", regprefix, regindex, regswizzle);

        mapping.var.localName = localName;

        uint32_t varOffset = defrange->offsetParent;
        uint32_t varLen = defrange->sizeInParent;

        const TypeDesc *vartype = &typeInfo[localType];

        RDCASSERT(varOffset + varLen <= vartype->byteSize);

        // step through struct members
        while(!vartype->members.empty())
        {
          bool found = false;

          // find the child member this register corresponds to. We don't handle overlaps between
          // members
          for(const TypeMember &mem : vartype->members)
          {
            TypeDesc &childType = typeInfo[mem.typeIndex];

            uint32_t memberOffset = mem.byteOffset;
            uint32_t memberLen = childType.byteSize;

            // if this member is before our variable, continue
            if(memberOffset + memberLen <= varOffset)
              continue;

            // if this member is after our variable, continue (though if members are sorted, this
            // means we won't find a candidate member)
            if(varOffset + varLen <= memberOffset)
              continue;

            if(memberOffset > varOffset || memberOffset + memberLen < varOffset + varLen)
            {
              RDCERR("member %s of %s doesn't fully enclose variable [%u,%u] vs [%u,%u]",
                     mem.name.c_str(), vartype->name.c_str(), memberOffset,
                     memberOffset + memberLen, varOffset, varOffset + varLen);
            }

            mapping.var.localName = std::string(mapping.var.localName) + "." + mem.name;

            // subtract off the offset of this member so we're now relative to it - since it might
            // be a struct itself and we need to recurse.
            varOffset -= memberOffset;

            vartype = &childType;

            found = true;

            break;
          }

          if(!found)
          {
            RDCERR("No member of %s corresponds to variable range [%u,%u]", vartype->name.c_str(),
                   varOffset, varOffset + varLen);
            mapping.var.localName = std::string(mapping.var.localName) + ".__unknown__";
            break;
          }
        }

        mapping.var.type = vartype->baseType;
        mapping.var.rows = 1;
        mapping.var.columns = vartype->vecSize;

        // if it's an array or matrix, figure out the index
        if(vartype->matArrayStride)
        {
          uint32_t idx = varOffset / vartype->matArrayStride;

          mapping.var.localName = StringFormat::Fmt("%s[%u]", mapping.var.localName.c_str(), idx);
          mapping.var.rows = RDCMAX(
              1U, (vartype->byteSize + (vartype->matArrayStride - 1)) / vartype->matArrayStride);

          varOffset -= vartype->matArrayStride * idx;
        }

        if(vartype->leafType != LF_MATRIX)
        {
          mapping.var.elements = mapping.var.rows;
          mapping.var.rows = 1;
        }

        RDCASSERT(mapping.var.rows <= 4 && mapping.var.columns <= 4);

        range.index = uint16_t(regindex & 0xffff);
        mapping.regFirstComp = regfirstcomp;
        mapping.varFirstComp = (varOffset % 16) / 4;
        mapping.numComps = regnumcomps;

        SPDBLOG("Valid from %x to %x", defrange->range.offStart,
                defrange->range.offStart + defrange->range.cbRange);

        mapping.range.startRange = defrange->range.offStart;
        mapping.range.endRange = defrange->range.offStart + defrange->range.cbRange;

        const CV_LVAR_ADDR_GAP *gaps = CV_DEFRANGESYMHLSL_GAPS_CONST_PTR(defrange);
        size_t gapcount = CV_DEFRANGESYMHLSL_GAPS_COUNT(defrange);
        if(gapcount > 0)
          SPDBLOG("Except for in:");
        for(size_t i = 0; i < gapcount; i++)
        {
          SPDBLOG("  Gap %zu: %x -> %x", i, defrange->range.offStart + gaps[i].gapStartOffset,
                  defrange->range.offStart + gaps[i].gapStartOffset + gaps[i].cbRange);

          LocalRange r = {defrange->range.offStart + gaps[i].gapStartOffset,
                          defrange->range.offStart + gaps[i].gapStartOffset + gaps[i].cbRange};

          mapping.gaps.push_back(r);
        }

        m_Locals.push_back(mapping);
      }
      else if(type == S_INLINESITE_END)
      {
        SPDBLOG("S_INLINESITE_END");
      }
      else if(type == S_END)
      {
        SPDBLOG("S_END");
      }
      else
      {
        SPDBLOG("Unhandled type %04x", type);
      }
    }
    RDCASSERT(cur == end);

    end = cur + modules[m].cbLines;

    while(cur < end)
    {
      CV_DebugSSubsectionHeader_t *subsection = (CV_DebugSSubsectionHeader_t *)cur;

      byte *substart = (byte *)(subsection + 1);

      cur += sizeof(CV_DebugSSubsectionHeader_t) + subsection->cbLen;

      byte *subend = cur;

      if(subsection->type == DEBUG_S_FILECHKSMS)    // hash
      {
        byte *iter = substart;
        while(iter < subend)
        {
          FileChecksum *checksum = (FileChecksum *)iter;

          uint32_t chunkOffs = uint32_t(iter - substart);

          iter += offsetof(FileChecksum, hashData);

          std::string name;

          if(Names.find(checksum->nameIndex) != Names.end())
          {
            name = Names[checksum->nameIndex];
            if(name.empty())
              name = Names[checksum->nameIndex] = "shader";
          }
          else
          {
            RDCERR("Encountered nameoffset %u that doesn't match any name.", checksum->nameIndex);
          }

          CV_SourceChksum_t hashType = (CV_SourceChksum_t)checksum->hashType;

          if(hashType != CHKSUM_TYPE_NONE)
          {
            byte hash[256];
            memcpy(hash, checksum->hashData, checksum->hashLength);

            char hashstr[16 * 2 + 1] = {0};
            char hex[] = "0123456789abcdef";

            for(uint8_t i = 0; i < RDCMIN(uint8_t(16), checksum->hashLength); i++)
            {
              hashstr[i * 2 + 0] = hex[(hash[i] & 0xf0) >> 4];
              hashstr[i * 2 + 1] = hex[(hash[i] & 0x0f) >> 0];
            }

            SPDBLOG("File %s has checksum %s%s", name.c_str(), hashstr,
                    checksum->hashLength > 16 ? "..." : "");

            int32_t fileIdx = -1;

            for(size_t i = 0; i < Files.size(); i++)
            {
              if(!_stricmp(Files[i].first.c_str(), name.c_str()))
              {
                fileIdx = (int32_t)i;
                break;
              }
            }

            FileMapping[chunkOffs] = fileIdx;
          }
          else
          {
            // this is a 'virtual' file. Create a source file that we can map lines to just for
            // something,
            // as we won't be able to reliably get the real source lines back. The PDB lies
            // convincingly about the
            // source according to #line
            if(!name.empty())
            {
              Files.push_back({name, ""});

              FileMapping[chunkOffs] = (int32_t)Files.size() - 1;
            }
            else
            {
              FileMapping[chunkOffs] = -1;
            }
          }

          iter = AlignUpPtr(iter + checksum->hashLength, 4);
        }
        RDCASSERT(iter == subend);
      }
      else if(subsection->type == DEBUG_S_LINES)
      {
        CV_DebugSLinesHeader_t *hdr = (CV_DebugSLinesHeader_t *)substart;

        bool hasColumns = (hdr->flags & CV_LINES_HAVE_COLUMNS);

        byte *iter = (byte *)(hdr + 1);
        while(iter < subend)
        {
          CV_DebugSLinesFileBlockHeader_t *file = (CV_DebugSLinesFileBlockHeader_t *)iter;
          CV_Line_t *lines = (CV_Line_t *)(file + 1);
          CV_Column_t *columns = (CV_Column_t *)(lines + file->nLines);

          iter = (byte *)file + file->cbBlock;

          int32_t fileIdx = -1;

          if(FileMapping.find(file->offFile) == FileMapping.end())
          {
            RDCERR(
                "SPDB chunk - line numbers file references index %u not encountered in file "
                "mapping",
                file->offFile);
          }
          else
          {
            fileIdx = FileMapping[file->offFile];
          }

          for(CV_off32_t l = 0; l < file->nLines; l++)
          {
            CV_Line_t &line = lines[l];

            LineColumnInfo lineCol;
            lineCol.fileIndex = fileIdx;
            lineCol.lineStart = line.linenumStart;
            lineCol.lineEnd = line.linenumStart + line.deltaLineEnd;

            if(hasColumns)
            {
              CV_Column_t &col = columns[l];
              lineCol.colStart = col.offColumnStart;
              lineCol.colEnd = col.offColumnEnd;
            }

            m_Lines[line.offset] = lineCol;
          }
        }
        RDCASSERT(iter == subend);
      }
      else if(subsection->type == DEBUG_S_INLINEELINES)
      {
        byte *iter = substart;
        uint32_t sourceLineType = *(uint32_t *)iter;
        iter += sizeof(uint32_t);

        if(sourceLineType == CV_INLINEE_SOURCE_LINE_SIGNATURE)
        {
          CodeViewInfo::InlineeSourceLine *inlinee = (CodeViewInfo::InlineeSourceLine *)iter;
          size_t count = (subend - iter) / sizeof(CodeViewInfo::InlineeSourceLine);
          for(size_t i = 0; i < count; i++, inlinee++)
          {
            inlines[i].id = inlinee->inlinee;
            inlines[i].fileOffs = inlinee->fileId;
            inlines[i].baseLineNum = inlinee->sourceLineNum;
          }
        }
        else if(sourceLineType == CV_INLINEE_SOURCE_LINE_SIGNATURE_EX)
        {
          size_t idx = 0;
          while(iter < subend)
          {
            CodeViewInfo::InlineeSourceLineEx *inlinee = (CodeViewInfo::InlineeSourceLineEx *)iter;

            iter += sizeof(CodeViewInfo::InlineeSourceLineEx) +
                    sizeof(CV_off32_t) * inlinee->countOfExtraFiles;

            inlines[idx].id = inlinee->inlinee;
            inlines[idx].fileOffs = inlinee->fileId;
            inlines[idx].baseLineNum = inlinee->sourceLineNum;
            idx++;
          }
        }
      }
      else
      {
        break;
      }
    }
  }

  for(auto it = m_Lines.begin(); it != m_Lines.end(); ++it)
    it->second.callstack.push_back(m_Functions[0].name);

  SPDBLOG("Applying %zu inline sites", inlines.size());

  for(size_t i = 0; i < inlines.size(); i++)
  {
    RDCASSERT(inlines[i].locations.size() > 1);

    if(inlines[i].locations.empty() || inlines[i].locations.size() == 1)
    {
      RDCWARN("Skipping patching function call with %d locations", inlines[i].locations.size());
      continue;
    }

    RDCASSERT(FileMapping.find(inlines[i].fileOffs) != FileMapping.end());

    if(FileMapping.find(inlines[i].fileOffs) == FileMapping.end())
    {
      RDCWARN("Got function call patch with fileoffs %x - skipping", inlines[i].fileOffs);
      continue;
    }

    SPDBLOG("Inline site %zu", i);

    int32_t fileIdx = FileMapping[inlines[i].fileOffs];

    for(size_t j = 0; j < inlines[i].locations.size(); j++)
    {
      InstructionLocation &loc = inlines[i].locations[j];

      int nPatched = 0;

      auto it = m_Lines.lower_bound(loc.offsetStart);

      for(; it != m_Lines.end() && it->first <= loc.offsetEnd; ++it)
      {
        if((it->first >= loc.offsetStart && it->first < loc.offsetEnd) ||
           (it->first == loc.offsetStart && it->first == loc.offsetEnd))
        {
          SPDBLOG("Patching %x between [%x,%x] from (%d %u:%u -> %u:%u) into (%d %u:%u -> %u:%u)",
                  it->first, loc.offsetStart, loc.offsetEnd, it->second.fileIndex,
                  it->second.lineStart, it->second.colStart, it->second.lineEnd, it->second.colEnd,
                  fileIdx, loc.lineStart + inlines[i].baseLineNum, loc.colStart,
                  loc.lineEnd + inlines[i].baseLineNum, loc.colEnd);

          it->second.fileIndex = fileIdx;
          it->second.lineStart = loc.lineStart + inlines[i].baseLineNum;
          it->second.lineEnd = loc.lineEnd + inlines[i].baseLineNum;
          it->second.colStart = loc.colStart;
          it->second.colEnd = loc.colEnd;
          if(loc.statement)
            it->second.callstack.push_back(m_Functions[inlines[i].id].name);
          nPatched++;
        }
      }

      if(nPatched == 0)
        RDCWARN("Can't find anything between offsets %x,%x as desired", loc.offsetStart,
                loc.offsetEnd);
    }
  }

  delete[] pages;

  // save the filenames in their original order
  std::vector<std::string> filenames;
  filenames.reserve(Files.size());
  for(size_t i = 0; i < Files.size(); i++)
    filenames.push_back(Files[i].first);

  // Sort files according to the order they come in the Names array, this seems to be more reliable
  // about placing the main file first.
  std::sort(Files.begin(), Files.end(), [&Names](const rdcpair<std::string, std::string> &a,
                                                 const rdcpair<std::string, std::string> &b) {
    // any entries that aren't found in Names at all (like @cmdline that we add) will be sorted to
    // the end.
    size_t aIdx = ~0U, bIdx = ~0U;

    size_t i = 0;
    for(auto it = Names.begin(); it != Names.end(); ++it)
    {
      if(it->second == a.first)
        aIdx = i;
      if(it->second == b.first)
        bIdx = i;

      i++;
    }

    // if neither were found, sort by filename
    if(aIdx == bIdx)
      return a.first < b.first;

    return aIdx < bIdx;
  });

  // create a map from filename -> index
  std::map<std::string, int32_t> remapping;
  for(size_t i = 0; i < Files.size(); i++)
    remapping[Files[i].first] = (int32_t)i;

  // remap the line info by looking up the original intended filename, then looking up the new index
  for(auto it = m_Lines.begin(); it != m_Lines.end(); ++it)
  {
    if(it->second.fileIndex == -1)
      continue;
    it->second.fileIndex = remapping[filenames[it->second.fileIndex]];
  }

  std::sort(m_Locals.begin(), m_Locals.end());

  m_HasDebugInfo = true;
}

void SPDBChunk::GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const
{
  auto it = m_Lines.lower_bound((uint32_t)offset);

  if(it != m_Lines.end() && (uintptr_t)it->first <= offset)
    lineInfo = it->second;
}

bool SPDBChunk::HasLocals() const
{
  return true;
}

void SPDBChunk::GetLocals(size_t instruction, uintptr_t offset,
                          rdcarray<LocalVariableMapping> &locals) const
{
  locals.clear();

  for(auto it = m_Locals.begin(); it != m_Locals.end(); ++it)
  {
    if(it->range.startRange > offset)
      break;

    if(it->range.endRange <= offset)
      continue;

    bool ingap = false;

    for(auto gapit = it->gaps.begin(); gapit != it->gaps.end(); gapit++)
    {
      if(gapit->startRange >= offset && gapit->endRange < offset)
      {
        ingap = true;
        break;
      }
    }

    if(ingap)
      continue;

    bool added = false;

    // we apply each matching local over the top. Where there is an overlap (e.g. two variables with
    // the same name) we take the last mapping as authoratitive. This is a good solution for the
    // case where one function with a parameter/variable name calls an inner function with the same
    // parameter name and there's shadowing. The later mapping will be for the inner function so we
    // use it in preference.

    // check if we already have a mapping for this variable
    for(LocalVariableMapping &a : locals)
    {
      const LocalVariableMapping &b = it->var;

      if(a.localName == b.localName)
      {
        RegisterRange range = b.registers[0];

        for(uint32_t i = 0; i < it->numComps; i++)
        {
          a.registers[it->varFirstComp + i].type = b.registers[0].type;
          a.registers[it->varFirstComp + i].index = b.registers[0].index;
          a.registers[it->varFirstComp + i].component = uint16_t(it->regFirstComp + i);
        }

        a.regCount = RDCMAX(a.regCount, it->varFirstComp + it->numComps);

        // we've processed this, no need to add a new entry
        added = true;
        break;
      }
    }

    if(!added)
    {
      locals.push_back(it->var);
      LocalVariableMapping &a = locals.back();

      // the register range is stored in [0] but we don't want to actually push that, so make it
      // undefined and grab it locally
      RegisterRange range;
      std::swap(a.registers[0], range);

      for(uint32_t i = 0; i < it->numComps; i++)
      {
        a.registers[it->varFirstComp + i].type = range.type;
        a.registers[it->varFirstComp + i].index = range.index;
        a.registers[it->varFirstComp + i].component = uint16_t(it->regFirstComp + i);
      }

      a.regCount = RDCMAX(it->var.columns, it->varFirstComp + it->numComps);
    }
  }
}

};    // namespace DXBC


Could you be the next youth voice of UNESCO?
Are you aged between 18 and 25 and want to take action to promote peace and social justice in New Zealand and the Pacific?
Then they want to hear from you.
Thirty graduates from the Office of Ethnic Communities' Young Leaders programme met in Wellington on Thursday 22 October to further develop their leadership skills and understanding of the public sector environment with a view to considering a career in this area.
Today the Minister Hon Peseta Sam Lotu-Iiga recognised and congratulated Christchurch Young Leaders programme members for continuing their leadership learning journey by developing a digital website presence project funded and supported by the Ministry of Youth Development Fund 2015/2016.
I recently congratulated 55 young people from 24 different ethnic groups who completed the Office of Ethnic Affairs Young Leaders Programme. This innovative course aims at building future public sector leaders and it was exciting to hear that many participants have already found jobs. These young people show what the future of New Zealand will look like. Statistics show we are becoming an increasingly multicultural country.
Shawn Kurian Thomas, 20 year old young leader who participated in the Christchurch Youth Leadership programme, recently received an honourable mention award from The United Nations Alliance of Civilizations (UNAOC) and the International Organization for Migration (IOM). The two organisations invited youth from all over the world to submit original and creative videos focusing on the themes of PLURAL+, a youth-produced video festival which encourages young people to explore migration, diversity, and social inclusion, as well as to share their creative vision with the world. Since 2009 over 400 entries from 63 countries have been submitted. Winning videos have been screened in dozens of festivals, and broadcast on television networks around the world.
Figuring out how culture affects real life experiences was the challenge put to members of our Young Leaders Auckland Pilot Programme at their most recent training session.
Role play and discussion were used to improve understanding about the complexities of culture, effective communication and the importance of cultural self awareness.
The importance of understanding unconscious bias which influences our attitudes and behaviour was also a focus.


text:
require 'chef/knife'
require 'chef/knife/base_vsphere_command'

# Lists all known clusters in the configured datacenter
class Chef::Knife::VsphereClusterList < Chef::Knife::BaseVsphereCommand
  banner 'knife vsphere cluster list'

  common_options

  def traverse_folders(folder)
    return if folder.is_a? RbVmomi::VIM::VirtualApp

    if folder.is_a? RbVmomi::VIM::ClusterComputeResource
      clusters = folder.path[3..-1].reject { |p| p.last == 'ClusterComputeResource' }
      puts "#{ui.color('Cluster', :cyan)}: " + clusters.map(&:last).join('/')
    end

    folders = find_all_in_folder(folder, RbVmomi::VIM::ManagedObject) || []
    folders.each do |child|
      traverse_folders(child)
    end
  end

  def find_cluster_folder(folderName)
    dc = datacenter
    base_entity = dc.hostFolder
    entity_array = folderName.split('/')
    entity_array.each do |entityArrItem|
      if entityArrItem != ''
        base_entity = base_entity.childEntity.grep(RbVmomi::VIM::ManagedObject).find { |f| f.name == entityArrItem } ||
                      abort("no such folder #{folderName} while looking for #{entityArrItem}")
      end
    end
    base_entity
  end

  def run
    vim_connection
    base_folder = find_cluster_folder(get_config(:folder))
    traverse_folders(base_folder)
  end
end

redpajama_set_name: RedPajamaGithub


text:
HomeBMWi3Features
BMW i3 Features
Best Plug-In Electric Car Deals For July 2019
Take advantage of these fantastic deals.
Here Are The Quickest Electric Cars For 2019
There's quick and then there's Tesla quick. But what if you can't afford a Tesla. Well, here we present other quick electric cars, including some that aren't so expensive.
Here Are The Quickest Selling Used Electric Cars
It may be hard to believe, but even used Teslas sell super quick. Tops on this list in the Tesla Model X. It's hot, used or not. Just 28 days on the used lot.
BMW Electric Cars: Past, Present And Future
BMW has been making EVs since the 1970s. Its best electric models are yet to come. The upcoming electric i4 and self-driving iNext could offer 400 miles of range.
By Bradley Berman Nov 20, 2018
Top 5 Most Reliable Electric Cars For 2018
We all know electric cars are more reliable than gas, but which are the most reliable of the electric bunch? You may be surprised to find 2 Teslas listed here.
Get $17,500 Off A BMW i3: Here's How
Georgia Power has seemingly partnered with BMW to offer a seriously appealing option for all future BMW i3 customers. Select customers may be eligible for an instant rebate of $10,000, but also, for a future $7,500 federal tax credit as well, totaling a whopping $17,500 in savings.
By Vanja Kljaic Sep 22, 2018
Top 6 Plug-In Hybrids Ranked By Electric Range
Here we take a look at the 6 highest electric range PHEVs on sale in the U.S. You'd be surprised to know that 97 miles is the high, while 33 is the low.
By Eric Loveday Jun 03, 2018
6 Used Plug-In Electric Cars That Spend The Least Days On Dealer Lots
The average used car spends 33.4 days on the lot, but the top 10 quickest movers spend only 70 to 80 percent of that amount of time, ranging from 22.2 to 26.1 days. Six of the top ten are alternative-fuel cars, with three electric cars and three plug-in hybrids.
How To Lease A $51,695 BMW i3 For $112 Per Month
The BMW i3 is not an inexpensive car, but it often comes with huge discounts. Deals vary geographically, and many times, most dealers won't be able to honor the amazing pricing you see advertised. However, if a deal is incredibly good, even if your local dealership ups the final numbers, you still win.
Germany's ADAC Creates Lease Deal For BMW i3
ADAC, the 100+ years old General German Automobile Club with more than 20 million members, is going to promote electrification by securing special deals from automakers.
PG&E Joins The Club Offering $10,000 BMW i3 Discount In California
Now you can get $10,000 off any new 2017 or 2018 BMW i3 in California, as long as you're a Pacific Gas and Electric (PG&E) customer.
By Steven Loveday Mar 05, 2018
Want $20,000 Off Your Nissan LEAF Or BMW i3? Here's How To Score That Deal
Nissan and BMW, in coordination with San Diego Gas & Electric have introduced a $10,000 discount for SDG&E customers who buy a 2017 Nissan LEAF or BMW i3
BMW i3 Listed Among Top 10 Best Used Cars Under £15,000 In UK
What are the best used vehicles that money can buy in the UK for under £15,000 ($19,250)? Apparently the BMW i3 makes the list!

redpajama_set_name: RedPajamaCommonCrawl


Since for the understanding of the exhibition's conception detailed explanations have been provided to clarify what the authors' intentions were behind their works, during the guided tour attention will be drawn to the opposite side – the recipient, an imaginary audience, and how people could perceive the journey that has been offered to them in the senselessness of the past and present illusions. One of the directions for the potential angle: the authors' interpretations of the past can be perceived as valuable data, which help to understand the present; however, it is possible that the photographs participate in the creation of disinformation effect providing misleading ideas and confirmation of false memories, which some people need to have on a regularly basis. It means that each piece of authentic evidence, in this case, a photograph, can be seen both as a data carrier and an artefact interpreted within the context of the recipient's perceptual factors. It is unknown whether it will happen during the guided tour, because due to the recently discovered "lazy brain" phenomenon there can be inner and external conflicts, but the term "fake news" probably will be used for the last time in the recipients' lives.
Anda Rožukalne, PhD, works at Riga Stradins University where she is the dean of the Department of Communication, Journalism and Multimedia Studies. She has worked professionally as a reporter and as an editor.


text:
'The Year Of Africa'
 Adekeye Adebajo's contribution to this issue of ARB induces us to reflect further on the big event in Africa this year: the 2010 World Cup. In an earlier article (Mail and Guardian, 12-18 March 2010) 'In the footsteps of Henry Kissinger's prognosis of the 1986 World Cup'; Adebajo had weighed the chances of an African team winning the much coveted trophy by relating each of the six African teams to their national characteristics. His prediction, which sadly has proven accurate, was that 'it will take a while longer before Africa's football and national characteristic align sufficiently to claim the ultimate prize'. As it turned out, even a semi-final place eluded Africa.
This despite the fact that the event had fortuitously coincided with the Golden Jubilee of the great year of Africa, 1960. Exactly fifty years ago, the wind of independence swept across the continent, along the trail first blazed by Kwame Nkrumah's Ghana in 1957. Again in September 1960, a bare-footed Ethiopian athlete by the name of Abebe Bikila won the Marathon at the Rome Olympics, smashing the world record and earning black Africa's first Olympic gold medal. Now, once again, Africa has become the focus of global attention.
There is no doubt that the stature and charisma of Nelson Mandela contributed a lot to the selection of South Africa to host this important event. At the same time, however, the selection marked the culmination of decades of struggle by leaders of African football for equity. Particular mention should be made in this respect of Yidnekachew Tesema, president of the Confederation of African Football (CAF) from 1972 to 1987. In his capacity as head of the African football governing body and member of the Executive Committee of FIFA, he waged an unrelenting struggle on two fronts: the exclusion of apartheid South Africa from international competitions and the equitable representation of Africa in the World Cup finals. Thanks to his and his colleagues' efforts, the place for African teams in the finals grew progressively rom half (with a playoff between the African and Asian champions to secure the one spot) to one, two and more recently five.
Even after the honour of hosting the event was awarded to South Africa some six years back, there were lingering doubts and reservations about its capacity to stage such a big event. The 'Cabinda incident', when the Togo national team that was taking part in the African Cup of Nations tournament in Angola in January 2010 was ambushed, had fueled these reservations. But all these doubts were blown away as South Africa made the whole continent proud by staging it in such a spectacular fashion.
The only two jarring notes on the impeccable organization came at the very end, and they had nothing to do with the South Africans themselves. At the final award ceremony, rather than letting South African president Jacob Zuma award the trophy to the winning Spanish team from his designated place with the decorum that the occasion demanded, Sepp Blatter dragged the guest of honour in rather undignified fashion to where the ecstatic champions were preparing to celebrate; in the end, he himself effectively gave the trophy to the Spanish captain, allowing the South African president only the barest of touches. The other jarring note occurred thousands of miles away in the Ugandan capital when two simultaneous explosions claimed the lives of over 70 spectators of the final match. Thus, by a curious twist of fate, just as the Somali rapper K'Naan had come to symbolize, alongside the Colombian star Shakira with her 'Waka Waka' ('This Time for Africa'), the festive spirit of the event with his uplifting song, so did the fanatical Somali group Al Shabab manage to spoil the show at the very last minute.
Even the distinctive vuzuzelas, so irksome at the beginning to many of the pros, who claimed the din made it impossible for them to communicate on the pitch, eventually came to be adopted by fans of all colours and hues. Indeed, their popularity went beyond the World Cup venues, as demonstrators elsewhere picked them up as effective media to blast their slogans. Hyundai capitalized on its popularity to manufacture a giant edition of the trumpet to advertise its wares. It is a mark of the commercialism that attends sporting events that a great proportion of these vuzuzelas were actually plastic versions of the traditional African instrument manufactured in bulk in China.
The success of the organization began with the architectural feat that the stadiums symbolized, particularly the distinctively African calabash-shaped one at Soccer City. That this iconic emblem of World Cup 2010 should be based in Soweto, the symbol of African resistance to apartheid and the venue where Mandela made his first speech after his release from over two decades of detention had great symbolic significance. The faultless pitches also put to shame the great citadel of English football, Wembley Stadium.
It was part of the ritual that the host nation should play the opening match. But few people expected that it would open the score tally with such a spectacular shot by Sipihwe Tshabalala. That feat could not be sustained, however, and what many people dreaded came to pass: Bafana Bafana made their exit at the first round. But not before inflicting a humiliating defeat on the French, champions of 1998 and runner-up in the last World Cup! But, the fear of teams playing in empty stadiums once the host nation was kicked out proved groundless, as the South Africans continued to crowd the stadiums and cheer on their preferred teams with their non-stop vuzuzelas. Even after the exit of the host team, attendance at Soccer City stadium remained at almost full capacity, which is a rarity.
The 2010 World Cup is memorable for many things. The capricious and controversial brand ball, the Jabulani ("rejoice" in Zulu!), turned out to be many a goalie's nightmare. Strikers too had a hard time controlling long passes,as the ball slid out at uncontrollable speed. This was also a World Cup where the Drogbas, the Messis, the Rooneys and the Ronaldos could not replicate their dazzling performances or their prodigious goal-scoring feats of the Premier League or La Liga. Some pundits attributed this to their being selfish. Diego Maradona, the hapless Argentinaian coach who saw his brilliant side trashed by Germany in the quarterfinals, forwarded a more convincing explanation: that they were not selfish enough but were rather shackled by team discipline, so that they ended up being led by their team mates rather than leading them, as he himself did in 1986. Like the equally lackluster Dunga of Brazil, who had sacrificed the celebrated Brazilian artistry for an allegedly more effective if dour strategy, Maradona was doubly mortified as the glory of lifting the prized trophy as both player and coach eluded him.
It was also a tournament where the finalists of the last World Cup, France and Italy, were sent home rather ignominiously at the end of the first round. As the tournament progressed, the South Americans, who had dominated the first two rounds, were reduced to one national team by the semi-final stage. What looked like developing into an all-South American final ended up as an all-European one? Brazil, the favorite to lift the trophy, was denied the rare honour of winning the tournament in every continent where it was staged.
Above all, the 2010 World Cup remains embedded in our memory because of the performance of the Black Stars of Ghana. As the other star-studded African teams crushed out at the first round, the Black Stars went on bravely to knock out the US in the second round. Thereafter, they became the continent's team, not just the national team of Ghana. South Africans, in a skillful adaptation of their own national team's name, christened the Black Stars 'Baghana Baghana'. They were poised to make history by proceeding to the semi-finals were it not for the cruel intervention of the hands – not feet – of a Uruguayan striker! It was not the first time in their troubled history that Africans were cheated of honour and glory in such devious fashion. As it turned out, Uruguay produced the best – Diego Forlan, who combined footballing skill with impeccable character and deservedly won the Golden Ball – and the most dubious, the striker-turned-goalie Luis Suarez.
As Ghana was given the decisive penalty at the very end of extra time, the hearts and minds of millions of Africans were riveted on that ball and the striker, Asamoah Gyan. Poor Gyan! What a responsibility he had to shoulder on behalf of his continent, as the Black Stars found themselves on the threshold of history to become the first African team to make it to the semi-finals. That it was indeed a heavy responsibility was proven when the otherwise deadly striker hit the crossbar instead of the net, thereby joining a legion of superstars who missed crucial penalties – Baggio, Beckham, Shevechenko and Zhiko. With that miss, the hearts of millions of Africans also sank.
Many an African spectator felt that FIFA, if it wishes to lend any credence to its much-trumpeted 'fair play' motto, should make a ruling that such flagrant violations of the rule of the game be punished by allowing the goal rather than showing the culprit a meaningless red card and awarding the cheated side a penalty that could be missed. But it would be a miracle if the Neanderthal mindset of FIFA officialdom ever entertains such an option. Their stubborn refusal to allow any technological aids has resulted in the unprecedented sacking of four accredited referees before the end of the tournament.
The most dramatic flop was the second goal that England scored against Germany, which neither the referee nor his assistant could see, although it became evident on replay that the ball had gone some two feet over the line. Thus, by a bizarre twist of history, the Germans had their revenge on their English rivals, who had won the 1966 World Cup after they were awarded the first of their two winning goals by the referee, although the ball had actually hit the cross bar and bounced on the playing field. It is a fitting tribute to FIFA that, four and a half decades later, one
still has to continue to rely on human frailty.
The Black Stars' record is all the more remarkable when one contrasts it with the dismal performance of the star-studded Indomitable Lions (Cameroon), who proved anything but indomitable, the [White?] Elephants (Côte d'Ivoire), the Super Eagles (Nigeria), who, far from flying, remained rooted to the ground, or the Desert Foxes (Algeria), whose goal-hunting proved sterile. Alas! Didier Drogba's dream of being the first African to lift the trophy to the African sky died at its fetal stage. The only plausible excuse the Elephants could adduce was that they were assigned to the so-called Group of Death, having had to beat Brazil and Portugal were they to qualify for the second round. Even more bizarre was the reaction of the Nigerian ruler, Good luck Jonathan. He was so cross with the national team that he banned it from playing international football for two years. Only to rescind that rather hasty and ill-considered decision in the face of FIFA's threat of dire consequences – and just one hour before the expiry of the deadline!
Such impulsive reactions by governments are surely not going to remedy the poor performance of African national teams. Rather, one has to learn from the success stories and try to replicate them. In this respect, the Black Stars of Ghana and the Pharaohs of Egypt are good examples. What the two national teams represent, respectively, are youth and home-grown talent. The Ghanaian team that so resonated with the heartbeat of Africa was built around the Black Satellites, the young team that won the FIFA under- 20 World Cup in 2009, beating in the final none other than Brazil. Indeed, the Black Satellites were runner-up in 1993 and 2001. There is no better illustration of the dividends that investing in youth pays than the composition of the Spanish champions. The core of that team (in fact seven out of the starting eleven) were from one team, Barcelona, and a number of these had been nurtured by the Barcelona youth academy.
The Pharaohs were not at the 2010 World Cup because they were beaten by the Algerians in the qualifiers. But many people argued that they would have performed much better than many of the other African teams had they
made it to the finals. This is because they have demonstrated their worth in the African Cup of Nations, winning it for an unprecedented three times in succession. Many experts argue that the secret of their success lies in the cohesiveness of the team, largely because most of the players play in the national league; thus, not only do they know each other well but they have also developed a greater sense of national purpose.
Strange as this World Cup was, we are left with some pleasant and not so pleasant memories. Now that the great excitement is over, Africa has to take stock of its situation. South Africa has to try and translate the well-deserved accolade it has earned worldwide into social peace and equitable development. The other African countries have to try and answer the disturbing question of how is it that, fifty years after independence, Africa cannot go beyond the quarter-final stage of the World Cup. What is certain is that neither desperate last-minute import of foreign coaches nor impulsive retaliatory bans by governments are going to provide the solution.
Bahru ZEWDE
Volume 06 N° 02, Septembre 2010


text:
Casting News - Bailey Bass to Play Female Lead in 'Interview with the Vampire'; KJ Smith Joins 'Raising Kanan'; Plus 2 More Updates!
Casting News - October 5, 2021
Amazon's The Boys & Lightyears; AMC's Interview with the Vampire; and Starz's Power Book III: Raising Kanan.
AMC's INTERVIEW WITH THE VAMPIRE
Bailey Bass will play the female lead in the upcoming drama starring Sam Reid and Jacob Anderson. She will appear in two of the upcoming sequels in the Avatar film franchise.
She will play Claudia.
AMAZON's LIGHTYEARS
Lily Cardone and Lowrey Brown have landed recurring roles on the upcoming drama starring Sissy Spacek and J.K. Simmons. Cardone previously guest starred on Netflix's Bloodline. Brown has recurred on OWN's If Loving You Is Wrong and FOX's The Gifted.
Cardone will play Young Irene from ages 19-30, with Spacek playing the character in the present. She is surprised to find herself charmed by Franklin, but later throws herself into building their life as a family, her work as a teacher, and parenting their son Michael.
Brown will play Young Franklin from ages 21-30, with Simmons playing the character in the present. He is entranced by Irene from the first time he sees her, through their first years together as newlyweds and as young parents.
AMAZON's THE BOYS
Frances Turner, Kristin Booth and Jack Doolan have booked recurring roles on the drama's upcoming third season. Turner previously starred in the fourth season of Amazon's The Man in the High Castle. Booth has appeared in 12 films of Hallmark's Signed, Sealed, Delivered franchise. Doolan. Doolan starred for two seasons in ITV's Marcella.
Turner will play Monique, the wife of Mother's Milk (Laz Alonso).
Booth and Doolan will play siblings Tessa and Tommy, aka the TNT Twins.
STARZ's POWER BOOK III: RAISING KANAN
KJ Smith will appear in a recurring role on the drama's upcoming second season. She currently stars in BET's Sistas, which resumes its third season later this month.
She will play Palomar, one of Famous' (Antonio Ortiz) new neighbors. She is a young mom who's frequently mistaken for her daughter, Corinne's, older sister. Any responsible parent wouldn't let their daughter within a mile of Kanan (Mekai Curtis), Famous and their friends, but Palomar isn't that parent.
Labels: Casting News , Interview with the Vampire , Night Sky , Power: Raising Kanan , The Boys


text:
National Teams Men's National Team Doneil Henry
Doneil
Suwon Samsung Bluewings
North York, CAN
School(s)
Where they grew up
Brampton, CAN
TEAM HONOURS (3)
Canadian Championship winner
International "A" Level - CAN MNT
41 Appearances
36 Starts
1 Goals
Doneil Jor-dee Ashley Henry... speaks English... his father was born in Spanish Town, JAM while his mother was born in Old Harbour, JAM... he was born in North York, ON, but grew up in neighbouring Brampton, ON... he was 10 years old when he started playing soccer for the Brampton YSC (the Bullets)... grew up competing in soccer, basketball and volleyball... enjoys spending time with family and friends... growing up, favourites included Sergio Ramos, Arsenal...
three-time Canadian Championship winner with Toronto FC (2010, 2011, 2012)...
said Henry in 2015, "I just want to defend hard as I take pride in our clean sheets with the backline and the goalkeeper; if we can just do that, then it's a job completed"... said Carl Robinson in 2017, "Doneil is an athletic centre back who's strong in the air. He likes to defend and will be a good complementary piece to our core of defenders"...
20140528_goal_Henry20
SEE MORE Doneil Henry MEDIA
Individual Honours
Canadian U-20 Players of the Year
Male / Masculin
Queen's Diamond Jubilee Medal
Recipient / Récipiendaire
Match by Match
Select a level International "A" International "B" International "C"


I'm working.please be quiet.This mean: I'm working now, at the time of speaking.The action is not finished.
Where are the children? They're swimming …….
Are you enjoying the party? (not Do you enjoy) .
Tim is talking to a friend on the phone. He says:I'm learning english at the moment.Tim is not learning english at the time of speaking.He means that he has started it, and he continues .
Sara wants to work in Italy, so she's learning Italian.
John and Linda are building their own house. They hope to finish it next summer.
The company I work for isn't doing so well this year.
1) Are you feeling OK ? Yes , I'm fine,thank you.
2) Please don't make so much noise. I'm trying to work.
5)What's Paul doing? He's reading the newspaper.
6) I don't have anywhere to live. I'm looking for an apartment.
7) What are the children doing ? They're watching television .
8) Look, there's Susanne ! Where's she going?
9) We need to leave soon. It's getting late.


Melania Trump defends president, says author of op-ed against him is 'sabotaging' U.S.
WASHINGTON - First Lady Melania Trump on Thursday accused the author of an anonymous attack on her husband in the op-ed pages of The New York Times of "sabotaging" the country.
The article's author, who is known to the newspaper but described only as a senior administration official, portrayed President Donald Trump as "amoral" and reckless, and said a "quiet resistance" had formed among top aides to thwart his most misguided actions.
She added that anyone bold enough to make such accusations should "stand by their words" rather than hiding behind anonymity.


text:
Public, private sectors need to leverage synergy and think out-of-the-box, says PM Modi
January 17, 2023 - Updated 12:54 am IST
Modi meets economists seeking suggestions for the Budget
By BL New Delhi Bureau
Prime Minister Narendra Modi on Friday called for out-of-the-box thinking by both public and private sectors to mitigate the risk and seize the opportunities available globally.
Modi held an interaction with economists at NITI Aayog. The meeting, with a theme of 'India's growth & resilience amidst global headwinds', was called to get suggestions for the Budget. The Union Budget for FY24 is expected to be presented on February 1.
According to a statement issued after the meeting, the Prime Minister noted that while there were risks, the emerging global environment offers new and diverse opportunities in areas such as digitisation, energy, healthcare and agriculture. "To seize these opportunities, public and private sector need to leverage synergies and think out of the box," the statement quoted the Prime Minister
Digital story
Modi applauded the success of the India Digital story and the rapid adoption of fintech across the country, and the potential for inclusive growth and development it promises. He underscored Nari Shakti as a key driver of India's growth and urged to continue making efforts to further enable and boost women's participation in the workforce.
In the ongoing International Year of Millets, Prime Minister highlighted the need to promote millets in view of their potential to transform the rural and agri sector, with their attributes such as being carbon neutral, conducive to natural farming, and affordable source of nutrition.
Participants in the meeting offered practical measures on ways in which India could prudently sustain its development momentum. Ideas and suggestions were shared with the Prime Minister on diverse topics ranging from agriculture to manufacturing. Recognising that the underlying global headwinds are likely to continue, strategic recommendations were also shared for further strengthening India's resilience. There was agreement that due to its resilience, India has emerged as a bright spot on the turbulent global stage. It was suggested that new growth impulses would need to build upon this foundation through holistic development across all sectors.
The meeting was attended by Finance Minister Nirmala Sitharaman, Minister of State for Planning (I/C), Rao Inderjit Singh , Vice-Chairman of NITI Aayog, Suman Berry, and Principal Secretary to Prime Minister, P K Misra beside Members of NITI Aayog, Cabinet Secretary, Chief Economic Adviser and CEO, NITI Aayog.
Malfunctioning of system, human error could have caused Nepal plane crash: Experts
China's 60,000 Covid death toll spurs calls for more data
Stocks that will see action today — January 16, 2023
Nifty Prediction – January 16, 2023: Stay out of the market
Union Budget 2023: Here are the top 5 personal tax expectations
Watch: Nepal plane crash reportedly shot by U.P. passenger during Facebook Live
'People crying in Amazon India office, 75% of my team is gone': employee amid layoffs
Real Madrid 1-3 Barcelona, HIGHLIGHTS: Gavi scores one, assists two in Barca's Spanish Super Cup win


WG1300-B0 WLAN Module TI CC3000 IEEE 802.11b/g solution.
WG1300-B0 is a 2.4GHz WLAN module which can be integrated with any low-cost/low power MCU to makes it the ideal solution for embedded applications.
With the necessary PHY, MAC and network layers, it makes WG1300-B0 can support WLAN application via SPI bus to communicate with host microcontrollers or other embedded processors. With worldwide certificate (CID: WFA19445) on WiFi 802.11 WPA2, WPA, and WEP system Interoperability test, customers can leverage modular certificate by adopting the same antenna and RF trace routing to save development cost and speed up time to market by following its reference design.


24 Preciosa Crystal Beads Alexandrite Purple Bicones Faceted Czech Glass Bead 4mm. Quality Czech crystal glass beads from Preciosa with sixteen precisely cut alexandrite facets for some Czech glass sparkle.
Manufactured in the Czech Republic. Czech glass bead colour code 20210.


LONDON: In a first, researchers in Scotland are using smartphone-based technology to find the reason behind plummeting numbers of harbour seals in the country.
Part of a three-year study by researchers at Sea Mammal Research Unit (SMRU) at University of St Andrews in Scotland, the smartphone-based technology is being used to monitor the well-being of marine mammals, The National reported.
"Over the last 15 years, many of the harbour seal populations in the Northern Isles and on the north and east coasts of Scotland have been declining. Marine data collected during this project on Orkney will help to assess the causes, management and mitigation options in relation to the harbour seals' decline and to prioritise future research directions," Bernie McConnell, SMRU's deputy director, was quoted as saying.
For the study, marine telemetry tags will be attached to the fur at the back of the heads of a number of harbour seals in Orkney Archipelago of Scotland.
Small and light, the tags work like smartphones, sending information back to scientists and will eventually drop off when the seal moults, the report explained.
The study is being carried out at the request of the Scottish Government and Scottish Natural Heritage after concerns were raised about the survival of harbour seals in the country.
"This exciting, collaborative study is vital to help us to better understand the drivers of population change in Scottish harbour seals, and to evaluate the potential conservation and management options open to us," professor John Baxter from Scottish Natural Heritage was quoted as saying.
Harbour seals - one of two seal species in Britain - have declined by up to 90 percent in some areas in and around the north and east coast of Scotland, including Orkney, since 2000.


Is there any way to make an object that only lights up from a light. And Make a light thats different colors. If you can help thanks.
I'm not sure I understand your question... The only way to light an object is with lights, unless you're using radiosity, in which case you can just check the "exclude from radiosity" box under the object properties. If you want multiple colors on your light, you'll have to use a spotlight with an image map attached to its projection.
A simple question of texture luminosity and the right lighting or else one of excluding the object from the available light sources?


DETROIT, MI- This weekend volunteers with the American Strays research project spread out across Detroit's 139 square miles searching for the city's estimated 50,000 stray dog population.
Tom McPhee, filmmaker and Executive Director of the World Animal Awareness Society, based in Ann Arbor, believes the number is much lower in reality. He has helped organized a group of volunteer teams to participate in the inaugural American Strays Canine Survey & Treasure Hunt to count Detroit's strays.
"Detroit has problems we just think it's a bit much that Detroit is being jumped on with this idea whole that there are 50 thousand stray dogs and their biting and hurting and its a vicious situation," said McPhee. "In terms of actual, what we call actual, stray dogs, the number is demonstrably different than 50 thousand number that's being published."
"It's not about the numbers, it's about what we're finding in the streets when we do come across a dog," There are about 1,200 dog bites in the city each year according to McPhee but that number factors in bites from neighbor's dog and other close proximity incidents.
McPhee and the American Strays research project is trying to take a closer look on just how many stray dogs really are in Detroit.
Working with Michigan State University, a map was created breaking the city down into 42 sections. Inside each section 50 points were randomly placed. Volunteers travelled to each location and observed for 5 minutes for possible strays. Their data was documented using GPS technology and photographs as well as coding for descriptions of the dogs seen.
Data Driven Detroit and Massey University, out of New Zealand, will work on the data collected. McPhee estimated the research results should be released in a few weeks.
This weekend's research project was the inaugural count of what will be a legacy program documenting and studying the number of strays in Detroit.
As well as collecting data for the American Strays research project, McPhee, an award-winning producer & director, is working on a documentary in a series about stray dogs.
The feature documentary currently in pre-production will focus on a handful of dogs in the city of Detroit. It will be "an intimate portrait of their lives," said filmmaker McPhee.
The filming will begin this fall and on into the winter months documenting the struggles of these dogs.
The documentary will be the third chapter in a series of films about American strays. The first presented the problems surrounding strays dog and the second addressed the measures that can be taken to fix the issue.
"We think its very important to tell this intimate portrait of what these animals are going through because most of them have become outcasts and have been pushed out in society. And we want people to reconnect with these animals, that they're beings, that they can connect with them and they're going through all these things- not just the bad interactions with people," said McPhee.


The Amelia Bluegrass Festival is held twice each year in Amelia, Virginia.
The 2019 Amelia Bluegrass Festival is set for May 16-18 (Thursday thru Saturday).
Offers on-site camping, water and electric hook-ups, a camp store, coin laundry, nature trail, fish pond, and a variety of of other amenities.
Ticket options include single and 3-day packages.
Visit event website for schedule, contact info, ticket prices and more.