mirror of
https://git.ryujinx.app/kenji-nx/ryujinx.git
synced 2025-12-13 04:37:02 +00:00
changed comments to english
This commit is contained in:
parent
fafbaedf98
commit
6447d3512c
2 changed files with 45 additions and 45 deletions
|
|
@ -30,15 +30,15 @@ private fun isHex16(name: String): Boolean =
|
|||
|
||||
data class SaveFolderMeta(
|
||||
val dir: File,
|
||||
val indexHex: String, // z.B. 0000000000000008 oder 000000000000000a
|
||||
val titleId: String?, // aus TITLEID.txt (lowercase) oder geerbter Wert
|
||||
val titleName: String?, // zweite Zeile aus TITLEID.txt, falls vorhanden
|
||||
val hasMarker: Boolean // true, wenn dieser Ordner die TITLEID.txt selbst hat
|
||||
val indexHex: String, // e.g., 0000000000000008 or 000000000000000a
|
||||
val titleId: String?, // from TITLEID.txt (lowercase) or inherited value
|
||||
val titleName: String?, // second line from TITLEID.txt, if present
|
||||
val hasMarker: Boolean // true if this folder itself contains TITLEID.txt
|
||||
)
|
||||
|
||||
/**
|
||||
* Scannt .../bis/user/save; ordnet nummerierte Ordner (16 Hex-Zeichen) per „Marker-Vererbung“:
|
||||
* Ein Ordner ohne TITLEID.txt gehört zum zuletzt gesehenen Ordner mit TITLEID.txt davor.
|
||||
* Scans .../bis/user/save; assigns numbered folders (16 hex chars) via “marker inheritance”:
|
||||
* A folder without TITLEID.txt belongs to the most recent preceding folder that does contain TITLEID.txt.
|
||||
*/
|
||||
fun listSaveFolders(activity: Activity): List<SaveFolderMeta> {
|
||||
val root = savesRootExternal(activity)
|
||||
|
|
@ -72,18 +72,18 @@ fun listSaveFolders(activity: Activity): List<SaveFolderMeta> {
|
|||
return out
|
||||
}
|
||||
|
||||
/* ===== TitleID-Kandidaten (Base/Update tolerant) & Gruppierung ===== */
|
||||
/* ===== TitleID candidates (base/update tolerant) & grouping ===== */
|
||||
|
||||
private fun hex16CandidatesForSaves(id: String): List<String> {
|
||||
val lc = id.trim().lowercase(Locale.ROOT)
|
||||
if (!isHex16(lc)) return listOf(lc)
|
||||
val head = lc.substring(0, 13) // erste 13 Zeichen
|
||||
val head = lc.substring(0, 13) // first 13 characters
|
||||
val base = head + "000" // ...000
|
||||
val upd = head + "800" // ...800
|
||||
return listOf(lc, base, upd).distinct()
|
||||
}
|
||||
|
||||
/** Alle Save-Ordner einer TitleID-Gruppe (Marker-Vererbung), Base/Update tolerant. */
|
||||
/** All save folders of a TitleID group (marker inheritance), tolerant of base/update IDs. */
|
||||
private fun listSaveGroupForTitle(activity: Activity, titleId: String): List<SaveFolderMeta> {
|
||||
val candidates = hex16CandidatesForSaves(titleId)
|
||||
val metas = listSaveFolders(activity)
|
||||
|
|
@ -93,7 +93,7 @@ private fun listSaveGroupForTitle(activity: Activity, titleId: String): List<Sav
|
|||
}
|
||||
}
|
||||
|
||||
/** Bevorzugt den Ordner mit TITLEID.txt; Fallback: lexikografisch kleinster der Gruppe. */
|
||||
/** Prefer the folder with TITLEID.txt; fallback: lexicographically smallest in the group. */
|
||||
private fun pickSaveDirWithMarker(activity: Activity, titleId: String): File? {
|
||||
val group = listSaveGroupForTitle(activity, titleId)
|
||||
val marker = group.firstOrNull { it.hasMarker }?.dir
|
||||
|
|
@ -110,8 +110,8 @@ private fun sanitizeFileName(s: String): String =
|
|||
s.replace(Regex("""[\\/:*?"<>|]"""), "_").trim().ifBlank { "save" }
|
||||
|
||||
/**
|
||||
* Schreibt eine ZIP im Format: ZIP enthält Ordner "TITLEID_UPPER/…" und darin
|
||||
* den reinen Inhalt des Ordners "0" (nicht den Ordner 0 selbst).
|
||||
* Writes a ZIP with the structure: the ZIP contains folder "TITLEID_UPPER/…" and inside it
|
||||
* the raw contents of folder "0" (not the folder "0" itself).
|
||||
*/
|
||||
fun exportSaveToZip(
|
||||
activity: Activity,
|
||||
|
|
@ -168,7 +168,7 @@ fun exportSaveToZip(
|
|||
}
|
||||
}
|
||||
|
||||
/** Hilfsname für CreateDocument: "<Name>_save_YYYY-MM-DD.zip" (aus Marker-Ordner) */
|
||||
/** Helper name for CreateDocument: "<Name>_save_YYYY-MM-DD.zip" (derived from marker folder) */
|
||||
fun buildSuggestedExportName(activity: Activity, titleId: String): String {
|
||||
val primary = pickSaveDirWithMarker(activity, titleId)
|
||||
val displayName = if (primary != null) {
|
||||
|
|
@ -214,7 +214,7 @@ private fun clearDirectory(dir: File) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Erwartet ZIP mit Struktur: TITLEID_UPPER/… – schreibt NUR in den Ordner mit TITLEID.txt.
|
||||
* Expects a ZIP with structure: TITLEID_UPPER/… — writes ONLY into the folder that has TITLEID.txt.
|
||||
*/
|
||||
fun importSaveFromZip(
|
||||
activity: Activity,
|
||||
|
|
@ -223,11 +223,11 @@ fun importSaveFromZip(
|
|||
): ImportResult {
|
||||
val cr: ContentResolver = activity.contentResolver
|
||||
|
||||
// TitelID-Ordner im ZIP finden
|
||||
// Find TitleID folder inside the ZIP
|
||||
var titleIdFromZip: String? = null
|
||||
var totalBytes = 0L
|
||||
|
||||
// Pass 1: Top-Level TitelID und Total ermitteln
|
||||
// Pass 1: determine top-level TitleID and total size
|
||||
runCatching {
|
||||
cr.openInputStream(zipUri)?.use { ins ->
|
||||
ZipInputStream(BufferedInputStream(ins)).use { zis ->
|
||||
|
|
@ -248,7 +248,7 @@ fun importSaveFromZip(
|
|||
if (titleIdFromZip == null) return ImportResult(false, "error importing save. missing TITLEID folder")
|
||||
val tidZip = titleIdFromZip!!.lowercase(Locale.ROOT)
|
||||
|
||||
// Größe nur unterhalb /<TITLEID>/… summieren
|
||||
// Sum size only for paths under /<TITLEID>/…
|
||||
runCatching {
|
||||
cr.openInputStream(zipUri)?.use { ins ->
|
||||
ZipInputStream(BufferedInputStream(ins)).use { zis ->
|
||||
|
|
@ -264,18 +264,18 @@ fun importSaveFromZip(
|
|||
}
|
||||
}
|
||||
|
||||
// Ziel: NUR der Marker-Ordner
|
||||
// Target: ONLY the marker folder
|
||||
val targetRoot = pickSaveDirWithMarker(activity, tidZip)
|
||||
?: pickSaveDirWithMarker(activity, tidZip.uppercase(Locale.ROOT))
|
||||
?: return ImportResult(false, "error importing save. start game once.")
|
||||
|
||||
// 0/1 vorbereiten (leeren)
|
||||
// Prepare 0/1 (clear)
|
||||
val zero = File(targetRoot, "0").apply { mkdirs() }
|
||||
val one = File(targetRoot, "1").apply { mkdirs() }
|
||||
clearDirectory(zero)
|
||||
clearDirectory(one)
|
||||
|
||||
// Pass 2: extrahieren
|
||||
// Pass 2: extract
|
||||
var written = 0L
|
||||
val buf = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
|
||||
|
|
@ -296,13 +296,13 @@ fun importSaveFromZip(
|
|||
out1.parentFile?.mkdirs()
|
||||
|
||||
if (!ensureInside(zero, out0) || !ensureInside(one, out1)) {
|
||||
// Zip-Slip Schutz
|
||||
// Zip-slip protection
|
||||
zis.closeEntry()
|
||||
ze = zis.nextEntry
|
||||
continue
|
||||
}
|
||||
|
||||
// Einmal lesen, zweimal schreiben
|
||||
// Read once, write twice
|
||||
val os0 = out0.outputStream()
|
||||
val os1 = out1.outputStream()
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ class HomeViews {
|
|||
var isFabVisible by remember { mutableStateOf(true) }
|
||||
val isNavigating = remember { mutableStateOf(false) }
|
||||
|
||||
// Save Manager State
|
||||
// Save Manager state
|
||||
val openSavesDialog = remember { mutableStateOf(false) }
|
||||
val saveImportBusy = remember { mutableStateOf(false) }
|
||||
val saveExportBusy = remember { mutableStateOf(false) }
|
||||
|
|
@ -143,7 +143,7 @@ class HomeViews {
|
|||
contract = ActivityResultContracts.OpenDocument()
|
||||
) { uri: Uri? ->
|
||||
val act = activity
|
||||
// Guard auf ausgewähltes Spiel – optional
|
||||
// Optional guard: ensure a selected game
|
||||
val tIdNow = viewModel.mainViewModel?.selected?.titleId.orEmpty()
|
||||
if (uri != null && act != null && tIdNow.isNotEmpty()) {
|
||||
saveImportBusy.value = true
|
||||
|
|
@ -195,7 +195,7 @@ class HomeViews {
|
|||
|
||||
val context = LocalContext.current
|
||||
|
||||
// NEW: Launcher für Amiibo (OpenDocument)
|
||||
// NEW: Launcher for Amiibo (OpenDocument)
|
||||
val pickAmiiboLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument()
|
||||
) { uri: Uri? ->
|
||||
|
|
@ -220,7 +220,7 @@ class HomeViews {
|
|||
}
|
||||
}
|
||||
|
||||
// NEW: Cheats Import (.txt)
|
||||
// NEW: Cheats import (.txt)
|
||||
val importCheatLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument()
|
||||
) { uri: Uri? ->
|
||||
|
|
@ -228,7 +228,7 @@ class HomeViews {
|
|||
val act = viewModel.activity
|
||||
val titleId = gm?.titleId ?: ""
|
||||
if (uri != null && act != null && titleId.isNotEmpty()) {
|
||||
// nur .txt akzeptieren
|
||||
// accept only .txt
|
||||
val okExt = runCatching {
|
||||
DocumentFile.fromSingleUri(act, uri)?.name?.lowercase()?.endsWith(".txt") == true
|
||||
}.getOrElse { false }
|
||||
|
|
@ -240,7 +240,7 @@ class HomeViews {
|
|||
val res = importCheatTxt(act, titleId, uri)
|
||||
if (res.isSuccess) {
|
||||
Toast.makeText(act, "Imported: ${res.getOrNull()?.name}", Toast.LENGTH_SHORT).show()
|
||||
// danach Liste aktualisieren
|
||||
// then refresh list
|
||||
cheatsForSelected.value = loadCheatsFromDisk(act, titleId)
|
||||
} else {
|
||||
Toast.makeText(act, "Import failed: ${res.exceptionOrNull()?.message}", Toast.LENGTH_LONG).show()
|
||||
|
|
@ -255,7 +255,7 @@ class HomeViews {
|
|||
val act = viewModel.activity
|
||||
val titleId = gm?.titleId ?: ""
|
||||
if (uri != null && act != null && titleId.isNotEmpty()) {
|
||||
// Persist permission (lesen)
|
||||
// Persist permission (read)
|
||||
try {
|
||||
act.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
} catch (_: Exception) {}
|
||||
|
|
@ -277,7 +277,7 @@ class HomeViews {
|
|||
"Copying… ${(prog.fraction * 100).toInt()}%"
|
||||
}
|
||||
|
||||
// Liste aktualisieren
|
||||
// refresh list
|
||||
modsForSelected.value = listMods(act, titleId)
|
||||
modsImportBusy.value = false
|
||||
|
||||
|
|
@ -438,7 +438,7 @@ class HomeViews {
|
|||
Icon(Icons.Filled.Settings, contentDescription = "Settings")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = query.value,
|
||||
|
|
@ -453,13 +453,13 @@ class HomeViews {
|
|||
singleLine = true,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
errorContainerColor = Color.Transparent,
|
||||
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||
unfocusedBorderColor = MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
errorContainerColor = Color.Transparent,
|
||||
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||
unfocusedBorderColor = MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -711,12 +711,12 @@ class HomeViews {
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
// LINKS: Import .txt
|
||||
// LEFT: Import .txt
|
||||
TextButton(onClick = {
|
||||
importCheatLauncher.launch(arrayOf("text/plain", "text/*", "*/*"))
|
||||
}) { Text("Import .txt") }
|
||||
|
||||
// RECHTS: Cancel + Save
|
||||
// RIGHT: Cancel + Save
|
||||
Row {
|
||||
TextButton(onClick = { openCheatsDialog.value = false }) { Text("Cancel") }
|
||||
TextButton(onClick = {
|
||||
|
|
@ -803,7 +803,7 @@ class HomeViews {
|
|||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
|
||||
// Import-Zeile
|
||||
// Import row
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -833,7 +833,7 @@ class HomeViews {
|
|||
)
|
||||
}
|
||||
|
||||
// Liste der Mods
|
||||
// List of mods
|
||||
if (modsForSelected.value.isEmpty()) {
|
||||
Text("No mods found for this title.")
|
||||
} else {
|
||||
|
|
@ -898,7 +898,7 @@ class HomeViews {
|
|||
)
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
// Import-Button
|
||||
// Import button
|
||||
androidx.compose.material3.Button(
|
||||
enabled = !saveImportBusy.value && !saveExportBusy.value &&
|
||||
(viewModel.mainViewModel?.selected?.titleId?.isNotEmpty() == true),
|
||||
|
|
@ -909,7 +909,7 @@ class HomeViews {
|
|||
}
|
||||
) { Text("Import ZIP") }
|
||||
|
||||
// Export-Button
|
||||
// Export button
|
||||
androidx.compose.material3.Button(
|
||||
enabled = !saveImportBusy.value && !saveExportBusy.value &&
|
||||
(viewModel.mainViewModel?.selected?.titleId?.isNotEmpty() == true),
|
||||
|
|
@ -953,7 +953,7 @@ class HomeViews {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Shortcut-Dialog
|
||||
// --- Shortcut dialog
|
||||
if (showShortcutDialog.value) {
|
||||
val gm = viewModel.mainViewModel?.selected
|
||||
AlertDialog(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue