/* Synchronet Unicode encode/decode/translate functions */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "unicode.h"
#include "cp437defs.h"
#include "unicode_defs.h"

// Want UNICODE encodings of terminal control characters?
#if defined USE_UNICODE_FOR_TERM_CTRL_CHARS
#   define UNICODE_TERM_CTRL_CHAR_CODE(x) x
#else
#   define UNICODE_TERM_CTRL_CHAR_CODE(x) 0
#endif

// CP437 character to/from UNICODE code point conversion
// The CP437 character value is the index into the table.
// If the value at that index is 0, no translation is needed (1:1 mapping).
enum unicode_codepoint cp437_unicode_tbl[] =
{
	/* 0x00 */ UNICODE_UNDEFINED,
	/* 0x01 */ UNICODE_WHITE_SMILING_FACE,
	/* 0x02 */ UNICODE_BLACK_SMILING_FACE,
	/* 0x03 */ UNICODE_BLACK_HEART_SUIT,
	/* 0x04 */ UNICODE_BLACK_DIAMOND_SUIT,
	/* 0x05 */ UNICODE_BLACK_CLUB_SUIT,
	/* 0x06 */ UNICODE_BLACK_SPADE_SUIT,
	/* 0x07 '\a' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_BULLET),
	/* 0x08 '\b' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_INVERSE_BULLET),
	/* 0x09 '\t' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_WHITE_CIRCLE),
	/* 0x0A '\n' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_INVERSE_WHITE_CIRCLE),
	/* 0x0B */ UNICODE_MALE_SIGN,
	/* 0x0C '\f' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_FEMALE_SIGN),
	/* 0x0D '\r' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_EIGHTH_NOTE),
	/* 0x0E */ UNICODE_BEAMED_EIGHTH_NOTES,
	/* 0x0F */ UNICODE_WHITE_SUN_WITH_RAYS,
	/* 0x10 */ UNICODE_BLACK_RIGHT_POINTING_POINTER,
	/* 0x11 */ UNICODE_BLACK_LEFT_POINTING_POINTER,
	/* 0x12 */ UNICODE_UP_DOWN_ARROW,
	/* 0x13 */ UNICODE_DOUBLE_EXCLAMATION_MARK,
	/* 0x14 */ UNICODE_PILCROW_SIGN,
	/* 0x15 */ UNICODE_SECTION_SIGN,
	/* 0x16 */ UNICODE_BLACK_RECTANGLE,
	/* 0x17 */ UNICODE_UP_DOWN_ARROW_WITH_BASE,
	/* 0x18 */ UNICODE_UPWARDS_ARROW,
	/* 0x19 */ UNICODE_DOWNWARDS_ARROW,
	/* 0x1A */ UNICODE_RIGHTWARDS_ARROW,
	/* 0x1B '\e' */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_LEFTWARDS_ARROW),
	/* 0x1C */ UNICODE_RIGHT_ANGLE,
	/* 0x1D */ UNICODE_LEFT_RIGHT_ARROW,
	/* 0x1E */ UNICODE_BLACK_UP_POINTING_TRIANGLE,
	/* 0x1F */ UNICODE_BLACK_DOWN_POINTING_TRIANGLE,
	/* 0x20-0x7C	(1:1 with US-ASCII and CP437) */
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	/* 0x7C */ UNICODE_BROKEN_BAR,
	/* 0x7D-0x7E	(1:1 with US-ASCII and CP437) */
	0,
	0,
	/* 0x7F (DEL) */ UNICODE_TERM_CTRL_CHAR_CODE(UNICODE_HOUSE),
	/* 0x80 */ UNICODE_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA,
	/* 0x81 */ UNICODE_LATIN_SMALL_LETTER_U_WITH_DIAERESIS,
	/* 0x82 */ UNICODE_LATIN_SMALL_LETTER_E_WITH_ACUTE,
	/* 0x83 */ UNICODE_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX,
	/* 0x84 */ UNICODE_LATIN_SMALL_LETTER_A_WITH_DIAERESIS,
	/* 0x85 */ UNICODE_LATIN_SMALL_LETTER_A_WITH_GRAVE,
	/* 0x86 */ UNICODE_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE,
	/* 0x87 */ UNICODE_LATIN_SMALL_LETTER_C_WITH_CEDILLA,
	/* 0x88 */ UNICODE_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX,
	/* 0x89 */ UNICODE_LATIN_SMALL_LETTER_E_WITH_DIAERESIS,
	/* 0x8A */ UNICODE_LATIN_SMALL_LETTER_E_WITH_GRAVE,
	/* 0x8B */ UNICODE_LATIN_SMALL_LETTER_I_WITH_DIAERESIS,
	/* 0x8C */ UNICODE_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX,
	/* 0x8D */ UNICODE_LATIN_SMALL_LETTER_I_WITH_GRAVE,
	/* 0x8E */ UNICODE_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS,
	/* 0x8F */ UNICODE_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE,
	/* 0x90 */ UNICODE_LATIN_CAPITAL_LETTER_E_WITH_ACUTE,
	/* 0x91 */ UNICODE_LATIN_SMALL_LETTER_AE,
	/* 0x92 */ UNICODE_LATIN_CAPITAL_LETTER_AE,
	/* 0x93 */ UNICODE_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX,
	/* 0x94 */ UNICODE_LATIN_SMALL_LETTER_O_WITH_DIAERESIS,
	/* 0x95 */ UNICODE_LATIN_SMALL_LETTER_O_WITH_GRAVE,
	/* 0x96 */ UNICODE_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX,
	/* 0x97 */ UNICODE_LATIN_SMALL_LETTER_U_WITH_GRAVE,
	/* 0x98 */ UNICODE_LATIN_SMALL_LETTER_Y_WITH_DIAERESIS,
	/* 0x99 */ UNICODE_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS,
	/* 0x9A */ UNICODE_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS,
	/* 0x9B */ UNICODE_CENT_SIGN,
	/* 0x9C */ UNICODE_POUND_SIGN,
	/* 0x9D */ UNICODE_YEN_SIGN,
	/* 0x9E */ UNICODE_PESETA_SIGN,
	/* 0x9F */ UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK,
	/* 0xA0 */ UNICODE_LATIN_SMALL_LETTER_A_WITH_ACUTE,
	/* 0xA1 */ UNICODE_LATIN_SMALL_LETTER_I_WITH_ACUTE,
	/* 0xA2 */ UNICODE_LATIN_SMALL_LETTER_O_WITH_ACUTE,
	/* 0xA3 */ UNICODE_LATIN_SMALL_LETTER_U_WITH_ACUTE,
	/* 0xA4 */ UNICODE_LATIN_SMALL_LETTER_N_WITH_TILDE,
	/* 0xA5 */ UNICODE_LATIN_CAPITAL_LETTER_N_WITH_TILDE,
	/* 0xA6 */ UNICODE_FEMININE_ORDINAL_INDICATOR,
	/* 0xA7 */ UNICODE_MASCULINE_ORDINAL_INDICATOR,
	/* 0xA8 */ UNICODE_INVERTED_QUESTION_MARK,
	/* 0xA9 */ UNICODE_REVERSED_NOT_SIGN,
	/* 0xAA */ UNICODE_NOT_SIGN,
	/* 0xAB */ UNICODE_VULGAR_FRACTION_ONE_HALF,
	/* 0xAC */ UNICODE_VULGAR_FRACTION_ONE_QUARTER,
	/* 0xAD */ UNICODE_INVERTED_EXCLAMATION_MARK,
	/* 0xAE */ UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,
	/* 0xAF */ UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,
	/* 0xB0 */ UNICODE_LIGHT_SHADE,
	/* 0xB1 */ UNICODE_MEDIUM_SHADE,
	/* 0xB2 */ UNICODE_DARK_SHADE,
	/* 0xB3 */ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL,
	/* 0xB4 */ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT,
	/* 0xB5 */ UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE,
	/* 0xB6 */ UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE,
	/* 0xB7 */ 0x2556,
	/* 0xB8 */ 0x2555,
	/* 0xB9 */ 0x2563,
	/* 0xBA */ 0x2551,
	/* 0xBB */ 0x2557,
	/* 0xBC */ 0x255D,
	/* 0xBD */ 0x255C,
	/* 0xBE */ 0x255B,
	/* 0xBF */ 0x2510,
	/* 0xC0 */ 0x2514,
	/* 0xC1 */ 0x2534,
	/* 0xC2 */ 0x252C,
	/* 0xC3 */ 0x251C,
	/* 0xC4 */ 0x2500,
	/* 0xC5 */ 0x253C,
	/* 0xC6 */ 0x255E,
	/* 0xC7 */ 0x255F,
	/* 0xC8 */ 0x255A,
	/* 0xC9 */ 0x2554,
	/* 0xCA */ 0x2569,
	/* 0xCB */ 0x2566,
	/* 0xCC */ 0x2560,
	/* 0xCD */ 0x2550,
	/* 0xCE */ 0x256C,
	/* 0xCF */ 0x2567,
	/* 0xD0 */ 0x2568,
	/* 0xD1 */ 0x2564,
	/* 0xD2 */ 0x2565,
	/* 0xD3 */ 0x2559,
	/* 0xD4 */ 0x2558,
	/* 0xD5 */ 0x2552,
	/* 0xD6 */ 0x2553,
	/* 0xD7 */ 0x256B,
	/* 0xD8 */ 0x256A,
	/* 0xD9 */ 0x2518,
	/* 0xDA */ 0x250C,
	/* 0xDB */ 0x2588,
	/* 0xDC */ UNICODE_LOWER_HALF_BLOCK,
	/* 0xDD */ 0x258C,
	/* 0xDE */ 0x2590,
	/* 0xDF */ UNICODE_UPPER_HALF_BLOCK,
	/* 0xE0 */ UNICODE_GREEK_SMALL_LETTER_ALPHA,
	/* 0xE1 */ UNICODE_GREEK_SMALL_LETTER_BETA, // or UNICODE_LATIN_SMALL_LETTER_SHARP_S
	/* 0xE2 */ UNICODE_GREEK_SMALL_LETTER_GAMMA,
	/* 0xE3 */ UNICODE_GREEK_SMALL_LETTER_PI,
	/* 0xE4 */ UNICODE_GREEK_CAPITAL_LETTER_SIGMA,
	/* 0xE5 */ UNICODE_GREEK_SMALL_LETTER_SIGMA,
	/* 0xE6 */ UNICODE_GREEK_SMALL_LETTER_MU, // or UNICODE_MICRO_SIGN
	/* 0xE7 */ UNICODE_GREEK_SMALL_LETTER_TAU,
	/* 0xE8 */ UNICODE_GREEK_CAPITAL_LETTER_PHI,
	/* 0xE9 */ UNICODE_GREEK_SMALL_LETTER_THETA, // or UNICODE_GREEK_CAPITAL_LETTER_THETA
	/* 0xEA */ UNICODE_GREEK_CAPITAL_LETTER_OMEGA,
	/* 0xEB */ UNICODE_GREEK_SMALL_LETTER_DELTA,
	/* 0xEC */ UNICODE_INFINITY,
	/* 0xED */ UNICODE_GREEK_SMALL_LETTER_PHI,
	/* 0xEE */ UNICODE_GREEK_SMALL_LETTER_EPSILON,
	/* 0xEF */ UNICODE_INTERSECTION,
	/* 0xF0 */ UNICODE_IDENTICAL_TO, // A.K.A. "TRIPLE BAR"
	/* 0xF1 */ UNICODE_PLUS_MINUS_SIGN,
	/* 0xF2 */ UNICODE_GREATER_THAN_OR_EQUAL_TO,
	/* 0xF3 */ UNICODE_LESS_THAN_OR_EQUAL_TO,
	/* 0xF4 */ UNICODE_TOP_HALF_INTEGRAL,
	/* 0xF5 */ UNICODE_BOTTOM_HALF_INTEGRAL,
	/* 0xF6 */ UNICODE_DIVISION_SIGN,
	/* 0xF7 */ UNICODE_ALMOST_EQUAL_TO,
	/* 0xF8 */ UNICODE_DEGREE_SIGN,
	/* 0xF9 */ UNICODE_BULLET_OPERATOR,
	/* 0xFA */ UNICODE_MIDDLE_DOT,
	/* 0xFB */ UNICODE_SQUARE_ROOT, // or UNICODE_CHECK_MARK
	/* 0xFC */ UNICODE_SUPERSCRIPT_LATIN_SMALL_LETTER_N,
	/* 0xFD */ UNICODE_SUPERSCRIPT_TWO,
	/* 0xFE */ UNICODE_HALFWIDTH_BLACK_SQUARE, // or UNICODE_BLACK_SQUARE
	/* 0xFF */ UNICODE_NO_BREAK_SPACE
};

