--- /dev/null
+/**
+ * @file font.h
+ * @brief Shared font support.
+ */
+#pragma once
+#include <3ds/types.h>
+
+///@name Data types
+///@{
+
+/// Character width information structure.
+typedef struct
+{
+ s8 left; ///< Horizontal offset to draw the glyph with.
+ u8 glyphWidth; ///< Width of the glyph.
+ u8 charWidth; ///< Width of the character, that is, horizontal distance to advance.
+} charWidthInfo_s;
+
+/// Font texture sheet information.
+typedef struct
+{
+ u8 cellWidth; ///< Width of a glyph cell.
+ u8 cellHeight; ///< Height of a glyph cell.
+ u8 baselinePos; ///< Vertical position of the baseline.
+ u8 maxCharWidth; ///< Maximum character width.
+
+ u32 sheetSize; ///< Size in bytes of a texture sheet.
+ u16 nSheets; ///< Number of texture sheets.
+ u16 sheetFmt; ///< GPU texture format (GPU_TEXCOLOR).
+
+ u16 nRows; ///< Number of glyphs per row per sheet.
+ u16 nLines; ///< Number of glyph rows per sheet.
+
+ u16 sheetWidth; ///< Texture sheet width.
+ u16 sheetHeight; ///< Texture sheet height.
+ u8* sheetData; ///< Pointer to texture sheet data.
+} TGLP_s;
+
+/// Font character width information block type.
+typedef struct tag_CWDH_s CWDH_s;
+
+/// Font character width information block structure.
+struct tag_CWDH_s
+{
+ u16 startIndex; ///< First Unicode codepoint the block applies to.
+ u16 endIndex; ///< Last Unicode codepoint the block applies to.
+ CWDH_s* next; ///< Pointer to the next block.
+
+ charWidthInfo_s widths[0]; ///< Table of character width information structures.
+};
+
+/// Font character map methods.
+enum
+{
+ CMAP_TYPE_DIRECT = 0, ///< Identity mapping.
+ CMAP_TYPE_TABLE = 1, ///< Mapping using a table.
+ CMAP_TYPE_SCAN = 2, ///< Mapping using a list of mapped characters.
+};
+
+/// Font character map type.
+typedef struct tag_CMAP_s CMAP_s;
+
+/// Font character map structure.
+struct tag_CMAP_s
+{
+ u16 codeBegin; ///< First Unicode codepoint the block applies to.
+ u16 codeEnd; ///< Last Unicode codepoint the block applies to.
+ u16 mappingMethod; ///< Mapping method.
+ u16 reserved;
+ CMAP_s* next; ///< Pointer to the next map.
+
+ union
+ {
+ u16 indexOffset; ///< For CMAP_TYPE_DIRECT: index of the first glyph.
+ u16 indexTable[0]; ///< For CMAP_TYPE_TABLE: table of glyph indices.
+ /// For CMAP_TYPE_SCAN: Mapping data.
+ struct
+ {
+ u16 nScanEntries; ///< Number of pairs.
+ /// Mapping pairs.
+ struct
+ {
+ u16 code; ///< Unicode codepoint.
+ u16 glyphIndex; ///< Mapped glyph index.
+ } scanEntries[0];
+ };
+ };
+};
+
+/// Font information structure.
+typedef struct
+{
+ u32 signature; ///< Signature (FINF).
+ u32 sectionSize; ///< Section size.
+
+ u8 fontType; ///< Font type
+ u8 lineFeed; ///< Line feed vertical distance.
+ u16 alterCharIndex; ///< Glyph index of the replacement character.
+ charWidthInfo_s defaultWidth; ///< Default character width information.
+ u8 encoding; ///< Font encoding (?)
+
+ TGLP_s* tglp; ///< Pointer to texture sheet information.
+ CWDH_s* cwdh; ///< Pointer to the first character width information block.
+ CMAP_s* cmap; ///< Pointer to the first character map.
+
+ u8 height; ///< Font height.
+ u8 width; ///< Font width.
+ u8 ascent; ///< Font ascent.
+ u8 padding;
+} FINF_s;
+
+/// Font structure.
+typedef struct
+{
+ u32 signature; ///< Signature (CFNU).
+ u16 endianness; ///< Endianness constant (0xFEFF).
+ u16 headerSize; ///< Header size.
+ u32 version; ///< Format version.
+ u32 fileSize; ///< File size.
+ u32 nBlocks; ///< Number of blocks.
+
+ FINF_s finf; ///< Font information.
+} CFNT_s;
+
+/// Font glyph position structure.
+typedef struct
+{
+ int sheetIndex; ///< Texture sheet index to use to render the glyph.
+ float xOffset; ///< Horizontal offset to draw the glyph width.
+ float xAdvance; ///< Horizontal distance to advance after drawing the glyph.
+ float width; ///< Glyph width.
+ ///< Texture coordinates to use to render the glyph.
+ struct
+ {
+ float left, top, right, bottom;
+ } texcoord;
+ ///< Vertex coordinates to use to render the glyph.
+ struct
+ {
+ float left, top, right, bottom;
+ } vtxcoord;
+} fontGlyphPos_s;
+
+/// Flags for use with fontCalcGlyphPos.
+enum
+{
+ GLYPH_POS_CALC_VTXCOORD = BIT(0), ///< Calculates vertex coordinates in addition to texture coordinates.
+ GLYPH_POS_AT_BASELINE = BIT(1), ///< Position the glyph at the baseline instead of at the top-left corner.
+ GLYPH_POS_Y_POINTS_UP = BIT(2), ///< Indicates that the Y axis points up instead of down.
+};
+
+///@}
+
+///@name Initialization and basic operations
+///@{
+
+/// Ensures the shared system font is mapped.
+Result fontEnsureMapped(void);
+
+/// Retrieves the font information structure of the shared system font.
+static inline FINF_s* fontGetInfo(void)
+{
+ extern CFNT_s* g_sharedFont;
+ return &g_sharedFont->finf;
+}
+
+/// Retrieves the texture sheet information of the shared system font.
+static inline TGLP_s* fontGetGlyphInfo(void)
+{
+ return fontGetInfo()->tglp;
+}
+
+/**
+ * @brief Retrieves the pointer to texture data for the specified texture sheet.
+ * @param sheetIndex Index of the texture sheet.
+ */
+static inline void* fontGetGlyphSheetTex(int sheetIndex)
+{
+ TGLP_s* tglp = fontGetGlyphInfo();
+ return &tglp->sheetData[sheetIndex*tglp->sheetSize];
+}
+
+/**
+ * @brief Retrieves the glyph index of the specified Unicode codepoint.
+ * @param codePoint Unicode codepoint.
+ */
+int fontGlyphIndexFromCodePoint(u32 codePoint);
+
+/**
+ * @brief Retrieves character width information of the specified glyph.
+ * @param glyphIndex Index of the glyph.
+ */
+charWidthInfo_s* fontGetCharWidthInfo(int glyphIndex);
+
+/**
+ * @brief Calculates position information for the specified glyph.
+ * @param out Output structure in which to write the information.
+ * @param glyphIndex Index of the glyph.
+ * @param flags Calculation flags (see GLYPH_POS_* flags).
+ * @param scaleX Scale factor to apply horizontally.
+ * @param scaleY Scale factor to apply vertically.
+ */
+void fontCalcGlyphPos(fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY);
+
+///@}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <3ds/font.h>
+#include <3ds/svc.h>
+#include <3ds/synchronization.h>
+#include <3ds/result.h>
+#include <3ds/services/apt.h>
+
+CFNT_s* g_sharedFont;
+static u32 sharedFontAddr;
+static int charPerSheet;
+
+Result fontEnsureMapped(void)
+{
+ if (g_sharedFont) return 0;
+ Result res = 0;
+ Handle hSharedFont = 0;
+
+ aptOpenSession();
+ res = APT_GetSharedFont(&hSharedFont, &sharedFontAddr);
+ aptCloseSession();
+ if (R_FAILED(res)) return res;
+
+ // Map the shared font if it's not already mapped.
+ res = svcMapMemoryBlock(hSharedFont, 0, MEMPERM_READ, MEMPERM_DONTCARE);
+ svcCloseHandle(hSharedFont);
+ if (R_FAILED(res) && res != 0xE0A01BF5)
+ return res;
+
+ g_sharedFont = (CFNT_s*)(sharedFontAddr+0x80);
+ charPerSheet = g_sharedFont->finf.tglp->nRows * g_sharedFont->finf.tglp->nLines;
+ return 0;
+}
+
+int fontGlyphIndexFromCodePoint(u32 codePoint)
+{
+ int ret = g_sharedFont->finf.alterCharIndex;
+ if (codePoint < 0x10000)
+ {
+ CMAP_s* cmap;
+ for (cmap = g_sharedFont->finf.cmap; cmap; cmap = cmap->next)
+ {
+ if (codePoint < cmap->codeBegin || codePoint > cmap->codeEnd)
+ continue;
+
+ if (cmap->mappingMethod == CMAP_TYPE_DIRECT)
+ {
+ ret = cmap->indexOffset + (codePoint - cmap->codeBegin);
+ break;
+ }
+
+ if (cmap->mappingMethod == CMAP_TYPE_TABLE)
+ {
+ ret = cmap->indexTable[codePoint - cmap->codeBegin];
+ break;
+ }
+
+ int j;
+ for (j = 0; j < cmap->nScanEntries; j ++)
+ if (cmap->scanEntries[j].code == codePoint)
+ break;
+ if (j < cmap->nScanEntries)
+ {
+ ret = cmap->scanEntries[j].glyphIndex;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+charWidthInfo_s* fontGetCharWidthInfo(int glyphIndex)
+{
+ charWidthInfo_s* info = NULL;
+ CWDH_s* cwdh;
+ for (cwdh = g_sharedFont->finf.cwdh; cwdh && !info; cwdh = cwdh->next)
+ {
+ if (glyphIndex < cwdh->startIndex || glyphIndex > cwdh->endIndex)
+ continue;
+ info = &cwdh->widths[glyphIndex - cwdh->startIndex];
+ }
+ if (!info)
+ info = &g_sharedFont->finf.defaultWidth;
+ return info;
+}
+
+void fontCalcGlyphPos(fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY)
+{
+ FINF_s* finf = &g_sharedFont->finf;
+ TGLP_s* tglp = finf->tglp;
+ charWidthInfo_s* cwi = fontGetCharWidthInfo(glyphIndex);
+
+ int sheetId = glyphIndex / charPerSheet;
+ int glInSheet = glyphIndex % charPerSheet;
+ out->sheetIndex = sheetId;
+ out->xOffset = scaleX*cwi->left;
+ out->xAdvance = scaleX*cwi->charWidth;
+ out->width = scaleX*cwi->glyphWidth;
+
+ int lineId = glInSheet / tglp->nRows;
+ int rowId = glInSheet % tglp->nRows;
+
+ float tx = (float)(rowId*(tglp->cellWidth+1)+1) / tglp->sheetWidth;
+ float ty = 1.0f - (float)((lineId+1)*(tglp->cellHeight+1)+1) / tglp->sheetHeight;
+ float tw = (float)cwi->glyphWidth / tglp->sheetWidth;
+ float th = (float)tglp->cellHeight / tglp->sheetHeight;
+ out->texcoord.left = tx;
+ out->texcoord.top = ty+th;
+ out->texcoord.right = tx+tw;
+ out->texcoord.bottom = ty;
+
+ if (flags & GLYPH_POS_CALC_VTXCOORD)
+ {
+ float vx = out->xOffset;
+ float vy = (flags & GLYPH_POS_AT_BASELINE) ? (scaleY*tglp->baselinePos) : 0;
+ float vw = out->width;
+ float vh = scaleY*tglp->cellHeight;
+ if (flags & GLYPH_POS_Y_POINTS_UP)
+ {
+ vy = -(vh-vy);
+ out->vtxcoord.left = vx;
+ out->vtxcoord.top = vy+vh;
+ out->vtxcoord.right = vx+vw;
+ out->vtxcoord.bottom = vy;
+ } else
+ {
+ vy = -vy;
+ out->vtxcoord.left = vx;
+ out->vtxcoord.top = vy;
+ out->vtxcoord.right = vx+vw;
+ out->vtxcoord.bottom = vy+vh;
+ }
+ }
+}