android: Turn GameInfo into a class (#6494)
By only loading data from disk when creating an instance of this new class instead of on every method call, we save a lot of file open operations, which due to SAF are very expensive. This should noticeably speed up game list scanning. No intended change in what metadata is shown.
This commit is contained in:
parent
2b8610fcc4
commit
62792b6b0e
10 changed files with 127 additions and 168 deletions
|
@ -128,30 +128,6 @@ public final class NativeLibrary {
|
|||
|
||||
public static native void InitGameIni(String gameID);
|
||||
|
||||
/**
|
||||
* Gets the embedded icon within the given ROM.
|
||||
*
|
||||
* @param filename the file path to the ROM.
|
||||
* @return an integer array containing the color data for the icon.
|
||||
*/
|
||||
public static native int[] GetIcon(String filename);
|
||||
|
||||
/**
|
||||
* Gets the embedded title of the given ISO/ROM.
|
||||
*
|
||||
* @param filename The file path to the ISO/ROM.
|
||||
* @return the embedded title of the ISO/ROM.
|
||||
*/
|
||||
public static native String GetTitle(String filename);
|
||||
|
||||
public static native String GetDescription(String filename);
|
||||
|
||||
public static native String GetGameId(String filename);
|
||||
|
||||
public static native String GetRegions(String filename);
|
||||
|
||||
public static native String GetCompany(String filename);
|
||||
|
||||
public static native String GetGitRevision();
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.citra.citra_emu.utils.FileUtil;
|
|||
import org.citra.citra_emu.utils.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
@ -206,27 +207,26 @@ public final class GameDatabase extends SQLiteOpenHelper {
|
|||
}
|
||||
|
||||
private static void attemptToAddGame(SQLiteDatabase database, String filePath) {
|
||||
String name = NativeLibrary.GetTitle(filePath);
|
||||
GameInfo gameInfo;
|
||||
try {
|
||||
gameInfo = new GameInfo(filePath);
|
||||
} catch (IOException e) {
|
||||
gameInfo = null;
|
||||
}
|
||||
|
||||
String name = gameInfo != null ? gameInfo.getTitle() : "";
|
||||
|
||||
// If the game's title field is empty, use the filename.
|
||||
if (name.isEmpty()) {
|
||||
name = filePath.substring(filePath.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
String gameId = NativeLibrary.GetGameId(filePath);
|
||||
|
||||
// If the game's ID field is empty, use the filename without extension.
|
||||
if (gameId.isEmpty()) {
|
||||
gameId = filePath.substring(filePath.lastIndexOf("/") + 1,
|
||||
filePath.lastIndexOf("."));
|
||||
}
|
||||
|
||||
ContentValues game = Game.asContentValues(name,
|
||||
NativeLibrary.GetDescription(filePath).replace("\n", " "),
|
||||
NativeLibrary.GetRegions(filePath),
|
||||
filePath.replace("\n", " "),
|
||||
gameInfo != null ? gameInfo.getRegions() : "Invalid region",
|
||||
filePath,
|
||||
gameId,
|
||||
NativeLibrary.GetCompany(filePath));
|
||||
filePath,
|
||||
gameInfo != null ? gameInfo.getCompany() : "");
|
||||
|
||||
// Try to update an existing game first.
|
||||
int rowsMatched = database.update(TABLE_NAME_GAMES, // Which table to update.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.citra.citra_emu.model;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GameInfo {
|
||||
@Keep
|
||||
private final long mPointer;
|
||||
|
||||
@Keep
|
||||
public GameInfo(String path) throws IOException {
|
||||
mPointer = initialize(path);
|
||||
if (mPointer == 0L) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
private static native long initialize(String path);
|
||||
|
||||
@Override
|
||||
protected native void finalize();
|
||||
|
||||
@NonNull
|
||||
public native String getTitle();
|
||||
|
||||
@NonNull
|
||||
public native String getRegions();
|
||||
|
||||
@NonNull
|
||||
public native String getCompany();
|
||||
|
||||
@Nullable
|
||||
public native int[] getIcon();
|
||||
}
|
|
@ -7,7 +7,9 @@ import com.squareup.picasso.Request;
|
|||
import com.squareup.picasso.RequestHandler;
|
||||
|
||||
import org.citra.citra_emu.NativeLibrary;
|
||||
import org.citra.citra_emu.model.GameInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class GameIconRequestHandler extends RequestHandler {
|
||||
|
@ -18,8 +20,14 @@ public class GameIconRequestHandler extends RequestHandler {
|
|||
|
||||
@Override
|
||||
public Result load(Request request, int networkPolicy) {
|
||||
int[] vector;
|
||||
try {
|
||||
String url = request.uri.toString();
|
||||
int[] vector = NativeLibrary.GetIcon(url);
|
||||
vector = new GameInfo(url).getIcon();
|
||||
} catch (IOException e) {
|
||||
vector = null;
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.RGB_565);
|
||||
bitmap.copyPixelsFromBuffer(IntBuffer.wrap(vector));
|
||||
return new Result(bitmap, Picasso.LoadedFrom.DISK);
|
||||
|
|
|
@ -20,7 +20,6 @@ add_library(citra-android SHARED
|
|||
emu_window/emu_window.cpp
|
||||
emu_window/emu_window.h
|
||||
game_info.cpp
|
||||
game_info.h
|
||||
game_settings.cpp
|
||||
game_settings.h
|
||||
id_cache.cpp
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/loader/smdh.h"
|
||||
#include "jni/game_info.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/id_cache.h"
|
||||
|
||||
namespace GameInfo {
|
||||
namespace {
|
||||
|
||||
std::vector<u8> GetSMDHData(std::string physical_name) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
std::vector<u8> GetSMDHData(const std::string& path) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(path);
|
||||
if (!loader) {
|
||||
return {};
|
||||
}
|
||||
|
@ -51,55 +52,55 @@ std::vector<u8> GetSMDHData(std::string physical_name) {
|
|||
return smdh;
|
||||
}
|
||||
|
||||
std::u16string GetTitle(std::string physical_name) {
|
||||
Loader::SMDH::TitleLanguage language = Loader::SMDH::TitleLanguage::English;
|
||||
std::vector<u8> smdh_data = GetSMDHData(physical_name);
|
||||
} // namespace
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||
// SMDH is not valid, return null
|
||||
return {};
|
||||
extern "C" {
|
||||
|
||||
static Loader::SMDH* GetPointer(JNIEnv* env, jobject obj) {
|
||||
return reinterpret_cast<Loader::SMDH*>(env->GetLongField(obj, IDCache::GetGameInfoPointer()));
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_org_citra_citra_1emu_model_GameInfo_initialize(JNIEnv* env, jclass,
|
||||
jstring j_path) {
|
||||
std::vector<u8> smdh_data = GetSMDHData(GetJString(env, j_path));
|
||||
|
||||
Loader::SMDH* smdh = nullptr;
|
||||
if (Loader::IsValidSMDH(smdh_data)) {
|
||||
smdh = new Loader::SMDH;
|
||||
memcpy(smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
}
|
||||
return reinterpret_cast<jlong>(smdh);
|
||||
}
|
||||
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_model_GameInfo_finalize(JNIEnv* env, jobject obj) {
|
||||
delete GetPointer(env, obj);
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_model_GameInfo_getTitle(JNIEnv* env, jobject obj) {
|
||||
Loader::SMDH* smdh = GetPointer(env, obj);
|
||||
Loader::SMDH::TitleLanguage language = Loader::SMDH::TitleLanguage::English;
|
||||
|
||||
// Get the title from SMDH in UTF-16 format
|
||||
std::u16string title{
|
||||
reinterpret_cast<char16_t*>(smdh.titles[static_cast<int>(language)].long_title.data())};
|
||||
reinterpret_cast<char16_t*>(smdh->titles[static_cast<size_t>(language)].long_title.data())};
|
||||
|
||||
return title;
|
||||
return ToJString(env, Common::UTF16ToUTF8(title).data());
|
||||
}
|
||||
|
||||
std::u16string GetPublisher(std::string physical_name) {
|
||||
jstring Java_org_citra_citra_1emu_model_GameInfo_getCompany(JNIEnv* env, jobject obj) {
|
||||
Loader::SMDH* smdh = GetPointer(env, obj);
|
||||
Loader::SMDH::TitleLanguage language = Loader::SMDH::TitleLanguage::English;
|
||||
std::vector<u8> smdh_data = GetSMDHData(physical_name);
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||
// SMDH is not valid, return null
|
||||
return {};
|
||||
}
|
||||
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
// Get the Publisher's name from SMDH in UTF-16 format
|
||||
char16_t* publisher;
|
||||
publisher =
|
||||
reinterpret_cast<char16_t*>(smdh.titles[static_cast<int>(language)].publisher.data());
|
||||
reinterpret_cast<char16_t*>(smdh->titles[static_cast<size_t>(language)].publisher.data());
|
||||
|
||||
return publisher;
|
||||
return ToJString(env, Common::UTF16ToUTF8(publisher).data());
|
||||
}
|
||||
|
||||
std::string GetRegions(std::string physical_name) {
|
||||
std::vector<u8> smdh_data = GetSMDHData(physical_name);
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||
// SMDH is not valid, return "Invalid region"
|
||||
return "Invalid region";
|
||||
}
|
||||
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
jstring Java_org_citra_citra_1emu_model_GameInfo_getRegions(JNIEnv* env, jobject obj) {
|
||||
Loader::SMDH* smdh = GetPointer(env, obj);
|
||||
|
||||
using GameRegion = Loader::SMDH::GameRegion;
|
||||
static const std::map<GameRegion, const char*> regions_map = {
|
||||
|
@ -107,10 +108,10 @@ std::string GetRegions(std::string physical_name) {
|
|||
{GameRegion::Europe, "Europe"}, {GameRegion::Australia, "Australia"},
|
||||
{GameRegion::China, "China"}, {GameRegion::Korea, "Korea"},
|
||||
{GameRegion::Taiwan, "Taiwan"}};
|
||||
std::vector<GameRegion> regions = smdh.GetRegions();
|
||||
std::vector<GameRegion> regions = smdh->GetRegions();
|
||||
|
||||
if (regions.empty()) {
|
||||
return "Invalid region";
|
||||
return ToJString(env, "Invalid region");
|
||||
}
|
||||
|
||||
const bool region_free =
|
||||
|
@ -119,7 +120,7 @@ std::string GetRegions(std::string physical_name) {
|
|||
});
|
||||
|
||||
if (region_free) {
|
||||
return "Region free";
|
||||
return ToJString(env, "Region free");
|
||||
}
|
||||
|
||||
const std::string separator = ", ";
|
||||
|
@ -128,23 +129,22 @@ std::string GetRegions(std::string physical_name) {
|
|||
result += separator + regions_map.at(*region);
|
||||
}
|
||||
|
||||
return result;
|
||||
return ToJString(env, result);
|
||||
}
|
||||
|
||||
std::vector<u16> GetIcon(std::string physical_name) {
|
||||
std::vector<u8> smdh_data = GetSMDHData(physical_name);
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||
// SMDH is not valid, return null
|
||||
return std::vector<u16>(0, 0);
|
||||
}
|
||||
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
jintArray Java_org_citra_citra_1emu_model_GameInfo_getIcon(JNIEnv* env, jobject obj) {
|
||||
Loader::SMDH* smdh = GetPointer(env, obj);
|
||||
|
||||
// Always get a 48x48(large) icon
|
||||
std::vector<u16> icon_data = smdh.GetIcon(true);
|
||||
return icon_data;
|
||||
}
|
||||
std::vector<u16> icon_data = smdh->GetIcon(true);
|
||||
if (icon_data.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace GameInfo
|
||||
jintArray icon = env->NewIntArray(static_cast<jsize>(icon_data.size() / 2));
|
||||
env->SetIntArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||
reinterpret_cast<jint*>(icon_data.data()));
|
||||
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace GameInfo {
|
||||
std::vector<u8> GetSMDHData(std::string physical_name);
|
||||
|
||||
std::u16string GetTitle(std::string physical_name);
|
||||
|
||||
std::u16string GetPublisher(std::string physical_name);
|
||||
|
||||
std::string GetRegions(std::string physical_name);
|
||||
|
||||
std::vector<u16> GetIcon(std::string physical_name);
|
||||
} // namespace GameInfo
|
|
@ -40,6 +40,8 @@ static jclass s_cheat_class;
|
|||
static jfieldID s_cheat_pointer;
|
||||
static jmethodID s_cheat_constructor;
|
||||
|
||||
static jfieldID s_game_info_pointer;
|
||||
|
||||
static std::unordered_map<VideoCore::LoadCallbackStage, jobject> s_java_load_callback_stages;
|
||||
|
||||
namespace IDCache {
|
||||
|
@ -135,6 +137,10 @@ jmethodID GetCheatConstructor() {
|
|||
return s_cheat_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetGameInfoPointer() {
|
||||
return s_game_info_pointer;
|
||||
}
|
||||
|
||||
jobject GetJavaLoadCallbackStage(VideoCore::LoadCallbackStage stage) {
|
||||
const auto it = s_java_load_callback_stages.find(stage);
|
||||
ASSERT_MSG(it != s_java_load_callback_stages.end(), "Invalid LoadCallbackStage: {}", stage);
|
||||
|
@ -205,6 +211,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||
s_cheat_constructor = env->GetMethodID(cheat_class, "<init>", "(J)V");
|
||||
env->DeleteLocalRef(cheat_class);
|
||||
|
||||
// Initialize GameInfo
|
||||
const jclass game_info_class = env->FindClass("org/citra/citra_emu/model/GameInfo");
|
||||
s_game_info_pointer = env->GetFieldID(game_info_class, "mPointer", "J");
|
||||
env->DeleteLocalRef(game_info_class);
|
||||
|
||||
// Initialize LoadCallbackStage map
|
||||
const auto to_java_load_callback_stage = [env](const std::string& stage) {
|
||||
jclass load_callback_stage_class = IDCache::GetDiskCacheLoadCallbackStageClass();
|
||||
|
|
|
@ -34,6 +34,8 @@ jclass GetCheatClass();
|
|||
jfieldID GetCheatPointer();
|
||||
jmethodID GetCheatConstructor();
|
||||
|
||||
jfieldID GetGameInfoPointer();
|
||||
|
||||
jobject GetJavaLoadCallbackStage(VideoCore::LoadCallbackStage stage);
|
||||
|
||||
} // namespace IDCache
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "jni/camera/still_image_camera.h"
|
||||
#include "jni/config.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "jni/game_info.h"
|
||||
#include "jni/game_settings.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "jni/input_manager.h"
|
||||
|
@ -436,60 +435,6 @@ void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved(JNIEnv* env,
|
|||
window->OnTouchMoved((int)x, (int)y);
|
||||
}
|
||||
|
||||
jintArray Java_org_citra_citra_1emu_NativeLibrary_GetIcon(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_file) {
|
||||
std::string filepath = GetJString(env, j_file);
|
||||
|
||||
std::vector<u16> icon_data = GameInfo::GetIcon(filepath);
|
||||
if (icon_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
jintArray icon = env->NewIntArray(static_cast<jsize>(icon_data.size() / 2));
|
||||
env->SetIntArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||
reinterpret_cast<jint*>(icon_data.data()));
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_NativeLibrary_GetTitle(JNIEnv* env, [[maybe_unused]] jclass clazz,
|
||||
jstring j_filename) {
|
||||
std::string filepath = GetJString(env, j_filename);
|
||||
auto Title = GameInfo::GetTitle(filepath);
|
||||
return env->NewStringUTF(Common::UTF16ToUTF8(Title).data());
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_NativeLibrary_GetDescription(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_filename) {
|
||||
return j_filename;
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_filename) {
|
||||
return j_filename;
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_filename) {
|
||||
std::string filepath = GetJString(env, j_filename);
|
||||
|
||||
std::string regions = GameInfo::GetRegions(filepath);
|
||||
|
||||
return env->NewStringUTF(regions.c_str());
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_filename) {
|
||||
std::string filepath = GetJString(env, j_filename);
|
||||
auto publisher = GameInfo::GetPublisher(filepath);
|
||||
return env->NewStringUTF(Common::UTF16ToUTF8(publisher).data());
|
||||
}
|
||||
|
||||
jstring Java_org_citra_citra_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz) {
|
||||
return nullptr;
|
||||
|
|
Loading…
Reference in a new issue