bool unicode_is_whitespace(enum unicode_codepoint u)
{
	return (u >= UNICODE_CHARACTER_TABULATION && u <= UNICODE_CARRIAGE_RETURN)
	    || u == UNICODE_SPACE
	    || u == UNICODE_NEXT_LINE
	    || u == UNICODE_NO_BREAK_SPACE
	    || u == UNICODE_OGHAM_SPACE_MARK
	    || (u >= UNICODE_EN_QUAD && u <= UNICODE_HAIR_SPACE)
	    || u == UNICODE_LINE_SEPARATOR
	    || u == UNICODE_PARAGRAPH_SEPARATOR
	    || u == UNICODE_NARROW_NO_BREAK_SPACE
	    || u == UNICODE_MEDIUM_MATHEMATICAL_SPACE
	    || u == UNICODE_IDEOGRAPHIC_SPACE;
}

bool unicode_is_zerowidth(enum unicode_codepoint u)
{
	switch (u) {
		case UNICODE_ZERO_WIDTH_SPACE:
		case UNICODE_ZERO_WIDTH_NON_JOINER:
		case UNICODE_ZERO_WIDTH_JOINER:
		case UNICODE_VARIATION_SELECTOR_1:
		case UNICODE_VARIATION_SELECTOR_2:
		case UNICODE_VARIATION_SELECTOR_3:
		case UNICODE_VARIATION_SELECTOR_4:
		case UNICODE_VARIATION_SELECTOR_5:
		case UNICODE_VARIATION_SELECTOR_6:
		case UNICODE_VARIATION_SELECTOR_7:
		case UNICODE_VARIATION_SELECTOR_8:
		case UNICODE_VARIATION_SELECTOR_9:
		case UNICODE_VARIATION_SELECTOR_10:
		case UNICODE_VARIATION_SELECTOR_11:
		case UNICODE_VARIATION_SELECTOR_12:
		case UNICODE_VARIATION_SELECTOR_13:
		case UNICODE_VARIATION_SELECTOR_14:
		case UNICODE_VARIATION_SELECTOR_15:
		case UNICODE_VARIATION_SELECTOR_16:
		case UNICODE_ZERO_WIDTH_NO_BREAK_SPACE:
			return true;
		default:
			return false;
	}
}

