From 55a9f8e937b19c85722afcae8382eb20eb2e5aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Fri, 3 Apr 2020 17:17:32 +0530 Subject: [PATCH] Refactor all Game-related Kotlin Classes/Objects This refactors most game-related classes and objects mainly adding spacing, adding comments and making improvements if possible. --- CONTRIBUTING.md | 6 +- app/src/main/AndroidManifest.xml | 2 +- app/src/main/cpp/main.cpp | 12 +- .../{GameActivity.kt => EmulationActivity.kt} | 132 ++++++++++++++---- app/src/main/java/emu/skyline/MainActivity.kt | 27 ++-- .../adapter/{GameAdapter.kt => AppAdapter.kt} | 91 +++++++++--- .../java/emu/skyline/adapter/HeaderAdapter.kt | 9 +- .../java/emu/skyline/loader/BaseLoader.kt | 109 ++++++++++++--- .../main/java/emu/skyline/loader/NroLoader.kt | 21 ++- .../java/emu/skyline/utility/GameDialog.kt | 16 +-- app/src/main/res/layout/game_activity.xml | 2 +- app/src/main/res/values/strings.xml | 3 +- 12 files changed, 317 insertions(+), 113 deletions(-) rename app/src/main/java/emu/skyline/{GameActivity.kt => EmulationActivity.kt} (54%) rename app/src/main/java/emu/skyline/adapter/{GameAdapter.kt => AppAdapter.kt} (50%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27ffe05c..d8a70bda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,7 +28,9 @@ Use doxygen style comments for: * Class/Struct Functions - Use `/**` block comments on their function with a brief, all arguments and the return value (The brief can be skipped if the function's arguments and return value alone explain what the function does) * Enumerations - Use a `/**` block comment with a brief for the enum itself and a `//!<` single-line comment for all the individual items -Note: The DeviceState object can be skipped from function argument documentation as well as class members in the constructor. +Notes: +* The DeviceState object can be skipped from function argument documentation as well as class members in the constructor +* Any class members don't need to be redundantly documented in the constructor ### Control flow statements (if, for and while): #### If a child control-flow statement has brackets, the parent statement must as well @@ -176,4 +178,4 @@ Handle b = 0x10; // Handle is a specific type that won't be automatically assign ### Constants If a variable is constant at compile time use `constexpr`, if it's only used in a local function then place it in the function but if it's used throughout a class then in the corresponding header add the variable to the `skyline::constant` namespace. If a constant is used throughout the codebase, add it to `common.h`. -In addition, try to `constexpr` as much as possible including constructors and functions so that they may be initialized at compile-time and have lesser runtime overhead during usage and certain values can be pre-calculated in advance. \ No newline at end of file +In addition, try to `constexpr` as much as possible including constructors and functions so that they may be initialized at compile-time and have lesser runtime overhead during usage and certain values can be pre-calculated in advance. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4f092d61..75e10a54 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,7 +44,7 @@ android:value="emu.skyline.SettingsActivity" /> diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index ef64e5b7..48ef8b08 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -21,7 +21,7 @@ void signalHandler(int signal) { FaultCount++; } -extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, jobject instance, jstring romJstring, jint romType, jint romFd, jint preferenceFd, jint logFd) { +extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeRom(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jint logFd) { Halt = false; FaultCount = 0; @@ -43,9 +43,9 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, try { skyline::kernel::OS os(jvmManager, logger, settings); - const char *romString = env->GetStringUTFChars(romJstring, nullptr); - logger->Info("Launching ROM {}", romString); - env->ReleaseStringUTFChars(romJstring, romString); + const char *romUri = env->GetStringUTFChars(romUriJstring, nullptr); + logger->Info("Launching ROM {}", romUri); + env->ReleaseStringUTFChars(romUriJstring, romUri); os.Execute(romFd, static_cast(romType)); } catch (std::exception &e) { logger->Error(e.what()); @@ -58,13 +58,13 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, logger->Info("Done in: {} ms", (std::chrono::duration_cast(end - start).count())); } -extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) { +extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) { JniMtx.lock(skyline::GroupMutex::Group::Group2); Halt = halt; JniMtx.unlock(); } -extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) { +extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) { JniMtx.lock(skyline::GroupMutex::Group::Group2); if (!env->IsSameObject(Surface, nullptr)) env->DeleteGlobalRef(Surface); diff --git a/app/src/main/java/emu/skyline/GameActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt similarity index 54% rename from app/src/main/java/emu/skyline/GameActivity.kt rename to app/src/main/java/emu/skyline/EmulationActivity.kt index 7cce2b4d..b783cc10 100644 --- a/app/src/main/java/emu/skyline/GameActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -10,105 +10,175 @@ import android.net.Uri import android.os.Bundle import android.os.ParcelFileDescriptor import android.util.Log -import android.view.InputQueue import android.view.Surface import android.view.SurfaceHolder import android.view.WindowManager import androidx.appcompat.app.AppCompatActivity -import emu.skyline.loader.getTitleFormat +import emu.skyline.loader.getRomFormat import kotlinx.android.synthetic.main.game_activity.* import java.io.File -import java.lang.reflect.Method -class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Callback { +/** + * This activity is used for emulation using libskyline + */ +class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { init { System.loadLibrary("skyline") // libskyline.so } + /** + * The file descriptor of the ROM + */ private lateinit var romFd: ParcelFileDescriptor - private lateinit var preferenceFd: ParcelFileDescriptor - private lateinit var logFd: ParcelFileDescriptor - private var surface: Surface? = null - private var inputQueue: Long = 0L - private var shouldFinish: Boolean = true - private lateinit var gameThread: Thread - private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int) + /** + * The file descriptor of the application Preference XML + */ + private lateinit var preferenceFd: ParcelFileDescriptor + + /** + * The file descriptor of the Log file + */ + private lateinit var logFd: ParcelFileDescriptor + + /** + * The surface object used for displaying frames + */ + private var surface: Surface? = null + + /** + * A boolean flag denoting if the emulation thread should call finish() or not + */ + private var shouldFinish: Boolean = true + + /** + * The Kotlin thread on which emulation code executes + */ + private lateinit var emulationThread: Thread + + /** + * This is the entry point into the emulation code for libskyline + * + * @param romUri The URI of the ROM as a string, used to print out in the logs + * @param romType The type of the ROM as an enum value + * @param romFd The file descriptor of the ROM object + * @param preferenceFd The file descriptor of the Preference XML + * @param logFd The file descriptor of the Log file + */ + private external fun executeRom(romUri: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int) + + /** + * This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation + * + * @param halt The value to set halt to + */ private external fun setHalt(halt: Boolean) + + /** + * This sets the surface object in libskyline to the provided value, emulation is halted if set to null + * + * @param surface The value to set surface to + */ private external fun setSurface(surface: Surface?) - fun executeRom(rom : Uri) { - val romType = getTitleFormat(rom, contentResolver).ordinal + /** + * This executes the specified ROM, [preferenceFd] and [logFd] are assumed to be valid beforehand + * + * @param rom The URI of the ROM to execute + */ + private fun executeRom(rom : Uri) { + val romType = getRomFormat(rom, contentResolver).ordinal romFd = contentResolver.openFileDescriptor(rom, "r")!! - gameThread = Thread { + + emulationThread = Thread { while ((surface == null)) Thread.yield() + executeRom(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd) + if (shouldFinish) runOnUiThread { finish() } } - gameThread.start() + + emulationThread.start() } + /** + * The onCreate handler for the activity, it sets up the FDs and calls [executeRom] + */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + setContentView(R.layout.game_activity) + val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml") preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE) + val log = File("${applicationInfo.dataDir}/skyline.log") logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE) + window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) game_view.holder.addCallback(this) + executeRom(intent.data!!) } + /** + * The onNewIntent handler is used to stop the currently executing ROM and replace it with the one specified in the new intent + */ override fun onNewIntent(intent: Intent?) { shouldFinish = false + setHalt(true) - gameThread.join() + emulationThread.join() + shouldFinish = true + romFd.close() + executeRom(intent?.data!!) + super.onNewIntent(intent) } + /** + * The onDestroy handler is used to halt emulation + */ override fun onDestroy() { shouldFinish = false + setHalt(true) - gameThread.join() + emulationThread.join() + romFd.close() preferenceFd.close() logFd.close() + super.onDestroy() } + /** + * The surfaceCreated handler passes in the surface to libskyline + */ override fun surfaceCreated(holder: SurfaceHolder?) { Log.d("surfaceCreated", "Holder: ${holder.toString()}") surface = holder!!.surface setSurface(surface) } + /** + * The surfaceChanged handler is purely used for debugging purposes + */ override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { Log.d("surfaceChanged", "Holder: ${holder.toString()}, Format: $format, Width: $width, Height: $height") } + /** + * The surfaceDestroyed handler passes sets the surface to null + */ override fun surfaceDestroyed(holder: SurfaceHolder?) { Log.d("surfaceDestroyed", "Holder: ${holder.toString()}") surface = null setSurface(surface) } - - override fun onInputQueueCreated(queue: InputQueue?) { - Log.i("onInputQueueCreated", "InputQueue: ${queue.toString()}") - val clazz = Class.forName("android.view.InputQueue") - val method: Method = clazz.getMethod("getNativePtr") - inputQueue = method.invoke(queue)!! as Long - //setQueue(inputQueue) - } - - override fun onInputQueueDestroyed(queue: InputQueue?) { - Log.d("onInputQueueDestroyed", "InputQueue: ${queue.toString()}") - inputQueue = 0L - //setQueue(inputQueue) - } } diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 78341cb3..41b190dc 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -19,8 +19,8 @@ import androidx.appcompat.widget.SearchView import androidx.documentfile.provider.DocumentFile import androidx.preference.PreferenceManager import com.google.android.material.snackbar.Snackbar -import emu.skyline.adapter.GameAdapter -import emu.skyline.adapter.GameItem +import emu.skyline.adapter.AppAdapter +import emu.skyline.adapter.AppItem import emu.skyline.loader.BaseLoader import emu.skyline.loader.NroLoader import emu.skyline.utility.GameDialog @@ -32,7 +32,7 @@ import kotlin.concurrent.thread class MainActivity : AppCompatActivity(), View.OnClickListener { private lateinit var sharedPreferences: SharedPreferences - private var adapter = GameAdapter(this) + private var adapter = AppAdapter(this) private fun notifyUser(text: String) { Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() @@ -49,13 +49,13 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { if (ext.equals(file.name?.substringAfterLast("."), ignoreCase = true)) { val document = RandomAccessDocument(this, file) if (loader.verifyFile(document)) { - val entry = loader.getTitleEntry(document, file.uri) + val entry = loader.getAppEntry(document, file.uri) runOnUiThread { if (!foundCurrent) { - adapter.addHeader(getString(R.string.nro)) + adapter.addHeader(loader.format.name) foundCurrent = true } - adapter.addItem(GameItem(entry)) + adapter.addItem(AppItem(entry)) } } document.close() @@ -75,25 +75,31 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { Log.w("refreshFiles", "Ran into exception while loading: ${e.message}") } } + thread(start = true) { val snackbar = Snackbar.make(findViewById(android.R.id.content), getString(R.string.searching_roms), Snackbar.LENGTH_INDEFINITE) runOnUiThread { snackbar.show() } + try { runOnUiThread { adapter.clear() } val foundNros = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!) + runOnUiThread { if (!foundNros) adapter.addHeader(getString(R.string.no_rom)) + try { adapter.save(File("${applicationInfo.dataDir}/roms.bin")) } catch (e: IOException) { Log.w("refreshFiles", "Ran into exception while saving: ${e.message}") } } + sharedPreferences.edit().putBoolean("refresh_required", false).apply() } catch (e: IllegalArgumentException) { runOnUiThread { sharedPreferences.edit().remove("search_location").apply() + val intent = intent finish() startActivity(intent) @@ -103,6 +109,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { notifyUser(e.message!!) } } + runOnUiThread { snackbar.dismiss() } } } @@ -124,15 +131,15 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { game_list.adapter = adapter game_list.onItemClickListener = OnItemClickListener { parent: AdapterView<*>, _: View?, position: Int, _: Long -> val item = parent.getItemAtPosition(position) - if (item is GameItem) { - val intent = Intent(this, GameActivity::class.java) + if (item is AppItem) { + val intent = Intent(this, EmulationActivity::class.java) intent.data = item.uri startActivity(intent) } } game_list.onItemLongClickListener = AdapterView.OnItemLongClickListener { parent, _, position, _ -> val item = parent.getItemAtPosition(position) - if (item is GameItem) { + if (item is AppItem) { val dialog = GameDialog(item) dialog.show(supportFragmentManager, "game") } @@ -205,7 +212,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { 2 -> { try { val uri = (intent!!.data!!) - val intentGame = Intent(this, GameActivity::class.java) + val intentGame = Intent(this, EmulationActivity::class.java) intentGame.data = uri if (resultCode != 0) startActivityForResult(intentGame, resultCode) diff --git a/app/src/main/java/emu/skyline/adapter/GameAdapter.kt b/app/src/main/java/emu/skyline/adapter/AppAdapter.kt similarity index 50% rename from app/src/main/java/emu/skyline/adapter/GameAdapter.kt rename to app/src/main/java/emu/skyline/adapter/AppAdapter.kt index 5321b1be..d92bb6c6 100644 --- a/app/src/main/java/emu/skyline/adapter/GameAdapter.kt +++ b/app/src/main/java/emu/skyline/adapter/AppAdapter.kt @@ -18,39 +18,69 @@ import android.view.Window import android.widget.ImageView import android.widget.RelativeLayout import android.widget.TextView +import androidx.core.graphics.drawable.toBitmap import emu.skyline.R -import emu.skyline.loader.TitleEntry +import emu.skyline.loader.AppEntry -class GameItem(val meta: TitleEntry) : BaseItem() { +/** + * This class is a wrapper around [AppEntry], it is used for passing around game metadata + */ +class AppItem(val meta: AppEntry) : BaseItem() { + /** + * The icon of the application + */ val icon: Bitmap? get() = meta.icon + /** + * The title of the application + */ val title: String get() = meta.name + " (" + type + ")" + /** + * The string used as the sub-title, we currently use the author + */ val subTitle: String? get() = meta.author + /** + * The URI of the application's image file + */ val uri: Uri get() = meta.uri + /** + * The format of the application ROM as a string + */ private val type: String - get() = meta.romType.name + get() = meta.format.name override fun key(): String? { - return if (meta.valid) meta.name + " " + meta.author else meta.name + return if (meta.author != null) meta.name + " " + meta.author else meta.name } } -internal class GameAdapter(val context: Context?) : HeaderAdapter(), View.OnClickListener { +/** + * This adapter is used to display all found applications using their metadata + */ +internal class AppAdapter(val context: Context?) : HeaderAdapter(), View.OnClickListener { + /** + * This adds a string header to the view + */ fun addHeader(string: String) { super.addHeader(BaseHeader(string)) } + /** + * The onClick handler, it's for displaying the icon preview + * + * @param view The specific view that was clicked + */ override fun onClick(view: View) { val position = view.tag as Int - if (getItem(position) is GameItem) { - val item = getItem(position) as GameItem + if (getItem(position) is AppItem) { + val item = getItem(position) as AppItem if (view.id == R.id.icon) { val builder = Dialog(context!!) builder.requestWindowFeature(Window.FEATURE_NO_TITLE) @@ -63,44 +93,61 @@ internal class GameAdapter(val context: Context?) : HeaderAdapter val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) cursor.moveToFirst() uriStr = cursor.getString(nameIndex) } - return TitleFormat.valueOf(uriStr.substring(uriStr.lastIndexOf(".") + 1).toUpperCase(Locale.ROOT)) + return RomFormat.valueOf(uriStr.substring(uriStr.lastIndexOf(".") + 1).toUpperCase(Locale.ROOT)) } -class TitleEntry(var name: String, var author: String, var romType: TitleFormat, var valid: Boolean, var uri: Uri, var icon: Bitmap) : Serializable { - constructor(context: Context, author: String, romType: TitleFormat, valid: Boolean, uri: Uri) : this("", author, romType, valid, uri, context.resources.getDrawable(R.drawable.ic_missing, context.theme).toBitmap(256, 256)) { - context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> - val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) - cursor.moveToFirst() - name = cursor.getString(nameIndex) - } +/** + * This class is used to hold an application's metadata in a serializable way + */ +class AppEntry : Serializable { + /** + * The name of the application + */ + var name: String + + /** + * The author of the application, if it can be extracted from the metadata + */ + var author: String? = null + + var icon: Bitmap? = null + + /** + * The format of the application ROM + */ + var format: RomFormat + + /** + * The URI of the application ROM + */ + var uri: Uri + + constructor(name: String, author: String, format: RomFormat, uri: Uri, icon: Bitmap) { + this.name = name + this.author = author + this.icon = icon + this.format = format + this.uri = uri } + constructor(context: Context, format: RomFormat, uri: Uri) { + this.name = context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> + val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + cursor.moveToFirst() + cursor.getString(nameIndex) + }!! + this.format = format + this.uri = uri + } + + /** + * This serializes this object into an OutputStream + * + * @param output The stream to which the object is written into + */ @Throws(IOException::class) private fun writeObject(output: ObjectOutputStream) { output.writeUTF(name) - output.writeUTF(author) - output.writeObject(romType) + output.writeObject(format) output.writeUTF(uri.toString()) - output.writeBoolean(valid) - icon.compress(Bitmap.CompressFormat.WEBP, 100, output) + output.writeBoolean(author != null) + if (author != null) + output.writeUTF(author) + output.writeBoolean(icon != null) + if (icon != null) + icon!!.compress(Bitmap.CompressFormat.WEBP, 100, output) } + /** + * This initializes the object from an InputStream + * + * @param input The stream from which the object data is retrieved from + */ @Throws(IOException::class, ClassNotFoundException::class) private fun readObject(input: ObjectInputStream) { name = input.readUTF() - author = input.readUTF() - romType = input.readObject() as TitleFormat + format = input.readObject() as RomFormat uri = Uri.parse(input.readUTF()) - valid = input.readBoolean() - icon = BitmapFactory.decodeStream(input) + if (input.readBoolean()) + author = input.readUTF() + if (input.readBoolean()) + icon = BitmapFactory.decodeStream(input) } } -internal abstract class BaseLoader(val context: Context, val romType: TitleFormat) { - abstract fun getTitleEntry(file: RandomAccessDocument, uri: Uri): TitleEntry +/** + * This class is used as the base class for all loaders + */ +internal abstract class BaseLoader(val context: Context, val format: RomFormat) { + /** + * This returns an AppEntry object for the supplied document + */ + abstract fun getAppEntry(file: RandomAccessDocument, uri: Uri): AppEntry + /** + * This returns if the supplied document is a valid ROM or not + */ abstract fun verifyFile(file: RandomAccessDocument): Boolean } diff --git a/app/src/main/java/emu/skyline/loader/NroLoader.kt b/app/src/main/java/emu/skyline/loader/NroLoader.kt index cd3c0ea6..828c2d07 100644 --- a/app/src/main/java/emu/skyline/loader/NroLoader.kt +++ b/app/src/main/java/emu/skyline/loader/NroLoader.kt @@ -8,45 +8,56 @@ package emu.skyline.loader import android.content.Context import android.graphics.BitmapFactory import android.net.Uri -import emu.skyline.R import emu.skyline.utility.RandomAccessDocument import java.io.IOException -internal class NroLoader(context: Context) : BaseLoader(context, TitleFormat.NRO) { - override fun getTitleEntry(file: RandomAccessDocument, uri: Uri): TitleEntry { +/** + * This loader is used to load in NRO (Nintendo Relocatable Object) files (https://switchbrew.org/wiki/NRO) + */ +internal class NroLoader(context: Context) : BaseLoader(context, RomFormat.NRO) { + override fun getAppEntry(file: RandomAccessDocument, uri: Uri): AppEntry { return try { file.seek(0x18) // Skip to NroHeader.size + val asetOffset = Integer.reverseBytes(file.readInt()) file.seek(asetOffset.toLong()) // Skip to the offset specified by NroHeader.size + val buffer = ByteArray(4) file.read(buffer) if (String(buffer) != "ASET") throw IOException() file.skipBytes(0x4) + val iconOffset = java.lang.Long.reverseBytes(file.readLong()) val iconSize = Integer.reverseBytes(file.readInt()) if (iconOffset == 0L || iconSize == 0) throw IOException() file.seek(asetOffset + iconOffset) + val iconData = ByteArray(iconSize) file.read(iconData) val icon = BitmapFactory.decodeByteArray(iconData, 0, iconSize) file.seek(asetOffset + 0x18.toLong()) + val nacpOffset = java.lang.Long.reverseBytes(file.readLong()) val nacpSize = java.lang.Long.reverseBytes(file.readLong()) if (nacpOffset == 0L || nacpSize == 0L) throw IOException() file.seek(asetOffset + nacpOffset) + val name = ByteArray(0x200) file.read(name) + val author = ByteArray(0x100) file.read(author) - TitleEntry(String(name).substringBefore((0.toChar())), String(author).substringBefore((0.toChar())), romType, true, uri, icon) + + AppEntry(String(name).substringBefore((0.toChar())), String(author).substringBefore((0.toChar())), format, uri, icon) } catch (e: IOException) { - TitleEntry(context, context.getString(R.string.aset_missing), romType, false, uri) + AppEntry(context, format, uri) } } override fun verifyFile(file: RandomAccessDocument): Boolean { try { file.seek(0x10) // Skip to NroHeader.magic + val buffer = ByteArray(4) file.read(buffer) if (String(buffer) != "NRO0") return false diff --git a/app/src/main/java/emu/skyline/utility/GameDialog.kt b/app/src/main/java/emu/skyline/utility/GameDialog.kt index 3c6277cc..5e7338c8 100644 --- a/app/src/main/java/emu/skyline/utility/GameDialog.kt +++ b/app/src/main/java/emu/skyline/utility/GameDialog.kt @@ -15,15 +15,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment -import emu.skyline.GameActivity +import emu.skyline.EmulationActivity import emu.skyline.R -import emu.skyline.adapter.GameItem +import emu.skyline.adapter.AppItem import kotlinx.android.synthetic.main.game_dialog.* class GameDialog() : DialogFragment() { - var item: GameItem? = null + var item: AppItem? = null - constructor(item: GameItem) : this() { + constructor(item: AppItem) : this() { this.item = item } @@ -33,7 +33,7 @@ class GameDialog() : DialogFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - if (item is GameItem) { + if (item is AppItem) { game_icon.setImageBitmap(item?.icon) game_title.text = item?.title game_subtitle.text = item?.subTitle @@ -42,16 +42,16 @@ class GameDialog() : DialogFragment() { game_pin.setOnClickListener { val info = ShortcutInfo.Builder(context, item?.title) info.setShortLabel(item?.meta?.name!!) - info.setActivity(ComponentName(context!!, GameActivity::class.java)) + info.setActivity(ComponentName(context!!, EmulationActivity::class.java)) info.setIcon(Icon.createWithBitmap(item?.icon)) - val intent = Intent(context, GameActivity::class.java) + val intent = Intent(context, EmulationActivity::class.java) intent.data = item?.uri intent.action = Intent.ACTION_VIEW info.setIntent(intent) shortcutManager.requestPinShortcut(info.build(), null) } game_play.setOnClickListener { - val intent = Intent(activity, GameActivity::class.java) + val intent = Intent(activity, EmulationActivity::class.java) intent.data = item?.uri startActivity(intent) } diff --git a/app/src/main/res/layout/game_activity.xml b/app/src/main/res/layout/game_activity.xml index 9a6ace7b..9436eba1 100644 --- a/app/src/main/res/layout/game_activity.xml +++ b/app/src/main/res/layout/game_activity.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".GameActivity"> + tools:context=".EmulationActivity"> Refresh The list of ROMs has been refreshed. - ASET Header Missing + Metadata Missing Icon Cannot find any ROMs - NROs Pin Play