Merge pull request #9146 from JosJuice/android-disable-cover-download

Android: Allow disabling cover downloading
This commit is contained in:
Léo Lam 2020-10-20 13:31:01 +02:00 committed by GitHub
commit 2e86e1a998
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 193 additions and 39 deletions

View File

@ -120,6 +120,14 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
notifyDataSetChanged(); notifyDataSetChanged();
} }
/**
* Re-fetches game metadata from the game file cache.
*/
public void refetchMetadata()
{
notifyItemRangeChanged(0, getItemCount());
}
/** /**
* Launches the game that was clicked on. * Launches the game that was clicked on.
* *

View File

@ -49,4 +49,10 @@ public class AdHocBooleanSetting implements AbstractBooleanSetting
{ {
NativeConfig.setBoolean(settings.getWriteLayer(), mFile, mSection, mKey, newValue); NativeConfig.setBoolean(settings.getWriteLayer(), mFile, mSection, mKey, newValue);
} }
public static boolean getBooleanGlobal(String file, String section, String key,
boolean defaultValue)
{
return NativeConfig.getBoolean(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue);
}
} }

View File

@ -0,0 +1,57 @@
package org.dolphinemu.dolphinemu.features.settings.model;
public class AdHocStringSetting implements AbstractStringSetting
{
private final String mFile;
private final String mSection;
private final String mKey;
private final String mDefaultValue;
public AdHocStringSetting(String file, String section, String key, String defaultValue)
{
mFile = file;
mSection = section;
mKey = key;
mDefaultValue = defaultValue;
if (!NativeConfig.isSettingSaveable(file, section, key))
{
throw new IllegalArgumentException("File/section/key is unknown or legacy");
}
}
@Override
public boolean isOverridden(Settings settings)
{
return NativeConfig.isOverridden(mFile, mSection, mKey);
}
@Override
public boolean isRuntimeEditable()
{
return true;
}
@Override
public boolean delete(Settings settings)
{
return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey);
}
@Override
public String getString(Settings settings)
{
return NativeConfig.getString(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue);
}
@Override
public void setString(Settings settings, String newValue)
{
NativeConfig.setString(settings.getWriteLayer(), mFile, mSection, mKey, newValue);
}
public static String getStringGlobal(String file, String section, String key, String defaultValue)
{
return NativeConfig.getString(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue);
}
}

View File

@ -38,6 +38,8 @@ public enum BooleanSetting implements AbstractBooleanSetting
MAIN_RECURSIVE_ISO_PATHS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, MAIN_RECURSIVE_ISO_PATHS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL,
"RecursiveISOPaths", false), "RecursiveISOPaths", false),
MAIN_USE_GAME_COVERS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL,
"UseGameCovers", true),
SYSCONF_SCREENSAVER(Settings.FILE_SYSCONF, "IPL", "SSV", false), SYSCONF_SCREENSAVER(Settings.FILE_SYSCONF, "IPL", "SSV", false),
SYSCONF_WIDESCREEN(Settings.FILE_SYSCONF, "IPL", "AR", true), SYSCONF_WIDESCREEN(Settings.FILE_SYSCONF, "IPL", "AR", true),
@ -191,4 +193,9 @@ public enum BooleanSetting implements AbstractBooleanSetting
settings.getSection(mFile, mSection).setBoolean(mKey, newValue); settings.getSection(mFile, mSection).setBoolean(mKey, newValue);
} }
} }
public boolean getBooleanGlobal()
{
return NativeConfig.getBoolean(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue);
}
} }

View File

@ -73,4 +73,9 @@ public enum FloatSetting implements AbstractFloatSetting
settings.getSection(mFile, mSection).setFloat(mKey, newValue); settings.getSection(mFile, mSection).setFloat(mKey, newValue);
} }
} }
public float getFloatGlobal()
{
return NativeConfig.getFloat(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue);
}
} }

View File

@ -130,4 +130,9 @@ public enum IntSetting implements AbstractIntSetting
settings.getSection(mFile, mSection).setInt(mKey, newValue); settings.getSection(mFile, mSection).setInt(mKey, newValue);
} }
} }
public int getIntGlobal()
{
return NativeConfig.getInt(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue);
}
} }

View File

@ -104,4 +104,9 @@ public enum StringSetting implements AbstractStringSetting
settings.getSection(mFile, mSection).setString(mKey, newValue); settings.getSection(mFile, mSection).setString(mKey, newValue);
} }
} }
public String getStringGlobal()
{
return NativeConfig.getString(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue);
}
} }

View File

@ -224,7 +224,6 @@ public final class SettingsFragmentPresenter
sl.add(new SubmenuSetting(R.string.advanced_submenu, MenuTag.CONFIG_ADVANCED)); sl.add(new SubmenuSetting(R.string.advanced_submenu, MenuTag.CONFIG_ADVANCED));
sl.add(new SubmenuSetting(R.string.log_submenu, MenuTag.CONFIG_LOG)); sl.add(new SubmenuSetting(R.string.log_submenu, MenuTag.CONFIG_LOG));
sl.add(new SubmenuSetting(R.string.debug_submenu, MenuTag.DEBUG)); sl.add(new SubmenuSetting(R.string.debug_submenu, MenuTag.DEBUG));
sl.add(new HeaderSetting(R.string.gametdb_thanks, 0));
} }
private void addGeneralSettings(ArrayList<SettingsItem> sl) private void addGeneralSettings(ArrayList<SettingsItem> sl)
@ -247,6 +246,8 @@ public final class SettingsFragmentPresenter
R.string.panic_handlers_description)); R.string.panic_handlers_description));
sl.add(new CheckBoxSetting(BooleanSetting.MAIN_OSD_MESSAGES, R.string.osd_messages, sl.add(new CheckBoxSetting(BooleanSetting.MAIN_OSD_MESSAGES, R.string.osd_messages,
R.string.osd_messages_description)); R.string.osd_messages_description));
sl.add(new CheckBoxSetting(BooleanSetting.MAIN_USE_GAME_COVERS, R.string.download_game_covers,
0));
} }
private void addAudioSettings(ArrayList<SettingsItem> sl) private void addAudioSettings(ArrayList<SettingsItem> sl)

View File

@ -152,8 +152,7 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
LinearLayout options = requireView().findViewById(R.id.layout_options); LinearLayout options = requireView().findViewById(R.id.layout_options);
Settings settings = ((EmulationActivity) requireActivity()).getSettings(); boolean savestatesEnabled = BooleanSetting.MAIN_ENABLE_SAVESTATES.getBooleanGlobal();
boolean savestatesEnabled = BooleanSetting.MAIN_ENABLE_SAVESTATES.getBoolean(settings);
int savestateVisibility = savestatesEnabled ? View.VISIBLE : View.GONE; int savestateVisibility = savestatesEnabled ? View.VISIBLE : View.GONE;
options.findViewById(R.id.menu_quicksave).setVisibility(savestateVisibility); options.findViewById(R.id.menu_quicksave).setVisibility(savestateVisibility);
options.findViewById(R.id.menu_quickload).setVisibility(savestateVisibility); options.findViewById(R.id.menu_quickload).setVisibility(savestateVisibility);

View File

@ -81,12 +81,7 @@ public class GameFileCache
*/ */
public boolean scanLibrary(Context context) public boolean scanLibrary(Context context)
{ {
boolean recursiveScan; boolean recursiveScan = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBooleanGlobal();
try (Settings settings = new Settings())
{
settings.loadSettings(null);
recursiveScan = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean(settings);
}
removeNonExistentGameFolders(context); removeNonExistentGameFolders(context);

View File

@ -87,6 +87,10 @@ public final class MainActivity extends AppCompatActivity implements MainView
mPresenter.addDirIfNeeded(this); mPresenter.addDirIfNeeded(this);
// In case the user changed a setting that affects how games are displayed,
// such as system language, cover downloading...
refetchMetadata();
if (sShouldRescanLibrary) if (sShouldRescanLibrary)
{ {
GameFileCacheService.startRescan(this); GameFileCacheService.startRescan(this);
@ -254,6 +258,18 @@ public final class MainActivity extends AppCompatActivity implements MainView
} }
} }
private void refetchMetadata()
{
for (Platform platform : Platform.values())
{
PlatformGamesView fragment = getPlatformGamesView(platform);
if (fragment != null)
{
fragment.refetchMetadata();
}
}
}
@Nullable @Nullable
private PlatformGamesView getPlatformGamesView(Platform platform) private PlatformGamesView getPlatformGamesView(Platform platform)
{ {
@ -289,11 +305,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
} }
}); });
try (Settings settings = new Settings()) mViewPager.setCurrentItem(IntSetting.MAIN_LAST_PLATFORM_TAB.getIntGlobal());
{
settings.loadSettings(null);
mViewPager.setCurrentItem(IntSetting.MAIN_LAST_PLATFORM_TAB.getInt(settings));
}
showGames(); showGames();
GameFileCacheService.startLoad(this); GameFileCacheService.startLoad(this);

