Exemple #1
0
PVR_ERROR CHTSPData::AddTimer(const PVR_TIMER &timer)
{
  XBMC->Log(LOG_DEBUG, "%s - channelUid=%d title=%s epgid=%d", __FUNCTION__, timer.iClientChannelUid, timer.strTitle, timer.iEpgUid);

  time_t startTime = timer.startTime;
  if (startTime <= 0)
  {
    int iGmtOffset;
    GetBackendTime(&startTime, &iGmtOffset);
  }

  htsmsg_t *msg = htsmsg_create_map();
  htsmsg_add_str(msg, "method",      "addDvrEntry");
  htsmsg_add_u32(msg, "eventId",     -1); // XXX tvheadend doesn't correct epg tags with wrong start and end times, so we'll use xbmc's values
  htsmsg_add_str(msg, "title",       timer.strTitle);
  htsmsg_add_u32(msg, "start",       startTime);
  htsmsg_add_u32(msg, "stop",        timer.endTime);
  htsmsg_add_u32(msg, "channelId",   timer.iClientChannelUid);
  htsmsg_add_u32(msg, "priority",    timer.iPriority);
  htsmsg_add_str(msg, "description", timer.strSummary);
  htsmsg_add_str(msg, "creator",     "XBMC");

  if ((msg = ReadResult(msg)) == NULL)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to get addDvrEntry", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  const char *strError = NULL;
  if ((strError = htsmsg_get_str(msg, "error")))
  {
    XBMC->Log(LOG_DEBUG, "%s - Error adding timer: '%s'", __FUNCTION__, strError);
    return PVR_ERROR_SERVER_ERROR;
  }

  unsigned int success;
  if (htsmsg_get_u32(msg, "success", &success) != 0)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
}
Exemple #2
0
bool cXVDRData::SetUpdateChannels(uint8_t method)
{
  cRequestPacket vrp;
  if (!vrp.init(XVDR_UPDATECHANNELS)) return false;
  if (!vrp.add_U8(method)) return false;

  cResponsePacket* vresp = ReadResult(&vrp);
  if (!vresp)
  {
    XBMC->Log(LOG_INFO, "Setting channel update method not supported by server. Consider updating the XVDR server.");
    return false;
  }

  XBMC->Log(LOG_INFO, "Channel update method set to %i", method);

  uint32_t ret = vresp->extract_U32();
  delete vresp;
  return ret == XVDR_RET_OK ? true : false;
}
Exemple #3
0
bool cVNSIChannelScan::ReadCountries()
{
  m_spinCountries = GUI->Control_getSpin(m_window, CONTROL_SPIN_COUNTRIES);
  m_spinCountries->Clear();

  CStdString dvdlang = XBMC->GetDVDMenuLanguage();
  dvdlang = dvdlang.ToUpper();

  cRequestPacket vrp;
  if (!vrp.init(VDR_SCAN_GETCOUNTRIES))
    return false;

  cResponsePacket* vresp = ReadResult(&vrp);
  if (!vresp)
    return false;

  int startIndex = -1;
  uint32_t retCode = vresp->extract_U32();
  if (retCode == VDR_RET_OK)
  {
    while (!vresp->end())
    {
      uint32_t    index     = vresp->extract_U32();
      const char *isoName   = vresp->extract_String();
      const char *longName  = vresp->extract_String();
      m_spinCountries->AddLabel(longName, index);
      if (dvdlang == isoName)
        startIndex = index;

      delete[] longName;
      delete[] isoName;
    }
    if (startIndex >= 0)
      m_spinCountries->SetValue(startIndex);
  }
  else
  {
    XBMC->Log(LOG_ERROR, "cVNSIChannelScan::ReadCountries() - Return error after reading countries (%i)", retCode);
  }
  delete vresp;
  return retCode == VDR_RET_OK;
}
 virtual ReadResult readObject(const std::string& uri, const Options* options) const
 {
     if ( "osgearth_engine_mp" == osgDB::getFileExtension( uri ) )
     {
         if ( "earth" == osgDB::getNameLessExtension( osgDB::getFileExtension( uri ) ) )
         {
             return readNode( uri, options );
         }
         else
         {
             MPTerrainEngineOptions terrainOpts;
             OE_INFO << LC << "Activated!" << std::endl;
             return ReadResult( new MPTerrainEngineNode() );
         }
     }
     else
     {
         return readNode( uri, options );
     }
 }    