size_t unicode_width(enum unicode_codepoint u, size_t zerowidth)
{
	switch (u) {
		case UNICODE_UNDEFINED:
			return 0;
		// Exceptions to the ranges (blocks/sub-blocks) in the default case
		case UNICODE_CIRCLED_NUMBER_TEN_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_TWENTY_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_THIRTY_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_FORTY_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_FIFTY_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_SIXTY_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_SEVENTY_ON_BLACK_SQUARE:
		case UNICODE_CIRCLED_NUMBER_EIGHTY_ON_BLACK_SQUARE:
		case UNICODE_HOT_PEPPER:
		case UNICODE_FORK_AND_KNIFE_WITH_PLATE:
			return 1;
		case UNICODE_MAHJONG_TILE_RED_DRAGON:
		case UNICODE_PLAYING_CARD_BLACK_JOKER:
		case UNICODE_NEGATIVE_SQUARED_AB:
		case UNICODE_HEAVY_EQUAL_SIGN:
			return 2;
		// Ranges
		default:
			if ((u >= UNICODE_SUBBLOCK_YIJING_HEXAGRAM_BEGIN             && u <= UNICODE_SUBBLOCK_YIJING_HEXAGRAM_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS1_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS1_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS2_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS2_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS3_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS3_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS4_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS4_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS5_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS5_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS6_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS6_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS7_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS7_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS8_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS8_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS9_BEGIN          && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS9_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS10_BEGIN         && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS10_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS11_BEGIN         && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS11_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS12_BEGIN         && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS12_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS13_BEGIN         && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS13_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS14_BEGIN         && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS14_END)
			    || (u >= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS15_BEGIN         && u <= UNICODE_SUBBLOCK_HALFWIDTH_SYMBOLS15_END)
			    )
				return 1;
			if ((u >= UNICODE_SUBBLOCK_FULLWIDTH_MISC_SYMBOLS_BEGIN      && u <= UNICODE_SUBBLOCK_FULLWIDTH_MISC_SYMBOLS_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_HANGUL_BEGIN            && u <= UNICODE_SUBBLOCK_FULLWIDTH_HANGUL_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_CHARS_BEGIN             && u <= UNICODE_SUBBLOCK_FULLWIDTH_CHARS_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_SYMBOLS_BEGIN           && u <= UNICODE_SUBBLOCK_FULLWIDTH_SYMBOLS_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_KHITAN_BEGIN            && u <= UNICODE_SUBBLOCK_FULLWIDTH_KHITAN_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_ENCLOSED_ALPHANUM_BEGIN && u <= UNICODE_SUBBLOCK_FULLWIDTH_ENCLOSED_ALPHANUM_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_REG_INDICATOR_SYM_BEGIN && u <= UNICODE_SUBBLOCK_FULLWIDTH_REG_INDICATOR_SYM_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_ENC_IDEOG_SUPP_BEGIN    && u <= UNICODE_SUBBLOCK_FULLWIDTH_ENC_IDEOG_SUPP_END)
			    || (u >= UNICODE_SUBBLOCK_FULLWIDTH_GEOM_SHAPES_EXT_BEGIN   && u <= UNICODE_SUBBLOCK_FULLWIDTH_GEOM_SHAPES_EXT_END)
			    || (u >= UNICODE_BLOCK_CJK_RADICALS_SUPPLEMENT_BEGIN        && u <= UNICIDE_BLOCK_YI_RADICALS_END)
			    || (u >= UNICIDE_BLOCK_HANGUL_JAMO_EXTENDED_A_BEGIN         && u <= UNICIDE_BLOCK_HANGUL_JAMO_EXTENDED_A_END)
			    || (u >= UNICIDE_BLOCK_HANGUL_SYLLABLES_BEGIN               && u <= UNICIDE_BLOCK_HANGUL_SYLLABLES_END)
			    || (u >= UNICODE_BLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_BEGIN   && u <= UNICODE_BLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_END)
			    || (u >= UNICODE_BLOCK_VERTICAL_FORMS_BEGIN                 && u <= UNICODE_BLOCK_VERTICAL_FORMS_END)
			    || (u >= UNICODE_BLOCK_CJK_COMPATIBILITY_FORMS_BEGIN        && u <= UNICODE_BLOCK_CJK_COMPATIBILITY_FORMS_END)
			    || (u >= UNICODE_BLOCK_SMALL_FORM_VARIANTS_BEGIN            && u <= UNICODE_BLOCK_SMALL_FORM_VARIANTS_END)
			    || (u >= UNICODE_BLOCK_TANGUT_BEGIN                         && u <= UNICODE_BLOCK_TANGUT_END)
			    || (u >= UNICODE_BLOCK_TANGUT_COMPONENTS_BEGIN              && u <= UNICODE_BLOCK_TANGUT_COMPONENTS_END)
			    || (u >= UNICODE_BLOCK_KANA_SUPPLEMENT_BEGIN                && u <= UNICODE_BLOCK_KANA_SUPPLEMENT_END)
			    || (u >= UNICODE_BLOCK_KANA_EXTENDED_A_BEGIN                && u <= UNICODE_BLOCK_KANA_EXTENDED_A_END)
			    || (u >= UNICODE_BLOCK_NUSHU_BEGIN                          && u <= UNICODE_BLOCK_NUSHU_END)
			    || (u >= UNICODE_BLOCK_EMOTICONS_BEGIN                      && u <= UNICODE_BLOCK_EMOTICONS_END)
			    || (u >= UNICODE_BLOCK_MISC_SYMBOLS_AND_PICTOGRAPHS_BEGIN   && u <= UNICODE_BLOCK_MISC_SYMBOLS_AND_PICTOGRAPHS_END)
			    || (u >= UNICODE_BLOCK_SUPP_SYMBOLS_AND_PICTOGRAPHS_BEGIN   && u <= UNICODE_BLOCK_SUPP_SYMBOLS_AND_PICTOGRAPHS_END)
			    || (u >= UNICODE_BLOCK_EXTA_SYMBOLS_AND_PICTOGRAPHS_BEGIN   && u <= UNICODE_BLOCK_EXTA_SYMBOLS_AND_PICTOGRAPHS_END)
			    )
				return 2;
			if (unicode_is_zerowidth(u))
				return zerowidth;
			return 1;
	}
}