View File

@ -32,6 +32,7 @@ import org.dolphinemu.dolphinemu.utils.StartupHandler;
import org.dolphinemu.dolphinemu.utils.TvUtil; import org.dolphinemu.dolphinemu.utils.TvUtil;
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
public final class TvMainActivity extends FragmentActivity implements MainView public final class TvMainActivity extends FragmentActivity implements MainView
@ -42,6 +43,8 @@ public final class TvMainActivity extends FragmentActivity implements MainView
private BrowseSupportFragment mBrowseFragment; private BrowseSupportFragment mBrowseFragment;
private ArrayList<ArrayObjectAdapter> mGameRows = new ArrayList<>();
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
@ -72,6 +75,10 @@ public final class TvMainActivity extends FragmentActivity implements MainView
mPresenter.addDirIfNeeded(this); mPresenter.addDirIfNeeded(this);
// In case the user changed a setting that affects how games are displayed,
// such as system language, cover downloading...
refetchMetadata();
if (sShouldRescanLibrary) if (sShouldRescanLibrary)
{ {
GameFileCacheService.startRescan(this); GameFileCacheService.startRescan(this);
@ -185,6 +192,14 @@ public final class TvMainActivity extends FragmentActivity implements MainView
buildRowsAdapter(); buildRowsAdapter();
} }
private void refetchMetadata()
{
for (ArrayObjectAdapter row : mGameRows)
{
row.notifyArrayItemRangeChanged(0, row.size());
}
}
/** /**
* Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity.
* *
@ -244,6 +259,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView
private void buildRowsAdapter() private void buildRowsAdapter()
{ {
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
mGameRows.clear();
if (PermissionsHandler.hasWriteAccess(this)) if (PermissionsHandler.hasWriteAccess(this))
{ {
@ -278,6 +294,9 @@ public final class TvMainActivity extends FragmentActivity implements MainView
ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter()); ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter());
row.addAll(0, gameFiles); row.addAll(0, gameFiles);
// Keep a reference to the row in case we need to refresh it.
mGameRows.add(row);
// Create a header for this row. // Create a header for this row.
HeaderItem header = new HeaderItem(platform.toInt(), platform.getHeaderName()); HeaderItem header = new HeaderItem(platform.toInt(), platform.getHeaderName());

View File

@ -85,6 +85,12 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
} }
} }
@Override
public void refetchMetadata()
{
mAdapter.refetchMetadata();
}
private void findViews(View root) private void findViews(View root)
{ {
mRecyclerView = root.findViewById(R.id.grid_games); mRecyclerView = root.findViewById(R.id.grid_games);

View File

@ -25,4 +25,9 @@ public interface PlatformGamesView
* To be called when the game file cache is updated. * To be called when the game file cache is updated.
*/ */
void showGames(); void showGames();
/**
* Re-fetches game metadata from the game file cache.
*/
void refetchMetadata();
} }

