Android: Generate xsystem35.gr file

The installer writes xsystem35.gr file into the game root directory.

Now save files are created at `files/save` directory, which is outside
the game directory. This scheme is the same as Kichikuou on Web, so it
would make transferring save data between two easier.
This commit is contained in:
kichikuou
2020-01-28 21:53:44 +09:00
parent c646028afc
commit c98a11dc0e
3 changed files with 62 additions and 31 deletions
@@ -41,7 +41,7 @@ class GameActivity : SDLActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
cdda = CddaPlayer(intent.getStringExtra(EXTRA_PLAYLIST_FILE))
cdda = CddaPlayer(File(intent.getStringExtra(EXTRA_PLAYLIST_FILE)))
}
override fun onStop() {
@@ -81,15 +81,14 @@ class GameActivity : SDLActivity() {
@Suppress("unused") fun midiCurrentPosition() = midi.currentPosition()
}
private class CddaPlayer(playlistPath: String?) {
private val playlist = playlistPath?.let {
private class CddaPlayer(private val playlistPath: File) {
private val playlist =
try {
File(it).readLines()
playlistPath.readLines()
} catch (e: IOException) {
Log.e("loadPlaylist", "Cannot load $playlistPath", e)
null
emptyList<String>()
}
} ?: emptyList()
private var currentTrack = 0
private val player = MediaPlayer()
private var playerPaused = false
@@ -104,7 +103,7 @@ private class CddaPlayer(playlistPath: String?) {
try {
player.apply {
reset()
setDataSource(f)
setDataSource(File(playlistPath.parent, f).path)
isLooping = loop == 0
prepare()
start()
@@ -24,6 +24,7 @@ import android.os.Message
import android.util.Log
import java.io.*
import java.nio.charset.Charset
import java.util.*
import java.util.zip.ZipInputStream
import kotlin.collections.ArrayList
@@ -61,9 +62,14 @@ class Launcher private constructor(private val rootDir: File) {
private set
init {
var saveDirFound = false
for (path in rootDir.listFiles()) {
if (!path.isDirectory)
continue
if (path.name == "save") {
saveDirFound = true
continue
}
try {
val title = File(path, TITLE_FILE).readText()
games.add(Entry(path, title))
@@ -72,6 +78,9 @@ class Launcher private constructor(private val rootDir: File) {
path.deleteRecursively()
}
}
if (!saveDirFound) {
File(rootDir, "save").mkdir()
}
}
fun install(input: InputStream) {
@@ -118,7 +127,7 @@ class Launcher private constructor(private val rootDir: File) {
private fun extractFiles(input: InputStream, outDir: File, handler: Handler) {
try {
val playlistWriter = PlaylistWriter()
val configWriter = GameConfigWriter()
val zip = if (Build.VERSION.SDK_INT >= 24) {
ZipInputStream(input.buffered(), Charset.forName("Shift_JIS"))
} else {
@@ -135,11 +144,13 @@ class Launcher private constructor(private val rootDir: File) {
val output = FileOutputStream(path).buffered()
zip.copyTo(output)
output.close()
playlistWriter.maybeAdd(path.path)
configWriter.maybeAdd(zipEntry.name)
}
zip.close()
playlistWriter.write(outDir)
configWriter.write(outDir)
handler.sendMessage(handler.obtainMessage(SUCCESS, outDir))
} catch (e: InstallFailureException) {
handler.sendMessage(handler.obtainMessage(FAILURE, e.msgId))
} catch (e: UTFDataFormatException) {
// Attempted to read Shift_JIS zip in Android < 7
handler.sendMessage(handler.obtainMessage(FAILURE, R.string.unsupported_zip))
@@ -149,12 +160,40 @@ class Launcher private constructor(private val rootDir: File) {
}
}
private class PlaylistWriter {
class InstallFailureException(val msgId: Int) : Exception()
// A helper class which generates xsystem35.gr and playlist.txt in the game root directory.
private class GameConfigWriter {
private val grLines: ArrayList<String> = arrayListOf()
private var basename: String? = null
private val aldRegex = """(.*?)([a-z])([a-z])\.ald""".toRegex(RegexOption.IGNORE_CASE)
private val resourceType = mapOf(
"d" to "Data",
"g" to "Graphics",
"m" to "Midi",
"r" to "Resource",
"s" to "Scenario",
"w" to "Wave")
private val audioRegex = """.*?(\d+)\.(wav|mp3|ogg)""".toRegex(RegexOption.IGNORE_CASE)
private val audioFiles: Array<String?> = arrayOfNulls(100)
fun maybeAdd(path: String) {
audioRegex.matchEntire(path)?.let {
val name = File(path).name
if (name.toLowerCase(Locale.US) == "system39.ain") {
grLines.add("Ain ${path}")
return
}
aldRegex.matchEntire(name)?.let {
val type = resourceType[it.groupValues[2]]
val id = it.groupValues[3].toUpperCase(Locale.US)
if (type != null) {
grLines.add("$type$id $path")
basename = it.groupValues[1]
}
}
audioRegex.matchEntire(name)?.let {
val track = it.groupValues[1].toInt()
if (track < audioFiles.size)
audioFiles[track] = path
@@ -162,8 +201,17 @@ class Launcher private constructor(private val rootDir: File) {
}
fun write(outDir: File) {
val text = audioFiles.joinToString("\n") { it ?: "" }.trimEnd('\n')
File(outDir, PLAYLIST_FILE).writeText(text)
basename ?: throw InstallFailureException(R.string.cannot_find_ald)
for (id in 'A' .. 'Z') {
grLines.add("Save$id ../save/${basename}s${id.toLowerCase()}.asd")
}
grLines.add("")
val gr = grLines.joinToString("\n")
Log.d("xsystem35.gr", gr)
File(outDir, "xsystem35.gr").writeText(gr)
val playlist = audioFiles.joinToString("\n") { it ?: "" }.trimEnd('\n')
File(outDir, PLAYLIST_FILE).writeText(playlist)
}
}
}
@@ -26,8 +26,6 @@ import android.widget.ArrayAdapter
import android.widget.ListView
import java.io.*
import java.util.Locale
class LauncherActivity : ListActivity(), AdapterView.OnItemLongClickListener, LauncherObserver {
private lateinit var launcher: Launcher
private lateinit var adapter: ArrayAdapter<String>
@@ -100,14 +98,9 @@ class LauncherActivity : ListActivity(), AdapterView.OnItemLongClickListener, La
}
private fun startGame(path: File) {
val gameRoot = findGameRoot(path)
if (gameRoot == null) {
errorDialog(R.string.cannot_find_ald)
return
}
val i = Intent()
i.setClass(applicationContext, GameActivity::class.java)
i.putExtra(GameActivity.EXTRA_GAME_ROOT, gameRoot.path)
i.putExtra(GameActivity.EXTRA_GAME_ROOT, path.path)
i.putExtra(GameActivity.EXTRA_TITLE_FILE, File(path, Launcher.TITLE_FILE).path)
i.putExtra(GameActivity.EXTRA_PLAYLIST_FILE, File(path, Launcher.PLAYLIST_FILE).path)
startActivity(i)
@@ -150,12 +143,3 @@ class ProgressDialogFragment : DialogFragment() {
dialog.setMessage(msg)
}
}
private fun findGameRoot(path: File): File? {
path.walkTopDown().forEach {
val name = it.name.toLowerCase(Locale.US)
if (name == "xsystem35.gr" || name.endsWith(".ald"))
return it.parentFile
}
return null
}