aboutsummaryrefslogtreecommitdiff
path: root/docs/encoding.html
blob: bc5bcc712538dac5b87499f12cffc155cd1186ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<!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>The full 256-byte array is reproduced verbatim in the
<a href="reference.html">reference tables</a>; this page describes its shape.</p>

<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>&gt; 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 &amp; timing →</a></span>
</div>

</body>
</html>