Erik's Blog - Talking to Your 3DS Via FTP - 2024-11-25

Why FTP?

As you may have read, I recently started tinkering with my old Nintendo 3DS. The most direct way to get data onto the SD card which serves as the 3DS’ “hard drive” is to take it out, connect it to your computer via an SD card reader and copy over the files. However, it becomes quite tedious after a while to do this repeatedly, especially because the 3DS’ SD card slot is much less accessible than its newer counterpart, the Nintendo Switch, where it lives just behind its own little door, easily accessible for anyone. But not the 3DS, no, no. The 3DS hides its SD card behind its back plate requiring you to remove (and then reapply) four screws as well as snap out (and back in) the back plate.

Is it difficult to unscrew four screws? No! Is it unnecessary? Yes!

Luckily, Luma3DS allows installation of an FTP server: FTPD. Now, all that’s missing is an appropriate client. However, when looking for one, it seemed to me that nowadays FTP is a bit of a niche protocol.

I tried a couple of GUI clients like Filezilla, but for unknown reasons I kept getting interruptions, crashes, and similar issues. Mind you, may those be skill-issues? Very likely, but nonetheless, I was in the market for another FTP client.

lftp to the Rescue!

Along came lftp, a convenient little command line program; and it just worked. A quick nix shell nixpkgs#lftp and I was talking to my 3DS via FTP.

To connect to our 3DS, we simply type

lftp ftp://[3DS address]

where [3DS address] is the address shown upon startup of FTPD. This already lets us interact with the remote file system; we can ls to see the contents of the current remote directory, cd into a different one, rm/rmdir to delete stuff, etc.

With the put command we can transfer files; like an experimental executable we built that will likely crash the 3DS.

put ~/development/hello-world/zig-out/hello-world.3dsx -o 3ds/hello-world.3dsx

With the get command we can retrieve files from the 3DS; like the dump files after crashing it with the executable we just transferred, so we can look at them in a hex editor and still not know what went wrong.

get luma/dumps/arm11/crash_dump_00000001.dmp -o ~/dump1.dmp

Finally, to transfer directories, we can use the mirror command. Maybe we overdid it with the crashes and there’s too many to transfer individually, so we mirror the whole dumps folder. The -P is short for --parallel and defines the number of parallel file transfers.

mirror -P 10 luma/dumps ~/3ds_dumps

By default mirror copies from remote to local, but we can reverse this with the -R or --reverse flag. Maybe we want to transfer all our input files for Advent of Code, for example.

mirror -R -P 10 ~/development/aoc24/input aoc24/input

Final Thoughts

All of this has made it a lot easier to tinker with the data on the my 3DS. I know I could mostly run things on an emulator for testing first, and I do for some stuff, but it just feels good to have the programs run directly on the 3DS.

A midway point is 3dslink which comes with devkitPro. By pressing the Y-button on the homebrew launcher, you can transfer and run local executable directly on the 3DS via:

3dslink zig-out/aoc24.3dsx