Alchemy
Loading...
Searching...
No Matches

decode.c

Go to the documentation of this file.
1/// @file
2/// The message decoder: articles, names and numbers written out.
3/// Part of window.c, the window object, compiled only through it.
4
6
7#if (LANGUAGE != LANGUAGE_JAPANESE)
8#if (LANGUAGE == LANGUAGE_ITALIAN)
9#define ARTICLE_KINDS 11
10#define ARTICLE_KIND_MASK 15
11#elif (LANGUAGE == LANGUAGE_SPANISH)
12#define ARTICLE_KINDS 5
13#define ARTICLE_KIND_MASK 15
14#else
15#define ARTICLE_KINDS 8
16#define ARTICLE_KIND_MASK 7
17#endif
18
19/// The article written before a name of each article class.
22};
23
24#endif
25
26// UiText_AppendArticleName copies a name into the decoded text at pos and
27// returns the position after it. A name may start with an article class
28// ({article_class:N}, MSG_ARTICLE_CLASS and its class), which picks the article
29// written before it. no is the article form the message asks for (its
30// argument code's operand); plural says the last number shown was not 1;
31// suffix tracks what the text last ended with, for the suffix codes (1: an s,
32// or in German s or z; 2: the end of a sentence, so the next word is
33// capitalised; 0: anything else). With mode set (battle messages) the name is
34// set off with spaces and the style codes around it.
35
36#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH) || \
37 (LANGUAGE == LANGUAGE_FRENCH) || (LANGUAGE == LANGUAGE_ITALIAN)
38/// The German, Spanish, French and Italian form: forms 0 and 4 take no article,
39/// the others the class's article (in Spanish, form bit 3 asks for the
40/// indefinite article), capitalised at the start of a sentence.
41/// The write position wraps in the decoded-text ring; language grammar remains
42/// separate.
43u32 UiText_AppendArticleName(s32 mode, u16 *name, u32 writePosition,
44 u16 *entries, s32 articleForm, s32 plural,
45 s32 *suffix) {
46 struct ArticleTable table;
47#if (LANGUAGE == LANGUAGE_SPANISH)
48 struct ArticleTable indefiniteTable;
49 u16 count;
50 s16 indefinite;
51#endif
52 s32 kind;
53 u16 head;
54 u16 code;
55 s8 *p;
56 s8 c8;
57 s32 cnt;
58
59#if (LANGUAGE == LANGUAGE_SPANISH)
60 count = articleForm & 7;
61 indefinite = articleForm & 8;
62#endif
63 if (mode != 0) {
64 entries[writePosition] = ' ';
65 writePosition = (writePosition + 1) & 0x1ff;
66 entries[writePosition] = 0x0a;
67 writePosition = (writePosition + 1) & 0x1ff;
68 entries[writePosition] = 0x0a;
69 writePosition = (writePosition + 1) & 0x1ff;
70 }
71#if (LANGUAGE == LANGUAGE_SPANISH)
72 if (count == 0 || count == 4) {
73#else
74 if (articleForm == 0 || articleForm == 4) {
75#endif
76 head = name[0];
77 if (head == 29)
78 name += 2;
79 } else {
80 kind = 0;
82#if (LANGUAGE == LANGUAGE_SPANISH)
83 indefiniteTable = gUiText_IndefiniteArticles;
84#endif
85 if (name[0] == 29) {
86 kind = name[1];
87 name += 2;
88 }
89#if (LANGUAGE == LANGUAGE_SPANISH)
90 if (indefinite == 0)
91 p = table.text[kind & ARTICLE_KIND_MASK];
92 else
93 p = indefiniteTable.text[kind & ARTICLE_KIND_MASK];
94#else
95 p = table.text[kind & ARTICLE_KIND_MASK];
96#endif
97 for (cnt = 0; cnt < 8; cnt++) {
98 if ((c8 = *p++) == 0)
99 break;
100 if (cnt == 0 && *suffix == 2)
101 c8 -= 0x20;
102 entries[writePosition] = c8;
103 writePosition = (writePosition + 1) & 0x1ff;
104 }
105 }
106 while (*name != 0) {
107 code = *name++;
108 entries[writePosition] = code;
109 writePosition = (writePosition + 1) & 0x1ff;
110#if (LANGUAGE == LANGUAGE_GERMAN)
111 if (code == 's' || code == 'S' || code == 'z' || code == 'Z')
112 *suffix = 1;
113 else
114 *suffix = 0;
115#elif (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_FRENCH)
116 if (code == 's' || code == 'S')
117 *suffix = 1;
118 else
119 *suffix = 0;
120#endif
121 }
122 if (mode != 0) {
123 entries[writePosition] = 0x0a;
124 writePosition = (writePosition + 1) & 0x1ff;
125 entries[writePosition] = 0x08;
126 writePosition = (writePosition + 1) & 0x1ff;
127 entries[writePosition] = ' ';
128 writePosition = (writePosition + 1) & 0x1ff;
129 }
130 return writePosition;
131}
132#endif
133
134#if (LANGUAGE == LANGUAGE_ENGLISH)
135/// The English form: form 1 (or 3 with a single item) takes "a" or "an" (by the
136/// name's first letter unless its class says otherwise), and form 2 (or 3 with
137/// several) adds the plural "s" or "es".
138/// The write position wraps in the decoded-text ring; language grammar remains
139/// separate.
140u32 UiText_AppendArticleName(s32 mode, u16 *name, u32 writePosition,
141 u16 *entries, s32 articleForm, s32 plural,
142 s32 *suffix) {
143 struct ArticleTable table;
144 s32 kind;
145 u16 head;
146 u16 code;
147 u8 *p;
148 s8 c8;
149 s32 cnt;
150
151 if (mode != 0) {
152 entries[writePosition] = ' ';
153 writePosition = (writePosition + 1) & 0x1ff;
154 entries[writePosition] = 0x0a;
155 writePosition = (writePosition + 1) & 0x1ff;
156 entries[writePosition] = 0x0a;
157 writePosition = (writePosition + 1) & 0x1ff;
158 }
159 if (articleForm == 1 || (articleForm == 3 && plural == 0)) {
160 kind = 0;
161 table = gUiText_ArticleTable;
162 head = name[0];
163 if (head == 29) {
164 kind = name[1] - 1;
165 name += 2;
166 }
167 if (kind == 0) {
168 if (head == 'A' || head == 'I' || head == 'U' || head == 'E' ||
169 head == 'O')
170 kind = 2;
171 else
172 kind = 1;
173 }
174 p = table.text[kind & 7];
175 for (cnt = 0; cnt < 8; cnt++) {
176 if ((c8 = *p++) == 0)
177 break;
178 entries[writePosition] = c8;
179 writePosition = (writePosition + 1) & 0x1ff;
180 }
181 } else {
182 head = name[0];
183 if (head == 29)
184 name += 2;
185 }
186 while (*name != 0) {
187 code = *name++;
188 entries[writePosition] = code;
189 writePosition = (writePosition + 1) & 0x1ff;
190 if (code == 'S' || code == 's')
191 *suffix = 1;
192 else
193 *suffix = 0;
194 }
195 if (articleForm == 2 || (articleForm == 3 && plural != 0)) {
196 if (*suffix != 0) {
197 entries[writePosition] = 'e';
198 writePosition = (writePosition + 1) & 0x1ff;
199 }
200 entries[writePosition] = 's';
201 writePosition = (writePosition + 1) & 0x1ff;
202 }
203 if (mode != 0) {
204 entries[writePosition] = 0x0a;
205 writePosition = (writePosition + 1) & 0x1ff;
206 entries[writePosition] = 0x08;
207 writePosition = (writePosition + 1) & 0x1ff;
208 entries[writePosition] = ' ';
209 writePosition = (writePosition + 1) & 0x1ff;
210 }
211 return writePosition;
212}
213#endif
214
215/// The control codes of a message as stored (the PO catalogs' names), which
216/// UiText_BuildRenderEntries expands into decoded text.
217enum {
224 /// German: a counted noun; Italian: a gender ending; Spanish and French: the
225 /// plural suffix
236 /// English: the plural suffix; German, Spanish, French and Italian: the
237 /// item's possessive
243};
244
245/// What halting speech (UiWork_SetBusyFlags) puts between characters.
246#define ENTRY_NARROW_GAP TEXT_PAUSE_SHORT
247#define ENTRY_WIDE_GAP TEXT_VOICED_MARK
248
249#if (LANGUAGE == LANGUAGE_ENGLISH) || (LANGUAGE == LANGUAGE_GERMAN) || \
250 (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_FRENCH) || \
251 (LANGUAGE == LANGUAGE_ITALIAN)
252
253#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH) || \
254 (LANGUAGE == LANGUAGE_FRENCH) || (LANGUAGE == LANGUAGE_ITALIAN)
255#if (LANGUAGE == LANGUAGE_FRENCH)
256extern s8 gUiText_ItemPronoun[];
257#else
258/// The possessive written before an item's name, chosen by the item's gender
259/// or by number.
260struct ItemPossessiveTable {
262};
263extern const struct ItemPossessiveTable gUiText_ItemPossessives;
264extern const s8 gUiText_ItemGenders[];
265extern u8 *gUiText_NumberWords[];
266#endif
267#endif
268
269/// Decodes message script into the ring of decoded text (UiRenderWork.entries)
270/// and returns where it starts; script -1 returns the last message's start
271/// again. Argument codes take their values from the argument table
272/// (UiWork_PushValueSlot), and clear empties the table afterwards. Names,
273/// numbers and articles are written out; style codes are kept with their
274/// operands for the message channel to run.
275/// script -1 reuses messageStart; clear consumes argument slots after decoding.
277 struct UiRenderWork *work = UI_WORK;
278 u32 code = 0;
279#if (LANGUAGE == LANGUAGE_ENGLISH)
280 s32 lineStart = 1;
281#endif
282 s32 quote = 0;
283 u32 start = work->count;
284 u32 decodedCount = 0;
285 s32 plural = 0;
286#if !(LANGUAGE == LANGUAGE_GERMAN) && !(LANGUAGE == LANGUAGE_ITALIAN)
287 s32 running = 1;
288#endif
289#if (LANGUAGE == LANGUAGE_ENGLISH)
290 u32 writePosition = start;
291 u32 previousCode;
292 s32 suffix = 0;
293#else
294#if (LANGUAGE == LANGUAGE_SPANISH)
295 struct ItemPossessiveTable possessives;
296 s32 form;
297#endif
298 s32 suffix = 2;
299#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_ITALIAN)
300 s32 running = 1;
301#endif
302 s8 *word;
303 s8 c8;
304 u32 writePosition = start;
305#endif
306 u16 *entry = work->entries;
307 s32 overflow = 0;
308 s32 (*decode)(struct TextReader *);
309 u32 nextPosition;
310 s32 no;
311 u32 j;
312 u32 i;
313 s32 value;
314 s32 argument;
315#if (LANGUAGE == LANGUAGE_ITALIAN)
316 s32 ending;
317#endif
318#ifdef UBFIX
319 u32 magnitude;
320#else
321 s32 magnitude;
322#endif
323 u8 *numberText;
324 u8 *nameBytes;
325 u16 *nameOutput;
326 u16 *p;
328 u8 numberBuffer[UI_NUMBER_BUFFER_SIZE];
329 struct TextReader reader;
330#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_ITALIAN)
331 struct ItemPossessiveTable possessives;
332 s32 kind;
333#endif
334
335 if (script == -1) {
336 start = work->messageStart;
337 } else {
338 {
339 u32 size;
340
341 /* FAKEMATCH(one-pass): the size set in a block of its own, so the slot
342 * number goes to r0 before the size's copy to r1; as a plain
343 * assignment, 22 of 706 instructions differ in tbs-en. */
344 do {
346 } while (0);
347 Dma_Set((const void *)Text_DecodeSymbol,
349 DMA_COPY32(size >> 2), REG_DMA3);
350 }
351 // The decoder runs from a copy in RAM, one symbol at a time.
352 decode = (s32 (*)(
354 UiText_LookupMessage(&reader, script);
355
356 do {
357#if (LANGUAGE == LANGUAGE_ENGLISH)
358 previousCode = code;
359#endif
360 code = (u32)decode(&reader);
361 if (code > 255)
362 code = 64;
363
364 // Once halting speech has broken off, the rest of the message is
365 // skipped, though its arguments are still taken from the table.
366 if (overflow != 0) {
367 if (code < 32) {
368 switch (code) {
370 decode(&reader);
372 break;
373 case MSG_END:
374 case MSG_END_WAIT:
375 case MSG_END_NOW:
376 running = 0;
377 break;
380 break;
382 decode(&reader);
384 break;
387 break;
390 break;
391 case MSG_TEXT_COLOR:
392 case MSG_TEXT_EFFECT:
393 decode(&reader);
394 break;
395 case MSG_ACTOR_NAME:
396 decode(&reader);
397 break;
399 decode(&reader);
400 break;
402 decode(&reader);
403 break;
404 case MSG_PAGE_BREAK:
405 running = 0;
406 code = 2;
407 break;
409 break;
410 case -1:
411 break;
412 default:
413 break;
414 }
415 }
416 } else {
417 // Halting speech pauses before each character.
418 if (work->spacingBefore != 0
420 && lineStart == 0 && code != 222 && code != 223
421#endif
422 ) {
423 entry[writePosition] = ENTRY_NARROW_GAP;
424 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
425 }
426
427#if (LANGUAGE == LANGUAGE_ENGLISH)
428 if (work->spacingAfter != 0 && lineStart == 0 && code != 222 &&
429 code != 223 && previousCode <= 256 && previousCode > 127 &&
430 previousCode != 222 && previousCode != 223 && previousCode != 32 &&
431 previousCode != 165 && previousCode != 161 && previousCode != 164) {
432 entry[writePosition] = ENTRY_WIDE_GAP;
433 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
434 }
435
436#endif
437 // A character. Halting speech breaks off with an ellipsis at the
438 // first space or after 11 characters.
439 if (code > 31) {
440 if (work->spacingBefore != 0 && (code == 32 || decodedCount > 10)) {
441 entry[writePosition] = '.';
442 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
443 entry[writePosition] = '.';
444 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
445 entry[writePosition] = '.';
446 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
447 overflow = 1;
448 if (decodedCount > 10)
449 code = 32;
450 }
451 if (code == 34) {
452 quote ^= 1;
453 if (quote != 0)
454 code = 142;
455 }
456 entry[writePosition] = (u16)code;
457 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
458#if (LANGUAGE == LANGUAGE_ENGLISH)
459 lineStart = 0;
460#else
461 if (code != ' '
463 && code != 161 && code != 191
464#endif
465 ) {
466 suffix = 0;
467 if (code == '.' || code == '!' || code == '?')
468 suffix = 2;
469 if (code == 's' || code == 'S'
471 || code == 'z' || code == 'Z'
472#endif
473 )
474 suffix = 1;
475 }
476#endif
477 } else {
478 // A control code: write out names, numbers and articles, and keep
479 // style codes with their operands.
480 switch (code) {
481 case MSG_END:
482 case MSG_END_WAIT:
483 case MSG_END_NOW:
484 running = 0;
485 break;
486 case MSG_TEXT_COLOR:
487 case MSG_TEXT_EFFECT:
489 entry[writePosition] = (u16)code;
490 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
491 entry[writePosition] = decode(&reader) + 0xFFFFU;
492 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
493 break;
496 magnitude = value;
497 if (value < 0)
498#ifdef UBFIX
499 // Signed -value overflows for INT_MIN; its unsigned magnitude
500 // still gives the correct singular/plural comparison.
501 magnitude = 0U - (u32)value;
502#else
503 magnitude = -value;
504#endif
505 plural = 1;
506 if (magnitude <= 1)
507 plural = 0;
508 numberText = UiText_FormatNumber(numberBuffer, value, 0);
509 i = numberText - numberBuffer;
510 while (i != 16 && numberBuffer[i] != 0) {
511 entry[writePosition] = numberBuffer[i];
512 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
513 i++;
514 }
515 break;
517 no = decode(&reader) - 1;
518 argument = UiText_LookupArgument(MSG_ARG_CLASS, clear);
519 UiText_DecodeMessage(argument + MsgClassName, name,
521 writePosition = UiText_AppendArticleName(
522 0, name, writePosition, entry, no, plural, &suffix);
523 break;
525 no = decode(&reader) - 1;
526#if (LANGUAGE == LANGUAGE_SPANISH)
527 form = no & 7;
528#endif
529 argument = UiText_LookupArgument(MSG_ARG_ITEM, clear);
530#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH) || \
531 (LANGUAGE == LANGUAGE_FRENCH) || (LANGUAGE == LANGUAGE_ITALIAN)
532 argument &= RENDER_ENTRY_MASK;
533#if (LANGUAGE == LANGUAGE_SPANISH)
534 if (form == 2 || (form == 3 && plural != 0) || form == 4)
535#else
536 if (no == 2 || (no == 3 && plural != 0) || no == 4)
537#endif
538 UiText_DecodeMessage(argument + MsgItemPluralName, name,
540 else
541 UiText_DecodeMessage(argument + MsgItemName, name,
543#else
544 UiText_DecodeMessage((argument & RENDER_ENTRY_MASK) + MsgItemName,
545 name, 24);
546#endif
547 writePosition = UiText_AppendArticleName(
548 0, name, writePosition, entry, no, plural, &suffix);
549 break;
551 argument = UiText_LookupArgument(MSG_ARG_ABILITY, clear);
552 UiText_DecodeMessage(argument + MsgAbilityName, name,
554 p = name;
555 nextPosition = writePosition;
556 while (*p != 0) {
557 entry[nextPosition] = *p;
558 p++;
559 nextPosition = (nextPosition + 1) & RENDER_ENTRY_MASK;
560 }
561 writePosition = nextPosition;
562 break;
567 MsgDebugEntryName,
569 p = name;
570 nextPosition = writePosition;
571 while (*p != 0) {
572 entry[nextPosition] = *p;
573 p++;
574 nextPosition = (nextPosition + 1) & RENDER_ENTRY_MASK;
575 }
576 writePosition = nextPosition;
577 break;
580 nameOutput = name;
581 i = 0;
582 do {
583 *nameOutput = *nameBytes;
584 i++;
585 nameBytes++;
586 nameOutput++;
587 } while (i <= 14);
588 writePosition = UiText_AppendArticleName(0, name, writePosition,
589 entry, 0, 0, &suffix);
590 break;
592 no = decode(&reader) - 1;
593 nameBytes =
595 nameOutput = name;
596 j = 0;
597 do {
598 *nameOutput = *nameBytes;
599 j++;
600 nameBytes++;
601 nameOutput++;
602 } while (j <= 14);
603 writePosition = UiText_AppendArticleName(
604 0, name, writePosition, entry, no, plural, &suffix);
605 break;
606 case MSG_ACTOR_NAME:
607 argument = decode(&reader) - 1;
608 nameBytes = UnitGetFar(argument)->name;
609 nameOutput = name;
610 j = 0;
611 do {
612 *nameOutput = *nameBytes;
613 j++;
614 nameBytes++;
615 nameOutput++;
616 } while (j <= 14);
617 writePosition = UiText_AppendArticleName(0, name, writePosition,
618 entry, 0, 0, &suffix);
619 break;
620 case MSG_BUTTON_ICON:
621 argument = ((decode(&reader) - 1) << 1) + 128;
622 entry[writePosition] = argument;
623 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
624 entry[writePosition] = argument + 1;
625 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
626 break;
627 case MSG_EM_DASH:
628 entry[writePosition] = TEXT_ARGUMENT_SPACE;
629 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
630 entry[writePosition] = '-';
631 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
632#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH) || \
633 (LANGUAGE == LANGUAGE_FRENCH) || (LANGUAGE == LANGUAGE_ITALIAN)
634 entry[writePosition] = ' ';
635 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
636#endif
637 break;
638#if (LANGUAGE == LANGUAGE_GERMAN)
640 no = decode(&reader) - 1;
641 nameBytes = gUiText_NumberWords[(no & 7) * 2 + plural];
642 while (*nameBytes != 0) {
643 entry[writePosition] = *nameBytes++;
644 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
645 }
646 break;
647#endif
648#if (LANGUAGE == LANGUAGE_ITALIAN)
650 ending = decode(&reader) - 1;
651 if (ending == 0) {
652 if (plural == 0)
653 entry[writePosition] = 'o';
654 else
655 entry[writePosition] = 'i';
656 } else if (plural == 0)
657 entry[writePosition] = 'a';
658 else
659 entry[writePosition] = 'e';
660 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
661 break;
662#elif (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_FRENCH)
664#elif (LANGUAGE == LANGUAGE_ENGLISH)
666#endif
667#if !(LANGUAGE == LANGUAGE_GERMAN) && !(LANGUAGE == LANGUAGE_ITALIAN)
668 if (plural != 0) {
669 if (suffix != 0) {
670 entry[writePosition] = 'e';
671 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
672 }
673 entry[writePosition] = 's';
674 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
675 }
676 break;
677#endif
678#if (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_ITALIAN)
680#if (LANGUAGE == LANGUAGE_SPANISH)
682 possessives = gUiText_ItemPossessives;
683 word = possessives.text[plural];
684#else
685 argument = UiText_LookupArgument(MSG_ARG_ITEM, 0);
686 argument &= RENDER_ENTRY_MASK;
687 possessives = gUiText_ItemPossessives;
688 kind = gUiText_ItemGenders[argument];
689 word = possessives.text[kind & 3];
690#endif
691 /* FAKEMATCH: value counts the letters, compared unsigned, for the
692 * Spanish and Italian allocation; with the function's j as the
693 * counter, 44 instructions differ in tbs-es. */
694 for (value = 0; (u32)value < 16; value++) {
695 if ((c8 = *word++) == 0)
696 break;
697 if (value == 0 && suffix == 2)
698 c8 -= 0x20;
699 entry[writePosition] = c8;
700 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
701 }
702 break;
703#endif
704#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_FRENCH)
706#if (LANGUAGE == LANGUAGE_GERMAN)
707 argument = UiText_LookupArgument(MSG_ARG_ITEM, 0);
708 argument &= RENDER_ENTRY_MASK;
709 possessives = gUiText_ItemPossessives;
710 kind = gUiText_ItemGenders[argument];
711 word = possessives.text[kind & 3];
712#else
714 word = gUiText_ItemPronoun;
715#endif
716 for (i = 0; i < 16; i++) {
717 if ((c8 = *word++) == 0)
718 break;
719 if (i == 0 && suffix == 2)
720 c8 -= 0x20;
721 entry[writePosition] = c8;
722 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
723 }
724 suffix = 0;
725 break;
726#endif
728#if (LANGUAGE == LANGUAGE_GERMAN)
729 if (suffix == 1)
730 entry[writePosition] = '\'';
731 else
732 entry[writePosition] = 's';
733 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
734#elif (LANGUAGE == LANGUAGE_ENGLISH)
735 entry[writePosition] = '\'';
736 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
737 if (suffix == 0) {
738 entry[writePosition] = 's';
739 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
740 }
741#endif
742 break;
743 case -1:
744 break;
745 case MSG_PAGE_BREAK:
746 case MSG_LINE_BREAK:
747#if (LANGUAGE == LANGUAGE_ENGLISH)
748 lineStart = 1;
749#endif
750 // Keep the break in the decoded text.
751 // Fall through.
752 default:
753 entry[writePosition] = (u16)code;
754 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
755#if (LANGUAGE == LANGUAGE_ENGLISH)
756 if (code == 's' || code == 'S')
757 suffix = 1;
758 else
759 suffix = 0;
760#endif
761 break;
762 }
763 }
764 }
765 decodedCount++;
766 } while (running != 0 && decodedCount <= RENDER_ENTRY_MASK);
767
768 entry[writePosition] = (u16)code;
769 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
770 entry[writePosition] = 0;
771 nextPosition = (writePosition + 1) & RENDER_ENTRY_MASK;
772 work->count = (u16)nextPosition;
774 work->messageStart = (u16)start;
775 }
776
777 if (clear != 0)
779
780 return start;
781}
782
783#endif
784
785#if (LANGUAGE == LANGUAGE_JAPANESE)
786
787/// The Japanese form: Japanese has no articles, so the name is copied as it is.
788/// The write position wraps in the decoded-text ring; language grammar remains
789/// separate.
790extern __inline__ u32 UiText_AppendArticleName(s32 mode, u16 *name,
791 u32 writePosition, u16 *entries,
792 s32 articleForm, s32 plural,
793 s32 *suffix) {
794 u16 code;
795
796 if (mode != 0) {
797 entries[writePosition] = ' ';
798 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
799 entries[writePosition] = 10;
800 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
801 entries[writePosition] = 10;
802 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
803 }
804 while (*name != 0) {
805 code = *name++;
806 entries[writePosition] = code;
807 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
808 }
809 if (mode != 0) {
810 entries[writePosition] = 10;
811 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
812 entries[writePosition] = 8;
813 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
814 entries[writePosition] = ' ';
815 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
816 }
817 return writePosition;
818}
819
820/// The Japanese UiText_BuildRenderEntries, as above with no articles or
821/// plurals.
822/// script -1 reuses messageStart; clear consumes argument slots after decoding.
823s32 UiText_BuildRenderEntries(s32 script, s32 clear) {
824 struct UiRenderWork *work = UI_WORK;
825 u32 code = 0;
826 s32 lineStart = 1;
827 u32 start = work->count;
828 u32 decodedCount = 0;
829 s32 running = 1;
830 u32 writePosition = start;
831 u16 *entry = work->entries;
832 u8 mode = work->menuState & 2;
833 s32 (*decode)(struct TextReader *);
834 s32 overflow = 0;
836 u32 nextPosition;
837 u32 i;
838 u32 j;
839 s32 argument;
840 s32 message;
841 s32 value;
842 u8 *numberText;
843 u8 *nameBytes;
844 u16 *nameOutput;
845 struct BattleUnit *unit;
847 u8 numberBuffer[UI_NUMBER_BUFFER_SIZE];
848 struct TextReader reader;
849
850 if (script == -1) {
851 start = work->messageStart;
852 } else {
853 {
854 u32 size;
855
856 /* FAKEMATCH(one-pass): the size set in a block of its own, as in the
857 * international copy above; as a plain assignment, 24 of 895
858 * instructions differ in tbs-ja. */
859 do {
861 } while (0);
862 Dma_Set((const void *)Text_DecodeSymbol,
864 DMA_COPY32(size >> 2), REG_DMA3);
865 }
866 // The decoder runs from a copy in RAM, one symbol at a time.
867 decode = (s32 (*)(
869 UiText_LookupMessage(&reader, script);
870 do {
871 previousCode = code;
872 code = (u32)decode(&reader);
873 // Once halting speech has broken off, the rest of the message is
874 // skipped, though its arguments are still taken from the table.
875 if (overflow != 0) {
876 if (code < 32) {
877 switch (code) {
880 break;
881 case MSG_END:
882 case MSG_END_WAIT:
883 case MSG_END_NOW:
884 running = 0;
885 break;
886 case MSG_TEXT_COLOR:
887 case MSG_TEXT_EFFECT:
888 decode(&reader);
889 break;
892 break;
895 break;
898 break;
901 break;
902 case MSG_ACTOR_NAME:
903 decode(&reader);
904 break;
906 break;
907 case MSG_PAGE_BREAK:
908 running = 0;
909 code = 2;
910 break;
912 break;
913 case -1:
914 break;
915 default:
916 break;
917 }
918 }
919 } else {
920 // Halting speech pauses before each character.
921 if (work->spacingBefore != 0 && lineStart == 0 && code != 222 &&
922 code != 223) {
923 entry[writePosition] = 5;
924 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
925 }
926 if (work->spacingAfter != 0 && lineStart == 0 && code != 222 &&
927 code != 223 && previousCode <= 256 && previousCode > 127 &&
928 previousCode != 222 && previousCode != 223 && previousCode != 32 &&
929 previousCode != 165 && previousCode != 161 && previousCode != 164) {
930 entry[writePosition] = 222;
931 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
932 }
933 // A character. Halting speech breaks off with an ellipsis at the
934 // first space or after 11 characters.
935 if (code > 31) {
936 if (mode != 0 && code == ' ') {
937 entry[writePosition] = code;
938 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
939 }
940 if (work->spacingBefore != 0 && (code == 32 || decodedCount > 10)) {
941 entry[writePosition] = 165;
942 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
943 entry[writePosition] = 165;
944 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
945 entry[writePosition] = 165;
946 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
947 entry[writePosition] = 161;
948 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
949 overflow = 1;
950 if (decodedCount > 10)
951 code = 32;
952 }
953 entry[writePosition] = (u16)code;
954 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
955 lineStart = 0;
956 } else {
957 // A control code: write out names and numbers, and keep style
958 // codes with their operands.
959 switch (code) {
960 case MSG_END:
961 case MSG_END_WAIT:
962 case MSG_END_NOW:
963 running = 0;
964 break;
965 case MSG_TEXT_COLOR:
966 case MSG_TEXT_EFFECT:
967 entry[writePosition] = (u16)code;
968 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
969 entry[writePosition] = decode(&reader) + 0xffffU;
970 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
971 break;
974 numberText = UiText_FormatNumber(numberBuffer, value, 0);
975 i = numberText - numberBuffer;
976 while (i != 16 && numberBuffer[i] != 0) {
977 entry[writePosition] = numberBuffer[i];
978 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
979 i++;
980 }
981 break;
983 argument = UiText_LookupArgument(MSG_ARG_CLASS, clear);
984 UiText_DecodeMessage(argument + MsgClassName, name,
986 writePosition = UiText_AppendArticleName(mode, name, writePosition,
987 entry, 0, 0, 0);
988 break;
990 argument = UiText_LookupArgument(MSG_ARG_ITEM, clear);
991 UiText_DecodeMessage((argument & 511) + MsgItemName, name,
993 writePosition = UiText_AppendArticleName(mode, name, writePosition,
994 entry, 0, 0, 0);
995 break;
997 argument = UiText_LookupArgument(MSG_ARG_ABILITY, clear);
998 UiText_DecodeMessage(argument + MsgAbilityName, name,
1000 writePosition = UiText_AppendArticleName(mode, name, writePosition,
1001 entry, 0, 0, 0);
1002 break;
1007 MsgDebugEntryName,
1009 writePosition = UiText_AppendArticleName(mode, name, writePosition,
1010 entry, 0, 0, 0);
1011 break;
1014 nameOutput = name;
1015 i = 0;
1016 do {
1017 *nameOutput = *nameBytes;
1018 i++;
1019 nameBytes++;
1020 nameOutput++;
1021 } while (i <= 14);
1022 writePosition = UiText_AppendArticleName(mode, name, writePosition,
1023 entry, 0, 0, 0);
1024 break;
1027 if (work->menuState != 0 &&
1028 (unit->classId == 122 || unit->classId == 125)) {
1029 message = MsgMysteryPersonName;
1030 if (unit->classId == 125)
1031 message++;
1032 UiText_DecodeMessage(message, name, 10);
1033 writePosition = UiText_AppendArticleName(
1034 mode, name, writePosition, entry, 0, 0, 0);
1035 } else {
1036 nameBytes = unit->name;
1037 nameOutput = name;
1038 j = 0;
1039 do {
1040 *nameOutput = *nameBytes;
1041 j++;
1042 nameBytes++;
1043 nameOutput++;
1044 } while (j <= 14);
1045 writePosition = UiText_AppendArticleName(
1046 mode, name, writePosition, entry, 0, 0, 0);
1047 }
1048 break;
1049 case MSG_ACTOR_NAME:
1050 argument = decode(&reader) - 1;
1051 nameBytes = UnitGetFar(argument)->name;
1052 nameOutput = name;
1053 j = 0;
1054 do {
1055 *nameOutput = *nameBytes;
1056 j++;
1057 nameBytes++;
1058 nameOutput++;
1059 } while (j <= 14);
1060 writePosition = UiText_AppendArticleName(mode, name, writePosition,
1061 entry, 0, 0, 0);
1062 break;
1063 case -1:
1064 break;
1065 case MSG_PAGE_BREAK:
1066 case MSG_LINE_BREAK:
1067 lineStart = 1;
1068 // Keep the break in the decoded text.
1069 // Fall through.
1070 default:
1071 entry[writePosition] = (u16)code;
1072 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
1073 break;
1074 }
1075 }
1076 }
1077 decodedCount++;
1078 } while (running != 0 && decodedCount <= RENDER_ENTRY_MASK);
1079 entry[writePosition] = (u16)code;
1080 writePosition = (writePosition + 1) & RENDER_ENTRY_MASK;
1081 entry[writePosition] = 0;
1082 nextPosition = (writePosition + 1) & RENDER_ENTRY_MASK;
1083 work->count = (u16)nextPosition;
1085 work->messageStart = (u16)start;
1086 }
1087 if (clear != 0)
1089 return start;
1090}
1091
1092#endif
const s8 *const gUiText_ArticleTable[8]
The article of each article class.
Definition articles.c:152
#define ENTRY_NARROW_GAP
What halting speech (UiWork_SetBusyFlags) puts between characters.
Definition decode.c:246
#define ENTRY_WIDE_GAP
Definition decode.c:247
#define ARTICLE_KIND_MASK
Definition decode.c:16
s32 UiText_BuildRenderEntries(s32 script, s32 clear)
Decodes message script into the ring of decoded text (UiRenderWork.entries) and returns where it star...
Definition decode.c:276
#define ARTICLE_KINDS
Definition decode.c:15
u32 UiText_AppendArticleName(s32 mode, u16 *name, u32 writePosition, u16 *entries, s32 articleForm, s32 plural, s32 *suffix)
The English form: form 1 (or 3 with a single item) takes "a" or "an" (by the name's first letter unle...
Definition decode.c:140
@ MSG_END
Definition decode.c:218
@ MSG_TEXT_COLOR
Definition decode.c:222
@ MSG_END_WAIT
Definition decode.c:220
@ MSG_ARGUMENT_SIGNED_NUMBER
Definition decode.c:233
@ MSG_ACTOR_NAME
Definition decode.c:228
@ MSG_END_NOW
Definition decode.c:242
@ MSG_PAGE_BREAK
Definition decode.c:219
@ MSG_ARGUMENT_ITEM_NAME
Definition decode.c:231
@ MSG_POSSESSIVE_SUFFIX
Definition decode.c:240
@ MSG_TEXT_EFFECT
Definition decode.c:223
@ MSG_BUTTON_ICON
Definition decode.c:239
@ MSG_ARTICLE_CLASS
Definition decode.c:241
@ MSG_EM_DASH
Definition decode.c:235
@ MSG_PLURAL_SUFFIX
English: the plural suffix; German, Spanish, French and Italian: the item's possessive.
Definition decode.c:238
@ MSG_ARGUMENT_ABILITY_NAME
Definition decode.c:232
@ MSG_LINE_BREAK
Definition decode.c:221
@ MSG_ARGUMENT_ACTOR_NAME
Definition decode.c:229
@ MSG_LOCALIZED_WORD
German: a counted noun; Italian: a gender ending; Spanish and French: the plural suffix.
Definition decode.c:226
@ MSG_ARGUMENT_LOCATION_NAME
Definition decode.c:234
@ MSG_PROTAGONIST_NAME
Definition decode.c:227
@ MSG_ARGUMENT_CLASS_NAME
Definition decode.c:230
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
#define LANGUAGE_ENGLISH
Definition edition.h:15
#define LANGUAGE
Definition edition.h:22
#define LANGUAGE_SPANISH
Definition edition.h:17
#define LANGUAGE_GERMAN
Definition edition.h:16
struct BattleUnit * UnitGetFar(u32 owner)
s32 Scene_GetAreaNameIndexFar(s32 scene, s32 entrance)
struct GameState gGameState
u8 * UiText_FormatNumber(u8 *buffer, s32 input, s32 width)
Writes value as decimal text into buffer (14 bytes) and returns where the text starts.
Definition glyphs.c:470
const u8 Text_DecodeSymbol[]
const struct ArticleTable gUiText_IndefiniteArticles
u8 Text_DecodeSymbolCodeSize[]
void * Runtime_AllocateHeapBlock(s32 kind, s32 size)
Returns heap slot kind's block, first allocating size bytes for it (rounded up to words),...
Definition heap.c:47
@ HEAP_SLOT_WINDOW
Definition heap_state.h:39
#define REG_DMA3
Definition io_reg.h:153
void Runtime_ReleaseHeapBlock(s32 slot)
u8 gWindowWork[]
The article written before a name of each article class.
Definition decode.c:20
s8 * text[ARTICLE_KINDS]
Definition decode.c:21
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 name[0x0f]
The name, one byte a character, ending in 0.
u8 classId
Who the unit is: a character's id (ISAAC to JENNA, below 8) or an enemy's id (BattleUnitSet).
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
A position in a Huffman-coded message: the previous code, which picks the next symbol's tree,...
Definition text_reader.h:13
u32 previousCode
Definition text_reader.h:14
The window system's work: the windows' tilemap, the windows, the three text channels,...
Definition window.h:19
u8 mode
Set while Mind Read shows thoughts: round frame corners, colour 8 text with no shadow,...
Definition window.h:35
u8 spacingBefore
1 for halting speech: a pause before each character, broken off with an ellipsis after the first word...
Definition window.h:68
u16 entries[RENDER_ENTRY_MASK+1]
decoded text, a ring of codes
Definition window.h:46
u16 count
where the next code goes in entries
Definition window.h:48
u8 menuState
1 from the work's setup; 2 while a battle message is decoded.
Definition window.h:39
u8 spacingAfter
1 puts code 222 after certain characters in English and Japanese (UiWork_SetBusyFlags bit 1).
Definition window.h:71
u16 messageStart
where the last decoded message began in entries
Definition window.h:49
#define RENDER_ENTRY_MASK
Definition tbs_edition.h:68
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
unsigned char u8
Definition types.h:11
#define UI_WORK
Definition window.h:267
@ MSG_ARG_ITEM
Definition window.h:140
@ MSG_ARG_ABILITY
Definition window.h:142
@ MSG_ARG_ACTOR
Definition window.h:139
@ MSG_ARG_NUMBER
Definition window.h:143
@ MSG_ARG_CLASS
Definition window.h:141
@ MSG_ARG_LOCATION
Definition window.h:144
#define DMA_COPY32(count)
The DMA control word that copies count words.
Definition window.h:112
void UiText_LookupMessage(struct TextReader *reader, s32 message)
Points reader at the start of message (its compressed bits and first context), running the ARM lookup...
Definition messages.c:379
u32 UiText_LookupArgument(u32 kind, u32 clear)
Returns the value of the first message argument of kind (MSG_ARG_), or 0 when there is none.
Definition messages.c:235
void UiText_DecodeMessage(s32 message, u16 *text, s32 capacity)
Decodes message into text as a zero-terminated string of 16-bit codes, with at most capacity entries,...
Definition messages.c:39
void UiWork_ClearValueNameTables(void)
Empties the table of message arguments.
Definition messages.c:199
What every part of the window object shares: its includes, edition values, macros and the codes of de...
#define ITEM_POSSESSIVE_TEXT_COUNT
#define UI_NUMBER_BUFFER_SIZE
#define UI_MESSAGE_NAME_HALFWORDS
#define TEXT_ARGUMENT_SPACE
#define TEXT_WORK_BLOCK
The heap block the message decoder routine is copied into to run.