char unicode_to_latin1(enum unicode_codepoint codepoint)
{
	if (codepoint < 0 || codepoint > 255)
		return UNICODE_UNDEFINED;
	return codepoint;
}

char unicode_to_cp437(enum unicode_codepoint codepoint)
{
	switch (codepoint) {
		case UNICODE_ACUTE_ACCENT:                              return '\'';

		case UNICODE_VERTICAL_LINE:                             return '|';
		case UNICODE_BROKEN_BAR:                                return '|';
		case UNICODE_MACRON:                                    return '~';

		case UNICODE_CENT_SIGN:                                 return CP437_CENT_SIGN;
		case UNICODE_POUND_SIGN:                                return CP437_POUND_SIGN;
		case UNICODE_YEN_SIGN:                                  return CP437_YEN_SIGN;
		case UNICODE_SECTION_SIGN:                              return CP437_SECTION_SIGN;
		case UNICODE_DEGREE_CELSIUS:
		case UNICODE_DEGREE_FAHRENHEIT:
		case UNICODE_DEGREE_SIGN:                               return CP437_DEGREE_SIGN;
		case UNICODE_PLUS_MINUS_SIGN:                           return CP437_PLUS_MINUS_SIGN;
		case UNICODE_SUPERSCRIPT_TWO:                           return CP437_SUPERSCRIPT_TWO;
		case UNICODE_PILCROW_SIGN:                              return CP437_PILCROW_SIGN;
		case UNICODE_INVERTED_QUESTION_MARK:                    return CP437_INVERTED_QUESTION_MARK;
		case UNICODE_INVERTED_EXCLAMATION_MARK:                 return CP437_INVERTED_EXCLAMATION_MARK;
		case UNICODE_DOUBLE_EXCLAMATION_MARK:                   return CP437_DOUBLE_EXCLAMATION_MARK;
		case UNICODE_LEFT_POINTING_ANGLE_BRACKET:               return '<';
		case UNICODE_RIGHT_POINTING_ANGLE_BRACKET:              return '>';
		case UNICODE_COUNTERSINK:                               return 'v';
		case UNICODE_APL_FUNCTIONAL_SYMBOL_I_BEAM:              return 'I';

		// Perform Upper -> Lower case mapping where an upper case equivalent doesn't exist in CP437:
		case UNICODE_LATIN_CAPITAL_LETTER_A_WITH_GRAVE:         return CP437_LATIN_SMALL_LETTER_A_WITH_GRAVE;
		case UNICODE_LATIN_CAPITAL_LETTER_A_WITH_ACUTE:         return CP437_LATIN_SMALL_LETTER_A_WITH_ACUTE;
		case UNICODE_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX:    return CP437_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX;

		case UNICODE_LATIN_CAPITAL_LETTER_E_WITH_GRAVE:         return CP437_LATIN_SMALL_LETTER_E_WITH_GRAVE;
		case UNICODE_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX:    return CP437_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX;
		case UNICODE_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS:     return CP437_LATIN_SMALL_LETTER_E_WITH_DIAERESIS;

		case UNICODE_LATIN_CAPITAL_LETTER_I_WITH_GRAVE:         return CP437_LATIN_SMALL_LETTER_I_WITH_GRAVE;
		case UNICODE_LATIN_CAPITAL_LETTER_I_WITH_ACUTE:         return CP437_LATIN_SMALL_LETTER_I_WITH_ACUTE;
		case UNICODE_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX:    return CP437_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX;
		case UNICODE_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS:     return CP437_LATIN_SMALL_LETTER_I_WITH_DIAERESIS;

		case UNICODE_LATIN_CAPITAL_LETTER_O_WITH_GRAVE:         return CP437_LATIN_SMALL_LETTER_O_WITH_GRAVE;
		case UNICODE_LATIN_CAPITAL_LETTER_O_WITH_ACUTE:         return CP437_LATIN_SMALL_LETTER_O_WITH_ACUTE;
		case UNICODE_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX:    return CP437_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX;

		case UNICODE_LATIN_CAPITAL_LETTER_U_WITH_GRAVE:         return CP437_LATIN_SMALL_LETTER_U_WITH_GRAVE;
		case UNICODE_LATIN_CAPITAL_LETTER_U_WITH_ACUTE:         return CP437_LATIN_SMALL_LETTER_U_WITH_ACUTE;
		case UNICODE_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX:    return CP437_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX;

		case UNICODE_LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS:     return CP437_LATIN_SMALL_LETTER_Y_WITH_DIAERESIS;

		// Greek letters
		case UNICODE_GREEK_CAPITAL_LETTER_ALPHA:
		case UNICODE_GREEK_CAPITAL_LETTER_ALPHA_WITH_TONOS:     return 'A';
		case UNICODE_GREEK_CAPITAL_LETTER_BETA:                 return 'B';
		case UNICODE_GREEK_CAPITAL_LETTER_GAMMA:
		case UNICODE_GREEK_SMALL_LETTER_GAMMA:                  return CP437_GREEK_CAPITAL_LETTER_GAMMA;
		case UNICODE_GREEK_CAPITAL_LETTER_DELTA:
		case UNICODE_GREEK_SMALL_LETTER_DELTA:                  return CP437_GREEK_SMALL_LETTER_DELTA;
		case UNICODE_GREEK_CAPITAL_LETTER_EPSILON:
		case UNICODE_GREEK_CAPITAL_LETTER_EPSILON_WITH_TONOS:
		case UNICODE_GREEK_SMALL_LETTER_EPSILON:
		case UNICODE_GREEK_SMALL_LETTER_EPSILON_WITH_TONOS:     return CP437_GREEK_SMALL_LETTER_EPSILION;
		case UNICODE_GREEK_CAPITAL_LETTER_ZETA:                 return 'Z';
		case UNICODE_GREEK_CAPITAL_LETTER_ETA:                  return 'H';
		case UNICODE_GREEK_CAPITAL_LETTER_THETA:
		case UNICODE_GREEK_SMALL_LETTER_THETA:
		case UNICODE_GREEK_THETA_SYMBOL:                        return CP437_GREEK_SMALL_LETTER_THETA;
		case UNICODE_GREEK_CAPITAL_LETTER_IOTA:
		case UNICODE_GREEK_CAPITAL_LETTER_IOTA_WITH_DIALYTIKA:  return 'I';
		case UNICODE_GREEK_CAPITAL_LETTER_KAPPA:                return 'K';
		case UNICODE_GREEK_CAPITAL_LETTER_MU:                   return 'M';
		case UNICODE_GREEK_CAPITAL_LETTER_NU:                   return 'N';
		case UNICODE_GREEK_CAPITAL_LETTER_OMICRON:              return 'O';
		case UNICODE_GREEK_CAPITAL_LETTER_PI:
		case UNICODE_GREEK_SMALL_LETTER_PI:
		case UNICODE_GREEK_PI_SYMBOL:                           return CP437_GREEK_SMALL_LETTER_PI;
		case UNICODE_GREEK_CAPITAL_LETTER_RHO:                  return 'P';
		case UNICODE_GREEK_CAPITAL_LETTER_SIGMA:                return CP437_GREEK_CAPITAL_LETTER_SIGMA;
		case UNICODE_GREEK_CAPITAL_LETTER_TAU:
		case UNICODE_GREEK_SMALL_LETTER_TAU:
			return 'T';
		case UNICODE_GREEK_CAPITAL_LETTER_UPSILON:              return 'Y';
		case UNICODE_GREEK_CAPITAL_LETTER_PHI:                  return CP437_GREEK_CAPITAL_LETTER_PHI;
		case UNICODE_GREEK_CAPITAL_LETTER_CHI:                  return 'X';
		case UNICODE_GREEK_CAPITAL_LETTER_OMEGA:
		case UNICODE_GREEK_CAPITAL_LETTER_OMEGA_WITH_TONOS:
		case UNICODE_GREEK_SMALL_LETTER_OMEGA:
		case UNICODE_GREEK_SMALL_LETTER_OMEGA_WITH_TONOS:       return CP437_GREEK_CAPITAL_LETTER_OMEGA;
		case UNICODE_GREEK_CAPITAL_LETTER_UPSILON_WITH_DIALYTIKA:
			return CP437_LATIN_SMALL_LETTER_Y_WITH_DIAERESIS;
		case UNICODE_GREEK_SMALL_LETTER_ALPHA:
		case UNICODE_GREEK_SMALL_LETTER_ALPHA_WITH_TONOS:       return CP437_GREEK_SMALL_LETTER_ALPHA;
		case UNICODE_GREEK_SMALL_LETTER_BETA:                   return CP437_GREEK_SMALL_LETTER_BETA;
		case UNICODE_GREEK_SMALL_LETTER_MU:                     return CP437_GREEK_SMALL_LETTER_MU;
		case UNICODE_GREEK_SMALL_LETTER_NU:                     return 'v';
		case UNICODE_GREEK_SMALL_LETTER_OMICRON:                return 'o';
		case UNICODE_GREEK_SMALL_LETTER_OMICRON_WITH_TONOS:     return CP437_LATIN_SMALL_LETTER_O_WITH_ACUTE;
		case UNICODE_GREEK_SMALL_LETTER_UPSILON:                return 'u';
		case UNICODE_GREEK_SMALL_LETTER_UPSILON_WITH_TONOS:     return CP437_LATIN_SMALL_LETTER_U_WITH_ACUTE;
		case UNICODE_GREEK_SMALL_LETTER_UPSILON_WITH_DIALYTIKA:
		case UNICODE_GREEK_SMALL_LETTER_UPSILON_WITH_DIALYTIKA_AND_TONOS:
			return CP437_LATIN_SMALL_LETTER_U_WITH_DIAERESIS;
		case UNICODE_GREEK_SMALL_LETTER_IOTA:
		case UNICODE_GREEK_SMALL_LETTER_IOTA_WITH_TONOS:        return 'i';
		case UNICODE_GREEK_SMALL_LETTER_KAPPA:                  return 'k';
		case UNICODE_GREEK_SMALL_LETTER_CHI:                    return 'x';
		case UNICODE_GREEK_SMALL_LETTER_SIGMA:
		case UNICODE_GREEK_SMALL_LETTER_FINAL_SIGMA:            return CP437_GREEK_SMALL_LETTER_SIGMA;
		case UNICODE_GREEK_SMALL_LETTER_RHO:                    return 'p';
		case UNICODE_GREEK_SMALL_LETTER_ZETA:                   return 'z';
		case UNICODE_GREEK_SMALL_LETTER_ETA:                    return 'n';

		case UNICODE_HYPHEN:
		case UNICODE_NON_BREAKING_HYPHEN:
		case UNICODE_FIGURE_DASH:
		case UNICODE_EN_DASH:                                   return '-';

		case UNICODE_EM_DASH:
			return '\xC4';

		case UNICODE_BULLET:
		case UNICODE_BULLET_OPERATOR:                           return CP437_BULLET_OPERATOR;

		case UNICODE_NO_BREAK_SPACE:
		case UNICODE_EN_QUAD:
		case UNICODE_EM_QUAD:
		case UNICODE_EN_SPACE:
		case UNICODE_EM_SPACE:                                  return ' ';

		case UNICODE_SQUARE_ROOT:                               return CP437_SQUARE_ROOT;
		case UNICODE_CHECK_MARK:
		case UNICODE_HEAVY_CHECK_MARK:                          return CP437_CHECK_MARK;

		case UNICODE_MULTIPLICATION_X:
		case UNICODE_HEAVY_MULTIPLICATION_X:
		case UNICODE_BALLOT_X:
		case UNICODE_HEAVY_BALLOT_X:                            return 'x';

		case UNICODE_DIVISION_SLASH:                            return '/';
		case UNICODE_SET_MINUS:                                 return '\\';
		case UNICODE_ASTERISK_OPERATOR:                         return '*';

		case UNICODE_DOUBLE_VERTICAL_LINE:                      return CP437_BOX_DRAWINGS_DOUBLE_VERTICAL;

		case UNICODE_DOUBLE_LOW_LINE:                           return '=';
		case UNICODE_LEFT_SINGLE_QUOTATION_MARK:
		case UNICODE_RIGHT_SINGLE_QUOTATION_MARK:
		case UNICODE_SINGLE_HIGH_REVERSED_9_QUOTATION_MARK:     return '\'';
		case UNICODE_SINGLE_LOW_9_QUOTATION_MARK:               return ',';
		case UNICODE_LEFT_DOUBLE_QUOTATION_MARK:
		case UNICODE_RIGHT_DOUBLE_QUOTATION_MARK:
		case UNICODE_DOUBLE_LOW_9_QUOTATION_MARK:
		case UNICODE_DOUBLE_HIGH_REVERSED_9_QUOTATION_MARK:     return '"';
		case UNICODE_DAGGER:                                    return CP437_BOX_DRAWINGS_VERTICAL_AND_HORIZONTAL;

		case UNICODE_BLACK_SQUARE:
		case UNICODE_BLACK_SQUARE_CENTERED:
		case UNICODE_BLACK_SQUARE_FOR_STOP:
		case UNICODE_BLACK_SMALL_SQUARE:
		case UNICODE_BLACK_MEDIUM_SQUARE:
		case UNICODE_BLACK_LARGE_SQUARE:
		case UNICODE_BLACK_MEDIUM_SMALL_SQUARE:
		case UNICODE_BLACK_VERY_SMALL_SQUARE:
		case UNICODE_HALFWIDTH_BLACK_SQUARE:                    return CP437_HALFWIDTH_BLACK_SQUARE;

		case UNICODE_HORIZONTAL_BAR:
		case UNICODE_OVERLINE:
		case 0x2500: // Box Drawings Light Horizontal
		case 0x2501: // Box Drawings Heavy Horizontal
		case 0x2504: // Box Drawings Light Triple Dash Horizontal
		case 0x2505: // Box Drawings Heavy Triple Dash Horizontal
		case 0x2508: // Box Drawings Light Quadruple Dash Horizontal
		case 0x2509: // Box Drawings Heavy Quadruple Dash Horizontal
		case 0x254C: // Box Drawings Light Double Dash Horizontal
		case 0x254D: // Box Drawings Heavy Double Dash Horizontal
		case 0x2574: // Box Drawings Light Left
		case 0x2576: // Box Drawings Light Right
		case 0x2578: // Box Drawings Heavy Left
		case 0x257A: // Box Drawings Heavy Right
		case 0x257C: // Box Drawings Light Left and Heavy Right
		case 0x257E: // Box Drawings Heavy Left and Light Right
			return '\xC4';

		case 0x2502: // Box Drawings Light Vertical
		case 0x2503: // Box Drawings Heavy Vertical
		case 0x2506: // Box Drawings Light Triple Dash Vertical
		case 0x2507: // Box Drawings Heavy Triple Dash Vertical
		case 0x250A: // Box Drawings Light Quadruple Dash Vertical
		case 0x250B: // Box Drawings Heavy Quadruple Dash Vertical
			return '\xB3';

		case 0x250C: // BOX DRAWINGS LIGHT DOWN AND RIGHT
		case 0x250D:
		case 0x250E:
		case 0x250F: // BOX DRAWINGS HEAVY DOWN AND RIGHT
			return '\xDA';

		case 0x2510: // BOX DRAWINGS LIGHT DOWN AND LEFT
		case 0x2511:
		case 0x2512:
		case 0x2513: // BOX DRAWINGS HEAVY DOWN AND LEFT
			return '\xBF';

		case 0x2514: // BOX DRAWINGS LIGHT UP AND RIGHT
		case 0x2515:
		case 0x2516:
		case 0x2517: // BOX DRAWINGS HEAVY UP AND RIGHT
			return '\xC0';

		case 0x2518: // BOX DRAWINGS LIGHT UP AND LEFT
		case 0x2519:
		case 0x251A:
		case 0x251B: // BOX DRAWINGS HEAVY UP AND LEFT
			return '\xD9';

		case 0x251C: // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
		case 0x251D:
		case 0x251E:
		case 0x251F:
		case 0x2520:
		case 0x2521:
		case 0x2522:
		case 0x2523: // BOX DRAWINGS HEAVY VERTICAL AND RIGHT
			return '\xC3';

		case 0x2524: // BOX DRAWINGS LIGHT VERTICAL AND LEFT
		case 0x2525:
		case 0x2526:
		case 0x2527:
		case 0x2528:
		case 0x2529:
		case 0x252A:
		case 0x252B:
			return '\xB4';

		case 0x252C: // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
		case 0x252D:
		case 0x252E:
		case 0x252F:
		case 0x2530:
		case 0x2531:
		case 0x2532: // BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY
		case 0x2533: // BOX DRAWINGS HEAVY DOWN AND HORIZONTAL
			return '\xC2';

		case 0x2534: // BOX DRAWINGS LIGHT UP AND HORIZONTAL
		case 0x2535: // BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT
		case 0x2536: // BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT
		case 0x2537: // BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY
		case 0x2538: // BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT
		case 0x2539: // BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY
		case 0x253A: // BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY
		case 0x253B: // BOX DRAWINGS HEAVY UP AND HORIZONTAL
			return '\xC1';

		case 0x253C: // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
		case 0x253D: // BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT
		case 0x253E: // BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT
		case 0x253F: // BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY
		case 0x2540: // BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT
		case 0x2541: // BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT
		case 0x2542: // BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT
		case 0x2543: // BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT
		case 0x2544: // BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT
		case 0x2545: // BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT
		case 0x2546: // BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT
		case 0x2547: // BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY
		case 0x2548: // BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY
		case 0x2549: // BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY
		case 0x254A: // BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY
		case 0x254B: // BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL
			return '\xC5';

		case 0x254E: // BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL
		case 0x254F: // BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
			return '|';

		case 0x256D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
			return '\xDA';

		case 0x256E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT
			return '\xBF';

		case 0x256F: // BOX DRAWINGS LIGHT ARC UP AND LEFT
			return '\xD9';

		case 0x2570: // BOX DRAWINGS LIGHT ARC UP AND RIGHT
			return '\xC0';

		case 0x2571: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
			return '/';

		case 0x2572: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
			return '\\';

		case 0x2573: // BOX DRAWINGS LIGHT DIAGONAL CROSS
			return 'X';

		case 0x2575: // Box Drawings Light Up
		case 0x2577: // Box Drawings Light Down
		case 0x2579: // Box Drawings Heavy Up
		case 0x257B: // Box Drawings Heavy Down
		case 0x257D: // Box Drawings Light Up and Heavy Down
		case 0x257F: // Box Drawings Heavy Up and Light Down
			return '\xB3';

		case UNICODE_FULL_BLOCK:
			return CP437_FULL_BLOCK;
		case UNICODE_LOWER_HALF_BLOCK:
			return CP437_LOWER_HALF_BLOCK;
		case UNICODE_LEFT_HALF_BLOCK:
			return CP437_LEFT_HALF_BLOCK;
		case UNICODE_RIGHT_HALF_BLOCK:
			return CP437_RIGHT_HALF_BLOCK;
		case UNICODE_UPPER_HALF_BLOCK:
			return CP437_UPPER_HALF_BLOCK;

		case 0x2581: // Lower One Eighth Block
			return '_';

		case 0x2582: // Lower One Quarter Block
		case 0x2583: // Lower Three Eighths Block
			return '\x16';

		case 0x2585: // Lower Five Eighths Block
		case 0x2586: // Lower Three Quarters Block
		case 0x2587: // Lower Seven Eighths Block
			return '\xDC';

		case 0x2589: // Left Seven Eighths Block
			return '\xDB';

		case 0x258A: // Left Three Quarters Block
		case 0x258B: // Left Five Eighths Block
		case 0x258D: // Left Three Eighths Block
		case 0x258E: // Left One Quarter Block
		case 0x258F: // Left One Eighth Block
			return '\xDD';

		case 0x2595: // Right One Eighth Block
			return '\xDE';

		case 0x2594: // Upper One Eighth Block
			return '\xDF';

		case UNICODE_KATAKANA_LETTER_TU:                    return CP437_BLACK_SMILING_FACE;

		case UNICODE_SMALL_COMMA:
		case UNICODE_SMALL_IDEOGRAPHIC_COMMA:               return ',';
		case UNICODE_SMALL_FULL_STOP:                       return '.';
		case UNICODE_SMALL_SEMICOLON:                       return ';';
		case UNICODE_SMALL_COLON:                           return ':';
		case UNICODE_SMALL_QUESTION_MARK:                   return '?';
		case UNICODE_SMALL_EXCLAMATION_MARK:                return '!';
		case UNICODE_SMALL_EM_DASH:                         return '-';
		case UNICODE_SMALL_LEFT_PARENTHESIS:                return '(';
		case UNICODE_SMALL_RIGHT_PARENTHESIS:               return ')';
		case UNICODE_SMALL_LEFT_CURLY_BRACKET:              return '{';
		case UNICODE_SMALL_RIGHT_CURLY_BRACKET:             return '}';
		case UNICODE_SMALL_LEFT_TORTOISE_SHELL_BRACKET:     return '[';
		case UNICODE_SMALL_RIGHT_TORTOISE_SHELL_BRACKET:    return ']';
		case UNICODE_SMALL_NUMBER_SIGN:                     return '#';
		case UNICODE_SMALL_AMPERSAND:                       return '&';
		case UNICODE_SMALL_ASTERISK:                        return '*';
		case UNICODE_SMALL_PLUS_SIGN:                       return '+';
		case UNICODE_SMALL_HYPHEN_MINUS:                    return '-';
		case UNICODE_SMALL_LESS_THAN_SIGN:                  return '<';
		case UNICODE_SMALL_GREATER_THAN_SIGN:               return '>';
		case UNICODE_SMALL_EQUALS_SIGN:                     return '=';
		case UNICODE_SMALL_REVERSE_SOLIDUS:                 return '\\';
		case UNICODE_SMALL_DOLLAR_SIGN:                     return '$';
		case UNICODE_SMALL_PERCENT_SIGN:                    return '%';
		case UNICODE_SMALL_COMMERCIAL_AT:                   return '@';

		case UNICODE_VIEWDATA_SQUARE:                       return '#';

		default:
		{
			int i;

			if (codepoint >= UNICODE_FULLWIDTH_EXCLAMATION_MARK && codepoint <= UNICODE_FULLWIDTH_TILDE)
				return '!' + (codepoint - UNICODE_FULLWIDTH_EXCLAMATION_MARK);

			// Look for a 1:1 match in the CP437 -> Unicode table
			for (i = 1; i < 0x100; i++) {
				if (cp437_unicode_tbl[i] == codepoint)
					return i;
			}
			break;
		}
	}

	return '\0'; // Not-mapped
}