View File

@ -24,44 +24,41 @@ public class Analytics
{ {
new AfterDirectoryInitializationRunner().run(context, false, () -> new AfterDirectoryInitializationRunner().run(context, false, () ->
{ {
Settings settings = new Settings(); if (!BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.getBooleanGlobal())
settings.loadSettings(null);
if (!BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.getBoolean(settings))
{ {
showMessage(context, settings); showMessage(context);
}
else
{
settings.close();
} }
}); });
} }
private static void showMessage(Context context, Settings settings) private static void showMessage(Context context)
{ {
new AlertDialog.Builder(context, R.style.DolphinDialogBase) new AlertDialog.Builder(context, R.style.DolphinDialogBase)
.setTitle(context.getString(R.string.analytics)) .setTitle(context.getString(R.string.analytics))
.setMessage(context.getString(R.string.analytics_desc)) .setMessage(context.getString(R.string.analytics_desc))
.setPositiveButton(R.string.yes, (dialogInterface, i) -> .setPositiveButton(R.string.yes, (dialogInterface, i) ->
{ {
firstAnalyticsAdd(settings, true); firstAnalyticsAdd(true);
}) })
.setNegativeButton(R.string.no, (dialogInterface, i) -> .setNegativeButton(R.string.no, (dialogInterface, i) ->
{ {
firstAnalyticsAdd(settings, false); firstAnalyticsAdd(false);
}) })
.show(); .show();
} }
private static void firstAnalyticsAdd(Settings settings, boolean enabled) private static void firstAnalyticsAdd(boolean enabled)
{ {
try (Settings settings = new Settings())
{
settings.loadSettings(null);
BooleanSetting.MAIN_ANALYTICS_ENABLED.setBoolean(settings, enabled); BooleanSetting.MAIN_ANALYTICS_ENABLED.setBoolean(settings, enabled);
BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.setBoolean(settings, true); BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.setBoolean(settings, true);
// Context is set to null to avoid toasts // Context is set to null to avoid toasts
settings.saveSettings(null, null); settings.saveSettings(null, null);
}
settings.close();
} }
public static void sendReport(String endpoint, byte[] data) public static void sendReport(String endpoint, byte[] data)

