mirror of
https://github.com/rafaelvcaetano/melonDS-android.git
synced 2025-02-24 15:50:58 +00:00
Identify DSiWare titles in the ROM list screen and prevent shortcuts from being created for these titles
This commit is contained in:
parent
23d09b5888
commit
946158402b
@ -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<Uri> {
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -0,0 +1,6 @@
|
||||
package me.magnum.melonds.domain.model
|
||||
|
||||
data class RomMetadata(
|
||||
val romTitle: String,
|
||||
val isDSiWareTitle: Boolean,
|
||||
)
|
@ -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!!)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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<RomConfigParcelable>()
|
||||
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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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<RomViewHolder>() {
|
||||
|
||||
private val roms: ArrayList<Rom> = 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<ImageView>(R.id.imageRomIcon)
|
||||
private val textViewRomName = itemView.findViewById<TextView>(R.id.textRomName)
|
||||
private val textViewRomPath = itemView.findViewById<TextView>(R.id.textRomPath)
|
||||
private val imagePlatformLogo = itemView.findViewById<ImageView>(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<Rom>, private val newRoms: List<Rom>) : 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
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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<String>(KEY_ROM_NAME)
|
||||
val isDsiWareTitle = romStreamProcessor.getValue<Boolean>(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<SectionProcessor>()
|
||||
private val values = mutableMapOf<String, Any>()
|
||||
|
||||
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 <T> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
app/src/main/res/drawable-night/logo_dsiware.png
Normal file
BIN
app/src/main/res/drawable-night/logo_dsiware.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/drawable/logo_dsiware.png
Normal file
BIN
app/src/main/res/drawable/logo_dsiware.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
@ -11,13 +11,26 @@
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoPlatform"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_alignTop="@+id/textRomName"
|
||||
android:layout_alignBottom="@+id/textRomName"
|
||||
android:layout_toEndOf="@id/imageRomIcon"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:visibility="visible"
|
||||
tools:src="@drawable/logo_dsiware"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textRomName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="18sp"
|
||||
android:layout_toEndOf="@id/imageRomIcon"
|
||||
android:layout_toEndOf="@id/logoPlatform"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/layoutRomItem"
|
||||
android:layout_width="match_parent"
|
||||
@ -16,8 +16,10 @@
|
||||
android:id="@+id/layout_rom_base_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toStartOf="@+id/buttonRomConfig">
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/buttonRomConfig"
|
||||
app:layout_goneMarginEnd="8dp">
|
||||
|
||||
<include layout="@layout/item_rom_base"
|
||||
android:layout_width="match_parent"
|
||||
@ -29,12 +31,13 @@
|
||||
android:id="@+id/buttonRomConfig"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/layout_rom_base_content"
|
||||
android:padding="8dp"
|
||||
android:contentDescription="@string/rom_settings"
|
||||
style="@style/Button.Ripple"
|
||||
app:tint="@color/romConfigButtonDefault"
|
||||
app:srcCompat="@drawable/ic_settings"
|
||||
android:nextFocusLeft="@+id/layoutRomItem" />
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
x
Reference in New Issue
Block a user