Skip to content

Commit

Permalink
Merge pull request #3326 from EasyRPG-NewFeatures/Primekick-DetectPro…
Browse files Browse the repository at this point in the history
…jectType

QoL: detect unsupported engines
  • Loading branch information
fdelapena authored Jan 21, 2025
2 parents a557aa6 + 2c19a17 commit f787328
Show file tree
Hide file tree
Showing 16 changed files with 430 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,28 +189,30 @@ Java_org_easyrpg_player_game_1browser_GameScanner_findGames(JNIEnv *env, jclass,
auto root = FileFinder::Root().Create(spath);
root.ClearCache();

std::vector<FilesystemView> fs_list = FileFinder::FindGames(root);
auto ge_list = FileFinder::FindGames(root);

jclass jgame_class = env->FindClass("org/easyrpg/player/game_browser/Game");
jobjectArray jgame_array = env->NewObjectArray(fs_list.size(), jgame_class, nullptr);
jobjectArray jgame_array = env->NewObjectArray(ge_list.size(), jgame_class, nullptr);

if (fs_list.empty()) {
if (ge_list.empty()) {
// No games found
return jgame_array;
}

jmethodID jgame_constructor = env->GetMethodID(jgame_class, "<init>", "(Ljava/lang/String;Ljava/lang/String;[B)V");
jmethodID jgame_constructor_unsupported = env->GetMethodID(jgame_class, "<init>", "(I)V");
jmethodID jgame_constructor_supported = env->GetMethodID(jgame_class, "<init>", "(Ljava/lang/String;Ljava/lang/String;[BI)V");

std::string root_path = FileFinder::GetFullFilesystemPath(root);
bool game_in_main_dir = false;
if (fs_list.size() == 1) {
if (root_path == FileFinder::GetFullFilesystemPath(fs_list[0])) {
if (ge_list.size() == 1) {
if (root_path == FileFinder::GetFullFilesystemPath(ge_list[0].fs)) {
game_in_main_dir = true;
}
}

for (size_t i = 0; i < fs_list.size(); ++i) {
auto& fs = fs_list[i];
for (size_t i = 0; i < ge_list.size(); ++i) {
auto& ge = ge_list[i];
auto& fs = ge.fs;

std::string full_path = FileFinder::GetFullFilesystemPath(fs);
std::string game_dir_name;
Expand All @@ -222,6 +224,19 @@ Java_org_easyrpg_player_game_1browser_GameScanner_findGames(JNIEnv *env, jclass,
game_dir_name = std::get<1>(FileFinder::GetPathAndFilename(fs.GetFullPath()));
}

// If game is unsupported, create a Game object with only directory name as title and project type id and continue early
if (ge.type > FileFinder::ProjectType::Supported) {
jobject jgame_object = env->NewObject(jgame_class, jgame_constructor_unsupported, (int)ge.type);

// Use the directory name as the title
jstring jfolder = env->NewStringUTF(game_dir_name.c_str());
jmethodID jset_folder_name_method = env->GetMethodID(jgame_class, "setGameFolderName", "(Ljava/lang/String;)V");
env->CallVoidMethod(jgame_object, jset_folder_name_method, jfolder);

env->SetObjectArrayElement(jgame_array, i, jgame_object);
continue;
}

std::string save_path;
if (!fs.IsFeatureSupported(Filesystem::Feature::Write)) {
// Is an archive and needs a redirected save path
Expand All @@ -236,7 +251,7 @@ Java_org_easyrpg_player_game_1browser_GameScanner_findGames(JNIEnv *env, jclass,
}

// Append subdirectory when the archive contains more than one game
if (fs_list.size() > 1) {
if (ge_list.size() > 1) {
save_path += FileFinder::GetFullFilesystemPath(fs).substr(root_path.size());
}

Expand Down Expand Up @@ -348,7 +363,7 @@ Java_org_easyrpg_player_game_1browser_GameScanner_findGames(JNIEnv *env, jclass,
/* Create an instance of "Game" */
jstring jgame_path = env->NewStringUTF(("content://" + full_path).c_str());
jstring jsave_path = env->NewStringUTF(save_path.c_str());
jobject jgame_object = env->NewObject(jgame_class, jgame_constructor, jgame_path, jsave_path, title_image);
jobject jgame_object = env->NewObject(jgame_class, jgame_constructor_supported, jgame_path, jsave_path, title_image, (int)ge.type);

if (title_from_ini) {
// Store the raw string in the Game instance so it can be reencoded later via user setting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.easyrpg.player.game_browser.Game;
import org.easyrpg.player.game_browser.GameBrowserActivity;
import org.easyrpg.player.game_browser.GameBrowserHelper;
import org.easyrpg.player.game_browser.ProjectType;
import org.easyrpg.player.player.AssetUtils;
import org.easyrpg.player.settings.SettingsManager;

Expand Down Expand Up @@ -122,7 +123,7 @@ private void startGameStandalone() {
String saveDir = getExternalFilesDir(null).getAbsolutePath() + "/Save";
new File(saveDir).mkdirs();

Game project = new Game(gameDir, saveDir, null);
Game project = new Game(gameDir, saveDir, null, ProjectType.SUPPORTED.ordinal());
project.setStandalone(true);
GameBrowserHelper.launchGame(this, project);
finish();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@

public class Game implements Comparable<Game> {
final static char escapeCode = '\u0001';
final static String cacheVersion = "1";
/** The title shown in the Game Browser */
private String title;
private String title = "";
/** Bytes of the title string in an unspecified encoding */
private byte[] titleRaw = null;
/** Human readable version of the game directory. Shown in the game browser
* when the specific setting is enabled.
*/
private String gameFolderName;
private String gameFolderName = "";
/** Path to the game folder (forwarded via --project-path */
private final String gameFolderPath;
/** Relative path to the save directory, made absolute by launchGame (forwarded via --save-path) */
Expand All @@ -35,9 +34,17 @@ public class Game implements Comparable<Game> {
private Bitmap titleScreen = null;
/** Game is launched from the APK via standalone mode */
private boolean standalone = false;
/** Associated project type. Used to differentiane between supported engines and known but unsupported engines */
private ProjectType projectType = ProjectType.UNKNOWN;

public Game(String gameFolderPath, String saveFolder, byte[] titleScreen) {
public Game(int projectTypeId) {
this.projectType = ProjectType.getProjectType(projectTypeId);
this.gameFolderPath = "";
}

public Game(String gameFolderPath, String saveFolder, byte[] titleScreen, int projectTypeId) {
this.gameFolderPath = gameFolderPath;
this.projectType = ProjectType.getProjectType(projectTypeId);

// is only relative here, launchGame will put this in the "saves" directory
if (!saveFolder.isEmpty()) {
Expand All @@ -57,7 +64,7 @@ public String getDisplayTitle() {
return customTitle;
}

if (SettingsManager.getGameBrowserLabelMode() == 0) {
if (SettingsManager.getGameBrowserLabelMode() == 0 && !getTitle().isEmpty()) {
return getTitle();
} else {
return gameFolderName;
Expand Down Expand Up @@ -121,6 +128,14 @@ private boolean isFavoriteFromSettings() {

@Override
public int compareTo(Game game) {
// Unsupported games last
if (this.projectType == ProjectType.SUPPORTED && game.projectType.ordinal() > ProjectType.SUPPORTED.ordinal()) {
return -1;
}
if (this.projectType.ordinal() > ProjectType.SUPPORTED.ordinal() && game.projectType == ProjectType.SUPPORTED) {
return 1;
}
// Favorites first
if (this.isFavorite() && !game.isFavorite()) {
return -1;
}
Expand Down Expand Up @@ -176,31 +191,39 @@ public String toString() {
public static Game fromCacheEntry(Context context, String cache) {
String[] entries = cache.split(String.valueOf(escapeCode));

if (entries.length != 7 || !entries[0].equals(cacheVersion)) {
if (entries.length != 7) {
return null;
}

String savePath = entries[1];
DocumentFile gameFolder = DocumentFile.fromTreeUri(context, Uri.parse(entries[2]));
int parsedProjectType = Integer.parseInt(entries[6]);
if (parsedProjectType > ProjectType.SUPPORTED.ordinal()) {
// Unsupported game
Game g = new Game(parsedProjectType);
g.setGameFolderName(entries[2]);
return g;
}

String savePath = entries[0];
DocumentFile gameFolder = DocumentFile.fromTreeUri(context, Uri.parse(entries[1]));
if (gameFolder == null) {
return null;
}

String gameFolderName = entries[3];
String gameFolderName = entries[2];

String title = entries[4];
String title = entries[3];

byte[] titleRaw = null;
if (!entries[5].equals("null")) {
titleRaw = Base64.decode(entries[5], 0);
if (!entries[4].equals("null")) {
titleRaw = Base64.decode(entries[4], 0);
}

byte[] titleScreen = null;
if (!entries[6].equals("null")) {
titleScreen = Base64.decode(entries[6], 0);
if (!entries[5].equals("null")) {
titleScreen = Base64.decode(entries[5], 0);
}

Game g = new Game(entries[2], savePath, titleScreen);
Game g = new Game(entries[1], savePath, titleScreen, parsedProjectType);
g.setTitle(title);
g.titleRaw = titleRaw;

Expand All @@ -216,33 +239,40 @@ public static Game fromCacheEntry(Context context, String cache) {
public String toCacheEntry() {
StringBuilder sb = new StringBuilder();

// Cache structure: savePath | gameFolderPath | title | titleRaw | titleScreen
sb.append(cacheVersion); // 0
// Cache structure: savePath | gameFolderPath | gameFolderName | title | titleRaw | titleScreen | projectType
sb.append(savePath); // 0
sb.append(escapeCode);
sb.append(savePath); // 1
sb.append(gameFolderPath); // 1
sb.append(escapeCode);
sb.append(gameFolderPath); // 2
sb.append(gameFolderName); // 2
sb.append(escapeCode);
sb.append(gameFolderName); // 3
sb.append(title); // 3
sb.append(escapeCode);
sb.append(title); // 4
sb.append(escapeCode);
if (titleRaw != null) { // 5
if (titleRaw != null) { // 4
sb.append(Base64.encodeToString(titleRaw, Base64.NO_WRAP));
} else {
sb.append("null");
}
sb.append(escapeCode);
if (titleScreen != null) { // 6
if (titleScreen != null) { // 5
ByteArrayOutputStream baos = new ByteArrayOutputStream();
titleScreen.compress(Bitmap.CompressFormat.PNG, 90, baos);
byte[] b = baos.toByteArray();
sb.append(Base64.encodeToString(b, Base64.NO_WRAP));
} else {
sb.append("null");
}
sb.append(escapeCode);
sb.append(projectType.ordinal()); // 6

return sb.toString();
}

public boolean isProjectTypeUnsupported() {
return this.projectType.ordinal() > ProjectType.SUPPORTED.ordinal();
}

public String getProjectTypeLabel() {
return this.projectType.getLabel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
Expand All @@ -16,13 +15,11 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
Expand Down Expand Up @@ -333,6 +330,24 @@ public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Game game = gameList.get(position);

if (game.isProjectTypeUnsupported()) {
// Title
holder.title.setText(game.getDisplayTitle());

// Subtitle - engine unsupported message
holder.subtitle.setText(activity.getResources().getString(R.string.unsupported_engine_card).replace("$ENGINE", game.getProjectTypeLabel()));

// Hide settings button
holder.settingsButton.setVisibility(View.INVISIBLE);

// Add click listeners
holder.title.setOnClickListener(v -> showUnsupportedProjectTypeExplanation(activity, game.getProjectTypeLabel()));
holder.subtitle.setOnClickListener(v -> showUnsupportedProjectTypeExplanation(activity, game.getProjectTypeLabel()));
holder.titleScreen.setOnClickListener(v -> showUnsupportedProjectTypeExplanation(activity, game.getProjectTypeLabel()));

return;
}

// Title
holder.title.setText(game.getDisplayTitle());
holder.title.setOnClickListener(v -> launchGame(position, false));
Expand Down Expand Up @@ -448,14 +463,28 @@ public void renameGame(final Context context, final ViewHolder holder, final Gam
builder.show();
}

private void showUnsupportedProjectTypeExplanation(final Context context, String projectType) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);

String message = context.getString(R.string.unsupported_engine_explanation).replace("$ENGINE", projectType);

builder
.setTitle(R.string.unsupported_engine_title)
.setMessage(message)
.setNeutralButton(R.string.ok, null);
builder.show();
}

public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView subtitle;
public ImageView titleScreen;
public ImageButton settingsButton, favoriteButton;

public ViewHolder(View v) {
super(v);
this.title = v.findViewById(R.id.title);
this.subtitle = v.findViewById(R.id.subtitle);
this.titleScreen = v.findViewById(R.id.screen);
this.settingsButton = v.findViewById(R.id.game_browser_thumbnail_option_button);
this.favoriteButton = v.findViewById(R.id.game_browser_thumbnail_favorite_button);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private void scanGames(Activity activity){
private int scanFolderHash(Context context, Uri folderURI) {
StringBuilder sb = new StringBuilder();

sb.append("2"); // Bump this when the cache layout changes
sb.append("3"); // Bump this when the cache layout changes
for (String[] array : Helper.listChildrenDocuments(context, folderURI)) {
sb.append(array[0]);
sb.append(array[1]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.easyrpg.player.game_browser;

import android.content.Context;

import org.easyrpg.player.R;

public enum ProjectType {
UNKNOWN("Unknown"),
SUPPORTED("Supported"),
RPG_MAKER_XP("RPG Maker XP"),
RPG_MAKER_VX("RPG Maker VX"),
RPG_MAKER_VX_ACE("RPG Maker VX Ace"),
RPG_MAKER_MV_MZ("RPG Maker MV/MZ"),
WOLF_RPG_EDITOR("Wolf RPG Editor"),
ENCRYPTED_2K3_MANIACS("Encrypted 2k3 (Maniacs Patch)"),
RPG_MAKER_95("RPG Maker 95"),
SIM_RPG_MAKER_95("Sim RPG Maker 95");

private final String label;

ProjectType(String label) {
this.label = label;
}

public String getLabel() {
return this.label;
}

public static ProjectType getProjectType(int i) {
return ProjectType.values()[i];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@
android:gravity="center_horizontal"
/>

<TextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"

android:layout_alignParentStart="true"

android:layout_centerVertical="true"
android:gravity="center_horizontal"
android:padding="8dp"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="normal" />

<ImageButton
android:id="@+id/game_browser_thumbnail_favorite_button"
android:background="@android:color/transparent"
Expand Down
Loading

0 comments on commit f787328

Please sign in to comment.