diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java index 5d4601b28..37edbb1a9 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java @@ -129,6 +129,8 @@ public final class NativeLibrary { 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(); diff --git a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java index 3320b7283..a50c48a93 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java @@ -97,6 +97,7 @@ public final class GameAdapter extends RecyclerView.Adapter impl holder.path = mCursor.getString(GameDatabase.GAME_COLUMN_PATH); holder.title = mCursor.getString(GameDatabase.GAME_COLUMN_TITLE); holder.description = mCursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION); + holder.regions = mCursor.getString(GameDatabase.GAME_COLUMN_REGIONS); holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY); final int backgroundColorId = isValidGame(holder.path) ? R.color.card_view_background : R.color.card_view_disabled; diff --git a/src/android/app/src/main/java/org/citra/citra_emu/model/Game.java b/src/android/app/src/main/java/org/citra/citra_emu/model/Game.java index e13071447..f929f8476 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/model/Game.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/model/Game.java @@ -12,17 +12,19 @@ public final class Game { private String mPath; private String mGameId; private String mCompany; + private String mRegions; - public Game(String title, String description, String path, + public Game(String title, String description, String regions, String path, String gameId, String company) { mTitle = title; mDescription = description; + mRegions = regions; mPath = path; mGameId = gameId; mCompany = company; } - public static ContentValues asContentValues(String title, String description, String path, String gameId, String company) { + public static ContentValues asContentValues(String title, String description, String regions, String path, String gameId, String company) { ContentValues values = new ContentValues(); if (gameId.isEmpty()) { @@ -32,6 +34,7 @@ public final class Game { values.put(GameDatabase.KEY_GAME_TITLE, title); values.put(GameDatabase.KEY_GAME_DESCRIPTION, description); + values.put(GameDatabase.KEY_GAME_REGIONS, regions); values.put(GameDatabase.KEY_GAME_PATH, path); values.put(GameDatabase.KEY_GAME_ID, gameId); values.put(GameDatabase.KEY_GAME_COMPANY, company); @@ -42,6 +45,7 @@ public final class Game { public static Game fromCursor(Cursor cursor) { return new Game(cursor.getString(GameDatabase.GAME_COLUMN_TITLE), cursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION), + cursor.getString(GameDatabase.GAME_COLUMN_REGIONS), cursor.getString(GameDatabase.GAME_COLUMN_PATH), cursor.getString(GameDatabase.GAME_COLUMN_GAME_ID), cursor.getString(GameDatabase.GAME_COLUMN_COMPANY)); @@ -59,6 +63,10 @@ public final class Game { return mCompany; } + public String getRegions() { + return mRegions; + } + public String getPath() { return mPath; } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/model/GameDatabase.java b/src/android/app/src/main/java/org/citra/citra_emu/model/GameDatabase.java index d81e18d6f..33fc7b349 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/model/GameDatabase.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/model/GameDatabase.java @@ -25,6 +25,7 @@ public final class GameDatabase extends SQLiteOpenHelper { public static final int GAME_COLUMN_PATH = 1; public static final int GAME_COLUMN_TITLE = 2; public static final int GAME_COLUMN_DESCRIPTION = 3; + public static final int GAME_COLUMN_REGIONS = 4; public static final int GAME_COLUMN_GAME_ID = 5; public static final int GAME_COLUMN_COMPANY = 6; public static final int FOLDER_COLUMN_PATH = 1; @@ -32,6 +33,7 @@ public final class GameDatabase extends SQLiteOpenHelper { public static final String KEY_GAME_PATH = "path"; public static final String KEY_GAME_TITLE = "title"; public static final String KEY_GAME_DESCRIPTION = "description"; + public static final String KEY_GAME_REGIONS = "regions"; public static final String KEY_GAME_ID = "game_id"; public static final String KEY_GAME_COMPANY = "company"; public static final String KEY_FOLDER_PATH = "path"; @@ -51,6 +53,7 @@ public final class GameDatabase extends SQLiteOpenHelper { + KEY_GAME_PATH + TYPE_STRING + SEPARATOR + KEY_GAME_TITLE + TYPE_STRING + SEPARATOR + KEY_GAME_DESCRIPTION + TYPE_STRING + SEPARATOR + + KEY_GAME_REGIONS + TYPE_STRING + SEPARATOR + KEY_GAME_ID + TYPE_STRING + SEPARATOR + KEY_GAME_COMPANY + TYPE_STRING + ")"; @@ -186,6 +189,7 @@ public final class GameDatabase extends SQLiteOpenHelper { ContentValues game = Game.asContentValues(name, NativeLibrary.GetDescription(filePath).replace("\n", " "), + NativeLibrary.GetRegions(filePath), filePath, gameId, NativeLibrary.GetCompany(filePath)); diff --git a/src/android/app/src/main/java/org/citra/citra_emu/viewholders/GameViewHolder.java b/src/android/app/src/main/java/org/citra/citra_emu/viewholders/GameViewHolder.java index 4d7a92604..a4d90650c 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/viewholders/GameViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/viewholders/GameViewHolder.java @@ -25,6 +25,7 @@ public class GameViewHolder extends RecyclerView.ViewHolder { public String path; public String title; public String description; + public String regions; public String company; public GameViewHolder(View itemView) { diff --git a/src/android/app/src/main/jni/game_info.cpp b/src/android/app/src/main/jni/game_info.cpp index 00361c83c..169f96190 100644 --- a/src/android/app/src/main/jni/game_info.cpp +++ b/src/android/app/src/main/jni/game_info.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "common/string_util.h" #include "core/hle/service/am/am.h" @@ -89,13 +90,60 @@ char16_t* GetPublisher(std::string physical_name) { // Get the Publisher's name from SMDH in UTF-16 format char16_t* publisher; publisher = - reinterpret_cast(smdh.titles[static_cast(language)].publisher.data()); + reinterpret_cast(smdh.titles[static_cast(language)].publisher.data()); LOG_INFO(Frontend, "Publisher: {}", Common::UTF16ToUTF8(publisher)); return publisher; } +std::string GetRegions(std::string physical_name) { + std::vector smdh_data = GetSMDHData(physical_name); + + if (!Loader::IsValidSMDH(smdh_data)) { + // SMDH is not valid, return null + LOG_ERROR(Frontend, "SMDH is Invalid"); + return "Invalid region"; + } + + Loader::SMDH smdh; + memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); + + using GameRegion = Loader::SMDH::GameRegion; + static const std::map regions_map = { + {GameRegion::Japan, "Japan"}, + {GameRegion::NorthAmerica, "North America"}, + {GameRegion::Europe, "Europe"}, + {GameRegion::Australia, "Australia"}, + {GameRegion::China, "China"}, + {GameRegion::Korea, "Korea"}, + {GameRegion::Taiwan, "Taiwan"}}; + std::vector regions = smdh.GetRegions(); + + if (regions.empty()) { + return "Invalid region"; + } + + const bool region_free = + std::all_of(regions_map.begin(), regions_map.end(), [®ions](const auto& it) { + return std::find(regions.begin(), regions.end(), it.first) != regions.end(); + }); + + if (region_free) { + return "Region free"; + } + + const std::string separator = ", "; + std::string result = regions_map.at(regions.front()); + for (auto region = ++regions.begin(); region != regions.end(); ++region) { + result += separator + regions_map.at(*region); + } + + LOG_INFO(Frontend, "Regions: {}", result); + + return result; +} + std::vector GetIcon(std::string physical_name) { std::vector smdh_data = GetSMDHData(physical_name); diff --git a/src/android/app/src/main/jni/game_info.h b/src/android/app/src/main/jni/game_info.h index a3712851c..dce523e50 100644 --- a/src/android/app/src/main/jni/game_info.h +++ b/src/android/app/src/main/jni/game_info.h @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include "common/common_types.h" @@ -13,5 +14,7 @@ char16_t* GetTitle(std::string physical_name); char16_t* GetPublisher(std::string physical_name); +std::string GetRegions(std::string physical_name); + std::vector GetIcon(std::string physical_name); } // namespace GameInfo diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index ff1dc6e79..67b311039 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -306,6 +306,16 @@ jstring Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIEnv* env, 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) { diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index f0a3c2bd1..5eafac129 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -57,6 +57,9 @@ JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIE jclass clazz, jstring j_filename); +JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env, jclass clazz, + jstring j_filename); + JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNIEnv* env, jclass clazz, jstring j_filename);