Пример #1
0
void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemList &items, bool reposAsFolders)
{
  CStdString xbmcPath = CSpecialProtocol::TranslatePath("special://xbmc/addons");
  items.ClearItems();
  for (unsigned i=0; i < addons.size(); i++)
  {
    AddonPtr addon = addons[i];
    CFileItemPtr pItem;
    if (reposAsFolders && addon->Type() == ADDON_REPOSITORY)
      pItem = FileItemFromAddon(addon, "addons://", true);
    else
      pItem = FileItemFromAddon(addon, path.Get(), false);
    AddonPtr addon2;
    if (CAddonMgr::Get().GetAddon(addon->ID(),addon2))
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(305));
    else if ((addon->Type() == ADDON_PVRDLL) && (CStdString(pItem->GetProperty("Addon.Path").asString()).Left(xbmcPath.size()).Equals(xbmcPath)))
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24023));

    if (!addon->Props().broken.IsEmpty())
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24098));
    if (addon2 && addon2->Version() < addon->Version())
    {
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24068));
      pItem->SetProperty("Addon.UpdateAvail", true);
    }
    CAddonDatabase::SetPropertiesFromAddon(addon,pItem);
    items.Add(pItem);
  }
}
Пример #2
0
bool CAddonDatabase::SearchTitle(const CStdString& search, VECADDONS& addons)
{
  try
  {
    if (NULL == m_pDB.get()) return false;
    if (NULL == m_pDS.get()) return false;

    CStdString strSQL;
    strSQL=PrepareSQL("select idAddon from addon where name like '%s%%'", search.c_str());

    if (!m_pDS->query(strSQL.c_str())) return false;
    if (m_pDS->num_rows() == 0) return false;

    while (!m_pDS->eof())
    {
      AddonPtr addon;
      GetAddon(m_pDS->fv(0).get_asInt(),addon);
      if (addon->Type() >= ADDON_UNKNOWN+1 && addon->Type() < ADDON_SCRAPER_LIBRARY)
        addons.push_back(addon);
      m_pDS->next();
    }
    m_pDS->close();
    return true;
  }
  catch (...)
  {
    CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
  }
  return false;
}
Пример #3
0
bool CAddonDatabase::Search(const std::string& search, VECADDONS& addons)
{
  try
  {
    if (NULL == m_pDB.get()) return false;
    if (NULL == m_pDS.get()) return false;

    std::string strSQL;
    strSQL=PrepareSQL("SELECT addonID FROM addon WHERE name LIKE '%%%s%%' OR summary LIKE '%%%s%%' OR description LIKE '%%%s%%'", search.c_str(), search.c_str(), search.c_str());
    CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());

    if (!m_pDS->query(strSQL.c_str())) return false;
    if (m_pDS->num_rows() == 0) return false;

    while (!m_pDS->eof())
    {
      AddonPtr addon;
      GetAddon(m_pDS->fv(0).get_asString(),addon);
      if (addon->Type() >= ADDON_UNKNOWN+1 && addon->Type() < ADDON_SCRAPER_LIBRARY)
        addons.push_back(addon);
      m_pDS->next();
    }
    m_pDS->close();
    return true;
  }
  catch (...)
  {
    CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
  }
  return false;
}
Пример #4
0
CFileItemPtr CAddonsDirectory::FileItemFromAddon(AddonPtr &addon, const CStdString &basePath, bool folder)
{
  if (!addon)
    return CFileItemPtr();

  // TODO: This can probably be done more efficiently
  CURL url(basePath);
  url.SetFileName(addon->ID());
  CStdString path(url.Get());
  if (folder)
    URIUtils::AddSlashAtEnd(path);

  CFileItemPtr item(new CFileItem(path, folder));

  CStdString strLabel(addon->Name());
  if (url.GetHostName().Equals("search"))
    strLabel.Format("%s - %s", TranslateType(addon->Type(), true), addon->Name());

  item->SetLabel(strLabel);

  if (!(basePath.Equals("addons://") && addon->Type() == ADDON_REPOSITORY))
    item->SetLabel2(addon->Version().c_str());
  item->SetArt("thumb", addon->Icon());
  item->SetLabelPreformated(true);
  item->SetIconImage("DefaultAddon.png");
  if (!addon->FanArt().IsEmpty() && 
      (URIUtils::IsInternetStream(addon->FanArt()) || 
       CFile::Exists(addon->FanArt())))
  {
    item->SetArt("fanart", addon->FanArt());
  }
  CAddonDatabase::SetPropertiesFromAddon(addon, item);
  return item;
}
Пример #5
0
void CAddonDatabase::SetPropertiesFromAddon(const AddonPtr& addon,
                                           CFileItemPtr& pItem)
{
  pItem->SetProperty("Addon.ID", addon->ID());
  pItem->SetProperty("Addon.Type", TranslateType(addon->Type(),true));
  pItem->SetProperty("Addon.intType", TranslateType(addon->Type()));
  pItem->SetProperty("Addon.Name", addon->Name());
  pItem->SetProperty("Addon.Version", addon->Version().asString());
  pItem->SetProperty("Addon.Summary", addon->Summary());
  pItem->SetProperty("Addon.Description", addon->Description());
  pItem->SetProperty("Addon.Creator", addon->Author());
  pItem->SetProperty("Addon.Disclaimer", addon->Disclaimer());
  pItem->SetProperty("Addon.Rating", addon->Stars());
  std::string starrating = StringUtils::Format("rating%d.png", addon->Stars());
  pItem->SetProperty("Addon.StarRating",starrating);
  pItem->SetProperty("Addon.Path", addon->Path());
  if (addon->Props().broken == "DEPSNOTMET")
    pItem->SetProperty("Addon.Broken", g_localizeStrings.Get(24044));
  else
    pItem->SetProperty("Addon.Broken", addon->Props().broken);
  std::map<std::string,std::string>::iterator it = 
                    addon->Props().extrainfo.find("language");
  if (it != addon->Props().extrainfo.end())
    pItem->SetProperty("Addon.Language", it->second);
}
Пример #6
0
int CAddonDatabase::AddAddon(const AddonPtr& addon,
                             int idRepo)
{
  try
  {
    if (NULL == m_pDB.get()) return -1;
    if (NULL == m_pDS.get()) return -1;

    bool bDisablePVRAddon = addon->Type() == ADDON_PVRDLL && !HasAddon(addon->ID());

    CStdString sql = PrepareSQL("insert into addon (id, type, name, summary,"
                               "description, stars, path, icon, changelog, "
                               "fanart, addonID, version, author, disclaimer, minversion)"
                               " values(NULL, '%s', '%s', '%s', '%s', %i,"
                               "'%s', '%s', '%s', '%s', '%s','%s','%s','%s','%s')",
                               TranslateType(addon->Type(),false).c_str(),
                               addon->Name().c_str(), addon->Summary().c_str(),
                               addon->Description().c_str(),addon->Stars(),
                               addon->Path().c_str(), addon->Props().icon.c_str(),
                               addon->ChangeLog().c_str(),addon->FanArt().c_str(),
                               addon->ID().c_str(), addon->Version().c_str(),
                               addon->Author().c_str(),addon->Disclaimer().c_str(),
                               addon->MinVersion().c_str());
    m_pDS->exec(sql.c_str());
    int idAddon = (int)m_pDS->lastinsertid();

    sql = PrepareSQL("insert into addonlinkrepo (idRepo, idAddon) values (%i,%i)",idRepo,idAddon);
    m_pDS->exec(sql.c_str());

    const InfoMap &info = addon->ExtraInfo();
    for (InfoMap::const_iterator i = info.begin(); i != info.end(); ++i)
    {
      sql = PrepareSQL("insert into addonextra(id, key, value) values (%i, '%s', '%s')", idAddon, i->first.c_str(), i->second.c_str());
      m_pDS->exec(sql.c_str());
    }
    const ADDONDEPS &deps = addon->GetDeps();
    for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i)
    {
      sql = PrepareSQL("insert into dependencies(id, addon, version, optional) values (%i, '%s', '%s', %i)", idAddon, i->first.c_str(), i->second.first.c_str(), i->second.second ? 1 : 0);
      m_pDS->exec(sql.c_str());
    }
    // these need to be configured
    if (bDisablePVRAddon)
      DisableAddon(addon->ID(), true);
    return idAddon;
  }
  catch (...)
  {
    CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addon->Name().c_str());
  }
  return -1;
}
Пример #7
0
JSONRPC_STATUS CAddonsOperations::GetAddonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
{
  std::string id = parameterObject["addonid"].asString();
  AddonPtr addon;
  if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, ADDON::ADDON_UNKNOWN, false) || addon.get() == NULL ||
      addon->Type() <= ADDON_UNKNOWN || addon->Type() >= ADDON_MAX)
    return InvalidParams;

  CAddonDatabase addondb;
  FillDetails(addon, parameterObject["properties"], result["addon"], addondb);

  return OK;
}
Пример #8
0
bool CAddonMgr::GetAddon(const CStdString &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/)
{
  CSingleLock lock(m_critSection);

  cp_status_t status;
  cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status);
  if (status == CP_OK && cpaddon)
  {
    addon = GetAddonFromDescriptor(cpaddon, type==ADDON_UNKNOWN?"":TranslateType(type));
    m_cpluff->release_info(m_cp_context, cpaddon);

    if (addon && addon.get())
    {
      if (enabledOnly && m_database.IsAddonDisabled(addon->ID()))
        return false;

      if (addon->Type() == ADDON_PVRDLL && g_PVRManager.IsStarted())
      {
        AddonPtr pvrAddon;
        if (g_PVRClients->GetClient(addon->ID(), pvrAddon))
          addon = pvrAddon;
      }
    }
    return NULL != addon.get();
  }
  if (cpaddon)
    m_cpluff->release_info(m_cp_context, cpaddon);

  return false;
}
Пример #9
0
bool CAddonMgr::CanAddonBeDisabled(const std::string& ID)
{
  if (ID.empty())
    return false;

  CSingleLock lock(m_critSection);
  // can't disable an already disabled addon
  if (IsAddonDisabled(ID))
    return false;

  AddonPtr localAddon;
  // can't disable an addon that isn't installed
  if (!IsAddonInstalled(ID, localAddon))
    return false;

  // can't disable an addon that is in use
  if (localAddon->IsInUse())
    return false;

  // installed PVR addons can always be disabled
  if (localAddon->Type() == ADDON_PVRDLL)
    return true;

  std::string systemAddonsPath = CSpecialProtocol::TranslatePath("special://xbmc/addons");
  // can't disable system addons
  if (StringUtils::StartsWith(localAddon->Path(), systemAddonsPath))
    return false;

  return true;
}
Пример #10
0
void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemList &items, bool reposAsFolders)
{
  items.ClearItems();
  for (unsigned i=0; i < addons.size(); i++)
  {
    AddonPtr addon = addons[i];
    CFileItemPtr pItem;
    if (reposAsFolders && addon->Type() == ADDON_REPOSITORY)
      pItem = FileItemFromAddon(addon, "addons://", true);
    else
      pItem = FileItemFromAddon(addon, path.Get(), false);
    AddonPtr addon2;
    if (CAddonMgr::Get().GetAddon(addon->ID(),addon2))
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(305));
    else if (CAddonMgr::Get().IsAddonDisabled(addon->ID()))
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24023));

    if (addon->Props().broken == "DEPSNOTMET")
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24049));
    else if (!addon->Props().broken.empty())
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24098));
    if (addon2 && addon2->Version() < addon->Version())
    {
      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24068));
      pItem->SetProperty("Addon.UpdateAvail", true);
    }
    CAddonDatabase::SetPropertiesFromAddon(addon,pItem);
    items.Add(pItem);
  }
}
Пример #11
0
bool CAddonMgr::GetAddon(const CStdString &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/)
{
  CSingleLock lock(m_critSection);

  CStdString xbmcPath = _P("special://xbmc/addons");
  cp_status_t status;
  cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status);
  if (status == CP_OK && cpaddon)
  {
    addon = GetAddonFromDescriptor(cpaddon);
    m_cpluff->release_info(m_cp_context, cpaddon);

    if (addon && addon.get() && enabledOnly)
    {
      if (addon->Type() == ADDON_PVRDLL && addon->Path().Left(xbmcPath.size()).Equals(xbmcPath))
      {
        if (!m_database.IsSystemPVRAddonEnabled(addon->ID()))
          return false;
      }
      else if (m_database.IsAddonDisabled(addon->ID()))
        return false;
    }
    return NULL != addon.get();
  }
  if (cpaddon)
    m_cpluff->release_info(m_cp_context, cpaddon);

  return false;
}
Пример #12
0
JSONRPC_STATUS CAddonsOperations::ExecuteAddon(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
{
  std::string id = parameterObject["addonid"].asString();
  AddonPtr addon;
  if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon) || addon.get() == NULL ||
      addon->Type() < ADDON_VIZ || addon->Type() >= ADDON_MAX)
    return InvalidParams;

  std::string argv;
  CVariant params = parameterObject["params"];
  if (params.isObject())
  {
    for (CVariant::const_iterator_map it = params.begin_map(); it != params.end_map(); it++)
    {
      if (it != params.begin_map())
        argv += ",";
      argv += it->first + "=" + it->second.asString();
    }
  }
  else if (params.isArray())
  {
    for (CVariant::const_iterator_array it = params.begin_array(); it != params.end_array(); it++)
    {
      if (it != params.begin_array())
        argv += ",";
      argv += StringUtils::Paramify(it->asString());
    }
  }
  else if (params.isString())
  {
    if (!params.empty())
      argv = StringUtils::Paramify(params.asString());
  }

  std::string cmd;
  if (params.empty())
    cmd = StringUtils::Format("RunAddon(%s)", id.c_str());
  else
    cmd = StringUtils::Format("RunAddon(%s, %s)", id.c_str(), argv.c_str());

  if (params["wait"].asBoolean())
    CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
  else
    CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);

  return ACK;
}
Пример #13
0
void CAddonDatabase::SetPropertiesFromAddon(const AddonPtr& addon,
                                           CFileItemPtr& pItem)
{
  pItem->SetProperty("Addon.ID", addon->ID());
  pItem->SetProperty("Addon.Type", TranslateType(addon->Type(),true));
  pItem->SetProperty("Addon.intType", TranslateType(addon->Type()));
  pItem->SetProperty("Addon.Name", addon->Name());
  pItem->SetProperty("Addon.Version", addon->Version().c_str());
  pItem->SetProperty("Addon.Summary", addon->Summary());
  pItem->SetProperty("Addon.Description", addon->Description());
  pItem->SetProperty("Addon.Creator", addon->Author());
  pItem->SetProperty("Addon.Disclaimer", addon->Disclaimer());
  pItem->SetProperty("Addon.Rating", addon->Stars());
  CStdString starrating;
  starrating.Format("rating%d.png", addon->Stars());
  pItem->SetProperty("Addon.StarRating",starrating);
  pItem->SetProperty("Addon.Path", addon->Path());
  pItem->SetProperty("Addon.Broken", addon->Props().broken);
}
Пример #14
0
JSONRPC_STATUS CAddonsOperations::SetAddonEnabled(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
{
  std::string id = parameterObject["addonid"].asString();
  AddonPtr addon;
  if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, ADDON::ADDON_UNKNOWN, false) || addon == nullptr ||
    addon->Type() <= ADDON_UNKNOWN || addon->Type() >= ADDON_MAX)
    return InvalidParams;

  bool disabled = false;
  if (parameterObject["enabled"].isBoolean())
    disabled = !parameterObject["enabled"].asBoolean();
  // we need to toggle the current disabled state of the addon
  else if (parameterObject["enabled"].isString())
    disabled = !CServiceBroker::GetAddonMgr().IsAddonDisabled(id);
  else
    return InvalidParams;

  bool success = disabled ? CServiceBroker::GetAddonMgr().DisableAddon(id) : CServiceBroker::GetAddonMgr().EnableAddon(id);
  return success ? ACK : InvalidParams;
}
Пример #15
0
void CAddonDatabase::SetPropertiesFromAddon(const AddonPtr& addon,
                                           CFileItemPtr& pItem)
{
  pItem->SetProperty("Addon.ID", addon->ID());
  pItem->SetProperty("Addon.Type", TranslateType(addon->Type(),true));
  pItem->SetProperty("Addon.intType", TranslateType(addon->Type()));
  pItem->SetProperty("Addon.Name", addon->Name());
  pItem->SetProperty("Addon.Version", addon->Version().c_str());
  pItem->SetProperty("Addon.Summary", addon->Summary());
  pItem->SetProperty("Addon.Description", addon->Description());
  pItem->SetProperty("Addon.Creator", addon->Author());
  pItem->SetProperty("Addon.Disclaimer", addon->Disclaimer());
  pItem->SetProperty("Addon.Rating", addon->Stars());
  CStdString starrating;
  starrating.Format("rating%d.png", addon->Stars());
  pItem->SetProperty("Addon.StarRating",starrating);
  pItem->SetProperty("Addon.Path", addon->Path());
  pItem->SetProperty("Addon.Broken", addon->Props().broken);
  std::map<CStdString,CStdString>::iterator it = 
                    addon->Props().extrainfo.find("language");
  if (it != addon->Props().extrainfo.end())
    pItem->SetProperty("Addon.Language", it->second);
}
Пример #16
0
bool CAddonSystemSettings::UnsetActive(const AddonPtr& addon)
{
  auto it = m_activeSettings.find(addon->Type());
  if (it == m_activeSettings.end())
    return true;

  auto setting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettings().GetSetting(it->second));
  if (setting->GetValue() != addon->ID())
    return true;

  if (setting->GetDefault() == addon->ID())
    return false; // Cant unset defaults

  setting->Reset();
  return true;
}
Пример #17
0
bool CAddonSystemSettings::UnsetActive(const AddonPtr& addon)
{
  auto it = settingMap.find(addon->Type());
  if (it == settingMap.end())
    return true;

  auto setting = static_cast<CSettingString*>(CSettings::GetInstance().GetSetting(it->second));
  if (setting->GetValue() != addon->ID())
    return true;

  if (setting->GetDefault() == addon->ID())
    return false; // Cant unset defaults

  setting->Reset();
  return true;
}
Пример #18
0
int CAddonDatabase::AddAddon(const AddonPtr& addon,
                             int idRepo)
{
  try
  {
    if (NULL == m_pDB.get()) return -1;
    if (NULL == m_pDS.get()) return -1;

    CStdString sql = PrepareSQL("insert into addon (id, type, name, summary,"
                               "description, stars, path, icon, changelog, "
                               "fanart, addonID, version, author, disclaimer)"
                               " values(NULL, '%s', '%s', '%s', '%s', %i,"
                               "'%s', '%s', '%s', '%s', '%s','%s','%s','%s')",
                               TranslateType(addon->Type(),false).c_str(),
                               addon->Name().c_str(), addon->Summary().c_str(),
                               addon->Description().c_str(),addon->Stars(),
                               addon->Path().c_str(), addon->Props().icon.c_str(),
                               addon->ChangeLog().c_str(),addon->FanArt().c_str(),
                               addon->ID().c_str(), addon->Version().str.c_str(),
                               addon->Author().c_str(),addon->Disclaimer().c_str());
    m_pDS->exec(sql.c_str());
    int idAddon = (int)m_pDS->lastinsertid();

    sql = PrepareSQL("insert into addonlinkrepo (idRepo, idAddon) values (%i,%i)",idRepo,idAddon);
    m_pDS->exec(sql.c_str());

    const InfoMap &info = addon->ExtraInfo();
    for (InfoMap::const_iterator i = info.begin(); i != info.end(); ++i)
    {
      sql = PrepareSQL("insert into addonextra(id, key, value) values (%i, '%s', '%s')", idAddon, i->first.c_str(), i->second.c_str());
      m_pDS->exec(sql.c_str());
    }
    return idAddon;
  }
  catch (...)
  {
    CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addon->Name().c_str());
  }
  return -1;
}
Пример #19
0
static CVariant Serialize(const AddonPtr& addon)
{
  CVariant variant;
  variant["addonid"] = addon->ID();
  variant["type"] = CAddonInfo::TranslateType(addon->Type(), false);
  variant["name"] = addon->Name();
  variant["version"] = addon->Version().asString();
  variant["summary"] = addon->Summary();
  variant["description"] = addon->Description();
  variant["path"] = addon->Path();
  variant["author"] = addon->Author();
  variant["thumbnail"] = addon->Icon();
  variant["disclaimer"] = addon->Disclaimer();
  variant["fanart"] = addon->FanArt();

  variant["dependencies"] = CVariant(CVariant::VariantTypeArray);
  for (const auto& dep : addon->GetDependencies())
  {
    CVariant info(CVariant::VariantTypeObject);
    info["addonid"] = dep.id;
    info["version"] = dep.requiredVersion.asString();
    info["optional"] = dep.optional;
    variant["dependencies"].push_back(std::move(info));
  }
  if (addon->Broken().empty())
    variant["broken"] = false;
  else
    variant["broken"] = addon->Broken();
  variant["extrainfo"] = CVariant(CVariant::VariantTypeArray);
  for (const auto& kv : addon->ExtraInfo())
  {
    CVariant info(CVariant::VariantTypeObject);
    info["key"] = kv.first;
    info["value"] = kv.second;
    variant["extrainfo"].push_back(std::move(info));
  }
  variant["rating"] = -1;
  return variant;
}
Пример #20
0
void CGUIWindowAddonBrowser::GetContextButtons(int itemNumber,
                                               CContextButtons& buttons)
{
  CFileItemPtr pItem = m_vecItems->Get(itemNumber);
  if (pItem->GetPath().Equals("addons://enabled/"))
    buttons.Add(CONTEXT_BUTTON_SCAN,24034);
  
  AddonPtr addon;
  if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons
    return;

  if (addon->Type() == ADDON_REPOSITORY && pItem->m_bIsFolder)
  {
    buttons.Add(CONTEXT_BUTTON_SCAN,24034);
    buttons.Add(CONTEXT_BUTTON_UPDATE_LIBRARY,24035);
  }

  buttons.Add(CONTEXT_BUTTON_INFO,24003);

  if (addon->HasSettings())
    buttons.Add(CONTEXT_BUTTON_SETTINGS,24020);
}
Пример #21
0
bool CRetroPlayerDialogs::InstallGameClientDialog(const CFileItem &file, GameClientPtr &result)
{
  VECADDONS addons;
  CGameManager::GetAllGameClients(addons);

  map<string, GameClientPtr> candidates;
  for (VECADDONS::const_iterator itRemote = addons.begin(); itRemote != addons.end(); ++itRemote)
  {
    if (!(*itRemote)->IsType(ADDON_GAMEDLL))
      continue;
    GameClientPtr gc = boost::dynamic_pointer_cast<CGameClient>(*itRemote);

    // Only add game clients to the list if they provide extensions or platforms
    if (!gc || (gc->GetExtensions().empty() /* && gc->GetPlatforms().empty() */))
      continue;

    if (!gc->CanOpen(file))
      continue;

    // If the game client is already installed and enabled, exclude it from the list
    AddonPtr addon;
    if (CAddonMgr::Get().GetAddon(gc->ID(), addon, ADDON_GAMEDLL, true))
      continue;

    // If GetAddon() returns false, but addon is non-NULL, then the add-on exists but is disabled
    bool bEnabled = (addon.get() == NULL);

    bool bBroken = !(*itRemote)->Props().broken.empty();

    if (bEnabled && bBroken)
      continue;

    string strName = gc->Name();

    // Append "(Disabled)" to the name if the add-on is disabled
    if (!bEnabled)
      strName = StringUtils::Format("%s (%s)", strName.c_str(), g_localizeStrings.Get(1223).c_str());

    candidates[strName] = gc;
  }

  if (candidates.empty())
  {
    CLog::Log(LOGDEBUG, "RetroPlayer: No compatible game clients for installation");
    // "Playback failed"
    // "No compatible emulators found for file:"
    // "FILENAME"
    CGUIDialogOK::ShowAndGetInput(16026, 16023, URIUtils::GetFileName(file.GetPath()), 0);
    return false;
  }

  // CContextButtons doesn't support keying by string, only int, so use a
  // parallel array to track the string values (client name)
  CContextButtons choicesInt;
  unsigned int i = 0;

  // Vector to translate button IDs to game client pointers
  vector<string> choicesStr;
  for (map<string, GameClientPtr>::const_iterator it = candidates.begin(); it != candidates.end(); ++it)
  {
    const string& strName = it->first;
    choicesInt.Add(i++, strName);
    choicesStr.push_back(strName);
  }

  int btnid = CGUIDialogContextMenu::ShowAndGetChoice(choicesInt);
  if (btnid < 0 || btnid >= (int)candidates.size())
  {
    CLog::Log(LOGDEBUG, "RetroPlayer: User canceled game client installation selection");
    return false;
  }

  map<string, GameClientPtr>::iterator it = candidates.find(choicesStr[btnid]);
  if (it == candidates.end())
    return false; // Shouldn't happen

  GameClientPtr &gc = it->second;

  // determining disabled status is the same test as earlier
  // TODO: Preserve disabled values in parallel array
  // TODO: Don't use parallel arrays
  AddonPtr addon; // Return value, will be assigned to result
  bool disabled = !CAddonMgr::Get().GetAddon(gc->ID(), addon, ADDON_GAMEDLL, true) && addon;

  if (disabled)
  {
    // TODO: Prompt the user to enable it
    CLog::Log(LOGDEBUG, "RetroPlayer: Game client %s installed but disabled, enabling it", gc->ID().c_str());
    CGameManager::Get().ClearAutoLaunch(); // Don't auto-launch queued game when the add-on is enabled
    CAddonMgr::Get().DisableAddon(gc->ID(), false);

    // Retrieve the newly-enabled add-on
    if (!CAddonMgr::Get().GetAddon(gc->ID(), addon) || !addon)
      return false;
  }
  else
  {
    CLog::Log(LOGDEBUG, "RetroPlayer: Installing game client %s", gc->ID().c_str());
    bool success = CAddonInstaller::Get().PromptForInstall(gc->ID(), addon);
    if (!success || !addon || addon->Type() != ADDON_GAMEDLL)
    {
      CLog::Log(LOGDEBUG, "RetroPlayer: Game client installation failed");
      // "id"
      // "Installation failed"
      // "Check the log file for details."
      CGUIDialogOK::ShowAndGetInput(gc->ID().c_str(), 114, 16029, 0);
      return false;
    }
  }

  GameClientPtr gameClient = boost::dynamic_pointer_cast<CGameClient>(addon);
  if (!gameClient)
  {
    CLog::Log(LOGERROR, "RetroPlayer: Add-on was not a game client!");
    return false;
  }

  result = gameClient;
  return true;
}