An office suite for the Nintendo DSi. Read and write Word, Excel, OpenDocument, EPUB and PDF files off your SD card, on a handheld from 2008.
The editor. The document takes one page and the tool palette the other — here the text tab, with bold/italic/underline/strike, outline and shadow, superscript and subscript, the size stepper, the face picker and the ink swatches. L swaps the two over. The status line reports the draft name, the zoom, and what L and R do.
Settings. Every row explains itself on the facing page rather than in a manual. This is the reading-hand question, which is the first thing a fresh install asks: only the bottom screen can be touched, so the book turns over to put that screen under your stylus hand — and turns the d-pad with it.
NitroOffice renders documents as a book across both screens — the ratio of a DS spread is much closer to a page than a single screen is, so a two-page spread reads like a paperback rather than like a scrolling window.
Everything on screen is drawn by the CPU: the glyphs come out of FreeType straight into VRAM, and the whole interface is generated in code rather than built from textures or a bitmap font. There is no 3D and no tilemap. Every format the program reads is parsed into one internal document model, so the reader, the spreadsheet grid, the editor and the exporters all work on the same thing regardless of what the file on the card happened to be.
| Format | Extension | Read | Write |
|---|---|---|---|
| Word (OOXML) | .docx |
✅ | ✅ |
| Word 97–2003 | .doc |
✅ | — |
| Excel (OOXML) | .xlsx |
✅ | — |
| Excel 97–2003 | .xls |
✅ | — |
| OpenDocument Text | .odt |
✅ | ✅ |
| OpenDocument Spreadsheet | .ods |
✅ | — |
| EPUB | .epub |
✅ | — |
.pdf |
✅ | — | |
| Images | .png .jpg .jpeg .gif .bmp |
✅ | — |
| NitroOffice document | .nof |
✅ | ✅ |
.nof is the program's own format and the only lossless one: it is the whole
document model written straight out, so a document saved and reopened is exactly
what it was. The DOCX and ODT writers produce real packages that open in Word
and LibreOffice.
A file is identified by its extension and by its first bytes (ZIP, OLE2,
%PDF-, NOC1). When the two disagree the info page says so, instead of
failing halfway through an import with nothing to explain it.
- Two-page spread or one page at a time.
- Paper, Sepia and Night colour schemes; the document's own colours are kept and only moved as far as the scheme needs.
- Four text sizes (10/12/14/16 px, shown as 83 %–133 %). Everything the document made larger or smaller keeps its proportion, so a heading stays a heading.
- Line spacing, margins, forced or suppressed justification, optional hyphenation.
- A Contents list built from the document's headings, and a Jump to a place bar for documents that have none.
- Form controls — check boxes and radio buttons — can be answered from the reader. A questionnaire somebody sent you is something to fill in, not something to open an editor for.
- Spreadsheets get their own screen: a scrollable grid, one sheet at a time, with number formats applied.
Five palette tabs, on whichever of the two screens you put them:
| Tab | What is on it |
|---|---|
| text | bold, italic, underline, strike · outline, shadow · superscript, subscript · size · font face · ink colour (four quick swatches plus a full picker) |
| para | left / centre / right / justified · left indent · first-line indent · space before and after · page break before |
| insert | table (rows × columns) · picture · line break · tab · new page · check box · radio button |
| table | add and delete rows and columns |
| file | new · save · on-screen keyboard · "dpad types" · undo / redo · drafts · save to card · close |
There is an on-screen keyboard, and the d-pad can be handed to it so text can be typed without the stylus. Undo and redo are held in a 256 KB history, which is dozens of typing steps or a handful of table snapshots; the oldest entries are dropped when it fills.
Six faces ship in the ROM: DejaVu Sans, Serif and Sans Mono for the three generic classes, and Liberation Sans, Serif and Mono as metric-compatible substitutes for Arial, Times New Roman and Courier New — same advance widths, so a document written against those keeps its line breaks. A substitution table maps thirty-odd real font names onto these six.
Each face is subset at build time to Latin-1 plus the punctuation the program actually emits; a full DejaVu Sans is ~750 KB and six unsubsetted families would be most of a ROM. Bold is synthesized by a one-pixel spread and italic by a twelve-degree slant, which divides the number of embedded faces by four.
You can add your own faces — see Your own fonts.
Opening a large DOCX takes a moment; opening it a second time does not. Imported documents are written to a cache on the card in the internal format, and the browser and info page tell you which documents will reopen instantly. Unsaved new documents live there too, as drafts, and the File tab lists them so a draft can never be saved and then lost. Edits are kept there as well — see Saving and exporting.
- A Nintendo DSi with homebrew, and an SD card. NitroOffice is built as a DSi application and uses the DSi's extended memory — it will not run on an original DS or DS Lite.
- Or melonDS in DSi mode, with a DSi NAND image.
- Download
nitrooffice.ndsfrom the latest release. - Copy it to your SD card. Put it in the folder your documents are in — the file browser opens in the directory the ROM was launched from, so the documents are already in front of you. Anywhere works; that is just the shortest route.
- Launch it from the DSi menu (Unlaunch) or from TWiLight Menu++.
- On the first run it asks which hand you hold the stylus in. Answer it once and every screen afterwards is drawn the right way round.
A launcher that passes arguments, such as an .argv file, can hand NitroOffice
a document to open. The first argument after the ROM opens in the reader
instead of the file browser. Backing out of it lands in the document's own
folder.
sd:/Documents/report.docxis used as written./Documents/report.docxis looked for on the card the ROM runs from.report.docxis looked for in the folder the ROM runs from.- If the path is not on the card it names, the other card is tried with the
same path, since loaders do not agree on whether the SD card is
sd:/orfat:/. - A document that cannot be read stops on its info page, which says why. A folder opens in the browser.
Hold SELECT at boot to see the argument the launcher passed and the path it was resolved to.
Run NitroOffice once from TWiLight Menu++ and your documents appear in the menu's
own file list, each with its own icon. Selecting one opens it in NitroOffice.
This needs a TWiLight Menu++ build that reads extras/config.<ext>.ini
launcher configs.
On a card that has _nds/TWiLightMenu, every launch checks and, if needed,
writes:
<card>/_nds/nitrooffice.nds a copy of the ROM
<card>/_nds/TWiLightMenu/extras/config.<ext>.ini one per format
<card>/_nds/TWiLightMenu/icons/NitroOffice/<ext>.png the file-list icons
The formats are .docx .doc .odt .nof .xlsx .xls .ods .pdf .epub.
- The ROM copy is what the configs launch, so it has to live somewhere fixed. It is copied only when it is missing or from a different build, a chunk per frame so the first screen is not held up, and under a temporary name until it is complete.
- A config written by NitroOffice starts with
; Written by NitroOffice.and is updated whenever a newer NitroOffice writes it differently. To keep your own edits, delete that line. A config without it, including one you wrote by hand before installing, is never touched. Delete it to get NitroOffice's. - Nothing is written to a card without TWiLight Menu++.
The SELECT self check reports whether the ROM copy is up to date.
<card>/_nds/NitroOffice/
settings.dat your choices, in the internal format
cache/index.dat the cache table
cache/<id>.doc one cached document each
fonts/ faces you added yourself (see below)
<card>/NitroOffice/ where "save to card" writes by default
A card with nowhere to write is not an error: settings fall back to defaults and the cache simply never hits.
Drop TrueType files into <card>/_nds/NitroOffice/fonts and they appear in the
editor's face picker.
.ttfonly. The toolchain has no CFF driver, so.otfwill not load.- 768 KB total. A face is resident for the whole session, so the budget is fixed at boot.
- The folder is read once, at boot. Faces that will not load are listed with a reason rather than hidden, so you can tell "wrong format" from "not read at all".
Only the bottom screen is touchable, which is why the reading-hand setting exists: choosing your hand turns the whole book over so the touchable screen falls under your stylus, and remaps the d-pad to match. Every direction below is the direction you actually press, whichever way round the console is held.
| Input | Action |
|---|---|
| d-pad, A | Choose a drive (SD card, flashcart) |
| START | Font specimen sheet |
| SELECT | Settings |
| Input | Action |
|---|---|
| d-pad, A, tap | Move and open |
| B | Up a level; from a drive root, back to the volume list |
| X | New blank document |
| START | Font specimen sheet |
| SELECT | Settings |
Selecting a file opens an info page first — format, size, whether it is cached, read speed, and its first bytes. From there A reads it and X opens it in the editor (spreadsheets are read-only, so they offer only A). B goes back.
| Input | Action |
|---|---|
| L, left, up | Previous page |
| R, right, down, A | Next page |
| Tap the far / near half of the touch page | Next / previous page |
| X / Y | Larger / smaller text |
| SELECT | Menu: Contents · Jump to a place · Text size · Edit this document · Reading options |
| B, START | Close the document |
Tapping a check box or radio button in the document answers it.
| Input | Action |
|---|---|
| d-pad | Move a cell |
| Drag | Scroll the grid |
| L / R | Previous / next sheet |
| X / Y | Larger / smaller text |
| B, START | Close |
| Input | Action |
|---|---|
| Tap the document | Place the caret; tapping a check box or radio button answers it instead |
| Drag on the document | Select text |
| Tap a tool or a key | Press it |
| R | Next palette tab |
| L | Swap the palette and the document between the two screens |
| START | Save |
| d-pad | Types on the on-screen keyboard when dpad types is on; otherwise moves the caret, when the document is on the far page and the stylus cannot reach it |
| Hold SELECT + d-pad | Extend the selection |
| B | Leave a picker page (font, colour, save-as) |
B does not close the document. Closing is the File tab's close, which is something you go and find — losing your place and your undo history to a reflex is not something an editor should allow.
| Input | Action |
|---|---|
| d-pad | Move between rows |
| A | Change the value |
| B, SELECT | Back |
Rows: Reading hand · Text size · Pages · Paper · Line spacing · Margins · Justify · Hyphens. The facing page explains whichever row is selected.
These are two different things, and the difference matters:
- save (File tab, or START) keeps your edits in the cache, filed under the document they came from, so reopening that file brings the edited version back on the page you left it. A brand-new document becomes a draft there and is listed in the File tab. Your original file on the card is not touched.
- save to card writes a real file you can take off the console:
.nof,.docxor.odt. The default destination is<card>/NitroOffice/, and you can browse to another folder. Pressing B during an export cancels it and takes the half-written temporary with it — the destination is never left damaged.
So: save often, save to card when you want the document to exist outside NitroOffice.
START from the browser or the volume list opens a specimen sheet: up/down change the size, A cycles the family, B toggles bold, Y toggles italic, and START leaves. It is there to prove the text engine and the glyph cache are behaving.
Hold SELECT while the program boots for a self-check console: what mounted, what is in the start directory (filenames in text and in hex, which settles whether the filesystem is handing back UTF-8), whether a document opens, how many supplied fonts were found, and how much contiguous memory is free.
- The Wonderful toolchain with BlocksDS
(
WONDERFUL_TOOLCHAIN,BLOCKSDS,BLOCKSDSEXT). - nitro-engine-advanced installed under
${BLOCKSDSEXT}. - Python 3, and
g++if you want to run the host tests.
FreeType, libpng and zlib come from the toolchain. pugixml, miniz, yas, the read
half of PDFio and stb_image are vendored in thirdparty/.
./setup-env.sh # once: creates ./env and installs architectds_nea_mod
./make.sh # build -> nitrooffice.nds
./make_debug.sh # build with DEBUG_BUILD (repaint timings, stderr log)
./clean.sh # remove build outputs
./make.sh --compdb # generate compile_commands.json for clangdmake.sh activates the venv and exports the toolchain variables itself, so it
works from a bare IDE terminal. The same commands are wired up as VS Code tasks
in .vscode/tasks.json.
Two things worth knowing:
- Read the build output, not the exit code.
make.shcan exit 0 even when ninja failed to compile something. - The build is memory-heavy. On a modest machine, drive ninja with
-j 2.
There are 37 host-side suites covering the parts that are pure logic — the document model, the ZIP reader, the XML pull parser, every importer, the layout engine, the exporters, the image decoders. They compile the project's own sources against the stubs in tests/host/stubs/, so nothing here is a copy of the code under test, and no DS toolchain is needed.
./tests/host/run.sh # build and run everything
./tests/host/run.sh test_zip # one suite
SAN=1 ./tests/host/run.sh # with ASan and UBSanSome extractor tests read real documents that are not in this repository; point
NITROOFFICE_TEST_DOCS at a folder of them, or let those tests skip.
| Directory | What is in it |
|---|---|
| source/app/ | Screens, input, settings, the on-screen keyboard |
| source/doc/ | The document model, styles, editing, undo, cache |
| source/import/ | DOCX, DOC, XLSX, XLS, ODT, ODS, EPUB, PDF, ZIP, CFB, XML |
| source/export/ | The DOCX, ODT and PNG writers |
| source/fs/ | Volumes, paths, file reading and safe writing |
| source/media/ | PNG, JPEG, GIF and BMP decoders |
| source/render/ | Book view, widgets, icons, themes, drawing |
| source/text/ | FreeType, metrics, line breaking, pagination |
| thirdparty/ | pugixml, miniz, yas, PDFio (read side), stb_image |
- Latin script only. The bundled faces are subset to Latin-1 and there is no shaper, so complex scripts and right-to-left text are not laid out.
- PDF is read-only, and is reflowed into the document model rather than reproduced page-for-page. A PDF that is mostly layout will not survive it.
- Export is limited to
.nof,.docxand.odt. Spreadsheets cannot be written back at all. - User fonts must be TrueType, within a 768 KB budget.
- DSi only — the build uses the DSi's extended EWRAM.
- Progressive JPEG and interlaced PNG go through a fallback decoder inside a fixed memory budget; very large ones of either may be refused.
- Very large documents are truncated rather than failing, and the reader says "first part only" when that happened.
NitroOffice is licensed under the Apache License 2.0.
Full third-party and font attributions are in NOTICE — pugixml, miniz, yas, PDFio, stb_image, libnds/BlocksDS, Nitro Engine Advanced, libpng, zlib, and the DejaVu and Liberation font families.
Portions of this software are copyright © The FreeType Project (www.freetype.org). All rights reserved.
Liberation is a trademark of Red Hat, Inc.