Exemple #5
0
bool cXVDRData::SupportChannelScan()
{
  cRequestPacket vrp;
  if (!vrp.init(XVDR_SCAN_SUPPORTED))
  {
    XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
    return false;
  }

  cResponsePacket* vresp = ReadResult(&vrp);
  if (!vresp)
  {
    XBMC->Log(LOG_ERROR, "%s - Can't get response packet", __FUNCTION__);
    return false;
  }

  uint32_t ret = vresp->extract_U32();
  delete vresp;
  return ret == XVDR_RET_OK ? true : false;
}
Exemple #6
0
int cXVDRData::GetRecordingsCount()
{
  cRequestPacket vrp;
  if (!vrp.init(XVDR_RECORDINGS_GETCOUNT))
  {
    XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
    return -1;
  }

  cResponsePacket* vresp = ReadResult(&vrp);
  if (!vresp)
  {
    XBMC->Log(LOG_ERROR, "%s - Can't get response packet", __FUNCTION__);
    return -1;
  }

  uint32_t count = vresp->extract_U32();

  delete vresp;
  return count;
}
Exemple #7
0
void cVNSIChannelScan::StopScan()
{
  cRequestPacket vrp;
  if (!vrp.init(VDR_SCAN_STOP))
    return;

  cResponsePacket* vresp = ReadResult(&vrp);
  if (!vresp)
    return;

  uint32_t retCode = vresp->extract_U32();
  if (retCode != VDR_RET_OK)
  {
    XBMC->Log(LOG_ERROR, "cVNSIChannelScan::StopScan() - Return error after stop (%i)", retCode);
    m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(24071));
    m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
    m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
    m_stopped = true;
  }
  return;
}
Exemple #8
0
PVR_ERROR cHTSPData::DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
{
  XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);

  htsmsg_t *msg = htsmsg_create_map();
  htsmsg_add_str(msg, "method", "deleteDvrEntry");
  htsmsg_add_u32(msg, "id", timerinfo.index);
  if ((msg = ReadResult(msg)) == NULL)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to get deleteDvrEntry", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  unsigned int success;
  if (htsmsg_get_u32(msg, "success", &success) != 0)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
}
Exemple #9
0
PVR_ERROR CHTSPData::DeleteRecording(const PVR_RECORDING &recording)
{
  XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);

  htsmsg_t *msg = htsmsg_create_map();
  htsmsg_add_str(msg, "method", "deleteDvrEntry");
  htsmsg_add_u32(msg, "id", atoi(recording.strRecordingId));
  if ((msg = ReadResult(msg)) == NULL)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to get deleteDvrEntry", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  unsigned int success;
  if (htsmsg_get_u32(msg, "success", &success) != 0)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
}
Exemple #10
0
bool CHTSPData::GetDriveSpace(long long *total, long long *used)
{
  htsmsg_t *msg = htsmsg_create_map();
  htsmsg_add_str(msg, "method", "getDiskSpace");
  if ((msg = ReadResult(msg)) == NULL)
  {
    XBMC->Log(LOG_DEBUG, "%s - failed to get getDiskSpace", __FUNCTION__);
    return false;
  }

  int64_t freespace;
  if (htsmsg_get_s64(msg, "freediskspace", &freespace) != 0)
    return false;

  int64_t totalspace;
  if (htsmsg_get_s64(msg, "totaldiskspace", &totalspace) != 0)
    return false;

  *total = totalspace / 1024;
  *used  = (totalspace - freespace) / 1024;
  return true;
}
Exemple #11
0
PVR_ERROR cXVDRData::DeleteRecording(const PVR_RECORDING& recinfo)
{
  cRequestPacket vrp;
  if (!vrp.init(XVDR_RECORDINGS_DELETE))
  {
    XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
    return PVR_ERROR_UNKNOWN;
  }

  if (!vrp.add_String(recinfo.strRecordingId))
    return PVR_ERROR_UNKNOWN;

  cResponsePacket* vresp = ReadResult(&vrp);
  if (vresp == NULL || vresp->noResponse())
  {
    delete vresp;
    return PVR_ERROR_UNKNOWN;
  }

  uint32_t returnCode = vresp->extract_U32();
  delete vresp;

  switch(returnCode)
  {
    case XVDR_RET_DATALOCKED:
      return PVR_ERROR_NOT_DELETED;

    case XVDR_RET_RECRUNNING:
      return PVR_ERROR_RECORDING_RUNNING;

    case XVDR_RET_DATAINVALID:
      return PVR_ERROR_NOT_POSSIBLE;

    case XVDR_RET_ERROR:
      return PVR_ERROR_SERVER_ERROR;
  }

  return PVR_ERROR_NO_ERROR;
}
Exemple #12
0
PVR_ERROR cHTSPData::AddTimer(const PVR_TIMERINFO &timerinfo)
{
  XBMC->Log(LOG_DEBUG, "%s - channelNumber=%d channelUid=%d title=%s epgid=%d", __FUNCTION__, timerinfo.channelUid, timerinfo.channelUid, timerinfo.title, timerinfo.epgid);

  htsmsg_t *msg = htsmsg_create_map();
  htsmsg_add_str(msg, "method", "addDvrEntry");
  htsmsg_add_u32(msg, "eventId", timerinfo.epgid);
  htsmsg_add_str(msg, "title", timerinfo.title);
  htsmsg_add_u32(msg, "start", timerinfo.starttime);
  htsmsg_add_u32(msg, "stop", timerinfo.endtime);
  htsmsg_add_u32(msg, "channelId", timerinfo.channelUid);
  htsmsg_add_u32(msg, "priority", timerinfo.priority);
  htsmsg_add_str(msg, "description", timerinfo.description);
  htsmsg_add_str(msg, "creator", "XBMC");

  if ((msg = ReadResult(msg)) == NULL)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to get addDvrEntry", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  const char *strError = NULL;
  if ((strError = htsmsg_get_str(msg, "error")))
  {
    XBMC->Log(LOG_DEBUG, "%s - Error adding timer: '%s'", __FUNCTION__, strError);
    return PVR_ERROR_SERVER_ERROR;
  }

  unsigned int success;
  if (htsmsg_get_u32(msg, "success", &success) != 0)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
}
Exemple #13
0
PVR_ERROR cXVDRData::UpdateTimer(const PVR_TIMER &timerinfo)
{
  // use timer margin to calculate start/end times
  uint32_t starttime = timerinfo.startTime - timerinfo.iMarginStart*60;
  uint32_t endtime = timerinfo.endTime + timerinfo.iMarginEnd*60;

  cRequestPacket vrp;
  if (!vrp.init(XVDR_TIMER_UPDATE))        return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iClientIndex))      return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.state == PVR_TIMER_STATE_SCHEDULED))     return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iPriority))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iLifetime))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iClientChannelUid)) return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(starttime))  return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(endtime))    return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.bIsRepeating ? timerinfo.firstDay : 0))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iWeekdays))return PVR_ERROR_UNKNOWN;
  if (!vrp.add_String(timerinfo.strTitle))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_String(""))                return PVR_ERROR_UNKNOWN;

  cResponsePacket* vresp = ReadResult(&vrp);
  if (vresp == NULL || vresp->noResponse())
  {
    delete vresp;
    return PVR_ERROR_UNKNOWN;
  }
  uint32_t returnCode = vresp->extract_U32();
  delete vresp;
  if (returnCode == XVDR_RET_DATAUNKNOWN)
    return PVR_ERROR_NOT_POSSIBLE;
  else if (returnCode == XVDR_RET_DATAINVALID)
    return PVR_ERROR_NOT_SAVED;
  else if (returnCode == XVDR_RET_ERROR)
    return PVR_ERROR_SERVER_ERROR;

  return PVR_ERROR_NO_ERROR;
}
Exemple #14
0
PVR_ERROR CHTSPData::RenameRecording(const PVR_RECORDING &recording, const char *strNewName)
{
  XBMC->Log(LOG_DEBUG, "%s - id=%s", __FUNCTION__, recording.strRecordingId);

  htsmsg_t *msg = htsmsg_create_map();
  htsmsg_add_str(msg, "method", "updateDvrEntry");
  htsmsg_add_u32(msg, "id",     atoi(recording.strRecordingId));
  htsmsg_add_str(msg, "title",  recording.strTitle);
  
  if ((msg = ReadResult(msg)) == NULL)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to get updateDvrEntry", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  unsigned int success;
  if (htsmsg_get_u32(msg, "success", &success) != 0)
  {
    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
    return PVR_ERROR_SERVER_ERROR;
  }

  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_SAVED;
}
virtual ReadResult openArchive(const std::string &file, ArchiveStatus status, unsigned int indexBlockSize = 4096, const Options *options = NULL) const
{
    std::string ext = osgDB::getLowerCaseFileExtension(file);

    if (!acceptsExtension(ext))
        return ReadResult::FILE_NOT_HANDLED;

    std::string fileName = osgDB::findDataFile(file, options);
    if (fileName.empty())
    {
        if (status == READ)
            return ReadResult::FILE_NOT_FOUND;

        fileName = file;
    }

    osg::ref_ptr<OSGA_Archive> archive = new OSGA_Archive;
    if (!archive->open(fileName, status, indexBlockSize))
    {
        return ReadResult(ReadResult::FILE_NOT_HANDLED);
    }

    return archive.get();
}
    virtual ReadResult readObject(std::istream& fin, const osgDB::ReaderWriter::Options* options) const
    {
        osgDB::Input fr;
        fr.attach(&fin);
        fr.setOptions(options);

        typedef std::vector< osg::ref_ptr<osgViewer::View> > ViewList;
        ViewList viewList;

        // load all nodes in file, placing them in a group.
        while(!fr.eof())
        {
            osg::ref_ptr<osg::Object> object = fr.readObject();
            osgViewer::View* view = dynamic_cast<osgViewer::View*>(object.get());
            if (view)
            {
                viewList.push_back(view);
            }
            else fr.advanceOverCurrentFieldOrBlock();
        }

        if  (viewList.empty())
        {
            return ReadResult("No data loaded");
        }
        else if (viewList.size()==1)
        {
            return viewList.front().get();
        }
        else
        {
            OSG_NOTICE<<"Found multiple view's, just taking first"<<std::endl;
            return viewList.front().get();
        }

    }
