diff --git a/README-es.md b/README-es.md index 57cd67e..7ff027a 100644 --- a/README-es.md +++ b/README-es.md @@ -49,7 +49,7 @@ Para actualizar, usa la misma cuenta y el mismo bundle ID efectivo de Sideloadly ## Juegos incluidos -En la app pulsa **Ver todos** para consultar los perfiles de esta copia. Busca por nombre, versión o Title ID; cada resultado enlaza a su ficha. El catálogo vuelve a leer los perfiles locales cada vez que lo abres. +El carrusel muestra los perfiles de esta copia; no proporciona ni descarga juegos. Debes añadir tus propios archivos del juego. Pulsa **Ver todos** debajo para consultar la lista completa de soporte. Busca por nombre, versión o Title ID; cada resultado enlaza a su ficha. El catálogo vuelve a leer los perfiles locales cada vez que lo abres. **Las pruebas propias cubren compilación y empaquetado en Mac, no una verificación automática de juego en iPhone.** Los perfiles y reportes de juego proceden de upstream; revisa [la ficha de tu juego](dm/games/README.md#games) antes de prepararlo. Un perfil no garantiza que cualquier versión funcione en cualquier iPhone. diff --git a/README.md b/README.md index 8789518..5578132 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ For updates, keep the same Apple account and effective bundle ID used in Sideloa ## Included game profiles -In the desktop app, click **View all** to browse every profile in your checkout. Search by name, version or Title ID; each result links to its game notes. The catalog reads the local profiles each time you open it. +The home carousel shows the game profiles in your checkout; it does not provide or download games. You must add your own game files. Click **View all** below it to browse the complete support list. Search by name, version or Title ID; each result links to its game notes. The catalog reads the local profiles each time you open it. **Our own checks cover macOS compilation and packaging, not automatic iPhone gameplay verification.** Profiles and gameplay reports come from upstream and may concern Mac or iPhone; read [your game's notes](dm/games/README.md#games). A profile does not guarantee that every game version works on every device. diff --git a/desktop/NxNativeEasy/Localization.swift b/desktop/NxNativeEasy/Localization.swift index d1f5d83..2b3cf74 100644 --- a/desktop/NxNativeEasy/Localization.swift +++ b/desktop/NxNativeEasy/Localization.swift @@ -12,6 +12,9 @@ enum EasyLanguage: String, CaseIterable { struct EasyStrings { static let english: [String:String] = [ + "Juegos con soporte en la herramienta": "Supported game profiles", + "No incluye ni descarga juegos. Debes añadir tus propios archivos.": "No games are included or downloaded. You must add your own files.", + "Carpeta de tu juego": "Your game folder", "Descargar Sideloadly": "Get Sideloadly", "Enviar a Sideloadly": "Send to Sideloadly", "Prepara tu juego.": "Prepare your game.", diff --git a/desktop/NxNativeEasy/main.swift b/desktop/NxNativeEasy/main.swift index c2b15ec..fe2845e 100644 --- a/desktop/NxNativeEasy/main.swift +++ b/desktop/NxNativeEasy/main.swift @@ -3,13 +3,6 @@ import AppKit import Foundation import Darwin -struct GameCard: Identifiable { - let id: String - let name: String - let symbol: String - let tint: Color -} - @MainActor final class BuildModel: ObservableObject { @Published var language: EasyLanguage = EasyLanguage.load() { didSet {language.save()} @@ -31,14 +24,10 @@ struct GameCard: Identifiable { private var process: Process? private var cancelled = false let repo: URL - let cards: [GameCard] = [ - .init(id:"dread",name:"Metroid Dread",symbol:"scope",tint:.orange), - .init(id:"wonder",name:"Super Mario Bros. Wonder",symbol:"sparkles",tint:.mint), - .init(id:"zelda",name:"Zelda: Breath of the Wild",symbol:"mountain.2",tint:.cyan) - ] init() { let configured = Bundle.main.object(forInfoDictionaryKey:"NxNativeSource") as? String ?? "" repo = URL(fileURLWithPath:configured) + refreshCatalog() if let path=UserDefaults.standard.string(forKey:"keysDirectory"), FileManager.default.fileExists(atPath:path+"/prod.keys") { keysFolder=URL(fileURLWithPath:path) } @@ -48,9 +37,12 @@ struct GameCard: Identifiable { } } var gameName: String { folder?.lastPathComponent ?? text("Ningún juego seleccionado") } - func openCatalog() { + private func refreshCatalog() { do {catalog=try GameCatalog.load(repo:repo);catalogFailed=false} catch {catalog=[];catalogFailed=true} + } + func openCatalog() { + refreshCatalog() showCatalog=true } func chooseFolder() { @@ -201,42 +193,43 @@ struct ContentView: View { Text(model.text("Creamos la IPA. La instalas en tu iPhone con Sideloadly.")) .font(.system(size:14)).foregroundStyle(.secondary) } - HStack(spacing:12) { - ForEach(model.cards) {card in - VStack(alignment:.leading,spacing:7) { - ZStack { - RoundedRectangle(cornerRadius:12).fill(card.tint.opacity(0.12)) - Image(systemName:card.symbol).font(.system(size:29,weight:.light)).foregroundStyle(card.tint) - }.frame(height:74) - Text(card.name).font(.system(size:10,weight:.medium)).foregroundStyle(ink).lineLimit(1) - }.frame(maxWidth:.infinity) + VStack(alignment:.leading,spacing:10) { + Text(model.text("Juegos con soporte en la herramienta")) + .font(.system(size:14,weight:.semibold)).foregroundStyle(ink) + Text(model.text("No incluye ni descarga juegos. Debes añadir tus propios archivos.")) + .font(.system(size:12)).foregroundStyle(.secondary) + ScrollView(.horizontal,showsIndicators:true) { + LazyHStack(spacing:12) { + ForEach(Array(model.catalog.enumerated()),id:\.element.id) { index, game in + let tint: Color = [.orange,.mint,.cyan,.purple,.pink][index % 5] + VStack(alignment:.leading,spacing:7) { + ZStack { + RoundedRectangle(cornerRadius:12).fill(tint.opacity(0.12)) + Image(systemName:"gamecontroller").font(.system(size:29,weight:.light)).foregroundStyle(tint) + }.frame(height:66) + Text(game.name).font(.system(size:11,weight:.medium)) + .foregroundStyle(ink).lineLimit(2).frame(height:30,alignment:.topLeading) + }.frame(width:165) + } + }.padding(.bottom,8) } Button {model.openCatalog()} label: { - VStack(alignment:.leading,spacing:7) { - ZStack { - RoundedRectangle(cornerRadius:12).fill(lime.opacity(0.12)) - Image(systemName:"square.grid.2x2").font(.system(size:28,weight:.light)).foregroundStyle(lime) - }.frame(height:74) - HStack { - Text(model.text("Ver todos")).font(.system(size:10,weight:.semibold)) - Spacer() - Image(systemName:"arrow.up.right").font(.system(size:10)) - }.foregroundStyle(ink) - }.frame(maxWidth:.infinity) - }.buttonStyle(.plain).accessibilityIdentifier("view-all-games") + Label(model.text("Ver todos"),systemImage:"magnifyingglass") + .font(.system(size:12,weight:.semibold)) + }.buttonStyle(.plain).foregroundStyle(lime).accessibilityIdentifier("view-all-games") } VStack(alignment:.leading,spacing:18) { HStack(spacing:12) { Image(systemName:"folder.fill").font(.system(size:24)).foregroundStyle(lime) VStack(alignment:.leading,spacing:4) { - Text(model.folder == nil ? model.text("Carpeta del juego") : model.gameName) + Text(model.folder == nil ? model.text("Carpeta de tu juego") : model.gameName) .font(.system(size:16,weight:.semibold)).foregroundStyle(ink) if model.folder == nil { Text(model.text("Con el juego base y sus actualizaciones.")).font(.system(size:12)).foregroundStyle(.secondary) } } Spacer() - Button(model.folder == nil ? model.text("Elegir") : model.text("Cambiar")) {model.chooseFolder()}.disabled(model.running).accessibilityLabel(model.text("Carpeta del juego")) + Button(model.folder == nil ? model.text("Elegir") : model.text("Cambiar")) {model.chooseFolder()}.disabled(model.running).accessibilityLabel(model.text("Carpeta de tu juego")) } HStack(spacing:12) { Image(systemName:"key.horizontal").font(.system(size:19)).foregroundStyle(lime).frame(width:24) diff --git a/docs/images/nxnative-easy.png b/docs/images/nxnative-easy.png index 530f743..8d51e4f 100644 Binary files a/docs/images/nxnative-easy.png and b/docs/images/nxnative-easy.png differ diff --git a/docs/personal-ios.md b/docs/personal-ios.md index 2c660f7..842e892 100644 --- a/docs/personal-ios.md +++ b/docs/personal-ios.md @@ -153,7 +153,7 @@ bytes, not Nintendo key material. ## Game catalog -The fourth home card opens **View all / Ver todos**. The native catalog reads every +The home carousel lists all local game profiles as a support reference, with an explicit reminder that users must supply their own game files. **View all / Ver todos** below the carousel opens the searchable catalog. The native catalog reads every 16-digit title directory containing `dm/games//game.tsv` from the current checkout when opened. It shows the profile name, registered build display versions and title ID, with a link to the corresponding repository game note. Search ignores case and accents