diff --git a/.upstream-ref b/.upstream-ref index fd4b88cfb..86e8667f4 100644 --- a/.upstream-ref +++ b/.upstream-ref @@ -1 +1 @@ -7249ee9f95ed728aff9a86395330d10d4c91fb5c +88c7e03f51e3934dc227089ea81ec64fcd525b03 diff --git a/GAMES.it.md b/GAMES.it.md index 86de57c56..4d33ebfd0 100644 --- a/GAMES.it.md +++ b/GAMES.it.md @@ -2,7 +2,7 @@ Follows [GAMES.md](GAMES.md) as of v1.0.2 # Giochi -Tutti i giochi per i quali questo fork dispone di un profilo, con il relativo pacchetto di aggiornamento NSP richiesto da ciascuno. La pagina principale per i giocatori è [README.it.md](README.it.md), mentre [dm/games/README.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/games/README.md) (in inglese) elenca gli stessi titoli con le note tecniche del motore per ciascuno di essi. Questa pagina raccoglie l'elenco completo e la galleria, così da mantenere il README principale snello. English: [GAMES.md](GAMES.md). 中文:[GAMES.zh-CN.md](GAMES.zh-CN.md). +Tutti i giochi per i quali questo fork dispone di un profilo, con il relativo pacchetto di aggiornamento NSP richiesto da ciascuno. La pagina principale per i giocatori è [README.it.md](README.it.md), mentre [dm/games/README.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/games/README.md) (in inglese) elenca gli stessi titoli con le note tecniche del motore per ciascuno di essi. Questa pagina raccoglie l'elenco completo e la galleria, così da mantenere il README principale snello. English: [GAMES.md](GAMES.md). 中文:[GAMES.zh-CN.md](GAMES.zh-CN.md). ## Galleria @@ -10,213 +10,213 @@ Screenshot accompagnati da una nota quando un gioco presenta indicazioni specifi - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + +
Donkey Kong Bananza
Donkey Kong Bananza
Switch 2, solo Mac, da una dump
Splatoon Raiders
Splatoon Raiders
Switch 2, solo Mac, da una dump
Fire Emblem Fortune's Weave
Fire Emblem Fortune's Weave
Switch 2, solo Mac, da una dump
Donkey Kong Bananza
Donkey Kong Bananza
Switch 2, solo Mac, da una dump
Splatoon Raiders
Splatoon Raiders
Switch 2, solo Mac, da una dump
Fire Emblem Fortune's Weave
Fire Emblem Fortune's Weave
Switch 2, solo Mac, da una dump
Mario Kart 8 Deluxe
Mario Kart 8 Deluxe
niente Mii
Mario vs. Donkey Kong
Mario vs. Donkey Kong
Paper Mario: The Thousand-Year Door
Paper Mario: The Thousand-Year Door
obiettivo 60 fps con 1.0.1
Mario Kart 8 Deluxe
Mario Kart 8 Deluxe
niente Mii
Mario vs. Donkey Kong
Mario vs. Donkey Kong
Paper Mario: The Thousand-Year Door
Paper Mario: The Thousand-Year Door
obiettivo 60 fps con 1.0.1
Super Mario 3D World + Bowser's Fury
Super Mario 3D World + Bowser's Fury
Super Mario Bros. Wonder
Super Mario Bros. Wonder
Super Mario Galaxy
Super Mario Galaxy
Super Mario 3D World + Bowser's Fury
Super Mario 3D World + Bowser's Fury
Super Mario Bros. Wonder
Super Mario Bros. Wonder
Super Mario Galaxy
Super Mario Galaxy
Super Mario Galaxy 2
Super Mario Galaxy 2
Super Mario Odyssey
Super Mario Odyssey
The Legend of Zelda: Breath of the Wild
The Legend of Zelda: Breath of the Wild
60 fps con 1.9.0
Super Mario Galaxy 2
Super Mario Galaxy 2
Super Mario Odyssey
Super Mario Odyssey
The Legend of Zelda: Breath of the Wild
The Legend of Zelda: Breath of the Wild
60 fps con 1.9.0
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Tears of the Kingdom
The Legend of Zelda: Tears of the Kingdom
60 fps con 1.4.3
13 Sentinels: Aegis Rim
13 Sentinels: Aegis Rim
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Tears of the Kingdom
The Legend of Zelda: Tears of the Kingdom
60 fps con 1.4.3
13 Sentinels: Aegis Rim
13 Sentinels: Aegis Rim
Absolum
Absolum
Ace Attorney Investigations Collection
Ace Attorney Investigations Collection
Advance Wars 1+2: Re-Boot Camp
Advance Wars 1+2: Re-Boot Camp
Absolum
Absolum
Ace Attorney Investigations Collection
Ace Attorney Investigations Collection
Advance Wars 1+2: Re-Boot Camp
Advance Wars 1+2: Re-Boot Camp
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES - nirvanA Initiative
AI: THE SOMNIUM FILES - nirvanA Initiative
Animal Crossing: New Horizons
Animal Crossing: New Horizons
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES - nirvanA Initiative
AI: THE SOMNIUM FILES - nirvanA Initiative
Animal Crossing: New Horizons
Animal Crossing: New Horizons
ANIMAL WELL
ANIMAL WELL
ASTRAL CHAIN
ASTRAL CHAIN
obiettivo 60 fps con 1.0.1
Balatro
Balatro
giocabile al tocco
ANIMAL WELL
ANIMAL WELL
ASTRAL CHAIN
ASTRAL CHAIN
obiettivo 60 fps con 1.0.1
Balatro
Balatro
giocabile al tocco
BALL x PIT
BALL x PIT
Bayonetta
Bayonetta
Bayonetta 2
Bayonetta 2
BALL x PIT
BALL x PIT
Bayonetta
Bayonetta
Bayonetta 2
Bayonetta 2
Bayonetta 3
Bayonetta 3
Big Brain Academy: Brain vs. Brain
Big Brain Academy: Brain vs. Brain
Brain Memory Training Academy
Brain Memory Training Academy
Bayonetta 3
Bayonetta 3
Big Brain Academy: Brain vs. Brain
Big Brain Academy: Brain vs. Brain
Brain Memory Training Academy
Brain Memory Training Academy
Captain Toad: Treasure Tracker
Captain Toad: Treasure Tracker
Catherine: Full Body
Catherine: Full Body
Celeste
Celeste
Captain Toad: Treasure Tracker
Captain Toad: Treasure Tracker
Catherine: Full Body
Catherine: Full Body
Celeste
Celeste
Cuphead
Cuphead
DARK SOULS: REMASTERED
DARK SOULS: REMASTERED
60 fps con 1.0.3
Digimon Story Cyber Sleuth: Complete Edition
Digimon Story Cyber Sleuth: Complete Edition
Cuphead
Cuphead
DARK SOULS: REMASTERED
DARK SOULS: REMASTERED
60 fps con 1.0.3
Digimon Story Cyber Sleuth: Complete Edition
Digimon Story Cyber Sleuth: Complete Edition
Donkey Kong Country: Tropical Freeze
Donkey Kong Country: Tropical Freeze
DRAGON QUEST VII Reimagined
DRAGON QUEST VII Reimagined
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
60 fps con 1.0.4
Donkey Kong Country: Tropical Freeze
Donkey Kong Country: Tropical Freeze
DRAGON QUEST VII Reimagined
DRAGON QUEST VII Reimagined
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
60 fps con 1.0.4
FINAL FANTASY RESONANCE
FINAL FANTASY RESONANCE
60 fps con 1.0.1
FINAL FANTASY TACTICS - The Ivalice Chronicles
FINAL FANTASY TACTICS - The Ivalice Chronicles
Fire Emblem Engage
Fire Emblem Engage
60 fps con 2.0.0
FINAL FANTASY RESONANCE
FINAL FANTASY RESONANCE
60 fps con 1.0.1
FINAL FANTASY TACTICS - The Ivalice Chronicles
FINAL FANTASY TACTICS - The Ivalice Chronicles
Fire Emblem Engage
Fire Emblem Engage
60 fps con 2.0.0
Fire Emblem: Three Houses
Fire Emblem: Three Houses
60 fps con 1.2.0
Ghost Trick: Phantom Detective
Ghost Trick: Phantom Detective
The Great Ace Attorney Chronicles
The Great Ace Attorney Chronicles
Fire Emblem: Three Houses
Fire Emblem: Three Houses
60 fps con 1.2.0
Ghost Trick: Phantom Detective
Ghost Trick: Phantom Detective
The Great Ace Attorney Chronicles
The Great Ace Attorney Chronicles
Hades
Hades
Hades II
Hades II
Hollow Knight: Silksong
Hollow Knight: Silksong
Hades
Hades
Hades II
Hades II
Hollow Knight: Silksong
Hollow Knight: Silksong
Hyrule Warriors: Age of Calamity
Hyrule Warriors: Age of Calamity
60 fps con 1.3.0
Kirby and the Forgotten Land
Kirby and the Forgotten Land
Luigi's Mansion 3
Luigi's Mansion 3
60 fps con 1.4.0
Hyrule Warriors: Age of Calamity
Hyrule Warriors: Age of Calamity
60 fps con 1.3.0
Kirby and the Forgotten Land
Kirby and the Forgotten Land
Luigi's Mansion 3
Luigi's Mansion 3
60 fps con 1.4.0
Metroid Dread
Metroid Dread
Monster Hunter Rise
Monster Hunter Rise
60 fps con 16.0.2
Monster Train 2
Monster Train 2
60 fps con 1.1.1
Metroid Dread
Metroid Dread
Monster Hunter Rise
Monster Hunter Rise
60 fps con 16.0.2
Monster Train 2
Monster Train 2
60 fps con 1.1.1
OCTOPATH TRAVELER
OCTOPATH TRAVELER
Octopath Traveler 0
Octopath Traveler 0
60 fps con 1.0.5
Octopath Traveler II
Octopath Traveler II
60 fps con 1.1.1
OCTOPATH TRAVELER
OCTOPATH TRAVELER
Octopath Traveler 0
Octopath Traveler 0
60 fps con 1.0.5
Octopath Traveler II
Octopath Traveler II
60 fps con 1.1.1
Patrick's Parabox
Patrick's Parabox
Persona 5 Royal
Persona 5 Royal
Pikmin 4
Pikmin 4
Patrick's Parabox
Patrick's Parabox
Persona 5 Royal
Persona 5 Royal
Pikmin 4
Pikmin 4
Pokémon Brilliant Diamond
Pokémon Brilliant Diamond
60 fps con 1.3.0
Pokémon Legends: Z-A
Pokémon Legends: Z-A
Pokémon Scarlet
Pokémon Scarlet
Pokémon Brilliant Diamond
Pokémon Brilliant Diamond
60 fps con 1.3.0
Pokémon Legends: Z-A
Pokémon Legends: Z-A
Pokémon Scarlet
Pokémon Scarlet
Pokémon Violet
Pokémon Violet
Pokémon Sword
Pokémon Sword
Pokémon Shield
Pokémon Shield
Pokémon Violet
Pokémon Violet
Pokémon Sword
Pokémon Sword
Pokémon Shield
Pokémon Shield
Portal
Portal
Raging Loop
Raging Loop
Rhythm Heaven Groove
Rhythm Heaven Groove
Portal
Portal
Raging Loop
Raging Loop
Rhythm Heaven Groove
Rhythm Heaven Groove
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
solo controller
Sea of Stars
Sea of Stars
SHIN MEGAMI TENSEI V: Vengeance
SHIN MEGAMI TENSEI V: Vengeance
60 fps con 1.0.3
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
solo controller
Sea of Stars
Sea of Stars
SHIN MEGAMI TENSEI V: Vengeance
SHIN MEGAMI TENSEI V: Vengeance
60 fps con 1.0.3
Stardew Valley
Stardew Valley
STEINS;GATE RE:BOOT
STEINS;GATE RE:BOOT
Super Smash Bros. Ultimate
Super Smash Bros. Ultimate
offline
Stardew Valley
Stardew Valley
STEINS;GATE RE:BOOT
STEINS;GATE RE:BOOT
Super Smash Bros. Ultimate
Super Smash Bros. Ultimate
offline
Taiko Risshiden V DX
Taiko Risshiden V DX
giapponese · giocabile al tocco
Unicorn Overlord
Unicorn Overlord
Yoshi's Crafted World
Yoshi's Crafted World
Taiko Risshiden V DX
Taiko Risshiden V DX
giapponese · giocabile al tocco
Unicorn Overlord
Unicorn Overlord
Yoshi's Crafted World
Yoshi's Crafted World
NieR:Automata The End of YoRHa Edition
NieR:Automata The End of YoRHa Edition
60 fps con 1.0.3
The Legend of Zelda: Echoes of Wisdom
The Legend of Zelda: Echoes of Wisdom
Donkey Kong Country Returns HD
Donkey Kong Country Returns HD
NieR:Automata The End of YoRHa Edition
NieR:Automata The End of YoRHa Edition
60 fps con 1.0.3
The Legend of Zelda: Echoes of Wisdom
The Legend of Zelda: Echoes of Wisdom
Donkey Kong Country Returns HD
Donkey Kong Country Returns HD
Mario + Rabbids Sparks of Hope
Mario + Rabbids Sparks of Hope
Xenoblade Chronicles 2
Xenoblade Chronicles 2
Xenoblade Chronicles 3
Xenoblade Chronicles 3
Mario + Rabbids Sparks of Hope
Mario + Rabbids Sparks of Hope
Xenoblade Chronicles 2
Xenoblade Chronicles 2
Xenoblade Chronicles 3
Xenoblade Chronicles 3
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
Return of the Obra Dinn
Return of the Obra Dinn
Blasphemous 2
Blasphemous 2
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
Return of the Obra Dinn
Return of the Obra Dinn
Blasphemous 2
Blasphemous 2
Silver Pines
Silver Pines
Blasphemous
Blasphemous
FINAL FANTASY X/X-2 HD Remaster
FINAL FANTASY X/X-2 HD Remaster
Silver Pines
Silver Pines
Blasphemous
Blasphemous
FINAL FANTASY X/X-2 HD Remaster
FINAL FANTASY X/X-2 HD Remaster
Mario + Rabbids Kingdom Battle
Mario + Rabbids Kingdom Battle
Persona 4 Golden
Persona 4 Golden
Xenoblade Chronicles: Definitive Edition
Xenoblade Chronicles: Definitive Edition
Mario + Rabbids Kingdom Battle
Mario + Rabbids Kingdom Battle
Persona 4 Golden
Persona 4 Golden
Xenoblade Chronicles: Definitive Edition
Xenoblade Chronicles: Definitive Edition
Pokémon Legends: Arceus
Pokémon Legends: Arceus
Super Mario Party Jamboree
Super Mario Party Jamboree
Cult of the Lamb
Cult of the Lamb
Pokémon Legends: Arceus
Pokémon Legends: Arceus
Super Mario Party Jamboree
Super Mario Party Jamboree
Cult of the Lamb
Cult of the Lamb
Paper Mario: The Origami King
Paper Mario: The Origami King
Batman: Arkham Knight
Batman: Arkham Knight
Bastion
Bastion
Paper Mario: The Origami King
Paper Mario: The Origami King
Batman: Arkham Knight
Batman: Arkham Knight
Bastion
Bastion
Baba Is You
Baba Is You
SteamWorld Heist II
SteamWorld Heist II
SteamWorld Heist: Ultimate Edition
SteamWorld Heist: Ultimate Edition
Baba Is You
Baba Is You
SteamWorld Heist II
SteamWorld Heist II
SteamWorld Heist: Ultimate Edition
SteamWorld Heist: Ultimate Edition
Subnautica: Below Zero
Subnautica: Below Zero
SteamWorld Dig 2
SteamWorld Dig 2
Streets of Rage 4
Streets of Rage 4
Subnautica: Below Zero
Subnautica: Below Zero
SteamWorld Dig 2
SteamWorld Dig 2
Streets of Rage 4
Streets of Rage 4
Mario Party Superstars
Mario Party Superstars
Subnautica
Subnautica
SteamWorld Dig
SteamWorld Dig
Mario Party Superstars
Mario Party Superstars
Subnautica
Subnautica
SteamWorld Dig
SteamWorld Dig
Team Sonic Racing
Team Sonic Racing
Well Dweller
Well Dweller
Need for Speed: Hot Pursuit Remastered
Need for Speed: Hot Pursuit Remastered
Team Sonic Racing
Team Sonic Racing
Well Dweller
Well Dweller
Need for Speed: Hot Pursuit Remastered
Need for Speed: Hot Pursuit Remastered
EA SPORTS FC 27
EA SPORTS FC 27
60 fps con 1.8d.310c
Portal 2
Portal 2
Super Monkey Ball Banana Rumble
Super Monkey Ball Banana Rumble
EA SPORTS FC 27
EA SPORTS FC 27
60 fps con 1.8d.310c
Portal 2
Portal 2
Super Monkey Ball Banana Rumble
Super Monkey Ball Banana Rumble
Atelier Ryza: Ever Darkness & the Secret Hideout DX
Atelier Ryza: Ever Darkness & the Secret Hideout DX
DRAGON QUEST III HD-2D Remake
DRAGON QUEST III HD-2D Remake
Hyrule Warriors: Definitive Edition
Hyrule Warriors: Definitive Edition
Atelier Ryza: Ever Darkness & the Secret Hideout DX
Atelier Ryza: Ever Darkness & the Secret Hideout DX
DRAGON QUEST III HD-2D Remake
DRAGON QUEST III HD-2D Remake
Hyrule Warriors: Definitive Edition
Hyrule Warriors: Definitive Edition
Metroid Prime Remastered
Metroid Prime Remastered
DRAGON QUEST I & II HD-2D Remake
DRAGON QUEST I & II HD-2D Remake
Dead Cells
Dead Cells
Metroid Prime Remastered
Metroid Prime Remastered
DRAGON QUEST I & II HD-2D Remake
DRAGON QUEST I & II HD-2D Remake
Dead Cells
Dead Cells
Hollow Knight
Hollow Knight
TRIANGLE STRATEGY
TRIANGLE STRATEGY
Castlevania: Belmont's Curse
Castlevania: Belmont's Curse
Hollow Knight
Hollow Knight
TRIANGLE STRATEGY
TRIANGLE STRATEGY
Castlevania: Belmont's Curse
Castlevania: Belmont's Curse
SUPER ROBOT WARS Y
SUPER ROBOT WARS Y
Super Mario RPG
Super Mario RPG
SUPER ROBOT WARS Y
SUPER ROBOT WARS Y
Super Mario RPG
Super Mario RPG
diff --git a/GAMES.md b/GAMES.md index b30c0bd6c..5f38cc7b8 100644 --- a/GAMES.md +++ b/GAMES.md @@ -1,6 +1,6 @@ # Games -Every game this fork has a profile for, with the update NSP each one needs. The player's page is [README.md](README.md), and [dm/games/README.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/games/README.md) lists the same games with the engine's own notes on each. This page is the full list, so the README stays short. 中文:[GAMES.zh-CN.md](GAMES.zh-CN.md). +Every game this fork has a profile for, with the update NSP each one needs. The player's page is [README.md](README.md), and [dm/games/README.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/games/README.md) lists the same games with the engine's own notes on each. This page is the full list, so the README stays short. 中文:[GAMES.zh-CN.md](GAMES.zh-CN.md). ## Gallery @@ -9,234 +9,239 @@ Screenshots, with a note where a game has one (a frame rate, a limit). The block - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + +
Donkey Kong Bananza
Donkey Kong Bananza
Switch 2, Mac only, from a dump
Splatoon Raiders
Splatoon Raiders
Switch 2, Mac only, from a dump
Fire Emblem Fortune's Weave
Fire Emblem Fortune's Weave
Switch 2, Mac only, from a dump, up to 120 fps
Donkey Kong Bananza
Donkey Kong Bananza
Switch 2, Mac only, from a dump
Splatoon Raiders
Splatoon Raiders
Switch 2, Mac only, from a dump
Fire Emblem Fortune's Weave
Fire Emblem Fortune's Weave
Switch 2, Mac only, from a dump, up to 120 fps
Pokémon Pokopia
Pokémon Pokopia
Switch 2, Mac only, from a dump
Hyrule Warriors: Age of Imprisonment
Hyrule Warriors: Age of Imprisonment
Switch 2, Mac only, from a NUP
Mario Kart World
Mario Kart World
Switch 2, Mac only, from a dump
Pokémon Pokopia
Pokémon Pokopia
Switch 2, Mac only, from a dump
Hyrule Warriors: Age of Imprisonment
Hyrule Warriors: Age of Imprisonment
Switch 2, Mac only, from a NUP
Mario Kart World
Mario Kart World
Switch 2, Mac only, from a dump
Mario Tennis Fever
Mario Tennis Fever
Switch 2, Mac only, from a dump
Yoshi and the Mysterious Book
Yoshi and the Mysterious Book
Switch 2, Mac only
Mario Kart 8 Deluxe
Mario Kart 8 Deluxe
no Mii heads
Mario Tennis Fever
Mario Tennis Fever
Switch 2, Mac only, from a dump
Yoshi and the Mysterious Book
Yoshi and the Mysterious Book
Switch 2, Mac only
The Legend of Zelda: Ocarina of Time
The Legend of Zelda: Ocarina of Time
Switch 2, Mac only, from a NUP
Mario vs. Donkey Kong
Mario vs. Donkey Kong
Paper Mario: The Thousand-Year Door
Paper Mario: The Thousand-Year Door
60 fps with 1.0.1
Super Mario 3D World + Bowser's Fury
Super Mario 3D World + Bowser's Fury
Mario Kart 8 Deluxe
Mario Kart 8 Deluxe
no Mii heads
Mario vs. Donkey Kong
Mario vs. Donkey Kong
Paper Mario: The Thousand-Year Door
Paper Mario: The Thousand-Year Door
60 fps with 1.0.1
Super Mario Bros. Wonder
Super Mario Bros. Wonder
Super Mario 3D All-Stars
Super Mario 3D All-Stars
Super Mario 64 and Sunshine; Galaxy is 32-bit ARM, which Apple silicon cannot run
Super Mario Galaxy
Super Mario Galaxy
Super Mario 3D World + Bowser's Fury
Super Mario 3D World + Bowser's Fury
Super Mario Bros. Wonder
Super Mario Bros. Wonder
Super Mario 3D All-Stars
Super Mario 3D All-Stars
Super Mario 64 and Sunshine; Galaxy is 32-bit ARM, which Apple silicon cannot run
Super Mario Galaxy 2
Super Mario Galaxy 2
Super Mario Odyssey
Super Mario Odyssey
The Legend of Zelda: Breath of the Wild
The Legend of Zelda: Breath of the Wild
60 fps with 1.9.0
Super Mario Galaxy
Super Mario Galaxy
Super Mario Galaxy 2
Super Mario Galaxy 2
Super Mario Odyssey
Super Mario Odyssey
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Tears of the Kingdom
The Legend of Zelda: Tears of the Kingdom
60 fps with 1.4.3
13 Sentinels: Aegis Rim
13 Sentinels: Aegis Rim
The Legend of Zelda: Breath of the Wild
The Legend of Zelda: Breath of the Wild
60 fps with 1.9.0
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Link's Awakening
The Legend of Zelda: Tears of the Kingdom
The Legend of Zelda: Tears of the Kingdom
60 fps with 1.4.3
Absolum
Absolum
Ace Attorney Investigations Collection
Ace Attorney Investigations Collection
Advance Wars 1+2: Re-Boot Camp
Advance Wars 1+2: Re-Boot Camp
13 Sentinels: Aegis Rim
13 Sentinels: Aegis Rim
Absolum
Absolum
Ace Attorney Investigations Collection
Ace Attorney Investigations Collection
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES - nirvanA Initiative
AI: THE SOMNIUM FILES - nirvanA Initiative
Animal Crossing: New Horizons
Animal Crossing: New Horizons
Advance Wars 1+2: Re-Boot Camp
Advance Wars 1+2: Re-Boot Camp
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES
AI: THE SOMNIUM FILES - nirvanA Initiative
AI: THE SOMNIUM FILES - nirvanA Initiative
ANIMAL WELL
ANIMAL WELL
ASTRAL CHAIN
ASTRAL CHAIN
60 fps target with 1.0.1
Balatro
Balatro
played by touch
Animal Crossing: New Horizons
Animal Crossing: New Horizons
ANIMAL WELL
ANIMAL WELL
ASTRAL CHAIN
ASTRAL CHAIN
60 fps target with 1.0.1
BALL x PIT
BALL x PIT
Bayonetta
Bayonetta
Bayonetta 2
Bayonetta 2
Balatro
Balatro
played by touch
BALL x PIT
BALL x PIT
Bayonetta
Bayonetta
Bayonetta 3
Bayonetta 3
Big Brain Academy: Brain vs. Brain
Big Brain Academy: Brain vs. Brain
Brain Memory Training Academy
Brain Memory Training Academy
Bayonetta 2
Bayonetta 2
Bayonetta 3
Bayonetta 3
Big Brain Academy: Brain vs. Brain
Big Brain Academy: Brain vs. Brain
Captain Toad: Treasure Tracker
Captain Toad: Treasure Tracker
Catherine: Full Body
Catherine: Full Body
Celeste
Celeste
Brain Memory Training Academy
Brain Memory Training Academy
Captain Toad: Treasure Tracker
Captain Toad: Treasure Tracker
Catherine: Full Body
Catherine: Full Body
Cuphead
Cuphead
DARK SOULS: REMASTERED
DARK SOULS: REMASTERED
60 fps with 1.0.3
Digimon Story Cyber Sleuth: Complete Edition
Digimon Story Cyber Sleuth: Complete Edition
Celeste
Celeste
Cuphead
Cuphead
DARK SOULS: REMASTERED
DARK SOULS: REMASTERED
60 fps with 1.0.3
Donkey Kong Country: Tropical Freeze
Donkey Kong Country: Tropical Freeze
DRAGON QUEST VII Reimagined
DRAGON QUEST VII Reimagined
60 fps with 1.2.0
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
60 fps with 1.0.4
Digimon Story Cyber Sleuth: Complete Edition
Digimon Story Cyber Sleuth: Complete Edition
Donkey Kong Country: Tropical Freeze
Donkey Kong Country: Tropical Freeze
DRAGON QUEST VII Reimagined
DRAGON QUEST VII Reimagined
60 fps with 1.2.0
FINAL FANTASY RESONANCE
FINAL FANTASY RESONANCE
60 fps with 1.0.1
FINAL FANTASY TACTICS - The Ivalice Chronicles
FINAL FANTASY TACTICS - The Ivalice Chronicles
Fire Emblem Engage
Fire Emblem Engage
60 fps with 2.0.0
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
60 fps with 1.0.4
FINAL FANTASY RESONANCE
FINAL FANTASY RESONANCE
60 fps with 1.0.1
FINAL FANTASY TACTICS - The Ivalice Chronicles
FINAL FANTASY TACTICS - The Ivalice Chronicles
Fire Emblem: Three Houses
Fire Emblem: Three Houses
60 fps with 1.2.0
Ghost Trick: Phantom Detective
Ghost Trick: Phantom Detective
The Great Ace Attorney Chronicles
The Great Ace Attorney Chronicles
Fire Emblem Engage
Fire Emblem Engage
60 fps with 2.0.0
Fire Emblem: Three Houses
Fire Emblem: Three Houses
60 fps with 1.2.0
Ghost Trick: Phantom Detective
Ghost Trick: Phantom Detective
Hades
Hades
Hades II
Hades II
Hollow Knight: Silksong
Hollow Knight: Silksong
The Great Ace Attorney Chronicles
The Great Ace Attorney Chronicles
Hades
Hades
Hades II
Hades II
Hyrule Warriors: Age of Calamity
Hyrule Warriors: Age of Calamity
60 fps with 1.3.0
Kirby and the Forgotten Land
Kirby and the Forgotten Land
Luigi's Mansion 3
Luigi's Mansion 3
60 fps with 1.4.0
Hollow Knight: Silksong
Hollow Knight: Silksong
Hyrule Warriors: Age of Calamity
Hyrule Warriors: Age of Calamity
60 fps with 1.3.0
Kirby and the Forgotten Land
Kirby and the Forgotten Land
Metroid Dread
Metroid Dread
Monster Hunter Rise
Monster Hunter Rise
60 fps with 16.0.2
Monster Train 2
Monster Train 2
60 fps with 1.1.1
Luigi's Mansion 3
Luigi's Mansion 3
60 fps with 1.4.0
Metroid Dread
Metroid Dread
Monster Hunter Rise
Monster Hunter Rise
60 fps with 16.0.2
OCTOPATH TRAVELER
OCTOPATH TRAVELER
Octopath Traveler 0
Octopath Traveler 0
60 fps with 1.0.5
Octopath Traveler II
Octopath Traveler II
60 fps with 1.1.1
Monster Train 2
Monster Train 2
60 fps with 1.1.1
OCTOPATH TRAVELER
OCTOPATH TRAVELER
Octopath Traveler 0
Octopath Traveler 0
60 fps with 1.0.5
Patrick's Parabox
Patrick's Parabox
Persona 5 Royal
Persona 5 Royal
Pikmin 4
Pikmin 4
Octopath Traveler II
Octopath Traveler II
60 fps with 1.1.1
Patrick's Parabox
Patrick's Parabox
Persona 5 Royal
Persona 5 Royal
Pokémon Brilliant Diamond
Pokémon Brilliant Diamond
60 fps with 1.3.0
Pokémon Legends: Z-A
Pokémon Legends: Z-A
Pokémon Scarlet
Pokémon Scarlet
Pikmin 4
Pikmin 4
Pokémon Brilliant Diamond
Pokémon Brilliant Diamond
60 fps with 1.3.0
Pokémon Legends: Z-A
Pokémon Legends: Z-A
Pokémon Violet
Pokémon Violet
Pokémon Sword
Pokémon Sword
Pokémon Shield
Pokémon Shield
Pokémon Scarlet
Pokémon Scarlet
Pokémon Violet
Pokémon Violet
Pokémon Sword
Pokémon Sword
Portal
Portal
Raging Loop
Raging Loop
Rhythm Heaven Groove
Rhythm Heaven Groove
Pokémon Shield
Pokémon Shield
Portal
Portal
Raging Loop
Raging Loop
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
controller only
Sea of Stars
Sea of Stars
SHIN MEGAMI TENSEI V: Vengeance
SHIN MEGAMI TENSEI V: Vengeance
60 fps with 1.0.3
Rhythm Heaven Groove
Rhythm Heaven Groove
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
ROMANCE OF THE THREE KINGDOMS 8 REMAKE
controller only
Sea of Stars
Sea of Stars
Stardew Valley
Stardew Valley
STEINS;GATE RE:BOOT
STEINS;GATE RE:BOOT
Super Smash Bros. Ultimate
Super Smash Bros. Ultimate
offline
SHIN MEGAMI TENSEI V: Vengeance
SHIN MEGAMI TENSEI V: Vengeance
60 fps with 1.0.3
Stardew Valley
Stardew Valley
STEINS;GATE RE:BOOT
STEINS;GATE RE:BOOT
Taiko Risshiden V DX
Taiko Risshiden V DX
Japanese · played by touch
Unicorn Overlord
Unicorn Overlord
Yoshi's Crafted World
Yoshi's Crafted World
Super Smash Bros. Ultimate
Super Smash Bros. Ultimate
offline
Taiko Risshiden V DX
Taiko Risshiden V DX
Japanese · played by touch
Unicorn Overlord
Unicorn Overlord
NieR:Automata The End of YoRHa Edition
NieR:Automata The End of YoRHa Edition
60 fps with 1.0.3
The Legend of Zelda: Echoes of Wisdom
The Legend of Zelda: Echoes of Wisdom
Donkey Kong Country Returns HD
Donkey Kong Country Returns HD
Yoshi's Crafted World
Yoshi's Crafted World
NieR:Automata The End of YoRHa Edition
NieR:Automata The End of YoRHa Edition
60 fps with 1.0.3
The Legend of Zelda: Echoes of Wisdom
The Legend of Zelda: Echoes of Wisdom
Mario + Rabbids Sparks of Hope
Mario + Rabbids Sparks of Hope
Xenoblade Chronicles 2
Xenoblade Chronicles 2
Xenoblade Chronicles 3
Xenoblade Chronicles 3
Donkey Kong Country Returns HD
Donkey Kong Country Returns HD
Mario + Rabbids Sparks of Hope
Mario + Rabbids Sparks of Hope
Xenoblade Chronicles 2
Xenoblade Chronicles 2
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
Return of the Obra Dinn
Return of the Obra Dinn
Blasphemous 2
Blasphemous 2
Xenoblade Chronicles 3
Xenoblade Chronicles 3
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
FINAL FANTASY CRYSTAL CHRONICLES Remastered Edition
Return of the Obra Dinn
Return of the Obra Dinn
Silver Pines
Silver Pines
Blasphemous
Blasphemous
FINAL FANTASY X/X-2 HD Remaster
FINAL FANTASY X/X-2 HD Remaster
Blasphemous 2
Blasphemous 2
Silver Pines
Silver Pines
Blasphemous
Blasphemous
Mario + Rabbids Kingdom Battle
Mario + Rabbids Kingdom Battle
Persona 4 Golden
Persona 4 Golden
Xenoblade Chronicles: Definitive Edition
Xenoblade Chronicles: Definitive Edition
FINAL FANTASY X/X-2 HD Remaster
FINAL FANTASY X/X-2 HD Remaster
Mario + Rabbids Kingdom Battle
Mario + Rabbids Kingdom Battle
Persona 4 Golden
Persona 4 Golden
Pokémon Legends: Arceus
Pokémon Legends: Arceus
Super Mario Party Jamboree
Super Mario Party Jamboree
Cult of the Lamb
Cult of the Lamb
Xenoblade Chronicles: Definitive Edition
Xenoblade Chronicles: Definitive Edition
Pokémon Legends: Arceus
Pokémon Legends: Arceus
Super Mario Party Jamboree
Super Mario Party Jamboree
Paper Mario: The Origami King
Paper Mario: The Origami King
Batman: Arkham Knight
Batman: Arkham Knight
Bastion
Bastion
Cult of the Lamb
Cult of the Lamb
Paper Mario: The Origami King
Paper Mario: The Origami King
Batman: Arkham Knight
Batman: Arkham Knight
Baba Is You
Baba Is You
SteamWorld Heist II
SteamWorld Heist II
SteamWorld Heist: Ultimate Edition
SteamWorld Heist: Ultimate Edition
Bastion
Bastion
Baba Is You
Baba Is You
SteamWorld Heist II
SteamWorld Heist II
Subnautica: Below Zero
Subnautica: Below Zero
SteamWorld Dig 2
SteamWorld Dig 2
Streets of Rage 4
Streets of Rage 4
SteamWorld Heist: Ultimate Edition
SteamWorld Heist: Ultimate Edition
Subnautica: Below Zero
Subnautica: Below Zero
SteamWorld Dig 2
SteamWorld Dig 2
Mario Party Superstars
Mario Party Superstars
Subnautica
Subnautica
SteamWorld Dig
SteamWorld Dig
Streets of Rage 4
Streets of Rage 4
Mario Party Superstars
Mario Party Superstars
Subnautica
Subnautica
Team Sonic Racing
Team Sonic Racing
Well Dweller
Well Dweller
Need for Speed: Hot Pursuit Remastered
Need for Speed: Hot Pursuit Remastered
60 fps with 1.0.3
SteamWorld Dig
SteamWorld Dig
Team Sonic Racing
Team Sonic Racing
Well Dweller
Well Dweller
EA SPORTS FC 27
EA SPORTS FC 27
60 fps with 1.8d.310c
Portal 2
Portal 2
Super Monkey Ball Banana Rumble
Super Monkey Ball Banana Rumble
Need for Speed: Hot Pursuit Remastered
Need for Speed: Hot Pursuit Remastered
60 fps with 1.0.3
EA SPORTS FC 27
EA SPORTS FC 27
60 fps with 1.8d.310c
Portal 2
Portal 2
Atelier Ryza: Ever Darkness & the Secret Hideout DX
Atelier Ryza: Ever Darkness & the Secret Hideout DX
DRAGON QUEST III HD-2D Remake
DRAGON QUEST III HD-2D Remake
60 fps with 1.1.2
Hyrule Warriors: Definitive Edition
Hyrule Warriors: Definitive Edition
Super Monkey Ball Banana Rumble
Super Monkey Ball Banana Rumble
Atelier Ryza: Ever Darkness & the Secret Hideout DX
Atelier Ryza: Ever Darkness & the Secret Hideout DX
DRAGON QUEST III HD-2D Remake
DRAGON QUEST III HD-2D Remake
60 fps with 1.1.2
Metroid Prime Remastered
Metroid Prime Remastered
DRAGON QUEST I & II HD-2D Remake
DRAGON QUEST I & II HD-2D Remake
Dead Cells
Dead Cells
Hyrule Warriors: Definitive Edition
Hyrule Warriors: Definitive Edition
Metroid Prime Remastered
Metroid Prime Remastered
DRAGON QUEST I & II HD-2D Remake
DRAGON QUEST I & II HD-2D Remake
Hollow Knight
Hollow Knight
TRIANGLE STRATEGY
TRIANGLE STRATEGY
60 fps with 1.1.1
Castlevania: Belmont's Curse
Castlevania: Belmont's Curse
Dead Cells
Dead Cells
Hollow Knight
Hollow Knight
TRIANGLE STRATEGY
TRIANGLE STRATEGY
60 fps with 1.1.1
SUPER ROBOT WARS Y
SUPER ROBOT WARS Y
Super Mario RPG
Super Mario RPG
The Legend of Zelda: Skyward Sword HD
The Legend of Zelda: Skyward Sword HD
Castlevania: Belmont's Curse
Castlevania: Belmont's Curse
SUPER ROBOT WARS Y
SUPER ROBOT WARS Y
Super Mario RPG
Super Mario RPG
Princess Peach: Showtime!
Princess Peach: Showtime!
60 fps with 1.0.0
Kirby Star Allies
Kirby Star Allies
DAVE THE DIVER
DAVE THE DIVER
The Legend of Zelda: Skyward Sword HD
The Legend of Zelda: Skyward Sword HD
Princess Peach: Showtime!
Princess Peach: Showtime!
60 fps with 1.0.0
Kirby Star Allies
Kirby Star Allies
Mario & Luigi: Brothership
Mario & Luigi: Brothership
up to 120 fps
DRAGON QUEST BUILDERS 2
DRAGON QUEST BUILDERS 2
Star Fox
Star Fox
Switch 2, Mac only, from a NUP
DAVE THE DIVER
DAVE THE DIVER
Mario & Luigi: Brothership
Mario & Luigi: Brothership
up to 120 fps
DRAGON QUEST BUILDERS 2
DRAGON QUEST BUILDERS 2
Star Fox
Star Fox
Switch 2, Mac only, from a NUP
Kirby Air Riders
Kirby Air Riders
Switch 2, Mac only, from a NUP
diff --git a/GAMES.zh-CN.md b/GAMES.zh-CN.md index 966a2931c..4f583f90e 100644 --- a/GAMES.zh-CN.md +++ b/GAMES.zh-CN.md @@ -1,6 +1,6 @@ # 游戏 -本 fork 有 profile 的全部游戏,以及每款需要的更新包。玩家主页是 [README.zh-CN.md](README.zh-CN.md)。[dm/games/README.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/games/README.md)(英文)是引擎对每款游戏的笔记。截图和 IPA 大小在英文页的[图库](GAMES.md#gallery)里。 +本 fork 有 profile 的全部游戏,以及每款需要的更新包。玩家主页是 [README.zh-CN.md](README.zh-CN.md)。[dm/games/README.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/games/README.md)(英文)是引擎对每款游戏的笔记。截图和 IPA 大小在英文页的[图库](GAMES.md#gallery)里。 ## Switch 2 游戏 diff --git a/README.de.md b/README.de.md index 8a5cc3c1a..924f6f27f 100644 --- a/README.de.md +++ b/README.de.md @@ -119,12 +119,12 @@ deine eigenen Spiele zwischenspeichert. Die **Startoptionen** (pro Spiel, bleiben bei Neubauten erhalten) legen die Bildrate, den Konsolenmodus (angedockt oder Handheld), die Render-Skalierung und das Upscaling, die Spielsprache, die Steuerung und mehr fest. Was jede Option bewirkt, steht in -[docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md). Ein Controller steuert den Launcher ebenso wie die Spiele. +[docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md). Ein Controller steuert den Launcher ebenso wie die Spiele. **Auf dem iPhone** zeigt die App dieselben Einstellungen, bevor das Spiel startet. Tippe auf **Spielen**. Die Touch-Steuerung gibt es in einem 2D- und einem 3D-Layout (tippe auf den Button oben in der Mitte), und ein verbundener Controller (Xbox, PlayStation, Switch Pro, MFi) ersetzt sie. -[docs/playing.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/playing.md) behandelt Touch, +[docs/playing.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/playing.md) behandelt Touch, Vibration und Bewegung. Kurze Ruckler, wenn eine Szene zum ersten Mal erscheint, sind normal: Die App kompiliert die Grafik dafür einmal und merkt sie sich. Die Daten des Spiels (`romfs.img`) können auf einer externen Festplatte liegen, die die Dateien-App auswählt. @@ -162,12 +162,12 @@ ein Profil, das noch nicht auf einem Handy ausprobiert wurde. Einige davon: | | Spiel | Wo es läuft | Status | |---|---|---|---| -| | The Legend of Zelda: Tears of the Kingdom | Mac und iPhone | **Hier verifiziert**: 304.243 Dateien, Byte für Byte geprüft, ca. 17 GB | -| | Hades II | Mac und iPhone | **Hier verifiziert** | -| | Animal Crossing: New Horizons | iPhone | **Hier verifiziert**: läuft flüssig | -| | Super Mario Odyssey | iPhone | Upstream als spielbar gemeldet | -| | The Legend of Zelda: Breath of the Wild | iPhone | Upstream als spielbar gemeldet, 60 fps mit 1.9.0 | -| | Mario Kart 8 Deluxe | iPhone | Upstream als spielbar gemeldet | +| | The Legend of Zelda: Tears of the Kingdom | Mac und iPhone | **Hier verifiziert**: 304.243 Dateien, Byte für Byte geprüft, ca. 17 GB | +| | Hades II | Mac und iPhone | **Hier verifiziert** | +| | Animal Crossing: New Horizons | iPhone | **Hier verifiziert**: läuft flüssig | +| | Super Mario Odyssey | iPhone | Upstream als spielbar gemeldet | +| | The Legend of Zelda: Breath of the Wild | iPhone | Upstream als spielbar gemeldet, 60 fps mit 1.9.0 | +| | Mario Kart 8 Deluxe | iPhone | Upstream als spielbar gemeldet | **[GAMES.md](GAMES.md)** enthält die Screenshot-Galerie mit der Installationsgröße jedes Spiels und die vollständige Tabelle mit der Update-Version, die jedes Spiel braucht: Die angezeigte Version der Update-NSP @@ -176,7 +176,7 @@ muss zu der des Profils passen, sonst sagt dir das der Build. ## Einschränkungen - **Nur die Spiele mit einem Profil.** Ein Titel außerhalb der Liste braucht eigene Bring-up-Arbeit. Der - Einstieg ist [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md), der Leitfaden von upstream für einen weiteren Switch-Titel. + Einstieg ist [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md), der Leitfaden von upstream für einen weiteren Switch-Titel. - **Große Spiele stoßen auf dem iPhone an ein Speicherlimit.** Ohne das Entitlement Increased Memory Limit gibt iOS der App etwa 3,4 GB, was für Tears of the Kingdom nicht reicht. Die App fordert es an, und AltStore Classic 2.2+, Impactor und Xcode mit einem Personal Team behalten es. SideStore mit einer @@ -199,7 +199,7 @@ muss zu der des Profils passen, sonst sagt dir das der Build. [patches/README.md](patches/README.md) erklärt das, und `python3 tools/engine.py status` listet auf, was mitgetragen wird. Ein täglicher Job verschiebt den Pin und öffnet einen Pull Request, oder ein Issue, das den Patch nennt, der nicht mehr passt. Die Links zu den Dokumenten der Engine in dieser README zeigen auf nxnative am festgelegten Commit. -- **Ein anderes Switch-Spiel:** [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md) (Extraktion, Binärübersicht, Bring-up). +- **Ein anderes Switch-Spiel:** [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md) (Extraktion, Binärübersicht, Bring-up). - **Versionsverlauf:** [CHANGELOG.md](CHANGELOG.md). ## Danksagung @@ -227,7 +227,7 @@ Die gesamte Arbeit an der Engine stammt von anderen. Dieses Repository ergänzt - **[SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross)**: SPIR-V zu Metal Shading Language. - **yuzu**, **deko3d**, **libvpx**, **stb_vorbis**, **libdeflate**: in der Danksagungsliste von upstream steht, was jedes beiträgt. -`dm/third_party` enthält andere Projekte mit ihren Lizenzen ([Liste](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/third_party/README.md)). +`dm/third_party` enthält andere Projekte mit ihren Lizenzen ([Liste](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/third_party/README.md)). ## Rechtliches diff --git a/README.fr.md b/README.fr.md index 768314c5e..59300bf0b 100644 --- a/README.fr.md +++ b/README.fr.md @@ -116,12 +116,12 @@ met en cache pour vos propres jeux. Les **Options de lancement** (par jeu, conservées d’une compilation à l’autre) règlent la fréquence d’images, le mode de la console (TV ou portable), l’échelle de rendu et la mise à l’échelle, la langue du jeu, les commandes et plus encore. Ce que fait chaque option est décrit dans -[docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md). Une manette pilote le lanceur aussi bien que les jeux. +[docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md). Une manette pilote le lanceur aussi bien que les jeux. **Sur l’iPhone**, l’app montre les mêmes réglages avant le démarrage du jeu. Touchez **Jouer**. Les commandes tactiles existent en disposition 2D et 3D (touchez le bouton en haut au centre), et une manette connectée (Xbox, PlayStation, Switch Pro, MFi) les remplace. -[docs/playing.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/playing.md) traite du tactile, des vibrations et du mouvement. De courtes saccades la première fois +[docs/playing.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/playing.md) traite du tactile, des vibrations et du mouvement. De courtes saccades la première fois qu’une scène apparaît sont normales : l’app compile les graphismes pour elle une fois et s’en souvient. Les données du jeu (`romfs.img`) peuvent se trouver sur un disque externe choisi dans l’app Fichiers. @@ -158,12 +158,12 @@ profil qui n’a pas encore été essayé sur un téléphone. En voici quelques- | | Jeu | Où il tourne | Statut | |---|---|---|---| -| | The Legend of Zelda: Tears of the Kingdom | Mac et iPhone | **Vérifié ici** : 304 243 fichiers, vérifiés octet par octet, environ 17 Go | -| | Hades II | Mac et iPhone | **Vérifié ici** | -| | Animal Crossing: New Horizons | iPhone | **Vérifié ici** : tourne de façon fluide | -| | Super Mario Odyssey | iPhone | Signalé jouable en amont | -| | The Legend of Zelda: Breath of the Wild | iPhone | Signalé jouable en amont, 60 fps avec 1.9.0 | -| | Mario Kart 8 Deluxe | iPhone | Signalé jouable en amont | +| | The Legend of Zelda: Tears of the Kingdom | Mac et iPhone | **Vérifié ici** : 304 243 fichiers, vérifiés octet par octet, environ 17 Go | +| | Hades II | Mac et iPhone | **Vérifié ici** | +| | Animal Crossing: New Horizons | iPhone | **Vérifié ici** : tourne de façon fluide | +| | Super Mario Odyssey | iPhone | Signalé jouable en amont | +| | The Legend of Zelda: Breath of the Wild | iPhone | Signalé jouable en amont, 60 fps avec 1.9.0 | +| | Mario Kart 8 Deluxe | iPhone | Signalé jouable en amont | **[GAMES.md](GAMES.md)** contient la galerie de captures avec la taille d’installation de chaque jeu, et le tableau complet avec la version de mise à jour dont chaque jeu a besoin : la version affichée du NSP de mise à @@ -172,7 +172,7 @@ jour doit correspondre à celle du profil, sinon la compilation le signale. ## Limites - **Seulement les jeux qui ont un profil.** Un titre hors de la liste demande son propre travail de mise en route. - Le point de départ est [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md), le guide du projet en amont pour un autre titre Switch. + Le point de départ est [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md), le guide du projet en amont pour un autre titre Switch. - **Les gros jeux atteignent une limite de mémoire sur iPhone.** Sans l’entitlement Increased Memory Limit, iOS donne à l’app environ 3,4 Go, ce qui ne suffit pas pour Tears of the Kingdom. L’app le demande, et AltStore Classic 2.2+, Impactor et Xcode avec une Personal Team le conservent. SideStore avec un identifiant @@ -196,7 +196,7 @@ jour doit correspondre à celle du profil, sinon la compilation le signale. l’explique, et `python3 tools/engine.py status` liste ce qui est porté. Une tâche quotidienne déplace l’épingle et ouvre une pull request, ou un ticket qui nomme le correctif qui ne s’applique plus. Les liens vers les documents du moteur dans ce README pointent vers nxnative au commit épinglé. -- **Un autre jeu Switch :** [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md) (extraction, analyse du binaire, mise en route). +- **Un autre jeu Switch :** [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md) (extraction, analyse du binaire, mise en route). - **Historique des versions :** [CHANGELOG.md](CHANGELOG.md). ## Remerciements @@ -224,7 +224,7 @@ Tout le travail sur le moteur est celui d’autres personnes. Ce dépôt ajoute - **[SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross)** : de SPIR-V vers Metal Shading Language. - **yuzu**, **deko3d**, **libvpx**, **stb_vorbis**, **libdeflate** : la liste de remerciements du projet en amont dit ce que chacun apporte. -`dm/third_party` contient d’autres projets avec leurs licences ([liste](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/third_party/README.md)). +`dm/third_party` contient d’autres projets avec leurs licences ([liste](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/third_party/README.md)). ## Mentions légales diff --git a/README.it.md b/README.it.md index 314f3a618..f7114ae03 100644 --- a/README.it.md +++ b/README.it.md @@ -117,11 +117,11 @@ per i tuoi giochi. **Sul Mac**, un gioco completato compare nella libreria con la sua copertina. **Play** lo avvia. Le **Opzioni di avvio** (specifiche per ogni gioco e mantenute anche dopo nuove compilazioni) regolano il frame rate, la modalità console (dock o portatile), la scala di rendering e l'upscaling, la lingua del gioco, i controlli e altro ancora. La spiegazione dettagliata di ogni opzione si trova in -[docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md). Un controller può comandare sia il launcher sia i giochi. +[docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md). Un controller può comandare sia il launcher sia i giochi. **Sull'iPhone**, l'app mostra le stesse impostazioni prima dell'avvio del gioco: tocca **Play**. I controlli touch sono disponibili in un layout 2D e uno 3D (tocca il pulsante in alto al centro per cambiare) e vengono sostituiti automaticamente quando colleghi un controller -(Xbox, PlayStation, Switch Pro, MFi). [docs/playing.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/playing.md) illustra i controlli touch, +(Xbox, PlayStation, Switch Pro, MFi). [docs/playing.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/playing.md) illustra i controlli touch, la vibrazione e i sensori di movimento. Brevi incertezze la prima volta che compare una nuova scena sono normali: l'app compila la grafica per quella scena una sola volta e poi la memorizza. I dati del gioco (`romfs.img`) possono risiedere anche su un'unità esterna selezionata tramite l'app File. @@ -152,12 +152,12 @@ parte ha un profilo che non è ancora stato provato su un telefono. Eccone alcun | | Gioco | Dove gira | Stato | |---|---|---|---| -| | The Legend of Zelda: Tears of the Kingdom | Mac e iPhone | **Verificato qui**: 304.243 file controllati byte per byte, circa 17 GB | -| | Hades II | Mac e iPhone | **Verificato qui** | -| | Animal Crossing: New Horizons | iPhone | **Verificato qui**: gira in modo fluido | -| | Super Mario Odyssey | iPhone | Segnalato come giocabile upstream | -| | The Legend of Zelda: Breath of the Wild | iPhone | Segnalato come giocabile upstream, 60 fps con la versione 1.9.0 | -| | Mario Kart 8 Deluxe | iPhone | Segnalato come giocabile upstream | +| | The Legend of Zelda: Tears of the Kingdom | Mac e iPhone | **Verificato qui**: 304.243 file controllati byte per byte, circa 17 GB | +| | Hades II | Mac e iPhone | **Verificato qui** | +| | Animal Crossing: New Horizons | iPhone | **Verificato qui**: gira in modo fluido | +| | Super Mario Odyssey | iPhone | Segnalato come giocabile upstream | +| | The Legend of Zelda: Breath of the Wild | iPhone | Segnalato come giocabile upstream, 60 fps con la versione 1.9.0 | +| | Mario Kart 8 Deluxe | iPhone | Segnalato come giocabile upstream | **[GAMES.it.md](GAMES.it.md)** contiene la galleria di screenshot con le dimensioni di installazione di ciascun gioco e la tabella completa con la versione dell'aggiornamento richiesta da ogni titolo: la versione visualizzata dell'NSP di aggiornamento @@ -166,7 +166,7 @@ deve corrispondere a quella prevista dal profilo, altrimenti il processo di comp ## Limitazioni - **Solo i giochi dotati di un profilo.** Un titolo non presente nell'elenco richiede un lavoro di adattamento dedicato, e il - punto di partenza è [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md), la guida upstream per aggiungere il supporto a un altro titolo Switch. + punto di partenza è [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md), la guida upstream per aggiungere il supporto a un altro titolo Switch. - **I giochi di grandi dimensioni su iPhone incontrano un limite di memoria.** Senza l'entitlement Increased Memory Limit, iOS mette a disposizione dell'app circa 3,4 GB, che non bastano per Tears of the Kingdom. L'app lo richiede automaticamente e AltStore Classic 2.2+, Impactor e Xcode con un Personal Team lo mantengono. SideStore con un ID Apple gratuito @@ -189,7 +189,7 @@ deve corrispondere a quella prevista dal profilo, altrimenti il processo di comp [patches/README.md](patches/README.md) ne spiega il funzionamento e `python3 tools/engine.py status` elenca le patch attualmente applicate. Un'operazione automatica giornaliera aggiorna il commit di riferimento e apre una pull request, oppure apre una issue indicando la patch che non si applica più correttamente. I collegamenti ai documenti del motore presenti in questo README puntano a nxnative al commit fissato. -- **Aggiungere un altro gioco Switch:** [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md) (estrazione, analisi dei binari, bring-up). +- **Aggiungere un altro gioco Switch:** [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md) (estrazione, analisi dei binari, bring-up). - **Cronologia delle versioni:** [CHANGELOG.it.md](CHANGELOG.it.md). ## Riconoscimenti @@ -221,7 +221,7 @@ Tutto il lavoro sul motore sottostante è opera di altri sviluppatori. Questo re - **yuzu**, **deko3d**, **libvpx**, **stb_vorbis**, **libdeflate** — consulta l'elenco dei ringraziamenti upstream per scoprire il contributo di ciascun progetto. -La cartella `dm/third_party` include altri progetti con le rispettive licenze ([elenco](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/third_party/README.md)). +La cartella `dm/third_party` include altri progetti con le rispettive licenze ([elenco](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/third_party/README.md)). ## Note legali diff --git a/README.md b/README.md index fe71b40d3..9c7d8335a 100644 --- a/README.md +++ b/README.md @@ -123,11 +123,11 @@ for your own games. **On the Mac**, a finished game appears in the library with its key art. **Play** starts it. **Launch options** (per game, kept across rebuilds) set the frame rate, console mode (docked or handheld), render scale and upscaling, game language, controls and more; what each does is in -[docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md). A controller drives the launcher as well as the games. +[docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md). A controller drives the launcher as well as the games. **On the iPhone**, the app shows the same settings before the game starts; tap **Play**. Touch controls come in a 2D and a 3D layout (tap the button at the top centre), and a connected controller -(Xbox, PlayStation, Switch Pro, MFi) replaces them. [docs/playing.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/playing.md) covers touch, +(Xbox, PlayStation, Switch Pro, MFi) replaces them. [docs/playing.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/playing.md) covers touch, rumble and motion. Short stutters the first time a scene appears are normal: the app compiles graphics for it once and remembers them. The game's data (`romfs.img`) can live on an external drive that the Files app picks. @@ -165,12 +165,12 @@ profile that has not been tried on a phone yet. A few of them: | | Game | Where it runs | Status | |---|---|---|---| -| | The Legend of Zelda: Tears of the Kingdom | Mac and iPhone | **Verified here**: 304,243 files, byte-checked; ~17 GB | -| | Hades II | Mac and iPhone | **Verified here** | -| | Animal Crossing: New Horizons | iPhone | **Verified here**: runs smoothly | -| | Super Mario Odyssey | iPhone | Reported playable upstream | -| | The Legend of Zelda: Breath of the Wild | iPhone | Reported playable upstream; 60 fps with 1.9.0 | -| | Mario Kart 8 Deluxe | iPhone | Reported playable upstream | +| | The Legend of Zelda: Tears of the Kingdom | Mac and iPhone | **Verified here**: 304,243 files, byte-checked; ~17 GB | +| | Hades II | Mac and iPhone | **Verified here** | +| | Animal Crossing: New Horizons | iPhone | **Verified here**: runs smoothly | +| | Super Mario Odyssey | iPhone | Reported playable upstream | +| | The Legend of Zelda: Breath of the Wild | iPhone | Reported playable upstream; 60 fps with 1.9.0 | +| | Mario Kart 8 Deluxe | iPhone | Reported playable upstream | **[GAMES.md](GAMES.md)** has the screenshot gallery with each game's install size, and the full table with the update version each game needs: the update NSP's @@ -187,7 +187,7 @@ the iPad. The other Switch 2 games were not tried here. [GAMES.md](GAMES.md#swit ## Limitations - **Only the games with a profile.** A title outside the list needs bring-up work of its own; the - place to start is [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md), upstream's guide for another Switch title. + place to start is [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md), upstream's guide for another Switch title. - **Big games on an iPhone hit a memory limit.** Without the Increased Memory Limit entitlement iOS gives the app about 3.4 GB, which is not enough for Tears of the Kingdom. The app requests it, and AltStore Classic 2.2+, Impactor and Xcode with a Personal Team keep it; SideStore with a free Apple @@ -211,7 +211,7 @@ the iPad. The other Switch 2 games were not tried here. [GAMES.md](GAMES.md#swit [patches/README.md](patches/README.md) explains it, and `python3 tools/engine.py status` lists what is carried. A daily job moves the pin and opens a pull request, or an issue naming the patch that no longer fits. The links to the engine's documents in this README point at nxnative at the pinned commit. -- **Another Switch game:** [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md) (extraction, binary survey, bring-up). +- **Another Switch game:** [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md) (extraction, binary survey, bring-up). - **Release history:** [CHANGELOG.md](CHANGELOG.md). ## Credits @@ -243,7 +243,7 @@ All of the engine work is other people's. This repository adds the launcher and - **yuzu**, **deko3d**, **libvpx**, **stb_vorbis**, **libdeflate** — see upstream's acknowledgement list for what each contributes. -`dm/third_party` ships other projects with their licenses ([list](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/third_party/README.md)). +`dm/third_party` ships other projects with their licenses ([list](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/third_party/README.md)). ## Legal diff --git a/README.sv.md b/README.sv.md index f218491f2..b7df8150d 100644 --- a/README.sv.md +++ b/README.sv.md @@ -115,12 +115,12 @@ dina egna spel. **På Mac** visas ett färdigt spel i biblioteket med sin key art. **Spela** startar det. **Startalternativ** (per spel, behålls vid ombyggen) styr bildfrekvens, konsolläge (dockad eller handhållen), renderskala och uppskalning, spelspråk, kontroller med mera. Vad varje alternativ gör står i -[docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md). En handkontroll styr launchern lika väl som spelen. +[docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md). En handkontroll styr launchern lika väl som spelen. **På iPhone** visar appen samma inställningar innan spelet startar. Tryck på **Spela**. Pekkontrollerna finns i en 2D- och en 3D-layout (tryck på knappen överst i mitten), och en ansluten handkontroll (Xbox, PlayStation, Switch Pro, MFi) ersätter dem. -[docs/playing.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/playing.md) tar upp touch, vibration och rörelse. Korta hack första gången en scen visas är +[docs/playing.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/playing.md) tar upp touch, vibration och rörelse. Korta hack första gången en scen visas är normala: Appen kompilerar grafiken för den en gång och kommer ihåg den. Spelets data (`romfs.img`) kan ligga på en extern enhet som appen Filer väljer. @@ -156,12 +156,12 @@ profil som ännu inte har prövats på en telefon. Några av dem: | | Spel | Var det körs | Status | |---|---|---|---| -| | The Legend of Zelda: Tears of the Kingdom | Mac och iPhone | **Verifierat här**: 304 243 filer, bytekontrollerade, ca 17 GB | -| | Hades II | Mac och iPhone | **Verifierat här** | -| | Animal Crossing: New Horizons | iPhone | **Verifierat här**: går smidigt | -| | Super Mario Odyssey | iPhone | Rapporterat som spelbart uppströms | -| | The Legend of Zelda: Breath of the Wild | iPhone | Rapporterat som spelbart uppströms, 60 fps med 1.9.0 | -| | Mario Kart 8 Deluxe | iPhone | Rapporterat som spelbart uppströms | +| | The Legend of Zelda: Tears of the Kingdom | Mac och iPhone | **Verifierat här**: 304 243 filer, bytekontrollerade, ca 17 GB | +| | Hades II | Mac och iPhone | **Verifierat här** | +| | Animal Crossing: New Horizons | iPhone | **Verifierat här**: går smidigt | +| | Super Mario Odyssey | iPhone | Rapporterat som spelbart uppströms | +| | The Legend of Zelda: Breath of the Wild | iPhone | Rapporterat som spelbart uppströms, 60 fps med 1.9.0 | +| | Mario Kart 8 Deluxe | iPhone | Rapporterat som spelbart uppströms | **[GAMES.md](GAMES.md)** har skärmbildsgalleriet med varje spels installationsstorlek och den fullständiga tabellen med den uppdateringsversion som varje spel behöver: Uppdaterings-NSP:ns visade version måste @@ -170,7 +170,7 @@ stämma med profilens, annars säger bygget ifrån. ## Begränsningar - **Bara spelen med en profil.** En titel utanför listan kräver eget bring-up-arbete. Börja med - [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md), uppströmsprojektets guide för en annan Switch-titel. + [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md), uppströmsprojektets guide för en annan Switch-titel. - **Stora spel slår i en minnesgräns på iPhone.** Utan behörigheten Increased Memory Limit ger iOS appen cirka 3,4 GB, vilket inte räcker för Tears of the Kingdom. Appen begär den, och AltStore Classic 2.2+, Impactor och Xcode med ett Personal Team behåller den. SideStore med ett gratis Apple-ID behöver en @@ -193,7 +193,7 @@ stämma med profilens, annars säger bygget ifrån. [patches/README.md](patches/README.md) förklarar det, och `python3 tools/engine.py status` listar vad som bärs. Ett dagligt jobb flyttar fästpunkten och öppnar en pull request, eller ett ärende som namnger patchen som inte längre passar. Länkarna till motorns dokument i den här README:n pekar på nxnative vid den fastlåsta committen. -- **Ett annat Switch-spel:** [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md) (extrahering, binärgenomgång, bring-up). +- **Ett annat Switch-spel:** [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md) (extrahering, binärgenomgång, bring-up). - **Versionshistorik:** [CHANGELOG.md](CHANGELOG.md). ## Tack @@ -221,7 +221,7 @@ Allt motorarbete är andras. Det här förrådet lägger launchern och patchkön - **[SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross)**: SPIR-V till Metal Shading Language. - **yuzu**, **deko3d**, **libvpx**, **stb_vorbis**, **libdeflate**: uppströmsprojektets tackslista säger vad var och en bidrar med. -`dm/third_party` innehåller andra projekt med deras licenser ([lista](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/third_party/README.md)). +`dm/third_party` innehåller andra projekt med deras licenser ([lista](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/third_party/README.md)). ## Juridik diff --git a/README.zh-CN.md b/README.zh-CN.md index f2b9eb986..f6bdd4a2d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -104,11 +104,11 @@ App 需要 iOS 或 iPadOS 26 或更新。封面是 Mac 启动器为你自己的 **在 Mac 上**,构建好的游戏带着封面出现在游戏库里,点 **Play** 启动。**启动选项**(按游戏保存,重新 构建后仍然保留)可设置帧率、主机模式(底座或掌机)、渲染缩放和超分辨率、游戏语言、操控等;每项的 -作用见 [docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md)(英文)。手柄既能操作游戏,也能操作启动器。 +作用见 [docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md)(英文)。手柄既能操作游戏,也能操作启动器。 **在 iPhone 上**,App 在游戏开始前显示同样的设置,点 **Play** 即可。触屏操控有 2D 和 3D 两种布局 (点顶部中央的按钮切换),连接手柄(Xbox、PlayStation、Switch Pro、MFi)后触屏控件会隐藏。 -触屏、震动和体感见 [docs/playing.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/playing.md)(英文)。新场景第一次出现时短促卡顿是正常的: +触屏、震动和体感见 [docs/playing.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/playing.md)(英文)。新场景第一次出现时短促卡顿是正常的: App 会为它编译一次图形并记住。游戏数据(`romfs.img`)可以放在外置硬盘上,用「文件」App 选取。 **游戏语言。** 默认英文。Mac 上用启动选项里的「游戏语言」一行。面板里没有的设置,可以写一个 @@ -138,12 +138,12 @@ iPhone 上在「文件」App 里该 App 名字下:`save/` 和 `log.txt`(上 | | 游戏 | 运行于 | 状态 | |---|---|---|---| -| | 塞尔达传说:王国之泪 | Mac 和 iPhone | **本 fork 验证**:304,243 个文件,逐字节校验;约 17 GB | -| | Hades II | Mac 和 iPhone | **本 fork 验证** | -| | 集合啦!动物森友会 | iPhone | **本 fork 验证**:流畅运行 | -| | 超级马力欧 奥德赛 | iPhone | 上游报告可玩 | -| | 塞尔达传说:旷野之息 | iPhone | 上游报告可玩;1.9.0 可 60 fps | -| | 马力欧卡丁车 8 豪华版 | iPhone | 上游报告可玩 | +| | 塞尔达传说:王国之泪 | Mac 和 iPhone | **本 fork 验证**:304,243 个文件,逐字节校验;约 17 GB | +| | Hades II | Mac 和 iPhone | **本 fork 验证** | +| | 集合啦!动物森友会 | iPhone | **本 fork 验证**:流畅运行 | +| | 超级马力欧 奥德赛 | iPhone | 上游报告可玩 | +| | 塞尔达传说:旷野之息 | iPhone | 上游报告可玩;1.9.0 可 60 fps | +| | 马力欧卡丁车 8 豪华版 | iPhone | 上游报告可玩 | **[GAMES.zh-CN.md](GAMES.zh-CN.md)**(带截图图库和每款的安装大小) 有完整表格和每款游戏需要的更新包版本:更新包 NSP 的显示版本必须与 profile 一致,否则构建会提示。 @@ -158,7 +158,7 @@ iPad 上基本没有问题。其他 Switch 2 游戏在本 fork 上没有试过 ## 限制 - **只支持有 profile 的游戏。** 列表之外的游戏需要单独的 bring-up 工作;起点是 - [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md)(上游为其他 Switch 游戏写的指南,英文)。 + [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md)(上游为其他 Switch 游戏写的指南,英文)。 - **大体积游戏在 iPhone 上会碰到内存上限。** 没有 Increased Memory Limit entitlement 时 iOS 只给 App 约 3.4 GB,不够《王国之泪》用。App 请求了它,AltStore Classic 2.2+、Impactor 和 Xcode + Personal Team 会保留;SideStore 配合免费 Apple ID 需要对 App ID 做一次性修复,Sideloadly 会丢掉它 @@ -177,7 +177,7 @@ iPad 上基本没有问题。其他 Switch 2 游戏在本 fork 上没有试过 `python3 tools/engine.py sync` 把它构建到 `engine/`。见 [patches/README.md](patches/README.md)。 `python3 tools/engine.py status` 列出带着的全部补丁。每天一次的任务移动固定提交并开 pull request, 某个补丁对不上时开一个点名该补丁的 issue。本页指向引擎文档的链接都指向 nxnative 的固定提交。 -- **其他 Switch 游戏:** [docs/agents](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/agents/README.md)(解包、二进制调查、bring-up)。 +- **其他 Switch 游戏:** [docs/agents](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/agents/README.md)(解包、二进制调查、bring-up)。 - **版本历史:** [CHANGELOG.zh-CN.md](CHANGELOG.zh-CN.md)。 ## 致谢 @@ -201,7 +201,7 @@ iPad 上基本没有问题。其他 Switch 2 游戏在本 fork 上没有试过 - **[SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross)** —— SPIR-V 转 Metal Shading Language。 - **yuzu**、**deko3d**、**libvpx**、**stb_vorbis**、**libdeflate** —— 各自贡献见上游致谢列表。 -`dm/third_party` 里随附了其他项目及其许可证([列表](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/dm/third_party/README.md))。 +`dm/third_party` 里随附了其他项目及其许可证([列表](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/dm/third_party/README.md))。 ## 法律 diff --git a/frontend/README.md b/frontend/README.md index b94b1a916..9622b501e 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -169,7 +169,7 @@ library manifest is stored at ## Launch options The button beside **Play** opens the game's launch options: the settings the iPhone -launcher shows before a game starts ([docs/settings.md](https://github.com/seedds/nxnative/blob/7249ee9f95ed728aff9a86395330d10d4c91fb5c/docs/settings.md)), read +launcher shows before a game starts ([docs/settings.md](https://github.com/seedds/nxnative/blob/88c7e03f51e3934dc227089ea81ec64fcd525b03/docs/settings.md)), read from the list the engine packs into the Mac app (`Contents/Resources/dm/launcher.json`, written by `dm/games/settings.py` from `dm/games/settings.tsv`, and the engine change that packs it is [seedds/nxnative#352](https://github.com/seedds/nxnative/pull/352), so a diff --git a/patches/0001-runtime-profile.patch b/patches/0001-runtime-profile.patch index 7c616de36..b5a2c9867 100644 --- a/patches/0001-runtime-profile.patch +++ b/patches/0001-runtime-profile.patch @@ -18,22 +18,22 @@ Issue: LNPANDY/NX2Apple#123 dm/host/hle_os.c | 4 +- dm/host/hle_profile.c | 281 +++++++++++++++++++++++++++++++ dm/host/hle_profile.h | 37 ++++ - dm/host/hle_svc.c | 34 ++-- + dm/host/hle_svc.c | 38 ++--- dm/host/loader.c | 2 +- dm/host/tests/Makefile | 68 ++++---- dm/host/tests/t_layout_runtime.c | 103 +++++++++++ dm/ios/Sources/DmGamepad.m | 2 +- docs/env_switches.tsv | 1 + - 13 files changed, 514 insertions(+), 57 deletions(-) + 13 files changed, 516 insertions(+), 59 deletions(-) create mode 100644 dm/host/hle_profile.c create mode 100644 dm/host/hle_profile.h create mode 100644 dm/host/tests/t_layout_runtime.c diff --git a/dm/games/game.py b/dm/games/game.py -index dcbd753..573b94a 100644 +index 53e23ee..b8277f3 100644 --- a/dm/games/game.py +++ b/dm/games/game.py -@@ -278,6 +278,40 @@ if __name__ == '__main__': +@@ -281,6 +281,40 @@ if __name__ == '__main__': elif len(a) == 3 and a[0] == 'modules': # the modules to load from a folder of rewritten modules: its main build's row, else all the profile's (main.c profile_modules) g, bid = load(a[1]), re.search(r'^# BUILD_ID\s+(\w+)', (Path(a[2]) / 'main.fixups.tsv').read_text(errors='replace'), re.M) print(' '.join(g.build_modules().get(bid.group(1) if bid else '', g.words('modules')))) @@ -465,19 +465,19 @@ index 0000000..a2febeb +const struct dm_profile *dm_profile(void); // loaded once; never NULL +void dm_profile_log(void); // one line per differing value (called after loading) diff --git a/dm/host/hle_svc.c b/dm/host/hle_svc.c -index 5fe7039..ea76b71 100644 +index 7179c96..e13bb44 100644 --- a/dm/host/hle_svc.c +++ b/dm/host/hle_svc.c -@@ -16,7 +16,7 @@ +@@ -25,7 +25,7 @@ // about 4.2 GB in total; on iOS without the "extended virtual addressing" entitlement a single reservation can be at most 6 GB // (read on a real device: 6 GB succeeds, 7 GB fails), so it cannot be made any wider -- // with an 8 GB reservation, builds from developer accounts that cannot sign that entitlement (free accounts) would exit at this step. -#define RESERVE DM_GAME_MEM_RESERVE // SMBW: the heap needs only 1.47 GB, so the block stays at 5 GB (the total proven on a real device; a single 6 GB block can be reserved, but Metal etc. still need address space) +#define RESERVE (dm_profile()->mem_reserve) // SMBW: the heap needs only 1.47 GB, so the block stays at 5 GB (the total proven on a real device; a single 6 GB block can be reserved, but Metal etc. still need address space) static uint8_t *g_as; // ASLR region base = the whole block - static size_t g_reserve; // its size: RESERVE, or more with a DM_ALIAS_SIZE alias region + static size_t g_reserve; // its size: RESERVE less the profile's alias region plus the one in use (mem_init_once) static uint8_t *g_heap; static _Atomic size_t g_heap_cur; static size_t g_heap_cap; -@@ -41,15 +41,15 @@ static void *alias_windows_thread(void *arg); +@@ -193,21 +193,21 @@ static void *alias_windows_thread(void *arg); static void *regions_thread(void *arg); static void mem_init_once(void) { size_t pg = (size_t)getpagesize(); @@ -488,26 +488,35 @@ index 5fe7039..ea76b71 100644 + (dm_profile()->mem_stack) > RESERVE - 0x10000000 - (dm_profile()->mem_alias) || + ((RESERVE | (dm_profile()->mem_alias) | (dm_profile()->mem_stack)) & (pg - 1))) die("guest address-space profile has invalid native boundaries"); - // DM_ALIAS_SIZE=: an alias region of that size instead of the profile's, the heap and stacks as they are; the block grows with it. A Switch-sized one (64 GB) holds what - // a game reserves unchanged, for measuring its address regions on the Mac (the NSP script's heap check); an iPhone has no room for more than the profile's -- size_t alias = DM_GAME_MEM_ALIAS; const char *as = getenv("DM_ALIAS_SIZE"); -+ size_t alias = (dm_profile()->mem_alias); const char *as = getenv("DM_ALIAS_SIZE"); + // The alias region is the Switch's 64 GB on the Mac (or the profile's, when larger): games lay their regions out for that much, and what the profile can give an iPhone (one + // reservation of at most 6 GB) crashed Trails in the Sky 1st Chapter at launch and Just Dance within a minute. It is address space, not memory: the application pool bounds + // what is mapped. DM_ALIAS_SIZE= sets another size, the heap and stacks as they are; the profile's value reproduces an iPhone's address space on the Mac (the NSP script's heap check) + #if TARGET_OS_IPHONE +- size_t alias = DM_GAME_MEM_ALIAS; ++ size_t alias = (dm_profile()->mem_alias); + #else +- size_t alias = DM_GAME_MEM_ALIAS > SWITCH_ALIAS ? DM_GAME_MEM_ALIAS : SWITCH_ALIAS; ++ size_t alias = (dm_profile()->mem_alias) > SWITCH_ALIAS ? (dm_profile()->mem_alias) : SWITCH_ALIAS; + #endif + const char *as = getenv("DM_ALIAS_SIZE"); if (as && *as) { alias = (size_t)strtoull(as, NULL, 0); if (alias < 0x10000000 || (alias & (pg - 1))) die("DM_ALIAS_SIZE %s: at least 256 MB, in whole host pages", as); } - g_reserve = RESERVE - DM_GAME_MEM_ALIAS + alias; + g_reserve = RESERVE - (dm_profile()->mem_alias) + alias; g_as = mmap(NULL, g_reserve, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (g_as == MAP_FAILED) die("reserve guest AS"); - // Fixed partition (all inside the reserved block): alias 2.875 GB, stack 256 MB, heap the remaining 1.625 GB. -@@ -59,7 +59,7 @@ static void mem_init_once(void) { + guest_file_init(); +@@ -220,8 +220,8 @@ static void mem_init_once(void) { + // and resource loading then MapPhysicalMemory's into the alias region after the node area on demand. With a 1.25 GB alias region only about 256 MB was left there, and 3 MB-class layout archives (WipeCourseIn / WipeWarp) // got no address, were skipped, and crashed on a null pointer when used (main +0x373e04). So alias = node area + 1.75 GB + slack; the heap needs only 1.47 GB, and 2.625 GB is plenty. - // The alias and stack sizes come from the game profile (mem_alias / mem_stack), the heap takes the rest; Mario: alias 0xB8000000, stack 0x10000000 → stack at +0xC8000000, heap at +0xD8000000 - g_alias = g_as + 0x10000000; g_alias_cap = alias; -- g_stack = g_alias + g_alias_cap; g_stack_cap = DM_GAME_MEM_STACK; -+ g_stack = g_alias + g_alias_cap; g_stack_cap = (dm_profile()->mem_stack); - g_heap = g_stack + g_stack_cap; g_heap_cap = g_reserve - (size_t)(g_heap - g_as); + // The alias and stack sizes come from the game profile (mem_alias / mem_stack, the Mac's alias region as above), the heap takes the rest of mem_reserve +- g_heap = g_as + 0x10000000; g_heap_cap = RESERVE - DM_GAME_MEM_ALIAS - DM_GAME_MEM_STACK - 0x10000000; +- g_stack = g_heap + g_heap_cap; g_stack_cap = DM_GAME_MEM_STACK; ++ g_heap = g_as + 0x10000000; g_heap_cap = RESERVE - (dm_profile()->mem_alias) - (dm_profile()->mem_stack) - 0x10000000; ++ g_stack = g_heap + g_heap_cap; g_stack_cap = (dm_profile()->mem_stack); + g_alias = g_stack + g_stack_cap; g_alias_cap = alias; if (!(g_alias_bits = calloc((g_alias_cap >> 18) + 1, sizeof *g_alias_bits))) die("alias region page bitmap calloc"); if (!(g_alias_exposed = calloc(g_alias_cap / pg / 64 + 1, sizeof *g_alias_exposed))) die("alias exposure bitmap calloc"); -@@ -78,7 +78,7 @@ static void mem_init(void) { +@@ -241,7 +241,7 @@ static void mem_init(void) { // and hands the whole block to its own StandardAllocator (main+0x32137a0) -- so heap size = pool − image footprint, it has only as much as the host reports. // Previously 3 GB was reported here and SetHeapSize never failed: the heap was about 150 MB smaller than on the real device, at the run entry peak the main heap had only about 18 MB left, and under high local load a timing shift made a 128 KB request return null, // which the guest writes to without a null check (CRASH main+0x1b36138, re-verified after merge with inv_tap). @@ -516,7 +525,7 @@ index 5fe7039..ea76b71 100644 static size_t image_bytes(void) { size_t n = 0; for (int i = 0; i < g_nmod; i++) if (!g_mod[i].nro) n += g_mod[i].size; // an NRO lives in memory the game allocated (its image and bss buffers) return (n + 0x1fffff) & ~(size_t)0x1fffff; } // Giant bss (modules with the patch area before the base, patch_nso.py FAR): the image bytes beyond the file part. Used memory was reported as "heap + 64 MB", where the 64 MB covers ordinary modules' code and data (Mario's two modules are about 74 MB together, -@@ -472,11 +472,11 @@ uint64_t svc_dispatch(int num, uint64_t *regs, uint64_t orig_addr) { +@@ -655,11 +655,11 @@ uint64_t svc_dispatch(int num, uint64_t *regs, uint64_t orig_addr) { // Logged once (Mario's retries would repeat it): a game that sizes its heap as pool − used memory and does not retry stops here when its modules are larger than the used memory reported // (profile mem_used; the NSP script's probe boot reads this line, and sets mem_used when no SetHeapSize succeeds afterwards) static int once; if (!once++) LOG("[mem] SetHeapSize %#zx + modules %#zx > app pool %#llx (used memory reported %#llx + bss %#zx)", size, image_bytes() - big_bss_bytes(), @@ -531,7 +540,7 @@ index 5fe7039..ea76b71 100644 pthread_mutex_unlock(&g_map_lock); regs[0] = 0x10801; return 0; } int rc = 0; -@@ -620,8 +620,8 @@ uint64_t svc_dispatch(int num, uint64_t *regs, uint64_t orig_addr) { +@@ -806,8 +806,8 @@ uint64_t svc_dispatch(int num, uint64_t *regs, uint64_t orig_addr) { // and maps new ones for later allocations, until a map fails: then it unmaps the cached block and retries (measured on the Mac: the refused 4 MB map at +0x43e2c000 was followed by // UnmapPhysicalMemory of the 3.15 GB block and the same map again). Without the limit the mapped total went past the pool in the first minute and the freed block stayed mapped. // Off by default: Ace Attorney Investigations Collection maps a little more than the 1 GB pool its profile reports @@ -542,7 +551,7 @@ index 5fe7039..ea76b71 100644 if (need && need > room) { pthread_mutex_unlock(&g_map_lock); static _Atomic int nref; if (atomic_fetch_add(&nref, 1) < 8) LOG("[mem] MapPhysicalMemory +%#zx refused: %.2f GB mapped, the application pool is full (ResultLimitReached, the SDK frees cached pages and retries)", -@@ -687,16 +687,16 @@ uint64_t svc_dispatch(int num, uint64_t *regs, uint64_t orig_addr) { +@@ -873,16 +873,16 @@ uint64_t svc_dispatch(int num, uint64_t *regs, uint64_t orig_addr) { case I_StackAddr: v = (uint64_t)g_stack; break; case I_StackSize: v = g_stack_cap; break; case I_TotalMem: case I_TotalNonSys: v = APP_POOL; break; case I_UsedMem: case I_UsedNonSys: pthread_mutex_lock(&g_map_lock); @@ -564,7 +573,7 @@ index 5fe7039..ea76b71 100644 // nn::os::GetFreeThreadCount (SDK 11+): the thread resource limit less the threads alive, as the kernel answers it (Atmosphère kern_svc_info.cpp). pm gives an application a limit of // 96 threads (pm_spec.cpp BaseApplicationThreads; a boost raises it to 256). Monster Train 2's Unity 2022.3 player creates a thread only when this is at least 1: the 0 it got here diff --git a/dm/host/loader.c b/dm/host/loader.c -index db23db2..5bc0117 100644 +index 26d6882..c12d21b 100644 --- a/dm/host/loader.c +++ b/dm/host/loader.c @@ -304,7 +304,7 @@ static void install_fps_state(module_t *m) { @@ -577,10 +586,10 @@ index db23db2..5bc0117 100644 float f = (float)t; memcpy(dm_fps_state, &f, sizeof f); // Render scale of the 3D views (the stubs at Tears of the Kingdom's main+0xd5cc0, Breath of the Wild's main+0x1266880): 1.0 keeps the game's own size and dynamic resolution; diff --git a/dm/host/tests/Makefile b/dm/host/tests/Makefile -index d37dec4..435c402 100644 +index a316bb2..057f13a 100644 --- a/dm/host/tests/Makefile +++ b/dm/host/tests/Makefile -@@ -70,7 +70,7 @@ run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture +@@ -71,7 +71,7 @@ run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_to run: $(B)/t_nro run: $(B)/t_symbols $(B)/t_symbols: t_symbols.c ../hle.c ../loader.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -589,7 +598,7 @@ index d37dec4..435c402 100644 .PHONY: symbols symbols: $(B)/t_symbols $(B)/t_symbols -@@ -88,12 +88,12 @@ $(B)/t_audren_layout: t_audren_layout.c ../hle_audren.c ../hle.h ../dm.h $(GAME_ +@@ -89,12 +89,12 @@ $(B)/t_audren_layout: t_audren_layout.c ../hle_audren.c ../hle.h ../dm.h $(GAME_ audren-layout: $(B)/t_audren_layout $(B)/t_audren_layout $(B)/t_system_event: t_system_event.c ../hle_thread.c ../hle_sdk16.c ../hle_misc.c ../hle_bigstack.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -604,7 +613,7 @@ index d37dec4..435c402 100644 .PHONY: nro nro: $(B)/t_nro $(B)/t_nro -@@ -107,7 +107,7 @@ cheats: $(B)/t_cheats +@@ -108,7 +108,7 @@ cheats: $(B)/t_cheats $(GAME_H): FORCE $(MAKE) -C .. game_profile GAME=$(GAME) $(B)/t_heap_size: t_heap_size.c ../hle_svc.c ../dm.h $(GAME_H) | $(B) @@ -613,7 +622,7 @@ index d37dec4..435c402 100644 .PHONY: heap_size heap_size: $(B)/t_heap_size $(B)/t_heap_size -@@ -117,27 +117,27 @@ $(B)/t_gesture: t_gesture.c ../hle_touch.c ../front.h ../dm.h $(GAME_H) | $(B) +@@ -118,27 +118,27 @@ $(B)/t_gesture: t_gesture.c ../hle_touch.c ../front.h ../dm.h $(GAME_H) | $(B) $(CLANG) -O1 -Wall -Wno-unused-function -I.. $(GINC) -o $@ t_gesture.c ../hle_touch.c $(B)/t_thread_leak: t_thread_leak.c ../thread.c ../dm.h $(GAME_H) | $(B) $(CLANG) -O1 -Wno-unused-function -I.. $(GINC) -o $@ t_thread_leak.c @@ -653,7 +662,7 @@ index d37dec4..435c402 100644 $(B)/t_save_sync_metadata: t_save_sync_metadata.c ../save_sync.c ../save_sync.h | $(B) $(CLANG) -O1 -Wall -Wno-unused-function -I.. -o $@ t_save_sync_metadata.c .PHONY: save-sync -@@ -163,7 +163,7 @@ $(B)/t_audio_spsc_tsan: t_audio_spsc.c ../hle_audio.c ../hle_bigstack.c ../hle.h +@@ -164,7 +164,7 @@ $(B)/t_audio_spsc_tsan: t_audio_spsc.c ../hle_audio.c ../hle_bigstack.c ../hle.h $(CLANG) -O1 -g -O1 -fsanitize=thread -Wno-unused-function -I.. $(GINC) -o $@ t_audio_spsc.c ../hle_bigstack.c -framework AudioToolbox run: $(B)/t_fpslock $(B)/t_fpslock: t_fpslock.c ../hle_fpslock.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -662,7 +671,7 @@ index d37dec4..435c402 100644 run: $(B)/t_ue_heap_span $(B)/t_ue_heap_span: t_ue_heap_span.c ../hle_uemalloc.c ../hle.h ../dm.h $(GAME_H) | $(B) $(CLANG) -O1 -g -Wall -Werror -Wno-unused-function $(GINC) -o $@ t_ue_heap_span.c -@@ -188,19 +188,19 @@ $(B)/t_pctl: t_pctl.c ../hle_misc.c ../hle.h ../dm.h $(GAME_H) | $(B) +@@ -192,19 +192,19 @@ $(B)/t_pctl: t_pctl.c ../hle_misc.c ../hle.h ../dm.h $(GAME_H) | $(B) $(B)/t_sf: t_sf.c ../hle_sf.c ../hle.h ../dm.h $(GAME_H) | $(B) $(CLANG) -O1 -Wall -Werror -Wno-unused-function -I.. $(GINC) -o $@ t_sf.c $(B)/t_shared_font: t_shared_font.c ../hle_sdk16.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -686,7 +695,7 @@ index d37dec4..435c402 100644 $(B)/t_motion: t_motion.c ../motion_fusion.h | $(B) $(CLANG) -O1 -Wall -Werror -Wno-unused-function -o $@ t_motion.c -lm run: $(B)/t_motion_apple -@@ -230,15 +230,15 @@ $(B)/t_haptics_apple: t_haptics_apple.m ../haptics_apple.m ../haptics_apple.h .. +@@ -234,15 +234,15 @@ $(B)/t_haptics_apple: t_haptics_apple.m ../haptics_apple.m ../haptics_apple.h .. $(B)/t_time: t_time.c ../hle_time.c ../hle.h ../dm.h $(GAME_H) | $(B) $(CLANG) -O1 -Wall -Werror -Wno-unused-function -I.. $(GINC) -o $@ t_time.c $(B)/t_oe: t_oe.c ../hle_oe.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -707,7 +716,7 @@ index d37dec4..435c402 100644 .PHONY: preempt preempt: $(B)/t_preempt $(B)/t_preempt -@@ -270,7 +270,7 @@ $(B): +@@ -274,7 +274,7 @@ $(B): mkdir -p $@ .PHONY: run tsan $(B)/t_resolution: t_resolution.c ../hle_fpslock.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -716,7 +725,7 @@ index d37dec4..435c402 100644 .PHONY: resolution run: resolution resolution: $(B)/t_resolution -@@ -283,17 +283,17 @@ tsan: $(B)/t_audio_spsc_tsan $(B)/t_hid_tsan +@@ -287,17 +287,17 @@ tsan: $(B)/t_audio_spsc_tsan $(B)/t_hid_tsan TMPDIR="$(abspath $(B))" $(B)/t_hid_tsan MEM_FLAGS = -O1 -Wall -Werror -Wno-unused-function -Imemory_profile -I.. @@ -740,7 +749,7 @@ index d37dec4..435c402 100644 .PHONY: memory memory-tsan memory-dirty memory-profile memory: $(B)/t_svc_memory $(B)/t_svc_memory_unlimited $(B)/t_svc_memory_16k $(B)/t_svc_memory -@@ -309,7 +309,7 @@ run: memory memory-dirty memory-profile +@@ -321,7 +321,7 @@ run: memory memory-dirty memory-profile memory-file # A native blocking notification wait must release the thread's guest core. $(B)/t_notify_sched: t_notify_sched.c t_condvar_stub.c ../hle_thread.c ../hle_oe.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -872,10 +881,10 @@ index d251b0d..c33176a 100644 _full = l == 1; _tap = l == 2; [self applyLayout]; diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 39316ad..8beb610 100644 +index 71a1887..4fd4ef6 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -250,6 +250,7 @@ DM_PROBE_LIMIT diagnostic Maximum printed hits per guest probe; 0 or absent leav +@@ -255,6 +255,7 @@ DM_PROBE_LIMIT diagnostic Maximum printed hits per guest probe; 0 or absent leav DM_PROBE_POLL diagnostic `=
[+]` (hex, 4 bytes by default, up to 8): a host thread spins on the bytes and on each change snapshots every thread's pc, lr and frames, for writes the watch misses DM_PROBE_WATCH diagnostic `=
[+]` (hex, 8 bytes by default): write-protects that host page and reports who writes these bytes, without a debugger DM_PROBE_X2 diagnostic `=` (hex): probe hits also dump that many bytes of the memory x2 points to diff --git a/patches/0003-ios-game-data-folder.patch b/patches/0003-ios-game-data-folder.patch index ceace8c37..fc123e458 100644 --- a/patches/0003-ios-game-data-folder.patch +++ b/patches/0003-ios-game-data-folder.patch @@ -301,7 +301,7 @@ index cddc294..577e6c4 100644 } - (BOOL)prefersHomeIndicatorAutoHidden { return YES; } diff --git a/dm/ios/Sources/main.m b/dm/ios/Sources/main.m -index b741e8a..54bcceb 100644 +index 7cd198e..bd3bafa 100644 --- a/dm/ios/Sources/main.m +++ b/dm/ios/Sources/main.m @@ -8,9 +8,10 @@ @@ -457,7 +457,7 @@ index b741e8a..54bcceb 100644 static int boot_env(void) { NSFileManager *fm = NSFileManager.defaultManager; NSString *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; -@@ -174,11 +268,30 @@ static int boot_env(void) { +@@ -174,12 +268,31 @@ static int boot_env(void) { NSString *lang = [NSBundle.mainBundle objectForInfoDictionaryKey:@"DMLanguage"]; if (!getenv("DM_LANG") && [lang isKindOfClass:NSString.class] && lang.length) setenv("DM_LANG", lang.UTF8String, 0); fprintf(stderr, "boot: game language %s\n", getenv("DM_LANG") ?: "en-US"); @@ -475,6 +475,7 @@ index b741e8a..54bcceb 100644 + BOOL per_game = [romfs hasPrefix:gdir]; // messaging nil is NO here: no data means the shared layout + if (!set_data_env(romfs, NO)) { if (!romfs) romfs_identity_report(doc); // a slim build says why its romfs.img was not used + fprintf(stderr, "boot: no game data yet (Documents/romfs.img, Documents/romfs or /romfs); the launcher asks for it\n"); } + setenv("DM_GUEST_FILE_DIR", NSTemporaryDirectory().fileSystemRepresentation, 0); // DM_GUEST_FILE (hle_svc.c) keeps the guest's memory in files there + setenv("DM_NVN_SHMAP", [dm stringByAppendingPathComponent:@"shader_map.tsv"].fileSystemRepresentation, 0); + // Shader libraries: a game's own /msl first (a per-game layout may carry its own), else + // the app's shared /msl (the program's /msl/p in a game of several programs). @@ -492,7 +493,7 @@ index b741e8a..54bcceb 100644 aoc_locate(); // add-on content: Documents/aoc, else /aoc; an empty Documents/aoc explicitly selects no DLC NSString *pso = [res stringByAppendingPathComponent:@"pso"]; // the pipeline archive made offline (build_ipa.sh, dm/gfx/tools/pso_offline.py): the shader pairs' bases, which the device then does not build if ([fm fileExistsAtPath:pso]) setenv("DM_PSO_BUNDLE", pso.fileSystemRepresentation, 0); -@@ -193,7 +306,7 @@ static int boot_env(void) { +@@ -194,7 +307,7 @@ static int boot_env(void) { // The host's profile is the program's (DM_PROGRAM, main.c dm_game_cur). oe::ExecuteProgram records the program and its parameters in Documents/program.bin (DM_EXEC_OUT, hle_oe.c), names it on screen, schedules a // notification that opens the app (front_ios.m front_program_handoff) and ends; this start reads the record and takes it away, so a later start, after the player quit the game, is at the launcher as on the Switch. // Started from the record, the settings screen stays away (DM_LAUNCHER=0): the player is on the way into a game. Returns the program's dm folder (the host's modules folder), nil when its game files are missing @@ -501,7 +502,7 @@ index b741e8a..54bcceb 100644 NSFileManager *fm = NSFileManager.defaultManager; NSString *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0], *res = NSBundle.mainBundle.resourcePath; NSString *dmdir = [res stringByAppendingPathComponent:@"dm"], *msl = [res stringByAppendingPathComponent:@"msl"], *prog = nil; -@@ -214,12 +327,7 @@ static int boot_env(void) { +@@ -215,12 +328,7 @@ static int boot_env(void) { setenv("DM_APP_DM", dmdir.fileSystemRepresentation, 1); // the launcher's settings (Launcher.swift) fprintf(stderr, "boot: program %d of %s (%zu bytes of launch parameter)\n", n, [[plist stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet] stringByReplacingOccurrencesOfString:@"\n" withString:@" "].UTF8String, d.length > 4 ? (size_t)d.length - 4 : 0); } diff --git a/patches/0004-ios-slim-ipa.patch b/patches/0004-ios-slim-ipa.patch index d5d42ffdf..8e3b27282 100644 --- a/patches/0004-ios-slim-ipa.patch +++ b/patches/0004-ios-slim-ipa.patch @@ -148,7 +148,7 @@ index a832ad5..0f5485d 100644 raise ValueError(f'bundle ID {bundle_id} is already used by {other}; choose a different --bundle-id for this game') diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 8beb610..0995d98 100644 +index 4fd4ef6..6f3b3ef 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv @@ -47,6 +47,7 @@ DM_FSLOG diagnostic `=` writes one TSV line per ReadFile: seconds since bo diff --git a/patches/0005-nsp-dotnet-home.patch b/patches/0005-nsp-dotnet-home.patch index 951c37a5b..6962adb4b 100644 --- a/patches/0005-nsp-dotnet-home.patch +++ b/patches/0005-nsp-dotnet-home.patch @@ -14,12 +14,12 @@ Issue: LNPANDY/NX2Apple#10 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index 48eb007..75ad0a7 100755 +index 15bebe3..771182e 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -33,7 +33,7 @@ # unless ALLOW_OTHER_METAL / --allow-other-metal. Issue #242 reported corruption with another compiler; the cause is still unconfirmed. - # Python 3.12+ is required for bounded archive mappings inside images. The Python running this file needs lz4, capstone 5.x and fonttools (and backports.zstd before Python 3.14); also needed: Xcode, xcodegen, .NET SDK 10, zstd. SPIRV-Cross is the in-repo build. A first run takes 2 (Metroid Dread) to 10 minutes (Breath of the Wild: 5 of them the shader split), longer when shader libraries must be compiled; a repeat run with unchanged inputs takes about 20 s (Metroid Dread), mostly packing the IPA. Key values are never printed. + # Python 3.12+ is required for bounded archive mappings inside images. The Python running this file needs lz4, capstone 5.x, fonttools and zbic (and backports.zstd before Python 3.14); also needed: Xcode, xcodegen, .NET SDK 10, zstd. SPIRV-Cross is the in-repo build. A first run takes 2 (Metroid Dread) to 10 minutes (Breath of the Wild: 5 of them the shader split), longer when shader libraries must be compiled; a repeat run with unchanged inputs takes about 20 s (Metroid Dread), mostly packing the IPA. Key values are never printed. # The console shows one line per stage ([k/n] stage, with a bar, done/total, percent, elapsed time and ETA where the work can be counted); the tools' own output goes only to logs/ (PROGRESS / --progress). --progress json writes newline-delimited JSON events to stdout. -import ast, collections, contextlib, ctypes, errno, getpass, hashlib, importlib.util, json, os, re, shlex, shutil, struct, subprocess, sys, tempfile, threading, time, types +import ast, collections, contextlib, ctypes, errno, getpass, pwd, hashlib, importlib.util, json, os, re, shlex, shutil, struct, subprocess, sys, tempfile, threading, time, types diff --git a/patches/0008-bc-cpu-decode.patch b/patches/0008-bc-cpu-decode.patch index 646b36ac7..1b07fda00 100644 --- a/patches/0008-bc-cpu-decode.patch +++ b/patches/0008-bc-cpu-decode.patch @@ -46,7 +46,7 @@ index 8a8adb8..223a7f7 100644 tex_dirty_upload on # 60 fps (30 on the Switch): the host decides each frame's step in whole vsyncs and holds the frame that long (rewriter stubs at FPSLocker's sites, patches.py FRAME_STEP); diff --git a/dm/games/game.py b/dm/games/game.py -index 573b94a..a8f472a 100644 +index b8277f3..978dd69 100644 --- a/dm/games/game.py +++ b/dm/games/game.py @@ -197,6 +197,8 @@ def header_one(g): @@ -58,27 +58,27 @@ index 573b94a..a8f472a 100644 tds = g.get('tex_dyn_slots', 'off') # shaders whose texture handle the translator could not trace to one slot: slot indices in c2, the bound slots in the fallback arrays (nvn_mtl.m): on / off if tds not in ('on', 'off'): sys.exit(f'{g.id}: tex_dyn_slots must be on or off: {tds}') vcr = g.get('vconv_range', 'off') # vertex attributes converted on the CPU only over the vertices each draw reads (nvn_mtl.m vc_draw_range): on / off -@@ -258,6 +260,7 @@ def header_one(g): +@@ -260,6 +262,7 @@ def header_one(g): f'#define DM_GAME_SHADER_KEY_SKIP_OFF {sko:#x}', f'#define DM_GAME_SHADER_KEY_SKIP_LEN {skl:#x}', f'#define DM_GAME_RENDER_COND {int(rcd == "on")}', f'#define DM_GAME_TEX_DIRTY_UPLOAD {int(tdu == "on")}', + f'#define DM_GAME_TEX_BC_CPU {int(tbc == "on")}', f'#define DM_GAME_TEX_DYN_SLOTS {int(tds == "on")}', f'#define DM_GAME_GPU_TS_SEGMENT {int(gts == "segment")}', - f'#define DM_GAME_UPSCALER_KEYS {q(",".join(ups))}', f'#define DM_GAME_UPSCALER_N {len(ups)}', + f'#define DM_GAME_DRAW_BARRIERS_GAME {int(dbr == "game")}', diff --git a/dm/gfx/nvn/nvn_mtl.m b/dm/gfx/nvn/nvn_mtl.m -index f73da6d..3a28360 100644 +index eb8b266..db76d30 100644 --- a/dm/gfx/nvn/nvn_mtl.m +++ b/dm/gfx/nvn/nvn_mtl.m -@@ -20,6 +20,7 @@ - #include "nvn_mtl.h" +@@ -21,6 +21,7 @@ + #include #include "nvn_obj.h" #include "../tex/nvn_fmt_mtl.h" +#include "../tex/nvn_bcdec.h" // BC on the CPU for GPUs without BC support (profile tex_bc_cpu) #include "../tex/nvn_deswizzle.h" #include "../tex/nvn_deswizzle_gpu.h" #include "nvn_builtin.h" -@@ -90,6 +91,8 @@ static inline uint64_t tex_slot_h(const uint64_t *tex, const uint64_t *img, unsi +@@ -92,6 +93,8 @@ static inline uint64_t tex_slot_h(const uint64_t *tex, const uint64_t *img, unsi // The shaders the host writes itself (built-in kernels, clears, copies, index conversions, geometry packing, the present: dm/gfx/msl), compiled when the host is built and loaded by // name (host_msl_lib), so none is compiled at run time. The diagnostic overrides (DM_NVN_HILITE, DM_NVN_MAGSCAN, DM_NVN_TEXDUMP) still compile theirs from source static id host_lib(const char *name, NSError **e) { return host_msl_lib(g_dev, nil, name, e); } @@ -87,7 +87,7 @@ index f73da6d..3a28360 100644 static NSMutableArray *g_l3_gpu_leaks; static int g_nvn_deswz_gpu; static id g_q; -@@ -426,6 +429,9 @@ void mtl_init(void) { +@@ -455,6 +458,9 @@ void mtl_init(void) { g_dev = MTLCreateSystemDefaultDevice(); if (!g_dev) MDIE("no Metal device"); if (!g_dev.hasUnifiedMemory) MDIE("needs a unified-memory device (zero-copy memory pools)"); @@ -97,7 +97,7 @@ index f73da6d..3a28360 100644 g_q = [g_dev newCommandQueue]; // A device without Metal 4 (the paravirtualized GPU of a virtual Mac, such as GitHub's macos-26 runner: no residency set, no MTL4CommandQueue) cannot run the NVN layer. The GL and // Vulkan layers draw on classic queues of their own and need only the device (mtl_device_classic): there the classic part starts and presents are dropped, which is all a -@@ -456,8 +462,9 @@ void mtl_init(void) { +@@ -485,8 +491,9 @@ void mtl_init(void) { return; } // BC textures are created and uploaded as BC, with no decompression path (dm/gfx/tex/README.md); without BC the first one aborts in Metal's descriptor validation mid-game @@ -109,8 +109,8 @@ index f73da6d..3a28360 100644 [g_q addResidencySet:g_res]; [g_q4 addResidencySet:g_res]; { void crash_set_wait_dump(void (*fn)(void)); void mtl_wait_state_dump(void); crash_set_wait_dump(mtl_wait_state_dump); } -@@ -2098,7 +2105,12 @@ static MTLTextureUsage img_usage(const tex_t *t, MTLPixelFormat pf) { - static int tex_atomic_capable(id base) { return pf_r32i(base.pixelFormat) && (base.usage & MTLTextureUsageShaderWrite); } +@@ -2211,7 +2218,12 @@ static int tex_atomic_alias(id base) { + } static MTLTextureDescriptor *tex_desc(const tex_t *t, int packaged) { uint32_t pf = 0; uint8_t swz[4]; - if (!nvn_fmt_to_mtl(t->d.format, &pf, swz) || !pf) { RED_ONCE(0, "texture format %#x has no Metal equivalent (first case)", t->d.format); return nil; } @@ -123,7 +123,7 @@ index f73da6d..3a28360 100644 // Metal's limits: 16384 in 2D, 2048 in 3D, 2048 array layers; beyond them Metal aborts the process on an assertion, so they are caught here with a red line if (t->d.width > 16384 || t->d.height > 16384 || t->d.depth > 2048) { RED_ONCE(71, "texture size exceeds Metal limits: %u×%u×%u levels %u format %#x target %d (first case ser=%llu)", t->d.width, t->d.height, t->d.depth, t->d.levels, t->d.format, t->d.target, (unsigned long long)t->serial); return nil; } MTLTextureDescriptor *d = [MTLTextureDescriptor new]; -@@ -2598,15 +2610,43 @@ static void rb_swap_rows(uint8_t *p, size_t wb, size_t rows, size_t nz, size_t r +@@ -2725,15 +2737,43 @@ static void rb_swap_rows(uint8_t *p, size_t wb, size_t rows, size_t nz, size_t r if (!(part = malloc(t->L.size ? t->L.size : 1))) MDIE("sparse texture copy of %llu bytes failed", (unsigned long long)t->L.size); nvn_sparse_copy(t->pool, part, (uint64_t)(uintptr_t)t->mem, t->L.size); src = part; } @@ -165,10 +165,10 @@ index f73da6d..3a28360 100644 + off += dst_pitch * h * z; + continue; + } - if (nvn_deswizzle(&t->d, l, y, src, (uint8_t *)stg.contents + off, pitch)) { RED_ONCE(9, "deswizzle failed format %#x target %d", t->d.format, t->d.target); break; } - if (rb_shift) rb_swap_rows((uint8_t *)stg.contents + off, pitch, rows, z, pitch, pitch * rows, rb_shift); - if (zb && l < 16) { const uint64_t *q = (const uint64_t *)((const uint8_t *)stg.contents + off); size_t nb = pitch * rows * z / 16; -@@ -2616,6 +2656,7 @@ static void rb_swap_rows(uint8_t *p, size_t wb, size_t rows, size_t nz, size_t r + if (linear) { // one 2D level (linear_fit): rows of the storage's stride, the same pitch GetStorageSize counts + size_t srow = t->L.layer_size / (t->d.height ? t->d.height : 1); + if (l || y || z != 1 || srow < pitch || srow * rows > t->L.size) { RED_ONCE(153, "linear texture upload: %ux%u format %#x target %d levels %u is not one 2D level of rows (stride %zu)", t->d.width, t->d.height, t->d.format, t->d.target, t->d.levels, srow); break; } +@@ -2747,6 +2787,7 @@ static void rb_swap_rows(uint8_t *p, size_t wb, size_t rows, size_t nz, size_t r off += pitch * rows * z; } } @@ -417,7 +417,7 @@ index 0000000..5fd5efe + return 0; +} diff --git a/dm/ios/Sources/main.m b/dm/ios/Sources/main.m -index 54bcceb..23e71a0 100644 +index bd3bafa..56267e1 100644 --- a/dm/ios/Sources/main.m +++ b/dm/ios/Sources/main.m @@ -9,6 +9,7 @@ @@ -2022,10 +2022,10 @@ index 0000000..553c3ec + +*/ diff --git a/dm/tools/docs_patches.py b/dm/tools/docs_patches.py -index 5dfb018..204c550 100644 +index ece9c89..fe2c1d0 100644 --- a/dm/tools/docs_patches.py +++ b/dm/tools/docs_patches.py -@@ -136,6 +136,8 @@ SWITCH_BINDING = [ +@@ -137,6 +137,8 @@ SWITCH_BINDING = [ ('jit_route', '', {'what': 'a route for the nn::jit capture', 'now': 'game', 'goal': 'game', 'purpose': 'runtime', 'why': "the game's own menus, for the image an iPhone serves"}), ('jit_out_code', '', {'what': 'where a nn::jit output lists the code it made', 'now': 'game', 'goal': 'game', 'purpose': 'runtime', 'why': "the layout of the game's own output buffer, for the image an iPhone serves"}), ('op_mode', '', {'what': 'the operation mode at start', 'now': 'game', 'goal': 'game', 'purpose': 'runtime', 'why': "the game's controls (handheld: the touch screen instead of a gyro pointer)"}), diff --git a/patches/0009-mac-keymap.patch b/patches/0009-mac-keymap.patch index 262575e6e..39d980495 100644 --- a/patches/0009-mac-keymap.patch +++ b/patches/0009-mac-keymap.patch @@ -237,19 +237,19 @@ index 0000000..56fa499 +} +static int pad_map_is_default(const pad_map *m) { for (int b = 0; b < IM_BUTTONS; b++) if (m->to[b] != b) return 0; return 1; } diff --git a/dm/host/tests/Makefile b/dm/host/tests/Makefile -index 435c402..d1fa46b 100644 +index 057f13a..57917f5 100644 --- a/dm/host/tests/Makefile +++ b/dm/host/tests/Makefile @@ -12,7 +12,7 @@ QUIET = $(if $(findstring -fsanitize,$(SAN)),,2>/dev/null) GINC = $(if $(strip $(GAME)),-I../gen/$(GAME) -I../../games/$(GAME),$(error GAME is not set: make GAME=: $(shell python3 ../../games/game.py list | awk -F'\t' '{printf "%s (%s) ", $$1, $$3}'))) $(B)/t_fs $(B)/t_fs_case $(B)/t_fs_commit_list $(B)/t_fs_commit_list_tsan: ../romfs_image.h GAME_H = ../gen/$(GAME)/game_profile.h --run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid -+run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid $(B)/t_input_map $(B)/t_launcher_contract +-run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid ++run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid $(B)/t_input_map $(B)/t_launcher_contract $(B)/t_language $(B)/t_heap_size $(B)/t_touch -@@ -62,6 +62,8 @@ run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture +@@ -63,6 +63,8 @@ run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_to $(B)/t_motion_pro_mac TMPDIR="$(abspath $(B))" $(B)/t_hid $(B)/t_pad_hid @@ -258,7 +258,7 @@ index 435c402..d1fa46b 100644 $(B)/t_haptics $(B)/t_haptics_apple TMPDIR="$(abspath $(B))" $(B)/t_cheats $(QUIET) -@@ -219,6 +221,10 @@ $(B)/t_motion_pro: t_motion_pro.c ../motion_pro.h | $(B) +@@ -223,6 +225,10 @@ $(B)/t_motion_pro: t_motion_pro.c ../motion_pro.h | $(B) $(CLANG) -O1 -g -Wall -Werror -fsanitize=address,undefined -o $@ t_motion_pro.c $(B)/t_motion_pro_mac: t_motion_pro_mac.m ../motion_pro_mac.h ../motion_pro.h ../dm_input.h | $(B) $(CLANG) -O1 -g -fobjc-arc -Wall -Werror -Wno-unused-function -fsanitize=address,undefined -I.. -o $@ t_motion_pro_mac.m -framework Foundation -framework GameController -framework IOKit @@ -385,10 +385,10 @@ index 6131969..b1ccc86 100644 How a Switch with Atmosphère runs cheats, and where this host differs, is in [Cheats on a Switch](reference/cheats.md). diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 0995d98..9da039d 100644 +index 6f3b3ef..3ca609a 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -81,6 +81,7 @@ DM_JIT_MISS_DUMP diagnostic Folder for the input of each `nn::jit` request a ser +@@ -85,6 +85,7 @@ DM_JIT_MISS_DUMP diagnostic Folder for the input of each `nn::jit` request a ser DM_JIT_SERVE path Folder of a `nn::jit` image (`jit.img`, `jit.tsv`, `jit.out`): the Mac serves requests from it as the iOS app does, and the plugin does not run DM_KBD switch `=`: the system keyboard returns that text as OK without showing an input box; unset with no window, it returns cancel DM_KBD_PLAYER switch `own` makes the Mac keyboard player 1 on its own, with controllers from player 2 on (by default the first controller shares player 1 with the keyboard) @@ -396,7 +396,7 @@ index 0995d98..9da039d 100644 DM_KEYS switch Scripted input: `>:[+key][~hold or ~f][*count/interval],…` presses buttons, sticks, touches and `Shake` at those times; `=@` reads the steps from the file DM_LANG player Game language as a language code (default `en-US`; the iOS app sets the one the IPA was packed with) DM_LAUNCHER switch `0` skips the iOS settings screen or the Mac settings window and starts the game at once, with the saved choices (on the Mac also `--no-launcher`) -@@ -224,6 +225,7 @@ DM_OODLE_JOB path `=`: the host runs the game's own Oodle decoder on e +@@ -229,6 +230,7 @@ DM_OODLE_JOB path `=`: the host runs the game's own Oodle decoder on e DM_OODLE_MODULES path The rewritten modules the Mac host's Oodle tool mode loads when `shader_split.py` decodes Trinity packs; set by the NSP script DM_OODLE_SERVE switch `=1`: the host answers decode requests on stdin / stdout with the game's own Oodle (or LZ5) decoder from the guest main thread before the SDK starts, until stdin ends DM_OPMODE player The operation mode the game sees: `0` handheld, `1` console (default: the profile's `op_mode`, console unless set); read once at startup diff --git a/patches/0017-fork-patches-stamp.patch b/patches/0017-fork-patches-stamp.patch index ad6bce0d6..869e970d4 100644 --- a/patches/0017-fork-patches-stamp.patch +++ b/patches/0017-fork-patches-stamp.patch @@ -23,7 +23,7 @@ Issue: LNPANDY/NX2Apple#123 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dm/ios/Sources/main.m b/dm/ios/Sources/main.m -index 23e71a0..c11b457 100644 +index 56267e1..076d4ef 100644 --- a/dm/ios/Sources/main.m +++ b/dm/ios/Sources/main.m @@ -234,6 +234,9 @@ static int boot_env(void) { diff --git a/patches/0018-nsp-dotnet-root-verify.patch b/patches/0018-nsp-dotnet-root-verify.patch index e7707ca4b..f5448a381 100644 --- a/patches/0018-nsp-dotnet-root-verify.patch +++ b/patches/0018-nsp-dotnet-root-verify.patch @@ -12,7 +12,7 @@ Issue: LNPANDY/NX2Apple#123 1 file changed, 23 insertions(+) diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index 75ad0a7..c60ea0e 100755 +index 771182e..6923691 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -2971,6 +2971,29 @@ def main(args=None): diff --git a/patches/0031-front-mac-dm-screen.patch b/patches/0031-front-mac-dm-screen.patch index f22e755a3..274c4104d 100644 --- a/patches/0031-front-mac-dm-screen.patch +++ b/patches/0031-front-mac-dm-screen.patch @@ -49,10 +49,10 @@ index b1ccc86..4d70499 100644 | Known gaps | Conditional render is always drawn; [indirect-count multi-draws](reference/rendering.md#indirect-count-multi-draws) can wait for a GPU-written count when the records need conversion, cannot be GPU-masked, or exceed the mask table capacity; geometry shaders do not translate. TBDR optimizations are off by default (they don't understand bindless textures yet). No amiibo. On the Mac, Mario's menu and dialogue text render blank (fine on the iPhone) | diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 9da039d..d54f213 100644 +index 3ca609a..483ef76 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -305,6 +305,7 @@ DM_SAVE path This run's save folder (must be set); the device save is ` +@@ -310,6 +310,7 @@ DM_SAVE path This run's save folder (must be set); the device save is ` DM_SAVED_CHOICES switch `0` ignores the player's saved choices (launcher. in the app's preferences) and leaves them saved; the Mac launcher and the iOS settings screen DM_SAVE_SYNC player Cloud saves: the folder whose `/save.dmsync` the save is synced through, `icloud` for iCloud Drive/nxnative Saves (the Mac); `0` or unset, off. The Mac launcher sets the folder chosen in its settings window's Cloud saves, the iPhone app the one picked in its launcher (dm/host/save_sync.h) DM_SCHEME_NAME switch Set by `build_ipa.sh` itself for xcodegen's `project.yml`: the Xcode scheme name, `DmIOS`, or `<display name> (<title ID>)` for a project prepared with `--prepare-xcode` diff --git a/patches/0032-front-mac-hide-the-pointer-after-a-few-idle-seco.patch b/patches/0032-front-mac-hide-the-pointer-after-a-few-idle-seco.patch index 0c9e6a722..f6e81b236 100644 --- a/patches/0032-front-mac-hide-the-pointer-after-a-few-idle-seco.patch +++ b/patches/0032-front-mac-hide-the-pointer-after-a-few-idle-seco.patch @@ -43,7 +43,7 @@ index ecbf20c..44eaf20 100644 [NSNotificationCenter.defaultCenter addObserverForName:nm object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *n) { GCController *c = n.object; int conn = n.name == GCControllerDidConnectNotification; diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index d54f213..c475c35 100644 +index 483ef76..c51d5c4 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv @@ -22,6 +22,7 @@ DM_CHEATS_WINDOW switch `1` opens the Mac Cheats panel beside the game from the @@ -55,7 +55,7 @@ index d54f213..c475c35 100644 DM_DBG_AU_FRAMES diagnostic `=<frames>` asks the Mac's Core Audio output for that many frames per callback instead of about 5 ms (1024 reproduces the iOS default that caused crackling) DM_DETCLOCK switch `=<step ns>`: from the first present on, the guest clock advances a fixed step per present instead of following the wall clock, and the random source becomes a fixed-seed stream (replay and test runs) diff --git a/docs/reference/frame-rate.md b/docs/reference/frame-rate.md -index 1e60583..79ebc58 100644 +index d644ee7..4cb6675 100644 --- a/docs/reference/frame-rate.md +++ b/docs/reference/frame-rate.md @@ -310,6 +310,14 @@ Frame pacing: the host's frame step, engine frame limiters, present intervals an diff --git a/patches/0037-totk-1.4.2-frame-step-sites.patch b/patches/0037-totk-1.4.2-frame-step-sites.patch index 78fd905be..b2ce0d158 100644 --- a/patches/0037-totk-1.4.2-frame-step-sites.patch +++ b/patches/0037-totk-1.4.2-frame-step-sites.patch @@ -38,7 +38,7 @@ Issue: LNPANDY/NX2Apple#303 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/dm/games/0100F2C0115B6000/README.md b/dm/games/0100F2C0115B6000/README.md -index 2125ea9..5505109 100644 +index d29d7af..10876cc 100644 --- a/dm/games/0100F2C0115B6000/README.md +++ b/dm/games/0100F2C0115B6000/README.md @@ -24,6 +24,13 @@ Update 1.4.3 (v720896) over the base game, SDK 20.5.22. Nintendo EPD's engine (s diff --git a/patches/0046-route-c-game-framework.patch b/patches/0046-route-c-game-framework.patch index 1a4b8cff3..da1ff3f2f 100644 --- a/patches/0046-route-c-game-framework.patch +++ b/patches/0046-route-c-game-framework.patch @@ -121,7 +121,7 @@ index 0000000..663ebe8 +} +#endif diff --git a/dm/ios/Sources/main.m b/dm/ios/Sources/main.m -index c11b457..69cf022 100644 +index 076d4ef..71f5be5 100644 --- a/dm/ios/Sources/main.m +++ b/dm/ios/Sources/main.m @@ -18,6 +18,14 @@ @@ -154,10 +154,10 @@ index c11b457..69cf022 100644 // Environment switch files (for on-device diagnostics: a sideloaded app cannot be given environment variables from outside): one KEY=VALUE per line, # starts a comment; Documents/env.txt takes precedence over dm/env.txt in the bundle (put there by build_ipa.sh's DM_ENV_FILE) NSMutableArray<NSString *> *from_file = [NSMutableArray array], *from_launch = [NSMutableArray array]; for (char **e = environ; *e; e++) { // set at launch (Xcode, devicectl): before the env files, and the launcher keeps them -@@ -282,6 +298,12 @@ static int boot_env(void) { - BOOL per_game = [romfs hasPrefix:gdir]; // messaging nil is NO here: no data means the shared layout +@@ -283,6 +299,12 @@ static int boot_env(void) { if (!set_data_env(romfs, NO)) { if (!romfs) romfs_identity_report(doc); // a slim build says why its romfs.img was not used fprintf(stderr, "boot: no game data yet (Documents/romfs.img, Documents/romfs or <App>/romfs); the launcher asks for it\n"); } + setenv("DM_GUEST_FILE_DIR", NSTemporaryDirectory().fileSystemRepresentation, 0); // DM_GUEST_FILE (hle_svc.c) keeps the guest's memory in files there +#if DM_FRAMEWORK_BUILD + // Route C: the modules, their rewriter tables and the shader map are the framework's. dlopen + dlsym + dladdr hand its + // mach header to the loader here, after the data went to DM_ROMFS and before loading starts (the design's order). diff --git a/patches/0047-route-c-framework-build.patch b/patches/0047-route-c-framework-build.patch index 76ac2d7cf..11e5f88c4 100644 --- a/patches/0047-route-c-framework-build.patch +++ b/patches/0047-route-c-framework-build.patch @@ -282,7 +282,7 @@ index 0000000..ec2c33b +if __name__ == '__main__': + main() diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index c60ea0e..d8455c3 100755 +index 6923691..ea22543 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -2807,6 +2807,7 @@ def parse_args(argv): diff --git a/patches/0049-dm-framework-env-switch-row.patch b/patches/0049-dm-framework-env-switch-row.patch index 669960411..d68519d4f 100644 --- a/patches/0049-dm-framework-env-switch-row.patch +++ b/patches/0049-dm-framework-env-switch-row.patch @@ -16,7 +16,7 @@ Issue: LNPANDY/NX2Apple#144 1 file changed, 8 insertions(+) diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index c475c35..7eda9cf 100644 +index c51d5c4..4b14917 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv @@ -40,9 +40,12 @@ DM_FIXED_TIME switch `=<seconds>`: the user clock always returns that time and t @@ -32,7 +32,7 @@ index c475c35..7eda9cf 100644 DM_FS diagnostic When set, logs file opens, paths not found or not mounted, and a read summary every 64 MiB or 1000 reads DM_FSLOG diagnostic `=<file>` writes one TSV line per ReadFile: seconds since boot, guest path, offset, length and guest buffer address DM_FSLOG_HASH diagnostic With `DM_FSLOG`, adds a column with the FNV-1a 64 hash of the bytes read -@@ -74,6 +77,7 @@ DM_HOME_QUIT player `0` turns off quitting with the controller's Home button (Ma +@@ -78,6 +81,7 @@ DM_HOME_QUIT player `0` turns off quitting with the controller's Home button (Ma DM_HOME_QUIT_CAPTURE test `=<folder>`: `tests/t_home_quit_mac.m` also draws the Home button's quit prompt and hold indicator into PNG files there DM_HOME_QUIT_TEXT switch The texts of the Home button's quit prompt in the player's language, one per line: the hold indicator, the title, the note, the quit and the cancel button; a front end such as NX2Apple sets it ([Starting a game](frontends.md#starting-a-game)), an empty or missing line keeps the English text DM_HTML path Folder of the offline HTML pages (default: `html` beside the RomFS) @@ -40,7 +40,7 @@ index c475c35..7eda9cf 100644 DM_ICON path `=<image>` gives `build_ipa.sh` another app icon (default the `icon.jpg` beside the RomFS) DM_IMPORTS diagnostic When set, logs each import the loader binds to a host function and each forced takeover DM_JIT_CAPTURE path Folder the Mac host writes what a game's `nn::jit` plugin makes to, one `<key>.jit` per request, for `dm/tools/jit_capture.py`; the NSP script sets it -@@ -316,6 +320,10 @@ DM_SF_TRACE diagnostic `=1` logs every request on the game's own HIPC client ses +@@ -321,6 +325,10 @@ DM_SF_TRACE diagnostic `=1` logs every request on the game's own HIPC client ses DM_SHAKE_G switch `=<G>`: on iOS, the acceleration beyond gravity above which a flick of the phone shakes the controller's six-axis sensor (default 1.5) DM_SHARED_FONT path Font file served for every shared-font type except the Nintendo extension (default `$DM_FONT_DIR/standard.ttf`, then macOS's Arial Unicode) DM_SHARED_FONT_EXT path Font file served for shared-font type 5, the Nintendo extension with the button icons (default `$DM_FONT_DIR/nintendo_ext.ttf`, made by `make_ext_font.py`) diff --git a/patches/0051-nsp-shell-framework.patch b/patches/0051-nsp-shell-framework.patch index d940b7d4a..2c36d3ee9 100644 --- a/patches/0051-nsp-shell-framework.patch +++ b/patches/0051-nsp-shell-framework.patch @@ -15,7 +15,7 @@ Issue: LNPANDY/NX2Apple#144 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index d8455c3..59f3df7 100755 +index ea22543..71922cd 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -2808,6 +2808,9 @@ def parse_args(argv): diff --git a/patches/0053-runtime-profile-every-site.patch b/patches/0053-runtime-profile-every-site.patch index 10c5c1409..e1524f40b 100644 --- a/patches/0053-runtime-profile-every-site.patch +++ b/patches/0053-runtime-profile-every-site.patch @@ -21,7 +21,7 @@ Issue: LNPANDY/NX2Apple#144 dm/gfx/nvn/hle_nvn.c | 39 ++++---- dm/gfx/nvn/nvn_capture.h | 2 +- dm/gfx/nvn/nvn_capture.inc | 2 +- - dm/gfx/nvn/nvn_mtl.m | 25 ++--- + dm/gfx/nvn/nvn_mtl.m | 27 +++--- dm/gfx/nvn/nvn_rec.h | 2 +- dm/gfx/tests/Makefile | 8 +- dm/gfx/tests/t_host_stubs.c | 2 + @@ -29,35 +29,35 @@ Issue: LNPANDY/NX2Apple#144 dm/host/hle_cheats.c | 10 +- dm/host/hle_fs.c | 4 +- dm/host/hle_oe.c | 4 +- - dm/host/hle_profile.c | 35 ++++++- + dm/host/hle_profile.c | 39 +++++++- dm/host/hle_profile.h | 13 ++- dm/host/hle_sdk16.c | 17 ++-- - dm/host/hle_svc.c | 4 +- + dm/host/hle_svc.c | 10 +- dm/host/hle_thread.c | 2 +- dm/host/hle_uecvar.c | 4 +- dm/host/main.c | 17 ++-- dm/host/tests/Makefile | 13 ++- dm/host/tests/t_profile_runtime.c | 100 ++++++++++++++++++++ - dm/tools/tests/test_game_profile_runtime.py | 54 +++++++++++ - 22 files changed, 292 insertions(+), 80 deletions(-) + dm/tools/tests/test_game_profile_runtime.py | 55 +++++++++++ + 22 files changed, 301 insertions(+), 84 deletions(-) create mode 100644 dm/host/tests/t_profile_runtime.c create mode 100644 dm/tools/tests/test_game_profile_runtime.py diff --git a/dm/games/game.py b/dm/games/game.py -index a8f472a..b6e546a 100644 +index 978dd69..cb28a9e 100644 --- a/dm/games/game.py +++ b/dm/games/game.py -@@ -293,7 +293,8 @@ if __name__ == '__main__': +@@ -296,7 +296,8 @@ if __name__ == '__main__': ('shared_font', 'SHARED_FONT_TTF'), ('pad_layout', 'PAD_LAYOUT'), ('op_mode', 'OPMODE'), ('perf_mode', 'PERF_BOOST'), ('feedback_snap', 'FEEDBACK_SNAP'), ('tex_flush_upload', 'TEX_FLUSH_UPLOAD'), ('render_cond', 'RENDER_COND'), ('tex_dirty_upload', 'TEX_DIRTY_UPLOAD'), ('tex_dyn_slots', 'TEX_DYN_SLOTS'), ('gpu_timestamps', 'GPU_TS_SEGMENT'), - ('vconv_range', 'VCONV_RANGE'), ('game_args', 'ARGS'), ('game_env', 'ENV')] + ('vconv_range', 'VCONV_RANGE'), ('stdalloc_cap', 'STDALLOC_CAP'), ('tex_bc_cpu', 'TEX_BC_CPU'), ('ue_texture_pool', 'UE_TEXTURE_POOL'), -+ ('enc_thread', 'ENC_THREAD'), ('game_args', 'ARGS'), ('game_env', 'ENV')] ++ ('enc_thread', 'ENC_THREAD'), ('draw_barriers', 'DRAW_BARRIERS_GAME'), ('game_args', 'ARGS'), ('game_env', 'ENV')] def val(macro): # the header's C spelling → the file's plain spelling v = M.get('DM_GAME_' + macro) if v is None: return None -@@ -309,7 +310,9 @@ if __name__ == '__main__': +@@ -312,7 +313,9 @@ if __name__ == '__main__': v = val(macro) if v is not None: L.append(f'{key}\t{v}') api = [val('NVN_API_MAJOR'), val('NVN_API_MINOR')] @@ -69,7 +69,7 @@ index a8f472a..b6e546a 100644 L.append(f'display_version\t{b}\t{d}') text = '\n'.join(L) + '\n' diff --git a/dm/gfx/nvn/hle_nvn.c b/dm/gfx/nvn/hle_nvn.c -index 904fbec..35441ea 100644 +index 277d7a2..7ec797a 100644 --- a/dm/gfx/nvn/hle_nvn.c +++ b/dm/gfx/nvn/hle_nvn.c @@ -192,16 +192,14 @@ __attribute__((weak)) int dm_sdk_ounce(void) { return 0; } @@ -91,12 +91,12 @@ index 904fbec..35441ea 100644 } NVN(nvnDeviceGetInteger, void, const NVNdevice *d, NVNdeviceInfo what, int *out) { (void)d; -@@ -387,16 +385,16 @@ static void ft_add(const tex_t *t) { - // COMPRESSIBLE is a memory-compression hint, not a data format: a game that sets it on every texture it creates (SD GUNDAM G GENERATION CROSS RAYS, - // SDK 9.4.1) still writes plain texels, so those textures must be registered too or the CPU writes that follow stay invisible (its tutorial overlay's +@@ -391,16 +389,16 @@ static void ft_add(const tex_t *t) { // background never appeared). A CPU_NO_ACCESS pool keeps the exclusion: there the compression tags may really be there. -- if (!DM_GAME_TEX_FLUSH_UPLOAD || !t->mem || !t->pool || t->has_layout || t->d.target == NVN_TEXTURE_TARGET_BUFFER || (t->pool->flags & NVN_MEMORY_POOL_FLAGS_CPU_NO_ACCESS) || -+ if (!dm_prof()->tex_flush_upload || !t->mem || !t->pool || t->has_layout || t->d.target == NVN_TEXTURE_TARGET_BUFFER || (t->pool->flags & NVN_MEMORY_POOL_FLAGS_CPU_NO_ACCESS) || + // A sparse pool's addresses are no memory the CPU writes or flushes, and no range nvn_dirty tracks, so they read as dirty at once: every texture in one, a render target too, came out CPU-written at its first + // use in a profile with tex_dirty_upload. What the CPU wrote into the physical pages behind one is seen through the page table instead (tex_from_storage, sparse_look) +- if (!DM_GAME_TEX_FLUSH_UPLOAD || !t->mem || !t->pool || t->pool->vp || t->has_layout || t->d.target == NVN_TEXTURE_TARGET_BUFFER || (t->pool->flags & NVN_MEMORY_POOL_FLAGS_CPU_NO_ACCESS) || ++ if (!dm_prof()->tex_flush_upload || !t->mem || !t->pool || t->pool->vp || t->has_layout || t->d.target == NVN_TEXTURE_TARGET_BUFFER || (t->pool->flags & NVN_MEMORY_POOL_FLAGS_CPU_NO_ACCESS) || (t->d.flags & (NVN_TEXTURE_FLAGS_LINEAR | NVN_TEXTURE_FLAGS_LINEAR_RENDER_TARGET))) return; pthread_mutex_lock(&g_ft_mu); if (g_nft == g_cft && !(g_ft = realloc(g_ft, (g_cft = g_cft ? 2 * g_cft : 1024) * sizeof *g_ft))) die("flush_tex allocation failed"); @@ -111,7 +111,7 @@ index 904fbec..35441ea 100644 pthread_mutex_lock(&g_ft_mu); for (size_t i = 0; i < g_nft; i++) if (g_ft[i].t.serial == serial) { g_ft[i] = g_ft[--g_nft]; break; } pthread_mutex_unlock(&g_ft_mu); -@@ -405,7 +403,7 @@ static void ft_del(uint64_t serial) { +@@ -409,7 +407,7 @@ static void ft_del(uint64_t serial) { // natively; its 121-layer ASTC terrain material arrays, 42 MB each). They stayed GPU-only textures with content 0, which ASTC decodes as the error colour, and the cave came out magenta. // ft_add write-protects a registered texture's pages, and on its first resolution (tex_mtl, submit thread) a write since then makes it CPU-written like a flushed one int nvn_ft_written(const tex_t *t) { @@ -120,7 +120,7 @@ index 904fbec..35441ea 100644 int r = 0; pthread_mutex_lock(&g_ft_mu); for (size_t i = 0; i < g_nft; i++) if (g_ft[i].t.serial == t->serial) { if (nvn_dirty_since_span(g_ft[i].span, g_ft[i].mem, g_ft[i].size, g_ft[i].stamp)) { r = 1; g_ft[i] = g_ft[--g_nft]; } -@@ -415,7 +413,7 @@ int nvn_ft_written(const tex_t *t) { +@@ -419,7 +417,7 @@ int nvn_ft_written(const tex_t *t) { return r; } int nvn_ft_tracked(uint64_t serial) { // registered and not resolved yet: its storage holds whatever the CPU has written (nvn_tex_mirror_b2t / nvn_tex_mirror_t2t) @@ -129,7 +129,7 @@ index 904fbec..35441ea 100644 int r = 0; pthread_mutex_lock(&g_ft_mu); for (size_t i = 0; i < g_nft; i++) if (g_ft[i].t.serial == serial) { r = 1; break; } pthread_mutex_unlock(&g_ft_mu); -@@ -430,7 +428,7 @@ int nvn_ft_tracked(uint64_t serial) { // registered and not resolved yet: its +@@ -434,7 +432,7 @@ int nvn_ft_tracked(uint64_t serial) { // registered and not resolved yet: its // Checking every texture at every submission cost TOTK 0.4 ms of submit CPU per frame on the Mac; this way a texture is picked up a few submissions later at most enum { FT_POLL_BYTES = 128 << 20 }; void nvn_ft_poll(void) { @@ -138,7 +138,7 @@ index 904fbec..35441ea 100644 static uint64_t gen; static size_t cur, left; static tex_t *hit; static size_t chit; size_t nhit = 0; uint64_t now = nvn_dirty_now(), scanned = 0; -@@ -448,7 +446,7 @@ void nvn_ft_poll(void) { +@@ -452,7 +450,7 @@ void nvn_ft_poll(void) { for (size_t i = 0; i < nhit; i++) if (!mtl_tex_gpu_owned(hit[i].serial)) mtl_tex_cpu_written(&hit[i]); // outside g_ft_mu: the ownership check takes the replay's serial table lock } static void ft_flush(const uint8_t *a, uint64_t n) { @@ -147,7 +147,7 @@ index 904fbec..35441ea 100644 pthread_mutex_lock(&g_ft_mu); for (size_t i = 0; i < g_nft;) if (g_ft[i].mem < a + n && a < g_ft[i].mem + g_ft[i].size) { mtl_tex_cpu_written(&g_ft[i].t); g_ft[i] = g_ft[--g_nft]; } else i++; -@@ -685,11 +683,12 @@ static int keys_hash(const NVNshaderData *sd, int mapped, uint8_t out[8]) { +@@ -703,11 +701,12 @@ static int keys_hash(const NVNshaderData *sd, int mapped, uint8_t out[8]) { uint8_t md[CC_SHA256_DIGEST_LENGTH]; CC_SHA256_CTX hc; CC_SHA256_Init(&hc); // Profile key shader_key_skip: bytes of the control section hashed as zeros (shader_split.py key_ctl). Batman: Arkham Knight's engine shares one control section among shaders whose // sections differ only in GLSLC's compile information (+0x778..+0x791, +0x7d0..+0x7e7): it hands a program the section of an earlier shader with the same rest, so the bytes there name another shader @@ -162,7 +162,7 @@ index 904fbec..35441ea 100644 CC_SHA256_Update(&hc, ctl, nctl); } else CC_SHA256_Update(&hc, sd->control, nctl); if (code) CC_SHA256_Update(&hc, (const void *)(uintptr_t)sd->data, ncode); -@@ -1763,7 +1762,7 @@ static _Atomic int g_high_cap = 60; +@@ -1787,7 +1786,7 @@ static _Atomic int g_high_cap = 60; static int fps_high(void) { int v = __atomic_load_n(&g_fps_high, __ATOMIC_RELAXED); if (v < 0) { @@ -171,7 +171,7 @@ index 904fbec..35441ea 100644 v = t == 120 && DM_GAME_FPS_MAX >= 120 && !dm_fps_state && dm_fps_lock_engine(); if (t == 120 && DM_GAME_FPS_MAX >= 120 && !v) LOG("[RED] [nvn] 120 fps: this main has %s, so it runs at 60", dm_fps_state ? "a frame step in whole vsyncs" : "no engine frame limit the host writes"); __atomic_store_n(&g_fps_high, v, __ATOMIC_RELAXED); -@@ -1799,7 +1798,7 @@ static int g_present_cap = -1; +@@ -1823,7 +1822,7 @@ static int g_present_cap = -1; static int fps_present_cap(void) { if (dm_fps_state || fps_high()) return fps_step_cap(); if (g_present_cap < 0) { @@ -180,7 +180,7 @@ index 904fbec..35441ea 100644 g_present_cap=t==30||t==60?t:t==120?60:0; // 120 here: not this game's (fps_high), so it runs at 60 if (g_present_cap && !dm_fps_lock_engine()) { LOG("[nvn] present cap %d fps (%s) not applied: this main has neither a frame step nor an engine frame limit the host writes, so the game's own present interval stays", g_present_cap, e ? "DM_FPS_TARGET" : "game profile fps_target"); -@@ -1824,7 +1823,7 @@ static void present_cfg(const win_t *o) { +@@ -1848,7 +1847,7 @@ static void present_cfg(const win_t *o) { // with a fixed hard floor of 60 fps; the logic stays at 25 ticks, and the client's frame-counted UI logic is unaffected - // with the same save and the same input, the UI message streams of the two modes match message by message. Proof (before character select): r9 game / display 60 ×3 / display 120 ×2, 6 groups in total, uicmp consistent // (the save indicator is exempted as a timing race), and the timing differences of key messages are within game mode's own noise band; proof after entering a run is still missing, pending action script coverage @@ -189,7 +189,7 @@ index 904fbec..35441ea 100644 LOG("[nvn] present tick %d Hz, interval mode %s (%s; present interval set by the game %d; %s)", g_hz, g_display_mode ? "display (one frame per tick, a cut)" : "game (original semantics)", m ? "DM_PRESENT_INTERVAL" : "game profile present_interval", o->interval, scr > 0 ? "window" : "no window"); } -@@ -1889,7 +1888,7 @@ static int fstep_cpu_slowed(uint64_t presents) { +@@ -1913,7 +1912,7 @@ static int fstep_cpu_slowed(uint64_t presents) { static int fstep_hold(void) { static int h = -1; if (h < 0) { @@ -198,7 +198,7 @@ index 904fbec..35441ea 100644 if (!h) LOG("[nvn] frame step: frames are not held longer than the cap's vsyncs (%s)", e ? "DM_FPS_HOLD" : "game profile fps_hold"); } return h; -@@ -2211,7 +2210,7 @@ static uint8_t *vpool_reserve(pool_t *o) { // → the reserved range, or NULL +@@ -2236,7 +2235,7 @@ static uint8_t *vpool_reserve(pool_t *o) { // → the reserved range, or NULL pthread_mutex_lock(&g_vp_mu); if (g_nvp == g_cvp && !(g_vp = realloc(g_vp, (g_cvp = g_cvp ? 2 * g_cvp : 64) * sizeof *g_vp))) die("sparse pool table allocation failed"); g_vp[g_nvp++] = v; o->vp = v; @@ -207,7 +207,7 @@ index 904fbec..35441ea 100644 pthread_mutex_unlock(&g_vp_mu); if (o->vmode == 2) LOG("[nvn] virtual pool %p (%#llx bytes, flags %#x): sparse, own address range %p+%#llx, physical pages are remapped into it", (const void *)o, (unsigned long long)o->size, o->flags, a, (unsigned long long)len); return a; -@@ -2838,11 +2837,11 @@ NVN(nvnCommandBufferClearBuffer, void, NVNcommandBuffer *c, NVNbufferAddress a, +@@ -2866,11 +2865,11 @@ NVN(nvnCommandBufferClearBuffer, void, NVNcommandBuffer *c, NVNbufferAddress a, // State the replay layer does not have yet: logged once (conditional rendering is treated as "always draw") // Render enable (profile render_cond, see OP_RENDER_COND in nvn_mtl.m): recorded and evaluated at replay; without the key everything draws, as before NVN(nvnCommandBufferSetRenderEnable, void, NVNcommandBuffer *c, uint32_t en) { @@ -248,10 +248,10 @@ index be405db..bb0b536 100644 for (char **e = *_NSGetEnviron(); *e; e++) if (!strncmp(*e, "DM_", 3) && strncmp(*e, "DM_NVN_CAPTURE=", 15)) KV("env\t%s\n", *e); #undef KV diff --git a/dm/gfx/nvn/nvn_mtl.m b/dm/gfx/nvn/nvn_mtl.m -index 3a28360..5d7f618 100644 +index db76d30..3ed684d 100644 --- a/dm/gfx/nvn/nvn_mtl.m +++ b/dm/gfx/nvn/nvn_mtl.m -@@ -430,7 +430,7 @@ void mtl_init(void) { +@@ -459,7 +459,7 @@ void mtl_init(void) { if (!g_dev) MDIE("no Metal device"); if (!g_dev.hasUnifiedMemory) MDIE("needs a unified-memory device (zero-copy memory pools)"); g_bc_gpu = [g_dev supportsBCTextureCompression]; @@ -260,16 +260,25 @@ index 3a28360..5d7f618 100644 if (g_bc_cpu) MLOG("BC textures: this GPU has no BC support, decoding them on the CPU (profile tex_bc_cpu)"); g_q = [g_dev newCommandQueue]; // A device without Metal 4 (the paravirtualized GPU of a virtual Mac, such as GitHub's macos-26 runner: no residency set, no MTL4CommandQueue) cannot run the NVN layer. The GL and -@@ -1513,7 +1513,7 @@ static void post(post_op o) { +@@ -1029,7 +1029,7 @@ static inline void m4_end(id e) { + // indirect arguments), or when anything other than a draw pass ran in between (compute, copies and uploads keep their full barriers). Only vertex-stage reads of the previous pass's results without a barrier change order + // DM_NVN_BARRIER=ab... alternates game and full with the DM_AB phases (measurement) + static int sw_mode(const char *name, int dflt); static inline int sw_on(int m); +-static int barrier_game(void) { static int v = -1; if (v < 0) { const char *e = getenv("DM_NVN_BARRIER"); v = e && !strncmp(e, "ab", 2) ? sw_mode("DM_NVN_BARRIER", 0) : e ? !strcmp(e, "game") : DM_GAME_DRAW_BARRIERS_GAME; if (v == 1) MLOG("DM_NVN_BARRIER=game: draw passes are ordered by the game's own barriers (fragment work still has barriers between passes; vertex work may overlap the previous pass's fragments)"); } return sw_on(v); } ++static int barrier_game(void) { static int v = -1; if (v < 0) { const char *e = getenv("DM_NVN_BARRIER"); v = e && !strncmp(e, "ab", 2) ? sw_mode("DM_NVN_BARRIER", 0) : e ? !strcmp(e, "game") : dm_prof()->draw_barriers_game; if (v == 1) MLOG("DM_NVN_BARRIER=game: draw passes are ordered by the game's own barriers (fragment work still has barriers between passes; vertex work may overlap the previous pass's fragments)"); } return sw_on(v); } + void mtl_barrier_stats(uint64_t *skip, uint64_t *full) { *skip = g_bar_skip; *full = g_bar_full; } + uint64_t mtl_cut_count(void); + // DM_NVN_GPUPROF=first frame[-last frame] (diagnostic): GPU time of every encoder of the main command buffers in those frames. A relaxed timestamp is written into a counter heap when an +@@ -1574,7 +1574,7 @@ static void post(post_op o) { // so a game that waits for the fence reads one submission's reports together; a segment whose feedback has not arrived by then takes the last segment's times #define SEG_CLK_N 1024 - struct seg_clk { _Atomic int refs; _Atomic uint64_t t[SEG_CLK_N][2]; }; // per submission (g_sclk, allocated at its first cut): guest ns of each cut segment's GPU start / end (0 = no feedback yet) + struct seg_clk { _Atomic int refs; _Atomic uint64_t t[SEG_CLK_N][2]; uint32_t nb; uint16_t b[SEG_CLK_N + 1]; }; // per submission (g_sclk, allocated at its first cut): guest ns of each cut segment's GPU start / end (0 = no feedback yet); b: with ts_on, the heap slots of the segment boundaries, segment k from b[k] to b[k + 1] -static int seg_ts(void) { static int v = -1; if (v < 0) { const char *e = getenv("DM_NVN_GPU_TS"); v = e && *e ? !strcmp(e, "segment") : DM_GAME_GPU_TS_SEGMENT; MLOG("counter timestamps: %s (profile key gpu_timestamps; DM_NVN_GPU_TS=segment / submission)", v ? "GPU start / end time of their segment" : "one time for the whole submission"); } return v; } +static int seg_ts(void) { static int v = -1; if (v < 0) { const char *e = getenv("DM_NVN_GPU_TS"); v = e && *e ? !strcmp(e, "segment") : dm_prof()->gpu_ts_segment; MLOG("counter timestamps: %s (profile key gpu_timestamps; DM_NVN_GPU_TS=segment / submission)", v ? "GPU start / end time of their segment" : "one time for the whole submission"); } return v; } static void seg_clk_put(seg_clk *c) { if (c && atomic_fetch_sub(&c->refs, 1) == 1) free(c); } static uint64_t gpu_s_guest_ns(double s) { // a feedback GPU time (seconds on the mach absolute time base) on the guest clock; 0 when unknown static mach_timebase_info_data_t tb; if (!tb.denom) mach_timebase_info(&tb); -@@ -1701,7 +1701,7 @@ static void post_run(post_rec *r, uint64_t c0, uint64_t c1) { +@@ -1773,7 +1773,7 @@ static void post_run(post_rec *r, uint64_t c0, uint64_t c1) { free(l); uint64_t rec[2] = {sum, gpu_ticks()}; memcpy((void *)ops[i].addr, rec, sizeof rec); } else if (ops[i].kind == 4) mirror_rb_land(ops[i].vis); @@ -278,16 +287,16 @@ index 3a28360..5d7f618 100644 } free(ops); seg_clk_put(sc); r->ops = NULL; r->sc = NULL; atomic_store(&r->claimed, 1); -@@ -3338,7 +3338,7 @@ static void sparse_view(NvPkg *pk, const tex_t *t) { +@@ -3489,7 +3489,7 @@ static void sparse_view(NvPkg *pk, const tex_t *t) { } } id have = tex_get(t->serial); - if (!packaged && !have && DM_GAME_TEX_DIRTY_UPLOAD && t->mem && on_sub_thread()) { int nvn_ft_written(const tex_t *); packaged = nvn_ft_written(t); } // first use of a texture the CPU wrote since init without a flush (hle_nvn.c) + if (!packaged && !have && dm_prof()->tex_dirty_upload && t->mem && on_sub_thread()) { int nvn_ft_written(const tex_t *); packaged = nvn_ft_written(t); } // first use of a texture the CPU wrote since init without a flush (hle_nvn.c) if (!packaged && !have && on_sub_thread() && tex_cpu_content(t)) { mtl_tex_cpu_written(t); packaged = 1; } // storage the CPU wrote before its first use (tex_cpu_content) - if (packaged) { - NvPkg *pk = [have isKindOfClass:[NvPkg class]] ? have : nil; // resolved as GPU-only before the CPU wrote it: dropped and rebuilt as packaged -@@ -4431,7 +4431,7 @@ static int clear_rect_zero(int i) { + if (!packaged && !have && on_sub_thread() && linear_cpu_only(t)) { // a linear texture no view on its buffer can take (iOS wants 64-byte rows; Axiom Verge's 8x8 RGBA8 linear textures have 32): uploaded, and again after CPU writes + uint32_t pf; void *mb; uint64_t mo; size_t row; +@@ -4621,7 +4621,7 @@ static int clear_rect_zero(int i) { static void shmap_read(void) { if (g_shmap) return; const char *e = getenv("DM_NVN_SHMAP"); @@ -296,7 +305,7 @@ index 3a28360..5d7f618 100644 NSString *txt = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; if (!txt) MDIE("cannot read the shader map %s (DM_NVN_SHMAP can name another)", path.UTF8String); g_shmap = [NSMutableDictionary dictionary]; -@@ -4458,7 +4458,7 @@ static void shmap_read(void) { +@@ -4648,7 +4648,7 @@ static void shmap_read(void) { if (c.count == 2 && [c[0] isEqual:@"game"]) st_game = c[1]; else if (c.count == 2 && [c[0] isEqual:@"inputs"]) st_inputs = c[1]; } if (!st_inputs) MLOG("[RED] shader library %s has no stamp (inputs.tsv): no full dmsh_build.py run made it as it is, so c1_full.tsv and cb_extent.tsv are not taken", g_msl_dir.UTF8String); @@ -305,7 +314,7 @@ index 3a28360..5d7f618 100644 else MLOG("shader library stamp: game %s, inputs %s", st_game.UTF8String, st_inputs.UTF8String); NSString *cp = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"c1_full.tsv"]; // Same directory as the mapping table (Mac: dm/games/<id>; iOS: dm/ inside the App, copied along by build_ipa.sh) NSString *ct = cp ? [NSString stringWithContentsOfFile:cp encoding:NSUTF8StringEncoding error:nil] : nil; -@@ -4722,7 +4722,7 @@ @implementation FnDev @end +@@ -4918,7 +4918,7 @@ @implementation FnDev @end if (stage == 0 && [j[@"layer_from"] isKindOfClass:[NSDictionary class]]) { o->lay_loc = [j[@"layer_from"][@"loc"] intValue]; o->lay_comp = [[j[@"layer_from"][@"comp"] description] UTF8String][0]; } char err[160]; if (nvn_refl_from_json(j, &o->rf, err, sizeof err)) { MLOG("[RED] shader %s reflection conversion failed: %s", pc.UTF8String, err); return nil; } @@ -314,7 +323,7 @@ index 3a28360..5d7f618 100644 // (rf.c1 is freed by dealloc when o goes: freeing it here too was a double free, which aborted FINAL FANTASY RESONANCE at its first such shader) MLOG("[RED] shader %s reflection conversion failed: a texture handle the translator could not trace to one slot (profile key tex_dyn_slots off)", pc.UTF8String); return nil; } if (rt_range_on()) o->range_ro=rt_range_bindings_ro(j); -@@ -6145,7 +6145,7 @@ static void vconv_fill(int f, uint64_t src, uint32_t stride, uint64_t nv, float +@@ -6356,7 +6356,7 @@ static void vconv_fill(int f, uint64_t src, uint32_t stride, uint64_t nv, float typedef struct { uint32_t mn, mx; uint64_t stamp, seen, lo, n; const void *span; int gpuw; } ir_ent; // gpuw: a GPU write was queued on the indices: never trusted again static nvn_hmap g_ir = {.klen = sizeof(ir_key)}; static uint64_t g_vc_rgn[VC_RGN / 64], g_vc_stat, g_vc_conv, g_vc_whole, g_vc_checks, g_vc_bad; // g_vc_rgn: 1 MB regions holding a kept index range (hashed); g_vc_conv / g_vc_whole: vertices converted / in the bound ranges @@ -323,7 +332,7 @@ index 3a28360..5d7f618 100644 static int vc_check(void) { static int v = -1; if (v < 0) { const char *e = getenv("DM_NVN_VCONV_CHECK"); v = e && *e && atoi(e); } return v; } static inline uint32_t vc_rgn(uint64_t r) { return (uint32_t)((r * 0x9E3779B97F4A7C15ull) >> 48) & (VC_RGN - 1); } static void vc_rgn_mark(uint64_t lo, uint64_t n) { for (uint64_t r = lo >> 20; r <= (lo + n - 1) >> 20; r++) { uint32_t b = vc_rgn(r); g_vc_rgn[b >> 6] |= 1ull << (b & 63); } } -@@ -6618,7 +6618,7 @@ static int pso4_on(void) { +@@ -6827,7 +6827,7 @@ static int pso4_on(void) { if (v < 0) { const char *e = getenv("DM_PSO_MTL4"); #if TARGET_OS_IPHONE @@ -332,16 +341,16 @@ index 3a28360..5d7f618 100644 if (!v) MLOG("render pipelines through the MTLDevice (iOS: DM_PSO_MTL4=1 or the profile key pso_mtl4 on turns on the Metal 4 bases, the pipeline archive and the hybrid pipelines)"); #else v = !(e && !strcmp(e, "0")); -@@ -11487,7 +11487,7 @@ void mtl_read_texels(const tex_t *t, unsigned level, unsigned layer0, const NVNc - // textures from the CPU, reads them back with ReadTexels on its own thread before any draw uses them, and copies the blocks into its 5120x5120 roughness atlas. Read from the - // never-uploaded host texture they were zeros, the atlas stayed empty, and the opening cave's stone blocks got roughness 0 and no torch light - int nvn_ft_written(const tex_t *); +@@ -11997,7 +11997,7 @@ void mtl_read_texels(const tex_t *t, unsigned level, unsigned layer0, const NVNc + for (size_t y = 0; y < hb; y++) memcpy(dst + y * row, (const uint8_t *)t->mem + (y0 + y) * srow + x0, wb); + return; + } - if (tex_from_storage(t) || (DM_GAME_TEX_DIRTY_UPLOAD && t->mem && nvn_ft_written(t))) { + if (tex_from_storage(t) || (dm_prof()->tex_dirty_upload && t->mem && nvn_ft_written(t))) { // A sparse pool's texture is read through a copy gathered from its page table, unmapped pages as 0, as pkg_upload_ uploads it: where nothing is mapped its address range is // inaccessible (reading the storage directly stopped the host with SIGBUS) const uint8_t *mem = t->mem; uint8_t *part = NULL; -@@ -12295,7 +12295,7 @@ void mtl_exec(const uint8_t *p) { +@@ -12821,7 +12821,7 @@ void mtl_exec(const uint8_t *p) { PD("Y signal_event cmd=%p dst=%p mode=%u value=%u", p, val, m, v); mtl_sub_event(val, m, v); break; } case OP_REPORT_COUNTER: { uint64_t t, ad; memcpy(&t, a, 8); memcpy(&ad, (const uint8_t *)a + 8, 8); g_cnt_report[t & 31]++; PD("Q report type=%llu cmd=%p dst=%#llx", (unsigned long long)t, p, (unsigned long long)ad); @@ -350,7 +359,7 @@ index 3a28360..5d7f618 100644 if (t == NVN_COUNTER_TYPE_SAMPLES_PASSED) { post((post_op){3, NULL, 0, 0, ad, (uint32_t)t, vis_snapshot()}); g_vis_cur = -1; } else mtl_sub_counter(ad, (uint32_t)t); break; } -@@ -13528,9 +13528,10 @@ uint64_t mtl_present_ext(uint64_t serial, double period, void *ev, uint64_t ev_v +@@ -14066,9 +14066,10 @@ uint64_t mtl_present_ext(uint64_t serial, double period, void *ev, uint64_t ev_v int mtl_snap_on(void) { static int v = -1; if (v >= 0) return v; const char *e = getenv("DM_PRESENT_SNAP"); @@ -376,7 +385,7 @@ index 3a3f5e0..10032d6 100644 if (g_rec) MLOG("Metal encoding runs on its own thread (DM_NVN_ENC_THREAD=0 turns it off)"); return g_rec; diff --git a/dm/gfx/tests/Makefile b/dm/gfx/tests/Makefile -index 5288b37..91a49ea 100644 +index 8ac4d86..95d583e 100644 --- a/dm/gfx/tests/Makefile +++ b/dm/gfx/tests/Makefile @@ -47,10 +47,10 @@ t_transform_feedback: t_transform_feedback.m $(MTL_T_DEP) @@ -395,12 +404,12 @@ index 5288b37..91a49ea 100644 clang $(CFLAGS) -o $@ t_dirty_slots.c ../../host/crash.c ../nvn/nvn_dirty.c t_dirty_race: t_dirty_race.c ../nvn/nvn_dirty.c ../../host/crash.c ../nvn/nvn_dirty.h $(GAME_H) diff --git a/dm/gfx/tests/t_host_stubs.c b/dm/gfx/tests/t_host_stubs.c -index 8f9b3f8..24d3c63 100644 +index 1b7f85f..43c6fba 100644 --- a/dm/gfx/tests/t_host_stubs.c +++ b/dm/gfx/tests/t_host_stubs.c -@@ -4,3 +4,5 @@ __attribute__((weak)) uint64_t g_t0_ns; // process start time (host main.c), r - __attribute__((weak)) _Atomic uint64_t dm_present_ns; // when the last present was made, for DM_HITCH_DUMP (hle_thread.c) +@@ -5,3 +5,5 @@ __attribute__((weak)) _Atomic uint64_t dm_present_ns; // when the last present __attribute__((weak)) int dm_guest_as_contains(uintptr_t address) { (void)address; return 0; } // no guest address space (hle_bigstack.c) + int g_shape_w, g_shape_h; void front_picture_shape(int w, int h) { g_shape_w = w; g_shape_h = h; } // the shape the present reports to the front end (t_present_crop reads it) __attribute__((weak)) void dm_vi_layer_crop(int c[4]) { c[0] = c[1] = c[2] = c[3] = 0; } // no vi layer crop (hle_vi.c), read by hle_nvn's present +// The runtime profile the NVN layer reads (dm_profile in hle_profile.c): no DM_PROFILE in a test, so these are the compiled game_profile.h values +#include "../../host/hle_profile.c" @@ -445,7 +454,7 @@ index 44eaf20..bf0a91e 100644 g_cheats_menu = [DmCheatsMenuDelegate new]; NSMenu *cm = [[NSMenu alloc] initWithTitle:@"Cheats"]; diff --git a/dm/host/hle_cheats.c b/dm/host/hle_cheats.c -index 3039303..fac8693 100644 +index 41ddc08..b728052 100644 --- a/dm/host/hle_cheats.c +++ b/dm/host/hle_cheats.c @@ -129,7 +129,7 @@ static char g_dir[1024]; // cheats folder (DM_CHEATS or <DM_ROOT>/cheats), @@ -457,7 +466,7 @@ index 3039303..fac8693 100644 static char g_others[OTHER_MAX][17]; static int g_nothers; // builds on disk that are not this game's build static char g_other_ver[OTHER_MAX][24]; // their display versions, from their meta files ("" unknown) static char g_version[24]; // this build's display version (the profile's build rows, else the meta file), "" unknown -@@ -1733,11 +1733,9 @@ static void report_build(void) { +@@ -1731,11 +1731,9 @@ static void report_build(void) { // This build's display version from the profile's build rows (by the whole main build ID), as GetDisplayVersion answers it static void profile_version(void) { @@ -471,7 +480,7 @@ index 3039303..fac8693 100644 } // The anchored sources (*.cheats.toml) in the folder, for the panel's "compile them for this build" hint -@@ -1855,7 +1853,7 @@ void dm_cheats_init(void) { +@@ -1853,7 +1851,7 @@ void dm_cheats_init(void) { e = getenv("DM_CHEATS"); if (e && *e) snprintf(g_dir, sizeof g_dir, "%s", e); else { e = getenv("DM_ROOT"); if (e && *e) snprintf(g_dir, sizeof g_dir, "%s/cheats", e); } @@ -525,10 +534,10 @@ index a079a02..14d284c 100644 // the default display resolution is the TV's (1080p docked) or the panel's (720p handheld) and only changes with the operation mode // Taiko Risshiden V DX (SDK 12.3.3) turns it on in nnMain (main+0x2e1e4c): the setter goes through the same apm session (null read at sdk+0x362b8). The host does not scale clocks; diff --git a/dm/host/hle_profile.c b/dm/host/hle_profile.c -index 7543e08..ba22ff7 100644 +index 7543e08..dd7ef64 100644 --- a/dm/host/hle_profile.c +++ b/dm/host/hle_profile.c -@@ -103,6 +103,22 @@ static uint64_t num(const char *v, uint64_t dflt) { +@@ -103,6 +103,25 @@ static uint64_t num(const char *v, uint64_t dflt) { #ifndef DM_GAME_VCONV_RANGE #define DM_GAME_VCONV_RANGE 0 #endif @@ -547,20 +556,23 @@ index 7543e08..ba22ff7 100644 +#endif +#ifndef DM_GAME_ENC_THREAD +#define DM_GAME_ENC_THREAD 0 ++#endif ++#ifndef DM_GAME_DRAW_BARRIERS_GAME ++#define DM_GAME_DRAW_BARRIERS_GAME 0 +#endif #ifndef DM_GAME_ARGS #define DM_GAME_ARGS "" #endif -@@ -142,6 +158,8 @@ static void defaults(void) { +@@ -142,6 +161,8 @@ static void defaults(void) { #else P.nvn_api_major = 0; P.nvn_api_minor = 0; #endif + P.stdalloc_cap = DM_GAME_STDALLOC_CAP; P.shader_skip_off = DM_GAME_SHADER_KEY_SKIP_OFF; P.shader_skip_len = DM_GAME_SHADER_KEY_SKIP_LEN; -+ P.tex_bc_cpu = DM_GAME_TEX_BC_CPU; P.ue_texture_pool = DM_GAME_UE_TEXTURE_POOL; P.enc_thread = DM_GAME_ENC_THREAD; ++ P.tex_bc_cpu = DM_GAME_TEX_BC_CPU; P.ue_texture_pool = DM_GAME_UE_TEXTURE_POOL; P.enc_thread = DM_GAME_ENC_THREAD; P.draw_barriers_game = DM_GAME_DRAW_BARRIERS_GAME; copy_str(P.args, sizeof P.args, DM_GAME_ARGS); copy_str(P.genv, sizeof P.genv, DM_GAME_ENV); copy_str(P.source, sizeof P.source, "compiled game_profile.h"); -@@ -172,6 +190,8 @@ static void set_kv(const char *k, const char *v) { +@@ -172,6 +193,8 @@ static void set_kv(const char *k, const char *v) { else if (!strcmp(k, "sys_resource")) P.sys_resource = num(v, P.sys_resource); else if (!strcmp(k, "mem_map_limit")) P.mem_map_limit = truthy(v); else if (!strcmp(k, "display_version")) { // build<TAB>version; one row per build (game.tsv's build rows give the same) @@ -569,7 +581,7 @@ index 7543e08..ba22ff7 100644 const char *t = strchr(v, '\t'); if (t && P.nver < DM_PROF_MAX_VER) { size_t n = (size_t)(t - v); if (n >= sizeof P.ver[0].build) n = sizeof P.ver[0].build - 1; memcpy(P.ver[P.nver].build, v, n); P.ver[P.nver].build[n] = 0; -@@ -188,7 +208,7 @@ static void set_kv(const char *k, const char *v) { +@@ -188,7 +211,7 @@ static void set_kv(const char *k, const char *v) { else if (!strcmp(k, "fps_hold")) P.fps_hold = truthy(v); else if (!strcmp(k, "present_snap")) P.present_snap = truthy(v); else if (!strcmp(k, "pso_mtl4")) P.pso_mtl4 = truthy(v); @@ -578,7 +590,7 @@ index 7543e08..ba22ff7 100644 else if (!strcmp(k, "pad_layout")) P.pad_layout = !strcmp(v, "full") || !strcmp(v, "1") ? 1 : !strcmp(v, "touch") || !strcmp(v, "2") ? 2 : 0; else if (!strcmp(k, "op_mode")) P.opmode = !strcmp(v, "console") ? 1 : !strcmp(v, "handheld") ? 0 : truthy(v); else if (!strcmp(k, "perf_mode")) P.perf_boost = !strcmp(v, "boost") ? 1 : !strcmp(v, "follow") ? 0 : truthy(v); -@@ -200,6 +220,15 @@ static void set_kv(const char *k, const char *v) { +@@ -200,6 +223,16 @@ static void set_kv(const char *k, const char *v) { else if (!strcmp(k, "gpu_timestamps")) P.gpu_ts_segment = !strcmp(v, "segment") || !strcmp(v, "1"); else if (!strcmp(k, "vconv_range")) P.vconv_range = truthy(v); else if (!strcmp(k, "device_api")) { char *e; P.nvn_api_major = (int)strtol(v, &e, 10); if (e && *e == '.') P.nvn_api_minor = atoi(e + 1); } @@ -591,27 +603,28 @@ index 7543e08..ba22ff7 100644 + else if (!strcmp(k, "tex_bc_cpu")) P.tex_bc_cpu = truthy(v); + else if (!strcmp(k, "ue_texture_pool")) P.ue_texture_pool = (int)num(v, (uint64_t)P.ue_texture_pool); + else if (!strcmp(k, "enc_thread")) P.enc_thread = truthy(v); ++ else if (!strcmp(k, "draw_barriers")) P.draw_barriers_game = !strcmp(v, "game") || !strcmp(v, "1"); else if (!strcmp(k, "game_args")) copy_str(P.args, sizeof P.args, v); else if (!strcmp(k, "game_env")) copy_str(P.genv, sizeof P.genv, v); // any other key (ipa, zip_store, shader_*, build, package, ...) is not a host constant: ignored -@@ -236,7 +265,7 @@ static void read_env(void) { +@@ -236,7 +269,7 @@ static void read_env(void) { static const char *keys[] = {"TITLE_ID","NAME","ROMFS_MARKER","PROGRAM_ID","MODULES","MEM_RESERVE","MEM_ALIAS","MEM_STACK","APP_POOL", "MEM_USED","SYS_RESOURCE","MEM_MAP_LIMIT","PRESENT_INTERVAL","CORE_PREEMPT","ALLOC_BIG_ALIGN","FPS_TARGET","FPS_HOLD","PRESENT_SNAP", "PSO_MTL4","SHARED_FONT","PAD_LAYOUT","OP_MODE","PERF_MODE","FEEDBACK_SNAP","TEX_FLUSH_UPLOAD","RENDER_COND","TEX_DIRTY_UPLOAD", - "TEX_DYN_SLOTS","GPU_TIMESTAMPS","VCONV_RANGE","DEVICE_API","GAME_ARGS","GAME_ENV", NULL}; -+ "TEX_DYN_SLOTS","GPU_TIMESTAMPS","VCONV_RANGE","DEVICE_API","STDALLOC_CAP","SHADER_KEY_SKIP","TEX_BC_CPU","UE_TEXTURE_POOL","ENC_THREAD","GAME_ARGS","GAME_ENV", NULL}; ++ "TEX_DYN_SLOTS","GPU_TIMESTAMPS","VCONV_RANGE","DEVICE_API","STDALLOC_CAP","SHADER_KEY_SKIP","TEX_BC_CPU","UE_TEXTURE_POOL","ENC_THREAD","DRAW_BARRIERS","GAME_ARGS","GAME_ENV", NULL}; char k[64], name[96]; for (int i = 0; keys[i]; i++) { snprintf(k, sizeof k, "%s", keys[i]); -@@ -278,4 +307,6 @@ void dm_profile_log(void) { +@@ -278,4 +311,6 @@ void dm_profile_log(void) { LOG(" graphics flush %d dirty %d rcond %d dynslots %d gputs %d vconv %d nvn_api %d.%d versions %d", p->tex_flush_upload, p->tex_dirty_upload, p->render_cond, p->tex_dyn_slots, p->gpu_ts_segment, p->vconv_range, p->nvn_api_major, p->nvn_api_minor, p->nver); -+ LOG(" more stdalloc_cap %#llx key_skip %#x+%#x bc_cpu %d ue_pool %d enc_thread %d", -+ (unsigned long long)p->stdalloc_cap, p->shader_skip_off, p->shader_skip_len, p->tex_bc_cpu, p->ue_texture_pool, p->enc_thread); ++ LOG(" more stdalloc_cap %#llx key_skip %#x+%#x bc_cpu %d ue_pool %d enc_thread %d draw_barriers_game %d", ++ (unsigned long long)p->stdalloc_cap, p->shader_skip_off, p->shader_skip_len, p->tex_bc_cpu, p->ue_texture_pool, p->enc_thread, p->draw_barriers_game); } diff --git a/dm/host/hle_profile.h b/dm/host/hle_profile.h -index a2febeb..5afd846 100644 +index a2febeb..9871608 100644 --- a/dm/host/hle_profile.h +++ b/dm/host/hle_profile.h @@ -3,6 +3,7 @@ @@ -628,7 +641,7 @@ index a2febeb..5afd846 100644 int nvn_api_major, nvn_api_minor; // major 0 = decide by the SDK version (hle_nvn.c nvn_api) + uint64_t stdalloc_cap; // StandardAllocator's address space (hle_svc.c); 0 = what the alias region has room for + uint32_t shader_skip_off, shader_skip_len; // shader_key_skip <offset>+<length>: control bytes the shader keys hash as zeros (len 0 = none) -+ int tex_bc_cpu, ue_texture_pool, enc_thread; // ue_texture_pool in MB (0 = the game's own pool) ++ int tex_bc_cpu, ue_texture_pool, enc_thread, draw_barriers_game; // ue_texture_pool in MB (0 = the game's own pool) char args[512], genv[1024]; // game_args / game_env char source[512]; // DM_PROFILE, or "compiled defaults" }; @@ -644,7 +657,7 @@ index a2febeb..5afd846 100644 + return p; +} // one line per differing value (called after loading) diff --git a/dm/host/hle_sdk16.c b/dm/host/hle_sdk16.c -index eb7e0f1..6d397ff 100644 +index 6ad1c76..6cd4f05 100644 --- a/dm/host/hle_sdk16.c +++ b/dm/host/hle_sdk16.c @@ -14,10 +14,10 @@ @@ -681,10 +694,24 @@ index eb7e0f1..6d397ff 100644 return (size_t)skip; } diff --git a/dm/host/hle_svc.c b/dm/host/hle_svc.c -index ea76b71..4069ab8 100644 +index e13bb44..0ac99e1 100644 --- a/dm/host/hle_svc.c +++ b/dm/host/hle_svc.c -@@ -379,11 +379,11 @@ static uint64_t alias_room(void) { +@@ -168,10 +168,10 @@ static void guest_file_init(void) { + const double G = 1073741824.0; const char *e = getenv("DM_GUEST_FILE"), *d = getenv("DM_GUEST_FILE_DIR"), *mf = getenv("DM_GUEST_FILE_MIN_FREE"); + #if TARGET_OS_IPHONE + uint64_t room = os_proc_available_memory(); +- int want = guest_file_wanted(e, DM_GAME_APP_POOL, room); +- LOG("[mem] guest memory: memory pool %.2f GB, room to the memory limit %.2f GB, DM_GUEST_FILE %s: %s", DM_GAME_APP_POOL / G, room / G, e && *e ? e : "unset", want ? "in files" : "anonymous"); ++ int want = guest_file_wanted(e, dm_profile()->app_pool, room); ++ LOG("[mem] guest memory: memory pool %.2f GB, room to the memory limit %.2f GB, DM_GUEST_FILE %s: %s", dm_profile()->app_pool / G, room / G, e && *e ? e : "unset", want ? "in files" : "anonymous"); + #else +- int want = guest_file_wanted(e, DM_GAME_APP_POOL, 0); ++ int want = guest_file_wanted(e, dm_profile()->app_pool, 0); + #endif + if (!want) return; + g_gf_min_free = mf && *mf ? env_size("DM_GUEST_FILE_MIN_FREE", 0, 0x100000) : (uint64_t)5 << 30; +@@ -562,11 +562,11 @@ static uint64_t alias_room(void) { static void stdalloc_init(uint64_t *r, int param) { (void)param; if (r[1]) return; // a heap over memory the game passes in: no address region @@ -699,7 +726,7 @@ index ea76b71..4069ab8 100644 } // ---- Unreal's engine region ---- diff --git a/dm/host/hle_thread.c b/dm/host/hle_thread.c -index 13d9587..38e94d4 100644 +index 19354f3..544b34c 100644 --- a/dm/host/hle_thread.c +++ b/dm/host/hle_thread.c @@ -528,7 +528,7 @@ static void hitch_init(void) { @@ -767,19 +794,19 @@ index 6617759..af89a94 100644 dm_cheats_init(); // the Atmosphère cheat files for this build (hle_cheats.c): picked by first 16 hex digits of main's build ID, which the loader knows now dm_sdk_major(); // logs the SDK version, which SDK-dependent behaviour follows (loader.c) diff --git a/dm/host/tests/Makefile b/dm/host/tests/Makefile -index d1fa46b..f61c812 100644 +index 57917f5..c6add38 100644 --- a/dm/host/tests/Makefile +++ b/dm/host/tests/Makefile @@ -12,7 +12,7 @@ QUIET = $(if $(findstring -fsanitize,$(SAN)),,2>/dev/null) GINC = $(if $(strip $(GAME)),-I../gen/$(GAME) -I../../games/$(GAME),$(error GAME is not set: make GAME=<base game title ID>: $(shell python3 ../../games/game.py list | awk -F'\t' '{printf "%s (%s) ", $$1, $$3}'))) $(B)/t_fs $(B)/t_fs_case $(B)/t_fs_commit_list $(B)/t_fs_commit_list_tsan: ../romfs_image.h GAME_H = ../gen/$(GAME)/game_profile.h --run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid $(B)/t_input_map $(B)/t_launcher_contract -+run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid $(B)/t_input_map $(B)/t_launcher_contract $(B)/t_profile_runtime +-run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid $(B)/t_input_map $(B)/t_launcher_contract ++run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture $(B)/t_oe $(B)/t_fs $(B)/t_fs_commit_list $(B)/t_thread_leak $(B)/t_fs_case $(B)/t_audio $(B)/t_output_stream $(B)/t_audio_spsc $(B)/t_swkbd $(B)/t_altstack $(B)/t_condvar $(B)/t_condvar_old $(B)/t_pollwait $(B)/t_pctl $(B)/t_sf $(B)/t_time $(B)/t_shared_font $(B)/t_vorbis $(B)/t_vorbis_divides $(B)/t_hid $(B)/t_haptics $(B)/t_haptics_apple $(B)/t_cheats $(B)/t_pad_hid $(B)/t_input_map $(B)/t_launcher_contract $(B)/t_profile_runtime $(B)/t_language $(B)/t_heap_size $(B)/t_touch -@@ -45,6 +45,7 @@ run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture +@@ -46,6 +46,7 @@ run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_to $(B)/t_sf $(B)/t_time $(B)/t_shared_font @@ -787,7 +814,7 @@ index d1fa46b..f61c812 100644 $(B)/t_system_event $(B)/t_vorbis $(B)/t_vorbis_divides -@@ -101,7 +102,7 @@ nro: $(B)/t_nro +@@ -102,7 +103,7 @@ nro: $(B)/t_nro $(B)/t_nro # the cheat engine (hle_cheats.c, compiled into the test with stubs for the loader, the heap and the keys) $(B)/t_cheats: t_cheats.c ../hle_cheats.c ../dm.h ../dm_input.h $(GAME_H) | $(B) @@ -796,7 +823,7 @@ index d1fa46b..f61c812 100644 .PHONY: cheats cheats: $(B)/t_cheats TMPDIR="$(abspath $(B))" $(B)/t_cheats -@@ -171,7 +172,7 @@ $(B)/t_ue_heap_span: t_ue_heap_span.c ../hle_uemalloc.c ../hle.h ../dm.h $(GAME_ +@@ -172,7 +173,7 @@ $(B)/t_ue_heap_span: t_ue_heap_span.c ../hle_uemalloc.c ../hle.h ../dm.h $(GAME_ $(CLANG) -O1 -g -Wall -Werror -Wno-unused-function $(GINC) -o $@ t_ue_heap_span.c run: $(B)/t_uecvar $(B)/t_uecvar: t_uecvar.c ../hle_uecvar.c ../hle.h ../dm.h $(GAME_H) | $(B) @@ -805,7 +832,7 @@ index d1fa46b..f61c812 100644 $(B)/t_swkbd: t_swkbd.c ../hle_swkbd.c ../hle.h ../dm.h $(GAME_H) | $(B) $(CLANG) -O1 -Wno-unused-function -I.. $(GINC) -o $@ t_swkbd.c ../hle_swkbd.c run: $(B)/t_bigstack -@@ -364,3 +365,9 @@ coverage: +@@ -376,3 +377,9 @@ coverage: rm -rf $(B)/cov/prof LLVM_PROFILE_FILE="$(call cov_profile,$(B)/cov)" $(MAKE) -i B=$(B)/cov SAN="$(COV_FLAGS)" run @$(call cov_export,$(B)/cov) @@ -817,7 +844,7 @@ index d1fa46b..f61c812 100644 + TMPDIR="$(abspath $(B))" $(B)/t_profile_runtime diff --git a/dm/host/tests/t_profile_runtime.c b/dm/host/tests/t_profile_runtime.c new file mode 100644 -index 0000000..c4b4194 +index 0000000..bbf0e32 --- /dev/null +++ b/dm/host/tests/t_profile_runtime.c @@ -0,0 +1,100 @@ @@ -845,7 +872,7 @@ index 0000000..c4b4194 + CHECK(!strcmp(p->args, "-foo=1 -bar")); + CHECK(p->stdalloc_cap == 0x180000000ull); + CHECK(p->shader_skip_off == 0x770 && p->shader_skip_len == 0x78); -+ CHECK(p->tex_bc_cpu == 1 && p->ue_texture_pool == 1000 && p->enc_thread == 1 && p->core_preempt == 1 && p->fps_target == 30); ++ CHECK(p->tex_bc_cpu == 1 && p->ue_texture_pool == 1000 && p->enc_thread == 1 && p->draw_barriers_game == 1 && p->core_preempt == 1 && p->fps_target == 30); + CHECK(p->shared_font_ttf == 1 && p->nvn_api_major == 55 && p->nvn_api_minor == 12); + CHECK(p->nver == 1 && !strcmp(p->ver[0].build, "abcd") && !strcmp(p->ver[0].ver, "2.0.1")); + return 0; @@ -855,7 +882,7 @@ index 0000000..c4b4194 + setenv("DM_PROFILE", path, 1); + const struct dm_profile *p = dm_profile(); + CHECK(p->shader_skip_len == 0 && p->shared_font_ttf == -1 && p->nvn_api_major == 0 && p->stdalloc_cap == 0); -+ CHECK(p->tex_bc_cpu == 0 && p->ue_texture_pool == 0 && p->enc_thread == 0); ++ CHECK(p->tex_bc_cpu == 0 && p->ue_texture_pool == 0 && p->enc_thread == 0 && p->draw_barriers_game == 0); + return 0; +} +static int child_env(void) { // DM_PROF_<KEY> wins over the file and over the compiled profile @@ -875,7 +902,7 @@ index 0000000..c4b4194 + CHECK(!strcmp(p->args, DM_GAME_ARGS)); + CHECK(p->stdalloc_cap == DM_GAME_STDALLOC_CAP); + CHECK(p->shader_skip_off == DM_GAME_SHADER_KEY_SKIP_OFF && p->shader_skip_len == DM_GAME_SHADER_KEY_SKIP_LEN); -+ CHECK(p->tex_bc_cpu == DM_GAME_TEX_BC_CPU && p->ue_texture_pool == DM_GAME_UE_TEXTURE_POOL && p->enc_thread == DM_GAME_ENC_THREAD); ++ CHECK(p->tex_bc_cpu == DM_GAME_TEX_BC_CPU && p->ue_texture_pool == DM_GAME_UE_TEXTURE_POOL && p->enc_thread == DM_GAME_ENC_THREAD && p->draw_barriers_game == DM_GAME_DRAW_BARRIERS_GAME); + CHECK(p->core_preempt == DM_GAME_CORE_PREEMPT && p->fps_target == DM_GAME_FPS_TARGET); +#ifdef DM_GAME_NVN_API_MAJOR + CHECK(p->nvn_api_major == DM_GAME_NVN_API_MAJOR && p->nvn_api_minor == DM_GAME_NVN_API_MINOR); @@ -906,9 +933,9 @@ index 0000000..c4b4194 + char full[300], none[300]; snprintf(full, sizeof full, "%s/full.tsv", dir); snprintf(none, sizeof none, "%s/none.tsv", dir); + if (write_file(full, "# values that differ from any compiled profile\n" + "title_id\t0100000000001234\nprogram_id\t0x0100000000001234\nname\tRuntime Test\nmodules\tmain subsdk0 sdk\ngame_args\t-foo=1 -bar\n" -+ "stdalloc_cap\t0x180000000\nshader_key_skip\t0x770+0x78\ntex_bc_cpu\t1\nue_texture_pool\t1000\nenc_thread\t1\ncore_preempt\t1\nfps_target\t30\n" ++ "stdalloc_cap\t0x180000000\nshader_key_skip\t0x770+0x78\ntex_bc_cpu\t1\nue_texture_pool\t1000\nenc_thread\t1\ndraw_barriers\tgame\ncore_preempt\t1\nfps_target\t30\n" + "shared_font\tttf\ndevice_api\t55.12\nbuild\tabcd\t2.0.1\tdisplay\n")) return 1; -+ if (write_file(none, "shader_key_skip\t0x0+0x0\nshared_font\tauto\ndevice_api\t0.0\nstdalloc_cap\t0x0\ntex_bc_cpu\t0\nue_texture_pool\t0\nenc_thread\t0\n")) return 1; ++ if (write_file(none, "shader_key_skip\t0x0+0x0\nshared_font\tauto\ndevice_api\t0.0\nstdalloc_cap\t0x0\ntex_bc_cpu\t0\nue_texture_pool\t0\nenc_thread\t0\ndraw_barriers\t0\n")) return 1; + const char *cases[][2] = {{"file", full}, {"none", none}, {"env", NULL}, {"compiled", NULL}}; + int bad = 0; + for (int i = 0; i < 4; i++) { @@ -923,10 +950,10 @@ index 0000000..c4b4194 +} diff --git a/dm/tools/tests/test_game_profile_runtime.py b/dm/tools/tests/test_game_profile_runtime.py new file mode 100644 -index 0000000..46e8f89 +index 0000000..a724f94 --- /dev/null +++ b/dm/tools/tests/test_game_profile_runtime.py -@@ -0,0 +1,54 @@ +@@ -0,0 +1,55 @@ +"""game.py profile: the runtime profile file the host reads from DM_PROFILE (dm/host/hle_profile.c set_kv) carries every key the host reads, with the "none" spellings +for a key the game leaves out (the host keeps the compiled game's value for a key the file lacks, so a second game's file must say it has none). No game files needed.""" +import subprocess @@ -939,7 +966,7 @@ index 0000000..46e8f89 +sys.path.insert(0, str(REPO / 'dm/games')) +import game # pyright: ignore[reportMissingImports] (dm/games, put on sys.path above) + -+KEYS = ('title_id', 'program_id', 'modules', 'stdalloc_cap', 'shader_key_skip', 'tex_bc_cpu', 'ue_texture_pool', 'enc_thread', 'core_preempt', 'fps_target', 'device_api', 'game_args') ++KEYS = ('title_id', 'program_id', 'modules', 'stdalloc_cap', 'shader_key_skip', 'tex_bc_cpu', 'ue_texture_pool', 'enc_thread', 'draw_barriers', 'core_preempt', 'fps_target', 'device_api', 'game_args') + + +def profile(gid): @@ -975,6 +1002,7 @@ index 0000000..46e8f89 + self.assertEqual(int(rows['ue_texture_pool']), int(g.get('ue_texture_pool', '0')), g.id) + self.assertEqual(int(rows['tex_bc_cpu']), int(g.get('tex_bc_cpu', 'off') == 'on'), g.id) + self.assertEqual(int(rows['enc_thread']), int(g.get('enc_thread', 'off') == 'on'), g.id) ++ self.assertEqual(int(rows['draw_barriers']), int(g.get('draw_barriers', 'full') == 'game'), g.id) + self.assertEqual(rows['device_api'] == '0.0', not g.get('device_api', ''), g.id) + self.assertEqual(rows['shared_font'] == 'auto', not g.get('shared_font', ''), g.id) + diff --git a/patches/0054-front-mac-start-with-the-pointer-hidden-when-the.patch b/patches/0054-front-mac-start-with-the-pointer-hidden-when-the.patch index 985da0861..e72119768 100644 --- a/patches/0054-front-mac-start-with-the-pointer-hidden-when-the.patch +++ b/patches/0054-front-mac-start-with-the-pointer-hidden-when-the.patch @@ -41,7 +41,7 @@ index bf0a91e..892df56 100644 } for (NSNotificationName nm in @[GCControllerDidConnectNotification, GCControllerDidDisconnectNotification]) diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 7eda9cf..ca7b535 100644 +index 4b14917..e43f2ba 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv @@ -22,6 +22,7 @@ DM_CHEATS_WINDOW switch `1` opens the Mac Cheats panel beside the game from the diff --git a/patches/0068-ios-quit-a-game-from-the-in-game-menu-or-by-hold.patch b/patches/0068-ios-quit-a-game-from-the-in-game-menu-or-by-hold.patch index 14a765ed1..348f26843 100644 --- a/patches/0068-ios-quit-a-game-from-the-in-game-menu-or-by-hold.patch +++ b/patches/0068-ios-quit-a-game-from-the-in-game-menu-or-by-hold.patch @@ -774,10 +774,10 @@ index 0000000..3f409e9 +if __name__ == "__main__": + unittest.main() diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index ca7b535..83e5942 100644 +index e43f2ba..85f6b3d 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -74,7 +74,7 @@ DM_HID_MAP switch `=<map>` such as `A=1,B=2,X=4,Y=5`: which HID button usage pre +@@ -78,7 +78,7 @@ DM_HID_MAP switch `=<map>` such as `A=1,B=2,X=4,Y=5`: which HID button usage pre DM_HID_PADS switch `0` stops reading raw HID gamepads that GameController does not list, such as an 8BitDo in its own mode (`pad_hid_mac.h`); Mac only DM_HITCH_DUMP diagnostic `=<ms>[:<s>]`: when no frame has been presented for that long (from s seconds on), logs every guest thread once per stall: wait, last SVC, pc, lr, frames and registers DM_HLECOUNT diagnostic `=<file>` (`%d` = process ID) writes at exit a TSV of how many times the guest called each HLE function, zero counts included diff --git a/patches/0072-nsp-script-the-first-dotnet-with-an-sdk-10-goes.patch b/patches/0072-nsp-script-the-first-dotnet-with-an-sdk-10-goes.patch index 72889c341..ac1f333a7 100644 --- a/patches/0072-nsp-script-the-first-dotnet-with-an-sdk-10-goes.patch +++ b/patches/0072-nsp-script-the-first-dotnet-with-an-sdk-10-goes.patch @@ -77,7 +77,7 @@ index f81a641..80cda9e 100644 + if __name__ == '__main__': unittest.main() diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index 59f3df7..486a464 100755 +index 71922cd..9cbf9b4 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -442,6 +442,23 @@ def configure_dotnet(env): diff --git a/patches/0073-nsp-script-hand-the-extraction-and-the-shader-sp.patch b/patches/0073-nsp-script-hand-the-extraction-and-the-shader-sp.patch index b54d3d2f3..43f24a62a 100644 --- a/patches/0073-nsp-script-hand-the-extraction-and-the-shader-sp.patch +++ b/patches/0073-nsp-script-hand-the-extraction-and-the-shader-sp.patch @@ -255,7 +255,7 @@ index 0000000..444e186 +if __name__ == '__main__': + unittest.main() diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 83e5942..732fea6 100644 +index 85f6b3d..b934480 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv @@ -36,6 +36,7 @@ DM_ENV_FILE path `=<file>` makes `build_ipa.sh` bake its `KEY=VALUE` lines into @@ -267,12 +267,12 @@ index 83e5942..732fea6 100644 DM_FIXED_TIME switch `=<seconds>`: the user clock always returns that time and the host time zone is pinned to UTC, for deterministic comparison DM_FOCUS_KEEP switch `1` keeps the game in focus while its Mac window is up, whichever app is active (minimizing still counts); `scripts/regress.py` sets it, so another app taking the key window during a run does not pause a game that stops out of focus diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index 486a464..ed02d0d 100755 +index 9cbf9b4..526292a 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -33,6 +33,7 @@ # unless ALLOW_OTHER_METAL / --allow-other-metal. Issue #242 reported corruption with another compiler; the cause is still unconfirmed. - # Python 3.12+ is required for bounded archive mappings inside images. The Python running this file needs lz4, capstone 5.x and fonttools (and backports.zstd before Python 3.14); also needed: Xcode, xcodegen, .NET SDK 10, zstd. SPIRV-Cross is the in-repo build. A first run takes 2 (Metroid Dread) to 10 minutes (Breath of the Wild: 5 of them the shader split), longer when shader libraries must be compiled; a repeat run with unchanged inputs takes about 20 s (Metroid Dread), mostly packing the IPA. Key values are never printed. + # Python 3.12+ is required for bounded archive mappings inside images. The Python running this file needs lz4, capstone 5.x, fonttools and zbic (and backports.zstd before Python 3.14); also needed: Xcode, xcodegen, .NET SDK 10, zstd. SPIRV-Cross is the in-repo build. A first run takes 2 (Metroid Dread) to 10 minutes (Breath of the Wild: 5 of them the shader split), longer when shader libraries must be compiled; a repeat run with unchanged inputs takes about 20 s (Metroid Dread), mostly packing the IPA. Key values are never printed. # The console shows one line per stage ([k/n] stage, with a bar, done/total, percent, elapsed time and ETA where the work can be counted); the tools' own output goes only to logs/ (PROGRESS / --progress). --progress json writes newline-delimited JSON events to stdout. +# DM_EXPERIMENTAL_BUILDER=1 with NXB_BIN=<path of nxbuild's nxb binary>: the extraction and the shader split run through nxbuild's binaries where it supports the case, with the same outputs and events; anything else runs as always (nxb_tool). import ast, collections, contextlib, ctypes, errno, getpass, pwd, hashlib, importlib.util, json, os, re, shlex, shutil, struct, subprocess, sys, tempfile, threading, time, types diff --git a/patches/0074-mac-split-a-joy-con-pair-into-two-sideways-contr.patch b/patches/0074-mac-split-a-joy-con-pair-into-two-sideways-contr.patch index 139968d29..c97c91294 100644 --- a/patches/0074-mac-split-a-joy-con-pair-into-two-sideways-contr.patch +++ b/patches/0074-mac-split-a-joy-con-pair-into-two-sideways-contr.patch @@ -579,10 +579,10 @@ index 0000000..a995be7 + } +} diff --git a/dm/host/tests/Makefile b/dm/host/tests/Makefile -index f61c812..361cb46 100644 +index c6add38..a35ca5e 100644 --- a/dm/host/tests/Makefile +++ b/dm/host/tests/Makefile -@@ -353,7 +353,7 @@ tsan-threads-run: $(addprefix $(B)/,$(TSAN_T)) +@@ -365,7 +365,7 @@ tsan-threads-run: $(addprefix $(B)/,$(TSAN_T)) $(B)/t_altstack DM_QOS_MAP=1 $(B)/t_notify_sched DM_QOS_MAP=1 $(B)/t_notify_sched --all-peers @@ -739,10 +739,10 @@ index 2b64998..500e86f 100644 int front_ios_boot_ok(void) { return 1; } void front_ios_boot(void) {} diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 732fea6..57e34f7 100644 +index b934480..d25e9e3 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -86,6 +86,7 @@ DM_JIT_CAPTURE path Folder the Mac host writes what a game's `nn::jit` plugin ma +@@ -90,6 +90,7 @@ DM_JIT_CAPTURE path Folder the Mac host writes what a game's `nn::jit` plugin ma DM_JIT_LOG diagnostic `1` logs every `nn::jit` `GenerateCode` request DM_JIT_MISS_DUMP diagnostic Folder for the input of each `nn::jit` request a served image lacks DM_JIT_SERVE path Folder of a `nn::jit` image (`jit.img`, `jit.tsv`, `jit.out`): the Mac serves requests from it as the iOS app does, and the plugin does not run diff --git a/patches/0075-front-mac-hide-the-pointer-before-the-game-takes.patch b/patches/0075-front-mac-hide-the-pointer-before-the-game-takes.patch index 433461f8d..724b16a18 100644 --- a/patches/0075-front-mac-hide-the-pointer-before-the-game-takes.patch +++ b/patches/0075-front-mac-hide-the-pointer-before-the-game-takes.patch @@ -16,10 +16,10 @@ Issue: LNPANDY/NX2Apple#380 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/dm/host/front_mac.m b/dm/host/front_mac.m -index 892df56..6f6491e 100644 +index 1d5f680..cfb6dcb 100644 --- a/dm/host/front_mac.m +++ b/dm/host/front_mac.m -@@ -1322,6 +1322,12 @@ void front_run(void (*guest)(void)) { +@@ -1390,6 +1390,12 @@ void front_run(void (*guest)(void)) { if (!getenv("DM_WINDOW")) { guest(); return; } @autoreleasepool { [NSApplication sharedApplication]; @@ -32,7 +32,7 @@ index 892df56..6f6491e 100644 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; NSRect r = NSMakeRect(0, 0, 1280, 720); NSWindow *win = [[NSWindow alloc] initWithContentRect:r -@@ -1356,7 +1362,9 @@ void front_run(void (*guest)(void)) { +@@ -1424,7 +1430,9 @@ void front_run(void (*guest)(void)) { [win makeKeyAndOrderFront:nil]; screen_refresh(win.screen ?: NSScreen.mainScreen); g_win = win; @@ -43,7 +43,7 @@ index 892df56..6f6491e 100644 // A host another process starts (scripts/regress.py) is not activated (its focus state stays 2), so its window opened wherever it landed in the window order, and behind // another app's window macOS fires its display link about once a second: Breath of the Wild stepped up to 3 vsyncs at boot and never came back (20 fps runs). // DM_WINDOW_TOP=1 orders it in front without activation and keeps it above other apps' windows; the log names every change of the window's visibility either way -@@ -1411,15 +1419,13 @@ void front_run(void (*guest)(void)) { +@@ -1479,15 +1487,13 @@ void front_run(void (*guest)(void)) { // The pointer hides after DM_CURSOR_HIDE seconds (3 by default, 0 keeps it) without movement over the game, and comes back with the next movement, click or scroll. macOS // hides it only for a game that asks (a game window does not, and a pointer left over a fullscreen game stays on the picture); it is also shown whenever the window is // not key or the pointer is outside the game view, so the title bar and other windows keep theirs diff --git a/patches/0076-host-read-embedded-sections-from-the-image-that.patch b/patches/0076-host-read-embedded-sections-from-the-image-that.patch index f28df168a..100261b69 100644 --- a/patches/0076-host-read-embedded-sections-from-the-image-that.patch +++ b/patches/0076-host-read-embedded-sections-from-the-image-that.patch @@ -44,7 +44,7 @@ index f3e38a1..dc6edac 100644 return NULL; } diff --git a/dm/gfx/nvn/hle_nvn.c b/dm/gfx/nvn/hle_nvn.c -index 381c3c8..6e4a638 100644 +index 7ec797a..44b7bd4 100644 --- a/dm/gfx/nvn/hle_nvn.c +++ b/dm/gfx/nvn/hle_nvn.c @@ -38,8 +38,7 @@ @@ -67,7 +67,7 @@ index 381c3c8..6e4a638 100644 for (unsigned i = 0; i < NVN_NFUNC; i++) nvn_slot[i].fn = (void *)nvn_unimpl_entry; for (unsigned k = 0; k < nim; k++) { diff --git a/dm/gfx/vk/hle_vk.m b/dm/gfx/vk/hle_vk.m -index 3728dff..bbe5bba 100644 +index 6b25664..9d141a4 100644 --- a/dm/gfx/vk/hle_vk.m +++ b/dm/gfx/vk/hle_vk.m @@ -21,8 +21,7 @@ @@ -112,10 +112,10 @@ index 672c16a..50bd6e6 100644 unsigned cap = 16; while (cap < n * 2) cap <<= 1; g_hle_idx = calloc(cap, sizeof *g_hle_idx); g_hle_mask = cap - 1; diff --git a/dm/host/hle_jit.c b/dm/host/hle_jit.c -index 6e1d614..aa99f11 100644 +index 198de55..da6203d 100644 --- a/dm/host/hle_jit.c +++ b/dm/host/hle_jit.c -@@ -145,15 +145,14 @@ static const jit_req *jit_find(const char *key) { for (int i = 0; i < g_nreq; i+ +@@ -146,15 +146,14 @@ static const jit_req *jit_find(const char *key) { for (int i = 0; i < g_nreq; i+ // The image at the layout's base: the Mac maps jit.img read + execute, iOS remaps the executable's signed __TEXT,__dm<tag>_jit pages there (as loader.c remaps the modules) #if TARGET_OS_IPHONE #include <mach/mach.h> @@ -148,7 +148,7 @@ index 0000000..9dde850 +extern const struct mach_header_64 __dso_handle; +#define DM_HOST_IMAGE (&__dso_handle) diff --git a/dm/host/loader.c b/dm/host/loader.c -index 5bc0117..1187658 100644 +index c12d21b..4845e63 100644 --- a/dm/host/loader.c +++ b/dm/host/loader.c @@ -8,9 +8,8 @@ @@ -171,7 +171,7 @@ index 5bc0117..1187658 100644 g_nforce = g_force ? (unsigned)(sz / sizeof *g_force) : 0; g_force_hits = calloc(g_nforce + 1, sizeof *g_force_hits); unsigned cap = 16; while (cap < g_nforce * 2) cap <<= 1; -@@ -707,7 +706,7 @@ static void remap_at(module_t *m, vm_address_t to, const void *from, unsigned lo +@@ -708,7 +707,7 @@ static void remap_at(module_t *m, vm_address_t to, const void *from, unsigned lo } // The image the module sections are read from (front.h dm_set_game_image_header). getsectiondata takes any loaded image's header and applies its slide #include "front.h" diff --git a/patches/0077-ios-shell-link-the-host-into-each-game-s-framewo.patch b/patches/0077-ios-shell-link-the-host-into-each-game-s-framewo.patch index 0a6c0db10..f10c93e47 100644 --- a/patches/0077-ios-shell-link-the-host-into-each-game-s-framewo.patch +++ b/patches/0077-ios-shell-link-the-host-into-each-game-s-framewo.patch @@ -526,10 +526,10 @@ index a2d527c..2f5b1ad 100644 @unittest.skipUnless(HAS_TOOLS and font_python(), 'needs macOS, XcodeGen, an iPhoneOS SDK and a python with fontTools') diff --git a/dm/ios/tools/build_ipa.sh b/dm/ios/tools/build_ipa.sh -index 97d22d1..3f81541 100755 +index eec7ee4..9f67e73 100755 --- a/dm/ios/tools/build_ipa.sh +++ b/dm/ios/tools/build_ipa.sh -@@ -209,9 +209,10 @@ if [ "$FRAMEWORK" = 1 ]; then +@@ -214,9 +214,10 @@ if [ "$FRAMEWORK" = 1 ]; then mkdir -p "$O" && ROMFS_ID=$(python3 -I "$DM/tools/romfs_image.py" --identity "$ROMFS" "$GAME" "$DM_APP_VERSION" "${PKG:-}") || die "identity of $ROMFS" fi NRO_TABLE=$PATCHED/nro.tsv; [ -f "$NRO_TABLE" ] || NRO_TABLE= # the NRO modules gen_embed.py packs: nro_count in the manifest @@ -541,7 +541,7 @@ index 97d22d1..3f81541 100755 printf 'OTHER_LDFLAGS = $(inherited) %s\n' "$F" > Gen/framework.xcconfig || die "framework link flags" t "framework: Gen/framework (modules $MODS, profile.tsv, game.manifest.json)" fi -@@ -252,9 +253,9 @@ fi +@@ -257,9 +258,9 @@ fi # game's per-game resources into it, so one folder is one game: the shader libraries (msl/<MSLDIR>, the shell's DM_MSL_DIR), the pipeline # archive (pso/, DM_PSO_BUNDLE) and the icon the shell's library shows. The game files and add-on content stay out (data, Documents/games/<id>/) if [ -n "$FRAMEWORK_ONLY" ]; then diff --git a/patches/0078-document-the-fork-touch-controls-so-environment.patch b/patches/0078-document-the-fork-touch-controls-so-environment.patch index 4603e7d8c..0239e6bb7 100644 --- a/patches/0078-document-the-fork-touch-controls-so-environment.patch +++ b/patches/0078-document-the-fork-touch-controls-so-environment.patch @@ -7,16 +7,15 @@ Subject: [PATCH] Document the fork touch controls so environment validation Upstream-Status: fork-only Reason: The fork touch-control patches add four environment settings without table rows, which makes the Tool tests job fail once it preserves test exit status Issue: LNPANDY/NX2Apple#690 - --- docs/env_switches.tsv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 772479e..6d37d61 100644 +index d25e9e3..08e9eec 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -358,7 +358,11 @@ DM_THERMAL_CAP switch `0` turns off the thermal cap, which holds the frame step +@@ -365,7 +365,11 @@ DM_THERMAL_CAP switch `0` turns off the thermal cap, which holds the frame step DM_THR diagnostic `=1` logs each SVC (except `SleepThread`) with its first four arguments, and each `CreateThread` with entry, stack, priority and core DM_THRCPU diagnostic `=<tsv path>[,<seconds>]` writes each thread's CPU time (100% = one core) and the total every interval (default 10 s); on iOS a relative path is under `Documents` DM_TOUCH_HAPTICS player `0` turns off the light haptic tap when a touch control is pressed (on by default); launcher setting diff --git a/patches/0079-ios-carry-cpu-bc-compatibility-through-game-pack.patch b/patches/0079-ios-carry-cpu-bc-compatibility-through-game-pack.patch index b4feb5dda..ae04370f5 100644 --- a/patches/0079-ios-carry-cpu-bc-compatibility-through-game-pack.patch +++ b/patches/0079-ios-carry-cpu-bc-compatibility-through-game-pack.patch @@ -10,7 +10,6 @@ Front-ends: the shell must read each framework dm/env.txt after the player envir Upstream-Status: fork-only (declined seedds/nxnative#488, seedds/nxnative#492) Reason: Repair the fork-only older-device BC decoder and runtime-profile packaging so the launcher compatibility switch reaches the selected game. Issue: LNPANDY/NX2Apple#719 - --- dm/ios/Sources/main.m | 3 +- dm/ios/tests/test_bc_device_check.py | 56 ++++++++++++++++++++++++++++ @@ -20,7 +19,7 @@ Issue: LNPANDY/NX2Apple#719 create mode 100644 dm/ios/tests/test_bc_device_check.py diff --git a/dm/ios/Sources/main.m b/dm/ios/Sources/main.m -index 69cf022..9824a00 100644 +index 71f5be5..757d875 100644 --- a/dm/ios/Sources/main.m +++ b/dm/ios/Sources/main.m @@ -9,7 +9,6 @@ @@ -31,7 +30,7 @@ index 69cf022..9824a00 100644 #include "front.h" #include "romfs_image.h" #include "DataBookmark.h" -@@ -212,7 +211,7 @@ const char *front_ios_device_check(void) { +@@ -212,7 +211,7 @@ static void aoc_locate(void) { static char msg[320]; static int done; if (done++) return msg[0] ? msg : NULL; id<MTLDevice> gpu = MTLCreateSystemDefaultDevice(); // the system default device, the one the graphics layers take (mtl_init repeats this check for the other routes) @@ -135,10 +134,10 @@ index fee9e53..0d20b82 100644 """The signing and pack steps on a stand-in app; no Xcode, no game files.""" diff --git a/dm/ios/tools/build_ipa.sh b/dm/ios/tools/build_ipa.sh -index 97d22d1..e40ed8e 100755 +index 9f67e73..f177de7 100755 --- a/dm/ios/tools/build_ipa.sh +++ b/dm/ios/tools/build_ipa.sh -@@ -197,7 +197,8 @@ else t "no app icon ($ICON not found): the home-screen icon stays blank"; fi +@@ -202,7 +202,8 @@ else t "no app icon ($ICON not found): the home-screen icon stays blank"; fi if [ "$FRAMEWORK" = 1 ]; then rm -rf Gen/framework && mkdir -p Gen/framework/dm || die "mkdir Gen/framework" cp -R Gen/dm/. Gen/framework/dm/ || die "copy the framework tables" diff --git a/patches/0080-keep-the-selected-python-when-generating-the-mac.patch b/patches/0080-keep-the-selected-python-when-generating-the-mac.patch index 4ddb70484..c5f18cb65 100644 --- a/patches/0080-keep-the-selected-python-when-generating-the-mac.patch +++ b/patches/0080-keep-the-selected-python-when-generating-the-mac.patch @@ -18,7 +18,6 @@ selected Python for host generation. Direct Make callers may set PYTHON. Upstream-Status: pending Reason: The Mac host Makefile can start an older PATH Python despite the launcher selecting a supported interpreter Issue: LNPANDY/NX2Apple#711 - --- dm/games/01006F8002326000/README.md | 2 ++ dm/host/Makefile | 11 +++++++---- @@ -41,7 +40,7 @@ index 33ba8bf..1365fd5 100644 - **The host's shaders compiled when the host is built** (`dm/gfx/msl`, `host_msl_lib`): the HUD's `Compiled 2 shaders … 102.70ms` in play on an iPhone 17 Pro Max came from clear-triangle libraries compiled from source when a new room first issued a scissored clear; [The host's own shaders compiled from source at run time](../../../docs/reference/shaders.md#the-hosts-own-shaders-compiled-from-source-at-run-time). diff --git a/dm/host/Makefile b/dm/host/Makefile -index 8b66f0b..45be7bb 100644 +index 2100b89..cac0b94 100644 --- a/dm/host/Makefile +++ b/dm/host/Makefile @@ -1,14 +1,17 @@ @@ -153,10 +152,10 @@ index 6175a81..6ce3573 100644 nsp = script('build_ipa_from_nsp') self.host_dirs(nsp.HOST_DIRS) diff --git a/docs/reference/packing.md b/docs/reference/packing.md -index 7f49e62..4432b04 100644 +index de3564f..e686c5b 100644 --- a/docs/reference/packing.md +++ b/docs/reference/packing.md -@@ -19,6 +19,13 @@ +@@ -33,6 +33,13 @@ ## Build tools @@ -171,10 +170,10 @@ index 7f49e62..4432b04 100644 - **Symptom:** the first extraction stops with `nx_extract exit code 131` and `.NET location: Not found`, even though `dotnet` is on `PATH`. The shader translator can fail the same way. diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py -index d7ebdb5..30587e1 100755 +index 526292a..f3c0428 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py -@@ -1488,8 +1488,8 @@ def build_mac_host(repo, out, env, logs, g): +@@ -1704,8 +1704,8 @@ def build_mac_host(repo, out, env, logs, g): and the macOS SDK) are as at the last build into out. The host runs the game's own arm64 code natively, so an Intel Mac stops here (GitHub issue #54)""" if not apple_silicon(): raise BuildError('this step boots the game in the Mac host, which runs the game\'s own arm64 code natively: it needs a Mac with Apple silicon, and this Mac has an Intel processor') host = repo / 'dm/host' diff --git a/patches/0081-support-steam-controller-and-shared-mac-control.patch b/patches/0081-support-steam-controller-and-shared-mac-control.patch index 7c3521ecd..27f0f8f74 100644 --- a/patches/0081-support-steam-controller-and-shared-mac-control.patch +++ b/patches/0081-support-steam-controller-and-shared-mac-control.patch @@ -62,7 +62,7 @@ Issue: seedds/nxnative#221 create mode 100644 docs/mac-controllers.md diff --git a/dm/games/0100F2C0115B6000/README.md b/dm/games/0100F2C0115B6000/README.md -index 5505109..a1faaee 100644 +index 10876cc..dd12253 100644 --- a/dm/games/0100F2C0115B6000/README.md +++ b/dm/games/0100F2C0115B6000/README.md @@ -64,6 +64,8 @@ Game-specific findings. The general host changes they led to are listed under [F @@ -74,7 +74,7 @@ index 5505109..a1faaee 100644 - SDK 20 calls it needed (each aborted in `sm::Initialize` or read a null applet proxy, one per boot): `oe::GetPreviousProgramIndex`, the hardware Opus decoder's C interface, cache storage, `fs::IsSaveDataExisting` for another title (it asks for Breath of the Wild's save: none), `MountSaveDataReadOnly`, `time::ClockSnapshot::CreateWithStandardSystemClock` ([Applet resource user ID, launch parameters and VR mode](../../../docs/reference/services.md#applet-resource-user-id-launch-parameters-and-vr-mode), [Hardware Opus decoders](../../../docs/reference/audio.md#hardware-opus-decoders-built-on-the-sdks-software-decoders), [Cache storage and other titles' save data](../../../docs/reference/services.md#cache-storage-and-other-titles-save-data), [Steady clock, clock snapshots and user clocks](../../../docs/reference/services.md#steady-clock-clock-snapshots-and-user-clocks)). - **Resolved: clock snapshot types** ([PR #385](https://github.com/seedds/nxnative/pull/385), static SDK check). SDK build `574284b40c06cf1821a44857939083bee5716820` passes type 1 for standard snapshots (`sdk+0x441890`) and 2 for adjustable snapshots (`sdk+0x441924`); both entries first read the time-service session through `sdk+0xb6b280`. The host now sets the standard type and takes over the adjustable entry as described in [Steady clock, clock snapshots and user clocks](../../../docs/reference/services.md#steady-clock-clock-snapshots-and-user-clocks). This verifies the ABI; gameplay was not rerun for this check. - `main` imports `nn::ro` (it loads NRO modules at run time). -@@ -112,6 +114,10 @@ General host changes first needed here; each applies to every game: +@@ -113,6 +115,10 @@ General host changes first needed here; each applies to every game: - **RomFS folder transfer and startup deletion failures:** [RomFS images](../../../docs/reference/packing.md#romfs-folder-transfers-fail-or-leave-stale-game-files). @@ -86,10 +86,10 @@ index 5505109..a1faaee 100644 - **Resolved in the host (#342): recurring startup crash during archive compaction**, with a workaround for Metal's serializer defect: [Pipeline archive compaction crashes in Metal's serializer](../../../docs/reference/shaders.md#a-compaction-flush-that-crashes-then-crashes-every-start). Confirmed with local manifest replays; no new gameplay or iOS validation. diff --git a/dm/host/Makefile b/dm/host/Makefile -index 2100b89..33f6b5d 100644 +index cac0b94..b97e46f 100644 --- a/dm/host/Makefile +++ b/dm/host/Makefile -@@ -17,7 +17,7 @@ GAME_H = gen/$(GAME)/game_profile.h +@@ -20,7 +20,7 @@ GAME_H = gen/$(GAME)/game_profile.h include ../mac/min_os.mk CFLAGS=-O1 -g -Wall -Wno-unused-function -mmacosx-version-min=$(MACOS_MIN) # Service-layer takeover hle_*.c files are included by wildcard: adding a file does not touch this line (avoids merge conflicts) @@ -98,7 +98,7 @@ index 2100b89..33f6b5d 100644 # NVN null device + nn::vi + the Vulkan layer (dm/gfx/) # Sources under dm/gfx, dm/gfx/nvn, dm/gfx/tex, dm/gfx/vk and dm/gfx/gl are included by wildcard (tests/ and metal/ do not go into the host): adding a file does not touch this line GFX=$(sort $(wildcard ../gfx/*.c ../gfx/nvn/*.c ../gfx/nvn/*.m ../gfx/nvn/*.S ../gfx/tex/*.c ../gfx/tex/*.m ../gfx/vk/*.m ../gfx/gl/*.m)) -@@ -76,7 +76,7 @@ endif +@@ -79,7 +79,7 @@ endif endif .PHONY: FORCE FORCE: @@ -1522,7 +1522,7 @@ index cfb6dcb..041bf07 100644 // Mouse as a single-point touchscreen (front.h dm_touch_set): left button down / drag = finger 0, release = lifted. The content view takes the clicks, // not a window-wide event monitor, so clicks on the title bar, the window buttons and the resize edges still reach AppKit. // The picture's width / height (front.h front_picture_shape; 16:9 until the first present), which the present fits into the view -@@ -507,9 +273,9 @@ void front_picture_shape(int w, int h) { if (w > 0 && h > 0) g_picture_aspect = +@@ -507,9 +273,9 @@ - (void)mouseDragged:(NSEvent *)e { double a = g_picture_aspect; if (w / h > a) w = h * a; else h = w / a; // the centered picture the present fits into the view (16:9, or a taller one under DM_SCREEN_ASPECT) double x0 = (b.width - w) / 2, y0 = (b.height - h) / 2; dm_touch t = {0, (float)fmin(fmax((p.x - x0) / w, 0), 0.999), (float)fmin(fmax(1 - (p.y - y0) / h, 0), 0.999)}; // The AppKit view origin is at bottom left, the touchscreen origin is at top left @@ -1610,7 +1610,7 @@ diff --git a/dm/host/haptics_apple.m b/dm/host/haptics_apple.m index ca0414f..849af06 100644 --- a/dm/host/haptics_apple.m +++ b/dm/host/haptics_apple.m -@@ -40,6 +40,9 @@ static dispatch_queue_t queue; +@@ -40,6 +40,9 @@ - (void)stop; static NSMutableArray<DmRumbleChannel *> *channels[DM_PLAYERS]; static GCController *controllers[DM_PLAYERS]; static GCHapticsLocality controllerLocalities[DM_PLAYERS]; @@ -1620,7 +1620,7 @@ index ca0414f..849af06 100644 #if TARGET_OS_IPHONE static BOOL phoneSupported; #endif -@@ -71,7 +74,7 @@ static CHHapticPattern *simple_pattern(dm_haptic_level level, NSError **error) { +@@ -71,7 +74,7 @@ static NSTimeInterval now(void) { // low band at 0.1-0.4 all race long: 0.45 leaves crashes and drifts). The mapping stays linear by default: the opposite need, low levels that // rotating motors do not render at all (Breath of the Wild's charged attack on an Xbox controller), is open (docs/reference/services.md) static float rumble_min; @@ -1629,7 +1629,7 @@ index ca0414f..849af06 100644 x = isfinite(x) ? fmaxf(0, fminf(1, x)) : 0; return x <= rumble_min ? 0 : (x - rumble_min) / (1 - rumble_min); } -@@ -161,7 +164,7 @@ static void report(NSError *e, NSString *target, const char *stage) { +@@ -161,7 +164,7 @@ - (BOOL)startEngine { return YES; } - (void)apply:(dm_haptic_level)level { @@ -1730,7 +1730,7 @@ index d808df0..3fb623b 100644 // The sink copies/enqueues each snapshot in order; called under the state lock, it must not reenter. // No hardware calls on the sending thread. A changed epoch invalidates all older queued snapshots. diff --git a/dm/host/hle_hid.c b/dm/host/hle_hid.c -index 18a72cc..38b433d 100644 +index 751b9da..5351ff9 100644 --- a/dm/host/hle_hid.c +++ b/dm/host/hle_hid.c @@ -2,7 +2,7 @@ @@ -2437,10 +2437,10 @@ index 0000000..a252573 + memset(out, 0, 64); out[0] = 1; out[1] = 0x87; out[2] = 3; out[3] = 48; out[4] = enabled ? 0x18 : 0; +} diff --git a/dm/host/tests/Makefile b/dm/host/tests/Makefile -index 361cb46..b3ee543 100644 +index a35ca5e..cac33f6 100644 --- a/dm/host/tests/Makefile +++ b/dm/host/tests/Makefile -@@ -70,6 +70,9 @@ run: $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_touch $(B)/t_gesture +@@ -71,6 +71,9 @@ run: $(B)/t_net_eventfd $(B)/t_language $(B)/t_motion $(B)/t_heap_size $(B)/t_to TMPDIR="$(abspath $(B))" $(B)/t_cheats $(QUIET) $(B)/t_nro $(B)/t_symbols @@ -2450,7 +2450,7 @@ index 361cb46..b3ee543 100644 run: $(B)/t_nro run: $(B)/t_symbols $(B)/t_symbols: t_symbols.c ../hle.c ../loader.c ../hle.h ../dm.h $(GAME_H) | $(B) -@@ -371,3 +374,11 @@ $(B)/t_profile_runtime: t_profile_runtime.c ../hle_profile.c ../hle_profile.h .. +@@ -383,3 +386,11 @@ $(B)/t_profile_runtime: t_profile_runtime.c ../hle_profile.c ../hle_profile.h .. .PHONY: profile-runtime profile-runtime: $(B)/t_profile_runtime TMPDIR="$(abspath $(B))" $(B)/t_profile_runtime @@ -3537,7 +3537,7 @@ diff --git a/dm/host/tests/t_haptics_apple.m b/dm/host/tests/t_haptics_apple.m index f7a5dfb..40fdeb7 100644 --- a/dm/host/tests/t_haptics_apple.m +++ b/dm/host/tests/t_haptics_apple.m -@@ -99,7 +99,7 @@ static void test_dispatch_after(dispatch_time_t when, dispatch_queue_t target, d +@@ -99,7 +99,7 @@ @interface TestEngine : NSObject @property(copy) void (^startingReset)(void); @end @implementation TestEngine @@ -3616,7 +3616,7 @@ index f7a5dfb..40fdeb7 100644 assert(ds4_crc((const uint8_t *)"123456789", 9) == 0xCBF43926u); // the report's CRC32 (IEEE, reflected) dm_haptics_apple_shutdown(); diff --git a/dm/host/tests/t_hid.c b/dm/host/tests/t_hid.c -index 2c8761f..9680dfc 100644 +index 18bb4b4..a69a9c9 100644 --- a/dm/host/tests/t_hid.c +++ b/dm/host/tests/t_hid.c @@ -15,15 +15,16 @@ void dm_oe_execute(int index, const void *param, size_t n) { (void)index; (void) @@ -3647,7 +3647,7 @@ index 2c8761f..9680dfc 100644 // A one-player game ({No1, Handheld}): player 2's controller plays as player 1, No2 reads as not connected g_signals = 0; ids((const uint32_t[]){0, NPAD_HANDHELD}, 2); -@@ -249,7 +250,7 @@ static void t_script_hold(void) { +@@ -250,7 +251,7 @@ static void t_script_hold(void) { CHECK(buttons_of(0, STYLE_FULLKEY) == (DM_BTN_A | DM_BTN_B | DM_BTN_X), "script, controller and keyboard buttons are combined"); CHECK(buttons_of(0, STYLE_FULLKEY) == (DM_BTN_A | DM_BTN_B | DM_BTN_X), "a scripted hold survives controller updates after the press latch is consumed"); CHECK(lx_of(0) == -0x7000, "the scripted stick wins over controller drift"); @@ -3656,7 +3656,7 @@ index 2c8761f..9680dfc 100644 } static void t_script(void) { g_mode = 1; dm_pad_players(1); -@@ -260,7 +261,7 @@ static void t_script(void) { +@@ -261,7 +262,7 @@ static void t_script(void) { script_hold = t_script_hold; keys_main(NULL); script_hold = NULL; CHECK(buttons_of(0, STYLE_FULLKEY) == (DM_BTN_B | DM_BTN_X), "script release preserves the controller and keyboard buttons"); CHECK(lx_of(0) == 120, "script release preserves the controller stick"); @@ -3665,7 +3665,7 @@ index 2c8761f..9680dfc 100644 dm_input_set(0, 0, 0, 0, 0); dm_pad_set(0, 0, 0, 0, 0); buttons_of(0, STYLE_FULLKEY); g_steps[0] = (key_step){.btn = DM_BTN_Y}; keys_main(NULL); CHECK(buttons_of(0, STYLE_FULLKEY) == DM_BTN_Y, "a short scripted press is latched until the game reads"); -@@ -273,6 +274,7 @@ static void t_script(void) { +@@ -274,6 +275,7 @@ static void t_script(void) { script_set(0, 0, 0, 0, 0); buttons_of(NPAD_HANDHELD, STYLE_HANDHELD); g_mode = 1; dm_pad_players(1); g_nsteps = 0; g_steps = NULL; @@ -3673,7 +3673,7 @@ index 2c8761f..9680dfc 100644 } // DM_REC (GitHub issue #313): player 1's physical input written as DM_KEYS steps, read back by the DM_KEYS parser and played on the scripted path at the game's reads (keys_sync); -@@ -405,7 +407,7 @@ static void t_motion_threads(void) { +@@ -406,7 +408,7 @@ static void t_motion_threads(void) { } static void t_motion_live(void) { g_mode = 1; dm_pad_players(1); @@ -3682,7 +3682,7 @@ index 2c8761f..9680dfc 100644 uint32_t h; uint32_t id = 0; CHECK(h_hid_SixHandles(&h, 1, &id, STYLE_FULLKEY) == 1, "one six-axis handle for player 1"); -@@ -459,7 +461,7 @@ static void t_motion_live(void) { +@@ -460,7 +462,7 @@ static void t_motion_live(void) { } static void t_motion_hid(void) { g_mode = 1; dm_pad_players(3); ids((uint32_t[]){0, 1, NPAD_HANDHELD}, 3); g_phone_mode = 0; @@ -3691,7 +3691,7 @@ index 2c8761f..9680dfc 100644 float flat[3] = {0, 0, -1}, tilted[3] = {.5f, 0, -.8660254f}, turn[3] = {0, 0, 2 * (float)M_PI}; motion_now += 1000000000; dm_motion_clear(0); -@@ -490,16 +492,246 @@ static void t_motion_hid(void) { +@@ -491,16 +493,246 @@ static void t_motion_hid(void) { dm_pad_players(1); g_phone_mode = -1; } @@ -3938,7 +3938,7 @@ index 2c8761f..9680dfc 100644 t_rec(); { // Nmouse with no device (SDK 21's forms): the pointer range is kept, the merged handle 0 takes settings, the standard mouse and the handle queries report nothing int w = 0, h = 0; uint64_t std = 7; uint32_t npad = 9; -@@ -513,6 +745,7 @@ int main(void) { +@@ -514,6 +746,7 @@ int main(void) { CHECK(!h_hid_NmouseEnable(5, 1) && !h_hid_NmousePos(5, 0, 0), "an unknown handle does not"); CHECK(!h_hid_NmouseState(NULL, merged) && !h_hid_NmouseNpad(&npad, merged) && npad == 9, "no state and no controller behind the merged handle"); } @@ -4418,10 +4418,10 @@ index 4d70499..07010f3 100644 With an Apple developer account, Xcode can sign and install the app itself: diff --git a/docs/env_switches.tsv b/docs/env_switches.tsv -index 57e34f7..96ccd19 100644 +index 08e9eec..3f37f7b 100644 --- a/docs/env_switches.tsv +++ b/docs/env_switches.tsv -@@ -89,7 +89,7 @@ DM_JIT_SERVE path Folder of a `nn::jit` image (`jit.img`, `jit.tsv`, `jit.out`): +@@ -93,7 +93,7 @@ DM_JIT_SERVE path Folder of a `nn::jit` image (`jit.img`, `jit.tsv`, `jit.out`): DM_JOYCON_SPLIT switch `1` splits a Bluetooth Nintendo Switch Joy-Con pair on macOS (`0x057e:0x2006` and `0x057e:0x2007`) into two sideways controllers over `IOHIDManager` (`pad_joycon_mac.h`, requires Input Monitoring permission) with per-half 6-axis motion and `LeftHandle` / `RightHandle` Core Haptics rumble DM_KBD switch `=<text>`: the system keyboard returns that text as OK without showing an input box; unset with no window, it returns cancel DM_KBD_PLAYER switch `own` makes the Mac keyboard player 1 on its own, with controllers from player 2 on (by default the first controller shares player 1 with the keyboard) @@ -4430,7 +4430,7 @@ index 57e34f7..96ccd19 100644 DM_KEYS switch Scripted input: `<seconds or f<frame>>:<key>[+key][~hold or ~f<frames>][*count/interval],…` presses buttons, sticks, touches and `Shake` at those times; `=@<file>` reads the steps from the file DM_LANG player Game language as a language code (default `en-US`; the iOS app sets the one the IPA was packed with) DM_LAUNCHER switch `0` skips the iOS settings screen or the Mac settings window and starts the game at once, with the saved choices (on the Mac also `--no-launcher`) -@@ -233,7 +233,7 @@ DM_OODLE_JOB path `=<job file>`: the host runs the game's own Oodle decoder on e +@@ -238,7 +238,7 @@ DM_OODLE_JOB path `=<job file>`: the host runs the game's own Oodle decoder on e DM_OODLE_MODULES path The rewritten modules the Mac host's Oodle tool mode loads when `shader_split.py` decodes Trinity packs; set by the NSP script DM_OODLE_SERVE switch `=1`: the host answers decode requests on stdin / stdout with the game's own Oodle (or LZ5) decoder from the guest main thread before the SDK starts, until stdin ends DM_OPMODE player The operation mode the game sees: `0` handheld, `1` console (default: the profile's `op_mode`, console unless set); read once at startup @@ -4439,7 +4439,7 @@ index 57e34f7..96ccd19 100644 DM_PAD_MERGE switch `=1` merges every controller into player 1, as before, instead of giving each controller a player of its own (Mac and iOS) DM_PATCH_FRONT switch `=<bytes>`: the size of the patch area right before the module base for a long text (`BIG_TEXT`); set only by the rewriter's own rerun with the size it needs DM_PHONE_MOTION switch On iOS, whether the phone's own device motion drives player 1's six-axis sensor: `auto` (default) while no controller supplies fresh motion, `1` phone only, `0` never; each source keeps its own orientation -@@ -332,6 +332,8 @@ DM_SLOWFRAME diagnostic `=1` writes the per-frame timing breakdown to the log on +@@ -337,6 +337,8 @@ DM_SLOWFRAME diagnostic `=1` writes the per-frame timing breakdown to the log on DM_SPIN switch `0` makes the rewriter's spin gate return at once, so guest poll loops spin with no sleep; the gate stays (on by default) DM_SPIN_LIST diagnostic `=1` makes `patch_nso.py` print every guest poll loop it found and sends through a spin gate DM_SPIN_POLL switch `=<µs>`: how long a spin-gate wait polls before it sleeps with the yield back-off (default 2000; `0` yields on every pass) @@ -4447,8 +4447,8 @@ index 57e34f7..96ccd19 100644 +DM_STEAM_HID switch `=0` disables the Mac direct Steam Controller HID adapter and leaves input to Apple GameController; enabled otherwise DM_SVC diagnostic `=1` logs memory SVCs (`SetHeapSize`, `MapMemory`, `Map`/`UnmapPhysicalMemory`) with their addresses and sizes DM_SYMCHECK diagnostic `=1` checks every hashed module symbol lookup against a linear search and logs a red line for each mismatch - DM_TBUF_ALIGN diagnostic `=<bytes>` uses that value as the linear-texture alignment for buffer textures when it is larger than the device's `minimumLinearTextureAlignmentForPixelFormat`: a Mac can then reproduce a phone's larger alignment (a 64-byte one fails the buffers a device with 64 bytes cannot view), to check `tbuf_as_2d`'s aligned-row copy -@@ -364,6 +366,7 @@ DM_TOUCH_LOG diagnostic `=1` logs each touch-screen read with touch points (samp + DM_TBUF_ALIGN diagnostic `=<bytes>` uses that value as the linear-texture alignment when it is larger than the device's `minimumLinearTextureAlignmentForPixelFormat`, for buffer textures and for linear textures on a pool's buffer: a Mac can then reproduce a phone's larger alignment (a 64-byte one fails the buffers and linear textures a device with 64 bytes cannot view), to check `tbuf_as_2d`'s aligned-row copy and the upload of unaligned linear textures (`linear_fit`) +@@ -373,6 +375,7 @@ DM_TOUCH_STICK player iOS left touch-stick position: corner (default), higher or DM_UDC_SELFTEST diagnostic `=1` runs a start-up check of DontCare load and store on private depth-stencil and RGBA8 textures (clear, Load + DontCare, read back) and logs one result line DM_UE_SHADERS path Folder of the Unreal shader code archives decoded by `uepak.py`, for `shader_split.py` (default `ue_shaders` beside the RomFS); needed when the RomFS has Unreal paks DM_UE_TEXTURE_POOL switch Megabytes Unreal's texture streaming pool (`r.Streaming.PoolSize`) is raised to, overriding the profile's `ue_texture_pool`; `0` keeps the game's own pool. On iOS only this switch turns it on (`Documents/env.txt`) @@ -4766,7 +4766,7 @@ index 0000000..e633df5 +first. For repeated development builds, the packer's existing `--mac-sign` +option can use a stable signing identity, see [Mac app signing](development.md#a-mac-app). diff --git a/docs/reference/services.md b/docs/reference/services.md -index cb19ffe..f2ba097 100644 +index 8a97476..813b5b0 100644 --- a/docs/reference/services.md +++ b/docs/reference/services.md @@ -277,6 +277,27 @@ Most service calls can be found before they crash, before the first boot or the diff --git a/patches/0082-ios-shell-copy-a-game-s-packed-shader-image-into.patch b/patches/0082-ios-shell-copy-a-game-s-packed-shader-image-into.patch index 2ec32d329..e36f3ec84 100644 --- a/patches/0082-ios-shell-copy-a-game-s-packed-shader-image-into.patch +++ b/patches/0082-ios-shell-copy-a-game-s-packed-shader-image-into.patch @@ -27,14 +27,13 @@ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Upstream-Status: fork-only Reason: the route C shell's framework build (0050, fork-only) copies upstream's packed shader image (7249ee9); upstream has no DM_FRAMEWORK_ONLY Issue: LNPANDY/NX2Apple#781 - --- dm/ios/tests/test_framework_build.py | 82 +++++++++++++++++++++++++++- dm/ios/tools/build_ipa.sh | 15 ++++- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/dm/ios/tests/test_framework_build.py b/dm/ios/tests/test_framework_build.py -index fee9e53..0dccfbf 100644 +index 0d20b82..cfba21f 100644 --- a/dm/ios/tests/test_framework_build.py +++ b/dm/ios/tests/test_framework_build.py @@ -1,13 +1,15 @@ @@ -66,7 +65,7 @@ index fee9e53..0dccfbf 100644 PREAMBLE = 'set -u\nt() { :; }; die() { echo "$*" >&2; exit 1; }\ngp() { python3 "$PROFILE_TOOL" "$GAME" "$1"; }\n' CAN_SIGN = sys.platform == 'darwin' and shutil.which('codesign') and shutil.which('cc') -@@ -207,5 +212,76 @@ class FrameworkPackagingTests(unittest.TestCase): +@@ -228,5 +233,76 @@ class FrameworkPackagingTests(unittest.TestCase): self.assertEqual(verified.returncode, 0, verified.stdout + verified.stderr) @@ -144,10 +143,10 @@ index fee9e53..0dccfbf 100644 if __name__ == '__main__': unittest.main() diff --git a/dm/ios/tools/build_ipa.sh b/dm/ios/tools/build_ipa.sh -index 9f67e73..707411c 100755 +index f177de7..3263fd5 100755 --- a/dm/ios/tools/build_ipa.sh +++ b/dm/ios/tools/build_ipa.sh -@@ -255,7 +255,7 @@ if [ "$PREPARE_XCODE" = YES ]; then +@@ -256,7 +256,7 @@ if [ "$PREPARE_XCODE" = YES ]; then fi # DM_FRAMEWORK_ONLY: the framework target alone, unsigned (build_shell.sh's project embeds it and Xcode signs it with the shell), then the @@ -156,7 +155,7 @@ index 9f67e73..707411c 100755 # archive (pso/, DM_PSO_BUNDLE) and the icon the shell's library shows. The game files and add-on content stay out (data, Documents/games/<id>/) if [ -n "$FRAMEWORK_ONLY" ]; then t "xcodebuild (Release, unsigned, the game framework $FW_NAME with its own host)" -@@ -268,8 +268,17 @@ if [ -n "$FRAMEWORK_ONLY" ]; then +@@ -269,8 +269,17 @@ if [ -n "$FRAMEWORK_ONLY" ]; then [ -f "$BUILT/$FW_NAME" ] || die "no built framework $BUILT" OUTFW=$FRAMEWORK_ONLY/$FW_NAME.framework; mkdir -p "$OUTFW" rsync -a --delete --exclude /msl --exclude /pso --exclude /icon.png -- "$BUILT/" "$OUTFW/" || die "copy the framework to $OUTFW" diff --git a/patches/0083-test-table-of-uniform-handles-expects-the-pool-m.patch b/patches/0083-test-table-of-uniform-handles-expects-the-pool-m.patch new file mode 100644 index 000000000..ab2679a0d --- /dev/null +++ b/patches/0083-test-table-of-uniform-handles-expects-the-pool-m.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: v0lumehi <aljic.zijad@gmail.com> +Date: Fri, 6 Nov 2026 12:06:45 +0100 +Subject: [PATCH] test_table_of_uniform_handles expects the pool marker: the + texture read goes through dm_pool_mark + +The test failed on main. It asserted the texture read of a table of uniform +handles as (&base->e[0])[min(uint(_7), base->n)]. Since the pool marker +(POOL_MARK, dm_pool_mark) a read of a texture pool also sets the ID's bit in +the bitmap at seen, so pool_arrays emits (&base->e[0])[dm_pool_mark(base, +min(uint(_7), base->n))] for the texture. A sampler pool is still read +without it. The code is right and the test predates the marker. + +The test now checks the texture read with dm_pool_mark and the sampler read +without it. Only the test changes. + +Upstream-Status: forwarded seedds/nxnative#1478 +Reason: the test predates the pool marker and fails on upstream's main too +Issue: LNPANDY/NX2Apple#813 + +--- + dm/tools/tests/test_dmsh_pool_arrays.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dm/tools/tests/test_dmsh_pool_arrays.py b/dm/tools/tests/test_dmsh_pool_arrays.py +index d779daa..29b7502 100644 +--- a/dm/tools/tests/test_dmsh_pool_arrays.py ++++ b/dm/tools/tests/test_dmsh_pool_arrays.py +@@ -97,7 +97,8 @@ kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0 + } + ''' + out = NS['pool_arrays'](src) +- self.assertIn('(&spvDescriptorSet0.cp_t2da_cb196612_10570000->e[0])[min(uint(_7), spvDescriptorSet0.cp_t2da_cb196612_10570000->n)]', out) ++ self.assertIn('(&spvDescriptorSet0.cp_t2da_cb196612_10570000->e[0])[dm_pool_mark(spvDescriptorSet0.cp_t2da_cb196612_10570000, min(uint(_7), spvDescriptorSet0.cp_t2da_cb196612_10570000->n))]', out) # a texture read marks its ID as seen ++ self.assertIn('(&spvDescriptorSet0.cp_t2da_cb196612_10570000Smplr->e[0])[min(uint(_7), spvDescriptorSet0.cp_t2da_cb196612_10570000Smplr->n)]', out) # a sampler read does not + self.assertIn('array<texture2d<float>, 32> fp_t2d_cb14_0 [[id(2)]];', out) # a pointer takes one ID; an array of 32 stays an array (nothing in the game's buffers is that short now but images and buffer textures) + b = {e['msl_name']: e for e in NS['reflect']('k', 'cs', out, {'sbufs': []})} + t, s = b['cp_t2da_cb196612_10570000'], b['cp_t2da_cb196612_10570000Smplr'] diff --git a/patches/series b/patches/series index 907dddffb..6815aba95 100644 --- a/patches/series +++ b/patches/series @@ -50,3 +50,4 @@ 0080-keep-the-selected-python-when-generating-the-mac.patch 0081-support-steam-controller-and-shared-mac-control.patch 0082-ios-shell-copy-a-game-s-packed-shader-image-into.patch +0083-test-table-of-uniform-handles-expects-the-pool-m.patch