diff --git a/android/app/src/main/java/io/github/kichikuou/xsystem35/GameActivity.kt b/android/app/src/main/java/io/github/kichikuou/xsystem35/GameActivity.kt index 57344f3..b444082 100644 --- a/android/app/src/main/java/io/github/kichikuou/xsystem35/GameActivity.kt +++ b/android/app/src/main/java/io/github/kichikuou/xsystem35/GameActivity.kt @@ -30,9 +30,11 @@ import java.io.IOException // Intent for this activity must have the following extra: // - EXTRA_GAME_ROOT (string): A path to the game installation. +// - EXTRA_SAVE_DIRECTORY (string): A path to the save data directory. class GameActivity : SDLActivity() { companion object { const val EXTRA_GAME_ROOT = "GAME_ROOT" + const val EXTRA_SAVE_DIRECTORY = "SAVE_DIRECTORY" const val EXTRA_ARCHIVE_NAME = "ARCHIVE_NAME" } @@ -61,6 +63,7 @@ class GameActivity : SDLActivity() { override fun getArguments(): Array { return arrayOf( "-gamedir", intent.getStringExtra(EXTRA_GAME_ROOT)!!, + "-savedir", intent.getStringExtra(EXTRA_SAVE_DIRECTORY)!!, "-playlist", Launcher.PLAYLIST_FILE) } diff --git a/android/app/src/main/java/io/github/kichikuou/xsystem35/Launcher.kt b/android/app/src/main/java/io/github/kichikuou/xsystem35/Launcher.kt index 1359cca..88f0ee4 100644 --- a/android/app/src/main/java/io/github/kichikuou/xsystem35/Launcher.kt +++ b/android/app/src/main/java/io/github/kichikuou/xsystem35/Launcher.kt @@ -25,9 +25,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.* -import java.lang.StringBuilder import java.nio.charset.Charset -import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream @@ -41,11 +39,11 @@ interface LauncherObserver { fun onInstallFailure(msgId: Int) } -private const val SAVE_DIR = "save" - class Launcher private constructor(private val rootDir: File) { companion object { + const val SAVE_DIR = "save" const val TITLE_FILE = "title.txt" + const val GAMEDIR_FILE = "game_directory.txt" const val PLAYLIST_FILE = "playlist2.txt" const val OLD_PLAYLIST_FILE = "playlist.txt" @@ -79,14 +77,14 @@ class Launcher private constructor(private val rootDir: File) { isInstalling = true GlobalScope.launch(Dispatchers.Main) { try { - withContext(Dispatchers.IO) { + val gameDir = withContext(Dispatchers.IO) { extractFiles(input, dir) { msg -> GlobalScope.launch(Dispatchers.Main) { observer?.onInstallProgress(msg) } } } - observer?.onInstallSuccess(dir, archiveName) + observer?.onInstallSuccess(gameDir, archiveName) } catch (e: InstallFailureException) { observer?.onInstallFailure(e.msgId) } catch (e: Exception) { @@ -114,9 +112,11 @@ class Launcher private constructor(private val rootDir: File) { continue } try { - val titleFile = File(path, TITLE_FILE) + val gameDirFile = File(path, GAMEDIR_FILE) + val gamePath = if (gameDirFile.exists()) File(path, gameDirFile.readText()) else path + val titleFile = File(gamePath, TITLE_FILE) val title = titleFile.readText() - games.add(Entry(path, title, titleFile.lastModified())) + games.add(Entry(gamePath, title, titleFile.lastModified())) migratePlaylist(path) } catch (e: IOException) { // Incomplete game installation. Delete it. @@ -179,7 +179,7 @@ class Launcher private constructor(private val rootDir: File) { } } - private fun extractFiles(input: InputStream, outDir: File, progressCallback: (String) -> Unit) { + private fun extractFiles(input: InputStream, outDir: File, progressCallback: (String) -> Unit): File { val configWriter = GameConfigWriter() val hadDecodeError = forEachZipEntry(input) { zipEntry, zip -> Log.i("extractFiles", zipEntry.name) @@ -193,12 +193,13 @@ class Launcher private constructor(private val rootDir: File) { } configWriter.maybeAdd(zipEntry.name) } - if (!configWriter.readyToWrite()) { + if (!configWriter.ready) { if (hadDecodeError) throw InstallFailureException(R.string.unsupported_zip) throw InstallFailureException(R.string.cannot_find_ald) } configWriter.write(outDir) + return configWriter.gameDir?.let { File(outDir, it) } ?: outDir } // Xsystem35-sdl2 <=2.2.0 had a bug where playlist had an extra empty line at @@ -217,66 +218,43 @@ class Launcher private constructor(private val rootDir: File) { class InstallFailureException(val msgId: Int) : Exception() - // A helper class which generates xsystem35.gr and playlist.txt in the game root directory. + // A helper class which generates GAMEDIR_FILE and PLAYLIST_FILE. private class GameConfigWriter { - private val grb = StringBuilder() - private var basename: String? = null - private val aldRegex = """(.*?)([a-z])([a-z])\.(ald|wai|bgi)""".toRegex(RegexOption.IGNORE_CASE) - private val aldType = mapOf( - "b" to "BGM", - "d" to "Data", - "g" to "Graphics", - "m" to "Midi", - "r" to "Resource", - "s" to "Scenario", - "w" to "Wave") - private val specialResources = mapOf( - "system39.ain" to "Ain", - "system39.ini" to "Init", - "sactefam.kld" to "SACT01" - ) + var ready = false + private set + var gameDir: String? = null + private set + private val aldRegex = """.*?s[a-z]\.ald""".toRegex(RegexOption.IGNORE_CASE) private val audioRegex = """.*?(\d+)\.(wav|mp3|ogg)""".toRegex(RegexOption.IGNORE_CASE) private val audioFiles: Array = arrayOfNulls(100) fun maybeAdd(path: String) { - val name = File(path).name - - specialResources[name.lowercase(Locale.US)]?.let { - grb.appendLine("$it $path") - return + aldRegex.matchEntire(path)?.let { + gameDir = File(path).parent + ready = true } - aldRegex.matchEntire(name)?.let { - val ext = it.groupValues[4].uppercase(Locale.US) - val type = if (ext == "ALD") { - aldType[it.groupValues[2].lowercase(Locale.US)] - } else { - ext - } - val id = it.groupValues[3].uppercase(Locale.US) - if (type != null) { - grb.appendLine("$type$id $path") - basename = it.groupValues[1] - } - } - audioRegex.matchEntire(name)?.let { + audioRegex.matchEntire(path)?.let { val track = it.groupValues[1].toInt() if (0 < track && track <= audioFiles.size) audioFiles[track - 1] = path } } - fun readyToWrite() = basename != null - fun write(outDir: File) { - for (id in 'A' .. 'Z') { - grb.appendLine("Save$id ../save/${basename}s${id.lowercase(Locale.US)}.asd") + // Generate GAMEDIR_FILE + gameDir?.let { + File(outDir, GAMEDIR_FILE).writeText(it) + } + // Generate PLAYLIST_FILE + val absGameDir = gameDir?.let { File(outDir, it) } ?: outDir + val playlistFile = File(absGameDir, PLAYLIST_FILE) + if (!playlistFile.exists()) { + val prefixToRemove = gameDir?.let { "$it/" } ?: "" + val playlist = audioFiles.joinToString("\n") { + it?.removePrefix(prefixToRemove) ?: "" + }.trimEnd('\n') + playlistFile.writeText(playlist) } - val gr = grb.toString() - Log.i("xsystem35.gr", gr) - File(outDir, "xsystem35.gr").writeText(gr) - - val playlist = audioFiles.joinToString("\n") { it ?: "" }.trimEnd('\n') - File(outDir, PLAYLIST_FILE).writeText(playlist) } } } diff --git a/android/app/src/main/java/io/github/kichikuou/xsystem35/LauncherActivity.kt b/android/app/src/main/java/io/github/kichikuou/xsystem35/LauncherActivity.kt index d3b43d5..e128ff8 100644 --- a/android/app/src/main/java/io/github/kichikuou/xsystem35/LauncherActivity.kt +++ b/android/app/src/main/java/io/github/kichikuou/xsystem35/LauncherActivity.kt @@ -180,6 +180,7 @@ class LauncherActivity : Activity(), LauncherObserver { val i = Intent() i.setClass(applicationContext, GameActivity::class.java) i.putExtra(GameActivity.EXTRA_GAME_ROOT, path.path) + i.putExtra(GameActivity.EXTRA_SAVE_DIRECTORY, File(filesDir, Launcher.SAVE_DIR).path) i.putExtra(GameActivity.EXTRA_ARCHIVE_NAME, archiveName) startActivity(i) }