diff --git a/app/src/main/java/me/magnum/melonds/common/romprocessors/CompressedRomFileProcessor.kt b/app/src/main/java/me/magnum/melonds/common/romprocessors/CompressedRomFileProcessor.kt index 8fe1a0e..3a76442 100644 --- a/app/src/main/java/me/magnum/melonds/common/romprocessors/CompressedRomFileProcessor.kt +++ b/app/src/main/java/me/magnum/melonds/common/romprocessors/CompressedRomFileProcessor.kt @@ -5,10 +5,7 @@ import android.graphics.Bitmap import android.net.Uri import io.reactivex.Single import me.magnum.melonds.common.uridelegates.UriHandler -import me.magnum.melonds.domain.model.Rom -import me.magnum.melonds.domain.model.RomConfig -import me.magnum.melonds.domain.model.RomInfo -import me.magnum.melonds.domain.model.SizeUnit +import me.magnum.melonds.domain.model.* import me.magnum.melonds.extensions.isBlank import me.magnum.melonds.extensions.nameWithoutExtension import me.magnum.melonds.impl.NdsRomCache @@ -28,8 +25,9 @@ abstract class CompressedRomFileProcessor(private val context: Context, private context.contentResolver.openInputStream(romUri)?.use { stream -> getNdsEntryStreamInFileStream(stream)?.use { romFileStream -> val romDocument = uriHandler.getUriDocument(romUri) - val romName = getRomNameInZipEntry(romFileStream).takeUnless { it.isBlank() } ?: romDocument?.nameWithoutExtension ?: "" - Rom(romName, romDocument?.name ?: "", romUri, parentUri, RomConfig()) + val romMetadata = getRomMetadataInZipEntry(romFileStream) + val romName = romMetadata.romTitle.takeUnless { it.isBlank() } ?: romDocument?.nameWithoutExtension ?: "" + Rom(romName, romDocument?.name ?: "", romUri, parentUri, RomConfig(), null, romMetadata.isDSiWareTitle) } } } catch (e: Exception) { @@ -80,8 +78,8 @@ abstract class CompressedRomFileProcessor(private val context: Context, private } } - private fun getRomNameInZipEntry(inputStream: InputStream): String { - return RomProcessor.getRomName(inputStream.buffered()) + private fun getRomMetadataInZipEntry(inputStream: InputStream): RomMetadata { + return RomProcessor.getRomMetadata(inputStream.buffered()) } private fun extractRomFile(rom: Rom): Single { diff --git a/app/src/main/java/me/magnum/melonds/common/romprocessors/NdsRomFileProcessor.kt b/app/src/main/java/me/magnum/melonds/common/romprocessors/NdsRomFileProcessor.kt index 3ff9bbf..5c78265 100644 --- a/app/src/main/java/me/magnum/melonds/common/romprocessors/NdsRomFileProcessor.kt +++ b/app/src/main/java/me/magnum/melonds/common/romprocessors/NdsRomFileProcessor.kt @@ -8,6 +8,7 @@ import me.magnum.melonds.common.uridelegates.UriHandler import me.magnum.melonds.domain.model.Rom import me.magnum.melonds.domain.model.RomConfig import me.magnum.melonds.domain.model.RomInfo +import me.magnum.melonds.domain.model.RomMetadata import me.magnum.melonds.extensions.isBlank import me.magnum.melonds.extensions.nameWithoutExtension import me.magnum.melonds.utils.RomProcessor @@ -16,10 +17,10 @@ class NdsRomFileProcessor(private val context: Context, private val uriHandler: override fun getRomFromUri(romUri: Uri, parentUri: Uri): Rom? { return try { - getRomName(romUri)?.let { name -> + getRomMetadata(romUri)?.let { metadata -> val romDocument = uriHandler.getUriDocument(romUri) - val romName = name.takeUnless { it.isBlank() } ?: romDocument?.nameWithoutExtension ?: "" - Rom(romName, romDocument?.name ?: "", romUri, parentUri, RomConfig()) + val romName = metadata.romTitle.takeUnless { it.isBlank() } ?: romDocument?.nameWithoutExtension ?: "" + Rom(romName, romDocument?.name ?: "", romUri, parentUri, RomConfig(), null, metadata.isDSiWareTitle) } } catch (e: Exception) { e.printStackTrace() @@ -53,9 +54,9 @@ class NdsRomFileProcessor(private val context: Context, private val uriHandler: return Single.just(rom.uri) } - private fun getRomName(uri: Uri): String? { + private fun getRomMetadata(uri: Uri): RomMetadata? { return context.contentResolver.openInputStream(uri)?.use { inputStream -> - RomProcessor.getRomName(inputStream.buffered()) + RomProcessor.getRomMetadata(inputStream.buffered()) } } } \ No newline at end of file diff --git a/app/src/main/java/me/magnum/melonds/domain/model/Rom.kt b/app/src/main/java/me/magnum/melonds/domain/model/Rom.kt index d226e3f..90cceec 100644 --- a/app/src/main/java/me/magnum/melonds/domain/model/Rom.kt +++ b/app/src/main/java/me/magnum/melonds/domain/model/Rom.kt @@ -10,6 +10,7 @@ data class Rom( val parentTreeUri: Uri, var config: RomConfig, var lastPlayed: Date? = null, + val isDsiWareTitle: Boolean, ) { override fun equals(other: Any?): Boolean { diff --git a/app/src/main/java/me/magnum/melonds/domain/model/RomMetadata.kt b/app/src/main/java/me/magnum/melonds/domain/model/RomMetadata.kt new file mode 100644 index 0000000..fd378f5 --- /dev/null +++ b/app/src/main/java/me/magnum/melonds/domain/model/RomMetadata.kt @@ -0,0 +1,6 @@ +package me.magnum.melonds.domain.model + +data class RomMetadata( + val romTitle: String, + val isDSiWareTitle: Boolean, +) \ No newline at end of file diff --git a/app/src/main/java/me/magnum/melonds/impl/XmlCheatDatabaseSAXHandler.kt b/app/src/main/java/me/magnum/melonds/impl/XmlCheatDatabaseSAXHandler.kt index 558f2df..7ef94e2 100644 --- a/app/src/main/java/me/magnum/melonds/impl/XmlCheatDatabaseSAXHandler.kt +++ b/app/src/main/java/me/magnum/melonds/impl/XmlCheatDatabaseSAXHandler.kt @@ -109,7 +109,7 @@ class XmlCheatDatabaseSAXHandler(private val listener: HandlerListener) : Defaul if (parsingDatabase) { if (parsingDatabaseName) { // Remove everything between parenthesis. Most likely it contains the DB's version - databaseName = textStringBuilder.toString().replace("\\(.*?\\)".toRegex(), "") + databaseName = textStringBuilder.toString().replace("\\(.*?\\)".toRegex(), "").trim() parsingDatabaseName = false emitCheatDatabaseName(databaseName!!) } diff --git a/app/src/main/java/me/magnum/melonds/migrations/Migration21to22.kt b/app/src/main/java/me/magnum/melonds/migrations/Migration21to22.kt index b58500b..ab7da29 100644 --- a/app/src/main/java/me/magnum/melonds/migrations/Migration21to22.kt +++ b/app/src/main/java/me/magnum/melonds/migrations/Migration21to22.kt @@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken import me.magnum.melonds.common.uridelegates.UriHandler import me.magnum.melonds.domain.model.Rom import me.magnum.melonds.migrations.legacy.Rom21 +import me.magnum.melonds.utils.RomProcessor import java.io.File import java.io.FileReader import java.io.OutputStreamWriter @@ -28,16 +29,20 @@ class Migration21to22( override fun migrate() { val originalRoms = getOriginalRoms() val newRoms = originalRoms.mapNotNull { rom -> - uriHandler.getUriDocument(rom.uri)?.name?.let { fileName -> - Rom( - rom.name, - fileName, - rom.uri, - rom.parentTreeUri, - rom.config, - rom.lastPlayed, - ) - } + val fileName = uriHandler.getUriDocument(rom.uri)?.name ?: return@mapNotNull null + val romMetadata = context.contentResolver.openInputStream(rom.uri)?.use { + RomProcessor.getRomMetadata(it.buffered()) + } ?: return@mapNotNull null + + Rom( + rom.name, + fileName, + rom.uri, + rom.parentTreeUri, + rom.config, + rom.lastPlayed, + romMetadata.isDSiWareTitle, + ) } saveNewRoms(newRoms) diff --git a/app/src/main/java/me/magnum/melonds/parcelables/RomParcelable.kt b/app/src/main/java/me/magnum/melonds/parcelables/RomParcelable.kt index cf41a95..839d7c3 100644 --- a/app/src/main/java/me/magnum/melonds/parcelables/RomParcelable.kt +++ b/app/src/main/java/me/magnum/melonds/parcelables/RomParcelable.kt @@ -22,7 +22,8 @@ class RomParcelable : Parcelable { val parentTreeUri = parcel.readString()!!.toUri() val lastPlayed = parcel.readLong().let { if (it == (-1).toLong()) null else Date(it) } val romConfig = parcel.parcelable() - rom = Rom(name!!, fileName!!, uri, parentTreeUri, romConfig!!.romConfig, lastPlayed) + val isDsiWareTitle = parcel.readInt() == 1 + rom = Rom(name!!, fileName!!, uri, parentTreeUri, romConfig!!.romConfig, lastPlayed, isDsiWareTitle) } override fun writeToParcel(dest: Parcel, flags: Int) { @@ -32,6 +33,7 @@ class RomParcelable : Parcelable { dest.writeString(rom.parentTreeUri.toString()) dest.writeLong(rom.lastPlayed?.time ?: -1) dest.writeParcelable(RomConfigParcelable(rom.config), 0) + dest.writeInt(if (rom.isDsiWareTitle) 1 else 0) } override fun describeContents(): Int { diff --git a/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt b/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt index d2e1ad0..547e05b 100644 --- a/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt +++ b/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt @@ -184,7 +184,7 @@ class RomListActivity : AppCompatActivity() { private fun addRomListFragment() { var romListFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_ROM_LIST) as RomListFragment? if (romListFragment == null) { - romListFragment = RomListFragment.newInstance(true) + romListFragment = RomListFragment.newInstance(true, RomListFragment.RomEnableCriteria.ENABLE_ALL) supportFragmentManager.commit { replace(R.id.layout_main, romListFragment, FRAGMENT_ROM_LIST) } diff --git a/app/src/main/java/me/magnum/melonds/ui/romlist/RomListFragment.kt b/app/src/main/java/me/magnum/melonds/ui/romlist/RomListFragment.kt index 8940a20..f74ac05 100644 --- a/app/src/main/java/me/magnum/melonds/ui/romlist/RomListFragment.kt +++ b/app/src/main/java/me/magnum/melonds/ui/romlist/RomListFragment.kt @@ -1,6 +1,8 @@ package me.magnum.melonds.ui.romlist import android.content.Context +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.view.LayoutInflater @@ -8,7 +10,9 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.core.content.res.ResourcesCompat import androidx.core.os.bundleOf +import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -30,22 +34,31 @@ import me.magnum.melonds.databinding.RomListFragmentBinding import me.magnum.melonds.domain.model.Rom import me.magnum.melonds.domain.model.RomIconFiltering import me.magnum.melonds.domain.model.RomScanningStatus +import me.magnum.melonds.extensions.setViewEnabledRecursive +import me.magnum.melonds.ui.romlist.RomListFragment.RomEnabledFilter import me.magnum.melonds.ui.romlist.RomListFragment.RomListAdapter.RomViewHolder @AndroidEntryPoint class RomListFragment : Fragment() { companion object { private const val KEY_ALLOW_ROM_CONFIGURATION = "allow_rom_configuration" + private const val KEY_ROM_ENABLE_CRITERIA = "rom_enable_criteria" - fun newInstance(allowRomConfiguration: Boolean): RomListFragment { + fun newInstance(allowRomConfiguration: Boolean, enableCriteria: RomEnableCriteria): RomListFragment { return RomListFragment().also { it.arguments = bundleOf( - KEY_ALLOW_ROM_CONFIGURATION to allowRomConfiguration + KEY_ALLOW_ROM_CONFIGURATION to allowRomConfiguration, + KEY_ROM_ENABLE_CRITERIA to enableCriteria.toString(), ) } } } + enum class RomEnableCriteria { + ENABLE_ALL, + ENABLE_NON_DSIWARE, + } + private lateinit var binding: RomListFragmentBinding private val romListViewModel: RomListViewModel by activityViewModels() private lateinit var romListAdapter: RomListAdapter @@ -63,16 +76,24 @@ class RomListFragment : Fragment() { binding.swipeRefreshRoms.setOnRefreshListener { romListViewModel.refreshRoms() } val allowRomConfiguration = arguments?.getBoolean(KEY_ALLOW_ROM_CONFIGURATION) ?: true - romListAdapter = RomListAdapter(allowRomConfiguration, requireContext(), lifecycleScope, object : RomClickListener { - override fun onRomClicked(rom: Rom) { - romListViewModel.setRomLastPlayedNow(rom) - romSelectedListener?.invoke(rom) - } + val romEnableCriteria = arguments?.getString(KEY_ROM_ENABLE_CRITERIA)?.let { RomEnableCriteria.valueOf(it) } ?: RomEnableCriteria.ENABLE_ALL - override fun onRomConfigClicked(rom: Rom) { - RomConfigDialog.newInstance(rom.name, rom.copy()).show(parentFragmentManager, null) - } - }) + romListAdapter = RomListAdapter( + allowRomConfiguration = allowRomConfiguration, + context = requireContext(), + coroutineScope = lifecycleScope, + listener = object : RomClickListener { + override fun onRomClicked(rom: Rom) { + romListViewModel.setRomLastPlayedNow(rom) + romSelectedListener?.invoke(rom) + } + + override fun onRomConfigClicked(rom: Rom) { + RomConfigDialog.newInstance(rom.name, rom.copy()).show(parentFragmentManager, null) + } + }, + romEnabledFilter = buildRomEnabledFilter(romEnableCriteria), + ) binding.listRoms.apply { val listLayoutManager = LinearLayoutManager(context) @@ -108,6 +129,13 @@ class RomListFragment : Fragment() { binding.textRomListEmpty.isVisible = emptyViewVisible } + private fun buildRomEnabledFilter(romEnableCriteria: RomEnableCriteria): RomEnabledFilter { + return when (romEnableCriteria) { + RomEnableCriteria.ENABLE_ALL -> RomEnabledFilter { true } + RomEnableCriteria.ENABLE_NON_DSIWARE -> RomEnabledFilter { !it.isDsiWareTitle} + } + } + fun setRomSelectedListener(listener: (Rom) -> Unit) { romSelectedListener = listener } @@ -116,7 +144,8 @@ class RomListFragment : Fragment() { private val allowRomConfiguration: Boolean, private val context: Context, private val coroutineScope: CoroutineScope, - private val listener: RomClickListener + private val listener: RomClickListener, + private val romEnabledFilter: RomEnabledFilter, ) : RecyclerView.Adapter() { private val roms: ArrayList = ArrayList() @@ -146,7 +175,8 @@ class RomListFragment : Fragment() { override fun onBindViewHolder(romViewHolder: RomViewHolder, i: Int) { val rom = roms[i] - romViewHolder.setRom(rom) + val isRomEnabled = romEnabledFilter.isRomEnabled(rom) + romViewHolder.setRom(rom, isRomEnabled) } override fun onViewRecycled(holder: RomViewHolder) { @@ -161,6 +191,7 @@ class RomListFragment : Fragment() { private val imageViewRomIcon = itemView.findViewById(R.id.imageRomIcon) private val textViewRomName = itemView.findViewById(R.id.textRomName) private val textViewRomPath = itemView.findViewById(R.id.textRomPath) + private val imagePlatformLogo = itemView.findViewById(R.id.logoPlatform) private lateinit var rom: Rom private var romIconLoadJob: Job? = null @@ -175,18 +206,44 @@ class RomListFragment : Fragment() { romIconLoadJob?.cancel() } - open fun setRom(rom: Rom) { + open fun setRom(rom: Rom, isEnabled: Boolean) { this.rom = rom textViewRomName.text = rom.name textViewRomPath.text = rom.fileName imageViewRomIcon.setImageDrawable(null) + imagePlatformLogo.isVisible = rom.isDsiWareTitle + + val platformDrawable = if (rom.isDsiWareTitle) { + ResourcesCompat.getDrawable(itemView.resources, R.drawable.logo_dsiware, null) + } else { + null + } + + if (platformDrawable != null && !isEnabled) { + platformDrawable.apply { + colorFilter = ColorMatrixColorFilter(ColorMatrix().apply { setSaturation(0f) }) + alpha = 127 + } + } + + imagePlatformLogo.setImageDrawable(platformDrawable) romIconLoadJob = coroutineScope.launch { val romIcon = romListViewModel.getRomIcon(rom) - val iconDrawable = BitmapDrawable(itemView.resources, romIcon.bitmap) - iconDrawable.paint.isFilterBitmap = romIcon.filtering == RomIconFiltering.LINEAR + val iconDrawable = BitmapDrawable(itemView.resources, romIcon.bitmap).apply { + paint.isFilterBitmap = romIcon.filtering == RomIconFiltering.LINEAR + if (isEnabled) { + colorFilter = null + alpha = 255 + } else { + colorFilter = ColorMatrixColorFilter(ColorMatrix().apply { setSaturation(0f) }) + alpha = 127 + } + } imageViewRomIcon.setImageDrawable(iconDrawable) } + + itemView.setViewEnabledRecursive(isEnabled) } protected fun getRom() = rom @@ -206,6 +263,11 @@ class RomListFragment : Fragment() { onRomConfigClick(getRom()) } } + + override fun setRom(rom: Rom, isEnabled: Boolean) { + super.setRom(rom, isEnabled) + imageViewButtonRomConfig.isGone = rom.isDsiWareTitle + } } inner class RomsDiffUtilCallback(private val oldRoms: List, private val newRoms: List) : DiffUtil.Callback() { @@ -241,4 +303,8 @@ class RomListFragment : Fragment() { fun onRomClicked(rom: Rom) fun onRomConfigClicked(rom: Rom) } + + fun interface RomEnabledFilter { + fun isRomEnabled(rom: Rom): Boolean + } } \ No newline at end of file diff --git a/app/src/main/java/me/magnum/melonds/ui/shortcutsetup/ShortcutSetupActivity.kt b/app/src/main/java/me/magnum/melonds/ui/shortcutsetup/ShortcutSetupActivity.kt index 16ac24f..92486ad 100644 --- a/app/src/main/java/me/magnum/melonds/ui/shortcutsetup/ShortcutSetupActivity.kt +++ b/app/src/main/java/me/magnum/melonds/ui/shortcutsetup/ShortcutSetupActivity.kt @@ -38,12 +38,17 @@ class ShortcutSetupActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_shortcut_setup) - val fragment = RomListFragment.newInstance(false) - fragment.setRomSelectedListener { onRomSelected(it) } - - supportFragmentManager.commit { - replace(R.id.layout_root, fragment, FRAGMENT_ROM_LIST) + val fragment = if (savedInstanceState == null) { + RomListFragment.newInstance(false, RomListFragment.RomEnableCriteria.ENABLE_NON_DSIWARE).also { + supportFragmentManager.commit { + replace(R.id.layout_root, it, FRAGMENT_ROM_LIST) + } + } + } else { + supportFragmentManager.findFragmentByTag(FRAGMENT_ROM_LIST) as RomListFragment } + + fragment.setRomSelectedListener { onRomSelected(it) } } private fun onRomSelected(rom: Rom) { diff --git a/app/src/main/java/me/magnum/melonds/utils/RomProcessor.kt b/app/src/main/java/me/magnum/melonds/utils/RomProcessor.kt index a35ac0e..1c23b2e 100644 --- a/app/src/main/java/me/magnum/melonds/utils/RomProcessor.kt +++ b/app/src/main/java/me/magnum/melonds/utils/RomProcessor.kt @@ -4,7 +4,9 @@ import android.graphics.Bitmap import android.graphics.Color import androidx.core.graphics.createBitmap import me.magnum.melonds.common.Crc32 +import me.magnum.melonds.common.cheats.ProgressTrackerInputStream import me.magnum.melonds.domain.model.RomInfo +import me.magnum.melonds.domain.model.RomMetadata import java.io.BufferedInputStream import java.io.InputStream import java.nio.ByteBuffer @@ -13,21 +15,54 @@ import kotlin.experimental.and import kotlin.math.min object RomProcessor { - fun getRomName(inputStream: BufferedInputStream): String { - // Banner offset is at header offset 0x68 - inputStream.skipStreamBytes(0x68) - // Obtain the banner offset - val offsetData = ByteArray(4) - inputStream.read(offsetData) + private val DSIWARE_CATEGORY = 0x00030004.toUInt() + private const val KEY_ROM_NAME = "name" + private const val KEY_ROM_IS_DSIWARE_TITLE = "isDsiWareTitle" - val bannerOffset = byteArrayToInt(offsetData) - inputStream.skipStreamBytes(bannerOffset.toLong() + 576 - (0x68 + 4)) - val titleData = ByteArray(128) - inputStream.read(titleData) - return String(titleData, StandardCharsets.UTF_16LE) - .trim() - .replaceFirst("\n.*?$".toRegex(), "") - .replace("\n", " ") + fun getRomMetadata(inputStream: BufferedInputStream): RomMetadata { + val romStreamProcessor = RomStreamDataProcessor().apply { + registerProcessor( + RomStreamDataProcessor.SectionProcessor.SubSectionProcessor( + KEY_ROM_NAME, + streamOffset = 0x68, + processor = { + val offsetData = ByteArray(4) + inputStream.read(offsetData) + val bannerOffset = byteArrayToInt(offsetData) + bannerOffset + (0x0340 - 4 * 2).toLong() + }, + valueProcessor = { + val titleData = ByteArray(128) + inputStream.read(titleData) + String(titleData, StandardCharsets.UTF_16LE) + .trim() + .substringBeforeLast('\n') + .replace("\n", " ") + } + ) + ) + registerProcessor( + RomStreamDataProcessor.SectionProcessor.SectionValueProcessor( + KEY_ROM_IS_DSIWARE_TITLE, + streamOffset = 0x230, + processor = { + val categoryData = ByteArray(4) + inputStream.read(categoryData) + val categoryId = byteArrayToInt(categoryData) + categoryId.toUInt() == DSIWARE_CATEGORY + } + ) + ) + process(inputStream) + } + + val romName = romStreamProcessor.getValue(KEY_ROM_NAME) + val isDsiWareTitle = romStreamProcessor.getValue(KEY_ROM_IS_DSIWARE_TITLE) + + return RomMetadata( + romName, + isDsiWareTitle, + ) } fun getRomIcon(inputStream: BufferedInputStream): Bitmap { @@ -160,7 +195,7 @@ object RomProcessor { * Custom made way to skip bytes in an input stream. When dealing with zipped files, the internal implementations (ZipInputStream and BufferedInputStream) don't work very * well. This one seems to work when dealing with a BufferedInputStream */ - private fun BufferedInputStream.skipStreamBytes(bytes: Long) { + private fun InputStream.skipStreamBytes(bytes: Long) { val buffer = ByteArray(1024) var remaining = bytes do { @@ -172,4 +207,43 @@ object RomProcessor { remaining -= read } while (remaining > 0) } + + private class RomStreamDataProcessor { + private val processors = mutableListOf() + private val values = mutableMapOf() + + fun registerProcessor(processor: SectionProcessor) { + processors.add(processor) + } + + fun process(stream: BufferedInputStream) { + val trackedStream = ProgressTrackerInputStream(stream) + val sortedProcessors = processors.sortedBy { it.streamOffset }.toMutableList() + + while (sortedProcessors.isNotEmpty()) { + val processor = sortedProcessors.removeFirst() + val bytesToSkip = processor.streamOffset - trackedStream.totalReadBytes + trackedStream.skipStreamBytes(bytesToSkip) + + if (processor is SectionProcessor.SectionValueProcessor) { + val value = processor.processor(trackedStream) + values[processor.key] = value + } else if (processor is SectionProcessor.SubSectionProcessor) { + val newOffset = processor.processor(trackedStream) + sortedProcessors.add(SectionProcessor.SectionValueProcessor(processor.key, newOffset, processor.valueProcessor)) + sortedProcessors.sortBy { it.streamOffset } + } + } + } + + @Suppress("UNCHECKED_CAST") + fun getValue(key: String): T { + return values[key] as T + } + + sealed class SectionProcessor(val streamOffset: Long) { + class SectionValueProcessor(val key: String, streamOffset: Long, val processor: (InputStream) -> Any) : SectionProcessor(streamOffset) + class SubSectionProcessor(val key: String, streamOffset: Long, val processor: (InputStream) -> Long, val valueProcessor: (InputStream) -> Any) : SectionProcessor(streamOffset) + } + } } diff --git a/app/src/main/res/drawable-night/logo_dsiware.png b/app/src/main/res/drawable-night/logo_dsiware.png new file mode 100644 index 0000000..86a456e Binary files /dev/null and b/app/src/main/res/drawable-night/logo_dsiware.png differ diff --git a/app/src/main/res/drawable/logo_dsiware.png b/app/src/main/res/drawable/logo_dsiware.png new file mode 100644 index 0000000..084600b Binary files /dev/null and b/app/src/main/res/drawable/logo_dsiware.png differ diff --git a/app/src/main/res/layout/item_rom_base.xml b/app/src/main/res/layout/item_rom_base.xml index 6eb8c92..3db5530 100644 --- a/app/src/main/res/layout/item_rom_base.xml +++ b/app/src/main/res/layout/item_rom_base.xml @@ -11,13 +11,26 @@ android:layout_height="48dp" android:layout_marginEnd="8dp" /> + + - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toStartOf="@id/buttonRomConfig" + app:layout_goneMarginEnd="8dp"> - \ No newline at end of file + \ No newline at end of file