/*
* $Id: ZsciiEncoding.java 536 2008-02-19 06:03:27Z weiju $
*
* Created on 2005/11/10
* Copyright 2005-2008 by Wei-ju Wu
* This file is part of The Z-machine Preservation Project (ZMPP).
*
* ZMPP is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZMPP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZMPP. If not, see <http://www.gnu.org/licenses/>.
*/
package org.zmpp.encoding;
/**
* The usage of ZSCII is a little confusing, within a story file it uses
* alphabet tables to encode/decode it to an unreadable format, for input and
* output it uses a more readable encoding which resembles iso-8859-n.
* ZsciiEncoding therefore captures this input/output aspect of ZSCII whereas
* ZsciiConverter and ZsciiString handle story file encoded strings.
*
* This class has a nonmodifiable state, so it can be shared throughout the
* whole application.
*
* @author Wei-ju Wu
* @version 1.0
*/
public class ZsciiEncoding {
public static final char NULL = 0;
public static final char DELETE = 8;
public static final char NEWLINE_10 = 10;
public static final char NEWLINE = 13;
public static final char ESCAPE = 27;
public static final char CURSOR_UP = 129;
public static final char CURSOR_DOWN = 130;
public static final char CURSOR_LEFT = 131;
public static final char CURSOR_RIGHT = 132;
public static final char ASCII_START = 32;
public static final char ASCII_END = 126;
/**
* The start of the accent range.
*/
public static final char ACCENT_START = 155;
/**
* End of the accent range.
*/
public static final char ACCENT_END = 251;
public static final char MOUSE_DOUBLE_CLICK = 253;
public static final char MOUSE_SINGLE_CLICK = 254;
private AccentTable accentTable;
/**
* Constructor.
*
* @param accentTable the accent table.
*/
public ZsciiEncoding(final AccentTable accentTable) {
super();
this.accentTable = accentTable;
}
/**
* Returns true if the input is a valid ZSCII character, false otherwise.
*
* @param zchar a ZSCII character
* @return true if valid, false otherwise
*/
public boolean isZsciiCharacter(final char zchar) {
switch (zchar) {
case NULL:
case DELETE:
case NEWLINE:
case ESCAPE:
return true;
default:
return isAscii(zchar) || isAccent(zchar)
|| isUnicodeCharacter(zchar);
}
}
/**
* Returns true if the specified character can be converted to a ZSCII
* character, false otherwise.
*
* @param c a unicode character
* @return true if c can be converted, false, otherwise
*/
public boolean isConvertableToZscii(final char c) {
return isAscii(c) || isInTranslationTable(c) || c == '\n'
|| c == 0 || isUnicodeCharacter(c);
}
/**
* Converts a ZSCII character to a unicode character. Will return '?' if the
* given character is not known.
*
* @param zchar a ZSCII character.
* @return the unicode representation
*/
public char getUnicodeChar(final char zchar) {
if (isAscii(zchar)) {
return (char) zchar;
}
if (isAccent(zchar)) {
final int index = zchar - ACCENT_START;
if (index < accentTable.getLength()) {
return (char) accentTable.getAccent(index);
}
}
if (zchar == NULL) {
return '\0';
}
if (zchar == NEWLINE || zchar == NEWLINE_10) {
return '\n';
}
if (isUnicodeCharacter(zchar)) {
return (char) zchar;
}
return '?';
}
/**
* Converts the specified string into its ZSCII representation.
*
* @param str the input string
* @return the ZSCII representation
*/
public char[] convertToZscii(final String str) {
final char[] result = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
result[i] = getZsciiChar(str.charAt(i));
}
return result;
}
/**
* Converts the specified unicode character to a ZSCII character. Will
* return 0 if the character can not be converted.
*
* @param c the unicode character to convert
* @return the ZSCII character
*/
public char getZsciiChar(final char c) {
if (isAscii(c)) {
return c;
} else if (isInTranslationTable(c)) {
return (char) (getIndexInTranslationTable(c) + ACCENT_START);
} else if (c == '\n') {
return NEWLINE;
}
return 0;
}
private boolean isInTranslationTable(final char c) {
for (int i = 0; i < accentTable.getLength(); i++) {
if (accentTable.getAccent(i) == c) {
return true;
}
}
return false;
}
private int getIndexInTranslationTable(final char c) {
for (int i = 0; i < accentTable.getLength(); i++) {
if (accentTable.getAccent(i) == c) {
return i;
}
}
return -1;
}
/**
* Tests the given ZSCII character if it falls in the ASCII range.
*
* @param zchar the input character
* @return true if in the ASCII range, false, otherwise
*/
public static boolean isAscii(final char zchar) {
return zchar >= ASCII_START && zchar <= ASCII_END;
}
/**
* Tests the given ZSCII character for whether it is in the special range.
*
* @param zchar the input character
* @return true if in special range, false, otherwise
*/
public static boolean isAccent(final char zchar) {
return zchar >= ACCENT_START && zchar <= ACCENT_END;
}
/**
* Returns true if zsciiChar is a cursor key.
*
* @param zsciiChar a cursor key
* @return true if cursor key, false, otherwise
*/
public static boolean isCursorKey(final char zsciiChar) {
return zsciiChar >= CURSOR_UP && zsciiChar <= CURSOR_RIGHT;
}
/**
* Returns true if zchar is in the unicode range.
*
* @param zchar a zscii character
* @return the unicode character
*/
private static boolean isUnicodeCharacter(final char zchar) {
return zchar >= 256;
}
/**
* Returns true if zsciiChar is a function key.
*
* @param zsciiChar the zscii char
* @return true if function key, false, otherwise
*/
public static boolean isFunctionKey(final char zsciiChar) {
return (zsciiChar >= 129 && zsciiChar <= 154)
|| (zsciiChar >= 252 && zsciiChar <= 254);
}
/**
* Converts the character to lower case.
*
* @param zsciiChar the ZSCII character to convert
* @return the lower case character
*/
public char toLower(final char zsciiChar) {
if (isAscii(zsciiChar)) {
return Character.toLowerCase(zsciiChar);
}
if (isAccent(zsciiChar)) {
return (char) (accentTable.getIndexOfLowerCase(zsciiChar - ACCENT_START)
+ ACCENT_START);
}
return zsciiChar;
}
}