Add hero save editor with D64 format documentation
Pencil button next to each hero disk opens an editor dialog that parses the D64 directly, lists all PRG save files in a spinner, and lets the user adjust all nine stats (Vitalität, grade, six percentage stats) with +/− buttons. Changes to multiple heroes in one session are written back together on Save. PETSCII decoding now distinguishes unshifted (0x41–0x5A → lowercase) from shifted (0xC1–0xDA → uppercase) so names display in natural mixed case (e.g. "Mark" instead of "MARK"). D64 sector layout helpers extracted to class level so createBlankD64 and the new editor share the same offset function. Save format documented in res/HELD_D64_FORMAT.md. Editor strings localised in EN/DE.
This commit is contained in:
+225
-20
@@ -12,9 +12,12 @@ import android.content.res.ColorStateList
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.view.Choreographer
|
import android.view.Choreographer
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.ScrollView
|
import android.widget.ScrollView
|
||||||
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
@@ -64,6 +67,24 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// ---- disk slots ---------------------------------------------------------
|
// ---- disk slots ---------------------------------------------------------
|
||||||
|
|
||||||
|
// ---- D64 helpers (shared by createBlankD64 and the hero editor) ----------
|
||||||
|
|
||||||
|
private val d64Spt = intArrayOf(
|
||||||
|
0,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||||
|
19, 19, 19, 19, 19, 19, 19,
|
||||||
|
18, 18, 18, 18, 18, 18,
|
||||||
|
17, 17, 17, 17, 17
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun d64Off(track: Int, sector: Int): Int {
|
||||||
|
var off = 0
|
||||||
|
for (t in 1 until track) off += d64Spt[t] * 256
|
||||||
|
return off + sector * 256
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- disk slots ---------------------------------------------------------
|
||||||
|
|
||||||
private val diskSlots = listOf(
|
private val diskSlots = listOf(
|
||||||
DiskSlot(1, 'A', R.id.btn_ep_1a, "Folge 1: Das geheimnisvolle Kraut"),
|
DiskSlot(1, 'A', R.id.btn_ep_1a, "Folge 1: Das geheimnisvolle Kraut"),
|
||||||
DiskSlot(1, 'B', R.id.btn_ep_1b, "Folge 2: Der unheimliche Tempel"),
|
DiskSlot(1, 'B', R.id.btn_ep_1b, "Folge 2: Der unheimliche Tempel"),
|
||||||
@@ -78,6 +99,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
DiskSlot(0, ' ', R.id.btn_hero_3, "Held 3", "HELD3.D64"),
|
DiskSlot(0, ' ', R.id.btn_hero_3, "Held 3", "HELD3.D64"),
|
||||||
)
|
)
|
||||||
private val episodeButtons = mutableMapOf<DiskSlot, Button>()
|
private val episodeButtons = mutableMapOf<DiskSlot, Button>()
|
||||||
|
private val heroEditButtons = mutableMapOf<DiskSlot, Button>()
|
||||||
private val loadedADisks = mutableSetOf<Int>()
|
private val loadedADisks = mutableSetOf<Int>()
|
||||||
private var activeSlot: DiskSlot? = null
|
private var activeSlot: DiskSlot? = null
|
||||||
private var currentDiskPath: String? = null // path last passed to engine.loadDisk()
|
private var currentDiskPath: String? = null // path last passed to engine.loadDisk()
|
||||||
@@ -187,6 +209,17 @@ class MainActivity : AppCompatActivity() {
|
|||||||
findViewById<Button>(btnId).setOnClickListener { confirmCreateHeroDisk(slot) }
|
findViewById<Button>(btnId).setOnClickListener { confirmCreateHeroDisk(slot) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val editButtonIds = mapOf(
|
||||||
|
R.id.btn_hero_1_edit to diskSlots.first { it.fileName == "HELD1.D64" },
|
||||||
|
R.id.btn_hero_2_edit to diskSlots.first { it.fileName == "HELD2.D64" },
|
||||||
|
R.id.btn_hero_3_edit to diskSlots.first { it.fileName == "HELD3.D64" },
|
||||||
|
)
|
||||||
|
for ((btnId, slot) in editButtonIds) {
|
||||||
|
val btn = findViewById<Button>(btnId)
|
||||||
|
heroEditButtons[slot] = btn
|
||||||
|
btn.setOnClickListener { showHeroEditor(slot) }
|
||||||
|
}
|
||||||
|
|
||||||
choreographer = Choreographer.getInstance()
|
choreographer = Choreographer.getInstance()
|
||||||
initEmulator()
|
initEmulator()
|
||||||
refreshDiskButtons()
|
refreshDiskButtons()
|
||||||
@@ -285,12 +318,14 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun refreshDiskButtons() {
|
private fun refreshDiskButtons() {
|
||||||
for (slot in diskSlots) {
|
for (slot in diskSlots) {
|
||||||
|
val exists = diskFile(slot) != null
|
||||||
val btn = episodeButtons[slot] ?: continue
|
val btn = episodeButtons[slot] ?: continue
|
||||||
btn.isEnabled = diskFile(slot) != null
|
btn.isEnabled = exists
|
||||||
btn.backgroundTintList = if (slot == activeSlot)
|
btn.backgroundTintList = if (slot == activeSlot)
|
||||||
ColorStateList.valueOf(Color.parseColor("#1A4A1A"))
|
ColorStateList.valueOf(Color.parseColor("#1A4A1A"))
|
||||||
else
|
else
|
||||||
ColorStateList.valueOf(Color.TRANSPARENT)
|
ColorStateList.valueOf(Color.TRANSPARENT)
|
||||||
|
heroEditButtons[slot]?.isEnabled = exists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,26 +421,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
/** Returns a 174 848-byte blank formatted 1541 D64 image with [diskLabel] as the disk name. */
|
/** Returns a 174 848-byte blank formatted 1541 D64 image with [diskLabel] as the disk name. */
|
||||||
private fun createBlankD64(diskLabel: String): ByteArray {
|
private fun createBlankD64(diskLabel: String): ByteArray {
|
||||||
// Sectors per track for a standard 35-track 1541 disk
|
|
||||||
val sectorsPerTrack = intArrayOf(
|
|
||||||
0, // index 0 unused
|
|
||||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, // 1-17
|
|
||||||
19, // 18 (directory)
|
|
||||||
19, 19, 19, 19, 19, 19, // 19-24
|
|
||||||
18, 18, 18, 18, 18, 18, // 25-30
|
|
||||||
17, 17, 17, 17, 17 // 31-35
|
|
||||||
)
|
|
||||||
|
|
||||||
val data = ByteArray(174848)
|
val data = ByteArray(174848)
|
||||||
|
|
||||||
fun offset(track: Int, sector: Int): Int {
|
|
||||||
var off = 0
|
|
||||||
for (t in 1 until track) off += sectorsPerTrack[t] * 256
|
|
||||||
return off + sector * 256
|
|
||||||
}
|
|
||||||
|
|
||||||
// BAM sector — track 18, sector 0
|
// BAM sector — track 18, sector 0
|
||||||
val bam = offset(18, 0)
|
val bam = d64Off(18, 0)
|
||||||
data[bam + 0] = 18 // first directory track
|
data[bam + 0] = 18 // first directory track
|
||||||
data[bam + 1] = 1 // first directory sector
|
data[bam + 1] = 1 // first directory sector
|
||||||
data[bam + 2] = 0x41 // DOS version 'A'
|
data[bam + 2] = 0x41 // DOS version 'A'
|
||||||
@@ -413,7 +432,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
for (t in 1..35) {
|
for (t in 1..35) {
|
||||||
val entry = bam + 4 + (t - 1) * 4
|
val entry = bam + 4 + (t - 1) * 4
|
||||||
val sectors = sectorsPerTrack[t]
|
val sectors = d64Spt[t]
|
||||||
// All sectors free; track 18 has sectors 0 (BAM) and 1 (dir) pre-allocated
|
// All sectors free; track 18 has sectors 0 (BAM) and 1 (dir) pre-allocated
|
||||||
var mask = (1 shl sectors) - 1
|
var mask = (1 shl sectors) - 1
|
||||||
var free = sectors
|
var free = sectors
|
||||||
@@ -440,13 +459,194 @@ class MainActivity : AppCompatActivity() {
|
|||||||
for (i in 167..255) data[bam + i] = 0xA0.toByte()
|
for (i in 167..255) data[bam + i] = 0xA0.toByte()
|
||||||
|
|
||||||
// First directory sector — track 18, sector 1
|
// First directory sector — track 18, sector 1
|
||||||
val dir = offset(18, 1)
|
val dir = d64Off(18, 1)
|
||||||
data[dir + 0] = 0x00 // no next track (end of chain)
|
data[dir + 0] = 0x00 // no next track (end of chain)
|
||||||
data[dir + 1] = 0xFF.toByte()
|
data[dir + 1] = 0xFF.toByte()
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- hero save editor ---------------------------------------------------
|
||||||
|
|
||||||
|
private fun petsciiToString(bytes: ByteArray): String = buildString {
|
||||||
|
for (b in bytes) {
|
||||||
|
val v = b.toInt() and 0xFF
|
||||||
|
when {
|
||||||
|
v == 0x00 || v == 0xA0 -> return@buildString
|
||||||
|
v in 0xC1..0xDA -> append((v - 0x80).toChar()) // shifted → A-Z uppercase
|
||||||
|
v in 0x41..0x5A -> append((v + 0x20).toChar()) // unshifted → a-z lowercase
|
||||||
|
v in 0x30..0x39 -> append(v.toChar())
|
||||||
|
v == 0x20 -> append(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Walks the directory chain and returns every PRG file found on the disk. */
|
||||||
|
private fun findAllHeroPrgs(d64: ByteArray): List<HeroPrg> {
|
||||||
|
val result = mutableListOf<HeroPrg>()
|
||||||
|
var track = 18; var sector = 1
|
||||||
|
while (track != 0) {
|
||||||
|
val base = d64Off(track, sector)
|
||||||
|
for (i in 0 until 8) {
|
||||||
|
val e = base + 2 + i * 32
|
||||||
|
val ft = d64[e].toInt() and 0xFF
|
||||||
|
if ((ft and 0x80) != 0 && (ft and 0x0F) == 0x02) {
|
||||||
|
val t = d64[e + 1].toInt() and 0xFF
|
||||||
|
val s = d64[e + 2].toInt() and 0xFF
|
||||||
|
if (t != 0) {
|
||||||
|
val sectorOff = d64Off(t, s)
|
||||||
|
val prgBase = sectorOff + 2
|
||||||
|
val name = petsciiToString(d64.copyOfRange(prgBase + 0x02, prgBase + 0x12))
|
||||||
|
result.add(HeroPrg(
|
||||||
|
name = name,
|
||||||
|
sectorOff = sectorOff,
|
||||||
|
values = intArrayOf(
|
||||||
|
d64[prgBase + 0x12].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x13].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x14].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x15].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x16].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x17].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x18].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x19].toInt() and 0xFF,
|
||||||
|
d64[prgBase + 0x1A].toInt() and 0xFF,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
track = d64[base].toInt() and 0xFF
|
||||||
|
sector = d64[base + 1].toInt() and 0xFF
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showHeroEditor(slot: DiskSlot) {
|
||||||
|
val file = diskFile(slot) ?: return
|
||||||
|
val d64 = try { file.readBytes() } catch (e: Exception) { return }
|
||||||
|
val heroes = findAllHeroPrgs(d64)
|
||||||
|
|
||||||
|
if (heroes.isEmpty()) {
|
||||||
|
Toast.makeText(this, getString(R.string.no_hero_save), Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val labels = arrayOf("Vitalität", "Tapferkeit", "Intelligenz", "Charme",
|
||||||
|
"Stärke", "Geschicklichkeit", "Angriffswert", "Verteidigung", "Grade")
|
||||||
|
val isPercent = booleanArrayOf(false, true, true, true, true, true, true, true, false)
|
||||||
|
val minVals = intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 1)
|
||||||
|
val maxVals = intArrayOf(27, 20, 20, 20, 20, 20, 20, 20, 9)
|
||||||
|
|
||||||
|
var selectedIdx = 0
|
||||||
|
val dp = { v: Int -> (v * resources.displayMetrics.density).toInt() }
|
||||||
|
|
||||||
|
fun displayStr(heroIdx: Int, statIdx: Int) =
|
||||||
|
if (isPercent[statIdx]) "${heroes[heroIdx].values[statIdx] * 5}%"
|
||||||
|
else "${heroes[heroIdx].values[statIdx]}"
|
||||||
|
|
||||||
|
val content = android.widget.LinearLayout(this).apply {
|
||||||
|
orientation = android.widget.LinearLayout.VERTICAL
|
||||||
|
setPadding(dp(16), dp(12), dp(16), dp(8))
|
||||||
|
setBackgroundColor(Color.parseColor("#1A1A2E"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hero selector — always shown (informative even with a single hero)
|
||||||
|
val spinner = Spinner(this).apply {
|
||||||
|
adapter = ArrayAdapter(
|
||||||
|
this@MainActivity,
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
heroes.map { it.name }
|
||||||
|
).also { it.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) }
|
||||||
|
layoutParams = android.widget.LinearLayout.LayoutParams(
|
||||||
|
android.widget.LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { bottomMargin = dp(12) }
|
||||||
|
}
|
||||||
|
content.addView(spinner)
|
||||||
|
|
||||||
|
// Stat value displays — shared across hero switches
|
||||||
|
val valueTvs = Array(labels.size) { i ->
|
||||||
|
TextView(this).apply {
|
||||||
|
text = displayStr(0, i)
|
||||||
|
textSize = 13f
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
gravity = android.view.Gravity.CENTER
|
||||||
|
layoutParams = android.widget.LinearLayout.LayoutParams(dp(52),
|
||||||
|
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in labels.indices) {
|
||||||
|
val row = android.widget.LinearLayout(this).apply {
|
||||||
|
orientation = android.widget.LinearLayout.HORIZONTAL
|
||||||
|
gravity = android.view.Gravity.CENTER_VERTICAL
|
||||||
|
layoutParams = android.widget.LinearLayout.LayoutParams(
|
||||||
|
android.widget.LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { bottomMargin = dp(4) }
|
||||||
|
}
|
||||||
|
row.addView(TextView(this).apply {
|
||||||
|
text = labels[i]
|
||||||
|
textSize = 12f
|
||||||
|
setTextColor(Color.parseColor("#CCCCCC"))
|
||||||
|
layoutParams = android.widget.LinearLayout.LayoutParams(
|
||||||
|
0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
|
})
|
||||||
|
row.addView(Button(this).apply {
|
||||||
|
text = "−"; textSize = 14f
|
||||||
|
layoutParams = android.widget.LinearLayout.LayoutParams(dp(44),
|
||||||
|
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||||
|
setOnClickListener {
|
||||||
|
val v = heroes[selectedIdx].values
|
||||||
|
if (v[i] > minVals[i]) { v[i]--; valueTvs[i].text = displayStr(selectedIdx, i) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
row.addView(valueTvs[i])
|
||||||
|
row.addView(Button(this).apply {
|
||||||
|
text = "+"; textSize = 14f
|
||||||
|
layoutParams = android.widget.LinearLayout.LayoutParams(dp(44),
|
||||||
|
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||||
|
setOnClickListener {
|
||||||
|
val v = heroes[selectedIdx].values
|
||||||
|
if (v[i] < maxVals[i]) { v[i]++; valueTvs[i].text = displayStr(selectedIdx, i) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
content.addView(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switching heroes refreshes all value views
|
||||||
|
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(p: AdapterView<*>?, v: android.view.View?, pos: Int, id: Long) {
|
||||||
|
selectedIdx = pos
|
||||||
|
for (i in labels.indices) valueTvs[i].text = displayStr(selectedIdx, i)
|
||||||
|
}
|
||||||
|
override fun onNothingSelected(p: AdapterView<*>?) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle(getString(R.string.editor_title, slot.label))
|
||||||
|
.setView(android.widget.ScrollView(this).apply {
|
||||||
|
addView(content)
|
||||||
|
setBackgroundColor(Color.parseColor("#1A1A2E"))
|
||||||
|
})
|
||||||
|
.setPositiveButton(R.string.editor_save) { _, _ ->
|
||||||
|
for (hero in heroes) {
|
||||||
|
val prgBase = hero.sectorOff + 2
|
||||||
|
for (i in hero.values.indices)
|
||||||
|
d64[prgBase + 0x12 + i] = hero.values[i].toByte()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
file.writeBytes(d64)
|
||||||
|
appendLog("Saved: ${heroes.joinToString { it.name }} (${slot.label})")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
appendLog("Save failed: ${slot.label}")
|
||||||
|
Log.e(TAG, "Hero editor write failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
// ---- splash screen ------------------------------------------------------
|
// ---- splash screen ------------------------------------------------------
|
||||||
|
|
||||||
private fun dismissSplashDelayed() {
|
private fun dismissSplashDelayed() {
|
||||||
@@ -543,3 +743,8 @@ class CompanionServer(
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** One hero's save data read from a D64 PRG sector. [values] is mutable for in-place editing. */
|
||||||
|
private class HeroPrg(val name: String, val sectorOff: Int, val values: IntArray)
|
||||||
|
|||||||
@@ -206,6 +206,10 @@
|
|||||||
<Button android:id="@+id/btn_hero_1_new" style="?attr/materialButtonOutlinedStyle"
|
<Button android:id="@+id/btn_hero_1_new" style="?attr/materialButtonOutlinedStyle"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp" android:text="@string/btn_new" android:textSize="11sp" />
|
android:layout_marginStart="4dp" android:text="@string/btn_new" android:textSize="11sp" />
|
||||||
|
<Button android:id="@+id/btn_hero_1_edit" style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp" android:minWidth="0dp" android:padding="0dp"
|
||||||
|
android:text="✏" android:textSize="14sp" android:enabled="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||||
@@ -217,6 +221,10 @@
|
|||||||
<Button android:id="@+id/btn_hero_2_new" style="?attr/materialButtonOutlinedStyle"
|
<Button android:id="@+id/btn_hero_2_new" style="?attr/materialButtonOutlinedStyle"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp" android:text="@string/btn_new" android:textSize="11sp" />
|
android:layout_marginStart="4dp" android:text="@string/btn_new" android:textSize="11sp" />
|
||||||
|
<Button android:id="@+id/btn_hero_2_edit" style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp" android:minWidth="0dp" android:padding="0dp"
|
||||||
|
android:text="✏" android:textSize="14sp" android:enabled="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||||
@@ -228,6 +236,10 @@
|
|||||||
<Button android:id="@+id/btn_hero_3_new" style="?attr/materialButtonOutlinedStyle"
|
<Button android:id="@+id/btn_hero_3_new" style="?attr/materialButtonOutlinedStyle"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp" android:text="@string/btn_new" android:textSize="11sp" />
|
android:layout_marginStart="4dp" android:text="@string/btn_new" android:textSize="11sp" />
|
||||||
|
<Button android:id="@+id/btn_hero_3_edit" style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="36dp" android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp" android:minWidth="0dp" android:padding="0dp"
|
||||||
|
android:text="✏" android:textSize="14sp" android:enabled="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -29,4 +29,10 @@
|
|||||||
<!-- Hero disk creation log -->
|
<!-- Hero disk creation log -->
|
||||||
<string name="hero_disk_created">Erstellt: %1$s</string>
|
<string name="hero_disk_created">Erstellt: %1$s</string>
|
||||||
<string name="hero_disk_create_failed">Fehler beim Erstellen: %1$s</string>
|
<string name="hero_disk_create_failed">Fehler beim Erstellen: %1$s</string>
|
||||||
|
|
||||||
|
<!-- Hero save editor -->
|
||||||
|
<string name="editor_title">%1$s bearbeiten</string>
|
||||||
|
<string name="editor_save">Speichern</string>
|
||||||
|
<string name="no_hero_save">Noch kein Heldspeicherstand auf dieser Diskette</string>
|
||||||
|
<string name="stat_name">Name</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -31,4 +31,10 @@
|
|||||||
<!-- Hero disk creation log -->
|
<!-- Hero disk creation log -->
|
||||||
<string name="hero_disk_created">Created: %1$s</string>
|
<string name="hero_disk_created">Created: %1$s</string>
|
||||||
<string name="hero_disk_create_failed">Failed to create: %1$s</string>
|
<string name="hero_disk_create_failed">Failed to create: %1$s</string>
|
||||||
|
|
||||||
|
<!-- Hero save editor -->
|
||||||
|
<string name="editor_title">Edit %1$s</string>
|
||||||
|
<string name="editor_save">Save</string>
|
||||||
|
<string name="no_hero_save">No hero save found on this disk yet</string>
|
||||||
|
<string name="stat_name">Name</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user