Exemple #17
0
void cVNSIChannelScan::StartScan()
{
  m_header = XBMC->GetLocalizedString(30025);
  m_Signal = XBMC->GetLocalizedString(30029);
  SetProgress(0);
  SetSignal(0, false);

  int source = m_spinSourceType->GetValue();
  switch (source)
  {
    case DVB_TERR:
      m_window->SetControlLabel(LABEL_TYPE, "DVB-T");
      break;
    case DVB_CABLE:
      m_window->SetControlLabel(LABEL_TYPE, "DVB-C");
      break;
    case DVB_SAT:
      m_window->SetControlLabel(LABEL_TYPE, "DVB-S/S2");
      break;
    case PVRINPUT:
      m_window->SetControlLabel(LABEL_TYPE, XBMC->GetLocalizedString(30032));
      break;
    case PVRINPUT_FM:
      m_window->SetControlLabel(LABEL_TYPE, XBMC->GetLocalizedString(30033));
      break;
    case DVB_ATSC:
      m_window->SetControlLabel(LABEL_TYPE, "ATSC");
      break;
  }

  cRequestPacket vrp;
  cResponsePacket* vresp = NULL;
  uint32_t retCode = VDR_RET_ERROR;
  if (!vrp.init(VDR_SCAN_START))                          goto SCANError;
  if (!vrp.add_U32(source))                               goto SCANError;
  if (!vrp.add_U8(m_radioButtonTV->IsSelected()))         goto SCANError;
  if (!vrp.add_U8(m_radioButtonRadio->IsSelected()))      goto SCANError;
  if (!vrp.add_U8(m_radioButtonFTA->IsSelected()))        goto SCANError;
  if (!vrp.add_U8(m_radioButtonScrambled->IsSelected()))  goto SCANError;
  if (!vrp.add_U8(m_radioButtonHD->IsSelected()))         goto SCANError;
  if (!vrp.add_U32(m_spinCountries->GetValue()))          goto SCANError;
  if (!vrp.add_U32(m_spinDVBCInversion->GetValue()))      goto SCANError;
  if (!vrp.add_U32(m_spinDVBCSymbolrates->GetValue()))    goto SCANError;
  if (!vrp.add_U32(m_spinDVBCqam->GetValue()))            goto SCANError;
  if (!vrp.add_U32(m_spinDVBTInversion->GetValue()))      goto SCANError;
  if (!vrp.add_U32(m_spinSatellites->GetValue()))         goto SCANError;
  if (!vrp.add_U32(m_spinATSCType->GetValue()))           goto SCANError;

  vresp = ReadResult(&vrp);
  if (!vresp)
    goto SCANError;

  retCode = vresp->extract_U32();
  if (retCode != VDR_RET_OK)
    goto SCANError;

  return;

SCANError:
  XBMC->Log(LOG_ERROR, "cVNSIChannelScan::StartScan() - Return error after start (%i)", retCode);
  m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(24071));
  m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
  m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
  m_stopped = true;
}
Exemple #18
0
PVR_ERROR cXVDRData::AddTimer(const PVR_TIMER &timerinfo)
{
  cRequestPacket vrp;
  if (!vrp.init(XVDR_TIMER_ADD))
  {
    XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
    return PVR_ERROR_UNKNOWN;
  }

  // add directory in front of the title
  std::string path;
  if(timerinfo.strDirectory != NULL && strlen(timerinfo.strDirectory) > 0) {
    path += timerinfo.strDirectory;
    if(path == "/") {
      path.clear();
    }
    else if(path.size() > 1) {
      if(path[0] == '/') {
        path = path.substr(1);
      }
    }

    if(path.size() > 0 && path[path.size()-1] != '/') {
      path += "/";
    }
  }

  if(timerinfo.strTitle != NULL) {
    path += timerinfo.strTitle;
  }

  // replace directory separators
  for(std::size_t i=0; i<path.size(); i++) {
    if(path[i] == '/' || path[i] == '\\') {
      path[i] = '~';
    }
  }

  if(path.empty()) {
    XBMC->Log(LOG_ERROR, "%s - Empty filename !", __FUNCTION__);
    return PVR_ERROR_UNKNOWN;
  }

  // use timer margin to calculate start/end times
  uint32_t starttime = timerinfo.startTime - timerinfo.iMarginStart*60;
  uint32_t endtime = timerinfo.endTime + timerinfo.iMarginEnd*60;

  if (!vrp.add_U32(timerinfo.state == PVR_TIMER_STATE_SCHEDULED))     return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iPriority))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iLifetime))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iClientChannelUid)) return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(starttime))  return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(endtime))    return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.bIsRepeating ? timerinfo.firstDay : 0))   return PVR_ERROR_UNKNOWN;
  if (!vrp.add_U32(timerinfo.iWeekdays))return PVR_ERROR_UNKNOWN;
  if (!vrp.add_String(path.c_str()))      return PVR_ERROR_UNKNOWN;
  if (!vrp.add_String(""))                return PVR_ERROR_UNKNOWN;

  cResponsePacket* vresp = ReadResult(&vrp);
  if (vresp == NULL || vresp->noResponse())
  {
    delete vresp;
    XBMC->Log(LOG_ERROR, "%s - Can't get response packet", __FUNCTION__);
    return PVR_ERROR_UNKNOWN;
  }
  uint32_t returnCode = vresp->extract_U32();
  delete vresp;
  if (returnCode == XVDR_RET_DATALOCKED)
    return PVR_ERROR_ALREADY_PRESENT;
  else if (returnCode == XVDR_RET_DATAINVALID)
    return PVR_ERROR_NOT_SAVED;
  else if (returnCode == XVDR_RET_ERROR)
    return PVR_ERROR_SERVER_ERROR;

  return PVR_ERROR_NO_ERROR;
}
 virtual ReadResult openArchive(const std::string& fileName,ArchiveStatus status, unsigned int , const Options* options) const
 {
     if (status!=READ) return ReadResult(ReadResult::FILE_NOT_HANDLED);
     else return readFile(ARCHIVE,fileName,options);
 }
        virtual ReadResult readNode(const std::string& uri, const Options* options) const
        {
            std::string ext = osgDB::getFileExtension(uri);
            if ( acceptsExtension(ext) )
            {
                // See if the filename starts with server: and strip it off.  This will trick OSG
                // into passing in the filename to our plugin instead of using the CURL plugin if
                // the filename contains a URL.  So, if you want to read a URL, you can use the
                // following format: osgDB::readNodeFile("server:http://myserver/myearth.earth").
                // This should only be necessary for the first level as the other files will have
                // a tilekey prepended to them.
                if ((uri.length() > 7) && (uri.substr(0, 7) == "server:"))
                    return readNode(uri.substr(7), options);

                // parse the tile key and engine ID:
                std::string tileDef = osgDB::getNameLessExtension(uri);
                unsigned int lod, x, y, engineID;
                sscanf(tileDef.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &engineID);

                // find the appropriate engine:
                osg::ref_ptr<MPTerrainEngineNode> engineNode;
                MPTerrainEngineNode::getEngineByUID( (UID)engineID, engineNode );
                if ( engineNode.valid() )
                {
                    OE_START_TIMER(tileLoadTime);

                    // see if we have a progress tracker
                    ProgressCallback* progress = 
                        options ? const_cast<ProgressCallback*>(
                        dynamic_cast<const ProgressCallback*>(options->getUserData())) : 0L;

                    // must have a ProgressCallback if we're profiling.
                    bool ownProgress = (progress == 0L);
                    if ( !progress && _profiling )
                        progress = new ProgressCallback();

                    // assemble the key and create the node:
                    const Profile* profile = engineNode->getMap()->getProfile();
                    TileKey key( lod, x, y, profile );

                    osg::ref_ptr<osg::Node> node;

                    if ( "osgearth_engine_mp_tile" == ext )
                    {
                        node = engineNode->createNode(key, progress);
                    }
                    else if ( "osgearth_engine_mp_standalone_tile" == ext )
                    {
                        node = engineNode->createStandaloneNode(key, progress);
                    }

                    double tileLoadTime = OE_STOP_TIMER(tileLoadTime);
                    if ( progress )
                        progress->stats()["tile_load_time"] = tileLoadTime;

                    if ( _profiling )
                    {
                        OE_NOTICE << "tile: " << tileDef << std::endl;
                        for(std::map<std::string,double>::iterator i = progress->stats().begin();
                            i != progress->stats().end();
                            ++i)
                        {
                            OE_NOTICE << "   " << i->first << " = " << std::setprecision(4) 
                                << i->second << " ("
                                << (int)((i->second/tileLoadTime)*100)
                                << "%)" 
                                << std::endl;
                        }

                        if ( ownProgress )
                        {
                            delete progress;
                            progress = 0L;
                        }
                    }

                    // Deal with failed loads.
                    if ( !node.valid() )
                    {
                        if ( key.getLOD() == 0 || (progress && progress->isCanceled()) )
                        {
                            // the tile will ask again next time.
                            return ReadResult::FILE_NOT_FOUND;
                        }
                        else
                        {
                            // the parent tile will never ask again as long as it remains in memory.
                            node = new InvalidTileNode( key );
                        }
                    }
                    else
                    {   
                        // notify the Terrain interface of a new tile
                        osg::Timer_t start = osg::Timer::instance()->tick();
                        engineNode->getTerrain()->notifyTileAdded(key, node.get());
                        osg::Timer_t end = osg::Timer::instance()->tick();
                    }

                    return ReadResult( node.get(), ReadResult::FILE_LOADED );
                }
                else
                {
                    return ReadResult::FILE_NOT_FOUND;
                }
            }
            else
            {
                return ReadResult::FILE_NOT_HANDLED;
            }
        }
        virtual ReadResult readNode(const std::string& uri, const Options* options) const
        {
            std::string ext = osgDB::getFileExtension(uri);
            if ( acceptsExtension(ext) )
            {
                // See if the filename starts with server: and strip it off.  This will trick OSG
                // into passing in the filename to our plugin instead of using the CURL plugin if
                // the filename contains a URL.  So, if you want to read a URL, you can use the
                // following format: osgDB::readNodeFile("server:http://myserver/myearth.earth").
                // This should only be necessary for the first level as the other files will have
                // a tilekey prepended to them.
                if ((uri.length() > 7) && (uri.substr(0, 7) == "server:"))
                    return readNode(uri.substr(7), options);

                // parse the tile key and engine ID:
                std::string tileDef = osgDB::getNameLessExtension(uri);
                unsigned int lod, x, y, engineID;
                sscanf(tileDef.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &engineID);

                // find the appropriate engine:
                osg::ref_ptr<MPTerrainEngineNode> engineNode;
                MPTerrainEngineNode::getEngineByUID( (UID)engineID, engineNode );
                if ( engineNode.valid() )
                {
                    Registry::instance()->startActivity(uri);

                    OE_START_TIMER(tileLoadTime);

                    // see if we have a progress tracker
                    ProgressCallback* progress = 
                        options ? const_cast<ProgressCallback*>(
                        dynamic_cast<const ProgressCallback*>(options->getUserData())) : 0L;

                    // must have a ProgressCallback if we're profiling.
                    bool ownProgress = (progress == 0L);
                    if ( !progress && _profiling )
                        progress = new ProgressCallback();

                    // assemble the key and create the node:
                    const Profile* profile = engineNode->getMap()->getProfile();
                    TileKey key( lod, x, y, profile );

                    osg::ref_ptr<osg::Node> node;

                    if ( "osgearth_engine_mp_tile" == ext )
                    {
                        node = engineNode->createNode(key, progress);
                    }
                    else if ( "osgearth_engine_mp_standalone_tile" == ext )
                    {
                        node = engineNode->createStandaloneNode(key, progress);
                    }

                    double tileLoadTime = OE_STOP_TIMER(tileLoadTime);
                    if ( progress )
                        progress->stats()["tile_load_time"] = tileLoadTime;

                    // profiling level 1 = detailed stats about individual loads.
                    if ( _profiling == 1 )
                    {
                        progress->stats()["http_get_time_avg"] =
                            progress->stats()["http_get_time"] / progress->stats()["http_get_count"];

                        OE_NOTICE << "tile: " << tileDef << std::endl;
                        for(ProgressCallback::Stats::iterator i = progress->stats().begin();
                            i != progress->stats().end();
                            ++i)
                        {
                            std::stringstream buf;
                            if ( osgEarth::endsWith(i->first, "_time") )
                            {
                                buf 
                                    << i->first << " = " << std::setprecision(4) << (i->second*1000.0) << "ms ("
                                    << (int)((i->second/tileLoadTime)*100) << "%)";
                            }
                            else
                            {
                                buf << i->first << " = " << std::setprecision(4) << i->second;
                            }
                            OE_NOTICE << "   " << buf.str() << std::endl;
                        }

                        if ( ownProgress )
                        {
                            delete progress;
                            progress = 0L;
                        }
                    }

                    // profiling level 2 = running 60-sample averages
                    else if ( _profiling == 2 )
                    {
                        static std::deque<double> tileLoadTimes[3];
                        static int    samples[3]       = { 64, 256, 1024 };
                        static double runningTotals[3] = { 0.0, 0.0, 0.0 };
                        static Threading::Mutex averageMutex;

                        averageMutex.lock();
                        for(int i=0; i<3; ++i)
                        {
                            runningTotals[i] += tileLoadTime;
                            tileLoadTimes[i].push_back( tileLoadTime );
                            if ( tileLoadTimes[i].size() > samples[i] )
                            {
                                runningTotals[i] -= tileLoadTimes[i].front();
                                tileLoadTimes[i].pop_front();
                            }
                        }
                        OE_NOTICE << "(samples)time : "
                            << "(" << samples[0] << "): " << (tileLoadTimes[0].size() == samples[0] ? (runningTotals[0]/(double)samples[0])*1000.0 : -1.0) << "ms; "
                            << "(" << samples[1] << "): " << (tileLoadTimes[1].size() == samples[1] ? (runningTotals[1]/(double)samples[1])*1000.0 : -1.0) << "ms; "
                            << "(" << samples[2] << "): " << (tileLoadTimes[2].size() == samples[2] ? (runningTotals[2]/(double)samples[2])*1000.0 : -1.0) << "ms; "
                            << std::endl;

                        averageMutex.unlock();
                    }
                    
                    Registry::instance()->endActivity(uri);

                    // Deal with failed loads.
                    if ( !node.valid() )
                    {
                        if ( key.getLOD() == 0  )
                        {
                            // the tile will ask again next time.
                            return ReadResult::FILE_NOT_FOUND;
                        }
                        else if (progress && progress->isCanceled())
                        {
                            if ( _profiling )
                            {
                                OE_NOTICE << LC << "Tile " << key.str() << " -- canceled!" << std::endl;
                            }
                            return ReadResult::FILE_NOT_FOUND;
                        }
                        else
                        {
                            // the parent tile will never ask again as long as it remains in memory.
                            node = new InvalidTileNode( key );
                        }
                    }
                    else
                    {   
                        // notify the Terrain interface of a new tile
                        // moved this to TileNodeRegistry::add
                        //osg::Timer_t start = osg::Timer::instance()->tick();
                        //engineNode->getTerrain()->notifyTileAdded(key, node.get());
                        //osg::Timer_t end = osg::Timer::instance()->tick();
                    }
                    
                    return ReadResult( node.get(), ReadResult::FILE_LOADED );
                }
                else
                {
                    return ReadResult::FILE_NOT_FOUND;
                }
            }
            else
            {
                return ReadResult::FILE_NOT_HANDLED;
            }
        }