The project's README from github.com/jhaizhou-ops/d2r-mobile. As one markdown file: README.md.
An open-source iOS compatibility layer for Diablo II: Resurrected with the Reign of the Warlock expansion. It brings the Nintendo Switch release you bought, expansion included, to iPhone and iPad in full: the original game code runs natively, every control is rebuilt for touch, the frame rate holds at 60 fps, and you are in a game one second after the main menu.
This is not a tech demo that "sort of runs". It is a mobile version polished for daily play, from your first character all the way through the endgame.
Platforms: iPhone / iPad (iOS 26 and later) · Game version: Switch release with Reign of the Warlock, 3.3.94012 (shown as 1.0.34.0 on the console) · English / 中文 · License: MIT · Bring your own game files
中文: README.zh-CN.md
| 100% | 60 fps | about 1 s | about 3.8 W |
|---|---|---|---|
| The original gameplay, because it is the original program | Steady, with no drops once the device warms up | From the main menu into a game | Whole-device power draw at 60 fps |
It is:
It is not:
All 44 iPhones and iPads in the closed beta — 35 devices registered for direct install (25 iPhones and 10 iPads), plus 9 TestFlight accounts with one device each — completed Normal, Nightmare and Hell with multiple characters, and the experience was excellent. From an iPhone 14 Pro Max with 6 GB of memory to the latest iPad Pro, not one device fell behind.
| Models | |
|---|---|
| iPhone | 14 Pro Max, 15, 15 Pro, 15 Pro Max, 16 Plus, 16 Pro, 16 Pro Max, 17 Pro Max and more |
| iPad | iPad Air, iPad mini, iPad Pro 11-inch and 12.9 / 13-inch (including M2 and M4) and more |
| Lowest model tested | iPhone 14 Pro Max (A16, 6 GB): a flawless 60 fps |
iPhone and iPad share a single install package.
Measured on a real device (iPhone 17 Pro Max):
| Item | Reading |
|---|---|
| In-game frame rate | 60 fps, steady, with no drops once the device warms up |
| App launch | 0.15 s |
| To the main menu | about 1 s |
| From the main menu into a game | about 1 s |
| Quick Restart, back in a game | about 1.2 s |
| Longest frame when changing acts | about 0.1 s |
| Whole-device power, 60 fps | about 3.8 W (in camp, 50% brightness): nearly 5 hours of continuous play on a full charge |
| Whole-device power, power saver mode at 30 fps | about 1.85 W, about half the 60 fps figure, for about twice the battery life |
Measurement conditions: power is whole-device power measured on 2026-10-10 on an iPhone 17 Pro Max, standing in the Act I camp at 50% brightness, with 60 fps and power saver mode measured in the same scene; battery life is calculated from the measured power and the battery capacity, not by playing from full to empty. Launch and game-entry times were measured on the same model in 2026-10; frame rate and act-change readings were taken on the same model on 2026-10-12 while moving through areas and changing acts after entering a game.
The install package is only a little over 100 MB. The game resources (about 5.6 GB) are placed on the phone separately, and updating the app touches neither your saves nor the resources.
Why it is this fast: the program executes natively, and not a single instruction goes through emulation; graphics go straight to Metal; every shader is translated offline and compiled ahead of time, so nothing is compiled when you enter a new scene. Older chips such as the A16 do not support the game's original compressed texture format, so we built a supplementary texture pack for them, and they run at full frame rate too.
This is not a gamepad pasted onto the screen. Every panel and every action was rebuilt for fingers under one design principle: keep the original interface language, and use gestures instead of adding buttons to the screen. Text sizes and tap targets are sized for fingers. You know how to play the moment you pick it up, and it stays comfortable over long sessions.
Connect a controller and press any button: the interface switches to the original controller layout, with button prompts at the bottom of every panel. Touch the screen and you are back in the touch interface instantly. Inventory, stash, skill tree, Chronicle and settings are fully controller-navigable, with the same button mapping as on Switch.
Finishing the original game is only the beginning. Without touching the original rules, we added a full set of endgame content that keeps you farming.
Enter through the red portal in Harrogath after completing Hell. There are 100 levels, and the monsters grow stronger with each one; fill the progress bar and slay the Rift Guardian within 15 minutes to clear a level. Every 2 levels cleared grants +1 Skill Point and +5 Stat Points, and the skill level cap rises from 20 to 40. Every 10 levels has a first-clear reward, and clearing level 10 unlocks six new equipment slots for your mercenary.
| Auto-recast buffs | Buff skills you have learned do not need a slot on the skill bar: when a buff runs out, it is recast the moment you stand still; buffs that come from your secondary weapons (such as Battle Orders from Call to Arms) swap weapons, recast and swap back on their own |
| Potions and belt | Auto-drink has two tiers: a healing potion below 50% Life, a rejuvenation potion below 30%; your mercenary is fed automatically by the same rule. Potions on the ground are picked up automatically when the belt has room; double-tap an empty belt slot to refill the whole belt |
| Loot filter | From Nightmare on, the original loot filter is switched on automatically to hide clutter: light filtering in Nightmare, heavy in Hell |
| Auto-pickup | Low runes and gems go straight into the Pouch; the high ones are left for you to pick up yourself |
| Valuable drop alerts | High runes and Unique items raise a light beam and land with a different drop sound; the phone vibrates at one of three strengths — light, medium or heavy — according to value |
| Runeword helper | Long-press a socketed base to list the Runewords it can make and their stats; once you have the runes, socket them all in one tap |
| Minimap guidance | Every map marks its Waypoint and exits; every quest goal is on the map; when a target is off screen, its name is written along the screen edge |
| Chores removed | Free identification, free Town Portal with no scrolls needed, spawning right beside the Waypoint, Quick Restart, refreshable vendor stock |
| Saves | Saved immediately when you switch apps, swipe the app away or quit; iCloud sync across devices (requires signing with a paid developer account) |
Most features can be toggled one by one in game under Options → Custom.
The whole game switches between English and Chinese under Options → Custom; after a restart, both the interface and the voice acting are in the language you chose. In English, items, skills and monsters use the original Diablo II wording, and everything we added — Greater Rift, Chronicle, the settings page, controller prompts — is in English too.
flowchart LR
G["Your game files<br/>(program + resources)"] --> P["Rewriter<br/>changes only a few instructions"]
G --> S["Shader translation<br/>original → Metal"]
P --> H["Host<br/>system services"]
S --> X["Graphics layer<br/>the graphics API on Metal"]
H --> A["iOS app<br/>touch and interface"]
X --> A
| Part | Where | What it does |
|---|---|---|
| Program rewriter | dm/tools/patch_nso.py |
Rewrites only the few instructions that cannot run unchanged under Apple's kernel (thread-pointer registers, system calls, timers) and leaves the rest of the program byte-for-byte as shipped |
| Host | dm/host |
Loads the rewritten program and provides the platform services it calls |
| Graphics | dm/gfx |
Implements the game's graphics API on Metal; shaders are translated offline from the game's originals |
| iOS app | dm/ios |
The app shell, touch controls and interface |
| Verification | dm/verify |
Checks that the rewritten code behaves exactly like the original, plus the regression gates every change goes through |
Why you can trust it: the stability is not luck. The test and verification tooling is part of the project — unit tests for the host and graphics layers, an equivalence check for every rewritten instruction, scripted walkthrough regression for the touch interface, and on-device smoke tests. Start with make -C dm/host/tests run, make -C dm/gfx/tests run and dm/verify/regress.sh (run them inside the Python environment prepared in step 2 below; regress.sh also needs brew install llvm). How to contribute is in CONTRIBUTING.md.
Both routes start with the files of the game you bought: the Switch release of Diablo II: Resurrected plus the Reign of the Warlock expansion, updated to 3.3.94012 (the version the console shows is 1.0.34.0). This project is built for the version with that expansion; using it without the expansion is not supported and has not been verified. How to prepare them is in docs/GAME_FILES.md.
| With a Mac | Without a Mac | |
|---|---|---|
| Computer | An Apple silicon Mac with Xcode installed | Windows / Linux / a Mac without Xcode |
| Method | A full build from source | Download a "shell" with no game content, use the packaging tool to add your own game content, then install it with a sideloading tool |
| Status | The main route; everything above comes from it | Works end to end on Windows, from making the package to playing on a device, and the game program produced on the two systems is byte-for-byte identical; the one step nobody has tried yet is signing with a sideloading tool — try it and tell us |
| Instructions | The three steps below | docs/NO_XCODE.md |
The Apple account used for signing: a paid developer account gives you everything; a free Apple ID works too (no iCloud save sync, and the app has to be re-signed and reinstalled every 7 days). The full list of dependencies is in docs/DEPENDENCIES.md.
Put your game files in the workspace (default ~/dm-work, or set DM_ROOT):
~/dm-work/data/switch/3.3.94012/Update/ExeFS/ main, sdk, subsdk0
~/dm-work/data/switch/3.3.94012/Update/RomFS/ data/…
Prepare the workspace. This checks your tools, rewrites the game program, and translates the game's shaders to Metal. The first run takes a while; later runs skip what is already done.
./setup.sh
source ~/dm-work/build/cache/dm/venv/bin/activate # run everything after this inside the Python environment setup prepared; if you changed DM_ROOT, use it in place of ~/dm-work
Fill in your signing details and build the app:
cp dm/ios/local.env.example dm/ios/local.env # set DM_TEAM_ID and DM_BUNDLE_ID; with a free Apple ID also set DM_FREE_ACCOUNT=1 and DM_IOS_UDID, and keep the phone connected while building
cd dm/ios && ./run.sh build # targets both iPhone and iPad by default; for iPhone only: DM_DEVICE_FAMILY=1 ./run.sh build
The first time you open the app on your phone, it asks for the "game resource folder" — the 18 resource archives from your game files. Where to put them is in the section "The "game resource folder" that goes on the phone" of docs/GAME_FILES.md.
./setup.sh check is a health check: it reports the state of this computer, the tools, the game files and the signing configuration item by item, and for everything missing it says where to get it and which command to run next; it changes nothing. If what you have is a whole game package (.xci / .nsp), ./setup.sh <game package> <prod.keys> first extracts the game files into place and then carries on (install hactool first). Run ./setup.sh again after every update to a new commit: it rebuilds the patched binaries and the shader library for the current sources (steps that are already up to date are skipped). It also rewrites three files in the repository that change with your local tool fingerprints (dm/gfx/tools/dmsh_chain.lock, dm/gfx/nvn/dmsh_chain.h, dm/gfx/nvn/c1_full.tsv); that is expected, do not commit them.
Download our prebuilt "shell" package (D2RMobile-shell.ipa), which contains no game content, from this repository's Releases page, add your own game files, make the install package on Windows or Linux, then sign and install it with a sideloading tool. On this route only, because the computer making the package has no Apple shader compiler, the first launch on the phone spends about 7 minutes compiling shaders (once, with a progress bar); a package built with Xcode has no such step. The steps, and how far each one has been verified, are in docs/NO_XCODE.md.
| Item | Status |
|---|---|
| Online play | Out of scope; fully offline |
Game packages in .nsp format |
Not verified (only .xci was tested) |
| Signing and installing with a sideloading tool (Sideloadly, AltStore and the like) | Not verified; open an issue and tell us how it went |
| Making the package on Linux | Not verified (verified on Windows and Mac) |
| The "one-line verdict" and "legacy Chinese name" entries in item notes | These two content tables are not shipped with the repository; you can supply your own, see dm/ios/tools/annot/README.md |
Taking it to other platforms: this approach is not limited to iPhone and iPad. It ports directly to Apple silicon Macs — our daily regression tests run the very same package on a Mac. Android phones use the same class of processor, so the game program executes natively there as well, and the route is fully open: the only parts to replace are the sides of the host and the graphics layer that face the platform. The author has no Android phone at hand, and would love to see someone bring it to Android.
| Document | Covers |
|---|---|
| dm/README.md | How the whole thing works, directory layout, common commands, the host's source files |
| dm/gfx/README.md | The graphics layer and the shader translation chain |
| dm/ios/README.md | The iOS shell, building and smoke testing |
| dm/verify/README.md | What each verification tool checks and what it needs |
| docs/GAME_FILES.md | Game files: which ones you need, how to get them from your own console, where to put them |
| docs/DEPENDENCIES.md | Dependencies and third-party licenses |
| docs/NO_XCODE.md | Building without Xcode and without a Mac |
| docs/DERIVED.md | Game-related material in this repository: what is here, what is not, and why |
You must own the game, including the Reign of the Warlock expansion. This is an independent, non-commercial project. It is not affiliated with, endorsed by, or sponsored by Blizzard Entertainment or Nintendo. Diablo and Blizzard Entertainment are trademarks of Blizzard Entertainment, Inc.; Nintendo Switch is a trademark of Nintendo. Use it only with a copy of the game that you own.
MIT, see LICENSE. Third-party code keeps its own license in its own directory; see docs/DEPENDENCIES.md.