diff options
| author | Alexander Kassouni <alex@kassouni.net> | 2026-09-05 17:51:13 -0700 |
|---|---|---|
| committer | Alexander Kassouni <alex@kassouni.net> | 2026-09-05 17:51:13 -0700 |
| commit | 46227e2003de28202cc4e82bca4f7f4745476c4e (patch) | |
| tree | fe450ac33ffa13747613c3cfdf36ea24b3036c46 /docs/encoding.html | |
init
Diffstat (limited to 'docs/encoding.html')
| -rw-r--r-- | docs/encoding.html | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/docs/encoding.html b/docs/encoding.html new file mode 100644 index 0000000..8b57261 --- /dev/null +++ b/docs/encoding.html @@ -0,0 +1,136 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>2. Text → voice code — Animalese</title> +<link rel="stylesheet" href="style.css"> +</head> +<body> + +<nav class="crumb"><a href="index.html">Animalese pipeline</a> / 2. Text → voice code</nav> + +<h1>2. Text → voice code</h1> +<p class="lede">A flat 256-entry table turns the game's own character encoding into a +34-value "voice code" alphabet. This is the only place text is looked at.</p> + +<h2>The game's character set</h2> + +<p>Messages are stored one byte per character in a custom encoding +(<code>include/m_font.h</code>). It is ASCII-compatible over exactly three ranges — +<code>0-9</code> at 48–57, <code>A-Z</code> at 65–90, <code>a-z</code> at 97–122 — and +diverges everywhere else. Notable non-ASCII placements:</p> + +<table> +<tr><th>Code</th><th>Character</th><th>Note</th></tr> +<tr><td>0–31</td><td>uppercase accented Latin, <code>¡ ¿ Ç Ð Ñ ß Þ</code></td><td>where ASCII control codes would be</td></tr> +<tr><td>43</td><td><code>♥</code></td><td>where ASCII has <code>+</code></td></tr> +<tr><td>47</td><td><code>♪</code></td><td>where ASCII has <code>/</code></td></tr> +<tr><td>59</td><td>droplet symbol</td><td>where ASCII has <code>;</code></td></tr> +<tr><td>92</td><td>annoyed symbol</td><td>where ASCII has <code>\</code></td></tr> +<tr><td>127</td><td><code>CHAR_CONTROL_CODE</code></td><td>escape byte; a control code follows</td></tr> +<tr><td>128</td><td><code>CHAR_MESSAGE_TAG</code></td><td>escape byte</td></tr> +<tr><td>210, 211</td><td>short space, wide space</td><td>layout-only spaces</td></tr> +</table> + +<h2>The voice table</h2> + +<p><code>mMsg_sound_voice_get(code)</code> (<code>src/game/m_msg_sound.c_inc:1</code>) is a +256-byte lookup with two hard-coded exceptions:</p> + +<pre>if (code == CHAR_CONTROL_CODE || code == CHAR_MESSAGE_TAG) + return -1; // no sound, and no state change +return voice_array[code];</pre> + +<p>Those are the only two entries in the array holding <code>0xFF</code>, so +<code>0xFF</code> is never actually returned from this function — it arrives at +<code>Na_VoiceSe</code> only as a padding value for the unused <code>b</code>/<code>c</code> +slots.</p> + +<h3>Voice code space</h3> + +<table> +<tr><th>Voice code</th><th>Meaning</th></tr> +<tr><td><code>0x53</code>–<code>0x5C</code></td><td>digits <code>0</code>–<code>9</code></td></tr> +<tr><td><code>0x5D</code>–<code>0x76</code></td><td>letters <code>a</code>–<code>z</code></td></tr> +<tr><td><code>0x80</code></td><td><code>♪</code> — plays the speaker's melody, bypasses everything</td></tr> +<tr><td><code>0x81</code></td><td>space</td></tr> +<tr><td><code>0x82</code></td><td><code>?</code> — raises pitch</td></tr> +<tr><td><code>0x83</code></td><td><code>!</code> — raises volume</td></tr> +<tr><td><code>0x84</code></td><td>newline (injected, never in the table)</td></tr> +<tr><td><code>0x85</code></td><td>mute — the catch-all for punctuation and symbols</td></tr> +<tr><td><code>0x86</code></td><td>click — plays <code>NA_SE_BEBE</code></td></tr> +<tr><td><code>0xFF</code></td><td>empty slot in a window</td></tr> +</table> + +<p>The split at <code>0x77</code> matters: <code>Sou_BoinShiinCheck</code> and several rules in +<code>Na_VoiceSe</code> test <code>> 0x77</code> to mean "this is punctuation, not a letter".</p> + +<h3>Case and accent folding</h3> + +<p>Upper and lower case map to the same code, and every accented Latin letter folds onto +its base letter. Some of the folds are worth knowing about:</p> + +<table> +<tr><th>Character(s)</th><th>Folds to</th></tr> +<tr><td><code>À Á  à Šà á â ã ä å</code>, <code>ª</code> (feminine ordinal)</td><td><code>a</code></td></tr> +<tr><td><code>ß</code> (eszett)</td><td><code>b</code> — a glyph-shape fold, not a phonetic one</td></tr> +<tr><td><code>Ç ç</code></td><td><code>c</code></td></tr> +<tr><td><code>Ð ð</code></td><td><code>d</code></td></tr> +<tr><td><code>È É Ê Ë è é ê ë</code></td><td><code>e</code></td></tr> +<tr><td><code>Ì Í Î Ï ì í î ï</code></td><td><code>i</code></td></tr> +<tr><td><code>Ñ ñ</code></td><td><code>n</code></td></tr> +<tr><td><code>Ò Ó Ô Õ Ö ò ó ô õ ö</code>, <code>º</code> (masculine ordinal)</td><td><code>o</code></td></tr> +<tr><td><code>Þ þ</code> (thorn)</td><td><code>p</code></td></tr> +<tr><td><code>Ù Ú Û Ü ù ú û ü</code></td><td><code>u</code></td></tr> +<tr><td><code>Ý ý ÿ</code></td><td><code>y</code></td></tr> +<tr><td><code>Œ œ</code></td><td><em>mute</em> — the only accented Latin pair that is not folded</td></tr> +</table> + +<h3>Everything else</h3> + +<ul> + <li>Space (32), short space (210), wide space (211), and code 255 → <code>0x81</code>.</li> + <li>Interpunct (133) → <code>0x86</code>, the click. It is the only character in the whole + table that maps to the click code.</li> + <li>All other punctuation, symbols, and the entire block from 155 upward → + <code>0x85</code>, mute.</li> +</ul> + +<h2>Two wrappers</h2> + +<h3>mMsg_sound_voice_get2</h3> + +<pre>if (code == CHAR_NEW_LINE) return 0x84; +return mMsg_sound_voice_get(code);</pre> + +<p>Newline has no entry in the table; the wrapper injects <code>0x84</code>. This is the +function the scheduler actually calls, so <code>0x84</code> reaches the engine while the +raw table never produces it. <span class="src"><code>m_msg_sound.c_inc:44</code></span></p> + +<h3>mMsg_sound_voice_get_for_editor</h3> + +<pre>sound = mMsg_sound_voice_get(code); +switch (sound) { + case 0x80: case 0x82: case 0x83: case 0x85: return 0x86; + default: return sound; +}</pre> + +<p>In the text editor, ♪, <code>?</code>, <code>!</code> and every mute character all become +the click. See <a href="editor.html">the editor path</a>. +<span class="src"><code>m_msg_sound.c_inc:26</code></span></p> + +<div class="note"> +<strong>Only these 34 values leave the text layer.</strong> Word boundaries, capitalisation, +sentence structure and the actual letters beyond <code>a-z0-9</code> are all gone by +the time audio sees anything. Whatever "understanding of English" the animalese has, +it lives entirely in the digraph table on the next page. +</div> + +<div class="pager"> + <span><a href="gating.html">← 1. Gating</a></span> + <span><a href="scheduling.html">3. Windowing & timing →</a></span> +</div> + +</body> +</html> |