xref: /haiku/src/add-ons/media/media-add-ons/radeon/CC.cpp (revision ffec4cb14d0b87745137bcb115e949f1fbc895b9)
1 /******************************************************************************
2 /
3 /	File:			CC.cpp
4 /
5 /	Description:	Closed caption module.
6 /
7 /	Copyright 2001, Carlos Hasan
8 /
9 *******************************************************************************/
10 
11 #include <stdio.h>
12 #include <Debug.h>
13 #include "CC.h"
14 
CCaption(CCapture & capture)15 CCaption::CCaption(CCapture & capture)
16 	:	fCapture(capture),
17 		fChannel(C_RADEON_CC1),
18 		fRow(0),
19 		fColumn(0),
20 		fColor(0),
21 		fLastControlCode(0)
22 {
23 	for (int row = 0; row < C_RADEON_CC_ROWS; row++) {
24 		for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) {
25 			fText[row][column] = fDisplayText[row][column] = 0x0000;
26 		}
27 	}
28 }
29 
~CCaption()30 CCaption::~CCaption()
31 {
32 }
33 
InitCheck() const34 status_t CCaption::InitCheck() const
35 {
36 	return fCapture.InitCheck();
37 }
38 
SetMode(cc_mode mode)39 void CCaption::SetMode(cc_mode mode)
40 {
41 	fChannel = mode;
42 	fLastControlCode = 0;
43 }
44 
DecodeBits(const unsigned char * buffer,int & data)45 bool CCaption::DecodeBits(const unsigned char * buffer, int & data)
46 {
47 	// assume the buffer is long enough (at least VBI line width bytes)
48 	if (buffer == NULL)
49 		return false;
50 
51 	// compute low/high levels
52 	int low = 0xff, high = 0x00;
53 	for (int offset = C_RADEON_CC_BLANK_START; offset < C_RADEON_CC_BLANK_START + 4 * C_RADEON_CC_BIT_DURATION; offset++) {
54 		if (low > buffer[offset])
55 			low = buffer[offset];
56 		if (high < buffer[offset])
57 			high = buffer[offset];
58 	}
59 	if (low + C_RADEON_CC_LEVEL_THRESHOLD >= high)
60 		return false;
61 
62 	const int middle = (low + high) >> 1;
63 
64 	// find position of the first pulse
65 	int start = C_RADEON_CC_BLANK_START + C_RADEON_CC_BIT_DURATION;
66 	while (start <= C_RADEON_CC_BLANK_START + 4 * C_RADEON_CC_BIT_DURATION) {
67 		if (buffer[start + 0] < middle && buffer[start + 1] < middle &&
68 			buffer[start + 3] > middle && buffer[start + 4] > middle)
69 			break;
70 		start++;
71 	}
72 	if (start >= C_RADEON_CC_BLANK_START + 4 * C_RADEON_CC_BIT_DURATION)
73 		return false;
74 
75 	// compute position of the last pulse
76 	int end = start + 17 * C_RADEON_CC_BIT_DURATION;
77 
78 	// start in middle of first pulse
79 	int bit = 0;
80 	bool one = true;
81 	for (int offset = start + C_RADEON_CC_BIT_DURATION / 2; offset < end; offset++) {
82 		int width = 1;
83 
84 		// search the next pulse front
85 		while (offset < end) {
86 			if (one) {
87 				if (buffer[offset + 0] > middle && buffer[offset + 1] > middle &&
88 					buffer[offset + 3] < middle && buffer[offset + 4] < middle)
89 					break;
90 			}
91 			else {
92 				if (buffer[offset + 0] < middle && buffer[offset + 1] < middle &&
93 					buffer[offset + 3] > middle && buffer[offset + 4] > middle)
94 					break;
95 			}
96 			offset++;
97 			width++;
98 		}
99 
100 		// compute pulse width in bits
101 		const int nbits = (width + (bit == 0 ? 0 : bit == 15 ? C_RADEON_CC_BIT_DURATION :
102 			C_RADEON_CC_BIT_DURATION / 2)) / C_RADEON_CC_BIT_DURATION;
103 		data >>= nbits;
104 		if (one)
105 			data |= (0xffff << (16 - nbits)) & 0xffff;
106 
107 		if ((bit += nbits) >= C_RADEON_CC_BITS_PER_FIELD)
108 			break;
109 
110 		one = !one;
111 	}
112 
113 	if (bit != C_RADEON_CC_BITS_PER_FIELD)
114 		return false;
115 
116 	return true;
117 }
118 
Decode(const unsigned char * buffer0,const unsigned char * buffer1)119 bool CCaption::Decode(const unsigned char * buffer0,
120 							const unsigned char * buffer1)
121 {
122 	enum caption_code {
123 		/* channel */
124 		C_CC1		= 0x0000,
125 		C_CC2		= 0x0800,
126 
127 		/* address */
128 		C_ROW_1		= 0x0100,
129 		C_ROW_2		= 0x0120,
130 		C_ROW_3		= 0x0200,
131 		C_ROW_4		= 0x0220,
132 		C_ROW_5		= 0x0500,
133 		C_ROW_6		= 0x0520,
134 		C_ROW_7		= 0x0600,
135 		C_ROW_8		= 0x0620,
136 		C_ROW_9		= 0x0700,
137 		C_ROW_10	= 0x0720,
138 		C_ROW_11	= 0x0000,
139 		C_ROW_12	= 0x0300,
140 		C_ROW_13	= 0x0320,
141 		C_ROW_14	= 0x0400,
142 		C_ROW_15	= 0x0420,
143 
144 		/* color */
145 		C_WHITE		= 0x0000,
146 		C_GREEN		= 0x0002,
147 		C_BLUE		= 0x0004,
148 		C_CYAN		= 0x0006,
149 		C_RED		= 0x0008,
150 		C_YELLOW	= 0x000a,
151 		C_MAGENTA	= 0x000c,
152 		C_ITALICS	= 0x000e,
153 
154 		/* underline */
155 		C_NORMAL	= 0x0000,
156 		C_UNDERLINE	= 0x0001,
157 
158 		/* control */
159 		C_RCL		= 0x0000,	// resume caption loading
160 		C_BS		= 0x0001,	// backspace
161 		C_AOF		= 0x0002,	// alarm off
162 		C_AON		= 0x0003,	// alarm on
163 		C_DER		= 0x0004,	// delete to end of row
164 		C_RU2		= 0x0005,	// roll-up captions (2 rows)
165 		C_RU3		= 0x0006,	// roll-up captions (3 rows)
166 		C_RU4		= 0x0007,	// roll-up captions (4 rows)
167 		C_FON		= 0x0008,	// flash on
168 		C_RDC		= 0x0009,	// resume direct captioning
169 		C_TR		= 0x000a,	// text restart
170 		C_RTD		= 0x000b,	// resume text display
171 		C_EDM		= 0x000c,	// erase displayed memory
172 		C_CR		= 0x000d,	// carriage return
173 		C_ENM		= 0x000e,	// erase non-displayed memory
174 		C_EOC		= 0x000f,	// end of caption
175 
176 		/* special character */
177 		C_reg		= 0x0000,	// registered
178 		C_deg		= 0x0001,	// degree
179 		C_frac12	= 0x0002,	// fraction 1/2
180 		C_iquest	= 0x0003,	// invert question
181 		C_trademark	= 0x0004,	// trademark
182 		C_cent		= 0x0005,	// cent
183 		C_pound		= 0x0006,	// pound
184 		C_music		= 0x0007,	// music note
185 		C_agrave	= 0x0008,	// agrave
186 		C_tspace	= 0x0009,	// transparent space
187 		C_egrave	= 0x000a,	// egrave
188 		C_acirc		= 0x000b,	// a circ
189 		C_ecirc		= 0x000c,	// e circ
190 		C_icirc		= 0x000d,	// i circ
191 		C_ocirc		= 0x000e,	// o circ
192 		C_ucirc		= 0x000f,	// u circ
193 
194 		/* standard character (ASCII) */
195 		C_aacute	= 0x002a,	// a acute accent
196 		C_eacute	= 0x005c,	// e acute accent
197 		C_iacute	= 0x005e,	// i acute accent
198 		C_oacute	= 0x005f,	// o acute accent
199 		C_uacute	= 0x0060,	// u acute accent
200 		C_ccedil	= 0x007b,	// c cedilla
201 		C_division	= 0x007c,	// division sign
202 		C_Ntilde	= 0x007d,	// N tilde
203 		C_ntilde	= 0x007e,	// n tilde
204 		C_block		= 0x007f	// solid block
205 	};
206 
207 	int code, channel, character;
208 
209 	/* decode next pair of bytes from the odd or even field buffer */
210 	if (!DecodeBits(fChannel == C_RADEON_CC1 || fChannel == C_RADEON_CC2 ? buffer0 : buffer1, code))
211 		return false;
212 
213 	/* swap decoded bytes */
214 	code = ((code << 8) & 0xff00) | ((code >> 8) & 0x00ff);
215 
216 	/* check parity */
217 	for (int bit = 0; bit < 7; bit++) {
218 		if ((code & (0x0001 << bit)) != 0)
219 			code ^= 0x0080;
220 		if ((code & (0x0100 << bit)) != 0)
221 			code ^= 0x8000;
222 	}
223 	if ((code & 0x8080) != 0x8080) {
224 		PRINT(("CCaption::Decode() - parity error (%04X)\n", code));
225 		return false;
226 	}
227 
228 	/* check channel number */
229 	channel = (code & 0x0800) >> 12;
230 	if (channel == 0) {
231 		if (fChannel != C_RADEON_CC1 && fChannel != C_RADEON_CC3) {
232 			PRINT(("CCaption::Decode() - ignore channel (%02X)\n", code & 0x7f7f));
233 			return false;
234 		}
235 	}
236 	else {
237 		if (fChannel != C_RADEON_CC2 && fChannel != C_RADEON_CC4) {
238 			PRINT(("CCaption::Decode() - ignore channel (%02X)\n", code & 0x7f7f));
239 			return false;
240 		}
241 	}
242 
243 	if ((code & 0x7000) == 0x0000) {
244 		/* one-byte standard character (0?XX) */
245 		character = code & 0x007f;
246 
247 		if (character >= 0x20) {
248 
249 			PRINT(("%c", character));
250 
251 			fText[fRow][fColumn] = (fColor << 8) + character;
252 			if (fColumn < C_RADEON_CC_COLUMNS - 1)
253 				fColumn++;
254 		}
255 		else if (character != 0x00) {
256 			PRINT(("<%04X>", code & 0x7f7f));
257 		}
258 	}
259 	else if ((code & 0x7770) == 0x1120) {
260 		/* middle row code (112X) */
261 		fColor = code & 0x000f;
262 
263 		fText[fRow][fColumn] = (fColor << 8) + ' ';
264 		if (fColumn < C_RADEON_CC_COLUMNS - 1)
265 			fColumn++;
266 
267 		switch (fColor & 0x000e) {
268 		case C_WHITE:
269 			PRINT(("<white"));
270 			break;
271 		case C_GREEN:
272 			PRINT(("<green"));
273 			break;
274 		case C_BLUE:
275 			PRINT(("<blue"));
276 			break;
277 		case C_CYAN:
278 			PRINT(("<cyan"));
279 			break;
280 		case C_RED:
281 			PRINT(("<red"));
282 			break;
283 		case C_YELLOW:
284 			PRINT(("<yellow"));
285 			break;
286 		case C_MAGENTA:
287 			PRINT(("<magenta"));
288 			break;
289 		case C_ITALICS:
290 			PRINT(("<italics"));
291 			break;
292 		}
293 		if ((fColor & 0x0001) != 0)
294 			PRINT((",underline>"));
295 		else
296 			PRINT((">"));
297 	}
298 	else if ((code & 0x7770) == 0x1130) {
299 		/* two-byte special character (113X) */
300 		character = (code & 0x000f);
301 
302 		fText[fRow][fColumn] = (fColor << 8) + character + 0x0080;
303 		if (fColumn < C_RADEON_CC_COLUMNS - 1)
304 			fColumn++;
305 
306 		switch (character) {
307 		case C_reg:
308 			PRINT(("&reg;"));
309 			break;
310 		case C_deg:
311 			PRINT(("&deg;"));
312 			break;
313 		case C_frac12:
314 			PRINT(("&frac12;"));
315 			break;
316 		case C_iquest:
317 			PRINT(("&iquest;"));
318 			break;
319 		case C_trademark:
320 			PRINT(("&trademark;"));
321 			break;
322 		case C_cent:
323 			PRINT(("&cent;"));
324 			break;
325 		case C_pound:
326 			PRINT(("&pound;"));
327 			break;
328 		case C_music:
329 			PRINT(("&music;"));
330 			break;
331 		case C_agrave:
332 			PRINT(("&agrave;"));
333 			break;
334 		case C_tspace:
335 			PRINT(("&tspace;"));
336 			break;
337 		case C_egrave:
338 			PRINT(("&egrave;"));
339 			break;
340 		case C_acirc:
341 			PRINT(("&acirc;"));
342 			break;
343 		case C_ecirc:
344 			PRINT(("&ecirc;"));
345 			break;
346 		case C_icirc:
347 			PRINT(("&icirc;"));
348 			break;
349 		case C_ocirc:
350 			PRINT(("&ocirc;"));
351 			break;
352 		case C_ucirc:
353 			PRINT(("&ucirc;"));
354 			break;
355 		default:
356 			PRINT(("<special=%04X>", code & 0x7f7f));
357 			break;
358 		}
359 	}
360 	else if ((code & 0x7770) == 0x1420) {
361 
362 		if (code == fLastControlCode)
363 			return false;
364 		fLastControlCode = code;
365 
366 		/* miscellaneous control code (142X) */
367 		switch (code & 0x000f) {
368 		case C_RCL:
369 			// resume caption loading
370 			PRINT(("<rcl>"));
371 			break;
372 
373 		case C_BS:
374 			// backspace
375 			PRINT(("<bs>"));
376 			if (fColumn > 0)
377 				fText[fRow][--fColumn] = 0x0000;
378 			break;
379 
380 		case C_AOF:
381 			// alarm off
382 			PRINT(("<aof>"));
383 			break;
384 
385 		case C_AON:
386 			// alarm on
387 			PRINT(("<aon>"));
388 			break;
389 
390 		case C_DER:
391 			// delete to end of row
392 			PRINT(("<der>"));
393 			for (int column = fColumn; column < C_RADEON_CC_COLUMNS; column++)
394 				fText[fRow][column] = 0x0000;
395 			break;
396 
397 		case C_RU2:
398 			// rollup captions (2 rows)
399 			PRINT(("<ru2>"));
400 			for (int row = 0; row < C_RADEON_CC_ROWS - 2; row++) {
401 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
402 					fText[row][column] = fText[row + 2][column];
403 			}
404 			for (int row = C_RADEON_CC_ROWS - 2; row < C_RADEON_CC_ROWS; row++) {
405 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
406 					fText[row][column] = 0x0000;
407 			}
408 			break;
409 
410 		case C_RU3:
411 			// rollup captions (3 rows)
412 			PRINT(("<ru3>"));
413 			for (int row = 0; row < C_RADEON_CC_ROWS - 3; row++) {
414 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
415 					fText[row][column] = fText[row + 2][column];
416 			}
417 			for (int row = C_RADEON_CC_ROWS - 3; row < C_RADEON_CC_ROWS; row++) {
418 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
419 					fText[row][column] = 0x0000;
420 			}
421 			break;
422 
423 		case C_RU4:
424 			// rollup captions (4 rows)
425 			PRINT(("<ru4>"));
426 			for (int row = 0; row < C_RADEON_CC_ROWS - 4; row++) {
427 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
428 					fText[row][column] = fText[row + 2][column];
429 			}
430 			for (int row = C_RADEON_CC_ROWS - 4; row < C_RADEON_CC_ROWS; row++) {
431 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
432 					fText[row][column] = 0x0000;
433 			}
434 			break;
435 
436 		case C_FON:
437 			// flash on
438 			PRINT(("<fon>"));
439 			break;
440 
441 		case C_RDC:
442 			// resume direct captioning
443 			PRINT(("<rdc>"));
444 			break;
445 
446 		case C_TR:
447 			// text restart
448 			PRINT(("<tr>"));
449 
450 			fRow = fColumn = 0;
451 			break;
452 
453 		case C_RTD:
454 			// resume text display
455 			PRINT(("<rtd>"));
456 
457 			fRow = fColumn = 0;
458 			break;
459 
460 		case C_EDM:
461 			// erase displayed memory
462 			PRINT(("<edm>\n"));
463 			for (int row = 0; row < C_RADEON_CC_ROWS; row++) {
464 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
465 					fDisplayText[row][column] = 0x0000;
466 			}
467 			DisplayCaptions();
468 			break;
469 
470 		case C_CR:
471 			// carriage return
472 			PRINT(("<cr>"));
473 
474 			/* has no effect in caption loading mode */
475 			break;
476 
477 		case C_ENM:
478 			// erase non-displayed memory
479 			PRINT(("<enm>"));
480 			for (int row = 0; row < C_RADEON_CC_ROWS; row++) {
481 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++)
482 					fText[row][column] = 0x0000;
483 			}
484 			break;
485 
486 		case C_EOC:
487 			// end of caption
488 			PRINT(("<eoc>\n"));
489 			for (int row = 0; row < C_RADEON_CC_ROWS; row++) {
490 				for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) {
491 					const int code = fText[row][column];
492 					fText[row][column] = fDisplayText[row][column];
493 					fDisplayText[row][column] = code;
494 				}
495 			}
496 
497 			DisplayCaptions();
498 			break;
499 		}
500 	}
501 	else if ((code & 0x7770) == 0x1720) {
502 		/* tab offset (172X) */
503 		const int offset = code & 0x000f;
504 
505 		if (offset >= 1 && offset <= 3) {
506 			PRINT(("<tab%d>", offset));
507 
508 			if ((fColumn += offset) >= C_RADEON_CC_COLUMNS - 1)
509 				fColumn = C_RADEON_CC_COLUMNS - 1;
510 		}
511 		else {
512 			PRINT(("<tab=%04X>", code & 0x7f7f));
513 		}
514 	}
515 	else if ((code & 0x7040) == 0x1040) {
516 		/* preamble address code (1Y4X, 1Y6X) */
517 
518 		switch (code & 0x0720) {
519 		case C_ROW_1:
520 			PRINT(("\n<row1"));
521 			fRow = 0;
522 			break;
523 		case C_ROW_2:
524 			PRINT(("\n<row2"));
525 			fRow = 1;
526 			break;
527 		case C_ROW_3:
528 			PRINT(("\n<row3"));
529 			fRow = 2;
530 			break;
531 		case C_ROW_4:
532 			PRINT(("\n<row4"));
533 			fRow = 3;
534 			break;
535 		case C_ROW_5:
536 			PRINT(("\n<row5"));
537 			fRow = 4;
538 			break;
539 		case C_ROW_6:
540 			PRINT(("\n<row6"));
541 			fRow = 5;
542 			break;
543 		case C_ROW_7:
544 			PRINT(("\n<row7"));
545 			fRow = 6;
546 			break;
547 		case C_ROW_8:
548 			PRINT(("\n<row8"));
549 			fRow = 7;
550 			break;
551 		case C_ROW_9:
552 			PRINT(("\n<row9"));
553 			fRow = 8;
554 			break;
555 		case C_ROW_10:
556 			PRINT(("\n<row10"));
557 			fRow = 9;
558 			break;
559 		case C_ROW_11:
560 			PRINT(("\n<row11"));
561 			fRow = 10;
562 			break;
563 		case C_ROW_12:
564 			PRINT(("\n<row12"));
565 			fRow = 11;
566 			break;
567 		case C_ROW_13:
568 			PRINT(("\n<row13"));
569 			fRow = 12;
570 			break;
571 		case C_ROW_14:
572 			PRINT(("\n<row14"));
573 			fRow = 13;
574 			break;
575 		case C_ROW_15:
576 			PRINT(("\n<row15"));
577 			fRow = 14;
578 			break;
579 		default:
580 			PRINT(("\n<pac=%04X>", code & 0x7f7f));
581 			return false;
582 		}
583 
584 		if ((code & 0x0010) == 0x0000) {
585 			/* change color */
586 			fColor = (code & 0x000f);
587 			fColumn = 0;
588 
589 			switch (fColor & 0x000e) {
590 			case C_WHITE:
591 				PRINT((",white"));
592 				break;
593 			case C_GREEN:
594 				PRINT((",green"));
595 				break;
596 			case C_BLUE:
597 				PRINT((",blue"));
598 				break;
599 			case C_CYAN:
600 				PRINT((",cyan"));
601 				break;
602 			case C_RED:
603 				PRINT((",red"));
604 				break;
605 			case C_YELLOW:
606 				PRINT((",yellow"));
607 				break;
608 			case C_MAGENTA:
609 				PRINT((",magenta"));
610 				break;
611 			case C_ITALICS:
612 				PRINT((",italics"));
613 				break;
614 			}
615 			if ((fColor & 0x0001) != 0)
616 				PRINT((",underline>"));
617 			else
618 				PRINT((">"));
619 
620 		}
621 		else {
622 			/* indent, white */
623 			fColor = C_WHITE | (code & 0x0001);
624 			fColumn = (code & 0x000e) << 1;
625 			PRINT((",col%d>", fColumn));
626 		}
627 	}
628 	else {
629 		/* two one-byte standard characters */
630 		character = (code >> 8) & 0x7f;
631 		if (character >= 0x20) {
632 
633 			PRINT(("%c", character));
634 
635 			fText[fRow][fColumn] = (fColor << 8) + character;
636 			if (fColumn < C_RADEON_CC_COLUMNS - 1)
637 				fColumn++;
638 		}
639 		else if (character != 0x00) {
640 			PRINT(("<%02X>", character));
641 		}
642 
643 		character = (code >> 0) & 0x7f;
644 		if (character >= 0x20) {
645 
646 			PRINT(("%c", character));
647 
648 			fText[fRow][fColumn] = (fColor << 8) + character;
649 			if (fColumn < C_RADEON_CC_COLUMNS - 1)
650 				fColumn++;
651 		}
652 		else if (character != 0x00) {
653 			PRINT(("<%02X>", character));
654 		}
655 	}
656 
657 	return true;
658 }
659 
DisplayCaptions() const660 void CCaption::DisplayCaptions() const
661 {
662 	printf("\x1b[H\x1b[J");
663 
664 	for (int row = 0; row < C_RADEON_CC_ROWS; row++) {
665 		for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) {
666 			if (fDisplayText[row][column] == 0x0000) {
667 				printf("\x1b[0;47;37m ");
668 				continue;
669 			}
670 
671 			const int code = (fDisplayText[row][column] >> 0) & 0xff;
672 			const int color = (fDisplayText[row][column] >> 8) & 0xff;
673 
674 			switch (color & 0x000e) {
675 			case 0x0000: // WHITE
676 				printf("\x1b[0;%d;40;37m", (color & 1) << 2);
677 				break;
678 			case 0x0002: // GREEN
679 				printf("\x1b[0;%d;40;32m", (color & 1) << 2);
680 				break;
681 			case 0x0004: // BLUE
682 				printf("\x1b[0;%d;40;34m", (color & 1) << 2);
683 				break;
684 			case 0x0006: // CYAN
685 				printf("\x1b[0;%d;40;35m", (color & 1) << 2);
686 				break;
687 			case 0x0008: // RED
688 				printf("\x1b[0;%d;40;31m", (color & 1) << 2);
689 				break;
690 			case 0x000a: // YELLOW
691 				printf("\x1b[0;%d;40;33m", (color & 1) << 2);
692 				break;
693 			case 0x000c: // MAGENTA
694 				printf("\x1b[0;%d;40;36m", (color & 1) << 2);
695 				break;
696 			case 0x000e: // WHITE ITALICS
697 				if ((color & 1) == 0)
698 					printf("\x1b[1;40;37m");
699 				else
700 					printf("\x1b[1;4;40;37m");
701 				break;
702 			}
703 
704 			if (code >= 0x20 && code <= 0x7f) {
705 				switch (code) {
706 				case 0x002a:
707 					// aacute
708 					printf("\xc3\xa1");
709 					break;
710 				case 0x005c:
711 					// eacute
712 					printf("\xc3\xa9");
713 					break;
714 				case 0x005e:
715 					// iacute
716 					printf("\xc3\xad");
717 					break;
718 				case 0x005f:
719 					// oacute
720 					printf("\xc3\xb3");
721 					break;
722 				case 0x0060:
723 					// uacute
724 					printf("\xc3\xba");
725 					break;
726 				case 0x007b:
727 					// ccedil
728 					printf("\xc3\xa7");
729 					break;
730 				case 0x007c:
731 					// division
732 					printf("\xc3\xb7");
733 					break;
734 				case 0x007d:
735 					// Ntilde
736 					printf("\xc3\x91");
737 					break;
738 				case 0x007e:
739 					// ntilde
740 					printf("\xc3\xb1");
741 					break;
742 				case 0x007f:
743 					// block
744 					printf("\xc1\xbf");
745 					break;
746 				default:
747 					// ASCII character
748 					printf("%c", code);
749 					break;
750 				}
751 			}
752 			else {
753 				switch (code) {
754 				case 0x0080:
755 					// reg
756 					printf("\xc2\xae");
757 					break;
758 				case 0x0081:
759 					// deg
760 					printf("\xc2\xb0");
761 					break;
762 				case 0x0082:
763 					// frac12
764 					printf("\xc2\xbd");
765 					break;
766 				case 0x0083:
767 					// iquest
768 					printf("\xc2\xbf");
769 					break;
770 				case 0x0084:
771 					// trademark
772 					printf("\xe2\x84");
773 					break;
774 				case 0x0085:
775 					// cent
776 					printf("\xc2\xa2");
777 					break;
778 				case 0x0086:
779 					// pound
780 					printf("\xc2\xa3");
781 					break;
782 				case 0x0087:
783 					// music
784 					printf("\xc2\xa7");
785 					break;
786 				case 0x0088:
787 					// agrave
788 					printf("\xc3\xa0");
789 					break;
790 				case 0x0089:
791 					// tspace
792 					printf("\x1b[0;47;37m ");
793 					break;
794 				case 0x008a:
795 					// egrave
796 					printf("\xc3\xa8");
797 					break;
798 				case 0x008b:
799 					// acirc
800 					printf("\xc3\xa2");
801 					break;
802 				case 0x008c:
803 					// ecirc
804 					printf("\xc3\xaa");
805 					break;
806 				case 0x008d:
807 					// icirc
808 					printf("\xc3\xae");
809 					break;
810 				case 0x008e:
811 					// ocirc
812 					printf("\xc3\xb4");
813 					break;
814 				case 0x008f:
815 					// ucirc
816 					printf("\xc3\xbb");
817 					break;
818 				default:
819 					// buggy code
820 					printf("<%02X>", code);
821 					break;
822 				}
823 			}
824 		}
825 		printf("\n");
826 	}
827 	printf("\x1b[0;30;47m");
828 }
829