View File

@ -10,6 +10,7 @@ import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
import java.io.File; import java.io.File;
@ -62,7 +63,7 @@ public class PicassoUtils
} }
// GameTDB has a pretty close to complete collection for US/EN covers. First pass at getting // GameTDB has a pretty close to complete collection for US/EN covers. First pass at getting
// the cover will be by the disk's region, second will be the US cover, and third EN. // the cover will be by the disk's region, second will be the US cover, and third EN.
else else if (BooleanSetting.MAIN_USE_GAME_COVERS.getBooleanGlobal())
{ {
Picasso.get() Picasso.get()
.load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile))) .load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile)))
@ -136,5 +137,16 @@ public class PicassoUtils
} }
}); });
} }
else
{
Picasso.get()
.load(R.drawable.no_banner)
.noFade()
.noPlaceholder()
.fit()
.centerInside()
.config(Bitmap.Config.ARGB_8888)
.into(imageView);
}
} }
} }

View File

@ -9,6 +9,7 @@ import android.util.SparseArray;
import android.view.InputDevice; import android.view.InputDevice;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.settings.model.AdHocStringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
@ -28,9 +29,8 @@ public class Rumble
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
String deviceName = activity.getSettings() String deviceName = AdHocStringSetting.getStringGlobal(Settings.FILE_DOLPHIN,
.getSection(Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS) Settings.SECTION_BINDINGS, SettingsFile.KEY_EMU_RUMBLE + i, "");
.getString(SettingsFile.KEY_EMU_RUMBLE + i, "");
if (!deviceName.isEmpty()) if (!deviceName.isEmpty())
{ {

View File

@ -158,7 +158,6 @@
<string name="lock_emulation_landscape_desc">Some touch controls will require additional tweaking if played in portrait</string> <string name="lock_emulation_landscape_desc">Some touch controls will require additional tweaking if played in portrait</string>
<string name="analytics">Enable usage statistics reporting</string> <string name="analytics">Enable usage statistics reporting</string>
<string name="analytics_desc">If authorized, Dolphin can collect data on its performance, feature usage, and configuration, as well as data on your system\'s hardware and operating system.\n\nNo private data is ever collected. This data helps us understand how people and emulated games use Dolphin and prioritize our efforts. It also helps us identify rare configurations that are causing bugs, performance and stability issues. This authorization can be revoked at any time through Dolphin\'s settings.</string> <string name="analytics_desc">If authorized, Dolphin can collect data on its performance, feature usage, and configuration, as well as data on your system\'s hardware and operating system.\n\nNo private data is ever collected. This data helps us understand how people and emulated games use Dolphin and prioritize our efforts. It also helps us identify rare configurations that are causing bugs, performance and stability issues. This authorization can be revoked at any time through Dolphin\'s settings.</string>
<string name="gametdb_thanks">Thanks to GameTDB.com for providing GameCube and Wii covers!</string>
<!-- Interface Preference Fragment --> <!-- Interface Preference Fragment -->
<string name="interface_submenu">Interface</string> <string name="interface_submenu">Interface</string>
@ -166,6 +165,7 @@
<string name="panic_handlers_description">Show a message box when a potentially serious error has occurred. Disabling this may avoid annoying and non-fatal messages, but it may result in major crashes having no explanation at all.</string> <string name="panic_handlers_description">Show a message box when a potentially serious error has occurred. Disabling this may avoid annoying and non-fatal messages, but it may result in major crashes having no explanation at all.</string>
<string name="osd_messages">Show On-Screen Display Messages</string> <string name="osd_messages">Show On-Screen Display Messages</string>
<string name="osd_messages_description">Display messages over the emulation screen area. These messages include memory card writes, video backend and CPU information, and JIT cache clearing.</string> <string name="osd_messages_description">Display messages over the emulation screen area. These messages include memory card writes, video backend and CPU information, and JIT cache clearing.</string>
<string name="download_game_covers">Download Game Covers from GameTDB.com</string>
<!-- Audio Settings --> <!-- Audio Settings -->
<string name="audio_submenu">Audio</string> <string name="audio_submenu">Audio</string>

View File

@ -9,7 +9,11 @@ namespace Config
// UI.General // UI.General
const Info<bool> MAIN_USE_DISCORD_PRESENCE{{System::Main, "General", "UseDiscordPresence"}, true}; const Info<bool> MAIN_USE_DISCORD_PRESENCE{{System::Main, "General", "UseDiscordPresence"}, true};
#ifdef ANDROID
const Info<bool> MAIN_USE_GAME_COVERS{{System::Main, "General", "UseGameCovers"}, true};
#else
const Info<bool> MAIN_USE_GAME_COVERS{{System::Main, "General", "UseGameCovers"}, false}; const Info<bool> MAIN_USE_GAME_COVERS{{System::Main, "General", "UseGameCovers"}, false};
#endif
const Info<bool> MAIN_FOCUSED_HOTKEYS{{System::Main, "General", "HotkeysRequireFocus"}, true}; const Info<bool> MAIN_FOCUSED_HOTKEYS{{System::Main, "General", "HotkeysRequireFocus"}, true};
const Info<bool> MAIN_RECURSIVE_ISO_PATHS{{System::Main, "General", "RecursiveISOPaths"}, false}; const Info<bool> MAIN_RECURSIVE_ISO_PATHS{{System::Main, "General", "RecursiveISOPaths"}, false};

View File

@ -52,6 +52,17 @@ namespace UICommon
namespace namespace
{ {
const std::string EMPTY_STRING; const std::string EMPTY_STRING;
bool UseGameCovers()
{
#ifdef ANDROID
// Android has its own code for handling covers, written completely in Java.
// It's best if we disable the C++ cover code on Android to avoid duplicated data and such.
return false;
#else
return Config::Get(Config::MAIN_USE_GAME_COVERS);
#endif
}
} // Anonymous namespace } // Anonymous namespace
DiscIO::Language GameFile::GetConfigLanguage() const DiscIO::Language GameFile::GetConfigLanguage() const
@ -169,7 +180,7 @@ bool GameFile::IsValid() const
bool GameFile::CustomCoverChanged() bool GameFile::CustomCoverChanged()
{ {
if (!m_custom_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS)) if (!m_custom_cover.buffer.empty() || !UseGameCovers())
return false; return false;
std::string path, name; std::string path, name;
@ -196,7 +207,7 @@ bool GameFile::CustomCoverChanged()
void GameFile::DownloadDefaultCover() void GameFile::DownloadDefaultCover()
{ {
if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS)) if (!m_default_cover.buffer.empty() || !UseGameCovers())
return; return;
const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP; const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;
@ -262,7 +273,7 @@ void GameFile::DownloadDefaultCover()
bool GameFile::DefaultCoverChanged() bool GameFile::DefaultCoverChanged()
{ {
if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS)) if (!m_default_cover.buffer.empty() || !UseGameCovers())
return false; return false;
const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP; const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;