/xbmc/Autorun.cpp

http://github.com/xbmc/xbmc · C++ · 531 lines · 393 code · 61 blank · 77 comment · 113 complexity · a254ef9c448bfbd19e53ec7d3d85e79f MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "Autorun.h"
  9. #include <stdlib.h>
  10. #include "Application.h"
  11. #include "GUIPassword.h"
  12. #include "GUIUserMessages.h"
  13. #include "PlayListPlayer.h"
  14. #include "ServiceBroker.h"
  15. #include "cores/playercorefactory/PlayerCoreFactory.h"
  16. #include "filesystem/StackDirectory.h"
  17. #include "filesystem/Directory.h"
  18. #include "filesystem/DirectoryFactory.h"
  19. #include "filesystem/File.h"
  20. #include "messaging/ApplicationMessenger.h"
  21. #include "messaging/helpers/DialogHelper.h"
  22. #include "profiles/ProfileManager.h"
  23. #include "settings/Settings.h"
  24. #include "settings/SettingsComponent.h"
  25. #include "settings/lib/Setting.h"
  26. #include "settings/lib/SettingDefinitions.h"
  27. #include "playlists/PlayList.h"
  28. #include "guilib/GUIComponent.h"
  29. #include "guilib/GUIWindowManager.h"
  30. #include "guilib/LocalizeStrings.h"
  31. #include "storage/MediaManager.h"
  32. #include "video/VideoDatabase.h"
  33. #include "utils/StringUtils.h"
  34. #include "utils/URIUtils.h"
  35. #include "utils/log.h"
  36. #include "utils/Variant.h"
  37. #ifdef HAS_CDDA_RIPPER
  38. #include "cdrip/CDDARipper.h"
  39. #endif
  40. using namespace XFILE;
  41. using namespace PLAYLIST;
  42. using namespace MEDIA_DETECT;
  43. using namespace KODI::MESSAGING;
  44. using KODI::MESSAGING::HELPERS::DialogResponse;
  45. CAutorun::CAutorun()
  46. {
  47. m_bEnable = true;
  48. }
  49. CAutorun::~CAutorun() = default;
  50. void CAutorun::ExecuteAutorun(const std::string& path, bool bypassSettings, bool ignoreplaying, bool startFromBeginning )
  51. {
  52. if (!ignoreplaying)
  53. {
  54. if (g_application.GetAppPlayer().IsPlayingAudio() ||
  55. g_application.GetAppPlayer().IsPlayingVideo() ||
  56. CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true))
  57. {
  58. return;
  59. }
  60. }
  61. if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN)
  62. return;
  63. CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(path);
  64. if ( pInfo == NULL )
  65. return ;
  66. g_application.ResetScreenSaver();
  67. g_application.WakeUpScreenSaverAndDPMS(); // turn off the screensaver if it's active
  68. #ifdef HAS_CDDA_RIPPER
  69. if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_RIP &&
  70. pInfo->IsAudio(1) && !CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().musicLocked())
  71. {
  72. CCDDARipper::GetInstance().RipCD();
  73. }
  74. else
  75. #endif
  76. PlayDisc(path, bypassSettings, startFromBeginning);
  77. }
  78. bool CAutorun::PlayDisc(const std::string& path, bool bypassSettings, bool startFromBeginning)
  79. {
  80. if ( !bypassSettings && CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) != AUTOCD_PLAY && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN))
  81. return false;
  82. int nSize = CServiceBroker::GetPlaylistPlayer().GetPlaylist( PLAYLIST_MUSIC ).size();
  83. int nAddedToPlaylist = 0;
  84. std::string mediaPath;
  85. CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(path);
  86. if (pInfo == NULL)
  87. return false;
  88. if (mediaPath.empty() && pInfo->IsAudio(1))
  89. mediaPath = "cdda://local/";
  90. if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1)))
  91. mediaPath = "iso9660://";
  92. if (mediaPath.empty())
  93. mediaPath = path;
  94. if (mediaPath.empty() || mediaPath == "iso9660://")
  95. mediaPath = CServiceBroker::GetMediaManager().GetDiscPath();
  96. const CURL pathToUrl(mediaPath);
  97. std::unique_ptr<IDirectory> pDir ( CDirectoryFactory::Create( pathToUrl ));
  98. bool bPlaying = RunDisc(pDir.get(), mediaPath, nAddedToPlaylist, true, bypassSettings, startFromBeginning);
  99. if ( !bPlaying && nAddedToPlaylist > 0 )
  100. {
  101. CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 );
  102. CServiceBroker::GetGUI()->GetWindowManager().SendMessage( msg );
  103. CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_MUSIC);
  104. // Start playing the items we inserted
  105. return CServiceBroker::GetPlaylistPlayer().Play(nSize, "");
  106. }
  107. return bPlaying;
  108. }
  109. /**
  110. * This method tries to determine what type of disc is located in the given drive and starts to play the content appropriately.
  111. */
  112. bool CAutorun::RunDisc(IDirectory* pDir, const std::string& strDrive, int& nAddedToPlaylist, bool bRoot, bool bypassSettings /* = false */, bool startFromBeginning /* = false */)
  113. {
  114. bool bPlaying(false);
  115. CFileItemList vecItems;
  116. const CURL pathToUrl(strDrive);
  117. if ( !pDir->GetDirectory( pathToUrl, vecItems ) )
  118. {
  119. return false;
  120. }
  121. // Sorting necessary for easier HDDVD handling
  122. vecItems.Sort(SortByLabel, SortOrderAscending);
  123. bool bAllowVideo = true;
  124. // bool bAllowPictures = true;
  125. bool bAllowMusic = true;
  126. if (!g_passwordManager.IsMasterLockUnlocked(false))
  127. {
  128. const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
  129. bAllowVideo = !profileManager->GetCurrentProfile().videoLocked();
  130. // bAllowPictures = !profileManager->GetCurrentProfile().picturesLocked();
  131. bAllowMusic = !profileManager->GetCurrentProfile().musicLocked();
  132. }
  133. // is this a root folder we have to check the content to determine a disc type
  134. if (bRoot)
  135. {
  136. std::string hddvdname = "";
  137. CFileItemPtr phddvdItem;
  138. bool bAutorunDVDs = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN);
  139. // check root folders next, for normal structured dvd's
  140. for (const auto& pItem : vecItems)
  141. {
  142. // is the current item a (non system) folder?
  143. if (pItem->m_bIsFolder && pItem->GetPath() != "." && pItem->GetPath() != "..")
  144. {
  145. std::string name = pItem->GetPath();
  146. URIUtils::RemoveSlashAtEnd(name);
  147. name = URIUtils::GetFileName(name);
  148. // Check if the current foldername indicates a DVD structure (name is "VIDEO_TS")
  149. if (StringUtils::EqualsNoCase(name, "VIDEO_TS") && bAllowVideo
  150. && (bypassSettings || bAutorunDVDs))
  151. {
  152. std::string path = URIUtils::AddFileToFolder(pItem->GetPath(), "VIDEO_TS.IFO");
  153. if(!CFile::Exists(path))
  154. path = URIUtils::AddFileToFolder(pItem->GetPath(), "video_ts.ifo");
  155. CFileItemPtr item(new CFileItem(path, false));
  156. item->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
  157. item->GetVideoInfoTag()->m_strFileNameAndPath =
  158. CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
  159. if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty())
  160. item->m_lStartOffset = STARTOFFSET_RESUME;
  161. CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
  162. CServiceBroker::GetPlaylistPlayer().SetShuffle (PLAYLIST_VIDEO, false);
  163. CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, item);
  164. CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
  165. CServiceBroker::GetPlaylistPlayer().Play(0, "");
  166. return true;
  167. }
  168. // Check if the current foldername indicates a Blu-Ray structure (default is "BDMV").
  169. // A BR should also include an "AACS" folder for encryption, Sony-BRs can also include update folders for PS3 (PS3_UPDATE / PS3_VPRM).
  170. //! @todo for the time being, the DVD autorun settings are used to determine if the BR should be started automatically.
  171. if (StringUtils::EqualsNoCase(name, "BDMV") && bAllowVideo
  172. && (bypassSettings || bAutorunDVDs))
  173. {
  174. CFileItemPtr item(new CFileItem(URIUtils::AddFileToFolder(pItem->GetPath(), "index.bdmv"), false));
  175. item->SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
  176. item->GetVideoInfoTag()->m_strFileNameAndPath =
  177. CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
  178. if (!startFromBeginning && !item->GetVideoInfoTag()->m_strFileNameAndPath.empty())
  179. item->m_lStartOffset = STARTOFFSET_RESUME;
  180. CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
  181. CServiceBroker::GetPlaylistPlayer().SetShuffle (PLAYLIST_VIDEO, false);
  182. CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, item);
  183. CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
  184. CServiceBroker::GetPlaylistPlayer().Play(0, "");
  185. return true;
  186. }
  187. // Check if the current foldername indicates a HD DVD structure (default is "HVDVD_TS").
  188. // Most HD DVD will also include an "ADV_OBJ" folder for advanced content. This folder should be handled first.
  189. //! @todo for the time being, the DVD autorun settings are used to determine if the HD DVD should be started automatically.
  190. CFileItemList items, sitems;
  191. // Advanced Content HD DVD (most discs?)
  192. if (StringUtils::EqualsNoCase(name, "ADV_OBJ"))
  193. {
  194. CLog::Log(LOGINFO,"HD DVD: Checking for playlist.");
  195. // find playlist file
  196. CDirectory::GetDirectory(pItem->GetPath(), items, "*.xpl", DIR_FLAG_DEFAULTS);
  197. if (items.Size())
  198. {
  199. // HD DVD Standard says the highest numbered playlist has to be handled first.
  200. CLog::Log(LOGINFO,"HD DVD: Playlist found. Set filetypes to *.xpl for external player.");
  201. items.Sort(SortByLabel, SortOrderDescending);
  202. phddvdItem = pItem;
  203. hddvdname = URIUtils::GetFileName(items[0]->GetPath());
  204. CLog::Log(LOGINFO,"HD DVD: %s", items[0]->GetPath().c_str());
  205. }
  206. }
  207. // Standard Content HD DVD (few discs?)
  208. if (StringUtils::EqualsNoCase(name, "HVDVD_TS") && bAllowVideo
  209. && (bypassSettings || bAutorunDVDs))
  210. {
  211. if (hddvdname == "")
  212. {
  213. CLog::Log(LOGINFO,"HD DVD: Checking for ifo.");
  214. // find Video Manager or Title Set Information
  215. CDirectory::GetDirectory(pItem->GetPath(), items, "HV*.ifo", DIR_FLAG_DEFAULTS);
  216. if (items.Size())
  217. {
  218. // HD DVD Standard says the lowest numbered ifo has to be handled first.
  219. CLog::Log(LOGINFO,"HD DVD: IFO found. Set filename to HV* and filetypes to *.ifo for external player.");
  220. items.Sort(SortByLabel, SortOrderAscending);
  221. phddvdItem = pItem;
  222. hddvdname = URIUtils::GetFileName(items[0]->GetPath());
  223. CLog::Log(LOGINFO,"HD DVD: %s",items[0]->GetPath().c_str());
  224. }
  225. }
  226. // Find and sort *.evo files for internal playback.
  227. // While this algorithm works for all of my HD DVDs, it may fail on other discs. If there are very large extras which are
  228. // alphabetically before the main movie they will be sorted to the top of the playlist and get played first.
  229. CDirectory::GetDirectory(pItem->GetPath(), items, "*.evo", DIR_FLAG_DEFAULTS);
  230. if (items.Size())
  231. {
  232. // Sort *.evo files in alphabetical order.
  233. items.Sort(SortByLabel, SortOrderAscending);
  234. int64_t asize = 0;
  235. int ecount = 0;
  236. // calculate average size of elements above 1gb
  237. for (int j = 0; j < items.Size(); j++)
  238. if (items[j]->m_dwSize > 1000000000)
  239. {
  240. ecount++;
  241. asize = asize + items[j]->m_dwSize;
  242. }
  243. if (ecount > 0)
  244. asize = asize / ecount;
  245. // Put largest files in alphabetical order to top of new list.
  246. for (int j = 0; j < items.Size(); j++)
  247. if (items[j]->m_dwSize >= asize)
  248. sitems.Add (items[j]);
  249. // Sort *.evo files by size.
  250. items.Sort(SortBySize, SortOrderDescending);
  251. // Add other files with descending size to bottom of new list.
  252. for (int j = 0; j < items.Size(); j++)
  253. if (items[j]->m_dwSize < asize)
  254. sitems.Add (items[j]);
  255. // Replace list with optimized list.
  256. items.Clear();
  257. items.Copy (sitems);
  258. sitems.Clear();
  259. }
  260. if (hddvdname != "")
  261. {
  262. CFileItem item(URIUtils::AddFileToFolder(phddvdItem->GetPath(), hddvdname), false);
  263. item.SetLabel(CServiceBroker::GetMediaManager().GetDiskLabel(strDrive));
  264. item.GetVideoInfoTag()->m_strFileNameAndPath =
  265. CServiceBroker::GetMediaManager().GetDiskUniqueId(strDrive);
  266. if (!startFromBeginning && !item.GetVideoInfoTag()->m_strFileNameAndPath.empty())
  267. item.m_lStartOffset = STARTOFFSET_RESUME;
  268. // get playername
  269. std::string hdVideoPlayer = CServiceBroker::GetPlayerCoreFactory().GetDefaultPlayer(item);
  270. // Single *.xpl or *.ifo files require an external player to handle playback.
  271. // If no matching rule was found, VideoPlayer will be default player.
  272. if (hdVideoPlayer != "VideoPlayer")
  273. {
  274. CLog::Log(LOGINFO,"HD DVD: External singlefile playback initiated: %s",hddvdname.c_str());
  275. g_application.PlayFile(item, hdVideoPlayer, false);
  276. return true;
  277. } else
  278. CLog::Log(LOGINFO,"HD DVD: No external player found. Fallback to internal one.");
  279. }
  280. // internal *.evo playback.
  281. CLog::Log(LOGINFO,"HD DVD: Internal multifile playback initiated.");
  282. CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
  283. CServiceBroker::GetPlaylistPlayer().SetShuffle (PLAYLIST_VIDEO, false);
  284. CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, items);
  285. CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
  286. CServiceBroker::GetPlaylistPlayer().Play(0, "");
  287. return true;
  288. }
  289. // Video CDs can have multiple file formats. First we need to determine which one is used on the CD
  290. std::string strExt;
  291. if (StringUtils::EqualsNoCase(name, "MPEGAV"))
  292. strExt = ".dat";
  293. if (StringUtils::EqualsNoCase(name, "MPEG2"))
  294. strExt = ".mpg";
  295. // If a file format was extracted we are sure this is a VCD. Autoplay if settings indicate we should.
  296. if (!strExt.empty() && bAllowVideo
  297. && (bypassSettings || bAutorunDVDs))
  298. {
  299. CFileItemList items;
  300. CDirectory::GetDirectory(pItem->GetPath(), items, strExt, DIR_FLAG_DEFAULTS);
  301. if (items.Size())
  302. {
  303. items.Sort(SortByLabel, SortOrderAscending);
  304. CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
  305. CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, items);
  306. CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
  307. CServiceBroker::GetPlaylistPlayer().Play(0, "");
  308. return true;
  309. }
  310. }
  311. /* Probably want this if/when we add some automedia action dialog...
  312. else if (pItem->GetPath().Find("PICTURES") != -1 && bAllowPictures
  313. && (bypassSettings))
  314. {
  315. bPlaying = true;
  316. std::string strExec = StringUtils::Format("RecursiveSlideShow(%s)", pItem->GetPath().c_str());
  317. CBuiltins::Execute(strExec);
  318. return true;
  319. }
  320. */
  321. }
  322. }
  323. }
  324. // check video first
  325. if (!nAddedToPlaylist && !bPlaying && (bypassSettings || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DVDS_AUTORUN)))
  326. {
  327. // stack video files
  328. CFileItemList tempItems;
  329. tempItems.Append(vecItems);
  330. if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_STACKVIDEOS))
  331. tempItems.Stack();
  332. CFileItemList itemlist;
  333. for (int i = 0; i < tempItems.Size(); i++)
  334. {
  335. CFileItemPtr pItem = tempItems[i];
  336. if (!pItem->m_bIsFolder && pItem->IsVideo())
  337. {
  338. bPlaying = true;
  339. if (pItem->IsStack())
  340. {
  341. //! @todo remove this once the app/player is capable of handling stacks immediately
  342. CStackDirectory dir;
  343. CFileItemList items;
  344. dir.GetDirectory(pItem->GetURL(), items);
  345. itemlist.Append(items);
  346. }
  347. else
  348. itemlist.Add(pItem);
  349. }
  350. }
  351. if (itemlist.Size())
  352. {
  353. if (!bAllowVideo)
  354. {
  355. if (!bypassSettings)
  356. return false;
  357. if (!g_passwordManager.IsMasterLockUnlocked(true))
  358. return false;
  359. }
  360. CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST_VIDEO);
  361. CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_VIDEO, itemlist);
  362. CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_VIDEO);
  363. CServiceBroker::GetPlaylistPlayer().Play(0, "");
  364. }
  365. }
  366. // then music
  367. if (!bPlaying && (bypassSettings || CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_PLAY) && bAllowMusic)
  368. {
  369. for (int i = 0; i < vecItems.Size(); i++)
  370. {
  371. CFileItemPtr pItem = vecItems[i];
  372. if (!pItem->m_bIsFolder && pItem->IsAudio())
  373. {
  374. nAddedToPlaylist++;
  375. CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST_MUSIC, pItem);
  376. }
  377. }
  378. }
  379. /* Probably want this if/when we add some automedia action dialog...
  380. // and finally pictures
  381. if (!nAddedToPlaylist && !bPlaying && bypassSettings && bAllowPictures)
  382. {
  383. for (int i = 0; i < vecItems.Size(); i++)
  384. {
  385. CFileItemPtr pItem = vecItems[i];
  386. if (!pItem->m_bIsFolder && pItem->IsPicture())
  387. {
  388. bPlaying = true;
  389. std::string strExec = StringUtils::Format("RecursiveSlideShow(%s)", strDrive.c_str());
  390. CBuiltins::Execute(strExec);
  391. break;
  392. }
  393. }
  394. }
  395. */
  396. // check subdirs if we are not playing yet
  397. if (!bPlaying)
  398. {
  399. for (int i = 0; i < vecItems.Size(); i++)
  400. {
  401. CFileItemPtr pItem = vecItems[i];
  402. if (pItem->m_bIsFolder)
  403. {
  404. if (pItem->GetPath() != "." && pItem->GetPath() != ".." )
  405. {
  406. if (RunDisc(pDir, pItem->GetPath(), nAddedToPlaylist, false, bypassSettings, startFromBeginning))
  407. {
  408. bPlaying = true;
  409. break;
  410. }
  411. }
  412. } // if (non system) folder
  413. } // for all items in directory
  414. } // if root folder
  415. return bPlaying;
  416. }
  417. void CAutorun::HandleAutorun()
  418. {
  419. #ifndef TARGET_WINDOWS
  420. if (!m_bEnable)
  421. {
  422. CDetectDVDMedia::m_evAutorun.Reset();
  423. return ;
  424. }
  425. if (CDetectDVDMedia::m_evAutorun.WaitMSec(0))
  426. {
  427. ExecuteAutorun();
  428. CDetectDVDMedia::m_evAutorun.Reset();
  429. }
  430. #endif
  431. }
  432. void CAutorun::Enable()
  433. {
  434. m_bEnable = true;
  435. }
  436. void CAutorun::Disable()
  437. {
  438. m_bEnable = false;
  439. }
  440. bool CAutorun::IsEnabled() const
  441. {
  442. return m_bEnable;
  443. }
  444. bool CAutorun::PlayDiscAskResume(const std::string& path)
  445. {
  446. return PlayDisc(path, true, !CanResumePlayDVD(path) ||
  447. HELPERS::ShowYesNoDialogText(CVariant{341}, CVariant{""}, CVariant{13404}, CVariant{12021}) ==
  448. DialogResponse::YES);
  449. }
  450. bool CAutorun::CanResumePlayDVD(const std::string& path)
  451. {
  452. std::string strUniqueId = CServiceBroker::GetMediaManager().GetDiskUniqueId(path);
  453. if (!strUniqueId.empty())
  454. {
  455. CVideoDatabase dbs;
  456. dbs.Open();
  457. CBookmark bookmark;
  458. if (dbs.GetResumeBookMark(strUniqueId, bookmark))
  459. return true;
  460. }
  461. return false;
  462. }
  463. void CAutorun::SettingOptionAudioCdActionsFiller(SettingConstPtr setting, std::vector<IntegerSettingOption> &list, int &current, void *data)
  464. {
  465. list.emplace_back(g_localizeStrings.Get(16018), AUTOCD_NONE);
  466. list.emplace_back(g_localizeStrings.Get(14098), AUTOCD_PLAY);
  467. #ifdef HAS_CDDA_RIPPER
  468. list.emplace_back(g_localizeStrings.Get(14096), AUTOCD_RIP);
  469. #endif
  470. }